From b84b87c0bb18cda99769d2fefe35381f37e635d9 Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Fri, 31 Aug 2018 15:45:43 +0200 Subject: daemon: Allow on the fly creation of secret service [ci skip] WIP --- Makefile.am | 1 + daemon/dbus/Makefile.am | 57 +++++++++-- daemon/dbus/gkd-dbus.c | 157 +++++++++++++++++++++++++++---- daemon/dbus/gkd-dbus.h | 4 - daemon/dbus/gkd-secret-util.c | 17 ++++ daemon/dbus/gkd-secret-util.h | 7 +- daemon/dbus/gkd-secrets-helper.c | 141 +++++++++++++++++++++++++++ daemon/dbus/org.gnome.keyring.Daemon.xml | 4 + 8 files changed, 358 insertions(+), 30 deletions(-) create mode 100644 daemon/dbus/gkd-secrets-helper.c diff --git a/Makefile.am b/Makefile.am index 640c0e98..dd297dcd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -158,6 +158,7 @@ AM_CPPFLAGS = \ $(GLIB_CFLAGS) bin_PROGRAMS = +libexec_PROGRAMS = BUILT_SOURCES = check_PROGRAMS = noinst_DATA = diff --git a/daemon/dbus/Makefile.am b/daemon/dbus/Makefile.am index 9d0b2e1a..536dcb59 100644 --- a/daemon/dbus/Makefile.am +++ b/daemon/dbus/Makefile.am @@ -1,5 +1,5 @@ -noinst_LTLIBRARIES += libgkd-dbus.la +noinst_LTLIBRARIES += libgkd-dbus.la libgkd-secrets.la daemon/dbus/gkd-secrets-generated.h: daemon/dbus/org.freedesktop.Secrets.xml $(AM_V_GEN) gdbus-codegen --interface-prefix org.freedesktop.Secrets. \ @@ -38,22 +38,45 @@ EXTRA_DIST += \ daemon/dbus/org.gnome.keyring.InternalUnsupportedGuiltRiddenInterface.xml \ $(NULL) -BUILT_SOURCES += \ +daemon_generated = \ daemon/dbus/gkd-daemon-generated.c \ - daemon/dbus/gkd-daemon-generated.h \ + daemon/dbus/gkd-daemon-generated.h + +secrets_generated = \ daemon/dbus/gkd-internal-generated.c \ daemon/dbus/gkd-internal-generated.h \ daemon/dbus/gkd-secrets-generated.c \ daemon/dbus/gkd-secrets-generated.h +BUILT_SOURCES += \ + $(daemon_generated) \ + $(secrets_generated) + libgkd_dbus_la_SOURCES = \ - $(BUILT_SOURCES) \ + $(daemon_generated) \ daemon/dbus/gkd-dbus.c \ daemon/dbus/gkd-dbus.h \ daemon/dbus/gkd-dbus-environment.c \ daemon/dbus/gkd-dbus-private.h \ daemon/dbus/gkd-dbus-secrets.c \ daemon/dbus/gkd-dbus-session.c \ + $(NULL) + +libgkd_dbus_la_LIBADD = \ + libgkd-secrets.la \ + $(GIO_LIBS) \ + $(GLIB_LIBS) \ + $(GOBJECT_LIBS) + +libgkd_dbus_la_CFLAGS = \ + -DGKD_SECRETS_HELPER=\"$(libexecdir)/gkd-secrets-helper\" \ + $(DAEMON_CFLAGS) \ + $(GCR_BASE_CFLAGS) \ + $(GIO_CFLAGS) \ + $(GOBJECT_CFLAGS) + +libgkd_secrets_la_SOURCES = \ + $(secrets_generated) \ daemon/dbus/gkd-secret-change.c \ daemon/dbus/gkd-secret-change.h \ daemon/dbus/gkd-secret-create.c \ @@ -82,20 +105,38 @@ libgkd_dbus_la_SOURCES = \ daemon/dbus/gkd-secret-unlock.c \ daemon/dbus/gkd-secret-unlock.h \ daemon/dbus/gkd-secret-util.c \ - daemon/dbus/gkd-secret-util.h \ - $(NULL) + daemon/dbus/gkd-secret-util.h -libgkd_dbus_la_LIBADD = \ +libgkd_secrets_la_LIBADD = \ $(GIO_LIBS) \ $(GLIB_LIBS) \ $(GOBJECT_LIBS) -libgkd_dbus_la_CFLAGS = \ +libgkd_secrets_la_CFLAGS = \ + $(DAEMON_CFLAGS) \ + $(GCR_BASE_CFLAGS) \ + $(GIO_CFLAGS) \ + $(GOBJECT_CFLAGS) + +libexec_PROGRAMS += gkd-secrets-helper + +gkd_secrets_helper_SOURCES = daemon/dbus/gkd-secrets-helper.c + +gkd_secrets_helper_CFLAGS = \ $(DAEMON_CFLAGS) \ $(GCR_BASE_CFLAGS) \ $(GIO_CFLAGS) \ $(GOBJECT_CFLAGS) +gkd_secrets_helper_LDADD = \ + libgkd-secrets.la \ + libgkm-secret-store.la \ + libgkm-wrap-layer.la \ + $(GCR_BASE_LIBS) \ + $(GIO_LIBS) \ + $(GLIB_LIBS) \ + $(GOBJECT_LIBS) + # ------------------------------------------------------------------- # TESTS diff --git a/daemon/dbus/gkd-dbus.c b/daemon/dbus/gkd-dbus.c index 6644a790..3d304bd4 100644 --- a/daemon/dbus/gkd-dbus.c +++ b/daemon/dbus/gkd-dbus.c @@ -32,6 +32,7 @@ #include "egg/egg-cleanup.h" #include +#include #include static GDBusConnection *dbus_conn = NULL; @@ -112,6 +113,142 @@ handle_get_control_directory (GkdExportedDaemon *skeleton, return TRUE; } +struct SecretHelperData +{ + GPid pid; + GMainLoop *loop; + GIOChannel *channel; + gchar *name; + guint output_id; + guint child_id; + guint timeout_id; +}; + +static gboolean +on_secrets_helper_output (GIOChannel *source, + GIOCondition condition, + gpointer user_data) +{ + struct SecretHelperData *data = user_data; + gchar *line; + gsize terminator_pos; + GError *error = NULL; + + if (g_io_channel_read_line (source, &line, NULL, + &terminator_pos, &error) == + G_IO_STATUS_NORMAL) { + line[terminator_pos] = '\0'; + data->name = line; + } + + data->output_id = 0; + g_main_loop_quit (data->loop); + return FALSE; +} + +static void +on_secrets_helper_child (GPid pid, + gint status, + gpointer user_data) +{ + struct SecretHelperData *data = user_data; + + g_spawn_close_pid (pid); + if (data->output_id > 0) + g_source_remove (data->output_id); + if (data->timeout_id > 0) + g_source_remove (data->timeout_id); + + data->child_id = 0; + g_main_loop_quit (data->loop); +} + +static gboolean +on_secrets_helper_timeout (gpointer user_data) +{ + struct SecretHelperData *data = user_data; + + kill (data->pid, SIGTERM); + data->timeout_id = 0; + g_main_loop_quit (data->loop); + return FALSE; +} + +static void +on_secrets_helper_peer_vanished (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + GPid pid = GPOINTER_TO_INT (user_data); + kill (pid, SIGTERM); +} + +static gboolean +handle_get_secret_service (GkdExportedDaemon *object, + GDBusMethodInvocation *invocation, + const gchar *arg_AppID) +{ + gchar *args[] = { + GKD_SECRETS_HELPER, + (gchar *) arg_AppID, + NULL + }; + gint standard_output; + GError *error = NULL; + struct SecretHelperData data; + const gchar *sender; + + memset (&data, 0, sizeof (data)); + + if (!g_spawn_async_with_pipes (NULL, args, NULL, G_SPAWN_DEFAULT, NULL, + NULL, &data.pid, NULL, &standard_output, NULL, &error)) { + g_dbus_method_invocation_return_gerror (invocation, error); + return FALSE; + } + + data.loop = g_main_loop_new (NULL, FALSE); + data.channel = g_io_channel_unix_new (standard_output); + data.name = NULL; + data.output_id = g_io_add_watch (data.channel, G_IO_IN, on_secrets_helper_output, &data); + data.child_id = g_child_watch_add (data.pid, on_secrets_helper_child, &data); + data.timeout_id = g_timeout_add (10000, on_secrets_helper_timeout, &data); + + g_main_loop_run (data.loop); + + if (data.output_id > 0) + g_source_remove (data.output_id); + if (data.child_id > 0) + g_source_remove (data.child_id); + if (data.timeout_id > 0) + g_source_remove (data.timeout_id); + + g_main_loop_unref (data.loop); + g_io_channel_unref (data.channel); + + if (data.name == NULL) { + g_dbus_method_invocation_return_error (invocation, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "couldn't launch gkd-secrets-service"); + return FALSE; + } + + sender = g_dbus_method_invocation_get_sender (invocation); + g_bus_watch_name_on_connection (g_dbus_method_invocation_get_connection (invocation), + sender, + G_BUS_NAME_WATCHER_FLAGS_NONE, + NULL, + on_secrets_helper_peer_vanished, + GINT_TO_POINTER (data.pid), + NULL); + + gkd_exported_daemon_complete_get_secret_service (object, + invocation, + data.name); + g_free (data.name); + return TRUE; +} + static void cleanup_singleton (gpointer user_data) { @@ -149,6 +286,9 @@ gkd_dbus_singleton_acquire (gboolean *acquired) g_signal_connect (skeleton, "handle-get-environment", G_CALLBACK (handle_get_environment), NULL); + g_signal_connect (skeleton, "handle-get-secret-service", + G_CALLBACK (handle_get_secret_service), NULL); + g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (skeleton), dbus_conn, GNOME_KEYRING_DAEMON_PATH, &error); @@ -282,20 +422,3 @@ gkd_dbus_setup (void) egg_cleanup_register (dbus_cleanup, NULL); return TRUE; } - -gboolean -gkd_dbus_invocation_matches_caller (GDBusMethodInvocation *invocation, - const char *caller) -{ - const char *invocation_caller; - - invocation_caller = g_dbus_method_invocation_get_sender (invocation); - if (!g_str_equal (invocation_caller, caller)) { - g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, - G_DBUS_ERROR_ACCESS_DENIED, - "Invalid caller"); - return FALSE; - } - - return TRUE; -} diff --git a/daemon/dbus/gkd-dbus.h b/daemon/dbus/gkd-dbus.h index 8a9ca450..0784a9a1 100644 --- a/daemon/dbus/gkd-dbus.h +++ b/daemon/dbus/gkd-dbus.h @@ -34,8 +34,4 @@ gboolean gkd_dbus_singleton_acquire (gboolean *acquired); gchar* gkd_dbus_singleton_control (void); -/* DBus utils */ -gboolean gkd_dbus_invocation_matches_caller (GDBusMethodInvocation *invocation, - const char *caller); - #endif /* GKD_DBUS_H */ diff --git a/daemon/dbus/gkd-secret-util.c b/daemon/dbus/gkd-secret-util.c index 29443f8c..a01635d5 100644 --- a/daemon/dbus/gkd-secret-util.c +++ b/daemon/dbus/gkd-secret-util.c @@ -146,3 +146,20 @@ gkd_secret_util_build_path (const gchar *base, gconstpointer identifier, gssize return g_string_free (result, FALSE); } + +gboolean +gkd_dbus_invocation_matches_caller (GDBusMethodInvocation *invocation, + const char *caller) +{ + const char *invocation_caller; + + invocation_caller = g_dbus_method_invocation_get_sender (invocation); + if (!g_str_equal (invocation_caller, caller)) { + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "Invalid caller"); + return FALSE; + } + + return TRUE; +} diff --git a/daemon/dbus/gkd-secret-util.h b/daemon/dbus/gkd-secret-util.h index 99e7814f..97abd7fd 100644 --- a/daemon/dbus/gkd-secret-util.h +++ b/daemon/dbus/gkd-secret-util.h @@ -23,7 +23,7 @@ #include "gkd-secret-types.h" -#include +#include gboolean gkd_secret_util_parse_path (const gchar *path, gchar **collection, @@ -33,4 +33,9 @@ gchar* gkd_secret_util_build_path (const g gconstpointer identifier, gssize n_identifier); + +/* DBus utils */ +gboolean gkd_dbus_invocation_matches_caller (GDBusMethodInvocation *invocation, + const char *caller); + #endif /* __GKD_SECRET_UTIL_H__ */ diff --git a/daemon/dbus/gkd-secrets-helper.c b/daemon/dbus/gkd-secrets-helper.c new file mode 100644 index 00000000..88aa67a5 --- /dev/null +++ b/daemon/dbus/gkd-secrets-helper.c @@ -0,0 +1,141 @@ +/* + * gnome-keyring + * + * Copyright (C) 2008 Stefan Walter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General License for more details. + * + * You should have received a copy of the GNU Lesser General + * License along with this program; if not, see + * . + */ + +#include "config.h" + +#include "egg/egg-cleanup.h" +#include "egg/egg-error.h" + +#include "gkd-dbus.h" +#include "gkd-secret-service.h" + +#include "pkcs11/wrap-layer/gkm-wrap-layer.h" +#include "pkcs11/secret-store/gkm-secret-store.h" + +#include +#include + +static void +pkcs11_cleanup (gpointer data) +{ + CK_FUNCTION_LIST_PTR pkcs11_roof = data; + CK_RV rv; + + g_assert (pkcs11_roof); + + rv = (pkcs11_roof->C_Finalize) (NULL); + + if (rv != CKR_OK) + g_warning ("couldn't finalize internal PKCS#11 stack (code: %d)", (gint)rv); +} + +static gboolean +pkcs11_initialize (const gchar *app_id) +{ + CK_FUNCTION_LIST_PTR secret_store; + CK_FUNCTION_LIST_PTR pkcs11_roof; + CK_C_INITIALIZE_ARGS init_args; + CK_RV rv; + GckSlot *slot = NULL; + GckModule *module; + GList *modules; + GkdSecretService *service; + GError *error = NULL; + GDBusConnection *connection; + gchar *path; + + /* Secrets */ + secret_store = gkm_secret_store_get_functions (); + + /* Add all of those into the wrapper layer */ + gkm_wrap_layer_add_module (secret_store); + + pkcs11_roof = gkm_wrap_layer_get_functions (); + + memset (&init_args, 0, sizeof (init_args)); + init_args.flags = CKF_OS_LOCKING_OK; + path = g_build_filename (g_get_user_data_dir (), "keyrings", app_id, NULL); + init_args.pReserved = g_strdup_printf ("directory=\"%s\"", path); + g_free (path); + + /* Initialize the whole caboodle */ + rv = (pkcs11_roof->C_Initialize) (&init_args); + g_free (init_args.pReserved); + + if (rv != CKR_OK) { + g_warning ("couldn't initialize internal PKCS#11 stack (code: %d)", (gint)rv); + return FALSE; + } + + egg_cleanup_register (pkcs11_cleanup, NULL); + + module = gck_module_new (pkcs11_roof); + g_return_val_if_fail (module, FALSE); + + modules = g_list_prepend (NULL, module); + slot = gck_modules_token_for_uri (modules, + "pkcs11:token=Secret%20Store", &error); + gck_list_unref_free (modules); + if (!slot) { + g_warning ("couldn't find secret store: %s", + egg_error_message (error)); + g_clear_error (&error); + return FALSE; + } + + connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + if (!connection) { + g_warning ("couldn't connect to session bus: %s", + egg_error_message (error)); + g_clear_error (&error); + return FALSE; + } + g_print ("%s\n", g_dbus_connection_get_unique_name (connection)); + + service = g_object_new (GKD_SECRET_TYPE_SERVICE, + "connection", connection, + "pkcs11-slot", slot, + NULL); + + egg_cleanup_register (g_object_unref, service); + egg_cleanup_register (g_object_unref, connection); + + return TRUE; +} + +int +main (int argc, char **argv) +{ + GMainLoop *loop; + + if (argc != 2) { + g_printerr ("gkd-secrets-helper APP-ID\n"); + return 1; + } + + if (!pkcs11_initialize (argv[1])) + return 1; + + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + g_main_loop_unref (loop); + + return 0; +} diff --git a/daemon/dbus/org.gnome.keyring.Daemon.xml b/daemon/dbus/org.gnome.keyring.Daemon.xml index b4334d9b..0cb8ab46 100644 --- a/daemon/dbus/org.gnome.keyring.Daemon.xml +++ b/daemon/dbus/org.gnome.keyring.Daemon.xml @@ -9,5 +9,9 @@ + + + + -- cgit v1.2.1