Skip to content

Commit 713a220

Browse files
authored
Merge pull request #10426 from JeremiahM37/fenrir-8
protocol correctness, OpenSSL-compat hardening, and sensitive-memory zeroization
2 parents 971d2b0 + a3baac7 commit 713a220

12 files changed

Lines changed: 235 additions & 22 deletions

File tree

src/dtls13.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2393,7 +2393,10 @@ static Dtls13Epoch* Dtls13NewEpochSlot(WOLFSSL* ssl)
23932393
WOLFSSL_MSG_EX("Delete epoch: %d", e->epochNumber);
23942394
#endif /* WOLFSSL_DEBUG_TLS */
23952395

2396-
XMEMSET(e, 0, sizeof(*e));
2396+
/* The slot we are reusing holds the previous epoch's symmetric keys, IVs,
2397+
* and sn-keys; use ForceZero so the wipe cannot be elided by the
2398+
* optimizer when the slot is later overwritten. */
2399+
ForceZero(e, sizeof(*e));
23972400

23982401
return e;
23992402
}
@@ -3058,11 +3061,17 @@ int SendDtls13Ack(WOLFSSL* ssl)
30583061
static int Dtls13RtxRecordMatchesReqCtx(Dtls13RtxRecord* r, byte* ctx,
30593062
byte ctxLen)
30603063
{
3064+
/* r->data points at the 12-byte Dtls13HandshakeHeader (set by
3065+
* Dtls13RtxNewRecord from message + recordHeaderLength); the
3066+
* CertificateRequest body starts at r->data[DTLS13_HANDSHAKE_HEADER_SZ]
3067+
* with a 1-byte request_context length followed by ctxLength bytes. */
30613068
if (r->handshakeType != certificate_request)
30623069
return 0;
3063-
if (r->length <= ctxLen + 1)
3070+
if (r->length < (word16)(DTLS13_HANDSHAKE_HEADER_SZ + 1 + ctxLen))
30643071
return 0;
3065-
return XMEMCMP(ctx, r->data + 1, ctxLen) == 0;
3072+
if (r->data[DTLS13_HANDSHAKE_HEADER_SZ] != ctxLen)
3073+
return 0;
3074+
return XMEMCMP(ctx, r->data + DTLS13_HANDSHAKE_HEADER_SZ + 1, ctxLen) == 0;
30663075
}
30673076

30683077
int Dtls13RtxProcessingCertificate(WOLFSSL* ssl, byte* input, word32 inputSize)

src/internal.c

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9193,6 +9193,11 @@ void wolfSSL_ResourceFree(WOLFSSL* ssl)
91939193
#ifdef WOLFSSL_DTLS13
91949194
Dtls13FreeFsmResources(ssl);
91959195

9196+
/* Zero per-epoch symmetric keys / IVs / sn-keys so they are not left
9197+
* resident in the heap after FreeSSL releases the SSL struct. Mirrors
9198+
* the existing ForceZero on ssl->keys and ssl->clientSecret/serverSecret. */
9199+
ForceZero(ssl->dtls13Epochs, sizeof(ssl->dtls13Epochs));
9200+
91969201
#ifdef WOLFSSL_RW_THREADED
91979202
wc_FreeMutex(&ssl->dtls13Rtx.mutex);
91989203
#endif
@@ -19657,15 +19662,21 @@ static int Dtls13UpdateWindow(WOLFSSL* ssl)
1965719662
/* zero based index */
1965819663
w64Decrement(&diff64);
1965919664

19660-
/* FIXME: check that diff64 < DTLS_WORDS_BITS */
19661-
diff = w64GetLow32(diff64);
19662-
wordIndex = ((int)diff) / DTLS_WORD_BITS;
19663-
wordOffset = ((int)diff) % DTLS_WORD_BITS;
19665+
/* If the high 32 bits are non-zero, the gap is >= 2^32 which is far
19666+
* beyond the replay window; truncating via w64GetLow32 would set the
19667+
* wrong bit. Reject such packets as out-of-window. */
19668+
if (w64GetHigh32(diff64) != 0) {
19669+
WOLFSSL_MSG("Invalid sequence number to Dtls13UpdateWindow");
19670+
return BAD_STATE_E;
19671+
}
1966419672

