[gdm] daemon: add ChoiceList PAM extension



commit d39ed7b722761e5110e26c8e937782ecb24b7315
Author: Ray Strode <rstrode redhat com>
Date:   Wed Jul 26 13:28:50 2017 -0400

    daemon: add ChoiceList PAM extension
    
    This commit adds one PAM extension, a "Choice List" using the
    new PAM_BINARY_PROMPT protocol added in the previous commit.  The
    PAM module sends a list of (key, row text) pairs, and GDM ferries
    the request to gnome-shell using a new user verifier sub-interface.
    
    gnome-shell should present the list to the user and pass back the
    corresponding key, which GDM ferries back to the PAM module.
    
    Note this commit is only the daemon side. A subsequent commit will
    add the libgdm API needed for gnome-shell to actually deal with
    this new PAM extension.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=788851

 daemon/gdm-session-worker.c         |   82 ++++++++++++++++++++++++++++++-
 daemon/gdm-session.c                |   92 +++++++++++++++++++++++++++++++++++
 daemon/gdm-session.xml              |   17 ++++++
 pam-extensions/gdm-pam-extensions.h |   45 +++++++++++++++++
 4 files changed, 234 insertions(+), 2 deletions(-)
---
diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c
index aafc888..a1e4636 100644
--- a/daemon/gdm-session-worker.c
+++ b/daemon/gdm-session-worker.c
@@ -188,6 +188,7 @@ static char gdm_pam_extension_environment_block[_POSIX_ARG_MAX];
 
 static const char * const
 gdm_supported_pam_extensions[] = {
+        GDM_PAM_EXTENSION_CHOICE_LIST,
         NULL
 };
 #endif
@@ -536,6 +537,61 @@ gdm_session_worker_report_problem (GdmSessionWorker *worker,
 
 #ifdef SUPPORTS_PAM_EXTENSIONS
 static gboolean
+gdm_session_worker_ask_list_of_choices (GdmSessionWorker *worker,
+                                        const char       *prompt_message,
+                                        GdmChoiceList    *list,
+                                        char            **answerp)
+{
+        GVariantBuilder builder;
+        GVariant *choices_as_variant;
+        GError *error = NULL;
+        gboolean res;
+        size_t i;
+
+        g_debug ("GdmSessionWorker: presenting user with list of choices:");
+
+        g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}"));
+
+        for (i = 0; i < list->number_of_items; i++) {
+                if (list->items[i].key == NULL) {
+                        g_warning ("choice list contains item with NULL key");
+                        g_variant_builder_clear (&builder);
+                        return FALSE;
+                }
+                g_debug ("GdmSessionWorker:        choices['%s'] = \"%s\"", list->items[i].key, 
list->items[i].text);
+                g_variant_builder_add (&builder, "{ss}", list->items[i].key, list->items[i].text);
+        }
+        g_debug ("GdmSessionWorker: (and waiting for reply)");
+
+        choices_as_variant = g_variant_builder_end (&builder);
+
+        res = gdm_dbus_worker_manager_call_choice_list_query_sync (worker->priv->manager,
+                                                                    worker->priv->service,
+                                                                    prompt_message,
+                                                                    choices_as_variant,
+                                                                    answerp,
+                                                                    NULL,
+                                                                    &error);
+
+        if (! res) {
+                g_debug ("GdmSessionWorker: list request failed: %s", error->message);
+                g_clear_error (&error);
+        } else {
+                g_debug ("GdmSessionWorker: user selected '%s'", *answerp);
+        }
+
+        return res;
+}
+
+static gboolean
+gdm_session_worker_process_choice_list_request (GdmSessionWorker                   *worker,
+                                                GdmPamExtensionChoiceListRequest  *request,
+                                                GdmPamExtensionChoiceListResponse *response)
+{
+        return gdm_session_worker_ask_list_of_choices (worker, request->prompt_message, &request->list, 
&response->key);
+}
+
+static gboolean
 gdm_session_worker_process_extended_pam_message (GdmSessionWorker          *worker,
                                                  const struct pam_message  *query,
                                                  char                     **response)
