Skip to content

Commit 3254e75

Browse files
authored
Merge pull request #10811 from kareem-wolfssl/gh10746
Correct alert type for missing supported_versions in HRR and avoid sending duplicate protocol_version alerts.
2 parents 98edc78 + 2765708 commit 3254e75

3 files changed

Lines changed: 75 additions & 62 deletions

File tree

src/internal.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33167,7 +33167,17 @@ static void MakePSKPreMasterSecret(Arrays* arrays, byte use_psk_key)
3316733167
#ifdef WOLFSSL_TLS13
3316833168
if (IsAtLeastTLSv1_3(pv)) {
3316933169
byte type = server_hello;
33170-
return DoTls13ServerHello(ssl, input, inOutIdx, helloSz, &type);
33170+
ret = DoTls13ServerHello(ssl, input, inOutIdx, helloSz, &type);
33171+
if (ret != 0 &&
33172+
ssl->alert_history.last_tx.level != alert_fatal) {
33173+
int alertType = TranslateErrorToAlert(ret);
33174+
if (alertType != invalid_alert) {
33175+
if (SendAlert(ssl, alert_fatal, alertType) ==
33176+
WC_NO_ERR_TRACE(SOCKET_ERROR_E))
33177+
ret = SOCKET_ERROR_E;
33178+
}
33179+
}
33180+
return ret;
3317133181
}
3317233182
#endif
3317333183

src/tls13.c

Lines changed: 47 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5483,7 +5483,6 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
54835483

54845484
if (args->pv.major != ssl->version.major ||
54855485
args->pv.minor != tls12minor) {
5486-
SendAlert(ssl, alert_fatal, wolfssl_alert_protocol_version);
54875486
WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
54885487
return VERSION_ERROR;
54895488
}
@@ -5567,39 +5566,42 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
55675566
}
55685567

