[gnome-keyring/dbus-api] [dbus] Implement getting and setting of secrets.



commit f3e360f119c6361940d2e4aecbd93395973688c1
Author: Stef Walter <stef memberwebs com>
Date:   Wed Nov 25 01:48:08 2009 +0000

    [dbus] Implement getting and setting of secrets.
    
    This is not very well tested, but all basically speced out
    and implemented. We use wrapping and unwrapping to transfer
    secrets via the aes algorithm, or simple get/set attributes
    when transferring via plaintext.

 daemon/dbus/Makefile.am               |    1 +
 daemon/dbus/gkd-secret-objects.c      |   88 +++++++-
 daemon/dbus/gkd-secret-secret.c       |  122 ++++++++++
 daemon/dbus/gkd-secret-secret.h       |   54 +++++
 daemon/dbus/gkd-secret-service.c      |   16 ++
 daemon/dbus/gkd-secret-service.h      |    6 +-
 daemon/dbus/gkd-secret-session.c      |  407 +++++++++++++++++++++++++++++++-
 daemon/dbus/gkd-secret-session.h      |    9 +
 daemon/dbus/gkd-secret-types.h        |    1 +
 daemon/dbus/gkd-secret-util.c         |   24 ++
 daemon/dbus/gkd-secret-util.h         |    2 +
 pkcs11/secret-store/gck-secret-item.c |   13 +
 12 files changed, 726 insertions(+), 17 deletions(-)
---
diff --git a/daemon/dbus/Makefile.am b/daemon/dbus/Makefile.am
index f6116e7..fc81765 100644
--- a/daemon/dbus/Makefile.am
+++ b/daemon/dbus/Makefile.am
@@ -22,6 +22,7 @@ libgkr_dbus_la_SOURCES = \
 	gkd-secret-objects.c gkd-secret-objects.h \
 	gkd-secret-property.c gkd-secret-property.h \
 	gkd-secret-prompt.c gkd-secret-prompt.h \
+	gkd-secret-secret.c gkd-secret-secret.h \
 	gkd-secret-service.c gkd-secret-service.h \
 	gkd-secret-session.c gkd-secret-session.h \
 	gkd-secret-types.h \
diff --git a/daemon/dbus/gkd-secret-objects.c b/daemon/dbus/gkd-secret-objects.c
index c143826..c8582ee 100644
--- a/daemon/dbus/gkd-secret-objects.c
+++ b/daemon/dbus/gkd-secret-objects.c
@@ -23,9 +23,11 @@
 
 #include "gkd-dbus-util.h"
 
-#include "gkd-secret-service.h"
 #include "gkd-secret-objects.h"
 #include "gkd-secret-property.h"
+#include "gkd-secret-secret.h"
+#include "gkd-secret-service.h"
+#include "gkd-secret-session.h"
 #include "gkd-secret-types.h"
 #include "gkd-secret-util.h"
 
@@ -282,12 +284,96 @@ item_method_delete (GkdSecretObjects *self, GP11Object *object, DBusMessage *mes
 }
 
 static DBusMessage*
