Merge lp:~christian-w/lightdm-gtk-greeter/pam-multiple-prompts into lp:~lightdm-gtk-greeter-team/lightdm-gtk-greeter/trunk

Proposed by Christian Seiler
Status: Merged
Merged at revision: 212
Proposed branch: lp:~christian-w/lightdm-gtk-greeter/pam-multiple-prompts
Merge into: lp:~lightdm-gtk-greeter-team/lightdm-gtk-greeter/trunk
Diff against target: 274 lines (+148/-22)
1 file modified
src/lightdm-gtk-greeter.c (+148/-22)
To merge this branch: bzr merge lp:~christian-w/lightdm-gtk-greeter/pam-multiple-prompts
Reviewer Review Type Date Requested Status
Sean Davis maintainer Approve
Review via email: mp+205254@code.launchpad.net

Commit message

Support multiple prompts in PAM conversation rounds

PAM allows for a single round of a conversation to contain multiple
prompts. Use case: if a user's password is expired, the Kerberso 5 PAM
module, pam_krb5, makes use of PAM's feature to supply multiple prompts
in order to ask the user to change their password during the
authentication phase (after verification of the old password).

This patch changes the show_message_cb and show_prompt_cb signal
handlers to record the information in a list of pending prompts and use
a central processing function to display one prompt at a time to make
multiple prompts work properly. Additionally, for all prompts after the
first one, the text accompanying the prompt is used for the message
label (unless that has already been filled by a message), so that it is
clear to the user for what additional information they are asked.

Description of the change

Comments (in addition to commit message):

Related discussion: http://lists.freedesktop.org/archives/lightdm/2014-February/000500.html

Compared to my initial patch, this now does NOT rely on any changes to
the lightdm API, so this should work with all lightdm versions.

I have tested this with a local Kerberos realm in a KVM on Debian
Wheezy with a manually compiled lightdm (from bzr trunk) and
lightdm-gtk-greeter (also from bzr trunk), for all six possible
combinations for a) the PAM module asking for a password change or not
and b) using the user list, selecting "other" on the user list and
entering the name manually and disabling the user list in the lightdm
config.

I have not tested this with gtk2, but I don't see why it wouldn't.

To post a comment you must log in.
Revision history for this message
Sean Davis (bluesabre) wrote :

This looks fine to me, I'll merge it now. Thanks for the patch!

