diff --git a/src/internal.c b/src/internal.c index 1f62c435246..cd897621c54 100644 --- a/src/internal.c +++ b/src/internal.c @@ -42698,6 +42698,12 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) ssl->options.resuming = 0; ssl->options.peerAuthGood = 0; } + #if defined(HAVE_SNI) || defined(HAVE_ALPN) + else if (VerifyTicketBinding(ssl) != 0) { + WOLFSSL_MSG("Resumed session binding mismatch, full handshake"); + ssl->options.resuming = 0; + } + #endif else { if (MatchSuite(ssl, &clSuites) < 0) { WOLFSSL_MSG("Unsupported cipher suite, OldClientHello"); @@ -42847,32 +42853,13 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) ssl->options.peerAuthGood = 0; return ret; } -#if defined(HAVE_SESSION_TICKET) && \ - (defined(HAVE_SNI) || defined(HAVE_ALPN)) - /* Do not resume session if sniHash/alpnHash do not match. */ - if (!ssl->options.useTicket) { - byte curHash[TICKET_BINDING_HASH_SZ]; -#ifdef HAVE_SNI - if (TicketSniHash(ssl, curHash) != 0 || - XMEMCMP(curHash, session->sniHash, - TICKET_BINDING_HASH_SZ) != 0) { - WOLFSSL_MSG("Resumed session SNI mismatch, full handshake"); - ssl->options.resuming = 0; - return ret; - } -#endif -#ifdef HAVE_ALPN - if (ssl->options.resuming && - (TicketAlpnHash(ssl, curHash) != 0 || - XMEMCMP(curHash, session->alpnHash, - TICKET_BINDING_HASH_SZ) != 0)) { - WOLFSSL_MSG("Resumed session ALPN mismatch, full handshake"); - ssl->options.resuming = 0; - return ret; - } -#endif +#if defined(HAVE_SNI) || defined(HAVE_ALPN) + if (VerifyTicketBinding(ssl) != 0) { + WOLFSSL_MSG("Resumed session binding mismatch, full handshake"); + ssl->options.resuming = 0; + return ret; } -#endif /* HAVE_SESSION_TICKET && (HAVE_SNI || HAVE_ALPN) */ +#endif #if !defined(WOLFSSL_NO_TICKET_EXPIRE) && !defined(NO_ASN_TIME) /* check if the ticket is valid */ @@ -44330,70 +44317,6 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) #endif /* !WOLFSSL_NO_TLS12 */ -#ifdef HAVE_SESSION_TICKET - -#ifdef WOLFSSL_TICKET_HAVE_ID - static void GetRealSessionID(WOLFSSL* ssl, const byte** id, byte* idSz) - { - if (ssl->session->haveAltSessionID) { - *id = ssl->session->altSessionID; - *idSz = ID_LEN; - } - else if (IsAtLeastTLSv1_3(ssl->version)) { - /* In TLS 1.3 ssl->session->sessionID only holds the client's - * legacy_session_id that we echo in the ServerHello. It is chosen - * by the peer and must not be used to identify the session in - * the ticket or the session cache. Report no ID so that - * SetupTicket generates a random one. */ - *id = NULL; - *idSz = 0; - } - else if (ssl->arrays != NULL) { - *id = ssl->arrays->sessionID; - *idSz = ssl->arrays->sessionIDSz; - } - else { - *id = ssl->session->sessionID; - *idSz = ssl->session->sessionIDSz; - } - } -#endif - - int SetupTicket(WOLFSSL* ssl) - { - int ret = 0; - - (void)ssl; - -#ifdef WOLFSSL_TLS13 - { - /* Client adds to ticket age to obfuscate. */ - byte ageAdd[AGEADD_LEN]; /* Obfuscation of age */ - ret = wc_RNG_GenerateBlock(ssl->rng, ageAdd, AGEADD_LEN); - if (ret != 0) - return ret; - ato32(ageAdd, &ssl->session->ticketAdd); - } -#endif - -#ifdef WOLFSSL_TICKET_HAVE_ID - { - const byte* id = NULL; - byte idSz = 0; - - GetRealSessionID(ssl, &id, &idSz); - if (idSz == 0) { - ret = wc_RNG_GenerateBlock(ssl->rng, ssl->session->altSessionID, - ID_LEN); - if (ret != 0) - return ret; - ssl->session->haveAltSessionID = 1; - } - } -#endif - return ret; - } - #ifdef HAVE_SNI /* Hash server-selected SNI; zeros dst when none. */ int TicketSniHash(WOLFSSL* ssl, byte* dst) @@ -44440,27 +44363,10 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) #endif #if defined(HAVE_SNI) || defined(HAVE_ALPN) - /* Server-side TLS 1.2 ticket-resumption binding check. Confirms the - * SNI/ALPN bound to the resumed session matches what was negotiated - * for the current connection. Must be called after extension - * parsing and ALPN_Select so the negotiated values are available, - * and only once DoClientTicketFinalize has populated - * ssl->session->sniHash/alpnHash from the decrypted ticket. - * - * Other resumption paths handle the same check themselves and do - * not use this function: - * - TLS 1.2 session-ID (stateful): HandleTlsResumption compares - * against the cached session at lookup time. - * - TLS 1.3 PSK: DoPreSharedKeys compares against each candidate - * ticket's bound hashes before committing, allowing the server - * to skip mismatching PSKs and pick the next one. - * - * Returns 0 on match, WOLFSSL_FATAL_ERROR on mismatch. The caller - * is responsible for the policy on mismatch -- RFC 6066 Section 3 - * mandates declining the resumption and proceeding with a full - * handshake rather than aborting. */ + /* Returns 0 when SNI/ALPN match ssl->session. Call after ALPN_Select. */ int VerifyTicketBinding(WOLFSSL* ssl) { + int ret = 0; byte curHash[TICKET_BINDING_HASH_SZ]; if (!ssl->options.resuming) @@ -44470,22 +44376,91 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) if (TicketSniHash(ssl, curHash) != 0 || XMEMCMP(curHash, ssl->session->sniHash, TICKET_BINDING_HASH_SZ) != 0) { - WOLFSSL_MSG("Ticket SNI mismatch"); - return WOLFSSL_FATAL_ERROR; + WOLFSSL_MSG("Resumed session SNI mismatch"); + ret = WOLFSSL_FATAL_ERROR; } -#endif +#endif /* HAVE_SNI */ #ifdef HAVE_ALPN - if (TicketAlpnHash(ssl, curHash) != 0 || + if (ret == 0 && (TicketAlpnHash(ssl, curHash) != 0 || XMEMCMP(curHash, ssl->session->alpnHash, - TICKET_BINDING_HASH_SZ) != 0) { - WOLFSSL_MSG("Ticket ALPN mismatch"); - return WOLFSSL_FATAL_ERROR; + TICKET_BINDING_HASH_SZ) != 0)) { + WOLFSSL_MSG("Resumed session ALPN mismatch"); + ret = WOLFSSL_FATAL_ERROR; + } +#endif /* HAVE_ALPN */ +#ifdef SESSION_CERTS + /* Drop the declined session's peer before the full handshake. */ + if (ret != 0) + ResetSessionCertChain(ssl); +#endif /* SESSION_CERTS */ + return ret; + } +#endif /* HAVE_SNI || HAVE_ALPN */ + +#ifdef HAVE_SESSION_TICKET + +#ifdef WOLFSSL_TICKET_HAVE_ID + static void GetRealSessionID(WOLFSSL* ssl, const byte** id, byte* idSz) + { + if (ssl->session->haveAltSessionID) { + *id = ssl->session->altSessionID; + *idSz = ID_LEN; + } + else if (IsAtLeastTLSv1_3(ssl->version)) { + /* In TLS 1.3 ssl->session->sessionID only holds the client's + * legacy_session_id that we echo in the ServerHello. It is chosen + * by the peer and must not be used to identify the session in + * the ticket or the session cache. Report no ID so that + * SetupTicket generates a random one. */ + *id = NULL; + *idSz = 0; + } + else if (ssl->arrays != NULL) { + *id = ssl->arrays->sessionID; + *idSz = ssl->arrays->sessionIDSz; + } + else { + *id = ssl->session->sessionID; + *idSz = ssl->session->sessionIDSz; } -#endif - return 0; } #endif + int SetupTicket(WOLFSSL* ssl) + { + int ret = 0; + + (void)ssl; + +#ifdef WOLFSSL_TLS13 + { + /* Client adds to ticket age to obfuscate. */ + byte ageAdd[AGEADD_LEN]; /* Obfuscation of age */ + ret = wc_RNG_GenerateBlock(ssl->rng, ageAdd, AGEADD_LEN); + if (ret != 0) + return ret; + ato32(ageAdd, &ssl->session->ticketAdd); + } +#endif + +#ifdef WOLFSSL_TICKET_HAVE_ID + { + const byte* id = NULL; + byte idSz = 0; + + GetRealSessionID(ssl, &id, &idSz); + if (idSz == 0) { + ret = wc_RNG_GenerateBlock(ssl->rng, ssl->session->altSessionID, + ID_LEN); + if (ret != 0) + return ret; + ssl->session->haveAltSessionID = 1; + } + } +#endif + return ret; + } + /* Returns 1 when the default ticket encryption callback is in use with a * hint of at least half the key lifetime. */ int DefTicketHintTooLarge(WOLFSSL* ssl) diff --git a/src/ssl_sess.c b/src/ssl_sess.c index 341ccd77afc..55c7dbed0a3 100644 --- a/src/ssl_sess.c +++ b/src/ssl_sess.c @@ -2931,28 +2931,28 @@ int wolfSSL_i2d_SSL_SESSION(WOLFSSL_SESSION* sess, unsigned char** p) #else /* ticketSeen Hi 32 bits | ticketSeen Lo 32 bits | ticketAdd */ size += OPAQUE32_LEN + OPAQUE32_LEN + OPAQUE32_LEN; -#endif +#endif /* WOLFSSL_32BIT_MILLI_TIME */ /* ticketNonce */ size += OPAQUE8_LEN + sess->ticketNonce.len; -#endif +#endif /* WOLFSSL_TLS13 */ #ifdef WOLFSSL_EARLY_DATA size += OPAQUE32_LEN; -#endif -#endif +#endif /* WOLFSSL_EARLY_DATA */ +#endif /* HAVE_SESSION_TICKET || !NO_PSK */ #ifdef HAVE_SESSION_TICKET /* ticket len | ticket */ size += OPAQUE16_LEN + sess->ticketLen; +#endif /* HAVE_SESSION_TICKET */ #if !defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS) #ifdef HAVE_SNI /* sniHash */ size += TICKET_BINDING_HASH_SZ; -#endif +#endif /* HAVE_SNI */ #ifdef HAVE_ALPN /* alpnHash */ size += TICKET_BINDING_HASH_SZ; -#endif +#endif /* HAVE_ALPN */ #endif /* !NO_WOLFSSL_SERVER && !NO_TLS */ -#endif #ifndef NO_WOLFSSL_SERVER /* peerAuthOk and isPSK */ size += OPAQUE8_LEN; @@ -3025,38 +3025,38 @@ int wolfSSL_i2d_SSL_SESSION(WOLFSSL_SESSION* sess, unsigned char** p) idx += OPAQUE32_LEN; c32toa((word32)sess->ticketSeen, data + idx); idx += OPAQUE32_LEN; -#endif +#endif /* WOLFSSL_32BIT_MILLI_TIME */ c32toa(sess->ticketAdd, data + idx); idx += OPAQUE32_LEN; data[idx++] = sess->ticketNonce.len; XMEMCPY(data + idx, sess->ticketNonce.data, sess->ticketNonce.len); idx += sess->ticketNonce.len; -#endif +#endif /* WOLFSSL_TLS13 */ #ifdef WOLFSSL_EARLY_DATA c32toa(sess->maxEarlyDataSz, data + idx); idx += OPAQUE32_LEN; -#endif -#endif +#endif /* WOLFSSL_EARLY_DATA */ +#endif /* HAVE_SESSION_TICKET || !NO_PSK */ #ifdef HAVE_SESSION_TICKET c16toa(sess->ticketLen, data + idx); idx += OPAQUE16_LEN; XMEMCPY(data + idx, sess->ticket, sess->ticketLen); idx += sess->ticketLen; +#endif /* HAVE_SESSION_TICKET */ #if !defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS) #ifdef HAVE_SNI XMEMCPY(data + idx, sess->sniHash, TICKET_BINDING_HASH_SZ); idx += TICKET_BINDING_HASH_SZ; -#endif +#endif /* HAVE_SNI */ #ifdef HAVE_ALPN XMEMCPY(data + idx, sess->alpnHash, TICKET_BINDING_HASH_SZ); idx += TICKET_BINDING_HASH_SZ; -#endif +#endif /* HAVE_ALPN */ #endif /* !NO_WOLFSSL_SERVER && !NO_TLS */ -#endif #ifndef NO_WOLFSSL_SERVER data[idx++] = (byte)(sess->peerAuthOk | (sess->isPSK << 1)); #endif } -#endif +#endif /* HAVE_EXT_CACHE */ (void)sess; (void)p; @@ -3273,7 +3273,7 @@ WOLFSSL_SESSION* wolfSSL_d2i_SSL_SESSION(WOLFSSL_SESSION** sess, idx += OPAQUE32_LEN; s->ticketSeen = ((sword64)seenHi << 32) + seenLo; } -#endif +#endif /* WOLFSSL_32BIT_MILLI_TIME */ if (i - idx < OPAQUE32_LEN) { ret = BUFFER_ERROR; @@ -3305,7 +3305,7 @@ WOLFSSL_SESSION* wolfSSL_d2i_SSL_SESSION(WOLFSSL_SESSION** sess, #endif /* defined(WOLFSSL_TICKET_NONCE_MALLOC) && FIPS_VERSION_GE(5,3) */ idx += s->ticketNonce.len; -#endif +#endif /* WOLFSSL_TLS13 */ #ifdef WOLFSSL_EARLY_DATA if (i - idx < OPAQUE32_LEN) { ret = BUFFER_ERROR; @@ -3313,8 +3313,8 @@ WOLFSSL_SESSION* wolfSSL_d2i_SSL_SESSION(WOLFSSL_SESSION** sess, } ato32(data + idx, &s->maxEarlyDataSz); idx += OPAQUE32_LEN; -#endif -#endif +#endif /* WOLFSSL_EARLY_DATA */ +#endif /* HAVE_SESSION_TICKET || !NO_PSK */ #ifdef HAVE_SESSION_TICKET /* ticket len */ if (i - idx < OPAQUE16_LEN) { @@ -3345,6 +3345,7 @@ WOLFSSL_SESSION* wolfSSL_d2i_SSL_SESSION(WOLFSSL_SESSION** sess, goto end; } XMEMCPY(s->ticket, data + idx, s->ticketLen); idx += s->ticketLen; +#endif /* HAVE_SESSION_TICKET */ #if !defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS) #ifdef HAVE_SNI /* sniHash - SNI binding for stateful resumption (RFC 6066 section 3) */ @@ -3354,7 +3355,7 @@ WOLFSSL_SESSION* wolfSSL_d2i_SSL_SESSION(WOLFSSL_SESSION** sess, } XMEMCPY(s->sniHash, data + idx, TICKET_BINDING_HASH_SZ); idx += TICKET_BINDING_HASH_SZ; -#endif +#endif /* HAVE_SNI */ #ifdef HAVE_ALPN /* alpnHash - ALPN binding for stateful resumption */ if (i - idx < TICKET_BINDING_HASH_SZ) { @@ -3363,9 +3364,8 @@ WOLFSSL_SESSION* wolfSSL_d2i_SSL_SESSION(WOLFSSL_SESSION** sess, } XMEMCPY(s->alpnHash, data + idx, TICKET_BINDING_HASH_SZ); idx += TICKET_BINDING_HASH_SZ; -#endif +#endif /* HAVE_ALPN */ #endif /* !NO_WOLFSSL_SERVER && !NO_TLS */ -#endif #ifndef NO_WOLFSSL_SERVER if (i - idx < OPAQUE8_LEN) { ret = BUFFER_ERROR; @@ -3974,16 +3974,15 @@ void SetupSession(WOLFSSL* ssl) session->sessionCtxSz = ssl->sessionCtxSz; } #endif -#if defined(HAVE_SESSION_TICKET) && \ - !defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS) +#if !defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS) /* Bind the current SNI/ALPN to the session to verify on later resumption */ #ifdef HAVE_SNI (void)TicketSniHash(ssl, session->sniHash); -#endif +#endif /* HAVE_SNI */ #ifdef HAVE_ALPN (void)TicketAlpnHash(ssl, session->alpnHash); -#endif -#endif /* HAVE_SESSION_TICKET && !NO_WOLFSSL_SERVER && !NO_TLS */ +#endif /* HAVE_ALPN */ +#endif /* !NO_WOLFSSL_SERVER && !NO_TLS */ session->timeout = ssl->timeout; #ifndef NO_ASN_TIME session->bornOn = LowResTimer(); diff --git a/tests/api/test_tls.c b/tests/api/test_tls.c index 590cde798b4..f092088bd10 100644 --- a/tests/api/test_tls.c +++ b/tests/api/test_tls.c @@ -5644,10 +5644,9 @@ int test_tls_set_session_min_downgrade(void) return EXPECT_RESULT(); } -#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ - (!defined(WOLFSSL_NO_TLS12) || defined(WOLFSSL_TLS13)) && \ - defined(HAVE_SNI) && defined(HAVE_SESSION_TICKET) && \ - !defined(NO_SESSION_CACHE) +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(HAVE_SNI) && \ + !defined(NO_SESSION_CACHE) && (!defined(WOLFSSL_NO_TLS12) || \ + (defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET))) /* Accept-all SNI callback. */ static int accept_any_sni_cb(WOLFSSL* ssl, int* ret, void* arg) { @@ -5663,7 +5662,7 @@ int test_tls12_session_id_resumption_sni_mismatch(void) EXPECT_DECLS; #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ !defined(WOLFSSL_NO_TLS12) && defined(HAVE_SNI) && \ - defined(HAVE_SESSION_TICKET) && !defined(NO_SESSION_CACHE) + !defined(NO_SESSION_CACHE) WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; WOLFSSL *ssl_c = NULL, *ssl_s = NULL; WOLFSSL_SESSION *sess = NULL; @@ -5680,8 +5679,10 @@ int test_tls12_session_id_resumption_sni_mismatch(void) ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); wolfSSL_CTX_set_servername_callback(ctx_s, accept_any_sni_cb); +#ifdef HAVE_SESSION_TICKET ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_c), WOLFSSL_SUCCESS); ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_s), WOLFSSL_SUCCESS); +#endif ExpectIntEQ(wolfSSL_UseSNI(ssl_c, WOLFSSL_SNI_HOST_NAME, sniA, (word16)XSTRLEN(sniA)), WOLFSSL_SUCCESS); ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); @@ -5704,8 +5705,10 @@ int test_tls12_session_id_resumption_sni_mismatch(void) ExpectNotNull(ssl_s = wolfSSL_new(ctx_s)); wolfSSL_SetIOReadCtx(ssl_s, &test_ctx); wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx); +#ifdef HAVE_SESSION_TICKET ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_c), WOLFSSL_SUCCESS); ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_s), WOLFSSL_SUCCESS); +#endif ExpectIntEQ(wolfSSL_UseSNI(ssl_c, WOLFSSL_SNI_HOST_NAME, sniB, (word16)XSTRLEN(sniB)), WOLFSSL_SUCCESS); ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS); @@ -5727,6 +5730,81 @@ int test_tls12_session_id_resumption_sni_mismatch(void) return EXPECT_RESULT(); } +/* Declined session ID resumption must not keep the cached peer cert. */ +int test_tls12_session_id_sni_mismatch_peer_cert(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_TLS12) && defined(HAVE_SNI) && \ + !defined(NO_SESSION_CACHE) && defined(SESSION_CERTS) && \ + defined(KEEP_PEER_CERT) && !defined(WOLFSSL_NO_CLIENT_AUTH) && \ + !defined(NO_RSA) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL_SESSION *sess = NULL; + WOLFSSL_X509* peer = NULL; + struct test_memio_ctx test_ctx; + const char* sniA = "private.example"; + const char* sniB = "public.example"; + + /* Full handshake with client auth under SNI A. */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + wolfSSL_CTX_set_servername_callback(ctx_s, accept_any_sni_cb); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL), + WOLFSSL_SUCCESS); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER | + WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); +#ifdef HAVE_SESSION_TICKET + ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_c), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_s), WOLFSSL_SUCCESS); +#endif + ExpectIntEQ(wolfSSL_UseSNI(ssl_c, WOLFSSL_SNI_HOST_NAME, + sniA, (word16)XSTRLEN(sniA)), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectNotNull(peer = wolfSSL_get_peer_certificate(ssl_s)); + wolfSSL_X509_free(peer); + peer = NULL; + ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); + + wolfSSL_free(ssl_c); ssl_c = NULL; + wolfSSL_free(ssl_s); ssl_s = NULL; + + /* Offer the session under SNI B to a server not asking for a cert. */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectNotNull(ssl_c = wolfSSL_new(ctx_c)); + wolfSSL_SetIOReadCtx(ssl_c, &test_ctx); + wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx); + ExpectNotNull(ssl_s = wolfSSL_new(ctx_s)); + wolfSSL_SetIOReadCtx(ssl_s, &test_ctx); + wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL); +#ifdef HAVE_SESSION_TICKET + ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_c), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_s), WOLFSSL_SUCCESS); +#endif + ExpectIntEQ(wolfSSL_UseSNI(ssl_c, WOLFSSL_SNI_HOST_NAME, + sniB, (word16)XSTRLEN(sniB)), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0); + ExpectNull(peer = wolfSSL_get_peer_certificate(ssl_s)); + wolfSSL_X509_free(peer); + + wolfSSL_SESSION_free(sess); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + /* A TLS 1.2 ticket that decrypts is copied to the session before it can be * declined, and the server writes its next ticket before SetupSession() * resets that. The new ticket must record only what the full handshake @@ -5962,7 +6040,7 @@ int test_tls12_session_id_resumption_alpn_mismatch(void) EXPECT_DECLS; #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ !defined(WOLFSSL_NO_TLS12) && defined(HAVE_ALPN) && \ - defined(HAVE_SESSION_TICKET) && !defined(NO_SESSION_CACHE) + !defined(NO_SESSION_CACHE) WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; WOLFSSL *ssl_c = NULL, *ssl_s = NULL; WOLFSSL_SESSION *sess = NULL; @@ -5978,8 +6056,10 @@ int test_tls12_session_id_resumption_alpn_mismatch(void) XMEMSET(&test_ctx, 0, sizeof(test_ctx)); ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); +#ifdef HAVE_SESSION_TICKET ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_c), WOLFSSL_SUCCESS); ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_s), WOLFSSL_SUCCESS); +#endif ExpectIntEQ(wolfSSL_UseALPN(ssl_c, (char*)alpnA, (word32)XSTRLEN(alpnA), WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS); ExpectIntEQ(wolfSSL_UseALPN(ssl_s, (char*)alpnA, (word32)XSTRLEN(alpnA), @@ -6006,8 +6086,10 @@ int test_tls12_session_id_resumption_alpn_mismatch(void) ExpectNotNull(ssl_s = wolfSSL_new(ctx_s)); wolfSSL_SetIOReadCtx(ssl_s, &test_ctx); wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx); +#ifdef HAVE_SESSION_TICKET ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_c), WOLFSSL_SUCCESS); ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_s), WOLFSSL_SUCCESS); +#endif ExpectIntEQ(wolfSSL_UseALPN(ssl_c, (char*)alpnB, (word32)XSTRLEN(alpnB), WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS); ExpectIntEQ(wolfSSL_UseALPN(ssl_s, (char*)alpnB, (word32)XSTRLEN(alpnB), @@ -6030,6 +6112,106 @@ int test_tls12_session_id_resumption_alpn_mismatch(void) return EXPECT_RESULT(); } +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(OLD_HELLO_ALLOWED) && !defined(WOLFSSL_NO_TLS12) && \ + defined(HAVE_SNI) && defined(HAVE_ALPN) && !defined(NO_SESSION_CACHE) +/* Resume a TLS 1.2 session ID with an SSLv2 ClientHello (no extensions). */ +static int test_tls12_old_hello_resume(int useSni, int useAlpn) +{ + EXPECT_DECLS; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + const char* sni = "public.example"; + const char alpn[] = "h2"; + byte hello[2 + 9 + 3 + ID_LEN + RAN_LEN]; + /* SSLv2 header, ClientHello, TLS 1.2, suite/ID/random lengths */ + const byte hdr[] = { + 0x80, (byte)(sizeof(hello) - 2), OLD_HELLO_ID, + SSLv3_MAJOR, TLSv1_2_MINOR, 0, 3, 0, ID_LEN, 0, RAN_LEN + }; + int idx = 0; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + wolfSSL_CTX_set_servername_callback(ctx_s, accept_any_sni_cb); +#ifdef HAVE_SESSION_TICKET + ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_c), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_s), WOLFSSL_SUCCESS); +#endif +#ifdef HAVE_EXTENDED_MASTER + /* An SSLv2 ClientHello cannot request EMS. */ + ExpectIntEQ(wolfSSL_DisableExtendedMasterSecret(ssl_c), WOLFSSL_SUCCESS); +#endif + if (useSni) { + ExpectIntEQ(wolfSSL_UseSNI(ssl_c, WOLFSSL_SNI_HOST_NAME, sni, + (word16)XSTRLEN(sni)), WOLFSSL_SUCCESS); + } + if (useAlpn) { + ExpectIntEQ(wolfSSL_UseALPN(ssl_c, (char*)alpn, (word32)XSTRLEN(alpn), + WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseALPN(ssl_s, (char*)alpn, (word32)XSTRLEN(alpn), + WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS); + } + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(ssl_c->session->sessionIDSz, ID_LEN); + + if (EXPECT_SUCCESS()) { + XMEMCPY(hello, hdr, sizeof(hdr)); + idx = (int)sizeof(hdr); + hello[idx++] = 0; /* TLS suite in SSLv2 three byte form */ + hello[idx++] = ssl_c->options.cipherSuite0; + hello[idx++] = ssl_c->options.cipherSuite; + XMEMCPY(hello + idx, ssl_c->session->sessionID, ID_LEN); + idx += ID_LEN; + XMEMSET(hello + idx, 0x5a, RAN_LEN); + } + + wolfSSL_free(ssl_c); ssl_c = NULL; + wolfSSL_free(ssl_s); ssl_s = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectNotNull(ssl_s = wolfSSL_new(ctx_s)); + wolfSSL_SetIOReadCtx(ssl_s, &test_ctx); + wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx); + if (useAlpn) { + ExpectIntEQ(wolfSSL_UseALPN(ssl_s, (char*)alpn, (word32)XSTRLEN(alpn), + WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS); + } + ExpectIntEQ(test_memio_inject_message(&test_ctx, 0, (const char*)hello, + (int)sizeof(hello)), 0); + ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_FATAL_ERROR); + ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR), + WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(wolfSSL_session_reused(ssl_s), !useSni && !useAlpn); + + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} +#endif + +/* SSLv2 ClientHello resumption must not bypass the SNI/ALPN binding. */ +int test_tls12_old_hello_resumption_binding(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(OLD_HELLO_ALLOWED) && !defined(WOLFSSL_NO_TLS12) && \ + defined(HAVE_SNI) && defined(HAVE_ALPN) && !defined(NO_SESSION_CACHE) + /* { useSni, useAlpn } */ + static const byte cases[][2] = { { 0, 0 }, { 1, 0 }, { 0, 1 } }; + size_t i; + + for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + ExpectIntEQ(test_tls12_old_hello_resume(cases[i][0], cases[i][1]), + TEST_SUCCESS); + } +#endif + return EXPECT_RESULT(); +} + /* TLS 1.3 PSK resumption must fall back to a full handshake if the ALPN * protocol negotiated for the resumed connection does not match the ALPN * bound to the original session. Mirrors diff --git a/tests/api/test_tls.h b/tests/api/test_tls.h index 4c9133427b6..69dcdde4835 100644 --- a/tests/api/test_tls.h +++ b/tests/api/test_tls.h @@ -93,10 +93,12 @@ int test_tls12_server_hello_tls13_suite(void); int test_tls12_client_hello_no_tls13_suites(void); int test_tls_set_session_min_downgrade(void); int test_tls12_session_id_resumption_sni_mismatch(void); +int test_tls12_session_id_sni_mismatch_peer_cert(void); int test_tls12_declined_ticket_auth_not_reminted(void); int test_tls13_session_resumption_sni_mismatch(void); int test_tls13_resumption_with_alpn(void); int test_tls12_session_id_resumption_alpn_mismatch(void); +int test_tls12_old_hello_resumption_binding(void); int test_tls13_session_resumption_alpn_mismatch(void); int test_tls_set_curves_list_ecc_fallback(void); int test_tls12_corrupted_finished(void); @@ -202,10 +204,12 @@ int test_tls_param_flags_crl_check(void); TEST_DECL_GROUP("tls", test_tls12_client_hello_no_tls13_suites), \ TEST_DECL_GROUP("tls", test_tls_set_session_min_downgrade), \ TEST_DECL_GROUP("tls", test_tls12_session_id_resumption_sni_mismatch), \ + TEST_DECL_GROUP("tls", test_tls12_session_id_sni_mismatch_peer_cert), \ TEST_DECL_GROUP("tls", test_tls12_declined_ticket_auth_not_reminted), \ TEST_DECL_GROUP("tls", test_tls13_session_resumption_sni_mismatch), \ TEST_DECL_GROUP("tls", test_tls13_resumption_with_alpn), \ TEST_DECL_GROUP("tls", test_tls12_session_id_resumption_alpn_mismatch),\ + TEST_DECL_GROUP("tls", test_tls12_old_hello_resumption_binding), \ TEST_DECL_GROUP("tls", test_tls13_session_resumption_alpn_mismatch), \ TEST_DECL_GROUP("tls", test_tls_set_curves_list_ecc_fallback), \ TEST_DECL_GROUP("tls", test_tls12_corrupted_finished), \ diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 6a2cd709e14..3ea829c0b4e 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -3989,13 +3989,8 @@ WOLFSSL_LOCAL int SetupClientSecureRenegotiation(WOLFSSL* ssl); #endif /* HAVE_SECURE_RENEGOTIATION */ -#ifdef HAVE_SESSION_TICKET -/* Max peer cert size for ticket: 2KB is reasonable for most RSA/ECC certs */ -#ifndef MAX_TICKET_PEER_CERT_SZ -#define MAX_TICKET_PEER_CERT_SZ 2048 -#endif #if defined(HAVE_SNI) || defined(HAVE_ALPN) -/* Hash algorithm used for SNI/ALPN binding in session tickets. +/* Hash algorithm used for SNI/ALPN binding in sessions and tickets. * Pick the best available at compile time. */ #ifndef TICKET_BINDING_HASH_TYPE #if !defined(NO_SHA256) @@ -4013,6 +4008,12 @@ WOLFSSL_LOCAL int SetupClientSecureRenegotiation(WOLFSSL* ssl); #endif #endif +#ifdef HAVE_SESSION_TICKET +/* Max peer cert size for ticket: 2KB is reasonable for most RSA/ECC certs */ +#ifndef MAX_TICKET_PEER_CERT_SZ +#define MAX_TICKET_PEER_CERT_SZ 2048 +#endif + /* Our ticket format. All members need to be a byte or array of byte to * avoid alignment issues */ typedef struct InternalTicket { @@ -5404,13 +5405,13 @@ struct WOLFSSL_SESSION { byte* ticket; word16 ticketLen; word16 ticketLenAlloc; /* is dynamic */ +#endif /* HAVE_SESSION_TICKET */ #ifdef HAVE_SNI byte sniHash[TICKET_BINDING_HASH_SZ]; /* SNI at issue */ #endif /* HAVE_SNI */ - #ifdef HAVE_ALPN +#ifdef HAVE_ALPN byte alpnHash[TICKET_BINDING_HASH_SZ]; /* ALPN at issue */ - #endif /* HAVE_ALPN */ -#endif /* HAVE_SESSION_TICKET */ +#endif /* HAVE_ALPN */ #ifdef SESSION_CERTS WOLFSSL_X509_CHAIN chain; /* peer cert chain, static */ @@ -7748,6 +7749,7 @@ WOLFSSL_LOCAL int DoClientTicket_ex(const WOLFSSL* ssl, PreSharedKey* psk, #endif WOLFSSL_LOCAL int DoClientTicket(WOLFSSL* ssl, const byte* input, word32 len); +#endif /* HAVE_SESSION_TICKET */ /* TicketSniHash, TicketAlpnHash, and VerifyTicketBinding are defined in * internal.c only when !NO_WOLFSSL_SERVER && !NO_TLS - gate the * declarations to match so client-only or no-TLS builds don't compile in @@ -7763,7 +7765,6 @@ WOLFSSL_LOCAL int TicketAlpnHash(WOLFSSL* ssl, byte* dst); WOLFSSL_LOCAL int VerifyTicketBinding(WOLFSSL* ssl); #endif #endif /* !NO_WOLFSSL_SERVER && !NO_TLS */ -#endif /* HAVE_SESSION_TICKET */ WOLFSSL_LOCAL int SendData(WOLFSSL* ssl, const void* data, size_t sz); #ifdef WOLFSSL_THREADED_CRYPT WOLFSSL_LOCAL int SendAsyncData(WOLFSSL* ssl);