diff --git a/src/internal.c b/src/internal.c index 1c5196874d2..5ecf2752211 100644 --- a/src/internal.c +++ b/src/internal.c @@ -6182,6 +6182,7 @@ void FreePeerCert(WOLFSSL* ssl) wolfSSL_X509_free(ssl->peerCert); ssl->peerCert = NULL; } + FreeDer(&ssl->peerCertDer); #else FreeX509(&ssl->peerCert); InitX509(&ssl->peerCert, 0, ssl->heap); @@ -16629,6 +16630,116 @@ int CopyDecodedToX509(WOLFSSL_X509* x509, DecodedCert* dCert) return ret; } +#if defined(KEEP_PEER_CERT) && !defined(NO_ASN) +/* Make the certificate in dCert the kept peer certificate, keeping only its + * encoding when the X509 object can be built from it later. + * + * With WOLFSSL_DYNAMIC_PEER_CERT, GetPeerCert() builds the X509 object from + * the encoding on first use, so a connection whose application never asks for + * it does not hold the decoded object, its names and their entries. Otherwise + * the object is filled in now, as by SetPeerCert(). + * + * Only for a certificate that was parsed without error, so that decoding it + * again succeeds. Use SetPeerCert() for one accepted despite an error. + * + * @param [in, out] ssl SSL/TLS object. + * @param [in] dCert Decoded peer certificate, with its encoding. + * @return 0 on success. + * @return MEMORY_E when the encoding cannot be stored; no cert is kept. + * @return Other error from SetPeerCert(). + */ +int SetPeerCertDer(WOLFSSL* ssl, DecodedCert* dCert) +{ +#ifdef WOLFSSL_DYNAMIC_PEER_CERT + if ((dCert->source != NULL) && (dCert->maxIdx > 0)) { + FreePeerCert(ssl); + if (AllocCopyDecodedDer(&ssl->peerCertDer, dCert->source, + dCert->maxIdx, dCert->srcIdx, ssl->heap) != 0) { + return MEMORY_E; + } + return 0; + } +#endif + return SetPeerCert(ssl, dCert); +} + +#ifdef WOLFSSL_DYNAMIC_PEER_CERT +/* Build the kept peer certificate object from the kept encoding. + * + * Only the encoding of a certificate that parsed without error is kept, and + * the certificate was verified when it was received, so it is only decoded + * here, as by d2i_X509(). A copy that fails part way is still kept, as by + * SetPeerCert(). When memory runs out the encoding is kept instead, so a + * later call can try again. + * + * @param [in, out] ssl SSL/TLS object with only the encoding kept. + */ +static void BuildPeerCert(WOLFSSL* ssl) +{ + int ret = 0; + WOLFSSL_X509* x509 = NULL; + WC_DECLARE_VAR(dCert, DecodedCert, 1, ssl->heap); + + WC_ALLOC_VAR_EX(dCert, DecodedCert, 1, ssl->heap, DYNAMIC_TYPE_DCERT, + ret = MEMORY_E); + if (ret == 0) { + InitDecodedCert(dCert, ssl->peerCertDer->buffer, + ssl->peerCertDer->length, ssl->heap); + #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) + /* CopyDecodedToX509() adds the name entries to the X509 directly. */ + dCert->noX509Name = 1; + #endif + ret = ParseCertRelative(dCert, CERT_TYPE, NO_VERIFY, NULL, NULL); + if (ret == 0) { + x509 = wolfSSL_X509_new_ex(ssl->heap); + if (x509 == NULL) { + ret = MEMORY_E; + } + } + if (ret == 0) { + ret = CopyDecodedToX509(x509, dCert); + if (ret == WC_NO_ERR_TRACE(MEMORY_E)) { + wolfSSL_X509_free(x509); + x509 = NULL; + } + } + FreeDecodedCert(dCert); + WC_FREE_VAR_EX(dCert, ssl->heap, DYNAMIC_TYPE_DCERT); + } + + if (x509 != NULL) { + /* The object has its own copy of the encoding. */ + FreeDer(&ssl->peerCertDer); + ssl->peerCert = x509; + } + else { + WOLFSSL_MSG("Unable to build peer certificate from its encoding"); + } +} +#endif /* WOLFSSL_DYNAMIC_PEER_CERT */ +#endif /* KEEP_PEER_CERT && !NO_ASN */ + +#if defined(KEEP_PEER_CERT) && defined(WOLFSSL_DYNAMIC_PEER_CERT) +/* Get the kept peer certificate object. + * + * When only the certificate's encoding is kept, the object is built from it + * now and kept in its place. + * + * @param [in, out] ssl SSL/TLS object. + * @return Kept peer certificate object. + * @return NULL when no certificate is kept or the object cannot be built. + */ +WOLFSSL_X509* GetPeerCert(WOLFSSL* ssl) +{ +#ifndef NO_ASN + if ((ssl->peerCert == NULL) && (ssl->peerCertDer != NULL)) { + BuildPeerCert(ssl); + } +#endif + return ssl->peerCert; +} +#endif /* KEEP_PEER_CERT && WOLFSSL_DYNAMIC_PEER_CERT */ + #endif /* KEEP_PEER_CERT || SESSION_CERTS */ #if defined(WOLFSSL_ACERT) @@ -17247,8 +17358,14 @@ int SetupStoreCtxCallback(WOLFSSL_X509_STORE_CTX** store_pt, #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) #ifdef KEEP_PEER_CERT if (args->certIdx == 0) { - /* Copy the leaf once, here or in ProcessPeerCerts(). */ - if (!args->peerCertSet) { + /* Copy the leaf once, here or in ProcessPeerCerts(). When + * ProcessPeerCerts() kept only its encoding, build the object + * from the decoded leaf rather than decoding it again. */ + if (!args->peerCertSet + #ifdef WOLFSSL_DYNAMIC_PEER_CERT + || ((ssl->peerCert == NULL) && args->dCertInit) + #endif + ) { if (SetPeerCert(ssl, args->dCert) == 0) args->peerCertSet = 1; else @@ -17993,6 +18110,13 @@ PRAGMA_GCC_DIAG_POP #endif /* Parse Certificate */ ret = ParseCertRelative(args->dCert, certType, verify, SSL_CM(ssl), extraSigners); +#ifdef KEEP_PEER_CERT + /* An error here can still be overridden and the leaf accepted, while + * decoding it again later fails the same way. */ + if ((certType == CERT_TYPE) && (args->certIdx == 0)) { + args->leafDecodeErr = (ret != 0); + } +#endif #ifdef WOLFSSL_SMALL_CERT_VERIFY /* Self signed: the key that verified the signature is its own */ if (verify == VERIFY_NAME && sigRet == 0 && sigCaKey != NULL && @@ -20698,12 +20822,17 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, #ifdef KEEP_PEER_CERT /* The verify callback may have copied the leaf already, on an - * error. Otherwise it is called later and uses this copy. */ + * error. Otherwise it is called later and uses what is kept + * here. Only a leaf that parsed without error is kept as its + * encoding alone, as only then can it be decoded again. */ if (args->fatal == 0 && !args->peerCertSet) { int copyRet = 0; /* replace old peer cert */ - copyRet = SetPeerCert(ssl, args->dCert); + if (args->leafDecodeErr) + copyRet = SetPeerCert(ssl, args->dCert); + else + copyRet = SetPeerCertDer(ssl, args->dCert); if (copyRet == 0) { args->peerCertSet = 1; } @@ -45469,13 +45598,12 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) if (!ssl->options.dtls) { const byte* certDer = NULL; word32 certDerSz = 0; - WOLFSSL_X509* peer = GetPeerCert(ssl); + const DerBuffer* peerDer = GetPeerCertDer(ssl); - if (peer != NULL && peer->derCert != NULL && - peer->derCert->length > 0) { + if (peerDer != NULL && peerDer->length > 0) { /* Use current peer certificate */ - certDer = peer->derCert->buffer; - certDerSz = peer->derCert->length; + certDer = peerDer->buffer; + certDerSz = peerDer->length; } #ifdef SESSION_CERTS else if (ssl->session->chain.count > 0) { @@ -46011,7 +46139,7 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) AddSessionCertToChain(&ssl->session->chain, it->peerCert, peerCertLen); #endif - ret = SetPeerCert(ssl, dCert); + ret = SetPeerCertDer(ssl, dCert); if (ret != 0) { FreePeerCert(ssl); } diff --git a/src/ssl_api_cert.c b/src/ssl_api_cert.c index 15781db1732..4ceeac270dd 100644 --- a/src/ssl_api_cert.c +++ b/src/ssl_api_cert.c @@ -2227,12 +2227,12 @@ int wolfSSL_cmp_peer_cert_to_file(WOLFSSL* ssl, const char *fname) XFILE file = XBADFILE; long sz = 0; void* heap = ssl->ctx->heap; - WOLFSSL_X509* peer_cert = GetPeerCert(ssl); + const DerBuffer* peerDer = GetPeerCertDer(ssl); DerBuffer* fileDer = NULL; /* handle gracefully if called before handshake, or if peer did not * present a cert. */ - if ((peer_cert == NULL) || (peer_cert->derCert == NULL)) { + if (peerDer == NULL) { WOLFSSL_MSG("wolfSSL_cmp_peer_cert_to_file without peer cert"); ret = WOLFSSL_FATAL_ERROR; } @@ -2262,8 +2262,8 @@ int wolfSSL_cmp_peer_cert_to_file(WOLFSSL* ssl, const char *fname) } /* Peer certificate matches when the DER lengths and bytes are equal. */ if ((ret == 0) && ((fileDer->length == 0) || - (fileDer->length != peer_cert->derCert->length) || - (XMEMCMP(peer_cert->derCert->buffer, fileDer->buffer, + (fileDer->length != peerDer->length) || + (XMEMCMP(peerDer->buffer, fileDer->buffer, fileDer->length) != 0))) { ret = WOLFSSL_FATAL_ERROR; } @@ -3055,8 +3055,9 @@ static int DecodeToX509(WOLFSSL_X509* x509, const byte* in, int len) #ifdef KEEP_PEER_CERT /* Get the kept peer certificate, owned by the SSL/TLS object. * - * The certificate is decoded from the session chain, and kept, when not - * already available on the object. + * The certificate is built from its kept encoding on first use. Otherwise it + * is decoded from the session chain, and kept, when not already available on + * the object. * * @param [in, out] ssl SSL/TLS object. * @return Kept peer certificate on success. @@ -3215,7 +3216,8 @@ static int PushCAx509Chain(WOLFSSL_CERT_MANAGER* cm, * certificate, index 0, is taken from the copy kept on the SSL/TLS object * instead; any other is left out of the stack, as it was before it had a slot. * - * @param [in] ssl SSL/TLS object. + * @param [in, out] ssl SSL/TLS object. The kept peer certificate may be + * built from its encoding. * @param [in] idx Index of the certificate in the chain. * @param [in, out] sk Stack to add the certificate to. * @param [out] pushed Certificate added to the stack, NULL when the slot @@ -3225,7 +3227,7 @@ static int PushCAx509Chain(WOLFSSL_CERT_MANAGER* cm, * @return Other negative value when the certificate cannot be decoded or * stored. */ -static int PushPeerCertToChain(const WOLFSSL* ssl, int idx, WOLFSSL_STACK* sk, +static int PushPeerCertToChain(WOLFSSL* ssl, int idx, WOLFSSL_STACK* sk, WOLFSSL_X509** pushed) { int ret = 0; @@ -3235,9 +3237,10 @@ static int PushPeerCertToChain(const WOLFSSL* ssl, int idx, WOLFSSL_STACK* sk, if (ssl->session->chain.certs[idx].length == 0) { #ifdef KEEP_PEER_CERT - if ((idx == 0) && (GetPeerCert(ssl) != NULL) && - (GetPeerCert(ssl)->issuer.sz > 0)) { - x509 = wolfSSL_X509_dup(GetPeerCert(ssl)); + WOLFSSL_X509* peer = (idx == 0) ? GetPeerCert(ssl) : NULL; + + if ((peer != NULL) && (peer->issuer.sz > 0)) { + x509 = wolfSSL_X509_dup(peer); if (x509 == NULL) { WOLFSSL_MSG("Error duplicating peer certificate"); ret = MEMORY_E; @@ -3290,13 +3293,14 @@ static int PushPeerCertToChain(const WOLFSSL* ssl, int idx, WOLFSSL_STACK* sk, * For a verified chain the CA certificates known for the last certificate are * placed at the bottom of the stack. * - * @param [in] ssl SSL/TLS object. - * @param [in] verifiedFlag Whether to append the known CA chain. + * @param [in, out] ssl SSL/TLS object. The kept peer certificate + * may be built from its encoding. + * @param [in] verifiedFlag Whether to append the known CA chain. * @return Stack of the peer's certificates on success. * @return NULL when ssl is NULL, the session holds no chain, or a certificate * cannot be created, decoded or stored. */ -static WOLF_STACK_OF(WOLFSSL_X509)* CreatePeerCertChain(const WOLFSSL* ssl, +static WOLF_STACK_OF(WOLFSSL_X509)* CreatePeerCertChain(WOLFSSL* ssl, int verifiedFlag) { WOLFSSL_STACK* sk = NULL; diff --git a/tests/api/test_ssl_cert.c b/tests/api/test_ssl_cert.c index 64c29841f44..362f674a44a 100644 --- a/tests/api/test_ssl_cert.c +++ b/tests/api/test_ssl_cert.c @@ -596,8 +596,12 @@ int test_wolfSSL_peer_cert_copy_oom(void) */ static int test_peer_cert_kept(const WOLFSSL* ssl) { - WOLFSSL_X509* peer = GetPeerCert(ssl); - return (peer != NULL) && (peer->issuer.sz > 0); +#ifdef WOLFSSL_DYNAMIC_PEER_CERT + /* The object, or only the encoding until the object is asked for. */ + return (ssl->peerCert != NULL) || (ssl->peerCertDer != NULL); +#else + return ssl->peerCert.issuer.sz > 0; +#endif } #if defined(HAVE_EX_DATA) && defined(HAVE_EX_DATA_CLEANUP_HOOKS) @@ -774,6 +778,355 @@ int test_wolfSSL_get_peer_certificate_ref(void) return EXPECT_RESULT(); } +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \ + defined(WOLFSSL_DYNAMIC_PEER_CERT) && !defined(NO_RSA) && \ + !defined(NO_TLS) +/* Leaf certificate the verify callback was given. */ +static WOLFSSL_X509* peer_on_demand_cb_leaf = NULL; + +/* Record the leaf certificate the verify callback is given and accept it. + * + * @param [in] preverify Result of verification so far. + * @param [in] store Store context with the certificate being checked. + * @return 1 to accept the certificate. + */ +static int peer_on_demand_verify_cb(int preverify, + WOLFSSL_X509_STORE_CTX* store) +{ + (void)preverify; + if (wolfSSL_X509_STORE_CTX_get_error_depth(store) == 0) + peer_on_demand_cb_leaf = wolfSSL_X509_STORE_CTX_get_current_cert(store); + return 1; +} + +/* Give the client a new server to handshake with. + * + * @param [in] ctx_s Server context. + * @param [in, out] ssl_s Server object, replaced by a new one. + * @param [in, out] test_ctx Memory I/O, emptied. + * @return TEST_SUCCESS on success. + */ +static int test_peer_cert_on_demand_new_server(WOLFSSL_CTX* ctx_s, + WOLFSSL** ssl_s, struct test_memio_ctx* test_ctx) +{ + EXPECT_DECLS; + + wolfSSL_free(*ssl_s); + *ssl_s = NULL; + ExpectNotNull(*ssl_s = wolfSSL_new(ctx_s)); + wolfSSL_SetIOWriteCtx(*ssl_s, test_ctx); + wolfSSL_SetIOReadCtx(*ssl_s, test_ctx); +#ifndef NO_DH + SetDH(*ssl_s); +#endif + test_memio_clear_buffer(test_ctx, 0); + test_memio_clear_buffer(test_ctx, 1); + + return EXPECT_RESULT(); +} + +/* Check that a connection keeps only the peer certificate's encoding until + * the object is asked for, and that the object then built is the one sent. + * + * @param [in] method_c Client method. + * @param [in] method_s Server method. + * @return TEST_SUCCESS on success. + */ +static int test_peer_cert_on_demand(method_provider method_c, + method_provider method_s) +{ + EXPECT_DECLS; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + WOLFSSL_X509* srv = NULL; + WOLFSSL_X509* peer = NULL; + const unsigned char* srvDer = NULL; + const unsigned char* der = NULL; + int srvDerSz = 0; + int derSz = 0; + + ExpectNotNull(srv = wolfSSL_X509_load_certificate_file(svrCertFile, + CERT_FILETYPE)); + ExpectNotNull(srvDer = wolfSSL_X509_get_der(srv, &srvDerSz)); + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + method_c, method_s), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* Only the encoding of the certificate the server sent is kept. */ + if (EXPECT_SUCCESS()) { + ExpectNull(ssl_c->peerCert); + ExpectNotNull(ssl_c->peerCertDer); + } + if (EXPECT_SUCCESS()) { + ExpectIntEQ((int)ssl_c->peerCertDer->length, srvDerSz); + ExpectBufEQ(ssl_c->peerCertDer->buffer, srvDer, srvDerSz); + } +#if defined(HAVE_EX_DATA) && !defined(NO_FILESYSTEM) && \ + defined(WOLFSSL_PEM_TO_DER) + /* Comparing with a file uses the encoding and builds nothing. */ + ExpectIntEQ(wolfSSL_cmp_peer_cert_to_file(ssl_c, svrCertFile), 0); + if (EXPECT_SUCCESS()) { + ExpectNull(ssl_c->peerCert); + } +#endif +#if defined(TEST_PEER_CERT_COPY_OOM) && defined(USE_WOLFSSL_MEMORY) + /* Out of memory, no object is returned and the encoding stays. */ + (void)wolfSSL_GetAllocators(&peer_copy_prev_malloc, + &peer_copy_prev_free, &peer_copy_prev_realloc); + ExpectIntEQ(wolfSSL_SetAllocators(peer_copy_fail_malloc, + peer_copy_fail_free, peer_copy_fail_realloc), 0); + peer_copy_fail_alloc = 1; + peer = wolfSSL_get_peer_certificate(ssl_c); + peer_copy_fail_alloc = 0; + (void)wolfSSL_SetAllocators(peer_copy_prev_malloc, peer_copy_prev_free, + peer_copy_prev_realloc); + ExpectNull(peer); + wolfSSL_X509_free(peer); + peer = NULL; + if (EXPECT_SUCCESS()) { + ExpectNull(ssl_c->peerCert); + ExpectNotNull(ssl_c->peerCertDer); + } +#endif + + /* Asking builds the object, which takes the place of the encoding. */ + ExpectNotNull(peer = wolfSSL_get_peer_certificate(ssl_c)); + if (EXPECT_SUCCESS()) { + ExpectPtrEq(peer, ssl_c->peerCert); + ExpectNull(ssl_c->peerCertDer); + } + ExpectNotNull(der = wolfSSL_X509_get_der(peer, &derSz)); + ExpectIntEQ(derSz, srvDerSz); + ExpectBufEQ(der, srvDer, srvDerSz); + ExpectIntEQ(wolfSSL_X509_NAME_cmp(wolfSSL_X509_get_subject_name(peer), + wolfSSL_X509_get_subject_name(srv)), 0); + ExpectIntEQ(wolfSSL_X509_NAME_cmp(wolfSSL_X509_get_issuer_name(peer), + wolfSSL_X509_get_issuer_name(srv)), 0); + ExpectIntEQ(wolfSSL_X509_NAME_entry_count( + wolfSSL_X509_get_subject_name(peer)), + wolfSSL_X509_NAME_entry_count(wolfSSL_X509_get_subject_name(srv))); + ExpectIntEQ(wolfSSL_X509_cmp(peer, srv), 0); + wolfSSL_X509_free(peer); + peer = NULL; + + /* Reusing the object for a new connection drops the built object. */ + ExpectIntEQ(wolfSSL_clear(ssl_c), WOLFSSL_SUCCESS); + if (EXPECT_SUCCESS()) { + ExpectNull(ssl_c->peerCert); + ExpectNull(ssl_c->peerCertDer); + } + + /* It also drops the encoding when no object was built from it. */ + ExpectIntEQ(test_peer_cert_on_demand_new_server(ctx_s, &ssl_s, &test_ctx), + TEST_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + if (EXPECT_SUCCESS()) { + ExpectNull(ssl_c->peerCert); + ExpectNotNull(ssl_c->peerCertDer); + } + ExpectIntEQ(wolfSSL_clear(ssl_c), WOLFSSL_SUCCESS); + if (EXPECT_SUCCESS()) { + ExpectNull(ssl_c->peerCert); + ExpectNull(ssl_c->peerCertDer); + } + + /* With a verify callback the object is built from the decoded leaf for + * the callback, and that object is the one kept. */ + ExpectIntEQ(test_peer_cert_on_demand_new_server(ctx_s, &ssl_s, &test_ctx), + TEST_SUCCESS); + peer_on_demand_cb_leaf = NULL; + wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_PEER, peer_on_demand_verify_cb); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + if (EXPECT_SUCCESS()) { + ExpectNotNull(ssl_c->peerCert); + ExpectNull(ssl_c->peerCertDer); + ExpectPtrEq(peer_on_demand_cb_leaf, ssl_c->peerCert); + } + ExpectNotNull(peer = wolfSSL_get_peer_certificate(ssl_c)); + if (EXPECT_SUCCESS()) { + ExpectPtrEq(peer, ssl_c->peerCert); + } + ExpectIntEQ(wolfSSL_X509_cmp(peer, srv), 0); + peer_on_demand_cb_leaf = NULL; + + wolfSSL_X509_free(peer); + wolfSSL_X509_free(srv); + wolfSSL_free(ssl_s); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_s); + wolfSSL_CTX_free(ctx_c); + + return EXPECT_RESULT(); +} +#endif + +/* Test that only the encoding of the peer's certificate is kept until the + * certificate object is asked for. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_peer_cert_on_demand(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \ + defined(WOLFSSL_DYNAMIC_PEER_CERT) && !defined(NO_RSA) && \ + !defined(NO_TLS) +#ifdef WOLFSSL_TLS13 + ExpectIntEQ(test_peer_cert_on_demand(wolfTLSv1_3_client_method, + wolfTLSv1_3_server_method), TEST_SUCCESS); +#endif +#ifndef WOLFSSL_NO_TLS12 + ExpectIntEQ(test_peer_cert_on_demand(wolfTLSv1_2_client_method, + wolfTLSv1_2_server_method), TEST_SUCCESS); +#endif +#endif + return EXPECT_RESULT(); +} + +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \ + defined(KEEP_PEER_CERT) && defined(WOLFSSL_CHAIN_VERIFY_CB) && \ + !defined(NO_RSA) && !defined(NO_TLS) && !defined(NO_SHA256) && \ + !defined(NO_ASN_TIME) && defined(WOLFSSL_CERT_GEN) && \ + defined(WOLFSSL_CERT_EXT) && defined(USE_CERT_BUFFERS_2048) +#define TEST_PEER_CERT_DECODE_ERR + +/* Accept the peer's chain, whatever wolfSSL makes of its content. + * + * @return 0 to accept the chain. + */ +static int peer_cert_decode_err_chain_cb(WOLFSSL* ssl, + const WOLFSSL_BUFFER_INFO* certs, int certsSz, void* ctx) +{ + (void)ssl; + (void)certs; + (void)certsSz; + (void)ctx; + return 0; +} +#endif + +/* Test that a peer certificate accepted despite an error decoding it is still + * kept as an object. + * + * The leaf has keyCertSign without being a CA, which parsing reports however + * it is done, and the chain verify callback accepts it. Decoding the kept + * encoding again would fail, so the decoded leaf is kept as the object. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_peer_cert_kept_decode_err(void) +{ + EXPECT_DECLS; +#ifdef TEST_PEER_CERT_DECODE_ERR + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + WOLFSSL_X509* peer = NULL; + const unsigned char* der = NULL; + byte* leafDer = NULL; + int leafDerSz = 0; + int derSz = 0; + RsaKey key; + WC_RNG rng; + Cert cert; + word32 idx = 0; + int keyInit = 0; + int rngInit = 0; + int certInit = 0; + + ExpectNotNull(leafDer = (byte*)XMALLOC(FOURK_BUF, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + ExpectIntEQ(wc_InitRng(&rng), 0); + if (EXPECT_SUCCESS()) + rngInit = 1; + ExpectIntEQ(wc_InitRsaKey(&key, NULL), 0); + if (EXPECT_SUCCESS()) + keyInit = 1; + ExpectIntEQ(wc_RsaPrivateKeyDecode(server_key_der_2048, &idx, &key, + (word32)sizeof_server_key_der_2048), 0); + + /* Self-signed leaf with keyCertSign but not a CA. */ + ExpectIntEQ(wc_InitCert(&cert), 0); + if (EXPECT_SUCCESS()) { + certInit = 1; + cert.isCA = 0; + cert.sigType = CTC_SHA256wRSA; + XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE - 1); + XSTRNCPY(cert.subject.org, "wolfSSL_test", CTC_NAME_SIZE - 1); + XSTRNCPY(cert.subject.commonName, "keyCertSign leaf", + CTC_NAME_SIZE - 1); + } + ExpectIntEQ(wc_SetKeyUsage(&cert, + "digitalSignature,keyEncipherment,keyCertSign"), 0); + ExpectIntGT(wc_MakeCert(&cert, leafDer, FOURK_BUF, &key, NULL, &rng), 0); + ExpectIntGT(leafDerSz = wc_SignCert(cert.bodySz, cert.sigType, leafDer, + FOURK_BUF, &key, NULL, &rng), 0); +#ifdef WOLFSSL_CERT_GEN_CACHE + if (certInit) + wc_SetCert_Free(&cert); +#endif + (void)certInit; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); +#ifdef WOLFSSL_TLS13 + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); +#else + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); +#endif + ExpectIntEQ(wolfSSL_use_certificate_buffer(ssl_s, leafDer, leafDerSz, + WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_use_PrivateKey_buffer(ssl_s, server_key_der_2048, + sizeof_server_key_der_2048, WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS); + wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_PEER, NULL); + ExpectIntEQ(wolfSSL_SetChainVerifyCb(ssl_c, + peer_cert_decode_err_chain_cb), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + +#ifdef WOLFSSL_DYNAMIC_PEER_CERT + #ifndef ALLOW_INVALID_CERTSIGN + /* The decoded leaf is kept, not only its encoding. */ + if (EXPECT_SUCCESS()) { + ExpectNotNull(ssl_c->peerCert); + ExpectNull(ssl_c->peerCertDer); + } + #endif + /* The kept object is handed out with a reference. */ + ExpectNotNull(peer = wolfSSL_get_peer_certificate(ssl_c)); + ExpectNotNull(der = wolfSSL_X509_get_der(peer, &derSz)); +#else + /* A copy is handed out, made by decoding the certificate again, which + * fails for this one, so check the kept object itself. */ + if (EXPECT_SUCCESS()) { + ExpectIntGT(ssl_c->peerCert.issuer.sz, 0); + ExpectNotNull(ssl_c->peerCert.derCert); + } + if (EXPECT_SUCCESS()) { + der = ssl_c->peerCert.derCert->buffer; + derSz = (int)ssl_c->peerCert.derCert->length; + } +#endif + ExpectIntEQ(derSz, leafDerSz); + ExpectBufEQ(der, leafDer, leafDerSz); + + wolfSSL_X509_free(peer); + wolfSSL_free(ssl_s); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_s); + wolfSSL_CTX_free(ctx_c); + if (keyInit) + wc_FreeRsaKey(&key); + if (rngInit) + wc_FreeRng(&rng); + XFREE(leafDer, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return EXPECT_RESULT(); +} + /* Guarded to match its only caller, test_wolfSSL_CTX_set_client_cert_cb(), * which needs OPENSSL_EXTRA for the ctx->CBClientCert field. */ #if defined(WOLFSSL_CERT_SETUP_CB) && defined(OPENSSL_EXTRA) && \ diff --git a/tests/api/test_ssl_cert.h b/tests/api/test_ssl_cert.h index 1f802590194..9b99a8036b7 100644 --- a/tests/api/test_ssl_cert.h +++ b/tests/api/test_ssl_cert.h @@ -43,6 +43,8 @@ int test_wolfSSL_get_chain_cert_pem(void); int test_wolfSSL_cmp_peer_cert_to_file(void); int test_wolfSSL_peer_cert_copy_oom(void); int test_wolfSSL_get_peer_certificate_ref(void); +int test_wolfSSL_peer_cert_on_demand(void); +int test_wolfSSL_peer_cert_kept_decode_err(void); int test_wolfSSL_CTX_set_client_cert_cb(void); int test_wolfSSL_CTX_set_cert_cb(void); int test_wolfSSL_cert_setup_cb_ret(void); @@ -92,6 +94,8 @@ int test_wolfSSL_SESSION_get0_peer_full_chain(void); TEST_DECL_GROUP("ssl_cert", test_wolfSSL_cmp_peer_cert_to_file), \ TEST_DECL_GROUP("ssl_cert", test_wolfSSL_peer_cert_copy_oom), \ TEST_DECL_GROUP("ssl_cert", test_wolfSSL_get_peer_certificate_ref), \ + TEST_DECL_GROUP("ssl_cert", test_wolfSSL_peer_cert_on_demand), \ + TEST_DECL_GROUP("ssl_cert", test_wolfSSL_peer_cert_kept_decode_err), \ TEST_DECL_GROUP("ssl_cert", test_wolfSSL_CTX_set_client_cert_cb), \ TEST_DECL_GROUP("ssl_cert", test_wolfSSL_CTX_set_cert_cb), \ TEST_DECL_GROUP("ssl_cert", test_wolfSSL_cert_setup_cb_ret), \ diff --git a/tests/api/test_tls13.c b/tests/api/test_tls13.c index 74982e5655e..be105f451fa 100644 --- a/tests/api/test_tls13.c +++ b/tests/api/test_tls13.c @@ -12945,6 +12945,7 @@ int test_tls13_ticket_peer_cert_reverify(void) * fallback that may see stale cache state. */ #ifdef WOLFSSL_DYNAMIC_PEER_CERT ExpectNull(ssl_s->peerCert); + ExpectNull(ssl_s->peerCertDer); #else ExpectIntEQ(ssl_s->peerCert.issuer.sz, 0); #endif diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 623df4f7569..94d4566ecbc 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -3035,7 +3035,10 @@ typedef struct ProcPeerCertArgs { word16 csrAcked:1; /* survives the staple checks clearing status_request */ #endif #ifdef KEEP_PEER_CERT - word16 peerCertSet:1; /* leaf already copied into ssl->peerCert */ + word16 peerCertSet:1; /* leaf already kept in ssl->peerCert, or only its + * encoding in ssl->peerCertDer */ + word16 leafDecodeErr:1; /* parsing the leaf returned an error, so a later + * decode of it may not give the same result */ #endif } ProcPeerCertArgs; WOLFSSL_LOCAL int DoVerifyCallback(WOLFSSL_CERT_MANAGER* cm, WOLFSSL* ssl, @@ -7319,9 +7322,14 @@ struct WOLFSSL { #endif #ifdef KEEP_PEER_CERT #ifdef WOLFSSL_DYNAMIC_PEER_CERT - WOLFSSL_X509* peerCert; /* X509 peer cert, NULL when none. - Holds one reference; replaced, - never reused, by a new cert */ + WOLFSSL_X509* peerCert; /* X509 peer cert, NULL when none + or not built yet from + peerCertDer. Holds one reference; + replaced, never reused, by a new + cert */ + DerBuffer* peerCertDer; /* Encoding of the peer cert until + peerCert is built from it, then + NULL. See GetPeerCert() */ #else WOLFSSL_X509 peerCert; /* X509 peer cert */ #endif @@ -8218,19 +8226,31 @@ WOLFSSL_LOCAL enum wc_HashType HashAlgoToType(int hashAlgo); DecodedCert* dCert); #endif #ifdef KEEP_PEER_CERT - /* The kept peer certificate object. NULL only with - * WOLFSSL_DYNAMIC_PEER_CERT, when no certificate is kept. */ + #ifdef WOLFSSL_DYNAMIC_PEER_CERT + WOLFSSL_LOCAL WOLFSSL_X509* GetPeerCert(WOLFSSL* ssl); + #else + /* The kept peer certificate object. */ static WC_INLINE WOLFSSL_X509* GetPeerCert(const WOLFSSL* ssl) { - #ifdef WOLFSSL_DYNAMIC_PEER_CERT - return ssl->peerCert; - #else return (WOLFSSL_X509*)&ssl->peerCert; - #endif + } + #endif + /* The encoding of the kept peer certificate, without building the X509 + * object. NULL when no certificate is kept. */ + static WC_INLINE const DerBuffer* GetPeerCertDer(const WOLFSSL* ssl) + { + #ifdef WOLFSSL_DYNAMIC_PEER_CERT + if (ssl->peerCertDer != NULL) + return ssl->peerCertDer; + return (ssl->peerCert != NULL) ? ssl->peerCert->derCert : NULL; + #else + return ssl->peerCert.derCert; + #endif } WOLFSSL_LOCAL void FreePeerCert(WOLFSSL* ssl); #ifndef NO_ASN WOLFSSL_LOCAL int SetPeerCert(WOLFSSL* ssl, DecodedCert* dCert); + WOLFSSL_LOCAL int SetPeerCertDer(WOLFSSL* ssl, DecodedCert* dCert); #endif #endif #endif