diff --git a/src/dtls13.c b/src/dtls13.c index 9d7f78bc369..2e3fa591533 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -1889,6 +1889,21 @@ static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32 size, return INCOMPLETE_DATA; } + /* Cap the handshake message size before it can be buffered for reassembly, + * matching the DTLSv1.2 path (DoDtlsHandShakeMsg()). RFC 9147 Sec 4.5.2 + * says invalid records SHOULD be silently discarded, so only error out once + * the record is authenticated (received in an encrypted epoch); a plaintext + * message is just dropped. */ + if (messageLength > MAX_HANDSHAKE_SZ) { + WOLFSSL_MSG("Handshake message too large"); + if (IsEncryptionOn(ssl, 0)) { + WOLFSSL_ERROR_VERBOSE(HANDSHAKE_SIZE_ERROR); + return HANDSHAKE_SIZE_ERROR; + } + *processedSize = idx + fragLength; + return 0; + } + if (fragOff + fragLength > messageLength) return BUFFER_ERROR; diff --git a/tests/api/test_dtls.c b/tests/api/test_dtls.c index ccacf370baa..3f6274bc203 100644 --- a/tests/api/test_dtls.c +++ b/tests/api/test_dtls.c @@ -784,6 +784,72 @@ int test_dtls13_short_read(void) } #endif /* WOLFSSL_DTLS13 && !defined(WOLFSSL_DTLS_RECORDS_CAN_SPAN_DATAGRAMS) */ +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) +/* A plaintext (epoch 0) handshake message whose handshake message_length claims + * more than MAX_HANDSHAKE_SZ must not be buffered for reassembly. We use the + * server's plaintext ServerHello: the client accepts a fragmented ServerHello + * (Dtls13AcceptFragmented()), so an incomplete one is normally stored for + * reassembly. Without the message_length cap in _Dtls13HandshakeRecv() the + * spoofed message would be added to the rx list; with it the unauthenticated + * message is silently dropped. */ +int test_dtls13_oversized_msg_length(void) +{ + EXPECT_DECLS; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + char sh[TEST_MEMIO_BUF_SZ]; + int shSz = (int)sizeof(sh); + int recLen = 0; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0); + + /* CH1 -> server, then server emits its first flight (plaintext ServerHello + * is the first record). */ + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntGT(test_ctx.c_msg_count, 0); + ExpectIntEQ(test_memio_copy_message(&test_ctx, 1, sh, &shSz, 0), 0); + ExpectIntGE(shSz, DTLS_RECORD_HEADER_SZ + DTLS_HANDSHAKE_HEADER_SZ); + /* First record is a plaintext handshake ServerHello. */ + ExpectIntEQ((byte)sh[0], handshake); + ExpectIntEQ((byte)sh[DTLS_RECORD_HEADER_SZ], server_hello); + if (EXPECT_SUCCESS()) + recLen = (((byte)sh[11]) << 8) | (byte)sh[12]; + + /* Spoof only the handshake message_length of the ServerHello record, + * leaving the record length and fragment_length intact so it clears record + * parsing and looks like the first fragment of an oversized message. */ + c32to24((word32)MAX_HANDSHAKE_SZ + 1, + (byte*)sh + DTLS_RECORD_HEADER_SZ + 1); + test_memio_clear_buffer(&test_ctx, 1); + ExpectIntEQ(test_memio_inject_message(&test_ctx, 1, sh, + DTLS_RECORD_HEADER_SZ + recLen), 0); + + /* The client must reject the oversized ServerHello without buffering it. */ + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectNull(ssl_c->dtls_rx_msg_list); + ExpectIntEQ(ssl_c->dtls_rx_msg_list_sz, 0); + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); + + return EXPECT_RESULT(); +} +#else +int test_dtls13_oversized_msg_length(void) +{ + return TEST_SKIPPED; +} +#endif + #if !defined(WOLFSSL_DTLS_RECORDS_CAN_SPAN_DATAGRAMS) int test_dtls12_short_read(void) { diff --git a/tests/api/test_dtls.h b/tests/api/test_dtls.h index 4523cdef1e9..bf7a0ef9707 100644 --- a/tests/api/test_dtls.h +++ b/tests/api/test_dtls.h @@ -31,6 +31,7 @@ int test_dtls12_record_length_mismatch(void); int test_dtls12_short_read(void); int test_dtls13_longer_length(void); int test_dtls13_short_read(void); +int test_dtls13_oversized_msg_length(void); int test_records_span_network_boundaries(void); int test_dtls_record_cross_boundaries(void); int test_dtls_rtx_across_epoch_change(void); @@ -111,6 +112,7 @@ int test_WOLFSSL_dtls_version_alert(void); TEST_DECL_GROUP("dtls", test_dtls12_short_read), \ TEST_DECL_GROUP("dtls", test_dtls13_longer_length), \ TEST_DECL_GROUP("dtls", test_dtls13_short_read), \ + TEST_DECL_GROUP("dtls", test_dtls13_oversized_msg_length), \ TEST_DECL_GROUP("dtls", test_records_span_network_boundaries), \ TEST_DECL_GROUP("dtls", test_dtls_record_cross_boundaries), \ TEST_DECL_GROUP("dtls", test_dtls_rtx_across_epoch_change), \