Skip to content

Commit 734a711

Browse files
Merge pull request #10220 from embhorn/zd21596
Fix TLS ext bounds checking
2 parents c6953b8 + 6f2d48c commit 734a711

7 files changed

Lines changed: 439 additions & 48 deletions

File tree

src/tls.c

Lines changed: 141 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6504,7 +6504,7 @@ static int TLSX_SessionTicket_Parse(WOLFSSL* ssl, const byte* input,
65046504
return ret;
65056505
}
65066506

6507-
WOLFSSL_LOCAL SessionTicket* TLSX_SessionTicket_Create(word32 lifetime,
6507+
WOLFSSL_TEST_VIS SessionTicket* TLSX_SessionTicket_Create(word32 lifetime,
65086508
byte* data, word16 size, void* heap)
65096509
{
65106510
SessionTicket* ticket = (SessionTicket*)XMALLOC(sizeof(SessionTicket),
@@ -6525,7 +6525,7 @@ WOLFSSL_LOCAL SessionTicket* TLSX_SessionTicket_Create(word32 lifetime,
65256525

65266526
return ticket;
65276527
}
6528-
WOLFSSL_LOCAL void TLSX_SessionTicket_Free(SessionTicket* ticket, void* heap)
6528+
WOLFSSL_TEST_VIS void TLSX_SessionTicket_Free(SessionTicket* ticket, void* heap)
65296529
{
65306530
if (ticket) {
65316531
XFREE(ticket->data, heap, DYNAMIC_TYPE_TLSX);
@@ -14885,9 +14885,27 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType,
1488514885
{
1488614886
int ret = 0;
1488714887
TLSX* extension;
14888-
word16 length = 0;
14888+
/* Use a word32 accumulator so that an extension whose contribution
14889+
* pushes the running total past 0xFFFF is detected rather than
14890+
* silently wrapped (the TLS extensions block length prefix on the
14891+
* wire is a 2-byte field). Callees that take a word16* accumulator
14892+
* are invoked via a per-iteration shim (`cbShim`) and their delta
14893+
* is added back into the word32 total.
14894+
*
14895+
* MAINTAINER NOTE: do NOT pass &length to any *_GET_SIZE function
14896+
* that expects a `word16*` out-parameter -- that would be a type
14897+
* mismatch (UB) and would silently bypass the overflow detection
14898+
* below. When adding a new extension case, either:
14899+
* - use `length += FOO_GET_SIZE(...)` when the helper returns a
14900+
* word16 by value, or
14901+
* - use the cbShim pattern: `cbShim = 0; ret = FOO_GET_SIZE(...,
14902+
* &cbShim); length += cbShim;`
14903+
*/
14904+
word32 length = 0;
14905+
word16 cbShim = 0;
1488914906
byte isRequest = (msgType == client_hello ||
1489014907
msgType == certificate_request);
14908+
(void)cbShim;
1489114909

1489214910
while ((extension = list)) {
1489314911
list = extension->next;
@@ -14971,23 +14989,31 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType,
1497114989
#endif
1497214990
#if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
1497314991
case TLSX_ENCRYPT_THEN_MAC:
14974-
ret = ETM_GET_SIZE(msgType, &length);
14992+
cbShim = 0;
14993+
ret = ETM_GET_SIZE(msgType, &cbShim);
14994+
length += cbShim;
1497514995
break;
1497614996
#endif /* HAVE_ENCRYPT_THEN_MAC */
1497714997

1497814998
#if defined(WOLFSSL_TLS13) || !defined(WOLFSSL_NO_TLS12) || !defined(NO_OLD_TLS)
1497914999
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
1498015000
case TLSX_PRE_SHARED_KEY:
15001+
cbShim = 0;
1498115002
ret = PSK_GET_SIZE((PreSharedKey*)extension->data, msgType,
14982-
&length);
15003+
&cbShim);
15004+
length += cbShim;
1498315005
break;
1498415006
#ifdef WOLFSSL_TLS13
1498515007
case TLSX_PSK_KEY_EXCHANGE_MODES:
14986-
ret = PKM_GET_SIZE((byte)extension->val, msgType, &length);
15008+
cbShim = 0;
15009+
ret = PKM_GET_SIZE((byte)extension->val, msgType, &cbShim);
15010+
length += cbShim;
1498715011
break;
1498815012
#ifdef WOLFSSL_CERT_WITH_EXTERN_PSK
1498915013
case TLSX_CERT_WITH_EXTERN_PSK:
14990-
ret = PSK_WITH_CERT_GET_SIZE(msgType, &length);
15014+
cbShim = 0;
15015+
ret = PSK_WITH_CERT_GET_SIZE(msgType, &cbShim);
15016+
length += cbShim;
1499115017
break;
1499215018
#endif
1499315019
#endif
@@ -14999,22 +15025,30 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType,
1499915025

1500015026
#ifdef WOLFSSL_TLS13
1500115027
case TLSX_SUPPORTED_VERSIONS:
15002-
ret = SV_GET_SIZE(extension->data, msgType, &length);
15028+
cbShim = 0;
15029+
ret = SV_GET_SIZE(extension->data, msgType, &cbShim);
15030+
length += cbShim;
1500315031
break;
1500415032

1500515033
case TLSX_COOKIE:
15006-
ret = CKE_GET_SIZE((Cookie*)extension->data, msgType, &length);
15034+
cbShim = 0;
15035+
ret = CKE_GET_SIZE((Cookie*)extension->data, msgType, &cbShim);
15036+
length += cbShim;
1500715037
break;
1500815038

1500915039
#ifdef WOLFSSL_EARLY_DATA
1501015040
case TLSX_EARLY_DATA:
15011-
ret = EDI_GET_SIZE(msgType, &length);
15041+
cbShim = 0;
15042+
ret = EDI_GET_SIZE(msgType, &cbShim);
15043+
length += cbShim;
1501215044
break;
1501315045
#endif
1501415046

1501515047
#ifdef WOLFSSL_POST_HANDSHAKE_AUTH
1501615048
case TLSX_POST_HANDSHAKE_AUTH:
15017-
ret = PHA_GET_SIZE(msgType, &length);
15049+
cbShim = 0;
15050+
ret = PHA_GET_SIZE(msgType, &cbShim);
15051+
length += cbShim;
1501815052
break;
1501915053
#endif
1502015054

@@ -15067,12 +15101,26 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType,
1506715101
break;
1506815102
}
1506915103

15104+
/* Early exit: stop accumulating as soon as the running total
15105+
* cannot possibly fit the 2-byte wire length. Check *before*
15106+
* marking the extension as processed so the semaphore is not
15107+
* left in an inconsistent state on the error path. */
15108+
if (length > WOLFSSL_MAX_16BIT) {
15109+
WOLFSSL_MSG("TLSX_GetSize extension length exceeds word16");
15110+
return BUFFER_E;
15111+
}
15112+
1507015113
/* marks the extension as processed so ctx level */
1507115114
/* extensions don't overlap with ssl level ones. */
1507215115
TURN_ON(semaphore, TLSX_ToSemaphore((word16)extension->type));
1507315116
}
1507415117

15075-
*pLength += length;
15118+
if ((word32)*pLength + length > WOLFSSL_MAX_16BIT) {
15119+
WOLFSSL_MSG("TLSX_GetSize total extensions length exceeds word16");
15120+
return BUFFER_E;
15121+
}
15122+
15123+
*pLength += (word16)length;
1507615124

1507715125
return ret;
1507815126
}
@@ -15083,10 +15131,20 @@ static int TLSX_Write(TLSX* list, byte* output, byte* semaphore,
1508315131
{
1508415132
int ret = 0;
1508515133
TLSX* extension;
15086-
word16 offset = 0;
15087-
word16 length_offset = 0;
15134+
/* Use word32 to symmetrize with TLSX_GetSize -- a single extension can
15135+
* contribute up to 0x10003 bytes (4-byte type/length header + 0xFFFF
15136+
* payload), which would word16-overflow undetectably (e.g. wrap to a
15137+
* value still above prevOffset). Per-iteration and aggregate bounds are
15138+
* checked below before truncating back into the word16 wire fields.
15139+
* Callees that take a word16* offset use the cbShim pattern (init to 0,
15140+
* then add the returned delta to the word32 accumulator). */
15141+
word32 offset = 0;
15142+
word32 length_offset = 0;
15143+
word32 prevOffset;
15144+
word16 cbShim = 0;
1508815145
byte isRequest = (msgType == client_hello ||
1508915146
msgType == certificate_request);
15147+
(void)cbShim;
1509015148

1509115149
while ((extension = list)) {
1509215150
list = extension->next;
@@ -15099,6 +15157,10 @@ static int TLSX_Write(TLSX* list, byte* output, byte* semaphore,
1509915157
if (!IS_OFF(semaphore, TLSX_ToSemaphore((word16)extension->type)))
1510015158
continue; /* skip! */
1510115159

15160+
/* Snapshot offset to detect word16 wrap within this iteration;
15161+
* see matching comment in TLSX_GetSize. */
15162+
prevOffset = offset;
15163+
1510215164
/* writes extension type. */
1510315165
c16toa((word16)extension->type, output + offset);
1510415166
offset += HELLO_EXT_TYPE_SZ + OPAQUE16_LEN;
@@ -15202,28 +15264,36 @@ static int TLSX_Write(TLSX* list, byte* output, byte* semaphore,
1520215264
#if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
1520315265
case TLSX_ENCRYPT_THEN_MAC:
1520415266
WOLFSSL_MSG("Encrypt-Then-Mac extension to write");
15205-
ret = ETM_WRITE(extension->data, output, msgType, &offset);
15267+
cbShim = 0;
15268+
ret = ETM_WRITE(extension->data, output, msgType, &cbShim);
15269+
offset += cbShim;
1520615270
break;
1520715271
#endif /* HAVE_ENCRYPT_THEN_MAC */
1520815272

1520915273
#if defined(WOLFSSL_TLS13) || !defined(WOLFSSL_NO_TLS12) || !defined(NO_OLD_TLS)
1521015274
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
1521115275
case TLSX_PRE_SHARED_KEY:
1521215276
WOLFSSL_MSG("Pre-Shared Key extension to write");
15277+
cbShim = 0;
1521315278
ret = PSK_WRITE((PreSharedKey*)extension->data, output + offset,
15214-
msgType, &offset);
15279+
msgType, &cbShim);
15280+
offset += cbShim;
1521515281
break;
1521615282

1521715283
#ifdef WOLFSSL_TLS13
1521815284
case TLSX_PSK_KEY_EXCHANGE_MODES:
1521915285
WOLFSSL_MSG("PSK Key Exchange Modes extension to write");
15286+
cbShim = 0;
1522015287
ret = PKM_WRITE((byte)extension->val, output + offset, msgType,
15221-
&offset);
15288+
&cbShim);
15289+
offset += cbShim;
1522215290
break;
1522315291
#ifdef WOLFSSL_CERT_WITH_EXTERN_PSK
1522415292
case TLSX_CERT_WITH_EXTERN_PSK:
1522515293
WOLFSSL_MSG("Cert with external PSK extension to write");
15226-
ret = PSK_WITH_CERT_WRITE(output + offset, msgType, &offset);
15294+
cbShim = 0;
15295+
ret = PSK_WITH_CERT_WRITE(output + offset, msgType, &cbShim);
15296+
offset += cbShim;
1522715297
break;
1522815298
#endif
1522915299
#endif
@@ -15237,28 +15307,36 @@ static int TLSX_Write(TLSX* list, byte* output, byte* semaphore,
1523715307
#ifdef WOLFSSL_TLS13
1523815308
case TLSX_SUPPORTED_VERSIONS:
1523915309
WOLFSSL_MSG("Supported Versions extension to write");
15310+
cbShim = 0;
1524015311
ret = SV_WRITE(extension->data, output + offset, msgType,
15241-
&offset);
15312+
&cbShim);
15313+
offset += cbShim;
1524215314
break;
1524315315

1524415316
case TLSX_COOKIE:
1524515317
WOLFSSL_MSG("Cookie extension to write");
15318+
cbShim = 0;
1524615319
ret = CKE_WRITE((Cookie*)extension->data, output + offset,
15247-
msgType, &offset);
15320+
msgType, &cbShim);
15321+
offset += cbShim;
1524815322
break;
1524915323

1525015324
#ifdef WOLFSSL_EARLY_DATA
1525115325
case TLSX_EARLY_DATA:
1525215326
WOLFSSL_MSG("Early Data extension to write");
15327+
cbShim = 0;
1525315328
ret = EDI_WRITE(extension->val, output + offset, msgType,
15254-
&offset);
15329+
&cbShim);
15330+
offset += cbShim;
1525515331
break;
1525615332
#endif
1525715333

1525815334
#ifdef WOLFSSL_POST_HANDSHAKE_AUTH
1525915335
case TLSX_POST_HANDSHAKE_AUTH:
1526015336
WOLFSSL_MSG("Post-Handshake Authentication extension to write");
15261-
ret = PHA_WRITE(output + offset, msgType, &offset);
15337+
cbShim = 0;
15338+
ret = PHA_WRITE(output + offset, msgType, &cbShim);
15339+
offset += cbShim;
1526215340
break;
1526315341
#endif
1526415342

@@ -15314,16 +15392,26 @@ static int TLSX_Write(TLSX* list, byte* output, byte* semaphore,
1531415392
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
1531515393
case TLSX_ECH:
1531615394
WOLFSSL_MSG("ECH extension to write");
15395+
cbShim = 0;
1531715396
ret = ECH_WRITE((WOLFSSL_ECH*)extension->data, msgType,
15318-
output + offset, &offset);
15397+
output + offset, &cbShim);
15398+
offset += cbShim;
1531915399
break;
1532015400
#endif
1532115401
default:
1532215402
break;
1532315403
}
1532415404

15405+
/* Per-extension data length is a 2-byte wire field; reject any
15406+
* single extension whose payload exceeds that before truncating. */
15407+
if (offset - length_offset > WOLFSSL_MAX_16BIT) {
15408+
WOLFSSL_MSG("TLSX_Write single extension length exceeds word16");
15409+
return BUFFER_E;
15410+
}
15411+
1532515412
/* writes extension data length. */
15326-
c16toa(offset - length_offset, output + length_offset - OPAQUE16_LEN);
15413+
c16toa((word16)(offset - length_offset),
15414+
output + length_offset - OPAQUE16_LEN);
1532715415

1532815416
/* marks the extension as processed so ctx level */
1532915417
/* extensions don't overlap with ssl level ones. */
@@ -15332,9 +15420,24 @@ static int TLSX_Write(TLSX* list, byte* output, byte* semaphore,
1533215420
/* if we encountered an error propagate it */
1533315421
if (ret != 0)
1533415422
break;
15423+
15424+
if (offset <= prevOffset) {
15425+
WOLFSSL_MSG("TLSX_Write extension made no progress");
15426+
return BUFFER_E;
15427+
}
1533515428
}
1533615429

15337-
*pOffset += offset;
15430+
/* Only validate and commit the aggregate offset when the loop
15431+
* completed without error; on the error path, leave *pOffset
15432+
* unchanged and return the original failure reason so callers
15433+
* see the real error instead of a masking BUFFER_E. */
15434+
if (ret == 0) {
15435+
if ((word32)*pOffset + offset > WOLFSSL_MAX_16BIT) {
15436+
WOLFSSL_MSG("TLSX_Write total extensions length exceeds word16");
15437+
return BUFFER_E;
15438+
}
15439+
*pOffset += (word16)offset;
15440+
}
1533815441

1533915442
return ret;
1534015443
}
@@ -16438,6 +16541,13 @@ int TLSX_GetRequestSize(WOLFSSL* ssl, byte msgType, word32* pLength)
1643816541
}
1643916542
#endif
1644016543

16544+
/* The TLS extensions block length prefix is a 2-byte field, so any
16545+
* accumulated total above 0xFFFF must be rejected rather than silently
16546+
* truncating and producing a short, malformed handshake message. */
16547+
if (length > (word16)(WOLFSSL_MAX_16BIT - OPAQUE16_LEN)) {
16548+
WOLFSSL_MSG("TLSX_GetRequestSize extensions exceed word16");
16549+
return BUFFER_E;
16550+
}
1644116551
if (length)
1644216552
length += OPAQUE16_LEN; /* for total length storage. */
1644316553

@@ -16641,6 +16751,12 @@ int TLSX_WriteRequest(WOLFSSL* ssl, byte* output, byte msgType, word32* pOffset)
1664116751
#endif
1664216752
#endif
1664316753

16754+
/* Wrap detection for the TLSX_Write calls above is handled inside
16755+
* TLSX_Write itself: any iteration that would push the local word16
16756+
* offset past 0xFFFF returns BUFFER_E so we never reach here with a
16757+
* truncated value. The TLS extensions block length prefix on the
16758+
* wire is a 2-byte field, matching this invariant. */
16759+
1664416760
if (offset > OPAQUE16_LEN || msgType != client_hello)
1664516761
c16toa(offset - OPAQUE16_LEN, output); /* extensions length */
1664616762

src/tls13.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3279,6 +3279,10 @@ int BuildTls13Message(WOLFSSL* ssl, byte* output, int outSz, const byte* input,
32793279

32803280
WOLFSSL_ENTER("BuildTls13Message");
32813281

3282+
if (ssl == NULL) {
3283+
return BAD_FUNC_ARG;
3284+
}
3285+
32823286
#ifdef WOLFSSL_ASYNC_CRYPT
32833287
ret = WC_NO_PENDING_E;
32843288
if (asyncOkay) {

0 commit comments

Comments
 (0)