Commit d39ed7b7 authored by Ray Strode's avatar Ray Strode

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
parent d5280a38
...@@ -188,6 +188,7 @@ static char gdm_pam_extension_environment_block[_POSIX_ARG_MAX]; ...@@ -188,6 +188,7 @@ static char gdm_pam_extension_environment_block[_POSIX_ARG_MAX];
static const char * const static const char * const
gdm_supported_pam_extensions[] = { gdm_supported_pam_extensions[] = {
GDM_PAM_EXTENSION_CHOICE_LIST,
NULL NULL
}; };
#endif #endif
...@@ -535,6 +536,61 @@ gdm_session_worker_report_problem (GdmSessionWorker *worker, ...@@ -535,6 +536,61 @@ gdm_session_worker_report_problem (GdmSessionWorker *worker,
} }
#ifdef SUPPORTS_PAM_EXTENSIONS #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 static gboolean
gdm_session_worker_process_extended_pam_message (GdmSessionWorker *worker, gdm_session_worker_process_extended_pam_message (GdmSessionWorker *worker,
const struct pam_message *query, const struct pam_message *query,
...@@ -555,8 +611,30 @@ gdm_session_worker_process_extended_pam_message (GdmSessionWorker *work ...@@ -555,8 +611,30 @@ gdm_session_worker_process_extended_pam_message (GdmSessionWorker *work
return FALSE; return FALSE;
} }
g_debug ("GdmSessionWorker: received extended pam message of unknown type %u", (unsigned int) extended_message->type); if (GDM_PAM_EXTENSION_MESSAGE_MATCH (extended_message, worker->priv->extensions, GDM_PAM_EXTENSION_CHOICE_LIST)) {
return FALSE; 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 #endif
......
...@@ -680,6 +680,44 @@ set_pending_query (GdmSessionConversation *conversation, ...@@ -680,6 +680,44 @@ set_pending_query (GdmSessionConversation *conversation,
conversation->pending_invocation = g_object_ref (message); conversation->pending_invocation = g_object_ref (message);
} }
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 static gboolean
gdm_session_handle_info_query (GdmDBusWorkerManager *worker_manager_interface, gdm_session_handle_info_query (GdmDBusWorkerManager *worker_manager_interface,
GDBusMethodInvocation *invocation, GDBusMethodInvocation *invocation,
...@@ -1137,6 +1175,10 @@ export_worker_manager_interface (GdmSession *self, ...@@ -1137,6 +1175,10 @@ export_worker_manager_interface (GdmSession *self,
"handle-problem", "handle-problem",
G_CALLBACK (gdm_session_handle_problem), G_CALLBACK (gdm_session_handle_problem),
self); 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), g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (worker_manager_interface),
connection, connection,
...@@ -1166,6 +1208,9 @@ unexport_worker_manager_interface (GdmSession *self, ...@@ -1166,6 +1208,9 @@ unexport_worker_manager_interface (GdmSession *self,
g_signal_handlers_disconnect_by_func (worker_manager_interface, g_signal_handlers_disconnect_by_func (worker_manager_interface,
G_CALLBACK (gdm_session_handle_problem), G_CALLBACK (gdm_session_handle_problem),
self); self);
g_signal_handlers_disconnect_by_func (worker_manager_interface,
G_CALLBACK (gdm_session_handle_choice_list_query),
self);
} }
static gboolean static gboolean
...@@ -1219,6 +1264,42 @@ begin_verification_conversation (GdmSession *self, ...@@ -1219,6 +1264,42 @@ begin_verification_conversation (GdmSession *self,
return conversation; return conversation;
} }
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 static gboolean
gdm_session_handle_client_enable_extensions (GdmDBusUserVerifier *user_verifier_interface, gdm_session_handle_client_enable_extensions (GdmDBusUserVerifier *user_verifier_interface,
GDBusMethodInvocation *invocation, GDBusMethodInvocation *invocation,
...@@ -1226,9 +1307,20 @@ gdm_session_handle_client_enable_extensions (GdmDBusUserVerifier *user_verifi ...@@ -1226,9 +1307,20 @@ gdm_session_handle_client_enable_extensions (GdmDBusUserVerifier *user_verifi
GDBusConnection *connection) GDBusConnection *connection)
{ {
GdmSession *self = g_object_get_data (G_OBJECT (connection), "gdm-session"); GdmSession *self = g_object_get_data (G_OBJECT (connection), "gdm-session");
size_t i;
g_hash_table_remove_all (self->priv->user_verifier_extensions); 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); gdm_dbus_user_verifier_complete_enable_extensions (user_verifier_interface, invocation);
return TRUE; return TRUE;
......
...@@ -22,6 +22,12 @@ ...@@ -22,6 +22,12 @@
<arg name="service_name" direction="in" type="s"/> <arg name="service_name" direction="in" type="s"/>
<arg name="problem" direction="in" type="s"/> <arg name="problem" direction="in" type="s"/>
</method> </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>
<interface name="org.gnome.DisplayManager.UserVerifier"> <interface name="org.gnome.DisplayManager.UserVerifier">
<method name="EnableExtensions"> <method name="EnableExtensions">
...@@ -78,6 +84,17 @@ ...@@ -78,6 +84,17 @@
<arg name="service_name" type="s"/> <arg name="service_name" type="s"/>
</signal> </signal>
</interface> </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"> <interface name="org.gnome.DisplayManager.Greeter">
<method name="SelectSession"> <method name="SelectSession">
<arg name="session" direction="in" type="s"/> <arg name="session" direction="in" type="s"/>
......
...@@ -130,4 +130,49 @@ typedef struct { ...@@ -130,4 +130,49 @@ typedef struct {
#define GDM_PAM_EXTENSION_SUPPORTED(name) GDM_PAM_EXTENSION_LOOK_UP_TYPE(name, (unsigned char *) NULL) #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 #endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment