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
73 changes: 69 additions & 4 deletions doc/dox_comments/header_files/ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1882,7 +1882,15 @@ int wolfSSL_set_dtls_fd_connected(WOLFSSL* ssl, int fd);
the listener for new connections and being able to isolate the
WOLFSSL object once the ClientHello is verified (either through a
cookie exchange or just checking if the ClientHello had the correct
format).
format). With DTLS cookies disabled, the callback is invoked once,
only after complete successful ClientHello processing, not on its
first fragment. The object is already stateful before input is read,
but the peer's return-routability has not been verified. Retries and
a second ClientHello do not repeat this no-cookie notification,
even if the callback returns a negative error code. A callback may
return WANT_READ or WANT_WRITE to pause the handshake; the next
accept call resumes after the notification. Other non-retryable
errors require abandoning the handshake.
DTLS 1.2:
https://datatracker.ietf.org/doc/html/rfc6347#section-4.2.1
DTLS 1.3:
Expand Down Expand Up @@ -2414,8 +2422,13 @@ int wolfSSL_accept(WOLFSSL* ssl);
/*!
\ingroup IO

\brief This function is called on the server side and statelessly listens
for an SSL client to initiate the DTLS handshake.
\brief This function is called on a server-side object and statelessly
listens for an SSL client to initiate the DTLS handshake. A general-purpose
object must first be made server-side with wolfSSL_set_accept_state().
Cookies must be enabled. A cookie-disabled DTLS object is rejected before
I/O or callback changes:
WOLFSSL_FATAL_ERROR is returned and wolfSSL_get_error() reports BAD_STATE_E.
Use wolfSSL_accept() for cookie-disabled connections instead.

\return WOLFSSL_SUCCESS ClientHello containing a valid cookie was received.
The connection can be continued with wolfSSL_accept().
Expand Down Expand Up @@ -2453,6 +2466,48 @@ int wolfSSL_accept(WOLFSSL* ssl);
*/
int wolfDTLS_accept_stateless(WOLFSSL* ssl);

/*!
\ingroup Setup

\brief Disable server cookies for DTLS 1.2, DTLS 1.3 and TLS 1.3, including
DTLS 1.2 fallback from DTLS 1.3. Cookies are enabled by default for DTLS.
For DTLS, ordinary
wolfSSL_accept() or wolfSSL_accept_TLSv13() commits it to stateful processing
before the first read, even if a nonblocking accept has no input available.
The application must isolate/demultiplex the peer before accepting; disabling
cookies removes return-routability verification and exposes the server to
DoS/amplification attacks. wolfDTLS_accept_stateless() cannot be used.

Primary and secondary cookie secrets for the applicable protocols are
securely erased and freed.

\param ssl DTLS or TLS 1.3 server session created with wolfSSL_new().
\return WOLFSSL_SUCCESS on success (including an unchanged mode).
\return BAD_FUNC_ARG if ssl is NULL or uses an unsupported protocol (TLS 1.2).
\return SIDE_ERROR if ssl is not a server.
\sa wolfSSL_enable_cookie
\sa wolfDTLS_SetChGoodCb
*/
int wolfSSL_disable_cookie(WOLFSSL* ssl);

/*!
\ingroup Setup
\brief Enable server cookies for DTLS 1.2, DTLS 1.3 and TLS 1.3.
Like wolfSSL_disable_cookie(), this does not change stateful processing.
It can undo a disable before accept begins, including
wolfSSL_disable_hrr_cookie(). Missing primary cookie secrets are randomly
generated immediately; existing primary and secondary secrets are preserved.

\param ssl DTLS or TLS 1.3 server session created with wolfSSL_new().
\return WOLFSSL_SUCCESS on success (including an unchanged mode).
\return BAD_FUNC_ARG if ssl is NULL or uses an unsupported protocol (TLS 1.2).
\return SIDE_ERROR if ssl is not a server.
\return MEMORY_ERROR if secret allocation fails, or another negative error
if random secret generation fails.
\sa wolfSSL_disable_cookie
*/
int wolfSSL_enable_cookie(WOLFSSL* ssl);