+item_method_get_secret (GkdSecretObjects *self, GP11Object *item, DBusMessage *message)
+{
+	DBusError derr = DBUS_ERROR_INIT;
+	GkdSecretSession *session;
+	GkdSecretSecret *secret;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	const char *caller;
+	const char *path;
+
+	if (!dbus_message_get_args (message, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID))
+		return NULL;
+
+	caller = dbus_message_get_sender (message);
+	g_return_val_if_fail (caller, NULL);
+
+	session = gkd_secret_service_lookup_session (self->service, path, caller);
+	if (session == NULL)
+		return dbus_message_new_error_printf (message, SECRET_ERROR_NO_SESSION,
+		                                      "No such session exists");
+
+	secret = gkd_secret_session_get_item_secret (session, item, &derr);
+	if (secret == NULL) {
+		reply = dbus_message_new_error (message, derr.name, derr.message);
+		dbus_error_free (&derr);
+		return reply;
+	}
+
+	reply = dbus_message_new_method_return (message);
+	dbus_message_iter_init_append (reply, &iter);
+	gkd_secret_secret_append (secret, &iter);
+	gkd_secret_secret_free (secret);
+	return reply;
+}
+
+static DBusMessage*
+item_method_set_secret (GkdSecretObjects *self, GP11Object *item, DBusMessage *message)
+{
+	DBusError derr = DBUS_ERROR_INIT;
+	DBusMessageIter iter;
+	DBusMessage *reply;
+	GkdSecretSecret *secret;
+	GkdSecretSession *session;
+	const char *caller;
+
+	if (!dbus_message_has_signature (message, GKD_SECRET_SECRET_SIG))
+		return NULL;
+	dbus_message_iter_init (message, &iter);
+	secret = gkd_secret_secret_parse (&iter);
+	if (secret == NULL)
+		return dbus_message_new_error (message, DBUS_ERROR_INVALID_ARGS,
+		                               "The secret is invalid");
+
+	caller = dbus_message_get_sender (message);
+	g_return_val_if_fail (caller, NULL);
+
+	session = gkd_secret_service_lookup_session (self->service, secret->path, caller);
+	if (session == NULL) {
+		gkd_secret_secret_free (secret);
+		return dbus_message_new_error_printf (message, SECRET_ERROR_NO_SESSION,
+		                                      "No such session exists");
+	}
+
+	gkd_secret_session_set_item_secret (session, item, secret, &derr);
+	gkd_secret_secret_free (secret);
+
+	if (dbus_error_is_set (&derr)) {
+		reply = dbus_message_new_error (message, derr.name, derr.message);
+		dbus_error_free (&derr);
+		return reply;
+	}
+
+	return dbus_message_new_method_return (message);
+}
+
+static DBusMessage*
 item_message_handler (GkdSecretObjects *self, GP11Object *object, DBusMessage *message)
 {
 	/* org.freedesktop.Secrets.Item.Delete() */
 	if (dbus_message_is_method_call (message, SECRET_ITEM_INTERFACE, "Delete"))
 		return item_method_delete (self, object, message);
 
+	/* org.freedesktop.Secrets.Session.GetSecret() */
+	else if (dbus_message_is_method_call (message, SECRET_SERVICE_INTERFACE, "GetSecret"))
+		return item_method_get_secret (self, object, message);
+
+	/* org.freedesktop.Secrets.Session.SetSecret() */
+	else if (dbus_message_is_method_call (message, SECRET_SERVICE_INTERFACE, "SetSecret"))
+		return item_method_set_secret (self, object, message);
+
 	/* org.freedesktop.DBus.Properties.Get */
 	if (dbus_message_is_method_call (message, PROPERTIES_INTERFACE, "Get"))
 		return item_property_get (object, message);
diff --git a/daemon/dbus/gkd-secret-secret.c b/daemon/dbus/gkd-secret-secret.c
new file mode 100644
index 0000000..7ac8385
--- /dev/null
+++ b/daemon/dbus/gkd-secret-secret.c
@@ -0,0 +1,122 @@
+/*
+ * 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 Public 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 Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gkd-secret-secret.h"
+
+#include "egg/egg-secure-memory.h"
+
+#include <string.h>
+
+GkdSecretSecret*
+gkd_secret_secret_create_and_take_memory (const gchar *path, gpointer parameter,
+                                          gsize n_parameter, gpointer value,
+                                          gsize n_value)
+{
+	GkdSecretSecret *secret;
+
+	secret = g_slice_new0 (GkdSecretSecret);
+	secret->path = g_strdup (path);
+	secret->parameter = parameter;
+	secret->n_parameter = n_parameter;
+	secret->value = value;
+	secret->n_value = n_value;
+	return secret;
+}
+
+GkdSecretSecret*
+gkd_secret_secret_parse (DBusMessageIter *iter)
+{
+	DBusMessageIter struc;
+	const void *parameter, *value;
+	int n_value, n_parameter;
+	const char *path;
+
+	g_return_val_if_fail (dbus_message_iter_get_arg_type (iter) == DBUS_TYPE_STRUCT, NULL);
+	dbus_message_iter_recurse (iter, &struc);
+
+	/* Get the path */
+	if (dbus_message_iter_get_arg_type (&struc) != DBUS_TYPE_OBJECT_PATH)
+		return NULL;
+	dbus_message_iter_get_basic (&struc, &path);
+
+	/* Get the parameter */
+	if (!dbus_message_iter_next (&struc) ||
+	    dbus_message_iter_get_arg_type (&struc) != DBUS_TYPE_ARRAY ||
+	    dbus_message_iter_get_element_type(&struc) != DBUS_TYPE_BYTE)
+		return NULL;
+	dbus_message_iter_get_fixed_array (&struc, &parameter, &n_parameter);
+
+	/* Get the value */
+	if (!dbus_message_iter_next (&struc) ||
+	    dbus_message_iter_get_arg_type (&struc) != DBUS_TYPE_ARRAY ||
+	    dbus_message_iter_get_element_type(&struc) != DBUS_TYPE_BYTE)
+		return NULL;
+	dbus_message_iter_get_fixed_array (&struc, &value, &n_value);
+
+	return gkd_secret_secret_create_and_take_memory (path,
+	                                                 n_parameter ? g_memdup (parameter, n_parameter) : NULL,
+	                                                 n_parameter,
+	                                                 n_value ? g_memdup (value, n_value) : NULL,
+	                                                 n_value);
+}
+
+void
+gkd_secret_secret_append (GkdSecretSecret *secret, DBusMessageIter *iter)
+{
+	DBusMessageIter struc;
+	int length;
+
+	dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, "oayay", &struc);
+	dbus_message_iter_append_basic (iter, DBUS_TYPE_OBJECT_PATH, &(secret->path));
+	length = secret->n_parameter;
+	dbus_message_iter_append_fixed_array (iter, DBUS_TYPE_BYTE, &(secret->parameter), length);
+	length = secret->n_value;
+	dbus_message_iter_append_fixed_array (iter, DBUS_TYPE_BYTE, &(secret->value), length);
+	dbus_message_iter_close_container (iter, &struc);
+}
+
+void
+gkd_secret_secret_free (gpointer data)
+{
+	GkdSecretSecret *secret;
+
+	if (!data)
+		return;
+
+	/*
+	 * These are not usually actual plain text secrets. However in
+	 * the case that they are, we want to clear them from memory.
+	 *
+	 * This is not foolproof in any way. If they're plaintext, they would
+	 * have been sent over DBus, and through all sorts of processes.
+	 */
+
+	secret = data;
+	g_free (secret->path);
+	egg_secure_clear (secret->parameter, secret->n_parameter);
+	g_free (secret->parameter);
+	egg_secure_clear (secret->value, secret->n_value);
+	g_free (secret->value);
+	g_slice_free (GkdSecretSecret, secret);
+
+}
diff --git a/daemon/dbus/gkd-secret-secret.h b/daemon/dbus/gkd-secret-secret.h
new file mode 100644
index 0000000..79806fa
--- /dev/null
+++ b/daemon/dbus/gkd-secret-secret.h
@@ -0,0 +1,54 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2009 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public 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 Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GKD_SECRET_SECRET_H__
+#define __GKD_SECRET_SECRET_H__
+
+#include "gkd-secret-types.h"
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+
+#define GKD_SECRET_SECRET_SIG "{sayay}"
+
+struct _GkdSecretSecret {
+	gchar *path;
+	gpointer parameter;
+	gsize n_parameter;
+	gpointer value;
+	gsize n_value;
+};
+
+GkdSecretSecret*       gkd_secret_secret_create_and_take_memory   (const gchar *path,
+                                                                   gpointer parameter,
+                                                                   gsize n_parameter,
+                                                                   gpointer value,
+                                                                   gsize n_value);
+
+GkdSecretSecret*       gkd_secret_secret_parse                    (DBusMessageIter *iter);
+
+void                   gkd_secret_secret_append                   (GkdSecretSecret *secret,
+                                                                   DBusMessageIter *iter);
+
+void                   gkd_secret_secret_free                     (gpointer data);
+
+#endif /* __GKD_SECRET_PROPERTY_H__ */
diff --git a/daemon/dbus/gkd-secret-service.c b/daemon/dbus/gkd-secret-service.c
index f1e81ce..b6c2f3a 100644
--- a/daemon/dbus/gkd-secret-service.c
+++ b/daemon/dbus/gkd-secret-service.c
@@ -892,6 +892,22 @@ gkd_secret_service_get_pkcs11_session (GkdSecretService *self, const gchar *call
 	return client->pkcs11_session;
 }
 
