Skip to content

Commit a46a2f6

Browse files
committed
tls: fix ECH heap buffer overflow via publicName SNI pollution
In TLSX_EchChangeSNI, the ctx->extensions branch set extensions unconditionally even when TLSX_Find returned NULL. This caused TLSX_UseSNI to attach the attacker-controlled publicName to the shared WOLFSSL_CTX when no inner SNI was configured. TLSX_EchRestoreSNI then failed to clean it up because its removal was gated on serverNameX != NULL. The inner ClientHello was sized before the pollution but written after it, causing TLSX_SNI_Write to memcpy 255 bytes past the allocation boundary. Fix by mirroring the guarded pattern of the ssl->extensions branch: only set extensions when TLSX_Find returns non-NULL, and only perform the SNI swap when extensions is non-NULL. Also move TLSX_Remove in TLSX_EchRestoreSNI outside the serverNameX guard so any injected publicName SNI is always cleaned up. Also return BAD_FUNC_ARG when ECH is used without an inner SNI, preventing ECH ClientHello construction in an invalid configuration. Reported by: Nicholas Carlini (Anthropic) & Thai Duong (Calif.io)
1 parent 0c54199 commit a46a2f6

File tree

1 file changed

+25
-12
lines changed

1 file changed

+25
-12
lines changed

src/tls.c

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16088,11 +16088,18 @@ static int TLSX_EchChangeSNI(WOLFSSL* ssl, TLSX** pEchX,
1608816088

1608916089
if (serverNameX == NULL && ssl->ctx && ssl->ctx->extensions) {
1609016090
serverNameX = TLSX_Find(ssl->ctx->extensions, TLSX_SERVER_NAME);
16091-
extensions = &ssl->ctx->extensions;
16091+
if (serverNameX != NULL)
16092+
extensions = &ssl->ctx->extensions;
16093+
}
16094+
16095+
/* ECH requires an inner SNI to be present for ClientHelloInner.
16096+
* Without it, fail instead of mutating extension lists. */
16097+
if (serverNameX == NULL) {
16098+
ret = BAD_FUNC_ARG;
1609216099
}
1609316100

1609416101
/* store the inner server name */
16095-
if (serverNameX != NULL) {
16102+
if (ret == 0 && serverNameX != NULL) {
1609616103
char* hostName = ((SNI*)serverNameX->data)->data.host_name;
1609716104
word32 hostNameSz = (word32)XSTRLEN(hostName) + 1;
1609816105

@@ -16103,15 +16110,19 @@ static int TLSX_EchChangeSNI(WOLFSSL* ssl, TLSX** pEchX,
1610316110
XMEMCPY(serverName, hostName, hostNameSz);
1610416111
}
1610516112

16106-
/* remove the inner server name */
16107-
TLSX_Remove(extensions, TLSX_SERVER_NAME, ssl->heap);
16113+
/* only swap the SNI if one was found; extensions is non-NULL if an
16114+
* SNI entry was found on ssl->extensions or ctx->extensions */
16115+
if (ret == 0 && extensions != NULL) {
16116+
/* remove the inner server name */
16117+
TLSX_Remove(extensions, TLSX_SERVER_NAME, ssl->heap);
1610816118

16109-
/* set the public name as the server name */
16110-
if ((ret = TLSX_UseSNI(extensions, WOLFSSL_SNI_HOST_NAME,
16111-
((WOLFSSL_ECH*)echX->data)->echConfig->publicName,
16112-
XSTRLEN(((WOLFSSL_ECH*)echX->data)->echConfig->publicName),
16113-
ssl->heap)) == WOLFSSL_SUCCESS)
16114-
ret = 0;
16119+
/* set the public name as the server name */
16120+
if ((ret = TLSX_UseSNI(extensions, WOLFSSL_SNI_HOST_NAME,
16121+
((WOLFSSL_ECH*)echX->data)->echConfig->publicName,
16122+
XSTRLEN(((WOLFSSL_ECH*)echX->data)->echConfig->publicName),
16123+
ssl->heap)) == WOLFSSL_SUCCESS)
16124+
ret = 0;
16125+
}
1611516126
}
1611616127
*pServerNameX = serverNameX;
1611716128
*pExtensions = extensions;
@@ -16124,10 +16135,12 @@ static int TLSX_EchRestoreSNI(WOLFSSL* ssl, char* serverName,
1612416135
{
1612516136
int ret = 0;
1612616137

16127-
if (serverNameX != NULL) {
16128-
/* remove the public name SNI */
16138+
/* always remove the publicName SNI we injected, regardless of whether
16139+
* there was a prior inner SNI to restore */
16140+
if (extensions != NULL)
1612916141
TLSX_Remove(extensions, TLSX_SERVER_NAME, ssl->heap);
1613016142

16143+
if (serverNameX != NULL) {
1613116144
/* restore the inner server name */
1613216145
ret = TLSX_UseSNI(extensions, WOLFSSL_SNI_HOST_NAME,
1613316146
serverName, XSTRLEN(serverName), ssl->heap);

0 commit comments

Comments
 (0)