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
67 changes: 40 additions & 27 deletions src/x509_str.c
Original file line number Diff line number Diff line change
Expand Up @@ -1193,6 +1193,46 @@ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx)
ret = X509StoreCheckPathLen(ctx);
}

/* 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.
*
* A mismatch is reported to the application verify callback the way
* OpenSSL's check_id_error() does: record error, error_depth and
* current_cert, then call it with ok=0, and let a return of 1 override.
* Without that call a callback installed to inspect or override
* verification errors never sees a hostname or IP mismatch. */
if (ctx->param != NULL) {
WOLFSSL_X509_STORE_CTX_verify_cb idVerifyCb =
X509StoreGetVerifyCb(ctx);

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;
if (idVerifyCb == NULL || idVerifyCb(0, ctx) != 1) {
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;
if (idVerifyCb == NULL || idVerifyCb(0, ctx) != 1) {
ret = WOLFSSL_FAILURE;
}
}
}
}

exit:
/* Copy back failed certs. */
numFailedCerts = wolfSSL_sk_X509_num(failedCerts);
Expand Down Expand Up @@ -1253,33 +1293,6 @@ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx)
wolfSSL_sk_X509_free(origTrustedSk);
}

/* 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;
}
}
}

/* Fail closed on the way out: every failure has to be reportable through
* X509_STORE_CTX_get_error(), or the application is told the chain was
* fine while this function reports failure. Not all of them record one -
Expand Down
104 changes: 104 additions & 0 deletions tests/api/test_ossl_x509_str.c
Original file line number Diff line number Diff line change
Expand Up @@ -1369,6 +1369,110 @@ int test_wolfSSL_X509_STORE_CTX_verify_cb(void)
return EXPECT_RESULT();
}

#if defined(OPENSSL_EXTRA) && !defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
!defined(NO_RSA) && !defined(NO_ASN_TIME) && !defined(NO_ASN)
/* Records every rejecting invocation and the error it carried, then accepts,
* the way an application overriding a verification error does. */
static int checkid_cb_rejects = 0;
static int checkid_cb_error = 0;
static int checkid_override_cb(int ok, X509_STORE_CTX* ctx)
{
if (ok == 0) {
checkid_cb_rejects++;
checkid_cb_error = X509_STORE_CTX_get_error(ctx);
}
return 1; /* accept */
}
#endif

/* A hostname or IP mismatch from X509_VERIFY_PARAM must be reported to the
* verify callback with ok=0, and the callback's acceptance must override it,
* as OpenSSL's check_id_error() does. */
int test_wolfSSL_X509_STORE_CTX_verify_cb_check_id(void)
{
EXPECT_DECLS;
#if defined(OPENSSL_EXTRA) && !defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
!defined(NO_RSA) && !defined(NO_ASN_TIME) && !defined(NO_ASN)
X509* ca = NULL;
X509* leaf = NULL;
X509_STORE* store = NULL;
X509_STORE_CTX* ctx = NULL;
X509_VERIFY_PARAM* param = NULL;

ExpectNotNull(ca = test_wolfSSL_X509_STORE_CTX_ex_helper(
"./certs/ca-cert.pem"));
/* server-cert.pem carries SAN DNS:example.com and IP:127.0.0.1. */
ExpectNotNull(leaf = test_wolfSSL_X509_STORE_CTX_ex_helper(
"./certs/server-cert.pem"));
ExpectNotNull(store = X509_STORE_new());
ExpectIntEQ(X509_STORE_add_cert(store, ca), 1);

/* Sanity check: with a matching hostname the chain verifies and the
* callback is never handed a rejection. */
checkid_cb_rejects = 0;
checkid_cb_error = 0;
ExpectNotNull(ctx = X509_STORE_CTX_new());
ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, NULL), 1);
X509_STORE_CTX_set_verify_cb(ctx, checkid_override_cb);
ExpectNotNull(param = X509_STORE_CTX_get0_param(ctx));
ExpectIntEQ(X509_VERIFY_PARAM_set1_host(param, "example.com", 0), 1);
ExpectIntEQ(X509_verify_cert(ctx), 1);
ExpectIntEQ(checkid_cb_rejects, 0);
X509_STORE_CTX_free(ctx);
ctx = NULL;
param = NULL;

