diff options
author | Daiki Ueno <dueno@src.gnome.org> | 2019-08-19 17:59:22 +0200 |
---|---|---|
committer | Daiki Ueno <ueno@gnu.org> | 2019-10-13 06:21:38 +0000 |
commit | 8f886f0797638b2d321aed49dd3481ad2ad5ca20 (patch) | |
tree | fb18202e21b6b2f68c56fd2bf0fb2a69abc7c36c /libsecret | |
parent | f2b7f6d505488a6bc2a04e48e89bf5511d2949a9 (diff) | |
download | libsecret-8f886f0797638b2d321aed49dd3481ad2ad5ca20.tar.gz |
secret-file-backend: Retrieve master password from flatpak portal
Diffstat (limited to 'libsecret')
-rw-r--r-- | libsecret/secret-backend.c | 19 | ||||
-rw-r--r-- | libsecret/secret-file-backend.c | 306 |
2 files changed, 306 insertions, 19 deletions
diff --git a/libsecret/secret-backend.c b/libsecret/secret-backend.c index baa2e9a..30e3abb 100644 --- a/libsecret/secret-backend.c +++ b/libsecret/secret-backend.c @@ -149,17 +149,24 @@ backend_get_impl_type (void) GIOExtension *e; GIOExtensionPoint *ep; - envvar = g_getenv ("SECRET_BACKEND"); - if (envvar == NULL || *envvar == '\0') - extension_name = "service"; - else - extension_name = envvar; - g_type_ensure (secret_service_get_type ()); #ifdef WITH_GCRYPT g_type_ensure (secret_file_backend_get_type ()); #endif +#ifdef WITH_GCRYPT + if (g_file_test ("/.flatpak-info", G_FILE_TEST_EXISTS)) + extension_name = "file"; + else +#endif + { + envvar = g_getenv ("SECRET_BACKEND"); + if (envvar == NULL || *envvar == '\0') + extension_name = "service"; + else + extension_name = envvar; + } + ep = g_io_extension_point_lookup (SECRET_BACKEND_EXTENSION_POINT_NAME); e = g_io_extension_point_get_extension_by_name (ep, extension_name); if (e == NULL) { diff --git a/libsecret/secret-file-backend.c b/libsecret/secret-file-backend.c index 3e6d0e2..c557754 100644 --- a/libsecret/secret-file-backend.c +++ b/libsecret/secret-file-backend.c @@ -21,6 +21,19 @@ #include "secret-private.h" #include "secret-retrievable.h" +#include "egg/egg-secure-memory.h" + +EGG_SECURE_DECLARE (secret_file_backend); + +#include <gio/gunixfdlist.h> +#include <gio/gunixinputstream.h> +#include <glib-unix.h> + +#define PORTAL_BUS_NAME "org.freedesktop.portal.Desktop" +#define PORTAL_OBJECT_PATH "/org/freedesktop/portal/desktop" +#define PORTAL_REQUEST_INTERFACE "org.freedesktop.portal.Request" +#define PORTAL_SECRET_INTERFACE "org.freedesktop.portal.Secret" + static void secret_file_backend_async_initable_iface (GAsyncInitableIface *iface); static void secret_file_backend_backend_iface (SecretBackendInterface *iface); @@ -138,6 +151,267 @@ on_collection_new_async (GObject *source_object, g_object_unref (task); } +typedef struct { + gint io_priority; + GFile *file; + GInputStream *stream; + gchar *buffer; + GDBusConnection *connection; + gchar *request_path; + guint portal_signal_id; + gulong cancellable_signal_id; +} InitClosure; + +static void +init_closure_free (gpointer data) +{ + InitClosure *init = data; + g_object_unref (init->file); + g_clear_object (&init->stream); + g_clear_pointer (&init->buffer, egg_secure_free); + g_clear_object (&init->connection); + g_clear_pointer (&init->request_path, g_free); + g_slice_free (InitClosure, init); +} + +#define PASSWORD_SIZE 64 + +static void +on_read_all (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + GInputStream *stream = G_INPUT_STREAM (source_object); + GTask *task = G_TASK (user_data); + InitClosure *init = g_task_get_task_data (task); + gsize bytes_read; + SecretValue *password; + GError *error = NULL; + + if (!g_input_stream_read_all_finish (stream, result, &bytes_read, + &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + if (bytes_read != PASSWORD_SIZE) { + g_task_return_new_error (task, + SECRET_ERROR, + SECRET_ERROR_PROTOCOL, + "invalid password returned from portal"); + g_object_unref (task); + return; + } + + password = secret_value_new (init->buffer, bytes_read, "text/plain"); + g_async_initable_new_async (SECRET_TYPE_FILE_COLLECTION, + init->io_priority, + g_task_get_cancellable (task), + on_collection_new_async, + task, + "file", g_object_ref (init->file), + "password", password, + NULL); + g_object_unref (init->file); + secret_value_unref (password); +} + +static void +on_portal_response (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + GTask *task = G_TASK (user_data); + InitClosure *init = g_task_get_task_data (task); + guint32 response; + + g_dbus_connection_signal_unsubscribe (connection, + init->portal_signal_id); + + g_variant_get (parameters, "(ua{sv})", &response, NULL); + + switch (response) { + case 0: + init->buffer = egg_secure_alloc (PASSWORD_SIZE); + g_input_stream_read_all_async (init->stream, + init->buffer, PASSWORD_SIZE, + G_PRIORITY_DEFAULT, + g_task_get_cancellable (task), + on_read_all, + task); + break; + case 1: + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_CANCELLED, + "user interaction cancelled"); + g_object_unref (task); + break; + case 2: + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "user interaction failed"); + g_object_unref (task); + break; + } +} + +static void +on_portal_request_close (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + GDBusConnection *connection = G_DBUS_CONNECTION (source_object); + GTask *task = G_TASK (user_data); + GError *error = NULL; + + if (!g_dbus_connection_call_finish (connection, result, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +static void +on_portal_cancel (GCancellable *cancellable, + gpointer user_data) +{ + GTask *task = G_TASK (user_data); + InitClosure *init = g_task_get_task_data (task); + + g_dbus_connection_call (init->connection, + PORTAL_BUS_NAME, + init->request_path, + PORTAL_REQUEST_INTERFACE, + "Close", + NULL, + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + on_portal_request_close, + task); + + g_cancellable_disconnect (cancellable, init->cancellable_signal_id); +} + +static void +on_portal_retrieve_secret (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + GDBusConnection *connection = G_DBUS_CONNECTION (source_object); + GTask *task = G_TASK (user_data); + InitClosure *init = g_task_get_task_data (task); + GCancellable *cancellable = g_task_get_cancellable (task); + GVariant *reply; + GError *error = NULL; + + reply = g_dbus_connection_call_with_unix_fd_list_finish (connection, + NULL, + result, + &error); + if (reply == NULL) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + g_variant_get (reply, "(o)", &init->request_path); + g_variant_unref (reply); + + init->portal_signal_id = + g_dbus_connection_signal_subscribe (connection, + PORTAL_BUS_NAME, + PORTAL_REQUEST_INTERFACE, + "Response", + init->request_path, + NULL, + G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, + on_portal_response, + task, + NULL); + + if (cancellable != NULL) + init->cancellable_signal_id = + g_cancellable_connect (cancellable, + G_CALLBACK (on_portal_cancel), + task, + NULL); +} + +static void +on_bus_get (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + GDBusConnection *connection; + GTask *task = G_TASK (user_data); + InitClosure *init = g_task_get_task_data (task); + GUnixFDList *fd_list; + gint fds[2]; + gint fd_index; + GVariantBuilder options; + GError *error = NULL; + + connection = g_bus_get_finish (result, &error); + if (connection == NULL) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + init->connection = connection; + + if (!g_unix_open_pipe (fds, FD_CLOEXEC, &error)) { + g_object_unref (connection); + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + fd_list = g_unix_fd_list_new (); + fd_index = g_unix_fd_list_append (fd_list, fds[1], &error); + close (fds[1]); + if (fd_index < 0) { + close (fds[0]); + g_object_unref (fd_list); + g_object_unref (connection); + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + close (fds[1]); + init->stream = g_unix_input_stream_new (fds[0], TRUE); + + g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT); + g_dbus_connection_call_with_unix_fd_list (connection, + PORTAL_BUS_NAME, + PORTAL_OBJECT_PATH, + PORTAL_SECRET_INTERFACE, + "RetrieveSecret", + g_variant_new ("(h@a{sv})", + fd_index, + g_variant_builder_end (&options)), + G_VARIANT_TYPE ("(o)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + fd_list, + g_task_get_cancellable (task), + on_portal_retrieve_secret, + task); + g_object_unref (fd_list); +} + static void secret_file_backend_real_init_async (GAsyncInitable *initable, int io_priority, @@ -152,6 +426,7 @@ secret_file_backend_real_init_async (GAsyncInitable *initable, const gchar *envvar; GTask *task; GError *error = NULL; + InitClosure *init; gboolean ret; task = g_task_new (initable, cancellable, callback, user_data); @@ -194,9 +469,25 @@ secret_file_backend_real_init_async (GAsyncInitable *initable, } envvar = g_getenv ("SECRET_FILE_TEST_PASSWORD"); - if (envvar != NULL && *envvar != '\0') + if (envvar != NULL && *envvar != '\0') { password = secret_value_new (envvar, -1, "text/plain"); - else { + g_async_initable_new_async (SECRET_TYPE_FILE_COLLECTION, + io_priority, + cancellable, + on_collection_new_async, + task, + "file", file, + "password", password, + NULL); + g_object_unref (file); + secret_value_unref (password); + } else if (g_file_test ("/.flatpak-info", G_FILE_TEST_EXISTS)) { + init = g_slice_new0 (InitClosure); + init->io_priority = io_priority; + init->file = file; + g_task_set_task_data (task, init, init_closure_free); + g_bus_get (G_BUS_TYPE_SESSION, cancellable, on_bus_get, task); + } else { g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, @@ -204,17 +495,6 @@ secret_file_backend_real_init_async (GAsyncInitable *initable, g_object_unref (task); return; } - - g_async_initable_new_async (SECRET_TYPE_FILE_COLLECTION, - io_priority, - cancellable, - on_collection_new_async, - task, - "file", file, - "password", password, - NULL); - g_object_unref (file); - secret_value_unref (password); } static gboolean |