Use credential sets to load smartcard keys
authorMartin Willi <martin@revosec.ch>
Mon, 19 Jul 2010 08:25:51 +0000 (10:25 +0200)
committerMartin Willi <martin@revosec.ch>
Wed, 4 Aug 2010 07:26:21 +0000 (09:26 +0200)
src/libcharon/plugins/stroke/stroke_cred.c
src/libstrongswan/plugins/openssl/openssl_rsa_private_key.c
src/libstrongswan/plugins/pkcs11/pkcs11_private_key.c

index e5718e5..cfa33a7 100644 (file)
@@ -31,6 +31,7 @@
 #include <credentials/certificates/crl.h>
 #include <credentials/certificates/ac.h>
 #include <credentials/sets/mem_cred.h>
+#include <credentials/sets/callback_cred.h>
 #include <utils/linked_list.h>
 #include <utils/lexparser.h>
 #include <threading/rwlock.h>
@@ -717,28 +718,60 @@ chunk_t passphrase_cb(passphrase_cb_data_t *data, int try)
 }
 
 /**
- * Smartcard PIN callback to read from stroke fd
+ * Data for PIN callback
  */
-chunk_t smartcard_cb(passphrase_cb_data_t *data, int try)
+typedef struct {
+       /** socket we use for prompting */
+       FILE *prompt;
+       /** card label */
+       char *card;
+       /** card keyid */
+       chunk_t keyid;
+       /** number of tries */
+       int try;
+} pin_cb_data_t;
+
+/**
+ * Callback function to receive PINs
+ */
+static shared_key_t* pin_cb(pin_cb_data_t *data,
+                                                       identification_t *me, identification_t *other,
+                                                       id_match_t *match_me, id_match_t *match_other)
 {
-       chunk_t secret = chunk_empty;;
+       chunk_t secret;
+       char buf[256];
 
-       if (try != 1)
+       if (!me || !chunk_equals(me->get_encoding(me), data->keyid))
        {
-               fprintf(data->prompt, "invalid passphrase, aborting\n");
-               return chunk_empty;
+               return NULL;
        }
-       fprintf(data->prompt, "Login to '%s' required\n", data->file);
-       fprintf(data->prompt, "Passphrase:\n");
-       if (fgets(data->buf, sizeof(data->buf), data->prompt))
+
+       if (data->try > 1)
        {
-               secret = chunk_create(data->buf, strlen(data->buf));
-               if (secret.len)
+               fprintf(data->prompt, "PIN invalid, giving up.\n", data->card);
+               return NULL;
+       }
+       data->try++;
+       fprintf(data->prompt, "Login to '%s' required\n", data->card);
+       fprintf(data->prompt, "PIN:\n");
+       if (fgets(buf, sizeof(buf), data->prompt))
+       {
+               secret = chunk_create(buf, strlen(buf));
+               if (secret.len > 1)
                {       /* trim appended \n */
                        secret.len--;
+                       if (match_me)
+                       {
+                               *match_me = ID_MATCH_PERFECT;
+                       }
+                       if (match_other)
+                       {
+                               *match_other = ID_MATCH_NONE;
+                       }
+                       return shared_key_create(SHARED_PIN, chunk_clone(secret));
                }
        }
-       return secret;
+       return NULL;
 }
 
 /**
@@ -754,8 +787,9 @@ static bool load_pin(private_stroke_cred_t *this, chunk_t line, int line_nr,
        chunk_t chunk;
        shared_key_t *shared;
        identification_t *id;
-       mem_cred_t *mem;
-       callback_set_t *cb;
+       mem_cred_t *mem = NULL;
+       callback_cred_t *cb = NULL;
+       pin_cb_data_t pin_data;
        enum {
                SC_FORMAT_SLOT_MODULE_KEYID,
                SC_FORMAT_SLOT_KEYID,
@@ -821,22 +855,33 @@ static bool load_pin(private_stroke_cred_t *this, chunk_t line, int line_nr,
                DBG1(DBG_CFG, "line %d: malformed PIN: %s", line_nr, ugh);
                return FALSE;
        }
+
+       chunk = chunk_from_hex(chunk_create(keyid, strlen(keyid)), NULL);
        if (secret.len == 7 && strneq(secret.ptr, "%prompt", 7))
        {
-               if (prompt)
-               {
-                       passphrase_cb_data_t data = {
-                               .prompt = prompt,
-                               .file = path,
-                       };
+               if (!prompt)
+               {       /* no IO channel to prompt, skip */
+                       free(chunk.ptr);
+                       return TRUE;
+               }
+               /* use callback credential set to prompt for the pin */
+               pin_data.prompt = prompt;
+               pin_data.card = smartcard;
+               pin_data.keyid = chunk;
+               pin_data.try = 1;
+               cb = callback_cred_create_shared((void*)pin_cb, &pin_data);
+               lib->credmgr->add_local_set(lib->credmgr, &cb->set);
+       }
+       else
+       {
+               /* provide our pin in a temporary credential set */
+               shared = shared_key_create(SHARED_PIN, secret);
+               id = identification_create_from_encoding(ID_KEY_ID, chunk);
+               mem = mem_cred_create();
+               mem->add_shared(mem, shared, id, NULL);
+               lib->credmgr->add_local_set(lib->credmgr, &mem->set);
+       }
 
