early-access version 2698

This commit is contained in:
pineappleEA
2022-04-24 22:29:35 +02:00
parent c96f949832
commit caa0c2911b
486 changed files with 37806 additions and 14362 deletions

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: x509_constraints.c,v 1.10 2020/09/21 05:41:43 tb Exp $ */
/* $OpenBSD: x509_constraints.c,v 1.26 2022/03/26 16:34:21 tb Exp $ */
/*
* Copyright (c) 2020 Bob Beck <beck@openbsd.org>
*
@@ -36,7 +36,7 @@
#define DOMAIN_PART_MAX_LEN 255
struct x509_constraints_name *
x509_constraints_name_new()
x509_constraints_name_new(void)
{
return (calloc(1, sizeof(struct x509_constraints_name)));
}
@@ -69,9 +69,11 @@ x509_constraints_name_dup(struct x509_constraints_name *name)
new->type = name->type;
new->af = name->af;
new->der_len = name->der_len;
if (name->der_len > 0 && (new->der = malloc(name->der_len)) == NULL)
goto err;
memcpy(new->der, name->der, name->der_len);
if (name->der_len > 0) {
if ((new->der = malloc(name->der_len)) == NULL)
goto err;
memcpy(new->der, name->der, name->der_len);
}
if (name->name != NULL && (new->name = strdup(name->name)) == NULL)
goto err;
if (name->local != NULL && (new->local = strdup(name->local)) == NULL)
@@ -84,9 +86,16 @@ x509_constraints_name_dup(struct x509_constraints_name *name)
}
struct x509_constraints_names *
x509_constraints_names_new()
x509_constraints_names_new(size_t names_max)
{
return (calloc(1, sizeof(struct x509_constraints_names)));
struct x509_constraints_names *new;
if ((new = calloc(1, sizeof(struct x509_constraints_names))) == NULL)
return NULL;
new->names_max = names_max;
return new;
}
void
@@ -114,8 +123,8 @@ int
x509_constraints_names_add(struct x509_constraints_names *names,
struct x509_constraints_name *name)
{
size_t i = names->names_count;
if (names->names_count >= names->names_max)
return 0;
if (names->names_count == names->names_len) {
struct x509_constraints_name **tmp;
if ((tmp = recallocarray(names->names, names->names_len,
@@ -124,7 +133,7 @@ x509_constraints_names_add(struct x509_constraints_names *names,
names->names_len += 32;
names->names = tmp;
}
names->names[i] = name;
names->names[names->names_count] = name;
names->names_count++;
return 1;
}
@@ -139,14 +148,16 @@ x509_constraints_names_dup(struct x509_constraints_names *names)
if (names == NULL)
return NULL;
if ((new = x509_constraints_names_new()) == NULL)
if ((new = x509_constraints_names_new(names->names_max)) == NULL)
goto err;
for (i = 0; i < names->names_count; i++) {
if ((name = x509_constraints_name_dup(names->names[i])) == NULL)
goto err;
if (!x509_constraints_names_add(new, name))
goto err;
}
return new;
err:
x509_constraints_names_free(new);
@@ -158,13 +169,15 @@ x509_constraints_names_dup(struct x509_constraints_names *names)
/*
* Validate that the name contains only a hostname consisting of RFC
* 5890 compliant A-labels (see RFC 6066 section 3). This is more
* permissive to allow for a leading '*' for a SAN DNSname wildcard,
* or a leading '.' for a subdomain based constraint, as well as
* allowing for '_' which is commonly accepted by nonconformant
* DNS implementaitons.
* permissive to allow for a leading '.' for a subdomain based
* constraint, as well as allowing for '_' which is commonly accepted
* by nonconformant DNS implementaitons.
*
* if "wildcards" is set it allows '*' to occur in the string at the end of a
* component.
*/
static int
x509_constraints_valid_domain_internal(uint8_t *name, size_t len)
x509_constraints_valid_domain_internal(uint8_t *name, size_t len, int wildcards)
{
uint8_t prev, c = 0;
int component = 0;
@@ -187,8 +200,8 @@ x509_constraints_valid_domain_internal(uint8_t *name, size_t len)
if (!isalnum(c) && c != '-' && c != '.' && c != '_' && c != '*')
return 0;
/* '*' can only be the first thing. */
if (c == '*' && !first)
/* if it is a '*', fail if not wildcards */
if (!wildcards && c == '*')
return 0;
/* '-' must not start a component or be at the end. */
@@ -210,6 +223,13 @@ x509_constraints_valid_domain_internal(uint8_t *name, size_t len)
component = 0;
continue;
}
/*
* Wildcards can only occur at the end of a component.
* c*.com is valid, c*c.com is not.
*/
if (prev == '*')
return 0;
/* Components must be 63 chars or less. */
if (++component > 63)
return 0;
@@ -222,15 +242,13 @@ x509_constraints_valid_domain(uint8_t *name, size_t len)
{
if (len == 0)
return 0;
if (name[0] == '*') /* wildcard not allowed in a domain name */
return 0;
/*
* A domain may not be less than two characters, so you can't
* have a require subdomain name with less than that.
*/
if (len < 3 && name[0] == '.')
return 0;
return x509_constraints_valid_domain_internal(name, len);
return x509_constraints_valid_domain_internal(name, len, 0);
}
int
@@ -241,15 +259,13 @@ x509_constraints_valid_host(uint8_t *name, size_t len)
if (len == 0)
return 0;
if (name[0] == '*') /* wildcard not allowed in a host name */
return 0;
if (name[0] == '.') /* leading . not allowed in a host name*/
return 0;
if (inet_pton(AF_INET, name, &sin4) == 1)
return 0;
if (inet_pton(AF_INET6, name, &sin6) == 1)
return 0;
return x509_constraints_valid_domain_internal(name, len);
return x509_constraints_valid_domain_internal(name, len, 0);
}
int
@@ -272,7 +288,7 @@ x509_constraints_valid_sandns(uint8_t *name, size_t len)
if (len >= 4 && name[0] == '*' && name[1] != '.')
return 0;
return x509_constraints_valid_domain_internal(name, len);
return x509_constraints_valid_domain_internal(name, len, 1);
}
static inline int
@@ -323,16 +339,16 @@ x509_constraints_parse_mailbox(uint8_t *candidate, size_t len,
if (c == '.')
goto bad;
}
if (wi > DOMAIN_PART_MAX_LEN)
goto bad;
if (accept) {
if (wi >= DOMAIN_PART_MAX_LEN)
goto bad;
working[wi++] = c;
accept = 0;
continue;
}
if (candidate_local != NULL) {
/* We are looking for the domain part */
if (wi > DOMAIN_PART_MAX_LEN)
if (wi >= DOMAIN_PART_MAX_LEN)
goto bad;
working[wi++] = c;
if (i == len - 1) {
@@ -347,7 +363,7 @@ x509_constraints_parse_mailbox(uint8_t *candidate, size_t len,
continue;
}
/* We are looking for the local part */
if (wi > LOCAL_PART_MAX_LEN)
if (wi >= LOCAL_PART_MAX_LEN)
break;
if (quoted) {
@@ -367,12 +383,14 @@ x509_constraints_parse_mailbox(uint8_t *candidate, size_t len,
*/
if (c == 9)
goto bad;
if (wi >= LOCAL_PART_MAX_LEN)
goto bad;
working[wi++] = c;
continue; /* all's good inside our quoted string */
}
if (c == '@') {
if (wi == 0)
goto bad;;
goto bad;
if (candidate_local != NULL)
goto bad;
candidate_local = strdup(working);
@@ -396,6 +414,8 @@ x509_constraints_parse_mailbox(uint8_t *candidate, size_t len,
}
if (!local_part_ok(c))
goto bad;
if (wi >= LOCAL_PART_MAX_LEN)
goto bad;
working[wi++] = c;
}
if (candidate_local == NULL || candidate_domain == NULL)
@@ -404,9 +424,14 @@ x509_constraints_parse_mailbox(uint8_t *candidate, size_t len,
strlen(candidate_domain)))
goto bad;
name->local = candidate_local;
name->name = candidate_domain;
name->type = GEN_EMAIL;
if (name != NULL) {
name->local = candidate_local;
name->name = candidate_domain;
name->type = GEN_EMAIL;
} else {
free(candidate_local);
free(candidate_domain);
}
return 1;
bad:
free(candidate_local);
@@ -420,16 +445,13 @@ x509_constraints_valid_domain_constraint(uint8_t *constraint, size_t len)
if (len == 0)
return 1; /* empty constraints match */
if (constraint[0] == '*') /* wildcard not allowed in a constraint */
return 0;
/*
* A domain may not be less than two characters, so you
* can't match a single domain of less than that
*/
if (len < 3 && constraint[0] == '.')
return 0;
return x509_constraints_valid_domain_internal(constraint, len);
return x509_constraints_valid_domain_internal(constraint, len, 0);
}
/*
@@ -494,7 +516,8 @@ x509_constraints_uri_host(uint8_t *uri, size_t len, char **hostpart)
host = authority;
if (!x509_constraints_valid_host(host, hostlen))
return 0;
*hostpart = strndup(host, hostlen);
if (hostpart != NULL)
*hostpart = strndup(host, hostlen);
return 1;
}
@@ -613,7 +636,11 @@ int
x509_constraints_dirname(uint8_t *dirname, size_t dlen,
uint8_t *constraint, size_t len)
{
if (len != dlen)
/*
* The constraint must be a prefix in DER format, so it can't be
* longer than the name it is checked against.
*/
if (len > dlen)
return 0;
return (memcmp(constraint, dirname, len) == 0);
}
@@ -630,35 +657,45 @@ x509_constraints_general_to_bytes(GENERAL_NAME *name, uint8_t **bytes,
if (name->type == GEN_DNS) {
ASN1_IA5STRING *aname = name->d.dNSName;
*bytes = aname->data;
*len = strlen(aname->data);
*len = aname->length;
return name->type;
}
if (name->type == GEN_EMAIL) {
ASN1_IA5STRING *aname = name->d.rfc822Name;
*bytes = aname->data;
*len = strlen(aname->data);
*len = aname->length;
return name->type;
}
if (name->type == GEN_URI) {
ASN1_IA5STRING *aname = name->d.uniformResourceIdentifier;
*bytes = aname->data;
*len = strlen(aname->data);
*len = aname->length;
return name->type;
}
if (name->type == GEN_DIRNAME) {
X509_NAME *dname = name->d.directoryName;
if (!dname->modified || i2d_X509_NAME(dname, NULL) >= 0) {
*bytes = dname->canon_enc;
*len = dname->canon_enclen;
return name->type;
}
}
if (name->type == GEN_IPADD) {
*bytes = name->d.ip->data;
*len = name->d.ip->length;
return name->type;
}
return 0;
}
@@ -696,11 +733,11 @@ x509_constraints_extract_names(struct x509_constraints_names *names,
*error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
goto err;
}
if ((vname->name = strdup(bytes)) == NULL) {
if ((vname->name = strndup(bytes, len)) == NULL) {
*error = X509_V_ERR_OUT_OF_MEM;
goto err;
}
vname->type=GEN_DNS;
vname->type = GEN_DNS;
include_cn = 0; /* don't use cn from subject */
break;
case GEN_EMAIL:
@@ -724,15 +761,15 @@ x509_constraints_extract_names(struct x509_constraints_names *names,
vname->type = GEN_URI;
break;
case GEN_DIRNAME:
if (len == 0) {
*error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
goto err;
}
if (bytes == NULL || ((vname->der = malloc(len)) ==
NULL)) {
*error = X509_V_ERR_OUT_OF_MEM;
goto err;
}
if (len == 0) {
*error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
goto err;
}
memcpy(vname->der, bytes, len);
vname->der_len = len;
vname->type = GEN_DIRNAME;
@@ -742,8 +779,7 @@ x509_constraints_extract_names(struct x509_constraints_names *names,
vname->af = AF_INET;
if (len == 16)
vname->af = AF_INET6;
if (vname->af != AF_INET && vname->af !=
AF_INET6) {
if (vname->af != AF_INET && vname->af != AF_INET6) {
*error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
goto err;
}
@@ -870,21 +906,34 @@ x509_constraints_extract_names(struct x509_constraints_names *names,
*/
int
x509_constraints_validate(GENERAL_NAME *constraint,
struct x509_constraints_name *name, int *error)
struct x509_constraints_name **out_name, int *out_error)
{
uint8_t *bytes = NULL;
size_t len = 0;
struct x509_constraints_name *name;
int error = X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX;
int name_type;
if (out_name == NULL || *out_name != NULL)
return 0;
if (out_error != NULL)
*out_error = 0;
if ((name = x509_constraints_name_new()) == NULL) {
error = X509_V_ERR_OUT_OF_MEM;
goto err;
}
name_type = x509_constraints_general_to_bytes(constraint, &bytes, &len);
switch (name_type) {
case GEN_DIRNAME:
if (bytes == NULL || (name->der = malloc(len)) == NULL) {
*error = X509_V_ERR_OUT_OF_MEM;
return 0;
}
if (len == 0)
goto err; /* XXX The RFCs are delightfully vague */
if (bytes == NULL || (name->der = malloc(len)) == NULL) {
error = X509_V_ERR_OUT_OF_MEM;
goto err;
}
memcpy(name->der, bytes, len);
name->der_len = len;
name->type = GEN_DIRNAME;
@@ -892,24 +941,31 @@ x509_constraints_validate(GENERAL_NAME *constraint,
case GEN_DNS:
if (!x509_constraints_valid_domain_constraint(bytes, len))
goto err;
if ((name->name = strdup(bytes)) == NULL) {
*error = X509_V_ERR_OUT_OF_MEM;
return 0;
if ((name->name = strndup(bytes, len)) == NULL) {
error = X509_V_ERR_OUT_OF_MEM;
goto err;
}
name->type = GEN_DNS;
break;
case GEN_EMAIL:
if (memchr(bytes, '@', len) != NULL) {
if (len > 0 && memchr(bytes + 1, '@', len - 1) != NULL) {
if (!x509_constraints_parse_mailbox(bytes, len, name))
goto err;
} else {
if (!x509_constraints_valid_domain_constraint(bytes,
len))
goto err;
if ((name->name = strdup(bytes)) == NULL) {
*error = X509_V_ERR_OUT_OF_MEM;
return 0;
}
break;
}
/*
* Mail constraints of the form @domain.com are accepted by
* OpenSSL and Microsoft.
*/
if (len > 0 && bytes[0] == '@') {
bytes++;
len--;
}
if (!x509_constraints_valid_domain_constraint(bytes, len))
goto err;
if ((name->name = strndup(bytes, len)) == NULL) {
error = X509_V_ERR_OUT_OF_MEM;
goto err;
}
name->type = GEN_EMAIL;
break;
@@ -927,15 +983,25 @@ x509_constraints_validate(GENERAL_NAME *constraint,
case GEN_URI:
if (!x509_constraints_valid_domain_constraint(bytes, len))
goto err;
name->name = strdup(bytes);
if ((name->name = strndup(bytes, len)) == NULL) {
error = X509_V_ERR_OUT_OF_MEM;
goto err;
}
name->type = GEN_URI;
break;
default:
break;
}
*out_name = name;
return 1;
err:
*error = X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX;
x509_constraints_name_free(name);
if (out_error != NULL)
*out_error = error;
return 0;
}
@@ -945,7 +1011,7 @@ x509_constraints_extract_constraints(X509 *cert,
struct x509_constraints_names *excluded,
int *error)
{
struct x509_constraints_name *vname;
struct x509_constraints_name *vname = NULL;
NAME_CONSTRAINTS *nc = cert->nc;
GENERAL_SUBTREE *subtree;
int i;
@@ -960,24 +1026,20 @@ x509_constraints_extract_constraints(X509 *cert,
*error = X509_V_ERR_SUBTREE_MINMAX;
return 0;
}
if ((vname = x509_constraints_name_new()) == NULL) {
*error = X509_V_ERR_OUT_OF_MEM;
if (!x509_constraints_validate(subtree->base, &vname, error))
return 0;
}
if (x509_constraints_validate(subtree->base, vname, error) ==
0) {
x509_constraints_name_free(vname);
return 0;
}
if (vname->type == 0) {
x509_constraints_name_free(vname);
vname = NULL;
continue;
}
if (!x509_constraints_names_add(permitted, vname)) {
x509_constraints_name_free(vname);
vname = NULL;
*error = X509_V_ERR_OUT_OF_MEM;
return 0;
}
vname = NULL;
}
for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->excludedSubtrees); i++) {
@@ -986,24 +1048,20 @@ x509_constraints_extract_constraints(X509 *cert,
*error = X509_V_ERR_SUBTREE_MINMAX;
return 0;
}
if ((vname = x509_constraints_name_new()) == NULL) {
*error = X509_V_ERR_OUT_OF_MEM;
if (!x509_constraints_validate(subtree->base, &vname, error))
return 0;
}
if (x509_constraints_validate(subtree->base, vname, error) ==
0) {
x509_constraints_name_free(vname);
return 0;
}
if (vname->type == 0) {
x509_constraints_name_free(vname);
vname = NULL;
continue;
}
if (!x509_constraints_names_add(excluded, vname)) {
x509_constraints_name_free(vname);
vname = NULL;
*error = X509_V_ERR_OUT_OF_MEM;
return 0;
}
vname = NULL;
}
return 1;
@@ -1115,7 +1173,8 @@ x509_constraints_chain(STACK_OF(X509) *chain, int *error, int *depth)
goto err;
if (chain_length == 1)
return 1;
if ((names = x509_constraints_names_new()) == NULL) {
if ((names = x509_constraints_names_new(
X509_VERIFY_MAX_CHAIN_NAMES)) == NULL) {
verify_err = X509_V_ERR_OUT_OF_MEM;
goto err;
}
@@ -1128,13 +1187,13 @@ x509_constraints_chain(STACK_OF(X509) *chain, int *error, int *depth)
if ((cert = sk_X509_value(chain, i)) == NULL)
goto err;
if (cert->nc != NULL) {
if ((permitted =
x509_constraints_names_new()) == NULL) {
if ((permitted = x509_constraints_names_new(
X509_VERIFY_MAX_CHAIN_CONSTRAINTS)) == NULL) {
verify_err = X509_V_ERR_OUT_OF_MEM;
goto err;
}
if ((excluded =
x509_constraints_names_new()) == NULL) {
if ((excluded = x509_constraints_names_new(
X509_VERIFY_MAX_CHAIN_CONSTRAINTS)) == NULL) {
verify_err = X509_V_ERR_OUT_OF_MEM;
goto err;
}
@@ -1159,10 +1218,6 @@ x509_constraints_chain(STACK_OF(X509) *chain, int *error, int *depth)
if (!x509_constraints_extract_names(names, cert, 0,
&verify_err))
goto err;
if (names->names_count > X509_VERIFY_MAX_CHAIN_NAMES) {
verify_err = X509_V_ERR_OUT_OF_MEM;
goto err;
}
}
x509_constraints_names_free(names);