Merge pull request #7029 from julek-wolfssl/zd/17108-fix

Additional TLS checks
This commit is contained in:
Sean Parkinson
2023-12-13 14:31:11 +10:00
committed by GitHub
5 changed files with 389 additions and 16 deletions

View File

@ -363,6 +363,14 @@ int Dtls13ProcessBufferedMessages(WOLFSSL* ssl)
if (!msg->ready)
break;
#ifndef WOLFSSL_DISABLE_EARLY_SANITY_CHECKS
ret = MsgCheckEncryption(ssl, msg->type, msg->encrypted);
if (ret != 0) {
SendAlert(ssl, alert_fatal, unexpected_message);
break;
}
#endif
/* We may have DTLS <=1.2 msgs stored from before we knew which version
* we were going to use. Interpret correctly. */
if (IsAtLeastTLSv1_3(ssl->version)) {
@ -1622,6 +1630,13 @@ static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32 size,
if (ret != 0)
return PARSE_ERROR;
/* Need idx + fragLength as we don't advance the inputBuffer idx value */
ret = EarlySanityCheckMsgReceived(ssl, handshakeType, idx + fragLength);
if (ret != 0) {
WOLFSSL_ERROR(ret);
return ret;
}
if (ssl->options.side == WOLFSSL_SERVER_END &&
ssl->options.acceptState < TLS13_ACCEPT_FIRST_REPLY_DONE) {
if (handshakeType != client_hello) {

View File

@ -547,7 +547,7 @@ int IsAtLeastTLSv1_3(const ProtocolVersion pv)
return ret;
}
int IsEncryptionOn(WOLFSSL* ssl, int isSend)
int IsEncryptionOn(const WOLFSSL* ssl, int isSend)
{
#ifdef WOLFSSL_DTLS
/* For DTLS, epoch 0 is always not encrypted. */
@ -4688,7 +4688,7 @@ static void SetDigest(WOLFSSL* ssl, int hashAlgo)
#endif /* !NO_CERTS */
#if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
static word32 MacSize(WOLFSSL* ssl)
static word32 MacSize(const WOLFSSL* ssl)
{
#ifdef HAVE_TRUNCATED_HMAC
word32 digestSz = ssl->truncated_hmac ? (byte)TRUNCATED_HMAC_SZ
@ -8972,7 +8972,8 @@ static void DtlsMsgAssembleCompleteMessage(DtlsMsg* msg)
}
int DtlsMsgSet(DtlsMsg* msg, word32 seq, word16 epoch, const byte* data, byte type,
word32 fragOffset, word32 fragSz, void* heap, word32 totalLen)
word32 fragOffset, word32 fragSz, void* heap, word32 totalLen,
byte encrypted)
{
word32 fragOffsetEnd = fragOffset + fragSz;
@ -8993,11 +8994,13 @@ int DtlsMsgSet(DtlsMsg* msg, word32 seq, word16 epoch, const byte* data, byte ty
WOLFSSL_ERROR_VERBOSE(SEQUENCE_ERROR);
return SEQUENCE_ERROR;
}
msg->encrypted = msg->encrypted && encrypted;
}
else {
msg->type = type;
msg->epoch = epoch;
msg->seq = seq;
msg->encrypted = encrypted;
}
if (msg->fragBucketList == NULL) {
@ -9118,6 +9121,7 @@ void DtlsMsgStore(WOLFSSL* ssl, word16 epoch, word32 seq, const byte* data,
*/
DtlsMsg* head = ssl->dtls_rx_msg_list;
byte encrypted = ssl->keys.decryptedCur == 1;
WOLFSSL_ENTER("DtlsMsgStore");
if (head != NULL) {
@ -9126,7 +9130,7 @@ void DtlsMsgStore(WOLFSSL* ssl, word16 epoch, word32 seq, const byte* data,
cur = DtlsMsgNew(dataSz, 0, heap);
if (cur != NULL) {
if (DtlsMsgSet(cur, seq, epoch, data, type,
fragOffset, fragSz, heap, dataSz) < 0) {
fragOffset, fragSz, heap, dataSz, encrypted) < 0) {
DtlsMsgDelete(cur, heap);
}
else {
@ -9138,13 +9142,13 @@ void DtlsMsgStore(WOLFSSL* ssl, word16 epoch, word32 seq, const byte* data,
else {
/* If this fails, the data is just dropped. */
DtlsMsgSet(cur, seq, epoch, data, type, fragOffset,
fragSz, heap, dataSz);
fragSz, heap, dataSz, encrypted);
}
}
else {
head = DtlsMsgNew(dataSz, 0, heap);
if (DtlsMsgSet(head, seq, epoch, data, type, fragOffset,
fragSz, heap, dataSz) < 0) {
fragSz, heap, dataSz, encrypted) < 0) {
DtlsMsgDelete(head, heap);
head = NULL;
}
@ -10714,6 +10718,297 @@ int CheckAvailableSize(WOLFSSL *ssl, int size)
return 0;
}
#ifndef WOLFSSL_DISABLE_EARLY_SANITY_CHECKS
int MsgCheckEncryption(WOLFSSL* ssl, byte type, byte encrypted)
{
#ifdef WOLFSSL_QUIC
/* QUIC protects messages outside of the TLS scope */
if (WOLFSSL_IS_QUIC(ssl) && IsAtLeastTLSv1_3(ssl->version))
return 0;
#endif
/* Verify which messages always have to be encrypted */
if (IsAtLeastTLSv1_3(ssl->version)) {
switch ((enum HandShakeType)type) {
case client_hello:
case server_hello:
case hello_verify_request:
case hello_retry_request:
case change_cipher_hs:
if (encrypted) {
WOLFSSL_MSG("Message can not be encrypted");
WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
return OUT_OF_ORDER_E;
}
break;
case hello_request:
case session_ticket:
case end_of_early_data:
case encrypted_extensions:
case certificate:
case server_key_exchange:
case certificate_request:
case server_hello_done:
case certificate_verify:
case client_key_exchange:
case finished:
case certificate_status:
case key_update:
if (!encrypted) {
WOLFSSL_MSG("Message always has to be encrypted");
WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
return OUT_OF_ORDER_E;
}
break;
case message_hash:
case no_shake:
default:
WOLFSSL_MSG("Unknown message type");
WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E);
return SANITY_MSG_E;
}
}
else {
switch ((enum HandShakeType)type) {
case client_hello:
if ((IsSCR(ssl) || ssl->options.handShakeDone) && !encrypted) {
WOLFSSL_MSG("Message has to be encrypted for SCR");
WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
return OUT_OF_ORDER_E;
}
break;
case server_hello:
case hello_verify_request:
case hello_retry_request:
case certificate:
case server_key_exchange:
case certificate_request:
case server_hello_done:
case certificate_verify:
case client_key_exchange:
case certificate_status:
case session_ticket:
case change_cipher_hs:
if (IsSCR(ssl)) {
if (!encrypted) {
WOLFSSL_MSG("Message has to be encrypted during SCR");
WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
return OUT_OF_ORDER_E;
}
}
else if (encrypted) {
WOLFSSL_MSG("Message can not be encrypted in regular "
"handshake");
WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
return OUT_OF_ORDER_E;
}
break;
case hello_request:
case finished:
if (!encrypted) {
WOLFSSL_MSG("Message always has to be encrypted");
WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
return OUT_OF_ORDER_E;
}
break;
case key_update:
case encrypted_extensions:
case end_of_early_data:
case message_hash:
case no_shake:
default:
WOLFSSL_MSG("Unknown message type");
WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E);
return SANITY_MSG_E;
}
}
return 0;
}
static WC_INLINE int isLastMsg(const WOLFSSL* ssl, word32 msgSz)
{
word32 extra = 0;
if (IsEncryptionOn(ssl, 0)) {
extra = ssl->keys.padSz;
#if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
if (ssl->options.startedETMRead)
extra += MacSize(ssl);
#endif
}
return (ssl->buffers.inputBuffer.idx - ssl->curStartIdx) + msgSz + extra
== ssl->curSize;
}
/* Check if the msg is the last msg in a record. This is also an easy way
* to check that a record doesn't span different key boundaries. */
static int MsgCheckBoundary(const WOLFSSL* ssl, byte type,
byte version_negotiated, word32 msgSz)
{
if (version_negotiated) {
if (IsAtLeastTLSv1_3(ssl->version)) {
switch ((enum HandShakeType)type) {
case hello_request:
case client_hello:
case server_hello:
case hello_verify_request:
case hello_retry_request:
case finished:
case end_of_early_data:
if (!isLastMsg(ssl, msgSz)) {
WOLFSSL_MSG("Message type is not last in record");
WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
return OUT_OF_ORDER_E;
}
break;
case session_ticket:
case encrypted_extensions:
case certificate:
case server_key_exchange:
case certificate_request:
case certificate_verify:
case client_key_exchange:
case certificate_status:
case key_update:
case change_cipher_hs:
break;
case server_hello_done:
case message_hash:
case no_shake:
default:
WOLFSSL_MSG("Unknown message type");
WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E);
return SANITY_MSG_E;
}
}
else {
switch ((enum HandShakeType)type) {
case hello_request:
case client_hello:
case hello_verify_request:
if (!isLastMsg(ssl, msgSz)) {
WOLFSSL_MSG("Message type is not last in record");
WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
return OUT_OF_ORDER_E;
}
break;
case server_hello:
case session_ticket:
case end_of_early_data:
case certificate:
case server_key_exchange:
case certificate_request:
case server_hello_done:
case certificate_verify:
case client_key_exchange:
case finished:
case certificate_status:
case change_cipher_hs:
break;
case hello_retry_request:
case encrypted_extensions:
case key_update:
case message_hash:
case no_shake:
default:
WOLFSSL_MSG("Unknown message type");
WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E);
return SANITY_MSG_E;
}
}
}
else {
switch ((enum HandShakeType)type) {
case hello_request:
case client_hello:
case hello_verify_request:
if (!isLastMsg(ssl, msgSz)) {
WOLFSSL_MSG("Message type is not last in record");
WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
return OUT_OF_ORDER_E;
}
break;
case server_hello:
case session_ticket:
case end_of_early_data:
case hello_retry_request:
case encrypted_extensions:
case certificate:
case server_key_exchange:
case certificate_request:
case server_hello_done:
case certificate_verify:
case client_key_exchange:
case finished:
case certificate_status:
case key_update:
case change_cipher_hs:
break;
case message_hash:
case no_shake:
default:
WOLFSSL_MSG("Unknown message type");
WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E);
return SANITY_MSG_E;
}
}
return 0;
}
#endif /* WOLFSSL_DISABLE_EARLY_SANITY_CHECKS */
/**
* This check is performed as soon as the handshake message type becomes known.
* These checks can not be delayed and need to be performed when the msg is
* received and not when it is processed (fragmentation may cause messages to
* be processed at a later time). This function CAN NOT be called on stored
* messages as it relies on the state of the WOLFSSL object right after
* receiving the message.
*
* @param ssl The current connection
* @param type The enum HandShakeType of the current message
* @param msgSz Size of the current message
* @return
*/
int EarlySanityCheckMsgReceived(WOLFSSL* ssl, byte type, word32 msgSz)
{
int ret = 0;
#ifndef WOLFSSL_DISABLE_EARLY_SANITY_CHECKS
byte version_negotiated = 0;
WOLFSSL_ENTER("EarlySanityCheckMsgReceived");
#ifdef WOLFSSL_DTLS
/* Version has only been negotiated after we either send or process a
* ServerHello message */
if (ssl->options.dtls)
version_negotiated = ssl->options.serverState >= SERVER_HELLO_COMPLETE;
else
#endif
version_negotiated = 1;
if (version_negotiated)
ret = MsgCheckEncryption(ssl, type, ssl->keys.decryptedCur == 1);
if (ret == 0)
ret = MsgCheckBoundary(ssl, type, version_negotiated, msgSz);
if (ret != 0
#ifdef WOLFSSL_DTLS
&& ssl->options.dtls && ssl->options.dtlsStateful
#endif
)
SendAlert(ssl, alert_fatal, unexpected_message);
WOLFSSL_LEAVE("EarlySanityCheckMsgReceived", ret);
#else
(void)ssl;
(void)type;
(void)msgSz;
#endif
return ret;
}
#ifdef WOLFSSL_DTLS13
static int GetInputData(WOLFSSL *ssl, word32 size);
static int GetDtls13RecordHeader(WOLFSSL* ssl, word32* inOutIdx,
@ -15785,7 +16080,6 @@ int DoFinished(WOLFSSL* ssl, const byte* input, word32* inOutIdx, word32 size,
return 0;
}
/* Make sure no duplicates, no fast forward, or other problems; 0 on success */
static int SanityCheckMsgReceived(WOLFSSL* ssl, byte type)
{
@ -16650,6 +16944,12 @@ static int DoHandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
return PARSE_ERROR;
}
ret = EarlySanityCheckMsgReceived(ssl, type, size);
if (ret != 0) {
WOLFSSL_ERROR(ret);
return ret;
}
if (size > MAX_HANDSHAKE_SZ) {
WOLFSSL_MSG("Handshake message too large");
WOLFSSL_ERROR_VERBOSE(HANDSHAKE_SIZE_ERROR);
@ -16673,6 +16973,13 @@ static int DoHandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
return PARSE_ERROR;
}
ret = EarlySanityCheckMsgReceived(ssl, type,
min(inputLength - HANDSHAKE_HEADER_SZ, size));
if (ret != 0) {
WOLFSSL_ERROR(ret);
return ret;
}
/* Cap the maximum size of a handshake message to something reasonable.
* By default is the maximum size of a certificate message assuming
* nine 2048-bit RSA certificates in the chain. */
@ -16711,6 +17018,13 @@ static int DoHandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
if (inputLength > pendSz)
inputLength = pendSz;
ret = EarlySanityCheckMsgReceived(ssl, ssl->arrays->pendingMsgType,
inputLength);
if (ret != 0) {
WOLFSSL_ERROR(ret);
return ret;
}
#ifdef WOLFSSL_ASYNC_CRYPT
if (ssl->error != WC_PENDING_E)
#endif
@ -17265,6 +17579,14 @@ int DtlsMsgDrain(WOLFSSL* ssl)
item->ready && ret == 0) {
word32 idx = 0;
#ifndef WOLFSSL_DISABLE_EARLY_SANITY_CHECKS
ret = MsgCheckEncryption(ssl, item->type, item->encrypted);
if (ret != 0) {
SendAlert(ssl, alert_fatal, unexpected_message);
break;
}
#endif
#ifdef WOLFSSL_NO_TLS12
ret = DoTls13HandShakeMsgType(ssl, item->fullMsg, &idx, item->type,
item->sz, item->sz);
@ -17314,6 +17636,12 @@ static int DoDtlsHandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
return PARSE_ERROR;
}
ret = EarlySanityCheckMsgReceived(ssl, type, fragSz);
if (ret != 0) {
WOLFSSL_ERROR(ret);
return ret;
}
/* Cap the maximum size of a handshake message to something reasonable.
* By default is the maximum size of a certificate message assuming
* nine 2048-bit RSA certificates in the chain. */
@ -20271,7 +20599,6 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr)
{
int ret = 0, type = internal_error, readSz;
int atomicUser = 0;
word32 startIdx = 0;
#if defined(WOLFSSL_DTLS)
int used;
#endif
@ -20563,7 +20890,8 @@ default:
ssl->keys.padSz = 0;
ssl->options.processReply = verifyEncryptedMessage;
startIdx = ssl->buffers.inputBuffer.idx; /* in case > 1 msg per */
/* in case > 1 msg per record */
ssl->curStartIdx = ssl->buffers.inputBuffer.idx;
FALL_THROUGH;
/* verify digest of encrypted message */
@ -20907,7 +21235,7 @@ default:
/* For TLS v1.1 the block size and explicit IV are added to idx,
* so it needs to be included in this limit check */
if ((ssl->curSize - ssl->keys.padSz -
(ssl->buffers.inputBuffer.idx - startIdx) -
(ssl->buffers.inputBuffer.idx - ssl->curStartIdx) -
MacSize(ssl) > MAX_PLAINTEXT_SZ)
#ifdef WOLFSSL_ASYNC_CRYPT
&& ssl->buffers.inputBuffer.length !=
@ -20929,7 +21257,7 @@ default:
* so it needs to be included in this limit check */
if (!IsAtLeastTLSv1_3(ssl->version)
&& ssl->curSize - ssl->keys.padSz -
(ssl->buffers.inputBuffer.idx - startIdx)
(ssl->buffers.inputBuffer.idx - ssl->curStartIdx)
> MAX_PLAINTEXT_SZ
#ifdef WOLFSSL_ASYNC_CRYPT
&& ssl->buffers.inputBuffer.length !=
@ -21333,7 +21661,8 @@ default:
return ret;
}
/* more messages per record */
else if ((ssl->buffers.inputBuffer.idx - startIdx) < ssl->curSize) {
else if ((ssl->buffers.inputBuffer.idx - ssl->curStartIdx)
< ssl->curSize) {
WOLFSSL_MSG("More messages in record");
ssl->options.processReply = runProcessingOneMessage;

View File

@ -11787,8 +11787,6 @@ int DoTls13HandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
WOLFSSL_ENTER("DoTls13HandShakeMsg");
if (ssl->arrays == NULL) {
if (GetHandshakeHeader(ssl, input, inOutIdx, &type, &size,
totalSz) != 0) {
SendAlert(ssl, alert_fatal, unexpected_message);
@ -11796,6 +11794,12 @@ int DoTls13HandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
return PARSE_ERROR;
}
ret = EarlySanityCheckMsgReceived(ssl, type, size);
if (ret != 0) {
WOLFSSL_ERROR(ret);
return ret;
}
return DoTls13HandShakeMsgType(ssl, input, inOutIdx, type, size,
totalSz);
}
@ -11812,6 +11816,13 @@ int DoTls13HandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
return PARSE_ERROR;
}
ret = EarlySanityCheckMsgReceived(ssl, type,
min(inputLength - HANDSHAKE_HEADER_SZ, size));
if (ret != 0) {
WOLFSSL_ERROR(ret);
return ret;
}
/* Cap the maximum size of a handshake message to something reasonable.
* By default is the maximum size of a certificate message assuming
* nine 2048-bit RSA certificates in the chain. */
@ -11847,6 +11858,14 @@ int DoTls13HandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
inputLength = ssl->arrays->pendingMsgSz -
ssl->arrays->pendingMsgOffset;
}
ret = EarlySanityCheckMsgReceived(ssl, ssl->arrays->pendingMsgType,
inputLength);
if (ret != 0) {
WOLFSSL_ERROR(ret);
return ret;
}
XMEMCPY(ssl->arrays->pendingMsg + ssl->arrays->pendingMsgOffset,
input + *inOutIdx, inputLength);
ssl->arrays->pendingMsgOffset += inputLength;

View File

@ -61806,6 +61806,7 @@ static word32 test_wolfSSL_dtls_stateless_HashWOLFSSL(const WOLFSSL* ssl)
sslCopy.buffers.outputBuffer.offset = 0;
sslCopy.error = 0;
sslCopy.curSize = 0;
sslCopy.curStartIdx = 0;
sslCopy.keys.curSeq_lo = 0;
XMEMSET(&sslCopy.curRL, 0, sizeof(sslCopy.curRL));
#ifdef WOLFSSL_DTLS13
@ -67470,7 +67471,11 @@ static int test_TLSX_CA_NAMES_bad_extension(void)
}
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
#ifndef WOLFSSL_DISABLE_EARLY_SANITY_CHECKS
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), OUT_OF_ORDER_E);
#else
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), BUFFER_ERROR);
#endif
wolfSSL_free(ssl_c);
ssl_c = NULL;