19665-
if (wordIndex >= WOLFSSL_DTLS_WINDOW_WORDS) {
19673+
diff = w64GetLow32(diff64);
19674+
if (diff >= (word32)(WOLFSSL_DTLS_WINDOW_WORDS * DTLS_WORD_BITS)) {
1966619675
WOLFSSL_MSG("Invalid sequence number to Dtls13UpdateWindow");
1966719676
return BAD_STATE_E;
1966819677
}
19678+
wordIndex = (int)(diff / DTLS_WORD_BITS);
19679+
wordOffset = (int)(diff % DTLS_WORD_BITS);
1966919680

1967019681
window[wordIndex] |= (1 << wordOffset);
1967119682
return 0;
@@ -19676,6 +19687,13 @@ static int Dtls13UpdateWindow(WOLFSSL* ssl)
1967619687

1967719688
/* as we are considering nextSeq inside the window, we should add + 1 */
1967819689
w64Increment(&diff64);
19690+
/* Same truncation hazard as the seq < nextSeq branch above: if the high
19691+
* 32 bits are non-zero the gap is >= 2^32, beyond anything the window
19692+
* can represent. Reject as out-of-window before truncating. */
19693+
if (w64GetHigh32(diff64) != 0) {
19694+
WOLFSSL_MSG("Invalid sequence number to Dtls13UpdateWindow");
19695+
return BAD_STATE_E;
19696+
}
1967919697
_DtlsUpdateWindowGTSeq(w64GetLow32(diff64), window);
1968019698

1968119699
w64Increment(&seq);
@@ -27381,7 +27399,7 @@ static const char* wolfSSL_ERR_reason_error_string_OpenSSL(unsigned long e)
2738127399
return "certificate has expired";
2738227400

2738327401
case WOLFSSL_X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
27384-
return "certificate signature failure";
27402+
return "format error in certificate's notBefore field";
2738527403

2738627404
case WOLFSSL_X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
2738727405
return "format error in certificate's notAfter field";

src/keys.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4138,6 +4138,8 @@ static int MakeSslMasterSecret(WOLFSSL* ssl)
41384138
ENCRYPT_LEN + WC_SHA_DIGEST_SIZE);
41394139
wc_MemZero_Add("MakeSslMasterSecret shaInput", shaInput,
41404140
PREFIX + ENCRYPT_LEN + 2 * RAN_LEN);
4141+
wc_MemZero_Add("MakeSslMasterSecret shaOutput", shaOutput,
4142+
WC_SHA_DIGEST_SIZE);
41414143
#endif
41424144

41434145
XMEMSET(shaOutput, 0, WC_SHA_DIGEST_SIZE);
@@ -4200,9 +4202,11 @@ static int MakeSslMasterSecret(WOLFSSL* ssl)
42004202

42014203
ForceZero(md5Input, ENCRYPT_LEN + WC_SHA_DIGEST_SIZE);
42024204
ForceZero(shaInput, PREFIX + ENCRYPT_LEN + 2 * RAN_LEN);
4205+
ForceZero(shaOutput, WC_SHA_DIGEST_SIZE);
42034206
#ifdef WOLFSSL_CHECK_MEM_ZERO
42044207
wc_MemZero_Check(md5Input, ENCRYPT_LEN + WC_SHA_DIGEST_SIZE);
42054208
wc_MemZero_Check(shaInput, PREFIX + ENCRYPT_LEN + 2 * RAN_LEN);
4209+
wc_MemZero_Check(shaOutput, WC_SHA_DIGEST_SIZE);
42064210
#endif
42074211

42084212
WC_FREE_VAR_EX(shaOutput, NULL, DYNAMIC_TYPE_TMP_BUFFER);

src/sniffer.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7591,11 +7591,15 @@ static int parseKeyLogFile(const char* fileName, char* error)
75917591

75927592
if (ret != 0) {
75937593
fclose(file);
7594+
ForceZero(secret, SECRET_LENGTH);
7595+
ForceZero(secretHex, sizeof(secretHex));
75947596
return ret;
75957597
}
75967598
}
75977599
fclose(file);
75987600

7601+
ForceZero(secret, SECRET_LENGTH);
7602+
ForceZero(secretHex, sizeof(secretHex));
75997603
return 0;
76007604
}
76017605

@@ -7613,6 +7617,7 @@ static void freeSecretList(void)
76137617

76147618
while (current != NULL) {
76157619
next = current->next;
7620+
ForceZero(current, sizeof(SecretNode));
76167621
XFREE(current, NULL, DYNAMIC_TYPE_SNIFFER_KEYLOG_NODE);
76177622
current = next;
76187623
}

src/ssl.c

