Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions doc/dox_comments/header_files/ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -6733,6 +6733,9 @@ long wolfSSL_CTX_set_tlsext_opaque_prf_input_callback_arg(
| SSL_OP_NO_TLSv1_1 | Disable TLS 1.1 |
| SSL_OP_NO_TLSv1_2 | Disable TLS 1.2 |
| SSL_OP_NO_TLSv1_3 | Disable TLS 1.3 (requires WOLFSSL_TLS13) |
| SSL_OP_NO_DTLSv1 | Disable DTLS 1.0; same bit as SSL_OP_NO_TLSv1_1, so it disables TLS 1.1 on a TLS object |
| SSL_OP_NO_DTLSv1_2 | Disable DTLS 1.2; same bit as SSL_OP_NO_TLSv1_2 |
| SSL_OP_NO_DTLSv1_3 | Disable DTLS 1.3 (requires WOLFSSL_DTLS13); same bit as SSL_OP_NO_TLSv1_3 |
| SSL_OP_NO_COMPRESSION | Disable record-layer compression (no-op unless HAVE_LIBZ) |
| SSL_OP_NO_TICKET | Disable RFC 5077 session tickets (TLS 1.2 only; TLS 1.3 ignores this flag); requires HAVE_SESSION_TICKET and (OPENSSL_EXTRA or HAVE_WEBSERVER or WOLFSSL_WPAS_SMALL) |
| SSL_OP_NO_RENEGOTIATION | Reject peer-initiated renegotiation |
Expand Down
123 changes: 118 additions & 5 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -2816,10 +2816,6 @@ void InitSSL_Method(WOLFSSL_METHOD* method, ProtocolVersion pv)
method->downgrade = 0;
}

#if defined(WOLFSSL_DTLS) && !defined(NO_WOLFSSL_SERVER)
static int InitSSL_DtlsServer(WOLFSSL* ssl);
#endif /* WOLFSSL_DTLS && !NO_WOLFSSL_SERVER */

#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_EITHER_SIDE) || \
defined(WOLFSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL)
int InitSSL_Side(WOLFSSL* ssl, word16 side)
Expand Down Expand Up @@ -8075,6 +8071,61 @@ int InitSSL_Suites(WOLFSSL* ssl)
return WOLFSSL_SUCCESS;
}

#ifdef WOLFSSL_DTLS
/* Maps a TLS minimum to the DTLS version that shares its mask bit. */
byte DtlsMinDowngrade(byte minDowngrade)
{
if (minDowngrade == TLSv1_3_MINOR)
return DTLSv1_3_MINOR;
if (minDowngrade == TLSv1_2_MINOR)
return DTLSv1_2_MINOR;
if (minDowngrade < DTLSv1_3_MINOR)
return DTLS_MINOR;
return minDowngrade;
}

/* Returns the newest built DTLS minor, no newer than maxMinor, that the version
* mask and minDowngrade allow, or 0. DTLS minors count down. */
byte DtlsMaskAllowedMinor(unsigned long mask, byte maxMinor, byte minDowngrade)
{
#ifdef WOLFSSL_DTLS13
if (maxMinor <= DTLSv1_3_MINOR &&
(mask & WOLFSSL_OP_NO_TLSv1_3) == 0) {
return DTLSv1_3_MINOR;
}
#endif
#ifndef WOLFSSL_NO_TLS12
if (maxMinor <= DTLSv1_2_MINOR &&
(mask & WOLFSSL_OP_NO_TLSv1_2) == 0 &&
minDowngrade >= DTLSv1_2_MINOR) {
return DTLSv1_2_MINOR;
}
#endif
#ifndef NO_OLD_TLS
if ((mask & WOLFSSL_OP_NO_TLSv1_1) == 0 &&
minDowngrade >= DTLS_MINOR) {
return DTLS_MINOR;
}
#endif
(void)mask;
(void)maxMinor;
(void)minDowngrade;
return 0;
}

