summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRay Strode <rstrode@redhat.com>2017-07-14 16:05:46 -0400
committerRay Strode <rstrode@redhat.com>2017-07-25 07:56:25 -0400
commit9662a643d4c0773619d3e5d77efb7fd5c09702d9 (patch)
treeb42f714142bae21a3d69f1a85529cbd1bf41e1cb
parent2a02df587ca08f596a8756078f12de04a4f9c286 (diff)
downloadgdm-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.c60
-rw-r--r--daemon/gdm-session.c83
-rw-r--r--daemon/gdm-session.xml15
-rw-r--r--pam-extensions/gdm-pam-extensions.h43
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