Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 36 additions & 12 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -25409,9 +25409,9 @@ int CreateOcspResponse(WOLFSSL* ssl, OcspRequest** ocspRequest,
ret = CheckOcspRequest(SSL_CM(ssl)->ocsp_stapling, request, response,
ssl->heap);

/* Suppressing, not critical */
if (ret == WC_NO_ERR_TRACE(OCSP_CERT_REVOKED) ||
ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN) ||
/* Suppressing soft-fail responder errors. OCSP_CERT_REVOKED is an
* explicit positive assertion of revocation and must not be ignored. */
if (ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN) ||
ret == WC_NO_ERR_TRACE(OCSP_LOOKUP_FAIL)) {
ret = 0;
}
Expand Down Expand Up @@ -26268,9 +26268,11 @@ int SendCertificateStatus(WOLFSSL* ssl)
ret = CheckOcspRequest(SSL_CM(ssl)->ocsp_stapling,
request, &responses[i + 1], ssl->heap);

/* Suppressing, not critical */
if (ret == WC_NO_ERR_TRACE(OCSP_CERT_REVOKED) ||
ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN) ||
/* Suppressing soft-fail responder errors.
* OCSP_CERT_REVOKED is an explicit positive
* assertion of revocation and must not be
* ignored. */
if (ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN) ||
ret == WC_NO_ERR_TRACE(OCSP_LOOKUP_FAIL)) {
ret = 0;
}
Expand Down Expand Up @@ -26298,9 +26300,10 @@ int SendCertificateStatus(WOLFSSL* ssl)
ret = CheckOcspRequest(SSL_CM(ssl)->ocsp_stapling,
request, &responses[++i], ssl->heap);

