Skip to content
Draft
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
126 changes: 32 additions & 94 deletions src/x509.c
Original file line number Diff line number Diff line change
Expand Up @@ -5666,91 +5666,6 @@ static int MatchIpName(const char* name, int nameSz, WOLFSSL_GENERAL_NAME* gn)
constraintData, constraintLen);
}

/* Extract host from URI for name constraint matching.
* URI format: scheme://[userinfo@]host[:port][/path][?query][#fragment]
* IPv6 literals are enclosed in brackets: scheme://[ipv6addr]:port/path
* Returns pointer to host start and sets hostLen, or NULL on failure. */
static const char* ExtractHostFromUri(const char* uri, int uriLen, int* hostLen)
{
const char* hostStart;
const char* hostEnd;
const char* p;
const char* uriEnd;

if (uri == NULL || uriLen <= 0 || hostLen == NULL) {
return NULL;
}

uriEnd = uri + uriLen;

/* Find "://" to skip scheme */
hostStart = NULL;
for (p = uri; p < uriEnd - 2; p++) {
if (p[0] == ':' && p[1] == '/' && p[2] == '/') {
hostStart = p + 3;
break;
}
}
if (hostStart == NULL || hostStart >= uriEnd) {
return NULL;
}

/* Skip userinfo if present (look for @ before any /, ?, #)
* userinfo can contain ':' (ex: user:pass@host), don't stop at ':'
* For IPv6, also don't stop at '[' in userinfo */
for (p = hostStart; p < uriEnd; p++) {
if (*p == '@') {
hostStart = p + 1;
break;
}
if (*p == '/' || *p == '?' || *p == '#') {
/* No userinfo found */
break;
}
/* If '[' before '@', found IPv6 literal, not userinfo */
if (*p == '[') {
break;
}
}
if (hostStart >= uriEnd) {
return NULL;
}

/* Check for IPv6 literal */
if (*hostStart == '[') {
/* Find closing bracket, skip opening one */
hostStart++;
hostEnd = hostStart;
while (hostEnd < uriEnd && *hostEnd != ']') {
hostEnd++;
}
if (hostEnd >= uriEnd) {
/* No closing bracket found, malformed */
return NULL;
}
/* hostEnd points to closing bracket, extract content between */
*hostLen = (int)(hostEnd - hostStart);
if (*hostLen <= 0) {
return NULL;
}
return hostStart;
}

/* Regular hostname, find end */
hostEnd = hostStart;
while (hostEnd < uriEnd && *hostEnd != ':' && *hostEnd != '/' &&
*hostEnd != '?' && *hostEnd != '#') {
hostEnd++;
}

*hostLen = (int)(hostEnd - hostStart);
if (*hostLen <= 0) {
return NULL;
}

return hostStart;
}