55695568
if ((args->idx - args->begin) + OPAQUE16_LEN > helloSz) {
5570-
if (!ssl->options.downgrade) {
5571-
/* Fewer than OPAQUE16_LEN bytes remain after the compression
5572-
* method, so there is no complete extensions length field. */
5573-
if ((args->idx - args->begin) < helloSz) {
5574-
/* A partial extensions length field is genuinely malformed:
5575-
* report it as a decode error. */
5576-
WOLFSSL_MSG("Truncated extensions length in ServerHello");
5577-
return BUFFER_ERROR;
5578-
}
5579-
/* No extensions field at all, so the server is not offering TLS 1.3
5580-
* (no supported_versions extension - see RFC 8446 4.2.1) but TLS 1.2
5581-
* or below. This is a well-formed message, so a TLS 1.3-only client
5582-
* (downgrade disabled) must reject it as a version mismatch, not as
5583-
* a malformed message. Returning VERSION_ERROR makes the caller send
5584-
* a protocol_version alert (RFC 8446 6.2) rather than decode_error. */
5585-
WOLFSSL_MSG("Server offered TLS 1.2 (no supported_versions ext) "
5586-
"but downgrade not allowed");
5587-
WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
5588-
return VERSION_ERROR;
5569+
/* Fewer than OPAQUE16_LEN bytes remain after the compression method, so
5570+
* there is no complete extensions length field. */
5571+
if ((args->idx - args->begin) < helloSz) {
5572+
/* A partial extensions length field is genuinely malformed: report
5573+
* it as a decode error regardless of message type. */
5574+
WOLFSSL_MSG("Truncated extensions length in ServerHello");
5575+
return BUFFER_ERROR;
55895576
}
55905577

5578+
/* No extensions field at all. */
55915579
if (args->extMsgType == hello_retry_request) {
55925580
/* The sentinel Random (RFC 8446 4.1.3) identifies this as a TLS 1.3
55935581
* HelloRetryRequest, which MUST carry supported_versions
5594-
* (4.1.4/4.2.1). Reaching this downgrade branch means it does not,
5595-
* so the HRR is malformed. Reject it before DoServerHello would
5596-
* reinterpret the sentinel as a plain TLS 1.2 ServerHello.Random.
5597-
* The sentinel has already identified this as an HRR, so report it
5598-
* uniformly as an invalid HRR; INVALID_PARAMETER maps to the
5599-
* illegal_parameter alert. */
5582+
* (4.2.1/9.2). Its complete absence is a missing mandatory
5583+
* extension, so - consistently with the extensions-present case
5584+
* handled later - report it as missing_extension (via
5585+
* INCOMPLETE_DATA), regardless of whether a downgrade would
5586+
* otherwise be allowed. Reject here before DoServerHello would
5587+
* reinterpret the sentinel as a plain TLS 1.2 ServerHello.Random. */
56005588
WOLFSSL_MSG("HelloRetryRequest with no supported_versions");
5601-
WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER);
5602-
return INVALID_PARAMETER;
5589+
WOLFSSL_ERROR_VERBOSE(INCOMPLETE_DATA);
5590+
return INCOMPLETE_DATA;
5591+
}
5592+
5593+
if (!ssl->options.downgrade) {
5594+
/* A plain ServerHello with no extensions is not offering TLS 1.3
5595+
* (no supported_versions extension - see RFC 8446 4.2.1) but TLS
5596+
* 1.2 or below. This is a well-formed message, so a TLS 1.3-only
5597+
* client (downgrade disabled) must reject it as a version mismatch,
5598+
* not as a malformed message. Returning VERSION_ERROR makes the
5599+
* caller send a protocol_version alert (RFC 8446 6.2) rather than
5600+
* decode_error. */
5601+
WOLFSSL_MSG("Server offered TLS 1.2 (no supported_versions ext) "
5602+
"but downgrade not allowed");
5603+
WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
5604+
return VERSION_ERROR;
56035605
}
56045606
#ifndef WOLFSSL_NO_TLS12
56055607
/* Force client hello version 1.2 to work for static RSA. */
@@ -5620,14 +5622,14 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
56205622
#endif
56215623
ssl->options.haveEMS = 0;
56225624
if (args->pv.minor < ssl->options.minDowngrade) {
5623-
SendAlert(ssl, alert_fatal, wolfssl_alert_protocol_version);
5625+
WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
56245626
return VERSION_ERROR;
56255627
}
56265628
#ifndef WOLFSSL_NO_TLS12
56275629
ssl->options.tls1_3 = 0;
56285630
return DoServerHello(ssl, input, inOutIdx, helloSz);
56295631
#else
5630-
SendAlert(ssl, alert_fatal, wolfssl_alert_protocol_version);
5632+
WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
56315633
return VERSION_ERROR;
56325634
#endif
56335635
}
@@ -5649,28 +5651,27 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
56495651
return ret;
56505652
}
56515653
if (!foundVersion) {
5654+
/* RFC 8446 4.1.4: "The server's extensions MUST contain
5655+
* 'supported_versions'." (also 9.2: "supported_versions" is
5656+
* REQUIRED for all ... HelloRetryRequest messages). The HRR random
5657+
* unambiguously identifies a TLS 1.3 server, so its absence is not
5658+
* a downgrade attempt but a missing mandatory extension, which per
5659+
* the "missing_extension" alert definition must be reported as
5660+
* such. Return INCOMPLETE_DATA (which maps to a missing_extension
5661+
* alert) and let the caller emit the alert via
5662+
* TranslateErrorToAlert(). */
5663+
if (*extMsgType == hello_retry_request) {
5664+
WOLFSSL_MSG("HelloRetryRequest missing supported_versions "
5665+
"extension");
5666+
WOLFSSL_ERROR_VERBOSE(INCOMPLETE_DATA);
5667+
return INCOMPLETE_DATA;
5668+
}
56525669
if (!ssl->options.downgrade) {
56535670
WOLFSSL_MSG("Server trying to downgrade to version less than "
56545671
"TLS v1.3");
5655-
SendAlert(ssl, alert_fatal, wolfssl_alert_protocol_version);
56565672
WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
56575673
return VERSION_ERROR;
56585674
}
5659-
5660-
if (args->extMsgType == hello_retry_request) {
5661-
/* The HelloRetryRequest sentinel Random is reserved for TLS 1.3
5662-
* (RFC 8446 4.1.3), but supported_versions (which an HRR MUST
5663-
* carry, 4.1.4/4.2.1) is absent, so the message is malformed.
5664-
* Reject before the downgrade reparses the remaining extensions
5665-
* as a TLS 1.2 server_hello, where a recognized-but-not-
5666-
* permitted extension (e.g. server_name) would wrongly yield an
5667-
* unsupported_extension alert. illegal_parameter (via
5668-
* EXT_NOT_ALLOWED) is chosen over missing_extension per the
5669-
* RFC 8446 4.2 rule for such extensions. */
5670-
WOLFSSL_MSG("HelloRetryRequest without supported_versions");
5671-
WOLFSSL_ERROR_VERBOSE(EXT_NOT_ALLOWED);
5672-
return EXT_NOT_ALLOWED;
5673-
}
56745675
#if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || \
56755676
defined(WOLFSSL_WPAS_SMALL)
56765677
/* Check if client has disabled TLS 1.2 */
@@ -5686,14 +5687,12 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
56865687

56875688
if (!ssl->options.dtls &&
56885689
args->pv.minor < ssl->options.minDowngrade) {
5689-
SendAlert(ssl, alert_fatal, wolfssl_alert_protocol_version);
56905690
WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
56915691
return VERSION_ERROR;
56925692
}
56935693

56945694
if (ssl->options.dtls &&
56955695
args->pv.minor > ssl->options.minDowngrade) {
5696-
SendAlert(ssl, alert_fatal, wolfssl_alert_protocol_version);
56975696
WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
56985697
return VERSION_ERROR;
56995698
}

tests/api/test_tls13.c

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6354,8 +6354,9 @@ int test_tls13_warning_alert_is_fatal(void)
63546354
/* Regression test for a malformed HelloRetryRequest sent to a downgrade capable
63556355
* (TLS 1.2 and TLS 1.3) client. Both crafted messages bear the HelloRetryRequest
63566356
* sentinel Random but omit supported_versions, which an HRR MUST carry, so the
6357-
* client must reject them with illegal_parameter (RFC 8446 Sec. 4.1.4/4.2)
6358-
* rather than downgrading to TLS 1.2 and losing the HRR context:
6357+
* client must reject them with missing_extension (RFC 8446 Sec. 4.1.4/4.2.1/9.2
6358+
* - a missing mandatory extension) rather than downgrading to TLS 1.2 and losing
6359+
* the HRR context:
63596360
* 1. has-extensions form: carries a server_name extension (recognized but not
63606361
* permitted in an HRR). Before the fix the downgrade reparsed it as a plain
63616362
* server_hello, where server_name is allowed, and the client wrongly sent
@@ -6446,16 +6447,18 @@ int test_tls13_hrr_recognized_ext_downgrade(void)
64466447
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1,
64476448
(const char *)hrr_sni_no_sv, sizeof(hrr_sni_no_sv)), 0);
64486449

6449-
/* Handshake must fail with EXT_NOT_ALLOWED (server_name is not permitted in
6450-
* a HelloRetryRequest), not UNSUPPORTED_EXTENSION. */
6450+
/* Handshake must fail because the mandatory supported_versions extension
6451+
* is absent from the HelloRetryRequest. The HRR is rejected before the
6452+
* downgrade reparses the remaining (server_name) extension, so the result
6453+
* is the missing mandatory extension (INCOMPLETE_DATA). */
64516454
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
64526455
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1),
6453-
WC_NO_ERR_TRACE(EXT_NOT_ALLOWED));
6456+
WC_NO_ERR_TRACE(INCOMPLETE_DATA));
64546457

