diff --git a/src/wolfio.c b/src/wolfio.c index 8c475aea4..4ee1605c8 100644 --- a/src/wolfio.c +++ b/src/wolfio.c @@ -51,6 +51,14 @@ Possible IO enable options: * HAVE_HTTP_CLIENT: Enables HTTP client API's default: off (unless HAVE_OCSP or HAVE_CRL_IO defined) * HAVE_IO_TIMEOUT: Enables support for connect timeout default: off + * + * DTLS_RECEIVEFROM_NO_TIMEOUT_ON_INVALID_PEER: This flag has effect only if + * ASN_NO_TIME is enabled. If enabled invalid peers messages are ignored + * indefinetely. If not enabled EmbedReceiveFrom will return timeout after + * DTLS_RECEIVEFROM_MAX_INVALID_PEER number of packets from invalid peers. When + * enabled, without a timer, EmbedReceivefrom can't check if the timeout is + * expired and it may never return under a continous flow of invalid packets. + * default: off */ @@ -59,6 +67,11 @@ Possible IO enable options: but they'll still need SetCallback xxx() at end of file */ +#if defined(NO_ASN_TIME) && !defined(DTLS_RECEIVEFROM_NO_TIMEOUT_ON_INVALID_PEER) \ + && !defined(DTLS_RECEIVEFROM_MAX_INVALID_PEER) +#define DTLS_RECEIVEFROM_MAX_INVALID_PEER 10 +#endif + #if defined(USE_WOLFSSL_IO) || defined(HAVE_HTTP_CLIENT) /* Translates return codes returned from @@ -333,7 +346,7 @@ static int sockAddrEqual( if (a->ss_family != b->ss_family) return 0; - if (a->ss_family == AF_INET) { + if (a->ss_family == WOLFSSL_IP4) { if (aLen < (XSOCKLENT)sizeof(SOCKADDR_IN)) return 0; @@ -349,7 +362,7 @@ static int sockAddrEqual( } #ifdef WOLFSSL_IPV6 - if (a->ss_family == AF_INET6) { + if (a->ss_family == WOLFSSL_IP6) { SOCKADDR_IN6 *a6, *b6; if (aLen < (XSOCKLENT)sizeof(SOCKADDR_IN6)) @@ -367,11 +380,20 @@ static int sockAddrEqual( return 1; } -#endif /* WOLFSSL_HAVE_IPV6 */ +#endif /* WOLFSSL_IPV6 */ return 0; } +#ifndef WOLFSSL_IPV6 +static int PeerIsIpv6(const SOCKADDR_S *peer, XSOCKLENT len) +{ + if (len < sizeof(peer->ss_family)) + return 0; + return peer->ss_family == WOLFSSL_IP6; +} +#endif /* !WOLFSSL_IPV6 */ + static int isDGramSock(int sfd) { char type = 0; @@ -400,6 +422,11 @@ int EmbedReceiveFrom(WOLFSSL *ssl, char *buf, int sz, void *ctx) SOCKADDR_S lclPeer; SOCKADDR_S* peer; XSOCKLENT peerSz = 0; +#ifndef NO_ASN_TIME + word32 start = 0; +#elif !defined(DTLS_RECEIVEFROM_NO_TIMEOUT_ON_INVALID_PEER) + word32 invalidPeerPackets = 0; +#endif WOLFSSL_ENTER("EmbedReceiveFrom"); @@ -407,6 +434,12 @@ int EmbedReceiveFrom(WOLFSSL *ssl, char *buf, int sz, void *ctx) peer = NULL; } else if (dtlsCtx->userSet) { +#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; + } +#endif peer = &lclPeer; XMEMSET(&lclPeer, 0, sizeof(lclPeer)); peerSz = sizeof(lclPeer); @@ -438,10 +471,26 @@ int EmbedReceiveFrom(WOLFSSL *ssl, char *buf, int sz, void *ctx) } #endif /* WOLFSSL_DTLS13 */ - if (!doDtlsTimeout) - dtls_timeout = 0; + do { - if (!wolfSSL_get_using_nonblock(ssl)) { + if (!doDtlsTimeout) { + dtls_timeout = 0; + } + else { +#ifndef NO_ASN_TIME + if (start == 0) { + start = LowResTimer(); + } + else { + dtls_timeout -= LowResTimer() - start; + start = LowResTimer(); + if (dtls_timeout < 0 || dtls_timeout > DTLS_TIMEOUT_MAX) + return WOLFSSL_CBIO_ERR_TIMEOUT; + } +#endif + } + + if (!wolfSSL_get_using_nonblock(ssl)) { #ifdef USE_WINDOWS_API DWORD timeout = dtls_timeout * 1000; #ifdef WOLFSSL_DTLS13 @@ -464,87 +513,101 @@ int EmbedReceiveFrom(WOLFSSL *ssl, char *buf, int sz, void *ctx) #endif /* WOLFSSL_DTLS13 */ timeout.tv_sec = dtls_timeout; #endif - if (setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, - sizeof(timeout)) != 0) { + if (setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, + sizeof(timeout)) != 0) { WOLFSSL_MSG("setsockopt rcvtimeo failed"); + } } - } #ifndef NO_ASN_TIME - else if(IsSCR(ssl)) { - if (ssl->dtls_start_timeout && - LowResTimer() - ssl->dtls_start_timeout > (word32)dtls_timeout) { - ssl->dtls_start_timeout = 0; - return WOLFSSL_CBIO_ERR_TIMEOUT; + else if (IsSCR(ssl)) { + if (ssl->dtls_start_timeout && + LowResTimer() - ssl->dtls_start_timeout > + (word32)dtls_timeout) { + ssl->dtls_start_timeout = 0; + return WOLFSSL_CBIO_ERR_TIMEOUT; + } + else if (!ssl->dtls_start_timeout) { + ssl->dtls_start_timeout = LowResTimer(); + } } - else if (!ssl->dtls_start_timeout) { - ssl->dtls_start_timeout = LowResTimer(); - } - } #endif /* !NO_ASN_TIME */ - recvd = (int)DTLS_RECVFROM_FUNCTION(sd, buf, sz, ssl->rflags, - (SOCKADDR*)peer, peer != NULL ? &peerSz : NULL); + recvd = (int)DTLS_RECVFROM_FUNCTION(sd, buf, 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; - } - - recvd = TranslateReturnCode(recvd, sd); - - if (recvd < 0) { - WOLFSSL_MSG("Embed Receive From error"); - recvd = TranslateIoError(recvd); - if (recvd == WOLFSSL_CBIO_ERR_WANT_READ && - !wolfSSL_dtls_get_using_nonblock(ssl)) { - recvd = WOLFSSL_CBIO_ERR_TIMEOUT; + /* 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 */ } - return recvd; - } - else if (recvd == 0) { - if (!isDGramSock(sd)) { - /* Closed TCP connection */ - recvd = WOLFSSL_CBIO_ERR_CONN_CLOSE; + else if (dtlsCtx->userSet) { + /* Truncate peer size */ + if (peerSz > (XSOCKLENT)sizeof(lclPeer)) + peerSz = (XSOCKLENT)sizeof(lclPeer); } else { - WOLFSSL_MSG("Ignoring 0-length datagram"); + /* Truncate peer size */ + if (peerSz > (XSOCKLENT)dtlsCtx->peer.bufSz) + peerSz = (XSOCKLENT)dtlsCtx->peer.bufSz; } - return recvd; - } - else if (dtlsCtx->connected) { - /* Nothing to do */ - } - else if (dtlsCtx->userSet) { - /* Check we received the packet from the correct peer */ - 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"); - return WOLFSSL_CBIO_ERR_WANT_READ; + + recvd = TranslateReturnCode(recvd, sd); + + if (recvd < 0) { + WOLFSSL_MSG("Embed Receive From error"); + recvd = TranslateIoError(recvd); + if (recvd == WOLFSSL_CBIO_ERR_WANT_READ && + !wolfSSL_dtls_get_using_nonblock(ssl)) { + recvd = WOLFSSL_CBIO_ERR_TIMEOUT; + } + return recvd; + } + else if (recvd == 0) { + if (!isDGramSock(sd)) { + /* Closed TCP connection */ + recvd = WOLFSSL_CBIO_ERR_CONN_CLOSE; + } + else { + WOLFSSL_MSG("Ignoring 0-length datagram"); + continue; + } + return recvd; + } + else if (dtlsCtx->connected) { + /* Nothing to do */ + } + else if (dtlsCtx->userSet) { + /* Check we received the packet from the correct peer */ + 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"); +#if defined(NO_ASN_TIME) && \ + !defined(DTLS_RECEIVEFROM_NO_TIMEOUT_ON_INVALID_PEER) + if (doDtlsTimeout) { + invalidPeerPackets++; + if (invalidPeerPackets > DTLS_RECEIVEFROM_MAX_INVALID_PEER) + return wolfSSL_dtls_get_using_nonblock(ssl) + ? WOLFSSL_CBIO_ERR_WANT_READ + : WOLFSSL_CBIO_ERR_TIMEOUT; + } +#endif /* NO_ASN_TIME && !DTLS_RECEIVEFROM_NO_TIMEOUT_ON_INVALID_PEER */ + continue; + } + } + else { + /* Store size of saved address */ + dtlsCtx->peer.sz = peerSz; } - } - else { - /* Store size of saved address */ - dtlsCtx->peer.sz = peerSz; - } #ifndef NO_ASN_TIME - ssl->dtls_start_timeout = 0; + ssl->dtls_start_timeout = 0; #endif /* !NO_ASN_TIME */ + break; + } while (1); return recvd; } @@ -569,6 +632,12 @@ int EmbedSendTo(WOLFSSL* ssl, char *buf, int sz, void *ctx) else if (!dtlsCtx->connected) { peer = (const SOCKADDR_S*)dtlsCtx->peer.sa; peerSz = dtlsCtx->peer.sz; +#ifndef WOLFSSL_IPV6 + if (PeerIsIpv6(peer, peerSz)) { + WOLFSSL_MSG("ipv6 dtls peer setted but no ipv6 support compiled"); + return NOT_COMPILED_IN; + } +#endif } sent = (int)DTLS_SENDTO_FUNCTION(sd, buf, sz, ssl->wflags, diff --git a/tests/api.c b/tests/api.c index d9805bff4..0e299dd77 100644 --- a/tests/api.c +++ b/tests/api.c @@ -5435,6 +5435,9 @@ static THREAD_RETURN WOLFSSL_THREAD test_server_nofail(void* args) input[idx] = '\0'; fprintf(stderr, "Client message: %s\n", input); } + else if (idx < 0) { + goto done; + } if (wolfSSL_write(ssl, msg, sizeof(msg)) != sizeof(msg)) { /*err_sys("SSL_write failed");*/ @@ -5684,8 +5687,6 @@ done: } #endif /* defined(OPENSSL_EXTRA) && !defined(NO_SESSION_CACHE) && !defined(WOLFSSL_TLS13) */ -typedef int (*cbType)(WOLFSSL_CTX *ctx, WOLFSSL *ssl); - static int test_client_nofail(void* args, cbType cb) { #if !defined(NO_WOLFSSL_CLIENT) @@ -5928,8 +5929,8 @@ done: return 0; } -void test_wolfSSL_client_server_nofail(callback_functions* client_cb, - callback_functions* server_cb) +void test_wolfSSL_client_server_nofail_ex(callback_functions* client_cb, + callback_functions* server_cb, cbType client_on_handshake) { func_args client_args; func_args server_args; @@ -5958,7 +5959,7 @@ void test_wolfSSL_client_server_nofail(callback_functions* client_cb, start_thread(test_server_nofail, &server_args, &serverThread); wait_tcp_ready(&server_args); - test_client_nofail(&client_args, NULL); + test_client_nofail(&client_args, client_on_handshake); join_thread(serverThread); client_cb->return_code = client_args.return_code; @@ -5971,6 +5972,13 @@ void test_wolfSSL_client_server_nofail(callback_functions* client_cb, #endif } +void test_wolfSSL_client_server_nofail(callback_functions* client_cb, + callback_functions* server_cb) +{ + test_wolfSSL_client_server_nofail_ex(client_cb, server_cb, NULL); +} + + #if defined(OPENSSL_EXTRA) && !defined(NO_SESSION_CACHE) && \ !defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_CLIENT) static void test_client_reuse_WOLFSSLobj(void* args, void *cb, void* server_args) @@ -65917,6 +65925,182 @@ static int test_wolfSSL_dtls13_null_cipher(void) return TEST_SKIPPED; } #endif +#if defined(WOLFSSL_DTLS) && !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \ + !defined(SINGLE_THREADED) + +static int test_dtls_msg_get_connected_port(int fd, word16 *port) +{ + SOCKADDR_S peer; + XSOCKLENT len; + int ret; + + XMEMSET((byte*)&peer, 0, sizeof(peer)); + len = sizeof(peer); + ret = getpeername(fd, (SOCKADDR*)&peer, &len); + if (ret != 0 || len > sizeof(peer)) + return -1; + switch (peer.ss_family) { +#ifdef WOLFSSL_IPV6 + case WOLFSSL_IP6: { + *port = ntohs(((SOCKADDR_IN6*)&peer)->sin6_port); + break; + } +#endif /* WOLFSSL_IPV6 */ + case WOLFSSL_IP4: + *port = ntohs(((SOCKADDR_IN*)&peer)->sin_port); + break; + default: + return -1; + } + return 0; +} + +static int test_dtls_msg_from_other_peer_cb(WOLFSSL_CTX *ctx, WOLFSSL *ssl) +{ + char buf[1] = {'t'}; + SOCKADDR_IN_T addr; + int sock_fd; + word16 port; + int err; + + (void)ssl; + (void)ctx; + + err = test_dtls_msg_get_connected_port(wolfSSL_get_fd(ssl), &port); + if (err != 0) + return -1; + + sock_fd = socket(AF_INET_V, SOCK_DGRAM, 0); + if (sock_fd == -1) + return -1; + build_addr(&addr, wolfSSLIP, port, 1, 0); + + /* send a packet to the server. Being another socket, the kernel will ensure + * the source port will be different. */ + err = (int)sendto(sock_fd, buf, sizeof(buf), 0, (SOCKADDR*)&addr, + sizeof(addr)); + + close(sock_fd); + if (err == -1) + return -1; + + return 0; +} + +/* setup a SSL session but just after the handshake send a packet to the server + * with a source address different than the one of the connected client. The I/O + * callback EmbedRecvFrom should just ignore the packet. Sending of the packet + * is done in test_dtls_msg_from_other_peer_cb */ +static int test_dtls_msg_from_other_peer(void) +{ + callback_functions client_cbs; + callback_functions server_cbs; + + XMEMSET((byte*)&client_cbs, 0, sizeof(client_cbs)); + XMEMSET((byte*)&server_cbs, 0, sizeof(server_cbs)); + + client_cbs.method = wolfDTLSv1_2_client_method; + server_cbs.method = wolfDTLSv1_2_server_method; + client_cbs.doUdp = 1; + server_cbs.doUdp = 1; + + test_wolfSSL_client_server_nofail_ex(&client_cbs, &server_cbs, + test_dtls_msg_from_other_peer_cb); + + if (client_cbs.return_code != WOLFSSL_SUCCESS || + server_cbs.return_code != WOLFSSL_SUCCESS) + return TEST_FAIL; + + return TEST_SUCCESS; +} +#else +static int test_dtls_msg_from_other_peer(void) +{ + return TEST_SKIPPED; +} +#endif /* defined(WOLFSSL_DTLS) && !defined(WOLFSSL_NO_TLS12) && \ + * !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \ + * !defined(SINGLE_THREADED) */ +#if defined(WOLFSSL_DTLS) && !defined(WOLFSSL_IPV6) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \ + defined(HAVE_IO_TESTS_DEPENDENCIES) +static int test_dtls_ipv6_check(void) +{ + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + SOCKADDR_IN fake_addr6; + int sockfd; + int ret; + + ctx_c = wolfSSL_CTX_new(wolfDTLSv1_2_client_method()); + if (ctx_c == NULL) + return TEST_FAIL; + ssl_c = wolfSSL_new(ctx_c); + if (ssl_c == NULL) + return TEST_FAIL; + ctx_s = wolfSSL_CTX_new(wolfDTLSv1_2_server_method()); + if (ctx_s == NULL) + return TEST_FAIL; + ret = wolfSSL_CTX_use_PrivateKey_file(ctx_s, svrKeyFile, + WOLFSSL_FILETYPE_PEM); + if (ret != WOLFSSL_SUCCESS) + return- -1; + ret = wolfSSL_CTX_use_certificate_file(ctx_s, svrCertFile, + WOLFSSL_FILETYPE_PEM); + if (ret != WOLFSSL_SUCCESS) + return -1; + ssl_s = wolfSSL_new(ctx_s); + if (ssl_s == NULL) + return TEST_FAIL; + XMEMSET((byte*)&fake_addr6, 0, sizeof(fake_addr6)); + /* mimic a sockaddr_in6 struct, this way we can't test without + * WOLFSSL_IPV6 */ + fake_addr6.sin_family = WOLFSSL_IP6; + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd == -1) + return TEST_FAIL; + ret = wolfSSL_set_fd(ssl_c, sockfd); + if (ret != WOLFSSL_SUCCESS) + return TEST_FAIL; + /* can't return error here, as the peer is opaque for wolfssl library at + * this point */ + ret = wolfSSL_dtls_set_peer(ssl_c, &fake_addr6, sizeof(fake_addr6)); + if (ret != WOLFSSL_SUCCESS) + return TEST_FAIL; + ret = fcntl(sockfd, F_SETFL, O_NONBLOCK); + if (ret == -1) + return TEST_FAIL; + wolfSSL_dtls_set_using_nonblock(ssl_c, 1); + ret = wolfSSL_connect(ssl_c); + if (ret != WOLFSSL_FAILURE && ssl_c->error != SOCKET_ERROR_E) + return TEST_FAIL; + + ret = wolfSSL_dtls_set_peer(ssl_s, &fake_addr6, sizeof(fake_addr6)); + if (ret != WOLFSSL_SUCCESS) + return TEST_FAIL; + /* re-use the socket */ + ret = wolfSSL_set_fd(ssl_c, sockfd); + if (ret != WOLFSSL_SUCCESS) + return TEST_FAIL; + wolfSSL_dtls_set_using_nonblock(ssl_s, 1); + ret = wolfSSL_accept(ssl_s); + if (ret != WOLFSSL_FAILURE && ssl_s->error != SOCKET_ERROR_E) + return TEST_FAIL; + close(sockfd); + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); + return TEST_SUCCESS; +} +#else +static int test_dtls_ipv6_check(void) +{ + return TEST_SKIPPED; +} +#endif /*----------------------------------------------------------------------------* | Main *----------------------------------------------------------------------------*/ @@ -66955,6 +67139,8 @@ TEST_CASE testCases[] = { TEST_DECL(test_override_alt_cert_chain), TEST_DECL(test_dtls13_bad_epoch_ch), TEST_DECL(test_wolfSSL_dtls13_null_cipher), + TEST_DECL(test_dtls_msg_from_other_peer), + TEST_DECL(test_dtls_ipv6_check), /* If at some point a stub get implemented this test should fail indicating * a need to implement a new test case */ diff --git a/wolfssl/test.h b/wolfssl/test.h index 27e7d833e..5bd9aea8f 100644 --- a/wolfssl/test.h +++ b/wolfssl/test.h @@ -636,6 +636,10 @@ typedef THREAD_RETURN WOLFSSL_THREAD THREAD_FUNC(void*); void start_thread(THREAD_FUNC fun, func_args* args, THREAD_TYPE* thread); void join_thread(THREAD_TYPE thread); +typedef int (*cbType)(WOLFSSL_CTX *ctx, WOLFSSL *ssl); + +void test_wolfSSL_client_server_nofail_ex(callback_functions* client_cb, + callback_functions* server_cb, cbType client_on_handshake); void test_wolfSSL_client_server_nofail(callback_functions* client_cb, callback_functions* server_cb);