mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-10 13:10:52 +02:00
Merge pull request #10638 from rizlik/nc_uri_trailing_dot
NameConstraints fixes
This commit is contained in:
+39
-99
@@ -5670,95 +5670,12 @@ 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.
|
||||
* permitted selects subtree semantics for wildcard DNS names: containment
|
||||
* for permitted subtrees, intersection for excluded subtrees.
|
||||
* Returns 1 if matches, 0 if not. */
|
||||
static int MatchNameConstraint(int type, const char* name, int nameSz,
|
||||
WOLFSSL_GENERAL_NAME* gn)
|
||||
WOLFSSL_GENERAL_NAME* gn, int permitted)
|
||||
{
|
||||
const char* baseStr;
|
||||
int baseLen;
|
||||
@@ -5788,21 +5705,13 @@ 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 {
|
||||
/* WOLFSSL_GEN_DNS uses DNS-style matching */
|
||||
return wolfssl_local_MatchBaseName(ASN_DNS_TYPE, name, nameSz,
|
||||
baseStr, baseLen);
|
||||
return wolfssl_local_MatchDnsNameConstraint(name, nameSz,
|
||||
baseStr, baseLen, permitted);
|
||||
}
|
||||
|
||||
default:
|
||||
@@ -5811,6 +5720,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.
|
||||
*
|
||||
@@ -5841,6 +5773,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);
|
||||
@@ -5857,7 +5797,7 @@ int wolfSSL_NAME_CONSTRAINTS_check_name(WOLFSSL_NAME_CONSTRAINTS* nc,
|
||||
}
|
||||
hasPermittedType = 1;
|
||||
|
||||
if (MatchNameConstraint(type, name, nameSz, gn)) {
|
||||
if (MatchNameConstraint(type, name, nameSz, gn, 1)) {
|
||||
matchedPermitted = 1;
|
||||
break;
|
||||
}
|
||||
@@ -5884,7 +5824,7 @@ int wolfSSL_NAME_CONSTRAINTS_check_name(WOLFSSL_NAME_CONSTRAINTS* nc,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (MatchNameConstraint(type, name, nameSz, gn)) {
|
||||
if (MatchNameConstraint(type, name, nameSz, gn, 0)) {
|
||||
WOLFSSL_MSG("Name in excluded subtrees");
|
||||
return 0;
|
||||
}
|
||||
|
||||
+40
@@ -23304,6 +23304,46 @@ 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));
|
||||
|
||||
/* An IPv4address host with the absolute-FQDN trailing dot is still not
|
||||
* a DNS host. */
|
||||
sanSz = build_simple_san(san, sizeof(san), URI, "https://12.31.2.3./");
|
||||
ExpectIntGT((int)sanSz, 0);
|
||||
ExpectIntEQ(verify_with_otherName_chain(nc, ncSz, 1, san, sanSz),
|
||||
WC_NO_ERR_TRACE(ASN_NAME_INVALID_E));
|
||||
|
||||
/* (12) One trailing dot on the constraint base is the absolute-FQDN
|
||||
* marker: DNS:example.com. denotes the same subtree as
|
||||
* DNS:example.com, so the permitted form accepts the bare SAN and
|
||||
* the excluded form rejects it. */
|
||||
ncSz = build_simple_nameConstraints(nc, sizeof(nc), 0, DNS,
|
||||
"example.com.");
|
||||
sanSz = build_simple_san(san, sizeof(san), DNS, "example.com");
|
||||
ExpectIntGT((int)ncSz, 0);
|
||||
ExpectIntGT((int)sanSz, 0);
|
||||
ExpectIntEQ(verify_with_otherName_chain(nc, ncSz, 1, san, sanSz), 0);
|
||||
|
||||
ncSz = build_simple_nameConstraints(nc, sizeof(nc), 1, DNS,
|
||||
"example.com.");
|
||||
ExpectIntGT((int)ncSz, 0);
|
||||
ExpectIntEQ(verify_with_otherName_chain(nc, ncSz, 1, san, sanSz),
|
||||
WC_NO_ERR_TRACE(ASN_NAME_INVALID_E));
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
+25
-2
@@ -1153,6 +1153,21 @@ 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);
|
||||
ExpectIntEQ(uriNC("https://a.host.com../", ".host.com"), 0);
|
||||
/* Empty interior labels are not valid DNS host labels. */
|
||||
ExpectIntEQ(uriNC("https://a..host.com/", ".host.com"), 0);
|
||||
|
||||
/*
|
||||
* Leading-dot constraint: proper subtree of hosts (apex excluded).
|
||||
*/
|
||||
@@ -1164,10 +1179,18 @@ 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);
|
||||
/* An IPv4address host is still not a DNS reg-name when written with the
|
||||
* absolute-FQDN trailing dot. */
|
||||
ExpectIntEQ(uriNC("https://12.31.2.3./", "12.31.2.3"), 0);
|
||||
ExpectIntEQ(uriNC("https://12.31.2.3./", "12.31.2.3."), 0);
|
||||
|
||||
/*
|
||||
* Malformed / degenerate URIs and inputs (reject).
|
||||
|
||||
@@ -2039,8 +2039,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,
|
||||
@@ -2243,6 +2243,71 @@ int test_wolfSSL_NAME_CONSTRAINTS_check_name(void)
|
||||
x509 = NULL;
|
||||
nc = NULL;
|
||||
|
||||
/* Wildcard names against an excluded DNS subtree. Build the constraints
|
||||
* programmatically: excluded;DNS:foo.example.com */
|
||||
ExpectNotNull(nc = wolfSSL_NAME_CONSTRAINTS_new());
|
||||
if (EXPECT_SUCCESS()) {
|
||||
GENERAL_SUBTREE* subtree = NULL;
|
||||
|
||||
ExpectNotNull(nc->excludedSubtrees = wolfSSL_sk_new_null());
|
||||
if (EXPECT_SUCCESS()) {
|
||||
nc->excludedSubtrees->type = STACK_TYPE_GENERAL_SUBTREE;
|
||||
}
|
||||
ExpectNotNull(subtree = wolfSSL_GENERAL_SUBTREE_new());
|
||||
if (EXPECT_SUCCESS()) {
|
||||
ExpectNotNull(subtree->base = wolfSSL_GENERAL_NAME_new());
|
||||
}
|
||||
if (EXPECT_SUCCESS()) {
|
||||
subtree->base->type = GEN_DNS;
|
||||
ExpectIntEQ(wolfSSL_ASN1_STRING_set(subtree->base->d.ia5,
|
||||
"foo.example.com", 15), WOLFSSL_SUCCESS);
|
||||
}
|
||||
if (EXPECT_SUCCESS()) {
|
||||
ExpectIntGT(wolfSSL_sk_push(nc->excludedSubtrees, subtree), 0);
|
||||
}
|
||||
if (EXPECT_FAIL()) {
|
||||
wolfSSL_GENERAL_SUBTREE_free(subtree);
|
||||
}
|
||||
}
|
||||
|
||||
if (EXPECT_SUCCESS()) {
|
||||
/* Literal names inside the excluded subtree are rejected. */
|
||||
ExpectIntEQ(wolfSSL_NAME_CONSTRAINTS_check_name(nc, GEN_DNS,
|
||||
"foo.example.com", 15), 0);
|
||||
ExpectIntEQ(wolfSSL_NAME_CONSTRAINTS_check_name(nc, GEN_DNS,
|
||||
"a.foo.example.com", 17), 0);
|
||||
/* Names outside the subtree pass; there are no permitted subtrees. */
|
||||
ExpectIntEQ(wolfSSL_NAME_CONSTRAINTS_check_name(nc, GEN_DNS,
|
||||
"bar.example.com", 15), 1);
|
||||
ExpectIntEQ(wolfSSL_NAME_CONSTRAINTS_check_name(nc, GEN_DNS,
|
||||
"*.other.com", 11), 1);
|
||||
/* A wildcard's '*' can expand to "foo", so "*.example.com" covers
|
||||
* the excluded "foo.example.com" and must be rejected. */
|
||||
ExpectIntEQ(wolfSSL_NAME_CONSTRAINTS_check_name(nc, GEN_DNS,
|
||||
"*.example.com", 13), 0);
|
||||
}
|
||||
|
||||
if (EXPECT_SUCCESS()) {
|
||||
/* One trailing dot on the base is the absolute-FQDN marker:
|
||||
* excluded;DNS:foo.example.com. covers the same subtree, so the
|
||||
* bare name is still rejected. */
|
||||
GENERAL_SUBTREE* subtree = NULL;
|
||||
|
||||
ExpectNotNull(subtree =
|
||||
wolfSSL_sk_GENERAL_SUBTREE_value(nc->excludedSubtrees, 0));
|
||||
if (EXPECT_SUCCESS()) {
|
||||
ExpectIntEQ(wolfSSL_ASN1_STRING_set(subtree->base->d.ia5,
|
||||
"foo.example.com.", 16), WOLFSSL_SUCCESS);
|
||||
}
|
||||
ExpectIntEQ(wolfSSL_NAME_CONSTRAINTS_check_name(nc, GEN_DNS,
|
||||
"foo.example.com", 15), 0);
|
||||
ExpectIntEQ(wolfSSL_NAME_CONSTRAINTS_check_name(nc, GEN_DNS,
|
||||
"bar.example.com", 15), 1);
|
||||
}
|
||||
|
||||
NAME_CONSTRAINTS_free(nc);
|
||||
nc = NULL;
|
||||
|
||||
/* Test IP address constraint checking with cert-ext-ncip.pem
|
||||
* This cert has permitted IP 192.168.1.0/255.255.255.0 */
|
||||
if ((f = XFOPEN("./certs/test/cert-ext-ncip.pem", "rb")) == XBADFILE) {
|
||||
|
||||
+193
-19
@@ -18121,19 +18121,92 @@ int wolfssl_local_MatchBaseName(int type, const char* name, int nameSz,
|
||||
return 1;
|
||||
}
|
||||
|
||||
int wolfssl_local_MatchUriNameConstraint(const char* uri, int uriSz,
|
||||
const char* base, int baseSz)
|
||||
/* RFC 3986 host classification for URI name-constraint checks. */
|
||||
typedef enum UriHostType {
|
||||
URI_HOST_REG_NAME = 0,
|
||||
URI_HOST_IP_LITERAL,
|
||||
URI_HOST_IPV4
|
||||
} UriHostType;
|
||||
|
||||
static int UriHostIsDecOctet(const char* s, int sSz)
|
||||
{
|
||||
int i;
|
||||
int val = 0;
|
||||
|
||||
if (s == NULL || sSz <= 0 || sSz > 3) {
|
||||
return 0;
|
||||
}
|
||||
if (sSz > 1 && s[0] == '0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < sSz; i++) {
|
||||
if (s[i] < '0' || s[i] > '9') {
|
||||
return 0;
|
||||
}
|
||||
val = (val * 10) + (s[i] - '0');
|
||||
}
|
||||
|
||||
return val <= 255;
|
||||
}
|
||||
|
||||
static int UriHostIsIpv4Address(const char* host, int hostSz)
|
||||
{
|
||||
int i;
|
||||
int partStart = 0;
|
||||
int partCount = 0;
|
||||
|
||||
if (host == NULL || hostSz <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i <= hostSz; i++) {
|
||||
if (i == hostSz || host[i] == '.') {
|
||||
if (!UriHostIsDecOctet(host + partStart, i - partStart)) {
|
||||
return 0;
|
||||
}
|
||||
partCount++;
|
||||
partStart = i + 1;
|
||||
}
|
||||
else if (host[i] < '0' || host[i] > '9') {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return partCount == 4;
|
||||
}
|
||||
|
||||
static int UriRegNameHasNonEmptyLabels(const char* host, int hostSz)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (host == NULL || hostSz <= 0 || host[0] == '.' ||
|
||||
host[hostSz - 1] == '.') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 1; i < hostSz; i++) {
|
||||
if (host[i] == '.' && host[i - 1] == '.') {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int GetUriHost(const char* uri, int uriSz, const char** host,
|
||||
int* hostSz, UriHostType* hostType)
|
||||
{
|
||||
const char* hostStart;
|
||||
const char* hostEnd;
|
||||
const char* p;
|
||||
const char* uriEnd;
|
||||
int hostSz;
|
||||
|
||||
/* Need at least 3 bytes for the "://" scheme separator; rejecting short
|
||||
* inputs early also keeps the loop bound (uriEnd - 2) from forming a
|
||||
* pointer before `uri`. */
|
||||
if (uri == NULL || uriSz < 3 || base == NULL || baseSz <= 0) {
|
||||
if (uri == NULL || uriSz < 3 || host == NULL || hostSz == NULL ||
|
||||
hostType == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -18174,7 +18247,8 @@ int wolfssl_local_MatchUriNameConstraint(const char* uri, int uriSz,
|
||||
if (hostEnd >= uriEnd) {
|
||||
return 0;
|
||||
}
|
||||
hostSz = (int)(hostEnd - hostStart);
|
||||
*hostSz = (int)(hostEnd - hostStart);
|
||||
*hostType = URI_HOST_IP_LITERAL;
|
||||
}
|
||||
else {
|
||||
hostEnd = hostStart;
|
||||
@@ -18182,10 +18256,63 @@ int wolfssl_local_MatchUriNameConstraint(const char* uri, int uriSz,
|
||||
*hostEnd != '?' && *hostEnd != '#') {
|
||||
hostEnd++;
|
||||
}
|
||||
hostSz = (int)(hostEnd - hostStart);
|
||||
*hostSz = (int)(hostEnd - hostStart);
|
||||
/* One trailing dot is the absolute-FQDN marker and not part of the
|
||||
* host: strip it before classifying so that "12.31.2.3." is
|
||||
* recognized as an IPv4 address and "host.com." denotes the same
|
||||
* reg-name as "host.com". */
|
||||
if (*hostSz > 0 && hostStart[*hostSz - 1] == '.') {
|
||||
(*hostSz)--;
|
||||
if (*hostSz <= 0 || hostStart[*hostSz - 1] == '.') {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
*hostType = UriHostIsIpv4Address(hostStart, *hostSz) ?
|
||||
URI_HOST_IPV4 : URI_HOST_REG_NAME;
|
||||
if (*hostType == URI_HOST_REG_NAME &&
|
||||
!UriRegNameHasNonEmptyLabels(hostStart, *hostSz)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (hostSz <= 0) {
|
||||
if (*hostSz <= 0) {
|
||||
return 0;
|
||||
}
|
||||
*host = hostStart;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int wolfssl_local_UriNameHasDnsHost(const char* uri, int uriSz)
|
||||
{
|
||||
const char* host = NULL;
|
||||
int hostSz = 0;
|
||||
UriHostType hostType = URI_HOST_REG_NAME;
|
||||
|
||||
if (!GetUriHost(uri, uriSz, &host, &hostSz, &hostType)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
(void)host;
|
||||
(void)hostSz;
|
||||
return hostType == URI_HOST_REG_NAME;
|
||||
}
|
||||
|
||||
int wolfssl_local_MatchUriNameConstraint(const char* uri, int uriSz,
|
||||
const char* base, int baseSz)
|
||||
{
|
||||
const char* hostStart = NULL;
|
||||
int hostSz = 0;
|
||||
UriHostType hostType = URI_HOST_REG_NAME;
|
||||
|
||||
if (base == NULL || baseSz <= 0 ||
|
||||
!GetUriHost(uri, uriSz, &hostStart, &hostSz, &hostType)) {
|
||||
return 0;
|
||||
}
|
||||
/* RFC 5280 URI constraints apply only to host names specified as fully
|
||||
* qualified domain names. RFC 3986 IP-literals and IPv4address hosts are
|
||||
* not DNS reg-names. */
|
||||
if (hostType != URI_HOST_REG_NAME) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -18200,6 +18327,15 @@ int wolfssl_local_MatchUriNameConstraint(const char* uri, int uriSz,
|
||||
}
|
||||
else {
|
||||
int i;
|
||||
/* GetUriHost already stripped the host's absolute-FQDN trailing dot;
|
||||
* treat one trailing dot on the base the same way so that "host.com."
|
||||
* and "host.com" compare equal. */
|
||||
if (base[baseSz - 1] == '.') {
|
||||
baseSz--;
|
||||
}
|
||||
if (baseSz <= 0) {
|
||||
return 0;
|
||||
}
|
||||
if (hostSz != baseSz) {
|
||||
return 0;
|
||||
}
|
||||
@@ -18415,6 +18551,23 @@ static int DnsNameHasWildcard(const char* name, int nameSz)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Match a DNS name against a DNS name-constraint base. A wildcard name
|
||||
* denotes a set of names: permitted subtrees require containment, excluded
|
||||
* subtrees intersection (selected by `permitted`). Literal names use plain
|
||||
* base-name matching, which normalizes the absolute-FQDN trailing dot
|
||||
* before its own length check.
|
||||
* Returns 1 on match, 0 otherwise. */
|
||||
int wolfssl_local_MatchDnsNameConstraint(const char* name, int nameSz,
|
||||
const char* base, int baseSz, int permitted)
|
||||
{
|
||||
if (DnsNameHasWildcard(name, nameSz)) {
|
||||
return wolfssl_local_MatchDnsConstraintWildcard(name, nameSz,
|
||||
base, baseSz, permitted);
|
||||
}
|
||||
return wolfssl_local_MatchBaseName(ASN_DNS_TYPE, name, nameSz, base,
|
||||
baseSz);
|
||||
}
|
||||
|
||||
/* Search through the list to find if the name is permitted.
|
||||
* name The DNS name to search for
|
||||
* dnsList The list to search through
|
||||
@@ -18465,12 +18618,10 @@ static int PermittedListOk(DNS_entry* name, Base_entry* dnsList, byte nameType)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (nameType == ASN_DNS_TYPE &&
|
||||
DnsNameHasWildcard(name->name, name->len)) {
|
||||
/* Wildcard DNS SAN: a '*' can expand to a longer label, so the
|
||||
* byte-length guard used for literal names below is invalid.
|
||||
* Permit only if every expansion stays inside the subtree. */
|
||||
if (wolfssl_local_MatchDnsConstraintWildcard(name->name,
|
||||
else if (nameType == ASN_DNS_TYPE) {
|
||||
/* Permit only if every expansion of a wildcard stays inside
|
||||
* the subtree. */
|
||||
if (wolfssl_local_MatchDnsNameConstraint(name->name,
|
||||
name->len, current->name, current->nameSz, 1)) {
|
||||
match = 1;
|
||||
break;
|
||||
@@ -18539,12 +18690,10 @@ static int IsInExcludedList(DNS_entry* name, Base_entry* dnsList, byte nameType)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (nameType == ASN_DNS_TYPE &&
|
||||
DnsNameHasWildcard(name->name, name->len)) {
|
||||
/* Wildcard DNS SAN: a '*' can expand to a longer label, so the
|
||||
* byte-length guard used for literal names below is invalid.
|
||||
* Exclude if any expansion can fall inside the subtree. */
|
||||
if (wolfssl_local_MatchDnsConstraintWildcard(name->name,
|
||||
else if (nameType == ASN_DNS_TYPE) {
|
||||
/* Exclude if any expansion of a wildcard can fall inside the
|
||||
* subtree. */
|
||||
if (wolfssl_local_MatchDnsNameConstraint(name->name,
|
||||
name->len, current->name, current->nameSz, 0)) {
|
||||
ret = 1;
|
||||
break;
|
||||
@@ -18564,12 +18713,25 @@ static int IsInExcludedList(DNS_entry* name, Base_entry* dnsList, byte nameType)
|
||||
}
|
||||
|
||||
|
||||
static int NameConstraintListHasType(Base_entry* list, byte nameType)
|
||||
{
|
||||
while (list != NULL) {
|
||||
if (list->type == nameType) {
|
||||
return 1;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ConfirmNameConstraints(Signer* signer, DecodedCert* cert)
|
||||
{
|
||||
const byte nameTypes[] = {ASN_RFC822_TYPE, ASN_DNS_TYPE, ASN_DIR_TYPE,
|
||||
ASN_IP_TYPE, ASN_URI_TYPE, ASN_OTHER_TYPE,
|
||||
ASN_RID_TYPE};
|
||||
int i;
|
||||
int uriConstraintsApply;
|
||||
|
||||
if (signer == NULL || cert == NULL)
|
||||
return 0;
|
||||
@@ -18578,6 +18740,10 @@ static int ConfirmNameConstraints(Signer* signer, DecodedCert* cert)
|
||||
!signer->extNameConstraintHasUnsupported)
|
||||
return 1;
|
||||
|
||||
uriConstraintsApply =
|
||||
NameConstraintListHasType(signer->excludedNames, ASN_URI_TYPE) ||
|
||||
NameConstraintListHasType(signer->permittedNames, ASN_URI_TYPE);
|
||||
|
||||
for (i=0; i < (int)sizeof(nameTypes); i++) {
|
||||
byte nameType = nameTypes[i];
|
||||
DNS_entry* name = NULL;
|
||||
@@ -18660,6 +18826,14 @@ static int ConfirmNameConstraints(Signer* signer, DecodedCert* cert)
|
||||
while (name != NULL) {
|
||||
/* Only check entries that match the current nameType. */
|
||||
if (name->type == nameType) {
|
||||
if (nameType == ASN_URI_TYPE && uriConstraintsApply &&
|
||||
!wolfssl_local_UriNameHasDnsHost(name->name,
|
||||
name->len)) {
|
||||
WOLFSSL_MSG("URI name constraint applied to URI without "
|
||||
"DNS host");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (IsInExcludedList(name, signer->excludedNames,
|
||||
nameType) == 1) {
|
||||
WOLFSSL_MSG("Excluded name was found!");
|
||||
|
||||
@@ -3222,10 +3222,15 @@ WOLFSSL_TEST_VIS int wolfssl_local_MatchIpSubnet(const byte* ip, int ipSz,
|
||||
WOLFSSL_TEST_VIS int wolfssl_local_MatchUriNameConstraint(const char* uri,
|
||||
int uriSz, const char* base,
|
||||
int baseSz);
|
||||
WOLFSSL_LOCAL int wolfssl_local_UriNameHasDnsHost(const char* uri,
|
||||
int uriSz);
|
||||
WOLFSSL_TEST_VIS int wolfssl_local_MatchDnsConstraintWildcard(
|
||||
const char* name, int nameSz,
|
||||
const char* base, int baseSz,
|
||||
int permitted);
|
||||
WOLFSSL_LOCAL int wolfssl_local_MatchDnsNameConstraint(const char* name,
|
||||
int nameSz, const char* base,
|
||||
int baseSz, int permitted);
|
||||
#endif
|
||||
|
||||
#if ((defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_IMPORT)) \
|
||||
|
||||
Reference in New Issue
Block a user