Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/x509_str.c
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,27 @@ 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) {
ctx->error = X509_V_ERR_HOSTNAME_MISMATCH;
ret = WOLFSSL_FAILURE;
}
Comment on lines +746 to +752
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When hostname verification fails, this sets ctx->error but leaves ctx->error_depth unchanged (it may contain the depth from the last chain validation step). For OpenSSL-compatible behavior, hostname mismatch should report an error depth of 0 (leaf). Set error_depth explicitly (e.g., wolfSSL_X509_STORE_CTX_set_error_depth(ctx, 0)) or use SetupStoreCtxError_ex(ctx, X509_V_ERR_HOSTNAME_MISMATCH, 0).

Copilot uses AI. Check for mistakes.
}
else if (ctx->param->ipasc[0] != '\0') {
if (wolfSSL_X509_check_ip_asc(ctx->current_cert,
ctx->param->ipasc,
ctx->param->hostFlags) != WOLFSSL_SUCCESS) {
ctx->error = X509_V_ERR_IP_ADDRESS_MISMATCH;
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as hostname mismatch: on IP address mismatch this updates ctx->error but not ctx->error_depth. Please set error_depth to 0 (leaf) when reporting X509_V_ERR_IP_ADDRESS_MISMATCH, to avoid leaking a previous chain-depth value.

Suggested change
ctx->error = X509_V_ERR_IP_ADDRESS_MISMATCH;
ctx->error = X509_V_ERR_IP_ADDRESS_MISMATCH;
ctx->error_depth = 0;

Copilot uses AI. Check for mistakes.
ret = WOLFSSL_FAILURE;
}
}
}

return ret == WOLFSSL_SUCCESS ? WOLFSSL_SUCCESS : WOLFSSL_FAILURE;
}

Expand Down
68 changes: 68 additions & 0 deletions tests/api/test_x509.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,74 @@ 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);
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regression test validates the error code for hostname mismatch, but it doesn't assert the reported error depth. Adding an assertion that wolfSSL_X509_STORE_CTX_get_error_depth(ctx) == 0 in the mismatch case would catch incorrect depth reporting (e.g., if the verification logic leaves a stale chain depth behind).

Suggested change
X509_V_ERR_HOSTNAME_MISMATCH);
X509_V_ERR_HOSTNAME_MISMATCH);
ExpectIntEQ(wolfSSL_X509_STORE_CTX_get_error_depth(ctx), 0);

Copilot uses AI. Check for mistakes.
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)
Expand Down
4 changes: 3 additions & 1 deletion tests/api/test_x509.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Loading