[gnome-keyring/wip/dueno/secrets-helper] daemon: Allow on the fly creation of secret service [ci skip]



commit e30239952cc5ac49c9148c0265fb4a977cf9fde2
Author: Daiki Ueno <dueno src gnome org>
Date:   Fri Aug 31 15:45:43 2018 +0200

    daemon: Allow on the fly creation of secret service [ci skip]
    
    WIP

 .gitignore                               |   1 +
 Makefile.am                              |   1 +
 daemon/dbus/Makefile.am                  |  51 ++++++-
 daemon/dbus/gkd-dbus.c                   | 225 ++++++++++++++++++++++++++++---
 daemon/dbus/gkd-dbus.h                   |   4 -
 daemon/dbus/gkd-secret-util.c            |  17 +++
 daemon/dbus/gkd-secret-util.h            |   5 +
 daemon/dbus/gkd-secrets-helper.c         | 153 +++++++++++++++++++++
 daemon/dbus/org.gnome.keyring.Daemon.xml |   4 +
 9 files changed, 434 insertions(+), 27 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 106e9df2..5c0737ac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -72,6 +72,7 @@ ltconfig
 ltmain.sh
 missing
 mkinstalldirs
+/gkd-secrets-helper
 /gnome-keyring-ask
 /gnome-keyring-daemon
 /list-keyrings
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..5076e0e3 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-dbus-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,44 @@ 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-dbus-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_dbus_secrets_la_SOURCES = \
+       $(secrets_generated) \
        daemon/dbus/gkd-secret-change.c \
        daemon/dbus/gkd-secret-change.h \
        daemon/dbus/gkd-secret-create.c \
@@ -85,12 +107,29 @@ libgkd_dbus_la_SOURCES = \
        daemon/dbus/gkd-secret-util.h \
        $(NULL)
 
-libgkd_dbus_la_LIBADD = \
+libgkd_dbus_secrets_la_LIBADD = \
        $(GIO_LIBS) \
        $(GLIB_LIBS) \
        $(GOBJECT_LIBS)
 
-libgkd_dbus_la_CFLAGS = \
+libgkd_dbus_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_LDADD = \
+       libgkd-dbus-secrets.la \
+       libgkm-wrap-layer.la \
+       libgkm-secret-store.la \
+       libgkm.la \
+       $(DAEMON_LIBS)
+
+gkd_secrets_helper_CFLAGS = \
        $(DAEMON_CFLAGS) \
        $(GCR_BASE_CFLAGS) \
        $(GIO_CFLAGS) \
diff --git a/daemon/dbus/gkd-dbus.c b/daemon/dbus/gkd-dbus.c
index 6644a790..ac1156da 100644
--- a/daemon/dbus/gkd-dbus.c
+++ b/daemon/dbus/gkd-dbus.c
@@ -32,7 +32,9 @@
 #include "egg/egg-cleanup.h"
 
 #include <glib.h>
+#include <glib-unix.h>
 #include <gio/gio.h>
+#include <errno.h>
 
 static GDBusConnection *dbus_conn = NULL;
 static gboolean object_registered = FALSE;
@@ -112,6 +114,204 @@ handle_get_control_directory (GkdExportedDaemon *skeleton,
        return TRUE;
 }
 
