Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .wolfssl_known_macro_extras
Original file line number Diff line number Diff line change
Expand Up @@ -939,6 +939,7 @@ WOLFSSL_NRF51_AES
WOLFSSL_NXP_CASPER_ECC_MUL2ADD
WOLFSSL_NXP_CASPER_ECC_MULMOD
WOLFSSL_NXP_LPC55S6X
WOLFSSL_OCSP_SCREEN_RESPONDER
WOLFSSL_OLDTLS_AEAD_CIPHERSUITES
WOLFSSL_OLD_SET_CURVES_LIST
WOLFSSL_OLD_TIMINGPADVERIFY
Expand Down
15 changes: 15 additions & 0 deletions src/dtls13.c
Original file line number Diff line number Diff line change
Expand Up @@ -1898,6 +1898,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;

Expand Down
105 changes: 105 additions & 0 deletions tests/api/test_dtls.c
Original file line number Diff line number Diff line change
Expand Up @@ -1411,6 +1411,107 @@ 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;
byte hsMsg[DTLS_HANDSHAKE_HEADER_SZ + 1];
word32 idx = 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[DTLS_RECORD_HEADER_SZ - 2]) << 8) |
(byte)sh[DTLS_RECORD_HEADER_SZ - 1];
/* the ServerHello record must be fully contained in the copied message */
ExpectIntLE(DTLS_RECORD_HEADER_SZ + recLen, shSz);

/* 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);
ssl_c = NULL;
wolfSSL_CTX_free(ctx_c);
ctx_c = NULL;
wolfSSL_free(ssl_s);
ssl_s = NULL;
wolfSSL_CTX_free(ctx_s);
ctx_s = NULL;

/* Stretching the test suite a bit, ideally we should not test using
* internal state, but we have no way to forge encrypted packet yet */
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);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
if (EXPECT_SUCCESS()) {
ssl_c->keys.curEpoch64 = w64From32(0x0, DTLS13_EPOCH_TRAFFIC0);
ssl_c->keys.decryptedCur = 1;
ssl_c->curRL.pvMajor = ssl_c->version.major;
ssl_c->curRL.pvMinor = DTLSv1_2_MINOR;
}
XMEMSET(hsMsg, 0, sizeof(hsMsg));
hsMsg[0] = key_update;
/* oversized message_length, single-byte first fragment of it */
c32to24((word32)MAX_HANDSHAKE_SZ + 1, hsMsg + 1);
hsMsg[DTLS_HANDSHAKE_HEADER_SZ - 1] = 1;
ExpectIntEQ(Dtls13HandshakeRecv(ssl_c, hsMsg, &idx, (word32)sizeof(hsMsg)),
HANDSHAKE_SIZE_ERROR);
ExpectNull(ssl_c->dtls_rx_msg_list);

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 /* HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES && WOLFSSL_DTLS13 */

#if !defined(WOLFSSL_DTLS_RECORDS_CAN_SPAN_DATAGRAMS)
int test_dtls12_short_read(void)
{
Expand Down Expand Up @@ -1658,6 +1759,10 @@ int test_dtls13_longer_length(void)
{
return TEST_SKIPPED;
}
int test_dtls13_oversized_msg_length(void)
{
return TEST_SKIPPED;
}
int test_dtls_record_cross_boundaries(void)
{
return TEST_SKIPPED;
Expand Down
2 changes: 2 additions & 0 deletions tests/api/test_dtls.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,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);
Expand Down Expand Up @@ -124,6 +125,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), \
Expand Down
3 changes: 2 additions & 1 deletion wolfssl/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -7451,6 +7451,7 @@ WOLFSSL_LOCAL void DtlsSetSeqNumForReply(WOLFSSL* ssl);
#ifdef WOLFSSL_API_PREFIX_MAP
#define Dtls13GetEpoch wolfSSL_Dtls13GetEpoch
#define Dtls13CheckEpoch wolfSSL_Dtls13CheckEpoch
#define Dtls13HandshakeRecv wolfSSL_Dtls13HandshakeRecv
#define Dtls13WriteAckMessage wolfSSL_Dtls13WriteAckMessage
#define Dtls13RtxAddAck wolfSSL_Dtls13RtxAddAck
#define Dtls13DoScheduledWork wolfSSL_Dtls13DoScheduledWork
Expand Down Expand Up @@ -7495,7 +7496,7 @@ WOLFSSL_LOCAL int Dtls13HandshakeSend(WOLFSSL* ssl, byte* output,
int hash_output);
WOLFSSL_LOCAL int Dtls13RecordRecvd(WOLFSSL* ssl);
WOLFSSL_TEST_VIS int Dtls13CheckEpoch(WOLFSSL* ssl, enum HandShakeType type);
WOLFSSL_LOCAL int Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input,
WOLFSSL_TEST_VIS int Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input,
word32* inOutIdx, word32 totalSz);
WOLFSSL_LOCAL int Dtls13HandshakeAddHeader(WOLFSSL* ssl, byte* output,
enum HandShakeType msg_type, word32 length);
Expand Down
Loading