/* Suppressing, not critical */
if (ret == WC_NO_ERR_TRACE(OCSP_CERT_REVOKED) ||
ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN) ||
/* Suppressing soft-fail responder errors.
* OCSP_CERT_REVOKED is an explicit positive assertion of
* revocation and must not be ignored. */
if (ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN) ||
ret == WC_NO_ERR_TRACE(OCSP_LOOKUP_FAIL)) {
ret = 0;
}
Expand Down Expand Up @@ -39949,11 +39952,27 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl)
static int DoClientTicketCheckVersion(const WOLFSSL* ssl,
InternalTicket* it)
{
if (ssl->version.minor < it->pv.minor) {
/* DTLS minor versions decrease as the protocol version increases
* (DTLS 1.0=0xFF, DTLS 1.2=0xFD, DTLS 1.3=0xFC), so the version
* comparisons are inverted relative to TLS. */
byte greaterVersion;
byte lesserVersion;
byte belowMinDowngrade;
Comment thread
julek-wolfssl marked this conversation as resolved.

if (ssl->options.dtls) {
greaterVersion = ssl->version.minor > it->pv.minor;
lesserVersion = ssl->version.minor < it->pv.minor;
}
else {
greaterVersion = ssl->version.minor < it->pv.minor;
lesserVersion = ssl->version.minor > it->pv.minor;
}

if (greaterVersion) {
WOLFSSL_MSG("Ticket has greater version");
return VERSION_ERROR;
}
else if (ssl->version.minor > it->pv.minor) {
else if (lesserVersion) {
if (IsAtLeastTLSv1_3(it->pv) != IsAtLeastTLSv1_3(ssl->version)) {
WOLFSSL_MSG("Tickets cannot be shared between "
"TLS 1.3 and TLS 1.2 and lower");
Expand All @@ -39967,7 +39986,12 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl)

WOLFSSL_MSG("Downgrading protocol due to ticket");

if (it->pv.minor < ssl->options.minDowngrade) {
if (ssl->options.dtls)
belowMinDowngrade = it->pv.minor > ssl->options.minDowngrade;
else
belowMinDowngrade = it->pv.minor < ssl->options.minDowngrade;

if (belowMinDowngrade) {
WOLFSSL_MSG("Ticket has lesser version than allowed");
return VERSION_ERROR;
}
Expand Down
9 changes: 8 additions & 1 deletion src/ocsp.c
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,14 @@ int CheckOcspRequest(WOLFSSL_OCSP* ocsp, OcspRequest* ocspRequest,
urlSz = ocspRequest->urlSz;
}
else {
/* cert doesn't have extAuthInfo, assuming CERT_GOOD */
/* No AIA URL and no override. ocspCheckAll asks for strict chain
* checking, so fail closed - but only on the client verification
* instance (cm->ocsp); stapling (cm->ocsp_stapling) shares the cm
* flag and must stay best-effort. */
if (ocsp->cm->ocspCheckAll && ocsp == ocsp->cm->ocsp) {
WOLFSSL_MSG("Cert has no OCSP URL and ocspCheckAll is set");
return OCSP_NEED_URL;
}
WOLFSSL_MSG("Cert has no OCSP URL, assuming CERT_GOOD");
return 0;
}
Expand Down
6 changes: 6 additions & 0 deletions src/ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -15871,6 +15871,12 @@ WOLFSSL_CTX* wolfSSL_set_SSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx)
* - changing the server certificate(s)
* - changing the server id for session handling
* and everything else in WOLFSSL* needs to remain untouched.
*
* SECURITY: swapping ssl->ctx switches cm-resolved settings (CA store,
* CRL, OCSP) to the new CTX but leaves ssl-cached ones (verify mode and
* callback, minDowngrade, key-size minimums, suites, version bounds)
* pinned to the original. SNI callbacks must re-apply those ssl-level
* settings explicitly; CRL/OCSP isolation requires an SSL-local store.
*/
WOLFSSL_ENTER("wolfSSL_set_SSL_CTX");
if (ssl == NULL || ctx == NULL)
Expand Down
17 changes: 17 additions & 0 deletions src/ssl_sess.c
Original file line number Diff line number Diff line change
Expand Up @@ -1610,6 +1610,23 @@ int wolfSSL_SetSession(WOLFSSL* ssl, WOLFSSL_SESSION* session)
ssl->options.haveEMS = (ssl->session->haveEMS) ? 1 : 0;

if (ssl->session->version.major != 0) {
/* Reject sessions whose protocol version is below the configured
* minimum so a stale cached session cannot make the client send a
* ClientHello advertising a version it isn't allowed to negotiate.
* DTLS minor versions are inverted: a higher minor means an older
* protocol, so the comparison flips. */
Comment thread
julek-wolfssl marked this conversation as resolved.
byte belowMinDowngrade;
if (ssl->options.dtls)
belowMinDowngrade = ssl->session->version.minor >
ssl->options.minDowngrade;
else
belowMinDowngrade = ssl->session->version.minor <
ssl->options.minDowngrade;
if (belowMinDowngrade) {
WOLFSSL_MSG("Session version below configured minDowngrade");
ssl->options.resuming = 0;
return WOLFSSL_FAILURE;
}
ssl->version = ssl->session->version;
if (IsAtLeastTLSv1_3(ssl->version))
ssl->options.tls1_3 = 1;
Expand Down
7 changes: 4 additions & 3 deletions src/tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -3666,9 +3666,10 @@ int ProcessChainOCSPRequest(WOLFSSL* ssl)
request->ssl = ssl;
ret = CheckOcspRequest(SSL_CM(ssl)->ocsp_stapling,
request, &csr->responses[i], ssl->heap);
/* Suppressing, not critical */
if (ret == WC_NO_ERR_TRACE(OCSP_CERT_REVOKED) ||
ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN) ||
/* Suppressing soft-fail responder errors. OCSP_CERT_REVOKED
* is an explicit positive assertion of revocation and must
* not be ignored. */
if (ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN) ||
ret == WC_NO_ERR_TRACE(OCSP_LOOKUP_FAIL)) {
ret = 0;
}
Expand Down
42 changes: 42 additions & 0 deletions tests/api/test_dtls.c
Original file line number Diff line number Diff line change
Expand Up @@ -3129,3 +3129,45 @@ int test_dtls13_oversized_cert_chain(void)
#endif
return EXPECT_RESULT();
}

/* DTLS counterpart to test_tls_set_session_min_downgrade. Exercises the
* inverted DTLS minor-version comparison (DTLS 1.2 minor 0xFD is "below"
* floor 0xFC = DTLS 1.3). */
int test_dtls_set_session_min_downgrade(void)
{
EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) && \
defined(WOLFSSL_DTLS13) && defined(HAVE_SESSION_TICKET)
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
WOLFSSL_SESSION *sess = NULL;
struct test_memio_ctx test_ctx;

XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method), 0);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));

