summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Seiler <christian@iwakd.de>2014-02-06 20:54:58 +0100
committerChristian Seiler <christian@iwakd.de>2014-02-06 20:54:58 +0100
commit2510c9baac26e7904d9cd772256c891211c7d5b6 (patch)
treef2c080e3f01351a0619c6d975e0a3e366d909a2e
parent0b1adeb4677b97b3552d3cedc25d179366f7a461 (diff)
downloadlightdm-gtk-greeter-git-2510c9baac26e7904d9cd772256c891211c7d5b6.tar.gz
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.
-rw-r--r--src/lightdm-gtk-greeter.c168
1 files changed, 147 insertions, 21 deletions
diff --git a/src/lightdm-gtk-greeter.c b/src/lightdm-gtk-greeter.c
index 550b19a..237545c 100644
--- a/src/lightdm-gtk-greeter.c
+++ b/src/lightdm-gtk-greeter.c
@@ -31,6 +31,7 @@
#else
#include <gdk/gdkkeysyms.h>
#endif
+#include <glib/gslist.h>
#ifdef HAVE_LIBINDICATOR
#include <libindicator/indicator-object.h>
@@ -72,6 +73,9 @@ static GPid a11y_kbd_pid = 0;
static GError *a11y_keyboard_error;
static GtkWindow *onboard_window;
+/* Pending Questions */
+static GSList *pending_questions = NULL;
+
GSList *backgrounds = NULL;
/* Current choices */
@@ -87,6 +91,7 @@ static GdkRGBA *default_background_color = NULL;
static GdkColor *default_background_color = NULL;
#endif
static gboolean cancelling = FALSE, prompted = FALSE;
+static gboolean prompt_active = FALSE, password_prompted = FALSE;
#if GTK_CHECK_VERSION (3, 0, 0)
#else
static GdkRegion *window_region = NULL;
@@ -94,6 +99,16 @@ static GdkRegion *window_region = NULL;
typedef struct
{
+ gboolean is_prompt;
+ union {
+ LightDMMessageType message;
+ LightDMPromptType prompt;
+ } type;
+ gchar *text;
+} PAMConversationMessage;
+
+typedef struct
+{
gint value;
/* +0 and -0 */
gint sign;
@@ -114,6 +129,13 @@ WindowPosition main_window_pos;
GdkPixbuf* default_user_pixbuf = NULL;
gchar* default_user_icon = "avatar-default";
+static void
+pam_message_finalize (PAMConversationMessage *message)
+{
+ g_free (message->text);
+ g_free (message);
+}
+
#ifdef HAVE_LIBINDICATOR
static gboolean
@@ -702,6 +724,14 @@ start_authentication (const gchar *username)
cancelling = FALSE;
prompted = FALSE;
+ password_prompted = FALSE;
+ prompt_active = FALSE;
+
+ if (pending_questions)
+ {
+ g_slist_free_full (pending_questions, (GDestroyNotify) pam_message_finalize);
+ pending_questions = NULL;
+ }
g_key_file_set_value (state, "greeter", "last-user", username);
data = g_key_file_to_data (state, &data_length, &error);
@@ -756,6 +786,12 @@ cancel_authentication (void)
GtkTreeIter iter;
gboolean other = FALSE;
+ if (pending_questions)
+ {
+ g_slist_free_full (pending_questions, (GDestroyNotify) pam_message_finalize);
+ pending_questions = NULL;
+ }
+
/* If in authentication then stop that first */
cancelling = FALSE;
if (lightdm_greeter_get_in_authentication (greeter))
@@ -765,6 +801,9 @@ cancel_authentication (void)
set_message_label ("");
}
+ /* Make sure password entry is back to normal */
+ gtk_entry_set_visibility (password_entry, FALSE);
+
/* Force refreshing the prompt_box for "Other" */
model = gtk_combo_box_get_model (user_combo);
@@ -1103,6 +1142,80 @@ user_combobox_active_changed_cb (GtkComboBox *widget, LightDMGreeter *greeter)
set_message_label ("");
}
+static const gchar*
+get_message_label (void)
+{
+ return gtk_label_get_text (message_label);
+}
+
+static void
+process_prompts (LightDMGreeter *greeter)
+{
+ if (!pending_questions)
+ return;
+
+ /* always allow the user to change username again */
+ gtk_widget_set_sensitive (GTK_WIDGET (username_entry), TRUE);
+ gtk_widget_set_sensitive (GTK_WIDGET (password_entry), TRUE);
+
+ /* Special case: no user selected from list, so PAM asks us for the user
+ * via a prompt. For that case, use the username field */
+ if (!prompted && pending_questions && !pending_questions->next &&
+ ((PAMConversationMessage *) pending_questions->data)->is_prompt &&
+ ((PAMConversationMessage *) pending_questions->data)->type.prompt != LIGHTDM_PROMPT_TYPE_SECRET &&
+ gtk_widget_get_visible ((GTK_WIDGET (username_entry))) &&
+ lightdm_greeter_get_authentication_user (greeter) == NULL)
+ {
+ prompted = TRUE;
+ prompt_active = TRUE;
+ gtk_widget_grab_focus (GTK_WIDGET (username_entry));
+ return;
+ }
+
+ while (pending_questions)
+ {
+ PAMConversationMessage *message = (PAMConversationMessage *) pending_questions->data;
+ pending_questions = g_slist_remove (pending_questions, (gconstpointer) message);
+
+ if (!message->is_prompt)
+ {
+ /* FIXME: this doesn't show multiple messages, but that was
+ * already the case before. */
+ set_message_label (message->text);
+ continue;
+ }
+
+ gtk_entry_set_text (password_entry, "");
+ gtk_entry_set_visibility (password_entry, message->type.prompt != LIGHTDM_PROMPT_TYPE_SECRET);
+ if (get_message_label()[0] == 0 && password_prompted)
+ {
+ /* No message was provided beforehand and this is not the
+ * first password prompt, so use the prompt as label,
+ * otherwise the user will be completely unclear of what
+ * is going on. Actually, the fact that prompt messages are
+ * not shown is problematic in general, especially if
+ * somebody uses a custom PAM module that wants to ask
+ * something different. */
+ gchar *str = message->text;
+ if (g_str_has_suffix (str, ": "))
+ str = g_strndup (str, strlen (str) - 2);
+ else if (g_str_has_suffix (str, ":"))
+ str = g_strndup (str, strlen (str) - 1);
+ set_message_label (str);
+ if (str != message->text)
+ g_free (str);
+ }
+ gtk_widget_grab_focus (GTK_WIDGET (password_entry));
+ prompted = TRUE;
+ password_prompted = TRUE;
+ prompt_active = TRUE;
+
+ /* If we have more stuff after a prompt, assume that other prompts are pending,
+ * so stop here. */
+ break;
+ }
+}
+
void login_cb (GtkWidget *widget);
G_MODULE_EXPORT
void
@@ -1115,11 +1228,19 @@ login_cb (GtkWidget *widget)
gtk_widget_set_sensitive (GTK_WIDGET (username_entry), FALSE);
gtk_widget_set_sensitive (GTK_WIDGET (password_entry), FALSE);
set_message_label ("");
+ prompt_active = FALSE;
if (lightdm_greeter_get_is_authenticated (greeter))
start_session ();
else if (lightdm_greeter_get_in_authentication (greeter))
+ {
lightdm_greeter_respond (greeter, gtk_entry_get_text (password_entry));
+ /* If we have questions pending, then we continue processing
+ * those, until we are done. (Otherwise, authentication will
+ * not complete.) */
+ if (pending_questions)
+ process_prompts (greeter);
+ }
else
start_authentication (lightdm_greeter_get_authentication_user (greeter));
}
@@ -1135,40 +1256,39 @@ cancel_cb (GtkWidget *widget)
static void
show_prompt_cb (LightDMGreeter *greeter, const gchar *text, LightDMPromptType type)
{
- prompted = TRUE;
-
- gtk_widget_set_sensitive (GTK_WIDGET (username_entry), TRUE);
- gtk_widget_set_sensitive (GTK_WIDGET (password_entry), TRUE);
- gtk_entry_set_text (password_entry, "");
- gtk_entry_set_visibility (password_entry, FALSE);
- if (type == LIGHTDM_PROMPT_TYPE_SECRET) // Password
+ PAMConversationMessage *message_obj = g_new (PAMConversationMessage, 1);
+ if (message_obj)
{
- gtk_widget_grab_focus (GTK_WIDGET (password_entry));
- }
- else
- {
- if (gtk_widget_get_visible ((GTK_WIDGET (username_entry))))
- gtk_widget_grab_focus (GTK_WIDGET (username_entry));
- else
- gtk_widget_grab_focus (GTK_WIDGET (password_entry));
+ message_obj->is_prompt = TRUE;
+ message_obj->type.prompt = type;
+ message_obj->text = g_strdup (text);
+ pending_questions = g_slist_append (pending_questions, message_obj);
}
+
+ if (!prompt_active)
+ process_prompts (greeter);
}
static void
show_message_cb (LightDMGreeter *greeter, const gchar *text, LightDMMessageType type)
{
- set_message_label (text);
-}
+ PAMConversationMessage *message_obj = g_new (PAMConversationMessage, 1);
+ if (message_obj)
+ {
+ message_obj->is_prompt = FALSE;
+ message_obj->type.message = type;
+ message_obj->text = g_strdup (text);
+ pending_questions = g_slist_append (pending_questions, message_obj);
+ }
-static const gchar*
-get_message_label (void)
-{
- return gtk_label_get_text (message_label);
+ if (!prompt_active)
+ process_prompts (greeter);
}
static void
authentication_complete_cb (LightDMGreeter *greeter)
{
+ prompt_active = FALSE;
gtk_entry_set_text (password_entry, "");
if (cancelling)
@@ -1177,6 +1297,12 @@ authentication_complete_cb (LightDMGreeter *greeter)
return;
}
+ if (pending_questions)
+ {
+ g_slist_free_full (pending_questions, (GDestroyNotify) pam_message_finalize);
+ pending_questions = NULL;
+ }
+
if (lightdm_greeter_get_is_authenticated (greeter))
{
if (prompted)