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
50 changes: 50 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,19 @@
configuration now sees neither the prototype nor the `client_cert_cb`
typedef instead of failing to build; no other configuration changes.

* **Behavioral change (`wolfSSL_write_early_data` and the AEAD key usage
limit)**: RFC 9846, Section 5.5 adds that "it is not possible to perform a
KeyUpdate for early data; therefore, implementations MUST NOT exceed the
limits when sending early data". Reaching the limit mid-early-data
previously drove the ordinary rekey path, which emitted a KeyUpdate while
the client was still in `CLIENT_HELLO_COMPLETE` - before the handshake had
finished, where a conforming peer must reject it. The write now fails
instead, returning `WOLFSSL_FATAL_ERROR` with `wolfSSL_get_error()`
reporting `TOO_MUCH_EARLY_DATA`. Callers that hit it should complete the
handshake and send the remainder with `wolfSSL_write()`, which rekeys
normally. Reaching the limit needs roughly 23.7 million early data records
on one connection, so no practical caller is affected.

## Fixes

* **Fix (certificate manager left pointing at a released store)**:
Expand Down Expand Up @@ -198,6 +211,43 @@
than withdrawing them, matching what a failure to allocate the `ecc_key`
already did. Only affects builds with `WOLFSSL_BLIND_PRIVATE_KEY`.

* **Fix (fatal-level `user_canceled` closed a TLS 1.3 connection)**: RFC 9846,
Section 6.1 states that this alert "generally has AlertLevel=warning" and
that "receiving implementations SHOULD continue to read data from the peer
until a 'close_notify' is received". wolfSSL already exempted
`user_canceled` from the TLS 1.3 rule that all error alerts are fatal, but
both `DoAlert()` and `DoProcessAlertRecord()` acted on the AlertLevel byte
before reaching those exemptions, so a peer sending the alert at fatal level
tore the connection down and invalidated the session. The level byte
carries no meaning in TLS 1.3, and the alert is now ignored whichever level
the peer used. TLS 1.2 and earlier are unchanged: a fatal-level alert
remains fatal there.

* **Fix (key update cap turned a peer's `update_requested` into a fatal
error)**: RFC 9846, Section 4.7.3 adds that a sender at the 2^48-1 key
update cap "MUST NOT send its own KeyUpdate ... and SHOULD instead ignore
the 'update_requested' flag". Responding to a peer's request went through
the ordinary send path, which refuses at the cap with `BAD_STATE_E`, and
that error propagated out and killed the connection. The request is now
dropped and the connection continues on its current keys until the Section
5.5 data limits force it closed. An application-initiated
`wolfSSL_update_keys()` at the cap still reports `BAD_STATE_E`; the rule
applies only to responding to a peer.

* **Fix (malformed extension aborted without sending `decode_error`)**: RFC
9846, Section 4.3 adds that trailing data in an extension is forbidden and
that "receivers MUST abort the handshake with a 'decode_error' alert if
there is data left over after parsing the structure". The extension parsers
detect malformed structures, but around a third of them report it as the
wolfCrypt `BUFFER_E` rather than `BUFFER_ERROR`, and only `BUFFER_ERROR` was
mapped to an alert. `TranslateErrorToAlert()` returned `invalid_alert` for
`BUFFER_E`, which every caller treats as "send nothing", so the handshake
aborted correctly but silently and the peer saw only a dropped connection.
Both codes now map to `decode_error`. This affects `pre_shared_key`,
`psk_key_exchange_modes`, `early_data`, `cookie`, `post_handshake_auth` and
the certificate type extensions, and more generally any malformed handshake
message reported with `BUFFER_E`.

# wolfSSL Release 5.9.2 (Jun 23, 2026)

Release 5.9.2 has been developed according to wolfSSL's development and QA
Expand Down
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -6047,7 +6047,7 @@ fi

