Skip to content

Commit 2d76a68

Browse files
committed
x509: reject depth-exhausted chains in wolfSSL_X509_verify_cert()
Fail compatibility-layer verification when the path-building loop runs out of its depth budget before reaching a configured trust anchor, instead of accepting the last verified link. Add a regression test.
1 parent 3e2c460 commit 2d76a68

2 files changed

Lines changed: 53 additions & 1 deletion

File tree

src/x509_str.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,18 @@ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx)
826826
depth--;
827827
}
828828

829+
/* Success requires the path to have reached a configured trust anchor
830+
* (done == 1) or to have terminated at a caller-trusted self-signed
831+
* certificate via the break above (done == 0 with depth still > 0). A
832+
* loop that instead ran out of its depth budget (depth <= 0) without
833+
* completing must fail closed: ret may still be WOLFSSL_SUCCESS from the
834+
* last link, but no trust anchor was reached. */
835+
if (ret == WOLFSSL_SUCCESS && done == 0 && depth <= 0) {
836+
SetupStoreCtxError_ex(ctx, WOLFSSL_X509_V_ERR_CERT_CHAIN_TOO_LONG,
837+
wolfSSL_sk_X509_num(ctx->chain));
838+
ret = WOLFSSL_FAILURE;
839+
}
840+
829841
exit:
830842
/* Copy back failed certs. */
831843
numFailedCerts = wolfSSL_sk_X509_num(failedCerts);
@@ -1044,7 +1056,7 @@ int wolfSSL_X509_STORE_CTX_set_ex_data_with_cleanup(
10441056
}
10451057
#endif /* HAVE_EX_DATA_CLEANUP_HOOKS */
10461058

1047-
#if defined(WOLFSSL_APACHE_HTTPD) || defined(OPENSSL_ALL)
1059+
#if defined(WOLFSSL_APACHE_HTTPD) || defined(OPENSSL_EXTRA)
10481060
void wolfSSL_X509_STORE_CTX_set_depth(WOLFSSL_X509_STORE_CTX* ctx, int depth)
10491061
{
10501062
WOLFSSL_ENTER("wolfSSL_X509_STORE_CTX_set_depth");

tests/api/test_ossl_x509_str.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1265,6 +1265,42 @@ static int test_untrusted_inter_no_stale_anchor(X509* leaf, X509* inter,
12651265
sk_X509_free(chain);
12661266
return EXPECT_RESULT();
12671267
}
1268+
1269+
/* Depth exhaustion: a chain that cannot be walked to the trusted anchor within
1270+
* the configured path-building depth budget must be rejected,.
1271+
*
1272+
* leaf-deep <- int-ca2 <- int-ca <- root (root trusted)
1273+
*
1274+
* The chain is genuine and verifies at the default depth (covered by
1275+
* test_untrusted_inter_two_level); here the depth is capped below the chain
1276+
* length so the budget is consumed before the trusted root is reached. The
1277+
* fix must report it as "certificate chain too long". */
1278+
static int test_untrusted_inter_depth_exhaustion(X509* leafDeep, X509* inter,
1279+
X509* inter2, X509* root)
1280+
{
1281+
EXPECT_DECLS;
1282+
X509_STORE* store = NULL;
1283+
X509_STORE_CTX* ctx = NULL;
1284+
STACK_OF(X509)* untrusted = NULL;
1285+
1286+
ExpectNotNull(store = X509_STORE_new());
1287+
ExpectIntEQ(X509_STORE_add_cert(store, root), 1);
1288+
ExpectNotNull(untrusted = sk_X509_new_null());
1289+
ExpectIntGT(sk_X509_push(untrusted, inter), 0);
1290+
ExpectIntGT(sk_X509_push(untrusted, inter2), 0);
1291+
ExpectNotNull(ctx = X509_STORE_CTX_new());
1292+
ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leafDeep, untrusted), 1);
1293+
/* Cap the path-building budget below the chain length so the walk runs
1294+
* out of depth before it can reach the trusted root. */
1295+
X509_STORE_CTX_set_depth(ctx, 1);
1296+
ExpectIntEQ(X509_verify_cert(ctx), 0);
1297+
ExpectIntEQ(X509_STORE_CTX_get_error(ctx),
1298+
X509_V_ERR_CERT_CHAIN_TOO_LONG);
1299+
X509_STORE_CTX_free(ctx);
1300+
X509_STORE_free(store);
1301+
sk_X509_free(untrusted);
1302+
return EXPECT_RESULT();
1303+
}
12681304
#endif /* OPENSSL_EXTRA && !NO_RSA && !NO_CERTS && !NO_FILESYSTEM */
12691305

12701306
int test_X509_verify_cert_untrusted_inter(void)
@@ -1287,6 +1323,7 @@ int test_X509_verify_cert_untrusted_inter(void)
12871323
int tamperedRes = 0;
12881324
int reusedStoreRes = 0;
12891325
int noStaleRes = 0;
1326+
int depthExhaustRes = 0;
12901327

12911328
ExpectNotNull(leaf = untrusted_inter_load(UA_CERT_DIR "leaf-cert.pem"));
12921329
ExpectNotNull(leafDeep =
@@ -1316,13 +1353,16 @@ int test_X509_verify_cert_untrusted_inter(void)
13161353
tamperedInter, root);
13171354
noStaleRes = test_untrusted_inter_no_stale_anchor(leaf, inter,
13181355
root);
1356+
depthExhaustRes = test_untrusted_inter_depth_exhaustion(leafDeep,
1357+
inter, inter2, root);
13191358
ExpectIntEQ(sanityRes, 1);
13201359
ExpectIntEQ(twoLevelRes, 1);
13211360
ExpectIntEQ(emptyStoreRes, 1);
13221361
ExpectIntEQ(wrongAnchorRes, 1);
13231362
ExpectIntEQ(tamperedRes, 1);
13241363
ExpectIntEQ(reusedStoreRes, 1);
13251364
ExpectIntEQ(noStaleRes, 1);
1365+
ExpectIntEQ(depthExhaustRes, 1);
13261366
}
13271367

13281368
X509_free(leaf);

0 commit comments

Comments
 (0)