#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <openssl/safestack.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include "bytestring.h"
#include "x509_internal.h"
#define LOCAL_PART_MAX_LEN (size_t)64
#define DOMAIN_PART_MAX_LEN (size_t)255
#define MAX_IP_ADDRESS_LENGTH (size_t)46
static int
cbs_is_ip_address(CBS *cbs, int *is_ip)
{
struct sockaddr_in6 sin6;
struct sockaddr_in sin4;
char *name = NULL;
*is_ip = 0;
if (CBS_len(cbs) > MAX_IP_ADDRESS_LENGTH)
return 1;
if (!CBS_strdup(cbs, &name))
return 0;
if (inet_pton(AF_INET, name, &sin4) == 1 ||
inet_pton(AF_INET6, name, &sin6) == 1)
*is_ip = 1;
free(name);
return 1;
}
struct x509_constraints_name *
x509_constraints_name_new(void)
{
return (calloc(1, sizeof(struct x509_constraints_name)));
}
void
x509_constraints_name_clear(struct x509_constraints_name *name)
{
free(name->name);
free(name->local);
free(name->der);
memset(name, 0, sizeof(*name));
}
void
x509_constraints_name_free(struct x509_constraints_name *name)
{
if (name == NULL)
return;
x509_constraints_name_clear(name);
free(name);
}
struct x509_constraints_name *
x509_constraints_name_dup(struct x509_constraints_name *name)
{
struct x509_constraints_name *new;
if ((new = x509_constraints_name_new()) == NULL)
goto err;
new->type = name->type;
new->af = name->af;
new->der_len = 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)
goto err;
memcpy(new->address, name->address, sizeof(name->address));
return new;
err:
x509_constraints_name_free(new);
return NULL;
}
struct x509_constraints_names *
x509_constraints_names_new(size_t names_max)
{
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
x509_constraints_names_clear(struct x509_constraints_names *names)
{
size_t i;
for (i = 0; i < names->names_count; i++)
x509_constraints_name_free(names->names[i]);
free(names->names);
memset(names, 0, sizeof(*names));
}
void
x509_constraints_names_free(struct x509_constraints_names *names)
{
if (names == NULL)
return;
x509_constraints_names_clear(names);
free(names);
}
int
x509_constraints_names_add(struct x509_constraints_names *names,
struct x509_constraints_name *name)
{
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,
names->names_len + 32, sizeof(*tmp))) == NULL)
return 0;
names->names_len += 32;
names->names = tmp;
}
names->names[names->names_count] = name;
names->names_count++;
return 1;
}
struct x509_constraints_names *
x509_constraints_names_dup(struct x509_constraints_names *names)
{
struct x509_constraints_names *new = NULL;
struct x509_constraints_name *name = NULL;
size_t i;
if (names == NULL)
return 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);
x509_constraints_name_free(name);
return NULL;
}
static int
x509_constraints_valid_domain_internal(CBS *cbs, int wildcards)
{
int first, component = 0;
uint8_t prev, c = 0;
size_t i, len;
CBS copy;
CBS_dup(cbs, ©);
len = CBS_len(cbs);
if (len > DOMAIN_PART_MAX_LEN)
return 0;
for (i = 0; i < len; i++) {
prev = c;
if (!CBS_get_u8(©, &c))
return 0;
first = (i == 0);
if (!isascii(c) || c == '\0')
return 0;
if (!isalnum(c) && c != '-' && c != '.' && c != '_' && c != '*')
return 0;
if (!wildcards && c == '*')
return 0;
if (c == '-' && (component == 0 || i == len - 1))
return 0;
if (c == '.' && ((component == 0 && !first) || i == len - 1))
return 0;
if (c == '.') {
if (prev == '-')
return 0;
component = 0;
continue;
}
if (prev == '*')
return 0;
if (++component > 63)
return 0;
}
return 1;
}
int
x509_constraints_valid_host(CBS *cbs, int permit_ip)
{
uint8_t first;
int is_ip;
if (!CBS_peek_u8(cbs, &first))
return 0;
if (first == '.')
return 0;
if (!permit_ip) {
if (!cbs_is_ip_address(cbs, &is_ip))
return 0;
if (is_ip)
return 0;
}
return x509_constraints_valid_domain_internal(cbs, 0);
}
int
x509_constraints_valid_sandns(CBS *cbs)
{
uint8_t first;
if (!CBS_peek_u8(cbs, &first))
return 0;
if (first == '.')
return 0;
if (CBS_len(cbs) < 4 && first == '*')
return 0;
return x509_constraints_valid_domain_internal(cbs, 1);
}
static inline int
local_part_ok(char c)
{
return (('0' <= c && c <= '9') || ('a' <= c && c <= 'z') ||
('A' <= c && c <= 'Z') || c == '!' || c == '#' || c == '$' ||
c == '%' || c == '&' || c == '\'' || c == '*' || c == '+' ||
c == '-' || c == '/' || c == '=' || c == '?' || c == '^' ||
c == '_' || c == '`' || c == '{' || c == '|' || c == '}' ||
c == '~' || c == '.');
}
int
x509_constraints_parse_mailbox(CBS *candidate,
struct x509_constraints_name *name)
{
char working[DOMAIN_PART_MAX_LEN + 1] = { 0 };
char *candidate_local = NULL;
char *candidate_domain = NULL;
CBS domain_cbs;
size_t i, len, wi = 0;
int accept = 0;
int quoted = 0;
CBS copy;
if (candidate == NULL)
return 0;
CBS_dup(candidate, ©);
if ((len = CBS_len(©)) == 0)
return 0;
if (len > LOCAL_PART_MAX_LEN + DOMAIN_PART_MAX_LEN + 1)
return 0;
for (i = 0; i < len; i++) {
char c;
if (!CBS_get_u8(©, &c))
goto bad;
if (!isascii(c) || c == '\r' || c == '\n' || c == '\0')
goto bad;
if (i == 0) {
if (c == '"')
quoted = 1;
if (c == '.')
goto bad;
}
if (accept) {
if (wi >= DOMAIN_PART_MAX_LEN)
goto bad;
working[wi++] = c;
accept = 0;
continue;
}
if (candidate_local != NULL) {
if (wi >= DOMAIN_PART_MAX_LEN)
goto bad;
working[wi++] = c;
if (i == len - 1) {
if (wi == 0)
goto bad;
if (candidate_domain != NULL)
goto bad;
candidate_domain = strdup(working);
if (candidate_domain == NULL)
goto bad;
}
continue;
}
if (wi >= LOCAL_PART_MAX_LEN)
break;
if (quoted) {
if (c == '\\') {
accept = 1;
continue;
}
if (c == '"' && i != 0) {
uint8_t next;
if (!CBS_peek_u8(©, &next))
goto bad;
if (next != '@')
goto bad;
quoted = 0;
}
if (c == 9)
goto bad;
if (wi >= LOCAL_PART_MAX_LEN)
goto bad;
working[wi++] = c;
continue;
}
if (c == '@') {
if (wi == 0)
goto bad;
if (candidate_local != NULL)
goto bad;
candidate_local = strdup(working);
if (candidate_local == NULL)
goto bad;
memset(working, 0, sizeof(working));
wi = 0;
continue;
}
if (c == '\\') {
uint8_t next;
if (!CBS_peek_u8(©, &next))
goto bad;
if (!local_part_ok(next))
goto bad;
accept = 1;
}
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)
goto bad;
CBS_init(&domain_cbs, candidate_domain, strlen(candidate_domain));
if (!x509_constraints_valid_host(&domain_cbs, 0))
goto bad;
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);
free(candidate_domain);
return 0;
}
int
x509_constraints_valid_domain_constraint(CBS *cbs)
{
uint8_t first;
if (CBS_len(cbs) == 0)
return 1;
if (CBS_len(cbs) < 3) {
if (!CBS_peek_u8(cbs, &first))
return 0;
if (first == '.')
return 0;
}
return x509_constraints_valid_domain_internal(cbs, 0);
}
int
x509_constraints_uri_host(uint8_t *uri, size_t len, char **hostpart)
{
size_t i, hostlen = 0;
uint8_t *authority = NULL;
char *host = NULL;
CBS host_cbs;
if (len < 3)
return 0;
for (i = 0; i < len - 1; i++) {
if (!isascii(uri[i]))
return 0;
if (uri[i] == '/' && uri[i + 1] == '/') {
authority = uri + i + 2;
break;
}
}
if (authority == NULL) {
if (hostpart != NULL)
*hostpart = strdup("");
return 1;
}
for (i = authority - uri; i < len; i++) {
if (!isascii(uri[i]))
return 0;
if (uri[i] == '@') {
hostlen = 0;
if (host != NULL)
break;
host = uri + i + 1;
continue;
}
if (uri[i] == ':' || uri[i] == '/' || uri[i] == '?' ||
uri[i] == '#')
break;
hostlen++;
}
if (hostlen == 0)
return 0;
if (host == NULL)
host = authority;
CBS_init(&host_cbs, host, hostlen);
if (!x509_constraints_valid_host(&host_cbs, 1))
return 0;
if (hostpart != NULL && !CBS_strdup(&host_cbs, hostpart))
return 0;
return 1;
}
int
x509_constraints_sandns(char *sandns, size_t dlen, char *constraint, size_t len)
{
char *suffix;
if (len == 0)
return 1;
if (dlen < len)
return 0;
suffix = sandns + (dlen - len);
return (strncasecmp(suffix, constraint, len) == 0);
}
int
x509_constraints_domain(char *domain, size_t dlen, char *constraint, size_t len)
{
if (len == 0)
return 1;
if (constraint[0] == '.') {
char *suffix;
if (dlen < len)
return 0;
suffix = domain + (dlen - len);
return (strncasecmp(suffix, constraint, len) == 0);
}
if (domain[0] == '.') {
char *suffix;
if (len < dlen)
return 0;
suffix = constraint + (len - dlen);
return (strncasecmp(suffix, domain, dlen) == 0);
}
if (dlen != len)
return 0;
return (strncasecmp(domain, constraint, len) == 0);
}
int
x509_constraints_uri(uint8_t *uri, size_t ulen, uint8_t *constraint,
size_t len,
int *error)
{
int ret = 0;
char *hostpart = NULL;
CBS cbs;
CBS_init(&cbs, constraint, len);
if (!x509_constraints_uri_host(uri, ulen, &hostpart)) {
*error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
goto err;
}
if (hostpart == NULL) {
*error = X509_V_ERR_OUT_OF_MEM;
goto err;
}
if (!x509_constraints_valid_domain_constraint(&cbs)) {
*error = X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX;
goto err;
}
ret = x509_constraints_domain(hostpart, strlen(hostpart), constraint,
len);
err:
free(hostpart);
return ret;
}
int
x509_constraints_ipaddr(uint8_t *address, size_t alen, uint8_t *constraint,
size_t len)
{
uint8_t *mask;
size_t i;
if (alen * 2 != len)
return 0;
mask = constraint + alen;
for (i = 0; i < alen; i++) {
if ((address[i] & mask[i]) != (constraint[i] & mask[i]))
return 0;
}
return 1;
}
int
x509_constraints_dirname(uint8_t *dirname, size_t dlen,
uint8_t *constraint, size_t len)
{
if (len > dlen)
return 0;
return (memcmp(constraint, dirname, len) == 0);
}
int
x509_constraints_general_to_bytes(GENERAL_NAME *name, uint8_t **bytes,
size_t *len)
{
*bytes = NULL;
*len = 0;
if (name->type == GEN_DNS) {
ASN1_IA5STRING *aname = name->d.dNSName;
*bytes = aname->data;
*len = aname->length;
return name->type;
}
if (name->type == GEN_EMAIL) {
ASN1_IA5STRING *aname = name->d.rfc822Name;
*bytes = aname->data;
*len = aname->length;
return name->type;
}
if (name->type == GEN_URI) {
ASN1_IA5STRING *aname = name->d.uniformResourceIdentifier;
*bytes = 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;
}
int
x509_constraints_extract_names(struct x509_constraints_names *names,
X509 *cert, int is_leaf, int *error)
{
struct x509_constraints_name *vname = NULL;
X509_NAME *subject_name;
GENERAL_NAME *name;
ssize_t i = 0;
int name_type, include_cn = is_leaf, include_email = is_leaf;
while ((name = sk_GENERAL_NAME_value(cert->altname, i++)) != NULL) {
uint8_t *bytes = NULL;
size_t len = 0;
CBS cbs;
if ((vname = x509_constraints_name_new()) == NULL) {
*error = X509_V_ERR_OUT_OF_MEM;
goto err;
}
name_type = x509_constraints_general_to_bytes(name, &bytes,
&len);
CBS_init(&cbs, bytes, len);
switch (name_type) {
case GEN_DNS:
if (!x509_constraints_valid_sandns(&cbs)) {
*error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
goto err;
}
if (!CBS_strdup(&cbs, &vname->name)) {
*error = X509_V_ERR_OUT_OF_MEM;
goto err;
}
vname->type = GEN_DNS;
include_cn = 0;
break;
case GEN_EMAIL:
if (!x509_constraints_parse_mailbox(&cbs, vname)) {
*error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
goto err;
}
vname->type = GEN_EMAIL;
include_email = 0;
break;
case GEN_URI:
if (!x509_constraints_uri_host(bytes, len,
&vname->name)) {
*error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
goto err;
}
if (vname->name == NULL) {
*error = X509_V_ERR_OUT_OF_MEM;
goto err;
}
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;
}
memcpy(vname->der, bytes, len);
vname->der_len = len;
vname->type = GEN_DIRNAME;
break;
case GEN_IPADD:
if (len == 4)
vname->af = AF_INET;
if (len == 16)
vname->af = AF_INET6;
if (vname->af != AF_INET && vname->af != AF_INET6) {
*error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
goto err;
}
memcpy(vname->address, bytes, len);
vname->type = GEN_IPADD;
break;
default:
x509_constraints_name_free(vname);
vname = NULL;
continue;
}
if (!x509_constraints_names_add(names, vname)) {
*error = X509_V_ERR_OUT_OF_MEM;
goto err;
}
vname = NULL;
}
x509_constraints_name_free(vname);
vname = NULL;
subject_name = X509_get_subject_name(cert);
if (X509_NAME_entry_count(subject_name) > 0) {
X509_NAME_ENTRY *email;
X509_NAME_ENTRY *cn;
if ((subject_name->modified &&
i2d_X509_NAME(subject_name, NULL) < 0) ||
(vname = x509_constraints_name_new()) == NULL ||
(vname->der = malloc(subject_name->canon_enclen)) == NULL) {
*error = X509_V_ERR_OUT_OF_MEM;
goto err;
}
memcpy(vname->der, subject_name->canon_enc,
subject_name->canon_enclen);
vname->der_len = subject_name->canon_enclen;
vname->type = GEN_DIRNAME;
if (!x509_constraints_names_add(names, vname)) {
*error = X509_V_ERR_OUT_OF_MEM;
goto err;
}
vname = NULL;
while (include_email &&
(i = X509_NAME_get_index_by_NID(subject_name,
NID_pkcs9_emailAddress, i)) >= 0) {
ASN1_STRING *aname;
CBS cbs;
if ((email = X509_NAME_get_entry(subject_name, i)) ==
NULL ||
(aname = X509_NAME_ENTRY_get_data(email)) == NULL) {
*error = X509_V_ERR_OUT_OF_MEM;
goto err;
}
CBS_init(&cbs, aname->data, aname->length);
if ((vname = x509_constraints_name_new()) == NULL) {
*error = X509_V_ERR_OUT_OF_MEM;
goto err;
}
if (!x509_constraints_parse_mailbox(&cbs, vname)) {
*error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
goto err;
}
vname->type = GEN_EMAIL;
if (!x509_constraints_names_add(names, vname)) {
*error = X509_V_ERR_OUT_OF_MEM;
goto err;
}
vname = NULL;
}
while (include_cn &&
(i = X509_NAME_get_index_by_NID(subject_name,
NID_commonName, i)) >= 0) {
CBS cbs;
ASN1_STRING *aname;
if ((cn = X509_NAME_get_entry(subject_name, i)) ==
NULL ||
(aname = X509_NAME_ENTRY_get_data(cn)) == NULL) {
*error = X509_V_ERR_OUT_OF_MEM;
goto err;
}
CBS_init(&cbs, aname->data, aname->length);
if (!x509_constraints_valid_host(&cbs, 0))
continue;
if ((vname = x509_constraints_name_new()) == NULL) {
*error = X509_V_ERR_OUT_OF_MEM;
goto err;
}
if (!CBS_strdup(&cbs, &vname->name)) {
*error = X509_V_ERR_OUT_OF_MEM;
goto err;
}
vname->type = GEN_DNS;
if (!x509_constraints_names_add(names, vname)) {
*error = X509_V_ERR_OUT_OF_MEM;
goto err;
}
vname = NULL;
}
}
return 1;
err:
x509_constraints_name_free(vname);
return 0;
}
int
x509_constraints_validate(GENERAL_NAME *constraint,
struct x509_constraints_name **out_name, int *out_error)
{
uint8_t next, *bytes = NULL;
size_t len = 0;
struct x509_constraints_name *name;
int error = X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX;
int name_type;
CBS cbs;
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);
CBS_init(&cbs, bytes, len);
switch (name_type) {
case GEN_DIRNAME:
if (len == 0)
goto err;
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;
break;
case GEN_DNS:
if (!x509_constraints_valid_domain_constraint(&cbs))
goto err;
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 (len > 0 && memchr(bytes + 1, '@', len - 1) != NULL) {
if (!x509_constraints_parse_mailbox(&cbs, name))
goto err;
break;
}
if (CBS_len(&cbs) > 0) {
if (!CBS_peek_u8(&cbs, &next))
goto err;
if (next == '@') {
if (!CBS_skip(&cbs, 1))
goto err;
}
}
if (!x509_constraints_valid_domain_constraint(&cbs))
goto err;
if (!CBS_strdup(&cbs, &name->name)) {
error = X509_V_ERR_OUT_OF_MEM;
goto err;
}
name->type = GEN_EMAIL;
break;
case GEN_IPADD:
if (len == 8)
name->af = AF_INET;
else if (len == 32)
name->af = AF_INET6;
else
goto err;
memcpy(&name->address[0], bytes, len);
name->type = GEN_IPADD;
break;
case GEN_URI:
if (!x509_constraints_valid_domain_constraint(&cbs))
goto err;
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:
x509_constraints_name_free(name);
if (out_error != NULL)
*out_error = error;
return 0;
}
int
x509_constraints_extract_constraints(X509 *cert,
struct x509_constraints_names *permitted,
struct x509_constraints_names *excluded,
int *error)
{
struct x509_constraints_name *vname = NULL;
NAME_CONSTRAINTS *nc = cert->nc;
GENERAL_SUBTREE *subtree;
int i;
if (nc == NULL)
return 1;
for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->permittedSubtrees); i++) {
subtree = sk_GENERAL_SUBTREE_value(nc->permittedSubtrees, i);
if (subtree->minimum || subtree->maximum) {
*error = X509_V_ERR_SUBTREE_MINMAX;
return 0;
}
if (!x509_constraints_validate(subtree->base, &vname, error))
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++) {
subtree = sk_GENERAL_SUBTREE_value(nc->excludedSubtrees, i);
if (subtree->minimum || subtree->maximum) {
*error = X509_V_ERR_SUBTREE_MINMAX;
return 0;
}
if (!x509_constraints_validate(subtree->base, &vname, error))
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;
}
int
x509_constraints_match(struct x509_constraints_name *name,
struct x509_constraints_name *constraint)
{
if (name->type != constraint->type)
return 0;
if (name->type == GEN_DNS)
return x509_constraints_sandns(name->name, strlen(name->name),
constraint->name, strlen(constraint->name));
if (name->type == GEN_URI)
return x509_constraints_domain(name->name, strlen(name->name),
constraint->name, strlen(constraint->name));
if (name->type == GEN_IPADD) {
size_t nlen = name->af == AF_INET ? 4 : 16;
size_t clen = name->af == AF_INET ? 8 : 32;
if (name->af != AF_INET && name->af != AF_INET6)
return 0;
if (constraint->af != AF_INET && constraint->af != AF_INET6)
return 0;
if (name->af != constraint->af)
return 0;
return x509_constraints_ipaddr(name->address, nlen,
constraint->address, clen);
}
if (name->type == GEN_EMAIL) {
if (constraint->local) {
return (strcmp(name->local, constraint->local) == 0 &&
strcmp(name->name, constraint->name) == 0);
}
return x509_constraints_domain(name->name, strlen(name->name),
constraint->name, strlen(constraint->name));
}
if (name->type == GEN_DIRNAME)
return x509_constraints_dirname(name->der, name->der_len,
constraint->der, constraint->der_len);
return 0;
}
int
x509_constraints_check(struct x509_constraints_names *names,
struct x509_constraints_names *permitted,
struct x509_constraints_names *excluded, int *error)
{
size_t i, j;
for (i = 0; i < names->names_count; i++) {
int permitted_seen = 0;
int permitted_matched = 0;
for (j = 0; j < excluded->names_count; j++) {
if (x509_constraints_match(names->names[i],
excluded->names[j])) {
*error = X509_V_ERR_EXCLUDED_VIOLATION;
return 0;
}
}
for (j = 0; j < permitted->names_count; j++) {
if (permitted->names[j]->type == names->names[i]->type)
permitted_seen++;
if (x509_constraints_match(names->names[i],
permitted->names[j])) {
permitted_matched++;
break;
}
}
if (permitted_seen && !permitted_matched) {
*error = X509_V_ERR_PERMITTED_VIOLATION;
return 0;
}
}
return 1;
}
int
x509_constraints_chain(STACK_OF(X509) *chain, int *error, int *depth)
{
int chain_length, verify_err = X509_V_ERR_UNSPECIFIED, i = 0;
struct x509_constraints_names *names = NULL;
struct x509_constraints_names *excluded = NULL;
struct x509_constraints_names *permitted = NULL;
size_t constraints_count = 0;
X509 *cert;
if (chain == NULL || (chain_length = sk_X509_num(chain)) == 0)
goto err;
if (chain_length == 1)
return 1;
if ((names = x509_constraints_names_new(
X509_VERIFY_MAX_CHAIN_NAMES)) == NULL) {
verify_err = X509_V_ERR_OUT_OF_MEM;
goto err;
}
if ((cert = sk_X509_value(chain, 0)) == NULL)
goto err;
if (!x509_constraints_extract_names(names, cert, 1, &verify_err))
goto err;
for (i = 1; i < chain_length; i++) {
if ((cert = sk_X509_value(chain, i)) == NULL)
goto err;
if (cert->nc != 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(
X509_VERIFY_MAX_CHAIN_CONSTRAINTS)) == NULL) {
verify_err = X509_V_ERR_OUT_OF_MEM;
goto err;
}
if (!x509_constraints_extract_constraints(cert,
permitted, excluded, &verify_err))
goto err;
constraints_count += permitted->names_count;
constraints_count += excluded->names_count;
if (constraints_count >
X509_VERIFY_MAX_CHAIN_CONSTRAINTS) {
verify_err = X509_V_ERR_OUT_OF_MEM;
goto err;
}
if (!x509_constraints_check(names, permitted, excluded,
&verify_err))
goto err;
x509_constraints_names_free(excluded);
excluded = NULL;
x509_constraints_names_free(permitted);
permitted = NULL;
}
if (!x509_constraints_extract_names(names, cert, 0,
&verify_err))
goto err;
}
x509_constraints_names_free(names);
return 1;
err:
*error = verify_err;
*depth = i;
x509_constraints_names_free(excluded);
x509_constraints_names_free(permitted);
x509_constraints_names_free(names);
return 0;
}