+typedef struct _GkdSecretsHelper {
+       gboolean ready;
+       gchar name[1024];
+       GPid pid;
+       gint input;
+       gint output;
+       guint output_id;
+       guint child_id;
+       guint timeout_id;
+       guint name_watch_id;
+} GkdSecretsHelper;
+
+static GList *helpers = NULL;
+static GMutex helpers_lock;
+
+static void
+cleanup_secrets_helper (GkdSecretsHelper *helper)
+{
+       int status;
+
+       if (helper->timeout_id > 0) {
+               g_source_remove (helper->timeout_id);
+               helper->timeout_id = 0;
+       }
+
+       if (helper->child_id > 0) {
+               g_source_remove (helper->child_id);
+               helper->child_id = 0;
+       }
+
+       if (helper->output_id > 0) {
+               g_source_remove (helper->output_id);
+               helper->output_id = 0;
+       }
+
+       if (helper->input > 0) {
+               close (helper->input);
+               helper->input = 0;
+       }
+
+       if (helper->output > 0) {
+               close (helper->output);
+               helper->output = 0;
+       }
+
+       if (helper->pid > 0) {
+               waitpid (helper->pid, &status, 0);
+               g_spawn_close_pid (helper->pid);
+               helper->pid = 0;
+       }
+}
+
+static void
+free_secrets_helper (GkdSecretsHelper *helper)
+{
+       cleanup_secrets_helper (helper);
+       g_free (helper);
+}
+
+static void
+on_child_watch (GPid pid,
+                gint status,
+                gpointer user_data)
+{
+       GkdSecretsHelper *helper = user_data;
+       GError *error = NULL;
+
+       if (pid != helper->pid)
+               return;
+
+       helper->pid = 0;
+       helper->output_id = 0;
+       helper->child_id = 0;
+
+       if (!g_spawn_check_exit_status (status, &error)) {
+               g_message ("gkd-secrets-helper: %s", error->message);
+               g_error_free (error);
+       }
+
+       g_spawn_close_pid (pid);
+}
+
+static gboolean
+on_output_watch (gint fd,
+                GIOCondition condition,
+                gpointer user_data)
+{
+       GkdSecretsHelper *helper = user_data;
+       guint8 buf[1024];
+       gssize len;
+
+       if (condition & G_IO_IN) {
+               guint8 *p;
+
+               len = read (fd, buf, sizeof (buf));
+               if (len < 0) {
+                       if (errno != EAGAIN && errno != EINTR)
+                               g_message ("couldn't read from gkd-secrets-helper: %m");
+                       condition |= G_IO_ERR;
+               }
+               p = memchr (buf, '\n', len);
+               if (p) {
+                       helper->ready = TRUE;
+                       memcpy (helper->name, buf, p - buf);
+                       helper->name[p - buf] = '\0';
+               }
+       }
+
+       if (condition & G_IO_HUP || condition & G_IO_ERR)
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean
+on_timeout (gpointer user_data)
+{
+       GkdSecretsHelper *helper = user_data;
+
+       cleanup_secrets_helper (helper);
+
+       return TRUE;
+}
+
+static void
+on_sender_vanished (GDBusConnection *connection,
+                   const gchar *name,
+                   gpointer user_data)
+{
+       GkdSecretsHelper *helper = user_data;
+
+       cleanup_secrets_helper (helper);
+}
+
+static void
+child_setup (gpointer user_data)
+{
+       close (STDERR_FILENO);
+}
+
+static gboolean
+handle_create_secret_service (GkdExportedDaemon *skeleton,
+                             GDBusMethodInvocation *invocation,
+                             const gchar *arg_KeyringDirectory,
+                             gpointer user_data)
+{
+       const gchar *argv[] = { GKD_SECRETS_HELPER, arg_KeyringDirectory, NULL };
+       GkdSecretsHelper *helper;
+       GError *error = NULL;
+
+       helper = g_new0 (GkdSecretsHelper, 1);
+
+       if (!g_spawn_async_with_pipes ("/", (gchar **)argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
+                                      child_setup, NULL, &helper->pid, NULL, &helper->output, NULL, &error)) 
{
+               g_dbus_method_invocation_return_gerror (invocation, error);
+               g_clear_error (&error);
+               g_free (helper);
+               return FALSE;
+       }
+
+       helper->output_id = g_unix_fd_add (helper->output,
+                                         G_IO_IN | G_IO_HUP | G_IO_ERR,
+                                         on_output_watch, helper);
+       helper->child_id = g_child_watch_add (helper->pid, on_child_watch, helper);
+       helper->timeout_id = g_timeout_add_seconds (5, on_timeout, helper);
+
+       while (!helper->ready && helper->timeout_id > 0)
+               g_main_context_iteration (NULL, FALSE);
+       g_source_remove (helper->timeout_id);
+
+       if (!helper->ready) {
+               g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                            "gkd-secrets-helper process is not ready");
+               g_dbus_method_invocation_return_gerror (invocation, error);
+               g_clear_error (&error);
+               cleanup_secrets_helper (helper);
+               g_free (helper);
+               return FALSE;
+       }
+
+       helper->name_watch_id = g_bus_watch_name_on_connection (g_dbus_method_invocation_get_connection 
(invocation),
+                                                               g_dbus_method_invocation_get_sender 
(invocation),
+                                                               G_BUS_NAME_WATCHER_FLAGS_NONE,
+                                                               NULL,
+                                                               on_sender_vanished,
+                                                               helper,
+                                                               (GDestroyNotify)cleanup_secrets_helper);
+
+       gkd_exported_daemon_complete_create_secret_service (skeleton, invocation,
+                                                           helper->name);
+
+       g_mutex_lock (&helpers_lock);
+       helpers = g_list_append (helpers, helper);
+       g_mutex_unlock (&helpers_lock);
+
+       return TRUE;
+}
+
 static void
 cleanup_singleton (gpointer user_data)
 {
@@ -123,6 +323,10 @@ cleanup_singleton (gpointer user_data)
                g_object_unref (skeleton);
        }
        object_registered = FALSE;
+
+       g_mutex_lock (&helpers_lock);
+       g_list_free_full (helpers, (GDestroyNotify)free_secrets_helper);
+       g_mutex_unlock (&helpers_lock);
 }
 
 gboolean
