From 984cf0036c882c4fada83448aaa37bbd5ebb8130 Mon Sep 17 00:00:00 2001 From: james Date: Thu, 20 Oct 2005 05:58:08 +0000 Subject: Merged with Alon's r688. git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@689 e7ae566f-a301-0410-adde-c780ea21d3b5 --- pkcs11-helper.c | 3239 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3239 insertions(+) create mode 100644 pkcs11-helper.c (limited to 'pkcs11-helper.c') diff --git a/pkcs11-helper.c b/pkcs11-helper.c new file mode 100644 index 0000000..32c986d --- /dev/null +++ b/pkcs11-helper.c @@ -0,0 +1,3239 @@ +/* + * Copyright (c) 2005 Alon Bar-Lev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifi- + * cation, are permitted provided that the following conditions are met: + * + * o Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * o Redistributions in binary form must reproduce the above copyright no- + * tice, this list of conditions and the following disclaimer in the do- + * cumentation and/or other materials provided with the distribution. + * + * o The names of the contributors may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LI- + * ABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUEN- + * TIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEV- + * ER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABI- + * LITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * The routines in this file deal with providing private key cryptography + * using RSA Security Inc. PKCS #11 Cryptographic Token Interface (Cryptoki). + * + */ + +#include "pkcs11-helper-config.h" + +#if defined(PKCS11H_ENABLE_HELPER) + +#if defined(WIN32) +#include "cryptoki-win32.h" +#else +#include "cryptoki.h" +#endif + +#include "pkcs11-helper.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 +# if OPENSSL_VERSION_NUMBER < 0x0090704fL +# define BROKEN_OPENSSL_ENGINE +# endif +# endif +#endif + + +/*=========================================== + * Low level prototypes + */ + +static +void +_pkcs11h_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 +_pkcs11h_getSlotById ( + IN const pkcs11h_session_t pkcs11h_session, + IN const char * const szSlot +); +static +CK_RV +_pkcs11h_getSlotByName ( + IN const pkcs11h_session_t pkcs11h_session, + IN const char * const szName +); +static +CK_RV +_pkcs11h_getSlotByLabel ( + IN const pkcs11h_session_t pkcs11h_session, + IN const char * const szLabel +); +static +CK_RV +_pkcs11h_getObjectById ( + IN const pkcs11h_session_t pkcs11h_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 +_pkcs11h_setSessionTokenInfo ( + IN const pkcs11h_session_t pkcs11h_session +); +static +CK_RV +_pkcs11h_resetSlot ( + IN const pkcs11h_session_t pkcs11h_session +); +static +CK_RV +_pkcs11h_loadCertificate ( + IN const pkcs11h_session_t pkcs11h_session, + IN const char * const szIdType, + IN const char * const szId +); +static +CK_RV +_pkcs11h_loadKeyProperties ( + IN const pkcs11h_session_t pkcs11h_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 +_pkcs11h_validateSession ( + IN const pkcs11h_session_t pkcs11h_session +); +static +CK_RV +_pkcs11h_login ( + IN const pkcs11h_session_t pkcs11h_session +); +static +CK_RV +_pkcs11h_logout ( + IN const pkcs11h_session_t pkcs11h_session +); +static +int +_pkcs11h_openssl_sign ( + IN int type, + IN const unsigned char *m, + IN unsigned int m_len, + OUT unsigned char *sigret, + OUT unsigned int *siglen, + IN OUT const RSA *rsa +); + +/*========================================== + * openssl interface + */ + +static +int +_pkcs11h_openssl_finish ( + IN OUT RSA *rsa +); +static +int +_pkcs11h_openssl_priv_dec ( + IN int flen, + IN const unsigned char *from, + OUT unsigned char *to, + IN OUT RSA *rsa, + IN int padding +); +static +int +_pkcs11h_openssl_priv_enc ( + IN int flen, + IN const unsigned char *from, + OUT unsigned char *to, + IN OUT RSA *rsa, + IN int padding +); +static +pkcs11h_openssl_session_t +_pkcs11h_openssl_get_pkcs11h_openssl_session ( + IN OUT const RSA *rsa +); +static +pkcs11h_session_t +_pkcs11h_openssl_get_pkcs11h_session ( + IN OUT const RSA *rsa +); + +/*========================================== + * Static data + */ + +pkcs11h_data_t pkcs11h_data = NULL; + +/*========================================== + * Internal utility functions + */ + +static +void +_pkcs11h_fixupFixedString ( + IN const char * const szSource, + OUT char * const szTarget, /* MUST BE >= nLength+1 */ + IN const size_t nLength /* FIXED STRING LENGTH */ +) { + char *p; + + PKCS11ASSERT (szSource!=NULL); + PKCS11ASSERT (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; + + PKCS11ASSERT (szSource!=NULL); + PKCS11ASSERT (target!=NULL); + PKCS11ASSERT (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]; + bool fBetter = false; + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _isBetterCertificate entry pCurrent=%p, nCurrentSize=%u, pNew=%p, nNewSize=%u", + pCurrent, + nCurrentSize, + pNew, + nNewSize + ); + + /* + * First certificae + * always select + */ + if (nCurrentSize == 0) { + fBetter = true; + } + else { + PKCS11ASSERT (pCurrent!=NULL); + PKCS11ASSERT (pNew!=NULL); + + 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; + } + + fBetter = strcmp (szNotBeforeCurrent, szNotBeforeNew) < 0; + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _isBetterCertificate return fBetter=%d", + fBetter ? 1 : 0 + ); + + return fBetter; +} + +/*======================================== + * Low level PKCS#11 functions + */ + +static +CK_RV +_pkcs11h_getSlotById ( + IN const pkcs11h_session_t pkcs11h_session, + IN const char * const szSlot +) { + pkcs11h_provider_t provider; + CK_RV rv = CKR_OK; + int provider_number; + int slot_number; + int i; + + PKCS11ASSERT (pkcs11h_session!=NULL); + PKCS11ASSERT (szSlot!=NULL); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_getSlotById entry pkcs11h_session=%p, szSlot=%s", + (void *)pkcs11h_session, + szSlot + ); + + if (rv == CKR_OK) { + if (strchr (szSlot, ':') == NULL) { + provider_number = 0; + slot_number = atoi (szSlot); + } + else { + sscanf (szSlot, "%d:%d", &provider_number, &slot_number); + } + } + + if (rv == CKR_OK) { + for ( + i=0, provider=pkcs11h_data->providers; + ( + i < provider_number && + provider != NULL && + rv == CKR_OK + ); + i++, provider = provider->next + ); + + if ( + provider == NULL || + ( + provider != NULL && + !provider->fEnabled + ) + ) { + rv = CKR_SLOT_ID_INVALID; + } + } + + if (rv == CKR_OK) { + pkcs11h_session->provider = provider; + pkcs11h_session->slot = slot_number; + + PKCS11LOG ( + PKCS11_LOG_DEBUG1, + "PKCS#11: slot selected %s-%ld", + pkcs11h_session->provider->szName, + pkcs11h_session->slot + ); + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_getSlotById return rv=%ld-'%s'", + rv, + pkcs11h_getMessage (rv) + ); + + return rv; +} + +static +CK_RV +_pkcs11h_getSlotByName ( + IN const pkcs11h_session_t pkcs11h_session, + IN const char * const szName +) { + CK_RV rv = CKR_OK; + + pkcs11h_provider_t provider; + bool fFound = false; + + PKCS11ASSERT (pkcs11h_session!=NULL); + PKCS11ASSERT (szName!=NULL); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_getSlotByName entry pkcs11h_session=%p, szName=%s", + (void *)pkcs11h_session, + szName + ); + + for ( + provider = pkcs11h_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]; + + _pkcs11h_fixupFixedString ( + (char *)info.slotDescription, + szCurrentName, + sizeof (info.slotDescription) + ); + + if (!strcmp (szCurrentName, szName)) { + fFound = true; + pkcs11h_session->provider = provider; + pkcs11h_session->slot = slots[s]; + + PKCS11LOG ( + PKCS11_LOG_DEBUG1, + "PKCS#11: slot selected %s-%ld", + pkcs11h_session->provider->szName, + pkcs11h_session->slot + ); + } + } + } + } + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_getSlotByName return fFound=%d-'%s'", + fFound ? 1 : 0, + pkcs11h_getMessage (rv) + ); + + return fFound ? CKR_OK : CKR_SLOT_ID_INVALID; +} + +static +CK_RV +_pkcs11h_getSlotByLabel ( + IN const pkcs11h_session_t pkcs11h_session, + IN const char * const szLabel +) { + CK_RV rv; + + pkcs11h_provider_t provider; + bool fFound = false; + + PKCS11ASSERT (pkcs11h_session!=NULL); + PKCS11ASSERT (szLabel!=NULL); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "_PKCS#11: pkcs11h_getSlotByLabel entry pkcs11h_session=%p, szName=%s", + (void *)pkcs11h_session, + szLabel + ); + + for ( + provider = pkcs11h_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]; + + _pkcs11h_fixupFixedString ( + (char *)info.label, + szCurrentLabel, + sizeof (info.label) + ); + + if (!strcmp (szCurrentLabel, szLabel)) { + fFound = true; + pkcs11h_session->provider = provider; + pkcs11h_session->slot = slots[s]; + + PKCS11LOG ( + PKCS11_LOG_DEBUG1, + "PKCS#11: slot selected %s-%ld", + pkcs11h_session->provider->szName, + pkcs11h_session->slot + ); + } + } + } + } + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_getSlotByLabel return fFound=%d", + fFound ? 1 : 0 + ); + + return fFound ? CKR_OK : CKR_SLOT_ID_INVALID; +} + +static +CK_RV +_pkcs11h_setSessionTokenInfo ( + IN const pkcs11h_session_t pkcs11h_session +) { + CK_TOKEN_INFO info; + CK_RV rv; + + PKCS11ASSERT (pkcs11h_session!=NULL); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_setSessionTokenInfo entry pkcs11h_session=%p", + (void *)pkcs11h_session + ); + + if ( + (rv = pkcs11h_session->provider->f->C_GetTokenInfo ( + pkcs11h_session->slot, + &info + )) == CKR_OK + ) { + _pkcs11h_fixupFixedString ( + (char *)info.label, + pkcs11h_session->szLabel, + sizeof (info.label) + ); + + memmove ( + pkcs11h_session->serialNumber, + info.serialNumber, + sizeof (pkcs11h_session->serialNumber) + ); + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_setSessionTokenInfo return rv=%ld-'%s'", + rv, + pkcs11h_getMessage (rv) + ); + + return rv; +} + +static +CK_RV +_pkcs11h_resetSlot ( + IN const pkcs11h_session_t pkcs11h_session +) { + CK_SLOT_ID slots[1024]; + CK_ULONG slotnum; + CK_RV rv; + bool fFound = false; + bool fCancel = false; + + PKCS11ASSERT (pkcs11h_session!=NULL); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_resetSlot entry pkcs11h_session=%p", + (void *)pkcs11h_session + ); + + do { + slotnum = sizeof (slots) / sizeof (CK_SLOT_ID); + if ( + (rv = pkcs11h_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 ( + pkcs11h_session->serialNumber, + info.serialNumber, + sizeof (pkcs11h_session->serialNumber) + ) + ) { + pkcs11h_session->slot = slots[s]; + fFound = true; + } + } + } + } + + if (!fFound) { + fCancel = !pkcs11h_data->hooks->card_prompt ( + pkcs11h_data->hooks->card_prompt_data, + pkcs11h_session->szLabel + ); + } + } while (!fFound && !fCancel); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_resetSlot return fFound=%d", + fFound ? 1 : 0 + ); + + return fFound ? CKR_OK : CKR_SLOT_ID_INVALID; +} + +static +CK_RV +_pkcs11h_getObjectById ( + IN const pkcs11h_session_t pkcs11h_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} + }; + + PKCS11ASSERT (pkcs11h_session!=NULL); + PKCS11ASSERT (id!=NULL); + PKCS11ASSERT (handle!=NULL); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_getObjectById entry pkcs11h_session=%p, class=%ld, id=%p, id_size=%u, handle=%p", + (void *)pkcs11h_session, + class, + id, + id_size, + (void *)handle + ); + + if (rv == CKR_OK) { + rv = pkcs11h_session->provider->f->C_FindObjectsInit ( + pkcs11h_session->session, + filter, + sizeof (filter) / sizeof (CK_ATTRIBUTE) + ); + } + + if (rv == CKR_OK) { + rv = pkcs11h_session->provider->f->C_FindObjects ( + pkcs11h_session->session, + handle, + 1, + &count + ); + } + + if ( + rv == CKR_OK && + count == 0 + ) { + rv = CKR_FUNCTION_REJECTED; + } + + pkcs11h_session->provider->f->C_FindObjectsFinal ( + pkcs11h_session->session + ); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_getObjectById return rv=%ld-'%s'", + rv, + pkcs11h_getMessage (rv) + ); + + return rv; +} + +static +CK_RV +_pkcs11h_loadCertificate ( + IN const pkcs11h_session_t pkcs11h_session, + IN const char * const szIdType, + IN const char * const szId +) { + CK_OBJECT_HANDLE objects[10]; + CK_ULONG objects_found; + CK_RV rv = CKR_OK; + + unsigned char selected_id[PKCS11H_MAX_ATTRIBUTE_SIZE]; + int selected_id_size = 0; + unsigned char selected_certificate[PKCS11H_MAX_ATTRIBUTE_SIZE]; + int selected_certificate_size = 0; + + CK_OBJECT_CLASS cert_filter_class = CKO_CERTIFICATE; + unsigned char cert_filter_by[PKCS11H_MAX_ATTRIBUTE_SIZE]; + CK_ATTRIBUTE cert_filter[] = { + {CKA_CLASS, &cert_filter_class, sizeof (cert_filter_class)}, + {0, cert_filter_by, 0} + }; + + PKCS11ASSERT (pkcs11h_session!=NULL); + PKCS11ASSERT (szIdType!=NULL); + PKCS11ASSERT (szId!=NULL); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_loadCertificate entry pkcs11h_session=%p, szIdType=%s, szId=%s", + (void *)pkcs11h_session, + szIdType, + szId + ); + + if (rv == CKR_OK) { + 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 { + rv = CKR_ARGUMENTS_BAD; + } + } + + if (rv == CKR_OK) { + rv = pkcs11h_session->provider->f->C_FindObjectsInit ( + pkcs11h_session->session, + cert_filter, + sizeof (cert_filter) / sizeof (CK_ATTRIBUTE) + ); + } + + if (rv == CKR_OK) { + while ( + (rv = pkcs11h_session->provider->f->C_FindObjects ( + pkcs11h_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 ( + pkcs11h_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 + ); + } + } + } + } + + pkcs11h_session->provider->f->C_FindObjectsFinal ( + pkcs11h_session->session + ); + rv = CKR_OK; + } + + if ( + rv == CKR_OK && + selected_certificate_size == 0 + ) { + rv = CKR_ATTRIBUTE_VALUE_INVALID; + } + + if ( + rv == CKR_OK && + (pkcs11h_session->certificate = (unsigned char *)malloc (selected_certificate_size)) == NULL + ) { + rv = CKR_HOST_MEMORY; + } + + if (rv == CKR_OK) { + pkcs11h_session->certificate_size = selected_certificate_size; + memmove ( + pkcs11h_session->certificate, + selected_certificate, + selected_certificate_size + ); + } + + if ( + rv == CKR_OK && + (pkcs11h_session->certificate_id = (unsigned char *)malloc (selected_id_size)) == NULL + ) { + rv = CKR_HOST_MEMORY; + } + + if (rv == CKR_OK) { + pkcs11h_session->certificate_id_size = selected_id_size; + memmove ( + pkcs11h_session->certificate_id, + selected_id, + selected_id_size + ); + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_loadCertificate return rv=%ld-'%s'", + rv, + pkcs11h_getMessage (rv) + ); + + return rv; +} + +static +CK_RV +_pkcs11h_loadKeyProperties ( + IN const pkcs11h_session_t pkcs11h_session +) { + CK_OBJECT_HANDLE key; + CK_RV rv = CKR_OK; + + 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)} + }; + + PKCS11ASSERT (pkcs11h_session!=NULL); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_loadKeyProperties entry pkcs11h_session=%p", + (void *)pkcs11h_session + ); + + if (!strcmp (pkcs11h_session->provider->szSignMode, "recover")) { + pkcs11h_session->fKeySignRecover = true; + } + else if (!strcmp (pkcs11h_session->provider->szSignMode, "sign")) { + pkcs11h_session->fKeySignRecover = false; + } + else { + if (rv == CKR_OK) { + rv = _pkcs11h_getObjectById ( + pkcs11h_session, + CKO_PRIVATE_KEY, + pkcs11h_session->certificate_id, + pkcs11h_session->certificate_id_size, + &key + ); + } + + if (rv == CKR_OK) { + rv = pkcs11h_session->provider->f->C_GetAttributeValue ( + pkcs11h_session->session, + key, + key_attrs, + sizeof (key_attrs) / sizeof (CK_ATTRIBUTE) + ); + } + + if (rv == CKR_OK) { + if (key_attrs_sign_recover != CK_FALSE) { + pkcs11h_session->fKeySignRecover = true; + } + else if (key_attrs_sign != CK_FALSE) { + pkcs11h_session->fKeySignRecover = false; + } + else { + rv = CKR_KEY_TYPE_INCONSISTENT; + } + } + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_loadKeyProperties return rv=%ld-'%s'", + rv, + pkcs11h_getMessage (rv) + ); + + return rv; +} + +static +CK_RV +_pkcs11h_validateSession ( + IN const pkcs11h_session_t pkcs11h_session +) { + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_validateSession entry pkcs11h_session=%p", + (void *)pkcs11h_session + ); + + if ( + pkcs11h_session->timePINExpire != (time_t)0 && + pkcs11h_session->timePINExpire < time (NULL) + ) { + _pkcs11h_logout (pkcs11h_session); + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_validateSession return" + ); + + return CKR_OK; +} + +static +CK_RV +_pkcs11h_login ( + IN const pkcs11h_session_t pkcs11h_session +) { + CK_RV rv = CKR_OK; + + + PKCS11ASSERT (pkcs11h_session!=NULL); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_login entry pkcs11h_session=%p", + (void *)pkcs11h_session + ); + + _pkcs11h_logout (pkcs11h_session); + + if (rv == CKR_OK) { + rv = _pkcs11h_resetSlot (pkcs11h_session); + } + + if (rv == CKR_OK) { + rv = pkcs11h_session->provider->f->C_OpenSession ( + pkcs11h_session->slot, + CKF_SERIAL_SESSION, + NULL_PTR, + NULL_PTR, + &pkcs11h_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 && + !pkcs11h_session->fProtectedAuthentication + ) { + if ( + !pkcs11h_data->hooks->pin_prompt ( + pkcs11h_data->hooks->pin_prompt_data, + pkcs11h_session->szLabel, + szPIN, + sizeof (szPIN) + ) + ) { + rv = CKR_FUNCTION_FAILED; + } + else { + utfPIN = (CK_UTF8CHAR_PTR)szPIN; + lPINLength = strlen (szPIN); + } + } + + if (pkcs11h_data->nPINCachePeriod == -1) { + pkcs11h_session->timePINExpire = 0; + } + else { + pkcs11h_session->timePINExpire = ( + time (NULL) + + (time_t)pkcs11h_data->nPINCachePeriod + ); + } + if ( + rv == CKR_OK && + (rv = pkcs11h_session->provider->f->C_Login ( + pkcs11h_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 && + pkcs11h_session->certificate_id != NULL + ) { + rv = _pkcs11h_getObjectById ( + pkcs11h_session, + CKO_PRIVATE_KEY, + pkcs11h_session->certificate_id, + pkcs11h_session->certificate_id_size, + &pkcs11h_session->key + ); + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_login return rv=%ld-'%s'", + rv, + pkcs11h_getMessage (rv) + ); + + return rv; +} + +static +CK_RV +_pkcs11h_logout ( + IN const pkcs11h_session_t pkcs11h_session +) { + PKCS11ASSERT (pkcs11h_session!=NULL); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_logout entry pkcs11h_session=%p", + (void *)pkcs11h_session + ); + + if (pkcs11h_session->session != (CK_SESSION_HANDLE)-1) { + pkcs11h_session->provider->f->C_Logout (pkcs11h_session->session); + pkcs11h_session->provider->f->C_CloseSession (pkcs11h_session->session); + pkcs11h_session->key = (CK_OBJECT_HANDLE)-1; + pkcs11h_session->session = (CK_SESSION_HANDLE)-1; + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_logout return" + ); + + return CKR_OK; +} + + +/*======================================= + * Simplified PKCS#11 functions + */ + +static +bool +_pkcs11h_hooks_card_prompt_default ( + IN const void * pData, + IN const char * const szLabel +) { + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_hooks_card_prompt_default pData=%p, szLabel=%s", + pData, + szLabel + ); + + return false; +} + +static +bool +_pkcs11h_hooks_pin_prompt_default ( + IN const void * pData, + IN const char * const szLabel, + OUT char * const szPIN, + IN const size_t nMaxPIN +) { + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_hooks_pin_prompt_default pData=%p, szLabel=%s", + pData, + szLabel + ); + + return false; +} + +CK_RV +pkcs11h_initialize () { + + CK_RV rv = CKR_OK; + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_initialize entry" + ); + + pkcs11h_terminate (); + + if ( + rv == CKR_OK && + (pkcs11h_data = (pkcs11h_data_t)malloc (sizeof (struct pkcs11h_data_s))) == NULL + ) { + rv = CKR_HOST_MEMORY; + } + + if (rv == CKR_OK) { + memset (pkcs11h_data, 0, sizeof (struct pkcs11h_data_s)); + } + + if ( + rv == CKR_OK && + (pkcs11h_data->hooks = (pkcs11h_hooks_t)malloc (sizeof (struct pkcs11h_hooks_s))) == NULL + ) { + rv = CKR_HOST_MEMORY; + } + + if (rv == CKR_OK) { + memset (pkcs11h_data->hooks, 0, sizeof (struct pkcs11h_hooks_s)); + + pkcs11h_data->fInitialized = true; + pkcs11h_data->nPINCachePeriod = -1; + + pkcs11h_setCardPromptHook (_pkcs11h_hooks_card_prompt_default, NULL); + pkcs11h_setPINPromptHook (_pkcs11h_hooks_pin_prompt_default, NULL); + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_initialize return rv=%ld-'%s'", + rv, + pkcs11h_getMessage (rv) + ); + + return rv; +} + +CK_RV +pkcs11h_terminate () { + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_terminate entry" + ); + + if (pkcs11h_data != NULL) { + pkcs11h_provider_t last = NULL; + + for ( + ; + pkcs11h_data->providers != NULL; + pkcs11h_data->providers = pkcs11h_data->providers->next + ) { + if (last != NULL) { + free (last); + } + last = pkcs11h_data->providers; + + if (pkcs11h_data->providers->szSignMode != NULL) { + free (pkcs11h_data->providers->szSignMode); + pkcs11h_data->providers->szSignMode = NULL; + } + + if (pkcs11h_data->providers->fShouldFinalize) { + pkcs11h_data->providers->f->C_Finalize (NULL); + pkcs11h_data->providers->fShouldFinalize = false; + } + + if (pkcs11h_data->providers->f != NULL) { + pkcs11h_data->providers->f = NULL; + } + + if (pkcs11h_data->providers->hLibrary != NULL) { +#if defined(WIN32) + FreeLibrary (pkcs11h_data->providers->hLibrary); +#else + dlclose (pkcs11h_data->providers->hLibrary); +#endif + pkcs11h_data->providers->hLibrary = NULL; + } + } + + if (last != NULL) { + free (last); + } + + if (pkcs11h_data->hooks != NULL) { + free (pkcs11h_data->hooks); + pkcs11h_data->hooks = NULL; + } + + free (pkcs11h_data); + pkcs11h_data = NULL; + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_terminate return" + ); + + return CKR_OK; +} + +CK_RV +pkcs11h_setPINPromptHook ( + IN const pkcs11h_hook_pin_prompt_t hook, + IN void * const pData +) { + PKCS11ASSERT (pkcs11h_data!=NULL); + PKCS11ASSERT (pkcs11h_data->fInitialized); + + pkcs11h_data->hooks->pin_prompt = hook; + pkcs11h_data->hooks->pin_prompt_data = pData; + + return CKR_OK; +} + +CK_RV +pkcs11h_setCardPromptHook ( + IN const pkcs11h_hook_card_prompt_t hook, + IN void * const pData +) { + PKCS11ASSERT (pkcs11h_data!=NULL); + PKCS11ASSERT (pkcs11h_data->fInitialized); + + pkcs11h_data->hooks->card_prompt = hook; + pkcs11h_data->hooks->card_prompt_data = pData; + + return CKR_OK; +} + +CK_RV +pkcs11h_setPINCachePeriod ( + IN const int nPINCachePeriod +) { + PKCS11ASSERT (pkcs11h_data!=NULL); + PKCS11ASSERT (pkcs11h_data->fInitialized); + + pkcs11h_data->nPINCachePeriod = nPINCachePeriod; + + return CKR_OK; +} + +CK_RV +pkcs11h_addProvider ( + IN const char * const szProvider, + IN const char * const szSignMode +) { + pkcs11h_provider_t provider = NULL; + CK_C_GetFunctionList gfl = NULL; + CK_RV rv = CKR_OK; + + PKCS11ASSERT (pkcs11h_data!=NULL); + PKCS11ASSERT (pkcs11h_data->fInitialized); + PKCS11ASSERT (szProvider!=NULL); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_addProvider entry pid=%d, szProvider=%s, szSignMode=%s", +#if defined(WIN32) + 0, +#else + getpid (), +#endif + szProvider, + szSignMode + ); + + if ( + rv == CKR_OK && + (provider = (pkcs11h_provider_t)malloc (sizeof (struct pkcs11h_provider_s))) == NULL + ) { + rv = CKR_HOST_MEMORY; + } + + if (rv == CKR_OK) { + memset (provider, 0, sizeof (struct pkcs11h_provider_s)); + provider->szName = strdup (szProvider); + + 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 (pkcs11h_data->providers == NULL) { + pkcs11h_data->providers = provider; + } + else { + pkcs11h_provider_t last = NULL; + + for ( + last = pkcs11h_data->providers; + last->next != NULL; + last = last->next + ); + last->next = provider; + } + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_addProvider return rv=%ld-'%s'", + rv, + pkcs11h_getMessage (rv) + ); + + return rv; +} + +CK_RV +pkcs11h_forkFixup () { + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_forkFixup entry pid=%d", +#if defined(WIN32) + 0 +#else + getpid () +#endif + ); + + if (pkcs11h_data != NULL && pkcs11h_data->fInitialized) { + + pkcs11h_provider_t current; + + for ( + current = pkcs11h_data->providers; + current != NULL; + current = current->next + ) { + if (current->fEnabled) { + current->f->C_Initialize (NULL); + } + } + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_forkFixup return" + ); + + return CKR_OK; +} + +CK_RV +pkcs11h_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 pkcs11h_session_t * const p_pkcs11h_session +) { + pkcs11h_session_t pkcs11h_session; + CK_RV rv = CKR_OK; + + PKCS11ASSERT (pkcs11h_data!=NULL); + PKCS11ASSERT (pkcs11h_data->fInitialized); + PKCS11ASSERT (szSlotType!=NULL); + PKCS11ASSERT (szSlot!=NULL); + PKCS11ASSERT (szIdType!=NULL); + PKCS11ASSERT (szId!=NULL); + PKCS11ASSERT (p_pkcs11h_session!=NULL); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_createSession entry szSlotType=%s, szSlot=%s, szIdType=%s, szId=%s, fProtectedAuthentication=%d, p_pkcs11h_session=%p", + szSlotType, + szSlot, + szIdType, + szId, + fProtectedAuthentication ? 1 : 0, + (void *)p_pkcs11h_session + ); + + if ( + rv == CKR_OK && + (pkcs11h_session = (pkcs11h_session_t)malloc (sizeof (struct pkcs11h_session_s))) == NULL + ) { + rv = CKR_HOST_MEMORY; + } + + if (rv == CKR_OK) { + *p_pkcs11h_session = pkcs11h_session; + memset (pkcs11h_session, 0, sizeof (struct pkcs11h_session_s)); + } + + if (rv == CKR_OK) { + pkcs11h_session->key = (CK_OBJECT_HANDLE)-1; + pkcs11h_session->session = (CK_SESSION_HANDLE)-1; + pkcs11h_session->fProtectedAuthentication = fProtectedAuthentication; + } + + if (rv == CKR_OK) { + bool fCancel = false; + + do { + if (!strcmp (szSlotType, "id")) { + rv = _pkcs11h_getSlotById (pkcs11h_session, szSlot); + } + else if (!strcmp (szSlotType, "name")) { + rv = _pkcs11h_getSlotByName (pkcs11h_session, szSlot); + } + else if (!strcmp (szSlotType, "label")) { + rv = _pkcs11h_getSlotByLabel (pkcs11h_session, szSlot); + } + else { + rv = CKR_ARGUMENTS_BAD; + } + + if (rv == CKR_SLOT_ID_INVALID) { + char szLabel[1024]; + strcpy (szLabel, "SLOT("); + strncat (szLabel, szSlotType, sizeof (szLabel)-1); + strncat (szLabel, "=", sizeof (szLabel)-1); + strncat (szLabel, szSlot, sizeof (szLabel)-1); + strncat (szLabel, ")", sizeof (szLabel)-1); + szLabel[sizeof (szLabel)-1] = 0; + fCancel = !pkcs11h_data->hooks->card_prompt ( + pkcs11h_data->hooks->card_prompt_data, + szLabel + ); + } + } while (rv == CKR_SLOT_ID_INVALID && !fCancel); + } + + if (rv == CKR_OK) { + rv = _pkcs11h_setSessionTokenInfo (pkcs11h_session); + } + + if (rv == CKR_OK) { + rv = _pkcs11h_login ( + pkcs11h_session + ); + } + + if (rv == CKR_OK) { + rv = _pkcs11h_loadCertificate ( + pkcs11h_session, + szIdType, + szId + ); + } + + if (rv == CKR_OK) { + rv = _pkcs11h_loadKeyProperties ( + pkcs11h_session + ); + } + + /* + * Complete missing login process + */ + if (rv == CKR_OK) { + rv = _pkcs11h_getObjectById ( + pkcs11h_session, + CKO_PRIVATE_KEY, + pkcs11h_session->certificate_id, + pkcs11h_session->certificate_id_size, + &pkcs11h_session->key + ); + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_createSession return rv=%ld-'%s'", + rv, + pkcs11h_getMessage (rv) + ); + + return rv; +} + +CK_RV +pkcs11h_freeSession ( + IN const pkcs11h_session_t pkcs11h_session +) { + PKCS11ASSERT (pkcs11h_data!=NULL); + PKCS11ASSERT (pkcs11h_data->fInitialized); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_freeSession entry pkcs11h_session=%p", + (void *)pkcs11h_session + ); + + if (pkcs11h_session != NULL) { + _pkcs11h_logout (pkcs11h_session); + + if (pkcs11h_session->certificate != NULL) { + free (pkcs11h_session->certificate); + } + if (pkcs11h_session->certificate_id != NULL) { + free (pkcs11h_session->certificate_id); + } + + free (pkcs11h_session); + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_freeSession return" + ); + + return CKR_OK; +} + +CK_RV +pkcs11h_sign ( + IN const pkcs11h_session_t pkcs11h_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; + + PKCS11ASSERT (pkcs11h_data!=NULL); + PKCS11ASSERT (pkcs11h_data->fInitialized); + PKCS11ASSERT (pkcs11h_session!=NULL); + PKCS11ASSERT (source!=NULL); + PKCS11ASSERT (target_size!=NULL); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_sign entry pkcs11h_session=%p, mech_type=%ld, source=%p, source_size=%u, target=%p, target_size=%p", + (void *)pkcs11h_session, + mech_type, + source, + source_size, + target, + (void *)target_size + ); + + if (rv == CKR_OK) { + rv = _pkcs11h_validateSession (pkcs11h_session); + } + + while (rv == CKR_OK && !fOpSuccess) { + rv = pkcs11h_session->provider->f->C_SignInit ( + pkcs11h_session->session, + &mech, + pkcs11h_session->key + ); + + if (rv == CKR_OK) { + fOpSuccess = true; + } + else { + if (!fLogonRetry) { + fLogonRetry = true; + rv = _pkcs11h_login (pkcs11h_session); + } + } + } + + if (rv == CKR_OK) { + CK_ULONG size = *target_size; + rv = pkcs11h_session->provider->f->C_Sign ( + pkcs11h_session->session, + (CK_BYTE_PTR)source, + source_size, + (CK_BYTE_PTR)target, + &size + ); + + *target_size = (int)size; + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_sign return rv=%ld-'%s'", + rv, + pkcs11h_getMessage (rv) + ); + + return rv; +} + +CK_RV +pkcs11h_signRecover ( + IN const pkcs11h_session_t pkcs11h_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; + + PKCS11ASSERT (pkcs11h_data!=NULL); + PKCS11ASSERT (pkcs11h_data->fInitialized); + PKCS11ASSERT (pkcs11h_session!=NULL); + PKCS11ASSERT (source!=NULL); + PKCS11ASSERT (target_size!=NULL); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_signRecover entry pkcs11h_session=%p, mech_type=%ld, source=%p, source_size=%u, target=%p, target_size=%p", + (void *)pkcs11h_session, + mech_type, + source, + source_size, + target, + (void *)target_size + ); + + if (rv == CKR_OK) { + rv = _pkcs11h_validateSession (pkcs11h_session); + } + + while (rv == CKR_OK && !fOpSuccess) { + rv = pkcs11h_session->provider->f->C_SignRecoverInit ( + pkcs11h_session->session, + &mech, + pkcs11h_session->key + ); + + if (rv == CKR_OK) { + fOpSuccess = true; + } + else { + if (!fLogonRetry) { + fLogonRetry = true; + rv = _pkcs11h_login (pkcs11h_session); + } + } + } + + if (rv == CKR_OK) { + CK_ULONG size = *target_size; + rv = pkcs11h_session->provider->f->C_SignRecover ( + pkcs11h_session->session, + (CK_BYTE_PTR)source, + source_size, + (CK_BYTE_PTR)target, + &size + ); + + *target_size = (int)size; + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_signRecover return rv=%ld-'%s'", + rv, + pkcs11h_getMessage (rv) + ); + + return rv; +} + +CK_RV +pkcs11h_decrypt ( + IN const pkcs11h_session_t pkcs11h_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; + + PKCS11ASSERT (pkcs11h_data!=NULL); + PKCS11ASSERT (pkcs11h_data->fInitialized); + PKCS11ASSERT (pkcs11h_session!=NULL); + PKCS11ASSERT (source!=NULL); + PKCS11ASSERT (target_size!=NULL); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_decrypt entry pkcs11h_session=%p, mech_type=%ld, source=%p, source_size=%u, target=%p, target_size=%p", + (void *)pkcs11h_session, + mech_type, + source, + source_size, + target, + (void *)target_size + ); + + if (rv != CKR_OK) { + rv = _pkcs11h_validateSession (pkcs11h_session); + } + + while (rv == CKR_OK && !fOpSuccess) { + rv = pkcs11h_session->provider->f->C_DecryptInit ( + pkcs11h_session->session, + &mech, + pkcs11h_session->key + ); + + if (rv == CKR_OK) { + fOpSuccess = true; + } + else { + if (!fLogonRetry) { + fLogonRetry = true; + rv = _pkcs11h_login (pkcs11h_session); + } + } + } + + if (rv == CKR_OK) { + size = *target_size; + rv = pkcs11h_session->provider->f->C_Decrypt ( + pkcs11h_session->session, + (CK_BYTE_PTR)source, + source_size, + (CK_BYTE_PTR)target, + &size + ); + + *target_size = (int)size; + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_decrypt return rv=%ld-'%s'", + rv, + pkcs11h_getMessage (rv) + ); + + return rv; +} + +CK_RV +pkcs11h_getCertificate ( + IN const pkcs11h_session_t pkcs11h_session, + OUT unsigned char * const certificate, + IN OUT size_t * const certificate_size +) { + CK_RV rv = CKR_OK; + + PKCS11ASSERT (pkcs11h_data!=NULL); + PKCS11ASSERT (pkcs11h_data->fInitialized); + PKCS11ASSERT (certificate_size!=NULL); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_getCertificate entry pkcs11h_session=%p, certificate=%p, certificate_size=%p", + (void *)pkcs11h_session, + certificate, + (void *)certificate_size + ); + + *certificate_size = pkcs11h_session->certificate_size; + + if (certificate != NULL) { + if ( + rv == CKR_OK && + *certificate_size > pkcs11h_session->certificate_size + ) { + rv = CKR_BUFFER_TOO_SMALL; + } + + if (rv == CKR_OK) { + memmove (certificate, pkcs11h_session->certificate, *certificate_size); + } + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_getCertificate return rv=%ld-'%s'", + rv, + pkcs11h_getMessage (rv) + ); + + return CKR_OK; +} + +char * +pkcs11h_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 + */ + +static +pkcs11h_openssl_session_t +_pkcs11h_openssl_get_pkcs11h_openssl_session ( + IN OUT const RSA *rsa +) { + pkcs11h_openssl_session_t session; + + PKCS11ASSERT (rsa!=NULL); + session = (pkcs11h_openssl_session_t)RSA_get_app_data (rsa); + PKCS11ASSERT (session!=NULL); + + return session; +} + +static +pkcs11h_session_t +_pkcs11h_openssl_get_pkcs11h_session ( + IN OUT const RSA *rsa +) { + pkcs11h_openssl_session_t session; + + PKCS11ASSERT (rsa!=NULL); + session = (pkcs11h_openssl_session_t)RSA_get_app_data (rsa); + PKCS11ASSERT (session!=NULL); + PKCS11ASSERT (session->pkcs11h_session!=NULL); + + return session->pkcs11h_session; +} + +static +int +_pkcs11h_openssl_priv_enc ( + IN int flen, + IN const unsigned char *from, + OUT unsigned char *to, + IN OUT RSA *rsa, + IN int padding +) { + PKCS11LOG (PKCS11_LOG_WARN, "PKCS#11: Private key encryption not supported"); + return -1; +} + +static +int +_pkcs11h_openssl_priv_dec ( + IN int flen, + IN const unsigned char *from, + OUT unsigned char *to, + IN OUT RSA *rsa, + IN int padding +) { + pkcs11h_session_t pkcs11h_session = _pkcs11h_openssl_get_pkcs11h_session (rsa); + CK_RV rv = CKR_OK; + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_openssl_priv_dec entered - flen=%d, from=%p, to=%p, rsa=%p, padding=%d", + flen, + from, + to, + (void *)rsa, + padding + ); + + PKCS11ASSERT (from!=NULL); + PKCS11ASSERT (to!=NULL); + + PKCS11LOG ( + PKCS11_LOG_DEBUG1, + "PKCS#11: Performing decryption using private key" + ); + + if (padding != RSA_PKCS1_PADDING) { + rv = CKR_ARGUMENTS_BAD; + } + + if ( + rv == CKR_OK && + (rv = pkcs11h_decrypt ( + pkcs11h_session, + CKM_RSA_PKCS, + from, + flen, + to, + (size_t *)&flen + )) != CKR_OK + ) { + PKCS11LOG (PKCS11_LOG_WARN, "PKCS#11: Cannot decrypt using private key %ld:'%s'", rv, pkcs11h_getMessage (rv)); + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_openssl_priv_dec - return rv=%ld-'%s'", + rv, + pkcs11h_getMessage (rv) + ); + + return rv == CKR_OK ? 1 : -1; +} + +static +int +_pkcs11h_openssl_sign ( + IN int type, + IN const unsigned char *m, + IN unsigned int m_len, + OUT unsigned char *sigret, + OUT unsigned int *siglen, + IN OUT const RSA *rsa +) { + pkcs11h_openssl_session_t pkcs11h_openssl_session = _pkcs11h_openssl_get_pkcs11h_openssl_session (rsa); + pkcs11h_session_t pkcs11h_session = _pkcs11h_openssl_get_pkcs11h_session (rsa); + CK_RV rv = CKR_OK; + + int myrsa_size = 0; + + unsigned char *enc_alloc = NULL; + unsigned char *enc; + int enc_len = 0; + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_openssl_sign entered - type=%d, m=%p, m_len=%u, signret=%p, signlen=%p, rsa=%p", + type, + m, + m_len, + sigret, + (void *)siglen, + (void *)rsa + ); + + PKCS11ASSERT (m!=NULL); + PKCS11ASSERT (sigret!=NULL); + PKCS11ASSERT (siglen!=NULL); + + if (rv == CKR_OK) { + myrsa_size=RSA_size(rsa); + } + + if (pkcs11h_openssl_session->fShouldPadSign) { + X509_SIG sig; + ASN1_TYPE parameter; + X509_ALGOR algor; + ASN1_OCTET_STRING digest; + + if ( + rv == CKR_OK && + (enc=enc_alloc=(unsigned char *)malloc ((unsigned int)myrsa_size+1)) == NULL + ) { + rv = CKR_HOST_MEMORY; + } + + if (rv == CKR_OK) { + sig.algor= &algor; + } + + if ( + rv == CKR_OK && + (sig.algor->algorithm=OBJ_nid2obj(type)) == NULL + ) { + rv = CKR_FUNCTION_FAILED; + } + + if ( + rv == CKR_OK && + sig.algor->algorithm->length == 0 + ) { + rv = CKR_KEY_SIZE_RANGE; + } + + if (rv == CKR_OK) { + parameter.type=V_ASN1_NULL; + parameter.value.ptr=NULL; + + sig.algor->parameter= ¶meter; + + sig.digest=&digest; + sig.digest->data=(unsigned char *)m; + sig.digest->length=m_len; + } + + if ( + rv == CKR_OK && + (enc_len=i2d_X509_SIG(&sig,NULL)) < 0 + ) { + rv = CKR_FUNCTION_FAILED; + } + + if ( + rv == CKR_OK && + enc_len > (myrsa_size-RSA_PKCS1_PADDING_SIZE) + ) { + rv = CKR_KEY_SIZE_RANGE; + } + + if (rv == CKR_OK) { + unsigned char *p=enc; + i2d_X509_SIG(&sig,&p); + } + } + else { + if (rv == CKR_OK) { + enc = (unsigned char *)m; + enc_len = m_len; + } + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG1, + "PKCS#11: Performing signature" + ); + + *siglen = myrsa_size; + + if (pkcs11h_session->fKeySignRecover) { + if ( + (rv = pkcs11h_signRecover ( + pkcs11h_session, + CKM_RSA_PKCS, + enc, + enc_len, + sigret, + siglen + )) != CKR_OK + ) { + PKCS11LOG (PKCS11_LOG_WARN, "PKCS#11: Cannot perform signature-recover %ld:'%s'", rv, pkcs11h_getMessage (rv)); + } + } + else { + if ( + (rv = pkcs11h_sign ( + pkcs11h_session, + CKM_RSA_PKCS, + enc, + enc_len, + sigret, + siglen + )) != CKR_OK + ) { + PKCS11LOG (PKCS11_LOG_WARN, "PKCS#11: Cannot perform signature %ld:'%s'", rv, pkcs11h_getMessage (rv)); + } + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_openssl_sign - return rv=%ld-'%s'", + rv, + pkcs11h_getMessage (rv) + ); + + if (enc_alloc != NULL) { + free (enc_alloc); + } + + return rv == CKR_OK ? 1 : -1; +} + +static +int +_pkcs11h_openssl_finish ( + IN OUT RSA *rsa +) { + pkcs11h_openssl_session_t pkcs11h_openssl_session = _pkcs11h_openssl_get_pkcs11h_openssl_session (rsa); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_openssl_finish - entered rsa=%p", + (void *)rsa + ); + + RSA_set_app_data (rsa, NULL); + + if (pkcs11h_openssl_session->orig_finish != NULL) { + pkcs11h_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 + } + + pkcs11h_openssl_freeSession (pkcs11h_openssl_session); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: _pkcs11h_openssl_finish - return" + ); + + return 1; +} + +pkcs11h_openssl_session_t +pkcs11h_openssl_createSession ( + IN const bool fShouldPadSign +) { + pkcs11h_openssl_session_t pkcs11h_openssl_session = NULL; + bool fOK = true; + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_openssl_createSession - entry fShouldPadSign=%d", + fShouldPadSign ? 1 : 0 + ); + + if ( + fOK && + (pkcs11h_openssl_session = (pkcs11h_openssl_session_t)malloc (sizeof (struct pkcs11h_openssl_session_s))) == NULL + ) { + fOK = false; + PKCS11LOG (PKCS11_LOG_WARN, "PKCS#11: Cannot allocate memory"); + } + + if (fOK) { + memset (pkcs11h_openssl_session, 0, sizeof (struct pkcs11h_openssl_session_s)); + } + + if (fOK) { + pkcs11h_openssl_session->fShouldPadSign = fShouldPadSign; + pkcs11h_openssl_session->nReferenceCount = 1; + } + + if (!fOK) { + free (pkcs11h_openssl_session); + pkcs11h_openssl_session = NULL; + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_openssl_createSession - return pkcs11h_openssl_session=%p", + (void *)pkcs11h_openssl_session + ); + + return pkcs11h_openssl_session; +} + +void +pkcs11h_openssl_freeSession ( + IN const pkcs11h_openssl_session_t pkcs11h_openssl_session +) { + PKCS11ASSERT (pkcs11h_openssl_session!=NULL); + PKCS11ASSERT (pkcs11h_openssl_session->nReferenceCount>0); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_openssl_freeSession - entry pkcs11h_openssl_session=%p, count=%d", + (void *)pkcs11h_openssl_session, + pkcs11h_openssl_session->nReferenceCount + ); + + pkcs11h_openssl_session->nReferenceCount--; + + if (pkcs11h_openssl_session->nReferenceCount == 0) { + if (pkcs11h_openssl_session->x509) { + X509_free (pkcs11h_openssl_session->x509); + pkcs11h_openssl_session->x509 = NULL; + } + if (pkcs11h_openssl_session->pkcs11h_session != NULL) { + pkcs11h_freeSession (pkcs11h_openssl_session->pkcs11h_session); + pkcs11h_openssl_session->pkcs11h_session = NULL; + } + + free (pkcs11h_openssl_session); + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_openssl_freeSession - return" + ); +} + +RSA * +pkcs11h_openssl_getRSA ( + IN const pkcs11h_openssl_session_t pkcs11h_openssl_session +) { + X509 *x509 = NULL; + RSA *rsa = NULL; + EVP_PKEY *pubkey = NULL; + CK_RV rv = CKR_OK; + + unsigned char certificate[10*1024]; + size_t certificate_size; + unsigned char *p; + bool fOK = true; + + PKCS11ASSERT (pkcs11h_openssl_session!=NULL); + PKCS11ASSERT (!pkcs11h_openssl_session->fInitialized); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_openssl_getRSA - entry pkcs11h_openssl_session=%p", + (void *)pkcs11h_openssl_session + ); + + if ( + fOK && + (x509 = X509_new ()) == NULL + ) { + fOK = false; + PKCS11LOG (PKCS11_LOG_WARN, "PKCS#11: Unable to allocate certificate object"); + } + + certificate_size = sizeof (certificate); + if ( + fOK && + (rv = pkcs11h_getCertificate ( + pkcs11h_openssl_session->pkcs11h_session, + certificate, + &certificate_size + )) != CKR_OK + ) { + fOK = false; + PKCS11LOG (PKCS11_LOG_WARN, "PKCS#11: Cannot read X.509 certificate from token %ld-'%s'", rv, pkcs11h_getMessage (rv)); + } + + p = certificate; + if ( + fOK && + !d2i_X509 (&x509, &p, certificate_size) + ) { + fOK = false; + PKCS11LOG (PKCS11_LOG_WARN, "PKCS#11: Unable to parse X.509 certificate"); + } + + if ( + fOK && + (pubkey = X509_get_pubkey (x509)) == NULL + ) { + fOK = false; + PKCS11LOG (PKCS11_LOG_WARN, "PKCS#11: Cannot get public key"); + } + + if ( + fOK && + pubkey->type != EVP_PKEY_RSA + ) { + fOK = false; + PKCS11LOG (PKCS11_LOG_WARN, "PKCS#11: Invalid public key algorithm"); + } + + if ( + fOK && + (rsa = EVP_PKEY_get1_RSA (pubkey)) == NULL + ) { + fOK = false; + PKCS11LOG (PKCS11_LOG_WARN, "PKCS#11: Cannot get RSA key"); + } + + if (fOK) { + const RSA_METHOD *def = RSA_get_default_method(); + + memmove (&pkcs11h_openssl_session->smart_rsa, def, sizeof(RSA_METHOD)); + + pkcs11h_openssl_session->orig_finish = def->finish; + + pkcs11h_openssl_session->smart_rsa.name = "pkcs11"; + pkcs11h_openssl_session->smart_rsa.rsa_priv_enc = _pkcs11h_openssl_priv_enc; + pkcs11h_openssl_session->smart_rsa.rsa_priv_dec = _pkcs11h_openssl_priv_dec; + pkcs11h_openssl_session->smart_rsa.rsa_sign = _pkcs11h_openssl_sign; + pkcs11h_openssl_session->smart_rsa.finish = _pkcs11h_openssl_finish; + pkcs11h_openssl_session->smart_rsa.flags = RSA_METHOD_FLAG_NO_CHECK | RSA_FLAG_EXT_PKEY; + + RSA_set_method (rsa, &pkcs11h_openssl_session->smart_rsa); + RSA_set_app_data (rsa, pkcs11h_openssl_session); + pkcs11h_openssl_session->nReferenceCount++; + } + +#ifdef BROKEN_OPENSSL_ENGINE + if (fOK) { + if (!rsa->engine) + rsa->engine = ENGINE_get_default_RSA(); + + ENGINE_set_RSA(ENGINE_get_default_RSA(), &pkcs11h_openssl_session->smart_rsa); + PKCS11LOG (PKCS11_LOG_WARN, "PKCS#11: OpenSSL engine support is broken! Workaround enabled"); + } +#endif + + if (fOK) { + /* + So that it won't hold RSA + */ + pkcs11h_openssl_session->x509 = X509_dup (x509); + rsa->flags |= RSA_FLAG_SIGN_VER; + pkcs11h_openssl_session->fInitialized = true; + } + else { + if (rsa != NULL) { + RSA_free (rsa); + rsa = NULL; + } + } + + /* + * 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; + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_openssl_getRSA - return rsa=%p", + (void *)rsa + ); + + return rsa; +} + +X509 * +pkcs11h_openssl_getX509 ( + IN const pkcs11h_openssl_session_t pkcs11h_openssl_session +) { + X509 *x509 = NULL; + + PKCS11ASSERT (pkcs11h_openssl_session!=NULL); + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_openssl_getX509 - entry pkcs11h_openssl_session=%p", + (void *)pkcs11h_openssl_session + ); + + if (pkcs11h_openssl_session->x509 != NULL) { + x509 = X509_dup (pkcs11h_openssl_session->x509); + } + + PKCS11LOG ( + PKCS11_LOG_DEBUG2, + "PKCS#11: pkcs11h_openssl_getX509 - return x509=%p", + (void *)x509 + ); + + return x509; +} + + +void +pkcs11h_standalone_dump_slots ( + IN const pkcs11h_output_print_t my_output, + IN const void *pData, + IN const char * const provider +) { + CK_RV rv = CKR_OK; + + pkcs11h_provider_t pkcs11h_provider; + + PKCS11ASSERT (provider!=NULL); + + if ( + rv == CKR_OK && + (rv = pkcs11h_initialize ()) != CKR_OK + ) { + my_output (pData, "PKCS#11: Cannot initialize interface %ld-'%s'\n", rv, pkcs11h_getMessage (rv)); + } + + if ( + rv == CKR_OK && + (rv = pkcs11h_addProvider (provider, NULL)) != CKR_OK + ) { + my_output (pData, "PKCS#11: Cannot initialize provider %ld-'%s'\n", rv, pkcs11h_getMessage (rv)); + } + + /* + * our provider is head + */ + if (rv == CKR_OK) { + pkcs11h_provider = pkcs11h_data->providers; + if (pkcs11h_provider == NULL || !pkcs11h_provider->fEnabled) { + my_output (pData, "PKCS#11: Cannot get provider %ld-'%s'\n", rv, pkcs11h_getMessage (rv)); + rv = CKR_GENERAL_ERROR; + } + } + + if (rv == CKR_OK) { + CK_INFO info; + + if ((rv = pkcs11h_provider->f->C_GetInfo (&info)) != CKR_OK) { + my_output (pData, "PKCS#11: Cannot get PKCS#11 provider information %ld-'%s'\n", rv, pkcs11h_getMessage (rv)); + rv = CKR_OK; + } + else { + char szManufacturerID[sizeof (info.manufacturerID)+1]; + + _pkcs11h_fixupFixedString ( + (char *)info.manufacturerID, + szManufacturerID, + sizeof (info.manufacturerID) + ); + + my_output ( + pData, + ( + "Provider Information:\n" + "\tcryptokiVersion:\t%u.%u\n" + "\tmanufacturerID:\t\t%s\n" + "\tflags:\t\t\t%d\n" + "\n" + ), + info.cryptokiVersion.major, + info.cryptokiVersion.minor, + szManufacturerID, + (unsigned)info.flags + ); + } + } + + if (rv == CKR_OK) { + CK_SLOT_ID slots[1024]; + CK_ULONG slotnum; + CK_SLOT_ID s; + + slotnum = sizeof (slots) / sizeof (CK_SLOT_ID); + if ( + (rv = pkcs11h_provider->f->C_GetSlotList ( + FALSE, + slots, + &slotnum + )) != CKR_OK + ) { + my_output (pData, "PKCS#11: Cannot get slot list %ld-'%s'\n", rv, pkcs11h_getMessage (rv)); + } + else { + my_output ( + pData, + ( + "The following slots are available for use with this provider.\n" + "Each slot shown below may be used as a parameter to a\n" + "%s and %s options.\n" + "\n" + "Slots: (id - name)\n" + ), + PKCS11_PRM_SLOT_TYPE, + PKCS11_PRM_SLOT_ID + ); + for (s=0;sf->C_GetSlotInfo ( + slots[s], + &info + )) == CKR_OK + ) { + char szCurrentName[sizeof (info.slotDescription)+1]; + + _pkcs11h_fixupFixedString ( + (char *)info.slotDescription, + szCurrentName, + sizeof (info.slotDescription) + ); + + my_output (pData, "\t%lu - %s\n", slots[s], szCurrentName); + } + } + } + } + + pkcs11h_terminate (); +} + +static +bool +_pkcs11h_standalone_dump_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 +pkcs11h_standalone_dump_objects ( + IN const pkcs11h_output_print_t my_output, + IN const void *pData, + IN const char * const provider, + IN const char * const slot, + IN const char * const pin +) { + CK_SLOT_ID s; + CK_RV rv = CKR_OK; + + pkcs11h_provider_t pkcs11h_provider; + + PKCS11ASSERT (provider!=NULL); + PKCS11ASSERT (slot!=NULL); + PKCS11ASSERT (pin!=NULL); + + s = atoi (slot); + + if ( + rv == CKR_OK && + (rv = pkcs11h_initialize ()) != CKR_OK + ) { + my_output (pData, "PKCS#11: Cannot initialize interface %ld-'%s'\n", rv, pkcs11h_getMessage (rv)); + } + + if ( + rv == CKR_OK && + (rv = pkcs11h_setPINPromptHook (_pkcs11h_standalone_dump_objects_pin_prompt, (void *)pin)) != CKR_OK + ) { + my_output (pData, "PKCS#11: Cannot set hooks %ld-'%s'\n", rv, pkcs11h_getMessage (rv)); + } + + if ( + rv == CKR_OK && + (rv = pkcs11h_addProvider (provider, NULL)) != CKR_OK + ) { + my_output (pData, "PKCS#11: Cannot initialize provider %ld-'%s'\n", rv, pkcs11h_getMessage (rv)); + } + + /* + * our provider is head + */ + if (rv == CKR_OK) { + pkcs11h_provider = pkcs11h_data->providers; + if (pkcs11h_provider == NULL || !pkcs11h_provider->fEnabled) { + my_output (pData, "PKCS#11: Cannot get provider %ld-'%s'\n", rv, pkcs11h_getMessage (rv)); + rv = CKR_GENERAL_ERROR; + } + } + + if (rv == CKR_OK) { + CK_TOKEN_INFO info; + + if ( + (rv = pkcs11h_provider->f->C_GetTokenInfo ( + s, + &info + )) != CKR_OK + ) { + my_output (pData, "PKCS#11: Cannot get token information for slot %ld %ld-'%s'\n", s, rv, pkcs11h_getMessage (rv)); + rv = CKR_OK; + } + 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]; + + _pkcs11h_fixupFixedString ( + (char *)info.label, + szLabel, + sizeof (info.label) + ); + _pkcs11h_fixupFixedString ( + (char *)info.manufacturerID, + szManufacturerID, + sizeof (info.manufacturerID) + ); + _pkcs11h_fixupFixedString ( + (char *)info.model, + szModel, + sizeof (info.model) + ); + _pkcs11h_fixupFixedString ( + (char *)info.serialNumber, + szSerialNumber, + sizeof (info.serialNumber) + ); + + my_output ( + pData, + ( + "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" + "%s \"label\" %s \"%s\" options.\n" + "\n" + ), + szLabel, + szManufacturerID, + szModel, + szSerialNumber, + (unsigned)info.flags, + PKCS11_PRM_SLOT_TYPE, + PKCS11_PRM_SLOT_ID, + szLabel + ); + } + } + + if (rv == CKR_OK) { + CK_SESSION_HANDLE session; + + if ( + (rv = pkcs11h_provider->f->C_OpenSession ( + s, + CKF_SERIAL_SESSION, + NULL_PTR, + NULL_PTR, + &session + )) != CKR_OK + ) { + my_output (pData, "PKCS#11: Cannot open session to slot %ld %ld-'%s'\n", s, rv, pkcs11h_getMessage (rv)); + rv = CKR_OK; + } + else { + CK_OBJECT_HANDLE objects[10]; + CK_ULONG objects_found; + + if ( + (rv = pkcs11h_provider->f->C_Login ( + session, + CKU_USER, + (CK_CHAR_PTR)pin, + (CK_ULONG)strlen (pin) + )) != CKR_OK && + rv != CKR_USER_ALREADY_LOGGED_IN + ) { + my_output (pData, "PKCS#11: Cannot login to token on slot %ld %ld-'%s'\n", s, rv, pkcs11h_getMessage (rv)); + } + + if ( + (rv = pkcs11h_provider->f->C_FindObjectsInit ( + session, + NULL, + 0 + )) != CKR_OK + ) { + my_output (pData, "PKCS#11: Cannot query objects for token on slot %ld %ld-'%s'\n", s, rv, pkcs11h_getMessage (rv)); + } + + my_output ( + pData, + ( + "The following objects are available for use with this token.\n" + "Each object shown below may be used as a parameter to\n" + "%s and %s options.\n" + "\n" + ), + PKCS11_PRM_OBJ_TYPE, + PKCS11_PRM_OBJ_ID + ); + + while ( + (rv = pkcs11h_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; + + my_output ( + pData, + ( + "Object\n" + "\tLabel:\t\t%s\n" + "\tId:\n" + ), + 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) { + my_output (pData, "Cannot create x509 context\n"); + } + 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) { + my_output (pData, "Cannot create BIO context\n"); + } + 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; + } + + my_output ( + pData, + ( + "\tsubject:\t%s\n" + "\tserialNumber:\t%s\n" + "\tnotBefore:\t%s\n" + ), + 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)} + }; + + my_output (pData, "\tType:\t\tPrivate Key\n"); + + if ( + pkcs11h_provider->f->C_GetAttributeValue ( + session, + objects[i], + attrs_key, + sizeof (attrs_key) / sizeof (CK_ATTRIBUTE) + ) == CKR_OK + ) { + my_output ( + pData, + ( + "\tSign:\t\t%s\n" + "\tSign Recover:\t%s\n" + ), + sign ? "TRUE" : "FALSE", + sign_recover ? "TRUE" : "FALSE" + ); + } + } + else { + my_output (pData, "\tType:\t\tUnsupported\n"); + } + } + } + + pkcs11h_provider->f->C_FindObjectsFinal (session); + pkcs11h_provider->f->C_Logout (session); + pkcs11h_provider->f->C_CloseSession (session); + } + } + } + + pkcs11h_terminate (); +} + +#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 + +#else +static void dummy (void) {} +#endif /* PKCS11_HELPER_ENABLE */ -- cgit v1.2.3