diff options
Diffstat (limited to 'gcr/gcr-pkcs11-importer.c')
-rw-r--r-- | gcr/gcr-pkcs11-importer.c | 222 |
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)); } |