Skip to content

Commit ed5155d

Browse files
committed
Add refcounts on key, keyMask, altKey, altKeyMask to prevent UAF
1 parent 444594e commit ed5155d

6 files changed

Lines changed: 127 additions & 47 deletions

File tree

src/internal.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7194,6 +7194,9 @@ static int SetSSL_CTX_CertsAndKeys(WOLFSSL* ssl, WOLFSSL_CTX* ctx)
71947194
}
71957195
#else
71967196
ssl->buffers.key = ctx->privateKey;
7197+
/* Hold a reference on the shared key so reloading it on the CTX cannot
7198+
* free it while this SSL still aliases it. */
7199+
RefDer(ssl->buffers.key);
71977200
#endif
71987201
#else
71997202
if (ctx->privateKey != NULL) {
@@ -7224,6 +7227,8 @@ static int SetSSL_CTX_CertsAndKeys(WOLFSSL* ssl, WOLFSSL_CTX* ctx)
72247227
#ifdef WOLFSSL_DUAL_ALG_CERTS
72257228
#ifndef WOLFSSL_BLIND_PRIVATE_KEY
72267229
ssl->buffers.altKey = ctx->altPrivateKey;
7230+
/* Hold a reference on the shared alternative key. */
7231+
RefDer(ssl->buffers.altKey);
72277232
#else
72287233
if (ctx->altPrivateKey != NULL) {
72297234
ret = AllocCopyDer(&ssl->buffers.altKey, ctx->altPrivateKey->buffer,
@@ -9211,12 +9216,25 @@ void wolfSSL_ResourceFree(WOLFSSL* ssl)
92119216
#ifndef NO_CERTS
92129217
ssl->keepCert = 0; /* make sure certificate is free'd */
92139218
wolfSSL_UnloadCertsKeys(ssl);
9214-
/* Release references on cert buffers aliased from the context. Owned
9215-
* buffers were already freed and cleared by wolfSSL_UnloadCertsKeys. */
9219+
/* Release references on cert and key buffers aliased from the context.
9220+
* Owned buffers were already freed and cleared by
9221+
* wolfSSL_UnloadCertsKeys. */
92169222
FreeDer(&ssl->buffers.certificate);
92179223
ssl->buffers.weOwnCert = 0;
92189224
FreeDer(&ssl->buffers.certChain);
92199225
ssl->buffers.weOwnCertChain = 0;
9226+
FreeDer(&ssl->buffers.key);
9227+
ssl->buffers.weOwnKey = 0;
9228+
#ifdef WOLFSSL_BLIND_PRIVATE_KEY
9229+
FreeDer(&ssl->buffers.keyMask);
9230+
#endif
9231+
#ifdef WOLFSSL_DUAL_ALG_CERTS
9232+
FreeDer(&ssl->buffers.altKey);
9233+
ssl->buffers.weOwnAltKey = 0;
9234+
#ifdef WOLFSSL_BLIND_PRIVATE_KEY
9235+
FreeDer(&ssl->buffers.altKeyMask);
9236+
#endif
9237+
#endif /* WOLFSSL_DUAL_ALG_CERTS */
92209238
#endif
92219239
#ifndef NO_RSA
92229240
FreeKey(ssl, DYNAMIC_TYPE_RSA, (void**)&ssl->peerRsaKey);

src/ssl.c

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12711,33 +12711,23 @@ void wolfSSL_certs_clear(WOLFSSL* ssl)
1271112711
#ifdef WOLFSSL_TLS13
1271212712
ssl->buffers.certChainCnt = 0;
1271312713
#endif
12714-
if (ssl->buffers.weOwnKey) {
12715-
FreeDer(&ssl->buffers.key);
12716-
#ifdef WOLFSSL_BLIND_PRIVATE_KEY
12717-
FreeDer(&ssl->buffers.keyMask);
12718-
#endif
12719-
ssl->buffers.weOwnKey = 0;
12720-
}
12721-
ssl->buffers.key = NULL;
12714+
/* Release our reference to the key (owned copy or CTX alias). */
12715+
FreeDer(&ssl->buffers.key);
12716+
ssl->buffers.weOwnKey = 0;
1272212717
#ifdef WOLFSSL_BLIND_PRIVATE_KEY
12723-
ssl->buffers.keyMask = NULL;
12718+
FreeDer(&ssl->buffers.keyMask);
1272412719
#endif
1272512720
ssl->buffers.keyType = 0;
1272612721
ssl->buffers.keyId = 0;
1272712722
ssl->buffers.keyLabel = 0;
1272812723
ssl->buffers.keySz = 0;
1272912724
ssl->buffers.keyDevId = 0;
1273012725
#ifdef WOLFSSL_DUAL_ALG_CERTS
12731-
if (ssl->buffers.weOwnAltKey) {
12732-
FreeDer(&ssl->buffers.altKey);
12733-
#ifdef WOLFSSL_BLIND_PRIVATE_KEY
12734-
FreeDer(&ssl->buffers.altKeyMask);
12735-
#endif
12736-
ssl->buffers.weOwnAltKey = 0;
12737-
}
12738-
ssl->buffers.altKey = NULL;
12726+
/* Release our reference to the alternative key (owned copy or CTX alias). */
12727+
FreeDer(&ssl->buffers.altKey);
12728+
ssl->buffers.weOwnAltKey = 0;
1273912729
#ifdef WOLFSSL_BLIND_PRIVATE_KEY
12740-
ssl->buffers.altKeyMask = NULL;
12730+
FreeDer(&ssl->buffers.altKeyMask);
1274112731
#endif
1274212732
#endif /* WOLFSSL_DUAL_ALG_CERTS */
1274312733
}
@@ -13201,7 +13191,11 @@ WOLFSSL_CTX* wolfSSL_set_SSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx)
1320113191
ssl->buffers.key = ctx->privateKey;
1320213192
}
1320313193
#else
13194+
/* Release our previous key reference before aliasing the new CTX's. */
13195+
FreeDer(&ssl->buffers.key);
13196+
ssl->buffers.weOwnKey = 0;
1320413197
ssl->buffers.key = ctx->privateKey;
13198+
RefDer(ssl->buffers.key);
1320513199
#endif
1320613200
#else
1320713201
if (ctx->privateKey != NULL) {
@@ -13238,7 +13232,11 @@ WOLFSSL_CTX* wolfSSL_set_SSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx)
1323813232
ssl->options.haveMlDsaSig = ctx->haveMlDsaSig;
1323913233
#ifdef WOLFSSL_DUAL_ALG_CERTS
1324013234
#ifndef WOLFSSL_BLIND_PRIVATE_KEY
13235+
/* Release our previous alt key reference before aliasing the new CTX's. */
13236+
FreeDer(&ssl->buffers.altKey);
13237+
ssl->buffers.weOwnAltKey = 0;
1324113238
ssl->buffers.altKey = ctx->altPrivateKey;
13239+
RefDer(ssl->buffers.altKey);
1324213240
#else
1324313241
if (ctx->altPrivateKey != NULL) {
1324413242
ret = AllocCopyDer(&ssl->buffers.altKey, ctx->altPrivateKey->buffer,

src/ssl_load.c

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,13 +1317,12 @@ static int ProcessBufferPrivKeyHandleDer(WOLFSSL_CTX* ctx, WOLFSSL* ssl,
13171317
else
13181318
#endif /* WOLFSSL_DUAL_ALG_CERTS */
13191319
if (ssl != NULL) {
1320-
/* Dispose of previous key if not context's. */
1321-
if (ssl->buffers.weOwnKey) {
1322-
FreeDer(&ssl->buffers.key);
1323-
#ifdef WOLFSSL_BLIND_PRIVATE_KEY
1324-
FreeDer(&ssl->buffers.keyMask);
1325-
#endif
1326-
}
1320+
/* Release our reference to the previous key (owned copy or CTX
1321+
* alias) before storing the new one. */
1322+
FreeDer(&ssl->buffers.key);
1323+
#ifdef WOLFSSL_BLIND_PRIVATE_KEY
1324+
FreeDer(&ssl->buffers.keyMask);
1325+
#endif
13271326
ssl->buffers.keyId = 0;
13281327
ssl->buffers.keyLabel = 0;
13291328
ssl->buffers.keyDevId = INVALID_DEVID;
@@ -4621,12 +4620,11 @@ int wolfSSL_use_PrivateKey_Id(WOLFSSL* ssl, const unsigned char* id,
46214620
}
46224621

46234622
/* Dispose of old private key if owned and allocate and copy in id. */
4624-
if (ssl->buffers.weOwnKey) {
4625-
FreeDer(&ssl->buffers.key);
4626-
#ifdef WOLFSSL_BLIND_PRIVATE_KEY
4627-
FreeDer(&ssl->buffers.keyMask);
4628-
#endif
4629-
}
4623+
/* Release our reference to the old key (owned copy or CTX alias). */
4624+
FreeDer(&ssl->buffers.key);
4625+
#ifdef WOLFSSL_BLIND_PRIVATE_KEY
4626+
FreeDer(&ssl->buffers.keyMask);
4627+
#endif
46304628
if (AllocCopyDer(&ssl->buffers.key, id, (word32)sz, PRIVATEKEY_TYPE,
46314629
ssl->heap) != 0) {
46324630
ret = 0;
@@ -4697,12 +4695,11 @@ int wolfSSL_use_PrivateKey_Label(WOLFSSL* ssl, const char* label, int devId)
46974695
sz = (word32)XSTRLEN(label) + 1;
46984696

46994697
/* Dispose of old private key if owned and allocate and copy in label. */
4700-
if (ssl->buffers.weOwnKey) {
4701-
FreeDer(&ssl->buffers.key);
4702-
#ifdef WOLFSSL_BLIND_PRIVATE_KEY
4703-
FreeDer(&ssl->buffers.keyMask);
4704-
#endif
4705-
}
4698+
/* Release our reference to the old key (owned copy or CTX alias). */
4699+
FreeDer(&ssl->buffers.key);
4700+
#ifdef WOLFSSL_BLIND_PRIVATE_KEY
4701+
FreeDer(&ssl->buffers.keyMask);
4702+
#endif
47064703
if (AllocCopyDer(&ssl->buffers.key, (const byte*)label, (word32)sz,
47074704
PRIVATEKEY_TYPE, ssl->heap) != 0) {
47084705
ret = 0;

src/tls13.c

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10142,20 +10142,22 @@ static int SendTls13CertificateVerify(WOLFSSL* ssl)
1014210142
}
1014310143
ssl->buffers.keyType = ssl->buffers.altKeyType;
1014410144
ssl->buffers.keySz = ssl->buffers.altKeySz;
10145-
/* If we own it, free key before overriding it. */
10146-
if (ssl->buffers.weOwnKey) {
10147-
FreeDer(&ssl->buffers.key);
10148-
#ifdef WOLFSSL_BLIND_PRIVATE_KEY
10149-
FreeDer(&ssl->buffers.keyMask);
10150-
#endif
10151-
}
10145+
/* Release our reference to the current key before
10146+
* overriding it (owned copy or CTX alias). */
10147+
FreeDer(&ssl->buffers.key);
10148+
#ifdef WOLFSSL_BLIND_PRIVATE_KEY
10149+
FreeDer(&ssl->buffers.keyMask);
10150+
#endif
1015210151

10153-
/* Swap keys */
10152+
/* Swap keys. key and altKey now reference the same buffer,
10153+
* so take a reference for the new alias. */
1015410154
ssl->buffers.key = ssl->buffers.altKey;
1015510155
ssl->buffers.weOwnKey = ssl->buffers.weOwnAltKey;
10156+
RefDer(ssl->buffers.key);
1015610157

1015710158
#ifdef WOLFSSL_BLIND_PRIVATE_KEY
1015810159
ssl->buffers.keyMask = ssl->buffers.altKeyMask;
10160+
RefDer(ssl->buffers.keyMask);
1015910161
/* Unblind the alternative key before decoding */
1016010162
wolfssl_priv_der_blind_toggle(ssl->buffers.key, ssl->buffers.keyMask);
1016110163
#endif

tests/api/test_tls13.c

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3687,6 +3687,69 @@ int test_tls13_cert_alias_uaf_sni(void)
36873687
}
36883688

36893689

3690+
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
3691+
defined(HAVE_SNI) && !defined(WOLFSSL_COPY_KEY) && \
3692+
!defined(WOLFSSL_BLIND_PRIVATE_KEY) && \
3693+
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
3694+
!defined(NO_RSA) && !defined(NO_CERTS)
3695+
/* React to the client SNI by reloading the private key on the existing CTX.
3696+
* Without WOLFSSL_COPY_KEY the key buffer is shared between the CTX and SSL,
3697+
* so freeing it on the CTX would dangle the SSL's alias. The server signs its
3698+
* CertificateVerify with that key, so a dangling alias is a heap-use-after-free
3699+
* on the signing path; the handshake must still complete. */
3700+
static int test_key_alias_sni_cb(WOLFSSL* ssl, int* ad, void* arg)
3701+
{
3702+
WOLFSSL_CTX* ctx = wolfSSL_get_SSL_CTX(ssl);
3703+
(void)ad;
3704+
(void)arg;
3705+
(void)wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
3706+
WOLFSSL_FILETYPE_PEM);
3707+
/* 0 means ack the servername and continue the handshake. */
3708+
return 0;
3709+
}
3710+
#endif
3711+
3712+
int test_tls13_key_alias_uaf_sni(void)
3713+
{
3714+
EXPECT_DECLS;
3715+
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
3716+
defined(HAVE_SNI) && !defined(WOLFSSL_COPY_KEY) && \
3717+
!defined(WOLFSSL_BLIND_PRIVATE_KEY) && \
3718+
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
3719+
!defined(NO_RSA) && !defined(NO_CERTS)
3720+
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
3721+
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
3722+
struct test_memio_ctx test_ctx;
3723+
const char* host = "example.com";
3724+
3725+
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
3726+
/* Server cert/key are loaded on ctx_s, so ssl_s->buffers.key aliases
3727+
* ctx_s->privateKey. */
3728+
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
3729+
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
3730+
3731+
/* Server swaps its CTX private key when the SNI arrives. */
3732+
wolfSSL_CTX_set_servername_callback(ctx_s, test_key_alias_sni_cb);
3733+
3734+
/* Client offers SNI so the server callback fires while parsing the
3735+
* ClientHello. */
3736+
ExpectIntEQ(wolfSSL_UseSNI(ssl_c, WOLFSSL_SNI_HOST_NAME, host,
3737+
(word16)XSTRLEN(host)), WOLFSSL_SUCCESS);
3738+
3739+
/* The callback frees the aliased key mid-handshake; the server then signs
3740+
* CertificateVerify with ssl->buffers.key. The handshake must complete
3741+
* without a UAF. */
3742+
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
3743+
3744+
wolfSSL_free(ssl_c);
3745+
wolfSSL_free(ssl_s);
3746+
wolfSSL_CTX_free(ctx_c);
3747+
wolfSSL_CTX_free(ctx_s);
3748+
#endif
3749+
return EXPECT_RESULT();
3750+
}
3751+
3752+
36903753
#if defined(HAVE_IO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
36913754
defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \
36923755
!defined(WOLFSSL_MLKEM_NO_DECAPSULATE) && \

tests/api/test_tls13.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ int test_tls13_rpk_handshake(void);
3131
int test_tls13_rpk_handshake_no_negotiation(void);
3232
int test_tls13_pha(void);
3333
int test_tls13_cert_alias_uaf_sni(void);
34+
int test_tls13_key_alias_uaf_sni(void);
3435
int test_tls13_pq_groups(void);
3536
int test_tls13_multi_pqc_key_share(void);
3637
int test_tls13_early_data(void);
@@ -109,6 +110,7 @@ int test_tls13_AEAD_limit_KU_aes128_ccm_8_sha256(void);
109110
TEST_DECL_GROUP("tls13", test_tls13_rpk_handshake_no_negotiation), \
110111
TEST_DECL_GROUP("tls13", test_tls13_pha), \
111112
TEST_DECL_GROUP("tls13", test_tls13_cert_alias_uaf_sni), \
113+
TEST_DECL_GROUP("tls13", test_tls13_key_alias_uaf_sni), \
112114
TEST_DECL_GROUP("tls13", test_tls13_pq_groups), \
113115
TEST_DECL_GROUP("tls13", test_tls13_multi_pqc_key_share), \
114116
TEST_DECL_GROUP("tls13", test_tls13_early_data), \

0 commit comments

Comments
 (0)