Merge pull request #5503 from julek-wolfssl/dtls-fragments

DTLS limit fragments
This commit is contained in:
David Garske 2022-08-31 09:53:09 -07:00 committed by GitHub
commit 8722a46d52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 296 additions and 49 deletions

View File

@ -1514,17 +1514,13 @@ static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32 size,
if (ret != 0)
return PARSE_ERROR;
if (idx + fragLength > size) {
WOLFSSL_ERROR(INCOMPLETE_DATA);
return INCOMPLETE_DATA;
}
if (fragOff + fragLength > messageLength)
return BUFFER_ERROR;
if (handshakeType == client_hello &&
/* Only when receiving an unverified ClientHello */
ssl->options.serverState < SERVER_HELLO_COMPLETE) {
if (ssl->options.side == WOLFSSL_SERVER_END &&
ssl->options.acceptState < TLS13_ACCEPT_FIRST_REPLY_DONE) {
if (handshakeType != client_hello) {
WOLFSSL_MSG("Ignoring other messages before we verify a ClientHello");
*processedSize = size;
return 0;
}
/* To be able to operate in stateless mode, we assume the ClientHello
* is in order and we use its Handshake Message number and Sequence
* Number for our Tx. */
@ -1534,6 +1530,14 @@ static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32 size,
ssl->dtls13Epochs[0].nextSeqNumber = ssl->keys.curSeq;
}
if (idx + fragLength > size) {
WOLFSSL_ERROR(INCOMPLETE_DATA);
return INCOMPLETE_DATA;
}
if (fragOff + fragLength > messageLength)
return BUFFER_ERROR;
ret = Dtls13RtxMsgRecvd(ssl, (enum HandShakeType)handshakeType, fragOff);
if (ret != 0)
return ret;
@ -1554,6 +1558,16 @@ static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32 size,
isFirst = fragOff == 0;
isComplete = isFirst && fragLength == messageLength;
if (!isComplete && !IsEncryptionOn(ssl, 0)) {
#ifdef WOLFSSL_DEBUG_TLS
WOLFSSL_MSG("DTLS1.3 not accepting fragmented plaintext message");
#endif /* WOLFSSL_DEBUG_TLS */
/* ignore the message */
*processedSize = idx + fragLength + ssl->keys.padSz;
return 0;
}
usingAsyncCrypto = ssl->devId != INVALID_DEVID;
/* store the message if any of the following: (a) incomplete message, (b)
@ -1565,10 +1579,17 @@ static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32 size,
ssl->keys.dtls_peer_handshake_number >
ssl->keys.dtls_expected_peer_handshake_number ||
usingAsyncCrypto) {
DtlsMsgStore(ssl, w64GetLow32(ssl->keys.curEpoch64),
ssl->keys.dtls_peer_handshake_number,
input + DTLS_HANDSHAKE_HEADER_SZ, messageLength, handshakeType,
fragOff, fragLength, ssl->heap);
if (ssl->dtls_rx_msg_list_sz < DTLS_POOL_SZ) {
DtlsMsgStore(ssl, w64GetLow32(ssl->keys.curEpoch64),
ssl->keys.dtls_peer_handshake_number,
input + DTLS_HANDSHAKE_HEADER_SZ, messageLength, handshakeType,
fragOff, fragLength, ssl->heap);
}
else {
/* DTLS_POOL_SZ outstanding messages is way more than enough for any
* valid peer */
return DTLS_TOO_MANY_FRAGMENTS_E;
}
*processedSize = idx + fragLength + ssl->keys.padSz;
if (Dtls13NextMessageComplete(ssl))

View File

