[gnome-keyring] gcr: Fix PKCS#11 importer attribute cleanup before import



commit 5f8076ae918be22a31f8c822711137a5060fea04
Author: Stef Walter <stefw collabora co uk>
Date:   Wed Oct 5 09:14:14 2011 +0200

    gcr: Fix PKCS#11 importer attribute cleanup before import
    
     * Add proper CKA_ID, CKA_DECRYPT, CKA_SIGN, CKA_SIGN_RECOVER
       CKA_UNWRAP, CKA_SENSITIVE, and CKA_PRIVATE attributes
     * Create pairs of keys and certificates, and import those
       first

 gcr/gcr-base.symbols      |   11 ++
 gcr/gcr-debug.c           |    1 +
 gcr/gcr-debug.h           |    3 +-
 gcr/gcr-pkcs11-importer.c |  222 +++++++++++++++++++++++++++++++++++++--------
 4 files changed, 196 insertions(+), 41 deletions(-)
---
diff --git a/gcr/gcr-base.symbols b/gcr/gcr-base.symbols
index 233ec15..8c251a4 100644
--- a/gcr/gcr-base.symbols
+++ b/gcr/gcr-base.symbols
@@ -52,8 +52,12 @@ gcr_data_error_get_domain
 gcr_data_error_get_type
 gcr_data_format_get_type
 gcr_error_get_domain
+gcr_fingerprint_from_attributes
+gcr_fingerprint_from_certificate_public_key
+gcr_fingerprint_from_subject_public_key_info
 gcr_icon_for_token
 gcr_importer_create_for_parsed
+gcr_importer_get_interaction
 gcr_importer_get_parser
 gcr_importer_get_prompt_behavior
 gcr_importer_get_slot
@@ -66,9 +70,16 @@ gcr_importer_queue_and_filter_for_parsed
 gcr_importer_queue_for_parsed
 gcr_importer_register
 gcr_importer_register_well_known
+gcr_importer_set_interaction
 gcr_importer_set_parser
 gcr_importer_set_prompt_behavior
 gcr_importer_set_slot
+gcr_import_interaction_get_importer
+gcr_import_interaction_get_type
+gcr_import_interaction_set_importer
+gcr_import_interaction_supplement
+gcr_import_interaction_supplement_async
+gcr_import_interaction_supplement_finish
 gcr_parsed_get_attributes
 gcr_parsed_get_data
 gcr_parsed_get_description
diff --git a/gcr/gcr-debug.c b/gcr/gcr-debug.c
index 1acdd5b..fea8fee 100644
--- a/gcr/gcr-debug.c
+++ b/gcr/gcr-debug.c
@@ -41,6 +41,7 @@ static GDebugKey keys[] = {
 	{ "parse", GCR_DEBUG_PARSE },
 	{ "gnupg", GCR_DEBUG_GNUPG },
 	{ "trust", GCR_DEBUG_TRUST },
+	{ "import", GCR_DEBUG_IMPORT },
 	{ 0, }
 };
 
diff --git a/gcr/gcr-debug.h b/gcr/gcr-debug.h
index cb92cee..20bc35f 100644
--- a/gcr/gcr-debug.h
+++ b/gcr/gcr-debug.h
@@ -32,7 +32,8 @@ typedef enum {
 	GCR_DEBUG_CERTIFICATE_CHAIN = 1 << 2,
 	GCR_DEBUG_PARSE = 1 << 3,
 	GCR_DEBUG_GNUPG = 1 << 4,
-	GCR_DEBUG_TRUST = 1 << 5
+	GCR_DEBUG_TRUST = 1 << 5,
+	GCR_DEBUG_IMPORT = 1 << 6,
 } GcrDebugFlags;
 
 gboolean           _gcr_debug_flag_is_set              (GcrDebugFlags flag);
diff --git a/gcr/gcr-pkcs11-importer.c b/gcr/gcr-pkcs11-importer.c
index 7c909bd..371f76b 100644
--- a/gcr/gcr-pkcs11-importer.c
+++ b/gcr/gcr-pkcs11-importer.c
@@ -24,7 +24,10 @@
 
 #include "config.h"
 
