From 025dbc3454b47e29dd3227703dc6855e0268c60a Mon Sep 17 00:00:00 2001 From: Kareem Date: Wed, 16 Apr 2025 16:16:32 -0700 Subject: [PATCH] Retry all certificates passed into wolfSSL_X509_verify_cert until a valid chain is found, rather than failing out on the first invalid chain. This allows for registering multiple certs with the same subject key, ie. alt cert chains. --- src/ssl.c | 47 +++++++++++++++++++++++++++++++++++ src/x509_str.c | 62 +++++++++++++++++++++++++++++++++++++++++++--- wolfssl/internal.h | 1 + 3 files changed, 107 insertions(+), 3 deletions(-) diff --git a/src/ssl.c b/src/ssl.c index 766ad47cd7..ea567919e3 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -6142,6 +6142,53 @@ int AddCA(WOLFSSL_CERT_MANAGER* cm, DerBuffer** pDer, int type, int verify) 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) +{ + Signer* current; + Signer* prev; + int ret = 0; + word32 row; + + if (cm == NULL || hash == NULL) { + return ret; + } + + row = HashSigner(hash); + + if (wc_LockMutex(&cm->caLock) != 0) { + return ret; + } + current = cm->caTable[row]; + prev = current; + while (current) { + byte* subjectHash; + + #ifndef NO_SKID + subjectHash = current->subjectKeyIdHash; + #else + subjectHash = current->subjectNameHash; + #endif + + if (XMEMCMP(hash, subjectHash, SIGNER_DIGEST_SIZE) == 0) { + if (current == cm->caTable[row]) { + cm->caTable[row] = cm->caTable[row]->next; + } + else { + prev->next = current->next; + } + FreeSigner(current, cm->heap); + ret = 1; + break; + } + prev = current; + current = current->next; + } + wc_UnLockMutex(&cm->caLock); + + return ret; +} + #endif /* !NO_CERTS */ diff --git a/src/x509_str.c b/src/x509_str.c index 3c31f455a1..45c5f56510 100644 --- a/src/x509_str.c +++ b/src/x509_str.c @@ -34,6 +34,8 @@ #ifdef OPENSSL_EXTRA static int X509StoreGetIssuerEx(WOLFSSL_X509 **issuer, WOLFSSL_STACK *certs, WOLFSSL_X509 *x); +static int X509StorePopCert(WOLFSSL_STACK *certs_stack, WOLFSSL_STACK *dest_stack, + WOLFSSL_X509 *cert); static int X509StoreAddCa(WOLFSSL_X509_STORE* store, WOLFSSL_X509* x509, int type); #endif @@ -415,10 +417,12 @@ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx) int i = 0; int numInterAdd = 0; int depth = 0; + int origDepth = 0; WOLFSSL_X509 *issuer = NULL; WOLFSSL_X509 *orig = NULL; WOLF_STACK_OF(WOLFSSL_X509)* certs = NULL; WOLF_STACK_OF(WOLFSSL_X509)* certsToUse = NULL; + WOLF_STACK_OF(WOLFSSL_X509)* failedCerts = NULL; WOLFSSL_ENTER("wolfSSL_X509_verify_cert"); if (ctx == NULL || ctx->store == NULL || ctx->store->cm == NULL @@ -427,6 +431,10 @@ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx) } certs = ctx->store->certs; + if (certs == NULL) { + return WOLFSSL_FATAL_ERROR; + } + if (ctx->setTrustedSk != NULL) { certs = ctx->setTrustedSk; } @@ -450,6 +458,10 @@ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx) } ctx->chain = wolfSSL_sk_X509_new_null(); + failedCerts = wolfSSL_sk_X509_new_null(); + if (!failedCerts) + return WOLFSSL_FATAL_ERROR; + if (ctx->depth > 0) { depth = ctx->depth + 1; } @@ -458,6 +470,7 @@ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx) } orig = ctx->current_cert; + origDepth = depth; while(done == 0 && depth > 0) { issuer = NULL; @@ -491,12 +504,14 @@ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx) ret = X509StoreAddCa(ctx->store, issuer, WOLFSSL_TEMP_CA); if (ret != WOLFSSL_SUCCESS) { - goto exit; + goto retry; } added = 1; ret = X509StoreVerifyCert(ctx); if (ret != WOLFSSL_SUCCESS) { - goto exit; + if ((origDepth - depth) <= 1) + added = 0; + goto retry; } /* Add it to the current chain and look at the issuer cert next */ wolfSSL_sk_X509_push(ctx->chain, ctx->current_cert); @@ -515,8 +530,9 @@ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx) (added == 1)) { wolfSSL_sk_X509_push(ctx->chain, ctx->current_cert); ret = WOLFSSL_SUCCESS; + } else { + goto retry; } - goto exit; } /* Cert verified, finish building the chain */ @@ -548,6 +564,19 @@ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx) } depth--; + continue; + +retry: + /* Current certificate failed, but it is possible there is an alternative + * cert with the same subject key which will work. Retry until all + * possible candidate certs are exhausted. */ + WOLFSSL_MSG("X509_verify_cert current cert failed, retrying with other certs."); + RemoveCA(ctx->store->cm, ctx->current_cert->subjKeyId); + X509StorePopCert(certs, failedCerts, ctx->current_cert); + if (numInterAdd > 0) + numInterAdd--; + ctx->current_cert = wolfSSL_sk_X509_pop(ctx->chain); + depth++; } exit: @@ -572,6 +601,17 @@ exit: wolfSSL_sk_X509_free(certsToUse); } + /* Copy back failed certs if verification failed. */ + if (ret != WOLFSSL_SUCCESS) { + while (wolfSSL_sk_X509_num(failedCerts) > 0) + { + wolfSSL_sk_X509_push(certs, wolfSSL_sk_X509_pop(failedCerts)); + } + } + if (failedCerts) { + wolfSSL_sk_X509_free(failedCerts); + } + return ret == WOLFSSL_SUCCESS ? WOLFSSL_SUCCESS : WOLFSSL_FAILURE; } @@ -1057,6 +1097,22 @@ static int X509StoreGetIssuerEx(WOLFSSL_X509 **issuer, return WOLFSSL_FAILURE; } +static int X509StorePopCert(WOLFSSL_STACK *certs_stack, WOLFSSL_STACK *dest_stack, WOLFSSL_X509 *cert) { + int i; + + if (certs_stack == NULL || dest_stack == NULL || cert == NULL) + return WOLFSSL_FATAL_ERROR; + + for (i = 0; i < wolfSSL_sk_X509_num(certs_stack); i++) { + if (wolfSSL_sk_X509_value(certs_stack, i) == cert) { + wolfSSL_sk_X509_push(dest_stack, wolfSSL_sk_pop_node(certs_stack, i)); + return WOLFSSL_SUCCESS; + } + } + + return WOLFSSL_FAILURE; +} + #endif /****************************************************************************** diff --git a/wolfssl/internal.h b/wolfssl/internal.h index c5beafa607..f17162b9a9 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -4276,6 +4276,7 @@ int ProcessOldClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, WOLFSSL_LOCAL int AddSigner(WOLFSSL_CERT_MANAGER* cm, Signer *s); WOLFSSL_LOCAL int AddCA(WOLFSSL_CERT_MANAGER* cm, DerBuffer** pDer, int type, int verify); + WOLFSSL_LOCAL int RemoveCA(WOLFSSL_CERT_MANAGER* cm, byte* hash); WOLFSSL_LOCAL int AlreadySigner(WOLFSSL_CERT_MANAGER* cm, byte* hash); #ifdef WOLFSSL_TRUST_PEER_CERT