diff options
author | Ray Strode <rstrode@redhat.com> | 2017-07-14 16:05:46 -0400 |
---|---|---|
committer | Ray Strode <rstrode@redhat.com> | 2017-07-25 07:56:25 -0400 |
commit | 9662a643d4c0773619d3e5d77efb7fd5c09702d9 (patch) | |
tree | b42f714142bae21a3d69f1a85529cbd1bf41e1cb | |
parent | 2a02df587ca08f596a8756078f12de04a4f9c286 (diff) | |
download | gdm-9662a643d4c0773619d3e5d77efb7fd5c09702d9.tar.gz |
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.
-rw-r--r-- | daemon/gdm-session-worker.c | 60 | ||||
-rw-r--r-- | daemon/gdm-session.c | 83 | ||||
-rw-r--r-- | daemon/gdm-session.xml | 15 | ||||
-rw-r--r-- | pam-extensions/gdm-pam-extensions.h | 43 |
4 files changed, 200 insertions, 1 deletions
diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c index 3235c397..1284cc86 100644 --- a/daemon/gdm-session-worker.c +++ b/daemon/gdm-session-worker.c @@ -186,6 +186,7 @@ struct GdmSessionWorkerPrivate #ifdef SUPPORTS_PAM_EXTENSIONS static const char * const gdm_supported_pam_extensions[] = { + GDM_PAM_EXTENSION_CHOICE_LIST, NULL }; #endif @@ -534,11 +535,50 @@ gdm_session_worker_report_problem (GdmSessionWorker *worker, #ifdef SUPPORTS_PAM_EXTENSIONS static gboolean +gdm_session_worker_ask_list_of_choices (GdmSessionWorker *worker, + GdmChoiceList *list, + char **answerp) +{ + GVariantBuilder builder; + GVariant *choices_as_variant; + size_t i; + + 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_variant_builder_add (&builder, "{ss}", list->items[i].key, list->items[i].text); + } + + choices_as_variant = g_variant_builder_end (&builder); + + return gdm_dbus_worker_manager_call_choice_list_query_sync (worker->priv->manager, + worker->priv->service, + choices_as_variant, + answerp, + NULL, + NULL); +} + +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->list, &response->key); +} + +static gboolean gdm_session_worker_process_extended_pam_message (GdmSessionWorker *worker, const struct pam_message *query, char **response) { GdmPamExtensionMessage *extended_message; + gboolean res; extended_message = GDM_PAM_EXTENSION_MESSAGE_FROM_PAM_MESSAGE (query); @@ -552,7 +592,24 @@ gdm_session_worker_process_extended_pam_message (GdmSessionWorker *work return FALSE; } - 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); + + 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; + } + + return TRUE; } #endif @@ -3409,3 +3466,4 @@ gdm_session_worker_new (const char *address, return GDM_SESSION_WORKER (object); } + diff --git a/daemon/gdm-session.c b/daemon/gdm-session.c index 98cb2e70..c23429f8 100644 --- a/daemon/gdm-session.c +++ b/daemon/gdm-session.c @@ -688,6 +688,39 @@ set_pending_query (GdmSessionConversation *conversation, } static gboolean +gdm_session_handle_choice_list_query (GdmDBusWorkerManager *worker_manager_interface, + GDBusMethodInvocation *invocation, + const char *service_name, + GVariant *query, + GdmSession *self) +{ + GdmSessionConversation *conversation; + GdmDBusUserVerifierChoiceList *choice_list_interface = NULL; + + 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); + + gdm_dbus_user_verifier_choice_list_emit_choice_query (choice_list_interface, + service_name, + query); + } + + return TRUE; +} + +static gboolean gdm_session_handle_info_query (GdmDBusWorkerManager *worker_manager_interface, GDBusMethodInvocation *invocation, const char *service_name, @@ -1145,6 +1178,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, @@ -1174,6 +1211,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 @@ -1237,12 +1277,48 @@ unexport_and_free_user_verifier_extension (GDBusInterfaceSkeleton *interface) } static gboolean +gdm_session_handle_client_select_choice (GdmDBusUserVerifierChoiceList *choice_list_interface, + GDBusMethodInvocation *invocation, + const char *service_name, + const char *answer, + GdmSession *self) +{ + 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; if (self->priv->user_verifier_extensions != NULL) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, @@ -1257,6 +1333,13 @@ gdm_session_handle_client_enable_extensions (GdmDBusUserVerifier *user_verifi (GDestroyNotify) unexport_and_free_user_verifier_extension); + for (i = 0; extensions[i] != NULL; i++) { + 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 af2976a4..3140054d 100644 --- a/daemon/gdm-session.xml +++ b/daemon/gdm-session.xml @@ -22,6 +22,11 @@ <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="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 +83,16 @@ <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="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 817b380c..9fb580f5 100644 --- a/pam-extensions/gdm-pam-extensions.h +++ b/pam-extensions/gdm-pam-extensions.h @@ -121,4 +121,47 @@ 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; + + 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,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->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 |