diff options
author | Ray Strode <rstrode@redhat.com> | 2017-07-26 13:28:50 -0400 |
---|---|---|
committer | Ray Strode <rstrode@redhat.com> | 2017-10-20 14:14:17 -0400 |
commit | d39ed7b722761e5110e26c8e937782ecb24b7315 (patch) | |
tree | 3fe23217bbe7f8af8c83535bb8866e0397ef52c9 /daemon | |
parent | d5280a38761a558c32c32e1e277ebd26f63af5c7 (diff) | |
download | gdm-d39ed7b722761e5110e26c8e937782ecb24b7315.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.
https://bugzilla.gnome.org/show_bug.cgi?id=788851
Diffstat (limited to 'daemon')
-rw-r--r-- | daemon/gdm-session-worker.c | 82 | ||||
-rw-r--r-- | daemon/gdm-session.c | 92 | ||||
-rw-r--r-- | daemon/gdm-session.xml | 17 |
3 files changed, 189 insertions, 2 deletions
diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c index aafc8880..a1e46366 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 aa437e56..19d26c92 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 af2976a4..137be5e2 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"/> |