diff --git a/src/ssl_certman.c b/src/ssl_certman.c index 09b8b7a5bc..8048802648 100644 --- a/src/ssl_certman.c +++ b/src/ssl_certman.c @@ -779,6 +779,45 @@ int wolfSSL_CertManagerSetCRLUnknownExtCallbackEx(WOLFSSL_CERT_MANAGER* cm, #endif /* HAVE_CRL */ #endif /* WC_ASN_UNKNOWN_EXT_CB */ +#if !defined(NO_WOLFSSL_CM_VERIFY) && \ + (!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH)) +/* Certificate verdicts a verify callback may override, matching the errors the + * TLS path also hands to it: validity dates, an untrusted or self-signed chain, + * a failed signature, weak key sizes, name, path length, key usage, unhandled + * critical extensions, and revocation. This is an allowlist, so any other error + * such as a parse, algorithm, resource, or lock failure that left no verified + * certificate fails closed and never reaches the callback. */ +static int cm_verify_err_overridable(int err) +{ + if ((err == WC_NO_ERR_TRACE(ASN_BEFORE_DATE_E)) || + (err == WC_NO_ERR_TRACE(ASN_AFTER_DATE_E)) || + (err == WC_NO_ERR_TRACE(ASN_NO_SIGNER_E)) || + (err == WC_NO_ERR_TRACE(ASN_SELF_SIGNED_E)) || + (err == WC_NO_ERR_TRACE(ASN_SIG_CONFIRM_E)) || + (err == WC_NO_ERR_TRACE(BAD_PADDING_E)) || + (err == WC_NO_ERR_TRACE(ASN_NAME_INVALID_E)) || + (err == WC_NO_ERR_TRACE(ASN_PATHLEN_INV_E)) || + (err == WC_NO_ERR_TRACE(ASN_PATHLEN_SIZE_E)) || + (err == WC_NO_ERR_TRACE(ASN_CRIT_EXT_E)) || + (err == WC_NO_ERR_TRACE(KEYUSAGE_E)) || + (err == WC_NO_ERR_TRACE(EXTKEYUSAGE_E)) || + (err == WC_NO_ERR_TRACE(RSA_KEY_SIZE_E)) || + (err == WC_NO_ERR_TRACE(ECC_KEY_SIZE_E)) || + (err == WC_NO_ERR_TRACE(FALCON_KEY_SIZE_E)) || + (err == WC_NO_ERR_TRACE(MLDSA_KEY_SIZE_E))) { + return 1; + } +#ifdef HAVE_CRL + if ((err == WC_NO_ERR_TRACE(CRL_CERT_REVOKED)) || + (err == WC_NO_ERR_TRACE(CRL_MISSING)) || + (err == WC_NO_ERR_TRACE(CRL_CERT_DATE_ERR))) { + return 1; + } +#endif + return 0; +} +#endif + #if (!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH)) || \ defined(OPENSSL_EXTRA) /* Verify the certificate. @@ -862,6 +901,20 @@ int CM_VerifyBuffer_ex(WOLFSSL_CERT_MANAGER* cm, const unsigned char* buff, #if !defined(NO_WOLFSSL_CM_VERIFY) && \ (!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH)) + /* Only a certificate-policy verdict may be handed to the callback; any + * other error leaves no verified certificate and fails closed. Classify + * this attempt's own result first so it cannot be lost. */ + if ((ret != 0) && !cm_verify_err_overridable(ret)) { + fatal = 1; + } + /* A prior load failure is what the callback should see, but only when this + * attempt did not fail closed; it too fails closed when not a verdict. */ + if ((!fatal) && (prev_err != 0)) { + ret = prev_err; + if (!cm_verify_err_overridable(ret)) { + fatal = 1; + } + } /* Use callback to perform verification too if available. */ if ((!fatal) && cm->verifyCallback) { WC_DECLARE_VAR(args, ProcPeerCertArgs, 1, 0); @@ -890,10 +943,6 @@ int CM_VerifyBuffer_ex(WOLFSSL_CERT_MANAGER* cm, const unsigned char* buff, args->dCert = cert; args->dCertInit = 1; - /* Replace value in ret with an error value passed in. */ - if (prev_err != 0) { - ret = prev_err; - } /* Use callback to verify certificate. */ ret = DoVerifyCallback(cm, NULL, ret, args); } diff --git a/tests/api.c b/tests/api.c index 2304f24f86..f36327e129 100644 --- a/tests/api.c +++ b/tests/api.c @@ -3883,6 +3883,76 @@ static int test_wolfSSL_OtherName(void) return EXPECT_RESULT(); } +#if !defined(NO_CERTS) && !defined(NO_WOLFSSL_CM_VERIFY) && \ + (!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH)) +static int cm_override_cb(int preverify, WOLFSSL_X509_STORE_CTX* store) +{ + (void)preverify; + (void)store; + return 1; /* override any error */ +} +#endif + +static int test_wolfSSL_CertManagerVerifyBuffer_internal_err(void) +{ + EXPECT_DECLS; +#if !defined(NO_CERTS) && !defined(NO_WOLFSSL_CM_VERIFY) && \ + (!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH)) + WOLFSSL_CERT_MANAGER* cm = NULL; + unsigned char bad[64]; +#if defined(USE_CERT_BUFFERS_2048) && !defined(NO_RSA) && !defined(NO_SHA256) + /* sha256WithRSAEncryption, the algorithm of server_cert_der_2048 */ + static const unsigned char sigAlgOid[9] = + { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b }; + unsigned char certBuf[2048]; + int i; + int last = -1; +#endif + + /* Malformed DER, so parsing fails with an internal error rather than a + * certificate verification verdict. */ + XMEMSET(bad, 0x30, sizeof(bad)); + bad[1] = 0x3e; + + /* A verify callback that overrides every error must not turn an internal + * error into a success. */ + ExpectNotNull(cm = wolfSSL_CertManagerNew()); + wolfSSL_CertManagerSetVerify(cm, cm_override_cb); + ExpectIntNE(wolfSSL_CertManagerVerifyBuffer(cm, bad, (long)sizeof(bad), + WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS); + + /* The certificate-loading path also reverifies with the callback, so a + * malformed CA must fail closed there too. */ + ExpectIntNE(wolfSSL_CertManagerLoadCABuffer(cm, bad, (long)sizeof(bad), + WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS); + +#if defined(USE_CERT_BUFFERS_2048) && !defined(NO_RSA) && !defined(NO_SHA256) + /* A mismatched outer signature-algorithm OID means the signature was + * never checked, so it must fail closed as well. */ + ExpectIntLE(sizeof_server_cert_der_2048, (int)sizeof(certBuf)); + if (EXPECT_SUCCESS()) { + XMEMCPY(certBuf, server_cert_der_2048, + (size_t)sizeof_server_cert_der_2048); + for (i = 0; i + (int)sizeof(sigAlgOid) <= + (int)sizeof_server_cert_der_2048; i++) { + if (XMEMCMP(certBuf + i, sigAlgOid, sizeof(sigAlgOid)) == 0) { + last = i; + } + } + ExpectIntGT(last, 0); + } + if (EXPECT_SUCCESS()) { + certBuf[last + sizeof(sigAlgOid) - 1] ^= 0x01; + ExpectIntNE(wolfSSL_CertManagerVerifyBuffer(cm, certBuf, + (long)sizeof_server_cert_der_2048, WOLFSSL_FILETYPE_ASN1), + WOLFSSL_SUCCESS); + } +#endif + wolfSSL_CertManagerFree(cm); +#endif + return EXPECT_RESULT(); +} + #ifdef HAVE_CERT_CHAIN_VALIDATION #ifndef WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION static int test_wolfSSL_CertRsaPss(void) @@ -40354,6 +40424,7 @@ TEST_CASE testCases[] = { !defined(WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION) TEST_DECL(test_wolfSSL_CertRsaPss), #endif + TEST_DECL(test_wolfSSL_CertManagerVerifyBuffer_internal_err), TEST_DECL(test_wolfSSL_CTX_load_verify_locations_ex), TEST_DECL(test_wolfSSL_CTX_load_verify_buffer_ex), TEST_DECL(test_wolfSSL_CTX_load_verify_chain_buffer_format),