| /* |
| * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. |
| * |
| * Use is subject to license terms. |
| */ |
| /* |
| * Copyright (c) 2012, OmniTI Computer Consulting, Inc. All rights reserved. |
| * Copyright 2018 OmniOS Community Edition (OmniOSce) Association. |
| * Copyright 2018 RackTop Systems. |
| */ |
| /* |
| * Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL |
| * project 2000. |
| */ |
| /* |
| * ==================================================================== |
| * Copyright (c) 2000-2004 The OpenSSL Project. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. All advertising materials mentioning features or use of this |
| * software must display the following acknowledgment: |
| * "This product includes software developed by the OpenSSL Project |
| * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" |
| * |
| * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to |
| * endorse or promote products derived from this software without |
| * prior written permission. For written permission, please contact |
| * licensing@OpenSSL.org. |
| * |
| * 5. Products derived from this software may not be called "OpenSSL" |
| * nor may "OpenSSL" appear in their names without prior written |
| * permission of the OpenSSL Project. |
| * |
| * 6. Redistributions of any form whatsoever must retain the following |
| * acknowledgment: |
| * "This product includes software developed by the OpenSSL Project |
| * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY |
| * EXPRESSED 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 OpenSSL PROJECT OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
| * STRICT LIABILITY, 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. |
| * ==================================================================== |
| * |
| * This product includes cryptographic software written by Eric Young |
| * (eay@cryptsoft.com). This product includes software written by Tim |
| * Hudson (tjh@cryptsoft.com). |
| * |
| */ |
| |
| #include <stdlib.h> |
| #include <kmfapiP.h> |
| #include <ber_der.h> |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <dirent.h> |
| #include <cryptoutil.h> |
| #include <synch.h> |
| #include <thread.h> |
| |
| /* OPENSSL related headers */ |
| #include <openssl/bio.h> |
| #include <openssl/bn.h> |
| #include <openssl/asn1.h> |
| #include <openssl/err.h> |
| #include <openssl/x509.h> |
| #include <openssl/rsa.h> |
| #include <openssl/dsa.h> |
| #include <openssl/x509v3.h> |
| #include <openssl/objects.h> |
| #include <openssl/pem.h> |
| #include <openssl/pkcs12.h> |
| #include <openssl/ocsp.h> |
| #include <openssl/des.h> |
| #include <openssl/rand.h> |
| #include "compat.h" |
| |
| #define PRINT_ANY_EXTENSION (\ |
| KMF_X509_EXT_KEY_USAGE |\ |
| KMF_X509_EXT_CERT_POLICIES |\ |
| KMF_X509_EXT_SUBJALTNAME |\ |
| KMF_X509_EXT_BASIC_CONSTRAINTS |\ |
| KMF_X509_EXT_NAME_CONSTRAINTS |\ |
| KMF_X509_EXT_POLICY_CONSTRAINTS |\ |
| KMF_X509_EXT_EXT_KEY_USAGE |\ |
| KMF_X509_EXT_INHIBIT_ANY_POLICY |\ |
| KMF_X509_EXT_AUTH_KEY_ID |\ |
| KMF_X509_EXT_SUBJ_KEY_ID |\ |
| KMF_X509_EXT_POLICY_MAPPING) |
| |
| static uchar_t P[] = { 0x00, 0x8d, 0xf2, 0xa4, 0x94, 0x49, 0x22, 0x76, |
| 0xaa, 0x3d, 0x25, 0x75, 0x9b, 0xb0, 0x68, 0x69, |
| 0xcb, 0xea, 0xc0, 0xd8, 0x3a, 0xfb, 0x8d, 0x0c, |
| 0xf7, 0xcb, 0xb8, 0x32, 0x4f, 0x0d, 0x78, 0x82, |
| 0xe5, 0xd0, 0x76, 0x2f, 0xc5, 0xb7, 0x21, 0x0e, |
| 0xaf, 0xc2, 0xe9, 0xad, 0xac, 0x32, 0xab, 0x7a, |
| 0xac, 0x49, 0x69, 0x3d, 0xfb, 0xf8, 0x37, 0x24, |
| 0xc2, 0xec, 0x07, 0x36, 0xee, 0x31, 0xc8, 0x02, |
| 0x91 }; |
| |
| static uchar_t Q[] = { 0x00, 0xc7, 0x73, 0x21, 0x8c, 0x73, 0x7e, 0xc8, |
| 0xee, 0x99, 0x3b, 0x4f, 0x2d, 0xed, 0x30, 0xf4, |
| 0x8e, 0xda, 0xce, 0x91, 0x5f }; |
| |
| static uchar_t G[] = { 0x00, 0x62, 0x6d, 0x02, 0x78, 0x39, 0xea, 0x0a, |
| 0x13, 0x41, 0x31, 0x63, 0xa5, 0x5b, 0x4c, 0xb5, |
| 0x00, 0x29, 0x9d, 0x55, 0x22, 0x95, 0x6c, 0xef, |
| 0xcb, 0x3b, 0xff, 0x10, 0xf3, 0x99, 0xce, 0x2c, |
| 0x2e, 0x71, 0xcb, 0x9d, 0xe5, 0xfa, 0x24, 0xba, |
| 0xbf, 0x58, 0xe5, 0xb7, 0x95, 0x21, 0x92, 0x5c, |
| 0x9c, 0xc4, 0x2e, 0x9f, 0x6f, 0x46, 0x4b, 0x08, |
| 0x8c, 0xc5, 0x72, 0xaf, 0x53, 0xe6, 0xd7, 0x88, |
| 0x02 }; |
| |
| #define SET_ERROR(h, c) h->lasterr.kstype = KMF_KEYSTORE_OPENSSL; \ |
| h->lasterr.errcode = c; |
| |
| #define SET_SYS_ERROR(h, c) h->lasterr.kstype = -1; h->lasterr.errcode = c; |
| |
| /* |
| * Declare some new macros for managing stacks of EVP_PKEYS. |
| */ |
| #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) |
| DECLARE_STACK_OF(EVP_PKEY) |
| |
| #define sk_EVP_PKEY_new_null() SKM_sk_new_null(EVP_PKEY) |
| #define sk_EVP_PKEY_free(st) SKM_sk_free(EVP_PKEY, (st)) |
| #define sk_EVP_PKEY_num(st) SKM_sk_num(EVP_PKEY, (st)) |
| #define sk_EVP_PKEY_value(st, i) SKM_sk_value(EVP_PKEY, (st), (i)) |
| #define sk_EVP_PKEY_push(st, val) SKM_sk_push(EVP_PKEY, (st), (val)) |
| #define sk_EVP_PKEY_pop_free(st, free_func) SKM_sk_pop_free(EVP_PKEY, (st), \ |
| (free_func)) |
| |
| #else |
| /* LINTED E_STATIC_UNUSED */ |
| DEFINE_STACK_OF(EVP_PKEY) |
| #endif |
| |
| mutex_t init_lock = DEFAULTMUTEX; |
| static int ssl_initialized = 0; |
| static BIO *bio_err = NULL; |
| |
| static int |
| test_for_file(char *, mode_t); |
| static KMF_RETURN |
| openssl_parse_bag(PKCS12_SAFEBAG *, char *, int, |
| STACK_OF(EVP_PKEY) *, STACK_OF(X509) *); |
| |
| static KMF_RETURN |
| local_export_pk12(KMF_HANDLE_T, KMF_CREDENTIAL *, int, KMF_X509_DER_CERT *, |
| int, KMF_KEY_HANDLE *, char *); |
| |
| static KMF_RETURN set_pkey_attrib(EVP_PKEY *, ASN1_TYPE *, int); |
| |
| static KMF_RETURN |
| extract_pem(KMF_HANDLE *, char *, char *, KMF_BIGINT *, char *, |
| CK_UTF8CHAR *, CK_ULONG, EVP_PKEY **, KMF_DATA **, int *); |
| |
| static KMF_RETURN |
| kmf_load_cert(KMF_HANDLE *, char *, char *, KMF_BIGINT *, KMF_CERT_VALIDITY, |
| char *, KMF_DATA *); |
| |
| static KMF_RETURN |
| load_certs(KMF_HANDLE *, char *, char *, KMF_BIGINT *, KMF_CERT_VALIDITY, |
| char *, KMF_DATA **, uint32_t *); |
| |
| static KMF_RETURN |
| sslBN2KMFBN(BIGNUM *, KMF_BIGINT *); |
| |
| static EVP_PKEY * |
| ImportRawRSAKey(KMF_RAW_RSA_KEY *); |
| |
| static KMF_RETURN |
| convertToRawKey(EVP_PKEY *, KMF_RAW_KEY_DATA *); |
| |
| KMF_RETURN |
| OpenSSL_FindCert(KMF_HANDLE_T, int, KMF_ATTRIBUTE *); |
| |
| void |
| OpenSSL_FreeKMFCert(KMF_HANDLE_T, KMF_X509_DER_CERT *); |
| |
| KMF_RETURN |
| OpenSSL_StoreCert(KMF_HANDLE_T handle, int, KMF_ATTRIBUTE *); |
| |
| KMF_RETURN |
| OpenSSL_DeleteCert(KMF_HANDLE_T handle, int, KMF_ATTRIBUTE *); |
| |
| KMF_RETURN |
| OpenSSL_CreateKeypair(KMF_HANDLE_T, int, KMF_ATTRIBUTE *); |
| |
| KMF_RETURN |
| OpenSSL_StoreKey(KMF_HANDLE_T, int, KMF_ATTRIBUTE *); |
| |
| KMF_RETURN |
| OpenSSL_EncodePubKeyData(KMF_HANDLE_T, KMF_KEY_HANDLE *, KMF_DATA *); |
| |
| KMF_RETURN |
| OpenSSL_SignData(KMF_HANDLE_T, KMF_KEY_HANDLE *, KMF_OID *, |
| KMF_DATA *, KMF_DATA *); |
| |
| KMF_RETURN |
| OpenSSL_DeleteKey(KMF_HANDLE_T, int, KMF_ATTRIBUTE *); |
| |
| KMF_RETURN |
| OpenSSL_ImportCRL(KMF_HANDLE_T, int, KMF_ATTRIBUTE *); |
| |
| KMF_RETURN |
| OpenSSL_DeleteCRL(KMF_HANDLE_T, int, KMF_ATTRIBUTE *); |
| |
| KMF_RETURN |
| OpenSSL_ListCRL(KMF_HANDLE_T, int, KMF_ATTRIBUTE *); |
| |
| KMF_RETURN |
| OpenSSL_FindCertInCRL(KMF_HANDLE_T, int, KMF_ATTRIBUTE *); |
| |
| KMF_RETURN |
| OpenSSL_CertGetPrintable(KMF_HANDLE_T, const KMF_DATA *, |
| KMF_PRINTABLE_ITEM, char *); |
| |
| KMF_RETURN |
| OpenSSL_GetErrorString(KMF_HANDLE_T, char **); |
| |
| KMF_RETURN |
| OpenSSL_FindPrikeyByCert(KMF_HANDLE_T, int, KMF_ATTRIBUTE *); |
| |
| KMF_RETURN |
| OpenSSL_DecryptData(KMF_HANDLE_T, KMF_KEY_HANDLE *, KMF_OID *, |
| KMF_DATA *, KMF_DATA *); |
| |
| KMF_RETURN |
| OpenSSL_CreateOCSPRequest(KMF_HANDLE_T, int, KMF_ATTRIBUTE *); |
| |
| KMF_RETURN |
| OpenSSL_GetOCSPStatusForCert(KMF_HANDLE_T, int, KMF_ATTRIBUTE *); |
| |
| KMF_RETURN |
| OpenSSL_FindKey(KMF_HANDLE_T, int, KMF_ATTRIBUTE *); |
| |
| KMF_RETURN |
| OpenSSL_ExportPK12(KMF_HANDLE_T, int, KMF_ATTRIBUTE *); |
| |
| KMF_RETURN |
| OpenSSL_CreateSymKey(KMF_HANDLE_T, int, KMF_ATTRIBUTE *); |
| |
| KMF_RETURN |
| OpenSSL_GetSymKeyValue(KMF_HANDLE_T, KMF_KEY_HANDLE *, KMF_RAW_SYM_KEY *); |
| |
| KMF_RETURN |
| OpenSSL_VerifyCRLFile(KMF_HANDLE_T, char *, KMF_DATA *); |
| |
| KMF_RETURN |
| OpenSSL_CheckCRLDate(KMF_HANDLE_T, char *); |
| |
| static |
| KMF_PLUGIN_FUNCLIST openssl_plugin_table = |
| { |
| 1, /* Version */ |
| NULL, /* ConfigureKeystore */ |
| OpenSSL_FindCert, |
| OpenSSL_FreeKMFCert, |
| OpenSSL_StoreCert, |
| NULL, /* ImportCert */ |
| OpenSSL_ImportCRL, |
| OpenSSL_DeleteCert, |
| OpenSSL_DeleteCRL, |
| OpenSSL_CreateKeypair, |
| OpenSSL_FindKey, |
| OpenSSL_EncodePubKeyData, |
| OpenSSL_SignData, |
| OpenSSL_DeleteKey, |
| OpenSSL_ListCRL, |
| NULL, /* FindCRL */ |
| OpenSSL_FindCertInCRL, |
| OpenSSL_GetErrorString, |
| OpenSSL_FindPrikeyByCert, |
| OpenSSL_DecryptData, |
| OpenSSL_ExportPK12, |
| OpenSSL_CreateSymKey, |
| OpenSSL_GetSymKeyValue, |
| NULL, /* SetTokenPin */ |
| OpenSSL_StoreKey, |
| NULL /* Finalize */ |
| }; |
| |
| #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) |
| static mutex_t *lock_cs; |
| static long *lock_count; |
| |
| static void |
| /* ARGSUSED1 */ |
| locking_cb(int mode, int type, char *file, int line) |
| { |
| if (mode & CRYPTO_LOCK) { |
| (void) mutex_lock(&(lock_cs[type])); |
| lock_count[type]++; |
| } else { |
| (void) mutex_unlock(&(lock_cs[type])); |
| } |
| } |
| |
| static unsigned long |
| thread_id() |
| { |
| return ((unsigned long)thr_self()); |
| } |
| #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L || LIBRESSL_VERSION_NUMBER */ |
| |
| KMF_PLUGIN_FUNCLIST * |
| KMF_Plugin_Initialize() |
| { |
| #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) |
| int i; |
| #endif |
| |
| (void) mutex_lock(&init_lock); |
| if (!ssl_initialized) { |
| /* |
| * Add support for extension OIDs that are not yet in the |
| * openssl default set. |
| */ |
| (void) OBJ_create("2.5.29.30", "nameConstraints", |
| "X509v3 Name Constraints"); |
| (void) OBJ_create("2.5.29.33", "policyMappings", |
| "X509v3 Policy Mappings"); |
| (void) OBJ_create("2.5.29.36", "policyConstraints", |
| "X509v3 Policy Constraints"); |
| (void) OBJ_create("2.5.29.46", "freshestCRL", |
| "X509v3 Freshest CRL"); |
| (void) OBJ_create("2.5.29.54", "inhibitAnyPolicy", |
| "X509v3 Inhibit Any-Policy"); |
| |
| #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) |
| /* |
| * Set up for thread-safe operation. |
| * This is not required for OpenSSL 1.1 |
| */ |
| lock_cs = OPENSSL_malloc(CRYPTO_num_locks() * sizeof (mutex_t)); |
| if (lock_cs == NULL) { |
| (void) mutex_unlock(&init_lock); |
| return (NULL); |
| } |
| |
| lock_count = OPENSSL_malloc(CRYPTO_num_locks() * sizeof (long)); |
| if (lock_count == NULL) { |
| OPENSSL_free(lock_cs); |
| (void) mutex_unlock(&init_lock); |
| return (NULL); |
| } |
| |
| for (i = 0; i < CRYPTO_num_locks(); i++) { |
| lock_count[i] = 0; |
| (void) mutex_init(&lock_cs[i], USYNC_THREAD, NULL); |
| } |
| |
| CRYPTO_set_id_callback((unsigned long (*)())thread_id); |
| if (CRYPTO_get_locking_callback() == NULL) |
| CRYPTO_set_locking_callback((void (*)())locking_cb); |
| |
| (void) OpenSSL_add_all_algorithms(); |
| |
| /* Enable error strings for reporting */ |
| (void) ERR_load_crypto_strings(); |
| #endif |
| |
| ssl_initialized = 1; |
| } |
| (void) mutex_unlock(&init_lock); |
| |
| return (&openssl_plugin_table); |
| } |
| |
| /* |
| * Convert an SSL DN to a KMF DN. |
| */ |
| static KMF_RETURN |
| get_x509_dn(X509_NAME *sslDN, KMF_X509_NAME *kmfDN) |
| { |
| KMF_DATA derdata; |
| KMF_RETURN rv = KMF_OK; |
| uchar_t *tmp; |
| |
| /* Convert to raw DER format */ |
| derdata.Length = i2d_X509_NAME(sslDN, NULL); |
| if ((tmp = derdata.Data = (uchar_t *)OPENSSL_malloc(derdata.Length)) |
| == NULL) { |
| return (KMF_ERR_MEMORY); |
| } |
| (void) i2d_X509_NAME(sslDN, &tmp); |
| |
| /* Decode to KMF format */ |
| rv = DerDecodeName(&derdata, kmfDN); |
| if (rv != KMF_OK) { |
| rv = KMF_ERR_BAD_CERT_FORMAT; |
| } |
| OPENSSL_free(derdata.Data); |
| |
| return (rv); |
| } |
| |
| int |
| isdir(char *path) |
| { |
| struct stat s; |
| |
| if (stat(path, &s) == -1) |
| return (0); |
| |
| return ((s.st_mode & S_IFMT) == S_IFDIR); |
| } |
| |
| static KMF_RETURN |
| ssl_cert2KMFDATA(KMF_HANDLE *kmfh, X509 *x509cert, KMF_DATA *cert) |
| { |
| KMF_RETURN rv = KMF_OK; |
| unsigned char *buf = NULL, *p; |
| int len; |
| |
| /* |
| * Convert the X509 internal struct to DER encoded data |
| */ |
| if ((len = i2d_X509(x509cert, NULL)) < 0) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| rv = KMF_ERR_BAD_CERT_FORMAT; |
| goto cleanup; |
| } |
| if ((buf = malloc(len)) == NULL) { |
| SET_SYS_ERROR(kmfh, errno); |
| rv = KMF_ERR_MEMORY; |
| goto cleanup; |
| } |
| |
| /* |
| * i2d_X509 will increment the buf pointer so that we need to |
| * save it. |
| */ |
| p = buf; |
| if ((len = i2d_X509(x509cert, &p)) < 0) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| free(buf); |
| rv = KMF_ERR_BAD_CERT_FORMAT; |
| goto cleanup; |
| } |
| |
| /* caller's responsibility to free it */ |
| cert->Data = buf; |
| cert->Length = len; |
| |
| cleanup: |
| if (rv != KMF_OK) { |
| if (buf) |
| free(buf); |
| cert->Data = NULL; |
| cert->Length = 0; |
| } |
| |
| return (rv); |
| } |
| |
| |
| static KMF_RETURN |
| check_cert(X509 *xcert, char *issuer, char *subject, KMF_BIGINT *serial, |
| boolean_t *match) |
| { |
| KMF_RETURN rv = KMF_OK; |
| boolean_t findIssuer = FALSE; |
| boolean_t findSubject = FALSE; |
| boolean_t findSerial = FALSE; |
| KMF_X509_NAME issuerDN, subjectDN; |
| KMF_X509_NAME certIssuerDN, certSubjectDN; |
| |
| *match = FALSE; |
| if (xcert == NULL) { |
| return (KMF_ERR_BAD_PARAMETER); |
| } |
| |
| (void) memset(&issuerDN, 0, sizeof (KMF_X509_NAME)); |
| (void) memset(&subjectDN, 0, sizeof (KMF_X509_NAME)); |
| (void) memset(&certIssuerDN, 0, sizeof (KMF_X509_NAME)); |
| (void) memset(&certSubjectDN, 0, sizeof (KMF_X509_NAME)); |
| |
| if (issuer != NULL && strlen(issuer)) { |
| rv = kmf_dn_parser(issuer, &issuerDN); |
| if (rv != KMF_OK) |
| return (KMF_ERR_BAD_PARAMETER); |
| |
| rv = get_x509_dn(X509_get_issuer_name(xcert), &certIssuerDN); |
| if (rv != KMF_OK) { |
| kmf_free_dn(&issuerDN); |
| return (KMF_ERR_BAD_PARAMETER); |
| } |
| |
| findIssuer = TRUE; |
| } |
| if (subject != NULL && strlen(subject)) { |
| rv = kmf_dn_parser(subject, &subjectDN); |
| if (rv != KMF_OK) { |
| rv = KMF_ERR_BAD_PARAMETER; |
| goto cleanup; |
| } |
| |
| rv = get_x509_dn(X509_get_subject_name(xcert), &certSubjectDN); |
| if (rv != KMF_OK) { |
| rv = KMF_ERR_BAD_PARAMETER; |
| goto cleanup; |
| } |
| findSubject = TRUE; |
| } |
| if (serial != NULL && serial->val != NULL) |
| findSerial = TRUE; |
| |
| if (findSerial) { |
| BIGNUM *bn; |
| |
| /* Comparing BIGNUMs is a pain! */ |
| bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(xcert), NULL); |
| if (bn != NULL) { |
| int bnlen = BN_num_bytes(bn); |
| |
| if (bnlen == serial->len) { |
| uchar_t *a = malloc(bnlen); |
| if (a == NULL) { |
| rv = KMF_ERR_MEMORY; |
| BN_free(bn); |
| goto cleanup; |
| } |
| bnlen = BN_bn2bin(bn, a); |
| *match = (memcmp(a, serial->val, serial->len) == |
| 0); |
| rv = KMF_OK; |
| free(a); |
| } |
| BN_free(bn); |
| if (!(*match)) |
| goto cleanup; |
| } else { |
| rv = KMF_OK; |
| goto cleanup; |
| } |
| } |
| if (findIssuer) { |
| *match = (kmf_compare_rdns(&issuerDN, &certIssuerDN) == 0); |
| if ((*match) == B_FALSE) { |
| /* stop checking and bail */ |
| rv = KMF_OK; |
| goto cleanup; |
| } |
| } |
| if (findSubject) { |
| *match = (kmf_compare_rdns(&subjectDN, &certSubjectDN) == 0); |
| if ((*match) == B_FALSE) { |
| /* stop checking and bail */ |
| rv = KMF_OK; |
| goto cleanup; |
| } |
| } |
| |
| *match = TRUE; |
| cleanup: |
| if (findIssuer) { |
| kmf_free_dn(&issuerDN); |
| kmf_free_dn(&certIssuerDN); |
| } |
| if (findSubject) { |
| kmf_free_dn(&subjectDN); |
| kmf_free_dn(&certSubjectDN); |
| } |
| |
| return (rv); |
| } |
| |
| |
| /* |
| * This function loads a certificate file into an X509 data structure, and |
| * checks if its issuer, subject or the serial number matches with those |
| * values. If it matches, then return the X509 data structure. |
| */ |
| static KMF_RETURN |
| load_X509cert(KMF_HANDLE *kmfh, |
| char *issuer, char *subject, KMF_BIGINT *serial, |
| char *pathname, X509 **outcert) |
| { |
| KMF_RETURN rv = KMF_OK; |
| X509 *xcert = NULL; |
| BIO *bcert = NULL; |
| boolean_t match = FALSE; |
| KMF_ENCODE_FORMAT format; |
| |
| /* |
| * auto-detect the file format, regardless of what |
| * the 'format' parameters in the params say. |
| */ |
| rv = kmf_get_file_format(pathname, &format); |
| if (rv != KMF_OK) { |
| if (rv == KMF_ERR_OPEN_FILE) |
| rv = KMF_ERR_CERT_NOT_FOUND; |
| return (rv); |
| } |
| |
| /* Not ASN1(DER) format */ |
| if ((bcert = BIO_new_file(pathname, "rb")) == NULL) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| rv = KMF_ERR_OPEN_FILE; |
| goto cleanup; |
| } |
| |
| if (format == KMF_FORMAT_PEM) |
| xcert = PEM_read_bio_X509_AUX(bcert, NULL, NULL, NULL); |
| else if (format == KMF_FORMAT_ASN1) |
| xcert = d2i_X509_bio(bcert, NULL); |
| else if (format == KMF_FORMAT_PKCS12) { |
| PKCS12 *p12 = d2i_PKCS12_bio(bcert, NULL); |
| if (p12 != NULL) { |
| (void) PKCS12_parse(p12, NULL, NULL, &xcert, NULL); |
| PKCS12_free(p12); |
| p12 = NULL; |
| } else { |
| SET_ERROR(kmfh, ERR_get_error()); |
| rv = KMF_ERR_BAD_CERT_FORMAT; |
| } |
| } else { |
| rv = KMF_ERR_BAD_PARAMETER; |
| goto cleanup; |
| } |
| |
| if (xcert == NULL) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| rv = KMF_ERR_BAD_CERT_FORMAT; |
| goto cleanup; |
| } |
| |
| if (check_cert(xcert, issuer, subject, serial, &match) != KMF_OK || |
| match == FALSE) { |
| rv = KMF_ERR_CERT_NOT_FOUND; |
| goto cleanup; |
| } |
| |
| if (outcert != NULL) { |
| *outcert = xcert; |
| } |
| |
| cleanup: |
| if (bcert != NULL) (void) BIO_free(bcert); |
| if (rv != KMF_OK && xcert != NULL) |
| X509_free(xcert); |
| |
| return (rv); |
| } |
| |
| static int |
| datacmp(const void *a, const void *b) |
| { |
| KMF_DATA *adata = (KMF_DATA *)a; |
| KMF_DATA *bdata = (KMF_DATA *)b; |
| if (adata->Length > bdata->Length) |
| return (-1); |
| if (adata->Length < bdata->Length) |
| return (1); |
| return (0); |
| } |
| |
| static KMF_RETURN |
| load_certs(KMF_HANDLE *kmfh, char *issuer, char *subject, KMF_BIGINT *serial, |
| KMF_CERT_VALIDITY validity, char *pathname, |
| KMF_DATA **certlist, uint32_t *numcerts) |
| { |
| KMF_RETURN rv = KMF_OK; |
| int i; |
| KMF_DATA *certs = NULL; |
| int nc = 0; |
| int hits = 0; |
| KMF_ENCODE_FORMAT format; |
| |
| rv = kmf_get_file_format(pathname, &format); |
| if (rv != KMF_OK) { |
| if (rv == KMF_ERR_OPEN_FILE) |
| rv = KMF_ERR_CERT_NOT_FOUND; |
| return (rv); |
| } |
| if (format == KMF_FORMAT_ASN1) { |
| /* load a single certificate */ |
| certs = (KMF_DATA *)malloc(sizeof (KMF_DATA)); |
| if (certs == NULL) |
| return (KMF_ERR_MEMORY); |
| certs->Data = NULL; |
| certs->Length = 0; |
| rv = kmf_load_cert(kmfh, issuer, subject, serial, validity, |
| pathname, certs); |
| if (rv == KMF_OK) { |
| *certlist = certs; |
| *numcerts = 1; |
| } else { |
| kmf_free_data(certs); |
| free(certs); |
| certs = NULL; |
| } |
| return (rv); |
| } else if (format == KMF_FORMAT_PKCS12) { |
| /* We need a credential to access a PKCS#12 file */ |
| rv = KMF_ERR_BAD_CERT_FORMAT; |
| } else if (format == KMF_FORMAT_PEM || |
| format != KMF_FORMAT_PEM_KEYPAIR) { |
| |
| /* This function only works on PEM files */ |
| rv = extract_pem(kmfh, issuer, subject, serial, pathname, |
| (uchar_t *)NULL, 0, NULL, &certs, &nc); |
| } else { |
| return (KMF_ERR_ENCODING); |
| } |
| |
| if (rv != KMF_OK) |
| return (rv); |
| |
| for (i = 0; i < nc; i++) { |
| if (validity == KMF_NONEXPIRED_CERTS) { |
| rv = kmf_check_cert_date(kmfh, &certs[i]); |
| } else if (validity == KMF_EXPIRED_CERTS) { |
| rv = kmf_check_cert_date(kmfh, &certs[i]); |
| if (rv == KMF_OK) |
| rv = KMF_ERR_CERT_NOT_FOUND; |
| if (rv == KMF_ERR_VALIDITY_PERIOD) |
| rv = KMF_OK; |
| } |
| if (rv != KMF_OK) { |
| /* Remove this cert from the list by clearing it. */ |
| kmf_free_data(&certs[i]); |
| } else { |
| hits++; /* count valid certs found */ |
| } |
| rv = KMF_OK; |
| } |
| if (rv == KMF_OK && hits > 0) { |
| /* |
| * Sort the list of certs by length to put the cleared ones |
| * at the end so they don't get accessed by the caller. |
| */ |
| qsort((void *)certs, nc, sizeof (KMF_DATA), datacmp); |
| *certlist = certs; |
| |
| /* since we sorted the list, just return the number of hits */ |
| *numcerts = hits; |
| } else { |
| if (rv == KMF_OK && hits == 0) |
| rv = KMF_ERR_CERT_NOT_FOUND; |
| if (certs != NULL) { |
| free(certs); |
| certs = NULL; |
| } |
| } |
| return (rv); |
| } |
| |
| static KMF_RETURN |
| kmf_load_cert(KMF_HANDLE *kmfh, |
| char *issuer, char *subject, KMF_BIGINT *serial, |
| KMF_CERT_VALIDITY validity, |
| char *pathname, |
| KMF_DATA *cert) |
| { |
| KMF_RETURN rv = KMF_OK; |
| X509 *x509cert = NULL; |
| |
| rv = load_X509cert(kmfh, issuer, subject, serial, pathname, &x509cert); |
| if (rv == KMF_OK && x509cert != NULL && cert != NULL) { |
| rv = ssl_cert2KMFDATA(kmfh, x509cert, cert); |
| if (rv != KMF_OK) { |
| goto cleanup; |
| } |
| if (validity == KMF_NONEXPIRED_CERTS) { |
| rv = kmf_check_cert_date(kmfh, cert); |
| } else if (validity == KMF_EXPIRED_CERTS) { |
| rv = kmf_check_cert_date(kmfh, cert); |
| if (rv == KMF_OK) { |
| /* |
| * This is a valid cert so skip it. |
| */ |
| rv = KMF_ERR_CERT_NOT_FOUND; |
| } |
| if (rv == KMF_ERR_VALIDITY_PERIOD) { |
| /* |
| * We want to return success when we |
| * find an invalid cert. |
| */ |
| rv = KMF_OK; |
| goto cleanup; |
| } |
| } |
| } |
| cleanup: |
| if (x509cert != NULL) |
| X509_free(x509cert); |
| |
| return (rv); |
| } |
| |
| static KMF_RETURN |
| readAltFormatPrivateKey(KMF_DATA *filedata, EVP_PKEY **pkey) |
| { |
| KMF_RETURN ret = KMF_OK; |
| KMF_RAW_RSA_KEY rsa; |
| BerElement *asn1 = NULL; |
| BerValue filebuf; |
| BerValue OID = { NULL, 0 }; |
| BerValue *Mod = NULL, *PubExp = NULL; |
| BerValue *PriExp = NULL, *Prime1 = NULL, *Prime2 = NULL; |
| BerValue *Coef = NULL; |
| BIGNUM *D = NULL, *P = NULL, *Q = NULL, *COEF = NULL; |
| BIGNUM *Exp1 = NULL, *Exp2 = NULL, *pminus1 = NULL; |
| BIGNUM *qminus1 = NULL; |
| BN_CTX *ctx = NULL; |
| |
| *pkey = NULL; |
| |
| filebuf.bv_val = (char *)filedata->Data; |
| filebuf.bv_len = filedata->Length; |
| |
| asn1 = kmfder_init(&filebuf); |
| if (asn1 == NULL) { |
| ret = KMF_ERR_MEMORY; |
| goto out; |
| } |
| |
| if (kmfber_scanf(asn1, "{{Dn{IIIIII}}}", |
| &OID, &Mod, &PubExp, &PriExp, &Prime1, |
| &Prime2, &Coef) == -1) { |
| ret = KMF_ERR_ENCODING; |
| goto out; |
| } |
| |
| /* |
| * We have to derive the 2 Exponents using Bignumber math. |
| * Exp1 = PriExp mod (Prime1 - 1) |
| * Exp2 = PriExp mod (Prime2 - 1) |
| */ |
| |
| /* D = PrivateExponent */ |
| D = BN_bin2bn((const uchar_t *)PriExp->bv_val, PriExp->bv_len, D); |
| if (D == NULL) { |
| ret = KMF_ERR_MEMORY; |
| goto out; |
| } |
| |
| /* P = Prime1 (first prime factor of Modulus) */ |
| P = BN_bin2bn((const uchar_t *)Prime1->bv_val, Prime1->bv_len, P); |
| if (D == NULL) { |
| ret = KMF_ERR_MEMORY; |
| goto out; |
| } |
| |
| /* Q = Prime2 (second prime factor of Modulus) */ |
| Q = BN_bin2bn((const uchar_t *)Prime2->bv_val, Prime2->bv_len, Q); |
| |
| if ((ctx = BN_CTX_new()) == NULL) { |
| ret = KMF_ERR_MEMORY; |
| goto out; |
| } |
| |
| /* Compute (P - 1) */ |
| pminus1 = BN_new(); |
| (void) BN_sub(pminus1, P, BN_value_one()); |
| |
| /* Exponent1 = D mod (P - 1) */ |
| Exp1 = BN_new(); |
| (void) BN_mod(Exp1, D, pminus1, ctx); |
| |
| /* Compute (Q - 1) */ |
| qminus1 = BN_new(); |
| (void) BN_sub(qminus1, Q, BN_value_one()); |
| |
| /* Exponent2 = D mod (Q - 1) */ |
| Exp2 = BN_new(); |
| (void) BN_mod(Exp2, D, qminus1, ctx); |
| |
| /* Coef = (Inverse Q) mod P */ |
| COEF = BN_new(); |
| (void) BN_mod_inverse(COEF, Q, P, ctx); |
| |
| /* Convert back to KMF format */ |
| (void) memset(&rsa, 0, sizeof (rsa)); |
| |
| if ((ret = sslBN2KMFBN(Exp1, &rsa.exp1)) != KMF_OK) |
| goto out; |
| if ((ret = sslBN2KMFBN(Exp2, &rsa.exp2)) != KMF_OK) |
| goto out; |
| if ((ret = sslBN2KMFBN(COEF, &rsa.coef)) != KMF_OK) |
| goto out; |
| |
| rsa.mod.val = (uchar_t *)Mod->bv_val; |
| rsa.mod.len = Mod->bv_len; |
| |
| rsa.pubexp.val = (uchar_t *)PubExp->bv_val; |
| rsa.pubexp.len = PubExp->bv_len; |
| |
| rsa.priexp.val = (uchar_t *)PriExp->bv_val; |
| rsa.priexp.len = PriExp->bv_len; |
| |
| rsa.prime1.val = (uchar_t *)Prime1->bv_val; |
| rsa.prime1.len = Prime1->bv_len; |
| |
| rsa.prime2.val = (uchar_t *)Prime2->bv_val; |
| rsa.prime2.len = Prime2->bv_len; |
| |
| *pkey = ImportRawRSAKey(&rsa); |
| out: |
| if (asn1 != NULL) |
| kmfber_free(asn1, 1); |
| |
| if (OID.bv_val) { |
| free(OID.bv_val); |
| } |
| if (PriExp) |
| free(PriExp); |
| |
| if (Mod) |
| free(Mod); |
| |
| if (PubExp) |
| free(PubExp); |
| |
| if (Coef) { |
| (void) memset(Coef->bv_val, 0, Coef->bv_len); |
| free(Coef->bv_val); |
| free(Coef); |
| } |
| if (Prime1) |
| free(Prime1); |
| if (Prime2) |
| free(Prime2); |
| |
| if (ctx != NULL) |
| BN_CTX_free(ctx); |
| |
| if (D) |
| BN_clear_free(D); |
| if (P) |
| BN_clear_free(P); |
| if (Q) |
| BN_clear_free(Q); |
| if (pminus1) |
| BN_clear_free(pminus1); |
| if (qminus1) |
| BN_clear_free(qminus1); |
| if (Exp1) |
| BN_clear_free(Exp1); |
| if (Exp2) |
| BN_clear_free(Exp2); |
| |
| return (ret); |
| |
| } |
| |
| static EVP_PKEY * |
| openssl_load_key(KMF_HANDLE_T handle, const char *file) |
| { |
| BIO *keyfile = NULL; |
| EVP_PKEY *pkey = NULL; |
| KMF_HANDLE *kmfh = (KMF_HANDLE *)handle; |
| KMF_ENCODE_FORMAT format; |
| KMF_RETURN rv; |
| KMF_DATA filedata; |
| |
| if (file == NULL) { |
| return (NULL); |
| } |
| |
| if (kmf_get_file_format((char *)file, &format) != KMF_OK) |
| return (NULL); |
| |
| keyfile = BIO_new_file(file, "rb"); |
| if (keyfile == NULL) { |
| goto end; |
| } |
| |
| if (format == KMF_FORMAT_ASN1) { |
| pkey = d2i_PrivateKey_bio(keyfile, NULL); |
| if (pkey == NULL) { |
| |
| (void) BIO_free(keyfile); |
| keyfile = NULL; |
| /* Try odd ASN.1 variations */ |
| rv = kmf_read_input_file(kmfh, (char *)file, |
| &filedata); |
| if (rv == KMF_OK) { |
| (void) readAltFormatPrivateKey(&filedata, |
| &pkey); |
| kmf_free_data(&filedata); |
| } |
| } |
| } else if (format == KMF_FORMAT_PEM || |
| format == KMF_FORMAT_PEM_KEYPAIR) { |
| pkey = PEM_read_bio_PrivateKey(keyfile, NULL, NULL, NULL); |
| if (pkey == NULL) { |
| KMF_DATA derdata; |
| /* |
| * Check if this is the alt. format |
| * RSA private key file. |
| */ |
| rv = kmf_read_input_file(kmfh, (char *)file, |
| &filedata); |
| if (rv == KMF_OK) { |
| uchar_t *d = NULL; |
| int len; |
| rv = kmf_pem_to_der(filedata.Data, |
| filedata.Length, &d, &len); |
| if (rv == KMF_OK && d != NULL) { |
| derdata.Data = d; |
| derdata.Length = (size_t)len; |
| (void) readAltFormatPrivateKey( |
| &derdata, &pkey); |
| free(d); |
| } |
| kmf_free_data(&filedata); |
| } |
| } |
| } |
| |
| end: |
| if (pkey == NULL) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| } |
| |
| if (keyfile != NULL) |
| (void) BIO_free(keyfile); |
| |
| return (pkey); |
| } |
| |
| KMF_RETURN |
| OpenSSL_FindCert(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist) |
| { |
| KMF_RETURN rv = KMF_OK; |
| KMF_HANDLE *kmfh = (KMF_HANDLE *)handle; |
| int i, n; |
| uint32_t maxcerts = 0; |
| uint32_t *num_certs; |
| KMF_X509_DER_CERT *kmf_cert = NULL; |
| char *dirpath = NULL; |
| char *filename = NULL; |
| char *fullpath = NULL; |
| char *issuer = NULL; |
| char *subject = NULL; |
| KMF_BIGINT *serial = NULL; |
| KMF_CERT_VALIDITY validity; |
| |
| num_certs = kmf_get_attr_ptr(KMF_COUNT_ATTR, attrlist, numattr); |
| if (num_certs == NULL) |
| return (KMF_ERR_BAD_PARAMETER); |
| |
| /* num_certs should reference the size of kmf_cert */ |
| maxcerts = *num_certs; |
| if (maxcerts == 0) |
| maxcerts = 0xFFFFFFFF; |
| *num_certs = 0; |
| |
| /* Get the optional returned certificate list */ |
| kmf_cert = kmf_get_attr_ptr(KMF_X509_DER_CERT_ATTR, attrlist, |
| numattr); |
| |
| /* |
| * The dirpath attribute and the filename attribute can not be NULL |
| * at the same time. |
| */ |
| dirpath = kmf_get_attr_ptr(KMF_DIRPATH_ATTR, attrlist, numattr); |
| filename = kmf_get_attr_ptr(KMF_CERT_FILENAME_ATTR, attrlist, |
| numattr); |
| |
| fullpath = get_fullpath(dirpath, filename); |
| if (fullpath == NULL) |
| return (KMF_ERR_BAD_PARAMETER); |
| |
| /* Get optional search criteria attributes */ |
| issuer = kmf_get_attr_ptr(KMF_ISSUER_NAME_ATTR, attrlist, numattr); |
| subject = kmf_get_attr_ptr(KMF_SUBJECT_NAME_ATTR, attrlist, numattr); |
| serial = kmf_get_attr_ptr(KMF_BIGINT_ATTR, attrlist, numattr); |
| rv = kmf_get_attr(KMF_CERT_VALIDITY_ATTR, attrlist, numattr, |
| &validity, NULL); |
| if (rv != KMF_OK) { |
| validity = KMF_ALL_CERTS; |
| rv = KMF_OK; |
| } |
| |
| if (isdir(fullpath)) { |
| DIR *dirp; |
| struct dirent *dp; |
| |
| n = 0; |
| /* open all files in the directory and attempt to read them */ |
| if ((dirp = opendir(fullpath)) == NULL) { |
| return (KMF_ERR_BAD_PARAMETER); |
| } |
| while ((dp = readdir(dirp)) != NULL) { |
| char *fname; |
| KMF_DATA *certlist = NULL; |
| uint32_t loaded_certs = 0; |
| |
| if (strcmp(dp->d_name, ".") == 0 || |
| strcmp(dp->d_name, "..") == 0) |
| continue; |
| |
| fname = get_fullpath(fullpath, (char *)&dp->d_name); |
| |
| rv = load_certs(kmfh, issuer, subject, serial, |
| validity, fname, &certlist, &loaded_certs); |
| |
| if (rv != KMF_OK) { |
| free(fname); |
| if (certlist != NULL) { |
| for (i = 0; i < loaded_certs; i++) |
| kmf_free_data(&certlist[i]); |
| free(certlist); |
| } |
| continue; |
| } |
| |
| /* If load succeeds, add certdata to the list */ |
| if (kmf_cert != NULL) { |
| for (i = 0; i < loaded_certs && |
| n < maxcerts; i++) { |
| kmf_cert[n].certificate.Data = |
| certlist[i].Data; |
| kmf_cert[n].certificate.Length = |
| certlist[i].Length; |
| |
| kmf_cert[n].kmf_private.keystore_type = |
| KMF_KEYSTORE_OPENSSL; |
| kmf_cert[n].kmf_private.flags = |
| KMF_FLAG_CERT_VALID; |
| kmf_cert[n].kmf_private.label = |
| strdup(fname); |
| n++; |
| } |
| /* |
| * If maxcerts < loaded_certs, clean up the |
| * certs that were not used. |
| */ |
| for (; i < loaded_certs; i++) |
| kmf_free_data(&certlist[i]); |
| } else { |
| for (i = 0; i < loaded_certs; i++) |
| kmf_free_data(&certlist[i]); |
| n += loaded_certs; |
| } |
| free(certlist); |
| free(fname); |
| } |
| (*num_certs) = n; |
| if (*num_certs == 0) |
| rv = KMF_ERR_CERT_NOT_FOUND; |
| if (*num_certs > 0) |
| rv = KMF_OK; |
| exit: |
| (void) closedir(dirp); |
| } else { |
| KMF_DATA *certlist = NULL; |
| uint32_t loaded_certs = 0; |
| |
| rv = load_certs(kmfh, issuer, subject, serial, validity, |
| fullpath, &certlist, &loaded_certs); |
| if (rv != KMF_OK) { |
| free(fullpath); |
| return (rv); |
| } |
| |
| n = 0; |
| if (kmf_cert != NULL && certlist != NULL) { |
| for (i = 0; i < loaded_certs && i < maxcerts; i++) { |
| kmf_cert[n].certificate.Data = |
| certlist[i].Data; |
| kmf_cert[n].certificate.Length = |
| certlist[i].Length; |
| kmf_cert[n].kmf_private.keystore_type = |
| KMF_KEYSTORE_OPENSSL; |
| kmf_cert[n].kmf_private.flags = |
| KMF_FLAG_CERT_VALID; |
| kmf_cert[n].kmf_private.label = |
| strdup(fullpath); |
| n++; |
| } |
| /* If maxcerts < loaded_certs, clean up */ |
| for (; i < loaded_certs; i++) |
| kmf_free_data(&certlist[i]); |
| } else if (certlist != NULL) { |
| for (i = 0; i < loaded_certs; i++) |
| kmf_free_data(&certlist[i]); |
| n = loaded_certs; |
| } |
| if (certlist != NULL) |
| free(certlist); |
| *num_certs = n; |
| } |
| |
| free(fullpath); |
| |
| return (rv); |
| } |
| |
| void |
| /*ARGSUSED*/ |
| OpenSSL_FreeKMFCert(KMF_HANDLE_T handle, |
| KMF_X509_DER_CERT *kmf_cert) |
| { |
| if (kmf_cert != NULL) { |
| if (kmf_cert->certificate.Data != NULL) { |
| kmf_free_data(&kmf_cert->certificate); |
| } |
| if (kmf_cert->kmf_private.label) |
| free(kmf_cert->kmf_private.label); |
| } |
| } |
| |
| /*ARGSUSED*/ |
| KMF_RETURN |
| OpenSSL_StoreCert(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist) |
| { |
| KMF_RETURN ret = KMF_OK; |
| KMF_DATA *cert = NULL; |
| char *outfilename = NULL; |
| char *dirpath = NULL; |
| char *fullpath = NULL; |
| KMF_ENCODE_FORMAT format; |
| |
| /* Get the cert data */ |
| cert = kmf_get_attr_ptr(KMF_CERT_DATA_ATTR, attrlist, numattr); |
| if (cert == NULL || cert->Data == NULL) |
| return (KMF_ERR_BAD_PARAMETER); |
| |
| /* Check the output filename and directory attributes. */ |
| outfilename = kmf_get_attr_ptr(KMF_CERT_FILENAME_ATTR, attrlist, |
| numattr); |
| if (outfilename == NULL) |
| return (KMF_ERR_BAD_PARAMETER); |
| |
| dirpath = kmf_get_attr_ptr(KMF_DIRPATH_ATTR, attrlist, numattr); |
| fullpath = get_fullpath(dirpath, outfilename); |
| if (fullpath == NULL) |
| return (KMF_ERR_BAD_CERTFILE); |
| |
| /* Check the optional format attribute */ |
| ret = kmf_get_attr(KMF_ENCODE_FORMAT_ATTR, attrlist, numattr, |
| &format, NULL); |
| if (ret != KMF_OK) { |
| /* If there is no format attribute, then default to PEM */ |
| format = KMF_FORMAT_PEM; |
| ret = KMF_OK; |
| } else if (format != KMF_FORMAT_ASN1 && format != KMF_FORMAT_PEM) { |
| ret = KMF_ERR_BAD_CERT_FORMAT; |
| goto out; |
| } |
| |
| /* Store the certificate in the file with the specified format */ |
| ret = kmf_create_cert_file(cert, format, fullpath); |
| |
| out: |
| if (fullpath != NULL) |
| free(fullpath); |
| |
| return (ret); |
| } |
| |
| |
| KMF_RETURN |
| OpenSSL_DeleteCert(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist) |
| { |
| KMF_RETURN rv; |
| KMF_HANDLE *kmfh = (KMF_HANDLE *)handle; |
| KMF_DATA certdata = { 0, NULL }; |
| char *dirpath = NULL; |
| char *filename = NULL; |
| char *fullpath = NULL; |
| char *issuer = NULL; |
| char *subject = NULL; |
| KMF_BIGINT *serial = NULL; |
| KMF_CERT_VALIDITY validity; |
| |
| /* |
| * Get the DIRPATH and CERT_FILENAME attributes. They can not be |
| * NULL at the same time. |
| */ |
| dirpath = kmf_get_attr_ptr(KMF_DIRPATH_ATTR, attrlist, numattr); |
| filename = kmf_get_attr_ptr(KMF_CERT_FILENAME_ATTR, attrlist, |
| numattr); |
| fullpath = get_fullpath(dirpath, filename); |
| if (fullpath == NULL) |
| return (KMF_ERR_BAD_PARAMETER); |
| |
| /* Get optional search criteria attributes */ |
| issuer = kmf_get_attr_ptr(KMF_ISSUER_NAME_ATTR, attrlist, numattr); |
| subject = kmf_get_attr_ptr(KMF_SUBJECT_NAME_ATTR, attrlist, numattr); |
| serial = kmf_get_attr_ptr(KMF_BIGINT_ATTR, attrlist, numattr); |
| rv = kmf_get_attr(KMF_CERT_VALIDITY_ATTR, attrlist, numattr, |
| &validity, NULL); |
| if (rv != KMF_OK) { |
| validity = KMF_ALL_CERTS; |
| rv = KMF_OK; |
| } |
| |
| if (isdir(fullpath)) { |
| DIR *dirp; |
| struct dirent *dp; |
| |
| /* open all files in the directory and attempt to read them */ |
| if ((dirp = opendir(fullpath)) == NULL) { |
| return (KMF_ERR_BAD_PARAMETER); |
| } |
| |
| while ((dp = readdir(dirp)) != NULL) { |
| if (strcmp(dp->d_name, ".") != 0 && |
| strcmp(dp->d_name, "..") != 0) { |
| char *fname; |
| |
| fname = get_fullpath(fullpath, |
| (char *)&dp->d_name); |
| |
| if (fname == NULL) { |
| rv = KMF_ERR_MEMORY; |
| break; |
| } |
| |
| rv = kmf_load_cert(kmfh, issuer, subject, |
| serial, validity, fname, &certdata); |
| |
| if (rv == KMF_ERR_CERT_NOT_FOUND) { |
| free(fname); |
| kmf_free_data(&certdata); |
| rv = KMF_OK; |
| continue; |
| } else if (rv != KMF_OK) { |
| free(fname); |
| break; |
| } |
| |
| if (unlink(fname) != 0) { |
| SET_SYS_ERROR(kmfh, errno); |
| rv = KMF_ERR_INTERNAL; |
| free(fname); |
| break; |
| } |
| free(fname); |
| kmf_free_data(&certdata); |
| } |
| } |
| (void) closedir(dirp); |
| } else { |
| /* Just try to load a single certificate */ |
| rv = kmf_load_cert(kmfh, issuer, subject, serial, validity, |
| fullpath, &certdata); |
| if (rv == KMF_OK) { |
| if (unlink(fullpath) != 0) { |
| SET_SYS_ERROR(kmfh, errno); |
| rv = KMF_ERR_INTERNAL; |
| } |
| } |
| } |
| |
| out: |
| if (fullpath != NULL) |
| free(fullpath); |
| |
| kmf_free_data(&certdata); |
| |
| return (rv); |
| } |
| |
| KMF_RETURN |
| OpenSSL_EncodePubKeyData(KMF_HANDLE_T handle, KMF_KEY_HANDLE *key, |
| KMF_DATA *keydata) |
| { |
| KMF_RETURN rv = KMF_OK; |
| KMF_HANDLE *kmfh = (KMF_HANDLE *)handle; |
| int n; |
| |
| if (key == NULL || keydata == NULL || |
| key->keyp == NULL) |
| return (KMF_ERR_BAD_PARAMETER); |
| |
| if (key->keyalg == KMF_RSA) { |
| RSA *pubkey = EVP_PKEY_get1_RSA(key->keyp); |
| |
| if (!(n = i2d_RSA_PUBKEY(pubkey, &keydata->Data))) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| return (KMF_ERR_ENCODING); |
| } |
| RSA_free(pubkey); |
| } else if (key->keyalg == KMF_DSA) { |
| DSA *pubkey = EVP_PKEY_get1_DSA(key->keyp); |
| |
| if (!(n = i2d_DSA_PUBKEY(pubkey, &keydata->Data))) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| return (KMF_ERR_ENCODING); |
| } |
| DSA_free(pubkey); |
| } else { |
| return (KMF_ERR_BAD_PARAMETER); |
| } |
| keydata->Length = n; |
| |
| cleanup: |
| if (rv != KMF_OK) { |
| if (keydata->Data) |
| free(keydata->Data); |
| keydata->Data = NULL; |
| keydata->Length = 0; |
| } |
| |
| return (rv); |
| } |
| |
| static KMF_RETURN |
| ssl_write_key(KMF_HANDLE *kmfh, KMF_ENCODE_FORMAT format, BIO *out, |
| KMF_CREDENTIAL *cred, EVP_PKEY *pkey, boolean_t private) |
| { |
| int rv = 0; |
| RSA *rsa; |
| DSA *dsa; |
| |
| if (pkey == NULL || out == NULL) |
| return (KMF_ERR_BAD_PARAMETER); |
| |
| switch (format) { |
| case KMF_FORMAT_RAWKEY: |
| /* same as ASN.1 */ |
| case KMF_FORMAT_ASN1: |
| if ((rsa = EVP_PKEY_get0_RSA(pkey)) != NULL) { |
| if (private) |
| rv = i2d_RSAPrivateKey_bio(out, rsa); |
| else |
| rv = i2d_RSAPublicKey_bio(out, rsa); |
| } else if ((dsa = EVP_PKEY_get0_DSA(pkey)) != NULL) { |
| rv = i2d_DSAPrivateKey_bio(out, dsa); |
| } |
| if (rv == 1) { |
| rv = KMF_OK; |
| } else { |
| SET_ERROR(kmfh, rv); |
| } |
| break; |
| case KMF_FORMAT_PEM: |
| if ((rsa = EVP_PKEY_get0_RSA(pkey)) != NULL) { |
| if (private) |
| rv = PEM_write_bio_RSAPrivateKey(out, |
| rsa, NULL, NULL, 0, NULL, |
| (cred != NULL ? cred->cred : NULL)); |
| else |
| rv = PEM_write_bio_RSAPublicKey(out, |
| rsa); |
| } else if ((dsa = EVP_PKEY_get0_DSA(pkey)) != NULL) { |
| rv = PEM_write_bio_DSAPrivateKey(out, |
| dsa, NULL, NULL, 0, NULL, |
| (cred != NULL ? cred->cred : NULL)); |
| } |
| |
| if (rv == 1) { |
| rv = KMF_OK; |
| } else { |
| SET_ERROR(kmfh, rv); |
| } |
| break; |
| |
| default: |
| rv = KMF_ERR_BAD_PARAMETER; |
| } |
| |
| return (rv); |
| } |
| |
| KMF_RETURN |
| OpenSSL_CreateKeypair(KMF_HANDLE_T handle, int numattr, |
| KMF_ATTRIBUTE *attrlist) |
| { |
| KMF_RETURN rv = KMF_OK; |
| KMF_HANDLE *kmfh = (KMF_HANDLE *)handle; |
| uint32_t eValue = RSA_F4; |
| BIGNUM *eValue_bn = NULL; |
| RSA *sslPrivKey = NULL; |
| DSA *sslDSAKey = NULL; |
| EVP_PKEY *eprikey = NULL; |
| EVP_PKEY *epubkey = NULL; |
| BIO *out = NULL; |
| KMF_KEY_HANDLE *pubkey = NULL, *privkey = NULL; |
| uint32_t keylen = 1024; |
| uint32_t keylen_size = sizeof (uint32_t); |
| boolean_t storekey = TRUE; |
| KMF_KEY_ALG keytype = KMF_RSA; |
| |
| eValue_bn = BN_new(); |
| if (eValue_bn == NULL) |
| return (KMF_ERR_MEMORY); |
| if (BN_set_word(eValue_bn, eValue) == 0) { |
| rv = KMF_ERR_KEYGEN_FAILED; |
| goto cleanup; |
| } |
| |
| rv = kmf_get_attr(KMF_STOREKEY_BOOL_ATTR, attrlist, numattr, |
| &storekey, NULL); |
| if (rv != KMF_OK) { |
| /* "storekey" is optional. Default is TRUE */ |
| rv = KMF_OK; |
| } |
| |
| rv = kmf_get_attr(KMF_KEYALG_ATTR, attrlist, numattr, |
| (void *)&keytype, NULL); |
| if (rv != KMF_OK) |
| /* keytype is optional. KMF_RSA is default */ |
| rv = KMF_OK; |
| |
| pubkey = kmf_get_attr_ptr(KMF_PUBKEY_HANDLE_ATTR, attrlist, numattr); |
| if (pubkey == NULL) { |
| rv = KMF_ERR_BAD_PARAMETER; |
| goto cleanup; |
| } |
| |
| privkey = kmf_get_attr_ptr(KMF_PRIVKEY_HANDLE_ATTR, attrlist, numattr); |
| if (privkey == NULL) { |
| rv = KMF_ERR_BAD_PARAMETER; |
| goto cleanup; |
| } |
| |
| (void) memset(pubkey, 0, sizeof (KMF_KEY_HANDLE)); |
| (void) memset(privkey, 0, sizeof (KMF_KEY_HANDLE)); |
| |
| eprikey = EVP_PKEY_new(); |
| if (eprikey == NULL) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| rv = KMF_ERR_KEYGEN_FAILED; |
| goto cleanup; |
| } |
| epubkey = EVP_PKEY_new(); |
| if (epubkey == NULL) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| rv = KMF_ERR_KEYGEN_FAILED; |
| goto cleanup; |
| } |
| if (keytype == KMF_RSA) { |
| KMF_BIGINT *rsaexp = NULL; |
| |
| rsaexp = kmf_get_attr_ptr(KMF_RSAEXP_ATTR, attrlist, numattr); |
| if (rsaexp != NULL) { |
| if (rsaexp->len > 0 && |
| rsaexp->len <= sizeof (eValue) && |
| rsaexp->val != NULL) { |
| /* LINTED E_BAD_PTR_CAST_ALIGN */ |
| eValue = *(uint32_t *)rsaexp->val; |
| if (BN_set_word(eValue_bn, eValue) == 0) { |
| rv = KMF_ERR_BAD_PARAMETER; |
| goto cleanup; |
| } |
| } else { |
| rv = KMF_ERR_BAD_PARAMETER; |
| goto cleanup; |
| } |
| } else { |
| /* RSA Exponent is optional. Default is 0x10001 */ |
| rv = KMF_OK; |
| } |
| |
| rv = kmf_get_attr(KMF_KEYLENGTH_ATTR, attrlist, numattr, |
| &keylen, &keylen_size); |
| if (rv == KMF_ERR_ATTR_NOT_FOUND) |
| /* keylen is optional, default is 1024 */ |
| rv = KMF_OK; |
| if (rv != KMF_OK) { |
| rv = KMF_ERR_BAD_PARAMETER; |
| goto cleanup; |
| } |
| |
| sslPrivKey = RSA_new(); |
| if (sslPrivKey == NULL || |
| RSA_generate_key_ex(sslPrivKey, keylen, eValue_bn, NULL) |
| == 0) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| rv = KMF_ERR_KEYGEN_FAILED; |
| } else { |
| (void) EVP_PKEY_set1_RSA(eprikey, sslPrivKey); |
| privkey->kstype = KMF_KEYSTORE_OPENSSL; |
| privkey->keyalg = KMF_RSA; |
| privkey->keyclass = KMF_ASYM_PRI; |
| privkey->israw = FALSE; |
| privkey->keyp = (void *)eprikey; |
| |
| /* OpenSSL derives the public key from the private */ |
| (void) EVP_PKEY_set1_RSA(epubkey, sslPrivKey); |
| pubkey->kstype = KMF_KEYSTORE_OPENSSL; |
| pubkey->keyalg = KMF_RSA; |
| pubkey->israw = FALSE; |
| pubkey->keyclass = KMF_ASYM_PUB; |
| pubkey->keyp = (void *)epubkey; |
| } |
| } else if (keytype == KMF_DSA) { |
| BIGNUM *p, *q, *g; |
| |
| sslDSAKey = DSA_new(); |
| if (sslDSAKey == NULL) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| return (KMF_ERR_MEMORY); |
| } |
| |
| p = BN_bin2bn(P, sizeof (P), NULL); |
| q = BN_bin2bn(Q, sizeof (Q), NULL); |
| g = BN_bin2bn(G, sizeof (G), NULL); |
| if (p == NULL || q == NULL || g == NULL) { |
| BN_free(p); |
| BN_free(q); |
| BN_free(g); |
| SET_ERROR(kmfh, ERR_get_error()); |
| rv = KMF_ERR_KEYGEN_FAILED; |
| goto cleanup; |
| } |
| |
| if (DSA_set0_pqg(sslDSAKey, p, q, g) == 0) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| rv = KMF_ERR_KEYGEN_FAILED; |
| goto cleanup; |
| } |
| |
| if (!DSA_generate_key(sslDSAKey)) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| rv = KMF_ERR_KEYGEN_FAILED; |
| goto cleanup; |
| } |
| |
| privkey->kstype = KMF_KEYSTORE_OPENSSL; |
| privkey->keyalg = KMF_DSA; |
| privkey->keyclass = KMF_ASYM_PRI; |
| privkey->israw = FALSE; |
| if (EVP_PKEY_set1_DSA(eprikey, sslDSAKey)) { |
| privkey->keyp = (void *)eprikey; |
| } else { |
| SET_ERROR(kmfh, ERR_get_error()); |
| rv = KMF_ERR_KEYGEN_FAILED; |
| goto cleanup; |
| } |
| |
| pubkey->kstype = KMF_KEYSTORE_OPENSSL; |
| pubkey->keyalg = KMF_DSA; |
| pubkey->keyclass = KMF_ASYM_PUB; |
| pubkey->israw = FALSE; |
| |
| if (EVP_PKEY_set1_DSA(epubkey, sslDSAKey)) { |
| pubkey->keyp = (void *)epubkey; |
| } else { |
| SET_ERROR(kmfh, ERR_get_error()); |
| rv = KMF_ERR_KEYGEN_FAILED; |
| goto cleanup; |
| } |
| } |
| |
| if (rv != KMF_OK) { |
| goto cleanup; |
| } |
| |
| if (storekey) { |
| KMF_ATTRIBUTE storeattrs[4]; /* max. 4 attributes needed */ |
| int i = 0; |
| char *keyfile = NULL, *dirpath = NULL; |
| KMF_ENCODE_FORMAT format; |
| /* |
| * Construct a new attribute arrray and call openssl_store_key |
| */ |
| kmf_set_attr_at_index(storeattrs, i, KMF_PRIVKEY_HANDLE_ATTR, |
| privkey, sizeof (privkey)); |
| i++; |
| |
| dirpath = kmf_get_attr_ptr(KMF_DIRPATH_ATTR, attrlist, numattr); |
| if (dirpath != NULL) { |
| storeattrs[i].type = KMF_DIRPATH_ATTR; |
| storeattrs[i].pValue = dirpath; |
| storeattrs[i].valueLen = strlen(dirpath); |
| i++; |
| } else { |
| rv = KMF_OK; /* DIRPATH is optional */ |
| } |
| keyfile = kmf_get_attr_ptr(KMF_KEY_FILENAME_ATTR, |
| attrlist, numattr); |
| if (keyfile != NULL) { |
| storeattrs[i].type = KMF_KEY_FILENAME_ATTR; |
| storeattrs[i].pValue = keyfile; |
| storeattrs[i].valueLen = strlen(keyfile); |
| i++; |
| } else { |
| goto cleanup; /* KEYFILE is required */ |
| } |
| rv = kmf_get_attr(KMF_ENCODE_FORMAT_ATTR, attrlist, numattr, |
| (void *)&format, NULL); |
| if (rv == KMF_OK) { |
| storeattrs[i].type = KMF_ENCODE_FORMAT_ATTR; |
| storeattrs[i].pValue = &format; |
| storeattrs[i].valueLen = sizeof (format); |
| i++; |
| } |
| |
| rv = OpenSSL_StoreKey(handle, i, storeattrs); |
| } |
| |
| cleanup: |
| if (eValue_bn != NULL) |
| BN_free(eValue_bn); |
| |
| if (rv != KMF_OK) { |
| if (eprikey != NULL) |
| EVP_PKEY_free(eprikey); |
| |
| if (epubkey != NULL) |
| EVP_PKEY_free(epubkey); |
| |
| if (pubkey->keylabel) { |
| free(pubkey->keylabel); |
| pubkey->keylabel = NULL; |
| } |
| |
| if (privkey->keylabel) { |
| free(privkey->keylabel); |
| privkey->keylabel = NULL; |
| } |
| |
| pubkey->keyp = NULL; |
| privkey->keyp = NULL; |
| } |
| |
| if (sslPrivKey) |
| RSA_free(sslPrivKey); |
| |
| if (sslDSAKey) |
| DSA_free(sslDSAKey); |
| |
| if (out != NULL) |
| (void) BIO_free(out); |
| |
| return (rv); |
| } |
| |
| /* |
| * Make sure the BN conversion is properly padded with 0x00 |
| * bytes. If not, signature verification for DSA signatures |
| * may fail in the case where the bignum value does not use |
| * all of the bits. |
| */ |
| static int |
| fixbnlen(const BIGNUM *bn, unsigned char *buf, int len) { |
| int bytes = len - BN_num_bytes(bn); |
| |
| /* prepend with leading 0x00 if necessary */ |
| while (bytes-- > 0) |
| *buf++ = 0; |
| |
| (void) BN_bn2bin(bn, buf); |
| /* |
| * Return the desired length since we prepended it |
| * with the necessary 0x00 padding. |
| */ |
| return (len); |
| } |
| |
| KMF_RETURN |
| OpenSSL_SignData(KMF_HANDLE_T handle, KMF_KEY_HANDLE *key, |
| KMF_OID *AlgOID, KMF_DATA *tobesigned, KMF_DATA *output) |
| { |
| KMF_RETURN ret = KMF_OK; |
| KMF_HANDLE *kmfh = (KMF_HANDLE *)handle; |
| KMF_ALGORITHM_INDEX AlgId; |
| EVP_MD_CTX *ctx; |
| const EVP_MD *md; |
| |
| if (key == NULL || AlgOID == NULL || |
| tobesigned == NULL || output == NULL || |
| tobesigned->Data == NULL || |
| output->Data == NULL) |
| return (KMF_ERR_BAD_PARAMETER); |
| |
| /* Map the OID to an OpenSSL algorithm */ |
| AlgId = x509_algoid_to_algid(AlgOID); |
| if (AlgId == KMF_ALGID_NONE) |
| return (KMF_ERR_BAD_ALGORITHM); |
| |
| if (key->keyalg == KMF_RSA) { |
| EVP_PKEY *pkey = (EVP_PKEY *)key->keyp; |
| uchar_t *p; |
| int len; |
| switch (AlgId) { |
| #ifndef OPENSSL_NO_MD5 |
| case KMF_ALGID_MD5WithRSA: |
| md = EVP_md5(); |
| break; |
| #endif |
| #ifndef OPENSSL_NO_SHA |
| case KMF_ALGID_SHA1WithRSA: |
| md = EVP_sha1(); |
| break; |
| #endif |
| #ifndef OPENSSL_NO_SHA256 |
| case KMF_ALGID_SHA256WithRSA: |
| md = EVP_sha256(); |
| break; |
| #endif |
| #ifndef OPENSSL_NO_SHA512 |
| case KMF_ALGID_SHA384WithRSA: |
| md = EVP_sha384(); |
| break; |
| case KMF_ALGID_SHA512WithRSA: |
| md = EVP_sha512(); |
| break; |
| #endif |
| case KMF_ALGID_RSA: |
| md = NULL; |
| break; |
| default: |
| return (KMF_ERR_BAD_ALGORITHM); |
| } |
| |
| if ((md == NULL) && (AlgId == KMF_ALGID_RSA)) { |
| RSA *rsa = EVP_PKEY_get1_RSA((EVP_PKEY *)pkey); |
| |
| p = output->Data; |
| if ((len = RSA_private_encrypt(tobesigned->Length, |
| tobesigned->Data, p, rsa, |
| RSA_PKCS1_PADDING)) <= 0) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| ret = KMF_ERR_INTERNAL; |
| } |
| output->Length = len; |
| } else { |
| if ((ctx = EVP_MD_CTX_new()) == NULL) |
| return (KMF_ERR_MEMORY); |
| (void) EVP_SignInit_ex(ctx, md, NULL); |
| (void) EVP_SignUpdate(ctx, tobesigned->Data, |
| (uint32_t)tobesigned->Length); |
| len = (uint32_t)output->Length; |
| p = output->Data; |
| if (!EVP_SignFinal(ctx, p, (uint32_t *)&len, pkey)) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| len = 0; |
| ret = KMF_ERR_INTERNAL; |
| } |
| output->Length = len; |
| EVP_MD_CTX_free(ctx); |
| } |
| } else if (key->keyalg == KMF_DSA) { |
| DSA *dsa = EVP_PKEY_get1_DSA(key->keyp); |
| |
| uchar_t hash[EVP_MAX_MD_SIZE]; |
| uint32_t hashlen; |
| DSA_SIG *dsasig; |
| |
| if (AlgId == KMF_ALGID_DSA || |
| AlgId == KMF_ALGID_SHA1WithDSA) |
| md = EVP_sha1(); |
| else if (AlgId == KMF_ALGID_SHA256WithDSA) |
| md = EVP_sha256(); |
| else /* Bad algorithm */ |
| return (KMF_ERR_BAD_ALGORITHM); |
| |
| /* |
| * OpenSSL EVP_Sign operation automatically converts to |
| * ASN.1 output so we do the operations separately so we |
| * are assured of NOT getting ASN.1 output returned. |
| * KMF does not want ASN.1 encoded results because |
| * not all mechanisms return ASN.1 encodings (PKCS#11 |
| * and NSS return raw signature data). |
| */ |
| if ((ctx = EVP_MD_CTX_new()) == NULL) |
| return (KMF_ERR_MEMORY); |
| (void) EVP_DigestInit_ex(ctx, md, NULL); |
| (void) EVP_DigestUpdate(ctx, tobesigned->Data, |
| tobesigned->Length); |
| (void) EVP_DigestFinal_ex(ctx, hash, &hashlen); |
| |
| /* Only sign first 20 bytes for SHA2 */ |
| if (AlgId == KMF_ALGID_SHA256WithDSA) |
| hashlen = 20; |
| dsasig = DSA_do_sign(hash, hashlen, dsa); |
| if (dsasig != NULL) { |
| int i; |
| const BIGNUM *r, *s; |
| |
| DSA_SIG_get0(dsasig, &r, &s); |
| output->Length = i = fixbnlen(r, output->Data, |
| hashlen); |
| |
| output->Length += fixbnlen(s, &output->Data[i], |
| hashlen); |
| |
| DSA_SIG_free(dsasig); |
| } else { |
| SET_ERROR(kmfh, ERR_get_error()); |
| } |
| EVP_MD_CTX_free(ctx); |
| } else { |
| return (KMF_ERR_BAD_PARAMETER); |
| } |
| cleanup: |
| return (ret); |
| } |
| |
| KMF_RETURN |
| /*ARGSUSED*/ |
| OpenSSL_DeleteKey(KMF_HANDLE_T handle, |
| int numattr, KMF_ATTRIBUTE *attrlist) |
| { |
| KMF_RETURN rv = KMF_OK; |
| KMF_KEY_HANDLE *key; |
| boolean_t destroy = B_TRUE; |
| |
| key = kmf_get_attr_ptr(KMF_KEY_HANDLE_ATTR, attrlist, numattr); |
| if (key == NULL || key->keyp == NULL) |
| return (KMF_ERR_BAD_PARAMETER); |
| |
| rv = kmf_get_attr(KMF_DESTROY_BOOL_ATTR, attrlist, numattr, |
| (void *)&destroy, NULL); |
| if (rv != KMF_OK) { |
| /* "destroy" is optional. Default is TRUE */ |
| rv = KMF_OK; |
| } |
| |
| if (key->keyclass != KMF_ASYM_PUB && |
| key->keyclass != KMF_ASYM_PRI && |
| key->keyclass != KMF_SYMMETRIC) |
| return (KMF_ERR_BAD_KEY_CLASS); |
| |
| if (key->keyclass == KMF_SYMMETRIC) { |
| kmf_free_raw_sym_key((KMF_RAW_SYM_KEY *)key->keyp); |
| key->keyp = NULL; |
| } else { |
| if (key->keyp != NULL) { |
| EVP_PKEY_free(key->keyp); |
| key->keyp = NULL; |
| } |
| } |
| |
| if (key->keylabel != NULL) { |
| EVP_PKEY *pkey = NULL; |
| /* If the file exists, make sure it is a proper key. */ |
| pkey = openssl_load_key(handle, key->keylabel); |
| if (pkey == NULL) { |
| if (key->keylabel != NULL) { |
| free(key->keylabel); |
| key->keylabel = NULL; |
| } |
| return (KMF_ERR_KEY_NOT_FOUND); |
| } |
| EVP_PKEY_free(pkey); |
| |
| if (destroy) { |
| if (unlink(key->keylabel) != 0) { |
| KMF_HANDLE *kmfh = (KMF_HANDLE *)handle; |
| SET_SYS_ERROR(kmfh, errno); |
| rv = KMF_ERR_INTERNAL; |
| } |
| } |
| if (key->keylabel != NULL) { |
| free(key->keylabel); |
| key->keylabel = NULL; |
| } |
| } |
| return (rv); |
| } |
| |
| KMF_RETURN |
| OpenSSL_GetErrorString(KMF_HANDLE_T handle, char **msgstr) |
| { |
| KMF_RETURN ret = KMF_OK; |
| KMF_HANDLE *kmfh = (KMF_HANDLE *)handle; |
| char str[256]; /* OpenSSL needs at least 120 byte buffer */ |
| |
| ERR_error_string_n(kmfh->lasterr.errcode, str, sizeof (str)); |
| if (strlen(str)) { |
| *msgstr = (char *)strdup(str); |
| if ((*msgstr) == NULL) |
| ret = KMF_ERR_MEMORY; |
| } else { |
| *msgstr = NULL; |
| } |
| |
| return (ret); |
| } |
| |
| static int |
| ext2NID(int kmfext) |
| { |
| switch (kmfext) { |
| case KMF_X509_EXT_KEY_USAGE: |
| return (NID_key_usage); |
| case KMF_X509_EXT_PRIV_KEY_USAGE_PERIOD: |
| return (NID_private_key_usage_period); |
| case KMF_X509_EXT_CERT_POLICIES: |
| return (NID_certificate_policies); |
| case KMF_X509_EXT_SUBJ_ALTNAME: |
| return (NID_subject_alt_name); |
| case KMF_X509_EXT_ISSUER_ALTNAME: |
| return (NID_issuer_alt_name); |
| case KMF_X509_EXT_BASIC_CONSTRAINTS: |
| return (NID_basic_constraints); |
| case KMF_X509_EXT_EXT_KEY_USAGE: |
| return (NID_ext_key_usage); |
| case KMF_X509_EXT_AUTH_KEY_ID: |
| return (NID_authority_key_identifier); |
| case KMF_X509_EXT_CRL_DIST_POINTS: |
| return (NID_crl_distribution_points); |
| case KMF_X509_EXT_SUBJ_KEY_ID: |
| return (NID_subject_key_identifier); |
| case KMF_X509_EXT_POLICY_MAPPINGS: |
| return (OBJ_sn2nid("policyMappings")); |
| case KMF_X509_EXT_NAME_CONSTRAINTS: |
| return (OBJ_sn2nid("nameConstraints")); |
| case KMF_X509_EXT_POLICY_CONSTRAINTS: |
| return (OBJ_sn2nid("policyConstraints")); |
| case KMF_X509_EXT_INHIBIT_ANY_POLICY: |
| return (OBJ_sn2nid("inhibitAnyPolicy")); |
| case KMF_X509_EXT_FRESHEST_CRL: |
| return (OBJ_sn2nid("freshestCRL")); |
| default: |
| return (NID_undef); |
| } |
| } |
| |
| KMF_RETURN |
| OpenSSL_CertGetPrintable(KMF_HANDLE_T handle, const KMF_DATA *pcert, |
| KMF_PRINTABLE_ITEM flag, char *resultStr) |
| { |
| KMF_RETURN ret = KMF_OK; |
| KMF_HANDLE *kmfh = (KMF_HANDLE *)handle; |
| X509 *xcert = NULL; |
| unsigned char *outbuf = NULL; |
| unsigned char *outbuf_p; |
| int j; |
| int ext_index, nid, len; |
| BIO *mem = NULL; |
| STACK_OF(OPENSSL_STRING) *emlst = NULL; |
| X509_EXTENSION *ex; |
| |
| if (pcert == NULL || pcert->Data == NULL || pcert->Length == 0) { |
| return (KMF_ERR_BAD_PARAMETER); |
| } |
| |
| /* copy cert data to outbuf */ |
| outbuf = malloc(pcert->Length); |
| if (outbuf == NULL) { |
| return (KMF_ERR_MEMORY); |
| } |
| (void) memcpy(outbuf, pcert->Data, pcert->Length); |
| |
| outbuf_p = outbuf; /* use a temp pointer; required by openssl */ |
| xcert = d2i_X509(NULL, (const uchar_t **)&outbuf_p, pcert->Length); |
| if (xcert == NULL) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| ret = KMF_ERR_ENCODING; |
| goto out; |
| } |
| |
| mem = BIO_new(BIO_s_mem()); |
| if (mem == NULL) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| ret = KMF_ERR_MEMORY; |
| goto out; |
| } |
| |
| switch (flag) { |
| case KMF_CERT_ISSUER: |
| (void) X509_NAME_print_ex(mem, X509_get_issuer_name(xcert), 0, |
| XN_FLAG_SEP_CPLUS_SPC); |
| len = BIO_gets(mem, resultStr, KMF_CERT_PRINTABLE_LEN); |
| break; |
| |
| case KMF_CERT_SUBJECT: |
| (void) X509_NAME_print_ex(mem, X509_get_subject_name(xcert), 0, |
| XN_FLAG_SEP_CPLUS_SPC); |
| len = BIO_gets(mem, resultStr, KMF_CERT_PRINTABLE_LEN); |
| break; |
| |
| case KMF_CERT_VERSION: |
| (void) snprintf(resultStr, KMF_CERT_PRINTABLE_LEN, |
| "%ld", X509_get_version(xcert)); |
| len = strlen(resultStr); |
| break; |
| |
| case KMF_CERT_SERIALNUM: |
| if (i2a_ASN1_INTEGER(mem, X509_get_serialNumber(xcert)) > 0) { |
| (void) strcpy(resultStr, "0x"); |
| len = BIO_gets(mem, &resultStr[2], |
| KMF_CERT_PRINTABLE_LEN - 2); |
| } |
| break; |
| |
| case KMF_CERT_NOTBEFORE: |
| (void) ASN1_TIME_print(mem, X509_getm_notBefore(xcert)); |
| len = BIO_gets(mem, resultStr, KMF_CERT_PRINTABLE_LEN); |
| break; |
| |
| case KMF_CERT_NOTAFTER: |
| (void) ASN1_TIME_print(mem, X509_getm_notAfter(xcert)); |
| len = BIO_gets(mem, resultStr, KMF_CERT_PRINTABLE_LEN); |
| break; |
| |
| case KMF_CERT_PUBKEY_DATA: |
| { |
| RSA *rsa; |
| DSA *dsa; |
| |
| EVP_PKEY *pkey = X509_get_pubkey(xcert); |
| if (pkey == NULL) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| ret = KMF_ERR_ENCODING; |
| goto out; |
| } |
| |
| if ((rsa = EVP_PKEY_get0_RSA(pkey)) != NULL) { |
| (void) BIO_printf(mem, |
| "RSA Public Key: (%d bit)\n", |
| RSA_bits(rsa)); |
| (void) RSA_print(mem, rsa, 0); |
| |
| } else if ((dsa = EVP_PKEY_get0_DSA(pkey)) != NULL) { |
| (void) BIO_printf(mem, |
| "%12sDSA Public Key:\n", ""); |
| (void) DSA_print(mem, dsa, 0); |
| } else { |
| (void) BIO_printf(mem, |
| "%12sUnknown Public Key:\n", ""); |
| } |
| (void) BIO_printf(mem, "\n"); |
| EVP_PKEY_free(pkey); |
| } |
| len = BIO_read(mem, resultStr, KMF_CERT_PRINTABLE_LEN); |
| break; |
| case KMF_CERT_SIGNATURE_ALG: |
| case KMF_CERT_PUBKEY_ALG: |
| { |
| #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) |
| ASN1_OBJECT *alg = NULL; |
| #else |
| const ASN1_OBJECT *alg = NULL; |
| #endif |
| |
| if (flag == KMF_CERT_SIGNATURE_ALG) { |
| #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) |
| alg = xcert->sig_alg->algorithm; |
| #else |
| const X509_ALGOR *sig_alg = NULL; |
| |
| X509_get0_signature(NULL, &sig_alg, xcert); |
| if (sig_alg != NULL) |
| X509_ALGOR_get0(&alg, NULL, NULL, |
| sig_alg); |
| #endif |
| } else { |
| #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) |
| alg = xcert->cert_info->key->algor->algorithm; |
| #else |
| X509_PUBKEY *key = X509_get_X509_PUBKEY(xcert); |
| |
| if (key != NULL) |
| (void) X509_PUBKEY_get0_param( |
| (ASN1_OBJECT **)&alg, NULL, 0, |
| NULL, key); |
| #endif |
| } |
| |
| if (alg == NULL) |
| len = -1; |
| else if ((len = i2a_ASN1_OBJECT(mem, alg)) > 0) |
| len = BIO_read(mem, resultStr, |
| KMF_CERT_PRINTABLE_LEN); |
| } |
| break; |
| |
| case KMF_CERT_EMAIL: |
| emlst = X509_get1_email(xcert); |
| for (j = 0; j < sk_OPENSSL_STRING_num(emlst); j++) |
| (void) BIO_printf(mem, "%s\n", |
| sk_OPENSSL_STRING_value(emlst, j)); |
| |
| len = BIO_gets(mem, resultStr, KMF_CERT_PRINTABLE_LEN); |
| X509_email_free(emlst); |
| break; |
| case KMF_X509_EXT_ISSUER_ALTNAME: |
| case KMF_X509_EXT_SUBJ_ALTNAME: |
| case KMF_X509_EXT_KEY_USAGE: |
| case KMF_X509_EXT_PRIV_KEY_USAGE_PERIOD: |
| case KMF_X509_EXT_CERT_POLICIES: |
| case KMF_X509_EXT_BASIC_CONSTRAINTS: |
| case KMF_X509_EXT_NAME_CONSTRAINTS: |
| case KMF_X509_EXT_POLICY_CONSTRAINTS: |
| case KMF_X509_EXT_EXT_KEY_USAGE: |
| case KMF_X509_EXT_INHIBIT_ANY_POLICY: |
| case KMF_X509_EXT_AUTH_KEY_ID: |
| case KMF_X509_EXT_SUBJ_KEY_ID: |
| case KMF_X509_EXT_POLICY_MAPPINGS: |
| case KMF_X509_EXT_CRL_DIST_POINTS: |
| case KMF_X509_EXT_FRESHEST_CRL: |
| nid = ext2NID(flag); |
| if (nid == NID_undef) { |
| ret = KMF_ERR_EXTENSION_NOT_FOUND; |
| goto out; |
| } |
| |
| ext_index = X509_get_ext_by_NID(xcert, nid, -1); |
| if (ext_index == -1) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| |
| ret = KMF_ERR_EXTENSION_NOT_FOUND; |
| goto out; |
| } |
| ex = X509_get_ext(xcert, ext_index); |
| |
| (void) i2a_ASN1_OBJECT(mem, X509_EXTENSION_get_object(ex)); |
| |
| if (BIO_printf(mem, ": %s\n", |
| X509_EXTENSION_get_critical(ex) ? "critical" : "") <= 0) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| ret = KMF_ERR_ENCODING; |
| goto out; |
| } |
| if (!X509V3_EXT_print(mem, ex, X509V3_EXT_DUMP_UNKNOWN, 4)) { |
| (void) BIO_printf(mem, "%*s", 4, ""); |
| (void) ASN1_STRING_print(mem, |
| X509_EXTENSION_get_data(ex)); |
| } |
| if (BIO_write(mem, "\n", 1) <= 0) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| ret = KMF_ERR_ENCODING; |
| goto out; |
| } |
| len = BIO_read(mem, resultStr, KMF_CERT_PRINTABLE_LEN); |
| } |
| if (len <= 0) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| ret = KMF_ERR_ENCODING; |
| } |
| |
| out: |
| if (outbuf != NULL) { |
| free(outbuf); |
| } |
| |
| if (xcert != NULL) { |
| X509_free(xcert); |
| } |
| |
| if (mem != NULL) { |
| (void) BIO_free(mem); |
| } |
| |
| return (ret); |
| } |
| |
| KMF_RETURN |
| /*ARGSUSED*/ |
| OpenSSL_FindPrikeyByCert(KMF_HANDLE_T handle, int numattr, |
| KMF_ATTRIBUTE *attrlist) |
| { |
| KMF_RETURN rv = KMF_OK; |
| KMF_KEYSTORE_TYPE kstype = KMF_KEYSTORE_OPENSSL; |
| KMF_KEY_CLASS keyclass = KMF_ASYM_PRI; |
| KMF_KEY_HANDLE *key = NULL; |
| uint32_t numkeys = 1; /* 1 key only */ |
| char *dirpath = NULL; |
| char *keyfile = NULL; |
| KMF_ATTRIBUTE new_attrlist[16]; |
| int i = 0; |
| |
| /* |
| * This is really just a FindKey operation, reuse the |
| * FindKey function. |
| */ |
| kmf_set_attr_at_index(new_attrlist, i, |
| KMF_KEYSTORE_TYPE_ATTR, &kstype, sizeof (kstype)); |
| i++; |
| |
| kmf_set_attr_at_index(new_attrlist, i, |
| KMF_COUNT_ATTR, &numkeys, sizeof (uint32_t)); |
| i++; |
| |
| kmf_set_attr_at_index(new_attrlist, i, |
| KMF_KEYCLASS_ATTR, &keyclass, sizeof (keyclass)); |
| i++; |
| |
| key = kmf_get_attr_ptr(KMF_KEY_HANDLE_ATTR, attrlist, numattr); |
| if (key == NULL) { |
| return (KMF_ERR_BAD_PARAMETER); |
| } else { |
| kmf_set_attr_at_index(new_attrlist, i, |
| KMF_KEY_HANDLE_ATTR, key, sizeof (KMF_KEY_HANDLE)); |
| i++; |
| } |
| |
| dirpath = kmf_get_attr_ptr(KMF_DIRPATH_ATTR, attrlist, numattr); |
| if (dirpath != NULL) { |
| kmf_set_attr_at_index(new_attrlist, i, |
| KMF_DIRPATH_ATTR, dirpath, strlen(dirpath)); |
| i++; |
| } |
| |
| keyfile = kmf_get_attr_ptr(KMF_KEY_FILENAME_ATTR, attrlist, numattr); |
| if (keyfile == NULL) |
| return (KMF_ERR_BAD_PARAMETER); |
| else { |
| kmf_set_attr_at_index(new_attrlist, i, |
| KMF_KEY_FILENAME_ATTR, keyfile, strlen(keyfile)); |
| i++; |
| } |
| |
| rv = OpenSSL_FindKey(handle, i, new_attrlist); |
| return (rv); |
| } |
| |
| KMF_RETURN |
| /*ARGSUSED*/ |
| OpenSSL_DecryptData(KMF_HANDLE_T handle, KMF_KEY_HANDLE *key, |
| KMF_OID *AlgOID, KMF_DATA *ciphertext, |
| KMF_DATA *output) |
| { |
| KMF_RETURN ret = KMF_OK; |
| RSA *rsa = NULL; |
| unsigned int in_len = 0, out_len = 0; |
| unsigned int total_decrypted = 0, modulus_len = 0; |
| uint8_t *in_data, *out_data; |
| int i, blocks; |
| |
| if (key == NULL || AlgOID == NULL || |
| ciphertext == NULL || output == NULL || |
| ciphertext->Data == NULL || |
| output->Data == NULL) |
| return (KMF_ERR_BAD_PARAMETER); |
| |
| if (key->keyalg == KMF_RSA) { |
| rsa = EVP_PKEY_get1_RSA((EVP_PKEY *)key->keyp); |
| modulus_len = RSA_size(rsa); |
| } else { |
| return (KMF_ERR_BAD_PARAMETER); |
| } |
| |
| blocks = ciphertext->Length/modulus_len; |
| out_data = output->Data; |
| in_data = ciphertext->Data; |
| out_len = modulus_len - 11; |
| in_len = modulus_len; |
| |
| for (i = 0; i < blocks; i++) { |
| out_len = RSA_private_decrypt(in_len, |
| in_data, out_data, rsa, RSA_PKCS1_PADDING); |
| |
| if (out_len == 0) { |
| ret = KMF_ERR_INTERNAL; |
| goto cleanup; |
| } |
| |
| out_data += out_len; |
| total_decrypted += out_len; |
| in_data += in_len; |
| } |
| |
| output->Length = total_decrypted; |
| |
| cleanup: |
| RSA_free(rsa); |
| if (ret != KMF_OK) |
| output->Length = 0; |
| |
| return (ret); |
| |
| } |
| |
| /* |
| * This function will create a certid from issuer_cert and user_cert. |
| * The caller should use OCSP_CERTID_free(OCSP_CERTID *) to deallocate |
| * certid memory after use. |
| */ |
| static KMF_RETURN |
| create_certid(KMF_HANDLE_T handle, const KMF_DATA *issuer_cert, |
| const KMF_DATA *user_cert, OCSP_CERTID **certid) |
| { |
| KMF_RETURN ret = KMF_OK; |
| KMF_HANDLE *kmfh = (KMF_HANDLE *)handle; |
| X509 *issuer = NULL; |
| X509 *cert = NULL; |
| unsigned char *ptmp; |
| |
| if (issuer_cert == NULL || user_cert == NULL) { |
| return (KMF_ERR_BAD_PARAMETER); |
| } |
| |
| /* convert the DER-encoded issuer cert to an internal X509 */ |
| ptmp = issuer_cert->Data; |
| issuer = d2i_X509(NULL, (const uchar_t **)&ptmp, |
| issuer_cert->Length); |
| if (issuer == NULL) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| ret = KMF_ERR_OCSP_BAD_ISSUER; |
| goto end; |
| } |
| |
| /* convert the DER-encoded user cert to an internal X509 */ |
| ptmp = user_cert->Data; |
| cert = d2i_X509(NULL, (const uchar_t **)&ptmp, |
| user_cert->Length); |
| if (cert == NULL) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| |
| ret = KMF_ERR_OCSP_BAD_CERT; |
| goto end; |
| } |
| |
| /* create a CERTID */ |
| *certid = OCSP_cert_to_id(NULL, cert, issuer); |
| if (*certid == NULL) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| ret = KMF_ERR_OCSP_CERTID; |
| goto end; |
| } |
| |
| end: |
| if (issuer != NULL) { |
| X509_free(issuer); |
| } |
| |
| if (cert != NULL) { |
| X509_free(cert); |
| } |
| |
| return (ret); |
| } |
| |
| KMF_RETURN |
| OpenSSL_CreateOCSPRequest(KMF_HANDLE_T handle, |
| int numattr, KMF_ATTRIBUTE *attrlist) |
| { |
| KMF_RETURN ret = KMF_OK; |
| KMF_HANDLE *kmfh = (KMF_HANDLE *)handle; |
| OCSP_CERTID *id = NULL; |
| OCSP_REQUEST *req = NULL; |
| BIO *derbio = NULL; |
| char *reqfile; |
| KMF_DATA *issuer_cert; |
| KMF_DATA *user_cert; |
| |
| user_cert = kmf_get_attr_ptr(KMF_USER_CERT_DATA_ATTR, |
| attrlist, numattr); |
| if (user_cert == NULL) |
| return (KMF_ERR_BAD_PARAMETER); |
| |
| issuer_cert = kmf_get_attr_ptr(KMF_ISSUER_CERT_DATA_ATTR, |
| attrlist, numattr); |
| if (issuer_cert == NULL) |
| return (KMF_ERR_BAD_PARAMETER); |
| |
| reqfile = kmf_get_attr_ptr(KMF_OCSP_REQUEST_FILENAME_ATTR, |
| attrlist, numattr); |
| if (reqfile == NULL) |
| return (KMF_ERR_BAD_PARAMETER); |
| |
| ret = create_certid(handle, issuer_cert, user_cert, &id); |
| if (ret != KMF_OK) { |
| return (ret); |
| } |
| |
| /* Create an OCSP request */ |
| req = OCSP_REQUEST_new(); |
| if (req == NULL) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| ret = KMF_ERR_OCSP_CREATE_REQUEST; |
| goto end; |
| } |
| |
| if (!OCSP_request_add0_id(req, id)) { |
| ret = KMF_ERR_OCSP_CREATE_REQUEST; |
| goto end; |
| } |
| |
| /* Write the request to the output file with DER encoding */ |
| derbio = BIO_new_file(reqfile, "wb"); |
| if (!derbio) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| ret = KMF_ERR_OPEN_FILE; |
| goto end; |
| } |
| if (i2d_OCSP_REQUEST_bio(derbio, req) <= 0) { |
| ret = KMF_ERR_ENCODING; |
| } |
| |
| end: |
| /* |
| * We don't need to free "id" explicitely, because OCSP_REQUEST_free() |
| * will also deallocate certid's space. |
| */ |
| if (req != NULL) { |
| OCSP_REQUEST_free(req); |
| } |
| |
| if (derbio != NULL) { |
| (void) BIO_free(derbio); |
| } |
| |
| return (ret); |
| } |
| |
| /* ocsp_find_signer_sk() is copied from openssl source */ |
| static X509 *ocsp_find_signer_sk(STACK_OF(X509) *certs, OCSP_BASICRESP *bs) |
| { |
| int i; |
| unsigned char tmphash[SHA_DIGEST_LENGTH], *keyhash; |
| const ASN1_OCTET_STRING *pid; |
| |
| #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) |
| OCSP_RESPID *id = bs->tbsResponseData->responderId; |
| |
| if (id->type == V_OCSP_RESPID_NAME) |
| return (X509_find_by_subject(certs, id->value.byName)); |
| |
| pid = id->value.byKey; |
| #else |
| const X509_NAME *pname; |
| |
| if (OCSP_resp_get0_id(bs, &pid, &pname) == 0) |
| return (NULL); |
| |
| if (pname != NULL) |
| return (X509_find_by_subject(certs, (X509_NAME *)pname)); |
| #endif |
| |
| /* Lookup by key hash */ |
| |
| /* If key hash isn't SHA1 length then forget it */ |
| if (pid->length != SHA_DIGEST_LENGTH) |
| return (NULL); |
| |
| keyhash = pid->data; |
| /* Calculate hash of each key and compare */ |
| for (i = 0; i < sk_X509_num(certs); i++) { |
| /* LINTED E_BAD_PTR_CAST_ALIGN */ |
| X509 *x = sk_X509_value(certs, i); |
| /* Use pubkey_digest to get the key ID value */ |
| (void) X509_pubkey_digest(x, EVP_sha1(), tmphash, NULL); |
| if (!memcmp(keyhash, tmphash, SHA_DIGEST_LENGTH)) |
| return (x); |
| } |
| return (NULL); |
| } |
| |
| /* ocsp_find_signer() is copied from openssl source */ |
| /* ARGSUSED2 */ |
| static int |
| ocsp_find_signer(X509 **psigner, OCSP_BASICRESP *bs, STACK_OF(X509) *certs, |
| X509_STORE *st, unsigned long flags) |
| { |
| X509 *signer; |
| if ((signer = ocsp_find_signer_sk(certs, bs))) { |
| *psigner = signer; |
| return (2); |
| } |
| |
| if (!(flags & OCSP_NOINTERN) && |
| (signer = ocsp_find_signer_sk( |
| (STACK_OF(X509) *)OCSP_resp_get0_certs(bs), bs))) { |
| *psigner = signer; |
| return (1); |
| } |
| /* Maybe lookup from store if by subject name */ |
| |
| *psigner = NULL; |
| return (0); |
| } |
| |
| /* |
| * This function will verify the signature of a basic response, using |
| * the public key from the OCSP responder certificate. |
| */ |
| static KMF_RETURN |
| check_response_signature(KMF_HANDLE_T handle, OCSP_BASICRESP *bs, |
| KMF_DATA *signer_cert, KMF_DATA *issuer_cert) |
| { |
| KMF_RETURN ret = KMF_OK; |
| KMF_HANDLE *kmfh = (KMF_HANDLE *)handle; |
| STACK_OF(X509) *cert_stack = NULL; |
| X509 *signer = NULL; |
| X509 *issuer = NULL; |
| #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) |
| EVP_PKEY *skey = NULL; |
| #else |
| STACK_OF(X509) *cert_stack2 = NULL; |
| #endif |
| unsigned char *ptmp; |
| |
| if (bs == NULL || issuer_cert == NULL) |
| return (KMF_ERR_BAD_PARAMETER); |
| |
| /* |
| * Find the certificate that signed the basic response. |
| * |
| * If signer_cert is not NULL, we will use that as the signer cert. |
| * Otherwise, we will check if the issuer cert is actually the signer. |
| * If we still do not find a signer, we will look for it from the |
| * certificate list came with the response file. |
| */ |
| if (signer_cert != NULL) { |
| ptmp = signer_cert->Data; |
| signer = d2i_X509(NULL, (const uchar_t **)&ptmp, |
| signer_cert->Length); |
| if (signer == NULL) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| ret = KMF_ERR_OCSP_BAD_SIGNER; |
| goto end; |
| } |
| } else { |
| /* |
| * Convert the issuer cert into X509 and push it into a |
| * stack to be used by ocsp_find_signer(). |
| */ |
| ptmp = issuer_cert->Data; |
| issuer = d2i_X509(NULL, (const uchar_t **)&ptmp, |
| issuer_cert->Length); |
| if (issuer == NULL) { |
| SET_ERROR(kmfh, ERR_get_error()); |
| ret = KMF_ERR_OCSP_BAD_ISSUER; |
| goto end; |
| } |
| |
| if ((cert_stack = sk_X509_new_null()) == NULL) { |
| ret = KMF_ERR_INTERNAL; |
| goto end; |
| } |
| |
| if (sk_X509_push(cert_stack, issuer) == NULL) { |
| ret = KMF_ERR_INTERNAL; |
| goto end; |
| } |
| |
| ret = ocsp_find_signer(&signer, bs, cert_stack, NULL, 0); |
| if (!ret) { |
| /* can not find the signer */ |
| ret = KMF_ERR_OCSP_BAD_SIGNER; |
| goto end; |
| } |
| } |
| |
| /* Verify the signature of the response */ |
| #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) |
| skey = X509_get_pubkey(signer); |
| if (skey == NULL) { |
| ret = KMF_ERR_OCSP_BAD_SIGNER; |
| goto end; |
| } |
| |
| ret = OCSP_BASICRESP_verify(bs, skey, 0); |
| #else |
| /* |
| * Technique based on |
| * https://mta.openssl.org/pipermail/openssl-users/ |
| * 2017-October/006814.html |
| */ |
| if ((cert_stack2 = sk_X509_new_null()) == NULL) { |
| ret = KMF_ERR_INTERNAL; |
| goto end; |
| } |
| |
| if (sk_X509_push(cert_stack2, signer) == NULL) { |
| ret = KMF_ERR_INTERNAL; |
| goto end; |
| } |
| |
| ret = OCSP_basic_verify(bs, cert_stack2, NULL, OCSP_NOVERIFY); |
| #endif |
| |
| if (ret == 0) { |
| ret = KMF_ERR_OCSP_RESPONSE_SIGNATURE; |
| goto end; |
| } |
| |
| end: |
| if (issuer != NULL) { |
| X509_free(issuer); |
| } |
| |
| if (signer != NULL) { |
| X509_free(signer); |
| } |
| |
| #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) |
| if (skey != NULL) { |
| EVP_PKEY_free(skey); |
| } |
| #else |
| if (cert_stack2 != NULL) { |
| sk_X509_free(cert_stack2); |
| } |
| #endif |
| |
| if (cert_stack != NULL) { |
| sk_X509_free(cert_stack); |
| } |
| |
| return (ret); |
| } |
| |
| KMF_RETURN |
| OpenSSL_GetOCSPStatusForCert(KMF_HANDLE_T handle, |
| int numattr, KMF_ATTRIBUTE *attrlist) |
| { |
| KMF_RETURN ret = KMF_OK; |
| BIO *derbio = NULL; |
| OCSP_RESPONSE *resp = NULL; |
| OCSP_BASICRESP *bs = NULL; |
| OCSP_CERTID *id = NULL; |
| OCSP_SINGLERESP *single = NULL; |
| ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; |
| int index, status, reason; |
| KMF_DATA *issuer_cert; |
| KMF_DATA *user_cert; |
| KMF_DATA *signer_cert; |
| KMF_DATA *response; |
| int *response_reason, *response_status, *cert_status; |
| boolean_t ignore_response_sign = B_FALSE; /* default is FALSE */ |
| uint32_t response_lifetime; |
| |
| issuer_cert = kmf_get_attr_ptr(KMF_ISSUER_CERT_DATA_ATTR, |
| attrlist, numattr); |
| if (issuer_cert == NULL) |
| return (KMF_ERR_BAD_PARAMETER); |
| |
| user_cert = kmf_get_attr_ptr(KMF_USER_CERT_DATA_ATTR, |
| attrlist, numattr); |
| if (us
|