diff --git a/src/tls.c b/src/tls.c index 6313385c01..7e724681af 100644 --- a/src/tls.c +++ b/src/tls.c @@ -2135,7 +2135,7 @@ static void TLSX_SNI_FreeAll(SNI* list, void* heap) } /** Tells the buffered size of the SNI objects in a list. */ -static word16 TLSX_SNI_GetSize(SNI* list) +WOLFSSL_TEST_VIS word16 TLSX_SNI_GetSize(SNI* list) { SNI* sni; word32 length = OPAQUE16_LEN; /* list length */ @@ -3241,15 +3241,19 @@ word16 TLSX_CSR_GetSize_ex(CertificateStatusRequest* csr, byte isRequest, if (csr->ssl != NULL && SSL_CM(csr->ssl) != NULL && SSL_CM(csr->ssl)->ocsp_stapling != NULL && SSL_CM(csr->ssl)->ocsp_stapling->statusCb != NULL) { + if (WOLFSSL_MAX_16BIT - OPAQUE8_LEN - OPAQUE24_LEN < + csr->ssl->ocspCsrResp[idx].length) { + return 0; + } size = OPAQUE8_LEN + OPAQUE24_LEN + csr->ssl->ocspCsrResp[idx].length; - if (size > WOLFSSL_MAX_16BIT) - return 0; return (word16)size; } - size = OPAQUE8_LEN + OPAQUE24_LEN + csr->responses[idx].length; - if (size > WOLFSSL_MAX_16BIT) + if (WOLFSSL_MAX_16BIT - OPAQUE8_LEN - OPAQUE24_LEN < + csr->responses[idx].length) { return 0; + } + size = OPAQUE8_LEN + OPAQUE24_LEN + csr->responses[idx].length; return (word16)size; } #else diff --git a/tests/api.c b/tests/api.c index b0861a5b48..f1e6e1780d 100644 --- a/tests/api.c +++ b/tests/api.c @@ -32914,6 +32914,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_certificate_authorities_certificate_request), TEST_DECL(test_certificate_authorities_client_hello), TEST_DECL(test_TLSX_TCA_Find), + TEST_DECL(test_TLSX_SNI_GetSize_overflow), TEST_DECL(test_wolfSSL_wolfSSL_UseSecureRenegotiation), TEST_DECL(test_wolfSSL_SCR_Reconnect), TEST_DECL(test_wolfSSL_SCR_check_enabled), diff --git a/tests/api/test_tls_ext.c b/tests/api/test_tls_ext.c index 4c89d7edf6..ad6053f727 100644 --- a/tests/api/test_tls_ext.c +++ b/tests/api/test_tls_ext.c @@ -545,3 +545,67 @@ int test_certificate_authorities_client_hello(void) { #endif return EXPECT_RESULT(); } + +/* Test that the SNI size calculation returns 0 on overflow instead of + * wrapping around to a small value (integer overflow vulnerability). */ +int test_TLSX_SNI_GetSize_overflow(void) +{ + EXPECT_DECLS; +#if defined(HAVE_SNI) && !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + TLSX* sni_ext = NULL; + SNI* head = NULL; + SNI* sni = NULL; + int i; + /* Each SNI adds ENUM_LEN(1) + OPAQUE16_LEN(2) + hostname_len to the size. + * With a 1-byte hostname, each entry adds 4 bytes. Starting from + * OPAQUE16_LEN(2) base, we need enough entries to exceed UINT16_MAX. */ + const int num_sni = (0xFFFF / 4) + 2; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + /* Add initial SNI via public API */ + ExpectIntEQ(WOLFSSL_SUCCESS, + wolfSSL_UseSNI(ssl, WOLFSSL_SNI_HOST_NAME, "a", 1)); + + /* Find the SNI extension and manually build a long chain */ + if (EXPECT_SUCCESS()) { + sni_ext = TLSX_Find(ssl->extensions, TLSX_SERVER_NAME); + ExpectNotNull(sni_ext); + } + + if (EXPECT_SUCCESS()) { + head = (SNI*)sni_ext->data; + ExpectNotNull(head); + } + + /* Append many SNI nodes to force overflow in the size calculation */ + for (i = 1; EXPECT_SUCCESS() && i < num_sni; i++) { + sni = (SNI*)XMALLOC(sizeof(SNI), NULL, DYNAMIC_TYPE_TLSX); + ExpectNotNull(sni); + if (sni != NULL) { + XMEMSET(sni, 0, sizeof(SNI)); + sni->type = WOLFSSL_SNI_HOST_NAME; + sni->data.host_name = (char*)XMALLOC(2, NULL, DYNAMIC_TYPE_TLSX); + ExpectNotNull(sni->data.host_name); + if (sni->data.host_name != NULL) { + sni->data.host_name[0] = 'a'; + sni->data.host_name[1] = '\0'; + } + sni->next = head->next; + head->next = sni; + } + } + + if (EXPECT_SUCCESS()) { + /* The fixed calculation should return 0 (overflow detected) */ + ExpectIntEQ(TLSX_SNI_GetSize((SNI*)sni_ext->data), 0); + } + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_tls_ext.h b/tests/api/test_tls_ext.h index c02067522d..ec7a5223be 100644 --- a/tests/api/test_tls_ext.h +++ b/tests/api/test_tls_ext.h @@ -27,5 +27,6 @@ int test_wolfSSL_DisableExtendedMasterSecret(void); int test_certificate_authorities_certificate_request(void); int test_certificate_authorities_client_hello(void); int test_TLSX_TCA_Find(void); +int test_TLSX_SNI_GetSize_overflow(void); #endif /* TESTS_API_TEST_TLS_EMS_H */ diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 7ebe3d7b6c..12c09a219b 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -3169,7 +3169,7 @@ struct TLSX { struct TLSX* next; /* List Behavior */ }; -WOLFSSL_LOCAL TLSX* TLSX_Find(TLSX* list, TLSX_Type type); +WOLFSSL_TEST_VIS TLSX* TLSX_Find(TLSX* list, TLSX_Type type); WOLFSSL_LOCAL void TLSX_Remove(TLSX** list, TLSX_Type type, void* heap); WOLFSSL_LOCAL void TLSX_FreeAll(TLSX* list, void* heap); WOLFSSL_LOCAL int TLSX_SupportExtensions(WOLFSSL* ssl); @@ -3237,6 +3237,7 @@ WOLFSSL_LOCAL int TLSX_UseSNI(TLSX** extensions, byte type, const void* data, WOLFSSL_LOCAL byte TLSX_SNI_Status(TLSX* extensions, byte type); WOLFSSL_LOCAL word16 TLSX_SNI_GetRequest(TLSX* extensions, byte type, void** data, byte ignoreStatus); +WOLFSSL_TEST_VIS word16 TLSX_SNI_GetSize(SNI* list); #ifndef NO_WOLFSSL_SERVER WOLFSSL_LOCAL void TLSX_SNI_SetOptions(TLSX* extensions, byte type,