[gnome-keyring] login: Factor out the code for storing/retrieving login keyring passwords



commit 354f9887eef1c2c6144b904739ca0230bc2f2a4e
Author: Stef Walter <stefw redhat com>
Date:   Sun Oct 26 07:37:28 2014 +0100

    login: Factor out the code for storing/retrieving login keyring passwords
    
    This was in the gpg-agent, and want to use it in our ssh-agent wrapper

 daemon/login/Makefile.am |    1 +
 daemon/login/gkd-login.c |  300 ++++++++++++++++++++++++++++++++++++++++++++++
 daemon/login/gkd-login.h |   20 +++
 3 files changed, 321 insertions(+), 0 deletions(-)
---
diff --git a/daemon/login/Makefile.am b/daemon/login/Makefile.am
index 609be5b..2b6d389 100644
--- a/daemon/login/Makefile.am
+++ b/daemon/login/Makefile.am
@@ -10,6 +10,7 @@ libgkd_login_la_SOURCES = \
        $(NULL)
 libgkd_login_la_CFLAGS = \
        $(GCK_CFLAGS) \
+       $(GCR_CFLAGS) \
        $(GOBJECT_CFLAGS)
 libgkd_login_la_LIBADD = \
        $(GCK_LIBS) \
diff --git a/daemon/login/gkd-login.c b/daemon/login/gkd-login.c
index 2c0cb20..a5d1a90 100644
--- a/daemon/login/gkd-login.c
+++ b/daemon/login/gkd-login.c
@@ -31,11 +31,14 @@
 #include "pkcs11/wrap-layer/gkm-wrap-layer.h"
 
 #include <gck/gck.h>
+#include <gcr/gcr-unlock-options.h>
 
 #include <glib/gi18n.h>
 
 #include <string.h>
 
+EGG_SECURE_DECLARE (gkd_login);
+
 static GList*
 module_instances (void)
 {
@@ -82,6 +85,10 @@ lookup_login_session (GList *modules)
        GckSlot *slot = NULL;
        GError *error = NULL;
        GckSession *session;
+       GList *owned = NULL;
+
+       if (modules == NULL)
+               modules = owned = module_instances ();
 
        slot = gck_modules_token_for_uri (modules, "pkcs11:token=Secret%20Store", &error);
        if (!slot) {
@@ -96,6 +103,7 @@ lookup_login_session (GList *modules)
        }
 
        g_object_unref (slot);
+       g_list_free_full (owned, g_object_unref);
 
        return session;
 }
@@ -415,3 +423,295 @@ gkd_login_change_lock (const gchar *original, const gchar *master)
        gck_list_unref_free (modules);
        return result;
 }