# OLD TLS
AC_ARG_ENABLE([oldtls],
[AS_HELP_STRING([--enable-oldtls],[Enable old TLS versions < 1.2 (default: disabled)])],
[AS_HELP_STRING([--enable-oldtls],[Enable old TLS versions < 1.2, which RFC 8996 and RFC 9846 forbid negotiating (default: disabled)])],
[ ENABLED_OLD_TLS=$enableval ],
[ ENABLED_OLD_TLS=no ]
)
Expand Down
22 changes: 20 additions & 2 deletions doc/dox_comments/header_files/ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2584,6 +2584,11 @@ int wolfSSL_shutdown(WOLFSSL* ssl);

\param ssl pointer to the SSL session, created with wolfSSL_new().

\note RFC 9846, Section 6.1 requires this alert to be followed by a
close notify, which is why the shutdown is part of this call. On the
receiving side the alert is not itself an error: a TLS 1.3 peer keeps
reading until the close notify arrives, whatever AlertLevel was used.

_Example_
\code
int ret = 0;
Expand Down Expand Up @@ -14770,6 +14775,13 @@ int wolfSSL_require_psk(WOLFSSL* ssl);

\return BAD_FUNC_ARG if ssl is NULL or not using TLS v1.3.
\return WANT_WRITE if the writing is not ready.
\return BAD_STATE_E if the connection has already performed the maximum
number of key updates. RFC 9846, Section 4.7.3 caps a TLS 1.3 sender at
2^48-1 key updates; beyond that the connection must be closed rather than
rekeyed. Note that a KeyUpdate arriving from the peer with
request_update set is ignored once this cap is reached, rather than
failing the connection, so only an application-initiated update reports
this error.
\return WOLFSSL_SUCCESS if successful.

_Example_
Expand Down Expand Up @@ -14800,7 +14812,8 @@ int wolfSSL_update_keys(WOLFSSL* ssl);
is received.

\param [in] ssl a pointer to a WOLFSSL structure, created using wolfSSL_new().
\param [out] required 0 when no key update response required. 1 when no key update response required.
\param [out] required 0 when no key update response is required. 1 when
a key update response from the peer is still outstanding.

\return 0 on successful.
\return BAD_FUNC_ARG if ssl is NULL or not using TLS v1.3.
Expand Down Expand Up @@ -15622,7 +15635,12 @@ int wolfSSL_set_max_early_data(WOLFSSL* ssl, unsigned int sz);
\return SIDE_ERROR if called with a server.
\return BAD_STATE_E if invoked without a valid session or without a valid
PSK cb
\return WOLFSSL_FATAL_ERROR if the connection is not made.
\return WOLFSSL_FATAL_ERROR if the connection is not made, or if the
AEAD key usage limit would be exceeded by this write, in which case
wolfSSL_get_error() reports TOO_MUCH_EARLY_DATA. A KeyUpdate cannot be
performed while sending early data (RFC 9846, Section 5.5), so no further
early data can be sent on this connection; complete the handshake with
wolfSSL_connect_TLSv13() and send the remainder with wolfSSL_write().
\return the amount of early data written in bytes if successful.