-       /* provide our pin in a temporary credential set */
-       shared = shared_key_create(SHARED_PIN, secret);
-       chunk = chunk_from_hex(chunk_create(keyid, strlen(keyid)), NULL);
-       id = identification_create_from_encoding(ID_KEY_ID, chunk);
-       set = mem_cred_create();
-       set->add_shared(set, shared, id, NULL);
-       lib->credmgr->add_local_set(lib->credmgr, &set->set);
        /* unlock: smartcard needs the pin and potentially calls public set */
        this->lock->unlock(this->lock);
        switch (format)
@@ -861,8 +906,16 @@ static bool load_pin(private_stroke_cred_t *this, chunk_t line, int line_nr,
                        break;
        }
        this->lock->write_lock(this->lock);
-       lib->credmgr->remove_local_set(lib->credmgr, &set->set);
-       set->destroy(set);
+       if (mem)
+       {
+               lib->credmgr->remove_local_set(lib->credmgr, &mem->set);
+               mem->destroy(mem);
+       }
+       if (cb)
+       {
+               lib->credmgr->remove_local_set(lib->credmgr, &cb->set);
+               cb->destroy(cb);
+       }
 
        if (key)
        {
index 3bca342..e50656e 100644 (file)
@@ -444,6 +444,48 @@ openssl_rsa_private_key_t *openssl_rsa_private_key_load(key_type_t type,
 }
 
 /**
+ * Login to engine with a PIN specified for a keyid
+ */
+static bool login(ENGINE *engine, chunk_t keyid)
+{
+       enumerator_t *enumerator;
+       shared_key_t *shared;
+       identification_t *id;
+       chunk_t key;
+       char pin[64];
+       bool found = FALSE, success = FALSE;
+
+       id = identification_create_from_encoding(ID_KEY_ID, keyid);
+       enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
+                                                                                                               SHARED_PIN, id, NULL);
+       while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
+       {
+               found = TRUE;
+               key = shared->get_key(shared);
+               if (snprintf(pin, sizeof(pin), "%.*s", key.len, key.ptr) >= sizeof(pin))
+               {
+                       continue;
+               }
+               if (ENGINE_ctrl_cmd_string(engine, "PIN", pin, 0))
+               {
+                       success = TRUE;
+                       break;
+               }
+               else
+               {
+                       DBG1(DBG_CFG, "setting PIN on engine failed");
+               }
+       }
+       enumerator->destroy(enumerator);
+       id->destroy(id);
+       if (!found)
+       {
+               DBG1(DBG_CFG, "no PIN found for %#B", &keyid);
+       }
+       return success;
+}
+
+/**
  * See header.
  */
 openssl_rsa_private_key_t *openssl_rsa_private_key_connect(key_type_t type,
@@ -452,8 +494,8 @@ openssl_rsa_private_key_t *openssl_rsa_private_key_connect(key_type_t type,
 #ifndef OPENSSL_NO_ENGINE
        private_openssl_rsa_private_key_t *this;
        char *engine_id = NULL;
-       char keyname[64], pin[32];;
-       chunk_t secret = chunk_empty, keyid = chunk_empty;;
+       char keyname[64];
+       chunk_t keyid = chunk_empty;;
        EVP_PKEY *key;
        ENGINE *engine;
        int slot = -1;
@@ -465,9 +507,6 @@ openssl_rsa_private_key_t *openssl_rsa_private_key_connect(key_type_t type,
                        case BUILD_PKCS11_KEYID:
                                keyid = va_arg(args, chunk_t);
                                continue;
-                       case BUILD_PASSPHRASE:
-                               secret = va_arg(args, chunk_t);
-                               continue;
                        case BUILD_PKCS11_SLOT:
                                slot = va_arg(args, int);
                                continue;
@@ -481,7 +520,7 @@ openssl_rsa_private_key_t *openssl_rsa_private_key_connect(key_type_t type,
                }
                break;
        }
-       if (!keyid.len || keyid.len > 40 || !secret.len)
+       if (!keyid.len || keyid.len > 40)
        {
                return NULL;
        }
@@ -497,8 +536,6 @@ openssl_rsa_private_key_t *openssl_rsa_private_key_connect(key_type_t type,
        }
        chunk_to_hex(keyid, keyname + strlen(keyname), FALSE);
 
-       snprintf(pin, sizeof(pin), "%.*s", secret.len, secret.ptr);
-
        if (!engine_id)
        {
                engine_id = lib->settings->get_str(lib->settings,
@@ -516,13 +553,12 @@ openssl_rsa_private_key_t *openssl_rsa_private_key_connect(key_type_t type,
                ENGINE_free(engine);
                return NULL;
        }
-       if (!ENGINE_ctrl_cmd_string(engine, "PIN", pin, 0))
+       if (!login(engine, keyid))
        {
-               DBG1(DBG_LIB, "failed to set PIN on engine '%s'", engine_id);
+               DBG1(DBG_LIB, "login to engine '%s' failed", engine_id);
                ENGINE_free(engine);
                return NULL;
        }
-
        key = ENGINE_load_private_key(engine, keyname, NULL, NULL);
        if (!key)
        {
index b50ae07..fa96c7f 100644 (file)
@@ -335,29 +335,38 @@ static bool find_key(private_pkcs11_private_key_t *this, chunk_t keyid)
  */
 static bool login(private_pkcs11_private_key_t *this, chunk_t keyid, int slot)
 {
+       enumerator_t *enumerator;
        identification_t *id;
        shared_key_t *shared;
        chunk_t pin;
        CK_RV rv;
+       bool found = FALSE, success = FALSE;
 
        id = identification_create_from_encoding(ID_KEY_ID, keyid);
-       shared = lib->credmgr->get_shared(lib->credmgr, SHARED_PIN, id, NULL);
-       id->destroy(id);
-       if (!shared)
-       {
-               DBG1(DBG_CFG, "no PIN found for PKCS#11 key %#B", keyid);
-               return FALSE;
-       }
-       pin = shared->get_key(shared);
-       rv = this->lib->f->C_Login(this->session, CKU_USER, pin.ptr, pin.len);
-       shared->destroy(shared);
-       if (rv != CKR_OK)
+       enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
+                                                                                                               SHARED_PIN, id, NULL);
+       while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
        {
+               found = TRUE;
+               pin = shared->get_key(shared);
+               rv = this->lib->f->C_Login(this->session, CKU_USER, pin.ptr, pin.len);
+               if (rv == CKR_OK)
+               {
+                       success = TRUE;
+                       break;
+               }
                DBG1(DBG_CFG, "login to '%s':%d failed: %N",
                         this->lib->get_name(this->lib), slot, ck_rv_names, rv);
+       }
+       enumerator->destroy(enumerator);
+       id->destroy(id);
+
+       if (!found)
+       {
+               DBG1(DBG_CFG, "no PIN found for PKCS#11 key %#B", &keyid);
                return FALSE;
        }
-       return TRUE;
+       return success;
 }
 
 /**