+
+gboolean
+gkd_login_available (GckSession *session)
+{
+       GckBuilder builder = GCK_BUILDER_INIT;
+       gboolean available = FALSE;
+       GError *error = NULL;
+       GList *objects;
+
+       if (!session)
+               session = lookup_login_session (NULL);
+       else
+               g_object_ref (session);
+
+       if (session) {
+               gck_builder_add_ulong (&builder, CKA_CLASS, CKO_G_COLLECTION);
+               gck_builder_add_string (&builder, CKA_ID, "login");
+               gck_builder_add_boolean (&builder, CKA_G_LOCKED, FALSE);
+
+               /* Check if the login keyring is usable */
+               objects = gck_session_find_objects (session, gck_builder_end (&builder), NULL, &error);
+               if (error) {
+                       g_warning ("couldn't lookup login keyring: %s", error->message);
+                       g_clear_error (&error);
+               } else if (objects) {
+                       available = TRUE;
+               }
+               g_list_free_full (objects, g_object_unref);
+       }
+
+       g_object_unref (session);
+       return available;
+}
+
+static GList *
+find_saved_items (GckSession *session,
+                  GckAttributes *attrs)
+{
+       GckBuilder builder = GCK_BUILDER_INIT;
+       GError *error = NULL;
+       const GckAttribute *attr;
+       GckObject *search;
+       GList *results;
+       gpointer data;
+       gsize n_data;
+
+       gck_builder_add_ulong (&builder, CKA_CLASS, CKO_G_SEARCH);
+       gck_builder_add_boolean (&builder, CKA_TOKEN, FALSE);
+
+       attr = gck_attributes_find (attrs, CKA_G_COLLECTION);
+       if (attr != NULL)
+               gck_builder_add_attribute (&builder, attr);
+
+       attr = gck_attributes_find (attrs, CKA_G_FIELDS);
+       g_return_val_if_fail (attr != NULL, NULL);
+       gck_builder_add_attribute (&builder, attr);
+
+       search = gck_session_create_object (session, gck_builder_end (&builder), NULL, &error);
+       if (search == NULL) {
+               g_warning ("couldn't perform search for gpg agent stored passphrases: %s",
+                          egg_error_message (error));
+               g_clear_error (&error);
+               return NULL;
+       }
+
+       data = gck_object_get_data (search, CKA_G_MATCHED, NULL, &n_data, &error);
+       gck_object_destroy (search, NULL, NULL);
+       g_object_unref (search);
+
+       if (data == NULL) {
+               g_warning ("couldn't retrieve list of gpg agent stored passphrases: %s",
+                          egg_error_message (error));
+               g_clear_error (&error);
+               return NULL;
+       }
+
+       results = gck_objects_from_handle_array (session, data, n_data / sizeof (CK_ULONG));
+
+       g_free (data);
+       return results;
+}
+
+static gboolean
+fields_to_attribute (GckBuilder *builder,
+                     const gchar *field,
+                     va_list va)
+{
+       GString *fields = g_string_sized_new (128);
+       const gchar *last = NULL;
+       const gchar *value;
+
+       while (field) {
+               if (g_strcmp0 (last, field) >= 0) {
+                       g_critical ("lookup fields must be sorted '%s' >= '%s'", last, field);
+                       return FALSE;
+               }
+
+               last = field;
+               value = va_arg (va, const gchar *);
+               g_return_val_if_fail (value != NULL, FALSE);
+
+               g_string_append (fields, field);
+               g_string_append_c (fields, '\0');
+               g_string_append (fields, value);
+               g_string_append_c (fields, '\0');
+
+               field = va_arg (va, const gchar *);
+       }
+
+       gck_builder_add_data (builder, CKA_G_FIELDS, (const guchar *)fields->str, fields->len);
+       g_string_free (fields, TRUE);
+       return TRUE;
+}
+
+gchar *
+gkd_login_lookup_password (GckSession *session,
+                           const gchar *field,
+                           ...)
+{
+       GckBuilder builder = GCK_BUILDER_INIT;
+       GckAttributes *attrs;
+       GList *objects, *l;
+       GError *error = NULL;
+       gpointer data = NULL;
+       gsize length;
+       va_list va;
+
+       if (!session)
+               session = lookup_login_session (NULL);
+       else
+               session = g_object_ref (session);
+       if (!session)
+               return NULL;
+
+       gck_builder_add_ulong (&builder, CKA_CLASS, CKO_SECRET_KEY);
+
+       va_start (va, field);
+       if (!fields_to_attribute (&builder, field, va))
+               g_return_val_if_reached (FALSE);
+       va_end (va);
+
+       attrs = gck_attributes_ref_sink (gck_builder_end (&builder));
+       objects = find_saved_items (session, attrs);
+       gck_attributes_unref (attrs);
+
+       /* Return first password */
+       data = NULL;
+       for (l = objects; l; l = g_list_next (l)) {
+               data = gck_object_get_data_full (l->data, CKA_VALUE, egg_secure_realloc, NULL, &length, 
&error);
+               if (error) {
+                       if (!g_error_matches (error, GCK_ERROR, CKR_USER_NOT_LOGGED_IN))
+                               g_warning ("couldn't lookup gpg agent password: %s", egg_error_message 
(error));
+                       g_clear_error (&error);
+                       data = NULL;
+               } else {
+                       break;
+               }
+       }
+
+       g_list_free_full (objects, g_object_unref);
+       g_object_unref (session);
+
+       /* Data is null terminated */
+       return data;
+}
+
+void
+gkd_login_clear_password (GckSession *session,
+                          const gchar *field,
+                          ...)
+{
+       GckBuilder builder = GCK_BUILDER_INIT;
+       GckAttributes *attrs;
+       GList *objects, *l;
+       GError *error = NULL;
+       va_list va;
+
+       if (!session)
+               session = lookup_login_session (NULL);
+       else
+               session = g_object_ref (session);
+       if (!session)
+               return;
+
+       gck_builder_add_ulong (&builder, CKA_CLASS, CKO_SECRET_KEY);
+
+       va_start (va, field);
+       if (!fields_to_attribute (&builder, field, va))
+               g_return_val_if_reached (FALSE);
+       va_end (va);
+
+       attrs = gck_attributes_ref_sink (gck_builder_end (&builder));
+       objects = find_saved_items (session, attrs);
+       gck_attributes_unref (attrs);
+
+       /* Delete first item */
+       for (l = objects; l; l = g_list_next (l)) {
+               if (gck_object_destroy (l->data, NULL, &error))
+                       break; /* Only delete the first item */
+               g_warning ("couldn't clear assword: %s", error->message);
+               g_clear_error (&error);
+       }
+
+       g_list_free_full (objects, g_object_unref);
+       g_object_unref (session);
+}
+
+gboolean
+gkd_login_store_password (GckSession *session,
+                          const gchar *password,
+                          const gchar *label,
+                          const gchar *method,
+                          gint lifetime,
+                          const gchar *field,
+                          ...)
+{
+       GckBuilder builder = GCK_BUILDER_INIT;
+       GckAttributes *attrs;
+       guchar *identifier;
+       GList *previous;
+       gboolean ret = FALSE;
+       GckObject *item;
+       GError *error = NULL;
+       gsize length;
+       va_list va;
+
+       if (!method)
+               method = GCR_UNLOCK_OPTION_SESSION;
+
+       if (!session)
+               session = lookup_login_session (NULL);
+       else
+               session = g_object_ref (session);
+       if (!session)
+               return FALSE;
+
+       va_start (va, field);
+       if (!fields_to_attribute (&builder, field, va))
+               g_return_val_if_reached (FALSE);
+       va_end (va);
+
+       if (g_str_equal (method, GCR_UNLOCK_OPTION_ALWAYS)) {
+               gck_builder_add_string (&builder, CKA_G_COLLECTION, "login");
+
+       } else {
+               if (g_str_equal (method, GCR_UNLOCK_OPTION_IDLE)) {
+                       gck_builder_add_boolean (&builder, CKA_GNOME_TRANSIENT, TRUE);
+                       gck_builder_add_ulong (&builder, CKA_G_DESTRUCT_IDLE, lifetime);
+
+               } else if (g_str_equal (method, GCR_UNLOCK_OPTION_TIMEOUT)) {
+                       gck_builder_add_boolean (&builder, CKA_GNOME_TRANSIENT, TRUE);
+                       gck_builder_add_ulong (&builder, CKA_G_DESTRUCT_AFTER, lifetime);
+
+               } else if (!g_str_equal (method, GCR_UNLOCK_OPTION_SESSION)) {
+                       g_message ("Unsupported gpg-cache-method setting: %s", method);
+               }
+               gck_builder_add_string (&builder, CKA_G_COLLECTION, "session");
+       }
+
+       gck_builder_add_boolean (&builder, CKA_TOKEN, TRUE);
+       gck_builder_add_ulong (&builder, CKA_CLASS, CKO_SECRET_KEY);
+
+       /* Find a previously stored object like this, and replace if so */
+       attrs = gck_attributes_ref_sink (gck_builder_end (&builder));
+       previous = find_saved_items (session, attrs);
+       if (previous) {
+               identifier = gck_object_get_data (previous->data, CKA_ID, NULL, &length, NULL);
+               if (identifier != NULL)
+                       gck_builder_add_data (&builder, CKA_ID, identifier, length);
+               g_free (identifier);
+               g_list_free_full (previous, g_object_unref);
+       }
+
+       /* Put in the remainder of the attributes */
+       gck_builder_add_all (&builder, attrs);
+       gck_builder_add_string (&builder, CKA_VALUE, password);
+       gck_builder_add_string (&builder, CKA_LABEL, label);
+       gck_attributes_unref (attrs);
+
+       item = gck_session_create_object (session, gck_builder_end (&builder), NULL, &error);
+       if (item == NULL) {
+               g_warning ("couldn't store gpg agent password: %s", egg_error_message (error));
+               g_clear_error (&error);
+               ret = FALSE;
+       } else {
+               g_object_unref (item);
+               ret = TRUE;
+       }
+
+       g_object_unref (session);
+       return ret;
+}
diff --git a/daemon/login/gkd-login.h b/daemon/login/gkd-login.h
index 0599275..5e21982 100644
--- a/daemon/login/gkd-login.h
+++ b/daemon/login/gkd-login.h
@@ -23,9 +23,29 @@
 
 #include <glib.h>
 
+typedef struct _GckSession GckSession;
+
 gboolean          gkd_login_unlock                   (const gchar *master);
 
 gboolean          gkd_login_change_lock              (const gchar *original,
                                                       const gchar *master);
 
+gboolean          gkd_login_available                (GckSession *session);
+
+gchar *           gkd_login_lookup_password          (GckSession *session,
+                                                     const gchar *field,
+                                                     ...) G_GNUC_NULL_TERMINATED;
+
+void              gkd_login_clear_password           (GckSession *session,
+                                                     const gchar *field,
+                                                     ...) G_GNUC_NULL_TERMINATED;
+
+gboolean          gkd_login_store_password           (GckSession *session,
+                                                     const gchar *password,
+                                                     const gchar *label,
+                                                     const gchar *method,
+                                                     gint lifetime,
+                                                     const gchar *field,
+                                                     ...) G_GNUC_NULL_TERMINATED;
+
 #endif /* __GKD_LOGIN_H__ */


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