/* DtlsMaskAllowedMinor() with the SSL's own version mask. */
byte DtlsAllowedMinor(const WOLFSSL* ssl, byte maxMinor, byte minDowngrade)
{
#if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || \
defined(WOLFSSL_WPAS_SMALL)
return DtlsMaskAllowedMinor(ssl->options.mask, maxMinor, minDowngrade);
#else
(void)ssl;
return DtlsMaskAllowedMinor(0, maxMinor, minDowngrade);
#endif
}
#endif

/* This function inherits a WOLFSSL_CTX's fields into an SSL object.
It is used during initialization and to switch an ssl's CTX with
wolfSSL_Set_SSL_CTX. Requires ssl->suites alloc and ssl-arrays with PSK
Expand Down Expand Up @@ -8103,6 +8154,25 @@ static int SetSSL_CTX_CheckVersion(WOLFSSL* ssl, WOLFSSL_CTX* ctx)
ssl->version.minor = TLSv1_2_MINOR;
}
#endif
#ifdef WOLFSSL_DTLS13
if (ssl->version.major == DTLS_MAJOR &&
ssl->version.minor == DTLSv1_3_MINOR &&
(ssl->options.mask & WOLFSSL_OP_NO_TLSv1_3) ==
WOLFSSL_OP_NO_TLSv1_3) {
byte minor = 0;

if (ctx->method->downgrade)
minor = DtlsAllowedMinor(ssl, DTLSv1_2_MINOR, ctx->minDowngrade);
if (minor == 0) {
WOLFSSL_MSG("\tInconsistent protocol options. DTLS 1.3 set but "
"not allowed and no lower DTLS version allowed.");
WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
return VERSION_ERROR;
}
WOLFSSL_MSG("\tOption set to not allow DTLSv1.3, Downgrading");
ssl->version.minor = minor;
}
#endif
if (ssl->version.minor == TLSv1_2_MINOR &&
(ssl->options.mask & WOLFSSL_OP_NO_TLSv1_2) == WOLFSSL_OP_NO_TLSv1_2) {
Comment on lines +8157 to 8177

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Openssl doesn't use SSL_OP_NO_TLSv1_3 for DTLS. Why are we allowing this?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OpenSSL does now. 4.1, the first release with DTLS 1.3, defines SSL_OP_NO_DTLSv1_3 as SSL_OP_BIT(29), the same bit as SSL_OP_NO_TLSv1_3, and uses it as the mask of the DTLS 1.3 methods (ssl/methods.c). DTLS_method() picks its version in ssl_get_min_max_version() when the handshake starts, skipping masked versions, so SSL_OP_NO_DTLSv1_3 gives DTLS 1.2 there.

wolfSSL master already did half of this: TLSX_SupportedVersions_GetSize() drops DTLS 1.3 from supported_versions under WOLFSSL_OP_NO_TLSv1_3, but the SSL stayed on DTLS 1.3 and still sent a TLS 1.3 key_share and the TLS 1.3-only groups. The PR now follows OpenSSL. wolfSSL_set_options() and wolfSSL_set_session() move the version straight away. wolfSSL_connect()/wolfSSL_accept() and the _TLSv13 entry points check the accumulated mask again when the handshake starts (DtlsCheckVersionMask()), so the order of the calls doesn't matter. On the server, the supported_versions parse only picks versions the mask allows. I also added SSL_OP_NO_DTLSv1_2 and SSL_OP_NO_DTLSv1_3, which use the same bits as in OpenSSL. I left out SSL_OP_NO_DTLSv1: OpenSSL defines it as the SSL_OP_NO_TLSv1 bit, while wolfSSL masks DTLS 1.0 with WOLFSSL_OP_NO_TLSv1_1, so the alias would behave differently.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, the OpenSSL 4.1 behavior checks out.

On SSL_OP_NO_DTLSv1: WOLFSSL_OP_NO_TLSv1_1 is 0x04000000, bit 26. That’s the same value OpenSSL uses for SSL_OP_NO_DTLSv1. So #define SSL_OP_NO_DTLSv1 WOLFSSL_OP_NO_TLSv1_1 would match OpenSSL’s value and mask DTLS 1.0 the way wolfSSL does. The only difference is on a TLS context, where it would mask TLS 1.1 rather than TLS 1.0. Code that sets a DTLS option on a TLS context should be rare.

Without the alias, code ported from OpenSSL that uses SSL_OP_NO_DTLSv1 won’t compile. I’d add the alias and document the TLS-context difference. What do you think?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, I had the bits wrong. wolfSSL's WOLFSSL_OP_NO_TLSv1_1 is OpenSSL's SSL_OP_NO_TLSv1/SSL_OP_NO_DTLSv1 value, so the alias matches OpenSSL's value and wolfSSL's DTLS 1.0 masking. Added SSL_OP_NO_DTLSv1 as WOLFSSL_OP_NO_TLSv1_1, and the wolfSSL_CTX_set_options() doc table now lists the three DTLS options, with the TLS 1.1 effect on a TLS object called out. test_dtls13_no_tls13_option uses the new name.

if (!ctx->method->downgrade) {
Expand Down Expand Up @@ -9044,7 +9114,7 @@ static void InitSSL_Tls13Options(WOLFSSL* ssl, WOLFSSL_CTX* ctx)
* @return 0 on success, and when there is nothing to do.
* @return A negative error code when a secret cannot be generated.
*/
static int InitSSL_DtlsServer(WOLFSSL* ssl)
int InitSSL_DtlsServer(WOLFSSL* ssl)
{
int ret;

Expand Down Expand Up @@ -36320,6 +36390,17 @@ static int IsTls13CipherSuite(byte first, byte second)
}
}

