Skip to content

Commit c761727

Browse files
committed
Let DTLS max proto and options lift an earlier cap
wolfSSL_set_max_proto_version() and wolfSSL_clear_options() only moved a DTLS SSL down. After a maximum of DTLS 1.2, a maximum of DTLS 1.3 or 0 returned success and left the SSL on DTLS 1.2, and clearing SSL_OP_NO_DTLSv1_3 did not bring DTLS 1.3 back. When a maximum loosens the mask, or wolfSSL_clear_options() clears a DTLS version bit, the version is now walked down from the CTX method version, so the SSL moves back up. A lift never moves an allowed version down, and keeps an allowed version when the walk finds none, so a CTX maximum lowered after wolfSSL_new() does not reach the SSL that way. A resuming client keeps its session's version while the mask allows it. wolfSSL_set_options() and the checks at handshake start still only step down. wolfSSL_SetSession() now fails when the suites cannot be rebuilt for the version the mask moved it to; a session with no version left is still reported when the handshake starts. The DTLS maximum shares the WOLFSSL_OP_NO_TLSv1_2/_3 bits with SSL_OP_NO_DTLSv1_2/_3. The new options field dtlsMaxMask records the bits the maximum added itself, and a later maximum clears only those, so a version the application or the CTX masked off stays off. wolfSSL_set_options() and wolfSSL_clear_options() take a bit out of the record. The mask no longer moves the version while the first handshake is under way. DtlsHandshakeStarted() reports that from handShakeDone, the connect and accept states, got_client_hello, buffered output and, with async crypto, an async state other than TLS_ASYNC_BEGIN or handler args kept in ssl->async, since DoTls13ClientHello() clears got_client_hello when it pends and can pend before it leaves TLS_ASYNC_BEGIN. Before, an async accept that re-entered could switch to DTLS 1.2 partway through a DTLS 1.3 ClientHello, and a client could have its version raised between WANT_WRITE and the retry that sent the ClientHello. In that window a DTLS wolfSSL_set_max_proto_version() that would change the mask fails without changing anything, since it could no longer take effect, and one that changes nothing succeeds. A maximum of 0 on a DTLS SSL means the newest DTLS version, so it fails there too instead of settling on the current cap. The maximum also refuses a change after the handshake: a lift there would make a later DTLS 1.2 renegotiation treat the server's downgrade sentinel as an attack. wolfSSL_clear() lets it apply again, and now resets the async handler state in builds without TLS 1.3 too, where a stale one would leave the SSL looking started. wolfSSL_clear_options() reports a failed suite rebuild but not VERSION_ERROR, which the handshake start reports, as wolfSSL_set_options() does. wolfSSL_CTX_set_max_proto_version() raises a fixed-version DTLS method to DTLS 1.3, as it does fixed TLS methods. A DTLS 1.3 method lowered to DTLS 1.2 could not be restored before. A wolfDTLSv1_2_method() CTX given a maximum of 0 now becomes DTLS 1.3-only, as a wolfTLSv1_2_method() CTX becomes TLS 1.3-only. wolfSSL_CTX_set_max_proto_version() with DTLS1_2_VERSION or DTLS1_VERSION also sets WOLFSSL_OP_NO_TLSv1_3 and WOLFSSL_OP_NO_TLSv1_2, as the TLS versions do (WOLFSSL_OP_NO_TLSv1_3 only when DTLS 1.3 is built, at both levels, so a maximum of 0 adds no bit that means nothing), so an SSL keeps the CTX maximum it was created with when the CTX maximum is raised later. Before, such an SSL stayed on DTLS 1.2 while TLSv1_3_Capable() read DTLS 1.3 from the shared method, and it rejected the server's downgrade sentinel. wolfSSL_CTX_get_max_proto_version() returns DTLS versions for a DTLS CTX, so its result can be passed back to the setter. It honors the CTX minimum, and for a fixed-version DTLS method it returns that version, or 0 when the mask or the minimum excludes it. It shares the version walk with DtlsAllowedMinor() through the new DtlsMaskAllowedMinor(), which takes the mask directly. wolfSSL_CTX_set_min_proto_version() and wolfSSL_CTX_set_max_proto_version() fail on a DTLS CTX when the mask and the minimum leave no DTLS version, or, for a fixed-version DTLS method, leave out the method's own version, as the TLS setters do. Before, they returned success and every later wolfSSL_new() failed with VERSION_ERROR. A rejected DTLS call restores the mask, the minimum, the method version and the min/max flags, so the CTX keeps working. wolfSSL_set_max_proto_version() refuses a TLS version on a DTLS SSL, like the CTX setter and OpenSSL. On a fixed-version DTLS SSL it also refuses a maximum below the SSL's version, judged by the bits the maximum adds, so an option the application set does not make every later maximum fail. Before, the bit was set and the SSL kept its version, so the ClientHello went out with an empty supported_versions list. DtlsCheckVersionMask() takes a mode (handshake start, lift, or the server's step-down inside DoClientHello()), so every caller goes through the same checks, and the DTLS mask helpers move above the OPENSSL_EXTRA block so the setters can use them. DtlsMaskVersion() no longer records VERSION_ERROR itself, so the option setters that ignore it leave nothing on the error queue; DoClientHello() records it where it sends the alert. When DtlsCheckVersionMask() lifts a server SSL to DTLS 1.3, it runs InitSSL_DtlsServer() again, since the DTLS 1.3 cookie secret and ClientHello fragmentation are only set up for the version the SSL was created with. Without them every DTLS 1.3 handshake of a server created under SSL_OP_NO_DTLSv1_3 and lifted later fails with COOKIE_ERROR. InitSSL_DtlsServer() only fills in what is missing. Tests: a server created under a CTX SSL_OP_NO_DTLSv1_3 and lifted, with a plain and a fragmented post-quantum ClientHello, a maximum of 0 after a handshake and a lower one after wolfSSL_clear(), a DTLS 1.2 cap lifted by a maximum, by a maximum of 0 and by clearing the option, a maximum lifted by clearing the option, a DTLS 1.0 cap lifted to DTLS 1.2, a lift under a CTX maximum lowered to DTLS 1.0 that keeps DTLS 1.2, the CTX maximum of DTLS 1.0, fixed DTLS 1.3 and DTLS 1.2 CTXs raised to DTLS 1.3, a TLS 1.2 maximum refused on a DTLS SSL, CTX and user options that survive later maximums, a maximum and option calls while the first ClientHello waits on WANT_WRITE, a maximum after the ClientHello was sent, maximums after the handshake and after wolfSSL_clear(), maximums that repeat, lift or tighten from a server SNI callback, a maximum of 0 mid-handshake, the CTX maximum read back and set again, a lower maximum refused and an equal one or 0 accepted on a fixed DTLS 1.3 SSL with SSL_OP_NO_DTLSv1_3 set and on a fixed DTLS 1.2 SSL, a DTLS 1.3 CTX minimum refused under SSL_OP_NO_DTLSv1_3 and a DTLS 1.2 CTX maximum refused under a DTLS 1.3 minimum, both leaving a working CTX, a minimum refused on a fixed DTLS 1.3 method that masks its own version, the CTX maximum of a fixed DTLS 1.3 method that masks its own version, a CTX maximum raised after wolfSSL_new(), option and maximum calls after the CTX maximum was lowered, the return value of wolfSSL_clear_options() when no version is left, the exact error for a DTLS 1.3-only client against a DTLS 1.2 server, and a TLS 1.3 minimum refused on a DTLS object without DTLS 1.3.
1 parent 0ab9ab5 commit c761727