Lines changed: 124 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20219,19 +20219,45 @@ int wolfSSL_RAND_poll(void)
2021920219
int wolfSSL_RAND_status(void)
2022020220
{
2022120221
int ret = WOLFSSL_SUCCESS;
20222+
int useGlobalRng = 1;
2022220223
#ifndef WOLFSSL_NO_OPENSSL_RAND_CB
2022320224
if (wolfSSL_RAND_InitMutex() == 0 &&
2022420225
wc_LockMutex(&gRandMethodMutex) == 0) {
20225-
if (gRandMethods && gRandMethods->status)
20226+
if (gRandMethods && gRandMethods->status) {
2022620227
ret = gRandMethods->status();
20228+
useGlobalRng = 0;
20229+
}
2022720230
wc_UnLockMutex(&gRandMethodMutex);
2022820231
}
2022920232
else {
2023020233
ret = WOLFSSL_FAILURE;
20234+
useGlobalRng = 0;
20235+
}
20236+
#endif
20237+
20238+
/* Drive the global RNG so init / DRBG state failures (mutex
20239+
* acquisition, reseed required, corrupted state) surface to the
20240+
* caller. DRBG output is deterministic between reseeds, so this
20241+
* does not directly probe the entropy source. */
20242+
#ifdef HAVE_GLOBAL_RNG
20243+
if (useGlobalRng) {
20244+
if (wolfSSL_RAND_Init() != WOLFSSL_SUCCESS) {
20245+
ret = WOLFSSL_FAILURE;
20246+
}
20247+
else if (wc_LockMutex(&globalRNGMutex) != 0) {
20248+
ret = WOLFSSL_FAILURE;
20249+
}
20250+
else {
20251+
byte b = 0;
20252+
int genRet = wc_RNG_GenerateBlock(&globalRNG, &b, 1);
20253+
wc_UnLockMutex(&globalRNGMutex);
20254+
ForceZero(&b, 1);
20255+
if (genRet != 0)
20256+
ret = WOLFSSL_FAILURE;
20257+
}
2023120258
}
20232-
#else
20233-
/* wolfCrypt provides enough seed internally, so return success */
2023420259
#endif
20260+
(void)useGlobalRng;
2023520261
return ret;
2023620262
}
2023720263

@@ -20263,14 +20289,106 @@ void wolfSSL_RAND_screen(void)
2026320289
}
2026420290
#endif
2026520291

