From 99ae809e08816bf7131b9c58078c4f0fe618ca4c Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 11 Mar 2026 18:19:29 +0100 Subject: [PATCH] Improve ctx->param handling - wolfSSL_X509_verify_cert: add host check from `ctx->param` - wolfSSL_X509_verify_cert: Set `ctx->error_depth` on error - Use WOLFSSL_-prefixed error constants (always available) instead of OPENSSL_COEXIST-guarded macros, fixing error code mismatch in coexist builds - Set ctx->current_cert = orig on hostname/IP mismatch so error reporting aligns with error_depth = 0 (leaf cert) - Add IP address verification test cases (match + mismatch) --- src/internal.c | 6 +++ src/x509_str.c | 27 ++++++++++ tests/api.c | 2 + tests/api/test_x509.c | 98 ++++++++++++++++++++++++++++++++++++ tests/api/test_x509.h | 4 +- wolfssl/ssl.h | 4 +- wolfssl/wolfcrypt/settings.h | 5 ++ 7 files changed, 144 insertions(+), 2 deletions(-) diff --git a/src/internal.c b/src/internal.c index 2f822d081f3..49152482fa7 100644 --- a/src/internal.c +++ b/src/internal.c @@ -27084,6 +27084,12 @@ static const char* wolfSSL_ERR_reason_error_string_OpenSSL(unsigned long e) case WOLFSSL_X509_V_ERR_SUBJECT_ISSUER_MISMATCH: return "subject issuer mismatch"; + case WOLFSSL_X509_V_ERR_HOSTNAME_MISMATCH: + return "hostname mismatch"; + + case WOLFSSL_X509_V_ERR_IP_ADDRESS_MISMATCH: + return "IP address mismatch"; + default: return NULL; } diff --git a/src/x509_str.c b/src/x509_str.c index b2249016244..2c48c419644 100644 --- a/src/x509_str.c +++ b/src/x509_str.c @@ -740,6 +740,33 @@ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx) wolfSSL_sk_X509_free(certsToUse); } + /* Enforce hostname / IP verification from X509_VERIFY_PARAM if set. + * Always check against the leaf (end-entity) certificate, captured in + * orig before the chain-building loop modified ctx->current_cert. */ + if (ctx->param != NULL) { + if (ret == WOLFSSL_SUCCESS && ctx->param->hostName[0] != '\0') { + if (wolfSSL_X509_check_host(orig, + ctx->param->hostName, + XSTRLEN(ctx->param->hostName), + ctx->param->hostFlags, NULL) != WOLFSSL_SUCCESS) { + ctx->error = WOLFSSL_X509_V_ERR_HOSTNAME_MISMATCH; + ctx->error_depth = 0; + ctx->current_cert = orig; + ret = WOLFSSL_FAILURE; + } + } + if (ret == WOLFSSL_SUCCESS && ctx->param->ipasc[0] != '\0') { + if (wolfSSL_X509_check_ip_asc(orig, + ctx->param->ipasc, + ctx->param->hostFlags) != WOLFSSL_SUCCESS) { + ctx->error = WOLFSSL_X509_V_ERR_IP_ADDRESS_MISMATCH; + ctx->error_depth = 0; + ctx->current_cert = orig; + ret = WOLFSSL_FAILURE; + } + } + } + return ret == WOLFSSL_SUCCESS ? WOLFSSL_SUCCESS : WOLFSSL_FAILURE; } diff --git a/tests/api.c b/tests/api.c index 794a9a58d4a..677a0c19434 100644 --- a/tests/api.c +++ b/tests/api.c @@ -27168,6 +27168,8 @@ static int error_test(void) {17, 15}, {19, 19}, {27, 26 }, + {61, 30}, + {63, 63}, #endif { -9, WC_SPAN1_FIRST_E + 1 }, { -124, -124 }, diff --git a/tests/api/test_x509.c b/tests/api/test_x509.c index 5b63281d29b..2482823a81d 100644 --- a/tests/api/test_x509.c +++ b/tests/api/test_x509.c @@ -244,6 +244,104 @@ int test_x509_GetCAByAKID(void) return EXPECT_RESULT(); } +/* Regression test: wolfSSL_X509_verify_cert() must honour the hostname set via + * X509_VERIFY_PARAM_set1_host(). Before the fix the hostname was stored in + * ctx->param->hostName but never consulted, so any chain-valid certificate + * would pass regardless of hostname mismatch (RFC 6125 sec. 6.4.1 violation). + * + * Uses existing PEM fixtures: + * svrCertFile - CN=www.wolfssl.com, SAN DNS=example.com, SAN IP=127.0.0.1 + * caCertFile - CA that signed svrCertFile + */ +int test_x509_verify_cert_hostname_check(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_FILESYSTEM) && !defined(NO_RSA) + WOLFSSL_X509_STORE* store = NULL; + WOLFSSL_X509_STORE_CTX* ctx = NULL; + WOLFSSL_X509* ca = NULL; + WOLFSSL_X509* leaf = NULL; + WOLFSSL_X509_VERIFY_PARAM* param = NULL; + + ExpectNotNull(store = wolfSSL_X509_STORE_new()); + ExpectNotNull(ca = wolfSSL_X509_load_certificate_file(caCertFile, + SSL_FILETYPE_PEM)); + ExpectIntEQ(wolfSSL_X509_STORE_add_cert(store, ca), WOLFSSL_SUCCESS); + + ExpectNotNull(leaf = wolfSSL_X509_load_certificate_file(svrCertFile, + SSL_FILETYPE_PEM)); + + /* Case 1: no hostname constraint - must succeed. */ + ExpectNotNull(ctx = wolfSSL_X509_STORE_CTX_new()); + ExpectIntEQ(wolfSSL_X509_STORE_CTX_init(ctx, store, leaf, NULL), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_verify_cert(ctx), WOLFSSL_SUCCESS); + wolfSSL_X509_STORE_CTX_free(ctx); + ctx = NULL; + + /* Case 2: hostname matches a SAN DNS entry - must succeed. */ + ExpectNotNull(ctx = wolfSSL_X509_STORE_CTX_new()); + ExpectIntEQ(wolfSSL_X509_STORE_CTX_init(ctx, store, leaf, NULL), + WOLFSSL_SUCCESS); + param = wolfSSL_X509_STORE_CTX_get0_param(ctx); + ExpectNotNull(param); + ExpectIntEQ(wolfSSL_X509_VERIFY_PARAM_set1_host(param, "example.com", + XSTRLEN("example.com")), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_verify_cert(ctx), WOLFSSL_SUCCESS); + wolfSSL_X509_STORE_CTX_free(ctx); + ctx = NULL; + + /* Case 3: hostname does not match - must FAIL with the right error code. */ + ExpectNotNull(ctx = wolfSSL_X509_STORE_CTX_new()); + ExpectIntEQ(wolfSSL_X509_STORE_CTX_init(ctx, store, leaf, NULL), + WOLFSSL_SUCCESS); + param = wolfSSL_X509_STORE_CTX_get0_param(ctx); + ExpectNotNull(param); + ExpectIntEQ(wolfSSL_X509_VERIFY_PARAM_set1_host(param, "wrong.com", + XSTRLEN("wrong.com")), WOLFSSL_SUCCESS); + ExpectIntNE(wolfSSL_X509_verify_cert(ctx), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_STORE_CTX_get_error(ctx), + X509_V_ERR_HOSTNAME_MISMATCH); + ExpectIntEQ(wolfSSL_X509_STORE_CTX_get_error_depth(ctx), 0); + wolfSSL_X509_STORE_CTX_free(ctx); + ctx = NULL; + +#ifdef WOLFSSL_IP_ALT_NAME + /* Case 4: IP matches a SAN IP entry - must succeed. */ + ExpectNotNull(ctx = wolfSSL_X509_STORE_CTX_new()); + ExpectIntEQ(wolfSSL_X509_STORE_CTX_init(ctx, store, leaf, NULL), + WOLFSSL_SUCCESS); + param = wolfSSL_X509_STORE_CTX_get0_param(ctx); + ExpectNotNull(param); + ExpectIntEQ(wolfSSL_X509_VERIFY_PARAM_set1_ip_asc(param, "127.0.0.1"), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_verify_cert(ctx), WOLFSSL_SUCCESS); + wolfSSL_X509_STORE_CTX_free(ctx); + ctx = NULL; + + /* Case 5: IP does not match - must FAIL with the right error code. */ + ExpectNotNull(ctx = wolfSSL_X509_STORE_CTX_new()); + ExpectIntEQ(wolfSSL_X509_STORE_CTX_init(ctx, store, leaf, NULL), + WOLFSSL_SUCCESS); + param = wolfSSL_X509_STORE_CTX_get0_param(ctx); + ExpectNotNull(param); + ExpectIntEQ(wolfSSL_X509_VERIFY_PARAM_set1_ip_asc(param, "192.168.1.1"), + WOLFSSL_SUCCESS); + ExpectIntNE(wolfSSL_X509_verify_cert(ctx), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_STORE_CTX_get_error(ctx), + X509_V_ERR_IP_ADDRESS_MISMATCH); + ExpectIntEQ(wolfSSL_X509_STORE_CTX_get_error_depth(ctx), 0); + wolfSSL_X509_STORE_CTX_free(ctx); + ctx = NULL; +#endif /* WOLFSSL_IP_ALT_NAME */ + + wolfSSL_X509_free(leaf); + wolfSSL_X509_free(ca); + wolfSSL_X509_STORE_free(store); +#endif /* OPENSSL_EXTRA && !NO_FILESYSTEM && !NO_RSA */ + return EXPECT_RESULT(); +} + int test_x509_set_serialNumber(void) { #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) diff --git a/tests/api/test_x509.h b/tests/api/test_x509.h index d0edaa2ad5a..b43f74722b1 100644 --- a/tests/api/test_x509.h +++ b/tests/api/test_x509.h @@ -25,10 +25,12 @@ int test_x509_rfc2818_verification_callback(void); int test_x509_GetCAByAKID(void); int test_x509_set_serialNumber(void); +int test_x509_verify_cert_hostname_check(void); #define TEST_X509_DECLS \ TEST_DECL_GROUP("x509", test_x509_rfc2818_verification_callback), \ TEST_DECL_GROUP("x509", test_x509_GetCAByAKID), \ - TEST_DECL_GROUP("x509", test_x509_set_serialNumber) + TEST_DECL_GROUP("x509", test_x509_set_serialNumber), \ + TEST_DECL_GROUP("x509", test_x509_verify_cert_hostname_check) #endif /* WOLFCRYPT_TEST_X509_H */ diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 17d108b592e..1c0301ab66e 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -2685,7 +2685,9 @@ enum { WOLFSSL_X509_V_ERR_PATH_LENGTH_EXCEEDED = 25, WOLFSSL_X509_V_ERR_CERT_REJECTED = 28, WOLFSSL_X509_V_ERR_SUBJECT_ISSUER_MISMATCH = 29, - WC_OSSL_V509_V_ERR_MAX = 30, + WOLFSSL_X509_V_ERR_HOSTNAME_MISMATCH = 62, + WOLFSSL_X509_V_ERR_IP_ADDRESS_MISMATCH = 64, + WC_OSSL_V509_V_ERR_MAX = 65, #ifdef HAVE_OCSP /* OCSP Flags */ diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index dd66a3d7e22..32f08095fd5 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -4819,6 +4819,11 @@ extern void uITRON4_free(void *p) ; #error "OPENSSL_ALL can not be defined with OPENSSL_COEXIST" #endif +/* OPENSSL_ALL requires WOLFSSL_IP_ALT_NAME for IP SAN verification. */ +#if defined(OPENSSL_ALL) && !defined(WOLFSSL_IP_ALT_NAME) + #error "OPENSSL_ALL requires WOLFSSL_IP_ALT_NAME" +#endif + #if !defined(NO_DSA) && defined(NO_SHA) #error "Please disable DSA if disabling SHA-1" #endif