summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStef Walter <stefw@redhat.com>2014-10-26 07:37:28 +0100
committerStef Walter <stefw@redhat.com>2014-11-08 20:10:36 +0100
commit354f9887eef1c2c6144b904739ca0230bc2f2a4e (patch)
tree5a247efb91fb1f6b20e89c42a690df15d06b233d
parentd9df0a179fbfae82f8cec20cf46e8b9f03c044ba (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.am1
-rw-r--r--daemon/login/gkd-login.c300
-rw-r--r--daemon/login/gkd-login.h20
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__ */