#ifdef WOLFSSL_DTLS
if (ssl->options.dtls && ssl->options.downgrade &&
!ssl->options.handShakeDone &&
DtlsAllowedMinor(ssl, ssl->version.minor, DTLS_MINOR) !=
ssl->version.minor) {
WOLFSSL_MSG("\tOption set to not allow this DTLS version");
WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
return VERSION_ERROR;
}
#endif

/* check if option is set to not allow the current version
* set from either wolfSSL_set_options or wolfSSL_CTX_set_options */
if (!ssl->options.dtls && ssl->options.downgrade &&
Expand Down Expand Up @@ -36418,6 +36499,24 @@ static int IsTls13CipherSuite(byte first, byte second)
}
#endif

#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
if (ssl->options.echAccepted &&
ssl->msgsReceived.got_hello_retry_request) {
WOLFSSL_MSG("ECH accepted in HelloRetryRequest, ServerHello is "
"not TLS 1.3");
WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER);
if (SendAlert(ssl, alert_fatal, illegal_parameter) ==
WC_NO_ERR_TRACE(SOCKET_ERROR_E)) {
return SOCKET_ERROR_E;
}
return INVALID_PARAMETER;
}
/* A server negotiating TLS 1.2 or older answers the outer hello. */
ret = TLSX_EchReplaceExtensions(ssl, 0);
if (ret != 0)
return ret;
#endif

/* random */
XMEMCPY(ssl->arrays->serverRandom, input + i, RAN_LEN);
i += RAN_LEN;
Expand Down Expand Up @@ -43124,6 +43223,20 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl)
TRUE, TRUE, TRUE, TRUE, ssl->options.side);
}

#ifdef WOLFSSL_DTLS
ret = DtlsCheckVersionMask(ssl, DTLS_MASK_IN_HELLO);
if (ret == WC_NO_ERR_TRACE(VERSION_ERROR)) {
WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
/* propagate socket errors to avoid re-calling send alert */
if (SendAlert(ssl, alert_fatal, wolfssl_alert_protocol_version)
== WC_NO_ERR_TRACE(SOCKET_ERROR_E)) {
ret = SOCKET_ERROR_E;
}
}
if (ret != 0)
goto out;
#endif

/* Version the record layer is on before the mask walk below steps it
* down. It is one the client offered (or the server's own maximum
* when the client offered more), so the two alert exits inside that
Expand Down
Loading
Loading