diff options
author | Stef Walter <stefw@redhat.com> | 2014-10-26 07:37:28 +0100 |
---|---|---|
committer | Stef Walter <stefw@redhat.com> | 2014-11-08 20:10:36 +0100 |
commit | 354f9887eef1c2c6144b904739ca0230bc2f2a4e (patch) | |
tree | 5a247efb91fb1f6b20e89c42a690df15d06b233d | |
parent | d9df0a179fbfae82f8cec20cf46e8b9f03c044ba (diff) |
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
-rw-r--r-- | daemon/login/Makefile.am | 1 | ||||
-rw-r--r-- | daemon/login/gkd-login.c | 300 | ||||
-rw-r--r-- | 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 609be5bb..2b6d3893 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 2c0cb20c..a5d1a907 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 05992758..5e21982f 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__ */ |