Skip to content

Commit 0582aa2

Browse files
committed
Add test case
1 parent f6084ed commit 0582aa2

3 files changed

Lines changed: 134 additions & 0 deletions

File tree

tests/api.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32854,6 +32854,7 @@ TEST_CASE testCases[] = {
3285432854
TEST_DECL(test_certificate_authorities_certificate_request),
3285532855
TEST_DECL(test_certificate_authorities_client_hello),
3285632856
TEST_DECL(test_TLSX_TCA_Find),
32857+
TEST_DECL(test_TLSX_SNI_GetSize_overflow),
3285732858
TEST_DECL(test_wolfSSL_wolfSSL_UseSecureRenegotiation),
3285832859
TEST_DECL(test_wolfSSL_SCR_Reconnect),
3285932860
TEST_DECL(test_wolfSSL_SCR_check_enabled),

tests/api/test_tls_ext.c

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,3 +545,135 @@ int test_certificate_authorities_client_hello(void) {
545545
#endif
546546
return EXPECT_RESULT();
547547
}
548+
549+
#if defined(HAVE_SNI) && !defined(NO_WOLFSSL_CLIENT)
550+
/* Walk the TLSX list to find an extension by type. */
551+
static TLSX* test_TLSX_find_ext_sni(TLSX* list, TLSX_Type type)
552+
{
553+
while (list) {
554+
if (list->type == type)
555+
return list;
556+
list = list->next;
557+
}
558+
return NULL;
559+
}
560+
561+
/* Replicate the SNI size calculation to test overflow protection.
562+
* The fixed TLSX_SNI_GetSize uses word32 internally and returns 0 on
563+
* overflow. This helper mirrors that logic so we can verify the behavior. */
564+
static word16 test_SNI_GetSize(SNI* list)
565+
{
566+
SNI* sni;
567+
word32 length = OPAQUE16_LEN;
568+
569+
while ((sni = list)) {
570+
list = sni->next;
571+
length += ENUM_LEN + OPAQUE16_LEN;
572+
switch (sni->type) {
573+
case WOLFSSL_SNI_HOST_NAME:
574+
length += (word16)XSTRLEN((char*)sni->data.host_name);
575+
break;
576+
}
577+
if (length > WOLFSSL_MAX_16BIT)
578+
return 0;
579+
}
580+
return (word16)length;
581+
}
582+
583+
/* Replicate the old (vulnerable) SNI size calculation using word16 to
584+
* demonstrate the overflow that the fix prevents. */
585+
static word16 test_SNI_GetSize_old(SNI* list)
586+
{
587+
SNI* sni;
588+
word16 length = OPAQUE16_LEN;
589+
590+
while ((sni = list)) {
591+
list = sni->next;
592+
length += ENUM_LEN + OPAQUE16_LEN;
593+
switch (sni->type) {
594+
case WOLFSSL_SNI_HOST_NAME:
595+
length += (word16)XSTRLEN((char*)sni->data.host_name);
596+
break;
597+
}
598+
}
599+
return length;
600+
}
601+
602+
/* Test that the SNI size calculation returns 0 on overflow instead of
603+
* wrapping around to a small value (integer overflow vulnerability). */
604+
/* closing #if for helper functions */
605+
#endif /* HAVE_SNI && !NO_WOLFSSL_CLIENT */
606+
607+
int test_TLSX_SNI_GetSize_overflow(void)
608+
{
609+
EXPECT_DECLS;
610+
#if defined(HAVE_SNI) && !defined(NO_WOLFSSL_CLIENT)
611+
WOLFSSL_CTX* ctx = NULL;
612+
WOLFSSL* ssl = NULL;
613+
TLSX* sni_ext = NULL;
614+
SNI* head = NULL;
615+
SNI* sni = NULL;
616+
int i;
617+
word16 fixed_size;
618+
word16 old_size;
619+
/* Each SNI adds ENUM_LEN(1) + OPAQUE16_LEN(2) + hostname_len to the size.
620+
* With a 1-byte hostname, each entry adds 4 bytes. Starting from
621+
* OPAQUE16_LEN(2) base, we need enough entries to exceed UINT16_MAX. */
622+
const int num_sni = (0xFFFF / 4) + 2;
623+
624+
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
625+
ExpectNotNull(ssl = wolfSSL_new(ctx));
626+
627+
/* Add initial SNI via public API */
628+
ExpectIntEQ(WOLFSSL_SUCCESS,
629+
wolfSSL_UseSNI(ssl, WOLFSSL_SNI_HOST_NAME, "a", 1));
630+
631+
/* Find the SNI extension and manually build a long chain */
632+
if (EXPECT_SUCCESS()) {
633+
sni_ext = test_TLSX_find_ext_sni(ssl->extensions, TLSX_SERVER_NAME);
634+
ExpectNotNull(sni_ext);
635+
}
636+
637+
if (EXPECT_SUCCESS()) {
638+
head = (SNI*)sni_ext->data;
639+
ExpectNotNull(head);
640+
}
641+
642+
/* Append many SNI nodes to force overflow in the size calculation */
643+
for (i = 1; EXPECT_SUCCESS() && i < num_sni; i++) {
644+
sni = (SNI*)XMALLOC(sizeof(SNI), NULL, DYNAMIC_TYPE_TLSX);
645+
ExpectNotNull(sni);
646+
if (sni != NULL) {
647+
XMEMSET(sni, 0, sizeof(SNI));
648+
sni->type = WOLFSSL_SNI_HOST_NAME;
649+
sni->data.host_name = (char*)XMALLOC(2, NULL, DYNAMIC_TYPE_TLSX);
650+
ExpectNotNull(sni->data.host_name);
651+
if (sni->data.host_name != NULL) {
652+
sni->data.host_name[0] = 'a';
653+
sni->data.host_name[1] = '\0';
654+
}
655+
sni->next = head->next;
656+
head->next = sni;
657+
}
658+
}
659+
660+
if (EXPECT_SUCCESS()) {
661+
/* The fixed calculation should return 0 (overflow detected) */
662+
fixed_size = test_SNI_GetSize((SNI*)sni_ext->data);
663+
ExpectIntEQ(fixed_size, 0);
664+
665+
/* The old (vulnerable) calculation would wrap around to a small value.
666+
* The unwrapped total is approximately OPAQUE16_LEN + num_sni * 4,
667+
* which is ~65,542. After wrapping past UINT16_MAX the result should
668+
* be drastically smaller than that expected total. */
669+
old_size = test_SNI_GetSize_old((SNI*)sni_ext->data);
670+
ExpectIntNE(old_size, 0);
671+
ExpectIntLT(old_size,
672+
OPAQUE16_LEN + num_sni * (ENUM_LEN + OPAQUE16_LEN + 1));
673+
}
674+
675+
wolfSSL_free(ssl);
676+
wolfSSL_CTX_free(ctx);
677+
#endif
678+
return EXPECT_RESULT();
679+
}

tests/api/test_tls_ext.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,6 @@ int test_wolfSSL_DisableExtendedMasterSecret(void);
2727
int test_certificate_authorities_certificate_request(void);
2828
int test_certificate_authorities_client_hello(void);
2929
int test_TLSX_TCA_Find(void);
30+
int test_TLSX_SNI_GetSize_overflow(void);
3031

3132
#endif /* TESTS_API_TEST_TLS_EMS_H */

0 commit comments

Comments
 (0)