8 files changed

Lines changed: 856 additions & 113 deletions

File tree

‎src/internal.c‎

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2818,10 +2818,6 @@ void InitSSL_Method(WOLFSSL_METHOD* method, ProtocolVersion pv)
28182818
method->downgrade = 0;
28192819
}
28202820

2821-
#if defined(WOLFSSL_DTLS) && !defined(NO_WOLFSSL_SERVER)
2822-
static int InitSSL_DtlsServer(WOLFSSL* ssl);
2823-
#endif /* WOLFSSL_DTLS && !NO_WOLFSSL_SERVER */
2824-
28252821
#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_EITHER_SIDE) || \
28262822
defined(WOLFSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL)
28272823
int InitSSL_Side(WOLFSSL* ssl, word16 side)
@@ -8218,15 +8214,8 @@ byte DtlsMinDowngrade(byte minDowngrade)
82188214

82198215
/* Returns the newest built DTLS minor, no newer than maxMinor, that the version
82208216
* mask and minDowngrade allow, or 0. DTLS minors count down. */
8221-
byte DtlsAllowedMinor(const WOLFSSL* ssl, byte maxMinor, byte minDowngrade)
8217+
byte DtlsMaskAllowedMinor(unsigned long mask, byte maxMinor, byte minDowngrade)
82228218
{
8223-
#if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || \
8224-
defined(WOLFSSL_WPAS_SMALL)
8225-
unsigned long mask = ssl->options.mask;
8226-
#else
8227-
unsigned long mask = 0;
8228-
#endif
8229-
82308219
#ifdef WOLFSSL_DTLS13
82318220
if (maxMinor <= DTLSv1_3_MINOR &&
82328221
(mask & WOLFSSL_OP_NO_TLSv1_3) == 0) {
@@ -8246,12 +8235,23 @@ byte DtlsAllowedMinor(const WOLFSSL* ssl, byte maxMinor, byte minDowngrade)
82468235
return DTLS_MINOR;
82478236
}
82488237
#endif
8249-
(void)ssl;
82508238
(void)mask;
82518239
(void)maxMinor;
82528240
(void)minDowngrade;
82538241
return 0;
82548242
}
8243+
8244+
/* DtlsMaskAllowedMinor() with the SSL's own version mask. */
8245+
byte DtlsAllowedMinor(const WOLFSSL* ssl, byte maxMinor, byte minDowngrade)
8246+
{
8247+
#if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || \
8248+
defined(WOLFSSL_WPAS_SMALL)
8249+
return DtlsMaskAllowedMinor(ssl->options.mask, maxMinor, minDowngrade);
8250+
#else
8251+
(void)ssl;
8252+
return DtlsMaskAllowedMinor(0, maxMinor, minDowngrade);
8253+
#endif
8254+
}
82558255
#endif
82568256

