diff options
author | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2010-11-28 11:42:20 +0000 |
---|---|---|
committer | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2010-11-28 11:42:20 +0000 |
commit | f73fba54dc8b30c6482e1e8abf15bbf455592fcd (patch) | |
tree | a449515607c5e51a5c703d7a9b1149c9e4a11560 /src/libstrongswan/plugins/pkcs11/pkcs11_private_key.c | |
parent | b8064f4099997a9e2179f3ad4ace605f5ccac3a1 (diff) | |
download | vyos-strongswan-f73fba54dc8b30c6482e1e8abf15bbf455592fcd.tar.gz vyos-strongswan-f73fba54dc8b30c6482e1e8abf15bbf455592fcd.zip |
[svn-upgrade] new version strongswan (4.5.0)
Diffstat (limited to 'src/libstrongswan/plugins/pkcs11/pkcs11_private_key.c')
-rw-r--r-- | src/libstrongswan/plugins/pkcs11/pkcs11_private_key.c | 600 |
1 files changed, 600 insertions, 0 deletions
diff --git a/src/libstrongswan/plugins/pkcs11/pkcs11_private_key.c b/src/libstrongswan/plugins/pkcs11/pkcs11_private_key.c new file mode 100644 index 000000000..cabca3f54 --- /dev/null +++ b/src/libstrongswan/plugins/pkcs11/pkcs11_private_key.c @@ -0,0 +1,600 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pkcs11_private_key.h" + +#include "pkcs11_library.h" +#include "pkcs11_manager.h" + +#include <debug.h> +#include <threading/mutex.h> + +typedef struct private_pkcs11_private_key_t private_pkcs11_private_key_t; + +/** + * Private data of an pkcs11_private_key_t object. + */ +struct private_pkcs11_private_key_t { + + /** + * Public pkcs11_private_key_t interface. + */ + pkcs11_private_key_t public; + + /** + * PKCS#11 module + */ + pkcs11_library_t *lib; + + /** + * Token session + */ + CK_SESSION_HANDLE session; + + /** + * Mutex to lock session + */ + mutex_t *mutex; + + /** + * Key object on the token + */ + CK_OBJECT_HANDLE object; + + /** + * Key requires reauthentication for each signature/decryption + */ + CK_BBOOL reauth; + + /** + * Keyid of the key we use + */ + identification_t *keyid; + + /** + * Associated public key + */ + public_key_t *pubkey; + + /** + * References to this key + */ + refcount_t ref; +}; + +METHOD(private_key_t, get_type, key_type_t, + private_pkcs11_private_key_t *this) +{ + return this->pubkey->get_type(this->pubkey); +} + +METHOD(private_key_t, get_keysize, int, + private_pkcs11_private_key_t *this) +{ + return this->pubkey->get_keysize(this->pubkey); +} + +/** + * See header. + */ +CK_MECHANISM_PTR pkcs11_signature_scheme_to_mech(signature_scheme_t scheme) +{ + static struct { + signature_scheme_t scheme; + CK_MECHANISM mechanism; + } mappings[] = { + {SIGN_RSA_EMSA_PKCS1_NULL, {CKM_RSA_PKCS, NULL, 0}}, + {SIGN_RSA_EMSA_PKCS1_SHA1, {CKM_SHA1_RSA_PKCS, NULL, 0}}, + {SIGN_RSA_EMSA_PKCS1_SHA256, {CKM_SHA256_RSA_PKCS, NULL, 0}}, + {SIGN_RSA_EMSA_PKCS1_SHA384, {CKM_SHA384_RSA_PKCS, NULL, 0}}, + {SIGN_RSA_EMSA_PKCS1_SHA512, {CKM_SHA512_RSA_PKCS, NULL, 0}}, + {SIGN_RSA_EMSA_PKCS1_MD5, {CKM_MD5_RSA_PKCS, NULL, 0}}, + }; + int i; + + for (i = 0; i < countof(mappings); i++) + { + if (mappings[i].scheme == scheme) + { + return &mappings[i].mechanism; + } + } + return NULL; +} + +/** + * See header. + */ +CK_MECHANISM_PTR pkcs11_encryption_scheme_to_mech(encryption_scheme_t scheme) +{ + static struct { + encryption_scheme_t scheme; + CK_MECHANISM mechanism; + } mappings[] = { + {ENCRYPT_RSA_PKCS1, {CKM_RSA_PKCS, NULL, 0}}, + {ENCRYPT_RSA_OAEP_SHA1, {CKM_RSA_PKCS_OAEP, NULL, 0}}, + }; + int i; + + for (i = 0; i < countof(mappings); i++) + { + if (mappings[i].scheme == scheme) + { + return &mappings[i].mechanism; + } + } + return NULL; +} + +/** + * Reauthenticate to do a signature + */ +static bool reauth(private_pkcs11_private_key_t *this) +{ + enumerator_t *enumerator; + shared_key_t *shared; + chunk_t pin; + CK_RV rv; + bool found = FALSE, success = FALSE; + + enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr, + SHARED_PIN, this->keyid, NULL); + while (enumerator->enumerate(enumerator, &shared, NULL, NULL)) + { + found = TRUE; + pin = shared->get_key(shared); + rv = this->lib->f->C_Login(this->session, CKU_CONTEXT_SPECIFIC, + pin.ptr, pin.len); + if (rv == CKR_OK) + { + success = TRUE; + break; + } + DBG1(DBG_CFG, "reauthentication login failed: %N", ck_rv_names, rv); + } + enumerator->destroy(enumerator); + + if (!found) + { + DBG1(DBG_CFG, "private key requires reauthentication, but no PIN found"); + return FALSE; + } + return success; +} + +METHOD(private_key_t, sign, bool, + private_pkcs11_private_key_t *this, signature_scheme_t scheme, + chunk_t data, chunk_t *signature) +{ + CK_MECHANISM_PTR mechanism; + CK_BYTE_PTR buf; + CK_ULONG len; + CK_RV rv; + + mechanism = pkcs11_signature_scheme_to_mech(scheme); + if (!mechanism) + { + DBG1(DBG_LIB, "signature scheme %N not supported", + signature_scheme_names, scheme); + return FALSE; + } + this->mutex->lock(this->mutex); + rv = this->lib->f->C_SignInit(this->session, mechanism, this->object); + if (this->reauth && !reauth(this)) + { + return FALSE; + } + if (rv != CKR_OK) + { + this->mutex->unlock(this->mutex); + DBG1(DBG_LIB, "C_SignInit() failed: %N", ck_rv_names, rv); + return FALSE; + } + len = (get_keysize(this) + 7) / 8; + buf = malloc(len); + rv = this->lib->f->C_Sign(this->session, data.ptr, data.len, buf, &len); + this->mutex->unlock(this->mutex); + if (rv != CKR_OK) + { + DBG1(DBG_LIB, "C_Sign() failed: %N", ck_rv_names, rv); + free(buf); + return FALSE; + } + *signature = chunk_create(buf, len); + return TRUE; +} + +METHOD(private_key_t, decrypt, bool, + private_pkcs11_private_key_t *this, encryption_scheme_t scheme, + chunk_t crypt, chunk_t *plain) +{ + CK_MECHANISM_PTR mechanism; + CK_BYTE_PTR buf; + CK_ULONG len; + CK_RV rv; + + mechanism = pkcs11_encryption_scheme_to_mech(scheme); + if (!mechanism) + { + DBG1(DBG_LIB, "encryption scheme %N not supported", + encryption_scheme_names, scheme); + return FALSE; + } + this->mutex->lock(this->mutex); + rv = this->lib->f->C_DecryptInit(this->session, mechanism, this->object); + if (this->reauth && !reauth(this)) + { + return FALSE; + } + if (rv != CKR_OK) + { + this->mutex->unlock(this->mutex); + DBG1(DBG_LIB, "C_DecryptInit() failed: %N", ck_rv_names, rv); + return FALSE; + } + len = (get_keysize(this) + 7) / 8; + buf = malloc(len); + rv = this->lib->f->C_Decrypt(this->session, crypt.ptr, crypt.len, buf, &len); + this->mutex->unlock(this->mutex); + if (rv != CKR_OK) + { + DBG1(DBG_LIB, "C_Decrypt() failed: %N", ck_rv_names, rv); + free(buf); + return FALSE; + } + *plain = chunk_create(buf, len); + return TRUE; +} + +METHOD(private_key_t, get_public_key, public_key_t*, + private_pkcs11_private_key_t *this) +{ + return this->pubkey->get_ref(this->pubkey); +} + +METHOD(private_key_t, get_fingerprint, bool, + private_pkcs11_private_key_t *this, cred_encoding_type_t type, + chunk_t *fingerprint) +{ + return this->pubkey->get_fingerprint(this->pubkey, type, fingerprint); +} + +METHOD(private_key_t, get_encoding, bool, + private_pkcs11_private_key_t *this, cred_encoding_type_t type, + chunk_t *encoding) +{ + return FALSE; +} + +METHOD(private_key_t, get_ref, private_key_t*, + private_pkcs11_private_key_t *this) +{ + ref_get(&this->ref); + return &this->public.key; +} + +METHOD(private_key_t, destroy, void, + private_pkcs11_private_key_t *this) +{ + if (ref_put(&this->ref)) + { + if (this->pubkey) + { + this->pubkey->destroy(this->pubkey); + } + this->mutex->destroy(this->mutex); + this->keyid->destroy(this->keyid); + this->lib->f->C_CloseSession(this->session); + free(this); + } +} + +/** + * Find the PKCS#11 library by its friendly name + */ +static pkcs11_library_t* find_lib(char *module) +{ + pkcs11_manager_t *manager; + enumerator_t *enumerator; + pkcs11_library_t *p11, *found = NULL; + CK_SLOT_ID slot; + + manager = pkcs11_manager_get(); + if (!manager) + { + return NULL; + } + enumerator = manager->create_token_enumerator(manager); + while (enumerator->enumerate(enumerator, &p11, &slot)) + { + if (streq(module, p11->get_name(p11))) + { + found = p11; + break; + } + } + enumerator->destroy(enumerator); + return found; +} + +/** + * Find the PKCS#11 lib having a keyid, and optionally a slot + */ +static pkcs11_library_t* find_lib_by_keyid(chunk_t keyid, int *slot) +{ + pkcs11_manager_t *manager; + enumerator_t *enumerator; + pkcs11_library_t *p11, *found = NULL; + CK_SLOT_ID current; + + manager = pkcs11_manager_get(); + if (!manager) + { + return NULL; + } + enumerator = manager->create_token_enumerator(manager); + while (enumerator->enumerate(enumerator, &p11, ¤t)) + { + if (*slot == -1 || *slot == current) + { + /* we look for a public key, it is usually readable without login */ + CK_OBJECT_CLASS class = CKO_PUBLIC_KEY; + CK_ATTRIBUTE tmpl[] = { + {CKA_CLASS, &class, sizeof(class)}, + {CKA_ID, keyid.ptr, keyid.len}, + }; + CK_OBJECT_HANDLE object; + CK_SESSION_HANDLE session; + CK_RV rv; + enumerator_t *keys; + + rv = p11->f->C_OpenSession(current, CKF_SERIAL_SESSION, NULL, NULL, + &session); + if (rv != CKR_OK) + { + DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", + ck_rv_names, rv); + continue; + } + keys = p11->create_object_enumerator(p11, session, + tmpl, countof(tmpl), NULL, 0); + if (keys->enumerate(keys, &object)) + { + DBG1(DBG_CFG, "found key on PKCS#11 token '%s':%d", + p11->get_name(p11), current); + found = p11; + *slot = current; + } + keys->destroy(keys); + p11->f->C_CloseSession(session); + if (found) + { + break; + } + } + } + enumerator->destroy(enumerator); + return found; +} + +/** + * Find the key on the token + */ +static bool find_key(private_pkcs11_private_key_t *this, chunk_t keyid) +{ + CK_OBJECT_CLASS class = CKO_PRIVATE_KEY; + CK_ATTRIBUTE tmpl[] = { + {CKA_CLASS, &class, sizeof(class)}, + {CKA_ID, keyid.ptr, keyid.len}, + }; + CK_OBJECT_HANDLE object; + CK_KEY_TYPE type; + CK_BBOOL reauth; + CK_ATTRIBUTE attr[] = { + {CKA_KEY_TYPE, &type, sizeof(type)}, + {CKA_ALWAYS_AUTHENTICATE, &reauth, sizeof(reauth)}, + {CKA_MODULUS, NULL, 0}, + {CKA_PUBLIC_EXPONENT, NULL, 0}, + }; + enumerator_t *enumerator; + chunk_t modulus, pubexp; + + enumerator = this->lib->create_object_enumerator(this->lib, + this->session, tmpl, countof(tmpl), attr, countof(attr)); + if (enumerator->enumerate(enumerator, &object)) + { + switch (type) + { + case CKK_RSA: + if (attr[2].ulValueLen == -1 || attr[3].ulValueLen == -1) + { + DBG1(DBG_CFG, "reading modulus/exponent from PKCS#1 failed"); + break; + } + modulus = chunk_create(attr[2].pValue, attr[2].ulValueLen); + pubexp = chunk_create(attr[3].pValue, attr[3].ulValueLen); + this->pubkey = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, + KEY_RSA, BUILD_RSA_MODULUS, modulus, + BUILD_RSA_PUB_EXP, pubexp, BUILD_END); + if (!this->pubkey) + { + DBG1(DBG_CFG, "extracting public key from PKCS#11 RSA " + "private key failed"); + } + this->reauth = reauth; + this->object = object; + break; + default: + DBG1(DBG_CFG, "PKCS#11 key type %d not supported", type); + break; + } + } + enumerator->destroy(enumerator); + return this->pubkey != NULL; +} + +/** + * Find a PIN and try to log in + */ +static bool login(private_pkcs11_private_key_t *this, int slot) +{ + enumerator_t *enumerator; + shared_key_t *shared; + chunk_t pin; + CK_RV rv; + CK_SESSION_INFO info; + bool found = FALSE, success = FALSE; + + rv = this->lib->f->C_GetSessionInfo(this->session, &info); + if (rv != CKR_OK) + { + DBG1(DBG_CFG, "C_GetSessionInfo failed: %N", ck_rv_names, rv); + return FALSE; + } + if (info.state != CKS_RO_PUBLIC_SESSION && + info.state != CKS_RW_PUBLIC_SESSION) + { /* already logged in with another session, skip */ + return TRUE; + } + + enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr, + SHARED_PIN, this->keyid, 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); + + if (!found) + { + DBG1(DBG_CFG, "no PIN found for PKCS#11 key %Y", this->keyid); + return FALSE; + } + return success; +} + +/** + * See header. + */ +pkcs11_private_key_t *pkcs11_private_key_connect(key_type_t type, va_list args) +{ + private_pkcs11_private_key_t *this; + char *module = NULL; + chunk_t keyid = chunk_empty; + int slot = -1; + CK_RV rv; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_PKCS11_KEYID: + keyid = va_arg(args, chunk_t); + continue; + case BUILD_PKCS11_SLOT: + slot = va_arg(args, int); + continue; + case BUILD_PKCS11_MODULE: + module = va_arg(args, char*); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + if (!keyid.len) + { + return NULL; + } + + INIT(this, + .public = { + .key = { + .get_type = _get_type, + .sign = _sign, + .decrypt = _decrypt, + .get_keysize = _get_keysize, + .get_public_key = _get_public_key, + .equals = private_key_equals, + .belongs_to = private_key_belongs_to, + .get_fingerprint = _get_fingerprint, + .has_fingerprint = private_key_has_fingerprint, + .get_encoding = _get_encoding, + .get_ref = _get_ref, + .destroy = _destroy, + }, + }, + .ref = 1, + ); + + if (module && slot != -1) + { + this->lib = find_lib(module); + if (!this->lib) + { + DBG1(DBG_CFG, "PKCS#11 module '%s' not found", module); + free(this); + return NULL; + } + } + else + { + this->lib = find_lib_by_keyid(keyid, &slot); + if (!this->lib) + { + DBG1(DBG_CFG, "no PKCS#11 module found having a keyid %#B", &keyid); + free(this); + return NULL; + } + } + + rv = this->lib->f->C_OpenSession(slot, CKF_SERIAL_SESSION, + NULL, NULL, &this->session); + if (rv != CKR_OK) + { + DBG1(DBG_CFG, "opening private key session on '%s':%d failed: %N", + module, slot, ck_rv_names, rv); + free(this); + return NULL; + } + + this->mutex = mutex_create(MUTEX_TYPE_DEFAULT); + this->keyid = identification_create_from_encoding(ID_KEY_ID, keyid); + + if (!login(this, slot)) + { + destroy(this); + return NULL; + } + + if (!find_key(this, keyid)) + { + destroy(this); + return NULL; + } + + return &this->public; +} |