_Example_
Expand Down
72 changes: 59 additions & 13 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@
* WOLFSSL_TLS13_NO_PEEK_HANDSHAKE_DONE:
* Disable peek returning WANT_READ for tickets default: off
* WOLFSSL_TLS13_IGNORE_AEAD_LIMITS:
* Ignore AEAD message limits from RFC 8446 default: off
* Ignore AEAD message limits from RFC 9846 5.5, which
* makes observing them a MUST default: off
* WOLFSSL_DTLS13_SEND_MOREACK_DEFAULT:
* Send more ACKs by default in DTLS 1.3 default: off
*
Expand Down Expand Up @@ -23961,7 +23962,7 @@ static void LogAlert(int type)
/* process alert, return level */
#ifndef NO_SESSION_CACHE
/* RFC 5246 Section 7.2.2: a TLS 1.2 session whose connection is terminated by a
* fatal alert MUST be invalidated so it cannot be resumed. (TLS 1.3 RFC 8446
* fatal alert MUST be invalidated so it cannot be resumed. (TLS 1.3 RFC 9846
* Section 6.2 only requires closing the connection, but evicting here too is
* sound defense-in-depth.) Evict the cached session (which also drops any
* associated ticket). Acts on an established connection or an in-progress
Expand All @@ -23975,7 +23976,7 @@ static void InvalidateSessionOnFatalAlert(WOLFSSL* ssl)
return;
/* Don't evict on an unauthenticated record: a TLS 1.3 plaintext alert
* received under encryption (current record not decrypted) is rejected (or
* ignored) by DoAlert, and the teardown alert routes back here. RFC 8446
* ignored) by DoAlert, and the teardown alert routes back here. RFC 9846
* 6.2 doesn't require TLS 1.3 eviction; TLS 1.2 alerts are plaintext so are
* unaffected. */
if (IsAtLeastTLSv1_3(ssl->version) && IsEncryptionOn(ssl, 0) &&
Expand Down Expand Up @@ -24039,10 +24040,16 @@ static int DoAlert(WOLFSSL* ssl, byte* input, word32* inOutIdx, int* type)
{
ssl->alert_history.last_rx.code = code;
ssl->alert_history.last_rx.level = level;
if (level == alert_fatal) {
/* RFC 9846 Section 6.1: "user_canceled" only "generally" has
* AlertLevel=warning, and a receiver SHOULD keep reading until
* "close_notify" arrives. The level byte is meaningless in TLS 1.3,
* so do not let a peer that sends the alert at fatal level tear the
* connection down. */
if (level == alert_fatal &&
!(IsAtLeastTLSv1_3(ssl->version) && code == user_canceled)) {
ssl->options.isClosed = 1; /* Don't send close_notify */
}
/* RFC 8446 Section 6.2: In TLS 1.3, all error alerts are implicitly
/* RFC 9846 Section 6.2: In TLS 1.3, all error alerts are implicitly
* fatal regardless of the AlertLevel byte. */
if (IsAtLeastTLSv1_3(ssl->version) &&
code != close_notify && code != user_canceled) {
Expand Down Expand Up @@ -24094,12 +24101,16 @@ static int DoAlert(WOLFSSL* ssl, byte* input, word32* inOutIdx, int* type)
}
#ifndef NO_SESSION_CACHE
/* Validated fatal alert: invalidate the session so it can't be resumed
* (RFC 5246 7.2.2; in TLS 1.3 all error alerts are fatal, RFC 8446
* 6.2). */
if (*type != close_notify &&
(level == alert_fatal ||
(IsAtLeastTLSv1_3(ssl->version) && *type != user_canceled)))
* (RFC 5246 7.2.2; in TLS 1.3 all error alerts are fatal, RFC 9846
* 6.2). "close_notify" is not an error, and "user_canceled" is exempt
* in TLS 1.3 at any AlertLevel (RFC 9846 6.1). */
if (IsAtLeastTLSv1_3(ssl->version)) {
if (*type != close_notify && *type != user_canceled)
InvalidateSessionOnFatalAlert(ssl);
}
else if (level == alert_fatal && *type != close_notify) {
InvalidateSessionOnFatalAlert(ssl);
}
#endif
}
return level;
Expand Down Expand Up @@ -24949,7 +24960,10 @@ static int DoProcessAlertRecord(WOLFSSL* ssl)
WOLFSSL_MSG("got ALERT!");
ret = DoAlert(ssl, ssl->buffers.inputBuffer.buffer,
&ssl->buffers.inputBuffer.idx, &type);
if (ret == alert_fatal)
/* RFC 9846 Section 6.1: keep reading past a TLS 1.3 "user_canceled" until
* "close_notify" arrives, whatever AlertLevel the peer used. */
if (ret == alert_fatal &&
!(IsAtLeastTLSv1_3(ssl->version) && type == user_canceled))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 [Low] TLS 1.3 user_canceled exemption is applied before version negotiation, silently ignoring a pre-1.3 peer's fatal alert · Logic errors

IsAtLeastTLSv1_3(ssl->version) reflects the maximum supported version, not the negotiated one, until the ServerHello is processed. A fatal-level user_canceled arriving before negotiation is therefore exempted from teardown even when the peer is TLS 1.2, contradicting the PR's stated "TLS 1.2 unchanged" and leaving the connection in WANT_READ instead of failing. The same premature check exists at src/internal.c:24049.

Related known finding #6723 (similar but distinct): Both rely on IsAtLeastTLSv1_3(ssl->version) in early record processing, but #6723 applies TLS 1.3 CCS handling to DTLS 1.3 records, whereas this exempts a fatal user_canceled alert before TLS version negotiation. They affect different record types, functions, protocol roles, and require separate patches.

Fix: Gate the exemption on a negotiated TLS 1.3 connection (e.g. ssl->options.tls1_3) rather than on ssl->version alone.

return FATAL_ERROR;
else if (ret < 0)
return ret;
Expand All @@ -24965,7 +24979,7 @@ static int DoProcessAlertRecord(WOLFSSL* ssl)
if (type == decrypt_error)
return FATAL_ERROR;

/* RFC 8446 Section 6.2: In TLS 1.3, all error alerts MUST
/* RFC 9846 Section 6.2: In TLS 1.3, all error alerts MUST
* be treated as fatal regardless of the AlertLevel byte.
* Only close_notify (handled above) and user_canceled
* are exempt. */
Expand Down Expand Up @@ -28594,7 +28608,7 @@ int IsSCR(WOLFSSL* ssl)
!defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS)
/*
* Enforce limits specified in
* https://www.rfc-editor.org/rfc/rfc8446#section-5.5
* https://www.rfc-editor.org/rfc/rfc9846#section-5.5
*/
static int CheckTLS13AEADSendLimit(WOLFSSL* ssl)
{
Expand Down Expand Up @@ -28664,6 +28678,20 @@ static int CheckTLS13AEADSendLimit(WOLFSSL* ssl)
if (w64GTE(seq, limit)) { /* cppcheck-suppress uninitvar
* (false positive from cppcheck-2.13.0)
*/
#ifdef WOLFSSL_EARLY_DATA
/* RFC 9846 Section 5.5: a KeyUpdate cannot be performed for early
* data, so a sender MUST NOT exceed the limits while sending it.
* There is no way to rekey at this point - the handshake has not
* finished, so a KeyUpdate here would be out of order - and the write
* has to fail instead. */
if (ssl->options.side == WOLFSSL_CLIENT_END &&
ssl->earlyData != no_early_data &&
ssl->earlyData != done_early_data) {
WOLFSSL_MSG("AEAD limit reached while sending early data");
WOLFSSL_ERROR_VERBOSE(TOO_MUCH_EARLY_DATA);
return TOO_MUCH_EARLY_DATA;
Comment thread
SparkiDev marked this conversation as resolved.
}
#endif
return Tls13UpdateKeys(ssl); /* Need to generate new keys */
}

Expand Down Expand Up @@ -28926,6 +28954,17 @@ int SendData(WOLFSSL* ssl, const void* data, size_t sz)
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS)
if (IsAtLeastTLSv1_3(ssl->version)) {
ret = CheckTLS13AEADSendLimit(ssl);
#ifdef WOLFSSL_EARLY_DATA
/* Hitting the early data limit part way through a multi-record
* write must not throw away the records already emitted in this
* call: the caller is told to send the remainder over the
* completed handshake, and it can only work out the remainder
* from the returned count. Report the short write; the next call
* re-runs this check with nothing sent yet and fails properly. */
if ((ret == WC_NO_ERR_TRACE(TOO_MUCH_EARLY_DATA)) && (sent > 0)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 [Low] SendData returns a short write without partialWrite enabled · TLS protocol issues

The new break exits the send loop with ssl->error == 0 and returns sent < sz. Every other short return in SendData is gated on ssl->options.partialWrite (line 29193), so a caller relying on the default all-or-error contract silently drops the untransmitted tail of the early data.

Fix: Record a sticky indicator (e.g. set ssl->error = TOO_MUCH_EARLY_DATA before breaking) so the caller can distinguish this short write from a complete one.

break;
}
#endif
if (ret != 0) {
ssl->error = ret;
return WOLFSSL_FATAL_ERROR;
Expand Down Expand Up @@ -38336,7 +38375,14 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
int TranslateErrorToAlert(int err)
{
switch (err) {
/* RFC 9846 Section 4.3 requires a "decode_error" alert when an
* extension has data left over after its structure is parsed, and
* Section 6.2 defines the alert for any field out of range or
* message of incorrect length. The extension parsers report those
* as either BUFFER_ERROR or the wolfCrypt BUFFER_E; both must map
* here, or the handshake aborts silently with no alert sent. */
case WC_NO_ERR_TRACE(BUFFER_ERROR):
case WC_NO_ERR_TRACE(BUFFER_E):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 [Medium] BUFFER_E - decode_error makes the DTLS stateless ClientHello path send a fatal alert and stick alert_history at… · Incorrect error handling

CheckDtlsCookie() (src/dtls.c:291,294) returns BUFFER_E for a malformed DTLS 1.3 cookie extension, which reaches DoClientHello() at src/internal.c:40993. That error is now translated to decode_error, so a fatal alert is sent to an address-unverified peer and DtlsIgnoreError(BUFFER_E) then zeroes ret and the object keeps running with alert_history.last_tx.level == alert_fatal, which permanently suppresses later alerts via the guards at src/internal.c:20676, src/internal.c:34575 and src/tls13.c:14996.

Related known finding #7568 (similar but distinct): Both concern fatal-alert behavior on DoClientHello error paths, but #7568 maps VERSION_ERROR from version negotiation to a missing/wrong protocol_version alert, while this maps malformed-cookie BUFFER_E to decode_error before DTLS address validation and leaves alert history poisoned. The faulting operations, causes, and required fixes differ.

Fix: Suppress the alert on the pre-cookie stateless path in DoClientHello(), or clear alert_history.last_tx in DtlsResetState().

return decode_error;
case WC_NO_ERR_TRACE(EXT_NOT_ALLOWED):
case WC_NO_ERR_TRACE(PEER_KEY_ERROR):
Expand Down
16 changes: 15 additions & 1 deletion src/ssl_api_rw.c
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,21 @@ static int wolfssl_write_dup_do_tls13_work(WOLFSSL* ssl)
{
/* keyUpdateRespond is cleared in SendTls13KeyUpdate. */
if (ssl->keys.keyUpdateRespond) {
ret = Tls13UpdateKeys(ssl);
/* RFC 9846 Section 4.7.3: a sender that would exceed the
* key update limit "MUST NOT send its own KeyUpdate ...
* and SHOULD instead ignore the 'update_requested' flag".
* The read side delegated this response without seeing the
* cap - it never sends KeyUpdates, so its count is not the
* one that matters - so the check belongs here, on the
* side that actually sends and owns the counter. */
if (Tls13KeyUpdateLimitReached(ssl)) {
WOLFSSL_MSG("Key update limit reached; ignoring "
"delegated update_requested");
ssl->keys.keyUpdateRespond = 0;
}
else {
ret = Tls13UpdateKeys(ssl);
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
* WOLFSSL_OLD_PRIME_CHECK: Use old DH prime checking method default: off
* WOLFSSL_STATIC_DH: Enable static DH cipher suites default: off
* WOLFSSL_STATIC_EPHEMERAL: Enable static ephemeral key loading default: off
* Reuses a key share across connections, which
* RFC 9846 4.3.8 forbids. Inspection/debug only.
*
* Post-Quantum:
* WOLFSSL_HAVE_MLKEM: Enable ML-KEM (Kyber) support default: off
Expand Down
52 changes: 40 additions & 12 deletions src/tls13.c
Original file line number Diff line number Diff line change
Expand Up @@ -13195,6 +13195,23 @@ static int SendTls13Finished(WOLFSSL* ssl)
}
#endif /* !NO_WOLFSSL_CLIENT || !NO_WOLFSSL_SERVER */

/* RFC 9846 Section 4.7.3: a TLS 1.3 sender MUST NOT allow its number of key
* updates to exceed 2^48-1. DTLS 1.3 bounds the epoch instead (RFC 9147
* Section 4.2.1), so this only covers TLS.
*
* ssl The SSL/TLS object.
* returns 1 when a further KeyUpdate would exceed the limit, 0 otherwise.
*/
int Tls13KeyUpdateLimitReached(WOLFSSL* ssl)
{
if (ssl->options.dtls)
return 0;

return w64GTE(ssl->keys.keyUpdateCount,
w64From32(TLS13_KEY_UPDATE_MAX_HI32,
TLS13_KEY_UPDATE_MAX_LO32));
}

/* handle generation TLS v1.3 key_update (24) */
/* Send the TLS v1.3 KeyUpdate message.
*
Expand Down Expand Up @@ -13226,17 +13243,12 @@ int SendTls13KeyUpdate(WOLFSSL* ssl)
}
#endif /* WOLFSSL_DTLS13 */

if (!ssl->options.dtls) {
/* RFC 9846 Section 4.7.3: a sending implementation MUST NOT allow its
* number of key updates to exceed 2^48-1. Receivers MUST NOT enforce
* this on the peer. */
if (w64GTE(ssl->keys.keyUpdateCount,
w64From32(TLS13_KEY_UPDATE_MAX_HI32,
TLS13_KEY_UPDATE_MAX_LO32))) {
WOLFSSL_MSG("TLS 1.3 key update count at maximum; refusing "
"KeyUpdate");
return BAD_STATE_E;
}
/* RFC 9846 Section 4.7.3: a sending implementation MUST NOT allow its
* number of key updates to exceed 2^48-1. Receivers MUST NOT enforce this
* on the peer. */
if (Tls13KeyUpdateLimitReached(ssl)) {
WOLFSSL_MSG("TLS 1.3 key update count at maximum; refusing KeyUpdate");
return BAD_STATE_E;
}

outputSz = OPAQUE8_LEN + MAX_MSG_EXTRA;
Expand Down Expand Up @@ -13407,7 +13419,12 @@ static int DoTls13KeyUpdate(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
#endif /* WOLFSSL_DTLS13 */

#if defined(HAVE_WRITE_DUP) && defined(WOLFSSL_TLS13)
/* Read side cannot write; delegate the response to the write side. */
/* Read side cannot write; delegate the response to the write side.
* The key update cap is deliberately not checked here: the two sides
* are separate WOLFSSL objects with separate keys, and only the write
* side ever sends a KeyUpdate, so this object's keyUpdateCount is not
* the one the limit applies to. The check is applied on the write side
* in wolfssl_write_dup_do_tls13_work(). */
if (ssl->dupWrite != NULL && ssl->dupSide == READ_DUP_SIDE) {
if (wc_LockMutex(&ssl->dupWrite->dupMutex) != 0)
return BAD_MUTEX_E;
Expand All @@ -13418,6 +13435,17 @@ static int DoTls13KeyUpdate(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
}
#endif /* HAVE_WRITE_DUP && WOLFSSL_TLS13 */

/* RFC 9846 Section 4.7.3: a sender that would exceed the key update
* limit "MUST NOT send its own KeyUpdate ... and SHOULD instead ignore
* the 'update_requested' flag". Dropping the response rather than
* failing keeps the connection alive on the current keys until the
* Section 5.5 data limits eventually force it closed. */
if (Tls13KeyUpdateLimitReached(ssl)) {
Comment thread
SparkiDev marked this conversation as resolved.
Comment thread
SparkiDev marked this conversation as resolved.
WOLFSSL_MSG("Key update limit reached; ignoring update_requested");
ssl->keys.keyUpdateRespond = 0;
return 0;
}

#ifndef WOLFSSL_RW_THREADED
return SendTls13KeyUpdate(ssl);
#else
Expand Down
Loading
Loading