diff options
author | james <james@e7ae566f-a301-0410-adde-c780ea21d3b5> | 2005-10-13 08:38:41 +0000 |
---|---|---|
committer | james <james@e7ae566f-a301-0410-adde-c780ea21d3b5> | 2005-10-13 08:38:41 +0000 |
commit | ce98fd24bd72d479805cb121ca8e118826f1ed76 (patch) | |
tree | b109113870455d2c5595a0833301f234353578e3 /pkcs11.c | |
parent | Renamed plugin to plugins to work around (diff) | |
download | openvpn-ce98fd24bd72d479805cb121ca8e118826f1ed76.tar.xz |
Merged PKCS#11 patch.
Pre-2.1_beta3
git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@604 e7ae566f-a301-0410-adde-c780ea21d3b5
Diffstat (limited to '')
-rw-r--r-- | pkcs11.c | 2392 |
1 files changed, 2392 insertions, 0 deletions
diff --git a/pkcs11.c b/pkcs11.c new file mode 100644 index 0000000..25ad525 --- /dev/null +++ b/pkcs11.c @@ -0,0 +1,2392 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single TCP/UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2002-2005 OpenVPN Solutions LLC <info@openvpn.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * The routines in this file deal with providing private key cryptography + * using RSA Security Inc. PKCS #11 Cryptographic Token Interface (Cryptoki). + * + */ + +#if defined(WIN32) +#include "config-win32.h" +#else +#include "config.h" +#endif + +#if defined(USE_CRYPTO) && defined(USE_SSL) && defined(ENABLE_PKCS11) + +#include "syshead.h" +#include "error.h" +#include "misc.h" +#include "ssl.h" + +#if !defined(IN) +#define IN +#endif +#if !defined(OUT) +#define OUT +#endif + +#if defined(WIN32) +#include "cryptoki-win32.h" +#else +#include "cryptoki.h" +#endif + +#include "pkcs11.h" + +/*=========================================== + * Constants + */ + +#if OPENSSL_VERSION_NUMBER < 0x00907000L && defined(CRYPTO_LOCK_ENGINE) +# define RSA_get_default_method RSA_get_default_openssl_method +#else +# ifdef HAVE_ENGINE_GET_DEFAULT_RSA +# include <openssl/engine.h> +# if OPENSSL_VERSION_NUMBER < 0x0090704fL +# define BROKEN_OPENSSL_ENGINE +# endif +# endif +#endif + +#define PKCS11_MAX_ATTRIBUTE_SIZE (10*1024) + +/*=========================================== + * Types + */ + +typedef struct pkcs11_provider_s { + struct pkcs11_provider_s *next; + + bool fEnabled; + +#if defined(WIN32) + HANDLE hLibrary; +#else + void *hLibrary; +#endif + CK_FUNCTION_LIST_PTR f; + bool fShouldFinalize; + char *szSignMode; +} *pkcs11_provider_t; + +typedef struct pkcs11_session_s { + + pkcs11_provider_t provider; + + unsigned char *certificate; + int certificate_size; + unsigned char *certificate_id; + int certificate_id_size; + + char *szPIN; + bool fLoginFailed; + + CK_SLOT_ID slot; + bool fKeySignRecover; + + CK_SESSION_HANDLE session; + CK_OBJECT_HANDLE key; +} *pkcs11_session_t; + +/*=========================================== + * Low level prototypes + */ + +static +void +_fixupFixedString ( + IN const char * const szSource, + OUT char * const szTarget, // MUST BE >= nLength+1 + IN const int nLength // FIXED STRING LENGTH +); +static +void +_hexToBinary ( + IN const char * const szSource, + OUT unsigned char * const target, + IN OUT int * const target_size +); +static +CK_RV +_pkcs11_getSlotById ( + IN const pkcs11_session_t pkcs11_session, + IN const char * const szSlot +); +static +CK_RV +_pkcs11_getSlotByName ( + IN const pkcs11_session_t pkcs11_session, + IN const char * const szName +); +static +CK_RV +_pkcs11_getSlotByLabel ( + IN const pkcs11_session_t pkcs11_session, + IN const char * const szLabel +); +static +CK_RV +_pkcs11_getObjectById ( + IN const pkcs11_session_t pkcs11_session, + IN const CK_OBJECT_CLASS class, + IN const unsigned char * const id, + IN const int id_size, + OUT CK_OBJECT_HANDLE * const handle +); +static +CK_RV +_pkcs11_loadCertificate ( + IN const pkcs11_session_t pkcs11_session, + IN const char * const szIdType, + IN const char * const szId +); +static +CK_RV +_pkcs11_loadKeyProperties ( + IN const pkcs11_session_t pkcs11_session +); +static +bool +_isBetterCertificate ( + IN const unsigned char * const pCurrent, + IN const int nCurrentSize, + IN const unsigned char * const pNew, + IN const int nNewSize +); + +/*========================================= + * Simplified functions prototypes + */ + +static +CK_RV +pkcs11_addProvider ( + IN const char * const szProvider, + IN const char * const szSignMode +); +static +CK_RV +pkcs11_finalize (); +static +CK_RV +pkcs11_createSession ( + IN const char * const szSlotType, + IN const char * const szSlot, + IN const char * const szIdType, + IN const char * const szId, + IN const char * const szPIN, + IN const bool fProtectedAuthentication, + OUT pkcs11_session_t * const pkcs11_session +); +static +CK_RV +pkcs11_freeSession ( + IN const pkcs11_session_t pkcs11_session +); +static +CK_RV +pkcs11_login ( + IN const pkcs11_session_t pkcs11_session +); +static +CK_RV +pkcs11_logout ( + IN const pkcs11_session_t pkcs11_session +); +static +CK_RV +pkcs11_sign ( + IN const pkcs11_session_t pkcs11_session, + IN const CK_MECHANISM_TYPE mech_type, + IN const unsigned char * const source, + IN const int source_size, + OUT unsigned char * const target, + IN OUT int * const target_size +); +static +CK_RV +pkcs11_signRecover ( + IN const pkcs11_session_t pkcs11_session, + IN const CK_MECHANISM_TYPE mech_type, + IN const unsigned char * const source, + IN const int source_size, + OUT unsigned char * const target, + IN OUT int * const target_size +); +static +CK_RV +pkcs11_decrypt ( + IN const pkcs11_session_t pkcs11_session, + IN const CK_MECHANISM_TYPE mech_type, + IN const unsigned char * const source, + IN const int source_size, + OUT unsigned char * const target, + IN OUT int * const target_size +); +static +CK_RV +pkcs11_getCertificate ( + IN const pkcs11_session_t pkcs11_session, + OUT char * const certificate, + IN OUT int * const certificate_size +); +static +char * +pkcs11_getMessage ( + IN const int rv +); + +/*========================================== + * Static data + */ + +static pkcs11_provider_t pkcs11_provider = NULL; + +/*========================================== + * Internal utility functions + */ + +static +void +_fixupFixedString ( + IN const char * const szSource, + OUT char * const szTarget, // MUST BE >= nLength+1 + IN const int nLength // FIXED STRING LENGTH +) { + char *p; + + ASSERT (szSource!=NULL); + ASSERT (szTarget!=NULL); + + p = szTarget+nLength; + memmove (szTarget, szSource, nLength); + *p = '\0'; + p--; + while (p >= szTarget && *p == ' ') { + *p = '\0'; + p--; + } +} + +static +void +_hexToBinary ( + IN const char * const szSource, + OUT unsigned char * const target, + IN OUT int * const target_size +) { + int target_max_size; + const char *p; + char buf[3] = {'\0', '\0', '\0'}; + int i = 0; + + ASSERT (szSource!=NULL); + ASSERT (target!=NULL); + ASSERT (target_size!=NULL); + + target_max_size = *target_size; + p = szSource; + *target_size = 0; + + while (*p != '\0' && *target_size < target_max_size) { + if (isxdigit (*p)) { + buf[i%2] = *p; + + if ((i%2) == 1) { + int v; + sscanf (buf, "%x", &v); + target[*target_size] = v & 0xff; + (*target_size)++; + } + + i++; + } + p++; + } +} + +static +bool +_isBetterCertificate ( + IN const unsigned char * const pCurrent, + IN const int nCurrentSize, + IN const unsigned char * const pNew, + IN const int nNewSize +) { + /* + * This function compare the notBefore + * and select the most recent certificate + * it does not deal with timezones... + * When openssl will have ASN1_TIME compare function + * it should be used. + */ + + X509 *x509Current = NULL, *x509New = NULL; + char szNotBeforeCurrent[1024], szNotBeforeNew[1024]; + + /* + * First certificae + * always select + */ + if (nCurrentSize == 0) { + return true; + } + + szNotBeforeCurrent[0] = '\0'; + szNotBeforeNew[0] = '\0'; + + x509Current = X509_new (); + x509New = X509_new (); + + if (x509Current != NULL && x509New != NULL) { + const unsigned char *p1, *p2; + + p1 = pCurrent; + p2 = pNew; + if ( + d2i_X509 (&x509Current, (unsigned char **)&p1, nCurrentSize) && + d2i_X509 (&x509New, (unsigned char **)&p2, nNewSize) + ) { + ASN1_TIME *notBeforeCurrent = X509_get_notBefore (x509Current); + ASN1_TIME *notBeforeNew = X509_get_notBefore (x509New); + + if ( + notBeforeCurrent != NULL && + notBeforeNew != NULL && + notBeforeCurrent->length < (int) sizeof (szNotBeforeCurrent) - 1 && + notBeforeNew->length < (int) sizeof (szNotBeforeNew) - 1 + ) { + memmove (szNotBeforeCurrent, notBeforeCurrent->data, notBeforeCurrent->length); + szNotBeforeCurrent[notBeforeCurrent->length] = '\0'; + memmove (szNotBeforeNew, notBeforeNew->data, notBeforeNew->length); + szNotBeforeNew[notBeforeNew->length] = '\0'; + } + } + } + + if (x509Current != NULL) { + X509_free (x509Current); + x509Current = NULL; + } + if (x509New != NULL) { + X509_free (x509New); + x509New = NULL; + } + + return strcmp (szNotBeforeCurrent, szNotBeforeNew) < 0; +} + +/*======================================== + * Low level PKCS#11 functions + */ + +static +CK_RV +_pkcs11_getSlotById ( + IN const pkcs11_session_t pkcs11_session, + IN const char * const szSlot +) { + pkcs11_provider_t provider; + int provider_number; + int slot_number; + int i; + + ASSERT (pkcs11_session!=NULL); + ASSERT (szSlot!=NULL); + + if (strchr (szSlot, ':') == NULL) { + provider_number = 0; + slot_number = atoi (szSlot); + } + else { + sscanf (szSlot, "%d:%d", &provider_number, &slot_number); + } + + for ( + i=0, provider=pkcs11_provider; + i < provider_number && provider != NULL; + i++, provider = provider->next + ); + + if ( + provider == NULL || + ( + provider != NULL && + !provider->fEnabled + ) + ) { + return CKR_SLOT_ID_INVALID; + } + + pkcs11_session->provider = provider; + pkcs11_session->slot = slot_number; + return CKR_OK; +} + +static +CK_RV +_pkcs11_getSlotByName ( + IN const pkcs11_session_t pkcs11_session, + IN const char * const szName +) { + CK_SLOT_ID slots[1024]; + CK_ULONG slotnum; + CK_SLOT_ID s; + CK_RV rv; + + pkcs11_provider_t provider; + bool fFound = false; + + ASSERT (pkcs11_session!=NULL); + ASSERT (szName!=NULL); + + for ( + provider = pkcs11_provider; + ( + provider != NULL && + !fFound + ); + provider = provider->next + ) { + if (!provider->fEnabled) { + continue; + } + + slotnum = sizeof (slots) / sizeof (CK_SLOT_ID); + if ( + (rv = provider->f->C_GetSlotList ( + TRUE, + slots, + &slotnum + )) == CKR_OK + ) { + for (s=0;!fFound && s<slotnum;s++) { + CK_SLOT_INFO info; + + if ( + (rv = provider->f->C_GetSlotInfo ( + slots[s], + &info + )) == CKR_OK + ) { + char szCurrentName[sizeof (info.slotDescription)+1]; + + _fixupFixedString ( + info.slotDescription, + szCurrentName, + sizeof (info.slotDescription) + ); + + if (!strcmp (szCurrentName, szName)) { + fFound = true; + pkcs11_session->provider = provider; + pkcs11_session->slot = slots[s]; + } + } + } + } + } + + return fFound ? CKR_OK : CKR_SLOT_ID_INVALID; +} + +static +CK_RV +_pkcs11_getSlotByLabel ( + IN const pkcs11_session_t pkcs11_session, + IN const char * const szLabel +) { + CK_SLOT_ID slots[1024]; + CK_ULONG slotnum; + CK_SLOT_ID s; + CK_RV rv; + + pkcs11_provider_t provider; + bool fFound = false; + + ASSERT (pkcs11_session!=NULL); + ASSERT (szLabel!=NULL); + + for ( + provider = pkcs11_provider; + ( + provider != NULL && + !fFound + ); + provider = provider->next + ) { + if (!provider->fEnabled) { + continue; + } + + slotnum = sizeof (slots) / sizeof (CK_SLOT_ID); + if ( + (rv = provider->f->C_GetSlotList ( + TRUE, + slots, + &slotnum + )) == CKR_OK + ) { + for (s=0;!fFound && s<slotnum;s++) { + CK_TOKEN_INFO info; + + if ( + (rv = provider->f->C_GetTokenInfo ( + slots[s], + &info + )) == CKR_OK + ) { + char szCurrentLabel[sizeof (info.label)+1]; + + _fixupFixedString ( + info.label, + szCurrentLabel, + sizeof (info.label) + ); + + if (!strcmp (szCurrentLabel, szLabel)) { + fFound = true; + pkcs11_session->provider = provider; + pkcs11_session->slot = slots[s]; + } + } + } + } + } + + return fFound ? CKR_OK : CKR_SLOT_ID_INVALID; +} + +static +CK_RV +_pkcs11_getObjectById ( + IN const pkcs11_session_t pkcs11_session, + IN const CK_OBJECT_CLASS class, + IN const unsigned char * const id, + IN const int id_size, + OUT CK_OBJECT_HANDLE * const handle +) { + CK_ULONG count; + bool fFound = false; + CK_RV rv; + + CK_ATTRIBUTE filter[] = { + {CKA_CLASS, (void *)&class, sizeof (class)}, + {CKA_ID, (void *)id, id_size} + }; + + ASSERT (pkcs11_session!=NULL); + ASSERT (id!=NULL); + ASSERT (handle!=NULL); + + if ( + (rv = pkcs11_session->provider->f->C_FindObjectsInit ( + pkcs11_session->session, + filter, + sizeof (filter) / sizeof (CK_ATTRIBUTE) + )) != CKR_OK + ) { + return rv; + } + + if ( + (rv = pkcs11_session->provider->f->C_FindObjects ( + pkcs11_session->session, + handle, + 1, + &count + )) == CKR_OK + ) { + if (count > 0) { + fFound = true; + } + } + + pkcs11_session->provider->f->C_FindObjectsFinal ( + pkcs11_session->session + ); + + return fFound ? CKR_OK : CKR_FUNCTION_REJECTED; +} + +static +CK_RV +_pkcs11_loadCertificate ( + IN const pkcs11_session_t pkcs11_session, + IN const char * const szIdType, + IN const char * const szId +) { + CK_OBJECT_HANDLE objects[10]; + CK_ULONG objects_found; + CK_RV rv; + + unsigned char selected_id[PKCS11_MAX_ATTRIBUTE_SIZE]; + int selected_id_size = 0; + unsigned char selected_certificate[PKCS11_MAX_ATTRIBUTE_SIZE]; + int selected_certificate_size = 0; + + CK_OBJECT_CLASS cert_filter_class = CKO_CERTIFICATE; + unsigned char cert_filter_by[PKCS11_MAX_ATTRIBUTE_SIZE]; + CK_ATTRIBUTE cert_filter[] = { + {CKA_CLASS, &cert_filter_class, sizeof (cert_filter_class)}, + {0, cert_filter_by, 0} + }; + + ASSERT (pkcs11_session!=NULL); + ASSERT (szIdType!=NULL); + ASSERT (szId!=NULL); + + if (!strcmp (szIdType, "label")) { + cert_filter[1].type = CKA_LABEL; + cert_filter[1].ulValueLen = (CK_ULONG)( + strlen (szId) < sizeof (cert_filter_by) ? + strlen (szId) : + sizeof (cert_filter_by) + ); + memmove ( + cert_filter_by, + szId, + cert_filter[1].ulValueLen + ); + } + else if (!strcmp (szIdType, "id")) { + int s = sizeof (cert_filter_by); + + cert_filter[1].type = CKA_ID; + _hexToBinary ( + szId, + cert_filter_by, + &s + ); + cert_filter[1].ulValueLen = s; + } + else if (!strcmp (szIdType, "subject")) { + memmove (&cert_filter[1], &cert_filter[0], sizeof (CK_ATTRIBUTE)); + } + else { + return CKR_ARGUMENTS_BAD; + } + + if ( + (rv = pkcs11_session->provider->f->C_FindObjectsInit ( + pkcs11_session->session, + cert_filter, + sizeof (cert_filter) / sizeof (CK_ATTRIBUTE) + )) != CKR_OK + ) { + return rv; + } + + while ( + (rv = pkcs11_session->provider->f->C_FindObjects ( + pkcs11_session->session, + objects, + sizeof (objects) / sizeof (CK_OBJECT_HANDLE), + &objects_found + )) == CKR_OK && + objects_found > 0 + ) { + CK_ULONG i; + + for (i=0;i<objects_found;i++) { + unsigned char attrs_id[PKCS11_MAX_ATTRIBUTE_SIZE]; + unsigned char attrs_value[PKCS11_MAX_ATTRIBUTE_SIZE]; + CK_ATTRIBUTE attrs[] = { + {CKA_ID, attrs_id, sizeof (attrs_id)}, + {CKA_VALUE, attrs_value, sizeof (attrs_value)} + }; + + if ( + pkcs11_session->provider->f->C_GetAttributeValue ( + pkcs11_session->session, + objects[i], + attrs, + sizeof (attrs) / sizeof (CK_ATTRIBUTE) + ) == CKR_OK + ) { + bool fSelected = false; + + if (!strcmp (szIdType, "subject")) { + X509 *x509 = NULL; + char szSubject[1024]; + unsigned char *p; + + x509 = X509_new (); + + p = attrs_value; + if (d2i_X509 (&x509, &p, attrs[1].ulValueLen)) { + X509_NAME_oneline ( + X509_get_subject_name (x509), + szSubject, + sizeof (szSubject) + ); + szSubject[sizeof (szSubject) - 1] = '\0'; + } + + if (x509 != NULL) { + X509_free (x509); + x509 = NULL; + } + + if (!strcmp (szId, szSubject)) { + fSelected = true; + } + } + else { + fSelected = true; + } + + if ( + fSelected && + _isBetterCertificate ( + selected_certificate, + selected_certificate_size, + attrs_value, + attrs[1].ulValueLen + ) + ) { + selected_certificate_size = attrs[1].ulValueLen; + memmove ( + selected_certificate, + attrs_value, + selected_certificate_size + ); + selected_id_size = attrs[0].ulValueLen; + memmove ( + selected_id, + attrs_id, + selected_id_size + ); + } + } + } + } + + pkcs11_session->provider->f->C_FindObjectsFinal ( + pkcs11_session->session + ); + + if (selected_certificate_size == 0) { + return CKR_ATTRIBUTE_VALUE_INVALID; + } + + if ((pkcs11_session->certificate = (unsigned char *)malloc (selected_certificate_size)) == NULL) { + return CKR_HOST_MEMORY; + } + pkcs11_session->certificate_size = selected_certificate_size; + memmove ( + pkcs11_session->certificate, + selected_certificate, + selected_certificate_size + ); + if ((pkcs11_session->certificate_id = (unsigned char *)malloc (selected_id_size)) == NULL) { + return CKR_HOST_MEMORY; + } + pkcs11_session->certificate_id_size = selected_id_size; + memmove ( + pkcs11_session->certificate_id, + selected_id, + selected_id_size + ); + + return CKR_OK; +} + +static +CK_RV +_pkcs11_loadKeyProperties ( + IN const pkcs11_session_t pkcs11_session +) { + CK_OBJECT_HANDLE key; + CK_RV rv; + + CK_BBOOL key_attrs_sign_recover; + CK_BBOOL key_attrs_sign; + CK_ATTRIBUTE key_attrs[] = { + {CKA_SIGN, &key_attrs_sign_recover, sizeof (key_attrs_sign_recover)}, + {CKA_SIGN_RECOVER, &key_attrs_sign, sizeof (key_attrs_sign)} + }; + + ASSERT (pkcs11_session!=NULL); + + if (!strcmp (pkcs11_session->provider->szSignMode, "recover")) { + pkcs11_session->fKeySignRecover = true; + } + else if (!strcmp (pkcs11_session->provider->szSignMode, "sign")) { + pkcs11_session->fKeySignRecover = false; + } + else { + if ( + (rv = _pkcs11_getObjectById ( + pkcs11_session, + CKO_PRIVATE_KEY, + pkcs11_session->certificate_id, + pkcs11_session->certificate_id_size, + &key + )) != CKR_OK + ) { + return rv; + } + + if ( + pkcs11_session->provider->f->C_GetAttributeValue ( + pkcs11_session->session, + key, + key_attrs, + sizeof (key_attrs) / sizeof (CK_ATTRIBUTE) + ) == CKR_OK + ) { + if (key_attrs_sign_recover != CK_FALSE) { + pkcs11_session->fKeySignRecover = true; + } + else if (key_attrs_sign != CK_FALSE) { + pkcs11_session->fKeySignRecover = false; + } + else { + return CKR_KEY_TYPE_INCONSISTENT; + } + } + + } + + return CKR_OK; +} + +/*======================================= + * Simplified PKCS#11 functions + */ + +static +CK_RV +pkcs11_addProvider ( + IN const char * const szProvider, + IN const char * const szSignMode +) { + pkcs11_provider_t provider = NULL; + CK_C_GetFunctionList gfl = NULL; + CK_RV rv = CKR_OK; + + ASSERT (szProvider!=NULL); + + if ( + rv == CKR_OK && + (provider = (pkcs11_provider_t)malloc (sizeof (struct pkcs11_provider_s))) == NULL + ) { + rv = CKR_HOST_MEMORY; + } + + if (rv == CKR_OK) { + memset (provider, 0, sizeof (struct pkcs11_provider_s)); + if (szSignMode == NULL) { + provider->szSignMode = strdup ("auto"); + } + else { + provider->szSignMode = strdup (szSignMode); + } + if (provider->szSignMode == NULL) { + rv = CKR_HOST_MEMORY; + } + } + + if (rv == CKR_OK) { +#if defined(WIN32) + provider->hLibrary = LoadLibrary (szProvider); +#else + provider->hLibrary = dlopen (szProvider, RTLD_NOW); +#endif + if (provider->hLibrary == NULL) { + rv = CKR_FUNCTION_FAILED; + } + } + + if (rv == CKR_OK) { +#if defined(WIN32) + gfl = (CK_C_GetFunctionList)GetProcAddress ( + provider->hLibrary, + "C_GetFunctionList" + ); +#else + gfl = (CK_C_GetFunctionList)dlsym ( + provider->hLibrary, + "C_GetFunctionList" + ); +#endif + if (gfl == NULL) { + rv = CKR_FUNCTION_FAILED; + } + } + + if (rv == CKR_OK) { + rv = gfl (&provider->f); + } + + if (rv == CKR_OK) { + if ((rv = provider->f->C_Initialize (NULL)) != CKR_OK) { + if (rv == CKR_CRYPTOKI_ALREADY_INITIALIZED) { + rv = CKR_OK; + } + } + else { + provider->fShouldFinalize = true; + } + } + + if (rv == CKR_OK) { + provider->fEnabled = true; + } + + if (provider != NULL) { + if (pkcs11_provider == NULL) { + pkcs11_provider = provider; + } + else { + pkcs11_provider_t last = NULL; + + for ( + last = pkcs11_provider; + last->next != NULL; + last = last->next + ); + last->next = provider; + } + } + + return rv; +} + +static +CK_RV +pkcs11_finalize () { + + pkcs11_provider_t last = NULL; + + for ( + ; + pkcs11_provider != NULL; + pkcs11_provider = pkcs11_provider->next + ) { + if (last != NULL) { + free (last); + } + last = pkcs11_provider; + + if (pkcs11_provider->szSignMode != NULL) { + free (pkcs11_provider->szSignMode); + pkcs11_provider->szSignMode = NULL; + } + + if (pkcs11_provider->fShouldFinalize) { + pkcs11_provider->f->C_Finalize (NULL); + pkcs11_provider->fShouldFinalize = false; + } + + if (pkcs11_provider->f != NULL) { + pkcs11_provider->f = NULL; + } + + if (pkcs11_provider->hLibrary != NULL) { +#if defined(WIN32) + FreeLibrary (pkcs11_provider->hLibrary); +#else + dlclose (pkcs11_provider->hLibrary); +#endif + pkcs11_provider->hLibrary = NULL; + } + } + + if (last != NULL) { + free (last); + } + + return CKR_OK; +} + +static +CK_RV +pkcs11_createSession ( + IN const char * const szSlotType, + IN const char * const szSlot, + IN const char * const szIdType, + IN const char * const szId, + IN const char * const szPIN, + IN const bool fProtectedAuthentication, + OUT pkcs11_session_t * const p_pkcs11_session +) { + pkcs11_session_t pkcs11_session; + CK_RV rv = CKR_OK; + + ASSERT (szSlotType!=NULL); + ASSERT (szSlot!=NULL); + ASSERT (szIdType!=NULL); + ASSERT (szId!=NULL); + ASSERT (szPIN!=NULL); + ASSERT (p_pkcs11_session!=NULL); + + if ( + rv == CKR_OK && + (pkcs11_session = (pkcs11_session_t)malloc (sizeof (struct pkcs11_session_s))) == NULL + ) { + rv = CKR_HOST_MEMORY; + } + + if (rv == CKR_OK) { + *p_pkcs11_session = pkcs11_session; + memset (pkcs11_session, 0, sizeof (struct pkcs11_session_s)); + } + + if ( + rv == CKR_OK && + !fProtectedAuthentication + ) { + if ((pkcs11_session->szPIN = strdup (szPIN)) == NULL) { + rv = CKR_HOST_MEMORY; + } + } + + if (rv == CKR_OK) { + pkcs11_session->fLoginFailed = false; + pkcs11_session->key = -1; + pkcs11_session->session = -1; + + if (!strcmp (szSlotType, "id")) { + rv = _pkcs11_getSlotById (pkcs11_session, szSlot); + } + else if (!strcmp (szSlotType, "name")) { + rv = _pkcs11_getSlotByName (pkcs11_session, szSlot); + } + else if (!strcmp (szSlotType, "label")) { + rv = _pkcs11_getSlotByLabel (pkcs11_session, szSlot); + } + else { + rv = CKR_ARGUMENTS_BAD; + } + } + + if (rv == CKR_OK) { + rv = pkcs11_login ( + pkcs11_session + ); + } + + if (rv == CKR_OK) { + rv = _pkcs11_loadCertificate ( + pkcs11_session, + szIdType, + szId + ); + } + + if (rv == CKR_OK) { + rv = _pkcs11_loadKeyProperties ( + pkcs11_session + ); + } + + pkcs11_logout ( + pkcs11_session + ); + + return rv; +} + +CK_RV +pkcs11_freeSession ( + IN const pkcs11_session_t pkcs11_session +) { + if (pkcs11_session != NULL) { + pkcs11_logout (pkcs11_session); + + if (pkcs11_session->szPIN != NULL) { + free (pkcs11_session->szPIN); + } + if (pkcs11_session->certificate != NULL) { + free (pkcs11_session->certificate); + } + if (pkcs11_session->certificate_id != NULL) { + free (pkcs11_session->certificate_id); + } + + free (pkcs11_session); + } + + return CKR_OK; +} + +static +CK_RV +pkcs11_login ( + IN const pkcs11_session_t pkcs11_session +) { + CK_RV rv = CKR_OK; + + ASSERT (pkcs11_session!=NULL); + + pkcs11_logout (pkcs11_session); + + if (rv == CKR_OK) { + rv = pkcs11_session->provider->f->C_OpenSession ( + pkcs11_session->slot, + CKF_SERIAL_SESSION, + NULL_PTR, + NULL_PTR, + &pkcs11_session->session + ); + } + + /* + * Do not lock the token + */ + if ( + rv == CKR_OK && + pkcs11_session->fLoginFailed + ) { + rv = CKR_PIN_INVALID; + } + + if ( + rv == CKR_OK && + (rv = pkcs11_session->provider->f->C_Login ( + pkcs11_session->session, + CKU_USER, + pkcs11_session->szPIN, + pkcs11_session->szPIN == NULL ? 0 : (CK_ULONG)strlen (pkcs11_session->szPIN) + )) != CKR_OK + ) { + if (rv == CKR_USER_ALREADY_LOGGED_IN) { + rv = CKR_OK; + } + else { + pkcs11_session->fLoginFailed = true; + } + } + + if ( + rv == CKR_OK && + pkcs11_session->certificate_id != NULL + ) { + rv = _pkcs11_getObjectById ( + pkcs11_session, + CKO_PRIVATE_KEY, + pkcs11_session->certificate_id, + pkcs11_session->certificate_id_size, + &pkcs11_session->key + ); + } + + if (rv != CKR_OK) { + pkcs11_logout (pkcs11_session); + } + + return rv; +} + +static +CK_RV +pkcs11_logout ( + IN const pkcs11_session_t pkcs11_session +) { + ASSERT (pkcs11_session!=NULL); + + if (pkcs11_session->session != (unsigned int)-1) { + pkcs11_session->provider->f->C_Logout (pkcs11_session->session); + pkcs11_session->provider->f->C_CloseSession (pkcs11_session->session); + pkcs11_session->key = -1; + pkcs11_session->session = -1; + } + + return CKR_OK; +} + +static +CK_RV +pkcs11_sign ( + IN const pkcs11_session_t pkcs11_session, + IN const CK_MECHANISM_TYPE mech_type, + IN const unsigned char * const source, + IN const int source_size, + OUT unsigned char * const target, + IN OUT int * const target_size +) { + CK_MECHANISM mech = { + mech_type, NULL, 0 + }; + CK_ULONG size; + CK_RV rv; + + ASSERT (pkcs11_session!=NULL); + ASSERT (source!=NULL); + ASSERT (target_size!=NULL); + + if ( + (rv = pkcs11_session->provider->f->C_SignInit ( + pkcs11_session->session, + &mech, + pkcs11_session->key + )) != CKR_OK + ) { + return rv; + } + + size = *target_size; + rv = pkcs11_session->provider->f->C_Sign ( + pkcs11_session->session, + (CK_BYTE_PTR)source, + source_size, + (CK_BYTE_PTR)target, + &size + ); + + *target_size = (int)size; + + return rv; +} + +static +CK_RV +pkcs11_signRecover ( + IN const pkcs11_session_t pkcs11_session, + IN const CK_MECHANISM_TYPE mech_type, + IN const unsigned char * const source, + IN const int source_size, + OUT unsigned char * const target, + IN OUT int * const target_size +) { + CK_MECHANISM mech = { + mech_type, NULL, 0 + }; + CK_ULONG size; + CK_RV rv; + + ASSERT (pkcs11_session!=NULL); + ASSERT (source!=NULL); + ASSERT (target_size!=NULL); + + if ( + (rv = pkcs11_session->provider->f->C_SignRecoverInit ( + pkcs11_session->session, + &mech, + pkcs11_session->key + )) != CKR_OK + ) { + return rv; + } + + size = *target_size; + rv = pkcs11_session->provider->f->C_SignRecover ( + pkcs11_session->session, + (CK_BYTE_PTR)source, + source_size, + (CK_BYTE_PTR)target, + &size + ); + + *target_size = (int)size; + + return rv; +} + +static +CK_RV +pkcs11_decrypt ( + IN const pkcs11_session_t pkcs11_session, + IN const CK_MECHANISM_TYPE mech_type, + IN const unsigned char * const source, + IN const int source_size, + OUT unsigned char * const target, + IN OUT int * const target_size +) { + CK_MECHANISM mech = { + mech_type, NULL, 0 + }; + CK_ULONG size; + CK_RV rv; + + ASSERT (pkcs11_session!=NULL); + ASSERT (source!=NULL); + ASSERT (target_size!=NULL); + + if ( + (rv = pkcs11_session->provider->f->C_DecryptInit ( + pkcs11_session->session, + &mech, + pkcs11_session->key + )) != CKR_OK + ) { + return rv; + } + + size = *target_size; + rv = pkcs11_session->provider->f->C_Decrypt ( + pkcs11_session->session, + (CK_BYTE_PTR)source, + source_size, + (CK_BYTE_PTR)target, + &size + ); + + *target_size = (int)size; + + return rv; +} + +static +CK_RV +pkcs11_getCertificate ( + IN const pkcs11_session_t pkcs11_session, + OUT char * const certificate, + IN OUT int * const certificate_size +) { + ASSERT (certificate_size!=NULL); + + *certificate_size = pkcs11_session->certificate_size; + + if (certificate == NULL) { + return CKR_OK; + } + + if (*certificate_size > pkcs11_session->certificate_size) { + return CKR_BUFFER_TOO_SMALL; + } + + memmove (certificate, pkcs11_session->certificate, *certificate_size); + + return CKR_OK; +} + +static +char * +pkcs11_getMessage ( + IN const int rv +) { + switch (rv) { + case CKR_OK: return "CKR_OK"; + case CKR_CANCEL: return "CKR_CANCEL"; + case CKR_HOST_MEMORY: return "CKR_HOST_MEMORY"; + case CKR_SLOT_ID_INVALID: return "CKR_SLOT_ID_INVALID"; + case CKR_GENERAL_ERROR: return "CKR_GENERAL_ERROR"; + case CKR_FUNCTION_FAILED: return "CKR_FUNCTION_FAILED"; + case CKR_ARGUMENTS_BAD: return "CKR_ARGUMENTS_BAD"; + case CKR_NO_EVENT: return "CKR_NO_EVENT"; + case CKR_NEED_TO_CREATE_THREADS: return "CKR_NEED_TO_CREATE_THREADS"; + case CKR_CANT_LOCK: return "CKR_CANT_LOCK"; + case CKR_ATTRIBUTE_READ_ONLY: return "CKR_ATTRIBUTE_READ_ONLY"; + case CKR_ATTRIBUTE_SENSITIVE: return "CKR_ATTRIBUTE_SENSITIVE"; + case CKR_ATTRIBUTE_TYPE_INVALID: return "CKR_ATTRIBUTE_TYPE_INVALID"; + case CKR_ATTRIBUTE_VALUE_INVALID: return "CKR_ATTRIBUTE_VALUE_INVALID"; + case CKR_DATA_INVALID: return "CKR_DATA_INVALID"; + case CKR_DATA_LEN_RANGE: return "CKR_DATA_LEN_RANGE"; + case CKR_DEVICE_ERROR: return "CKR_DEVICE_ERROR"; + case CKR_DEVICE_MEMORY: return "CKR_DEVICE_MEMORY"; + case CKR_DEVICE_REMOVED: return "CKR_DEVICE_REMOVED"; + case CKR_ENCRYPTED_DATA_INVALID: return "CKR_ENCRYPTED_DATA_INVALID"; + case CKR_ENCRYPTED_DATA_LEN_RANGE: return "CKR_ENCRYPTED_DATA_LEN_RANGE"; + case CKR_FUNCTION_CANCELED: return "CKR_FUNCTION_CANCELED"; + case CKR_FUNCTION_NOT_PARALLEL: return "CKR_FUNCTION_NOT_PARALLEL"; + case CKR_FUNCTION_NOT_SUPPORTED: return "CKR_FUNCTION_NOT_SUPPORTED"; + case CKR_KEY_HANDLE_INVALID: return "CKR_KEY_HANDLE_INVALID"; + case CKR_KEY_SIZE_RANGE: return "CKR_KEY_SIZE_RANGE"; + case CKR_KEY_TYPE_INCONSISTENT: return "CKR_KEY_TYPE_INCONSISTENT"; + case CKR_KEY_NOT_NEEDED: return "CKR_KEY_NOT_NEEDED"; + case CKR_KEY_CHANGED: return "CKR_KEY_CHANGED"; + case CKR_KEY_NEEDED: return "CKR_KEY_NEEDED"; + case CKR_KEY_INDIGESTIBLE: return "CKR_KEY_INDIGESTIBLE"; + case CKR_KEY_FUNCTION_NOT_PERMITTED: return "CKR_KEY_FUNCTION_NOT_PERMITTED"; + case CKR_KEY_NOT_WRAPPABLE: return "CKR_KEY_NOT_WRAPPABLE"; + case CKR_KEY_UNEXTRACTABLE: return "CKR_KEY_UNEXTRACTABLE"; + case CKR_MECHANISM_INVALID: return "CKR_MECHANISM_INVALID"; + case CKR_MECHANISM_PARAM_INVALID: return "CKR_MECHANISM_PARAM_INVALID"; + case CKR_OBJECT_HANDLE_INVALID: return "CKR_OBJECT_HANDLE_INVALID"; + case CKR_OPERATION_ACTIVE: return "CKR_OPERATION_ACTIVE"; + case CKR_OPERATION_NOT_INITIALIZED: return "CKR_OPERATION_NOT_INITIALIZED"; + case CKR_PIN_INCORRECT: return "CKR_PIN_INCORRECT"; + case CKR_PIN_INVALID: return "CKR_PIN_INVALID"; + case CKR_PIN_LEN_RANGE: return "CKR_PIN_LEN_RANGE"; + case CKR_PIN_EXPIRED: return "CKR_PIN_EXPIRED"; + case CKR_PIN_LOCKED: return "CKR_PIN_LOCKED"; + case CKR_SESSION_CLOSED: return "CKR_SESSION_CLOSED"; + case CKR_SESSION_COUNT: return "CKR_SESSION_COUNT"; + case CKR_SESSION_HANDLE_INVALID: return "CKR_SESSION_HANDLE_INVALID"; + case CKR_SESSION_PARALLEL_NOT_SUPPORTED: return "CKR_SESSION_PARALLEL_NOT_SUPPORTED"; + case CKR_SESSION_READ_ONLY: return "CKR_SESSION_READ_ONLY"; + case CKR_SESSION_EXISTS: return "CKR_SESSION_EXISTS"; + case CKR_SESSION_READ_ONLY_EXISTS: return "CKR_SESSION_READ_ONLY_EXISTS"; + case CKR_SESSION_READ_WRITE_SO_EXISTS: return "CKR_SESSION_READ_WRITE_SO_EXISTS"; + case CKR_SIGNATURE_INVALID: return "CKR_SIGNATURE_INVALID"; + case CKR_SIGNATURE_LEN_RANGE: return "CKR_SIGNATURE_LEN_RANGE"; + case CKR_TEMPLATE_INCOMPLETE: return "CKR_TEMPLATE_INCOMPLETE"; + case CKR_TEMPLATE_INCONSISTENT: return "CKR_TEMPLATE_INCONSISTENT"; + case CKR_TOKEN_NOT_PRESENT: return "CKR_TOKEN_NOT_PRESENT"; + case CKR_TOKEN_NOT_RECOGNIZED: return "CKR_TOKEN_NOT_RECOGNIZED"; + case CKR_TOKEN_WRITE_PROTECTED: return "CKR_TOKEN_WRITE_PROTECTED"; + case CKR_UNWRAPPING_KEY_HANDLE_INVALID: return "CKR_UNWRAPPING_KEY_HANDLE_INVALID"; + case CKR_UNWRAPPING_KEY_SIZE_RANGE: return "CKR_UNWRAPPING_KEY_SIZE_RANGE"; + case CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT: return "CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT"; + case CKR_USER_ALREADY_LOGGED_IN: return "CKR_USER_ALREADY_LOGGED_IN"; + case CKR_USER_NOT_LOGGED_IN: return "CKR_USER_NOT_LOGGED_IN"; + case CKR_USER_PIN_NOT_INITIALIZED: return "CKR_USER_PIN_NOT_INITIALIZED"; + case CKR_USER_TYPE_INVALID: return "CKR_USER_TYPE_INVALID"; + case CKR_USER_ANOTHER_ALREADY_LOGGED_IN: return "CKR_USER_ANOTHER_ALREADY_LOGGED_IN"; + case CKR_USER_TOO_MANY_TYPES: return "CKR_USER_TOO_MANY_TYPES"; + case CKR_WRAPPED_KEY_INVALID: return "CKR_WRAPPED_KEY_INVALID"; + case CKR_WRAPPED_KEY_LEN_RANGE: return "CKR_WRAPPED_KEY_LEN_RANGE"; + case CKR_WRAPPING_KEY_HANDLE_INVALID: return "CKR_WRAPPING_KEY_HANDLE_INVALID"; + case CKR_WRAPPING_KEY_SIZE_RANGE: return "CKR_WRAPPING_KEY_SIZE_RANGE"; + case CKR_WRAPPING_KEY_TYPE_INCONSISTENT: return "CKR_WRAPPING_KEY_TYPE_INCONSISTENT"; + case CKR_RANDOM_SEED_NOT_SUPPORTED: return "CKR_RANDOM_SEED_NOT_SUPPORTED"; + case CKR_RANDOM_NO_RNG: return "CKR_RANDOM_NO_RNG"; + case CKR_DOMAIN_PARAMS_INVALID: return "CKR_DOMAIN_PARAMS_INVALID"; + case CKR_BUFFER_TOO_SMALL: return "CKR_BUFFER_TOO_SMALL"; + case CKR_SAVED_STATE_INVALID: return "CKR_SAVED_STATE_INVALID"; + case CKR_INFORMATION_SENSITIVE: return "CKR_INFORMATION_SENSITIVE"; + case CKR_STATE_UNSAVEABLE: return "CKR_STATE_UNSAVEABLE"; + case CKR_CRYPTOKI_NOT_INITIALIZED: return "CKR_CRYPTOKI_NOT_INITIALIZED"; + case CKR_CRYPTOKI_ALREADY_INITIALIZED: return "CKR_CRYPTOKI_ALREADY_INITIALIZED"; + case CKR_MUTEX_BAD: return "CKR_MUTEX_BAD"; + case CKR_MUTEX_NOT_LOCKED: return "CKR_MUTEX_NOT_LOCKED"; + case CKR_FUNCTION_REJECTED: return "CKR_FUNCTION_REJECTED"; + case CKR_VENDOR_DEFINED: return "CKR_VENDOR_DEFINED"; + default: return "Unknown PKCS#11 error"; + } +} + +/*========================================== + * openvpn interface + */ + +typedef struct openssl_session_s { + int (*orig_finish)(RSA *rsa); + pkcs11_session_t pkcs11_session; +} *openssl_session_t; + +static +pkcs11_session_t +_openssl_get_pkcs11_session (const RSA *rsa) { + openssl_session_t session; + + ASSERT (rsa!=NULL); + session = (openssl_session_t)RSA_get_app_data (rsa); + ASSERT (session!=NULL); + ASSERT (session->pkcs11_session!=NULL); + + return session->pkcs11_session; +} + +static +int +openssl_pkcs11_priv_enc ( + int flen, + const unsigned char *from, + unsigned char *to, + RSA *rsa, + int padding +) { + msg(M_WARN, "PKCS#11: Private key encryption not supported"); + return -1; +} + +static +int +openssl_pkcs11_priv_dec ( + int flen, const unsigned char *from, + unsigned char *to, + RSA *rsa, + int padding +) { + pkcs11_session_t pkcs11_session = _openssl_get_pkcs11_session (rsa); + CK_RV rv = CKR_OK; + + msg ( + D_PKCS11_DEBUG, + "PKCS#11: openssl_pkcs11_priv_dec entered - flen=%d, from=%p, to=%p, rsa=%p, padding=%d", + flen, + from, + to, + rsa, + padding + ); + + ASSERT (from!=NULL); + ASSERT (to!=NULL); + + msg ( + D_SHOW_PKCS11, + "PKCS#11: Performing decryption using private key" + ); + + if (padding != RSA_PKCS1_PADDING) { + rv = CKR_ARGUMENTS_BAD; + } + + if ( + rv == CKR_OK && + (rv = pkcs11_login (pkcs11_session)) != CKR_OK + ) { + msg (M_WARN, "PKCS#11: Cannot login to token %ld:'%s'", rv, pkcs11_getMessage (rv)); + } + + if ( + rv == CKR_OK && + (rv = pkcs11_decrypt ( + pkcs11_session, + CKM_RSA_PKCS, + from, + flen, + to, + &flen + )) != CKR_OK + ) { + msg (M_WARN, "PKCS#11: Cannot decrypt using private key %ld:'%s'", rv, pkcs11_getMessage (rv)); + } + + pkcs11_logout (pkcs11_session); + + msg ( + D_PKCS11_DEBUG, + "PKCS#11: openssl_pkcs11_priv_dec - return rv=%ld", + rv + ); + + return rv == CKR_OK ? 1 : -1; +} + +static +int +openssl_pkcs11_sign ( + int type, + const unsigned char *m, + unsigned int m_len, + unsigned char *sigret, + unsigned int *siglen, + const RSA *rsa +) { + pkcs11_session_t pkcs11_session = _openssl_get_pkcs11_session (rsa); + CK_RV rv = CKR_OK; + + msg ( + D_PKCS11_DEBUG, + "PKCS#11: openssl_pkcs11_priv_sign entered - type=%d, m=%p, m_len=%u, signret=%p, signlen=%p, rsa=%p", + type, + m, + m_len, + sigret, + siglen, + rsa + ); + + ASSERT (m!=NULL); + ASSERT (siglen!=NULL); + + msg ( + D_SHOW_PKCS11, + "PKCS#11: Performing signature" + ); + + *siglen = RSA_size(rsa); + + if ( + rv == CKR_OK && + (rv = pkcs11_login (pkcs11_session)) != CKR_OK + ) { + msg (M_WARN, "PKCS#11: Cannot login to token %ld:'%s'", rv, pkcs11_getMessage (rv)); + } + + if (rv == CKR_OK) { + if (pkcs11_session->fKeySignRecover) { + if ( + (rv = pkcs11_signRecover ( + pkcs11_session, + CKM_RSA_PKCS, + m, + m_len, + sigret, + siglen + )) != CKR_OK + ) { + msg (M_WARN, "PKCS#11: Cannot perform signature-recover %ld:'%s'", rv, pkcs11_getMessage (rv)); + } + } + else { + if ( + (rv = pkcs11_sign ( + pkcs11_session, + CKM_RSA_PKCS, + m, + m_len, + sigret, + siglen + )) != CKR_OK + ) { + msg (M_WARN, "PKCS#11: Cannot perform signature %ld:'%s'", rv, pkcs11_getMessage (rv)); + } + } + } + + pkcs11_logout (pkcs11_session); + + msg ( + D_PKCS11_DEBUG, + "PKCS#11: openssl_pkcs11_priv_sign - return rv=%ld", + rv + ); + + return rv == CKR_OK ? 1 : -1; +} + +static +int +openssl_pkcs11_finish(RSA *rsa) { + pkcs11_session_t pkcs11_session = _openssl_get_pkcs11_session (rsa); + openssl_session_t openssl_session; + + msg ( + D_PKCS11_DEBUG, + "PKCS#11: openssl_pkcs11_finish - entered - rsa=%p", + rsa + ); + + openssl_session = (openssl_session_t)RSA_get_app_data (rsa); + + RSA_set_app_data (rsa, NULL); + pkcs11_freeSession (pkcs11_session); + + if (openssl_session->orig_finish != NULL) { + openssl_session->orig_finish (rsa); + +#ifdef BROKEN_OPENSSL_ENGINE + { + /* We get called TWICE here, once for + * releasing the key and also for + * releasing the engine. + * To prevent endless recursion, FIRST + * clear rsa->engine, THEN call engine->finish + */ + ENGINE *e = rsa->engine; + rsa->engine = NULL; + if (e) { + ENGINE_finish(e); + } + } +#endif + } + + free (openssl_session); + + msg ( + D_PKCS11_DEBUG, + "PKCS#11: openssl_pkcs11_finish - return" + ); + + return 1; +} + +static RSA_METHOD * +openssl_pkcs11_get_rsa_method(RSA *rsa) +{ + static RSA_METHOD smart_rsa; + const RSA_METHOD *def = RSA_get_default_method(); + + ASSERT (rsa); + + /* use the OpenSSL version */ + memmove (&smart_rsa, def, sizeof(smart_rsa)); + + /* save original */ + ((openssl_session_t)RSA_get_app_data (rsa))->orig_finish = def->finish; + + smart_rsa.name = "pkcs11"; + smart_rsa.rsa_priv_enc = openssl_pkcs11_priv_enc; + smart_rsa.rsa_priv_dec = openssl_pkcs11_priv_dec; + smart_rsa.rsa_sign = openssl_pkcs11_sign; + smart_rsa.finish = openssl_pkcs11_finish; + smart_rsa.flags = RSA_METHOD_FLAG_NO_CHECK | RSA_FLAG_EXT_PKEY; + return &smart_rsa; +} + + +#ifdef BROKEN_OPENSSL_ENGINE +static void broken_openssl_init() __attribute__ ((constructor)); +static void broken_openssl_init() +{ + SSL_library_init(); + ENGINE_load_openssl(); + ENGINE_register_all_RSA(); +} +#endif + +int +SSL_CTX_use_pkcs11 ( + IN OUT SSL_CTX * const ssl_ctx, + IN const char * const pkcs11_slot_type, + IN const char * const pkcs11_slot, + IN const char * const pkcs11_id_type, + IN const char * const pkcs11_id, + IN const char * const pin, + IN const bool pkcs11_protected_authentication +) { + X509 *x509 = NULL; + RSA *rsa = NULL; + EVP_PKEY *pubkey = NULL; + openssl_session_t openssl_session = NULL; + bool fShouldFreeOpenSSLSession = true; + CK_RV rv = CKR_OK; + + unsigned char certificate[10*1024]; + int certificate_size; + unsigned char *p; + bool fOK = true; + + msg ( + D_PKCS11_DEBUG, + "PKCS#11: SSL_CTX_use_pkcs11 - entered - ssl_ctx=%p, pkcs11_slot_type='%s', pkcs11_slot='%s', pkcs11_id_type='%s', pkcs11_id='%s', pkcs11_protected_authentication=%d", + ssl_ctx, + pkcs11_slot_type, + pkcs11_slot, + pkcs11_id_type, + pkcs11_id, + pkcs11_protected_authentication ? 1 : 0 + ); + + ASSERT (ssl_ctx!=NULL); + ASSERT (pkcs11_slot_type!=NULL); + ASSERT (pkcs11_slot!=NULL); + ASSERT (pkcs11_id_type!=NULL); + ASSERT (pkcs11_id!=NULL); + if (!pkcs11_protected_authentication) { + ASSERT (pin!=NULL); + } + + if ( + fOK && + (openssl_session = (openssl_session_t)malloc (sizeof (struct openssl_session_s))) == NULL + ) { + fOK = false; + msg (M_WARN, "PKCS#11: Cannot allocate memory"); + } + + if (fOK) { + memset (openssl_session, 0, sizeof (struct openssl_session_s)); + } + + if ( + fOK && + (rv = pkcs11_createSession ( + pkcs11_slot_type, + pkcs11_slot, + pkcs11_id_type, + pkcs11_id, + pin, + pkcs11_protected_authentication, + &openssl_session->pkcs11_session + )) != CKR_OK + ) { + fOK = false; + msg (M_WARN, "PKCS#11: Cannot set parameters %ld-'%s'", rv, pkcs11_getMessage (rv)); + } + + if ( + fOK && + (x509 = X509_new ()) == NULL + ) { + fOK = false; + msg (M_WARN, "PKCS#11: Unable to allocate certificate object"); + } + + certificate_size = sizeof (certificate); + if ( + fOK && + (rv = pkcs11_getCertificate ( + openssl_session->pkcs11_session, + certificate, + &certificate_size + )) != CKR_OK + ) { + fOK = false; + msg (M_WARN, "PKCS#11: Cannot read X.509 certificate from token %ld-'%s'", rv, pkcs11_getMessage (rv)); + } + + p = certificate; + if ( + fOK && + !d2i_X509 (&x509, &p, certificate_size) + ) { + fOK = false; + msg (M_WARN, "PKCS#11: Unable to parse X.509 certificate"); + } + + if ( + fOK && + (pubkey = X509_get_pubkey (x509)) == NULL + ) { + fOK = false; + msg (M_WARN, "PKCS#11: Cannot get public key"); + } + + if ( + fOK && + pubkey->type != EVP_PKEY_RSA + ) { + fOK = false; + msg (M_WARN, "PKCS#11: Invalid public key algorithm"); + } + + if ( + fOK && + (rsa = EVP_PKEY_get1_RSA (pubkey)) == NULL + ) { + fOK = false; + msg (M_WARN, "PKCS#11: Cannot get RSA key"); + } + + if (fOK) { + RSA_set_app_data (rsa, openssl_session); + RSA_set_method (rsa, openssl_pkcs11_get_rsa_method (rsa)); + rsa->flags |= RSA_FLAG_SIGN_VER; + + // it will be freed when rsa usage count will be zero + fShouldFreeOpenSSLSession = false; + } + +#ifdef BROKEN_OPENSSL_ENGINE + if (fOK) { + if (!rsa->engine) + rsa->engine = ENGINE_get_default_RSA(); + + ENGINE_set_RSA(ENGINE_get_default_RSA(), openssl_pkcs11_get_rsa_method(rsa)); + msg(M_WARN, "PKCS#11: OpenSSL engine support is broken! Workaround enabled"); + } +#endif + + if ( + fOK && + !SSL_CTX_use_certificate (ssl_ctx, x509) + ) { + fOK = false; + msg (M_WARN, "PKCS#11: Cannot set certificate for openssl"); + } + + if ( + fOK && + !SSL_CTX_use_RSAPrivateKey (ssl_ctx, rsa) + ) { + fOK = false; + msg (M_WARN, "PKCS#11: Cannot set private key for openssl"); + } + + /* + * openssl objects have reference + * count, so release them + */ + if (pubkey != NULL) { + EVP_PKEY_free (pubkey); + pubkey = NULL; + } + + if (x509 != NULL) { + X509_free (x509); + x509 = NULL; + } + + if (rsa != NULL) { + RSA_free (rsa); + rsa = NULL; + } + + if (fShouldFreeOpenSSLSession) { + if (openssl_session != NULL) { + if (openssl_session->pkcs11_session != NULL) { + pkcs11_freeSession (openssl_session->pkcs11_session); + } + free (openssl_session); + openssl_session = NULL; + } + } + + msg ( + D_PKCS11_DEBUG, + "PKCS#11: SSL_CTX_use_pkcs11 - return fOK=%d, rv=%ld", + fOK ? 1 : 0, + rv + ); + + return fOK; +} + +void +add_pkcs11 ( + IN const char * const provider, + IN const char * const sign_mode +) { + CK_RV rv; + + msg ( + D_PKCS11_DEBUG, + "PKCS#11: add_pkcs11 - entered - provider='%s', sign_mode='%s'", + provider, + sign_mode == NULL ? "default" : sign_mode + ); + + msg ( + M_INFO, + "PKCS#11: Adding PKCS#11 provider '%s'", + provider + ); + + if ((rv = pkcs11_addProvider (provider, sign_mode)) != CKR_OK) { + msg (M_WARN, "PKCS#11: Cannot initialize provider '%s' %ld-'%s'", provider, rv, pkcs11_getMessage (rv)); + } + + msg ( + D_PKCS11_DEBUG, + "PKCS#11: add_pkcs11 - return" + ); +} + +void +free_pkcs11 () { + msg ( + D_PKCS11_DEBUG, + "PKCS#11: free_pkcs11 - entered" + ); + + pkcs11_finalize (); + + msg ( + D_PKCS11_DEBUG, + "PKCS#11: free_pkcs11 - return" + ); +} + +void +show_pkcs11_slots ( + IN const int msglev, + IN const int warnlev, + IN const char * const provider +) { + CK_INFO info; + CK_SLOT_ID slots[1024]; + CK_ULONG slotnum; + CK_SLOT_ID s; + CK_RV rv; + + ASSERT (provider!=NULL); + + if ( + (rv = pkcs11_addProvider (provider, NULL)) != CKR_OK + ) { + msg (M_FATAL, "PKCS#11: Cannot initialize provider %ld-'%s'", rv, pkcs11_getMessage (rv)); + } + + if ( + (rv = pkcs11_provider->f->C_GetInfo (&info)) != CKR_OK + ) { + msg (warnlev, "PKCS#11: Cannot get PKCS#11 provider information %ld-'%s'", rv, pkcs11_getMessage (rv)); + } + else { + char szManufacturerID[sizeof (info.manufacturerID)+1]; + + _fixupFixedString ( + info.manufacturerID, + szManufacturerID, + sizeof (info.manufacturerID) + ); + + msg ( + msglev, + ( + "Provider Information:\n" + "\tcryptokiVersion: %u.%u\n" + "\tmanufacturerID: %s\n" + "\tflags: %d\n" + ), + info.cryptokiVersion.major, + info.cryptokiVersion.minor, + szManufacturerID, + (unsigned)info.flags + ); + } + + slotnum = sizeof (slots) / sizeof (CK_SLOT_ID); + if ( + (rv = pkcs11_provider->f->C_GetSlotList ( + FALSE, + slots, + &slotnum + )) != CKR_OK + ) { + msg (warnlev, "PKCS#11: Cannot get slot list %ld-'%s'", rv, pkcs11_getMessage (rv)); + } + else { + msg ( + msglev, + ( + "The following slots are available for use with this provider.\n" + "Each slot shown below may be used as a parameter to a\n" + "--pkcs11-slot-type and --pkcs11-slot options.\n" + "\n" + "Slots: (id - name)" + ) + ); + for (s=0;s<slotnum;s++) { + CK_SLOT_INFO info; + + if ( + (rv = pkcs11_provider->f->C_GetSlotInfo ( + slots[s], + &info + )) == CKR_OK + ) { + char szCurrentName[sizeof (info.slotDescription)+1]; + + _fixupFixedString ( + info.slotDescription, + szCurrentName, + sizeof (info.slotDescription) + ); + + msg (msglev, "\t%lu - %s", slots[s], szCurrentName); + } + } + } + + pkcs11_finalize (); +} + +void +show_pkcs11_objects ( + IN const int msglev, + IN const int warnlev, + IN const char * const provider, + IN const char * const slot, + IN const char * const pin +) { + CK_OBJECT_HANDLE objects[10]; + CK_SESSION_HANDLE session; + CK_ULONG objects_found; + CK_TOKEN_INFO info; + CK_SLOT_ID s; + CK_RV rv; + + ASSERT (provider!=NULL); + ASSERT (slot!=NULL); + ASSERT (pin!=NULL); + + s = atoi (slot); + + if ( + (rv = pkcs11_addProvider (provider, NULL)) != CKR_OK + ) { + msg (M_FATAL, "PKCS#11: Cannot initialize provider %ld-'%s'", rv, pkcs11_getMessage (rv)); + } + + if ( + (rv = pkcs11_provider->f->C_GetTokenInfo ( + s, + &info + )) != CKR_OK + ) { + msg (warnlev, "PKCS#11: Cannot get token information for slot %ld %ld-'%s'", s, rv, pkcs11_getMessage (rv)); + } + else { + char szLabel[sizeof (info.label)+1]; + char szManufacturerID[sizeof (info.manufacturerID)+1]; + char szModel[sizeof (info.model)+1]; + char szSerialNumber[sizeof (info.serialNumber)+1]; + + _fixupFixedString ( + info.label, + szLabel, + sizeof (info.label) + ); + _fixupFixedString ( + info.manufacturerID, + szManufacturerID, + sizeof (info.manufacturerID) + ); + _fixupFixedString ( + info.model, + szModel, + sizeof (info.model) + ); + _fixupFixedString ( + info.serialNumber, + szSerialNumber, + sizeof (info.serialNumber) + ); + + msg ( + msglev, + ( + "Token Information:\n" + "\tlabel:\t\t%s\n" + "\tmanufacturerID:\t%s\n" + "\tmodel:\t\t%s\n" + "\tserialNumber:\t%s\n" + "\tflags:\t\t%08x\n" + "\n" + "You can access this token using\n" + "--pkcs11-slot-type \"label\" --pkcs11-slot \"%s\" options.\n" + ), + szLabel, + szManufacturerID, + szModel, + szSerialNumber, + (unsigned)info.flags, + szLabel + ); + } + + if ( + (rv = pkcs11_provider->f->C_OpenSession ( + s, + CKF_SERIAL_SESSION, + NULL_PTR, + NULL_PTR, + &session + )) != CKR_OK + ) { + msg (M_FATAL, "PKCS#11: Cannot open session to slot %ld %ld-'%s'", s, rv, pkcs11_getMessage (rv)); + } + + if ( + (rv = pkcs11_provider->f->C_Login ( + session, + CKU_USER, + (CK_CHAR_PTR)pin, + (CK_ULONG)strlen (pin) + )) != CKR_OK && + rv != CKR_USER_ALREADY_LOGGED_IN + ) { + msg (M_FATAL, "PKCS#11: Cannot login to token on slot %ld %ld-'%s'", s, rv, pkcs11_getMessage (rv)); + } + + if ( + (rv = pkcs11_provider->f->C_FindObjectsInit ( + session, + NULL, + 0 + )) != CKR_OK + ) { + msg (M_FATAL, "PKCS#11: Cannot query objects for token on slot %ld %ld-'%s'", s, rv, pkcs11_getMessage (rv)); + } + + msg ( + msglev, + "The following objects are available for use with this token.\n" + "Each object shown below may be used as a parameter to\n" + "--pkcs11-id-type and --pkcs11-id options.\n" + ); + + while ( + (rv = pkcs11_provider->f->C_FindObjects ( + session, + objects, + sizeof (objects) / sizeof (CK_OBJECT_HANDLE), + &objects_found + )) == CKR_OK && + objects_found > 0 + ) { + CK_ULONG i; + + for (i=0;i<objects_found;i++) { + CK_OBJECT_CLASS attrs_class; + unsigned char attrs_id[PKCS11_MAX_ATTRIBUTE_SIZE]; + unsigned char attrs_label[PKCS11_MAX_ATTRIBUTE_SIZE]; + CK_ATTRIBUTE attrs[] = { + {CKA_CLASS, &attrs_class, sizeof (attrs_class)}, + {CKA_ID, attrs_id, sizeof (attrs_id)}, + {CKA_LABEL, attrs_label, sizeof (attrs_label)-1} + }; + + if ( + pkcs11_provider->f->C_GetAttributeValue ( + session, + objects[i], + attrs, + sizeof (attrs) / sizeof (CK_ATTRIBUTE) + ) == CKR_OK + ) { + int id_len = attrs[1].ulValueLen; + int j; + + attrs_label[attrs[2].ulValueLen] = 0; + + msg ( + msglev, + ( + "Object\n" + "\tLabel:\t\t%s\n" + "\tId:" + ), + attrs_label + ); + + + for (j=0;j<id_len;j+=16) { + char szLine[3*16+1]; + int k; + + szLine[0] = '\0'; + for (k=0;k<16 && j+k<id_len;k++) { + sprintf (szLine+strlen (szLine), "%02x ", attrs_id[j+k]); + } + + msg (msglev, "\t\t%s", szLine); + } + + if (attrs_class == CKO_CERTIFICATE) { + unsigned char certificate[PKCS11_MAX_ATTRIBUTE_SIZE]; + CK_ATTRIBUTE attrs_cert[] = { + {CKA_VALUE, certificate, sizeof (certificate)} + }; + + msg (msglev, "\tType:\t\tCertificate"); + + if ( + pkcs11_provider->f->C_GetAttributeValue ( + session, + objects[i], + attrs_cert, + sizeof (attrs_cert) / sizeof (CK_ATTRIBUTE) + ) == CKR_OK + ) { + X509 *x509 = NULL; + BIO *bioSerial = NULL; + + char szSubject[1024]; + char szSerial[1024]; + char szNotBefore[1024]; + + szSubject[0] = '\0'; + szSerial[0] = '\0'; + szNotBefore[0] = '\0'; + + if ((x509 = X509_new ()) == NULL) { + msg (warnlev, "Cannot create x509 context"); + } + else { + unsigned char *p; + + p = certificate; + if (d2i_X509 (&x509, &p, attrs_cert[0].ulValueLen)) { + + ASN1_TIME *notBefore = X509_get_notBefore (x509); + if (notBefore != NULL && notBefore->length < (int) sizeof (szNotBefore) - 1) { + memmove (szNotBefore, notBefore->data, notBefore->length); + szNotBefore[notBefore->length] = '\0'; + } + + X509_NAME_oneline ( + X509_get_subject_name (x509), + szSubject, + sizeof (szSubject) + ); + szSubject[sizeof (szSubject) - 1] = '\0'; + } + } + + if ((bioSerial = BIO_new (BIO_s_mem ())) == NULL) { + msg (warnlev, "Cannot create BIO context"); + } + else { + int n; + + i2a_ASN1_INTEGER(bioSerial, X509_get_serialNumber (x509)); + n = BIO_read (bioSerial, szSerial, sizeof (szSerial)-1); + if (n<0) { + szSerial[0] = '\0'; + } + else { + szSerial[n] = '\0'; + } + } + + + if (x509 != NULL) { + X509_free (x509); + x509 = NULL; + } + if (bioSerial != NULL) { + BIO_free_all (bioSerial); + bioSerial = NULL; + } + + msg ( + msglev, + ( + "\tsubject:\t%s\n" + "\tserialNumber:\t%s\n" + "\tnotBefore:\t%s" + ), + szSubject, + szSerial, + szNotBefore + ); + } + } + else if (attrs_class == CKO_PRIVATE_KEY) { + CK_BBOOL sign_recover; + CK_BBOOL sign; + CK_ATTRIBUTE attrs_key[] = { + {CKA_SIGN, &sign_recover, sizeof (sign_recover)}, + {CKA_SIGN_RECOVER, &sign, sizeof (sign)} + }; + + msg (msglev, "\tType:\t\tPrivate Key"); + + if ( + pkcs11_provider->f->C_GetAttributeValue ( + session, + objects[i], + attrs_key, + sizeof (attrs_key) / sizeof (CK_ATTRIBUTE) + ) == CKR_OK + ) { + msg ( + msglev, + ( + "\tSign:\t\t%s\n" + "\tSign Recover:\t%s" + ), + sign ? "TRUE" : "FALSE", + sign_recover ? "TRUE" : "FALSE" + ); + } + } + else { + msg (msglev, "\tType:\t\tUnsupported"); + } + } + } + } + pkcs11_provider->f->C_FindObjectsFinal (session); + pkcs11_provider->f->C_Logout (session); + pkcs11_provider->f->C_CloseSession (session); + pkcs11_finalize (); +} + +#else +static void dummy (void) {} +#endif /* USE_OPENSC && USE_SSL && USE_CRYPTO */ |