20292+
#ifndef WOLFSSL_RAND_LOAD_FILE_BUF_SZ
20293+
#define WOLFSSL_RAND_LOAD_FILE_BUF_SZ 256
20294+
#endif
20295+
#ifndef WOLFSSL_RAND_LOAD_FILE_MAX_BYTES
20296+
#define WOLFSSL_RAND_LOAD_FILE_MAX_BYTES (1L << 20)
20297+
#endif
20298+
2026620299
int wolfSSL_RAND_load_file(const char* fname, long len)
2026720300
{
20301+
#if !defined(NO_FILESYSTEM) && defined(HAVE_HASHDRBG)
20302+
XFILE f;
20303+
long maxBytes;
20304+
long readSoFar = 0;
20305+
int ret = 0;
20306+
#ifndef WOLFSSL_SMALL_STACK
20307+
unsigned char buf[WOLFSSL_RAND_LOAD_FILE_BUF_SZ];
20308+
#else
20309+
unsigned char* buf;
20310+
#endif
20311+
20312+
WOLFSSL_ENTER("wolfSSL_RAND_load_file");
20313+
20314+
if (fname == NULL)
20315+
return WOLFSSL_FATAL_ERROR;
20316+
20317+
/* OpenSSL semantics: RAND_load_file(file, -1) reads up to an
20318+
* implementation-defined maximum. WOLFSSL_RAND_LOAD_FILE_MAX_BYTES
20319+
* caps the read so callers passing -1 to ingest a seed file aren't
20320+
* silently truncated at a small default. */
20321+
maxBytes = (len < 0) ? WOLFSSL_RAND_LOAD_FILE_MAX_BYTES : len;
20322+
if (maxBytes == 0)
20323+
return 0;
20324+
20325+
f = XFOPEN(fname, "rb");
20326+
if (f == XBADFILE) {
20327+
WOLFSSL_MSG("RAND_load_file: cannot open file");
20328+
return WOLFSSL_FATAL_ERROR;
20329+
}
20330+
20331+
#ifdef WOLFSSL_SMALL_STACK
20332+
buf = (unsigned char*)XMALLOC(WOLFSSL_RAND_LOAD_FILE_BUF_SZ, NULL,
20333+
DYNAMIC_TYPE_TMP_BUFFER);
20334+
if (buf == NULL) {
20335+
XFCLOSE(f);
20336+
return WOLFSSL_FATAL_ERROR;
20337+
}
20338+
#endif
20339+
#ifdef WOLFSSL_CHECK_MEM_ZERO
20340+
wc_MemZero_Add("wolfSSL_RAND_load_file buf", buf,
20341+
WOLFSSL_RAND_LOAD_FILE_BUF_SZ);
20342+
#endif
20343+
20344+
if (initGlobalRNG == 0 && wolfSSL_RAND_Init() != WOLFSSL_SUCCESS) {
20345+
WOLFSSL_MSG("RAND_load_file: global RNG not available");
20346+
ret = WOLFSSL_FATAL_ERROR;
20347+
goto cleanup;
20348+
}
20349+
20350+
while (readSoFar < maxBytes) {
20351+
size_t toRead = (size_t)((maxBytes - readSoFar) <
20352+
WOLFSSL_RAND_LOAD_FILE_BUF_SZ
20353+
? (maxBytes - readSoFar) : WOLFSSL_RAND_LOAD_FILE_BUF_SZ);
20354+
size_t n = XFREAD(buf, 1, toRead, f);
20355+
if (n == 0)
20356+
break;
20357+
if (wc_LockMutex(&globalRNGMutex) != 0) {
20358+
ret = WOLFSSL_FATAL_ERROR;
20359+
break;
20360+
}
20361+
if (wc_RNG_DRBG_Reseed(&globalRNG, buf, (word32)n) != 0) {
20362+
wc_UnLockMutex(&globalRNGMutex);
20363+
WOLFSSL_MSG("RAND_load_file: DRBG reseed failed");
20364+
ret = WOLFSSL_FATAL_ERROR;
20365+
break;
20366+
}
20367+
wc_UnLockMutex(&globalRNGMutex);
20368+
readSoFar += (long)n;
20369+
}
20370+
20371+
cleanup:
20372+
XFCLOSE(f);
20373+
ForceZero(buf, WOLFSSL_RAND_LOAD_FILE_BUF_SZ);
20374+
#ifdef WOLFSSL_SMALL_STACK
20375+
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
20376+
#elif defined(WOLFSSL_CHECK_MEM_ZERO)
20377+
wc_MemZero_Check(buf, WOLFSSL_RAND_LOAD_FILE_BUF_SZ);
20378+
#endif
20379+
20380+
if (ret < 0)
20381+
return WOLFSSL_FATAL_ERROR;
20382+
return (int)readSoFar;
20383+
#else
20384+
/* Without HAVE_HASHDRBG / filesystem support there is no way to feed
20385+
* external entropy to the wolfCrypt RNG; return success so callers
20386+
* in those configurations are not broken. */
2026820387
(void)fname;
20269-
/* wolfCrypt provides enough entropy internally or will report error */
2027020388
if (len == -1)
2027120389
return 1024;
20272-
else
20273-
return (int)len;
20390+
return (int)len;
20391+
#endif
2027420392
}
2027520393

2027620394
#endif /* OPENSSL_EXTRA */

