Use credential sets to load smartcard keys
This commit is contained in:
@@ -31,6 +31,7 @@
|
|||||||
#include <credentials/certificates/crl.h>
|
#include <credentials/certificates/crl.h>
|
||||||
#include <credentials/certificates/ac.h>
|
#include <credentials/certificates/ac.h>
|
||||||
#include <credentials/sets/mem_cred.h>
|
#include <credentials/sets/mem_cred.h>
|
||||||
|
#include <credentials/sets/callback_cred.h>
|
||||||
#include <utils/linked_list.h>
|
#include <utils/linked_list.h>
|
||||||
#include <utils/lexparser.h>
|
#include <utils/lexparser.h>
|
||||||
#include <threading/rwlock.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 */
|
||||||
chunk_t secret = chunk_empty;;
|
FILE *prompt;
|
||||||
|
/** card label */
|
||||||
|
char *card;
|
||||||
|
/** card keyid */
|
||||||
|
chunk_t keyid;
|
||||||
|
/** number of tries */
|
||||||
|
int try;
|
||||||
|
} pin_cb_data_t;
|
||||||
|
|
||||||
if (try != 1)
|
/**
|
||||||
|
* 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;
|
||||||
|
char buf[256];
|
||||||
|
|
||||||
|
if (!me || !chunk_equals(me->get_encoding(me), data->keyid))
|
||||||
{
|
{
|
||||||
fprintf(data->prompt, "invalid passphrase, aborting\n");
|
return NULL;
|
||||||
return chunk_empty;
|
|
||||||
}
|
}
|
||||||
fprintf(data->prompt, "Login to '%s' required\n", data->file);
|
|
||||||
fprintf(data->prompt, "Passphrase:\n");
|
if (data->try > 1)
|
||||||
if (fgets(data->buf, sizeof(data->buf), data->prompt))
|
|
||||||
{
|
{
|
||||||
secret = chunk_create(data->buf, strlen(data->buf));
|
fprintf(data->prompt, "PIN invalid, giving up.\n", data->card);
|
||||||
if (secret.len)
|
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 */
|
{ /* trim appended \n */
|
||||||
secret.len--;
|
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;
|
chunk_t chunk;
|
||||||
shared_key_t *shared;
|
shared_key_t *shared;
|
||||||
identification_t *id;
|
identification_t *id;
|
||||||
mem_cred_t *mem;
|
mem_cred_t *mem = NULL;
|
||||||
callback_set_t *cb;
|
callback_cred_t *cb = NULL;
|
||||||
|
pin_cb_data_t pin_data;
|
||||||
enum {
|
enum {
|
||||||
SC_FORMAT_SLOT_MODULE_KEYID,
|
SC_FORMAT_SLOT_MODULE_KEYID,
|
||||||
SC_FORMAT_SLOT_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);
|
DBG1(DBG_CFG, "line %d: malformed PIN: %s", line_nr, ugh);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chunk = chunk_from_hex(chunk_create(keyid, strlen(keyid)), NULL);
|
||||||
if (secret.len == 7 && strneq(secret.ptr, "%prompt", 7))
|
if (secret.len == 7 && strneq(secret.ptr, "%prompt", 7))
|
||||||
{
|
{
|
||||||
if (prompt)
|
if (!prompt)
|
||||||
{
|
{ /* no IO channel to prompt, skip */
|
||||||
passphrase_cb_data_t data = {
|
free(chunk.ptr);
|
||||||
.prompt = prompt,
|
return TRUE;
|
||||||
.file = path,
|
}
|
||||||
};
|
/* 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 */
|
/* unlock: smartcard needs the pin and potentially calls public set */
|
||||||
this->lock->unlock(this->lock);
|
this->lock->unlock(this->lock);
|
||||||
switch (format)
|
switch (format)
|
||||||
@@ -861,8 +906,16 @@ static bool load_pin(private_stroke_cred_t *this, chunk_t line, int line_nr,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this->lock->write_lock(this->lock);
|
this->lock->write_lock(this->lock);
|
||||||
lib->credmgr->remove_local_set(lib->credmgr, &set->set);
|
if (mem)
|
||||||
set->destroy(set);
|
{
|
||||||
|
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)
|
if (key)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -443,6 +443,48 @@ openssl_rsa_private_key_t *openssl_rsa_private_key_load(key_type_t type,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* See header.
|
||||||
*/
|
*/
|
||||||
@@ -452,8 +494,8 @@ openssl_rsa_private_key_t *openssl_rsa_private_key_connect(key_type_t type,
|
|||||||
#ifndef OPENSSL_NO_ENGINE
|
#ifndef OPENSSL_NO_ENGINE
|
||||||
private_openssl_rsa_private_key_t *this;
|
private_openssl_rsa_private_key_t *this;
|
||||||
char *engine_id = NULL;
|
char *engine_id = NULL;
|
||||||
char keyname[64], pin[32];;
|
char keyname[64];
|
||||||
chunk_t secret = chunk_empty, keyid = chunk_empty;;
|
chunk_t keyid = chunk_empty;;
|
||||||
EVP_PKEY *key;
|
EVP_PKEY *key;
|
||||||
ENGINE *engine;
|
ENGINE *engine;
|
||||||
int slot = -1;
|
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:
|
case BUILD_PKCS11_KEYID:
|
||||||
keyid = va_arg(args, chunk_t);
|
keyid = va_arg(args, chunk_t);
|
||||||
continue;
|
continue;
|
||||||
case BUILD_PASSPHRASE:
|
|
||||||
secret = va_arg(args, chunk_t);
|
|
||||||
continue;
|
|
||||||
case BUILD_PKCS11_SLOT:
|
case BUILD_PKCS11_SLOT:
|
||||||
slot = va_arg(args, int);
|
slot = va_arg(args, int);
|
||||||
continue;
|
continue;
|
||||||
@@ -481,7 +520,7 @@ openssl_rsa_private_key_t *openssl_rsa_private_key_connect(key_type_t type,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!keyid.len || keyid.len > 40 || !secret.len)
|
if (!keyid.len || keyid.len > 40)
|
||||||
{
|
{
|
||||||
return NULL;
|
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);
|
chunk_to_hex(keyid, keyname + strlen(keyname), FALSE);
|
||||||
|
|
||||||
snprintf(pin, sizeof(pin), "%.*s", secret.len, secret.ptr);
|
|
||||||
|
|
||||||
if (!engine_id)
|
if (!engine_id)
|
||||||
{
|
{
|
||||||
engine_id = lib->settings->get_str(lib->settings,
|
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);
|
ENGINE_free(engine);
|
||||||
return NULL;
|
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);
|
ENGINE_free(engine);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
key = ENGINE_load_private_key(engine, keyname, NULL, NULL);
|
key = ENGINE_load_private_key(engine, keyname, NULL, NULL);
|
||||||
if (!key)
|
if (!key)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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)
|
static bool login(private_pkcs11_private_key_t *this, chunk_t keyid, int slot)
|
||||||
{
|
{
|
||||||
|
enumerator_t *enumerator;
|
||||||
identification_t *id;
|
identification_t *id;
|
||||||
shared_key_t *shared;
|
shared_key_t *shared;
|
||||||
chunk_t pin;
|
chunk_t pin;
|
||||||
CK_RV rv;
|
CK_RV rv;
|
||||||
|
bool found = FALSE, success = FALSE;
|
||||||
|
|
||||||
id = identification_create_from_encoding(ID_KEY_ID, keyid);
|
id = identification_create_from_encoding(ID_KEY_ID, keyid);
|
||||||
shared = lib->credmgr->get_shared(lib->credmgr, SHARED_PIN, id, NULL);
|
enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
|
||||||
id->destroy(id);
|
SHARED_PIN, id, NULL);
|
||||||
if (!shared)
|
while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
|
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",
|
DBG1(DBG_CFG, "login to '%s':%d failed: %N",
|
||||||
this->lib->get_name(this->lib), slot, ck_rv_names, rv);
|
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 FALSE;
|
||||||
}
|
}
|
||||||
return TRUE;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user