Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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 @@ -7660,6 +7660,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
155 changes: 143 additions & 12 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -2818,10 +2818,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 @@ -8053,7 +8049,7 @@ int wolfSSL_CTX_IsPrivatePkSet(WOLFSSL_CTX* ctx)
}
#endif /* HAVE_PK_CALLBACKS */

static void InitSuites_EitherSide(Suites* suites, ProtocolVersion pv, int keySz,
void InitSuites_EitherSide(Suites* suites, ProtocolVersion pv, int keySz,
word16 haveRSA, word16 havePSK, word16 haveDH, word16 haveECDSAsig,
word16 haveECC, word16 haveStaticECC,
word16 haveAnon,
Expand Down Expand Up @@ -8097,6 +8093,20 @@ void InitSSL_CTX_Suites(const WOLFSSL_CTX* ctx, Suites* suites)
haveAnon, ctx->method->side);
}

/* Returns the newest version a downgrade SSL can be moved back up to. DTLS
* minors count down. */
ProtocolVersion SslNewestVersion(const WOLFSSL* ssl)
{
ProtocolVersion pv = ssl->version;
byte methodMinor = ssl->ctx->method->version.minor;

if (ssl->options.downgrade && (ssl->options.dtls ?
methodMinor < pv.minor : methodMinor > pv.minor)) {
pv = ssl->ctx->method->version;
}
return pv;
}

int InitSSL_Suites(WOLFSSL* ssl)
{
int keySz = 0;
Expand Down Expand Up @@ -8155,6 +8165,8 @@ int InitSSL_Suites(WOLFSSL* ssl)
havePSK, ssl->options.haveDH, ssl->options.haveECDSAsig,
ssl->options.haveECC, ssl->options.haveStaticECC,
ssl->options.useAnon, ssl->options.side);
if (FilterSuitesForVersion(ssl) != 0)
return MEMORY_E;
}

#if !defined(NO_CERTS) && !defined(WOLFSSL_SESSION_EXPORT)
Expand Down Expand Up @@ -8203,6 +8215,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 @@ -8231,6 +8298,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 +8301 to 8321

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 @@ -9218,7 +9304,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 @@ -10438,6 +10524,8 @@ void FreeSuites(WOLFSSL* ssl)
#endif
XFREE(ssl->suites, ssl->heap, DYNAMIC_TYPE_SUITES);
ssl->suites = NULL;
XFREE(ssl->suitesUser, ssl->heap, DYNAMIC_TYPE_SUITES);
ssl->suitesUser = NULL;
}


Expand Down Expand Up @@ -34780,7 +34868,8 @@ int SetCipherList_ex(const WOLFSSL_CTX* ctx, const WOLFSSL* ssl,
side = ctx->method->side;
}
else if (ssl != NULL) {
version = ssl->version;
/* A later lift needs the suites of the newer version in the list. */
version = SslNewestVersion(ssl);
#ifndef NO_CERTS
privateKeySz = ssl->buffers.keySz;
#endif
Expand Down Expand Up @@ -35062,9 +35151,7 @@ int SetSuitesHashSigAlgo(Suites* suites, const char* list)
const char* s = list;
byte sig_alg = 0;
byte mac_alg = no_mac;

/* Setting is destructive on error. */
suites->hashSigAlgoSz = 0;
byte hashSigAlgo[WOLFSSL_MAX_SIGALGO];

do {
if (*list == '+') {
Expand Down Expand Up @@ -35104,7 +35191,7 @@ int SetSuitesHashSigAlgo(Suites* suites, const char* list)
break;
}
}
AddSuiteHashSigAlgo(suites->hashSigAlgo, mac_alg, sig_alg, 0, &idx);
AddSuiteHashSigAlgo(hashSigAlgo, mac_alg, sig_alg, 0, &idx);
sig_alg = 0;
mac_alg = no_mac;
s = list + 1;
Expand All @@ -35117,7 +35204,8 @@ int SetSuitesHashSigAlgo(Suites* suites, const char* list)
if (s != list && (sig_alg != 0 || mac_alg != 0)) {
ret = 0;
}
else {
if (ret == 1) {
XMEMCPY(suites->hashSigAlgo, hashSigAlgo, idx);
suites->hashSigAlgoSz = idx;
}

Expand Down Expand Up @@ -37466,6 +37554,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 @@ -37564,6 +37663,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 @@ -44372,6 +44489,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