diff --git a/src/x509_str.c b/src/x509_str.c index b224901624..cd557d05d5 100644 --- a/src/x509_str.c +++ b/src/x509_str.c @@ -740,6 +740,37 @@ 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. */ + if (ret == WOLFSSL_SUCCESS && ctx->param != NULL) { + if (ctx->param->hostName[0] != '\0') { + if (wolfSSL_X509_check_host(ctx->current_cert, + ctx->param->hostName, + XSTRLEN(ctx->param->hostName), + ctx->param->hostFlags, NULL) != WOLFSSL_SUCCESS) { +#ifndef OPENSSL_COEXIST + ctx->error = X509_V_ERR_HOSTNAME_MISMATCH; +#else + ctx->error = 1; /* Return generic error */ +#endif + ctx->error_depth = 0; + ret = WOLFSSL_FAILURE; + } + } + else if (ctx->param->ipasc[0] != '\0') { + if (wolfSSL_X509_check_ip_asc(ctx->current_cert, + ctx->param->ipasc, + ctx->param->hostFlags) != WOLFSSL_SUCCESS) { +#ifndef OPENSSL_COEXIST + ctx->error = X509_V_ERR_IP_ADDRESS_MISMATCH; +#else + ctx->error = 1; /* Return generic error */ +#endif + ctx->error_depth = 0; + ret = WOLFSSL_FAILURE; + } + } + } + return ret == WOLFSSL_SUCCESS ? WOLFSSL_SUCCESS : WOLFSSL_FAILURE; } diff --git a/tests/api/test_x509.c b/tests/api/test_x509.c index 5b63281d29..47571f795f 100644 --- a/tests/api/test_x509.c +++ b/tests/api/test_x509.c @@ -244,6 +244,75 @@ 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 §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; + + 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 d0edaa2ad5..b43f74722b 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 */