mirror of https://github.com/wolfSSL/wolfssl.git
Merge pull request #11156 from gasbytes/x509_store_ctx_verify_cb_fix
Report X509_VERIFY_PARAM hostname/IP mismatches to the verify callback so applications can inspect or override thempull/11294/head
commit
514af438d7
|
|
@ -1201,6 +1201,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:
|
||||
/* failedCerts, certsToUse and origTrustedSk hold only borrowed references;
|
||||
* free the stack nodes, not the certs. All three are per-verification
|
||||
|
|
@ -1228,33 +1268,6 @@ exit:
|
|||
wolfSSL_sk_X509_free(certsToUse);
|
||||
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 -
|
||||
|
|
|
|||
|
|
@ -1369,6 +1369,131 @@ 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 each rejection and its error, then overrides it only if
|
||||
* checkid_cb_accept says to; anything else is accepted. */
|
||||
static int checkid_cb_rejects = 0;
|
||||
static int checkid_cb_error = 0;
|
||||
static int checkid_cb_accept = 1;
|
||||
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 checkid_cb_accept;
|
||||
}
|
||||
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 */
|
||||
|
||||
/* A callback that declines to override must leave the mismatch fatal: only
|
||||
* a return of 1 overrides it. */
|
||||
checkid_cb_rejects = 0;
|
||||
checkid_cb_error = 0;
|
||||
checkid_cb_accept = 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);
|
||||
ExpectIntNE(X509_verify_cert(ctx), 1);
|
||||
ExpectIntEQ(checkid_cb_rejects, 1);
|
||||
ExpectIntEQ(checkid_cb_error, X509_V_ERR_HOSTNAME_MISMATCH);
|
||||
ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_ERR_HOSTNAME_MISMATCH);
|
||||
checkid_cb_accept = 1;
|
||||
X509_STORE_CTX_free(ctx);
|
||||
ctx = NULL;
|
||||
param = NULL;
|
||||
|
||||
/* 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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -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), \
|
||||
|
|
|
|||
Loading…
Reference in New Issue