@ -522,7 +522,7 @@ int IsAtLeastTLSv1_3(const ProtocolVersion pv)
return ret;
}
static WC_INLINE int IsEncryptionOn(WOLFSSL* ssl, int isSend)
int IsEncryptionOn(WOLFSSL* ssl, int isSend)
{
#ifdef WOLFSSL_DTLS
/* For DTLS, epoch 0 is always not encrypted. */
@ -16177,6 +16177,16 @@ static int DoDtlsHandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
}
}
#if !defined(NO_WOLFSSL_SERVER)
if (ssl->options.side == WOLFSSL_SERVER_END &&
ssl->options.acceptState < ACCEPT_FIRST_REPLY_DONE &&
type != client_hello) {
WOLFSSL_MSG("Ignoring other messages before we verify a ClientHello");
*inOutIdx = totalSz;
return 0;
}
#endif
/* Check the handshake sequence number first. If out of order,
* add the current message to the list. If the message is in order,
* but it is a fragment, add the current message to the list, then
@ -16204,12 +16214,15 @@ static int DoDtlsHandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
* with newer and newer cookies.) */
if (type != client_hello) {
WOLFSSL_MSG("Current message is out of order");
if (ssl->dtls_rx_msg_list_sz < DTLS_POOL_SZ) {
DtlsMsgStore(ssl, ssl->keys.curEpoch,
ssl->keys.dtls_peer_handshake_number,
input + *inOutIdx, size, type,
fragOffset, fragSz, ssl->heap);
if (ssl->dtls_rx_msg_list_sz >= DTLS_POOL_SZ) {
WOLFSSL_MSG("Reached rx msg limit error");
return DTLS_TOO_MANY_FRAGMENTS_E;
}
DtlsMsgStore(ssl, ssl->keys.curEpoch,
ssl->keys.dtls_peer_handshake_number,
input + *inOutIdx, size, type,
fragOffset, fragSz, ssl->heap);
*inOutIdx += fragSz;
#if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
if (ssl->options.startedETMRead && ssl->keys.curEpoch != 0) {
@ -16305,12 +16318,15 @@ static int DoDtlsHandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
return 0;
}
if (ssl->dtls_rx_msg_list_sz < DTLS_POOL_SZ) {
DtlsMsgStore(ssl, ssl->keys.curEpoch,
ssl->keys.dtls_peer_handshake_number,
input + *inOutIdx, size, type,
fragOffset, fragSz, ssl->heap);
if (ssl->dtls_rx_msg_list_sz >= DTLS_POOL_SZ) {
WOLFSSL_MSG("Reached rx msg limit error");
WOLFSSL_ERROR(DTLS_TOO_MANY_FRAGMENTS_E);
return DTLS_TOO_MANY_FRAGMENTS_E;
}
DtlsMsgStore(ssl, ssl->keys.curEpoch,
ssl->keys.dtls_peer_handshake_number,
input + *inOutIdx, size, type,
fragOffset, fragSz, ssl->heap);
*inOutIdx += fragSz;
*inOutIdx += ssl->keys.padSz;
#if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
@ -16354,6 +16370,10 @@ static int DoDtlsHandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
/* In async mode always store the message and process it with
* DtlsMsgDrain because in case of a WC_PENDING_E it will be
* easier this way. */
if (ssl->dtls_rx_msg_list_sz >= DTLS_POOL_SZ) {
WOLFSSL_MSG("Reached rx msg limit error");
return DTLS_TOO_MANY_FRAGMENTS_E;
}
DtlsMsgStore(ssl, ssl->keys.curEpoch,
ssl->keys.dtls_peer_handshake_number,
input + idx, size, type,
@ -22862,6 +22882,8 @@ const char* wolfSSL_ERR_reason_error_string(unsigned long e)
#endif
case DTLS_CID_ERROR:
return "DTLS ConnectionID mismatch or missing";
case DTLS_TOO_MANY_FRAGMENTS_E:
return "Received too many fragmented messages from peer error";
default :
return "unknown error number";

View File

@ -12406,8 +12406,6 @@ int wolfSSL_DTLS_SetCookieSecret(WOLFSSL* ssl,
FALL_THROUGH;
case HELLO_AGAIN :
if (ssl->options.certOnly)
return WOLFSSL_SUCCESS;
#ifdef WOLFSSL_TLS13
if (ssl->options.tls1_3)
@ -12461,6 +12459,8 @@ int wolfSSL_DTLS_SetCookieSecret(WOLFSSL* ssl,
FALL_THROUGH;
case FIRST_REPLY_DONE :
if (ssl->options.certOnly)
return WOLFSSL_SUCCESS;
#if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH)
#ifdef WOLFSSL_TLS13
if (ssl->options.tls1_3)

View File

@ -10354,8 +10354,6 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl)
FALL_THROUGH;
case HELLO_AGAIN:
if (ssl->options.certOnly)
return WOLFSSL_SUCCESS;
if (ssl->options.serverState ==
SERVER_HELLO_RETRY_REQUEST_COMPLETE) {
@ -10403,6 +10401,8 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl)
FALL_THROUGH;
case FIRST_REPLY_DONE:
if (ssl->options.certOnly)
return WOLFSSL_SUCCESS;
#ifdef WOLFSSL_EARLY_DATA
if (!ssl->options.dtls && ssl->earlyData != no_early_data
&& !WOLFSSL_IS_QUIC(ssl)) {

View File

@ -5038,6 +5038,9 @@ static THREAD_RETURN WOLFSSL_THREAD test_server_nofail(void* args)
opts->return_code = TEST_SUCCESS;
done:
if (cbf != NULL)
cbf->last_err = err;
wolfSSL_shutdown(ssl);
wolfSSL_free(ssl);
if (!sharedCtx)
@ -5467,6 +5470,9 @@ static int test_client_nofail(void* args, cbType cb)
((func_args*)args)->return_code = TEST_SUCCESS;
done:
if (cbf != NULL)
cbf->last_err = err;
wolfSSL_free(ssl);
if (!sharedCtx)
wolfSSL_CTX_free(ctx);
@ -55025,6 +55031,198 @@ static int test_wolfSSL_dtls_plaintext(void) {
}
#endif
#if defined(HAVE_IO_TESTS_DEPENDENCIES) && !defined(SINGLE_THREADED) && \
defined(WOLFSSL_DTLS)
static void test_wolfSSL_dtls12_fragments_spammer(WOLFSSL* ssl)
{
byte b[1100]; /* buffer for the messages to send */
size_t idx = 0;
size_t seq_offset = 0;
size_t msg_offset = 0;
int i;
int fd = wolfSSL_get_fd(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 */
AssertIntEQ(ret, 1);
/* Now let's start spamming the peer with fragments it needs to store */
XMEMSET(b, -1, sizeof(b));
/* record layer */
/* handshake type */
b[idx++] = 22;
/* protocol version */
b[idx++] = 0xfe;
b[idx++] = 0xfd; /* DTLS 1.2 */
/* epoch 0 */
XMEMSET(b + idx, 0, 2);
idx += 2;
/* sequence number */
XMEMSET(b + idx, 0, 6);
seq_offset = idx + 2; /* increment only the low 32 bits */
idx += 6;
/* static length in BE */
c16toa(42, b + idx);
idx += 2;
/* handshake layer */
/* cert type */
b[idx++] = 11;
/* length */
c32to24(1000, b + idx);
idx += 3;
/* message seq */
c16toa(0, b + idx);
msg_offset = idx;
idx += 2;
/* frag offset */
c32to24(500, b + idx);
idx += 3;
/* frag length */
c32to24(30, b + idx);
idx += 3;
for (i = 0; i < DTLS_POOL_SZ * 2 && ret > 0;
seq_number++, msg_number++, i++) {
struct timespec delay;
XMEMSET(&delay, 0, sizeof(delay));
delay.tv_nsec = 10000000; /* wait 0.01 seconds */
c32toa(seq_number, b + seq_offset);
c16toa(msg_number, b + msg_offset);
ret = (int)send(fd, b, 55, 0);
nanosleep(&delay, NULL);
}
}
#ifdef WOLFSSL_DTLS13
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);
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);
/* Now let's start spamming the peer with fragments it needs to store */
XMEMSET(b, -1, sizeof(b));
/* handshake type */
b[idx++] = 11;
/* length */
c32to24(10000, b + idx);
idx += 3;
/* message_seq */
msg_offset = idx;
idx += 2;
/* fragment_offset */
c32to24(5000, b + idx);
idx += 3;
/* fragment_length */
c32to24(100, b + idx);
idx += 3;
/* fragment contents */
idx += 100;
for (; ret > 0; msg_number++) {
byte sendBuf[150];
int sendSz = sizeof(sendBuf);
struct timespec delay;
XMEMSET(&delay, 0, sizeof(delay));
delay.tv_nsec = 10000000; /* wait 0.01 seconds */
c16toa(msg_number, b + msg_offset);
sendSz = BuildTls13Message(ssl, sendBuf, sendSz, b,
(int)idx, handshake, 0, 0, 0);
ret = (int)send(fd, sendBuf, (size_t)sendSz, 0);
nanosleep(&delay, NULL);
}
}
#endif
static int test_wolfSSL_dtls_fragments(void)
{
tcp_ready ready;
func_args client_args;
func_args server_args;
callback_functions func_cb_client;
callback_functions func_cb_server;
THREAD_TYPE serverThread;
size_t i;
struct test_params {
method_provider client_meth;
method_provider server_meth;
ssl_callback spammer;
} params[] = {
{wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method,
test_wolfSSL_dtls12_fragments_spammer},
#ifdef WOLFSSL_DTLS13
{wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method,
test_wolfSSL_dtls13_fragments_spammer},
#endif
};
printf(testingFmt, "test_wolfSSL_dtls_fragments");
for (i = 0; i < sizeof(params)/sizeof(*params); i++) {
XMEMSET(&client_args, 0, sizeof(func_args));
XMEMSET(&server_args, 0, sizeof(func_args));
XMEMSET(&func_cb_client, 0, sizeof(callback_functions));
XMEMSET(&func_cb_server, 0, sizeof(callback_functions));
#ifdef WOLFSSL_TIRTOS
fdOpenSession(Task_self());
#endif
StartTCP();
InitTcpReady(&ready);
#if defined(USE_WINDOWS_API)
/* use RNG to get random port if using windows */
ready.port = GetRandomPort();
#endif
server_args.signal = &ready;
server_args.callbacks = &func_cb_server;
client_args.signal = &ready;
client_args.callbacks = &func_cb_client;
func_cb_client.doUdp = func_cb_server.doUdp = 1;
func_cb_server.method = params[i].server_meth;
func_cb_client.method = params[i].client_meth;
func_cb_client.ssl_ready = params[i].spammer;
start_thread(test_server_nofail, &server_args, &serverThread);
wait_tcp_ready(&server_args);
test_client_nofail(&client_args, NULL);
join_thread(serverThread);
AssertFalse(client_args.return_code);
AssertFalse(server_args.return_code);
/* The socket should be closed by the server resulting in a
* socket error */
AssertIntEQ(func_cb_client.last_err, SOCKET_ERROR_E);
/* Check the server returned an error indicating the msg buffer
* was full */
AssertIntEQ(func_cb_server.last_err, DTLS_TOO_MANY_FRAGMENTS_E);
FreeTcpReady(&ready);
#ifdef WOLFSSL_TIRTOS
fdOpenSession(Task_self());
#endif
}
printf(resultFmt, passed);
return 0;
}
#else
static int test_wolfSSL_dtls_fragments(void) {
return 0;
}
#endif
#if !defined(NO_RSA) && !defined(NO_SHA) && !defined(NO_FILESYSTEM) && \
!defined(NO_CERTS) && (!defined(NO_WOLFSSL_CLIENT) || \
!defined(WOLFSSL_NO_CLIENT_AUTH))
@ -57600,6 +57798,7 @@ TEST_CASE testCases[] = {
TEST_DECL(test_wolfSSL_msgCb),
TEST_DECL(test_wolfSSL_either_side),
TEST_DECL(test_wolfSSL_DTLS_either_side),
TEST_DECL(test_wolfSSL_dtls_fragments),
TEST_DECL(test_generate_cookie),
TEST_DECL(test_wolfSSL_X509_STORE_set_flags),
TEST_DECL(test_wolfSSL_X509_LOOKUP_load_file),

View File

@ -178,8 +178,8 @@ enum wolfSSL_ErrorCodes {
FALCON_KEY_SIZE_E = -451, /* Wrong key size for Falcon. */
QUIC_TP_MISSING_E = -452, /* QUIC transport parameter missing */
DILITHIUM_KEY_SIZE_E = -453, /* Wrong key size for Dilithium. */
DTLS_CID_ERROR = -454, /* Wrong or missing CID */
DTLS_TOO_MANY_FRAGMENTS_E = -455, /* Received too many fragments */
/* add strings to wolfSSL_ERR_reason_error_string in internal.c !!!!! */
/* begin negotiation parameter errors */

View File

@ -1383,7 +1383,8 @@ enum Misc {
DTLS_RECORD_EXTRA = 8, /* diff from normal */
DTLS_HANDSHAKE_SEQ_SZ = 2, /* handshake header sequence number */
DTLS_HANDSHAKE_FRAG_SZ = 3, /* fragment offset and length are 24 bit */
DTLS_POOL_SZ = 255,/* allowed number of list items in TX pool */
DTLS_POOL_SZ = 20, /* allowed number of list items in TX and
* RX pool */
DTLS_EXPORT_PRO = 165,/* wolfSSL protocol for serialized session */
DTLS_EXPORT_STATE_PRO = 166,/* wolfSSL protocol for serialized state */
TLS_EXPORT_PRO = 167,/* wolfSSL protocol for serialized TLS */
@ -5312,6 +5313,7 @@ WOLFSSL_LOCAL int StoreKeys(WOLFSSL* ssl, const byte* keyData, int side);
WOLFSSL_LOCAL int IsTLS(const WOLFSSL* ssl);
WOLFSSL_LOCAL int IsAtLeastTLSv1_2(const WOLFSSL* ssl);
WOLFSSL_LOCAL int IsAtLeastTLSv1_3(ProtocolVersion pv);
WOLFSSL_LOCAL int IsEncryptionOn(WOLFSSL* ssl, int isSend);
WOLFSSL_LOCAL int TLSv1_3_Capable(WOLFSSL* ssl);
WOLFSSL_LOCAL void FreeHandshakeResources(WOLFSSL* ssl);
@ -5608,7 +5610,8 @@ WOLFSSL_LOCAL int BuildMessage(WOLFSSL* ssl, byte* output, int outSz,
int sizeOnly, int asyncOkay, int epochOrder);
#ifdef WOLFSSL_TLS13
int BuildTls13Message(WOLFSSL* ssl, byte* output, int outSz, const byte* input,
/* Use WOLFSSL_API to use this function in tests/api.c */
WOLFSSL_API int BuildTls13Message(WOLFSSL* ssl, byte* output, int outSz, const byte* input,
int inSz, int type, int hashOutput, int sizeOnly, int asyncOkay);
#endif

View File

@ -613,6 +613,7 @@ typedef struct callback_functions {
#endif
int devId;
int return_code;
int last_err;
unsigned char isSharedCtx:1;
unsigned char loadToSSL:1;
unsigned char ticNoInit:1;
@ -2093,32 +2094,33 @@ static WC_INLINE void udp_accept(SOCKET_T* sockfd, SOCKET_T* clientfd,
}
#endif
if (args != NULL && args->signal != NULL) {
#if defined(_POSIX_THREADS) && !defined(__MINGW32__)
/* signal ready to accept data */
{
tcp_ready* ready = args->signal;
PTHREAD_CHECK_RET(pthread_mutex_lock(&ready->mutex));
ready->ready = 1;
ready->port = port;
PTHREAD_CHECK_RET(pthread_cond_signal(&ready->cond));
PTHREAD_CHECK_RET(pthread_mutex_unlock(&ready->mutex));
}
/* signal ready to accept data */
tcp_ready* ready = args->signal;
PTHREAD_CHECK_RET(pthread_mutex_lock(&ready->mutex));
ready->ready = 1;
ready->port = port;
PTHREAD_CHECK_RET(pthread_cond_signal(&ready->cond));
PTHREAD_CHECK_RET(pthread_mutex_unlock(&ready->mutex));
#elif defined (WOLFSSL_TIRTOS)
/* Need mutex? */
tcp_ready* ready = args->signal;
ready->ready = 1;
ready->port = port;
/* Need mutex? */
tcp_ready* ready = args->signal;
ready->ready = 1;
ready->port = port;
#elif defined(NETOS)
{
tcp_ready* ready = args->signal;
(void)tx_mutex_get(&ready->mutex, TX_WAIT_FOREVER);
ready->ready = 1;
ready->port = port;
(void)tx_mutex_put(&ready->mutex);
}
#else
(void)port;
(void)port;
#endif
}
else {
fprintf(stderr, "args or args->signal was NULL. Not setting ready info.");
}
*clientfd = *sockfd;
}