summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaiki Ueno <dueno@src.gnome.org>2019-09-08 17:52:35 +0200
committerDaiki Ueno <dueno@src.gnome.org>2019-09-09 07:16:24 +0200
commit03873e4721bf529f3eb952ab4d5a3bc81477d2c2 (patch)
tree7996986c5568cbbbdde1c66d8d9412343fc36b2a
parent05635f67bf02b91b165c43e56f9b6cd2ddaf862c (diff)
downloadgcr-wip/dueno/prompter.tar.gz
system-prompter: Add DBus interface sending secret through FD [ci skip]wip/dueno/prompter
-rw-r--r--gcr/Makefile.am3
-rw-r--r--gcr/gcr-dbus-constants.h2
-rw-r--r--gcr/gcr-system-prompt.c101
-rw-r--r--gcr/gcr-system-prompter.c144
-rw-r--r--gcr/org.gnome.keyring.Prompter2.xml84
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>