wolfSSL_free(ssl_c); ssl_c = NULL;
wolfSSL_free(ssl_s); ssl_s = NULL;
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;

XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfDTLS_client_method, wolfDTLS_server_method), 0);
ExpectIntEQ(wolfSSL_SetMinVersion(ssl_c, WOLFSSL_DTLSV1_3),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_FAILURE);
if (ssl_c != NULL)
ExpectIntEQ(ssl_c->options.resuming, 0);

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();
}
4 changes: 3 additions & 1 deletion tests/api/test_dtls.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ int test_dtls_mtu_split_messages(void);
int test_dtls13_min_rtx_interval(void);
int test_dtls13_no_session_id_echo(void);
int test_dtls13_oversized_cert_chain(void);
int test_dtls_set_session_min_downgrade(void);

#define TEST_DTLS_DECLS \
TEST_DECL_GROUP("dtls", test_dtls12_basic_connection_id), \
Expand Down Expand Up @@ -91,5 +92,6 @@ int test_dtls13_oversized_cert_chain(void);
TEST_DECL_GROUP("dtls", test_dtls_memio_wolfio_stateless), \
TEST_DECL_GROUP("dtls", test_dtls13_min_rtx_interval), \
TEST_DECL_GROUP("dtls", test_dtls13_no_session_id_echo), \
TEST_DECL_GROUP("dtls", test_dtls13_oversized_cert_chain)
TEST_DECL_GROUP("dtls", test_dtls13_oversized_cert_chain), \
TEST_DECL_GROUP("dtls", test_dtls_set_session_min_downgrade)
#endif /* TESTS_API_DTLS_H */
42 changes: 42 additions & 0 deletions tests/api/test_tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,48 @@ int test_tls12_etm_failed_resumption(void)
return EXPECT_RESULT();
}

/* wolfSSL_set_session() must reject a TLS 1.2 session when minDowngrade is
* set to TLS 1.3. */
int test_tls_set_session_min_downgrade(void)
{
EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
!defined(WOLFSSL_NO_TLS12) && defined(WOLFSSL_TLS13) && \
defined(HAVE_SESSION_TICKET)
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
WOLFSSL_SESSION *sess = NULL;
struct test_memio_ctx test_ctx;

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);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));

wolfSSL_free(ssl_c); ssl_c = NULL;
wolfSSL_free(ssl_s); ssl_s = NULL;
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;

XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLS_client_method, wolfTLS_server_method), 0);
ExpectIntEQ(wolfSSL_SetMinVersion(ssl_c, WOLFSSL_TLSV1_3),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_FAILURE);
if (ssl_c != NULL)
ExpectIntEQ(ssl_c->options.resuming, 0);

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();
}