+GkdSecretSession*
+gkd_secret_service_lookup_session (GkdSecretService *self, const gchar *path,
+                                   const gchar *caller)
+{
+	ServiceClient *client;
+
+	g_return_val_if_fail (GKD_SECRET_IS_SERVICE (self), NULL);
+	g_return_val_if_fail (path, NULL);
+	g_return_val_if_fail (caller, NULL);
+
+	client = g_hash_table_lookup (self->clients, caller);
+	g_return_val_if_fail (client, NULL);
+
+	return g_hash_table_lookup (client->sessions, path);
+}
+
 void
 gkd_secret_service_close_session (GkdSecretService *self, GkdSecretSession *session)
 {
diff --git a/daemon/dbus/gkd-secret-service.h b/daemon/dbus/gkd-secret-service.h
index edd67ff..8652b02 100644
--- a/daemon/dbus/gkd-secret-service.h
+++ b/daemon/dbus/gkd-secret-service.h
@@ -59,9 +59,9 @@ GP11Session*            gkd_secret_service_get_pkcs11_session      (GkdSecretSer
 
 GkdSecretObjects*       gkd_secret_service_get_objects             (GkdSecretService *self);
 
-#if 0
-void                    gkd_secret_service_refresh                 (GkdSecretService *self);
-#endif
+GkdSecretSession*       gkd_secret_service_lookup_session          (GkdSecretService *self,
+                                                                    const gchar *path,
+                                                                    const gchar *caller);
 
 void                    gkd_secret_service_close_session           (GkdSecretService *self,
                                                                     GkdSecretSession *sess);
diff --git a/daemon/dbus/gkd-secret-session.c b/daemon/dbus/gkd-secret-session.c
index 127be61..95b7eb6 100644
--- a/daemon/dbus/gkd-secret-session.c
+++ b/daemon/dbus/gkd-secret-session.c
@@ -21,11 +21,17 @@
 
 #include "config.h"
 
+#include "gkd-secret-secret.h"
 #include "gkd-secret-service.h"
 #include "gkd-secret-session.h"
 #include "gkd-secret-types.h"
+#include "gkd-secret-util.h"
 #include "gkd-dbus-util.h"
 
+#include "egg/egg-dh.h"
+
+#include "pkcs11/pkcs11i.h"
+
 #include <string.h>
 
 enum {
@@ -42,6 +48,7 @@ struct _GkdSecretSession {
 	GkdSecretService *service;
 	gchar *caller_exec;
 	gchar *caller;
+	GP11Object *key;
 };
 
 G_DEFINE_TYPE (GkdSecretSession, gkd_secret_session, G_TYPE_OBJECT);
@@ -52,6 +59,322 @@ static guint unique_session_number = 0;
  * INTERNAL
  */
 
+static void
+take_session_key (GkdSecretSession *self, GP11Object *key)
+{
+	if (self->key) {
+		gp11_object_destroy (self->key, NULL);
+		g_object_unref (self->key);
+		self->key = NULL;
+	}
+
+	self->key = key;
+}
+
+static gboolean
+aes_create_dh_keys (GP11Session *session, const gchar *group,
+                    GP11Object **pub_key, GP11Object **priv_key)
+{
+	GP11Attributes *attrs;
+	gconstpointer prime, base;
+	gsize n_prime, n_base;
+	GP11Mechanism *mech;
+	GError *error = NULL;
+	gboolean ret;
+
+	if (!egg_dh_default_params_raw ("ietf-ike-grp-modp-1024", &prime,
+	                                &n_prime, &base, &n_base)) {
+		g_warning ("couldn't load dh parameter group: %s", group);
+		return FALSE;
+	}
+
+	attrs = gp11_attributes_new ();
+	gp11_attributes_add_data (attrs, CKA_PRIME, prime, n_prime);
+	gp11_attributes_add_data (attrs, CKA_BASE, base, n_base);
+
+	mech = gp11_mechanism_new (CKM_DH_PKCS_KEY_PAIR_GEN);
+
+	/* Perform the DH key generation */
+	ret = gp11_session_generate_key_pair_full (session, mech, attrs, attrs,
+	                                           pub_key, priv_key, NULL, &error);
+
+	gp11_mechanism_unref (mech);
+	gp11_attributes_unref (attrs);
+
+	if (ret == FALSE) {
+		g_warning ("couldn't generate dh key pair: %s", error->message);
+		g_clear_error (&error);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+aes_derive_key (GP11Session *session, GP11Object *priv_key,
+                gconstpointer input, gsize n_input, GP11Object **aes_key)
+{
+	GError *error = NULL;
+	GP11Mechanism *mech;
+	GP11Attributes *attrs;
+
+	mech = gp11_mechanism_new_with_param (CKM_DH_PKCS_DERIVE, input, n_input);
+	attrs = gp11_attributes_newv (CKA_VALUE_LEN, GP11_ULONG, 16UL, GP11_INVALID);
+
+	*aes_key = gp11_session_derive_key_full (session, priv_key, mech, attrs, NULL, &error);
+
+	gp11_mechanism_unref (mech);
+	gp11_attributes_unref (attrs);
+
+	if (!*aes_key) {
+		g_warning ("couldn't derive aes key from dh key pair: %s", error->message);
+		g_clear_error (&error);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static DBusMessage*
+aes_negotiate (GkdSecretSession *self, DBusMessage *message, gconstpointer input, gsize n_input)
+{
+	DBusMessageIter iter, variant;
+	GP11Session *session;
+	GP11Object *pub, *priv, *key;
+	gboolean complete = TRUE;
+	GError *error = NULL;
+	DBusMessage *reply;
+	gpointer output;
+	gsize n_output;
+	gboolean ret;
+
+	session = gkd_secret_service_get_pkcs11_session (self->service, self->caller);
+	g_return_val_if_fail (session, NULL);
+
+	if (!aes_create_dh_keys (session, "ietf-ike-grp-modp-1024", &pub, &priv))
+		return dbus_message_new_error_printf (message, DBUS_ERROR_FAILED,
+		                                       "Failed to create necessary crypto keys.");
+
+	/* Get the output data */
+	gp11_object_set_session (pub, session);
+	output = gp11_object_get_data (pub, CKA_VALUE, &n_output, &error);
+	gp11_object_destroy (pub, NULL);
+	g_object_unref (pub);
+
+	if (output == NULL) {
+		g_warning ("couldn't get public key DH value: %s", error->message);
+		g_clear_error (&error);
+		g_object_unref (priv);
+		return dbus_message_new_error_printf (message, DBUS_ERROR_FAILED,
+		                                      "Failed to retrieve necessary crypto keys.");
+	}
+
+	ret = aes_derive_key (session, priv, input, n_input, &key);
+
+	gp11_object_destroy (priv, NULL);
+	g_object_unref (priv);
+
+	if (ret == FALSE) {
+		g_free (output);
+		return dbus_message_new_error_printf (message, DBUS_ERROR_FAILED,
+		                                       "Failed to create necessary crypto key.");
+	}
+
+	gp11_object_set_session (key, session);
+	take_session_key (self, key);
+
+	reply = dbus_message_new_method_return (message);
+	dbus_message_iter_init_append (message, &iter);
+	dbus_message_iter_open_container (&iter, DBUS_TYPE_VARIANT, "ay", &variant);
+	dbus_message_iter_append_fixed_array (&variant, DBUS_TYPE_BYTE, &output, n_output);
+	dbus_message_iter_close_container (&iter, &variant);
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &complete);
+
+	g_free (output);
+	return reply;
+}
+
+static GkdSecretSecret*
+aes_wrap_item_secret (GkdSecretSession *self, GP11Object *item, DBusError *derr)
+{
+	GP11Mechanism *mech;
+	GP11Session *session;
+	gpointer value, iv;
+	gsize n_value, n_iv;
+	GError *error = NULL;
+
+	g_assert (GP11_IS_OBJECT (self->key));
+
+	session = gp11_object_get_session (item);
+	g_return_val_if_fail (session, FALSE);
+
+	n_iv = 16;
+	iv = g_malloc (n_iv);
+	gcry_create_nonce (iv, n_iv);
+
+	mech = gp11_mechanism_new_with_param (CKM_AES_CBC_PAD, iv, n_iv);
+
+	value = gp11_session_wrap_key_full (session, self->key, mech, item, &n_value,
+	                                    NULL, &error);
+
+	gp11_mechanism_unref (mech);
+
+	if (value == NULL) {
+		if (error->code == CKR_USER_NOT_LOGGED_IN) {
+			dbus_set_error_const (derr, SECRET_ERROR_IS_LOCKED,
+			                      "Cannot get secret of a locked object");
+		} else {
+			g_message ("couldn't wrap item secret: %s", error->message);
+			dbus_set_error_const (derr, DBUS_ERROR_FAILED,
+			                      "Couldn't get item secret");
+		}
+		g_clear_error (&error);
+		g_free (iv);
+		return NULL;
+	}
+
+	return gkd_secret_secret_create_and_take_memory (self->object_path, iv, n_iv, value, n_value);
+}
+
+static gboolean
+aes_unwrap_item_secret (GkdSecretSession *self, GP11Object *item,
+                        GkdSecretSecret *secret, DBusError *derr)
+{
+	GP11Mechanism *mech;
+	GP11Object *object;
+	GP11Session *session;
+	GError *error = NULL;
+	GP11Attributes *attrs;
+
+	g_assert (GP11_IS_OBJECT (self->key));
+
+	session = gp11_object_get_session (item);
+	g_return_val_if_fail (session, FALSE);
+
+	/*
+	 * By getting these attributes, and then using them in the unwrap,
+	 * the unwrap won't generate a new object, but merely set the secret.
+	 */
+
+	attrs = gkd_secret_util_attributes_for_item (item);
+	g_return_val_if_fail (attrs, FALSE);
+
+	mech = gp11_mechanism_new_with_param (CKM_AES_CBC_PAD, secret->parameter,
+	                                      secret->n_parameter);
+
+	object = gp11_session_unwrap_key_full (session, self->key, mech, secret->value,
+	                                       secret->n_value, attrs, NULL, &error);
+
+	gp11_mechanism_unref (mech);
+	gp11_attributes_unref (attrs);
+
+	if (object == NULL) {
+		if (error->code == CKR_USER_NOT_LOGGED_IN) {
+			dbus_set_error_const (derr, SECRET_ERROR_IS_LOCKED,
+			                      "Cannot set secret of a locked item");
+		} else if (error->code == CKR_WRAPPED_KEY_INVALID ||
+		           error->code == CKR_WRAPPED_KEY_LEN_RANGE ||
+		           error->code == CKR_MECHANISM_PARAM_INVALID) {
+			dbus_set_error_const (derr, DBUS_ERROR_INVALID_ARGS,
+			                      "The secret was transferred or encrypted in an invalid way.");
+		} else {
+			g_message ("couldn't unwrap item secret: %s", error->message);
+			dbus_set_error_const (derr, DBUS_ERROR_FAILED, "Couldn't set item secret");
+		}
+		g_clear_error (&error);
+		return FALSE;
+	}
+
+	if (!gp11_object_equal (object, item)) {
+		g_warning ("unwrapped secret went to new object, instead of item");
+		dbus_set_error_const (derr, DBUS_ERROR_FAILED, "Couldn't set item secret");
+		g_object_unref (object);
+		return FALSE;
+	}
+
+	g_object_unref (object);
+	return TRUE;
+}
+
+
+static DBusMessage*
+plain_negotiate (GkdSecretSession *self, DBusMessage *message)
+{
+	DBusMessageIter iter, variant;
+	const char *output = "";
+	dbus_bool_t complete = TRUE;
+	DBusMessage *reply;
+
+	take_session_key (self, NULL);
+
+	reply = dbus_message_new_method_return (message);
+	dbus_message_iter_init_append (message, &iter);
+	dbus_message_iter_open_container (&iter, DBUS_TYPE_VARIANT, "s", &variant);
+	dbus_message_iter_append_basic (&variant, DBUS_TYPE_STRING, &output);
+	dbus_message_iter_close_container (&iter, &variant);
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &complete);
+	return reply;
+}
+
+static GkdSecretSecret*
+plain_get_item_secret (GkdSecretSession *self, GP11Object *item, DBusError *derr)
+{
+	GError *error = NULL;
+	gpointer value;
+	gsize n_value;
+
+	/*
+	 * We're going to be sending this over DBus, which is going to litter it
+	 * all over memory. So we don't need to worry about using 'secure' memory.
+	 */
+
+	value = gp11_object_get_data (item, CKA_VALUE, &n_value, &error);
+	if (value == NULL) {
+		if (error->code == CKR_USER_NOT_LOGGED_IN) {
+			dbus_set_error_const (derr, SECRET_ERROR_IS_LOCKED,
+			                      "Cannot get secret of a locked object");
+		} else {
+			g_message ("couldn't get item secret: %s", error->message);
+			dbus_set_error_const (derr, DBUS_ERROR_FAILED,
+			                      "Couldn't get item secret");
+		}
+		g_clear_error (&error);
+		return NULL;
+	}
+
+	return gkd_secret_secret_create_and_take_memory (self->object_path, NULL, 0, value, n_value);
+}
+
+static gboolean
+plain_set_item_secret (GkdSecretSession *self, GP11Object *item,
+                       GkdSecretSecret *secret, DBusError *derr)
+{
+	GP11Attributes *attrs;
+	GError *error = NULL;
+	gboolean ret;
+
+	attrs = gp11_attributes_new ();
+	gp11_attributes_add_data (attrs, CKA_VALUE, secret->value, secret->n_value);
+
+	ret = gp11_object_set_full (item, attrs, NULL, &error);
+
+	gp11_attributes_unref (attrs);
+
+	if (ret == FALSE) {
+		if (error->code == CKR_USER_NOT_LOGGED_IN) {
+			dbus_set_error_const (derr, SECRET_ERROR_IS_LOCKED,
+			                      "Cannot set secret of a locked item");
+		} else {
+			g_message ("couldn't set item secret: %s", error->message);
+			dbus_set_error_const (derr, DBUS_ERROR_FAILED, "Couldn't set item secret");
+		}
+		g_clear_error (&error);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
 /* -----------------------------------------------------------------------------
  * DBUS
  */
@@ -74,6 +397,46 @@ session_method_close (GkdSecretSession *self, DBusMessage *message)
 }
 
 static DBusMessage*
+session_method_negotiate (GkdSecretSession *self, DBusMessage *message)
+{
+	DBusMessage *reply;
+	const char *algorithm;
+	DBusMessageIter iter, variant;
+	gconstpointer input;
+	int n_input;
+
+	/* Parse the incoming message */
+	if (!dbus_message_has_signature (message, "sv"))
+		return NULL;
+	if (!dbus_message_iter_init (message, &iter))
+		g_return_val_if_reached (NULL);
+	dbus_message_iter_get_basic (&iter, &algorithm);
+	g_return_val_if_fail (algorithm, NULL);
+	if (!dbus_message_iter_next (&iter))
+		g_return_val_if_reached (NULL);
+	dbus_message_iter_recurse (&iter, &variant);
+
+	/* Plain transfers? just remove our session key */
+	if (g_str_equal (algorithm, "plain")) {
+		if (!g_str_equal ("s", dbus_message_iter_get_signature (&variant)))
+			return NULL; /* TODO: Return bad parameter? */
+		reply = plain_negotiate (self, message);
+
+	} else if (g_str_equal (algorithm, "dh-ietf1024-aes128-cbc-pkcs7")) {
+		if (!g_str_equal ("ay", dbus_message_iter_get_signature (&variant)))
+			return NULL; /* TODO: Return bad paramater? */
+		dbus_message_iter_get_fixed_array (&variant, &input, &n_input);
+		reply = aes_negotiate (self, message, input, n_input);
+
+	} else {
+		reply = dbus_message_new_error_printf (message, SECRET_ERROR_NOT_SUPPORTED,
+		                                       "The algorithm '%s' is not supported", algorithm);
+	}
+
+	return reply;
+}
+
+static DBusMessage*
 session_property_handler (GkdSecretSession *self, DBusMessage *message)
 {
 	g_return_val_if_reached (NULL); /* TODO: Need to implement */
@@ -135,6 +498,8 @@ gkd_secret_session_dispose (GObject *obj)
 		self->service = NULL;
 	}
 
+	take_session_key (self, NULL);
+
 	G_OBJECT_CLASS (gkd_secret_session_parent_class)->dispose (obj);
 }
 
@@ -145,6 +510,7 @@ gkd_secret_session_finalize (GObject *obj)
 
 	g_assert (!self->object_path);
 	g_assert (!self->service);
+	g_assert (!self->key);
 
 	g_free (self->caller_exec);
 	self->caller_exec = NULL;
@@ -264,24 +630,12 @@ gkd_secret_session_dispatch (GkdSecretSession *self, DBusMessage *message)
 
 	/* org.freedesktop.Secrets.Session.Negotiate() */
 	else if (dbus_message_is_method_call (message, SECRET_SERVICE_INTERFACE, "Negotiate"))
-		g_return_val_if_reached (NULL); /* TODO: Need to implement */
-
-	/* org.freedesktop.Secrets.Session.GetSecret() */
-	else if (dbus_message_is_method_call (message, SECRET_SERVICE_INTERFACE, "GetSecret"))
-		g_return_val_if_reached (NULL); /* TODO: Need to implement */
-
-	/* org.freedesktop.Secrets.Session.SetSecret() */
-	else if (dbus_message_is_method_call (message, SECRET_SERVICE_INTERFACE, "SetSecret"))
-		g_return_val_if_reached (NULL); /* TODO: Need to implement */
+		reply = session_method_negotiate (self, message);
 
 	/* org.freedesktop.Secrets.Session.GetSecrets() */
 	else if (dbus_message_is_method_call (message, SECRET_SERVICE_INTERFACE, "GetSecrets"))
 		g_return_val_if_reached (NULL); /* TODO: Need to implement */
 
-	/* org.freedesktop.Secrets.Session.GetSecret() */
-	else if (dbus_message_is_method_call (message, SECRET_SERVICE_INTERFACE, "GetSecret"))
-		g_return_val_if_reached (NULL); /* TODO: Need to implement */
-
 	else if (dbus_message_has_interface (message, DBUS_INTERFACE_INTROSPECTABLE))
 		return gkd_dbus_introspect_handle (message, "session");
 
@@ -308,3 +662,30 @@ gkd_secret_session_get_object_path (GkdSecretSession *self)
 	g_return_val_if_fail (GKD_SECRET_IS_SESSION (self), NULL);
 	return self->object_path;
 }
+
+GkdSecretSecret*
+gkd_secret_session_get_item_secret (GkdSecretSession *self, GP11Object *item,
+                                    DBusError *derr)
+{
+	g_return_val_if_fail (GKD_SECRET_IS_SESSION (self), NULL);
+	g_return_val_if_fail (GP11_IS_OBJECT (item), NULL);
+
+	if (self->key == NULL)
+		return plain_get_item_secret (self, item, derr);
+	else
+		return aes_wrap_item_secret (self, item, derr);
+}
+
+gboolean
+gkd_secret_session_set_item_secret (GkdSecretSession *self, GP11Object *item,
+                                    GkdSecretSecret *secret, DBusError *derr)
+{
+	g_return_val_if_fail (GKD_SECRET_IS_SESSION (self), FALSE);
+	g_return_val_if_fail (GP11_IS_OBJECT (item), FALSE);
+	g_return_val_if_fail (secret, FALSE);
+
+	if (self->key == NULL)
+		return plain_set_item_secret (self, item, secret, derr);
+	else
+		return aes_unwrap_item_secret (self, item, secret, derr);
+}
diff --git a/daemon/dbus/gkd-secret-session.h b/daemon/dbus/gkd-secret-session.h
index 6c13d76..ec82584 100644
--- a/daemon/dbus/gkd-secret-session.h
+++ b/daemon/dbus/gkd-secret-session.h
@@ -50,4 +50,13 @@ const gchar*        gkd_secret_session_get_caller_executable   (GkdSecretSession
 
 const gchar*        gkd_secret_session_get_object_path         (GkdSecretSession *self);
 
+GkdSecretSecret*    gkd_secret_session_get_item_secret         (GkdSecretSession *self,
+                                                                GP11Object *item,
+                                                                DBusError *derr);
+
+gboolean            gkd_secret_session_set_item_secret         (GkdSecretSession *self,
+                                                                GP11Object *item,
+                                                                GkdSecretSecret *secret,
+                                                                DBusError *derr);
+
 #endif /* __GKD_SECRET_SESSION_H__ */
diff --git a/daemon/dbus/gkd-secret-types.h b/daemon/dbus/gkd-secret-types.h
index c230ba4..645ac04 100644
--- a/daemon/dbus/gkd-secret-types.h
+++ b/daemon/dbus/gkd-secret-types.h
@@ -48,6 +48,7 @@ typedef struct _GkdSecretCreate GkdSecretCreate;
 typedef struct _GkdSecretItem GkdSecretItem;
 typedef struct _GkdSecretObjects GkdSecretObjects;
 typedef struct _GkdSecretPrompt GkdSecretPrompt;
+typedef struct _GkdSecretSecret GkdSecretSecret;
 typedef struct _GkdSecretService GkdSecretService;
 typedef struct _GkdSecretSession GkdSecretSession;
 typedef struct _GkdSecretUnlock GkdSecretUnlock;
diff --git a/daemon/dbus/gkd-secret-util.c b/daemon/dbus/gkd-secret-util.c
index 1c7e162..10ea9a0 100644
--- a/daemon/dbus/gkd-secret-util.c
+++ b/daemon/dbus/gkd-secret-util.c
@@ -409,3 +409,27 @@ gkd_secret_util_identifier_for_collection (GP11Object *collection)
 
 	return identifier;
 }
+
+GP11Attributes*
+gkd_secret_util_attributes_for_item (GP11Object *item)
+{
+	gchar *coll, *identifier;
+	GP11Attributes *attrs;
+	gchar *path;
+
+	path = gkd_secret_util_path_for_item (item);
+	if (path == NULL)
+		return NULL;
+
+	if (!parse_collection_and_item_from_path (path, &coll, &identifier))
+		g_return_val_if_reached (NULL);
+
+	attrs = gp11_attributes_new ();
+	gp11_attributes_add_string (attrs, CKA_G_COLLECTION, coll);
+	gp11_attributes_add_string (attrs, CKA_ID, identifier);
+
+	g_free (identifier);
+	g_free (coll);
+	g_free (path);
+	return attrs;
+}
diff --git a/daemon/dbus/gkd-secret-util.h b/daemon/dbus/gkd-secret-util.h
index 611899a..bf208bb 100644
--- a/daemon/dbus/gkd-secret-util.h
+++ b/daemon/dbus/gkd-secret-util.h
@@ -42,4 +42,6 @@ gchar*            gkd_secret_util_path_for_item                         (GP11Obj
 
 gchar*            gkd_secret_util_identifier_for_collection             (GP11Object *collection);
 
+GP11Attributes*   gkd_secret_util_attributes_for_item                   (GP11Object *item);
+
 #endif /* __GKD_SECRET_UTIL_H__ */
diff --git a/pkcs11/secret-store/gck-secret-item.c b/pkcs11/secret-store/gck-secret-item.c
index 704177c..238998d 100644
--- a/pkcs11/secret-store/gck-secret-item.c
+++ b/pkcs11/secret-store/gck-secret-item.c
@@ -107,6 +107,7 @@ factory_create_item (GckSession *session, GckTransaction *transaction,
 	GckManager *s_manager;
 	CK_ATTRIBUTE *attr;
 	gboolean is_token;
+	gchar *identifier;
 
 	g_return_val_if_fail (GCK_IS_TRANSACTION (transaction), NULL);
 	g_return_val_if_fail (attrs || !n_attrs, NULL);
@@ -134,6 +135,18 @@ factory_create_item (GckSession *session, GckTransaction *transaction,
 		return NULL;
 	}
 
+	/* If an ID was specified, then try and see if that ID already exists */
+	if (gck_attributes_find_string (attrs, n_attrs, CKA_ID, &identifier)) {
+		item = gck_secret_collection_get_item (collection, identifier);
+		if (item == NULL) {
+			gck_transaction_fail (transaction, CKR_TEMPLATE_INCONSISTENT);
+			return NULL;
+		} else {
+			gck_attributes_consume (attrs, n_attrs, CKA_ID, G_MAXULONG);
+			return g_object_ref (item);
+		}
+	}
+
 	/* Create a new collection which will own the item */
 	item = gck_secret_collection_create_item (collection, transaction);
 	gck_session_complete_object_creation (session, transaction, GCK_OBJECT (item), attrs, n_attrs);



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