Skip to content

Commit aec9b20

Browse files
Merge pull request #10702 from Frauschi/zd21992
Various fixes
2 parents 9d60981 + e6f02ec commit aec9b20

22 files changed

Lines changed: 657 additions & 47 deletions

src/bio.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,16 @@ static int wolfSSL_BIO_MEMORY_read(WOLFSSL_BIO* bio, void* buf, int len)
120120
WOLFSSL_ENTER("wolfSSL_BIO_MEMORY_read");
121121
}
122122

123+
/* Reject a negative length up front. Callers are expected to validate, but
124+
* guarding here too prevents a negative len from defeating the signed
125+
* bounds checks below (sz/memSz comparisons) and reaching XMEMCPY with a
126+
* length of (size_t)-1. Return the same error the public wolfSSL_BIO_read()
127+
* does for a negative length rather than silently reporting 0 bytes read. A
128+
* zero length is handled correctly by the logic below (copies nothing). */
129+
if (len < 0) {
130+
return WOLFSSL_BIO_ERROR;
131+
}
132+
123133
sz = wolfSSL_BIO_pending(bio);
124134
if (sz > 0) {
125135
int memSz;

src/internal.c

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15690,31 +15690,29 @@ PRAGMA_GCC_DIAG_POP
1569015690
ret = ParseCertRelative(args->dCert, certType, verify, SSL_CM(ssl), extraSigners);
1569115691

1569215692
#if defined(HAVE_RPK)
15693-
/* if cert type has negotiated with peer, confirm the cert received has
15694-
* the same type.
15695-
*/
15696-
if (ret == 0 ) {
15697-
if (ssl->options.side == WOLFSSL_CLIENT_END) {
15698-
if (ssl->options.rpkState.received_ServerCertTypeCnt == 1) {
15693+
/* Confirm the received certificate's form (X.509 vs raw public key) matches
15694+
* the type negotiated with the peer. A raw public key (RFC 7250) has no
15695+
* chain, so ParseCertRelative() accepts it without any trust verification;
15696+
* it must only be accepted when RPK was negotiated for this peer. When no
15697+
* type was negotiated the default is X.509 (RFC 7250/8446), so an
15698+
* un-negotiated bare key is rejected. The negotiated type is the received
15699+
* server cert type (client) or the selected client cert type (server). */
15700+
if (ret == 0) {
15701+
cType = WOLFSSL_CERT_TYPE_X509;
15702+
if (ssl->options.side == WOLFSSL_CLIENT_END) {
15703+
if (ssl->options.rpkState.received_ServerCertTypeCnt == 1)
1569915704
cType = ssl->options.rpkState.received_ServerCertTypes[0];
15700-
if ((cType == WOLFSSL_CERT_TYPE_RPK && !args->dCert->isRPK) ||
15701-
(cType == WOLFSSL_CERT_TYPE_X509 && args->dCert->isRPK)) {
15702-
/* cert type mismatch */
15703-
WOLFSSL_MSG("unsupported certificate type received");
15704-
ret = UNSUPPORTED_CERTIFICATE;
15705-
}
15706-
}
1570715705
}
1570815706
else if (ssl->options.side == WOLFSSL_SERVER_END) {
15709-
if (ssl->options.rpkState.received_ClientCertTypeCnt == 1) {
15707+
if (ssl->options.rpkState.sending_ClientCertTypeCnt == 1)
1571015708
cType = ssl->options.rpkState.sending_ClientCertTypes[0];
15711-
if ((cType == WOLFSSL_CERT_TYPE_RPK && !args->dCert->isRPK) ||
15712-
(cType == WOLFSSL_CERT_TYPE_X509 && args->dCert->isRPK)) {
15713-
/* cert type mismatch */
15714-
WOLFSSL_MSG("unsupported certificate type received");
15715-
ret = UNSUPPORTED_CERTIFICATE;
15716-
}
15717-
}
15709+
}
15710+
15711+
if ((cType == WOLFSSL_CERT_TYPE_RPK && !args->dCert->isRPK) ||
15712+
(cType != WOLFSSL_CERT_TYPE_RPK && args->dCert->isRPK)) {
15713+
/* cert type mismatch - includes an un-negotiated raw public key */
15714+
WOLFSSL_MSG("unsupported certificate type received");
15715+
ret = UNSUPPORTED_CERTIFICATE;
1571815716
}
1571915717
}
1572015718
#endif /* HAVE_RPK */

src/ssl_certman.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3205,10 +3205,15 @@ int AddCA(WOLFSSL_CERT_MANAGER* cm, DerBuffer** pDer, int type, int verify)
32053205
}
32063206
#ifndef ALLOW_INVALID_CERTSIGN
32073207
else if (ret == 0 && cert->isCA == 1 && type != WOLFSSL_USER_CA &&
3208-
type != WOLFSSL_TEMP_CA && !cert->selfSigned &&
3208+
!cert->selfSigned && cert->extKeyUsageSet &&
32093209
(cert->extKeyUsage & KEYUSE_KEY_CERT_SIGN) == 0) {
3210-
/* Intermediate CA certs are required to have the keyCertSign
3211-
* extension set. User loaded root certs are not. */
3210+
/* Intermediate CA certs - including chain-supplied temporary CAs
3211+
* (WOLFSSL_TEMP_CA) added while building a path - are required to have
3212+
* the keyCertSign key usage when a Key Usage extension is present.
3213+
* Only operator-loaded root certs (WOLFSSL_USER_CA) and self-signed
3214+
* roots are exempt. Per RFC 5280 an absent Key Usage extension implies
3215+
* all usages, so only enforce this when the extension is actually
3216+
* present (extKeyUsageSet). */
32123217
WOLFSSL_MSG("\tDoesn't have key usage certificate signing");
32133218
ret = NOT_CA_ERROR;
32143219
}

src/ssl_p7p12.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,19 @@ int wolfSSL_PKCS7_verify(PKCS7* pkcs7, WOLFSSL_STACK* certs,
816816
if (ret != 0)
817817
return WOLFSSL_FAILURE;
818818

819+
/* Reject a degenerate (certs-only) PKCS#7 with no verified signer. Such an
820+
* object has empty signerInfos, so wc_PKCS7_VerifySignedData() succeeds
821+
* without authenticating the content. pkcs7.verifyCert is only set once a
822+
* signer's signature has actually been verified, so a NULL value here means
823+
* the content carries no valid signature and must not be reported as
824+
* verified - regardless of PKCS7_NOVERIFY, which only suppresses signer
825+
* certificate chain validation, not the requirement that a signature exist.
826+
*/
827+
if (p7->pkcs7.verifyCert == NULL) {
828+
WOLFSSL_MSG("PKCS7 has no verified signer (degenerate/certs-only)");
829+
return WOLFSSL_FAILURE;
830+
}
831+
819832
if ((flags & PKCS7_NOVERIFY) != PKCS7_NOVERIFY) {
820833
/* Verify signer certificates */
821834
if (store == NULL || store->cm == NULL) {

src/tls13.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11835,7 +11835,10 @@ int DoTls13Finished(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
1183511835
#endif
1183611836
if (
1183711837
#ifdef WOLFSSL_POST_HANDSHAKE_AUTH
11838-
!ssl->options.verifyPostHandshake &&
11838+
/* Exempt only the initial handshake; a pending post-handshake
11839+
* CertificateRequest (certReqCtx != NULL) still requires a peer
11840+
* certificate and a valid CertificateVerify. */
11841+
(!ssl->options.verifyPostHandshake || ssl->certReqCtx != NULL) &&
1183911842
#endif
1184011843
(!ssl->options.havePeerCert || !ssl->options.havePeerVerify)) {
1184111844
ret = NO_PEER_CERT; /* NO_PEER_VERIFY */
@@ -13507,7 +13510,19 @@ static int SanityCheckTls13MsgReceived(WOLFSSL* ssl, byte type)
1350713510
*/
1350813511
if (ssl->options.verifyPeer &&
1350913512
#ifdef WOLFSSL_POST_HANDSHAKE_AUTH
13510-
!ssl->options.verifyPostHandshake &&
13513+
/* The post-handshake-auth exemption is only valid during
13514+
* the initial handshake. On the server, once a
13515+
* post-handshake CertificateRequest is outstanding
13516+
* (certReqCtx != NULL), a Certificate is required again.
13517+
* Scoped to the server: certReqCtx means something
13518+
* different on the client (a received request) and the
13519+
* client does not process an inbound Finished in that
13520+
* state. Whether an empty Certificate is then accepted
13521+
* follows the verify mode (FAIL_IF_NO_PEER_CERT), exactly
13522+
* as for first-handshake client authentication. */
13523+
(!ssl->options.verifyPostHandshake ||
13524+
(ssl->options.side == WOLFSSL_SERVER_END &&
13525+
ssl->certReqCtx != NULL)) &&
1351113526
#endif
1351213527
!ssl->msgsReceived.got_certificate) {
1351313528
WOLFSSL_MSG("Finished received out of order - "

tests/api/test_ecc.c

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,110 @@ int test_wc_ecc_shared_secret(void)
578578
return EXPECT_RESULT();
579579
} /* END tests_wc_ecc_shared_secret */
580580

581+
#if defined(HAVE_ECC) && defined(HAVE_ECC_DHE) && !defined(WC_NO_RNG) && \
582+
(defined(HAVE_ECC384) || defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES))
583+
/* Verify the output-buffer size contract of wc_ecc_shared_secret() at the
584+
* field-size boundary. The single-precision (SP) math secret generators for
585+
* P-384/P-521 historically validated the caller's buffer against the wrong
586+
* length (e.g. P-521 checked 65 but writes 66), so a buffer declared one byte
587+
* short of the field size slipped past the check and was overwritten. Assert
588+
* that fieldSz-1 is rejected with BUFFER_E and fieldSz succeeds, for whichever
589+
* math backend is built.
590+
*
591+
* Coverage note: this drives the blocking generators only (wc_ecc_shared_secret
592+
* is synchronous). The fix also corrected the non-blocking (_nb) variants
593+
* (sp_ecc_secret_gen_384_nb / _521_nb), which need WOLFSSL_SP_NONBLOCK plus the
594+
* specialized SP build and are not exercised here. Of the blocking cases only
595+
* P-521 (65->66) actually fails without the fix; P-384 already used 48, so its
596+
* case is a guard against regression rather than a reproduction. */
597+
static int ecc_shared_secret_size_bound(WC_RNG* rng, int curveId, int fieldSz)
598+
{
599+
EXPECT_DECLS;
600+
ecc_key key;
601+
ecc_key pub;
602+
byte out[80]; /* >= P-521 field size (66) */
603+
word32 outlen;
604+
int keyInit = 0, pubInit = 0;
605+
int ret;
606+
607+
XMEMSET(&key, 0, sizeof(key));
608+
XMEMSET(&pub, 0, sizeof(pub));
609+
610+
ExpectIntEQ(wc_ecc_init(&key), 0);
611+
if (EXPECT_SUCCESS()) keyInit = 1;
612+
ExpectIntEQ(wc_ecc_init(&pub), 0);
613+
if (EXPECT_SUCCESS()) pubInit = 1;
614+
615+
ret = wc_ecc_make_key_ex(rng, fieldSz, &key, curveId);
616+
#if defined(WOLFSSL_ASYNC_CRYPT)
617+
ret = wc_AsyncWait(ret, &key.asyncDev, WC_ASYNC_FLAG_NONE);
618+
#endif
619+
ExpectIntEQ(ret, 0);
620+
621+
ret = wc_ecc_make_key_ex(rng, fieldSz, &pub, curveId);
622+
#if defined(WOLFSSL_ASYNC_CRYPT)
623+
ret = wc_AsyncWait(ret, &pub.asyncDev, WC_ASYNC_FLAG_NONE);
624+
#endif
625+
ExpectIntEQ(ret, 0);
626+
627+
#if defined(ECC_TIMING_RESISTANT) && (!defined(HAVE_FIPS) || \
628+
(!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION != 2))) && \
629+
!defined(HAVE_SELFTEST)
630+
ExpectIntEQ(wc_ecc_set_rng(&key, rng), 0);
631+
#endif
632+
633+
/* One byte short of the field size: must be rejected, not written past. */
634+
outlen = (word32)(fieldSz - 1);
635+
ExpectIntEQ(wc_ecc_shared_secret(&key, &pub, out, &outlen),
636+
WC_NO_ERR_TRACE(BUFFER_E));
637+
638+
/* Exactly the field size: must succeed and report the field size. */
639+
outlen = (word32)fieldSz;
640+
ExpectIntEQ(wc_ecc_shared_secret(&key, &pub, out, &outlen), 0);
641+
ExpectIntEQ(outlen, (word32)fieldSz);
642+
643+
if (pubInit)
644+
wc_ecc_free(&pub);
645+
if (keyInit)
646+
wc_ecc_free(&key);
647+
return EXPECT_RESULT();
648+
}
649+
#endif
650+
651+
/*
652+
* Testing wc_ecc_shared_secret() output buffer bounds at the field-size edge.
653+
*/
654+
int test_wc_ecc_shared_secret_size_bounds(void)
655+
{
656+
EXPECT_DECLS;
657+
#if defined(HAVE_ECC) && defined(HAVE_ECC_DHE) && !defined(WC_NO_RNG) && \
658+
(defined(HAVE_ECC384) || defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES))
659+
WC_RNG rng;
660+
int rngInit = 0;
661+
662+
XMEMSET(&rng, 0, sizeof(rng));
663+
PRIVATE_KEY_UNLOCK();
664+
ExpectIntEQ(wc_InitRng(&rng), 0);
665+
if (EXPECT_SUCCESS())
666+
rngInit = 1;
667+
668+
#if defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)
669+
ExpectIntEQ(ecc_shared_secret_size_bound(&rng, ECC_SECP384R1, 48), 1);
670+
#endif
671+
#if defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)
672+
ExpectIntEQ(ecc_shared_secret_size_bound(&rng, ECC_SECP521R1, 66), 1);
673+
#endif
674+
675+
if (rngInit)
676+
DoExpectIntEQ(wc_FreeRng(&rng), 0);
677+
#ifdef FP_ECC
678+
wc_ecc_fp_free();
679+
#endif
680+
PRIVATE_KEY_LOCK();
681+
#endif
682+
return EXPECT_RESULT();
683+
}
684+
581685
/*
582686
* testint wc_ecc_export_x963()
583687
*/

tests/api/test_ecc.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ int test_wc_ecc_size(void);
3636
int test_wc_ecc_params(void);
3737
int test_wc_ecc_signVerify_hash(void);
3838
int test_wc_ecc_shared_secret(void);
39+
int test_wc_ecc_shared_secret_size_bounds(void);
3940
int test_wc_ecc_export_x963(void);
4041
int test_wc_ecc_export_x963_ex(void);
4142
int test_wc_ecc_import_x963(void);
@@ -74,6 +75,7 @@ int test_wc_EccPrivateKeyToDer(void);
7475
TEST_DECL_GROUP("ecc", test_wc_ecc_params), \
7576
TEST_DECL_GROUP("ecc", test_wc_ecc_signVerify_hash), \
7677
TEST_DECL_GROUP("ecc", test_wc_ecc_shared_secret), \
78+
TEST_DECL_GROUP("ecc", test_wc_ecc_shared_secret_size_bounds), \
7779
TEST_DECL_GROUP("ecc", test_wc_ecc_export_x963), \
7880
TEST_DECL_GROUP("ecc", test_wc_ecc_export_x963_ex), \
7981
TEST_DECL_GROUP("ecc", test_wc_ecc_import_x963), \

tests/api/test_ossl_bio.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -980,6 +980,45 @@ int test_wolfSSL_BIO_write(void)
980980
return EXPECT_RESULT();
981981
}
982982

983+
/* A negative length must never defeat the memory-BIO read bounds check and
984+
* reach XMEMCPY with (size_t)-1. This exercises the public BIO_read() boundary
985+
* (which rejects a negative length before dispatch); the matching guard in the
986+
* static wolfSSL_BIO_MEMORY_read() sink is defense-in-depth and not separately
987+
* reachable through the public API. Verify a negative length is rejected with
988+
* an error without copying, a zero length reads nothing, and the pending data
989+
* is left intact and still readable. */
990+
int test_wolfSSL_BIO_read_negative_len(void)
991+
{
992+
EXPECT_DECLS;
993+
#if defined(OPENSSL_EXTRA)
994+
BIO* bio = NULL;
995+
char msg[] = "negative length test";
996+
int msgLen = (int)XSTRLEN(msg);
997+
char out[64];
998+
999+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
1000+
ExpectIntEQ(BIO_write(bio, msg, msgLen), msgLen);
1001+
1002+
/* Negative length: must be rejected with an error, not a wild copy and not
1003+
* a silent 0-byte read. */
1004+
XMEMSET(out, 0, sizeof(out));
1005+
ExpectIntLT(BIO_read(bio, out, -1), 0);
1006+
/* Data must be untouched - still all pending. */
1007+
ExpectIntEQ(BIO_pending(bio), msgLen);
1008+
1009+
/* Zero length: nothing read, data still pending. */
1010+
ExpectIntEQ(BIO_read(bio, out, 0), 0);
1011+
ExpectIntEQ(BIO_pending(bio), msgLen);
1012+
1013+
/* A normal read still returns the intact message. */
1014+
ExpectIntEQ(BIO_read(bio, out, (int)sizeof(out)), msgLen);
1015+
ExpectIntEQ(XMEMCMP(out, msg, msgLen), 0);
1016+
1017+
BIO_free(bio);
1018+
#endif
1019+
return EXPECT_RESULT();
1020+
}
1021+
9831022

9841023
int test_wolfSSL_BIO_printf(void)
9851024
{

tests/api/test_ossl_bio.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ int test_wolfSSL_BIO_datagram(void);
3535
int test_wolfSSL_BIO_s_null(void);
3636
int test_wolfSSL_BIO_accept(void);
3737
int test_wolfSSL_BIO_write(void);
38+
int test_wolfSSL_BIO_read_negative_len(void);
3839
int test_wolfSSL_BIO_printf(void);
3940
int test_wolfSSL_BIO_f_md(void);
4041
int test_wolfSSL_BIO_up_ref(void);
@@ -55,6 +56,7 @@ int test_wolfSSL_BIO_get_init(void);
5556
TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_should_retry), \
5657
TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_s_null), \
5758
TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_write), \
59+
TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_read_negative_len), \
5860
TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_printf), \
5961
TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_f_md), \
6062
TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_up_ref), \

0 commit comments

Comments
 (0)