/*!
\ingroup Setup

Expand Down Expand Up @@ -14563,7 +14618,11 @@ int wolfSSL_connect(WOLFSSL* ssl);
exchange is enabled by default. The Cookie holds a hash of the current
transcript so that another server process can handle the ClientHello in
reply. The secret is used when generating the integrity check on the Cookie
data.
data. This replaces or regenerates the HRR secret, then delegates to
wolfSSL_enable_cookie(). Changing
cookie mode after handshake processing starts is unsupported; rotating a
secret while cookies are already enabled remains supported. DTLS 1.2-only callers
should use wolfSSL_enable_cookie() and wolfSSL_DTLS_SetCookieSecret().

\param [in,out] ssl a pointer to a WOLFSSL structure, created using wolfSSL_new().
\param [in] secret a pointer to a buffer holding the secret.
Expand Down Expand Up @@ -14651,13 +14710,19 @@ int wolfSSL_set_hrr_cookie_secret_secondary(WOLFSSL* ssl,
protocol DTLS v1.3, a cookie exchange will not be included in the
handshake. Please note that not doing a cookie exchange when using protocol
DTLS v1.3 can make the server susceptible to DoS/Amplification attacks.
This delegates to wolfSSL_disable_cookie(), including its
DTLS 1.2 fallback policy. Cookie mode changes after handshake processing
starts are unsupported. On
success the primary and secondary HRR secrets are erased as before.
TLS 1.3 over a reliable transport is unchanged.

\param [in,out] ssl a pointer to a WOLFSSL structure, created using wolfSSL_new().

\return WOLFSSL_SUCCESS if successful
\return BAD_FUNC_ARG if ssl is NULL or not using TLS v1.3
\return SIDE_ERROR if invoked on client

\sa wolfSSL_disable_cookie
\sa wolfSSL_send_hrr_cookie
*/
int wolfSSL_disable_hrr_cookie(WOLFSSL* ssl);
Expand Down
2 changes: 2 additions & 0 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -2437,6 +2437,7 @@ int InitSSL_Side(WOLFSSL* ssl, word16 side)
#if defined(WOLFSSL_DTLS) && !defined(NO_WOLFSSL_SERVER)
if (ssl->options.dtls && ssl->options.side == WOLFSSL_SERVER_END) {
int ret;
ssl->options.sendCookie = 1;
ret = wolfSSL_DTLS_SetCookieSecret(ssl, NULL, 0);
if (ret != 0) {
WOLFSSL_MSG("DTLS Cookie Secret error");
Expand Down Expand Up @@ -8566,6 +8567,7 @@ static int InitSSL_DtlsServer(WOLFSSL* ssl)
int ret;

if (ssl->options.dtls && ssl->options.side == WOLFSSL_SERVER_END) {
ssl->options.sendCookie = 1; /* Cookies enabled for every DTLS version. */
/* Initialize both in case we allow downgrading. */
ret = wolfSSL_DTLS_SetCookieSecret(ssl, NULL, 0);
if (ret != 0) {
Expand Down
1 change: 1 addition & 0 deletions src/ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -5745,6 +5745,7 @@ size_t wolfSSL_get_client_random(const WOLFSSL* ssl, unsigned char* out,
#endif
#ifdef WOLFSSL_DTLS
ssl->options.dtlsStateful = 0;
ssl->options.chGoodCbDone = 0;
#endif
#ifdef WOLFSSL_TLS13
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
Expand Down
135 changes: 134 additions & 1 deletion src/ssl_api_dtls.c
Original file line number Diff line number Diff line change
Expand Up @@ -1719,6 +1719,19 @@ int wolfDTLS_accept_stateless(WOLFSSL* ssl)
if (ssl == NULL)
return WOLFSSL_FATAL_ERROR;

if (ssl->options.side != WOLFSSL_SERVER_END) {
WOLFSSL_MSG("wolfDTLS_accept_stateless requires a server-side object");
ssl->error = SIDE_ERROR;
return WOLFSSL_FATAL_ERROR;
}

if (ssl->options.dtls && !ssl->options.sendCookie) {
WOLFSSL_MSG("wolfDTLS_accept_stateless requires cookies enabled; "
"use wolfSSL_accept");
ssl->error = BAD_STATE_E;
return WOLFSSL_FATAL_ERROR;
}

/* Save this to restore it later */
disableRead = (byte)ssl->options.disableRead;
cb.userCb = ssl->chGoodCb;
Expand Down Expand Up @@ -1752,7 +1765,8 @@ int wolfDTLS_accept_stateless(WOLFSSL* ssl)
return ret;
}

/* Set the callback to call when a ClientHello with a valid cookie is received.
/* Notify after cookie verification, or, with cookies disabled, after complete
* successful ClientHello processing (once per connection).
*
* WC_NO_INLINE: wolfDTLS_accept_stateless passes the address of a stack-local
* context here; the restore call before return clears it again. Preventing
Expand All @@ -1779,6 +1793,32 @@ int wolfDTLS_SetChGoodCb(WOLFSSL* ssl, ClientHelloGoodCb cb, void* user_ctx)
return WOLFSSL_SUCCESS;
}

/* Notify the ClientHello good callback once when cookies are disabled.
*
* Called by the accept functions at the first ClientHello transition, after
* a complete ClientHello has been processed. With cookies enabled the callback
* is invoked from the stateless ClientHello processing instead.
*
* @param [in, out] ssl SSL/TLS object.
* @return 0 when the callback is not called or succeeds.
* @return The callback's negative error code otherwise.
*/
int DtlsNoCookieChGood(WOLFSSL* ssl)
{
int ret = 0;

if (ssl->options.dtls && !ssl->options.sendCookie &&
ssl->chGoodCb != NULL && !ssl->options.chGoodCbDone) {
/* Record before calling so an error cannot repeat the callback. */
ssl->options.chGoodCbDone = 1;
ret = ssl->chGoodCb(ssl, ssl->chGoodCtx);
if (ret > 0)
ret = 0;
}

return ret;
}

/* Set a secondary DTLS 1.2 cookie secret used only when verifying a received
* HelloVerifyRequest cookie, and only if the primary secret (set by
* wolfSSL_DTLS_SetCookieSecret()) fails to verify it.
Expand Down Expand Up @@ -1850,6 +1890,99 @@ int wolfSSL_DTLS_SetCookieSecretSecondary(WOLFSSL* ssl,

#endif /* WOLFSSL_DTLS && !NO_WOLFSSL_SERVER */

#if (defined(WOLFSSL_DTLS) || defined(WOLFSSL_SEND_HRR_COOKIE)) && \
!defined(NO_WOLFSSL_SERVER)
static int CheckCookieSide(WOLFSSL* ssl)
{
if (ssl == NULL)
return BAD_FUNC_ARG;
#ifdef WOLFSSL_DTLS
if (!ssl->options.dtls)
#endif
{
#ifdef WOLFSSL_SEND_HRR_COOKIE
if (!IsAtLeastTLSv1_3(ssl->version))
#endif
return BAD_FUNC_ARG;
}
if (ssl->options.side != WOLFSSL_SERVER_END)
return SIDE_ERROR;
return WOLFSSL_SUCCESS;
}

/* Securely erase and free a cookie secret.
*
* @param [in] ssl SSL/TLS object.
* @param [in, out] secret Cookie secret buffer, left empty.
*/
void FreeCookieSecret(WOLFSSL* ssl, buffer* secret)
{
if (secret->buffer != NULL) {
ForceZero(secret->buffer, secret->length);
XFREE(secret->buffer, ssl->heap, DYNAMIC_TYPE_COOKIE_PWD);
secret->buffer = NULL;
secret->length = 0;
}
}

int wolfSSL_disable_cookie(WOLFSSL* ssl)
{
int ret;

WOLFSSL_ENTER("wolfSSL_disable_cookie");

ret = CheckCookieSide(ssl);
if (ret != WOLFSSL_SUCCESS)
return ret;

#ifdef WOLFSSL_DTLS
if (ssl->options.dtls) {
FreeCookieSecret(ssl, &ssl->buffers.dtlsCookieSecret);
FreeCookieSecret(ssl, &ssl->buffers.dtlsCookieSecretSecondary);
}
#endif
#ifdef WOLFSSL_SEND_HRR_COOKIE
FreeCookieSecret(ssl, &ssl->buffers.tls13CookieSecret);
FreeCookieSecret(ssl, &ssl->buffers.tls13CookieSecretSecondary);
#endif
ssl->options.sendCookie = 0;
return WOLFSSL_SUCCESS;
}

/* Prepare missing secrets without replacing application-supplied secrets. */
int wolfSSL_enable_cookie(WOLFSSL* ssl)
{
int ret;

WOLFSSL_ENTER("wolfSSL_enable_cookie");

ret = CheckCookieSide(ssl);
if (ret != WOLFSSL_SUCCESS)
return ret;

#ifdef WOLFSSL_DTLS
/* DTLS 1.3 also needs this secret for DTLS 1.2 fallback. */
if (ssl->options.dtls && ssl->buffers.dtlsCookieSecret.buffer == NULL) {
ret = wolfSSL_DTLS_SetCookieSecret(ssl, NULL, 0);
if (ret != 0) {
FreeCookieSecret(ssl, &ssl->buffers.dtlsCookieSecret);
return ret;
}
}
#endif
#ifdef WOLFSSL_SEND_HRR_COOKIE
if (IsAtLeastTLSv1_3(ssl->version) &&
ssl->buffers.tls13CookieSecret.buffer == NULL) {
ret = Tls13SetCookieSecret(ssl, NULL, 0);
if (ret != WOLFSSL_SUCCESS)
return ret;
}
#endif
ssl->options.sendCookie = 1;
return WOLFSSL_SUCCESS;
}
#endif /* (WOLFSSL_DTLS || WOLFSSL_SEND_HRR_COOKIE) && !NO_WOLFSSL_SERVER */

#endif /* !WOLFCRYPT_ONLY */

#endif /* !WOLFSSL_SSL_API_DTLS_INCLUDED */
11 changes: 11 additions & 0 deletions src/ssl_api_hs.c
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,8 @@ int wolfSSL_accept(WOLFSSL* ssl)
ssl->options.dtls = 1;
ssl->options.tls = 1;
ssl->options.tls1_1 = 1;
if (!ssl->options.sendCookie)
ssl->options.dtlsStateful = 1;
if ((!IsDtlsNotSctpMode(ssl)) || (IsSCR(ssl))) {
ssl->options.dtlsStateful = 1;
}
Expand All @@ -951,6 +953,15 @@ int wolfSSL_accept(WOLFSSL* ssl)
return WOLFSSL_FATAL_ERROR;
}
}
#ifdef WOLFSSL_DTLS
/* Notify before the existing first CH transition. */
if (ssl->options.acceptState == ACCEPT_BEGIN) {
if ((ssl->error = DtlsNoCookieChGood(ssl)) < 0) {
WOLFSSL_ERROR(ssl->error);
return WOLFSSL_FATAL_ERROR;
}
}
#endif
#ifdef WOLFSSL_TLS13
ssl->options.acceptState = ACCEPT_CLIENT_HELLO_DONE;
WOLFSSL_MSG("accept state ACCEPT_CLIENT_HELLO_DONE");
Expand Down
Loading
Loading