@@ -148,6 +352,8 @@ gkd_dbus_singleton_acquire (gboolean *acquired)
                                  G_CALLBACK (handle_get_control_directory), NULL);
                g_signal_connect (skeleton, "handle-get-environment",
                                  G_CALLBACK (handle_get_environment), NULL);
+               g_signal_connect (skeleton, "handle-create-secret-service",
+                                 G_CALLBACK (handle_create_secret_service), NULL);
 
                g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (skeleton), dbus_conn,
                                                  GNOME_KEYRING_DAEMON_PATH, &error);
@@ -259,6 +465,7 @@ dbus_cleanup (gpointer unused)
        gkd_dbus_secrets_cleanup (dbus_conn);
        gkd_dbus_session_cleanup (dbus_conn);
        gkd_dbus_environment_cleanup (dbus_conn);
+       g_mutex_clear (&helpers_lock);
 }
 
 gboolean
@@ -278,24 +485,8 @@ gkd_dbus_setup (void)
 
        /* Secrets API */
        gkd_dbus_secrets_init (dbus_conn);
+       g_mutex_init (&helpers_lock);
 
        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..91ada5b7 100644
--- a/daemon/dbus/gkd-secret-util.h
+++ b/daemon/dbus/gkd-secret-util.h
@@ -24,6 +24,7 @@
 #include "gkd-secret-types.h"
 
 #include <glib.h>
+#include <gio/gio.h>
 
 gboolean          gkd_secret_util_parse_path                            (const gchar *path,
                                                                          gchar **collection,
@@ -33,4 +34,8 @@ 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..2372e07e
--- /dev/null
+++ b/daemon/dbus/gkd-secrets-helper.c
@@ -0,0 +1,153 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#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 <string.h>
+#include <gck/gck.h>
+
+static CK_FUNCTION_LIST_PTR pkcs11_roof;
+static GkdSecretService *service;
+static GDBusConnection *connection = NULL;
+
+static void
+pkcs11_cleanup (gpointer unused)
+{
+       CK_RV rv;
+
+       if (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);
+       }
+       g_clear_object (&service);
+       g_clear_object (&connection);
+}
+
+static gboolean
+pkcs11_initialize (const gchar *path)
+{
+       CK_FUNCTION_LIST_PTR secret_store;
+       CK_C_INITIALIZE_ARGS init_args;
+       CK_RV rv;
+       GckSlot *slot = NULL;
+       GckModule *module = NULL;
+       GList *modules = NULL;
+       GError *error = NULL;
+
+       /* 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;
+       init_args.pReserved = g_strdup_printf ("directory=\"%s\"", 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);
+               goto cleanup;
+       }
+
+       module = gck_module_new (pkcs11_roof);
+       if (!module)
+               goto cleanup;
+
+       modules = g_list_prepend (NULL, module);
+       module = NULL;
+       slot = gck_modules_token_for_uri (modules,
+                                         "pkcs11:token=Secret%20Store", &error);
+       if (!slot) {
+               g_warning ("couldn't find secret store: %s",
+                          egg_error_message (error));
+               g_clear_error (&error);
+               goto cleanup;
+       }
+       gck_list_unref_free (modules);
+
+       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);
+               goto cleanup;
+       }
+       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);
+       g_object_unref (connection);
+       g_object_unref (slot);
+
+       egg_cleanup_register (pkcs11_cleanup, NULL);
+
+       return TRUE;
+
+ cleanup:
+
+       gck_list_unref_free (modules);
+       g_clear_object (&module);
+       g_clear_object (&slot);
+       g_clear_object (&service);
+       g_clear_object (&connection);
+
+       return FALSE;
+}
+
+int
+main (int argc, char **argv)
+{
+       GMainLoop *loop;
+
+       if (argc != 2) {
+               g_printerr ("gkd-secrets-helper PATH\n");
+               return 1;
+       }
+
+       if (!pkcs11_initialize (argv[1]))
+               return 1;
+
+       loop = g_main_loop_new (NULL, FALSE);
+       g_unix_fd_add (STDIN_FILENO, G_IO_IN | G_IO_HUP | G_IO_ERR, g_main_loop_quit, loop);
+       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..7fccf687 100644
--- a/daemon/dbus/org.gnome.keyring.Daemon.xml
+++ b/daemon/dbus/org.gnome.keyring.Daemon.xml
@@ -9,5 +9,9 @@
     <method name="GetControlDirectory">
       <arg name="ControlDirectory" type="s" direction="out"/>
     </method>
+    <method name="CreateSecretService">
+      <arg name="KeyringDirectory" type="s" direction="in"/>
+      <arg name="DBusName" type="s" direction="out"/>
+    </method>
   </interface>
 </node>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]