Merge lp:~mterry/unity8/dismiss-old-pam-prompts into lp:unity8

Proposed by Michael Terry
Status: Merged
Approved by: Michael Terry
Approved revision: 1180
Merged at revision: 1192
Proposed branch: lp:~mterry/unity8/dismiss-old-pam-prompts
Merge into: lp:unity8
Diff against target: 153 lines (+43/-15)
2 files modified
qml/Greeter/GreeterContent.qml (+0/-1)
tests/mocks/LightDM/demo/GreeterPrivate.cpp (+43/-14)
To merge this branch: bzr merge lp:~mterry/unity8/dismiss-old-pam-prompts
Reviewer Review Type Date Requested Status
Michał Sawicz Approve
PS Jenkins bot (community) continuous-integration Approve
Stephen M. Webb (community) Approve
Review via email: mp+231363@code.launchpad.net

Commit message

Allow logging into a desktop or tablet session again, by properly dismissing old PAM conversations. (LP: #1350878)

In a desktop or tablet, we were accidentally starting two PAM conversations in sequence on startup. Which is a small bug; it shouldn't normally be a problem, since each new PAM conversation should kill the old one.

But the way we were killing the old one was subject to a thread race condition. See, a PAM conversation thread won't exit until all its prompts are answered. And what we do when we kill a PAM conversation is to answer all prompts with empty strings.

But it's possible that when we want to kill a PAM conversation that it hasn't actually gotten to the point of prompting us yet. And when those prompts do come through, we were treating them as prompts for the new PAM conversation.

So I've changed the PAM conversation logic to include a pam_handle and compare the handle with the current handle when being prompted. If it's an old handle, we just dismiss the prompt with an empty string response.

Oh, and I fixed the bug that caused two prompts on startup in the first place. (But we still need the above logic anyway, for when you switch users quickly.)

Description of the change

Allow logging into a desktop or tablet session again, by properly dismissing old PAM conversations. (LP: #1350878)

In a desktop or tablet, we were accidentally starting two PAM conversations in sequence on startup. Which is a small bug; it shouldn't normally be a problem, since each new PAM conversation should kill the old one.

But the way we were killing the old one was subject to a thread race condition. See, a PAM conversation thread won't exit until all its prompts are answered. And what we do when we kill a PAM conversation is to answer all prompts with empty strings.

But it's possible that when we want to kill a PAM conversation that it hasn't actually gotten to the point of prompting us yet. And when those prompts do come through, we were treating them as prompts for the new PAM conversation.

So I've changed the PAM conversation logic to include a pam_handle and compare the handle with the current handle when being prompted. If it's an old handle, we just dismiss the prompt with an empty string response.

Oh, and I fixed the bug that caused two prompts on startup in the first place. (But we still need the above logic anyway, for when you switch users quickly.)

== Checklist ==

 * Are there any related MPs required for this MP to build/function as expected? Please list.
 - No

 * Did you perform an exploratory manual test run of your code change and any related functionality?
 - Yes

 * Did you make sure that your branch does not contain spurious tags?
 - Yes

 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
 - NA

 * If you changed the UI, has there been a design review?
 - NA

To post a comment you must log in.
Revision history for this message
Michael Terry (mterry) wrote :

To test this, you can edit qml/Shell.qml to set "tablet: true" and then run "./run.sh --lightdm=demo -f" and try to log in with your desktop password.

Or you can install a unity8 desktop session and try to log into unity8. Or install Touch on a tablet and set a password.

Revision history for this message
Stephen M. Webb (bregma) wrote :

Works for me on the desktop.

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote :

 * Did you perform an exploratory manual test run of the code change and any related functionality?
Stephen did :)

 * Did CI run pass? If not, please explain why.
Y

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'qml/Greeter/GreeterContent.qml'
--- qml/Greeter/GreeterContent.qml 2014-06-11 15:36:51 +0000
+++ qml/Greeter/GreeterContent.qml 2014-08-19 13:44:40 +0000
@@ -87,7 +87,6 @@
8787
88 onLoaded: {88 onLoaded: {
89 item.currentIndex = greeterContentLoader.currentIndex;89 item.currentIndex = greeterContentLoader.currentIndex;
90 item.resetAuthentication();
91 }90 }
9291
93 Binding {92 Binding {
9493
=== modified file 'tests/mocks/LightDM/demo/GreeterPrivate.cpp'
--- tests/mocks/LightDM/demo/GreeterPrivate.cpp 2014-07-15 16:38:02 +0000
+++ tests/mocks/LightDM/demo/GreeterPrivate.cpp 2014-08-19 13:44:40 +0000
@@ -33,6 +33,12 @@
33{33{
34 Q_OBJECT34 Q_OBJECT
3535
36 struct AppData
37 {
38 GreeterImpl *impl;
39 pam_handle *handle;
40 };
41
36 typedef QFutureInterface<QString> ResponseFuture;42 typedef QFutureInterface<QString> ResponseFuture;
3743
38public:44public:
@@ -46,11 +52,11 @@
4652
47 connect(&futureWatcher, SIGNAL(finished()),53 connect(&futureWatcher, SIGNAL(finished()),
48 this, SLOT(finishPam()));54 this, SLOT(finishPam()));
49 connect(this, SIGNAL(showMessage(QString, QLightDM::Greeter::MessageType)),55 connect(this, SIGNAL(showMessage(pam_handle *, QString, QLightDM::Greeter::MessageType)),
50 greeter, SIGNAL(showMessage(QString, QLightDM::Greeter::MessageType)));56 this, SLOT(handleMessage(pam_handle *, QString, QLightDM::Greeter::MessageType)));
51 // This next connect is how we pass ResponseFutures between threads57 // This next connect is how we pass ResponseFutures between threads
52 connect(this, SIGNAL(showPrompt(QString, QLightDM::Greeter::PromptType, QLightDM::GreeterImpl::ResponseFuture)),58 connect(this, SIGNAL(showPrompt(pam_handle *, QString, QLightDM::Greeter::PromptType, QLightDM::GreeterImpl::ResponseFuture)),
53 this, SLOT(handlePrompt(QString, QLightDM::Greeter::PromptType, QLightDM::GreeterImpl::ResponseFuture)));59 this, SLOT(handlePrompt(pam_handle *, QString, QLightDM::Greeter::PromptType, QLightDM::GreeterImpl::ResponseFuture)));
54 }60 }
5561
56 void start(QString username)62 void start(QString username)
@@ -64,14 +70,19 @@
64 pam_end(handle, PAM_CONV_ERR);70 pam_end(handle, PAM_CONV_ERR);
65 }71 }
6672
73 AppData *appData = new AppData();
74 appData->impl = this;
75
67 // Now actually start a new conversation with PAM76 // Now actually start a new conversation with PAM
68 pam_conv conversation;77 pam_conv conversation;
69 conversation.conv = converseWithPam;78 conversation.conv = converseWithPam;
70 conversation.appdata_ptr = static_cast<void*>(this);79 conversation.appdata_ptr = static_cast<void*>(appData);
7180
72 if (pam_start("lightdm", username.toUtf8(), &conversation, &pamHandle) == PAM_SUCCESS) {81 if (pam_start("lightdm", username.toUtf8(), &conversation, &pamHandle) == PAM_SUCCESS) {
82 appData->handle = pamHandle;
73 futureWatcher.setFuture(QtConcurrent::run(authenticateWithPam, pamHandle));83 futureWatcher.setFuture(QtConcurrent::run(authenticateWithPam, pamHandle));
74 } else {84 } else {
85 delete appData;
75 greeterPrivate->authenticated = false;86 greeterPrivate->authenticated = false;
76 Q_EMIT greeter->showMessage("Internal error: could not start PAM authentication", QLightDM::Greeter::MessageTypeError);87 Q_EMIT greeter->showMessage("Internal error: could not start PAM authentication", QLightDM::Greeter::MessageTypeError);
77 Q_EMIT greeter->authenticationComplete();88 Q_EMIT greeter->authenticationComplete();
@@ -103,7 +114,9 @@
103 if (!tmp_response)114 if (!tmp_response)
104 return PAM_CONV_ERR;115 return PAM_CONV_ERR;
105116
106 GreeterImpl* impl = static_cast<GreeterImpl*>(appdata_ptr);117 AppData *appData = static_cast<AppData*>(appdata_ptr);
118 GreeterImpl *impl = appData->impl;
119 pam_handle *handle = appData->handle;
107120
108 int count;121 int count;
109 QVector<ResponseFuture> responses;122 QVector<ResponseFuture> responses;
@@ -117,7 +130,7 @@
117 QString message(msg[count]->msg);130 QString message(msg[count]->msg);
118 responses.append(ResponseFuture());131 responses.append(ResponseFuture());
119 responses.last().reportStarted();132 responses.last().reportStarted();
120 Q_EMIT impl->showPrompt(message, Greeter::PromptTypeQuestion, responses.last());133 Q_EMIT impl->showPrompt(handle, message, Greeter::PromptTypeQuestion, responses.last());
121 break;134 break;
122 }135 }
123 case PAM_PROMPT_ECHO_OFF:136 case PAM_PROMPT_ECHO_OFF:
@@ -125,19 +138,19 @@
125 QString message(msg[count]->msg);138 QString message(msg[count]->msg);
126 responses.append(ResponseFuture());139 responses.append(ResponseFuture());
127 responses.last().reportStarted();140 responses.last().reportStarted();
128 Q_EMIT impl->showPrompt(message, Greeter::PromptTypeSecret, responses.last());141 Q_EMIT impl->showPrompt(handle, message, Greeter::PromptTypeSecret, responses.last());
129 break;142 break;
130 }143 }
131 case PAM_TEXT_INFO:144 case PAM_TEXT_INFO:
132 {145 {
133 QString message(msg[count]->msg);146 QString message(msg[count]->msg);
134 Q_EMIT impl->showMessage(message, Greeter::MessageTypeInfo);147 Q_EMIT impl->showMessage(handle, message, Greeter::MessageTypeInfo);
135 break;148 break;
136 }149 }
137 default:150 default:
138 {151 {
139 QString message(msg[count]->msg);152 QString message(msg[count]->msg);
140 Q_EMIT impl->showMessage(message, Greeter::MessageTypeError);153 Q_EMIT impl->showMessage(handle, message, Greeter::MessageTypeError);
141 break;154 break;
142 }155 }
143 }156 }
@@ -159,6 +172,8 @@
159 }172 }
160 }173 }
161174
175 delete appData;
176
162 if (raise_error)177 if (raise_error)
163 {178 {
164 for (int i = 0; i < count; ++i)179 for (int i = 0; i < count; ++i)
@@ -186,8 +201,8 @@
186 }201 }
187202
188Q_SIGNALS:203Q_SIGNALS:
189 void showMessage(QString text, QLightDM::Greeter::MessageType type);204 void showMessage(pam_handle *handle, QString text, QLightDM::Greeter::MessageType type);
190 void showPrompt(QString text, QLightDM::Greeter::PromptType type, QLightDM::GreeterImpl::ResponseFuture response);205 void showPrompt(pam_handle *handle, QString text, QLightDM::Greeter::PromptType type, QLightDM::GreeterImpl::ResponseFuture response);
191206
192private Q_SLOTS:207private Q_SLOTS:
193 void finishPam()208 void finishPam()
@@ -205,8 +220,22 @@
205 Q_EMIT greeter->authenticationComplete();220 Q_EMIT greeter->authenticationComplete();
206 }221 }
207222
208 void handlePrompt(QString text, QLightDM::Greeter::PromptType type, QLightDM::GreeterImpl::ResponseFuture future)223 void handleMessage(pam_handle *handle, QString text, QLightDM::Greeter::MessageType type)
209 {224 {
225 if (handle != pamHandle)
226 return;
227
228 Q_EMIT greeter->showMessage(text, type);
229 }
230
231 void handlePrompt(pam_handle *handle, QString text, QLightDM::Greeter::PromptType type, QLightDM::GreeterImpl::ResponseFuture future)
232 {
233 if (handle != pamHandle) {
234 future.reportResult(QString());
235 future.reportFinished();
236 return;
237 }
238
210 futures.enqueue(future);239 futures.enqueue(future);
211 Q_EMIT greeter->showPrompt(text, type);240 Q_EMIT greeter->showPrompt(text, type);
212 }241 }

Subscribers

People subscribed via source and target branches