/* Helper to check if name string matches a single GENERAL_NAME constraint.
* Returns 1 if matches, 0 if not. */
static int MatchNameConstraint(int type, const char* name, int nameSz,
Expand Down Expand Up @@ -5784,15 +5699,7 @@ static int MatchNameConstraint(int type, const char* name, int nameSz,
nameSz, baseStr, baseLen);
}
else if (type == WOLFSSL_GEN_URI) {
const char* host;
int hostLen;

/* For URI, extract host and match against DNS-style */
host = ExtractHostFromUri(name, nameSz, &hostLen);
if (host == NULL) {
return 0;
}
return wolfssl_local_MatchBaseName(ASN_DNS_TYPE, host, hostLen,
return wolfssl_local_MatchUriNameConstraint(name, nameSz,
baseStr, baseLen);
}
else {
Expand All @@ -5807,6 +5714,29 @@ static int MatchNameConstraint(int type, const char* name, int nameSz,
}
}

static int NameConstraintsHasType(const WOLFSSL_STACK* sk, int type)
{
int i;
int num;

if (sk == NULL) {
return 0;
}

num = wolfSSL_sk_GENERAL_SUBTREE_num(sk);
for (i = 0; i < num; i++) {
WOLFSSL_GENERAL_SUBTREE* subtree;

subtree = wolfSSL_sk_GENERAL_SUBTREE_value(sk, i);
if (subtree != NULL && subtree->base != NULL &&
subtree->base->type == type) {
return 1;
}
}

return 0;
}

/*
* Check if a name string satisfies given name constraints.
*
Expand Down Expand Up @@ -5837,6 +5767,14 @@ int wolfSSL_NAME_CONSTRAINTS_check_name(WOLFSSL_NAME_CONSTRAINTS* nc,
return 0;
}

if (type == WOLFSSL_GEN_URI &&
(NameConstraintsHasType(nc->permittedSubtrees, type) ||
NameConstraintsHasType(nc->excludedSubtrees, type)) &&
!wolfssl_local_UriNameHasDnsHost(name, nameSz)) {
WOLFSSL_MSG("URI name constraint applied to URI without DNS host");
return 0;
}

/* Check permitted subtrees */
if (nc->permittedSubtrees != NULL) {
num = wolfSSL_sk_GENERAL_SUBTREE_num(nc->permittedSubtrees);
Expand Down
16 changes: 16 additions & 0 deletions tests/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -23046,6 +23046,22 @@ static int test_NameConstraints_DnsUriWildcard(void)
sanSz = build_simple_san(san, sizeof(san), URI, "https://www.host.com/");
ExpectIntGT((int)sanSz, 0);
ExpectIntEQ(verify_with_otherName_chain(nc, ncSz, 1, san, sanSz), 0);

/* (11) RFC 5280 requires a DNS host when URI constraints are applied.
* Fail closed even for excluded-only constraints where a boolean
* non-match would otherwise pass. */
ncSz = build_simple_nameConstraints(nc, sizeof(nc), 1, URI,
"blocked.com");
sanSz = build_simple_san(san, sizeof(san), URI, "https://12.31.2.3/");
ExpectIntGT((int)ncSz, 0);
ExpectIntGT((int)sanSz, 0);
ExpectIntEQ(verify_with_otherName_chain(nc, ncSz, 1, san, sanSz),
WC_NO_ERR_TRACE(ASN_NAME_INVALID_E));

sanSz = build_simple_san(san, sizeof(san), URI, "https://[v1.addr.]/");
ExpectIntGT((int)sanSz, 0);
ExpectIntEQ(verify_with_otherName_chain(nc, ncSz, 1, san, sanSz),
WC_NO_ERR_TRACE(ASN_NAME_INVALID_E));
#endif
return EXPECT_RESULT();
}
Expand Down
20 changes: 18 additions & 2 deletions tests/api/test_asn.c
Original file line number Diff line number Diff line change
Expand Up @@ -1153,6 +1153,18 @@ int test_wolfssl_local_MatchUriNameConstraint(void)
ExpectIntEQ(uriNC("https://host.com.evil.com", "host.com"), 0);
ExpectIntEQ(uriNC("https://other.com", "host.com"), 0);

/* A single trailing dot is the absolute-FQDN marker: "host.com." and
* "host.com" denote the same host and must compare equal, matching the
* DNS name-constraint path. */
ExpectIntEQ(uriNC("https://host.com./", "host.com"), 1);
ExpectIntEQ(uriNC("https://host.com.:8443/x", "host.com"), 1);
ExpectIntEQ(uriNC("https://host.com", "host.com."), 1);
ExpectIntEQ(uriNC("https://host.com./", "host.com."), 1);
ExpectIntEQ(uriNC("https://v1.addr./", "v1.addr"), 1);
ExpectIntEQ(uriNC("https://v1.addr/", "v1.addr."), 1);
/* Only ONE trailing dot is the marker; an empty last label is not. */
ExpectIntEQ(uriNC("https://host.com../", "host.com"), 0);

/*
* Leading-dot constraint: proper subtree of hosts (apex excluded).
*/
Expand All @@ -1164,10 +1176,14 @@ int test_wolfssl_local_MatchUriNameConstraint(void)
ExpectIntEQ(uriNC("https://evilhost.com", ".host.com"), 0);

/*
* IPv6 literal host extraction ([..]) then exact match.
* RFC 5280 URI constraints require a DNS host. IP-literals / IPvFuture
* hosts in brackets and IPv4address hosts are not DNS reg-names.
*/
ExpectIntEQ(uriNC("https://[2001:db8::1]:443/x", "2001:db8::1"), 1);
ExpectIntEQ(uriNC("https://[2001:db8::1]:443/x", "2001:db8::1"), 0);
ExpectIntEQ(uriNC("https://[2001:db8::1]", "2001:db8::2"), 0);
ExpectIntEQ(uriNC("https://[v1.addr.]/", "v1.addr"), 0);
ExpectIntEQ(uriNC("https://[v1.addr.]/", "v1.addr."), 0);
ExpectIntEQ(uriNC("https://12.31.2.3/", "12.31.2.3"), 0);

/*
* Malformed / degenerate URIs and inputs (reject).
Expand Down
6 changes: 3 additions & 3 deletions tests/api/test_ossl_x509_ext.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ int test_wolfSSL_X509_get_extension_flags(void)
return EXPECT_RESULT();
}


int test_wolfSSL_X509_get_ext(void)
{
EXPECT_DECLS;
Expand Down Expand Up @@ -2037,8 +2038,8 @@ int test_wolfSSL_NAME_CONSTRAINTS_uri(void)
ExpectIntEQ(wolfSSL_NAME_CONSTRAINTS_check_name(nc, GEN_URI,
"https://user:pass@www.wolfssl.com/path", 38), 1);

/* IPv6 literal URIs, host extracted without brackets.
* These don't match .wolfssl.com constraint (different host type) */
/* URI constraints require a DNS reg-name host, so IP-literals do not
* match the .wolfssl.com constraint. */
ExpectIntEQ(wolfSSL_NAME_CONSTRAINTS_check_name(nc, GEN_URI,
"https://[::1]:8080/path", 23), 0);
ExpectIntEQ(wolfSSL_NAME_CONSTRAINTS_check_name(nc, GEN_URI,
Expand Down Expand Up @@ -2431,4 +2432,3 @@ int test_wolfSSL_NAME_CONSTRAINTS_excluded(void)
* !IGNORE_NAME_CONSTRAINTS */
return EXPECT_RESULT();
}

Loading
Loading