-#include "gcr-base.h"
+#define DEBUG_FLAG GCR_DEBUG_IMPORT
+#include "gcr-debug.h"
+#include "gcr-fingerprint.h"
+#include "gcr-icons.h"
 #include "gcr-internal.h"
 #include "gcr-library.h"
 #include "gcr-import-interaction.h"
@@ -32,8 +35,12 @@
 #include "gcr-parser.h"
 #include "gcr-pkcs11-importer.h"
 
+#include "egg/egg-hex.h"
+
 #include <gck/gck.h>
 
+#include <gcrypt.h>
+
 #include <glib/gi18n-lib.h>
 
 enum {
@@ -53,7 +60,7 @@ struct _GcrPkcs11Importer {
 	GckSlot *slot;
 	GList *objects;
 	GckSession *session;
-	GQueue queue;
+	GQueue *queue;
 	GTlsInteraction *interaction;
 	gboolean any_private;
 };
@@ -197,17 +204,15 @@ state_create_object (GSimpleAsyncResult *res,
 	GError *error = NULL;
 
 	/* No more objects */
-	if (g_queue_is_empty (&self->queue)) {
+	if (g_queue_is_empty (self->queue)) {
 		next_state (res, state_complete);
 
 	} else {
 
 		/* Pop first one off the list */
-		attrs = g_queue_pop_head (&self->queue);
+		attrs = g_queue_pop_head (self->queue);
 		g_assert (attrs != NULL);
 
-		gck_attributes_add_boolean (attrs, CKA_TOKEN, CK_TRUE);
-
 		if (async) {
 			gck_session_create_object_async (self->session, attrs,
 			                                 data->cancellable, on_create_object,
@@ -223,14 +228,163 @@ state_create_object (GSimpleAsyncResult *res,
 }
 
 /* ---------------------------------------------------------------------------------
- * PROMPTING
+ * SUPPLEMENTING and FIXING UP
  */
 
+typedef struct {
+	GckAttributes *certificate;
+	GckAttributes *private_key;
+} CertificateKeyPair;
+
+static void
+supplement_id_for_data (GckAttributes *attrs,
+                        guchar *nonce,
+                        gsize n_once,
+                        gpointer data,
+                        gsize n_data)
+{
+	gcry_md_hd_t mdh;
+	gcry_error_t gcry;
+
+	if (gck_attributes_find (attrs, CKA_ID) != NULL)
+		return;
+
+	gcry = gcry_md_open (&mdh, GCRY_MD_SHA1, 0);
+	g_return_if_fail (gcry == 0);
+
+	gcry_md_write (mdh, nonce, n_once);
+	gcry_md_write (mdh, data, n_data);
+
+	gck_attributes_add_data (attrs, CKA_ID,
+	                         gcry_md_read (mdh, 0),
+	                         gcry_md_get_algo_dlen (GCRY_MD_SHA1));
+
+	gcry_md_close (mdh);
+}
+
+static void
+supplement_attributes (GcrPkcs11Importer *self)
+{
+	GHashTable *pairs;
+	GHashTable *paired;
+	CertificateKeyPair *pair;
+	GckAttributes *attrs;
+	gulong klass;
+	guchar *finger;
+	gchar *fingerprint;
+	guchar nonce[20];
+	GHashTableIter iter;
+	gsize n_finger;
+	GQueue *queue;
+	GList *l;
+
+	/* A table of certificate/key pairs by fingerprint */
+	pairs = g_hash_table_new_full (g_str_hash, g_str_equal,
+	                               g_free, g_free);
+
+	for (l = self->queue->head; l != NULL; l = g_list_next (l)) {
+		attrs = l->data;
+		if (!gck_attributes_find_ulong (attrs, CKA_CLASS, &klass))
+			g_return_if_reached ();
+
+		/* Make a string fingerprint for this guy */
+		finger = gcr_fingerprint_from_attributes (attrs, G_CHECKSUM_SHA1,
+		                                          &n_finger);
+		if (finger) {
+			fingerprint = egg_hex_encode (finger, n_finger);
+			g_free (finger);
+
+			pair = g_hash_table_lookup (pairs, fingerprint);
+			if (pair == NULL) {
+				pair = g_new0 (CertificateKeyPair, 1);
+				g_hash_table_insert (pairs, fingerprint, pair);
+			} else {
+				g_free (fingerprint);
+			}
+		} else {
+			pair = NULL;
+		}
+
+		fingerprint = NULL;
+
+		gck_attributes_set_boolean (attrs, CKA_TOKEN, CK_TRUE);
+
+		switch (klass) {
+		case CKO_CERTIFICATE:
+			gck_attributes_set_boolean (attrs, CKA_PRIVATE, FALSE);
+			if (pair != NULL && pair->certificate == NULL)
+				pair->certificate = attrs;
+			break;
+		case CKO_PRIVATE_KEY:
+			gck_attributes_set_boolean (attrs, CKA_PRIVATE, TRUE);
+			gck_attributes_add_boolean (attrs, CKA_DECRYPT, TRUE);
+			gck_attributes_add_boolean (attrs, CKA_SIGN, TRUE);
+			gck_attributes_add_boolean (attrs, CKA_SIGN_RECOVER, TRUE);
+			gck_attributes_add_boolean (attrs, CKA_UNWRAP, TRUE);
+			gck_attributes_add_boolean (attrs, CKA_SENSITIVE, TRUE);
+			if (pair != NULL && pair->private_key == NULL)
+				pair->private_key = attrs;
+			break;
+		}
+	}
+
+	/* For generation of CKA_ID's */
+	gcry_create_nonce (nonce, sizeof (nonce));
+
+	/* A table for marking which attributes are in the pairs table */
+	paired = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+	/* Now move everything in pairs to the front */
+	queue = g_queue_new ();
+	g_hash_table_iter_init (&iter, pairs);
+	while (g_hash_table_iter_next (&iter, (gpointer *)&fingerprint, (gpointer *)&pair)) {
+		if (pair->certificate != NULL && pair->private_key != NULL) {
+			/*
+			 * Generate a CKA_ID based on the fingerprint and nonce,
+			 * and do the same CKA_ID for both private key and certificate.
+			 */
+			supplement_id_for_data (pair->private_key, nonce, sizeof (nonce),
+			                        fingerprint, strlen (fingerprint));
+			g_queue_push_tail (queue, pair->private_key);
+			g_hash_table_insert (paired, pair->private_key, "present");
+			supplement_id_for_data (pair->certificate, nonce, sizeof (nonce),
+			                        fingerprint, strlen (fingerprint));
+			g_queue_push_tail (queue, pair->certificate);
+			g_hash_table_insert (paired, pair->certificate, "present");
+		}
+	}
+
+	/* Go through the old queue, and look for anything not paired */
+	for (l = self->queue->head; l != NULL; l = g_list_next (l)) {
+		attrs = l->data;
+		if (!g_hash_table_lookup (paired, attrs)) {
+			/*
+			 * Generate a CKA_ID based on the location of attrs in,
+			 * memory, since this together with the nonce should
+			 * be unique.
+			 */
+			supplement_id_for_data (attrs, nonce, sizeof (nonce),
+			                        &attrs, sizeof (gpointer));
+			g_queue_push_tail (queue, l->data);
+		}
+	}
+
+	/* And swap the new queue into place */
+	g_queue_free (self->queue);
+	self->queue = queue;
+
+	g_hash_table_destroy (paired);
+	g_hash_table_destroy (pairs);
+}
+
 static void
 complete_supplement (GSimpleAsyncResult *res,
-                      GError *error)
+                     GError *error)
 {
+	GcrImporterData *data = g_simple_async_result_get_op_res_gpointer (res);
+
 	if (error == NULL) {
+		supplement_attributes (data->importer);
 		next_state (res, state_create_object);
 	} else {
 		g_simple_async_result_take_error (res, error);
@@ -338,7 +492,7 @@ state_open_session (GSimpleAsyncResult *res,
 static void
 _gcr_pkcs11_importer_init (GcrPkcs11Importer *self)
 {
-	g_queue_init (&self->queue);
+	self->queue = g_queue_new ();
 }
 
 static void
@@ -351,8 +505,8 @@ _gcr_pkcs11_importer_dispose (GObject *obj)
 	g_clear_object (&self->session);
 	g_clear_object (&self->interaction);
 
-	while (!g_queue_is_empty (&self->queue))
-		gck_attributes_unref (g_queue_pop_head (&self->queue));
+	while (!g_queue_is_empty (self->queue))
+		gck_attributes_unref (g_queue_pop_head (self->queue));
 
 	G_OBJECT_CLASS (_gcr_pkcs11_importer_parent_class)->dispose (obj);
 }
@@ -495,27 +649,6 @@ _gcr_pkcs11_importer_class_init (GcrPkcs11ImporterClass *klass)
 	_gcr_initialize_library ();
 }
 
-static gint
-on_sort_by_private_key_first (gconstpointer a,
-                              gconstpointer b,
-                              gpointer user_data)
-{
-	gulong class_a, class_b;
-
-	if (!gck_attributes_find_ulong ((GckAttributes *)a, CKA_CLASS, &class_a))
-		class_a = 0;
-	if (!gck_attributes_find_ulong ((GckAttributes *)b, CKA_CLASS, &class_b))
-		class_b = 0;
-
-	if (class_a == class_b)
-		return 0;
-	if (class_a == CKO_PRIVATE_KEY)
-		return -1;
-	if (class_b == CKO_PRIVATE_KEY)
-		return 1;
-	return 0;
-}
-
 static GList *
 list_all_slots (void)
 {
@@ -543,13 +676,19 @@ is_slot_importable (GckSlot *slot,
 	gboolean match;
 	guint i;
 
-	if (token->flags & CKF_WRITE_PROTECTED)
+	if (token->flags & CKF_WRITE_PROTECTED) {
+		_gcr_debug ("token is not importable: %s: write protected", token->label);
 		return FALSE;
-	if (!(token->flags & CKF_TOKEN_INITIALIZED))
+	}
+	if (!(token->flags & CKF_TOKEN_INITIALIZED)) {
+		_gcr_debug ("token is not importable: %s: not initialized", token->label);
 		return FALSE;
+	}
 	if ((token->flags & CKF_LOGIN_REQUIRED) &&
-	    !(token->flags & CKF_USER_PIN_INITIALIZED))
+	    !(token->flags & CKF_USER_PIN_INITIALIZED)) {
+		_gcr_debug ("token is not importable: %s: user pin not initialized", token->label);
 		return FALSE;
+	}
 
 	for (i = 0; token_blacklist[i] != NULL; i++) {
 		uri = gck_uri_parse (token_blacklist[i], GCK_URI_FOR_TOKEN | GCK_URI_FOR_MODULE, &error);
@@ -562,8 +701,10 @@ is_slot_importable (GckSlot *slot,
 		match = gck_slot_match (slot, uri);
 		gck_uri_data_free (uri);
 
-		if (match)
+		if (match) {
+			_gcr_debug ("token is not importable: %s: on the black list", token->label);
 			return FALSE;
+		}
 	}
 
 	return TRUE;
@@ -582,14 +723,16 @@ _gcr_pkcs11_importer_create_for_parsed (GcrParsed *parsed)
 	for (l = slots; l != NULL; l = g_list_next (l)) {
 		token_info = gck_slot_get_token_info (l->data);
 		importable = is_slot_importable (l->data, token_info);
-		gck_token_info_free (token_info);
 
 		if (importable) {
+			_gcr_debug ("creating importer for token: %s", token_info->label);
 			self = _gcr_pkcs11_importer_new (l->data);
 			if (!gcr_importer_queue_for_parsed (self, parsed))
 				g_assert_not_reached ();
 			results = g_list_prepend (results, self);
 		}
+
+		gck_token_info_free (token_info);
 	}
 	gck_list_unref_free (slots);
 
@@ -692,7 +835,7 @@ GList *
 _gcr_pkcs11_importer_get_queued (GcrPkcs11Importer *self)
 {
 	g_return_val_if_fail (GCR_IS_PKCS11_IMPORTER (self), NULL);
-	return g_list_copy (self->queue.head);
+	return g_list_copy (self->queue->head);
 }
 
 void
@@ -702,6 +845,5 @@ _gcr_pkcs11_importer_queue (GcrPkcs11Importer *self,
 	g_return_if_fail (GCR_IS_PKCS11_IMPORTER (self));
 	g_return_if_fail (attrs != NULL);
 
-	g_queue_insert_sorted (&self->queue, gck_attributes_ref (attrs),
-	                       on_sort_by_private_key_first, NULL);
+	g_queue_push_tail (self->queue, gck_attributes_ref (attrs));
 }



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