6455-
/* RFC 8446 Sec. 4.2: the client MUST abort with illegal_parameter (47),
6456-
* not unsupported_extension (110). */
6458+
/* RFC 8446 Sec. 4.1.4/4.2: a mandatory-but-absent extension MUST abort with
6459+
* a missing_extension (109) alert. */
64576460
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
6458-
ExpectIntEQ(h.last_tx.code, illegal_parameter);
6461+
ExpectIntEQ(h.last_tx.code, missing_extension);
64596462
ExpectIntEQ(h.last_tx.level, alert_fatal);
64606463

64616464
wolfSSL_free(ssl_c);
@@ -6464,7 +6467,7 @@ int test_tls13_hrr_recognized_ext_downgrade(void)
64646467
ctx_c = NULL;
64656468

64666469
/* Scenario 2: HelloRetryRequest sentinel with no extensions field at all.
6467-
* Must also be rejected with illegal_parameter rather than downgraded. */
6470+
* Must also be rejected with missing_extension rather than downgraded. */
64686471
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
64696472
XMEMSET(&h, 0, sizeof(h));
64706473

@@ -6474,14 +6477,15 @@ int test_tls13_hrr_recognized_ext_downgrade(void)
64746477
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1,
64756478
(const char *)hrr_no_ext, sizeof(hrr_no_ext)), 0);
64766479

6477-
/* No offending extension is present here, so the client reports the message
6478-
* as an invalid HRR; the wire alert is still illegal_parameter. */
6480+
/* An HRR with no extensions field at all is still missing the mandatory
6481+
* supported_versions extension, so - consistently with scenario 1 - the
6482+
* client aborts with missing_extension (RFC 8446 4.2.1/9.2). */
64796483
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
64806484
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1),
6481-
WC_NO_ERR_TRACE(INVALID_PARAMETER));
6485+
WC_NO_ERR_TRACE(INCOMPLETE_DATA));
64826486

64836487
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
6484-
ExpectIntEQ(h.last_tx.code, illegal_parameter);
6488+
ExpectIntEQ(h.last_tx.code, missing_extension);
64856489
ExpectIntEQ(h.last_tx.level, alert_fatal);
64866490

64876491
wolfSSL_free(ssl_c);

0 commit comments

Comments
 (0)