diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index ce340519d8..34bb0fa3fd 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -906,6 +906,7 @@ WOLFSSL_IMX6_CAAM_BLOB WOLFSSL_IMX6_CAAM_RNG WOLFSSL_IMXRT_DCP WOLFSSL_ISOTP +WOLFSSL_KEEP_HOST_HEADER_PROBES WOLFSSL_KEIL WOLFSSL_KEIL_NET WOLFSSL_KYBER_NO_DECAPSULATE diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f183f8eee..9c7bf8e985 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,6 +101,7 @@ check_include_file("time.h" HAVE_TIME_H) check_include_file("sys/ioctl.h" HAVE_SYS_IOCTL_H) check_include_file("sys/socket.h" HAVE_SYS_SOCKET_H) check_include_file("sys/time.h" HAVE_SYS_TIME_H) +check_include_file("sys/un.h" HAVE_SYS_UN_H) check_include_file("errno.h" HAVE_ERRNO_H) check_include_file("dlfcn.h" HAVE_DLFCN_H) check_include_file("inttypes.h" HAVE_INTTYPES_H) @@ -113,13 +114,21 @@ check_include_file("sys/stat.h" HAVE_SYS_STAT_H) check_include_file("sys/types.h" HAVE_SYS_TYPES_H) check_include_file("unistd.h" HAVE_UNISTD_H) -# types.h depends on HAVE_LIMITS_H, and it is defined in options.h (rather than -# config.h) so that applications consuming wolfSSL headers see it. The in-tree -# build, however, is configured through config.h/compile definitions and does -# not include options.h, so define it here as well. +# types.h depends on HAVE_LIMITS_H, and callbacks.h sizes WOLFSSL_TIMEVAL with +# HAVE_SYS_TIME_H. Both are defined in options.h (rather than config.h) so that +# applications consuming wolfSSL headers see the same value the library was +# built with. The in-tree build, however, is configured through +# config.h/compile definitions and does not include options.h, so define them +# here as well. if(HAVE_LIMITS_H) add_definitions("-DHAVE_LIMITS_H") endif() +if(HAVE_SYS_TIME_H) + add_definitions("-DHAVE_SYS_TIME_H") +endif() +if(HAVE_SYS_UN_H) + add_definitions("-DHAVE_SYS_UN_H") +endif() include(CheckFunctionExists) diff --git a/cmake/config.in b/cmake/config.in index 6054b6dbe7..c5a04f7c18 100644 --- a/cmake/config.in +++ b/cmake/config.in @@ -34,9 +34,6 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STRING_H @HAVE_STRING_H@ -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_SYS_TIME_H @HAVE_SYS_TIME_H@ - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SECURITY_SECTRUSTSETTINGS_H @HAVE_SECURITY_SECTRUSTSETTINGS_H@ diff --git a/cmake/options.h.in b/cmake/options.h.in index 0c6de3c9d4..ac9bc57f4d 100644 --- a/cmake/options.h.in +++ b/cmake/options.h.in @@ -41,6 +41,12 @@ extern "C" { /* Since types.h depends on HAVE_LIMITS_H, we must define it in options.h. */ #undef HAVE_LIMITS_H #cmakedefine HAVE_LIMITS_H @HAVE_LIMITS_H@ +/* callbacks.h sizes WOLFSSL_TIMEVAL with HAVE_SYS_TIME_H, so it must be in + * options.h too. */ +#undef HAVE_SYS_TIME_H +#cmakedefine HAVE_SYS_TIME_H @HAVE_SYS_TIME_H@ +#undef HAVE_SYS_UN_H +#cmakedefine HAVE_SYS_UN_H @HAVE_SYS_UN_H@ #undef ASIO_USE_WOLFSSL #cmakedefine ASIO_USE_WOLFSSL #undef BOOST_ASIO_USE_WOLFSSL diff --git a/configure.ac b/configure.ac index f31b8e0da2..afd652dc6a 100644 --- a/configure.ac +++ b/configure.ac @@ -224,6 +224,13 @@ fi AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stddef.h time.h sys/ioctl.h sys/socket.h sys/time.h errno.h sys/un.h ctype.h sys/random.h]) # Special case: Since types.h depends on HAVE_LIMITS_H, we must define it in options.h. AC_CHECK_HEADER([limits.h], [AM_CPPFLAGS="$AM_CPPFLAGS -DHAVE_LIMITS_H=1"], []) +# Special case: these gate the layout of public types in installed headers +# (union WOLFSSL_BIO_ADDR in wolfio.h, WOLFSSL_TIMEVAL in callbacks.h), so +# applications must see the same value the library was built with. These stay +# in AC_CHECK_HEADERS above as well: only the plural form defines them in +# confdefs.h, which the AC_CHECK_DECLS prologue below needs to see sys/time.h. +AC_CHECK_HEADER([sys/un.h], [AM_CPPFLAGS="$AM_CPPFLAGS -DHAVE_SYS_UN_H=1"], []) +AC_CHECK_HEADER([sys/time.h], [AM_CPPFLAGS="$AM_CPPFLAGS -DHAVE_SYS_TIME_H=1"], []) AC_CHECK_LIB([network],[socket]) AC_C_BIGENDIAN AC_C___ATOMIC diff --git a/src/internal.c b/src/internal.c index 271c941e98..18d0d6b41b 100644 --- a/src/internal.c +++ b/src/internal.c @@ -14621,11 +14621,11 @@ int CheckHostName(DecodedCert* dCert, const char *domainName, return ret; } -int CheckIPAddr(DecodedCert* dCert, const char* ipasc) +int CheckIPAddr(DecodedCert* dCert, const char* ipasc, size_t ipascLen) { WOLFSSL_MSG("Checking IPAddr"); - return CheckHostName(dCert, ipasc, (size_t)XSTRLEN(ipasc), 0, 1); + return CheckHostName(dCert, ipasc, ipascLen, 0, 1); } @@ -16031,6 +16031,9 @@ int DoVerifyCallback(WOLFSSL_CERT_MANAGER* cm, WOLFSSL* ssl, int cert_err, #if defined(OPENSSL_EXTRA) /* Perform domain and IP check only for the leaf certificate */ if (args->certIdx == 0) { + size_t ipascLen = ((ssl != NULL) && (ssl->param != NULL)) ? + XSTRLEN(ssl->param->ipasc) : 0; + /* perform domain name check on the peer certificate */ if (args->dCertInit && args->dCert && (ssl != NULL) && ssl->param && ssl->param->hostName[0]) { @@ -16071,8 +16074,8 @@ int DoVerifyCallback(WOLFSSL_CERT_MANAGER* cm, WOLFSSL* ssl, int cert_err, /* perform IP address check on the peer certificate */ if ((args->dCertInit != 0) && (args->dCert != NULL) && (ssl != NULL) && - (ssl->param != NULL) && (XSTRLEN(ssl->param->ipasc) > 0)) { - if (CheckIPAddr(args->dCert, ssl->param->ipasc) != 0) { + (ssl->param != NULL) && (ipascLen > 0)) { + if (CheckIPAddr(args->dCert, ssl->param->ipasc, ipascLen) != 0) { if (cert_err == 0) { ret = IPADDR_MISMATCH; WOLFSSL_ERROR_VERBOSE(ret); @@ -18894,7 +18897,8 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, #ifndef OPENSSL_EXTRA if (!ssl->options.verifyNone && ssl->buffers.ipasc.buffer) { if (CheckIPAddr(args->dCert, - (const char*)ssl->buffers.ipasc.buffer) != 0) { + (const char*)ssl->buffers.ipasc.buffer, + (size_t)ssl->buffers.ipasc.length) != 0) { WOLFSSL_MSG("IPAddr match on alt names failed"); ret = IPADDR_MISMATCH; WOLFSSL_ERROR_VERBOSE(ret); diff --git a/src/x509.c b/src/x509.c index a8f0690b80..18f3ed5a0c 100644 --- a/src/x509.c +++ b/src/x509.c @@ -15950,10 +15950,7 @@ int wolfSSL_X509_check_host(WOLFSSL_X509 *x, const char *chk, size_t chklen, } #ifdef WOLFSSL_IP_ALT_NAME - /* chk is length delimited and may not be NUL terminated, so check it - * against the iPAddress entries directly rather than through the - * NUL terminated CheckIPAddr helper. */ - ret = CheckHostName(dCert, (char *)chk, chklen, 0, 1); + ret = CheckIPAddr(dCert, (char *)chk, chklen); if (ret == 0) { goto out; } @@ -16008,7 +16005,7 @@ int wolfSSL_X509_check_ip_asc(WOLFSSL_X509 *x, const char *ipasc, ret = WOLFSSL_FAILURE; } else { - ret = CheckIPAddr(dCert, ipasc); + ret = CheckIPAddr(dCert, ipasc, (size_t)XSTRLEN(ipasc)); if (ret != 0) { ret = WOLFSSL_FAILURE; } diff --git a/tests/api/test_ossl_x509.c b/tests/api/test_ossl_x509.c index b262b72142..dabbe81a29 100644 --- a/tests/api/test_ossl_x509.c +++ b/tests/api/test_ossl_x509.c @@ -422,6 +422,20 @@ int test_wolfSSL_X509_check_host(void) ExpectIntEQ(wolfSSL_X509_check_host(x509, altName, XSTRLEN(altName), WOLFSSL_MULTI_LABEL_WILDCARDS, NULL), WC_NO_ERR_TRACE(WOLFSSL_FAILURE)); + /* chk of exactly chklen bytes with no terminator - every consumer must + * stay within the caller's declared length. */ + { + char* bounded = (char*)XMALLOC(XSTRLEN(altName), NULL, + DYNAMIC_TYPE_TMP_BUFFER); + ExpectNotNull(bounded); + if (bounded != NULL) { + XMEMCPY(bounded, altName, XSTRLEN(altName)); + ExpectIntEQ(X509_check_host(x509, bounded, XSTRLEN(altName), 0, + NULL), WOLFSSL_SUCCESS); + XFREE(bounded, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + } + X509_free(x509); ExpectIntEQ(X509_check_host(NULL, altName, XSTRLEN(altName), 0, NULL), diff --git a/tests/api/test_pkcs7.c b/tests/api/test_pkcs7.c index 2cf7a8b04f..951a02cff6 100644 --- a/tests/api/test_pkcs7.c +++ b/tests/api/test_pkcs7.c @@ -6774,18 +6774,11 @@ int test_wc_PKCS7_VerifySignedData_TruncEContentTag(void) * SignedData bundle truncated at the certificates [0] IMPLICIT tag. * Verifies that the parser rejects the malformed input rather than * dereferencing past the end of the buffer. - * - * TODO: limited to NO_PKCS7_STREAM because the streaming parser's stage 3 - * early-exit check (pkcs7.c near line 6594) accepts any bundle - * whose remaining footer is < 6 bytes as a successful degenerate end, - * so the bounds check at line 6765 is unreachable in streaming mode. - * Drop the NO_PKCS7_STREAM gate if/when the early-exit check becomes - * more accurate. */ int test_wc_PKCS7_VerifySignedData_TruncCertSetTag(void) { EXPECT_DECLS; -#if defined(HAVE_PKCS7) && defined(NO_PKCS7_STREAM) +#if defined(HAVE_PKCS7) PKCS7* pkcs7 = NULL; WOLFSSL_SMALL_STACK_STATIC byte der[] = { @@ -6825,7 +6818,71 @@ int test_wc_PKCS7_VerifySignedData_TruncCertSetTag(void) ExpectIntNE(wc_PKCS7_VerifySignedData(pkcs7, der, derSz), 0); wc_PKCS7_Free(pkcs7); -#endif /* HAVE_PKCS7 && NO_PKCS7_STREAM */ +#endif /* HAVE_PKCS7 */ + return EXPECT_RESULT(); +} + +/* + * SignedData bundle with a non-empty digestAlgorithms SET whose signerInfos + * field is absent entirely - the bundle stops right after the eContent. + * signerInfos is a required field, so the bundle must be rejected both with + * the default settings and with wc_PKCS7_AllowDegenerate() turned off. + */ +int test_wc_PKCS7_VerifySignedData_NoSignerInfos(void) +{ + EXPECT_DECLS; +#if defined(HAVE_PKCS7) && !defined(NO_SHA256) + PKCS7* pkcs7 = NULL; + + WOLFSSL_SMALL_STACK_STATIC byte der[] = { + /* outer ContentInfo SEQUENCE (99 bytes content) */ + 0x30, 0x63, + /* contentType OID signedData */ + 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02, + /* [0] EXPLICIT (86 bytes content) */ + 0xA0, 0x56, + /* SignedData SEQUENCE (84 bytes content) */ + 0x30, 0x54, + /* version INTEGER 1 */ + 0x02, 0x01, 0x01, + /* digestAlgorithms SET (15 bytes) { sha256 AlgorithmIdentifier } */ + 0x31, 0x0F, + 0x30, 0x0D, + 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, + 0x05, 0x00, + /* encapContentInfo SEQUENCE (62 bytes content) */ + 0x30, 0x3E, + /* eContentType OID 1.2.840.113549.1.7.1 (data) */ + 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01, + /* eContent [0] EXPLICIT (49 bytes content) */ + 0xA0, 0x31, + /* OCTET STRING (47 bytes content) */ + 0x04, 0x2F, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41 + /* no certificates, no signerInfos: bundle ends here */ + }; + word32 derSz = (word32)sizeof(der); + + ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId)); + ExpectIntEQ(wc_PKCS7_Init(pkcs7, HEAP_HINT, INVALID_DEVID), 0); + ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0); + ExpectIntNE(wc_PKCS7_VerifySignedData(pkcs7, der, derSz), 0); + wc_PKCS7_Free(pkcs7); + pkcs7 = NULL; + + ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId)); + ExpectIntEQ(wc_PKCS7_Init(pkcs7, HEAP_HINT, INVALID_DEVID), 0); + ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0); + wc_PKCS7_AllowDegenerate(pkcs7, 0); + ExpectIntNE(wc_PKCS7_VerifySignedData(pkcs7, der, derSz), 0); + wc_PKCS7_Free(pkcs7); + +#endif /* HAVE_PKCS7 && !NO_SHA256 */ return EXPECT_RESULT(); } @@ -6961,6 +7018,139 @@ static int pkcs7_build_digestparam_mismatch(byte* cert, word32 certSz, * mismatch). The fix is symmetric, so both directions are exercised, over both * the attribute-free and signed-attribute signing paths. */ +#if defined(HAVE_PKCS7) && !defined(NO_PKCS7_STREAM) && !defined(NO_SHA256) +/* Feed der[0..derSz) to the verifier in fixed size chunks, mimicking a caller + * that streams the bundle in. Returns the final return code, which is + * WC_PKCS7_WANT_READ_E if the parser never reached a decision. */ +static int pkcs7_verify_chunked(const byte* der, word32 derSz, word32 chunkSz) +{ + PKCS7* pkcs7; + int ret = WC_NO_ERR_TRACE(WC_PKCS7_WANT_READ_E); + word32 z; + + pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId); + if (pkcs7 == NULL) + return MEMORY_E; + if (wc_PKCS7_Init(pkcs7, HEAP_HINT, INVALID_DEVID) != 0 || + wc_PKCS7_InitWithCert(pkcs7, NULL, 0) != 0) { + wc_PKCS7_Free(pkcs7); + return BAD_FUNC_ARG; + } + + for (z = 0; z < derSz; z += chunkSz) { + word32 n = (derSz - z < chunkSz) ? derSz - z : chunkSz; + + ret = wc_PKCS7_VerifySignedData(pkcs7, (byte*)der + z, n); + if (ret != WC_NO_ERR_TRACE(WC_PKCS7_WANT_READ_E)) + break; + } + + wc_PKCS7_Free(pkcs7); + return ret; +} +#endif + +/* + * The streaming verifier decides at the stage 3 handoff whether the bundle has + * a footer left, and the amount of input handed over per call changes which + * read windows are satisfied from the caller's buffer and which from the + * internal one. Sweep every chunk size over a well formed bundle, which must + * verify however it is split, and over a bundle whose outer ContentInfo ends + * with the eContent, which carries no signerInfos field at all and must never + * be reported as verified. + */ +int test_wc_PKCS7_VerifySignedData_ChunkSweep(void) +{ + EXPECT_DECLS; +#if defined(HAVE_PKCS7) && !defined(NO_PKCS7_STREAM) && !defined(NO_SHA256) + word32 chunkSz; + + /* non-empty digestAlgorithms SET, eContent, and then end of bundle: no + * certificates and no signerInfos */ + WOLFSSL_SMALL_STACK_STATIC byte noSigner[] = { + 0x30, 0x63, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, + 0x02, 0xA0, 0x56, 0x30, 0x54, 0x02, 0x01, 0x01, 0x31, 0x0F, 0x30, 0x0D, + 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, + 0x00, 0x30, 0x3E, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, + 0x07, 0x01, 0xA0, 0x31, 0x04, 0x2F, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41 + }; + + /* same, with a lone certificates [0] tag: a truncated footer */ + WOLFSSL_SMALL_STACK_STATIC byte footTrunc[] = { + 0x30, 0x64, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, + 0x02, 0xA0, 0x57, 0x30, 0x55, 0x02, 0x01, 0x01, 0x31, 0x0F, 0x30, 0x0D, + 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, + 0x00, 0x30, 0x3E, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, + 0x07, 0x01, 0xA0, 0x31, 0x04, 0x2F, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0xA0 + }; + /* same, with a well formed empty signerInfos SET */ + WOLFSSL_SMALL_STACK_STATIC byte footEmptySi[] = { + 0x30, 0x65, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, + 0x02, 0xA0, 0x58, 0x30, 0x56, 0x02, 0x01, 0x01, 0x31, 0x0F, 0x30, 0x0D, + 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, + 0x00, 0x30, 0x3E, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, + 0x07, 0x01, 0xA0, 0x31, 0x04, 0x2F, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x31, 0x00 + }; + +#if !defined(NO_RSA) && defined(USE_CERT_BUFFERS_2048) + { + byte cert[sizeof(client_cert_der_2048)]; + byte key[sizeof(client_key_der_2048)]; + word32 certSz = (word32)sizeof(cert); + word32 keySz = (word32)sizeof(key); + byte msg[FOURK_BUF]; + int msgSz = 0; + + XMEMCPY(cert, client_cert_der_2048, certSz); + XMEMCPY(key, client_key_der_2048, keySz); + + XMEMSET(msg, 0, sizeof(msg)); + ExpectIntGT(msgSz = pkcs7_sign_digest_params(cert, certSz, key, keySz, + 0, 0, msg, (word32)sizeof(msg)), 0); + + /* a well formed bundle must verify no matter how the input is split */ + for (chunkSz = 1; (msgSz > 0) && (chunkSz <= (word32)msgSz); + chunkSz++) { + ExpectIntEQ(pkcs7_verify_chunked(msg, (word32)msgSz, chunkSz), 0); + } + } +#endif + + /* signerInfos is a required field, so this one must never verify. The + * exact code is asserted so a setup failure cannot pass as a rejection. */ + for (chunkSz = 1; chunkSz <= (word32)sizeof(noSigner); chunkSz++) { + ExpectIntEQ(pkcs7_verify_chunked(noSigner, (word32)sizeof(noSigner), + chunkSz), WC_NO_ERR_TRACE(PKCS7_NO_SIGNER_E)); + } + + /* a truncated footer is malformed and must never verify either */ + for (chunkSz = 1; chunkSz <= (word32)sizeof(footTrunc); chunkSz++) { + ExpectIntEQ(pkcs7_verify_chunked(footTrunc, (word32)sizeof(footTrunc), + chunkSz), WC_NO_ERR_TRACE(ASN_PARSE_E)); + } + + /* a well formed empty signerInfos SET is a degenerate end, accepted by + * default however the input is split */ + for (chunkSz = 1; chunkSz <= (word32)sizeof(footEmptySi); chunkSz++) { + ExpectIntEQ(pkcs7_verify_chunked(footEmptySi, + (word32)sizeof(footEmptySi), chunkSz), 0); + } +#endif + return EXPECT_RESULT(); +} + int test_wc_PKCS7_VerifySignedData_NoDigestParams(void) { EXPECT_DECLS; diff --git a/tests/api/test_pkcs7.h b/tests/api/test_pkcs7.h index a7f45b3dca..451977d646 100644 --- a/tests/api/test_pkcs7.h +++ b/tests/api/test_pkcs7.h @@ -81,6 +81,8 @@ int test_wc_PKCS7_VerifySignedData_PKCS7ContentSeq(void); int test_wc_PKCS7_VerifySignedData_IndefLenOOB(void); int test_wc_PKCS7_VerifySignedData_TruncEContentTag(void); int test_wc_PKCS7_VerifySignedData_TruncCertSetTag(void); +int test_wc_PKCS7_VerifySignedData_NoSignerInfos(void); +int test_wc_PKCS7_VerifySignedData_ChunkSweep(void); int test_wc_PKCS7_VerifySignedData_NoDigestParams(void); @@ -138,6 +140,8 @@ int test_wc_PKCS7_VerifySignedData_NoDigestParams(void); TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_IndefLenOOB), \ TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_TruncEContentTag), \ TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_TruncCertSetTag), \ + TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_NoSignerInfos), \ + TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_ChunkSweep), \ TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_NoDigestParams) #define TEST_PKCS7_ENCRYPTED_DATA_DECLS \ diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 8168d0d21e..c30da3161a 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -9631,6 +9631,11 @@ static int ecc_verify_hash(mp_int *r, mp_int *s, const byte* hash, u1 = u1tmp; u2 = u2tmp; #endif + /* zeroed so the cleanup below no-ops if the init is skipped */ + if (u1 != NULL) + XMEMSET(u1, 0, sizeof(mp_int)); + if (u2 != NULL) + XMEMSET(u2, 0, sizeof(mp_int)); #else u1 = e; u2 = w; @@ -14088,18 +14093,23 @@ static int accel_fp_mul2add(int idx1, int idx2, int first; #ifdef WOLFSSL_SMALL_STACK + /* each is zeroed on acquisition so the cleanup below no-ops if a later + * allocation fails and the init is skipped */ tka = (mp_int*)XMALLOC(sizeof(mp_int), NULL, DYNAMIC_TYPE_ECC); if (tka == NULL) { err = MEMORY_E; goto done; } + XMEMSET(tka, 0, sizeof(mp_int)); tkb = (mp_int*)XMALLOC(sizeof(mp_int), NULL, DYNAMIC_TYPE_ECC); if (tkb == NULL) { err = MEMORY_E; goto done; } + XMEMSET(tkb, 0, sizeof(mp_int)); order = (mp_int*)XMALLOC(sizeof(mp_int), NULL, DYNAMIC_TYPE_ECC); if (order == NULL) { err = MEMORY_E; goto done; } + XMEMSET(order, 0, sizeof(mp_int)); #endif if (mp_init_multi(tka, tkb, order, NULL, NULL, NULL) != MP_OKAY) { diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c index d9f1dbb172..4d1ef1fb2b 100644 --- a/wolfcrypt/src/pkcs7.c +++ b/wolfcrypt/src/pkcs7.c @@ -570,6 +570,8 @@ static const char* wc_PKCS7_GetStateName(int in) case WC_PKCS7_VERIFY_STAGE2: return "WC_PKCS7_VERIFY_STAGE2"; case WC_PKCS7_VERIFY_STAGE3: return "WC_PKCS7_VERIFY_STAGE3"; + case WC_PKCS7_VERIFY_STAGE3_FOOTER: + return "WC_PKCS7_VERIFY_STAGE3_FOOTER"; case WC_PKCS7_VERIFY_STAGE4: return "WC_PKCS7_VERIFY_STAGE4"; case WC_PKCS7_VERIFY_STAGE5: return "WC_PKCS7_VERIFY_STAGE5"; case WC_PKCS7_VERIFY_STAGE6: return "WC_PKCS7_VERIFY_STAGE6"; @@ -6894,6 +6896,7 @@ static int PKCS7_VerifySignedData(wc_PKCS7* pkcs7, const byte* hashBuf, #endif int multiPart = 0, keepContent; int contentLen = 0; + int shortFooter = 0; byte* pkiMsg = in; word32 pkiMsgSz = inSz; @@ -7513,23 +7516,40 @@ static int PKCS7_VerifySignedData(wc_PKCS7* pkcs7, const byte* hashBuf, /* check if bundle has more elements or footer, if not, set content * to pkcs7->content and hash to pkcs7->hash. * - * NOTE: this check returns success whenever fewer than 6 bytes - * follow the content within the outer ContentInfo, which also - * accepts truncated bundles whose footer was cut short (e.g. a - * lone certificates [0] tag with no length). Distinguishing a - * legitimate degenerate end (such as an empty signerInfos SET - * "31 00") from truncated junk would require peeking at the - * remaining bytes or making stage 4's `expected` window smaller. - */ - if (ret == 0 && pkcs7->stream->maxLen > 0 && - (pkcs7->stream->maxLen - pkcs7->stream->totalRd) - < ASN_TAG_SZ + MAX_LENGTH_SZ) { + * maxLen only bounds the bundle when stage 1 read it from a + * complete outer SEQUENCE header; a caller feeding small chunks can + * leave it behind totalRd, so only consult it when it is still + * ahead. The residual only decides between an ended bundle, a + * footer too short for the stages below to read, and a footer they + * can parse. */ + if (pkcs7->stream->maxLen > 0 && + pkcs7->stream->maxLen >= pkcs7->stream->totalRd) { + word32 remaining = (pkcs7->stream->maxLen - + pkcs7->stream->totalRd) + pkcs7->stream->length; + + /* signerInfos is a required field of SignedData, so a bundle + * whose outer ContentInfo ends with the content carries no + * signer at all */ + if (remaining == 0) { + WOLFSSL_MSG("PKCS7 bundle ends before signerInfos"); + ret = PKCS7_NO_SIGNER_E; + break; + } - ret = 0; - break; + /* Below the window the stages after this one read, so the + * footer cannot be handed to them. Nothing this small can hold + * a SignerInfo either, so read it here and check it against + * the only shape it is allowed to have. */ + if (remaining < (ASN_TAG_SZ + MAX_LENGTH_SZ) * 2) { + pkcs7->stream->expected = remaining; + shortFooter = 1; + } + } + if (!shortFooter) { + /* expect data length to be enough to check set and seq of + * certs */ + pkcs7->stream->expected = (ASN_TAG_SZ + MAX_LENGTH_SZ) * 2; } - /* expect data length to be enough to check set and seq of certs */ - pkcs7->stream->expected = (ASN_TAG_SZ + MAX_LENGTH_SZ) * 2; #else /* Break out before content because it can be optional in degenerate @@ -7636,7 +7656,87 @@ static int PKCS7_VerifySignedData(wc_PKCS7* pkcs7, const byte* hashBuf, contentSz = pkcs7->contentSz; } #endif /* !NO_PKCS7_STREAM */ - wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_VERIFY_STAGE4); + wc_PKCS7_ChangeState(pkcs7, shortFooter ? + WC_PKCS7_VERIFY_STAGE3_FOOTER : WC_PKCS7_VERIFY_STAGE4); + + FALL_THROUGH; + + case WC_PKCS7_VERIFY_STAGE3_FOOTER: + #ifndef NO_PKCS7_STREAM + /* Only entered for a footer too short for the stages below. It has + * to be an empty signerInfos SET, optionally preceded by empty + * certificates [0] and crls [1], and it has to account for every + * byte left in the outer ContentInfo. */ + if (pkcs7->state == WC_PKCS7_VERIFY_STAGE3_FOOTER) { + byte* foot = NULL; + word32 footIdx = 0, footEnd; + int footLen = 0; + + /* the footer lives in the second buffer when the caller used + * wc_PKCS7_VerifySignedData_ex(), same as stages 4 and 6 */ + if (in2 && in2Sz > 0) { + src = in2; + srcSz = in2Sz; + } + else { + src = in; + srcSz = inSz; + } + + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, src, srcSz, + pkcs7->stream->expected, &foot, &footIdx)) + != 0) { + break; + } + footEnd = footIdx + pkcs7->stream->expected; + + if (footIdx < footEnd && foot[footIdx] == + (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) { + footIdx++; + if (GetLength(foot, &footIdx, &footLen, footEnd) < 0 || + footLen != 0) { + ret = ASN_PARSE_E; + } + } + if (ret == 0 && footIdx < footEnd && foot[footIdx] == + (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 1)) { + footIdx++; + if (GetLength(foot, &footIdx, &footLen, footEnd) < 0 || + footLen != 0) { + ret = ASN_PARSE_E; + } + } + if (ret == 0) { + if (footIdx >= footEnd || + foot[footIdx] != (ASN_CONSTRUCTED | ASN_SET)) { + ret = ASN_PARSE_E; + } + else { + footIdx++; + if (GetLength(foot, &footIdx, &footLen, footEnd) < 0 || + footLen != 0) { + ret = ASN_PARSE_E; + } + } + } + if (ret == 0 && footIdx != footEnd) { + ret = ASN_PARSE_E; + } + if (ret != 0) { + WOLFSSL_MSG("PKCS7 malformed signerInfos footer"); + break; + } + + /* a well formed empty signerInfos SET: a degenerate end, so + * honour the caller's choice the way + * wc_PKCS7_ParseSignerInfo() would have */ + if (pkcs7->noDegenerate == 1) { + WOLFSSL_MSG("Set to not allow degenerate cases"); + ret = PKCS7_NO_SIGNER_E; + } + break; + } + #endif /* !NO_PKCS7_STREAM */ FALL_THROUGH; diff --git a/wolfcrypt/src/rsa.c b/wolfcrypt/src/rsa.c index 706a626aa5..d4e93ff32a 100644 --- a/wolfcrypt/src/rsa.c +++ b/wolfcrypt/src/rsa.c @@ -5453,6 +5453,7 @@ int wc_CheckProbablePrime_ex(const byte* pRaw, word32 pRawSz, } else ret = 0; + if (ret == 0) #endif ret = mp_init_multi(p, q, e, NULL, NULL, NULL); diff --git a/wolfcrypt/src/sakke.c b/wolfcrypt/src/sakke.c index 28a2e01bf0..f98f376f69 100644 --- a/wolfcrypt/src/sakke.c +++ b/wolfcrypt/src/sakke.c @@ -2089,6 +2089,16 @@ static int sakke_accumulate_line_add_one(mp_proj* v, mp_int* prime, mp_digit mp, t3 = (mp_int *)XMALLOC(sizeof(*t3), NULL, DYNAMIC_TYPE_TMP_BUFFER); if (t3 == NULL) err = 1; + + /* zeroed so the cleanup below no-ops if the init is skipped */ + if (h != NULL) + XMEMSET(h, 0, sizeof(*h)); + if (ty != NULL) + XMEMSET(ty, 0, sizeof(*ty)); + if (tz != NULL) + XMEMSET(tz, 0, sizeof(*tz)); + if (t3 != NULL) + XMEMSET(t3, 0, sizeof(*t3)); #else mp_int tmp[4]; mp_int* h = &tmp[0]; diff --git a/wolfcrypt/src/srp.c b/wolfcrypt/src/srp.c index 3409dbec43..44c48d45ef 100644 --- a/wolfcrypt/src/srp.c +++ b/wolfcrypt/src/srp.c @@ -617,6 +617,11 @@ int wc_SrpGetPublic(Srp* srp, byte* pub, word32* size) if (((i = (mp_int *)XMALLOC(sizeof(*i), srp->heap, DYNAMIC_TYPE_TMP_BUFFER)) == NULL) || ((j = (mp_int *)XMALLOC(sizeof(*j), srp->heap, DYNAMIC_TYPE_TMP_BUFFER)) == NULL)) r = MEMORY_E; + /* zeroed so the cleanup below no-ops if the init is skipped */ + if (i != NULL) + XMEMSET(i, 0, sizeof(*i)); + if (j != NULL) + XMEMSET(j, 0, sizeof(*j)); if (!r) #endif { @@ -762,6 +767,16 @@ int wc_SrpComputeKey(Srp* srp, byte* clientPubKey, word32 clientPubKeySz, temp1 = (mp_int *)XMALLOC(sizeof *temp1, srp->heap, DYNAMIC_TYPE_SRP); temp2 = (mp_int *)XMALLOC(sizeof *temp2, srp->heap, DYNAMIC_TYPE_SRP); + /* zeroed so the cleanup below no-ops if the init is skipped */ + if (u != NULL) + XMEMSET(u, 0, sizeof *u); + if (s != NULL) + XMEMSET(s, 0, sizeof *s); + if (temp1 != NULL) + XMEMSET(temp1, 0, sizeof *temp1); + if (temp2 != NULL) + XMEMSET(temp2, 0, sizeof *temp2); + if ((hash == NULL) || (digest == NULL) || (u == NULL) || diff --git a/wolfssl/internal.h b/wolfssl/internal.h index dd20de7d6c..7bfd1eb1d1 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -2335,7 +2335,8 @@ WOLFSSL_TEST_VIS int MatchDomainName(const char* pattern, int len, WOLFSSL_LOCAL int CheckForAltNames(DecodedCert* dCert, const char* domain, word32 domainLen, int* checkCN, unsigned int flags, byte isIP); -WOLFSSL_LOCAL int CheckIPAddr(DecodedCert* dCert, const char* ipasc); +WOLFSSL_LOCAL int CheckIPAddr(DecodedCert* dCert, const char* ipasc, + size_t ipascLen); WOLFSSL_LOCAL void CopyDecodedName(WOLFSSL_X509_NAME* name, DecodedCert* dCert, int nameType); #endif WOLFSSL_LOCAL int SetupTicket(WOLFSSL* ssl); diff --git a/wolfssl/wolfcrypt/pkcs7.h b/wolfssl/wolfcrypt/pkcs7.h index 6c37344c0b..c654c0b7e8 100644 --- a/wolfssl/wolfcrypt/pkcs7.h +++ b/wolfssl/wolfcrypt/pkcs7.h @@ -141,7 +141,10 @@ enum PKCS7_STATE { WC_PKCS7_DECRYPT_PWRI, WC_PKCS7_DECRYPT_ORI, - WC_PKCS7_DECRYPT_DONE + WC_PKCS7_DECRYPT_DONE, + + /* appended so the values above stay stable */ + WC_PKCS7_VERIFY_STAGE3_FOOTER }; diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 461a2f3e83..bdd5018a41 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -407,6 +407,20 @@ #define WOLF_CRYPT_FIPS_H #endif +/* A configure-generated options.h is sometimes copied in as user_settings.h to + * seed a Windows build, carrying the POSIX host's header probes with it. Drop + * the ones naming a header the target lacks: MinGW ships (so only + * MSVC drops it) but not (so all of Windows does). + * WOLFSSL_KEEP_HOST_HEADER_PROBES skips this. */ +#ifndef WOLFSSL_KEEP_HOST_HEADER_PROBES + #ifdef _MSC_VER + #undef HAVE_SYS_TIME_H + #endif + #ifdef _WIN32 + #undef HAVE_SYS_UN_H + #endif +#endif + /* Microsoft's ARM64 compiler defines _M_ARM64 but not __aarch64__. The wolfSSL * ARMv8 assembly (WOLFSSL_ARMASM) and all of its C callers are gated on * __aarch64__, so map _M_ARM64 across when building that assembly with MSVC and