View File

@ -5146,6 +5146,7 @@ typedef struct DtlsMsg {
byte type;
byte fragBucketListCount;
byte ready:1;
byte encrypted:1;
} DtlsMsg;
@ -5475,6 +5476,7 @@ struct WOLFSSL {
word32 timeout; /* session timeout */
word32 fragOffset; /* fragment offset */
word16 curSize;
word32 curStartIdx;
byte verifyDepth;
RecordLayerHeader curRL;
MsgsReceived msgsReceived; /* peer messages received */
@ -6110,7 +6112,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 IsEncryptionOn(const WOLFSSL* ssl, int isSend);
WOLFSSL_LOCAL int TLSv1_3_Capable(WOLFSSL* ssl);
WOLFSSL_LOCAL void FreeHandshakeResources(WOLFSSL* ssl);
@ -6232,6 +6234,9 @@ WOLFSSL_LOCAL int BuildTlsFinished(WOLFSSL* ssl, Hashes* hashes,
WOLFSSL_LOCAL void FreeArrays(WOLFSSL* ssl, int keep);
WOLFSSL_LOCAL int CheckAvailableSize(WOLFSSL *ssl, int size);
WOLFSSL_LOCAL int GrowInputBuffer(WOLFSSL* ssl, int size, int usedLength);
WOLFSSL_LOCAL int MsgCheckEncryption(WOLFSSL* ssl, byte type, byte encrypted);
WOLFSSL_LOCAL int EarlySanityCheckMsgReceived(WOLFSSL* ssl, byte type,
word32 msgSz);
#if !defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH)
WOLFSSL_LOCAL void DoCertFatalAlert(WOLFSSL* ssl, int ret);
#endif
@ -6271,7 +6276,7 @@ WOLFSSL_LOCAL int cipherExtraData(WOLFSSL* ssl);
WOLFSSL_LOCAL int DtlsMsgSet(DtlsMsg* msg, word32 seq, word16 epoch,
const byte* data, byte type,
word32 fragOffset, word32 fragSz, void* heap,
word32 totalLen);
word32 totalLen, byte encrypted);
/* Use WOLFSSL_API to enable src/api.c testing */
WOLFSSL_API DtlsMsg* DtlsMsgFind(DtlsMsg* head, word16 epoch, word32 seq);