summaryrefslogtreecommitdiff
path: root/gcr/gcr-pkcs11-importer.c
diff options
context:
space:
mode:
authorStef Walter <stefw@collabora.co.uk>2011-10-05 09:14:14 +0200
committerStef Walter <stefw@collabora.co.uk>2011-10-05 09:14:14 +0200
commit19e2f3edee54c77e86fa8924060083930e3cc9a9 (patch)
tree68a0d2b24780e6e6ce64f6ac1c7b0f3b8cb871c2 /gcr/gcr-pkcs11-importer.c
parenta6d4a9fe03b0109ef4b24e06dfdd5fb056aed879 (diff)
downloadgcr-19e2f3edee54c77e86fa8924060083930e3cc9a9.tar.gz
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
Diffstat (limited to 'gcr/gcr-pkcs11-importer.c')
-rw-r--r--gcr/gcr-pkcs11-importer.c222
1 files changed, 182 insertions, 40 deletions
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));
}