int test_tls_set_curves_list_ecc_fallback(void)
{
EXPECT_DECLS;
Expand Down
2 changes: 2 additions & 0 deletions tests/api/test_tls.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ int test_tls_certreq_order(void);
int test_tls12_bad_cv_sig_alg(void);
int test_tls12_no_null_compression(void);
int test_tls12_etm_failed_resumption(void);
int test_tls_set_session_min_downgrade(void);
int test_tls_set_curves_list_ecc_fallback(void);
int test_tls12_corrupted_finished(void);
int test_tls12_peerauth_failsafe(void);
Expand All @@ -47,6 +48,7 @@ int test_tls12_peerauth_failsafe(void);
TEST_DECL_GROUP("tls", test_tls12_bad_cv_sig_alg), \
TEST_DECL_GROUP("tls", test_tls12_no_null_compression), \
TEST_DECL_GROUP("tls", test_tls12_etm_failed_resumption), \
TEST_DECL_GROUP("tls", test_tls_set_session_min_downgrade), \
TEST_DECL_GROUP("tls", test_tls_set_curves_list_ecc_fallback), \
TEST_DECL_GROUP("tls", test_tls12_corrupted_finished), \
TEST_DECL_GROUP("tls", test_tls12_peerauth_failsafe)
Expand Down
42 changes: 42 additions & 0 deletions tests/api/test_tls13.c
Original file line number Diff line number Diff line change
Expand Up @@ -5288,6 +5288,48 @@ int test_tls13_short_session_ticket(void)
}


/* RFC 8446 Section 4.6.1: a NewSessionTicket lifetime greater than
* MAX_LIFETIME (604800 seconds, 7 days) must be rejected. The public
* wolfSSL_CTX_set_TicketHint setter clamps the value, so write the
* out-of-range hint directly into the server CTX to force the server to
* encode an over-limit lifetime onto the wire and confirm the client's
* DoTls13NewSessionTicket bound check fires. */
int test_tls13_new_session_ticket_max_lifetime(void)
{
EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)
struct test_memio_ctx test_ctx;
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
char buf[64];

XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);

/* Bypass the public-API clamp at 604800. */
if (EXPECT_SUCCESS()) {
ctx_s->ticketHint = MAX_LIFETIME + 1;
}

ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

/* Reading the post-handshake NewSessionTicket should surface the
* over-limit lifetime as SERVER_HINT_ERROR. */
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), WOLFSSL_FATAL_ERROR);
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
WC_NO_ERR_TRACE(SERVER_HINT_ERROR));

wolfSSL_free(ssl_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_c);
wolfSSL_CTX_free(ctx_s);
#endif
return EXPECT_RESULT();
}


/* Test that a corrupted TLS 1.3 Finished verify_data is properly rejected
* with VERIFY_FINISHED_ERROR. We run the handshake step-by-step and corrupt
* the server's client_write_MAC_secret before it processes the client's
Expand Down
2 changes: 2 additions & 0 deletions tests/api/test_tls13.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ int test_tls13_pqc_hybrid_truncated_keyshare(void);
int test_tls13_pqc_hybrid_malformed_ecdh(void);
int test_tls13_empty_record_limit(void);
int test_tls13_short_session_ticket(void);
int test_tls13_new_session_ticket_max_lifetime(void);
int test_tls13_early_data_0rtt_replay(void);
int test_tls13_0rtt_default_off(void);
int test_tls13_0rtt_stateless_replay(void);
Expand Down Expand Up @@ -99,6 +100,7 @@ int test_tls13_cipher_fuzz_aes128_ccm_8_sha256(void);
TEST_DECL_GROUP("tls13", test_tls13_pqc_hybrid_malformed_ecdh), \
TEST_DECL_GROUP("tls13", test_tls13_empty_record_limit), \
TEST_DECL_GROUP("tls13", test_tls13_short_session_ticket), \
TEST_DECL_GROUP("tls13", test_tls13_new_session_ticket_max_lifetime), \
TEST_DECL_GROUP("tls13", test_tls13_early_data_0rtt_replay), \
TEST_DECL_GROUP("tls13", test_tls13_0rtt_default_off), \
TEST_DECL_GROUP("tls13", test_tls13_0rtt_stateless_replay), \
Expand Down
Loading