diff --git a/src/internal.c b/src/internal.c index dce0ff717..fd42cdc4b 100755 --- a/src/internal.c +++ b/src/internal.c @@ -7477,8 +7477,23 @@ int CheckHostName(DecodedCert* dCert, char *domainName, size_t domainNameLen) } #endif -#if defined(KEEP_PEER_CERT) || defined(SESSION_CERTS) +#ifdef SESSION_CERTS +static void AddSessionCertToChain(WOLFSSL_X509_CHAIN* chain, + byte* certBuf, word32 certSz) +{ + if (chain->count < MAX_CHAIN_DEPTH && + certSz < MAX_X509_SIZE) { + chain->certs[chain->count].length = certSz; + XMEMCPY(chain->certs[chain->count].buffer, certBuf, certSz); + chain->count++; + } + else { + WOLFSSL_MSG("Couldn't store chain cert for session"); + } +} +#endif +#if defined(KEEP_PEER_CERT) || defined(SESSION_CERTS) /* Copy parts X509 needs from Decoded cert, 0 on success */ int CopyDecodedToX509(WOLFSSL_X509* x509, DecodedCert* dCert) { @@ -7723,6 +7738,9 @@ typedef struct ProcPeerCertArgs { int certIdx; int fatal; int lastErr; +#ifdef WOLFSSL_ALT_CERT_CHAINS + int lastCaErr; +#endif #ifdef WOLFSSL_TLS13 byte ctxSz; #endif @@ -7776,6 +7794,7 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, ProcPeerCertArgs args[1]; #endif + buffer* cert; #ifdef WOLFSSL_TRUST_PEER_CERT byte haveTrustPeer = 0; /* was cert verified by loaded trusted peer cert */ #endif @@ -7927,18 +7946,8 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, args->certs[args->totalCerts].buffer = input + args->idx; #ifdef SESSION_CERTS - if (ssl->session.chain.count < MAX_CHAIN_DEPTH && - certSz < MAX_X509_SIZE) { - ssl->session.chain.certs[ - ssl->session.chain.count].length = certSz; - XMEMCPY(ssl->session.chain.certs[ - ssl->session.chain.count].buffer, - input + args->idx, certSz); - ssl->session.chain.count++; - } - else { - WOLFSSL_MSG("Couldn't store chain cert for session"); - } + AddSessionCertToChain(&ssl->session.chain, + input + args->idx, certSz); #endif /* SESSION_CERTS */ args->idx += certSz; @@ -7991,10 +8000,11 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, and CA test */ TrustedPeerCert* tp; + cert = &args->certs[args->certIdx]; + if (!args->dCertInit) { InitDecodedCert(args->dCert, - args->certs[args->certIdx].buffer, - args->certs[args->certIdx].length, ssl->heap); + cert->buffer, cert->length, ssl->heap); args->dCert->sigCtx.devId = ssl->devId; /* setup async dev */ #ifdef WOLFSSL_ASYNC_CRYPT args->dCert->sigCtx.asyncCtx = ssl; @@ -8058,11 +8068,11 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, #endif if (args->certIdx == 0) { byte* subjectHash; + cert = &args->certs[args->certIdx]; if (!args->dCertInit) { InitDecodedCert(args->dCert, - args->certs[args->certIdx].buffer, - args->certs[args->certIdx].length, ssl->heap); + cert->buffer, cert->length, ssl->heap); args->dCert->sigCtx.devId = ssl->devId; #ifdef WOLFSSL_ASYNC_CRYPT args->dCert->sigCtx.asyncCtx = ssl; @@ -8102,14 +8112,14 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, && !haveTrustPeer #endif /* WOLFSSL_TRUST_PEER_CERT */ ) { - byte* subjectHash; + byte *subjectHash; args->certIdx = args->count - 1; + cert = &args->certs[args->certIdx]; if (!args->dCertInit) { InitDecodedCert(args->dCert, - args->certs[args->certIdx].buffer, - args->certs[args->certIdx].length, ssl->heap); + cert->buffer, cert->length, ssl->heap); args->dCert->sigCtx.devId = ssl->devId; /* setup async dev */ #ifdef WOLFSSL_ASYNC_CRYPT args->dCert->sigCtx.asyncCtx = ssl; @@ -8186,16 +8196,13 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, } else if (ret == 0 && !AlreadySigner(ssl->ctx->cm, subjectHash)) { DerBuffer* add = NULL; - ret = AllocDer(&add, args->certs[args->certIdx].length, - CA_TYPE, ssl->heap); + ret = AllocDer(&add, cert->length, CA_TYPE, ssl->heap); if (ret < 0) goto exit_ppc; WOLFSSL_MSG("Adding CA from chain"); - XMEMCPY(add->buffer, args->certs[args->certIdx].buffer, - args->certs[args->certIdx].length); - + XMEMCPY(add->buffer, cert->buffer, cert->length); #ifdef WOLFSSL_NGINX if (args->certIdx > args->untrustedDepth) @@ -8207,9 +8214,35 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, if (ret == 1) { ret = 0; /* SSL_SUCCESS for external */ } + + #ifdef WOLFSSL_ALT_CERT_CHAINS + /* if the previous CA cert failed, clear last error */ + if (args->lastCaErr != 0) { + WOLFSSL_MSG("Using alternate cert chain"); + ssl->options.usingAltCertChain = 1; + + /* clear last CA fail since CA cert was validated */ + args->lastCaErr = 0; + + #ifdef SESSION_CERTS + AddSessionCertToChain(&ssl->session.altChain, + cert->buffer, cert->length); + #endif /* SESSION_CERTS */ + } + #endif } else if (ret != 0) { WOLFSSL_MSG("Failed to verify CA from chain"); + #ifdef WOLFSSL_ALT_CERT_CHAINS + if (args->lastCaErr == 0) { + /* store CA error and proceed to next cert */ + args->lastCaErr = ret; + ret = 0; + } + else { + args->lastErr = args->lastCaErr; + } + #endif #ifdef OPENSSL_EXTRA ssl->peerVerifyRet = X509_V_ERR_INVALID_CA; #endif @@ -8297,11 +8330,11 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, WOLFSSL_MSG("Verifying Peer's cert"); args->certIdx = 0; + cert = &args->certs[args->certIdx]; if (!args->dCertInit) { InitDecodedCert(args->dCert, - args->certs[args->certIdx].buffer, - args->certs[args->certIdx].length, ssl->heap); + cert->buffer, cert->length, ssl->heap); args->dCert->sigCtx.devId = ssl->devId; /* setup async dev */ #ifdef WOLFSSL_ASYNC_CRYPT args->dCert->sigCtx.asyncCtx = ssl; @@ -8330,6 +8363,12 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, #ifdef OPENSSL_EXTRA ssl->peerVerifyRet = X509_V_OK; #endif + #if defined(SESSION_CERTS) && defined(WOLFSSL_ALT_CERT_CHAINS) + if (ssl->options.usingAltCertChain) { + AddSessionCertToChain(&ssl->session.altChain, + cert->buffer, cert->length); + } + #endif /* SESSION_CERTS && WOLFSSL_ALT_CERT_CHAINS */ args->fatal = 0; } else if (ret == ASN_PARSE_E || ret == BUFFER_E) { @@ -8802,6 +8841,9 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, if (store->discardSessionCerts) { WOLFSSL_MSG("Verify callback requested discard sess certs"); ssl->session.chain.count = 0; + #ifdef WOLFSSL_ALT_CERT_CHAINS + ssl->session.altChain.count = 0; + #endif } #endif /* SESSION_CERTS */ } @@ -8847,6 +8889,9 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, if (store->discardSessionCerts) { WOLFSSL_MSG("Verify callback requested discard sess certs"); ssl->session.chain.count = 0; + #ifdef WOLFSSL_ALT_CERT_CHAINS + ssl->session.altChain.count = 0; + #endif } #endif /* SESSION_CERTS */ } diff --git a/src/ssl.c b/src/ssl.c index 620b9c52f..99309405d 100755 --- a/src/ssl.c +++ b/src/ssl.c @@ -22832,8 +22832,31 @@ int wolfSSL_EC_KEY_LoadDer(WOLFSSL_EC_KEY* key, #endif /* OPENSSL_EXTRA */ +#ifdef WOLFSSL_ALT_CERT_CHAINS +int wolfSSL_is_peer_alt_cert_chain(const WOLFSSL* ssl) +{ + int isUsing = 0; + if (ssl) + isUsing = ssl->options.usingAltCertChain; + return isUsing; +} +#endif /* WOLFSSL_ALT_CERT_CHAINS */ + + #ifdef SESSION_CERTS +#ifdef WOLFSSL_ALT_CERT_CHAINS +/* Get peer's alternate certificate chain */ +WOLFSSL_X509_CHAIN* wolfSSL_get_peer_alt_chain(WOLFSSL* ssl) +{ + WOLFSSL_ENTER("wolfSSL_get_peer_alt_chain"); + if (ssl) + return &ssl->session.altChain; + + return 0; +} +#endif /* WOLFSSL_ALT_CERT_CHAINS */ + /* Get peer's certificate chain */ WOLFSSL_X509_CHAIN* wolfSSL_get_peer_chain(WOLFSSL* ssl) diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 24521897e..11b19e900 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -42,6 +42,8 @@ ASN Options: Only enabled for OCSP. * WOLFSSL_NO_OCSP_ISSUER_CHECK: Can be defined for backwards compatibility to disable checking of OCSP subject hash with issuer hash. + * WOLFSSL_ALT_CERT_CHAINS: Allows matching multiple CA's to validate + chain based on issuer and public key (includes signature confirmation) */ #ifndef NO_ASN @@ -6069,6 +6071,25 @@ Signer* GetCAByName(void* signers, byte* hash) #endif /* WOLFCRYPT_ONLY || NO_CERTS */ +#if (defined(WOLFSSL_ALT_CERT_CHAINS) || \ + defined(WOLFSSL_NO_TRUSTED_CERTS_VERIFY)) && !defined(NO_SKID) +static Signer* GetCABySubjectAndPubKey(DecodedCert* cert, void* cm) +{ + Signer* ca = NULL; + if (cert->extSubjKeyIdSet) + ca = GetCA(cm, cert->extSubjKeyId); + if (ca == NULL) + ca = GetCAByName(cm, cert->subjectHash); + if (ca) { + if ((ca->pubKeySize == cert->pubKeySize) && + (XMEMCMP(ca->publicKey, cert->publicKey, ca->pubKeySize) == 0)) { + return ca; + } + } + return NULL; +} +#endif + int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm) { int ret = 0; @@ -6147,25 +6168,22 @@ int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm) if (cert->ca == NULL) cert->ca = GetCAByName(cm, cert->issuerHash); - /* alternate lookup method using subject and match on public key */ + /* OCSP Only: alt lookup using subject and pub key w/o sig check */ #ifdef WOLFSSL_NO_TRUSTED_CERTS_VERIFY if (cert->ca == NULL && verify == VERIFY_OCSP) { - if (cert->extSubjKeyIdSet) { - cert->ca = GetCA(cm, cert->extSubjKeyId); - } - if (cert->ca == NULL) { - cert->ca = GetCAByName(cm, cert->subjectHash); - } + cert->ca = GetCABySubjectAndPubKey(cert, cm); if (cert->ca) { - if ((cert->ca->pubKeySize == cert->pubKeySize) && - (XMEMCMP(cert->ca->publicKey, cert->publicKey, - cert->ca->pubKeySize) == 0)) { - ret = 0; /* success */ - goto exit_pcr; - } + ret = 0; /* success */ + goto exit_pcr; } } #endif /* WOLFSSL_NO_TRUSTED_CERTS_VERIFY */ + + /* alt lookup using subject and public key */ + #ifdef WOLFSSL_ALT_CERT_CHAINS + if (cert->ca == NULL) + cert->ca = GetCABySubjectAndPubKey(cert, cm); + #endif #else cert->ca = GetCA(cm, cert->issuerHash); #endif /* !NO_SKID */ @@ -6191,10 +6209,10 @@ int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm) /* Need the CA's public key hash for OCSP */ #ifdef NO_SHA ret = wc_Sha256Hash(cert->ca->publicKey, cert->ca->pubKeySize, - cert->issuerKeyHash); + cert->issuerKeyHash); #else ret = wc_ShaHash(cert->ca->publicKey, cert->ca->pubKeySize, - cert->issuerKeyHash); + cert->issuerKeyHash); #endif /* NO_SHA */ if (ret != 0) return ret; @@ -6235,7 +6253,7 @@ int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm) } } -#ifdef WOLFSSL_NO_TRUSTED_CERTS_VERIFY +#if defined(WOLFSSL_NO_TRUSTED_CERTS_VERIFY) && !defined(NO_SKID) exit_pcr: #endif diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 9818ebda7..1aeddd0c3 100755 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -2640,6 +2640,9 @@ struct WOLFSSL_SESSION { word16 haveEMS; /* ext master secret flag */ #ifdef SESSION_CERTS WOLFSSL_X509_CHAIN chain; /* peer cert chain, static */ + #ifdef WOLFSSL_ALT_CERT_CHAINS + WOLFSSL_X509_CHAIN altChain; /* peer alt cert chain, static */ + #endif #endif #if defined(SESSION_CERTS) || (defined(WOLFSSL_TLS13) && \ defined(HAVE_SESSION_TICKET)) @@ -2809,14 +2812,14 @@ typedef struct Options { wc_psk_client_callback client_psk_cb; wc_psk_server_callback server_psk_cb; #endif /* NO_PSK */ -#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) - word16 havePSK:1; /* psk key set by user */ -#endif /* HAVE_SESSION_TICKET || !NO_PSK */ #ifdef OPENSSL_EXTRA unsigned long mask; /* store SSL_OP_ flags */ #endif /* on/off or small bit flags, optimize layout */ +#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) + word16 havePSK:1; /* psk key set by user */ +#endif /* HAVE_SESSION_TICKET || !NO_PSK */ word16 sendVerify:2; /* false = 0, true = 1, sendBlank = 2 */ word16 sessionCacheOff:1; word16 sessionCacheFlushOff:1; @@ -2895,6 +2898,9 @@ typedef struct Options { #if defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_SERVER) word16 sendCookie:1; /* Server creates a Cookie in HRR */ #endif +#ifdef WOLFSSL_ALT_CERT_CHAINS + word16 usingAltCertChain:1;/* Alternate cert chain was used */ +#endif /* need full byte values for this section */ byte processReply; /* nonblocking resume */ diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 5f5bf56ce..60adb3514 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -1193,6 +1193,11 @@ WOLFSSL_API int wolfSSL_CTX_set_timeout(WOLFSSL_CTX*, unsigned int); /* get wolfSSL peer X509_CHAIN */ WOLFSSL_API WOLFSSL_X509_CHAIN* wolfSSL_get_peer_chain(WOLFSSL* ssl); +#ifdef WOLFSSL_ALT_CERT_CHAINS +WOLFSSL_API int wolfSSL_is_peer_alt_cert_chain(const WOLFSSL* ssl); +/* get wolfSSL alternate peer X509_CHAIN */ +WOLFSSL_API WOLFSSL_X509_CHAIN* wolfSSL_get_peer_alt_chain(WOLFSSL* ssl); +#endif /* peer chain count */ WOLFSSL_API int wolfSSL_get_chain_count(WOLFSSL_X509_CHAIN* chain); /* index cert length */ @@ -1410,7 +1415,7 @@ WOLFSSL_API WC_RNG* wolfSSL_GetRNG(WOLFSSL*); WOLFSSL_API int wolfSSL_CTX_SetMinVersion(WOLFSSL_CTX* ctx, int version); WOLFSSL_API int wolfSSL_SetMinVersion(WOLFSSL* ssl, int version); WOLFSSL_API int wolfSSL_GetObjectSize(void); /* object size based on build */ -WOLFSSL_API int wolfSSL_CTX_GetObjectSize(void); +WOLFSSL_API int wolfSSL_CTX_GetObjectSize(void); WOLFSSL_API int wolfSSL_METHOD_GetObjectSize(void); WOLFSSL_API int wolfSSL_GetOutputSize(WOLFSSL*, int); WOLFSSL_API int wolfSSL_GetMaxOutputSize(WOLFSSL*); diff --git a/wolfssl/test.h b/wolfssl/test.h index a5b3961a8..fbdfa6e30 100644 --- a/wolfssl/test.h +++ b/wolfssl/test.h @@ -517,10 +517,32 @@ static INLINE void ShowX509(WOLFSSL_X509* x509, const char* hdr) #endif /* KEEP_PEER_CERT || SESSION_CERTS */ +#if defined(SESSION_CERTS) && defined(SHOW_CERTS) +static INLINE void ShowX509Chain(WOLFSSL_X509_CHAIN* chain, int count, + const char* hdr) +{ + int i; + int length; + unsigned char buffer[3072]; + WOLFSSL_X509* chainX509; + + for (i = 0; i < count; i++) { + wolfSSL_get_chain_cert_pem(chain, i, buffer, sizeof(buffer), &length); + buffer[length] = 0; + printf("\n%s: %d has length %d data = \n%s\n", hdr, i, length, buffer); + + chainX509 = wolfSSL_get_chain_X509(chain, i); + if (chainX509) + ShowX509(chainX509, hdr); + else + printf("get_chain_X509 failed\n"); + wolfSSL_FreeX509(chainX509); + } +} +#endif static INLINE void showPeer(WOLFSSL* ssl) { - WOLFSSL_CIPHER* cipher; #ifdef HAVE_ECC const char *name; @@ -559,31 +581,26 @@ static INLINE void showPeer(WOLFSSL* ssl) #endif if (wolfSSL_session_reused(ssl)) printf("SSL reused session\n"); +#ifdef WOLFSSL_ALT_CERT_CHAINS + if (wolfSSL_is_peer_alt_cert_chain(ssl)) + printf("Alternate cert chain used\n"); +#endif #if defined(SESSION_CERTS) && defined(SHOW_CERTS) { - WOLFSSL_X509_CHAIN* chain = wolfSSL_get_peer_chain(ssl); - int count = wolfSSL_get_chain_count(chain); - int i; + WOLFSSL_X509_CHAIN* chain; - for (i = 0; i < count; i++) { - int length; - unsigned char buffer[3072]; - WOLFSSL_X509* chainX509; + chain = wolfSSL_get_peer_chain(ssl); + ShowX509Chain(chain, wolfSSL_get_chain_count(chain), "session cert"); - wolfSSL_get_chain_cert_pem(chain,i,buffer, sizeof(buffer), &length); - buffer[length] = 0; - printf("cert %d has length %d data = \n%s\n", i, length, buffer); - - chainX509 = wolfSSL_get_chain_X509(chain, i); - if (chainX509) - ShowX509(chainX509, "session cert info:"); - else - printf("get_chain_X509 failed\n"); - wolfSSL_FreeX509(chainX509); + #ifdef WOLFSSL_ALT_CERT_CHAINS + if (wolfSSL_is_peer_alt_cert_chain(ssl)) { + chain = wolfSSL_get_peer_alt_chain(ssl); + ShowX509Chain(chain, wolfSSL_get_chain_count(chain), "alt cert"); } + #endif } -#endif +#endif /* SESSION_CERTS && SHOW_CERTS */ (void)ssl; } @@ -1234,7 +1251,7 @@ static INLINE int myVerify(int preverify, WOLFSSL_X509_STORE_CTX* store) printf("\tPeer has no cert!\n"); #else printf("\tPeer certs: %d\n", store->totalCerts); - #ifdef VERIFY_CALLBACK_SHOW_PEER_CERTS + #ifdef SHOW_CERTS { int i; for (i=0; itotalCerts; i++) { WOLFSSL_BUFFER_INFO* cert = &store->certs[i];