/* * 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 * * 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 #include "syshead.h" #if defined(ENABLE_PKCS11) #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" /*=========================================== * MACROS */ #define snprintf openvpn_snprintf /*=========================================== * 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 # if OPENSSL_VERSION_NUMBER < 0x0090704fL # define BROKEN_OPENSSL_ENGINE # endif # endif #endif #define PKCS11_MAX_ATTRIBUTE_SIZE (10*1024) /*=========================================== * Types */ typedef bool (*pkcs11_hook_card_prompt_t)( IN const void *pData, IN const char * const szLabel ); typedef bool (*pkcs11_hook_pin_prompt_t)( IN const void *pData, IN const char * const szLabel, OUT char * const szPIN, IN const size_t nMaxPIN ); typedef struct pkcs11_hooks_s { void *card_prompt_data; void *pin_prompt_data; pkcs11_hook_card_prompt_t card_prompt; pkcs11_hook_pin_prompt_t pin_prompt; } *pkcs11_hooks_t; 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; bool fProtectedAuthentication; char szLabel[sizeof (((CK_TOKEN_INFO *)NULL)->label)+1]; CK_CHAR serialNumber[sizeof (((CK_TOKEN_INFO *)NULL)->serialNumber)]; unsigned char *certificate; size_t certificate_size; unsigned char *certificate_id; size_t certificate_id_size; CK_SLOT_ID slot; bool fKeySignRecover; CK_SESSION_HANDLE session; CK_OBJECT_HANDLE key; time_t timePINExpire; } *pkcs11_session_t; typedef struct pkcs11_data_s { bool fInitialized; int nPINCachePeriod; pkcs11_provider_t providers; pkcs11_hooks_t hooks; } *pkcs11_data_t; /*=========================================== * Low level prototypes */ static void _fixupFixedString ( IN const char * const szSource, OUT char * const szTarget, /* MUST BE >= nLength+1 */ IN const size_t nLength /* FIXED STRING LENGTH */ ); static void _hexToBinary ( IN const char * const szSource, OUT unsigned char * const target, IN OUT size_t * 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 size_t id_size, OUT CK_OBJECT_HANDLE * const handle ); static CK_RV _pkcs11_setSessionTokenInfo ( IN const pkcs11_session_t pkcs11_session ); static CK_RV _pkcs11_resetSlot ( IN const pkcs11_session_t pkcs11_session ); 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 size_t nCurrentSize, IN const unsigned char * const pNew, IN const size_t nNewSize ); static CK_RV _pkcs11_validateSession ( 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 ); /*========================================= * Simplified functions prototypes */ static bool _pkcs11_hooks_card_prompt_default ( IN const void *pData, IN const char * const szLabel ); static bool _pkcs11_hooks_pin_prompt_default ( IN const void *pData, IN const char * const szLabel, OUT char * const szPIN, IN const size_t nMaxPIN ); static CK_RV pkcs11_initialize (); static CK_RV pkcs11_terminate (); static CK_RV pkcs11_setCardPromptHook ( IN const pkcs11_hook_card_prompt_t hook, IN void * const pData ); static CK_RV pkcs11_setPINPromptHook ( IN const pkcs11_hook_pin_prompt_t hook, IN void * const pData ); static CK_RV pkcs11_setPINCachePeriod ( IN const int nPINCachePeriod ); static CK_RV pkcs11_addProvider ( IN const char * const szProvider, IN const char * const szSignMode ); static CK_RV pkcs11_forkFixup (); 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 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_sign ( IN const pkcs11_session_t pkcs11_session, IN const CK_MECHANISM_TYPE mech_type, IN const unsigned char * const source, IN const size_t source_size, OUT unsigned char * const target, IN OUT size_t * 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 size_t source_size, OUT unsigned char * const target, IN OUT size_t * 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 size_t source_size, OUT unsigned char * const target, IN OUT size_t * const target_size ); static CK_RV pkcs11_getCertificate ( IN const pkcs11_session_t pkcs11_session, OUT unsigned char * const certificate, IN OUT size_t * const certificate_size ); static char * pkcs11_getMessage ( IN const int rv ); /*========================================== * Static data */ static pkcs11_data_t pkcs11_data = NULL; /*========================================== * Internal utility functions */ static void _fixupFixedString ( IN const char * const szSource, OUT char * const szTarget, /* MUST BE >= nLength+1 */ IN const size_t 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 size_t * const target_size ) { size_t 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) { unsigned 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 size_t nCurrentSize, IN const unsigned char * const pNew, IN const size_t 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_data->providers; 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_RV rv; pkcs11_provider_t provider; bool fFound = false; ASSERT (pkcs11_session!=NULL); ASSERT (szName!=NULL); for ( provider = pkcs11_data->providers; ( provider != NULL && !fFound ); provider = provider->next ) { CK_SLOT_ID slots[1024]; CK_ULONG slotnum; if (!provider->fEnabled) { continue; } slotnum = sizeof (slots) / sizeof (CK_SLOT_ID); if ( (rv = provider->f->C_GetSlotList ( TRUE, slots, &slotnum )) == CKR_OK ) { CK_SLOT_ID s; for (s=0;!fFound && sf->C_GetSlotInfo ( slots[s], &info )) == CKR_OK ) { char szCurrentName[sizeof (info.slotDescription)+1]; _fixupFixedString ( (char *)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_RV rv; pkcs11_provider_t provider; bool fFound = false; ASSERT (pkcs11_session!=NULL); ASSERT (szLabel!=NULL); for ( provider = pkcs11_data->providers; ( provider != NULL && !fFound ); provider = provider->next ) { CK_SLOT_ID slots[1024]; CK_ULONG slotnum; if (!provider->fEnabled) { continue; } slotnum = sizeof (slots) / sizeof (CK_SLOT_ID); if ( (rv = provider->f->C_GetSlotList ( TRUE, slots, &slotnum )) == CKR_OK ) { CK_SLOT_ID s; for (s=0;!fFound && sf->C_GetTokenInfo ( slots[s], &info )) == CKR_OK ) { char szCurrentLabel[sizeof (info.label)+1]; _fixupFixedString ( (char *)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_setSessionTokenInfo ( IN const pkcs11_session_t pkcs11_session ) { CK_TOKEN_INFO info; CK_RV rv; ASSERT (pkcs11_session!=NULL); if ( (rv = pkcs11_session->provider->f->C_GetTokenInfo ( pkcs11_session->slot, &info )) == CKR_OK ) { _fixupFixedString ( (char *)info.label, pkcs11_session->szLabel, sizeof (info.label) ); memmove ( pkcs11_session->serialNumber, info.serialNumber, sizeof (pkcs11_session->serialNumber) ); } return rv; } static CK_RV _pkcs11_resetSlot ( IN const pkcs11_session_t pkcs11_session ) { CK_SLOT_ID slots[1024]; CK_ULONG slotnum; CK_RV rv; bool fFound = false; bool fCancel = false; ASSERT (pkcs11_session!=NULL); do { slotnum = sizeof (slots) / sizeof (CK_SLOT_ID); if ( (rv = pkcs11_session->provider->f->C_GetSlotList ( TRUE, slots, &slotnum )) == CKR_OK ) { CK_SLOT_ID s; for (s=0;!fFound && sprovider->f->C_GetTokenInfo ( slots[s], &info )) == CKR_OK ) { if ( !memcmp ( pkcs11_session->serialNumber, info.serialNumber, sizeof (pkcs11_session->serialNumber) ) ) { pkcs11_session->slot = slots[s]; fFound = true; } } } } if (!fFound) { fCancel = !pkcs11_data->hooks->card_prompt ( pkcs11_data->hooks->card_prompt_data, pkcs11_session->szLabel ); } } while (!fFound && !fCancel); 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 size_t id_size, OUT CK_OBJECT_HANDLE * const handle ) { CK_ULONG count; CK_RV rv = CKR_OK; 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 == CKR_OK) { rv = pkcs11_session->provider->f->C_FindObjectsInit ( pkcs11_session->session, filter, sizeof (filter) / sizeof (CK_ATTRIBUTE) ); } if (rv == CKR_OK) { rv = pkcs11_session->provider->f->C_FindObjects ( pkcs11_session->session, handle, 1, &count ); } if ( rv == CKR_OK && count == 0 ) { rv = CKR_FUNCTION_REJECTED; } pkcs11_session->provider->f->C_FindObjectsFinal ( pkcs11_session->session ); return rv; } 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")) { size_t 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;iprovider->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; } static CK_RV _pkcs11_validateSession ( IN const pkcs11_session_t pkcs11_session ) { if ( pkcs11_session->timePINExpire != (time_t)0 && pkcs11_session->timePINExpire < time (NULL) ) { _pkcs11_logout (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_resetSlot (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 ); } if (rv == CKR_OK) { int nRetryCount = 0; do { CK_UTF8CHAR_PTR utfPIN = NULL; CK_ULONG lPINLength = 0; char szPIN[1024]; /* * Assume OK for next iteration */ rv = CKR_OK; if ( rv == CKR_OK && !pkcs11_session->fProtectedAuthentication ) { if ( !pkcs11_data->hooks->pin_prompt ( pkcs11_data->hooks->pin_prompt_data, pkcs11_session->szLabel, szPIN, sizeof (szPIN) ) ) { rv = CKR_FUNCTION_FAILED; } else { utfPIN = (CK_UTF8CHAR_PTR)szPIN; lPINLength = strlen (szPIN); } } if (pkcs11_data->nPINCachePeriod == -1) { pkcs11_session->timePINExpire = 0; } else { pkcs11_session->timePINExpire = ( time (NULL) + (time_t)pkcs11_data->nPINCachePeriod ); } if ( rv == CKR_OK && (rv = pkcs11_session->provider->f->C_Login ( pkcs11_session->session, CKU_USER, utfPIN, lPINLength )) != CKR_OK ) { if (rv == CKR_USER_ALREADY_LOGGED_IN) { rv = CKR_OK; } } /* * Clean PIN buffer */ memset (szPIN, 0, sizeof (szPIN)); } while ( ++nRetryCount < 3 && ( rv == CKR_PIN_INCORRECT || rv == CKR_PIN_INVALID ) ); } 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 ); } return rv; } static CK_RV _pkcs11_logout ( IN const pkcs11_session_t pkcs11_session ) { ASSERT (pkcs11_session!=NULL); if (pkcs11_session->session != (CK_SESSION_HANDLE)-1) { pkcs11_session->provider->f->C_Logout (pkcs11_session->session); pkcs11_session->provider->f->C_CloseSession (pkcs11_session->session); pkcs11_session->key = (CK_OBJECT_HANDLE)-1; pkcs11_session->session = (CK_SESSION_HANDLE)-1; } return CKR_OK; } /*======================================= * Simplified PKCS#11 functions */ static bool _pkcs11_hooks_card_prompt_default ( IN const void * pData, IN const char * const szLabel ) { return false; } static bool _pkcs11_hooks_pin_prompt_default ( IN const void * pData, IN const char * const szLabel, OUT char * const szPIN, IN const size_t nMaxPIN ) { return false; } static CK_RV pkcs11_initialize () { pkcs11_terminate (); pkcs11_data = (pkcs11_data_t)malloc (sizeof (struct pkcs11_data_s)); if (pkcs11_data == NULL) { return CKR_HOST_MEMORY; } memset (pkcs11_data, 0, sizeof (struct pkcs11_data_s)); pkcs11_data->nPINCachePeriod = -1; pkcs11_data->hooks = (pkcs11_hooks_t)malloc (sizeof (struct pkcs11_hooks_s)); if (pkcs11_data->hooks == NULL) { return CKR_HOST_MEMORY; } memset (pkcs11_data->hooks, 0, sizeof (struct pkcs11_hooks_s)); pkcs11_data->fInitialized = true; pkcs11_setCardPromptHook (_pkcs11_hooks_card_prompt_default, NULL); pkcs11_setPINPromptHook (_pkcs11_hooks_pin_prompt_default, NULL); return CKR_OK; } static CK_RV pkcs11_terminate () { if (pkcs11_data != NULL) { pkcs11_provider_t last = NULL; for ( ; pkcs11_data->providers != NULL; pkcs11_data->providers = pkcs11_data->providers->next ) { if (last != NULL) { free (last); } last = pkcs11_data->providers; if (pkcs11_data->providers->szSignMode != NULL) { free (pkcs11_data->providers->szSignMode); pkcs11_data->providers->szSignMode = NULL; } if (pkcs11_data->providers->fShouldFinalize) { pkcs11_data->providers->f->C_Finalize (NULL); pkcs11_data->providers->fShouldFinalize = false; } if (pkcs11_data->providers->f != NULL) { pkcs11_data->providers->f = NULL; } if (pkcs11_data->providers->hLibrary != NULL) { #if defined(WIN32) FreeLibrary (pkcs11_data->providers->hLibrary); #else dlclose (pkcs11_data->providers->hLibrary); #endif pkcs11_data->providers->hLibrary = NULL; } } if (last != NULL) { free (last); } if (pkcs11_data->hooks != NULL) { free (pkcs11_data->hooks); pkcs11_data->hooks = NULL; } free (pkcs11_data); pkcs11_data = NULL; } return CKR_OK; } static CK_RV pkcs11_setPINPromptHook ( IN const pkcs11_hook_pin_prompt_t hook, IN void * const pData ) { ASSERT (pkcs11_data!=NULL); ASSERT (pkcs11_data->fInitialized); pkcs11_data->hooks->pin_prompt = hook; pkcs11_data->hooks->pin_prompt_data = pData; return CKR_OK; } static CK_RV pkcs11_setCardPromptHook ( IN const pkcs11_hook_card_prompt_t hook, IN void * const pData ) { ASSERT (pkcs11_data!=NULL); ASSERT (pkcs11_data->fInitialized); pkcs11_data->hooks->card_prompt = hook; pkcs11_data->hooks->card_prompt_data = pData; return CKR_OK; } static CK_RV pkcs11_setPINCachePeriod ( IN const int nPINCachePeriod ) { ASSERT (pkcs11_data!=NULL); ASSERT (pkcs11_data->fInitialized); pkcs11_data->nPINCachePeriod = nPINCachePeriod; return CKR_OK; } 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 (pkcs11_data!=NULL); ASSERT (pkcs11_data->fInitialized); 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 /* * Make compiler happy! */ void *p = dlsym ( provider->hLibrary, "C_GetFunctionList" ); memmove ( &gfl, &p, sizeof (void *) ); #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_data->providers == NULL) { pkcs11_data->providers = provider; } else { pkcs11_provider_t last = NULL; for ( last = pkcs11_data->providers; last->next != NULL; last = last->next ); last->next = provider; } } return rv; } static CK_RV pkcs11_forkFixup () { pkcs11_provider_t current; ASSERT (pkcs11_data!=NULL); ASSERT (pkcs11_data->fInitialized); for ( current = pkcs11_data->providers; current != NULL; current = current->next ) { if (current->fEnabled) { current->f->C_Initialize (NULL); } } 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 bool fProtectedAuthentication, OUT pkcs11_session_t * const p_pkcs11_session ) { pkcs11_session_t pkcs11_session; CK_RV rv = CKR_OK; ASSERT (pkcs11_data!=NULL); ASSERT (pkcs11_data->fInitialized); ASSERT (szSlotType!=NULL); ASSERT (szSlot!=NULL); ASSERT (szIdType!=NULL); ASSERT (szId!=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) { pkcs11_session->key = (CK_OBJECT_HANDLE)-1; pkcs11_session->session = (CK_SESSION_HANDLE)-1; pkcs11_session->fProtectedAuthentication = fProtectedAuthentication; } if (rv == CKR_OK) { bool fCancel = false; do { 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_SLOT_ID_INVALID) { char szLabel[1024]; snprintf (szLabel, sizeof (szLabel), "SLOT(%s=%s)", szSlotType, szSlot); fCancel = !pkcs11_data->hooks->card_prompt ( pkcs11_data->hooks->card_prompt_data, szLabel ); } } while (rv == CKR_SLOT_ID_INVALID && !fCancel); } if (rv == CKR_OK) { rv = _pkcs11_setSessionTokenInfo (pkcs11_session); } 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 ); } /* * Complete missing login process */ if (rv == CKR_OK) { rv = _pkcs11_getObjectById ( pkcs11_session, CKO_PRIVATE_KEY, pkcs11_session->certificate_id, pkcs11_session->certificate_id_size, &pkcs11_session->key ); } return rv; } CK_RV pkcs11_freeSession ( IN const pkcs11_session_t pkcs11_session ) { ASSERT (pkcs11_data!=NULL); ASSERT (pkcs11_data->fInitialized); if (pkcs11_session != NULL) { _pkcs11_logout (pkcs11_session); 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_sign ( IN const pkcs11_session_t pkcs11_session, IN const CK_MECHANISM_TYPE mech_type, IN const unsigned char * const source, IN const size_t source_size, OUT unsigned char * const target, IN OUT size_t * const target_size ) { CK_MECHANISM mech = { mech_type, NULL, 0 }; CK_RV rv = CKR_OK; bool fLogonRetry = false; bool fOpSuccess = false; ASSERT (pkcs11_data!=NULL); ASSERT (pkcs11_data->fInitialized); ASSERT (pkcs11_session!=NULL); ASSERT (source!=NULL); ASSERT (target_size!=NULL); rv = _pkcs11_validateSession (pkcs11_session); while (rv == CKR_OK && !fOpSuccess) { rv = pkcs11_session->provider->f->C_SignInit ( pkcs11_session->session, &mech, pkcs11_session->key ); if (rv == CKR_OK) { fOpSuccess = true; } else { if (!fLogonRetry) { fLogonRetry = true; rv = _pkcs11_login (pkcs11_session); } } } if (rv == CKR_OK) { CK_ULONG 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 size_t source_size, OUT unsigned char * const target, IN OUT size_t * const target_size ) { CK_MECHANISM mech = { mech_type, NULL, 0 }; CK_RV rv = CKR_OK; bool fLogonRetry = false; bool fOpSuccess = false; ASSERT (pkcs11_data!=NULL); ASSERT (pkcs11_data->fInitialized); ASSERT (pkcs11_session!=NULL); ASSERT (source!=NULL); ASSERT (target_size!=NULL); rv = _pkcs11_validateSession (pkcs11_session); while (rv == CKR_OK && !fOpSuccess) { rv = pkcs11_session->provider->f->C_SignRecoverInit ( pkcs11_session->session, &mech, pkcs11_session->key ); if (rv == CKR_OK) { fOpSuccess = true; } else { if (!fLogonRetry) { fLogonRetry = true; rv = _pkcs11_login (pkcs11_session); } } } if (rv == CKR_OK) { CK_ULONG 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 size_t source_size, OUT unsigned char * const target, IN OUT size_t * const target_size ) { CK_MECHANISM mech = { mech_type, NULL, 0 }; CK_ULONG size; CK_RV rv = CKR_OK; bool fLogonRetry = false; bool fOpSuccess = false; ASSERT (pkcs11_data!=NULL); ASSERT (pkcs11_data->fInitialized); ASSERT (pkcs11_session!=NULL); ASSERT (source!=NULL); ASSERT (target_size!=NULL); rv = _pkcs11_validateSession (pkcs11_session); while (rv == CKR_OK && !fOpSuccess) { rv = pkcs11_session->provider->f->C_DecryptInit ( pkcs11_session->session, &mech, pkcs11_session->key ); if (rv == CKR_OK) { fOpSuccess = true; } else { if (!fLogonRetry) { fLogonRetry = true; rv = _pkcs11_login (pkcs11_session); } } } if (rv == CKR_OK) { 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 unsigned char * const certificate, IN OUT size_t * const certificate_size ) { ASSERT (pkcs11_data!=NULL); ASSERT (pkcs11_data->fInitialized); 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"; } } /*========================================== * openssl interface */ typedef struct openssl_session_s { RSA_METHOD smart_rsa; 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, (void *)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_decrypt ( pkcs11_session, CKM_RSA_PKCS, from, flen, to, (size_t *)&flen )) != CKR_OK ) { msg (M_WARN, "PKCS#11: Cannot decrypt using private key %ld:'%s'", rv, pkcs11_getMessage (rv)); } 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, (void *)siglen, (void *)rsa ); ASSERT (m!=NULL); ASSERT (siglen!=NULL); msg ( D_SHOW_PKCS11, "PKCS#11: Performing signature" ); *siglen = RSA_size(rsa); 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)); } } 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", (void *)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; } void openssl_pkcs11_set_rsa(const openssl_session_t openssl_session, RSA *rsa) { const RSA_METHOD *def = RSA_get_default_method(); ASSERT (openssl_session!=NULL); ASSERT (rsa!=NULL); memmove (&openssl_session->smart_rsa, def, sizeof(RSA_METHOD)); openssl_session->orig_finish = def->finish; openssl_session->smart_rsa.name = "pkcs11"; openssl_session->smart_rsa.rsa_priv_enc = openssl_pkcs11_priv_enc; openssl_session->smart_rsa.rsa_priv_dec = openssl_pkcs11_priv_dec; openssl_session->smart_rsa.rsa_sign = openssl_pkcs11_sign; openssl_session->smart_rsa.finish = openssl_pkcs11_finish; openssl_session->smart_rsa.flags = RSA_METHOD_FLAG_NO_CHECK | RSA_FLAG_EXT_PKEY; RSA_set_method (rsa, &openssl_session->smart_rsa); RSA_set_app_data (rsa, openssl_session); #ifdef BROKEN_OPENSSL_ENGINE if (fOK) { if (!rsa->engine) rsa->engine = ENGINE_get_default_RSA(); ENGINE_set_RSA(ENGINE_get_default_RSA(), openssl_session->smart_rsa); msg(M_WARN, "PKCS#11: OpenSSL engine support is broken! Workaround enabled"); } #endif } #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 /*========================================== * openvpn interface */ static bool _openvpn_pkcs11_card_prompt ( IN const void *pData, IN const char * const szLabel ) { static struct user_pass token_pass; char szPrompt[1024]; char szTemp[1024]; ASSERT (szLabel!=NULL); openvpn_snprintf (szPrompt, sizeof (szPrompt), "INSERT"); token_pass.defined = false; token_pass.nocache = true; get_user_pass (&token_pass, NULL, true, szPrompt, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE); strncpynt (szTemp, token_pass.password, sizeof (szTemp)); purge_user_pass (&token_pass, true); if (strlen (szTemp) == 0) { return false; } else { return true; } } static bool _openvpn_pkcs11_pin_prompt ( IN const void *pData, IN const char * const szLabel, OUT char * const szPIN, IN const size_t nMaxPIN ) { static struct user_pass token_pass; char szPrompt[1024]; ASSERT (szLabel!=NULL); openvpn_snprintf (szPrompt, sizeof (szPrompt), "%s token", szLabel); token_pass.defined = false; token_pass.nocache = true; get_user_pass (&token_pass, NULL, true, szPrompt, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE); strncpynt (szPIN, token_pass.password, nMaxPIN); purge_user_pass (&token_pass, true); if (strlen (szPIN) == 0) { return false; } else { return true; } } void init_pkcs11 ( const int nPINCachePeriod ) { CK_RV rv; msg ( D_PKCS11_DEBUG, "PKCS#11: init_pkcs11 - entered" ); if ((rv = pkcs11_initialize ()) != CKR_OK) { msg (M_FATAL, "PKCS#11: Cannot initialize %ld-'%s'", rv, pkcs11_getMessage (rv)); } /*Until REQUEST/REPLY interface. if ((rv = pkcs11_setCardPromptHook (_openvpn_pkcs11_card_prompt, NULL)) != CKR_OK) { msg (M_FATAL, "PKCS#11: Cannot set hooks %ld-'%s'", rv, pkcs11_getMessage (rv)); } */ if ((rv = pkcs11_setPINPromptHook (_openvpn_pkcs11_pin_prompt, NULL)) != CKR_OK) { msg (M_FATAL, "PKCS#11: Cannot set hooks %ld-'%s'", rv, pkcs11_getMessage (rv)); } if ((rv = pkcs11_setPINCachePeriod (nPINCachePeriod)) != CKR_OK) { msg (M_FATAL, "PKCS#11: Cannot set PIN cache period %ld-'%s'", rv, pkcs11_getMessage (rv)); } msg ( D_PKCS11_DEBUG, "PKCS#11: init_pkcs11 - return" ); } void free_pkcs11 () { msg ( D_PKCS11_DEBUG, "PKCS#11: free_pkcs11 - entered" ); pkcs11_terminate (); msg ( D_PKCS11_DEBUG, "PKCS#11: free_pkcs11 - return" ); } void fork_fix_pkcs11 () { pkcs11_forkFixup (); } 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" ); } 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 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]; size_t 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", (void *)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 ( 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, 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) { openssl_pkcs11_set_rsa (openssl_session, rsa); rsa->flags |= RSA_FLAG_SIGN_VER; /* it will be freed when rsa usage count will be zero */ fShouldFreeOpenSSLSession = false; } 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 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; pkcs11_provider_t pkcs11_provider; ASSERT (provider!=NULL); if ( (rv = pkcs11_initialize ()) != CKR_OK ) { msg (M_FATAL, "PKCS#11: Cannot initialize interface %ld-'%s'", rv, pkcs11_getMessage (rv)); } if ( (rv = pkcs11_addProvider (provider, NULL)) != CKR_OK ) { msg (M_FATAL, "PKCS#11: Cannot initialize provider %ld-'%s'", rv, pkcs11_getMessage (rv)); } /* * our provider is head */ pkcs11_provider = pkcs11_data->providers; if (pkcs11_provider == NULL || !pkcs11_provider->fEnabled) { msg (M_FATAL, "PKCS#11: Cannot get 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 ( (char *)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;sf->C_GetSlotInfo ( slots[s], &info )) == CKR_OK ) { char szCurrentName[sizeof (info.slotDescription)+1]; _fixupFixedString ( (char *)info.slotDescription, szCurrentName, sizeof (info.slotDescription) ); msg (msglev, "\t%lu - %s", slots[s], szCurrentName); } } } pkcs11_terminate (); } static bool _show_pkcs11_objects_pin_prompt ( IN const void *pData, IN const char * const szLabel, OUT char * const szPIN, IN const size_t nMaxPIN ) { strncpy (szPIN, (char *)pData, nMaxPIN); return true; } 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; pkcs11_provider_t pkcs11_provider; ASSERT (provider!=NULL); ASSERT (slot!=NULL); ASSERT (pin!=NULL); s = atoi (slot); if ( (rv = pkcs11_initialize ()) != CKR_OK ) { msg (M_FATAL, "PKCS#11: Cannot initialize interface %ld-'%s'", rv, pkcs11_getMessage (rv)); } if ( (rv = pkcs11_setPINPromptHook (_show_pkcs11_objects_pin_prompt, (void *)pin)) != CKR_OK ) { msg (M_FATAL, "PKCS#11: Cannot set hooks %ld-'%s'", rv, pkcs11_getMessage (rv)); } if ( (rv = pkcs11_addProvider (provider, NULL)) != CKR_OK ) { msg (M_FATAL, "PKCS#11: Cannot initialize provider %ld-'%s'", rv, pkcs11_getMessage (rv)); } /* * our provider is head */ pkcs11_provider = pkcs11_data->providers; if (pkcs11_provider == NULL || !pkcs11_provider->fEnabled) { msg (M_FATAL, "PKCS#11: Cannot get 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 ( (char *)info.label, szLabel, sizeof (info.label) ); _fixupFixedString ( (char *)info.manufacturerID, szManufacturerID, sizeof (info.manufacturerID) ); _fixupFixedString ( (char *)info.model, szModel, sizeof (info.model) ); _fixupFixedString ( (char *)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;if->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;jf->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_terminate (); } #else static void dummy (void) {} #endif /* ENABLE_PKCS11 */