mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-09 09:30:55 +02:00
X509 validation fixes
This commit is contained in:
@@ -13542,6 +13542,57 @@ static int PatternHasWildcardInALabel(const char* pattern, word32 patternLen)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Validate the placement of a wildcard ('*') in a presented identifier per
|
||||
* RFC 6125 sec. 6.4.3 / RFC 9525 sec. 6.3 and CA/Browser Forum Baseline
|
||||
* Requirements sec. 3.2.2.6:
|
||||
* - a wildcard may only appear in the left-most label of the pattern, and
|
||||
* - a left-most label consisting solely of the wildcard ("*") may match only
|
||||
* when at least two further labels (i.e. at least two dots) follow it.
|
||||
*
|
||||
* This rejects a bare "*" (matches any single-label name), "*.com" (wildcard
|
||||
* immediately to the left of a registry/public suffix), and
|
||||
* "foo.*.example.com" (wildcard not in the left-most label), while still
|
||||
* accepting the legitimate "*.example.com" form. Partial left-most wildcards
|
||||
* such as "a*" or "a*b*" retain their existing matching behavior - they are
|
||||
* not bare wildcard labels and are not subject to the two-label requirement.
|
||||
* pattern/patternLen must already have any single trailing FQDN dot stripped.
|
||||
*
|
||||
* Returns 1 if the pattern has no wildcard or its wildcard placement is
|
||||
* acceptable, 0 otherwise. */
|
||||
static int WildcardPlacementOK(const char* pattern, word32 patternLen)
|
||||
{
|
||||
word32 i;
|
||||
int sawWildcard = 0;
|
||||
int sawDot = 0;
|
||||
int dots = 0;
|
||||
|
||||
for (i = 0; i < patternLen; i++) {
|
||||
if (pattern[i] == '*') {
|
||||
/* A wildcard is only permitted in the left-most label: reject any
|
||||
* '*' that appears after a label separator. */
|
||||
if (sawDot)
|
||||
return 0;
|
||||
sawWildcard = 1;
|
||||
}
|
||||
else if (pattern[i] == '.') {
|
||||
sawDot = 1;
|
||||
dots++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sawWildcard)
|
||||
return 1;
|
||||
|
||||
/* A left-most label that is exactly "*" (a bare wildcard label) requires at
|
||||
* least two further labels. This rejects a bare "*" (0 dots) and "*.tld"
|
||||
* (1 dot) but still allows "*.example.com" (2 dots). */
|
||||
if (pattern[0] == '*' && (patternLen == 1 || pattern[1] == '.') &&
|
||||
dots < 2)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Match names with wildcards, each wildcard can represent a single name
|
||||
component or fragment but not multiple names, i.e.,
|
||||
*.z.com matches y.z.com but not x.y.z.com
|
||||
@@ -13598,6 +13649,13 @@ int MatchDomainName(const char* pattern, int patternLen, const char* str,
|
||||
}
|
||||
}
|
||||
|
||||
/* RFC 6125 sec. 6.4.3 / RFC 9525 sec. 6.3 + CA/Browser Forum BR
|
||||
* sec. 3.2.2.6: reject a pattern whose wildcard is not confined to the
|
||||
* left-most label, or that has fewer than two labels to the right of the
|
||||
* wildcard (e.g. "*", "*.com", "foo.*.example.com"). */
|
||||
if (!WildcardPlacementOK(pattern, (word32)patternLen))
|
||||
return 0;
|
||||
|
||||
while (patternLen > 0) {
|
||||
/* Get the next pattern char to evaluate */
|
||||
char p = (char)XTOLOWER((unsigned char)*pattern);
|
||||
|
||||
+100
@@ -629,6 +629,99 @@ static int X509StoreCertIsTrusted(WOLFSSL_X509_STORE* store,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Enforce the BasicConstraints pathLenConstraint (RFC 5280 sec. 4.2.1.9 and
|
||||
* the path validation rules in sec. 6.1.4 (l)/(m)) over the certification path
|
||||
* assembled in ctx->chain.
|
||||
*
|
||||
* wolfSSL_X509_verify_cert() authenticates each certificate individually via
|
||||
* the CertManager, which parses every certificate as CERT_TYPE. The issuer
|
||||
* pathLen check in ParseCertRelative() is gated on a non-CERT_TYPE certificate
|
||||
* type (it is reached on the TLS handshake path via CHAIN_CERT_TYPE), so the
|
||||
* OpenSSL-compatibility path never enforced it. Re-create that check here over
|
||||
* the completed path so that a CA asserting pathlen:N cannot issue more than N
|
||||
* subordinate intermediate CAs.
|
||||
*
|
||||
* ctx->chain is ordered leaf first (index 0) up to the trust anchor (highest
|
||||
* index). Walk from the trust anchor down toward the leaf, tracking the
|
||||
* remaining number of non-self-issued intermediate certificates permitted.
|
||||
* WOLFSSL_MAX_PATH_LEN is used as the "no constraint" sentinel, mirroring
|
||||
* InitDecodedCert_ex()/ParseCertRelative(). The leaf (index 0) issues nothing
|
||||
* and is therefore not subject to the constraint.
|
||||
*
|
||||
* Returns WOLFSSL_SUCCESS if the path satisfies every pathLenConstraint, or
|
||||
* WOLFSSL_FAILURE (with ctx->error set) on the first violation. */
|
||||
static int X509StoreCheckPathLen(WOLFSSL_X509_STORE_CTX* ctx)
|
||||
{
|
||||
int num;
|
||||
int i;
|
||||
word32 maxPathLen = WOLFSSL_MAX_PATH_LEN;
|
||||
WOLFSSL_X509* anchor;
|
||||
|
||||
if (ctx == NULL || ctx->chain == NULL)
|
||||
return WOLFSSL_SUCCESS;
|
||||
|
||||
num = wolfSSL_sk_X509_num(ctx->chain);
|
||||
/* A pathLen violation requires at least one intermediate between the leaf
|
||||
* (index 0) and the trust anchor, i.e. a chain of three or more. */
|
||||
if (num < 3)
|
||||
return WOLFSSL_SUCCESS;
|
||||
|
||||
/* The trust anchor (top of chain) is not part of the prospective
|
||||
* certification path (RFC 5280 sec. 6.1): it does not consume path-length
|
||||
* budget. A self-signed anchor that asserts its own pathLenConstraint does
|
||||
* still bound the path, matching ParseCertRelative()'s trust-anchor
|
||||
* handling, so seed the budget from it. The partial-chain branch of
|
||||
* wolfSSL_X509_verify_cert() pushes the terminal certificate twice, so the
|
||||
* anchor pointer can also appear at num-2; it is skipped by pointer below
|
||||
* to avoid double-counting. */
|
||||
anchor = wolfSSL_sk_X509_value(ctx->chain, num - 1);
|
||||
if (anchor != NULL && anchor->isCa && anchor->basicConstPlSet)
|
||||
maxPathLen = (word32)anchor->pathLength;
|
||||
|
||||
for (i = num - 2; i >= 1; i--) {
|
||||
WOLFSSL_X509* cert = wolfSSL_sk_X509_value(ctx->chain, i);
|
||||
int selfIssued;
|
||||
|
||||
if (cert == NULL || cert == anchor)
|
||||
continue;
|
||||
|
||||
selfIssued =
|
||||
(wolfSSL_X509_NAME_cmp(&cert->issuer, &cert->subject) == 0);
|
||||
|
||||
/* RFC 5280 sec. 6.1.4 (l): a non-self-issued certificate consumes one
|
||||
* unit of the issuer's remaining path length budget. */
|
||||
if (!selfIssued) {
|
||||
if (maxPathLen == 0) {
|
||||
SetupStoreCtxError_ex(ctx,
|
||||
WOLFSSL_X509_V_ERR_PATH_LENGTH_EXCEEDED, i);
|
||||
#if defined(OPENSSL_ALL) || defined(WOLFSSL_QT)
|
||||
/* Allow an application verify callback to override, matching
|
||||
* the INVALID_CA handling in wolfSSL_X509_verify_cert(). */
|
||||
if (ctx->store != NULL && ctx->store->verify_cb != NULL &&
|
||||
ctx->store->verify_cb(0, ctx) == 1) {
|
||||
/* Overridden: keep walking without decrementing (budget is
|
||||
* already exhausted). */
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
return WOLFSSL_FAILURE;
|
||||
}
|
||||
else if (maxPathLen != WOLFSSL_MAX_PATH_LEN) {
|
||||
maxPathLen--;
|
||||
}
|
||||
}
|
||||
|
||||
/* RFC 5280 sec. 6.1.4 (m): tighten the budget with this CA's own
|
||||
* pathLenConstraint, if present. */
|
||||
if (cert->isCa && cert->basicConstPlSet &&
|
||||
(word32)cert->pathLength < maxPathLen) {
|
||||
maxPathLen = (word32)cert->pathLength;
|
||||
}
|
||||
}
|
||||
|
||||
return WOLFSSL_SUCCESS;
|
||||
}
|
||||
|
||||
/* Verifies certificate chain using WOLFSSL_X509_STORE_CTX
|
||||
* returns 1 on success or <= 0 on failure.
|
||||
*/
|
||||
@@ -878,6 +971,13 @@ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx)
|
||||
ret = WOLFSSL_FAILURE;
|
||||
}
|
||||
|
||||
/* RFC 5280 sec. 6.1.4: the per-certificate CertManager verification above
|
||||
* does not enforce the issuer's BasicConstraints pathLenConstraint on this
|
||||
* API path, so check it over the assembled path before reporting success. */
|
||||
if (ret == WOLFSSL_SUCCESS) {
|
||||
ret = X509StoreCheckPathLen(ctx);
|
||||
}
|
||||
|
||||
exit:
|
||||
/* Copy back failed certs. */
|
||||
numFailedCerts = wolfSSL_sk_X509_num(failedCerts);
|
||||
|
||||
@@ -1862,6 +1862,82 @@ int test_wolfSSL_X509_check_host_embedded_nul_san(void)
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/* Verify that MatchDomainName() enforces RFC 6125 sec. 6.4.3 / RFC 9525
|
||||
* sec. 6.3 and CA/Browser Forum BR sec. 3.2.2.6 wildcard placement rules:
|
||||
* a wildcard is confined to the left-most label, and a bare wildcard label
|
||||
* ("*") requires at least two further labels. Regression test for the
|
||||
* x509-limbo findings that "*", "*.com" and "foo.*.example.com" were matched.
|
||||
*
|
||||
* MatchDomainName() is exposed for testing via the visibility mechanism
|
||||
* declared in wolfssl/internal.h. */
|
||||
int test_wolfSSL_MatchDomainName_wildcard(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if !defined(NO_ASN) && !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS)
|
||||
static const struct {
|
||||
const char* pattern;
|
||||
const char* host;
|
||||
unsigned int flags;
|
||||
int expected; /* 1 = match, 0 = no match */
|
||||
const char* note;
|
||||
} cases[] = {
|
||||
/* --- The reported forbidden patterns must NOT match. --- */
|
||||
/* Bare wildcard: matches any single-label name. */
|
||||
{ "*", "com", 0, 0, "bare wildcard" },
|
||||
{ "*", "anything", 0, 0,
|
||||
"bare wildcard 2" },
|
||||
/* Wildcard not in the left-most label. */
|
||||
{ "foo.*.example.com", "foo.bar.example.com", 0, 0,
|
||||
"wildcard in middle label" },
|
||||
{ "foo.*.example.com", "foo.x.example.com", 0, 0,
|
||||
"wildcard in middle label 2" },
|
||||
/* Bare wildcard immediately left of a public/registry suffix. */
|
||||
{ "*.com", "example.com", 0, 0,
|
||||
"public-suffix wildcard" },
|
||||
{ "*.com", "evil.com", 0, 0,
|
||||
"public-suffix wildcard 2" },
|
||||
/* Two label-spanning wildcards: the second is not left-most. */
|
||||
{ "*.*.example.com", "a.b.example.com", 0, 0,
|
||||
"second wildcard not left-most" },
|
||||
|
||||
/* --- Legitimate wildcards must still match. --- */
|
||||
{ "*.example.com", "foo.example.com", 0, 1,
|
||||
"single left-most wildcard" },
|
||||
{ "*.example.com", "bar.example.com", 0, 1,
|
||||
"single left-most wildcard 2" },
|
||||
/* Two labels after the wildcard is sufficient; no public-suffix list
|
||||
* is consulted (matching OpenSSL). */
|
||||
{ "*.co.uk", "foo.co.uk", 0, 1,
|
||||
"two labels after wildcard" },
|
||||
/* Partial left-most wildcards retain their existing behavior and are
|
||||
* not subject to the bare-wildcard two-label requirement. */
|
||||
{ "a*.example.com", "abc.example.com", 0, 1,
|
||||
"partial left-most wildcard" },
|
||||
/* A wildcard never spans a label separator. */
|
||||
{ "*.example.com", "foo.bar.example.com", 0, 0,
|
||||
"wildcard does not cross a dot" },
|
||||
};
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
|
||||
int got = MatchDomainName(
|
||||
cases[i].pattern, (int)XSTRLEN(cases[i].pattern),
|
||||
cases[i].host, (word32)XSTRLEN(cases[i].host),
|
||||
cases[i].flags);
|
||||
ExpectIntEQ(got, cases[i].expected);
|
||||
if (! EXPECT_SUCCESS()) {
|
||||
fprintf(stderr,
|
||||
"MatchDomainName(\"%s\", \"%s\", flags=0x%x) = %d, "
|
||||
"expected %d (%s)\n",
|
||||
cases[i].pattern, cases[i].host, cases[i].flags,
|
||||
got, cases[i].expected, cases[i].note);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* !NO_ASN && !WOLFCRYPT_ONLY && !NO_CERTS */
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
int test_wolfSSL_X509_max_altnames(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
|
||||
@@ -51,6 +51,7 @@ int test_wolfSSL_X509_name_match3(void);
|
||||
int test_wolfssl_local_IsValidFQDN(void);
|
||||
int test_wolfSSL_MatchDomainName_idn(void);
|
||||
int test_wolfSSL_X509_check_host_embedded_nul_san(void);
|
||||
int test_wolfSSL_MatchDomainName_wildcard(void);
|
||||
int test_wolfSSL_X509_max_altnames(void);
|
||||
int test_wolfSSL_X509_max_name_constraints(void);
|
||||
int test_wolfSSL_X509_check_ca(void);
|
||||
@@ -85,6 +86,7 @@ int test_wolfSSL_X509_cmp(void);
|
||||
TEST_DECL_GROUP("ossl_x509", test_wolfssl_local_IsValidFQDN), \
|
||||
TEST_DECL_GROUP("ossl_x509", test_wolfSSL_MatchDomainName_idn), \
|
||||
TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_check_host_embedded_nul_san),\
|
||||
TEST_DECL_GROUP("ossl_x509", test_wolfSSL_MatchDomainName_wildcard), \
|
||||
TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_max_altnames), \
|
||||
TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_max_name_constraints), \
|
||||
TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_check_ca), \
|
||||
|
||||
@@ -957,6 +957,173 @@ static int test_wolfSSL_X509_STORE_CTX_ex12(void)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(OPENSSL_EXTRA) && !defined(NO_CERTS) && defined(HAVE_ECC)
|
||||
/* Regression test for the x509-limbo "pathlen" finding:
|
||||
* wolfSSL_X509_verify_cert() must enforce the BasicConstraints
|
||||
* pathLenConstraint (RFC 5280 sec. 4.2.1.9 / sec. 6.1.4). A CA asserting
|
||||
* pathlen:0 may only issue end-entity certificates; it must not be permitted
|
||||
* to issue a further intermediate CA.
|
||||
*
|
||||
* root -> ica0 (CA, pathlen:0) -> ica1 (CA, pathlen:0) -> leaf
|
||||
*
|
||||
* ica1 is an intermediate CA following ica0, which ica0's pathlen:0 forbids,
|
||||
* so the chain must be rejected with X509_V_ERR_PATH_LENGTH_EXCEEDED. Before
|
||||
* the fix this OpenSSL-compatibility path accepted the chain because each
|
||||
* certificate was verified individually (as CERT_TYPE) without the issuer
|
||||
* pathLen check the TLS handshake path performs. */
|
||||
static X509* pathlen_pem_to_x509(const char* pem)
|
||||
{
|
||||
X509* x = NULL;
|
||||
BIO* bio = BIO_new_mem_buf(pem, -1);
|
||||
if (bio != NULL) {
|
||||
x = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
||||
BIO_free(bio);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
static int test_wolfSSL_X509_verify_cert_pathlen(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
/* x509-limbo intermediate-pathlen-0 chain (NIST P-256). */
|
||||
static const char* root_pem =
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIBjzCCATWgAwIBAgIUBr1M7Bi+lJsTuNKLX1Me8do6eaQwCgYIKoZIzj0EAwIw\n"
|
||||
"GjEYMBYGA1UEAwwPeDUwOS1saW1iby1yb290MCAXDTcwMDEwMTAwMDAwMVoYDzI5\n"
|
||||
"NjkwNTAzMDAwMDAxWjAaMRgwFgYDVQQDDA94NTA5LWxpbWJvLXJvb3QwWTATBgcq\n"
|
||||
"hkjOPQIBBggqhkjOPQMBBwNCAAQbEiFksWbYAbT6XaE4bwPlfA9TBdDVohu/uCIL\n"
|
||||
"xP51Zj39ZQijgxN9jxXyvCgo8Of/x5M0IHSUuc17eaBD+EEbo1cwVTAPBgNVHRMB\n"
|
||||
"Af8EBTADAQH/MAsGA1UdDwQEAwIBBjAWBgNVHREEDzANggtleGFtcGxlLmNvbTAd\n"
|
||||
"BgNVHQ4EFgQUonHZJ38dXr7snblbNqaxj6KZwOYwCgYIKoZIzj0EAwIDSAAwRQIh\n"
|
||||
"ANxM3gjJ/3FIvHLgFt9MlFXDUZs38+f+90xZ+UWEtg/sAiBwDNrH5LaFE+kGvHih\n"
|
||||
"aXu0ueUy9q+2v7FMk1DqDF5QMA==\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
static const char* ica0_pem =
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIB/zCCAaWgAwIBAgIUWQmq3hQYYc2aAeHbkAhBu2TswggwCgYIKoZIzj0EAwIw\n"
|
||||
"GjEYMBYGA1UEAwwPeDUwOS1saW1iby1yb290MCAXDTcwMDEwMTAwMDAwMVoYDzI5\n"
|
||||
"NjkwNTAzMDAwMDAxWjBmMTgwNgYDVQQLDC8zODQ3NTQ4NjM0MDcyNTc1NDQyNDQy\n"
|
||||
"MTQxNDk1NTczNzU0OTc0NTAyNTYxMjE5NjEqMCgGA1UEAwwheDUwOS1saW1iby1p\n"
|
||||
"bnRlcm1lZGlhdGUtcGF0aGxlbi0wMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE\n"
|
||||
"htjb5R5OipW7n8rfvqA7yP/yxpdq9QzwSxC41RjoE//1SX16xsjOAIOYu/1L8iEq\n"
|
||||
"1Y6x7yBnkoFFpWW0R2JN0aN7MHkwEgYDVR0TAQH/BAgwBgEB/wIBADALBgNVHQ8E\n"
|
||||
"BAMCAgQwFgYDVR0RBA8wDYILZXhhbXBsZS5jb20wHwYDVR0jBBgwFoAUonHZJ38d\n"
|
||||
"Xr7snblbNqaxj6KZwOYwHQYDVR0OBBYEFI5psdjr2bzbP8AYzh747r3zKXjzMAoG\n"
|
||||
"CCqGSM49BAMCA0gAMEUCIQChdmrAmCCIGBKdR31PSCKcBFkhqIg1rFH5n9ISC0XT\n"
|
||||
"MwIgBpm/FQrFVRPNXUq3Pjp5nCCQNuusc/UF0WQtogoxMcs=\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
static const char* ica1_pem =
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIICTDCCAfKgAwIBAgIUUzHoF6QQi7Q1abKHM2OkqXAyhXAwCgYIKoZIzj0EAwIw\n"
|
||||
"ZjE4MDYGA1UECwwvMzg0NzU0ODYzNDA3MjU3NTQ0MjQ0MjE0MTQ5NTU3Mzc1NDk3\n"
|
||||
"NDUwMjU2MTIxOTYxKjAoBgNVBAMMIXg1MDktbGltYm8taW50ZXJtZWRpYXRlLXBh\n"
|
||||
"dGhsZW4tMDAgFw03MDAxMDEwMDAwMDFaGA8yOTY5MDUwMzAwMDAwMVowZzE5MDcG\n"
|
||||
"A1UECwwwNTA4MzE1NzY5OTY4MTEzNTA3Njc4NzYzNDMxNDk4OTE0NTU3NTI4MDQ4\n"
|
||||
"OTc2MzkyMSowKAYDVQQDDCF4NTA5LWxpbWJvLWludGVybWVkaWF0ZS1wYXRobGVu\n"
|
||||
"LTAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQJAU5C0BQNKWv1lpCWYvrbguNZ\n"
|
||||
"7Ru3850SYzxAHVdqoqpXQqz1rxMGkIIQzNk1GhUXbMUVbQD0jvaJcoGfkNO5o3sw\n"
|
||||
"eTASBgNVHRMBAf8ECDAGAQH/AgEAMAsGA1UdDwQEAwICBDAWBgNVHREEDzANggtl\n"
|
||||
"eGFtcGxlLmNvbTAfBgNVHSMEGDAWgBSOabHY69m82z/AGM4e+O698yl48zAdBgNV\n"
|
||||
"HQ4EFgQUCRY9Zhvn1ujILJxVlXN6ngBEGUcwCgYIKoZIzj0EAwIDSAAwRQIhAPWq\n"
|
||||
"eItvUILeT5ZV1sA/2T2KXLmhO+lyaIJKbayTWTluAiBAnlFFqQhLRPC9aXnmvzld\n"
|
||||
"4OQO4zOBVTRVR1fyTaGRLA==\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
static const char* leaf_pem =
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIB/jCCAaOgAwIBAgIUUgfgeS9xej4Xq/a8Bg2Dzj3MedEwCgYIKoZIzj0EAwIw\n"
|
||||
"ZzE5MDcGA1UECwwwNTA4MzE1NzY5OTY4MTEzNTA3Njc4NzYzNDMxNDk4OTE0NTU3\n"
|
||||
"NTI4MDQ4OTc2MzkyMSowKAYDVQQDDCF4NTA5LWxpbWJvLWludGVybWVkaWF0ZS1w\n"
|
||||
"YXRobGVuLTAwIBcNNzAwMTAxMDAwMDAxWhgPMjk2OTA1MDMwMDAwMDFaMBYxFDAS\n"
|
||||
"BgNVBAMMC2V4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERG/C\n"
|
||||
"Q3diuQGNPeztXUpMthR9/695MnMj/tpF6NHkEBO91bBKFdjhnievo6XnpfEOer/z\n"
|
||||
"nHvUEwH3UH7swoP3qKN8MHowHQYDVR0OBBYEFMorXKe6f7/o5tnf5iacR/PPM9F3\n"
|
||||
"MB8GA1UdIwQYMBaAFAkWPWYb59boyCycVZVzep4ARBlHMAsGA1UdDwQEAwIHgDAT\n"
|
||||
"BgNVHSUEDDAKBggrBgEFBQcDATAWBgNVHREEDzANggtleGFtcGxlLmNvbTAKBggq\n"
|
||||
"hkjOPQQDAgNJADBGAiEAjQovFZD9svq8vGyuCa82Cq3/YeoHkDyyRalhv4BV7X8C\n"
|
||||
"IQDqXCv7h0gMIVsWOSef8zu4DubHxn7Icm7DwJg0O2lSuw==\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
X509* root = NULL;
|
||||
X509* ica0 = NULL;
|
||||
X509* ica1 = NULL;
|
||||
X509* leaf = NULL;
|
||||
X509_STORE* store = NULL;
|
||||
X509_STORE_CTX* ctx = NULL;
|
||||
STACK_OF(X509)* inter = NULL;
|
||||
|
||||
ExpectNotNull(root = pathlen_pem_to_x509(root_pem));
|
||||
ExpectNotNull(ica0 = pathlen_pem_to_x509(ica0_pem));
|
||||
ExpectNotNull(ica1 = pathlen_pem_to_x509(ica1_pem));
|
||||
ExpectNotNull(leaf = pathlen_pem_to_x509(leaf_pem));
|
||||
|
||||
ExpectNotNull(store = X509_STORE_new());
|
||||
ExpectIntEQ(X509_STORE_add_cert(store, root), 1);
|
||||
ExpectNotNull(inter = sk_X509_new_null());
|
||||
ExpectIntGT(sk_X509_push(inter, ica0), 0);
|
||||
ExpectIntGT(sk_X509_push(inter, ica1), 0);
|
||||
|
||||
ExpectNotNull(ctx = X509_STORE_CTX_new());
|
||||
ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, inter), 1);
|
||||
/* Must be rejected: ica1 violates ica0's pathlen:0 constraint. */
|
||||
ExpectIntNE(X509_verify_cert(ctx), 1);
|
||||
ExpectIntEQ(X509_STORE_CTX_get_error(ctx),
|
||||
X509_V_ERR_PATH_LENGTH_EXCEEDED);
|
||||
|
||||
X509_STORE_CTX_free(ctx);
|
||||
X509_STORE_free(store);
|
||||
sk_X509_free(inter);
|
||||
X509_free(root);
|
||||
X509_free(ica0);
|
||||
X509_free(ica1);
|
||||
X509_free(leaf);
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
#if !defined(NO_FILESYSTEM) && !defined(NO_RSA)
|
||||
/* Positive control: a legitimate chain whose intermediates assert pathlen:1
|
||||
* must still verify, guarding the pathLen enforcement against over-rejection.
|
||||
*
|
||||
* ca-cert -> ca-int (pathlen:1) -> ca-int2 (pathlen:1) -> server-chain
|
||||
*
|
||||
* ca-int permits one following intermediate (ca-int2), so the chain is valid. */
|
||||
static int test_wolfSSL_X509_verify_cert_pathlen_ok(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
X509* ca = NULL;
|
||||
X509* caInt = NULL;
|
||||
X509* caInt2 = NULL;
|
||||
X509* leaf = NULL;
|
||||
X509_STORE* store = NULL;
|
||||
X509_STORE_CTX* ctx = NULL;
|
||||
|
||||
ExpectNotNull(ca = test_wolfSSL_X509_STORE_CTX_ex_helper(
|
||||
"./certs/ca-cert.pem"));
|
||||
ExpectNotNull(caInt = test_wolfSSL_X509_STORE_CTX_ex_helper(
|
||||
"./certs/intermediate/ca-int-cert.pem"));
|
||||
ExpectNotNull(caInt2 = test_wolfSSL_X509_STORE_CTX_ex_helper(
|
||||
"./certs/intermediate/ca-int2-cert.pem"));
|
||||
ExpectNotNull(leaf = test_wolfSSL_X509_STORE_CTX_ex_helper(
|
||||
"./certs/intermediate/server-chain.pem"));
|
||||
|
||||
ExpectNotNull(store = X509_STORE_new());
|
||||
ExpectIntEQ(X509_STORE_add_cert(store, ca), 1);
|
||||
ExpectIntEQ(X509_STORE_add_cert(store, caInt), 1);
|
||||
ExpectIntEQ(X509_STORE_add_cert(store, caInt2), 1);
|
||||
ExpectNotNull(ctx = X509_STORE_CTX_new());
|
||||
ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, NULL), 1);
|
||||
ExpectIntEQ(X509_verify_cert(ctx), 1);
|
||||
ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_OK);
|
||||
|
||||
X509_STORE_CTX_free(ctx);
|
||||
X509_STORE_free(store);
|
||||
X509_free(ca);
|
||||
X509_free(caInt);
|
||||
X509_free(caInt2);
|
||||
X509_free(leaf);
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
#endif /* !NO_FILESYSTEM && !NO_RSA */
|
||||
#endif /* OPENSSL_EXTRA && !NO_CERTS && HAVE_ECC */
|
||||
|
||||
int test_wolfSSL_X509_STORE_CTX_ex(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
@@ -1002,6 +1169,10 @@ int test_wolfSSL_X509_STORE_CTX_ex(void)
|
||||
&testData), 1);
|
||||
#ifdef HAVE_ECC
|
||||
ExpectIntEQ(test_wolfSSL_X509_STORE_CTX_ex12(), 1);
|
||||
ExpectIntEQ(test_wolfSSL_X509_verify_cert_pathlen(), 1);
|
||||
#if !defined(NO_FILESYSTEM) && !defined(NO_RSA)
|
||||
ExpectIntEQ(test_wolfSSL_X509_verify_cert_pathlen_ok(), 1);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if(testData.x509Ca) {
|
||||
|
||||
Reference in New Issue
Block a user