/* Same chain, mismatching hostname: the callback must see ok=0 with
* X509_V_ERR_HOSTNAME_MISMATCH, and its acceptance must stand. */
checkid_cb_rejects = 0;
checkid_cb_error = 0;
ExpectNotNull(ctx = X509_STORE_CTX_new());
ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, NULL), 1);
X509_STORE_CTX_set_verify_cb(ctx, checkid_override_cb);
ExpectNotNull(param = X509_STORE_CTX_get0_param(ctx));
ExpectIntEQ(X509_VERIFY_PARAM_set1_host(param, "not-example.com", 0), 1);
ExpectIntEQ(X509_verify_cert(ctx), 1);
ExpectIntEQ(checkid_cb_rejects, 1);
ExpectIntEQ(checkid_cb_error, X509_V_ERR_HOSTNAME_MISMATCH);
X509_STORE_CTX_free(ctx);
ctx = NULL;
param = NULL;

#ifdef WOLFSSL_IP_ALT_NAME
/* Mismatching IP: same contract, with X509_V_ERR_IP_ADDRESS_MISMATCH. */
checkid_cb_rejects = 0;
checkid_cb_error = 0;
ExpectNotNull(ctx = X509_STORE_CTX_new());
ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, NULL), 1);
X509_STORE_CTX_set_verify_cb(ctx, checkid_override_cb);
ExpectNotNull(param = X509_STORE_CTX_get0_param(ctx));
ExpectIntEQ(X509_VERIFY_PARAM_set1_ip_asc(param, "10.0.0.1"), 1);
ExpectIntEQ(X509_verify_cert(ctx), 1);
ExpectIntEQ(checkid_cb_rejects, 1);
ExpectIntEQ(checkid_cb_error, X509_V_ERR_IP_ADDRESS_MISMATCH);
X509_STORE_CTX_free(ctx);
ctx = NULL;
param = NULL;
#endif /* WOLFSSL_IP_ALT_NAME */

/* With no callback installed the mismatch must still fail closed and stay
* reportable through get_error(). */
ExpectNotNull(ctx = X509_STORE_CTX_new());
ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, NULL), 1);
ExpectNotNull(param = X509_STORE_CTX_get0_param(ctx));
ExpectIntEQ(X509_VERIFY_PARAM_set1_host(param, "not-example.com", 0), 1);
ExpectIntNE(X509_verify_cert(ctx), 1);
ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_ERR_HOSTNAME_MISMATCH);

X509_STORE_CTX_free(ctx);
X509_STORE_free(store);
X509_free(leaf);
X509_free(ca);
#endif /* OPENSSL_EXTRA && !NO_CERTS && !NO_FILESYSTEM && !NO_RSA &&
* !NO_ASN_TIME && !NO_ASN */
return EXPECT_RESULT();
}

/* The trust anchor's own pathLenConstraint must bound the path (matching
* OpenSSL's -partial_chain behavior and wolfSSL's native ParseCertRelative).
* Trust chainF-ICA2 (pathlen:0) directly as a partial-chain anchor and verify
Expand Down
3 changes: 3 additions & 0 deletions tests/api/test_ossl_x509_str.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ int test_wolfSSL_X509_verify_cert_pathlen_override(void);
int test_wolfSSL_X509_verify_cert_pathlen_override_ctx_cb(void);
int test_wolfSSL_X509_verify_cert_pathlen_anchor(void);
int test_wolfSSL_X509_STORE_CTX_verify_cb(void);
int test_wolfSSL_X509_STORE_CTX_verify_cb_check_id(void);
int test_X509_verify_cert_untrusted_inter(void);
int test_X509_verify_cert_ca_no_keycertsign(void);
int test_X509_STORE_untrusted(void);
Expand Down Expand Up @@ -75,6 +76,8 @@ int test_wolfSSL_CTX_set_cert_store(void);
test_wolfSSL_X509_verify_cert_pathlen_anchor), \
TEST_DECL_GROUP("ossl_x509_store", \
test_wolfSSL_X509_STORE_CTX_verify_cb), \
TEST_DECL_GROUP("ossl_x509_store", \
test_wolfSSL_X509_STORE_CTX_verify_cb_check_id), \
TEST_DECL_GROUP("ossl_x509_store", test_X509_verify_cert_untrusted_inter), \
TEST_DECL_GROUP("ossl_x509_store", \
test_X509_verify_cert_ca_no_keycertsign), \
Expand Down
Loading