review: Approve (maintainer)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/lightdm-gtk-greeter.c'
2--- src/lightdm-gtk-greeter.c 2014-01-22 00:01:35 +0000
3+++ src/lightdm-gtk-greeter.c 2014-02-06 20:14:42 +0000
4@@ -31,6 +31,7 @@
5 #else
6 #include <gdk/gdkkeysyms.h>
7 #endif
8+#include <glib/gslist.h>
9
10 #ifdef HAVE_LIBINDICATOR
11 #include <libindicator/indicator-object.h>
12@@ -72,6 +73,9 @@
13 static GError *a11y_keyboard_error;
14 static GtkWindow *onboard_window;
15
16+/* Pending Questions */
17+static GSList *pending_questions = NULL;
18+
19 GSList *backgrounds = NULL;
20
21 /* Current choices */
22@@ -87,6 +91,7 @@
23 static GdkColor *default_background_color = NULL;
24 #endif
25 static gboolean cancelling = FALSE, prompted = FALSE;
26+static gboolean prompt_active = FALSE, password_prompted = FALSE;
27 #if GTK_CHECK_VERSION (3, 0, 0)
28 #else
29 static GdkRegion *window_region = NULL;
30@@ -94,6 +99,16 @@
31
32 typedef struct
33 {
34+ gboolean is_prompt;
35+ union {
36+ LightDMMessageType message;
37+ LightDMPromptType prompt;
38+ } type;
39+ gchar *text;
40+} PAMConversationMessage;
41+
42+typedef struct
43+{
44 gint value;
45 /* +0 and -0 */
46 gint sign;
47@@ -114,6 +129,13 @@
48 GdkPixbuf* default_user_pixbuf = NULL;
49 gchar* default_user_icon = "avatar-default";
50
51+static void
52+pam_message_finalize (PAMConversationMessage *message)
53+{
54+ g_free (message->text);
55+ g_free (message);
56+}
57+
58
59 #ifdef HAVE_LIBINDICATOR
60 static gboolean
61@@ -702,6 +724,14 @@
62
63 cancelling = FALSE;
64 prompted = FALSE;
65+ password_prompted = FALSE;
66+ prompt_active = FALSE;
67+
68+ if (pending_questions)
69+ {
70+ g_slist_free_full (pending_questions, (GDestroyNotify) pam_message_finalize);
71+ pending_questions = NULL;
72+ }
73
74 g_key_file_set_value (state, "greeter", "last-user", username);
75 data = g_key_file_to_data (state, &data_length, &error);
76@@ -756,6 +786,12 @@
77 GtkTreeIter iter;
78 gboolean other = FALSE;
79
80+ if (pending_questions)
81+ {
82+ g_slist_free_full (pending_questions, (GDestroyNotify) pam_message_finalize);
83+ pending_questions = NULL;
84+ }
85+
86 /* If in authentication then stop that first */
87 cancelling = FALSE;
88 if (lightdm_greeter_get_in_authentication (greeter))
89@@ -765,6 +801,9 @@
90 set_message_label ("");
91 }
92
93+ /* Make sure password entry is back to normal */
94+ gtk_entry_set_visibility (password_entry, FALSE);
95+
96 /* Force refreshing the prompt_box for "Other" */
97 model = gtk_combo_box_get_model (user_combo);
98
99@@ -1103,6 +1142,80 @@
100 set_message_label ("");
101 }
102
103+static const gchar*
104+get_message_label (void)
105+{
106+ return gtk_label_get_text (message_label);
107+}
108+
109+static void
110+process_prompts (LightDMGreeter *greeter)
111+{
112+ if (!pending_questions)
113+ return;
114+
115+ /* always allow the user to change username again */
116+ gtk_widget_set_sensitive (GTK_WIDGET (username_entry), TRUE);
117+ gtk_widget_set_sensitive (GTK_WIDGET (password_entry), TRUE);
118+
119+ /* Special case: no user selected from list, so PAM asks us for the user
120+ * via a prompt. For that case, use the username field */
121+ if (!prompted && pending_questions && !pending_questions->next &&
122+ ((PAMConversationMessage *) pending_questions->data)->is_prompt &&
123+ ((PAMConversationMessage *) pending_questions->data)->type.prompt != LIGHTDM_PROMPT_TYPE_SECRET &&
124+ gtk_widget_get_visible ((GTK_WIDGET (username_entry))) &&
125+ lightdm_greeter_get_authentication_user (greeter) == NULL)
126+ {
127+ prompted = TRUE;
128+ prompt_active = TRUE;
129+ gtk_widget_grab_focus (GTK_WIDGET (username_entry));
130+ return;
131+ }
132+
133+ while (pending_questions)
134+ {
135+ PAMConversationMessage *message = (PAMConversationMessage *) pending_questions->data;
136+ pending_questions = g_slist_remove (pending_questions, (gconstpointer) message);
137+
138+ if (!message->is_prompt)
139+ {
140+ /* FIXME: this doesn't show multiple messages, but that was
141+ * already the case before. */
142+ set_message_label (message->text);
143+ continue;
144+ }
145+
146+ gtk_entry_set_text (password_entry, "");
147+ gtk_entry_set_visibility (password_entry, message->type.prompt != LIGHTDM_PROMPT_TYPE_SECRET);
148+ if (get_message_label()[0] == 0 && password_prompted)
149+ {
150+ /* No message was provided beforehand and this is not the
151+ * first password prompt, so use the prompt as label,
152+ * otherwise the user will be completely unclear of what
153+ * is going on. Actually, the fact that prompt messages are
154+ * not shown is problematic in general, especially if
155+ * somebody uses a custom PAM module that wants to ask
156+ * something different. */
157+ gchar *str = message->text;
158+ if (g_str_has_suffix (str, ": "))
159+ str = g_strndup (str, strlen (str) - 2);
160+ else if (g_str_has_suffix (str, ":"))
161+ str = g_strndup (str, strlen (str) - 1);
162+ set_message_label (str);
163+ if (str != message->text)
164+ g_free (str);
165+ }
166+ gtk_widget_grab_focus (GTK_WIDGET (password_entry));
167+ prompted = TRUE;
168+ password_prompted = TRUE;
169+ prompt_active = TRUE;
170+
171+ /* If we have more stuff after a prompt, assume that other prompts are pending,
172+ * so stop here. */
173+ break;
174+ }
175+}
176+
177 void login_cb (GtkWidget *widget);
178 G_MODULE_EXPORT
179 void
180@@ -1115,11 +1228,19 @@
181 gtk_widget_set_sensitive (GTK_WIDGET (username_entry), FALSE);
182 gtk_widget_set_sensitive (GTK_WIDGET (password_entry), FALSE);
183 set_message_label ("");
184+ prompt_active = FALSE;
185
186 if (lightdm_greeter_get_is_authenticated (greeter))
187 start_session ();
188 else if (lightdm_greeter_get_in_authentication (greeter))
189+ {
190 lightdm_greeter_respond (greeter, gtk_entry_get_text (password_entry));
191+ /* If we have questions pending, then we continue processing
192+ * those, until we are done. (Otherwise, authentication will
193+ * not complete.) */
194+ if (pending_questions)
195+ process_prompts (greeter);
196+ }
197 else
198 start_authentication (lightdm_greeter_get_authentication_user (greeter));
199 }
200@@ -1135,40 +1256,39 @@
201 static void
202 show_prompt_cb (LightDMGreeter *greeter, const gchar *text, LightDMPromptType type)
203 {
204- prompted = TRUE;
205+ PAMConversationMessage *message_obj = g_new (PAMConversationMessage, 1);
206+ if (message_obj)
207+ {
208+ message_obj->is_prompt = TRUE;
209+ message_obj->type.prompt = type;
210+ message_obj->text = g_strdup (text);
211+ pending_questions = g_slist_append (pending_questions, message_obj);
212+ }
213
214- gtk_widget_set_sensitive (GTK_WIDGET (username_entry), TRUE);
215- gtk_widget_set_sensitive (GTK_WIDGET (password_entry), TRUE);
216- gtk_entry_set_text (password_entry, "");
217- gtk_entry_set_visibility (password_entry, FALSE);
218- if (type == LIGHTDM_PROMPT_TYPE_SECRET) // Password
219- {
220- gtk_widget_grab_focus (GTK_WIDGET (password_entry));
221- }
222- else
223- {
224- if (gtk_widget_get_visible ((GTK_WIDGET (username_entry))))
225- gtk_widget_grab_focus (GTK_WIDGET (username_entry));
226- else
227- gtk_widget_grab_focus (GTK_WIDGET (password_entry));
228- }
229+ if (!prompt_active)
230+ process_prompts (greeter);
231 }
232
233 static void
234 show_message_cb (LightDMGreeter *greeter, const gchar *text, LightDMMessageType type)
235 {
236- set_message_label (text);
237-}
238+ PAMConversationMessage *message_obj = g_new (PAMConversationMessage, 1);
239+ if (message_obj)
240+ {
241+ message_obj->is_prompt = FALSE;
242+ message_obj->type.message = type;
243+ message_obj->text = g_strdup (text);
244+ pending_questions = g_slist_append (pending_questions, message_obj);
245+ }
246
247-static const gchar*
248-get_message_label (void)
249-{
250- return gtk_label_get_text (message_label);
251+ if (!prompt_active)
252+ process_prompts (greeter);
253 }
254
255 static void
256 authentication_complete_cb (LightDMGreeter *greeter)
257 {
258+ prompt_active = FALSE;
259 gtk_entry_set_text (password_entry, "");
260
261 if (cancelling)
262@@ -1177,6 +1297,12 @@
263 return;
264 }
265
266+ if (pending_questions)
267+ {
268+ g_slist_free_full (pending_questions, (GDestroyNotify) pam_message_finalize);
269+ pending_questions = NULL;
270+ }
271+
272 if (lightdm_greeter_get_is_authenticated (greeter))
273 {
274 if (prompted)

Subscribers

People subscribed via source and target branches