From daa57c492d46e6a45b232cb197f79ab33f3c3b96 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 30 Oct 2024 16:11:00 +0100 Subject: [PATCH 1/8] DTLS: Add server side stateless and CID QoL API - wolfDTLS_accept_stateless - statelessly listen for incoming connections - wolfSSL_inject - insert data into WOLFSSL object - wolfSSL_SSL(Enable|Disable)Read - enable/disable reading from IO - wolfSSL_get_wfd - get the write side file descriptor - wolfSSL_dtls_set_pending_peer - set the pending peer that will be upgraded to regular peer when we successfully de-protect a DTLS record - wolfSSL_dtls_get0_peer - zero copy access to the peer address - wolfSSL_is_stateful - boolean to check if we have entered stateful processing - wolfSSL_dtls_cid_get0_rx - zero copy access to the rx cid - wolfSSL_dtls_cid_get0_tx - zero copy access to the tx cid - wolfSSL_dtls_cid_parse - extract cid from a datagram/message --- doc/dox_comments/header_files/ssl.h | 245 ++++++++++++++++++++++++- doc/dox_comments/header_files/wolfio.h | 43 +++++ src/dtls.c | 69 +++++++ src/dtls13.c | 5 + src/internal.c | 81 ++++++-- src/ssl.c | 242 +++++++++++++++++++++--- src/wolfio.c | 14 ++ tests/api.c | 197 ++++++++++++++++++-- wolfssl/internal.h | 10 + wolfssl/ssl.h | 19 +- wolfssl/wolfio.h | 2 + 11 files changed, 873 insertions(+), 54 deletions(-) diff --git a/doc/dox_comments/header_files/ssl.h b/doc/dox_comments/header_files/ssl.h index 04407dfd9f..4d816fc936 100644 --- a/doc/dox_comments/header_files/ssl.h +++ b/doc/dox_comments/header_files/ssl.h @@ -1997,8 +1997,8 @@ const char* wolfSSL_get_cipher_name(WOLFSSL* ssl); /*! \ingroup IO - \brief This function returns the file descriptor (fd) used as the - input/output facility for the SSL connection. Typically this + \brief This function returns the read file descriptor (fd) used as the + input facility for the SSL connection. Typically this will be a socket file descriptor. \return fd If successful the call will return the SSL session file @@ -2016,9 +2016,38 @@ const char* wolfSSL_get_cipher_name(WOLFSSL* ssl); \endcode \sa wolfSSL_set_fd + \sa wolfSSL_set_read_fd + \sa wolfSSL_set_write_fd */ int wolfSSL_get_fd(const WOLFSSL*); +/*! + \ingroup IO + + \brief This function returns the write file descriptor (fd) used as the + output facility for the SSL connection. Typically this + will be a socket file descriptor. + + \return fd If successful the call will return the SSL session file + descriptor. + + \param ssl pointer to the SSL session, created with wolfSSL_new(). + + _Example_ + \code + int sockfd; + WOLFSSL* ssl = 0; + ... + sockfd = wolfSSL_get_wfd(ssl); + ... + \endcode + + \sa wolfSSL_set_fd + \sa wolfSSL_set_read_fd + \sa wolfSSL_set_write_fd +*/ +int wolfSSL_get_wfd(const WOLFSSL*); + /*! \ingroup Setup @@ -2289,6 +2318,48 @@ int wolfSSL_peek(WOLFSSL* ssl, void* data, int sz); */ int wolfSSL_accept(WOLFSSL*); +/*! + \ingroup IO + + \brief This function is called on the server side and statelessly listens + for an SSL client to initiate the DTLS handshake. + + \return WOLFSSL_SUCCESS ClientHello containing a valid cookie was received. + The connection can be continued with wolfSSL_accept(). + \return WOLFSSL_FAILURE The I/O layer returned WANT_READ. This is either + because there is no data to read and we are using non-blocking sockets or + we sent a cookie request and we are waiting for a reply. The user should + call wolfDTLS_accept_stateless again after data becomes available in + the I/O layer. + \return WOLFSSL_FATAL_ERROR A fatal error occurred. The ssl object should be + free'd and allocated again to continue. + + \param ssl a pointer to a WOLFSSL structure, created using wolfSSL_new(). + + _Example_ + \code + int ret = 0; + int err = 0; + WOLFSSL* ssl; + ... + do { + ret = wolfDTLS_accept_stateless(ssl); + if (ret == WOLFSSL_FATAL_ERROR) + // re-allocate the ssl object with wolfSSL_free() and wolfSSL_new() + } while (ret != WOLFSSL_SUCCESS); + ret = wolfSSL_accept(ssl); + if (ret != SSL_SUCCESS) { + err = wolfSSL_get_error(ssl, ret); + printf(“error = %d, %s\n”, err, wolfSSL_ERR_error_string(err, buffer)); + } + \endcode + + \sa wolfSSL_accept + \sa wolfSSL_get_error + \sa wolfSSL_connect +*/ +int wolfDTLS_accept_stateless(WOLFSSL* ssl); + /*! \ingroup Setup @@ -3856,12 +3927,52 @@ int wolfSSL_dtls(WOLFSSL* ssl); \endcode \sa wolfSSL_dtls_get_current_timeout + \sa wolfSSL_dtls_set_pending_peer \sa wolfSSL_dtls_get_peer \sa wolfSSL_dtls_got_timeout \sa wolfSSL_dtls */ int wolfSSL_dtls_set_peer(WOLFSSL* ssl, void* peer, unsigned int peerSz); +/*! + \brief This function sets the pending DTLS peer, peer (sockaddr_in) with + size of peerSz. This sets the pending peer that will be upgraded to a + regular peer when we successfully de-protect the next record. This is useful + in scenarios where the peer's address can change to avoid off-path attackers + from changing the peer address. This should be used with Connection ID's to + allow seamless and safe transition to a new peer address. + + \return SSL_SUCCESS will be returned upon success. + \return SSL_FAILURE will be returned upon failure. + \return SSL_NOT_IMPLEMENTED will be returned if wolfSSL was not compiled + with DTLS support. + + \param ssl a pointer to a WOLFSSL structure, created using wolfSSL_new(). + \param peer pointer to peer’s sockaddr_in structure. If NULL then the peer + information in ssl is cleared. + \param peerSz size of the sockaddr_in structure pointed to by peer. If 0 + then the peer information in ssl is cleared. + + _Example_ + \code + int ret = 0; + WOLFSSL* ssl; + sockaddr_in addr; + ... + ret = wolfSSL_dtls_set_pending_peer(ssl, &addr, sizeof(addr)); + if (ret != SSL_SUCCESS) { + // failed to set DTLS peer + } + \endcode + + \sa wolfSSL_dtls_get_current_timeout + \sa wolfSSL_dtls_set_peer + \sa wolfSSL_dtls_get_peer + \sa wolfSSL_dtls_got_timeout + \sa wolfSSL_dtls +*/ +int wolfSSL_dtls_set_pending_peer(WOLFSSL* ssl, void* peer, unsigned int peerSz); + /*! \brief This function gets the sockaddr_in (of size peerSz) of the current DTLS peer. The function will compare peerSz to the actual DTLS peer size @@ -3899,6 +4010,40 @@ int wolfSSL_dtls_set_peer(WOLFSSL* ssl, void* peer, unsigned int peerSz); */ int wolfSSL_dtls_get_peer(WOLFSSL* ssl, void* peer, unsigned int* peerSz); +/*! + \brief This function gets the sockaddr_in (of size peerSz) of the current + DTLS peer. This is a zero-copy alternative to wolfSSL_dtls_get_peer(). + + \return SSL_SUCCESS will be returned upon success. + \return SSL_FAILURE will be returned upon failure. + \return SSL_NOT_IMPLEMENTED will be returned if wolfSSL was not compiled + with DTLS support. + + \param ssl a pointer to a WOLFSSL structure, created using wolfSSL_new(). + \param peer pointer to return the internal buffer holding the peer address + \param peerSz output the size of the actual sockaddr_in structure + pointed to by peer. + + _Example_ + \code + int ret = 0; + WOLFSSL* ssl; + sockaddr_in* addr; + unsigned int addrSz; + ... + ret = wolfSSL_dtls_get_peer(ssl, &addr, &addrSz); + if (ret != SSL_SUCCESS) { + // failed to get DTLS peer + } + \endcode + + \sa wolfSSL_dtls_get_current_timeout + \sa wolfSSL_dtls_got_timeout + \sa wolfSSL_dtls_set_peer + \sa wolfSSL_dtls +*/ +int wolfSSL_dtls_get0_peer(WOLFSSL* ssl, const void** peer, unsigned int* peerSz); + /*! \ingroup Debug @@ -14138,6 +14283,35 @@ int wolfSSL_write_early_data(WOLFSSL* ssl, const void* data, int wolfSSL_read_early_data(WOLFSSL* ssl, void* data, int sz, int* outSz); +/*! + \ingroup IO + + \brief + + \param [in] ssl a pointer to a WOLFSSL structure, created using wolfSSL_new(). + \param [in] data data to inject into the ssl object. + \param [in] sz number of bytes of data to inject. + + \return BAD_FUNC_ARG if any pointer parameter is NULL or sz <= 0 + \return APP_DATA_READY if there is application data left to read + \return MEMORY_E if allocation fails + \return WOLFSSL_SUCCESS on success + + _Example_ + \code + byte buf[2000] + sz = recv(fd, buf, sizeof(buf), 0); + if (sz <= 0) + // error + if (wolfSSL_inject(ssl, buf, sz) != WOLFSSL_SUCCESS) + // error + sz = wolfSSL_read(ssl, buf, sizeof(buf); + \endcode + + \sa wolfSSL_read +*/ +int wolfSSL_inject(WOLFSSL* ssl, const void* data, int sz); + /*! \ingroup Setup @@ -14955,6 +15129,7 @@ connection into the buffer pointed by the parameter buffer. See RFC 9146 and RFC \param buffer A buffer where the ConnectionID will be copied \param bufferSz available space in buffer + \sa wolfSSL_dtls_cid_get0_rx \sa wolfSSL_dtls_cid_use \sa wolfSSL_dtls_cid_is_enabled \sa wolfSSL_dtls_cid_set @@ -14967,6 +15142,27 @@ int wolfSSL_dtls_cid_get_rx(WOLFSSL* ssl, unsigned char* buffer, /*! +\brief Get the ConnectionID used by the other peer. See RFC 9146 and RFC +9147. + + \return WOLFSSL_SUCCESS if ConnectionID was correctly copied, error code + otherwise + + \param ssl A WOLFSSL object pointern + \param cid Pointer that will be set to the internal memory that holds the CID + + \sa wolfSSL_dtls_cid_get_rx + \sa wolfSSL_dtls_cid_use + \sa wolfSSL_dtls_cid_is_enabled + \sa wolfSSL_dtls_cid_set + \sa wolfSSL_dtls_cid_get_rx_size + \sa wolfSSL_dtls_cid_get_tx_size + \sa wolfSSL_dtls_cid_get_tx +*/ +int wolfSSL_dtls_cid_get0_rx(WOLFSSL* ssl, unsigned char** cid); + +/*! + \brief Get the size of the ConnectionID used to send records in this connection. See RFC 9146 and RFC 9147. The size is stored in the parameter size. @@ -14998,6 +15194,7 @@ available size need to be provided in bufferSz. \param buffer A buffer where the ConnectionID will be copied \param bufferSz available space in buffer + \sa wolfSSL_dtls_cid_get0_tx \sa wolfSSL_dtls_cid_use \sa wolfSSL_dtls_cid_is_enabled \sa wolfSSL_dtls_cid_set @@ -15008,6 +15205,50 @@ available size need to be provided in bufferSz. int wolfSSL_dtls_cid_get_tx(WOLFSSL* ssl, unsigned char* buffer, unsigned int bufferSz); +/*! + +\brief Get the ConnectionID used when sending records in this connection. See +RFC 9146 and RFC 9147. + + \return WOLFSSL_SUCCESS if ConnectionID was correctly retrieved, error code + otherwise + + \param ssl A WOLFSSL object pointern + \param cid Pointer that will be set to the internal memory that holds the CID + + \sa wolfSSL_dtls_cid_get_tx + \sa wolfSSL_dtls_cid_use + \sa wolfSSL_dtls_cid_is_enabled + \sa wolfSSL_dtls_cid_set + \sa wolfSSL_dtls_cid_get_rx_size + \sa wolfSSL_dtls_cid_get_rx + \sa wolfSSL_dtls_cid_get_tx_size +*/ +int wolfSSL_dtls_cid_get0_tx(WOLFSSL* ssl, unsigned char** cid); + +/*! + +\brief Extract the ConnectionID from a record datagram/message. See +RFC 9146 and RFC 9147. + + \param msg buffer holding the datagram read from the network + \param msgSz size of msg in bytes + \param cid pointer to the start of the CID inside the msg buffer + \param cidSz the expected size of the CID. The record layer does not have a CID + size field so we have to know beforehand the size of the CID. It is recommended + to use a constant CID for all connections. + + \sa wolfSSL_dtls_cid_get_tx + \sa wolfSSL_dtls_cid_use + \sa wolfSSL_dtls_cid_is_enabled + \sa wolfSSL_dtls_cid_set + \sa wolfSSL_dtls_cid_get_rx_size + \sa wolfSSL_dtls_cid_get_rx + \sa wolfSSL_dtls_cid_get_tx_size +*/ +void wolfSSL_dtls_cid_parse(const unsigned char* msg, unsigned int msgSz, + const unsigned char** cid, unsigned int cidSz); + /*! \ingroup TLS diff --git a/doc/dox_comments/header_files/wolfio.h b/doc/dox_comments/header_files/wolfio.h index 5e52e3573b..673242aaf7 100644 --- a/doc/dox_comments/header_files/wolfio.h +++ b/doc/dox_comments/header_files/wolfio.h @@ -575,3 +575,46 @@ int wolfSSL_SetIO_ISOTP(WOLFSSL *ssl, isotp_wolfssl_ctx *ctx, can_recv_fn recv_fn, can_send_fn send_fn, can_delay_fn delay_fn, word32 receive_delay, char *receive_buffer, int receive_buffer_size, void *arg); + +/*! + \ingroup Setup + + \brief This function disables reading from the IO layer. + + \param ssl the wolfSSL context + + _Example_ + \code + WOLFSSL_CTX* ctx = wolfSSL_CTX_new(method); + WOLFSSL* ssl = wolfSSL_new(ctx); + wolfSSL_SSLDisableRead(ssl); + \endcode + + \sa wolfSSL_CTX_SetIORecv + \sa wolfSSL_SSLSetIORecv + \sa wolfSSL_SSLEnableRead + */ +void wolfSSL_SSLDisableRead(WOLFSSL *ssl); + +/*! + \ingroup Setup + + \brief This function enables reading from the IO layer. Reading is enabled + by default and should be used to undo wolfSSL_SSLDisableRead(); + + \param ssl the wolfSSL context + + _Example_ + \code + WOLFSSL_CTX* ctx = wolfSSL_CTX_new(method); + WOLFSSL* ssl = wolfSSL_new(ctx); + wolfSSL_SSLDisableRead(ssl); + ... + wolfSSL_SSLEnableRead(ssl); + \endcode + + \sa wolfSSL_CTX_SetIORecv + \sa wolfSSL_SSLSetIORecv + \sa wolfSSL_SSLEnableRead + */ +void wolfSSL_SSLEnableRead(WOLFSSL *ssl); diff --git a/src/dtls.c b/src/dtls.c index d2c1d4c03e..6ea786c6a1 100644 --- a/src/dtls.c +++ b/src/dtls.c @@ -101,6 +101,15 @@ void DtlsResetState(WOLFSSL* ssl) ssl->options.tls = 0; ssl->options.tls1_1 = 0; ssl->options.tls1_3 = 0; +#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID) + ssl->buffers.dtlsCtx.processingPendingRecord = 0; + /* Clear the pending peer in case user set */ + XFREE(ssl->buffers.dtlsCtx.pendingPeer.sa, ssl->heap, + DYNAMIC_TYPE_SOCKADDR); + ssl->buffers.dtlsCtx.pendingPeer.sa = NULL; + ssl->buffers.dtlsCtx.pendingPeer.sz = 0; + ssl->buffers.dtlsCtx.pendingPeer.bufSz = 0; +#endif } int DtlsIgnoreError(int err) @@ -1108,6 +1117,26 @@ static int DtlsCidGet(WOLFSSL* ssl, unsigned char* buf, int bufferSz, int rx) return WOLFSSL_SUCCESS; } +static int DtlsCidGet0(WOLFSSL* ssl, unsigned char** cid, int rx) +{ + ConnectionID* id; + CIDInfo* info; + + if (ssl == NULL || cid == NULL) + return BAD_FUNC_ARG; + + info = DtlsCidGetInfo(ssl); + if (info == NULL) + return WOLFSSL_FAILURE; + + id = rx ? info->rx : info->tx; + if (id == NULL || id->length == 0) + return WOLFSSL_SUCCESS; + + *cid = id->id; + return WOLFSSL_SUCCESS; +} + static CIDInfo* DtlsCidGetInfoFromExt(byte* ext) { WOLFSSL** sslPtr; @@ -1366,6 +1395,11 @@ int wolfSSL_dtls_cid_get_rx(WOLFSSL* ssl, unsigned char* buf, return DtlsCidGet(ssl, buf, bufferSz, 1); } +int wolfSSL_dtls_cid_get0_rx(WOLFSSL* ssl, unsigned char** cid) +{ + return DtlsCidGet0(ssl, cid, 1); +} + int wolfSSL_dtls_cid_get_tx_size(WOLFSSL* ssl, unsigned int* size) { return DtlsCidGetSize(ssl, size, 0); @@ -1377,10 +1411,40 @@ int wolfSSL_dtls_cid_get_tx(WOLFSSL* ssl, unsigned char* buf, return DtlsCidGet(ssl, buf, bufferSz, 0); } +int wolfSSL_dtls_cid_get0_tx(WOLFSSL* ssl, unsigned char** cid) +{ + return DtlsCidGet0(ssl, cid, 0); +} + int wolfSSL_dtls_cid_max_size(void) { return DTLS_CID_MAX_SIZE; } + +void wolfSSL_dtls_cid_parse(const unsigned char* msg, unsigned int msgSz, + const unsigned char** cid, unsigned int cidSz) +{ + if (cid == NULL) + return; + *cid = NULL; + /* we need at least the first byte to check version */ + if (msg == NULL || cidSz == 0 || msgSz < OPAQUE8_LEN + cidSz) + return; + if (msg[0] == dtls12_cid) { + /* DTLS 1.2 CID packet */ + if (msgSz < DTLS_RECORD_HEADER_SZ + cidSz) + return; + /* content type(1) + version(2) + epoch(2) + sequence(6) */ + *cid = msg + ENUM_LEN + VERSION_SZ + OPAQUE16_LEN + OPAQUE16_LEN + + OPAQUE32_LEN; + } + else if (Dtls13UnifiedHeaderCIDPresent(msg[0])) { + /* DTLS 1.3 CID packet */ + if (msgSz < OPAQUE8_LEN + cidSz) + return; + *cid = msg + OPAQUE8_LEN; + } +} #endif /* WOLFSSL_DTLS_CID */ byte DtlsGetCidTxSize(WOLFSSL* ssl) @@ -1413,6 +1477,11 @@ byte DtlsGetCidRxSize(WOLFSSL* ssl) #endif } +byte wolfSSL_is_stateful(WOLFSSL* ssl) +{ + return (byte)(ssl != NULL ? ssl->options.dtlsStateful : 0); +} + #endif /* WOLFSSL_DTLS */ #endif /* WOLFCRYPT_ONLY */ diff --git a/src/dtls13.c b/src/dtls13.c index 5011f7d85b..161ce4f4c5 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -1151,6 +1151,11 @@ static int Dtls13UnifiedHeaderParseCID(WOLFSSL* ssl, byte flags, return 0; } +int Dtls13UnifiedHeaderCIDPresent(byte flags) +{ + return Dtls13IsUnifiedHeader(flags) && (flags & DTLS13_CID_BIT); +} + #else #define Dtls13AddCID(a, b, c, d) 0 #define Dtls13UnifiedHeaderParseCID(a, b, c, d, e) 0 diff --git a/src/internal.c b/src/internal.c index 5b69bae1d6..88feeab08f 100644 --- a/src/internal.c +++ b/src/internal.c @@ -8261,6 +8261,11 @@ void wolfSSL_ResourceFree(WOLFSSL* ssl) } XFREE(ssl->buffers.dtlsCtx.peer.sa, ssl->heap, DYNAMIC_TYPE_SOCKADDR); ssl->buffers.dtlsCtx.peer.sa = NULL; +#ifdef WOLFSSL_DTLS_CID + XFREE(ssl->buffers.dtlsCtx.pendingPeer.sa, ssl->heap, + DYNAMIC_TYPE_SOCKADDR); + ssl->buffers.dtlsCtx.pendingPeer.sa = NULL; +#endif #ifndef NO_WOLFSSL_SERVER if (ssl->buffers.dtlsCookieSecret.buffer != NULL) { ForceZero(ssl->buffers.dtlsCookieSecret.buffer, @@ -11522,15 +11527,15 @@ static int GetDtlsRecordHeader(WOLFSSL* ssl, word32* inOutIdx, #ifdef WOLFSSL_DTLS_CID if (rh->type == dtls12_cid) { - byte cid[DTLS_CID_MAX_SIZE]; + byte* ourCid = NULL; if (ssl->buffers.inputBuffer.length - *inOutIdx < (word32)cidSz + LENGTH_SZ) return LENGTH_ERROR; - if (cidSz > DTLS_CID_MAX_SIZE || - wolfSSL_dtls_cid_get_rx(ssl, cid, cidSz) != WOLFSSL_SUCCESS) + if (cidSz != DtlsGetCidRxSize(ssl) || + wolfSSL_dtls_cid_get0_rx(ssl, &ourCid) != WOLFSSL_SUCCESS) return DTLS_CID_ERROR; - if (XMEMCMP(ssl->buffers.inputBuffer.buffer + *inOutIdx, - cid, cidSz) != 0) + if (XMEMCMP(ssl->buffers.inputBuffer.buffer + *inOutIdx, ourCid, cidSz) + != 0) return DTLS_CID_ERROR; *inOutIdx += cidSz; } @@ -11740,7 +11745,7 @@ int GetDtlsHandShakeHeader(WOLFSSL* ssl, const byte* input, { word32 idx = *inOutIdx; - *inOutIdx += HANDSHAKE_HEADER_SZ + DTLS_HANDSHAKE_EXTRA; + *inOutIdx += DTLS_HANDSHAKE_HEADER_SZ; if (*inOutIdx > totalSz) { WOLFSSL_ERROR(BUFFER_E); return BUFFER_E; @@ -21045,11 +21050,14 @@ static int GetInputData(WOLFSSL *ssl, word32 size) int usedLength; int dtlsExtra = 0; + if (ssl->options.disableRead) + return WC_NO_ERR_TRACE(WANT_READ); /* check max input length */ - usedLength = (int)(ssl->buffers.inputBuffer.length - ssl->buffers.inputBuffer.idx); - maxLength = (int)(ssl->buffers.inputBuffer.bufferSize - (word32)usedLength); - inSz = (int)(size - (word32)usedLength); /* from last partial read */ + usedLength = (int)(ssl->buffers.inputBuffer.length - + ssl->buffers.inputBuffer.idx); + maxLength = (int)(ssl->buffers.inputBuffer.bufferSize - + (word32)usedLength); #ifdef WOLFSSL_DTLS if (ssl->options.dtls && IsDtlsNotSctpMode(ssl)) { @@ -21063,11 +21071,20 @@ static int GetInputData(WOLFSSL *ssl, word32 size) if (size < (word32)inSz) dtlsExtra = (int)(inSz - size); } + else #endif + { + /* check that no lengths or size values are negative */ + if (usedLength < 0 || maxLength < 0) { + return BUFFER_ERROR; + } - /* check that no lengths or size values are negative */ - if (usedLength < 0 || maxLength < 0 || inSz <= 0) { - return BUFFER_ERROR; + /* Return if we have enough data already in the buffer */ + if (size <= (word32)usedLength) { + return 0; + } + + inSz = (int)(size - (word32)usedLength); /* from last partial read */ } if (inSz > maxLength) { @@ -21541,6 +21558,23 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) &ssl->curRL, &ssl->curSize); #ifdef WOLFSSL_DTLS +#ifdef WOLFSSL_DTLS_CID + if (ssl->options.dtls) { + if (!ssl->buffers.dtlsCtx.processingPendingRecord) + ssl->buffers.dtlsCtx.processingPendingRecord = 1; + else { + ssl->buffers.dtlsCtx.processingPendingRecord = 0; + if (ssl->buffers.dtlsCtx.pendingPeer.sa != NULL) { + /* Clear the pending peer */ + XFREE(ssl->buffers.dtlsCtx.pendingPeer.sa, ssl->heap, + DYNAMIC_TYPE_SOCKADDR); + ssl->buffers.dtlsCtx.pendingPeer.sa = NULL; + ssl->buffers.dtlsCtx.pendingPeer.sz = 0; + ssl->buffers.dtlsCtx.pendingPeer.bufSz = 0; + } + } + } +#endif if (ssl->options.dtls && DtlsShouldDrop(ssl, ret)) { ssl->options.processReply = doProcessInit; ssl->buffers.inputBuffer.length = 0; @@ -21965,8 +21999,9 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) /* the record layer is here */ case runProcessingOneRecord: -#ifdef WOLFSSL_DTLS13 +#ifdef WOLFSSL_DTLS if (ssl->options.dtls) { +#ifdef WOLFSSL_DTLS13 if (IsAtLeastTLSv1_3(ssl->version)) { if (!Dtls13CheckWindow(ssl)) { /* drop packet */ @@ -21990,11 +22025,27 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) } } } - else if (IsDtlsNotSctpMode(ssl)) { + else +#endif /* WOLFSSL_DTLS13 */ + if (IsDtlsNotSctpMode(ssl)) { DtlsUpdateWindow(ssl); } +#ifdef WOLFSSL_DTLS_CID + /* Update the peer if we were able to de-protect the message */ + if (IsEncryptionOn(ssl, 0) && + ssl->buffers.dtlsCtx.pendingPeer.sa != NULL) { + XFREE(ssl->buffers.dtlsCtx.peer.sa, ssl->heap, + DYNAMIC_TYPE_SOCKADDR); + ssl->buffers.dtlsCtx.peer = + ssl->buffers.dtlsCtx.pendingPeer; + ssl->buffers.dtlsCtx.pendingPeer.sa = NULL; + ssl->buffers.dtlsCtx.pendingPeer.sz = 0; + ssl->buffers.dtlsCtx.pendingPeer.bufSz = 0; + ssl->buffers.dtlsCtx.processingPendingRecord = 0; + } +#endif } -#endif /* WOLFSSL_DTLS13 */ +#endif /* WOLFSSL_DTLS */ ssl->options.processReply = runProcessingOneMessage; FALL_THROUGH; diff --git a/src/ssl.c b/src/ssl.c index a848b8b5cb..f487dfad9e 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -1732,6 +1732,17 @@ int wolfSSL_get_fd(const WOLFSSL* ssl) return fd; } +int wolfSSL_get_wfd(const WOLFSSL* ssl) +{ + int fd = -1; + WOLFSSL_ENTER("wolfSSL_get_fd"); + if (ssl) { + fd = ssl->wfd; + } + WOLFSSL_LEAVE("wolfSSL_get_fd", fd); + return fd; +} + int wolfSSL_dtls(WOLFSSL* ssl) { @@ -1864,38 +1875,89 @@ int wolfSSL_dtls_free_peer(void* addr) } #endif +#ifdef WOLFSSL_DTLS +static int SockAddrSet(WOLFSSL_SOCKADDR* sockAddr, void* peer, + unsigned int peerSz, void* heap) +{ + if (peer == NULL || peerSz == 0) { + if (sockAddr->sa != NULL) + XFREE(sockAddr->sa, heap, DYNAMIC_TYPE_SOCKADDR); + sockAddr->sa = NULL; + sockAddr->sz = 0; + sockAddr->bufSz = 0; + return WOLFSSL_SUCCESS; + } + + if (peerSz > sockAddr->bufSz) { + if (sockAddr->sa != NULL) + XFREE(sockAddr->sa, heap, DYNAMIC_TYPE_SOCKADDR); + sockAddr->sa = + (void*)XMALLOC(peerSz, heap, DYNAMIC_TYPE_SOCKADDR); + if (sockAddr->sa == NULL) { + sockAddr->sz = 0; + sockAddr->bufSz = 0; + return WOLFSSL_FAILURE; + } + sockAddr->bufSz = peerSz; + } + XMEMCPY(sockAddr->sa, peer, peerSz); + sockAddr->sz = peerSz; + return WOLFSSL_SUCCESS; +} +#endif + int wolfSSL_dtls_set_peer(WOLFSSL* ssl, void* peer, unsigned int peerSz) { #ifdef WOLFSSL_DTLS - void* sa; + int ret; if (ssl == NULL) return WOLFSSL_FAILURE; - if (peer == NULL || peerSz == 0) { - if (ssl->buffers.dtlsCtx.peer.sa != NULL) - XFREE(ssl->buffers.dtlsCtx.peer.sa,ssl->heap,DYNAMIC_TYPE_SOCKADDR); - ssl->buffers.dtlsCtx.peer.sa = NULL; - ssl->buffers.dtlsCtx.peer.sz = 0; - ssl->buffers.dtlsCtx.peer.bufSz = 0; + ret = SockAddrSet(&ssl->buffers.dtlsCtx.peer, peer, peerSz, ssl->heap); + if (ret == WOLFSSL_SUCCESS && !(peer == NULL || peerSz == 0)) + ssl->buffers.dtlsCtx.userSet = 1; + else ssl->buffers.dtlsCtx.userSet = 0; - return WOLFSSL_SUCCESS; - } + return ret; +#else + (void)ssl; + (void)peer; + (void)peerSz; + return WOLFSSL_NOT_IMPLEMENTED; +#endif +} + +#ifdef WOLFSSL_DTLS_CID +int wolfSSL_dtls_set_pending_peer(WOLFSSL* ssl, void* peer, unsigned int peerSz) +{ +#ifdef WOLFSSL_DTLS + int ret = WOLFSSL_FAILURE; - sa = (void*)XMALLOC(peerSz, ssl->heap, DYNAMIC_TYPE_SOCKADDR); - if (sa != NULL) { - if (ssl->buffers.dtlsCtx.peer.sa != NULL) { - XFREE(ssl->buffers.dtlsCtx.peer.sa,ssl->heap,DYNAMIC_TYPE_SOCKADDR); - ssl->buffers.dtlsCtx.peer.sa = NULL; + if (ssl == NULL) + return WOLFSSL_FAILURE; + + if (ssl->buffers.dtlsCtx.peer.sa != NULL && + ssl->buffers.dtlsCtx.peer.sz == peerSz && + XMEMCMP(ssl->buffers.dtlsCtx.peer.sa, peer, peerSz) == 0) { + /* Already the current peer. */ + if (ssl->buffers.dtlsCtx.pendingPeer.sa != NULL) { + /* Clear any other pendingPeer */ + XFREE(ssl->buffers.dtlsCtx.pendingPeer.sa, ssl->heap, + DYNAMIC_TYPE_SOCKADDR); + ssl->buffers.dtlsCtx.pendingPeer.sa = NULL; + ssl->buffers.dtlsCtx.pendingPeer.sz = 0; + ssl->buffers.dtlsCtx.pendingPeer.bufSz = 0; } - XMEMCPY(sa, peer, peerSz); - ssl->buffers.dtlsCtx.peer.sa = sa; - ssl->buffers.dtlsCtx.peer.sz = peerSz; - ssl->buffers.dtlsCtx.peer.bufSz = peerSz; - ssl->buffers.dtlsCtx.userSet = 1; - return WOLFSSL_SUCCESS; + ret = WOLFSSL_SUCCESS; } - return WOLFSSL_FAILURE; + else { + ret = SockAddrSet(&ssl->buffers.dtlsCtx.pendingPeer, peer, peerSz, + ssl->heap); + } + if (ret == WOLFSSL_SUCCESS) + ssl->buffers.dtlsCtx.processingPendingRecord = 0; + return ret; #else (void)ssl; (void)peer; @@ -1903,6 +1965,7 @@ int wolfSSL_dtls_set_peer(WOLFSSL* ssl, void* peer, unsigned int peerSz) return WOLFSSL_NOT_IMPLEMENTED; #endif } +#endif int wolfSSL_dtls_get_peer(WOLFSSL* ssl, void* peer, unsigned int* peerSz) { @@ -1927,6 +1990,27 @@ int wolfSSL_dtls_get_peer(WOLFSSL* ssl, void* peer, unsigned int* peerSz) #endif } +int wolfSSL_dtls_get0_peer(WOLFSSL* ssl, const void** peer, + unsigned int* peerSz) +{ +#ifdef WOLFSSL_DTLS + if (ssl == NULL) + return WOLFSSL_FAILURE; + + if (peer == NULL || peerSz == NULL) + return WOLFSSL_FAILURE; + + *peer = ssl->buffers.dtlsCtx.peer.sa; + *peerSz = ssl->buffers.dtlsCtx.peer.sz; + return WOLFSSL_SUCCESS; +#else + (void)ssl; + (void)peer; + (void)peerSz; + return WOLFSSL_NOT_IMPLEMENTED; +#endif +} + #if defined(WOLFSSL_SCTP) && defined(WOLFSSL_DTLS) @@ -2897,6 +2981,42 @@ int wolfSSL_write(WOLFSSL* ssl, const void* data, int sz) return ret; } +int wolfSSL_inject(WOLFSSL* ssl, const void* data, int sz) +{ + int maxLength; + int usedLength; + + WOLFSSL_ENTER("wolfSSL_read_internal"); + + if (ssl == NULL || data == NULL || sz <= 0) + return BAD_FUNC_ARG; + + usedLength = (int)(ssl->buffers.inputBuffer.length - + ssl->buffers.inputBuffer.idx); + maxLength = (int)(ssl->buffers.inputBuffer.bufferSize - + (word32)usedLength); + + if (sz > maxLength) { + /* Need to make space */ + int ret; + if (ssl->buffers.clearOutputBuffer.length > 0) { + /* clearOutputBuffer points into so reallocating inputBuffer will + * invalidate clearOutputBuffer and lose app data */ + WOLFSSL_MSG("Can't inject while there is application data to read"); + return APP_DATA_READY; + } + ret = GrowInputBuffer(ssl, sz, usedLength); + if (ret < 0) + return ret; + } + + XMEMCPY(ssl->buffers.inputBuffer.buffer + ssl->buffers.inputBuffer.idx, + data, sz); + ssl->buffers.inputBuffer.length += sz; + + return WOLFSSL_SUCCESS; +} + static int wolfSSL_read_internal(WOLFSSL* ssl, void* data, int sz, int peek) { int ret; @@ -9222,7 +9342,13 @@ int wolfSSL_dtls_retransmit(WOLFSSL* ssl) return WOLFSSL_FATAL_ERROR; if (!ssl->options.handShakeDone) { - int result = DtlsMsgPoolSend(ssl, 0); + int result; +#ifdef WOLFSSL_DTLS13 + if (IsAtLeastTLSv1_3(ssl->version)) + result = Dtls13DoScheduledWork(ssl); + else +#endif + result = DtlsMsgPoolSend(ssl, 0); if (result < 0) { ssl->error = result; WOLFSSL_ERROR(result); @@ -9230,7 +9356,7 @@ int wolfSSL_dtls_retransmit(WOLFSSL* ssl) } } - return 0; + return WOLFSSL_SUCCESS; } #endif /* DTLS */ @@ -10399,6 +10525,76 @@ int wolfSSL_DTLS_SetCookieSecret(WOLFSSL* ssl, #if defined(WOLFSSL_DTLS) && !defined(NO_WOLFSSL_SERVER) +struct chGoodDisableReadCbCtx { + ClientHelloGoodCb userCb; + void* userCtx; +}; + +static int chGoodDisableReadCB(WOLFSSL* ssl, void* ctx) +{ + struct chGoodDisableReadCbCtx* cb = (struct chGoodDisableReadCbCtx*)ctx; + int ret = 0; + if (cb->userCb != NULL) + ret = cb->userCb(ssl, cb->userCtx); + if (ret >= 0) + wolfSSL_SSLDisableRead(ssl); + return ret; +} + +/** + * Statelessly listen for a connection + * @param ssl The ssl object to use for listening to connections + * @return WOLFSSL_SUCCESS - ClientHello containing a valid cookie was received + * The connection can be continued with wolfSSL_accept + * WOLFSSL_FAILURE - The I/O layer returned WANT_READ. This is either + * because there is no data to read and we are using + * non-blocking sockets or we sent a cookie request + * and we are waiting for a reply. The user should + * call wolfDTLS_accept_stateless again after data + * becomes available in the I/O layer. + * WOLFSSL_FATAL_ERROR - A fatal error occurred. The ssl object should + * be free'd and allocated again to continue. + */ +int wolfDTLS_accept_stateless(WOLFSSL* ssl) +{ + byte disableRead; + int ret = WOLFSSL_FATAL_ERROR; + struct chGoodDisableReadCbCtx cb; + + WOLFSSL_ENTER("wolfDTLS_SetChGoodCb"); + + if (ssl == NULL) + return WOLFSSL_FATAL_ERROR; + + /* Save this to restore it later */ + disableRead = (byte)ssl->options.disableRead; + cb.userCb = ssl->chGoodCb; + cb.userCtx = ssl->chGoodCtx; + + /* Register our own callback so that we can disable reading */ + if (wolfDTLS_SetChGoodCb(ssl, chGoodDisableReadCB, &cb) != WOLFSSL_SUCCESS) + return WOLFSSL_FATAL_ERROR; + + ret = wolfSSL_accept(ssl); + /* restore user options */ + ssl->options.disableRead = disableRead; + (void)wolfDTLS_SetChGoodCb(ssl, cb.userCb, cb.userCtx); + if (ret == WOLFSSL_SUCCESS) { + WOLFSSL_MSG("should not happen. maybe the user called " + "wolfDTLS_accept_stateless instead of wolfSSL_accept"); + } + else if (ssl->error == WC_NO_ERR_TRACE(WANT_READ)) { + if (ssl->options.dtlsStateful) + ret = WOLFSSL_SUCCESS; + else + ret = WOLFSSL_FAILURE; + } + else { + ret = WOLFSSL_FATAL_ERROR; + } + return ret; +} + int wolfDTLS_SetChGoodCb(WOLFSSL* ssl, ClientHelloGoodCb cb, void* user_ctx) { WOLFSSL_ENTER("wolfDTLS_SetChGoodCb"); diff --git a/src/wolfio.c b/src/wolfio.c index 8d0b2f089b..3aed642675 100644 --- a/src/wolfio.c +++ b/src/wolfio.c @@ -2351,6 +2351,20 @@ void wolfSSL_SSLSetIOSend(WOLFSSL *ssl, CallbackIOSend CBIOSend) } } +void wolfSSL_SSLDisableRead(WOLFSSL *ssl) +{ + if (ssl) { + ssl->options.disableRead = 1; + } +} + +void wolfSSL_SSLEnableRead(WOLFSSL *ssl) +{ + if (ssl) { + ssl->options.disableRead = 0; + } +} + void wolfSSL_SetIOReadCtx(WOLFSSL* ssl, void *rctx) { diff --git a/tests/api.c b/tests/api.c index ef5d8ad3be..e45973dcad 100644 --- a/tests/api.c +++ b/tests/api.c @@ -90313,7 +90313,7 @@ static void test_wolfSSL_dtls_plaintext_server(WOLFSSL* ssl) static void test_wolfSSL_dtls_plaintext_client(WOLFSSL* ssl) { byte ch[50]; - int fd = wolfSSL_get_fd(ssl); + int fd = wolfSSL_get_wfd(ssl); byte msg[] = "This is a msg for the server"; byte reply[40]; @@ -90383,7 +90383,7 @@ static void test_wolfSSL_dtls12_fragments_spammer(WOLFSSL* ssl) size_t seq_offset = 0; size_t msg_offset = 0; int i; - int fd = wolfSSL_get_fd(ssl); + int fd = wolfSSL_get_wfd(ssl); int ret = wolfSSL_connect_cert(ssl); /* This gets us past the cookie */ word32 seq_number = 100; /* start high so server definitely reads this */ word16 msg_number = 50; /* start high so server has to buffer this */ @@ -90445,7 +90445,7 @@ static void test_wolfSSL_dtls13_fragments_spammer(WOLFSSL* ssl) byte b[150]; /* buffer for the messages to send */ size_t idx = 0; size_t msg_offset = 0; - int fd = wolfSSL_get_fd(ssl); + int fd = wolfSSL_get_wfd(ssl); word16 msg_number = 10; /* start high so server has to buffer this */ int ret = wolfSSL_connect_cert(ssl); /* This gets us past the cookie */ AssertIntEQ(ret, 1); @@ -90550,7 +90550,7 @@ static void test_wolfSSL_dtls_send_alert(WOLFSSL* ssl) 0x46 /* protocol version */ }; - fd = wolfSSL_get_fd(ssl); + fd = wolfSSL_get_wfd(ssl); ret = (int)send(fd, alert_msg, sizeof(alert_msg), 0); AssertIntGT(ret, 0); } @@ -90620,7 +90620,7 @@ static void test_wolfSSL_send_bad_record(WOLFSSL* ssl) 0x03, 0x18, 0x72 }; - fd = wolfSSL_get_fd(ssl); + fd = wolfSSL_get_wfd(ssl); AssertIntGE(fd, 0); ret = (int)send(fd, bad_msg, sizeof(bad_msg), 0); AssertIntEQ(ret, sizeof(bad_msg)); @@ -90947,7 +90947,7 @@ static void test_wolfSSL_dtls_send_ch(WOLFSSL* ssl) 0x31, 0x68, 0x4c }; - fd = wolfSSL_get_fd(ssl); + fd = wolfSSL_get_wfd(ssl); ret = (int)send(fd, ch_msg, sizeof(ch_msg), 0); AssertIntGT(ret, 0); /* consume the HRR otherwise handshake will fail */ @@ -91018,7 +91018,7 @@ static void test_wolfSSL_dtls_send_ch_with_invalid_cookie(WOLFSSL* ssl) 0x02, 0x02, 0x2f }; - fd = wolfSSL_get_fd(ssl); + fd = wolfSSL_get_wfd(ssl); ret = (int)send(fd, ch_msh_invalid_cookie, sizeof(ch_msh_invalid_cookie), 0); AssertIntGT(ret, 0); /* should reply with an illegal_parameter reply */ @@ -93932,18 +93932,31 @@ static int test_wolfSSL_dtls_stateless2(void) XMEMSET(&test_ctx, 0, sizeof(test_ctx)); ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method), 0); - ExpectNotNull(ssl_c2 = wolfSSL_new(ctx_c)); - wolfSSL_SetIOWriteCtx(ssl_c2, &test_ctx); - wolfSSL_SetIOReadCtx(ssl_c2, &test_ctx); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, NULL, &ssl_c2, NULL, + wolfDTLSv1_2_client_method, NULL), 0); + ExpectFalse(wolfSSL_is_stateful(ssl_s)); /* send CH */ ExpectTrue((wolfSSL_connect(ssl_c2) == WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)) && (ssl_c2->error == WC_NO_ERR_TRACE(WANT_READ))); - ExpectTrue((wolfSSL_accept(ssl_s) == WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)) && - (ssl_s->error == WC_NO_ERR_TRACE(WANT_READ))); + ExpectIntEQ(wolfDTLS_accept_stateless(ssl_s), WOLFSSL_FAILURE); + ExpectFalse(wolfSSL_is_stateful(ssl_s)); ExpectIntNE(test_ctx.c_len, 0); /* consume HRR */ test_ctx.c_len = 0; + /* send CH1 */ + ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_FATAL_ERROR); + ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR), + WOLFSSL_ERROR_WANT_READ); + /* send HRR */ + ExpectIntEQ(wolfDTLS_accept_stateless(ssl_s), WOLFSSL_FAILURE); + /* send CH2 */ + ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_FATAL_ERROR); + ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR), + WOLFSSL_ERROR_WANT_READ); + /* send HRR */ + ExpectIntEQ(wolfDTLS_accept_stateless(ssl_s), WOLFSSL_SUCCESS); ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectTrue(wolfSSL_is_stateful(ssl_s)); wolfSSL_free(ssl_c2); wolfSSL_free(ssl_c); @@ -100204,6 +100217,163 @@ static int test_ocsp_callback_fails(void) defined(HAVE_OCSP) && \ defined(HAVE_CERTIFICATE_STATUS_REQUEST) */ +#ifdef HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES +static int test_wolfSSL_SSLDisableRead_recv(WOLFSSL *ssl, char *buf, int sz, + void *ctx) +{ + (void)ssl; + (void)buf; + (void)sz; + (void)ctx; + return WOLFSSL_CBIO_ERR_GENERAL; +} + +static int test_wolfSSL_SSLDisableRead(void) +{ + EXPECT_DECLS; + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL *ssl_c = NULL; + struct test_memio_ctx test_ctx; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, NULL, &ssl_c, NULL, + wolfTLS_client_method, NULL), 0); + wolfSSL_SSLSetIORecv(ssl_c, test_wolfSSL_SSLDisableRead_recv); + wolfSSL_SSLDisableRead(ssl_c); + + /* Disabling reading should not even go into the IO layer */ + ExpectIntEQ(wolfSSL_negotiate(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + wolfSSL_SSLEnableRead(ssl_c); + /* By enabling reading we should reach the IO that will return an error */ + ExpectIntEQ(wolfSSL_negotiate(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), SOCKET_ERROR_E); + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + return EXPECT_RESULT(); +} +#else +static int test_wolfSSL_SSLDisableRead(void) +{ + EXPECT_DECLS; + return EXPECT_RESULT(); +} +#endif + +static int test_wolfSSL_inject(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) + size_t i; + struct { + method_provider client_meth; + method_provider server_meth; + const char* tls_version; + } params[] = { +#if defined(WOLFSSL_TLS13) +/* With WOLFSSL_TLS13_MIDDLEBOX_COMPAT a short ID will result in an error */ + { wolfTLSv1_3_client_method, wolfTLSv1_3_server_method, "TLSv1_3" }, +#ifdef WOLFSSL_DTLS13 + { wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method, "DTLSv1_3" }, +#endif +#endif +#ifndef WOLFSSL_NO_TLS12 + { wolfTLSv1_2_client_method, wolfTLSv1_2_server_method, "TLSv1_2" }, +#ifdef WOLFSSL_DTLS + { wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method, "DTLSv1_2" }, +#endif +#endif +#if !defined(NO_OLD_TLS) + { wolfTLSv1_1_client_method, wolfTLSv1_1_server_method, "TLSv1_1" }, +#ifdef WOLFSSL_DTLS + { wolfDTLSv1_client_method, wolfDTLSv1_server_method, "DTLSv1_0" }, +#endif +#endif + }; + + for (i = 0; i < XELEM_CNT(params) && !EXPECT_FAIL(); i++) { + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + struct test_memio_ctx test_ctx; + WOLFSSL_ALERT_HISTORY h; + int rounds; + + printf("Testing %s\n", params[i].tls_version); + + XMEMSET(&h, 0, sizeof(h)); + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + params[i].client_meth, params[i].server_meth), 0); + + for (rounds = 0; rounds < 10 && EXPECT_SUCCESS(); rounds++) { + if (wolfSSL_negotiate(ssl_c) != 1) { + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), + WOLFSSL_ERROR_WANT_READ); + } + if (test_ctx.s_len > 0) { + ExpectIntEQ(wolfSSL_inject(ssl_s, test_ctx.s_buff, + test_ctx.s_len), 1); + test_ctx.s_len = 0; + } + if (wolfSSL_negotiate(ssl_s) != 1) { + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), + WOLFSSL_ERROR_WANT_READ); + } + if (test_ctx.c_len > 0) { + ExpectIntEQ(wolfSSL_inject(ssl_c, test_ctx.c_buff, + test_ctx.c_len), 1); + test_ctx.c_len = 0; + } + } + ExpectIntEQ(wolfSSL_negotiate(ssl_c), 1); + ExpectIntEQ(wolfSSL_negotiate(ssl_s), 1); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + } +#endif + return EXPECT_RESULT(); +} + +static int test_wolfSSL_dtls_cid_parse(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID) + const unsigned char* cid = NULL; + /* Taken from Wireshark. Right-click -> copy -> ... as escaped string */ + /* Plaintext ServerHelloDone. No CID. */ + byte noCid[] = + "\x16\xfe\xfd\x00\x00\x00\x00\x00\x00\x00\x04\x00\x0c\x0e\x00\x00" \ + "\x00\x00\x04\x00\x00\x00\x00\x00\x00"; + /* 1.2 app data containing CID */ + byte cid12[] = + "\x19\xfe\xfd\x00\x01\x00\x00\x00\x00\x00\x01\x77\xa3\x79\x34\xb3" \ + "\xf1\x1f\x34\x00\x1f\xdb\x8c\x28\x25\x9f\xe1\x02\x26\x77\x1c\x3a" \ + "\x50\x1b\x50\x99\xd0\xb5\x20\xd8\x2c\x2e\xaa\x36\x36\xe0\xb7\xb7" \ + "\xf7\x7d\xff\xb0"; + /* 1.3 app data containing CID */ + byte cid13[] = + "\x3f\x70\x64\x04\xc6\xfb\x97\x21\xd9\x28\x27\x00\x17\xc1\x01\x86" \ + "\xe7\x23\x2c\xad\x65\x83\xa8\xf4\xbf\xbf\x7b\x25\x16\x80\x19\xc3" \ + "\x81\xda\xf5\x3f"; + + wolfSSL_dtls_cid_parse(noCid, sizeof(noCid), &cid, 8); + ExpectPtrEq(cid, NULL); + wolfSSL_dtls_cid_parse(cid12, sizeof(cid12), &cid, 8); + ExpectPtrEq(cid, cid12 + 11); + wolfSSL_dtls_cid_parse(cid13, sizeof(cid13), &cid, 8); + ExpectPtrEq(cid, cid13 + 1); + + +#endif + return EXPECT_RESULT(); +} /*----------------------------------------------------------------------------* | Main @@ -101607,6 +101777,9 @@ TEST_CASE testCases[] = { TEST_DECL(test_get_signature_nid), TEST_DECL(test_tls_cert_store_unchanged), TEST_DECL(test_wolfSSL_SendUserCanceled), + TEST_DECL(test_wolfSSL_SSLDisableRead), + TEST_DECL(test_wolfSSL_inject), + TEST_DECL(test_wolfSSL_dtls_cid_parse), /* This test needs to stay at the end to clean up any caches allocated. */ TEST_DECL(test_wolfSSL_Cleanup) }; diff --git a/wolfssl/internal.h b/wolfssl/internal.h index f5ce5b02ef..f2c63eeeb3 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -2759,6 +2759,11 @@ struct WOLFSSL_SOCKADDR { typedef struct WOLFSSL_DTLS_CTX { WOLFSSL_SOCKADDR peer; +#ifdef WOLFSSL_DTLS_CID + WOLFSSL_SOCKADDR pendingPeer; /* When using CID's, we don't want to update + * the peer's address until we successfully + * de-protect the record. */ +#endif int rfd; int wfd; byte userSet:1; @@ -2766,6 +2771,9 @@ typedef struct WOLFSSL_DTLS_CTX { * connected (connect() and bind() both called). * This means that sendto and recvfrom do not need to * specify and store the peer address. */ +#ifdef WOLFSSL_DTLS_CID + byte processingPendingRecord:1; +#endif } WOLFSSL_DTLS_CTX; @@ -3712,6 +3720,7 @@ WOLFSSL_LOCAL int TLSX_ConnectionID_Parse(WOLFSSL* ssl, const byte* input, WOLFSSL_LOCAL void DtlsCIDOnExtensionsParsed(WOLFSSL* ssl); WOLFSSL_LOCAL byte DtlsCIDCheck(WOLFSSL* ssl, const byte* input, word16 inputSize); +WOLFSSL_LOCAL int Dtls13UnifiedHeaderCIDPresent(byte flags); #endif /* WOLFSSL_DTLS_CID */ WOLFSSL_LOCAL byte DtlsGetCidTxSize(WOLFSSL* ssl); WOLFSSL_LOCAL byte DtlsGetCidRxSize(WOLFSSL* ssl); @@ -5052,6 +5061,7 @@ struct Options { #if defined(HAVE_DANE) word16 useDANE:1; #endif /* HAVE_DANE */ + word16 disableRead:1; #ifdef WOLFSSL_DTLS byte haveMcast; /* using multicast ? */ #endif diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index c3d45a6ea6..ebce8efddc 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -1359,6 +1359,7 @@ WOLFSSL_API const char* wolfSSL_get_shared_ciphers(WOLFSSL* ssl, char* buf, int len); WOLFSSL_API const char* wolfSSL_get_curve_name(WOLFSSL* ssl); WOLFSSL_API int wolfSSL_get_fd(const WOLFSSL* ssl); +WOLFSSL_API int wolfSSL_get_wfd(const WOLFSSL* ssl); /* please see note at top of README if you get an error from connect */ WOLFSSL_ABI WOLFSSL_API int wolfSSL_connect(WOLFSSL* ssl); WOLFSSL_ABI WOLFSSL_API int wolfSSL_write( @@ -1366,6 +1367,7 @@ WOLFSSL_ABI WOLFSSL_API int wolfSSL_write( WOLFSSL_ABI WOLFSSL_API int wolfSSL_read(WOLFSSL* ssl, void* data, int sz); WOLFSSL_API int wolfSSL_peek(WOLFSSL* ssl, void* data, int sz); WOLFSSL_ABI WOLFSSL_API int wolfSSL_accept(WOLFSSL* ssl); +WOLFSSL_API int wolfSSL_inject(WOLFSSL* ssl, const void* data, int sz); WOLFSSL_API int wolfSSL_CTX_mutual_auth(WOLFSSL_CTX* ctx, int req); WOLFSSL_API int wolfSSL_mutual_auth(WOLFSSL* ssl, int req); @@ -1733,8 +1735,16 @@ WOLFSSL_API int wolfSSL_dtls(WOLFSSL* ssl); WOLFSSL_API void* wolfSSL_dtls_create_peer(int port, char* ip); WOLFSSL_API int wolfSSL_dtls_free_peer(void* addr); -WOLFSSL_API int wolfSSL_dtls_set_peer(WOLFSSL* ssl, void* peer, unsigned int peerSz); -WOLFSSL_API int wolfSSL_dtls_get_peer(WOLFSSL* ssl, void* peer, unsigned int* peerSz); +WOLFSSL_API int wolfSSL_dtls_set_peer(WOLFSSL* ssl, void* peer, + unsigned int peerSz); +WOLFSSL_API int wolfSSL_dtls_set_pending_peer(WOLFSSL* ssl, void* peer, + unsigned int peerSz); +WOLFSSL_API int wolfSSL_dtls_get_peer(WOLFSSL* ssl, void* peer, + unsigned int* peerSz); +WOLFSSL_API int wolfSSL_dtls_get0_peer(WOLFSSL* ssl, const void** peer, + unsigned int* peerSz); + +WOLFSSL_API byte wolfSSL_is_stateful(WOLFSSL* ssl); #if defined(WOLFSSL_SCTP) && defined(WOLFSSL_DTLS) WOLFSSL_API int wolfSSL_CTX_dtls_set_sctp(WOLFSSL_CTX* ctx); @@ -4709,6 +4719,7 @@ WOLFSSL_API int wolfSSL_CTX_DisableExtendedMasterSecret(WOLFSSL_CTX* ctx); #if defined(WOLFSSL_DTLS) && !defined(NO_WOLFSSL_SERVER) +WOLFSSL_API int wolfDTLS_accept_stateless(WOLFSSL* ssl); /* notify user we parsed a verified ClientHello is done. This only has an effect * on the server end. */ typedef int (*ClientHelloGoodCb)(WOLFSSL* ssl, void*); @@ -5844,11 +5855,15 @@ WOLFSSL_API int wolfSSL_dtls_cid_get_rx_size(WOLFSSL* ssl, unsigned int* size); WOLFSSL_API int wolfSSL_dtls_cid_get_rx(WOLFSSL* ssl, unsigned char* buffer, unsigned int bufferSz); +WOLFSSL_API int wolfSSL_dtls_cid_get0_rx(WOLFSSL* ssl, unsigned char** cid); WOLFSSL_API int wolfSSL_dtls_cid_get_tx_size(WOLFSSL* ssl, unsigned int* size); WOLFSSL_API int wolfSSL_dtls_cid_get_tx(WOLFSSL* ssl, unsigned char* buffer, unsigned int bufferSz); +WOLFSSL_API int wolfSSL_dtls_cid_get0_tx(WOLFSSL* ssl, unsigned char** cid); WOLFSSL_API int wolfSSL_dtls_cid_max_size(void); +WOLFSSL_API void wolfSSL_dtls_cid_parse(const unsigned char* msg, + unsigned int msgSz, const unsigned char** cid, unsigned int cidSz); #endif /* defined(WOLFSSL_DTLS_CID) */ #ifdef WOLFSSL_DTLS_CH_FRAG diff --git a/wolfssl/wolfio.h b/wolfssl/wolfio.h index 576e8f4675..4d1145b005 100644 --- a/wolfssl/wolfio.h +++ b/wolfssl/wolfio.h @@ -611,6 +611,8 @@ WOLFSSL_API void wolfSSL_CTX_SetIORecv(WOLFSSL_CTX *ctx, CallbackIORecv CBIORecv WOLFSSL_API void wolfSSL_CTX_SetIOSend(WOLFSSL_CTX *ctx, CallbackIOSend CBIOSend); WOLFSSL_API void wolfSSL_SSLSetIORecv(WOLFSSL *ssl, CallbackIORecv CBIORecv); WOLFSSL_API void wolfSSL_SSLSetIOSend(WOLFSSL *ssl, CallbackIOSend CBIOSend); +WOLFSSL_API void wolfSSL_SSLDisableRead(WOLFSSL *ssl); +WOLFSSL_API void wolfSSL_SSLEnableRead(WOLFSSL *ssl); /* deprecated old name */ #define wolfSSL_SetIORecv wolfSSL_CTX_SetIORecv #define wolfSSL_SetIOSend wolfSSL_CTX_SetIOSend From 71337d2959b274c713bd6a88f30410f10ca34063 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 27 Nov 2024 13:11:35 +0100 Subject: [PATCH 2/8] Client TLS: Set traffic decrypt keys when parsing Finished --- src/tls13.c | 29 ++++++++++++++++++++--------- tests/api.c | 18 ++++++++++++++++++ 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/tls13.c b/src/tls13.c index fd7328c154..2c8767f207 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -2413,6 +2413,9 @@ static WC_INLINE void WriteSEQTls13(WOLFSSL* ssl, int verifyOrder, byte* out) if (seq[1] > ssl->keys.sequence_number_lo) ssl->keys.sequence_number_hi++; } +#ifdef WOLFSSL_DEBUG_TLS + WOLFSSL_MSG_EX("TLS 1.3 Write Sequence %d %d", seq[0], seq[1]); +#endif c32toa(seq[0], out); c32toa(seq[1], out + OPAQUE32_LEN); @@ -2428,14 +2431,11 @@ static WC_INLINE void WriteSEQTls13(WOLFSSL* ssl, int verifyOrder, byte* out) static WC_INLINE void BuildTls13Nonce(WOLFSSL* ssl, byte* nonce, const byte* iv, int order) { - int i; - + int seq_offset = AEAD_NONCE_SZ - SEQ_SZ; /* The nonce is the IV with the sequence XORed into the last bytes. */ - WriteSEQTls13(ssl, order, nonce + AEAD_NONCE_SZ - SEQ_SZ); - for (i = 0; i < AEAD_NONCE_SZ - SEQ_SZ; i++) - nonce[i] = iv[i]; - for (; i < AEAD_NONCE_SZ; i++) - nonce[i] ^= iv[i]; + WriteSEQTls13(ssl, order, nonce + seq_offset); + XMEMCPY(nonce, iv, seq_offset); + xorbuf(nonce + seq_offset, iv + seq_offset, SEQ_SZ); } #if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) @@ -10905,6 +10905,7 @@ int DoTls13Finished(WOLFSSL* ssl, const byte* input, word32* inOutIdx, /* Force input exhaustion at ProcessReply by consuming padSz. */ *inOutIdx += size + ssl->keys.padSz; +#ifndef NO_WOLFSSL_SERVER if (ssl->options.side == WOLFSSL_SERVER_END && !ssl->options.handShakeDone) { #ifdef WOLFSSL_EARLY_DATA @@ -10917,6 +10918,7 @@ int DoTls13Finished(WOLFSSL* ssl, const byte* input, word32* inOutIdx, if ((ret = SetKeysSide(ssl, DECRYPT_SIDE_ONLY)) != 0) return ret; } +#endif #ifndef NO_WOLFSSL_CLIENT if (ssl->options.side == WOLFSSL_CLIENT_END) @@ -11149,14 +11151,14 @@ static int SendTls13Finished(WOLFSSL* ssl) !ssl->options.handShakeDone) { #ifdef WOLFSSL_EARLY_DATA if (ssl->earlyData != no_early_data) { - if ((ret = DeriveTls13Keys(ssl, no_key, ENCRYPT_AND_DECRYPT_SIDE, + if ((ret = DeriveTls13Keys(ssl, no_key, ENCRYPT_SIDE_ONLY, 1)) != 0) { return ret; } } #endif /* Setup keys for application data messages. */ - if ((ret = SetKeysSide(ssl, ENCRYPT_AND_DECRYPT_SIDE)) != 0) + if ((ret = SetKeysSide(ssl, ENCRYPT_SIDE_ONLY)) != 0) return ret; #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) @@ -12831,12 +12833,21 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, ssl->earlyData == no_early_data)) != 0) { return ret; } + if (ssl->earlyData != no_early_data) { + if ((ret = DeriveTls13Keys(ssl, no_key, DECRYPT_SIDE_ONLY, + 1)) != 0) { + return ret; + } + } #else if ((ret = DeriveTls13Keys(ssl, traffic_key, ENCRYPT_AND_DECRYPT_SIDE, 1)) != 0) { return ret; } #endif + /* Setup keys for application data messages. */ + if ((ret = SetKeysSide(ssl, DECRYPT_SIDE_ONLY)) != 0) + return ret; } #ifdef WOLFSSL_POST_HANDSHAKE_AUTH if (type == certificate_request && diff --git a/tests/api.c b/tests/api.c index e45973dcad..22e910147c 100644 --- a/tests/api.c +++ b/tests/api.c @@ -99277,9 +99277,11 @@ static int test_tls13_early_data(void) XMEMSET(&test_ctx, 0, sizeof(test_ctx)); ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, params[i].client_meth, params[i].server_meth), 0); + wolfSSL_SetLoggingPrefix("client"); ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS); #ifdef WOLFSSL_DTLS13 if (params[i].isUdp) { + wolfSSL_SetLoggingPrefix("server"); #ifdef WOLFSSL_DTLS13_NO_HRR_ON_RESUME ExpectIntEQ(wolfSSL_dtls13_no_hrr_on_resume(ssl_s, 1), WOLFSSL_SUCCESS); #else @@ -99291,6 +99293,7 @@ static int test_tls13_early_data(void) #endif /* Test 0-RTT data */ + wolfSSL_SetLoggingPrefix("client"); ExpectIntEQ(wolfSSL_write_early_data(ssl_c, msg, sizeof(msg), &written), sizeof(msg)); ExpectIntEQ(written, sizeof(msg)); @@ -99302,6 +99305,7 @@ static int test_tls13_early_data(void) } /* Read first 0-RTT data (if split otherwise entire data) */ + wolfSSL_SetLoggingPrefix("server"); ExpectIntEQ(wolfSSL_read_early_data(ssl_s, msgBuf, sizeof(msgBuf), &read), sizeof(msg)); ExpectIntEQ(read, sizeof(msg)); @@ -99319,6 +99323,7 @@ static int test_tls13_early_data(void) } if (params[i].isUdp) { + wolfSSL_SetLoggingPrefix("client"); ExpectIntEQ(wolfSSL_connect(ssl_c), -1); ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WC_NO_ERR_TRACE(APP_DATA_READY)); @@ -99335,17 +99340,21 @@ static int test_tls13_early_data(void) * handshake status to us with non-blocking IO and we can't use * wolfSSL_accept as TLS layer may return ZERO_RETURN due to early data * parsing logic. */ + wolfSSL_SetLoggingPrefix("server"); ExpectFalse(wolfSSL_is_init_finished(ssl_s)); ExpectIntEQ(wolfSSL_read_early_data(ssl_s, msgBuf, sizeof(msgBuf), &read), 0); ExpectIntEQ(read, 0); ExpectTrue(wolfSSL_is_init_finished(ssl_s)); + wolfSSL_SetLoggingPrefix("client"); ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS); } else { + wolfSSL_SetLoggingPrefix("client"); ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS); + wolfSSL_SetLoggingPrefix("server"); ExpectFalse(wolfSSL_is_init_finished(ssl_s)); ExpectIntEQ(wolfSSL_read_early_data(ssl_s, msgBuf, sizeof(msgBuf), &read), 0); @@ -99353,18 +99362,23 @@ static int test_tls13_early_data(void) ExpectTrue(wolfSSL_is_init_finished(ssl_s)); /* Read server 0.5-RTT data */ + wolfSSL_SetLoggingPrefix("client"); ExpectIntEQ(wolfSSL_read(ssl_c, msgBuf, sizeof(msgBuf)), sizeof(msg4)); ExpectStrEQ(msg4, msgBuf); } /* Test bi-directional write */ + wolfSSL_SetLoggingPrefix("client"); ExpectIntEQ(wolfSSL_write(ssl_c, msg2, sizeof(msg2)), sizeof(msg2)); + wolfSSL_SetLoggingPrefix("server"); ExpectIntEQ(wolfSSL_read(ssl_s, msgBuf, sizeof(msgBuf)), sizeof(msg2)); ExpectStrEQ(msg2, msgBuf); ExpectIntEQ(wolfSSL_write(ssl_s, msg3, sizeof(msg3)), sizeof(msg3)); + wolfSSL_SetLoggingPrefix("client"); ExpectIntEQ(wolfSSL_read(ssl_c, msgBuf, sizeof(msgBuf)), sizeof(msg3)); ExpectStrEQ(msg3, msgBuf); + wolfSSL_SetLoggingPrefix(NULL); ExpectTrue(wolfSSL_session_reused(ssl_c)); ExpectTrue(wolfSSL_session_reused(ssl_s)); @@ -100310,10 +100324,12 @@ static int test_wolfSSL_inject(void) params[i].client_meth, params[i].server_meth), 0); for (rounds = 0; rounds < 10 && EXPECT_SUCCESS(); rounds++) { + wolfSSL_SetLoggingPrefix("client"); if (wolfSSL_negotiate(ssl_c) != 1) { ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); } + wolfSSL_SetLoggingPrefix("server"); if (test_ctx.s_len > 0) { ExpectIntEQ(wolfSSL_inject(ssl_s, test_ctx.s_buff, test_ctx.s_len), 1); @@ -100323,11 +100339,13 @@ static int test_wolfSSL_inject(void) ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); } + wolfSSL_SetLoggingPrefix("client"); if (test_ctx.c_len > 0) { ExpectIntEQ(wolfSSL_inject(ssl_c, test_ctx.c_buff, test_ctx.c_len), 1); test_ctx.c_len = 0; } + wolfSSL_SetLoggingPrefix(NULL); } ExpectIntEQ(wolfSSL_negotiate(ssl_c), 1); ExpectIntEQ(wolfSSL_negotiate(ssl_s), 1); From 3ded2bc05dd3a48377dad9a00881c1e273b9cfff Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Tue, 10 Dec 2024 14:29:46 +0100 Subject: [PATCH 3/8] Code review and jenkins fixes --- doc/dox_comments/header_files/ssl.h | 17 +++++++++++------ src/dtls.c | 18 ++++++++---------- src/internal.c | 3 +-- src/ssl.c | 10 ++++++---- src/wolfio.c | 10 +++++++--- tests/api.c | 12 +++--------- wolfssl/internal.h | 4 ++++ wolfssl/ssl.h | 4 ++-- 8 files changed, 42 insertions(+), 36 deletions(-) diff --git a/doc/dox_comments/header_files/ssl.h b/doc/dox_comments/header_files/ssl.h index 4d816fc936..7d7a629f8f 100644 --- a/doc/dox_comments/header_files/ssl.h +++ b/doc/dox_comments/header_files/ssl.h @@ -3971,7 +3971,8 @@ int wolfSSL_dtls_set_peer(WOLFSSL* ssl, void* peer, unsigned int peerSz); \sa wolfSSL_dtls_got_timeout \sa wolfSSL_dtls */ -int wolfSSL_dtls_set_pending_peer(WOLFSSL* ssl, void* peer, unsigned int peerSz); +int wolfSSL_dtls_set_pending_peer(WOLFSSL* ssl, void* peer, + unsigned int peerSz); /*! \brief This function gets the sockaddr_in (of size peerSz) of the current @@ -4042,7 +4043,8 @@ int wolfSSL_dtls_get_peer(WOLFSSL* ssl, void* peer, unsigned int* peerSz); \sa wolfSSL_dtls_set_peer \sa wolfSSL_dtls */ -int wolfSSL_dtls_get0_peer(WOLFSSL* ssl, const void** peer, unsigned int* peerSz); +int wolfSSL_dtls_get0_peer(WOLFSSL* ssl, const void** peer, + unsigned int* peerSz); /*! \ingroup Debug @@ -14286,9 +14288,13 @@ int wolfSSL_read_early_data(WOLFSSL* ssl, void* data, int sz, /*! \ingroup IO - \brief + \brief This function is called to inject data into the WOLFSSL object. This + is useful when data needs to be read from a single place and demultiplexed + into multiple connections. The caller should then call wolfSSL_read() to + extract the plaintext data from the WOLFSSL object. - \param [in] ssl a pointer to a WOLFSSL structure, created using wolfSSL_new(). + \param [in] ssl a pointer to a WOLFSSL structure, created using + wolfSSL_new(). \param [in] data data to inject into the ssl object. \param [in] sz number of bytes of data to inject. @@ -15145,8 +15151,7 @@ int wolfSSL_dtls_cid_get_rx(WOLFSSL* ssl, unsigned char* buffer, \brief Get the ConnectionID used by the other peer. See RFC 9146 and RFC 9147. - \return WOLFSSL_SUCCESS if ConnectionID was correctly copied, error code - otherwise + \return WOLFSSL_SUCCESS if ConnectionID was correctly set in cid. \param ssl A WOLFSSL object pointern \param cid Pointer that will be set to the internal memory that holds the CID diff --git a/src/dtls.c b/src/dtls.c index 6ea786c6a1..fc9014c31a 100644 --- a/src/dtls.c +++ b/src/dtls.c @@ -1421,29 +1421,27 @@ int wolfSSL_dtls_cid_max_size(void) return DTLS_CID_MAX_SIZE; } -void wolfSSL_dtls_cid_parse(const unsigned char* msg, unsigned int msgSz, - const unsigned char** cid, unsigned int cidSz) +const unsigned char* wolfSSL_dtls_cid_parse(const unsigned char* msg, + unsigned int msgSz, unsigned int cidSz) { - if (cid == NULL) - return; - *cid = NULL; /* we need at least the first byte to check version */ if (msg == NULL || cidSz == 0 || msgSz < OPAQUE8_LEN + cidSz) - return; + return NULL; if (msg[0] == dtls12_cid) { /* DTLS 1.2 CID packet */ if (msgSz < DTLS_RECORD_HEADER_SZ + cidSz) - return; + return NULL; /* content type(1) + version(2) + epoch(2) + sequence(6) */ - *cid = msg + ENUM_LEN + VERSION_SZ + OPAQUE16_LEN + OPAQUE16_LEN + + return msg + ENUM_LEN + VERSION_SZ + OPAQUE16_LEN + OPAQUE16_LEN + OPAQUE32_LEN; } else if (Dtls13UnifiedHeaderCIDPresent(msg[0])) { /* DTLS 1.3 CID packet */ if (msgSz < OPAQUE8_LEN + cidSz) - return; - *cid = msg + OPAQUE8_LEN; + return NULL; + return msg + OPAQUE8_LEN; } + return NULL; } #endif /* WOLFSSL_DTLS_CID */ diff --git a/src/internal.c b/src/internal.c index 88feeab08f..16ccd6d892 100644 --- a/src/internal.c +++ b/src/internal.c @@ -11531,8 +11531,7 @@ static int GetDtlsRecordHeader(WOLFSSL* ssl, word32* inOutIdx, if (ssl->buffers.inputBuffer.length - *inOutIdx < (word32)cidSz + LENGTH_SZ) return LENGTH_ERROR; - if (cidSz != DtlsGetCidRxSize(ssl) || - wolfSSL_dtls_cid_get0_rx(ssl, &ourCid) != WOLFSSL_SUCCESS) + if (wolfSSL_dtls_cid_get0_rx(ssl, &ourCid) != WOLFSSL_SUCCESS) return DTLS_CID_ERROR; if (XMEMCMP(ssl->buffers.inputBuffer.buffer + *inOutIdx, ourCid, cidSz) != 0) diff --git a/src/ssl.c b/src/ssl.c index f487dfad9e..3fcda70506 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -1932,14 +1932,16 @@ int wolfSSL_dtls_set_peer(WOLFSSL* ssl, void* peer, unsigned int peerSz) int wolfSSL_dtls_set_pending_peer(WOLFSSL* ssl, void* peer, unsigned int peerSz) { #ifdef WOLFSSL_DTLS - int ret = WOLFSSL_FAILURE; + int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE); if (ssl == NULL) return WOLFSSL_FAILURE; if (ssl->buffers.dtlsCtx.peer.sa != NULL && ssl->buffers.dtlsCtx.peer.sz == peerSz && - XMEMCMP(ssl->buffers.dtlsCtx.peer.sa, peer, peerSz) == 0) { + sockAddrEqual((SOCKADDR_S*)ssl->buffers.dtlsCtx.peer.sa, + (XSOCKLENT)ssl->buffers.dtlsCtx.peer.sz, (SOCKADDR_S*)peer, + (XSOCKLENT)peerSz)) { /* Already the current peer. */ if (ssl->buffers.dtlsCtx.pendingPeer.sa != NULL) { /* Clear any other pendingPeer */ @@ -2986,7 +2988,7 @@ int wolfSSL_inject(WOLFSSL* ssl, const void* data, int sz) int maxLength; int usedLength; - WOLFSSL_ENTER("wolfSSL_read_internal"); + WOLFSSL_ENTER("wolfSSL_inject"); if (ssl == NULL || data == NULL || sz <= 0) return BAD_FUNC_ARG; @@ -10558,7 +10560,7 @@ static int chGoodDisableReadCB(WOLFSSL* ssl, void* ctx) int wolfDTLS_accept_stateless(WOLFSSL* ssl) { byte disableRead; - int ret = WOLFSSL_FATAL_ERROR; + int ret = WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR); struct chGoodDisableReadCbCtx cb; WOLFSSL_ENTER("wolfDTLS_SetChGoodCb"); diff --git a/src/wolfio.c b/src/wolfio.c index 3aed642675..1e31f7304f 100644 --- a/src/wolfio.c +++ b/src/wolfio.c @@ -569,7 +569,7 @@ STATIC int nucyassl_sendto(INT sd, CHAR *buf, UINT16 sz, INT16 flags, #define DTLS_RECVFROM_FUNCTION recvfrom #endif -static int sockAddrEqual( +int sockAddrEqual( SOCKADDR_S *a, XSOCKLENT aLen, SOCKADDR_S *b, XSOCKLENT bLen) { if (aLen != bLen) @@ -690,6 +690,10 @@ int EmbedReceiveFrom(WOLFSSL *ssl, char *buf, int sz, void *ctx) newPeer = 1; peer = (SOCKADDR_S*)dtlsCtx->peer.sa; } + else if (!ssl->options.dtlsStateful) { + newPeer = 1; + peer = (SOCKADDR_S*)dtlsCtx->peer.sa; + } else { peer = &lclPeer; XMEMCPY(peer, (SOCKADDR_S*)dtlsCtx->peer.sa, sizeof(lclPeer)); @@ -853,8 +857,8 @@ int EmbedReceiveFrom(WOLFSSL *ssl, char *buf, int sz, void *ctx) dtlsCtx->peer.sz = peerSz; } #ifndef WOLFSSL_PEER_ADDRESS_CHANGES - else if ((dtlsCtx->peer.sz != (unsigned int)peerSz) || - (XMEMCMP(peer, dtlsCtx->peer.sa, peerSz) != 0)) { + else if (!sockAddrEqual(peer, peerSz, (SOCKADDR_S*)dtlsCtx->peer.sa, + dtlsCtx->peer.sz)) { return WOLFSSL_CBIO_ERR_GENERAL; } #endif diff --git a/tests/api.c b/tests/api.c index 22e910147c..df77768ea6 100644 --- a/tests/api.c +++ b/tests/api.c @@ -100363,7 +100363,6 @@ static int test_wolfSSL_dtls_cid_parse(void) { EXPECT_DECLS; #if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID) - const unsigned char* cid = NULL; /* Taken from Wireshark. Right-click -> copy -> ... as escaped string */ /* Plaintext ServerHelloDone. No CID. */ byte noCid[] = @@ -100381,14 +100380,9 @@ static int test_wolfSSL_dtls_cid_parse(void) "\xe7\x23\x2c\xad\x65\x83\xa8\xf4\xbf\xbf\x7b\x25\x16\x80\x19\xc3" \ "\x81\xda\xf5\x3f"; - wolfSSL_dtls_cid_parse(noCid, sizeof(noCid), &cid, 8); - ExpectPtrEq(cid, NULL); - wolfSSL_dtls_cid_parse(cid12, sizeof(cid12), &cid, 8); - ExpectPtrEq(cid, cid12 + 11); - wolfSSL_dtls_cid_parse(cid13, sizeof(cid13), &cid, 8); - ExpectPtrEq(cid, cid13 + 1); - - + ExpectPtrEq(wolfSSL_dtls_cid_parse(noCid, sizeof(noCid), 8), NULL); + ExpectPtrEq(wolfSSL_dtls_cid_parse(cid12, sizeof(cid12), 8), cid12 + 11); + ExpectPtrEq(wolfSSL_dtls_cid_parse(cid13, sizeof(cid13), 8), cid13 + 1); #endif return EXPECT_RESULT(); } diff --git a/wolfssl/internal.h b/wolfssl/internal.h index f2c63eeeb3..b01c1e7c2e 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -6717,6 +6717,10 @@ WOLFSSL_LOCAL word32 MacSize(const WOLFSSL* ssl); WOLFSSL_LOCAL int DoClientHelloStateless(WOLFSSL* ssl, const byte* input, word32 helloSz, byte isFirstCHFrag, byte* tls13); #endif /* !defined(NO_WOLFSSL_SERVER) */ +#if !defined(WOLFCRYPT_ONLY) && defined(USE_WOLFSSL_IO) + WOLFSSL_LOCAL int sockAddrEqual(SOCKADDR_S *a, XSOCKLENT aLen, + SOCKADDR_S *b, XSOCKLENT bLen); +#endif #endif /* WOLFSSL_DTLS */ #if defined(HAVE_SECURE_RENEGOTIATION) && defined(WOLFSSL_DTLS) diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index ebce8efddc..1a29c4c19e 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -5862,8 +5862,8 @@ WOLFSSL_API int wolfSSL_dtls_cid_get_tx(WOLFSSL* ssl, unsigned char* buffer, unsigned int bufferSz); WOLFSSL_API int wolfSSL_dtls_cid_get0_tx(WOLFSSL* ssl, unsigned char** cid); WOLFSSL_API int wolfSSL_dtls_cid_max_size(void); -WOLFSSL_API void wolfSSL_dtls_cid_parse(const unsigned char* msg, - unsigned int msgSz, const unsigned char** cid, unsigned int cidSz); +WOLFSSL_API const unsigned char* wolfSSL_dtls_cid_parse(const unsigned char* msg, + unsigned int msgSz, unsigned int cidSz); #endif /* defined(WOLFSSL_DTLS_CID) */ #ifdef WOLFSSL_DTLS_CH_FRAG From 4795e0d920578a8633b84461b695adafdc99a4e6 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 12 Dec 2024 19:06:05 +0100 Subject: [PATCH 4/8] Refactor dtls pending peer processing --- src/internal.c | 113 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 82 insertions(+), 31 deletions(-) diff --git a/src/internal.c b/src/internal.c index 16ccd6d892..a40d757b24 100644 --- a/src/internal.c +++ b/src/internal.c @@ -21317,7 +21317,8 @@ static int DtlsShouldDrop(WOLFSSL* ssl, int retcode) } #endif /* WOLFSSL_DTLS */ -#if defined(WOLFSSL_TLS13) || defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID) +#if defined(WOLFSSL_TLS13) || \ + (defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID)) static int removeMsgInnerPadding(WOLFSSL* ssl) { word32 i = ssl->buffers.inputBuffer.idx + @@ -21349,16 +21350,58 @@ static int removeMsgInnerPadding(WOLFSSL* ssl) } #endif -int ProcessReply(WOLFSSL* ssl) +#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID) +static void dtlsClearPeer(WOLFSSL_SOCKADDR* peer) { - return ProcessReplyEx(ssl, 0); + XFREE(peer->sa, NULL, DYNAMIC_TYPE_SOCKADDR); + peer->sa = NULL; + peer->sz = 0; + peer->bufSz = 0; } + +/** + * @brief Handle pending peer during record processing. + * @param ssl WOLFSSL object. + * @param deprotected 0 when we have not decrypted the record yet + * 1 when we have decrypted and verified the record + */ +static void dtlsProcessPendingPeer(WOLFSSL* ssl, int deprotected) +{ + if (ssl->buffers.dtlsCtx.pendingPeer.sa != NULL) { + if (!deprotected) { + /* Here we have just read an entire record from the network. It is + * still encrypted. If processingPendingRecord is set then that + * means that an error occurred when processing the previous record. + * In that case we should clear the pendingPeer because we only + * want to allow it to be valid for one record. */ + if (ssl->buffers.dtlsCtx.processingPendingRecord) { + /* Clear the pending peer. */ + dtlsClearPeer(&ssl->buffers.dtlsCtx.pendingPeer); + } + ssl->buffers.dtlsCtx.processingPendingRecord = + !ssl->buffers.dtlsCtx.processingPendingRecord; + } + else { + /* Pending peer present and record deprotected. Update the peer. */ + dtlsClearPeer(&ssl->buffers.dtlsCtx.peer); + ssl->buffers.dtlsCtx.peer = ssl->buffers.dtlsCtx.pendingPeer; + XMEMSET(&ssl->buffers.dtlsCtx.pendingPeer, 0, + sizeof(WOLFSSL_SOCKADDR)); + ssl->buffers.dtlsCtx.processingPendingRecord = 0; + } + } + else { + ssl->buffers.dtlsCtx.processingPendingRecord = 0; + } +} +#endif + /* Process input requests. Return 0 is done, 1 is call again to complete, and negative number is error. If allowSocketErr is set, SOCKET_ERROR_E in ssl->error will be whitelisted. This is useful when the connection has been closed and the endpoint wants to check for an alert sent by the other end. */ -int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) +static int DoProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) { int ret = 0, type = internal_error, readSz; int atomicUser = 0; @@ -21558,21 +21601,8 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) #ifdef WOLFSSL_DTLS #ifdef WOLFSSL_DTLS_CID - if (ssl->options.dtls) { - if (!ssl->buffers.dtlsCtx.processingPendingRecord) - ssl->buffers.dtlsCtx.processingPendingRecord = 1; - else { - ssl->buffers.dtlsCtx.processingPendingRecord = 0; - if (ssl->buffers.dtlsCtx.pendingPeer.sa != NULL) { - /* Clear the pending peer */ - XFREE(ssl->buffers.dtlsCtx.pendingPeer.sa, ssl->heap, - DYNAMIC_TYPE_SOCKADDR); - ssl->buffers.dtlsCtx.pendingPeer.sa = NULL; - ssl->buffers.dtlsCtx.pendingPeer.sz = 0; - ssl->buffers.dtlsCtx.pendingPeer.bufSz = 0; - } - } - } + if (ssl->options.dtls) + dtlsProcessPendingPeer(ssl, 0); #endif if (ssl->options.dtls && DtlsShouldDrop(ssl, ret)) { ssl->options.processReply = doProcessInit; @@ -21953,7 +21983,8 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) } if (IsEncryptionOn(ssl, 0) && ssl->keys.decryptedCur == 1) { -#if defined(WOLFSSL_TLS13) || defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID) +#if defined(WOLFSSL_TLS13) || \ + (defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID)) int removePadding = 0; if (ssl->options.tls1_3) removePadding = 1; @@ -22031,17 +22062,8 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) } #ifdef WOLFSSL_DTLS_CID /* Update the peer if we were able to de-protect the message */ - if (IsEncryptionOn(ssl, 0) && - ssl->buffers.dtlsCtx.pendingPeer.sa != NULL) { - XFREE(ssl->buffers.dtlsCtx.peer.sa, ssl->heap, - DYNAMIC_TYPE_SOCKADDR); - ssl->buffers.dtlsCtx.peer = - ssl->buffers.dtlsCtx.pendingPeer; - ssl->buffers.dtlsCtx.pendingPeer.sa = NULL; - ssl->buffers.dtlsCtx.pendingPeer.sz = 0; - ssl->buffers.dtlsCtx.pendingPeer.bufSz = 0; - ssl->buffers.dtlsCtx.processingPendingRecord = 0; - } + if (IsEncryptionOn(ssl, 0)) + dtlsProcessPendingPeer(ssl, 1); #endif } #endif /* WOLFSSL_DTLS */ @@ -22512,6 +22534,35 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) } } +int ProcessReply(WOLFSSL* ssl) +{ + return ProcessReplyEx(ssl, 0); +} + +int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) +{ + int ret; + + ret = DoProcessReplyEx(ssl, allowSocketErr); + +#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID) + if (ssl->options.dtls) { + /* Don't clear pending peer if we are going to re-enter + * DoProcessReplyEx */ + if (ret != WC_NO_ERR_TRACE(WANT_READ) +#ifdef WOLFSSL_ASYNC_CRYPT + && ret != WC_NO_ERR_TRACE(WC_PENDING_E) +#endif + ) { + dtlsClearPeer(&ssl->buffers.dtlsCtx.pendingPeer); + ssl->buffers.dtlsCtx.processingPendingRecord = 0; + } + } +#endif + + return ret; +} + #if !defined(WOLFSSL_NO_TLS12) || !defined(NO_OLD_TLS) || \ (defined(WOLFSSL_TLS13) && defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT)) int SendChangeCipher(WOLFSSL* ssl) From a1ee9534118d77118c932f2f00316fcaa78bb5b7 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Mon, 16 Dec 2024 18:05:50 +0100 Subject: [PATCH 5/8] Protect peer access when WOLFSSL_RW_THREADED --- src/dtls.c | 1 + src/internal.c | 16 ++++-- src/ssl.c | 37 +++++++++++--- src/tls13.c | 2 + src/wolfio.c | 119 ++++++++++++++++++++++++++------------------- wolfssl/internal.h | 4 ++ 6 files changed, 116 insertions(+), 63 deletions(-) diff --git a/src/dtls.c b/src/dtls.c index fc9014c31a..d31e92f015 100644 --- a/src/dtls.c +++ b/src/dtls.c @@ -230,6 +230,7 @@ static int CreateDtls12Cookie(const WOLFSSL* ssl, const WolfSSL_CH* ch, ssl->buffers.dtlsCookieSecret.buffer, ssl->buffers.dtlsCookieSecret.length); if (ret == 0) { + /* peerLock not necessary. Still in handshake phase. */ ret = wc_HmacUpdate(&cookieHmac, (const byte*)ssl->buffers.dtlsCtx.peer.sa, ssl->buffers.dtlsCtx.peer.sz); diff --git a/src/internal.c b/src/internal.c index a40d757b24..ed25e70dd8 100644 --- a/src/internal.c +++ b/src/internal.c @@ -7392,6 +7392,11 @@ int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup) ssl->buffers.dtlsCtx.rfd = -1; ssl->buffers.dtlsCtx.wfd = -1; +#ifdef WOLFSSL_RW_THREADED + if (wc_InitRwLock(&ssl->buffers.dtlsCtx.peerLock) != 0) + return BAD_MUTEX_E; +#endif + ssl->IOCB_ReadCtx = &ssl->buffers.dtlsCtx; /* prevent invalid pointer access if not */ ssl->IOCB_WriteCtx = &ssl->buffers.dtlsCtx; /* correctly set */ #else @@ -8261,6 +8266,9 @@ void wolfSSL_ResourceFree(WOLFSSL* ssl) } XFREE(ssl->buffers.dtlsCtx.peer.sa, ssl->heap, DYNAMIC_TYPE_SOCKADDR); ssl->buffers.dtlsCtx.peer.sa = NULL; +#ifdef WOLFSSL_RW_THREADED + wc_FreeRwLock(&ssl->buffers.dtlsCtx.peerLock); +#endif #ifdef WOLFSSL_DTLS_CID XFREE(ssl->buffers.dtlsCtx.pendingPeer.sa, ssl->heap, DYNAMIC_TYPE_SOCKADDR); @@ -21384,11 +21392,11 @@ static void dtlsProcessPendingPeer(WOLFSSL* ssl, int deprotected) } else { /* Pending peer present and record deprotected. Update the peer. */ - dtlsClearPeer(&ssl->buffers.dtlsCtx.peer); - ssl->buffers.dtlsCtx.peer = ssl->buffers.dtlsCtx.pendingPeer; - XMEMSET(&ssl->buffers.dtlsCtx.pendingPeer, 0, - sizeof(WOLFSSL_SOCKADDR)); + (void)wolfSSL_dtls_set_peer(ssl, + &ssl->buffers.dtlsCtx.pendingPeer.sa, + ssl->buffers.dtlsCtx.pendingPeer.sz); ssl->buffers.dtlsCtx.processingPendingRecord = 0; + dtlsClearPeer(&ssl->buffers.dtlsCtx.pendingPeer); } } else { diff --git a/src/ssl.c b/src/ssl.c index 3fcda70506..fafb318b10 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -1913,12 +1913,19 @@ int wolfSSL_dtls_set_peer(WOLFSSL* ssl, void* peer, unsigned int peerSz) if (ssl == NULL) return WOLFSSL_FAILURE; - +#ifdef WOLFSSL_RW_THREADED + if (wc_LockRwLock_Wr(&ssl->buffers.dtlsCtx.peerLock) != 0) + return WOLFSSL_FAILURE; +#endif ret = SockAddrSet(&ssl->buffers.dtlsCtx.peer, peer, peerSz, ssl->heap); if (ret == WOLFSSL_SUCCESS && !(peer == NULL || peerSz == 0)) ssl->buffers.dtlsCtx.userSet = 1; else ssl->buffers.dtlsCtx.userSet = 0; +#ifdef WOLFSSL_RW_THREADED + if (wc_UnLockRwLock(&ssl->buffers.dtlsCtx.peerLock) != 0) + ret = WOLFSSL_FAILURE; +#endif return ret; #else (void)ssl; @@ -1936,7 +1943,10 @@ int wolfSSL_dtls_set_pending_peer(WOLFSSL* ssl, void* peer, unsigned int peerSz) if (ssl == NULL) return WOLFSSL_FAILURE; - +#ifdef WOLFSSL_RW_THREADED + if (wc_LockRwLock_Rd(&ssl->buffers.dtlsCtx.peerLock) != 0) + return WOLFSSL_FAILURE; +#endif if (ssl->buffers.dtlsCtx.peer.sa != NULL && ssl->buffers.dtlsCtx.peer.sz == peerSz && sockAddrEqual((SOCKADDR_S*)ssl->buffers.dtlsCtx.peer.sa, @@ -1959,6 +1969,10 @@ int wolfSSL_dtls_set_pending_peer(WOLFSSL* ssl, void* peer, unsigned int peerSz) } if (ret == WOLFSSL_SUCCESS) ssl->buffers.dtlsCtx.processingPendingRecord = 0; +#ifdef WOLFSSL_RW_THREADED + if (wc_UnLockRwLock(&ssl->buffers.dtlsCtx.peerLock) != 0) + ret = WOLFSSL_FAILURE; +#endif return ret; #else (void)ssl; @@ -1972,18 +1986,25 @@ int wolfSSL_dtls_set_pending_peer(WOLFSSL* ssl, void* peer, unsigned int peerSz) int wolfSSL_dtls_get_peer(WOLFSSL* ssl, void* peer, unsigned int* peerSz) { #ifdef WOLFSSL_DTLS - if (ssl == NULL) { + int ret = WOLFSSL_FAILURE; + if (ssl == NULL) return WOLFSSL_FAILURE; - } - +#ifdef WOLFSSL_RW_THREADED + if (wc_LockRwLock_Rd(&ssl->buffers.dtlsCtx.peerLock) != 0) + return WOLFSSL_FAILURE; +#endif if (peer != NULL && peerSz != NULL && *peerSz >= ssl->buffers.dtlsCtx.peer.sz && ssl->buffers.dtlsCtx.peer.sa != NULL) { *peerSz = ssl->buffers.dtlsCtx.peer.sz; XMEMCPY(peer, ssl->buffers.dtlsCtx.peer.sa, *peerSz); - return WOLFSSL_SUCCESS; + ret = WOLFSSL_SUCCESS; } - return WOLFSSL_FAILURE; +#ifdef WOLFSSL_RW_THREADED + if (wc_UnLockRwLock(&ssl->buffers.dtlsCtx.peerLock) != 0) + ret = WOLFSSL_FAILURE; +#endif + return ret; #else (void)ssl; (void)peer; @@ -1995,7 +2016,7 @@ int wolfSSL_dtls_get_peer(WOLFSSL* ssl, void* peer, unsigned int* peerSz) int wolfSSL_dtls_get0_peer(WOLFSSL* ssl, const void** peer, unsigned int* peerSz) { -#ifdef WOLFSSL_DTLS +#if defined(WOLFSSL_DTLS) && !defined(WOLFSSL_RW_THREADED) if (ssl == NULL) return WOLFSSL_FAILURE; diff --git a/src/tls13.c b/src/tls13.c index 2c8767f207..545abfcecb 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -3621,6 +3621,7 @@ int CreateCookieExt(const WOLFSSL* ssl, byte* hash, word16 hashSz, #ifdef WOLFSSL_DTLS13 /* Tie cookie to peer address */ if (ret == 0) { + /* peerLock not necessary. Still in handshake phase. */ if (ssl->options.dtls && ssl->buffers.dtlsCtx.peer.sz > 0) { ret = wc_HmacUpdate(&cookieHmac, (byte*)ssl->buffers.dtlsCtx.peer.sa, @@ -6409,6 +6410,7 @@ int TlsCheckCookie(const WOLFSSL* ssl, const byte* cookie, word16 cookieSz) #ifdef WOLFSSL_DTLS13 /* Tie cookie to peer address */ if (ret == 0) { + /* peerLock not necessary. Still in handshake phase. */ if (ssl->options.dtls && ssl->buffers.dtlsCtx.peer.sz > 0) { ret = wc_HmacUpdate(&cookieHmac, (byte*)ssl->buffers.dtlsCtx.peer.sa, diff --git a/src/wolfio.c b/src/wolfio.c index 1e31f7304f..5e62e9fbd8 100644 --- a/src/wolfio.c +++ b/src/wolfio.c @@ -660,8 +660,17 @@ int EmbedReceiveFrom(WOLFSSL *ssl, char *buf, int sz, void *ctx) word32 invalidPeerPackets = 0; #endif int newPeer = 0; + int ret = 0; WOLFSSL_ENTER("EmbedReceiveFrom"); + (void)ret; /* possibly unused */ + + XMEMSET(&lclPeer, 0, sizeof(lclPeer)); + +#ifdef WOLFSSL_RW_THREADED + if (wc_LockRwLock_Rd(&ssl->buffers.dtlsCtx.peerLock) != 0) + return WOLFSSL_CBIO_ERR_GENERAL; +#endif if (dtlsCtx->connected) { peer = NULL; @@ -670,37 +679,32 @@ int EmbedReceiveFrom(WOLFSSL *ssl, char *buf, int sz, void *ctx) #ifndef WOLFSSL_IPV6 if (PeerIsIpv6((SOCKADDR_S*)dtlsCtx->peer.sa, dtlsCtx->peer.sz)) { WOLFSSL_MSG("ipv6 dtls peer set but no ipv6 support compiled"); - return NOT_COMPILED_IN; + ret = WOLFSSL_CBIO_ERR_GENERAL; } #endif peer = &lclPeer; - XMEMSET(&lclPeer, 0, sizeof(lclPeer)); peerSz = sizeof(lclPeer); } else { /* Store the peer address. It is used to calculate the DTLS cookie. */ - if (dtlsCtx->peer.sa == NULL) { - dtlsCtx->peer.sa = (void*)XMALLOC(sizeof(SOCKADDR_S), - ssl->heap, DYNAMIC_TYPE_SOCKADDR); - dtlsCtx->peer.sz = 0; - if (dtlsCtx->peer.sa != NULL) - dtlsCtx->peer.bufSz = sizeof(SOCKADDR_S); - else - dtlsCtx->peer.bufSz = 0; - newPeer = 1; - peer = (SOCKADDR_S*)dtlsCtx->peer.sa; - } - else if (!ssl->options.dtlsStateful) { - newPeer = 1; - peer = (SOCKADDR_S*)dtlsCtx->peer.sa; - } - else { - peer = &lclPeer; - XMEMCPY(peer, (SOCKADDR_S*)dtlsCtx->peer.sa, sizeof(lclPeer)); + newPeer = dtlsCtx->peer.sa == NULL || !ssl->options.dtlsStateful; + peer = &lclPeer; + if (dtlsCtx->peer.sa != NULL) { + XMEMCPY(peer, (SOCKADDR_S*)dtlsCtx->peer.sa, MIN(sizeof(lclPeer), + dtlsCtx->peer.sz)); } - peerSz = dtlsCtx->peer.bufSz; + peerSz = sizeof(lclPeer); } +#ifdef WOLFSSL_RW_THREADED + /* We make a copy above to avoid holding the lock for the entire function */ + if (wc_UnLockRwLock(&ssl->buffers.dtlsCtx.peerLock) != 0) + return WOLFSSL_CBIO_ERR_GENERAL; +#endif + + if (ret != 0) + return ret; + /* Don't use ssl->options.handShakeDone since it is true even if * we are in the process of renegotiation */ doDtlsTimeout = ssl->options.handShakeState != HANDSHAKE_DONE; @@ -709,12 +713,9 @@ int EmbedReceiveFrom(WOLFSSL *ssl, char *buf, int sz, void *ctx) if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version)) { doDtlsTimeout = doDtlsTimeout || ssl->dtls13Rtx.rtxRecords != NULL; #ifdef WOLFSSL_RW_THREADED - { - int ret = wc_LockMutex(&ssl->dtls13Rtx.mutex); - if (ret < 0) { - return ret; - } - } + ret = wc_LockMutex(&ssl->dtls13Rtx.mutex); + if (ret != 0) + return ret; #endif doDtlsTimeout = doDtlsTimeout || (ssl->dtls13FastTimeout && ssl->dtls13Rtx.seenRecords != NULL); @@ -785,26 +786,16 @@ int EmbedReceiveFrom(WOLFSSL *ssl, char *buf, int sz, void *ctx) } #endif /* !NO_ASN_TIME */ - recvd = (int)DTLS_RECVFROM_FUNCTION(sd, buf, (size_t)sz, ssl->rflags, - (SOCKADDR*)peer, peer != NULL ? &peerSz : NULL); - - /* From the RECV(2) man page - * The returned address is truncated if the buffer provided is too - * small; in this case, addrlen will return a value greater than was - * supplied to the call. - */ - if (dtlsCtx->connected) { - /* No need to sanitize the value of peerSz */ - } - else if (dtlsCtx->userSet) { - /* Truncate peer size */ - if (peerSz > (XSOCKLENT)sizeof(lclPeer)) - peerSz = (XSOCKLENT)sizeof(lclPeer); - } - else { - /* Truncate peer size */ - if (peerSz > (XSOCKLENT)dtlsCtx->peer.bufSz) - peerSz = (XSOCKLENT)dtlsCtx->peer.bufSz; + { + XSOCKLENT inPeerSz = peerSz; + recvd = (int)DTLS_RECVFROM_FUNCTION(sd, buf, (size_t)sz, + ssl->rflags, (SOCKADDR*)peer, peer != NULL ? &inPeerSz : NULL); + /* Truncate peerSz. From the RECV(2) man page + * The returned address is truncated if the buffer provided is too + * small; in this case, addrlen will return a value greater than was + * supplied to the call. + */ + peerSz = MIN(peerSz, inPeerSz); } recvd = TranslateIoReturnCode(recvd, sd, SOCKET_RECEIVING); @@ -833,11 +824,23 @@ int EmbedReceiveFrom(WOLFSSL *ssl, char *buf, int sz, void *ctx) } else if (dtlsCtx->userSet) { /* Check we received the packet from the correct peer */ + int ignore = 0; +#ifdef WOLFSSL_RW_THREADED + if (wc_LockRwLock_Rd(&ssl->buffers.dtlsCtx.peerLock) != 0) + return WOLFSSL_CBIO_ERR_GENERAL; +#endif if (dtlsCtx->peer.sz > 0 && (peerSz != (XSOCKLENT)dtlsCtx->peer.sz || !sockAddrEqual(peer, peerSz, (SOCKADDR_S*)dtlsCtx->peer.sa, dtlsCtx->peer.sz))) { WOLFSSL_MSG(" Ignored packet from invalid peer"); + ignore = 1; + } +#ifdef WOLFSSL_RW_THREADED + if (wc_UnLockRwLock(&ssl->buffers.dtlsCtx.peerLock) != 0) + return WOLFSSL_CBIO_ERR_GENERAL; +#endif + if (ignore) { #if defined(NO_ASN_TIME) && \ !defined(DTLS_RECEIVEFROM_NO_TIMEOUT_ON_INVALID_PEER) if (doDtlsTimeout) { @@ -853,13 +856,27 @@ int EmbedReceiveFrom(WOLFSSL *ssl, char *buf, int sz, void *ctx) } else { if (newPeer) { - /* Store size of saved address */ - dtlsCtx->peer.sz = peerSz; + /* Store size of saved address. Locking handled internally. */ + if (wolfSSL_dtls_set_peer(ssl, peer, peerSz) != WOLFSSL_SUCCESS) + return WOLFSSL_CBIO_ERR_GENERAL; } #ifndef WOLFSSL_PEER_ADDRESS_CHANGES - else if (!sockAddrEqual(peer, peerSz, (SOCKADDR_S*)dtlsCtx->peer.sa, + else { + ret = 0; +#ifdef WOLFSSL_RW_THREADED + if (wc_LockRwLock_Rd(&ssl->buffers.dtlsCtx.peerLock) != 0) + return WOLFSSL_CBIO_ERR_GENERAL; +#endif + if (!sockAddrEqual(peer, peerSz, (SOCKADDR_S*)dtlsCtx->peer.sa, dtlsCtx->peer.sz)) { - return WOLFSSL_CBIO_ERR_GENERAL; + ret = WOLFSSL_CBIO_ERR_GENERAL; + } +#ifdef WOLFSSL_RW_THREADED + if (wc_UnLockRwLock(&ssl->buffers.dtlsCtx.peerLock) != 0) + return WOLFSSL_CBIO_ERR_GENERAL; +#endif + if (ret != 0) + return ret; } #endif } diff --git a/wolfssl/internal.h b/wolfssl/internal.h index b01c1e7c2e..af8e4a9bd8 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -2758,6 +2758,10 @@ struct WOLFSSL_SOCKADDR { }; typedef struct WOLFSSL_DTLS_CTX { +#ifdef WOLFSSL_RW_THREADED + /* Protect peer access after the handshake */ + wolfSSL_RwLock peerLock; +#endif WOLFSSL_SOCKADDR peer; #ifdef WOLFSSL_DTLS_CID WOLFSSL_SOCKADDR pendingPeer; /* When using CID's, we don't want to update From faa7b8dfaad1e9b57b780044d9ac65f297e5ae7a Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Tue, 17 Dec 2024 00:48:36 +0100 Subject: [PATCH 6/8] wolfSSLReceive: Error return on interrupted connection Interrupted connection should return control to the user since they may want to handle the signal that caused the interrupt. Otherwise, we might never give back control to the user (the timeout would error out but that causes a big delay). socat.yml: in test 475, the test would send a SIGTERM after 3 seconds. We would continue to ignore this signal and continue to call `recvfrom`. Instead we should error out and give control back to the user. --- .github/workflows/socat.yml | 2 +- src/internal.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/socat.yml b/.github/workflows/socat.yml index 3d6d8fa58d..91417e7a71 100644 --- a/.github/workflows/socat.yml +++ b/.github/workflows/socat.yml @@ -78,4 +78,4 @@ jobs: run: | export LD_LIBRARY_PATH=$GITHUB_WORKSPACE/build-dir/lib:$LD_LIBRARY_PATH export SHELL=/bin/bash - SOCAT=$GITHUB_WORKSPACE/socat-1.8.0.0/socat ./test.sh -t 0.5 --expect-fail 36,64,146,214,216,217,309,310,386,399,402,403,459,460,467,468,478,492,528,530 + SOCAT=$GITHUB_WORKSPACE/socat-1.8.0.0/socat ./test.sh -t 0.5 --expect-fail 36,64,146,214,216,217,309,310,386,399,402,403,459,460,467,468,475,478,492,528,530 diff --git a/src/internal.c b/src/internal.c index ed25e70dd8..f2cf820f65 100644 --- a/src/internal.c +++ b/src/internal.c @@ -10594,7 +10594,7 @@ static int wolfSSLReceive(WOLFSSL* ssl, byte* buf, word32 sz) } } #endif - goto retry; + return WOLFSSL_FATAL_ERROR; case WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_CONN_CLOSE): ssl->options.isClosed = 1; From fe9a5fcd425165b03dea6d5225fbdbf4e6450bdc Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Tue, 17 Dec 2024 11:07:39 +0100 Subject: [PATCH 7/8] fixup! Code review and jenkins fixes --- src/ssl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ssl.c b/src/ssl.c index fafb318b10..816aeee8e2 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -1986,7 +1986,7 @@ int wolfSSL_dtls_set_pending_peer(WOLFSSL* ssl, void* peer, unsigned int peerSz) int wolfSSL_dtls_get_peer(WOLFSSL* ssl, void* peer, unsigned int* peerSz) { #ifdef WOLFSSL_DTLS - int ret = WOLFSSL_FAILURE; + int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE); if (ssl == NULL) return WOLFSSL_FAILURE; #ifdef WOLFSSL_RW_THREADED From 9cb75ef5f8144ca9a598bdb26187221b862ea667 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Tue, 17 Dec 2024 11:17:23 +0100 Subject: [PATCH 8/8] fixup! DTLS: Add server side stateless and CID QoL API --- src/dtls.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dtls.c b/src/dtls.c index d31e92f015..ae278046ba 100644 --- a/src/dtls.c +++ b/src/dtls.c @@ -1436,12 +1436,14 @@ const unsigned char* wolfSSL_dtls_cid_parse(const unsigned char* msg, return msg + ENUM_LEN + VERSION_SZ + OPAQUE16_LEN + OPAQUE16_LEN + OPAQUE32_LEN; } +#ifdef WOLFSSL_DTLS13 else if (Dtls13UnifiedHeaderCIDPresent(msg[0])) { /* DTLS 1.3 CID packet */ if (msgSz < OPAQUE8_LEN + cidSz) return NULL; return msg + OPAQUE8_LEN; } +#endif return NULL; } #endif /* WOLFSSL_DTLS_CID */