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/doc/dox_comments/header_files/ssl.h b/doc/dox_comments/header_files/ssl.h index 04407dfd9f..7d7a629f8f 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,53 @@ 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 +4011,41 @@ 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 +14285,39 @@ 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 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] 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 +15135,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 +15148,26 @@ 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 set in cid. + + \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 +15199,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 +15210,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/examples/client/client.c b/examples/client/client.c index 52fba30792..7df9821e70 100644 --- a/examples/client/client.c +++ b/examples/client/client.c @@ -2404,7 +2404,7 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args) err_sys("provided connection ID is too big"); } else { - strcpy(dtlsCID, myoptarg); + XSTRLCPY(dtlsCID, myoptarg, DTLS_CID_BUFFER_SIZE); } } break; diff --git a/examples/server/server.c b/examples/server/server.c index 8e71d6bbad..8fa6076e22 100644 --- a/examples/server/server.c +++ b/examples/server/server.c @@ -2423,7 +2423,7 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) err_sys("provided connection ID is too big"); } else { - strcpy(dtlsCID, myoptarg); + XSTRLCPY(dtlsCID, myoptarg, DTLS_CID_BUFFER_SIZE); } } break; diff --git a/src/dtls.c b/src/dtls.c index d2c1d4c03e..ae278046ba 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) @@ -221,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); @@ -1108,6 +1118,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 +1396,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 +1412,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; } + +const unsigned char* wolfSSL_dtls_cid_parse(const unsigned char* msg, + unsigned int msgSz, unsigned int cidSz) +{ + /* we need at least the first byte to check version */ + if (msg == NULL || cidSz == 0 || msgSz < OPAQUE8_LEN + cidSz) + return NULL; + if (msg[0] == dtls12_cid) { + /* DTLS 1.2 CID packet */ + if (msgSz < DTLS_RECORD_HEADER_SZ + cidSz) + return NULL; + /* content type(1) + version(2) + epoch(2) + sequence(6) */ + 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 */ byte DtlsGetCidTxSize(WOLFSSL* ssl) @@ -1413,6 +1478,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 b4743f0f9e..85b8d616b7 100644 --- a/src/internal.c +++ b/src/internal.c @@ -7621,6 +7621,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 @@ -8493,6 +8498,14 @@ 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); + ssl->buffers.dtlsCtx.pendingPeer.sa = NULL; +#endif #ifndef NO_WOLFSSL_SERVER if (ssl->buffers.dtlsCookieSecret.buffer != NULL) { ForceZero(ssl->buffers.dtlsCookieSecret.buffer, @@ -10813,7 +10826,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; @@ -11754,15 +11767,14 @@ 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 (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; } @@ -11972,7 +11984,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; @@ -21277,11 +21289,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)) { @@ -21295,11 +21310,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) { @@ -21533,7 +21557,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 + @@ -21565,16 +21590,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. */ + (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 { + 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; @@ -21773,6 +21840,10 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) &ssl->curRL, &ssl->curSize); #ifdef WOLFSSL_DTLS +#ifdef WOLFSSL_DTLS_CID + if (ssl->options.dtls) + dtlsProcessPendingPeer(ssl, 0); +#endif if (ssl->options.dtls && DtlsShouldDrop(ssl, ret)) { ssl->options.processReply = doProcessInit; ssl->buffers.inputBuffer.length = 0; @@ -22152,7 +22223,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; @@ -22197,8 +22269,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 */ @@ -22222,11 +22295,18 @@ 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)) + dtlsProcessPendingPeer(ssl, 1); +#endif } -#endif /* WOLFSSL_DTLS13 */ +#endif /* WOLFSSL_DTLS */ ssl->options.processReply = runProcessingOneMessage; FALL_THROUGH; @@ -22694,6 +22774,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) diff --git a/src/ssl.c b/src/ssl.c index 6e3f279d0e..c38fcbf1fa 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -1767,6 +1767,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) { @@ -1899,38 +1910,105 @@ 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; +#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; - return WOLFSSL_SUCCESS; - } +#ifdef WOLFSSL_RW_THREADED + if (wc_UnLockRwLock(&ssl->buffers.dtlsCtx.peerLock) != 0) + ret = WOLFSSL_FAILURE; +#endif + return ret; +#else + (void)ssl; + (void)peer; + (void)peerSz; + return WOLFSSL_NOT_IMPLEMENTED; +#endif +} + +#if defined(WOLFSSL_DTLS_CID) && !defined(WOLFSSL_NO_SOCK) +int wolfSSL_dtls_set_pending_peer(WOLFSSL* ssl, void* peer, unsigned int peerSz) +{ +#ifdef WOLFSSL_DTLS + int ret = WC_NO_ERR_TRACE(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; +#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, + (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 */ + 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; +#ifdef WOLFSSL_RW_THREADED + if (wc_UnLockRwLock(&ssl->buffers.dtlsCtx.peerLock) != 0) + ret = WOLFSSL_FAILURE; +#endif + return ret; #else (void)ssl; (void)peer; @@ -1938,22 +2016,51 @@ int wolfSSL_dtls_set_peer(WOLFSSL* ssl, void* peer, unsigned int peerSz) return WOLFSSL_NOT_IMPLEMENTED; #endif } +#endif /* WOLFSSL_DTLS_CID && !WOLFSSL_NO_SOCK */ int wolfSSL_dtls_get_peer(WOLFSSL* ssl, void* peer, unsigned int* peerSz) { #ifdef WOLFSSL_DTLS - if (ssl == NULL) { + int ret = WC_NO_ERR_TRACE(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; + (void)peerSz; + return WOLFSSL_NOT_IMPLEMENTED; +#endif +} + +int wolfSSL_dtls_get0_peer(WOLFSSL* ssl, const void** peer, + unsigned int* peerSz) +{ +#if defined(WOLFSSL_DTLS) && !defined(WOLFSSL_RW_THREADED) + 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; @@ -2996,6 +3103,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_inject"); + + 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; @@ -9617,7 +9760,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); @@ -9625,7 +9774,7 @@ int wolfSSL_dtls_retransmit(WOLFSSL* ssl) } } - return 0; + return WOLFSSL_SUCCESS; } #endif /* DTLS */ @@ -10794,6 +10943,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 = WC_NO_ERR_TRACE(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/tls13.c b/src/tls13.c index f09fe7027c..3da3bb0e2e 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) @@ -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, @@ -10905,6 +10907,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 +10920,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 +11153,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 +12835,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/src/wolfio.c b/src/wolfio.c index 8d0b2f089b..5e62e9fbd8 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) @@ -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,33 +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 { - 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; @@ -705,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); @@ -781,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); @@ -829,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) { @@ -849,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 ((dtlsCtx->peer.sz != (unsigned int)peerSz) || - (XMEMCMP(peer, dtlsCtx->peer.sa, peerSz) != 0)) { - return WOLFSSL_CBIO_ERR_GENERAL; + 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)) { + 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 } @@ -2351,6 +2372,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 77c21a4ebf..8e75372b0d 100644 --- a/tests/api.c +++ b/tests/api.c @@ -90317,7 +90317,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]; @@ -90387,7 +90387,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 */ @@ -90449,7 +90449,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); @@ -90554,7 +90554,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); } @@ -90624,7 +90624,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)); @@ -90951,7 +90951,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 */ @@ -91022,7 +91022,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 */ @@ -94637,18 +94637,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); @@ -99223,7 +99236,7 @@ static int test_dtls12_basic_connection_id(void) #endif /* WOLFSSL_AES_128 && WOLFSSL_STATIC_RSA */ #endif /* NO_SHA256 */ #endif /* NO_RSA */ -#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) +#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) && !defined(HAVE_FIPS) "DHE-RSA-CHACHA20-POLY1305", "DHE-RSA-CHACHA20-POLY1305-OLD", "ECDHE-RSA-CHACHA20-POLY1305", @@ -99232,7 +99245,7 @@ static int test_dtls12_basic_connection_id(void) #ifndef NO_PSK "DHE-PSK-AES128-CBC-SHA256", "DHE-PSK-AES256-GCM-SHA384", -#ifndef HAVE_NULL_CIPHER +#ifdef HAVE_NULL_CIPHER "DHE-PSK-NULL-SHA256", #endif "DHE-PSK-AES128-CCM", @@ -99550,7 +99563,7 @@ static int test_dtls13_basic_connection_id(void) && defined(WOLFSSL_DTLS_CID) unsigned char client_cid[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; unsigned char server_cid[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - unsigned char readBuf[30]; + unsigned char readBuf[50]; const char* params[] = { #ifndef NO_SHA256 #ifdef WOLFSSL_AES_128 @@ -99969,9 +99982,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 @@ -99983,6 +99998,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)); @@ -99994,6 +100010,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)); @@ -100011,6 +100028,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)); @@ -100027,17 +100045,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); @@ -100045,18 +100067,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)); @@ -100909,6 +100936,165 @@ 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++) { + 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); + test_ctx.s_len = 0; + } + if (wolfSSL_negotiate(ssl_s) != 1) { + 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); + + 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) + /* 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"; +#ifdef WOLFSSL_DTLS13 + /* 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"; +#endif + + ExpectPtrEq(wolfSSL_dtls_cid_parse(noCid, sizeof(noCid), 8), NULL); + ExpectPtrEq(wolfSSL_dtls_cid_parse(cid12, sizeof(cid12), 8), cid12 + 11); +#ifdef WOLFSSL_DTLS13 + ExpectPtrEq(wolfSSL_dtls_cid_parse(cid13, sizeof(cid13), 8), cid13 + 1); +#endif +#endif + return EXPECT_RESULT(); +} /*----------------------------------------------------------------------------* | Main @@ -102316,6 +102502,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 4cedf122f8..16c3ecfc40 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -2764,7 +2764,16 @@ 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 + * the peer's address until we successfully + * de-protect the record. */ +#endif int rfd; int wfd; byte userSet:1; @@ -2772,6 +2781,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; @@ -3718,6 +3730,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); @@ -5061,6 +5074,7 @@ struct Options { #if defined(HAVE_DANE) word16 useDANE:1; #endif /* HAVE_DANE */ + word16 disableRead:1; #ifdef WOLFSSL_DTLS byte haveMcast; /* using multicast ? */ #endif @@ -6729,6 +6743,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 623639a20f..8b7ebedc7d 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); @@ -4727,6 +4737,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*); @@ -5861,11 +5872,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 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 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