src/ssl_crypto.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2659,7 +2659,7 @@ void wolfSSL_DES_cbc_encrypt(const unsigned char* input, unsigned char* output,
26592659
WOLFSSL_ENTER("wolfSSL_DES_cbc_encrypt");
26602660

26612661
#ifdef WOLFSSL_SMALL_STACK
2662-
des = (Des*)XMALLOC(sizeof(Des3), NULL, DYNAMIC_TYPE_CIPHER);
2662+
des = (Des*)XMALLOC(sizeof(Des), NULL, DYNAMIC_TYPE_CIPHER);
26632663
if (des == NULL) {
26642664
WOLFSSL_MSG("Failed to allocate memory for Des object");
26652665
}

src/ssl_p7p12.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -988,7 +988,7 @@ int wolfSSL_PEM_write_bio_PKCS7(WOLFSSL_BIO* bio, PKCS7* p7)
988988
outputHead = (byte*)XMALLOC(outputHeadSz, bio->heap,
989989
DYNAMIC_TYPE_TMP_BUFFER);
990990
if (outputHead == NULL)
991-
return MEMORY_E;
991+
return WOLFSSL_FAILURE;
992992

993993
outputFoot = (byte*)XMALLOC(outputFootSz, bio->heap,
994994
DYNAMIC_TYPE_TMP_BUFFER);

src/tls13.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,17 +1051,22 @@ int Tls13_Exporter(WOLFSSL* ssl, unsigned char *out, size_t outLen,
10511051
protocol, protocolLen, (byte*)label, (word32)labelLen,
10521052
emptyHash, hashLen, (int)hashType);
10531053
if (ret != 0)
1054-
return ret;
1054+
goto cleanup;
10551055

10561056
/* Hash(context_value) */
10571057
ret = wc_Hash(hashType, context, (word32)contextLen, hashOut, WC_MAX_DIGEST_SIZE);
10581058
if (ret != 0)
1059-
return ret;
1059+
goto cleanup;
10601060

10611061
ret = Tls13HKDFExpandLabel(ssl, out, (word32)outLen, firstExpand, hashLen,
10621062
protocol, protocolLen, exporterLabel, EXPORTER_LABEL_SZ,
10631063
hashOut, hashLen, (int)hashType);
10641064

1065+
cleanup:
1066+
/* firstExpand is the per-label Derive-Secret PRK and hashOut holds
1067+
* Hash(context_value); wipe both before the stack frame is reclaimed. */
1068+
ForceZero(firstExpand, sizeof(firstExpand));
1069+
ForceZero(hashOut, sizeof(hashOut));
10651070
return ret;
10661071
}
10671072
#endif
@@ -6093,8 +6098,13 @@ static int DoTls13CertificateRequest(WOLFSSL* ssl, const byte* input,
60936098
len = input[(*inOutIdx)++];
60946099
if ((*inOutIdx - begin) + len > size)
60956100
return BUFFER_ERROR;
6096-
if (ssl->options.connectState < FINISHED_DONE && len > 0)
6097-
return BUFFER_ERROR;
6101+
/* INVALID_PARAMETER does not map to illegal_parameter in the central
6102+
* alert path, so emit the alert explicitly before returning. */
6103+
if (ssl->options.connectState < FINISHED_DONE && len > 0) {
6104+
SendAlert(ssl, alert_fatal, illegal_parameter);
6105+
WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER);
6106+
return INVALID_PARAMETER;
6107+
}
60986108

60996109
#ifdef WOLFSSL_POST_HANDSHAKE_AUTH
61006110
/* Remember the request context bytes; the CertReqCtx allocation and

tests/api/test_certman.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3519,3 +3519,19 @@ int test_wolfSSL_CertManagerRejectMD5Cert(void)
35193519
#endif
35203520
return EXPECT_RESULT();
35213521
}
3522+
3523+
int test_wolfSSL_X509_V_ERR_strings(void)
3524+
{
3525+
EXPECT_DECLS;
3526+
#if !defined(NO_ERROR_STRINGS) && (defined(OPENSSL_EXTRA) || \
3527+
defined(OPENSSL_EXTRA_X509_SMALL) || \
3528+
defined(HAVE_WEBSERVER) || defined(HAVE_MEMCACHED))
3529+
ExpectStrEQ(wolfSSL_ERR_reason_error_string(
3530+
WOLFSSL_X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD),
3531+
"format error in certificate's notBefore field");
3532+
ExpectStrEQ(wolfSSL_ERR_reason_error_string(
3533+
WOLFSSL_X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD),
3534+
"format error in certificate's notAfter field");
3535+
#endif
3536+
return EXPECT_RESULT();
3537+
}

tests/api/test_certman.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ int test_wolfSSL_CRL_unknown_critical_entry_ext(void);
5050
int test_wolfSSL_CertManagerCheckOCSPResponse(void);
5151
int test_various_pathlen_chains(void);
5252
int test_wolfSSL_CertManagerRejectMD5Cert(void);
53+
int test_wolfSSL_X509_V_ERR_strings(void);
5354

5455
#define TEST_CERTMAN_DECLS \
5556
TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerAPI), \
@@ -78,7 +79,8 @@ int test_wolfSSL_CertManagerRejectMD5Cert(void);
7879
TEST_DECL_GROUP("certman", test_wolfSSL_CRL_unknown_critical_entry_ext), \
7980
TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerCheckOCSPResponse), \
8081
TEST_DECL_GROUP("certman", test_various_pathlen_chains), \
81-
TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerRejectMD5Cert)
82+
TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerRejectMD5Cert), \
83+
TEST_DECL_GROUP("certman", test_wolfSSL_X509_V_ERR_strings)
8284

8385
#endif /* WOLFCRYPT_TEST_CERTMAN_H */
8486

0 commit comments

Comments
 (0)