82578257
/* This function inherits a WOLFSSL_CTX's fields into an SSL object.
@@ -9288,7 +9288,7 @@ static void InitSSL_Tls13Options(WOLFSSL* ssl, WOLFSSL_CTX* ctx)
92889288
* @return 0 on success, and when there is nothing to do.
92899289
* @return A negative error code when a secret cannot be generated.
92909290
*/
9291-
static int InitSSL_DtlsServer(WOLFSSL* ssl)
9291+
int InitSSL_DtlsServer(WOLFSSL* ssl)
92929292
{
92939293
int ret;
92949294

@@ -44460,8 +44460,9 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl)
4446044460
}
4446144461

4446244462
#ifdef WOLFSSL_DTLS
44463-
ret = DtlsCheckVersionMask(ssl);
44463+
ret = DtlsCheckVersionMask(ssl, DTLS_MASK_IN_HELLO);
4446444464
if (ret == WC_NO_ERR_TRACE(VERSION_ERROR)) {
44465+
WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
4446544466
/* propagate socket errors to avoid re-calling send alert */
4446644467
if (SendAlert(ssl, alert_fatal, wolfssl_alert_protocol_version)
4446744468
== WC_NO_ERR_TRACE(SOCKET_ERROR_E)) {

0 commit comments

Comments
 (0)