/* ssl_certman.c * * Copyright (C) 2006-2026 wolfSSL Inc. * * This file is part of wolfSSL. * * wolfSSL is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * wolfSSL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ #include #include #if !defined(WOLFSSL_SSL_CERTMAN_INCLUDED) #ifndef WOLFSSL_IGNORE_FILE_WARN #warning ssl_certman.c not to be compiled separately from ssl.c #endif #else #ifndef NO_CERTS /* Pick an available TLS method. * * Used when creating temporary WOLFSSL_CTX. * * @return A TLS method on success. * @return NULL when no TLS method built into wolfSSL. */ static WC_INLINE WOLFSSL_METHOD* cm_pick_method(void* heap) { (void)heap; #ifndef NO_WOLFSSL_CLIENT #if !defined(NO_OLD_TLS) && defined(WOLFSSL_ALLOW_SSLV3) return wolfSSLv3_client_method_ex(heap); #elif !defined(NO_OLD_TLS) && defined(WOLFSSL_ALLOW_TLSV10) return wolfTLSv1_client_method_ex(heap); #elif !defined(NO_OLD_TLS) return wolfTLSv1_1_client_method_ex(heap); #elif !defined(WOLFSSL_NO_TLS12) return wolfTLSv1_2_client_method_ex(heap); #elif defined(WOLFSSL_TLS13) return wolfTLSv1_3_client_method_ex(heap); #else return NULL; #endif #elif !defined(NO_WOLFSSL_SERVER) #if !defined(NO_OLD_TLS) && defined(WOLFSSL_ALLOW_SSLV3) return wolfSSLv3_server_method_ex(heap); #elif !defined(NO_OLD_TLS) && defined(WOLFSSL_ALLOW_TLSV10) return wolfTLSv1_server_method_ex(heap); #elif !defined(NO_OLD_TLS) return wolfTLSv1_1_server_method_ex(heap); #elif !defined(WOLFSSL_NO_TLS12) return wolfTLSv1_2_server_method_ex(heap); #elif defined(WOLFSSL_TLS13) return wolfTLSv1_3_server_method_ex(heap); #else return NULL; #endif #else return NULL; #endif } static void DoCertManagerFree(WOLFSSL_CERT_MANAGER* cm); /* Create a new certificate manager with a heap hint. * * @param [in] heap Heap hint. * @return Certificate manager object on success. * @return NULL on failure. */ WOLFSSL_CERT_MANAGER* wolfSSL_CertManagerNew_ex(void* heap) { int err = 0; WOLFSSL_CERT_MANAGER* cm; WOLFSSL_ENTER("wolfSSL_CertManagerNew"); if (heap == NULL) { WOLFSSL_MSG("heap param is null"); } else { /* Some systems may have heap in unexpected segments. (IRAM vs DRAM) */ WOLFSSL_MSG_EX("heap param = %p", heap); } WOLFSSL_MSG_EX("DYNAMIC_TYPE_CERT_MANAGER Allocating = %d bytes", (word32)sizeof(WOLFSSL_CERT_MANAGER)); /* Allocate memory for certificate manager. */ cm = (WOLFSSL_CERT_MANAGER*)XMALLOC(sizeof(WOLFSSL_CERT_MANAGER), heap, DYNAMIC_TYPE_CERT_MANAGER); if (cm == NULL) { WOLFSSL_MSG_EX("XMALLOC failed to allocate WOLFSSL_CERT_MANAGER %d " "bytes.", (int)sizeof(WOLFSSL_CERT_MANAGER)); err = 1; } if (!err) { /* Reset all fields. */ XMEMSET(cm, 0, sizeof(WOLFSSL_CERT_MANAGER)); /* Set heap hint early so cleanup can use it. */ cm->heap = heap; /* Create a mutex for use when modify table of stored CAs. */ if (wc_InitMutex(&cm->caLock) != 0) { WOLFSSL_MSG("Bad mutex init"); err = 1; } else { cm->caLockInit = 1; } } if (!err) { /* Initialize reference count. */ wolfSSL_RefInit(&cm->ref, &err); #ifdef WOLFSSL_REFCNT_ERROR_RETURN if (err != 0) { WOLFSSL_MSG("Bad reference count init"); } else { cm->refInit = 1; } #else cm->refInit = 1; #endif } #ifdef WOLFSSL_TRUST_PEER_CERT if (!err) { /* Create a mutex for use when modify table of trusted peers. */ if (wc_InitMutex(&cm->tpLock) != 0) { WOLFSSL_MSG("Bad mutex init"); err = 1; } else { cm->tpLockInit = 1; } } #endif if (!err) { /* Set default minimum key sizes allowed. */ #ifndef NO_RSA cm->minRsaKeySz = MIN_RSAKEY_SZ; #endif #ifdef HAVE_ECC cm->minEccKeySz = MIN_ECCKEY_SZ; #endif #ifdef HAVE_FALCON cm->minFalconKeySz = MIN_FALCONKEY_SZ; #endif /* HAVE_FALCON */ #ifdef HAVE_DILITHIUM cm->minDilithiumKeySz = MIN_DILITHIUMKEY_SZ; #endif /* HAVE_DILITHIUM */ } /* Dispose of certificate manager on error. The reference count may not * have been initialized, so bypass the ref check and free directly. */ if (err && (cm != NULL)) { DoCertManagerFree(cm); cm = NULL; } return cm; } /* Create a new certificate manager. * * @return Certificate manager object on success. * @return NULL on failure. */ WOLFSSL_CERT_MANAGER* wolfSSL_CertManagerNew(void) { /* No heap hint. */ return wolfSSL_CertManagerNew_ex(NULL); } /* Unconditionally dispose of all resources owned by the certificate manager * and free cm itself, bypassing any reference count check. Only frees the * sub-resources that are marked as initialized in the cm bitfield, so it is * safe to call on a cm that was only partially initialized by * wolfSSL_CertManagerNew_ex. * * @param [in, out] cm Certificate manager (must be non-NULL). */ static void DoCertManagerFree(WOLFSSL_CERT_MANAGER* cm) { #ifdef HAVE_CRL /* Dispose of CRL handler. */ if (cm->crl != NULL) { /* Dispose of CRL object - indicating dynamically allocated. */ FreeCRL(cm->crl, 1); } #endif #ifdef HAVE_OCSP /* Dispose of OCSP handler. */ if (cm->ocsp != NULL) { FreeOCSP(cm->ocsp, 1); } /* Dispose of URL. */ XFREE(cm->ocspOverrideURL, cm->heap, DYNAMIC_TYPE_URL); #if !defined(NO_WOLFSSL_SERVER) && \ (defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \ defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)) /* Dispose of OCSP stapling handler. */ if (cm->ocsp_stapling) { FreeOCSP(cm->ocsp_stapling, 1); } #endif #endif /* HAVE_OCSP */ /* Dispose of CA table and mutex. */ FreeSignerTable(cm->caTable, CA_TABLE_SIZE, cm->heap); if (cm->caLockInit) { wc_FreeMutex(&cm->caLock); } #ifdef WOLFSSL_TRUST_PEER_CERT /* Dispose of trusted peer table and mutex. */ FreeTrustedPeerTable(cm->tpTable, TP_TABLE_SIZE, cm->heap); if (cm->tpLockInit) { wc_FreeMutex(&cm->tpLock); } #endif /* Dispose of reference count. */ if (cm->refInit) { wolfSSL_RefFree(&cm->ref); } /* Dispose of certificate manager memory. */ XFREE(cm, cm->heap, DYNAMIC_TYPE_CERT_MANAGER); } /* Dispose of certificate manager. * * @param [in, out] cm Certificate manager. */ void wolfSSL_CertManagerFree(WOLFSSL_CERT_MANAGER* cm) { WOLFSSL_ENTER("wolfSSL_CertManagerFree"); /* Validate parameter. */ if (cm != NULL) { int doFree = 0; int ret; /* Decrement reference count and check if value is 0. */ wolfSSL_RefDec(&cm->ref, &doFree, &ret); #ifdef WOLFSSL_REFCNT_ERROR_RETURN if (ret != 0) { WOLFSSL_MSG("Couldn't lock cm mutex"); } #else (void)ret; #endif if (doFree) { DoCertManagerFree(cm); } } } /* Increase reference count on certificate manager. * * @param [in, out] cm Certificate manager. * @return WOLFSSL_SUCCESS on success. * @return 0 when cm is NULL or locking mutex fails. */ int wolfSSL_CertManager_up_ref(WOLFSSL_CERT_MANAGER* cm) { int ret = WOLFSSL_SUCCESS; /* Validate parameter. */ if (cm == NULL) { ret = 0; } if (ret == WOLFSSL_SUCCESS) { int err; /* Increment reference. */ wolfSSL_RefInc(&cm->ref, &err); #ifdef WOLFSSL_REFCNT_ERROR_RETURN if (err) { WOLFSSL_MSG("Failed to lock cm mutex"); ret = 0; } #else (void)err; #endif } return ret; } #if defined(OPENSSL_EXTRA) && !defined(NO_FILESYSTEM) #if defined(WOLFSSL_SIGNER_DER_CERT) static WC_INLINE int wolfssl_cm_get_certs_der(WOLFSSL_CERT_MANAGER* cm, DerBuffer*** buffers, int* cnt) { int err = 0; Signer* signers = NULL; DerBuffer** certBuffers = NULL; int i = 0; word32 row = 0; int numCerts = 0; /* Iterate once to get the number of certs, for memory allocation * purposes. */ for (row = 0; row < CA_TABLE_SIZE; row++) { /* Get signer information of CAs in a row. */ signers = cm->caTable[row]; /* Count each signer in row that has a DER certificate buffer. */ while ((signers != NULL) && (signers->derCert != NULL) && (signers->derCert->buffer != NULL)) { ++numCerts; signers = signers->next; } } /* Check we found certificates. */ if (numCerts == 0) { err = 1; } if (!err) { /* Allocate memory for pointers to each DER buffer. */ certBuffers = (DerBuffer**)XMALLOC( sizeof(DerBuffer*) * (size_t)numCerts, cm->heap, DYNAMIC_TYPE_TMP_BUFFER); if (certBuffers == NULL) { err = 1; } } if (!err) { /* Reset pointers. */ XMEMSET(certBuffers, 0, sizeof(DerBuffer*) * (size_t)numCerts); } /* Copy the certs locally so that we can release the caLock. If the lock * is held when wolfSSL_d2i_X509 is called, GetCA will also try to get * the lock, leading to deadlock. */ for (row = 0; (!err) && (row < CA_TABLE_SIZE); row++) { /* Get signer information of CAs in a row. */ signers = cm->caTable[row]; /* Copy each DER certificate buffer of signers in a row. */ while ((signers != NULL) && (signers->derCert != NULL) && (signers->derCert->buffer != NULL)) { /* Allocate memory to hold DER certificate buffer. */ int ret = AllocDer(&certBuffers[i], signers->derCert->length, CA_TYPE, cm->heap); if (ret < 0) { err = 1; break; } /* Copy buffer into array element. */ XMEMCPY(certBuffers[i]->buffer, signers->derCert->buffer, signers->derCert->length); certBuffers[i]->length = signers->derCert->length; /* Store in next index. */ ++i; /* Move on to next signer in row. */ signers = signers->next; } } *buffers = certBuffers; *cnt = numCerts; return err; } /* Retrieve stack of X509 certificates in a certificate manager (CM). * * @param [in] cm Certificate manager. * * @return Stack of X509 certs on success * @return NULL on failure. */ WOLFSSL_STACK* wolfSSL_CertManagerGetCerts(WOLFSSL_CERT_MANAGER* cm) { WOLFSSL_STACK* sk = NULL; int numCerts = 0; DerBuffer** certBuffers = NULL; int i = 0; int err = 0; WOLFSSL_ENTER("wolfSSL_CertManagerGetCerts"); /* Validate parameter. */ if (cm == NULL) { err = 1; } if (!err) { /* Create an empty certificate stack to return. */ sk = wolfSSL_sk_X509_new_null(); if (sk == NULL) { err = 1; } } /* Lock CA table. */ if ((!err) && (wc_LockMutex(&cm->caLock) != 0)) { err = 1; } if (!err) { err = wolfssl_cm_get_certs_der(cm, &certBuffers, &numCerts); /* Release CA lock. */ wc_UnLockMutex(&cm->caLock); } /* Put each DER certificate buffer into a stack of WOLFSSL_X509 */ for (i = 0; (!err) && (i < numCerts); ++i) { const byte* derBuffer = NULL; WOLFSSL_X509* x509 = NULL; /* Get pointer to DER encoding of certificate. */ derBuffer = certBuffers[i]->buffer; /* Decode certificate. */ wolfSSL_d2i_X509(&x509, &derBuffer, (int)certBuffers[i]->length); if (x509 == NULL) { err = 1; } /* Decode certificate. */ if ((!err) && (wolfSSL_sk_X509_push(sk, x509) <= 0)) { wolfSSL_X509_free(x509); x509 = NULL; err = 1; } } if (certBuffers != NULL) { /* Dispose of temporary cert storage (for access outside of lock). */ for (i = 0; i < numCerts && certBuffers[i] != NULL; ++i) { FreeDer(&certBuffers[i]); } XFREE(certBuffers, cm->heap, DYNAMIC_TYPE_TMP_BUFFER); } /* Dispose of stack of certificates on error. */ if (err && (sk != NULL)) { wolfSSL_sk_X509_pop_free(sk, NULL); sk = NULL; } return sk; } #endif /* WOLFSSL_SIGNER_DER_CERT */ #endif /* OPENSSL_EXTRA && !NO_FILESYSTEM */ /* Unload the CA signer table. * * @param [in] cm Certificate manager. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when cm is NULL. * @return BAD_MUTEX_E when locking fails. */ int wolfSSL_CertManagerUnloadCAs(WOLFSSL_CERT_MANAGER* cm) { int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_CertManagerUnloadCAs"); /* Validate parameter. */ if (cm == NULL) { ret = BAD_FUNC_ARG; } /* Lock CA table. */ if ((ret == WOLFSSL_SUCCESS) && (wc_LockMutex(&cm->caLock) != 0)) { ret = BAD_MUTEX_E; } if (ret == WOLFSSL_SUCCESS) { /* Dispose of CA table. */ FreeSignerTable(cm->caTable, CA_TABLE_SIZE, cm->heap); /* Unlock CA table. */ wc_UnLockMutex(&cm->caLock); } return ret; } int wolfSSL_CertManagerUnloadTypeCerts( WOLFSSL_CERT_MANAGER* cm, byte type) { int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_CertManagerUnloadTypeCerts"); /* Validate parameter. */ if (cm == NULL) { ret = BAD_FUNC_ARG; } /* Lock CA table. */ if ((ret == WOLFSSL_SUCCESS) && (wc_LockMutex(&cm->caLock) != 0)) { ret = BAD_MUTEX_E; } if (ret == WOLFSSL_SUCCESS) { /* Dispose of CA table. */ FreeSignerTableType(cm->caTable, CA_TABLE_SIZE, type, cm->heap); /* Unlock CA table. */ wc_UnLockMutex(&cm->caLock); } return ret; } #if defined(OPENSSL_EXTRA) static int wolfSSL_CertManagerUnloadTempIntermediateCerts( WOLFSSL_CERT_MANAGER* cm) { WOLFSSL_ENTER("wolfSSL_CertManagerUnloadTempIntermediateCerts"); return wolfSSL_CertManagerUnloadTypeCerts(cm, WOLFSSL_TEMP_CA); } #endif int wolfSSL_CertManagerUnloadIntermediateCerts( WOLFSSL_CERT_MANAGER* cm) { WOLFSSL_ENTER("wolfSSL_CertManagerUnloadIntermediateCerts"); return wolfSSL_CertManagerUnloadTypeCerts(cm, WOLFSSL_CHAIN_CA); } #ifdef WOLFSSL_TRUST_PEER_CERT /* Unload the trusted peers table. * * @param [in] cm Certificate manager. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when cm is NULL. * @return BAD_MUTEX_E when locking fails. */ int wolfSSL_CertManagerUnload_trust_peers(WOLFSSL_CERT_MANAGER* cm) { int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_CertManagerUnload_trust_peers"); /* Validate parameter. */ if (cm == NULL) { ret = BAD_FUNC_ARG; } /* Lock trusted peers table. */ if ((ret == WOLFSSL_SUCCESS) && (wc_LockMutex(&cm->tpLock) != 0)) { ret = BAD_MUTEX_E; } if (ret == WOLFSSL_SUCCESS) { /* Dispose of trusted peers table. */ FreeTrustedPeerTable(cm->tpTable, TP_TABLE_SIZE, cm->heap); /* Unlock trusted peers table. */ wc_UnLockMutex(&cm->tpLock); } return ret; } #endif /* WOLFSSL_TRUST_PEER_CERT */ /* Load certificate/s from buffer with flags and type. * * @param [in] cm Certificate manager. * @param [in] buff Buffer holding encoding of certificate. * @param [in] sz Length in bytes of data in buffer. * @param [in] format Format of encoding. Valid values: * WOLFSSL_FILETYPE_ASN1, WOLFSSL_FILETYPE_PEM. * @param [in] userChain Indicates buffer holds chain of certificates. * @param [in] flags Flags to modify behaviour of loading. Valid flags: * WOLFSSL_LOAD_FLAG_IGNORE_ERR, * WOLFSSL_LOAD_FLAG_DATE_ERR_OKAY, * WOLFSSL_LOAD_FLAG_PEM_CA_ONLY, * WOLFSSL_LOAD_FLAG_IGNORE_BAD_PATH_ERR, and * WOLFSSL_LOAD_FLAG_IGNORE_ZEROFILE. * @param [in] type The CA cert's type, used in the internal CA table. Defaults to WOLFSSL_USER_CA, passing in WOLFSSL_USER_CA = noop. Recommended to set to WOLFSSL_USER_INTER when loading intermediate certs to allow unloading via wolfSSL_CertManagerUnloadTypeCerts. * @return WOLFSSL_SUCCESS on success. * @return WOLFSSL_FATAL_ERROR when cm is NULL or failed create WOLFSSL_CTX. * @return Other values on loading failure. */ int wolfSSL_CertManagerLoadCABufferType(WOLFSSL_CERT_MANAGER* cm, const unsigned char* buff, long sz, int format, int userChain, word32 flags, int type) { int ret = WOLFSSL_SUCCESS; WOLFSSL_CTX* tmp = NULL; DecodedCert* dCert = NULL; DerBuffer* der = NULL; WOLFSSL_ENTER("wolfSSL_CertManagerLoadCABufferType"); /* Validate parameters. */ if (cm == NULL) { WOLFSSL_MSG("No CertManager error"); ret = WOLFSSL_FATAL_ERROR; } /* Allocate a temporary WOLFSSL_CTX to load with. */ if ((ret == WOLFSSL_SUCCESS) && ((tmp = wolfSSL_CTX_new_ex(cm_pick_method(cm->heap), cm->heap)) == NULL)) { WOLFSSL_MSG("CTX new failed"); ret = WOLFSSL_FATAL_ERROR; } if (ret == WOLFSSL_SUCCESS) { /* Some configurations like OPENSSL_COMPATIBLE_DEFAULTS may turn off * verification by default. Let's restore our desired defaults. */ wolfSSL_CTX_set_verify(tmp, WOLFSSL_VERIFY_DEFAULT, NULL); /* Replace certificate manager with one to load certificate/s into. */ wolfSSL_CertManagerFree(tmp->cm); tmp->cm = cm; /* Load certificate buffer. */ ret = wolfSSL_CTX_load_verify_buffer_ex(tmp, buff, sz, format, userChain, flags); /* Clear certificate manager in WOLFSSL_CTX so it won't be freed. */ tmp->cm = NULL; } if (ret == WOLFSSL_SUCCESS && type != WOLFSSL_USER_CA) { dCert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), cm->heap, DYNAMIC_TYPE_DCERT); if (dCert == NULL) { ret = WOLFSSL_FATAL_ERROR; } else { XMEMSET(dCert, 0, sizeof(DecodedCert)); if (format == WOLFSSL_FILETYPE_PEM) { #ifndef WOLFSSL_PEM_TO_DER ret = NOT_COMPILED_IN; #else ret = PemToDer(buff, sz, CERT_TYPE, &der, cm->heap, NULL, NULL); if (!ret) { /* Replace buffer pointer and size with DER buffer. */ buff = der->buffer; sz = (long)der->length; ret = WOLFSSL_SUCCESS; } else { WOLFSSL_ERROR(ret); ret = WOLFSSL_FATAL_ERROR; } #endif } if (ret == WOLFSSL_SUCCESS) { wc_InitDecodedCert(dCert, buff, (word32)sz, cm->heap); ret = wc_ParseCert(dCert, CERT_TYPE, NO_VERIFY, NULL); if (ret) { ret = WOLFSSL_FATAL_ERROR; } else { ret = SetCAType(cm, dCert->extSubjKeyId, type); } } if (dCert) { wc_FreeDecodedCert(dCert); XFREE(dCert, cm->heap, DYNAMIC_TYPE_DCERT); } if (der) { FreeDer(&der); } } } /* Dispose of temporary WOLFSSL_CTX. */ wolfSSL_CTX_free(tmp); return ret; } /* Load certificate/s from buffer with flags. * * @param [in] cm Certificate manager. * @param [in] buff Buffer holding encoding of certificate. * @param [in] sz Length in bytes of data in buffer. * @param [in] format Format of encoding. Valid values: * WOLFSSL_FILETYPE_ASN1, WOLFSSL_FILETYPE_PEM. * @param [in] userChain Indicates buffer holds chain of certificates. * @param [in] flags Flags to modify behaviour of loading. Valid flags: * WOLFSSL_LOAD_FLAG_IGNORE_ERR, * WOLFSSL_LOAD_FLAG_DATE_ERR_OKAY, * WOLFSSL_LOAD_FLAG_PEM_CA_ONLY, * WOLFSSL_LOAD_FLAG_IGNORE_BAD_PATH_ERR, and * WOLFSSL_LOAD_FLAG_IGNORE_ZEROFILE. * @return WOLFSSL_SUCCESS on success. * @return WOLFSSL_FATAL_ERROR when cm is NULL or failed create WOLFSSL_CTX. * @return Other values on loading failure. */ int wolfSSL_CertManagerLoadCABuffer_ex(WOLFSSL_CERT_MANAGER* cm, const unsigned char* buff, long sz, int format, int userChain, word32 flags) { return wolfSSL_CertManagerLoadCABufferType(cm, buff, sz, format, userChain, flags, WOLFSSL_USER_CA); } /* Load certificate/s from buffer into table. * * Uses default load verification flags and is not a user chain. * * @param [in] cm Certificate manager. * @param [in] buff Buffer holding encoding of certificate. * @param [in] sz Length in bytes of data in buffer. * @param [in] format Format of encoding. Valid values: * WOLFSSL_FILETYPE_ASN1, WOLFSSL_FILETYPE_PEM. * @return WOLFSSL_SUCCESS on success. * @return WOLFSSL_FATAL_ERROR when cm is NULL or failed create WOLFSSL_CTX. * @return Other values on loading failure. */ int wolfSSL_CertManagerLoadCABuffer(WOLFSSL_CERT_MANAGER* cm, const unsigned char* buff, long sz, int format) { return wolfSSL_CertManagerLoadCABuffer_ex(cm, buff, sz, format, 0, WOLFSSL_LOAD_VERIFY_DEFAULT_FLAGS); } #ifndef NO_WOLFSSL_CM_VERIFY /* Set the verification callback into certificate manager. * * @param [in] cm Certificate manager. * @param [in] vc Verification callback. */ void wolfSSL_CertManagerSetVerify(WOLFSSL_CERT_MANAGER* cm, VerifyCallback vc) { WOLFSSL_ENTER("wolfSSL_CertManagerSetVerify"); if (cm != NULL) { cm->verifyCallback = vc; } } #endif /* !NO_WOLFSSL_CM_VERIFY */ #ifdef WC_ASN_UNKNOWN_EXT_CB void wolfSSL_CertManagerSetUnknownExtCallback(WOLFSSL_CERT_MANAGER* cm, wc_UnknownExtCallback cb) { WOLFSSL_ENTER("wolfSSL_CertManagerSetUnknownExtCallback"); if (cm != NULL) { cm->unknownExtCallback = cb; } } #endif /* WC_ASN_UNKNOWN_EXT_CB */ #if (!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH)) || \ defined(OPENSSL_EXTRA) /* Verify the certificate. * * Uses the verification callback if available. * * @param [in] cm Certificate manager. * @param [in] buff Buffer holding encoded certificate. * @param [in] sz Size in bytes of data in buffer. * @param [in] format Format of encoding. Valid values: * WOLFSSL_FILETYPE_ASN1, WOLFSSL_FILETYPE_PEM. * @param [in] prev_err Previous error. Passed to callback. * @return WOLFSSL_SUCCESS on success. * @return MEMORY_E when dynamic memory allocation fails. * @return NOT_COMPILED_IN when converting from PEM to DER is not a feature of * the wolfSSL build. */ int CM_VerifyBuffer_ex(WOLFSSL_CERT_MANAGER* cm, const unsigned char* buff, long sz, int format, int prev_err) { int ret = 0; int fatal = 0; DerBuffer* der = NULL; WC_DECLARE_VAR(cert, DecodedCert, 1, 0); WOLFSSL_ENTER("CM_VerifyBuffer_ex"); (void)prev_err; /* Allocate memory for decoded certificate. */ WC_ALLOC_VAR_EX(cert, DecodedCert, 1, cm->heap, DYNAMIC_TYPE_DCERT, { ret=MEMORY_E; fatal=1; }); if (WC_VAR_OK(cert)) { /* Reset fields of decoded certificate. */ XMEMSET(cert, 0, sizeof(DecodedCert)); if (format == WOLFSSL_FILETYPE_PEM) { #ifndef WOLFSSL_PEM_TO_DER ret = NOT_COMPILED_IN; fatal = 1; #else /* Convert to DER from PEM. */ ret = PemToDer(buff, sz, CERT_TYPE, &der, cm->heap, NULL, NULL); if (ret != 0) { fatal = 1; } else { /* Replace buffer pointer and size with DER buffer. */ buff = der->buffer; sz = (long)der->length; } #endif } } if (ret == 0) { /* Create a decoded certificate with DER buffer. */ InitDecodedCert(cert, buff, (word32)sz, cm->heap); #ifdef WC_ASN_UNKNOWN_EXT_CB if (cm->unknownExtCallback != NULL) wc_SetUnknownExtCallback(cert, cm->unknownExtCallback); #endif /* Parse DER into decoded certificate fields and verify signature * against a known CA. */ ret = ParseCertRelative(cert, CERT_TYPE, VERIFY, cm, NULL); } #ifdef HAVE_CRL if ((ret == 0) && cm->crlEnabled) { /* Check for a CRL for the CA and check validity of certificate. */ ret = CheckCertCRL(cm->crl, cert); } #endif (void)fatal; #if !defined(NO_WOLFSSL_CM_VERIFY) && \ (!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH)) /* Use callback to perform verification too if available. */ if ((!fatal) && cm->verifyCallback) { WC_DECLARE_VAR(args, ProcPeerCertArgs, 1, 0); buffer certBuf; #ifdef WOLFSSL_SMALL_STACK /* Allocate memory for object to hold arguments for callback. */ args = (ProcPeerCertArgs*)XMALLOC(sizeof(ProcPeerCertArgs), cm->heap, DYNAMIC_TYPE_TMP_BUFFER); if (args == NULL) { ret = MEMORY_E; fatal = 1; } if (!fatal) #endif { XMEMSET(args, 0, sizeof(ProcPeerCertArgs)); /* DER encoding. */ certBuf.buffer = (byte*)buff; certBuf.length = (unsigned int)sz; /* One certificate available. */ args->totalCerts = 1; args->certs = &certBuf; args->dCert = cert; args->dCertInit = 1; /* Replace value in ret with an error value passed in. */ if (prev_err != 0) { ret = prev_err; } /* Use callback to verify certificate. */ ret = DoVerifyCallback(cm, NULL, ret, args); } WC_FREE_VAR_EX(args, cm->heap, DYNAMIC_TYPE_TMP_BUFFER); } #endif /* Dispose of allocated memory. */ FreeDecodedCert(cert); FreeDer(&der); WC_FREE_VAR_EX(cert, cm->heap, DYNAMIC_TYPE_DCERT); /* Convert the ret value to a return value. */ return (ret == 0) ? WOLFSSL_SUCCESS : ret; } /* Verify the certificate. * * Uses the verification callback if available. * * @param [in] cm Certificate manager. * @param [in] buff Buffer holding encoded certificate. * @param [in] sz Size in bytes of data in buffer. * @param [in] format Format of encoding. Valid values: * WOLFSSL_FILETYPE_ASN1, WOLFSSL_FILETYPE_PEM. * @param [in] prev_err Previous error. Passed to callback. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when cm or buff is NULL or sz is negative or zero. * @return WOLFSSL_BAD_FILETYPE when format is invalid. * @return MEMORY_E when dynamic memory allocation fails. * @return NOT_COMPILED_IN when converting from PEM to DER is not a feature of * the wolfSSL build. */ int wolfSSL_CertManagerVerifyBuffer(WOLFSSL_CERT_MANAGER* cm, const unsigned char* buff, long sz, int format) { int ret; WOLFSSL_ENTER("wolfSSL_CertManagerVerifyBuffer"); /* Validate parameters. */ if ((cm == NULL) || (buff == NULL) || (sz <= 0)) { ret = BAD_FUNC_ARG; } else if ((format != WOLFSSL_FILETYPE_ASN1) && (format != WOLFSSL_FILETYPE_PEM)) { ret = WOLFSSL_BAD_FILETYPE; } else { /* No previous error. */ ret = CM_VerifyBuffer_ex(cm, buff, sz, format, 0); } return ret; } #endif /* (!NO_WOLFSSL_CLIENT || !WOLFSSL_NO_CLIENT_AUTH) || OPENSSL_EXTRA */ #ifndef NO_FILESYSTEM #if (!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH)) || \ defined(OPENSSL_EXTRA) /* Verify the certificate loaded from a file. * * Uses the verification callback if available. * * @param [in] cm Certificate manager. * @param [in] format Format of encoding. Valid values: * WOLFSSL_FILETYPE_ASN1, WOLFSSL_FILETYPE_PEM. * @param [in] prev_err Previous error. Passed to callback. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when cm or buff is NULL or sz is negative. * @return WOLFSSL_BAD_FILETYPE when format is invalid. * @return WOLFSSL_BAD_FILE when reading the certificate file fails. * @return MEMORY_E when dynamic memory allocation fails. * @return NOT_COMPILED_IN when converting from PEM to DER is not a feature of * the wolfSSL build. */ int wolfSSL_CertManagerVerify(WOLFSSL_CERT_MANAGER* cm, const char* fname, int format) { int ret = WOLFSSL_SUCCESS; #ifndef WOLFSSL_SMALL_STACK byte staticBuffer[FILE_BUFFER_SIZE]; #endif byte* buff = NULL; long sz = 0; XFILE file = XBADFILE; WOLFSSL_ENTER("wolfSSL_CertManagerVerify"); #ifndef WOLFSSL_SMALL_STACK buff = staticBuffer; #endif /* Validate parameters. cm and format validated in: * wolfSSL_CertManagerVerifyBuffer */ if ((cm == NULL) || (fname == NULL)) { ret = BAD_FUNC_ARG; } /* Open the file containing a certificate. */ if ((ret == WOLFSSL_SUCCESS) && ((file = XFOPEN(fname, "rb")) == XBADFILE)) { ret = WOLFSSL_BAD_FILE; } /* Get the length of the file. */ if (ret == WOLFSSL_SUCCESS) { ret = wolfssl_file_len(file, &sz); if (ret == 0) { ret = WOLFSSL_SUCCESS; } } /* Allocate dynamic memory for file contents if no static buffer or too * small. */ #ifndef WOLFSSL_SMALL_STACK if ((ret == WOLFSSL_SUCCESS) && (sz > (long)sizeof(staticBuffer))) #else if (ret == WOLFSSL_SUCCESS) #endif { WOLFSSL_MSG("Getting dynamic buffer"); buff = (byte*)XMALLOC((size_t)sz, cm->heap, DYNAMIC_TYPE_FILE); if (buff == NULL) { ret = WOLFSSL_BAD_FILE; } } /* Read all the file into buffer. */ if ((ret == WOLFSSL_SUCCESS) && (XFREAD(buff, 1, (size_t)sz, file) != (size_t)sz)) { ret = WOLFSSL_BAD_FILE; } /* Close file if opened. */ if (file != XBADFILE) { XFCLOSE(file); } if (ret == WOLFSSL_SUCCESS) { /* Verify the certificate read. */ ret = wolfSSL_CertManagerVerifyBuffer(cm, buff, sz, format); } /* Dispose of buffer if it was allocated. */ #ifndef WOLFSSL_SMALL_STACK if (buff != staticBuffer) #endif { if (cm != NULL) { XFREE(buff, cm->heap, DYNAMIC_TYPE_FILE); } } return ret; } #endif /* Load the CA file and/or certificate files in a path. * * @param [in] cm Certificate manager. * @param [in] file Name of CA file. * @param [in] path Path to a directory containing certificates. * @return WOLFSSL_SUCCESS on success. * @return WOLFSSL_FATAL_ERROR when cm is NULL or unable to create WOLFSSL_CTX. * @return Otherwise failure. */ int wolfSSL_CertManagerLoadCA(WOLFSSL_CERT_MANAGER* cm, const char* file, const char* path) { int ret = WOLFSSL_SUCCESS; WOLFSSL_CTX* tmp = NULL; WOLFSSL_ENTER("wolfSSL_CertManagerLoadCA"); /* Validate parameters. file and path validated in: * wolfSSL_CTX_load_verify_locations*/ if (cm == NULL) { WOLFSSL_MSG("No CertManager error"); ret = WOLFSSL_FATAL_ERROR; } /* Create temporary WOLFSSL_CTX. */ if ((ret == WOLFSSL_SUCCESS) && ((tmp = wolfSSL_CTX_new_ex(cm_pick_method(cm->heap), cm->heap)) == NULL)) { WOLFSSL_MSG("CTX new failed"); ret = WOLFSSL_FATAL_ERROR; } if (ret == WOLFSSL_SUCCESS) { /* Some configurations like OPENSSL_COMPATIBLE_DEFAULTS may turn off * verification by default. Let's restore our desired defaults. */ wolfSSL_CTX_set_verify(tmp, WOLFSSL_VERIFY_DEFAULT, NULL); /* Replace certificate manager with one to load certificate/s into. */ wolfSSL_CertManagerFree(tmp->cm); tmp->cm = cm; /* Load certificate from file and path. */ ret = wolfSSL_CTX_load_verify_locations(tmp, file, path); /* Clear certificate manager in WOLFSSL_CTX so it won't be freed. */ tmp->cm = NULL; } /* Dispose of temporary WOLFSSL_CTX. */ wolfSSL_CTX_free(tmp); return ret; } #endif /* NO_FILESYSTEM */ #if defined(PERSIST_CERT_CACHE) /* Version of layout of cache of CA certificates. */ #define WOLFSSL_CACHE_CERT_VERSION 1 /* CA certificates cache information. */ typedef struct { /* Cache certificate layout version id. */ int version; /* Number of hash table rows. Maximum of CA_TABLE_SIZE. */ int rows; /* Number of columns per row. */ int columns[CA_TABLE_SIZE]; /* Size of Signer object. */ int signerSz; } CertCacheHeader; /* current cert persistence layout is: 1) CertCacheHeader 2) caTable update WOLFSSL_CERT_CACHE_VERSION if change layout for the following PERSIST_CERT_CACHE functions */ /* Return number of bytes of memory needed to persist this signer. * * Assumes we have locked CA table. * * @param [in] Signer Signer entry in CA table. * @return Number of bytes. */ static WC_INLINE int cm_get_signer_memory(Signer* signer) { int sz = sizeof(signer->pubKeySize) + sizeof(signer->keyOID) + sizeof(signer->nameLen) + sizeof(signer->subjectNameHash); #if !defined(NO_SKID) sz += (int)sizeof(signer->subjectKeyIdHash); #endif /* Add dynamic bytes needed. */ sz += (int)signer->pubKeySize; sz += signer->nameLen; return sz; } /* Return number of bytes of memory needed to persist this row. * * Assumes we have locked CA table. * * @param [in] row A row of signers from the CA table. * @return Number of bytes. */ static WC_INLINE int cm_get_cert_cache_row_memory(Signer* row) { int sz = 0; /* Each signer in row. */ while (row != NULL) { /* Add in size of this signer. */ sz += cm_get_signer_memory(row); row = row->next; } return sz; } /* Return the number of bytes of memory to persist cert cache. * * Assumes we have locked CA table. * * @param [in] cm Certificate manager. * @return Number of bytes. */ static WC_INLINE int cm_get_cert_cache_mem_size(WOLFSSL_CERT_MANAGER* cm) { int sz; int i; sz = sizeof(CertCacheHeader); /* Each row in table. */ for (i = 0; i < CA_TABLE_SIZE; i++) { /* Add in size of this row. */ sz += cm_get_cert_cache_row_memory(cm->caTable[i]); } return sz; } /* Get count of columns for each row. * * Assumes we have locked CA table. * * @param [in] cm Certificate manager. * @param [in] columns Array of row counts. */ static WC_INLINE void cm_set_cert_header_Columns(WOLFSSL_CERT_MANAGER* cm, int* columns) { int i; Signer* row; /* Each row in table. */ for (i = 0; i < CA_TABLE_SIZE; i++) { int count = 0; /* Get row from table. */ row = cm->caTable[i]; /* Each entry in row. */ while (row != NULL) { /* Update count. */ ++count; row = row->next; } /* Store row count. */ columns[i] = count; } } /* Restore whole cert row from memory, * * Assumes we have locked CA table. * * @param [in] cm Certificate manager. * @param [in] current Buffer containing rows. * @param [in] row Row number being restored. * @param [in] listSz Number of entries in row. * @param [in] end End of data in buffer. * @return Number of bytes consumed on success. * @return PARSE_ERROR when listSz is less than zero. * @return BUFFER_E when buffer is too small. * @return MEMORY_E when dynamic memory allocation fails. * @return Negative value on error. */ static WC_INLINE int cm_restore_cert_row(WOLFSSL_CERT_MANAGER* cm, byte* current, int row, int listSz, const byte* end) { int ret = 0; int idx = 0; /* Validate parameters. */ if (listSz < 0) { WOLFSSL_MSG("Row header corrupted, negative value"); ret = PARSE_ERROR; } /* Process all entries. */ while ((ret == 0) && (listSz > 0)) { Signer* signer = NULL; byte* publicKey; byte* start = current + idx; /* for end checks on this signer */ int minSz = sizeof(signer->pubKeySize) + sizeof(signer->keyOID) + sizeof(signer->nameLen) + sizeof(signer->subjectNameHash); #ifndef NO_SKID minSz += (int)sizeof(signer->subjectKeyIdHash); #endif /* Check minimal size of bytes available. */ if (start + minSz > end) { WOLFSSL_MSG("Would overread restore buffer"); ret = BUFFER_E; } /* Make a new signer. */ if ((ret == 0) && ((signer = MakeSigner(cm->heap)) == NULL)) { ret = MEMORY_E; } if (ret == 0) { /* Copy in public key size. */ XMEMCPY(&signer->pubKeySize, current + idx, sizeof(signer->pubKeySize)); idx += (int)sizeof(signer->pubKeySize); /* Copy in public key OID. */ XMEMCPY(&signer->keyOID, current + idx, sizeof(signer->keyOID)); idx += (int)sizeof(signer->keyOID); /* Check bytes available for public key. */ if (start + minSz + signer->pubKeySize > end) { WOLFSSL_MSG("Would overread restore buffer"); ret = BUFFER_E; } } if (ret == 0) { /* Allocate memory for public key to be stored in. */ publicKey = (byte*)XMALLOC(signer->pubKeySize, cm->heap, DYNAMIC_TYPE_KEY); if (publicKey == NULL) { ret = MEMORY_E; } } if (ret == 0) { /* Copy in public key. */ XMEMCPY(publicKey, current + idx, signer->pubKeySize); signer->publicKey = publicKey; idx += (int)signer->pubKeySize; /* Copy in certificate name length. */ XMEMCPY(&signer->nameLen, current + idx, sizeof(signer->nameLen)); idx += (int)sizeof(signer->nameLen); /* Check bytes available for certificate name. */ if (start + minSz + signer->pubKeySize + signer->nameLen > end) { WOLFSSL_MSG("Would overread restore buffer"); ret = BUFFER_E; } } if (ret == 0) { /* Allocate memory for public key to be stored in. */ signer->name = (char*)XMALLOC((size_t)signer->nameLen, cm->heap, DYNAMIC_TYPE_SUBJECT_CN); if (signer->name == NULL) { ret = MEMORY_E; } } if (ret == 0) { /* Copy in certificate name. */ /* safe cast -- allocated by above XMALLOC(). */ XMEMCPY((void *)(wc_ptr_t)signer->name, current + idx, (size_t)signer->nameLen); idx += signer->nameLen; /* Copy in hash of subject name. */ XMEMCPY(signer->subjectNameHash, current + idx, SIGNER_DIGEST_SIZE); idx += SIGNER_DIGEST_SIZE; #ifndef NO_SKID /* Copy in hash of subject key. */ XMEMCPY(signer->subjectKeyIdHash, current + idx,SIGNER_DIGEST_SIZE); idx += SIGNER_DIGEST_SIZE; #endif /* Make next Signer the head of the row. */ signer->next = cm->caTable[row]; /* Add Signer to start of row. */ cm->caTable[row] = signer; /* Done one more Signer. */ --listSz; } if ((ret != 0) && (signer != NULL)) { /* Dispose of allocated signer. */ FreeSigner(signer, cm->heap); } } if (ret == 0) { /* Return the number of bytes used on success. */ ret = idx; } return ret; } /* Store whole CA certificate row into memory. * * Assumes we have locked CA table. * * @param [in] cm Certificate manager. * @param [in] current Buffer to write to. * @param [in] row Row number being stored. * @return Number of bytes added. */ static WC_INLINE int cm_store_cert_row(WOLFSSL_CERT_MANAGER* cm, byte* current, int row) { int added = 0; Signer* list; /* Get the row - a linked list. */ list = cm->caTable[row]; /* Each certificate in row. */ while (list != NULL) { /* Public key size. */ XMEMCPY(current + added, &list->pubKeySize, sizeof(list->pubKeySize)); added += (int)sizeof(list->pubKeySize); /* Public key OID. */ XMEMCPY(current + added, &list->keyOID, sizeof(list->keyOID)); added += (int)sizeof(list->keyOID); /* Public key. */ XMEMCPY(current + added, list->publicKey, (size_t)list->pubKeySize); added += (int)list->pubKeySize; /* Certificate name length. */ XMEMCPY(current + added, &list->nameLen, sizeof(list->nameLen)); added += (int)sizeof(list->nameLen); /* Certificate name. */ XMEMCPY(current + added, list->name, (size_t)list->nameLen); added += list->nameLen; /* Hash of subject name. */ XMEMCPY(current + added, list->subjectNameHash, SIGNER_DIGEST_SIZE); added += SIGNER_DIGEST_SIZE; #ifndef NO_SKID /* Hash of public key. */ XMEMCPY(current + added, list->subjectKeyIdHash,SIGNER_DIGEST_SIZE); added += SIGNER_DIGEST_SIZE; #endif /* Next certificate in row. */ list = list->next; } return added; } /* Persist CA certificate cache to memory. * * Assumes we have locked CA table. * * @param [in] cm Certificate manager. * @param [in] mem Memory to persist into. * @param [in] sz Size in bytes of memory. * @return WOLFSSL_SUCCESS on success. * @return BUFFER_E when memory is too small. */ static WC_INLINE int cm_do_mem_save_cert_cache(WOLFSSL_CERT_MANAGER* cm, void* mem, int sz) { int ret = WOLFSSL_SUCCESS; int realSz; int i; WOLFSSL_ENTER("cm_do_mem_save_cert_cache"); /* Calculate amount of memory required to store CA certificate table. */ realSz = cm_get_cert_cache_mem_size(cm); if (realSz > sz) { WOLFSSL_MSG("Mem output buffer too small"); ret = BUFFER_E; } if (ret == WOLFSSL_SUCCESS) { byte* current; CertCacheHeader hdr; /* Create header for storage. */ hdr.version = WOLFSSL_CACHE_CERT_VERSION; hdr.rows = CA_TABLE_SIZE; cm_set_cert_header_Columns(cm, hdr.columns); hdr.signerSz = (int)sizeof(Signer); /* Copy header into memory. */ XMEMCPY(mem, &hdr, sizeof(CertCacheHeader)); current = (byte*)mem + sizeof(CertCacheHeader); /* Each row of table. */ for (i = 0; i < CA_TABLE_SIZE; ++i) { /* Append row to memory. */ current += cm_store_cert_row(cm, current, i); } } return ret; } #if !defined(NO_FILESYSTEM) /* Persist CA certificate cache to file. * * Locks CA table. * * @param [in] cm Certificate manager. * @param [in] fname File name to write to. * @return WOLFSSL_SUCCESS on success. * @return WOLFSSL_BAD_FILE when opening file fails. * @return BAD_MUTEX_E when locking fails. * @return MEMORY_E when dynamic memory allocation fails. * @return FWRITE_ERROR when writing to file fails. */ int CM_SaveCertCache(WOLFSSL_CERT_MANAGER* cm, const char* fname) { XFILE file; int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("CM_SaveCertCache"); /* Open file for writing. */ file = XFOPEN(fname, "w+b"); if (file == XBADFILE) { WOLFSSL_MSG("Couldn't open cert cache save file"); ret = WOLFSSL_BAD_FILE; } /* Lock CA table. */ if ((ret == WOLFSSL_SUCCESS) && (wc_LockMutex(&cm->caLock) != 0)) { WOLFSSL_MSG("wc_LockMutex on caLock failed"); ret = BAD_MUTEX_E; } if (ret == WOLFSSL_SUCCESS) { byte* mem; /* Calculate size of memory required to store CA table. */ size_t memSz = (size_t)cm_get_cert_cache_mem_size(cm); /* Allocate memory to hold CA table. */ mem = (byte*)XMALLOC(memSz, cm->heap, DYNAMIC_TYPE_TMP_BUFFER); if (mem == NULL) { WOLFSSL_MSG("Alloc for tmp buffer failed"); ret = MEMORY_E; } if (ret == WOLFSSL_SUCCESS) { /* Store CA table in memory. */ ret = cm_do_mem_save_cert_cache(cm, mem, (int)memSz); } if (ret == WOLFSSL_SUCCESS) { /* Write memory to file. */ int sz = (int)XFWRITE(mem, memSz, 1, file); if (sz != 1) { WOLFSSL_MSG("Cert cache file write failed"); ret = FWRITE_ERROR; } } XFREE(mem, cm->heap, DYNAMIC_TYPE_TMP_BUFFER); /* Unlock CA table. */ wc_UnLockMutex(&cm->caLock); } /* Close file. */ if (file != XBADFILE) { XFCLOSE(file); } return ret; } /* Restore CA certificate cache from file. * * @param [in] cm Certificate manager. * @param [in] fname File name to write to. * @return WOLFSSL_SUCCESS on success. * @return WOLFSSL_BAD_FILE when opening or using file fails. * @return MEMORY_E when dynamic memory allocation fails. * @return FREAD_ERROR when reading from file fails. */ int CM_RestoreCertCache(WOLFSSL_CERT_MANAGER* cm, const char* fname) { XFILE file; int ret = WOLFSSL_SUCCESS; int memSz = 0; byte* mem = NULL; WOLFSSL_ENTER("CM_RestoreCertCache"); /* Open file for reading. */ file = XFOPEN(fname, "rb"); if (file == XBADFILE) { WOLFSSL_MSG("Couldn't open cert cache save file"); ret = WOLFSSL_BAD_FILE; } if (ret == WOLFSSL_SUCCESS) { /* Read file into allocated memory. */ ret = wolfssl_read_file(file, (char**)&mem, &memSz); if (ret == 0) { ret = WOLFSSL_SUCCESS; } } if (ret == WOLFSSL_SUCCESS) { /* Create the CA certificate table from memory. */ ret = CM_MemRestoreCertCache(cm, mem, memSz); if (ret != WOLFSSL_SUCCESS) { WOLFSSL_MSG("Mem restore cert cache failed"); } } /* Dispose of dynamic memory read into. */ XFREE(mem, cm->heap, DYNAMIC_TYPE_TMP_BUFFER); /* Close file. */ if (file != XBADFILE) { XFCLOSE(file); } return ret; } #endif /* NO_FILESYSTEM */ /* Persist CA certificate cache to memory. * * Locks CA table. * * @param [in] cm Certificate manager. * @param [in] mem Memory to persist into. * @param [in] sz Size in bytes of memory. * @param [out] used Number of bytes used when persisting cache. * @return WOLFSSL_SUCCESS on success. * @return BAD_MUTEX_E when locking fails. * @return BUFFER_E when memory is too small. */ int CM_MemSaveCertCache(WOLFSSL_CERT_MANAGER* cm, void* mem, int sz, int* used) { int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("CM_MemSaveCertCache"); /* Lock CA table. */ if (wc_LockMutex(&cm->caLock) != 0) { WOLFSSL_MSG("wc_LockMutex on caLock failed"); ret = BAD_MUTEX_E; } if (ret == WOLFSSL_SUCCESS) { /* Save CA table into memory. */ ret = cm_do_mem_save_cert_cache(cm, mem, sz); if (ret == WOLFSSL_SUCCESS) { /* Get the number of bytes used. */ *used = cm_get_cert_cache_mem_size(cm); } /* Unlock CA table. */ wc_UnLockMutex(&cm->caLock); } return ret; } /* Restore CA certificate table from memory, * * Locks CA table. * * @param [in] cm Certificate manager. * @param [in] mem Buffer containing rows. * @param [in] sz Size in bytes of data in buffer. * @return WOLFSSL_SUCCESS on success. * @return BUFFER_E when buffer is too small. * @return BAD_MUTEX_E when locking fails. * @return MEMORY_E when dynamic memory allocation fails. */ int CM_MemRestoreCertCache(WOLFSSL_CERT_MANAGER* cm, const void* mem, int sz) { int ret = WOLFSSL_SUCCESS; int i; CertCacheHeader* hdr = (CertCacheHeader*)mem; byte* current = (byte*)mem + sizeof(CertCacheHeader); byte* end = (byte*)mem + sz; /* don't go over */ WOLFSSL_ENTER("CM_MemRestoreCertCache"); /* Check memory available is bigger than cache header. */ if ((sz < (int)sizeof(CertCacheHeader)) || (current > end)) { WOLFSSL_MSG("Cert Cache Memory buffer too small"); ret = BUFFER_E; } /* Validate the cache header. */ if ((ret == WOLFSSL_SUCCESS) && ((hdr->version != WOLFSSL_CACHE_CERT_VERSION) || (hdr->rows != CA_TABLE_SIZE) || (hdr->signerSz != (int)sizeof(Signer)))) { WOLFSSL_MSG("Cert Cache Memory header mismatch"); ret = CACHE_MATCH_ERROR; } /* Lock CA table. */ if ((ret == WOLFSSL_SUCCESS) && (wc_LockMutex(&cm->caLock) != 0)) { WOLFSSL_MSG("wc_LockMutex on caLock failed"); ret = BAD_MUTEX_E; } if (ret == WOLFSSL_SUCCESS) { /* Dispose of current CA certificate table. */ FreeSignerTable(cm->caTable, CA_TABLE_SIZE, cm->heap); /* Each row. */ for (i = 0; i < CA_TABLE_SIZE; ++i) { /* Restore a row from memory. */ int added = cm_restore_cert_row(cm, current, i, hdr->columns[i], end); /* Bail on error. */ if (added < 0) { WOLFSSL_MSG("cm_restore_cert_row error"); ret = added; break; } /* Update pointer to data of next row. */ current += added; } /* Unlock CA table. */ wc_UnLockMutex(&cm->caLock); } return ret; } /* Calculate size of CA certificate cache when persisted to memory. * * Locks CA table. * * @param [in] cm Certificate manager. * @return Number of bytes on success. * @return BAD_MUTEX_E when locking fails. */ int CM_GetCertCacheMemSize(WOLFSSL_CERT_MANAGER* cm) { int ret; WOLFSSL_ENTER("CM_GetCertCacheMemSize"); /* Lock CA table. */ if (wc_LockMutex(&cm->caLock) != 0) { WOLFSSL_MSG("wc_LockMutex on caLock failed"); ret = BAD_MUTEX_E; } else { /* Calculate memory size. */ ret = cm_get_cert_cache_mem_size(cm); /* Unlock CA table. */ wc_UnLockMutex(&cm->caLock); } return ret; } #endif /* PERSIST_CERT_CACHE */ /******************************************************************************* * CRL handling ******************************************************************************/ /* Enables/disables the use of CRLs when validating certificates. * * @param [in] cm Certificate manager. * @param [in] options Options for using CRLs. Valid flags: * WOLFSSL_CRL_CHECKALL, WOLFSSL_CRL_CHECK. * @return WOLFSSL_SUCCESS on success. * @return WOLFSSL_FAILURE when initializing the CRL object fails. * @return BAD_FUNC_ARG when cm is NULL. * @return MEMORY_E when dynamic memory allocation fails. * @return NOT_COMPILED_IN when the CRL feature is disabled. */ int wolfSSL_CertManagerEnableCRL(WOLFSSL_CERT_MANAGER* cm, int options) { int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_CertManagerEnableCRL"); (void)options; /* Validate parameters. */ if (cm == NULL) { ret = BAD_FUNC_ARG; } #if defined(OPENSSL_COMPATIBLE_DEFAULTS) /* If disabling then don't worry about whether CRL feature is enabled. */ if ((ret == WOLFSSL_SUCCESS) && (options == 0)) { /* Disable leaf CRL check. */ cm->crlEnabled = 0; /* Disable all CRL checks. */ cm->crlCheckAll = 0; } else #endif if (ret == WOLFSSL_SUCCESS) { #ifndef HAVE_CRL /* CRL feature not enabled. */ ret = NOT_COMPILED_IN; #else /* Create CRL object if not present. */ if (cm->crl == NULL) { /* Allocate memory for CRL object. */ cm->crl = (WOLFSSL_CRL*)XMALLOC(sizeof(WOLFSSL_CRL), cm->heap, DYNAMIC_TYPE_CRL); if (cm->crl == NULL) { ret = MEMORY_E; } if (ret == WOLFSSL_SUCCESS) { /* Reset fields of CRL object. */ XMEMSET(cm->crl, 0, sizeof(WOLFSSL_CRL)); /* Initialize CRL object. */ if (InitCRL(cm->crl, cm) != 0) { WOLFSSL_MSG("Init CRL failed"); /* Dispose of CRL object - indicating dynamically allocated. */ FreeCRL(cm->crl, 1); cm->crl = NULL; ret = WOLFSSL_FAILURE; } } } if (ret == WOLFSSL_SUCCESS) { #if defined(HAVE_CRL_IO) && defined(USE_WOLFSSL_IO) /* Use built-in callback to lookup CRL from URL. */ cm->crl->crlIOCb = EmbedCrlLookup; #endif #if defined(OPENSSL_COMPATIBLE_DEFAULTS) if ((options & WOLFSSL_CRL_CHECKALL) || (options & WOLFSSL_CRL_CHECK)) #endif { /* Enable leaf CRL check. */ cm->crlEnabled = 1; if (options & WOLFSSL_CRL_CHECKALL) { /* Enable all CRL check. */ cm->crlCheckAll = 1; } } } #endif } return ret; } /* Disables the CRL checks. * * @param [in] cm Certificate manager. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when cm is NULL. */ int wolfSSL_CertManagerDisableCRL(WOLFSSL_CERT_MANAGER* cm) { int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_CertManagerDisableCRL"); /* Validate parameter. */ if (cm == NULL) { ret = BAD_FUNC_ARG; } if (ret == WOLFSSL_SUCCESS) { /* Disable CRL checking. */ cm->crlEnabled = 0; cm->crlCheckAll = 0; } return ret; } #ifdef HAVE_CRL /* Load CRL for use. * * @param [in] cm Certificate manager. * @param [in] buff Buffer holding CRL. * @param [in] sz Size in bytes of CRL in buffer. * @param [in] type Format of encoding. Valid values: * WOLFSSL_FILETYPE_ASN1, WOLFSSL_FILETYPE_PEM. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when cm or buff is NULL or sz is negative or zero. * @return DUPE_ENTRY_E if the same or a newer CRL already exists in the cm. * @return WOLFSSL_FATAL_ERROR when creating CRL object fails. */ int wolfSSL_CertManagerLoadCRLBuffer(WOLFSSL_CERT_MANAGER* cm, const unsigned char* buff, long sz, int type) { int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_CertManagerLoadCRLBuffer"); /* Validate parameters. */ if ((cm == NULL) || (buff == NULL) || (sz <= 0)) { ret = BAD_FUNC_ARG; } /* Create a CRL object if not available and enable CRL checking. */ if ((ret == WOLFSSL_SUCCESS) && (cm->crl == NULL) && (wolfSSL_CertManagerEnableCRL(cm, WOLFSSL_CRL_CHECK) != WOLFSSL_SUCCESS)) { WOLFSSL_MSG("Enable CRL failed"); ret = WOLFSSL_FATAL_ERROR; } if (ret == WOLFSSL_SUCCESS) { /* Load CRL into CRL object of the certificate manager. */ ret = BufferLoadCRL(cm->crl, buff, sz, type, VERIFY); } return ret; } /* Free the CRL object of the certificate manager. * * @param [in] cm Certificate manager. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when cm is NULL. */ int wolfSSL_CertManagerFreeCRL(WOLFSSL_CERT_MANAGER* cm) { int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_CertManagerFreeCRL"); /* Validate parameter. */ if (cm == NULL) { ret = BAD_FUNC_ARG; } /* Check whether CRL object exists. */ if ((ret == WOLFSSL_SUCCESS) && (cm->crl != NULL)) { /* Dispose of CRL object - indicating dynamically allocated. */ FreeCRL(cm->crl, 1); cm->crl = NULL; } return ret; } /* Check DER encoded certificate against CRLs if checking enabled. * * @param [in] cm Certificate manager. * @param [in] der DER encode certificate. * @param [in] sz Size in bytes of DER encode certificate. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when cm or der is NULL or sz is negative or zero. * @return MEMORY_E when dynamic memory allocation fails. */ int wolfSSL_CertManagerCheckCRL(WOLFSSL_CERT_MANAGER* cm, const unsigned char* der, int sz) { int ret = 0; WC_DECLARE_VAR(cert, DecodedCert, 1, 0); WOLFSSL_ENTER("wolfSSL_CertManagerCheckCRL"); /* Validate parameters. */ if ((cm == NULL) || (der == NULL) || (sz <= 0)) { ret = BAD_FUNC_ARG; } /* Check if CRL checking enabled. */ if ((ret == 0) && cm->crlEnabled) { /* Allocate memory for decoded certificate. */ WC_ALLOC_VAR_EX(cert, DecodedCert, 1, NULL, DYNAMIC_TYPE_DCERT, ret=MEMORY_E); if (WC_VAR_OK(cert)) { /* Initialize decoded certificate with buffer. */ InitDecodedCert(cert, der, (word32)sz, NULL); /* Parse certificate and perform CRL checks. */ ret = ParseCertRelative(cert, CERT_TYPE, VERIFY_CRL, cm, NULL); if (ret != 0) { WOLFSSL_MSG("ParseCert failed"); } /* Do CRL checks with decoded certificate. */ else if ((ret = CheckCertCRL(cm->crl, cert)) != 0) { WOLFSSL_MSG("CheckCertCRL failed"); } /* Dispose of dynamically allocated memory. */ FreeDecodedCert(cert); WC_FREE_VAR_EX(cert, NULL, DYNAMIC_TYPE_DCERT); } } return (ret == 0) ? WOLFSSL_SUCCESS : ret; } /* Set the missing CRL callback. * * @param [in] cm Certificate manager. * @param [in] cb Missing CRL callback. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when cm is NULL. */ int wolfSSL_CertManagerSetCRL_Cb(WOLFSSL_CERT_MANAGER* cm, CbMissingCRL cb) { int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_CertManagerSetCRL_Cb"); /* Validate parameters. */ if (cm == NULL) { ret = BAD_FUNC_ARG; } if (ret == WOLFSSL_SUCCESS) { /* Store callback. */ cm->cbMissingCRL = cb; } return ret; } int wolfSSL_CertManagerSetCRL_ErrorCb(WOLFSSL_CERT_MANAGER* cm, crlErrorCb cb, void* ctx) { int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_CertManagerSetCRL_Cb"); /* Validate parameters. */ if (cm == NULL) { ret = BAD_FUNC_ARG; } if (ret == WOLFSSL_SUCCESS) { /* Store callback. */ cm->crlCb = cb; cm->crlCbCtx = ctx; } return ret; } #ifdef HAVE_CRL_UPDATE_CB int wolfSSL_CertManagerGetCRLInfo(WOLFSSL_CERT_MANAGER* cm, CrlInfo* info, const byte* buff, long sz, int type) { return GetCRLInfo(cm->crl, info, buff, sz, type); } /* Set the callback to be called when a CRL entry has * been updated (new entry had the same issuer hash and * a newer CRL number). * * @param [in] cm Certificate manager. * @param [in] cb CRL update callback. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when cm is NULL. */ int wolfSSL_CertManagerSetCRLUpdate_Cb(WOLFSSL_CERT_MANAGER* cm, CbUpdateCRL cb) { int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_CertManagerSetCRLUpdate_Cb"); /* Validate parameters. */ if (cm == NULL) { ret = BAD_FUNC_ARG; } if (ret == WOLFSSL_SUCCESS) { /* Store callback. */ cm->cbUpdateCRL = cb; } return ret; } #endif #ifdef HAVE_CRL_IO /* Set the CRL I/O callback. * * @param [in] cm Certificate manager. * @param [in] cb CRL I/O callback. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when cm is NULL. */ int wolfSSL_CertManagerSetCRL_IOCb(WOLFSSL_CERT_MANAGER* cm, CbCrlIO cb) { int ret = WOLFSSL_SUCCESS; /* Validate parameters. */ if (cm == NULL) { ret = BAD_FUNC_ARG; } if ((ret == WOLFSSL_SUCCESS) && (cm->crl != NULL)) { /* Store callback. */ cm->crl->crlIOCb = cb; } return ret; } #endif #ifndef NO_FILESYSTEM /* Load CRL/s from path with the option of monitoring for changes. * * @param [in] cm Certificate manager. * @param [in] path Path to a directory containing CRLs. * @param [in] type Format of encoding. Valid values: * WOLFSSL_FILETYPE_ASN1, WOLFSSL_FILETYPE_PEM. * @param [in] monitor Whether to monitor path for changes to files. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when cm or path is NULL. * @return WOLFSSL_FATAL_ERROR when enabling CRLs fails. */ int wolfSSL_CertManagerLoadCRL(WOLFSSL_CERT_MANAGER* cm, const char* path, int type, int monitor) { int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_CertManagerLoadCRL"); /* Validate parameters. */ if ((cm == NULL) || (path == NULL)) { ret = BAD_FUNC_ARG; } /* Create a CRL object if not available. */ if ((ret == WOLFSSL_SUCCESS) && (cm->crl == NULL) && (wolfSSL_CertManagerEnableCRL(cm, WOLFSSL_CRL_CHECK) != WOLFSSL_SUCCESS)) { WOLFSSL_MSG("Enable CRL failed"); ret = WOLFSSL_FATAL_ERROR; } if (ret == WOLFSSL_SUCCESS) { /* Load CRLs from path into CRL object of certificate manager. */ ret = LoadCRL(cm->crl, path, type, monitor); } return ret; } /* Load CRL from file. * * @param [in] cm Certificate manager. * @param [in] file Path to a directory containing CRLs. * @param [in] type Format of encoding. Valid values: * WOLFSSL_FILETYPE_ASN1, WOLFSSL_FILETYPE_PEM. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when cm or file is NULL. * @return WOLFSSL_FATAL_ERROR when enabling CRLs fails. */ int wolfSSL_CertManagerLoadCRLFile(WOLFSSL_CERT_MANAGER* cm, const char* file, int type) { int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_CertManagerLoadCRLFile"); /* Validate parameters. */ if ((cm == NULL) || (file == NULL)) { ret = BAD_FUNC_ARG; } /* Create a CRL object if not available. */ if ((ret == WOLFSSL_SUCCESS) && (cm->crl == NULL) && (wolfSSL_CertManagerEnableCRL(cm, WOLFSSL_CRL_CHECK) != WOLFSSL_SUCCESS)) { WOLFSSL_MSG("Enable CRL failed"); ret = WOLFSSL_FATAL_ERROR; } if (ret == WOLFSSL_SUCCESS) { /* Load CRL file into CRL object of certificate manager. */ ret = ProcessFile(NULL, file, type, CRL_TYPE, NULL, 0, cm->crl, VERIFY); } return ret; } #endif /* !NO_FILESYSTEM */ #endif /* HAVE_CRL */ /******************************************************************************* * OCSP handling ******************************************************************************/ /* Enables OCSP when validating certificates and sets options. * * @param [in] cm Certificate manager. * @param [in] options Options for using OCSP. Valid flags: * WOLFSSL_OCSP_URL_OVERRIDE, WOLFSSL_OCSP_NO_NONCE, * WOLFSSL_OCSP_CHECKALL. * @return WOLFSSL_SUCCESS on success. * @return 0 when initializing the OCSP object fails. * @return BAD_FUNC_ARG when cm is NULL. * @return MEMORY_E when dynamic memory allocation fails. * @return NOT_COMPILED_IN when the OCSP feature is disabled. */ int wolfSSL_CertManagerEnableOCSP(WOLFSSL_CERT_MANAGER* cm, int options) { int ret = WOLFSSL_SUCCESS; (void)options; WOLFSSL_ENTER("wolfSSL_CertManagerEnableOCSP"); /* Validate parameters. */ if (cm == NULL) { ret = BAD_FUNC_ARG; } #ifndef HAVE_OCSP if (ret == WOLFSSL_SUCCESS) { /* OCSP feature not enabled. */ ret = NOT_COMPILED_IN; } #else if (ret == WOLFSSL_SUCCESS) { /* Check whether OCSP object is available. */ if (cm->ocsp == NULL) { /* Allocate memory for OCSP object. */ cm->ocsp = (WOLFSSL_OCSP*)XMALLOC(sizeof(WOLFSSL_OCSP), cm->heap, DYNAMIC_TYPE_OCSP); if (cm->ocsp == NULL) { ret = MEMORY_E; } if (ret == WOLFSSL_SUCCESS) { /* Reset the fields of the OCSP object. */ XMEMSET(cm->ocsp, 0, sizeof(WOLFSSL_OCSP)); /* Initialize the OCSP object. */ if (InitOCSP(cm->ocsp, cm) != 0) { WOLFSSL_MSG("Init OCSP failed"); /* Dispose of OCSP object - indicating dynamically * allocated. */ FreeOCSP(cm->ocsp, 1); cm->ocsp = NULL; ret = 0; } } } } if (ret == WOLFSSL_SUCCESS) { /* Enable OCSP checking. */ cm->ocspEnabled = 1; /* Enable URL override if requested. */ if (options & WOLFSSL_OCSP_URL_OVERRIDE) { cm->ocspUseOverrideURL = 1; } /* Set nonce option for creating OCSP requests. */ cm->ocspSendNonce = (options & WOLFSSL_OCSP_NO_NONCE) != WOLFSSL_OCSP_NO_NONCE; /* Set all OCSP checks on if requested. */ if (options & WOLFSSL_OCSP_CHECKALL) { cm->ocspCheckAll = 1; } #ifndef WOLFSSL_USER_IO /* Set built-in OCSP lookup. */ cm->ocspIOCb = EmbedOcspLookup; cm->ocspRespFreeCb = EmbedOcspRespFree; cm->ocspIOCtx = cm->heap; #endif /* WOLFSSL_USER_IO */ } #endif /* HAVE_OCSP */ return ret; } /* Disables the OCSP checks. * * @param [in] cm Certificate manager. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when cm is NULL. */ int wolfSSL_CertManagerDisableOCSP(WOLFSSL_CERT_MANAGER* cm) { int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_CertManagerDisableOCSP"); /* Validate parameter. */ if (cm == NULL) { ret = BAD_FUNC_ARG; } if (ret == WOLFSSL_SUCCESS) { /* Disable use of OCSP with certificate validation. */ cm->ocspEnabled = 0; } return ret; } /* Enables OCSP stapling with certificates in manager. * * @param [in] cm Certificate manager. * @param [in] options Options for using OCSP. Valid flags: * WOLFSSL_OCSP_URL_OVERRIDE, WOLFSSL_OCSP_NO_NONCE, * WOLFSSL_OCSP_CHECKALL. * @return WOLFSSL_SUCCESS on success. * @return 0 when initializing the OCSP stapling object fails. * @return BAD_FUNC_ARG when cm is NULL. * @return MEMORY_E when dynamic memory allocation fails. * @return NOT_COMPILED_IN when the OCSP stapling feature is disabled. */ int wolfSSL_CertManagerEnableOCSPStapling(WOLFSSL_CERT_MANAGER* cm) { int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_CertManagerEnableOCSPStapling"); /* Validate parameters. */ if (cm == NULL) { ret = BAD_FUNC_ARG; } #if !defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \ !defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) if (ret == WOLFSSL_SUCCESS) { /* OCSP stapling feature not enabled. */ ret = NOT_COMPILED_IN; } #else #ifndef NO_WOLFSSL_SERVER if (ret == WOLFSSL_SUCCESS) { /* Check whether OCSP object is available. */ if (cm->ocsp_stapling == NULL) { /* Allocate memory for OCSP stapling object. */ cm->ocsp_stapling = (WOLFSSL_OCSP*)XMALLOC(sizeof(WOLFSSL_OCSP), cm->heap, DYNAMIC_TYPE_OCSP); if (cm->ocsp_stapling == NULL) { ret = MEMORY_E; } if (ret == WOLFSSL_SUCCESS) { /* Reset the fields of the OCSP object. */ XMEMSET(cm->ocsp_stapling, 0, sizeof(WOLFSSL_OCSP)); /* Initialize the OCSP stapling object. */ if (InitOCSP(cm->ocsp_stapling, cm) != 0) { WOLFSSL_MSG("Init OCSP failed"); /* Dispose of OCSP stapling object - indicating dynamically * allocated. */ FreeOCSP(cm->ocsp_stapling, 1); cm->ocsp_stapling = NULL; ret = 0; } } } } #ifndef WOLFSSL_USER_IO if (ret == WOLFSSL_SUCCESS) { /* Set built-in OCSP lookup. */ cm->ocspIOCb = EmbedOcspLookup; cm->ocspRespFreeCb = EmbedOcspRespFree; cm->ocspIOCtx = cm->heap; } #endif /* WOLFSSL_USER_IO */ #endif /* NO_WOLFSSL_SERVER */ if (ret == WOLFSSL_SUCCESS) { /* Enable OCSP stapling. */ cm->ocspStaplingEnabled = 1; } #endif /* HAVE_CERTIFICATE_STATUS_REQUEST || * HAVE_CERTIFICATE_STATUS_REQUEST_V2 */ return ret; } /* Disables OCSP Stapling. * * @param [in] cm Certificate manager. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when cm is NULL. */ int wolfSSL_CertManagerDisableOCSPStapling(WOLFSSL_CERT_MANAGER* cm) { int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_CertManagerDisableOCSPStapling"); /* Validate parameter. */ if (cm == NULL) { ret = BAD_FUNC_ARG; } if (ret == WOLFSSL_SUCCESS) { #if defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \ defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) /* Disable use of OCSP Stapling. */ cm->ocspStaplingEnabled = 0; #else /* OCSP stapling feature not enabled. */ ret = NOT_COMPILED_IN; #endif } return ret; } /* Enable the must use OCSP Stapling option. * * @param [in] cm Certificate manager. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when cm is NULL. */ int wolfSSL_CertManagerEnableOCSPMustStaple(WOLFSSL_CERT_MANAGER* cm) { int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_CertManagerEnableOCSPMustStaple"); /* Validate parameter. */ if (cm == NULL) { ret = BAD_FUNC_ARG; } if (ret == WOLFSSL_SUCCESS) { #if defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \ defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) #ifndef NO_WOLFSSL_CLIENT /* Enable must use OCSP Stapling option. */ cm->ocspMustStaple = 1; #endif #else /* OCSP stapling feature not enabled. */ ret = NOT_COMPILED_IN; #endif } return ret; } /* Disable the must use OCSP Stapling option. * * @param [in] cm Certificate manager. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when cm is NULL. */ int wolfSSL_CertManagerDisableOCSPMustStaple(WOLFSSL_CERT_MANAGER* cm) { int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_CertManagerDisableOCSPMustStaple"); /* Validate parameter. */ if (cm == NULL) { ret = BAD_FUNC_ARG; } if (ret == WOLFSSL_SUCCESS) { #if defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \ defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) #ifndef NO_WOLFSSL_CLIENT /* Disable must use OCSP Stapling option. */ cm->ocspMustStaple = 0; #endif #else /* OCSP stapling feature not enabled. */ ret = NOT_COMPILED_IN; #endif } return ret; } #ifdef HAVE_OCSP /* Check DER encoded certificate against with OCSP if checking enabled. * * @param [in] cm Certificate manager. * @param [in] der DER encode certificate. * @param [in] sz Size in bytes of DER encode certificate. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when cm or der is NULL or sz is negative or 0. * @return MEMORY_E when dynamic memory allocation fails. */ int wolfSSL_CertManagerCheckOCSP(WOLFSSL_CERT_MANAGER* cm, const unsigned char* der, int sz) { int ret = 0; WC_DECLARE_VAR(cert, DecodedCert, 1, 0); WOLFSSL_ENTER("wolfSSL_CertManagerCheckOCSP"); /* Validate parameters. */ if ((cm == NULL) || (der == NULL) || (sz <= 0)) { ret = BAD_FUNC_ARG; } /* Check if OCSP checking enabled. */ if ((ret == 0) && cm->ocspEnabled) { /* Allocate memory for decoded certificate. */ WC_ALLOC_VAR_EX(cert, DecodedCert, 1, cm->heap, DYNAMIC_TYPE_DCERT, ret=MEMORY_E); if (WC_VAR_OK(cert)) { /* Initialize decoded certificate with buffer. */ InitDecodedCert(cert, der, (word32)sz, NULL); /* Parse certificate and perform CRL checks. */ ret = ParseCertRelative(cert, CERT_TYPE, VERIFY_OCSP, cm, NULL); if (ret != 0) { WOLFSSL_MSG("ParseCert failed"); } /* Do OCSP checks with decoded certificate. */ else if ((ret = CheckCertOCSP(cm->ocsp, cert)) != 0) { WOLFSSL_MSG("CheckCertOCSP failed"); } /* Dispose of dynamically allocated memory. */ FreeDecodedCert(cert); WC_FREE_VAR_EX(cert, cm->heap, DYNAMIC_TYPE_DCERT); } } return (ret == 0) ? WOLFSSL_SUCCESS : ret; } /* Check OCSP response. * * @param [in] cm Certificate manager. * @param [in] response Buffer holding OCSP response. * @param [in] responseSz Size in bytes of OCSP response. * @param [in] responseBuffer Buffer to copy response into. * @param [in] status Place to store certificate status. * @param [in] entry Place to store OCSP entry. * @param [in] ocspRequest OCSP request to match with response. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when cm or response is NULL. */ int wolfSSL_CertManagerCheckOCSPResponse(WOLFSSL_CERT_MANAGER *cm, byte *response, int responseSz, buffer *responseBuffer, CertStatus *status, OcspEntry *entry, OcspRequest *ocspRequest) { int ret = 0; WOLFSSL_ENTER("wolfSSL_CertManagerCheckOCSPResponse"); /* Validate parameters. */ if ((cm == NULL) || (response == NULL)) { ret = BAD_FUNC_ARG; } if ((ret == 0) && cm->ocspEnabled) { /* Check OCSP response with OCSP object from certificate manager. */ ret = CheckOcspResponse(cm->ocsp, response, responseSz, responseBuffer, status, entry, ocspRequest, NULL); } return (ret == 0) ? WOLFSSL_SUCCESS : ret; } /* Set the OCSP override URL. * * @param [in] cm Certificate manager. * @param [in] url URL to get an OCSP response from. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when cm is NULL. * @return MEMORY_E when dynamic memory allocation fails. */ int wolfSSL_CertManagerSetOCSPOverrideURL(WOLFSSL_CERT_MANAGER* cm, const char* url) { int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_CertManagerSetOCSPOverrideURL"); /* Validate parameters. */ if (cm == NULL) { ret = BAD_FUNC_ARG; } if (ret == WOLFSSL_SUCCESS) { /* Dispose of old URL. */ XFREE(cm->ocspOverrideURL, cm->heap, DYNAMIC_TYPE_URL); if (url != NULL) { /* Calculate size of URL string. Include terminator character. */ int urlSz = (int)XSTRLEN(url) + 1; /* Allocate memory for URL to be copied into. */ cm->ocspOverrideURL = (char*)XMALLOC((size_t)urlSz, cm->heap, DYNAMIC_TYPE_URL); if (cm->ocspOverrideURL == NULL) { ret = MEMORY_E; } if (ret == WOLFSSL_SUCCESS) { /* Copy URL into certificate manager. */ XMEMCPY(cm->ocspOverrideURL, url, (size_t)urlSz); } } else { /* No URL to set so make it NULL. */ cm->ocspOverrideURL = NULL; } } return ret; } /* Set the OCSP I/O callback, OCSP response free callback and related data. * * @param [in] cm Certificate manager. * @param [in] ioCb OCSP callback. * @param [in] respFreeCb Callback to free OCSP response buffer. * @param [in] ioCbCtx Context data to pass to OCSP callbacks. * @return WOLFSSL_SUCCESS on success. * @return BAD_FUNC_ARG when cm is NULL. */ int wolfSSL_CertManagerSetOCSP_Cb(WOLFSSL_CERT_MANAGER* cm, CbOCSPIO ioCb, CbOCSPRespFree respFreeCb, void* ioCbCtx) { int ret = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_CertManagerSetOCSP_Cb"); /* Validate parameters. */ if (cm == NULL) { ret = BAD_FUNC_ARG; } if (ret == WOLFSSL_SUCCESS) { /* Set callbacks and data into certificate manager. */ cm->ocspIOCb = ioCb; cm->ocspRespFreeCb = respFreeCb; cm->ocspIOCtx = ioCbCtx; } return ret; } #endif /* HAVE_OCSP */ /****************************************************************************** * Internal APIs that use WOLFSSL_CERT_MANAGER ******************************************************************************/ /* hash is the SHA digest of name, just use first 32 bits as hash */ static WC_INLINE word32 HashSigner(const byte* hash) { return MakeWordFromHash(hash) % CA_TABLE_SIZE; } /* does CA already exist on signer list */ int AlreadySigner(WOLFSSL_CERT_MANAGER* cm, byte* hash) { Signer* signers; int ret = 0; word32 row; if (cm == NULL || hash == NULL) { return ret; } row = HashSigner(hash); if (wc_LockMutex(&cm->caLock) != 0) { return ret; } signers = cm->caTable[row]; while (signers) { byte* subjectHash; #ifndef NO_SKID subjectHash = signers->subjectKeyIdHash; #else subjectHash = signers->subjectNameHash; #endif if (XMEMCMP(hash, subjectHash, SIGNER_DIGEST_SIZE) == 0) { ret = 1; /* success */ break; } signers = signers->next; } wc_UnLockMutex(&cm->caLock); return ret; } #ifdef WOLFSSL_TRUST_PEER_CERT /* hash is the SHA digest of name, just use first 32 bits as hash */ static WC_INLINE word32 TrustedPeerHashSigner(const byte* hash) { return MakeWordFromHash(hash) % TP_TABLE_SIZE; } /* does trusted peer already exist on signer list */ int AlreadyTrustedPeer(WOLFSSL_CERT_MANAGER* cm, DecodedCert* cert) { TrustedPeerCert* tp; int ret = 0; word32 row = TrustedPeerHashSigner(cert->subjectHash); if (wc_LockMutex(&cm->tpLock) != 0) return ret; tp = cm->tpTable[row]; while (tp) { if ((XMEMCMP(cert->subjectHash, tp->subjectNameHash, SIGNER_DIGEST_SIZE) == 0) #ifndef WOLFSSL_NO_ISSUERHASH_TDPEER && (XMEMCMP(cert->issuerHash, tp->issuerHash, SIGNER_DIGEST_SIZE) == 0) #endif ) ret = 1; #ifndef NO_SKID if (cert->extSubjKeyIdSet) { /* Compare SKID as well if available */ if (ret == 1 && XMEMCMP(cert->extSubjKeyId, tp->subjectKeyIdHash, SIGNER_DIGEST_SIZE) != 0) ret = 0; } #endif if (ret == 1) break; tp = tp->next; } wc_UnLockMutex(&cm->tpLock); return ret; } /* return Trusted Peer if found, otherwise NULL type is what to match on */ TrustedPeerCert* GetTrustedPeer(void* vp, DecodedCert* cert) { WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)vp; TrustedPeerCert* ret = NULL; TrustedPeerCert* tp = NULL; word32 row; if (cm == NULL || cert == NULL) return NULL; row = TrustedPeerHashSigner(cert->subjectHash); if (wc_LockMutex(&cm->tpLock) != 0) return ret; tp = cm->tpTable[row]; while (tp) { if ((XMEMCMP(cert->subjectHash, tp->subjectNameHash, SIGNER_DIGEST_SIZE) == 0) #ifndef WOLFSSL_NO_ISSUERHASH_TDPEER && (XMEMCMP(cert->issuerHash, tp->issuerHash, SIGNER_DIGEST_SIZE) == 0) #endif ) ret = tp; #ifndef NO_SKID if (cert->extSubjKeyIdSet) { /* Compare SKID as well if available */ if (ret != NULL && XMEMCMP(cert->extSubjKeyId, tp->subjectKeyIdHash, SIGNER_DIGEST_SIZE) != 0) ret = NULL; } #endif if (ret != NULL) break; tp = tp->next; } wc_UnLockMutex(&cm->tpLock); return ret; } int MatchTrustedPeer(TrustedPeerCert* tp, DecodedCert* cert) { if (tp == NULL || cert == NULL) return BAD_FUNC_ARG; /* subject key id or subject hash has been compared when searching tpTable for the cert from function GetTrustedPeer */ /* compare signatures */ if (tp->sigLen == cert->sigLength) { if (XMEMCMP(tp->sig, cert->signature, cert->sigLength)) { return WOLFSSL_FAILURE; } } else { return WOLFSSL_FAILURE; } return WOLFSSL_SUCCESS; } #endif /* WOLFSSL_TRUST_PEER_CERT */ /* return CA if found, otherwise NULL */ Signer* GetCA(void* vp, byte* hash) { WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)vp; Signer* ret = NULL; Signer* signers; word32 row = 0; if (cm == NULL || hash == NULL) return NULL; row = HashSigner(hash); if (wc_LockMutex(&cm->caLock) != 0) return ret; signers = cm->caTable[row]; while (signers) { byte* subjectHash; #ifndef NO_SKID subjectHash = signers->subjectKeyIdHash; #else subjectHash = signers->subjectNameHash; #endif if (XMEMCMP(hash, subjectHash, SIGNER_DIGEST_SIZE) == 0) { ret = signers; break; } signers = signers->next; } wc_UnLockMutex(&cm->caLock); return ret; } #if defined(HAVE_OCSP) Signer* GetCAByKeyHash(void* vp, const byte* keyHash) { WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)vp; Signer* ret = NULL; Signer* signers; int row; if (cm == NULL || keyHash == NULL) return NULL; /* try lookup using keyHash as subjKeyID first */ ret = GetCA(vp, (byte*)keyHash); if (ret != NULL && XMEMCMP(ret->subjectKeyHash, keyHash, KEYID_SIZE) == 0) { return ret; } /* if we can't find the cert, we have to scan the full table */ if (wc_LockMutex(&cm->caLock) != 0) return NULL; /* Unfortunately we need to look through the entire table */ for (row = 0; row < CA_TABLE_SIZE && ret == NULL; row++) { for (signers = cm->caTable[row]; signers != NULL; signers = signers->next) { if (XMEMCMP(signers->subjectKeyHash, keyHash, KEYID_SIZE) == 0) { ret = signers; break; } } } wc_UnLockMutex(&cm->caLock); return ret; } #endif #ifdef WOLFSSL_AKID_NAME Signer* GetCAByAKID(void* vp, const byte* issuer, word32 issuerSz, const byte* serial, word32 serialSz) { WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)vp; Signer* ret = NULL; Signer* signers; byte nameHash[SIGNER_DIGEST_SIZE]; byte serialHash[SIGNER_DIGEST_SIZE]; word32 row; if (cm == NULL || issuer == NULL || issuerSz == 0 || serial == NULL || serialSz == 0) return NULL; if (CalcHashId(issuer, issuerSz, nameHash) != 0 || CalcHashId(serial, serialSz, serialHash) != 0) return NULL; if (wc_LockMutex(&cm->caLock) != 0) return ret; /* Unfortunately we need to look through the entire table */ for (row = 0; row < CA_TABLE_SIZE && ret == NULL; row++) { for (signers = cm->caTable[row]; signers != NULL; signers = signers->next) { if (XMEMCMP(signers->issuerNameHash, nameHash, SIGNER_DIGEST_SIZE) == 0 && XMEMCMP(signers->serialHash, serialHash, SIGNER_DIGEST_SIZE) == 0) { ret = signers; break; } } } wc_UnLockMutex(&cm->caLock); return ret; } #endif #ifndef NO_SKID /* return CA if found, otherwise NULL. Walk through hash table. */ Signer* GetCAByName(void* vp, byte* hash) { WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)vp; Signer* ret = NULL; Signer* signers; word32 row; if (cm == NULL) return NULL; if (wc_LockMutex(&cm->caLock) != 0) return ret; for (row = 0; row < CA_TABLE_SIZE && ret == NULL; row++) { signers = cm->caTable[row]; while (signers && ret == NULL) { if (XMEMCMP(hash, signers->subjectNameHash, SIGNER_DIGEST_SIZE) == 0) { ret = signers; } signers = signers->next; } } wc_UnLockMutex(&cm->caLock); return ret; } #endif #ifdef WOLFSSL_TRUST_PEER_CERT /* add a trusted peer cert to linked list */ int AddTrustedPeer(WOLFSSL_CERT_MANAGER* cm, DerBuffer** pDer, int verify) { int ret = 0; int row = 0; TrustedPeerCert* peerCert; DecodedCert* cert; DerBuffer* der = *pDer; WOLFSSL_MSG("Adding a Trusted Peer Cert"); cert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), cm->heap, DYNAMIC_TYPE_DCERT); if (cert == NULL) { FreeDer(&der); return MEMORY_E; } InitDecodedCert(cert, der->buffer, der->length, cm->heap); if ((ret = ParseCert(cert, TRUSTED_PEER_TYPE, verify, cm)) != 0) { FreeDecodedCert(cert); XFREE(cert, cm->heap, DYNAMIC_TYPE_DCERT); FreeDer(&der); return ret; } WOLFSSL_MSG("\tParsed new trusted peer cert"); peerCert = (TrustedPeerCert*)XMALLOC(sizeof(TrustedPeerCert), cm->heap, DYNAMIC_TYPE_CERT); if (peerCert == NULL) { FreeDecodedCert(cert); XFREE(cert, cm->heap, DYNAMIC_TYPE_DCERT); FreeDer(&der); return MEMORY_E; } XMEMSET(peerCert, 0, sizeof(TrustedPeerCert)); if (AlreadyTrustedPeer(cm, cert)) { WOLFSSL_MSG("\tAlready have this CA, not adding again"); FreeTrustedPeer(peerCert, cm->heap); (void)ret; } else { /* add trusted peer signature */ peerCert->sigLen = cert->sigLength; peerCert->sig = (byte *)XMALLOC(cert->sigLength, cm->heap, DYNAMIC_TYPE_SIGNATURE); if (peerCert->sig == NULL) { FreeDecodedCert(cert); XFREE(cert, cm->heap, DYNAMIC_TYPE_DCERT); FreeTrustedPeer(peerCert, cm->heap); FreeDer(&der); return MEMORY_E; } XMEMCPY(peerCert->sig, cert->signature, cert->sigLength); /* add trusted peer name */ peerCert->nameLen = cert->subjectCNLen; peerCert->name = cert->subjectCN; #ifndef IGNORE_NAME_CONSTRAINTS peerCert->permittedNames = cert->permittedNames; peerCert->excludedNames = cert->excludedNames; #endif /* add SKID when available and hash of name */ #ifndef NO_SKID XMEMCPY(peerCert->subjectKeyIdHash, cert->extSubjKeyId, SIGNER_DIGEST_SIZE); #endif XMEMCPY(peerCert->subjectNameHash, cert->subjectHash, SIGNER_DIGEST_SIZE); #ifndef WOLFSSL_NO_ISSUERHASH_TDPEER XMEMCPY(peerCert->issuerHash, cert->issuerHash, SIGNER_DIGEST_SIZE); #endif /* If Key Usage not set, all uses valid. */ peerCert->next = NULL; cert->subjectCN = 0; #ifndef IGNORE_NAME_CONSTRAINTS cert->permittedNames = NULL; cert->excludedNames = NULL; #endif row = (int)TrustedPeerHashSigner(peerCert->subjectNameHash); if (wc_LockMutex(&cm->tpLock) == 0) { peerCert->next = cm->tpTable[row]; cm->tpTable[row] = peerCert; /* takes ownership */ wc_UnLockMutex(&cm->tpLock); } else { WOLFSSL_MSG("\tTrusted Peer Cert Mutex Lock failed"); FreeDecodedCert(cert); XFREE(cert, cm->heap, DYNAMIC_TYPE_DCERT); FreeTrustedPeer(peerCert, cm->heap); FreeDer(&der); return BAD_MUTEX_E; } } WOLFSSL_MSG("\tFreeing parsed trusted peer cert"); FreeDecodedCert(cert); XFREE(cert, cm->heap, DYNAMIC_TYPE_DCERT); WOLFSSL_MSG("\tFreeing der trusted peer cert"); FreeDer(&der); WOLFSSL_MSG("\t\tOK Freeing der trusted peer cert"); WOLFSSL_LEAVE("AddTrustedPeer", ret); return WOLFSSL_SUCCESS; } #endif /* WOLFSSL_TRUST_PEER_CERT */ int AddSigner(WOLFSSL_CERT_MANAGER* cm, Signer *s) { byte* subjectHash; Signer* signers; word32 row; if (cm == NULL || s == NULL) return BAD_FUNC_ARG; #ifndef NO_SKID subjectHash = s->subjectKeyIdHash; #else subjectHash = s->subjectNameHash; #endif if (AlreadySigner(cm, subjectHash)) { FreeSigner(s, cm->heap); return 0; } row = HashSigner(subjectHash); if (wc_LockMutex(&cm->caLock) != 0) return BAD_MUTEX_E; signers = cm->caTable[row]; s->next = signers; cm->caTable[row] = s; wc_UnLockMutex(&cm->caLock); return 0; } /* owns der, internal now uses too */ /* type flag ids from user or from chain received during verify don't allow chain ones to be added w/o isCA extension */ int AddCA(WOLFSSL_CERT_MANAGER* cm, DerBuffer** pDer, int type, int verify) { int ret = 0; Signer* signer = NULL; word32 row = 0; byte* subjectHash = NULL; WC_DECLARE_VAR(cert, DecodedCert, 1, 0); DerBuffer* der = *pDer; WOLFSSL_MSG_CERT_LOG("Adding a CA"); if (cm == NULL) { FreeDer(pDer); return BAD_FUNC_ARG; } #ifdef WOLFSSL_SMALL_STACK cert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), NULL, DYNAMIC_TYPE_DCERT); if (cert == NULL) { FreeDer(pDer); return MEMORY_E; } #endif InitDecodedCert(cert, der->buffer, der->length, cm->heap); #ifdef WC_ASN_UNKNOWN_EXT_CB if (cm->unknownExtCallback != NULL) { wc_SetUnknownExtCallback(cert, cm->unknownExtCallback); } #endif WOLFSSL_MSG_CERT("\tParsing new CA"); ret = ParseCert(cert, CA_TYPE, verify, cm); WOLFSSL_MSG("\tParsed new CA"); #ifdef WOLFSSL_DEBUG_CERTS { const char* err_msg; if (ret == 0) { WOLFSSL_MSG_CERT_EX(WOLFSSL_MSG_CERT_INDENT "issuer: '%s'", cert->issuer); WOLFSSL_MSG_CERT_EX(WOLFSSL_MSG_CERT_INDENT "subject: '%s'", cert->subject); } else { WOLFSSL_MSG_CERT( WOLFSSL_MSG_CERT_INDENT "Failed during parse of new CA"); err_msg = wc_GetErrorString(ret); WOLFSSL_MSG_CERT_EX(WOLFSSL_MSG_CERT_INDENT "error ret: %d; %s", ret, err_msg); } } #endif /* WOLFSSL_DEBUG_CERTS */ #ifndef NO_SKID subjectHash = cert->extSubjKeyId; #else subjectHash = cert->subjectHash; #endif /* check CA key size */ if (verify && (ret == 0 )) { switch (cert->keyOID) { #ifndef NO_RSA #ifdef WC_RSA_PSS case RSAPSSk: #endif case RSAk: if (cm->minRsaKeySz < 0 || cert->pubKeySize < (word16)cm->minRsaKeySz) { ret = RSA_KEY_SIZE_E; WOLFSSL_MSG_CERT_LOG("\tCA RSA key size error"); WOLFSSL_MSG_CERT_EX("\tCA RSA pubKeySize = %d; " "minRsaKeySz = %d", cert->pubKeySize, cm->minRsaKeySz); } break; #endif /* !NO_RSA */ #ifdef HAVE_ECC case ECDSAk: if (cm->minEccKeySz < 0 || cert->pubKeySize < (word16)cm->minEccKeySz) { ret = ECC_KEY_SIZE_E; WOLFSSL_MSG_CERT_LOG("\tCA ECC key size error"); WOLFSSL_MSG_CERT_EX("\tCA ECC pubKeySize = %d; " "minEccKeySz = %d", cert->pubKeySize, cm->minEccKeySz); } break; #endif /* HAVE_ECC */ #ifdef HAVE_ED25519 case ED25519k: if (cm->minEccKeySz < 0 || ED25519_KEY_SIZE < (word16)cm->minEccKeySz) { ret = ECC_KEY_SIZE_E; WOLFSSL_MSG("\tCA ECC key size error"); } break; #endif /* HAVE_ED25519 */ #ifdef HAVE_ED448 case ED448k: if (cm->minEccKeySz < 0 || ED448_KEY_SIZE < (word16)cm->minEccKeySz) { ret = ECC_KEY_SIZE_E; WOLFSSL_MSG("\tCA ECC key size error"); } break; #endif /* HAVE_ED448 */ #if defined(HAVE_FALCON) case FALCON_LEVEL1k: if (cm->minFalconKeySz < 0 || FALCON_LEVEL1_KEY_SIZE < (word16)cm->minFalconKeySz) { ret = FALCON_KEY_SIZE_E; WOLFSSL_MSG("\tCA Falcon level 1 key size error"); } break; case FALCON_LEVEL5k: if (cm->minFalconKeySz < 0 || FALCON_LEVEL5_KEY_SIZE < (word16)cm->minFalconKeySz) { ret = FALCON_KEY_SIZE_E; WOLFSSL_MSG("\tCA Falcon level 5 key size error"); } break; #endif /* HAVE_FALCON */ #if defined(HAVE_DILITHIUM) #ifdef WOLFSSL_DILITHIUM_FIPS204_DRAFT case DILITHIUM_LEVEL2k: if (cm->minDilithiumKeySz < 0 || DILITHIUM_LEVEL2_KEY_SIZE < (word16)cm->minDilithiumKeySz) { ret = DILITHIUM_KEY_SIZE_E; WOLFSSL_MSG("\tCA Dilithium level 2 key size error"); } break; case DILITHIUM_LEVEL3k: if (cm->minDilithiumKeySz < 0 || DILITHIUM_LEVEL3_KEY_SIZE < (word16)cm->minDilithiumKeySz) { ret = DILITHIUM_KEY_SIZE_E; WOLFSSL_MSG("\tCA Dilithium level 3 key size error"); } break; case DILITHIUM_LEVEL5k: if (cm->minDilithiumKeySz < 0 || DILITHIUM_LEVEL5_KEY_SIZE < (word16)cm->minDilithiumKeySz) { ret = DILITHIUM_KEY_SIZE_E; WOLFSSL_MSG("\tCA Dilithium level 5 key size error"); } break; #endif /* WOLFSSL_DILITHIUM_FIPS204_DRAFT */ case ML_DSA_LEVEL2k: if (cm->minDilithiumKeySz < 0 || ML_DSA_LEVEL2_KEY_SIZE < (word16)cm->minDilithiumKeySz) { ret = DILITHIUM_KEY_SIZE_E; WOLFSSL_MSG("\tCA Dilithium level 2 key size error"); } break; case ML_DSA_LEVEL3k: if (cm->minDilithiumKeySz < 0 || ML_DSA_LEVEL3_KEY_SIZE < (word16)cm->minDilithiumKeySz) { ret = DILITHIUM_KEY_SIZE_E; WOLFSSL_MSG("\tCA Dilithium level 3 key size error"); } break; case ML_DSA_LEVEL5k: if (cm->minDilithiumKeySz < 0 || ML_DSA_LEVEL5_KEY_SIZE < (word16)cm->minDilithiumKeySz) { ret = DILITHIUM_KEY_SIZE_E; WOLFSSL_MSG("\tCA Dilithium level 5 key size error"); } break; #endif /* HAVE_DILITHIUM */ default: WOLFSSL_MSG("\tNo key size check done on CA"); break; /* no size check if key type is not in switch */ } } if (ret == 0 && cert->isCA == 0 && type != WOLFSSL_USER_CA && type != WOLFSSL_TEMP_CA) { WOLFSSL_MSG("\tCan't add as CA if not actually one"); ret = NOT_CA_ERROR; } #ifndef ALLOW_INVALID_CERTSIGN else if (ret == 0 && cert->isCA == 1 && type != WOLFSSL_USER_CA && type != WOLFSSL_TEMP_CA && !cert->selfSigned && (cert->extKeyUsage & KEYUSE_KEY_CERT_SIGN) == 0) { /* Intermediate CA certs are required to have the keyCertSign * extension set. User loaded root certs are not. */ WOLFSSL_MSG("\tDoesn't have key usage certificate signing"); ret = NOT_CA_ERROR; } #endif else if (ret == 0 && AlreadySigner(cm, subjectHash)) { WOLFSSL_MSG("\tAlready have this CA, not adding again"); (void)ret; } else if (ret == 0) { /* take over signer parts */ signer = MakeSigner(cm->heap); if (!signer) ret = MEMORY_ERROR; } if (ret == 0 && signer != NULL) { ret = FillSigner(signer, cert, type, der); if (ret == 0){ #ifndef NO_SKID row = HashSigner(signer->subjectKeyIdHash); #else row = HashSigner(signer->subjectNameHash); #endif } #if defined(WOLFSSL_RENESAS_TSIP_TLS) || defined(WOLFSSL_RENESAS_FSPSM_TLS) /* Verify CA by TSIP so that generated tsip key is going to */ /* be able to be used for peer's cert verification */ /* TSIP is only able to handle USER CA, and only one CA. */ /* Therefore, it doesn't need to call TSIP again if there is already */ /* verified CA. */ if ( ret == 0 && signer != NULL ) { signer->cm_idx = row; if (type == WOLFSSL_USER_CA) { if ((ret = wc_Renesas_cmn_RootCertVerify(cert->source, cert->maxIdx, cert->sigCtx.CertAtt.pubkey_n_start, cert->sigCtx.CertAtt.pubkey_n_len - 1, cert->sigCtx.CertAtt.pubkey_e_start, cert->sigCtx.CertAtt.pubkey_e_len - 1, row/* cm index */)) < 0) WOLFSSL_MSG("Renesas_RootCertVerify() failed"); else WOLFSSL_MSG("Renesas_RootCertVerify() succeed or skipped"); } } #endif /* TSIP or SCE */ if (ret == 0 && wc_LockMutex(&cm->caLock) == 0) { signer->next = cm->caTable[row]; cm->caTable[row] = signer; /* takes ownership */ wc_UnLockMutex(&cm->caLock); if (cm->caCacheCallback) cm->caCacheCallback(der->buffer, (int)der->length, type); } else { WOLFSSL_MSG("\tCA Mutex Lock failed"); ret = BAD_MUTEX_E; } } WOLFSSL_MSG("\tFreeing Parsed CA"); FreeDecodedCert(cert); if (ret != 0 && signer != NULL) FreeSigner(signer, cm->heap); WC_FREE_VAR_EX(cert, NULL, DYNAMIC_TYPE_DCERT); WOLFSSL_MSG("\tFreeing der CA"); FreeDer(pDer); WOLFSSL_MSG("\t\tOK Freeing der CA"); WOLFSSL_LEAVE("AddCA", ret); return ret == 0 ? WOLFSSL_SUCCESS : ret; } /* Removes the CA with the passed in subject hash from the cert manager's CA cert store. */ int RemoveCA(WOLFSSL_CERT_MANAGER* cm, byte* hash, int type) { Signer* current; Signer** prev; int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE); word32 row; WOLFSSL_MSG("Removing a CA"); if (cm == NULL || hash == NULL) { return BAD_FUNC_ARG; } row = HashSigner(hash); if (wc_LockMutex(&cm->caLock) != 0) { return BAD_MUTEX_E; } current = cm->caTable[row]; prev = &cm->caTable[row]; while (current) { byte* subjectHash; #ifndef NO_SKID subjectHash = current->subjectKeyIdHash; #else subjectHash = current->subjectNameHash; #endif if ((current->type == type) && (XMEMCMP(hash, subjectHash, SIGNER_DIGEST_SIZE) == 0)) { *prev = current->next; FreeSigner(current, cm->heap); ret = WOLFSSL_SUCCESS; break; } prev = ¤t->next; current = current->next; } wc_UnLockMutex(&cm->caLock); WOLFSSL_LEAVE("RemoveCA", ret); return ret; } /* Sets the CA with the passed in subject hash to the provided type. */ int SetCAType(WOLFSSL_CERT_MANAGER* cm, byte* hash, int type) { Signer* current; int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE); word32 row; WOLFSSL_MSG_EX("Setting CA to type %d", type); if (cm == NULL || hash == NULL || type < WOLFSSL_USER_CA || type > WOLFSSL_USER_INTER) { return ret; } row = HashSigner(hash); if (wc_LockMutex(&cm->caLock) != 0) { return ret; } current = cm->caTable[row]; while (current) { byte* subjectHash; #ifndef NO_SKID subjectHash = current->subjectKeyIdHash; #else subjectHash = current->subjectNameHash; #endif if (XMEMCMP(hash, subjectHash, SIGNER_DIGEST_SIZE) == 0) { current->type = (byte)type; ret = WOLFSSL_SUCCESS; break; } current = current->next; } wc_UnLockMutex(&cm->caLock); WOLFSSL_LEAVE("SetCAType", ret); return ret; } #endif /* NO_CERTS */ #endif /* !WOLFSSL_SSL_CERTMAN_INCLUDED */