diff options
author | Daiki Ueno <dueno@src.gnome.org> | 2019-09-08 17:52:35 +0200 |
---|---|---|
committer | Daiki Ueno <dueno@src.gnome.org> | 2019-09-09 07:16:24 +0200 |
commit | 03873e4721bf529f3eb952ab4d5a3bc81477d2c2 (patch) | |
tree | 7996986c5568cbbbdde1c66d8d9412343fc36b2a | |
parent | 05635f67bf02b91b165c43e56f9b6cd2ddaf862c (diff) | |
download | gcr-wip/dueno/prompter.tar.gz |
system-prompter: Add DBus interface sending secret through FD [ci skip]wip/dueno/prompter
-rw-r--r-- | gcr/Makefile.am | 3 | ||||
-rw-r--r-- | gcr/gcr-dbus-constants.h | 2 | ||||
-rw-r--r-- | gcr/gcr-system-prompt.c | 101 | ||||
-rw-r--r-- | gcr/gcr-system-prompter.c | 144 | ||||
-rw-r--r-- | gcr/org.gnome.keyring.Prompter2.xml | 84 |
5 files changed, 267 insertions, 67 deletions
diff --git a/gcr/Makefile.am b/gcr/Makefile.am index 3ad96f4..4913dca 100644 --- a/gcr/Makefile.am +++ b/gcr/Makefile.am @@ -155,7 +155,8 @@ gcr/gcr-oids.c: gcr/gcr-oids.list gcr/gcr-mkoids gcr/gcr-oids.h: gcr/gcr-oids.c DBUS_XML_DEFINITIONS = \ - gcr/org.gnome.keyring.Prompter.xml + gcr/org.gnome.keyring.Prompter.xml \ + gcr/org.gnome.keyring.Prompter2.xml gcr/gcr-dbus-generated.c: $(DBUS_XML_DEFINITIONS) $(AM_V_GEN) $(GDBUS_CODEGEN) --interface-prefix org.gnome.keyring.internal. \ diff --git a/gcr/gcr-dbus-constants.h b/gcr/gcr-dbus-constants.h index e4303d9..193542c 100644 --- a/gcr/gcr-dbus-constants.h +++ b/gcr/gcr-dbus-constants.h @@ -34,12 +34,14 @@ G_BEGIN_DECLS #define GCR_DBUS_PROMPT_OBJECT_PREFIX "/org/gnome/keyring/Prompt" #define GCR_DBUS_PROMPTER_INTERFACE "org.gnome.keyring.internal.Prompter" +#define GCR_DBUS_PROMPTER2_INTERFACE "org.gnome.keyring.internal.Prompter2" #define GCR_DBUS_PROMPTER_METHOD_BEGIN "BeginPrompting" #define GCR_DBUS_PROMPTER_METHOD_STOP "StopPrompting" #define GCR_DBUS_PROMPTER_METHOD_PERFORM "PerformPrompt" #define GCR_DBUS_CALLBACK_INTERFACE "org.gnome.keyring.internal.Prompter.Callback" +#define GCR_DBUS_CALLBACK2_INTERFACE "org.gnome.keyring.internal.Prompter.Callback2" #define GCR_DBUS_PROMPT_ERROR_IN_PROGRESS "org.gnome.keyring.Prompter.InProgress" #define GCR_DBUS_PROMPT_ERROR_FAILED "org.gnome.keyring.Prompter.Failed" diff --git a/gcr/gcr-system-prompt.c b/gcr/gcr-system-prompt.c index 08a4a4a..7d13881 100644 --- a/gcr/gcr-system-prompt.c +++ b/gcr/gcr-system-prompt.c @@ -120,6 +120,7 @@ struct _GcrSystemPromptPrivate { GSimpleAsyncResult *pending; gchar *last_response; + gint fd; }; static void gcr_system_prompt_prompt_iface (GcrPromptIface *iface); @@ -485,11 +486,15 @@ perform_close (GcrSystemPrompt *self, if (self->pv->begun_prompting) { if (self->pv->connection && self->pv->prompt_path && self->pv->prompt_owner) { - g_debug ("Calling the prompter %s method", GCR_DBUS_PROMPTER_METHOD_STOP); + const gchar *interface_name = + self->pv->exchange ? GCR_DBUS_PROMPTER_INTERFACE : GCR_DBUS_PROMPTER2_INTERFACE; + + g_debug ("Calling the prompter %s.%s method", + interface_name, GCR_DBUS_PROMPTER_METHOD_STOP); g_dbus_connection_call (self->pv->connection, self->pv->prompter_bus_name, GCR_DBUS_PROMPTER_OBJECT_PATH, - GCR_DBUS_PROMPTER_INTERFACE, + interface_name, GCR_DBUS_PROMPTER_METHOD_STOP, g_variant_new ("(o)", self->pv->prompt_path), G_VARIANT_TYPE ("()"), @@ -647,7 +652,6 @@ prompt_method_ready (GcrSystemPrompt *self, GDBusMethodInvocation *invocation, GVariant *parameters) { - GcrSecretExchange *exchange; GSimpleAsyncResult *res; GVariantIter *iter; gchar *received; @@ -664,12 +668,15 @@ prompt_method_ready (GcrSystemPrompt *self, update_properties_from_iter (self, iter); g_variant_iter_free (iter); - exchange = gcr_system_prompt_get_secret_exchange (self); - if (gcr_secret_exchange_receive (exchange, received)) - self->pv->received = TRUE; - else - g_warning ("received invalid secret exchange string"); - g_free (received); + if (self->pv->exchange == NULL) { + + } else { + if (gcr_secret_exchange_receive (exchange, received)) + self->pv->received = TRUE; + else + g_warning ("received invalid secret exchange string"); + g_free (received); + } res = g_object_ref (self->pv->pending); g_clear_object (&self->pv->pending); @@ -755,6 +762,7 @@ register_prompt_object (GcrSystemPrompt *self, GError **error) { GError *lerror = NULL; + GDBusInterfaceInfo *interface_info; guint id; /* @@ -766,9 +774,13 @@ register_prompt_object (GcrSystemPrompt *self, g_dbus_connection_unregister_object (self->pv->connection, self->pv->prompt_registered); + interface_info = self->pv->exchange ? + _gcr_dbus_prompter_callback_interface_info () : + _gcr_dbus_prompter_callback2_interface_info (); + id = g_dbus_connection_register_object (self->pv->connection, self->pv->prompt_path, - _gcr_dbus_prompter_callback_interface_info (), + interface_info, &prompt_dbus_vtable, self, NULL, &lerror); self->pv->prompt_registered = id; @@ -1168,10 +1180,8 @@ perform_prompt_async (GcrSystemPrompt *self, gpointer user_data) { GSimpleAsyncResult *res; - GcrSecretExchange *exchange; GVariantBuilder *builder; CallClosure *closure; - gchar *sent; g_return_if_fail (GCR_IS_SYSTEM_PROMPT (self)); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); @@ -1195,12 +1205,6 @@ perform_prompt_async (GcrSystemPrompt *self, g_debug ("prompting for password"); - exchange = gcr_system_prompt_get_secret_exchange (self); - if (self->pv->received) - sent = gcr_secret_exchange_send (exchange, NULL, 0); - else - sent = gcr_secret_exchange_begin (exchange); - closure->watch_id = g_bus_watch_name_on_connection (self->pv->connection, self->pv->prompter_bus_name, G_BUS_NAME_WATCHER_FLAGS_NONE, @@ -1213,22 +1217,57 @@ perform_prompt_async (GcrSystemPrompt *self, /* Reregister the prompt object in the current GMainContext */ register_prompt_object (self, NULL); - g_dbus_connection_call (self->pv->connection, - self->pv->prompter_bus_name, - GCR_DBUS_PROMPTER_OBJECT_PATH, - GCR_DBUS_PROMPTER_INTERFACE, - GCR_DBUS_PROMPTER_METHOD_PERFORM, - g_variant_new ("(osa{sv}s)", self->pv->prompt_path, - type, builder, sent), - G_VARIANT_TYPE ("()"), - G_DBUS_CALL_FLAGS_NO_AUTO_START, - -1, cancellable, - on_perform_prompt_complete, - g_object_ref (res)); + if (self->pv->exchange == NULL) { + gint fds[2]; + GError *error = NULL; + + if (!g_unix_open_pipe (fds, 0, &error)) { + g_simple_async_result_set_from_error (res, error); + g_simple_async_result_complete (res); + g_object_unref (res); + return; + } + + self->pv->fd = fds[0]; + + g_dbus_connection_call (self->pv->connection, + self->pv->prompter_bus_name, + GCR_DBUS_PROMPTER_OBJECT_PATH, + GCR_DBUS_PROMPTER2_INTERFACE, + GCR_DBUS_PROMPTER_METHOD_PERFORM, + g_variant_new ("(osa{sv}h)", self->pv->prompt_path, + type, builder, fds[1]), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + -1, cancellable, + on_perform_prompt_complete, + g_object_ref (res)); + } else { + gchar *sent; + + if (self->pv->received) + sent = gcr_secret_exchange_send (self->pv->exchange, NULL, 0); + else + sent = gcr_secret_exchange_begin (self->pv->exchange); + + g_dbus_connection_call (self->pv->connection, + self->pv->prompter_bus_name, + GCR_DBUS_PROMPTER_OBJECT_PATH, + GCR_DBUS_PROMPTER_INTERFACE, + GCR_DBUS_PROMPTER_METHOD_PERFORM, + g_variant_new ("(osa{sv}s)", self->pv->prompt_path, + type, builder, sent), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + -1, cancellable, + on_perform_prompt_complete, + g_object_ref (res)); + g_free (sent); + } + g_variant_builder_unref(builder); self->pv->pending = res; - g_free (sent); } static GcrPromptReply diff --git a/gcr/gcr-system-prompter.c b/gcr/gcr-system-prompter.c index 10948cf..be78dad 100644 --- a/gcr/gcr-system-prompter.c +++ b/gcr/gcr-system-prompter.c @@ -35,6 +35,8 @@ #include "egg/egg-error.h" +#include <gio/gunixfdlist.h> +#include <gio/gunixoutputstream.h> #include <string.h> /** @@ -91,6 +93,7 @@ struct _GcrSystemPrompterPrivate { GType prompt_type; guint prompter_registered; + guint prompter2_registered; GDBusConnection *connection; GHashTable *callbacks; /* Callback -> guint watch_id */ @@ -105,6 +108,7 @@ G_DEFINE_TYPE_WITH_PRIVATE (GcrSystemPrompter, gcr_system_prompter, G_TYPE_OBJEC typedef struct { const gchar *path; const gchar *name; + const gchar *interface_name; } Callback; typedef struct { @@ -120,6 +124,7 @@ typedef struct { gboolean received; gboolean closed; guint close_sig; + gint fd; } ActivePrompt; static void prompt_send_ready (ActivePrompt *active, @@ -165,8 +170,10 @@ callback_dup (Callback *original) g_assert (original != NULL); g_assert (original->path != NULL); g_assert (original->name != NULL); + g_assert (original->interface_name != NULL); callback->path = g_strdup (original->path); callback->name = g_strdup (original->name); + callback->interface_name = g_strdup (original->interface_name); return callback; } @@ -176,6 +183,7 @@ callback_free (gpointer data) Callback *callback = data; g_free ((gchar *)callback->path); g_free ((gchar *)callback->name); + g_free ((gchar *)callback->interface_name); g_slice_free (Callback, callback); } @@ -209,6 +217,8 @@ active_prompt_unref (gpointer data) g_signal_handler_disconnect (active->prompt, active->notify_sig); if (g_signal_handler_is_connected (active->prompt, active->close_sig)) g_signal_handler_disconnect (active->prompt, active->close_sig); + if (active->fd >= 0) + close (active->fd); g_object_unref (active->prompt); g_hash_table_destroy (active->changed); if (active->exchange) @@ -242,6 +252,7 @@ active_prompt_create (GcrSystemPrompter *self, active->notify_sig = g_signal_connect (active->prompt, "notify", G_CALLBACK (on_prompt_notify), active); active->close_sig = g_signal_connect (active->prompt, "prompt-close", G_CALLBACK (on_prompt_close), active); active->changed = g_hash_table_new (g_direct_hash, g_direct_equal); + active->fd = -1; /* Insert us into the active hash table */ g_hash_table_replace (self->pv->active, active->callback, active); @@ -629,39 +640,65 @@ prompt_send_ready (ActivePrompt *active, { GcrSystemPrompter *self; GVariantBuilder *builder; - GcrSecretExchange *exchange; - gchar *sent; g_assert (active->ready == FALSE); - exchange = active_prompt_get_secret_exchange (active); - if (!active->received) { - g_return_if_fail (secret == NULL); - sent = gcr_secret_exchange_begin (exchange); - } else { - sent = gcr_secret_exchange_send (exchange, secret, -1); - } - self = active->prompter; builder = prompt_build_properties (active->prompt, active->changed); - g_debug ("calling the %s method on %s@%s", - GCR_DBUS_CALLBACK_METHOD_READY, active->callback->path, active->callback->name); + g_debug ("calling the %s.%s method on %s@%s", + active->callback->interface_name, GCR_DBUS_CALLBACK_METHOD_READY, + active->callback->path, active->callback->name); + + if (g_str_equal (active->callback->interface_name, GCR_DBUS_PROMPTER2_INTERFACE)) { + GOutputStream *stream; + + stream = g_unix_output_stream_new (active->fd, TRUE); + if (!g_output_stream_write_all (stream, secret, strlen (secret), NULL, NULL, &error)) { + g_object_unref (stream); + g_dbus_method_invocation_take_error (invocation, error); + } - g_dbus_connection_call (self->pv->connection, - active->callback->name, - active->callback->path, - GCR_DBUS_CALLBACK_INTERFACE, - GCR_DBUS_CALLBACK_METHOD_READY, - g_variant_new ("(sa{sv}s)", response, builder, sent), - G_VARIANT_TYPE ("()"), - G_DBUS_CALL_FLAGS_NO_AUTO_START, - -1, active->cancellable, - on_prompt_ready_complete, - active_prompt_ref (active)); + g_dbus_connection_call (self->pv->connection, + active->callback->name, + active->callback->path, + GCR_DBUS_CALLBACK2_INTERFACE, + GCR_DBUS_CALLBACK_METHOD_READY, + g_variant_new ("(sa{sv})", response, builder), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + -1, active->cancellable, + on_prompt_ready_complete, + active_prompt_ref (active)); + + } else { + GcrSecretExchange *exchange; + gchar *sent; + + exchange = active_prompt_get_secret_exchange (active); + if (!active->received) { + g_return_if_fail (secret == NULL); + sent = gcr_secret_exchange_begin (exchange); + } else { + sent = gcr_secret_exchange_send (exchange, secret, -1); + } + + g_dbus_connection_call (self->pv->connection, + active->callback->name, + active->callback->path, + GCR_DBUS_CALLBACK_INTERFACE, + GCR_DBUS_CALLBACK_METHOD_READY, + g_variant_new ("(sa{sv}s)", response, builder, sent), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + -1, active->cancellable, + on_prompt_ready_complete, + active_prompt_ref (active)); + + g_free (sent); + } g_variant_builder_unref (builder); - g_free (sent); } static void @@ -768,6 +805,7 @@ prompter_method_begin_prompting (GcrSystemPrompter *self, guint watch_id; lookup.name = caller = g_dbus_method_invocation_get_sender (invocation); + lookup.interface_name = g_dbus_method_invocation_get_interface_name (invocation); g_variant_get (parameters, "(&o)", &lookup.path); g_debug ("received %s call from callback %s@%s", @@ -881,13 +919,31 @@ prompter_method_perform_prompt (GcrSystemPrompter *self, const gchar *type; GVariantIter *iter; const gchar *received; + gint fd; lookup.name = g_dbus_method_invocation_get_sender (invocation); - g_variant_get (parameters, "(&o&sa{sv}&s)", - &lookup.path, &type, &iter, &received); + lookup.interface_name = g_dbus_method_invocation_get_interface_name (invocation); + if (g_strcmp0 (lookup.interface_name, GCR_DBUS_PROMPTER2_INTERFACE)) { + GDBusMessage *message; + GUnixFDList *fd_list; + gint idx; - g_debug ("received %s call from callback %s@%s", - GCR_DBUS_PROMPTER_METHOD_PERFORM, + message = g_dbus_method_invocation_get_message (invocation); + fd_list = g_dbus_message_get_unix_fd_list (message); + + g_assert (fd_list != NULL); + + g_variant_get (parameters, "(&o&sa{sv}&h)", + &lookup.path, &type, &iter, &idx); + + fd = g_unix_fd_list_get (fd_list, idx, NULL); + } else { + g_variant_get (parameters, "(&o&sa{sv}&s)", + &lookup.path, &type, &iter, &received); + } + + g_debug ("received %s.%s call from callback %s@%s", + lookup.interface_name, GCR_DBUS_PROMPTER_METHOD_PERFORM, lookup.path, lookup.name); active = g_hash_table_lookup (self->pv->active, &lookup); @@ -914,13 +970,17 @@ prompter_method_perform_prompt (GcrSystemPrompter *self, prompt_update_properties (active->prompt, iter); g_variant_iter_free (iter); - exchange = active_prompt_get_secret_exchange (active); - if (!gcr_secret_exchange_receive (exchange, received)) { - g_debug ("received invalid secret exchange from callback %s@%s", - lookup.path, lookup.name); - g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, - "Invalid secret exchange received"); - return; + if (g_strcmp0 (lookup.interface_name, GCR_DBUS_PROMPTER2_INTERFACE)) + active->fd = fd; + else { + exchange = active_prompt_get_secret_exchange (active); + if (!gcr_secret_exchange_receive (exchange, received)) { + g_debug ("received invalid secret exchange from callback %s@%s", + lookup.path, lookup.name); + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, + "Invalid secret exchange received"); + return; + } } active->received = TRUE; @@ -1039,6 +1099,16 @@ gcr_system_prompter_register (GcrSystemPrompter *self, g_warning ("error registering prompter %s", egg_error_message (error)); g_clear_error (&error); } + + self->pv->prompter2_registered = g_dbus_connection_register_object (connection, + GCR_DBUS_PROMPTER_OBJECT_PATH, + _gcr_dbus_prompter2_interface_info (), + &prompter_dbus_vtable, + self, NULL, &error); + if (error != NULL) { + g_warning ("error registering prompter2 %s", egg_error_message (error)); + g_clear_error (&error); + } } /** @@ -1078,6 +1148,10 @@ gcr_system_prompter_unregister (GcrSystemPrompter *self, g_assert_not_reached (); self->pv->prompter_registered = 0; + if (!g_dbus_connection_unregister_object (self->pv->connection, self->pv->prompter2_registered)) + g_assert_not_reached (); + self->pv->prompter2_registered = 0; + g_clear_object (&self->pv->connection); } diff --git a/gcr/org.gnome.keyring.Prompter2.xml b/gcr/org.gnome.keyring.Prompter2.xml new file mode 100644 index 0000000..e378983 --- /dev/null +++ b/gcr/org.gnome.keyring.Prompter2.xml @@ -0,0 +1,84 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> + +<node> + <!-- + * WARNING: This is an internal interface, and not a public API. It + * can change between releases. + * + * This can be viewed as an interface for remoting the GcrPrompt + * interface. + * + * We use a callback interface exported from the prompting client. + * The prompter calls this rather than emit signals. It does this so + * that the prompter can be aware if the client returns errors or is + * no longer around. + --> + + <interface name="org.gnome.keyring.internal.Prompter2"> + <!-- + * Prompts first call BeginPrompting() with the path that. This + * returns immediately. The prompter then calls PromptReady() + * on the callback when it's ready for that client prompt to + * start showing prompts. + --> + <method name="BeginPrompting"> + <!-- DBus path to the client prompt callback --> + <arg name="callback" type="o" direction="in"/> + </method> + + <!-- + * Called by client prompts to actually show a prompt. This can + * only be called after the prompter calls PromptReady() on + * the callback. + * + * This returns immediately, and PromptReady() will be called + * on the callback when the prompt completes. + --> + <method name="PerformPrompt"> + <!-- DBus path to the client prompt callback --> + <arg name="callback" type="o" direction="in"/> + + <!-- Type of prompt: 'password' or 'confirm' --> + <arg name="type" type="s" direction="in"/> + + <!-- GcrPrompt properties which changed since last prompt --> + <arg name="properties" type="a{sv}" direction="in"/> + + <annotation name="org.gtk.GDBus.C.UnixFD" value="true"/> + <arg type="h" name="fd" direction="in"/> + </method> + + <!-- + * Called by client prompts to stop prompting. If a prompt is + * currently being displayed then it will be cancelled as if + * the user cancelled it. + * + * Will call PromptDone() on the client callback. + --> + <method name="StopPrompting"> + <!-- DBus path to the client prompt callback --> + <arg name="callback" type="o" direction="in"/> + </method> + </interface> + + <!-- Called when ready to prompt or a prompt completes --> + <interface name="org.gnome.keyring.internal.Prompter.Callback2"> + + <!-- + * Called by the prompter when ready to show a prompt. This + * occurs when the prompter is ready for the first prompt, + * and also after prompts complete. + --> + <method name="PromptReady"> + <arg name="reply" type="s" direction="in"/> + <arg name="properties" type="a{sv}" direction="in"/> + </method> + + <!-- + * Called when the prompter stops prompting for this callback + --> + <method name="PromptDone"> + </method> + </interface> +</node> |