@@ -555,8 +611,30 @@ gdm_session_worker_process_extended_pam_message (GdmSessionWorker          *work
                 return FALSE;
         }
 
-        g_debug ("GdmSessionWorker: received extended pam message of unknown type %u", (unsigned int) 
extended_message->type);
-        return FALSE;
+        if (GDM_PAM_EXTENSION_MESSAGE_MATCH (extended_message, worker->priv->extensions, 
GDM_PAM_EXTENSION_CHOICE_LIST)) {
+                GdmPamExtensionChoiceListRequest *list_request = (GdmPamExtensionChoiceListRequest *) 
extended_message;
+                GdmPamExtensionChoiceListResponse *list_response = malloc 
(GDM_PAM_EXTENSION_CHOICE_LIST_RESPONSE_SIZE);
+
+                g_debug ("GdmSessionWorker: received extended pam message '%s'", 
GDM_PAM_EXTENSION_CHOICE_LIST);
+
+                GDM_PAM_EXTENSION_CHOICE_LIST_RESPONSE_INIT (list_response);
+
+                res = gdm_session_worker_process_choice_list_request (worker, list_request, list_response);
+
+                if (! res) {
+                        g_free (list_response);
+                        return FALSE;
+                }
+
+                *response = GDM_PAM_EXTENSION_MESSAGE_TO_PAM_REPLY (list_response);
+                return TRUE;
+        } else {
+                g_debug ("GdmSessionWorker: received extended pam message of unknown type %u", (unsigned 
int) extended_message->type);
+                return FALSE;
+
+        }
+
+        return TRUE;
 }
 #endif
 
diff --git a/daemon/gdm-session.c b/daemon/gdm-session.c
index aa437e5..19d26c9 100644
--- a/daemon/gdm-session.c
+++ b/daemon/gdm-session.c
@@ -681,6 +681,44 @@ set_pending_query (GdmSessionConversation *conversation,
 }
 
 static gboolean
+gdm_session_handle_choice_list_query (GdmDBusWorkerManager  *worker_manager_interface,
+                                      GDBusMethodInvocation *invocation,
+                                      const char            *service_name,
+                                      const char            *prompt_message,
+                                      GVariant              *query,
+                                      GdmSession            *self)
+{
+        GdmSessionConversation *conversation;
+        GdmDBusUserVerifierChoiceList *choice_list_interface = NULL;
+
+        g_debug ("GdmSession: choice query for service '%s'", service_name);
+
+        if (self->priv->user_verifier_extensions != NULL)
+                choice_list_interface = g_hash_table_lookup (self->priv->user_verifier_extensions,
+                                                             
gdm_dbus_user_verifier_choice_list_interface_info ()->name);
+
+        if (choice_list_interface == NULL) {
+                g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+                                                       G_DBUS_ERROR_NOT_SUPPORTED,
+                                                       "ChoiceList interface not supported by client");
+                return TRUE;
+        }
+
+        conversation = find_conversation_by_name (self, service_name);
+        if (conversation != NULL) {
+                set_pending_query (conversation, invocation);
+
+                g_debug ("GdmSession: emitting choice query '%s'", prompt_message);
+                gdm_dbus_user_verifier_choice_list_emit_choice_query (choice_list_interface,
+                                                                      service_name,
+                                                                      prompt_message,
+                                                                      query);
+        }
+
+        return TRUE;
+}
+
+static gboolean
 gdm_session_handle_info_query (GdmDBusWorkerManager  *worker_manager_interface,
                                GDBusMethodInvocation *invocation,
                                const char            *service_name,
@@ -1137,6 +1175,10 @@ export_worker_manager_interface (GdmSession      *self,
                           "handle-problem",
                           G_CALLBACK (gdm_session_handle_problem),
                           self);
+        g_signal_connect (worker_manager_interface,
+                          "handle-choice-list-query",
+                          G_CALLBACK (gdm_session_handle_choice_list_query),
+                          self);
 
         g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (worker_manager_interface),
                                           connection,
@@ -1166,6 +1208,9 @@ unexport_worker_manager_interface (GdmSession           *self,
         g_signal_handlers_disconnect_by_func (worker_manager_interface,
                                               G_CALLBACK (gdm_session_handle_problem),
                                               self);
+        g_signal_handlers_disconnect_by_func (worker_manager_interface,
+                                              G_CALLBACK (gdm_session_handle_choice_list_query),
+                                              self);
 }
 
 static gboolean
@@ -1220,15 +1265,62 @@ begin_verification_conversation (GdmSession            *self,
 }
 
 static gboolean
+gdm_session_handle_client_select_choice (GdmDBusUserVerifierChoiceList    *choice_list_interface,
+                                         GDBusMethodInvocation            *invocation,
+                                         const char                       *service_name,
+                                         const char                       *answer,
+                                         GdmSession                       *self)
+{
+        g_debug ("GdmSession: user selected choice '%s'", answer);
+        gdm_dbus_user_verifier_choice_list_complete_select_choice (choice_list_interface, invocation);
+        gdm_session_answer_query (self, service_name, answer);
+        return TRUE;
+}
+
+static void
+export_user_verifier_choice_list_interface (GdmSession      *self,
+                                            GDBusConnection *connection)
+{
+        GdmDBusUserVerifierChoiceList   *interface;
+
+        interface = GDM_DBUS_USER_VERIFIER_CHOICE_LIST (gdm_dbus_user_verifier_choice_list_skeleton_new ());
+
+        g_signal_connect (interface,
+                          "handle-select-choice",
+                          G_CALLBACK (gdm_session_handle_client_select_choice),
+                          self);
+
+        g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (interface),
+                                          connection,
+                                          GDM_SESSION_DBUS_OBJECT_PATH,
+                                          NULL);
+
+        g_hash_table_insert (self->priv->user_verifier_extensions,
+                             gdm_dbus_user_verifier_choice_list_interface_info ()->name,
+                             interface);
+}
+
+static gboolean
 gdm_session_handle_client_enable_extensions (GdmDBusUserVerifier    *user_verifier_interface,
                                              GDBusMethodInvocation  *invocation,
                                              const char * const *    extensions,
                                              GDBusConnection        *connection)
 {
         GdmSession *self = g_object_get_data (G_OBJECT (connection), "gdm-session");
+        size_t i;
 
         g_hash_table_remove_all (self->priv->user_verifier_extensions);
 
+        for (i = 0; extensions[i] != NULL; i++) {
+                if (g_hash_table_lookup (self->priv->user_verifier_extensions, extensions[i]) != NULL)
+                        continue;
+
+                if (strcmp (extensions[i],
+                            gdm_dbus_user_verifier_choice_list_interface_info ()->name) == 0)
+                        export_user_verifier_choice_list_interface (self, connection);
+
+        }
+
         gdm_dbus_user_verifier_complete_enable_extensions (user_verifier_interface, invocation);
 
         return TRUE;
diff --git a/daemon/gdm-session.xml b/daemon/gdm-session.xml
index af2976a..137be5e 100644
--- a/daemon/gdm-session.xml
+++ b/daemon/gdm-session.xml
@@ -22,6 +22,12 @@
       <arg name="service_name" direction="in" type="s"/>
       <arg name="problem" direction="in" type="s"/>
     </method>
+    <method name="ChoiceListQuery">
+      <arg name="service_name" direction="in" type="s"/>
+      <arg name="prompt_message" direction="in" type="s"/>
+      <arg name="query" direction="in" type="a{ss}"/>
+      <arg name="answer" direction="out" type="s"/>
+    </method>
   </interface>
   <interface name="org.gnome.DisplayManager.UserVerifier">
     <method name="EnableExtensions">
@@ -78,6 +84,17 @@
       <arg name="service_name" type="s"/>
     </signal>
   </interface>
+  <interface name="org.gnome.DisplayManager.UserVerifier.ChoiceList">
+    <method name="SelectChoice">
+      <arg name="service_name" direction="in" type="s"/>
+      <arg name="choice" direction="in" type="s"/>
+    </method>
+    <signal name="ChoiceQuery">
+      <arg name="service_name" type="s"/>
+      <arg name="prompt_message" type="s"/>
+      <arg name="list" type="a{ss}"/>
+    </signal>
+  </interface>
   <interface name="org.gnome.DisplayManager.Greeter">
     <method name="SelectSession">
       <arg name="session" direction="in" type="s"/>
diff --git a/pam-extensions/gdm-pam-extensions.h b/pam-extensions/gdm-pam-extensions.h
index eff2f40..cecb742 100644
--- a/pam-extensions/gdm-pam-extensions.h
+++ b/pam-extensions/gdm-pam-extensions.h
@@ -130,4 +130,49 @@ typedef struct {
 
 #define GDM_PAM_EXTENSION_SUPPORTED(name) GDM_PAM_EXTENSION_LOOK_UP_TYPE(name, (unsigned char *) NULL)
 
+typedef struct {
+        const char *key;
+        const char *text;
+} GdmChoiceListItems;
+
+typedef struct {
+        size_t number_of_items;
+        GdmChoiceListItems items[];
+} GdmChoiceList;
+
+typedef struct {
+        GdmPamExtensionMessage header;
+
+        char *prompt_message;
+        GdmChoiceList list;
+} GdmPamExtensionChoiceListRequest;
+
+typedef struct {
+        GdmPamExtensionMessage header;
+
+        char *key;
+} GdmPamExtensionChoiceListResponse;
+
+#define GDM_PAM_EXTENSION_CHOICE_LIST "org.gnome.DisplayManager.UserVerifier.ChoiceList"
+
+#define GDM_CHOICE_LIST_SIZE(num_items) (offsetof(GdmChoiceList, items) + (num_items) * sizeof 
(GdmChoiceListItems))
+#define GDM_PAM_EXTENSION_CHOICE_LIST_REQUEST_SIZE(num_items) (offsetof(GdmPamExtensionChoiceListRequest, 
list) + GDM_CHOICE_LIST_SIZE((num_items)))
+#define GDM_PAM_EXTENSION_CHOICE_LIST_REQUEST_INIT(request, title, num_items) \
+{ \
+        int _n = num_items; \
+        GDM_PAM_EXTENSION_LOOK_UP_TYPE (GDM_PAM_EXTENSION_CHOICE_LIST, &request->header.type); \
+        request->header.length = htobe32 (GDM_PAM_EXTENSION_CHOICE_LIST_REQUEST_SIZE(_n)); \
+        request->prompt_message = title; \
+        request->list.number_of_items = _n; \
+}
+
+#define GDM_PAM_EXTENSION_CHOICE_LIST_RESPONSE_SIZE sizeof (GdmPamExtensionChoiceListResponse)
+#define GDM_PAM_EXTENSION_CHOICE_LIST_RESPONSE_INIT(response) \
+{ \
+        GDM_PAM_EXTENSION_LOOK_UP_TYPE (GDM_PAM_EXTENSION_CHOICE_LIST, &response->header.type); \
+        response->header.length = htobe32 (GDM_PAM_EXTENSION_CHOICE_LIST_RESPONSE_SIZE); \
+        response->key = NULL; \
+}
+#define GDM_PAM_EXTENSION_REPLY_TO_CHOICE_LIST_RESPONSE(reply) ((GdmPamExtensionChoiceListResponse *) (void 
*) reply->resp)
+
 #endif


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]