1 /* $OpenBSD: x509_constraints.c,v 1.10 2020/09/21 05:41:43 tb Exp $ */
3 * Copyright (c) 2020 Bob Beck <beck@openbsd.org>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 #include <sys/socket.h>
26 #include <arpa/inet.h>
28 #include <openssl/safestack.h>
29 #include <openssl/x509.h>
30 #include <openssl/x509v3.h>
32 #include "x509_internal.h"
34 /* RFC 2821 section 4.5.3.1 */
35 #define LOCAL_PART_MAX_LEN 64
36 #define DOMAIN_PART_MAX_LEN 255
38 struct x509_constraints_name *
39 x509_constraints_name_new()
41 return (calloc(1, sizeof(struct x509_constraints_name)));
45 x509_constraints_name_clear(struct x509_constraints_name *name)
50 memset(name, 0, sizeof(*name));
54 x509_constraints_name_free(struct x509_constraints_name *name)
58 x509_constraints_name_clear(name);
62 struct x509_constraints_name *
63 x509_constraints_name_dup(struct x509_constraints_name *name)
65 struct x509_constraints_name *new;
67 if ((new = x509_constraints_name_new()) == NULL)
69 new->type = name->type;
71 new->der_len = name->der_len;
72 if (name->der_len > 0 && (new->der = malloc(name->der_len)) == NULL)
74 memcpy(new->der, name->der, name->der_len);
75 if (name->name != NULL && (new->name = strdup(name->name)) == NULL)
77 if (name->local != NULL && (new->local = strdup(name->local)) == NULL)
79 memcpy(new->address, name->address, sizeof(name->address));
82 x509_constraints_name_free(new);
86 struct x509_constraints_names *
87 x509_constraints_names_new()
89 return (calloc(1, sizeof(struct x509_constraints_names)));
93 x509_constraints_names_clear(struct x509_constraints_names *names)
97 for (i = 0; i < names->names_count; i++)
98 x509_constraints_name_free(names->names[i]);
100 memset(names, 0, sizeof(*names));
104 x509_constraints_names_free(struct x509_constraints_names *names)
109 x509_constraints_names_clear(names);
114 x509_constraints_names_add(struct x509_constraints_names *names,
115 struct x509_constraints_name *name)
117 size_t i = names->names_count;
119 if (names->names_count == names->names_len) {
120 struct x509_constraints_name **tmp;
121 if ((tmp = recallocarray(names->names, names->names_len,
122 names->names_len + 32, sizeof(*tmp))) == NULL)
124 names->names_len += 32;
127 names->names[i] = name;
128 names->names_count++;
132 struct x509_constraints_names *
133 x509_constraints_names_dup(struct x509_constraints_names *names)
135 struct x509_constraints_names *new = NULL;
136 struct x509_constraints_name *name = NULL;
142 if ((new = x509_constraints_names_new()) == NULL)
144 for (i = 0; i < names->names_count; i++) {
145 if ((name = x509_constraints_name_dup(names->names[i])) == NULL)
147 if (!x509_constraints_names_add(new, name))
152 x509_constraints_names_free(new);
153 x509_constraints_name_free(name);
159 * Validate that the name contains only a hostname consisting of RFC
160 * 5890 compliant A-labels (see RFC 6066 section 3). This is more
161 * permissive to allow for a leading '*' for a SAN DNSname wildcard,
162 * or a leading '.' for a subdomain based constraint, as well as
163 * allowing for '_' which is commonly accepted by nonconformant
164 * DNS implementaitons.
167 x509_constraints_valid_domain_internal(uint8_t *name, size_t len)
174 if (len > DOMAIN_PART_MAX_LEN)
177 for (i = 0; i < len; i++) {
183 /* Everything has to be ASCII, with no NUL byte */
184 if (!isascii(c) || c == '\0')
186 /* It must be alphanumeric, a '-', '.', '_' or '*' */
187 if (!isalnum(c) && c != '-' && c != '.' && c != '_' && c != '*')
190 /* '*' can only be the first thing. */
191 if (c == '*' && !first)
194 /* '-' must not start a component or be at the end. */
195 if (c == '-' && (component == 0 || i == len - 1))
199 * '.' must not be at the end. It may be first overall
200 * but must not otherwise start a component.
202 if (c == '.' && ((component == 0 && !first) || i == len - 1))
206 /* Components can not end with a dash. */
209 /* Start new component */
213 /* Components must be 63 chars or less. */
214 if (++component > 63)
221 x509_constraints_valid_domain(uint8_t *name, size_t len)
225 if (name[0] == '*') /* wildcard not allowed in a domain name */
228 * A domain may not be less than two characters, so you can't
229 * have a require subdomain name with less than that.
231 if (len < 3 && name[0] == '.')
233 return x509_constraints_valid_domain_internal(name, len);
237 x509_constraints_valid_host(uint8_t *name, size_t len)
239 struct sockaddr_in sin4;
240 struct sockaddr_in6 sin6;
244 if (name[0] == '*') /* wildcard not allowed in a host name */
246 if (name[0] == '.') /* leading . not allowed in a host name*/
248 if (inet_pton(AF_INET, name, &sin4) == 1)
250 if (inet_pton(AF_INET6, name, &sin6) == 1)
252 return x509_constraints_valid_domain_internal(name, len);
256 x509_constraints_valid_sandns(uint8_t *name, size_t len)
261 if (name[0] == '.') /* leading . not allowed in a SAN DNS name */
264 * A domain may not be less than two characters, so you
265 * can't wildcard a single domain of less than that
267 if (len < 4 && name[0] == '*')
270 * A wildcard may only be followed by a '.'
272 if (len >= 4 && name[0] == '*' && name[1] != '.')
275 return x509_constraints_valid_domain_internal(name, len);
279 local_part_ok(char c)
281 return (('0' <= c && c <= '9') || ('a' <= c && c <= 'z') ||
282 ('A' <= c && c <= 'Z') || c == '!' || c == '#' || c == '$' ||
283 c == '%' || c == '&' || c == '\'' || c == '*' || c == '+' ||
284 c == '-' || c == '/' || c == '=' || c == '?' || c == '^' ||
285 c == '_' || c == '`' || c == '{' || c == '|' || c == '}' ||
286 c == '~' || c == '.');
290 * Parse "candidate" as an RFC 2821 mailbox.
291 * Returns 0 if candidate is not a valid mailbox or if an error occurs.
292 * Returns 1 if candidate is a mailbox and adds newly allocated
293 * local and domain parts of the mailbox to "name->local" and name->name"
296 x509_constraints_parse_mailbox(uint8_t *candidate, size_t len,
297 struct x509_constraints_name *name)
299 char working[DOMAIN_PART_MAX_LEN + 1] = { 0 };
300 char *candidate_local = NULL;
301 char *candidate_domain = NULL;
306 if (candidate == NULL)
309 /* It can't be bigger than the local part, domain part and the '@' */
310 if (len > LOCAL_PART_MAX_LEN + DOMAIN_PART_MAX_LEN + 1)
313 for (i = 0; i < len; i++) {
314 char c = candidate[i];
315 /* non ascii, cr, lf, or nul is never allowed */
316 if (!isascii(c) || c == '\r' || c == '\n' || c == '\0')
319 /* local part is quoted part */
322 /* can not start with a . */
326 if (wi > DOMAIN_PART_MAX_LEN)
333 if (candidate_local != NULL) {
334 /* We are looking for the domain part */
335 if (wi > DOMAIN_PART_MAX_LEN)
341 if (candidate_domain != NULL)
343 candidate_domain = strdup(working);
344 if (candidate_domain == NULL)
349 /* We are looking for the local part */
350 if (wi > LOCAL_PART_MAX_LEN)
358 if (c == '"' && i != 0) {
359 /* end the quoted part. @ must be next */
360 if (i + 1 == len || candidate[i + 1] != '@')
365 * XXX Go strangely permits sp but forbids ht
371 continue; /* all's good inside our quoted string */
376 if (candidate_local != NULL)
378 candidate_local = strdup(working);
379 if (candidate_local == NULL)
381 memset(working, 0, sizeof(working));
387 * RFC 3936 hints these can happen outside of
388 * quotend string. don't include the \ but
389 * next character must be ok.
393 if (!local_part_ok(candidate[i + 1]))
397 if (!local_part_ok(c))
401 if (candidate_local == NULL || candidate_domain == NULL)
403 if (!x509_constraints_valid_host(candidate_domain,
404 strlen(candidate_domain)))
407 name->local = candidate_local;
408 name->name = candidate_domain;
409 name->type = GEN_EMAIL;
412 free(candidate_local);
413 free(candidate_domain);
418 x509_constraints_valid_domain_constraint(uint8_t *constraint, size_t len)
421 return 1; /* empty constraints match */
423 if (constraint[0] == '*') /* wildcard not allowed in a constraint */
427 * A domain may not be less than two characters, so you
428 * can't match a single domain of less than that
430 if (len < 3 && constraint[0] == '.')
432 return x509_constraints_valid_domain_internal(constraint, len);
436 * Extract the host part of a URI, returns the host part as a c string
437 * the caller must free, or or NULL if it could not be found or is
441 * the authority part of a uri starts with // and is terminated with
442 * the next '/', '?', '#' or end of the URI.
444 * The authority itself contains [userinfo '@'] host [: port]
446 * so the host starts at the start or after the '@', and ends
447 * with end of URI, '/', '?', "#', or ':'.
450 x509_constraints_uri_host(uint8_t *uri, size_t len, char **hostpart)
452 size_t i, hostlen = 0;
453 uint8_t *authority = NULL;
457 * Find first '//'. there must be at least a '//' and
462 for (i = 0; i < len - 1; i++) {
463 if (!isascii(uri[i]))
465 if (uri[i] == '/' && uri[i + 1] == '/') {
466 authority = uri + i + 2;
470 if (authority == NULL)
472 for (i = authority - uri; i < len; i++) {
473 if (!isascii(uri[i]))
475 /* it has a userinfo part */
478 /* it can only have one */
481 /* start after the userinfo part */
485 /* did we find the end? */
486 if (uri[i] == ':' || uri[i] == '/' || uri[i] == '?' ||
495 if (!x509_constraints_valid_host(host, hostlen))
497 *hostpart = strndup(host, hostlen);
502 x509_constraints_sandns(char *sandns, size_t dlen, char *constraint, size_t len)
507 return 1; /* an empty constraint matches everything */
509 /* match the end of the domain */
512 suffix = sandns + (dlen - len);
513 return (strncasecmp(suffix, constraint, len) == 0);
517 * Validate a pre-validated domain of length dlen against a pre-validated
518 * constraint of length len.
520 * returns 1 if the domain and constraint match.
521 * returns 0 otherwise.
523 * an empty constraint matches everyting.
524 * constraint will be matched against the domain as a suffix if it
526 * domain will be matched against the constraint as a suffix if it
530 x509_constraints_domain(char *domain, size_t dlen, char *constraint, size_t len)
533 return 1; /* an empty constraint matches everything */
535 if (constraint[0] == '.') {
536 /* match the end of the domain */
540 suffix = domain + (dlen - len);
541 return (strncasecmp(suffix, constraint, len) == 0);
543 if (domain[0] == '.') {
544 /* match the end of the constraint */
548 suffix = constraint + (len - dlen);
549 return (strncasecmp(suffix, domain, dlen) == 0);
551 /* otherwise we must exactly match the constraint */
554 return (strncasecmp(domain, constraint, len) == 0);
558 x509_constraints_uri(uint8_t *uri, size_t ulen, uint8_t *constraint, size_t len,
562 char *hostpart = NULL;
564 if (!x509_constraints_uri_host(uri, ulen, &hostpart)) {
565 *error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
568 if (hostpart == NULL) {
569 *error = X509_V_ERR_OUT_OF_MEM;
572 if (!x509_constraints_valid_domain_constraint(constraint, len)) {
573 *error = X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX;
576 ret = x509_constraints_domain(hostpart, strlen(hostpart), constraint,
584 * Verify a validated address of size alen with a validated contraint
585 * of size constraint_len. returns 1 if matching, 0 if not.
586 * Addresses are assumed to be pre-validated for a length of 4 and 8
587 * respectively for ipv4 addreses and constraints, and a length of
588 * 16 and 32 respectively for ipv6 address constraints by the caller.
591 x509_constraints_ipaddr(uint8_t *address, size_t alen, uint8_t *constraint,
600 mask = constraint + alen;
601 for (i = 0; i < alen; i++) {
602 if ((address[i] & mask[i]) != (constraint[i] & mask[i]))
609 * Verify a canonicalized der encoded constraint dirname
610 * a canonicalized der encoded constraint.
613 x509_constraints_dirname(uint8_t *dirname, size_t dlen,
614 uint8_t *constraint, size_t len)
618 return (memcmp(constraint, dirname, len) == 0);
622 * De-obfuscate a GENERAL_NAME into useful bytes for a name or constraint.
625 x509_constraints_general_to_bytes(GENERAL_NAME *name, uint8_t **bytes,
631 if (name->type == GEN_DNS) {
632 ASN1_IA5STRING *aname = name->d.dNSName;
633 *bytes = aname->data;
634 *len = strlen(aname->data);
637 if (name->type == GEN_EMAIL) {
638 ASN1_IA5STRING *aname = name->d.rfc822Name;
639 *bytes = aname->data;
640 *len = strlen(aname->data);
643 if (name->type == GEN_URI) {
644 ASN1_IA5STRING *aname = name->d.uniformResourceIdentifier;
645 *bytes = aname->data;
646 *len = strlen(aname->data);
649 if (name->type == GEN_DIRNAME) {
650 X509_NAME *dname = name->d.directoryName;
651 if (!dname->modified || i2d_X509_NAME(dname, NULL) >= 0) {
652 *bytes = dname->canon_enc;
653 *len = dname->canon_enclen;
657 if (name->type == GEN_IPADD) {
658 *bytes = name->d.ip->data;
659 *len = name->d.ip->length;
667 * Extract the relevant names for constraint checking from "cert",
668 * validate them, and add them to the list of cert names for "chain".
669 * returns 1 on success sets error and returns 0 on failure.
672 x509_constraints_extract_names(struct x509_constraints_names *names,
673 X509 *cert, int is_leaf, int *error)
675 struct x509_constraints_name *vname = NULL;
676 X509_NAME *subject_name;
679 int name_type, include_cn = is_leaf, include_email = is_leaf;
681 /* first grab the altnames */
682 while ((name = sk_GENERAL_NAME_value(cert->altname, i++)) != NULL) {
683 uint8_t *bytes = NULL;
686 if ((vname = x509_constraints_name_new()) == NULL) {
687 *error = X509_V_ERR_OUT_OF_MEM;
691 name_type = x509_constraints_general_to_bytes(name, &bytes,
695 if (!x509_constraints_valid_sandns(bytes, len)) {
696 *error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
699 if ((vname->name = strdup(bytes)) == NULL) {
700 *error = X509_V_ERR_OUT_OF_MEM;
704 include_cn = 0; /* don't use cn from subject */
707 if (!x509_constraints_parse_mailbox(bytes, len,
709 *error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
712 vname->type = GEN_EMAIL;
713 include_email = 0; /* don't use email from subject */
716 if (!x509_constraints_uri_host(bytes, len, &vname->name)) {
717 *error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
720 if (vname->name == NULL) {
721 *error = X509_V_ERR_OUT_OF_MEM;
724 vname->type = GEN_URI;
727 if (bytes == NULL || ((vname->der = malloc(len)) ==
729 *error = X509_V_ERR_OUT_OF_MEM;
733 *error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
736 memcpy(vname->der, bytes, len);
737 vname->der_len = len;
738 vname->type = GEN_DIRNAME;
744 vname->af = AF_INET6;
745 if (vname->af != AF_INET && vname->af !=
747 *error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
750 memcpy(vname->address, bytes, len);
751 vname->type = GEN_IPADD;
754 /* Ignore this name */
755 x509_constraints_name_free(vname);
759 if (!x509_constraints_names_add(names, vname)) {
760 *error = X509_V_ERR_OUT_OF_MEM;
766 x509_constraints_name_free(vname);
769 subject_name = X509_get_subject_name(cert);
770 if (X509_NAME_entry_count(subject_name) > 0) {
771 X509_NAME_ENTRY *email;
774 * This cert has a non-empty subject, so we must add
775 * the subject as a dirname to be compared against
776 * any dirname constraints
778 if ((subject_name->modified &&
779 i2d_X509_NAME(subject_name, NULL) < 0) ||
780 (vname = x509_constraints_name_new()) == NULL ||
781 (vname->der = malloc(subject_name->canon_enclen)) == NULL) {
782 *error = X509_V_ERR_OUT_OF_MEM;
786 memcpy(vname->der, subject_name->canon_enc,
787 subject_name->canon_enclen);
788 vname->der_len = subject_name->canon_enclen;
789 vname->type = GEN_DIRNAME;
790 if (!x509_constraints_names_add(names, vname)) {
791 *error = X509_V_ERR_OUT_OF_MEM;
796 * Get any email addresses from the subject line, and
797 * add them as mbox names to be compared against any
800 while (include_email &&
801 (i = X509_NAME_get_index_by_NID(subject_name,
802 NID_pkcs9_emailAddress, i)) >= 0) {
804 if ((email = X509_NAME_get_entry(subject_name, i)) == NULL ||
805 (aname = X509_NAME_ENTRY_get_data(email)) == NULL) {
806 *error = X509_V_ERR_OUT_OF_MEM;
809 if ((vname = x509_constraints_name_new()) == NULL) {
810 *error = X509_V_ERR_OUT_OF_MEM;
813 if (!x509_constraints_parse_mailbox(aname->data,
814 aname->length, vname)) {
815 *error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
818 vname->type = GEN_EMAIL;
819 if (!x509_constraints_names_add(names, vname)) {
820 *error = X509_V_ERR_OUT_OF_MEM;
826 * Include the CN as a hostname to be checked againt
827 * name constraints if it looks like a hostname.
830 (i = X509_NAME_get_index_by_NID(subject_name,
831 NID_commonName, i)) >= 0) {
833 if ((cn = X509_NAME_get_entry(subject_name, i)) == NULL ||
834 (aname = X509_NAME_ENTRY_get_data(cn)) == NULL) {
835 *error = X509_V_ERR_OUT_OF_MEM;
838 if (!x509_constraints_valid_host(aname->data,
840 continue; /* ignore it if not a hostname */
841 if ((vname = x509_constraints_name_new()) == NULL) {
842 *error = X509_V_ERR_OUT_OF_MEM;
845 if ((vname->name = strndup(aname->data,
846 aname->length)) == NULL) {
847 *error = X509_V_ERR_OUT_OF_MEM;
850 vname->type = GEN_DNS;
851 if (!x509_constraints_names_add(names, vname)) {
852 *error = X509_V_ERR_OUT_OF_MEM;
860 x509_constraints_name_free(vname);
865 * Validate a constraint in a general name, putting the relevant data
866 * into "name" if valid. returns 0, and sets error if the constraint is
867 * not valid. returns 1 if the constraint validated. name->type will be
868 * set to a valid type if there is constraint data in name, or unmodified
869 * if the GENERAL_NAME had a valid type but was ignored.
872 x509_constraints_validate(GENERAL_NAME *constraint,
873 struct x509_constraints_name *name, int *error)
875 uint8_t *bytes = NULL;
879 name_type = x509_constraints_general_to_bytes(constraint, &bytes, &len);
882 if (bytes == NULL || (name->der = malloc(len)) == NULL) {
883 *error = X509_V_ERR_OUT_OF_MEM;
887 goto err; /* XXX The RFCs are delightfully vague */
888 memcpy(name->der, bytes, len);
890 name->type = GEN_DIRNAME;
893 if (!x509_constraints_valid_domain_constraint(bytes, len))
895 if ((name->name = strdup(bytes)) == NULL) {
896 *error = X509_V_ERR_OUT_OF_MEM;
899 name->type = GEN_DNS;
902 if (memchr(bytes, '@', len) != NULL) {
903 if (!x509_constraints_parse_mailbox(bytes, len, name))
906 if (!x509_constraints_valid_domain_constraint(bytes,
909 if ((name->name = strdup(bytes)) == NULL) {
910 *error = X509_V_ERR_OUT_OF_MEM;
914 name->type = GEN_EMAIL;
917 /* Constraints are ip then mask */
924 memcpy(&name->address[0], bytes, len);
925 name->type = GEN_IPADD;
928 if (!x509_constraints_valid_domain_constraint(bytes, len))
930 name->name = strdup(bytes);
931 name->type = GEN_URI;
938 *error = X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX;
943 x509_constraints_extract_constraints(X509 *cert,
944 struct x509_constraints_names *permitted,
945 struct x509_constraints_names *excluded,
948 struct x509_constraints_name *vname;
949 NAME_CONSTRAINTS *nc = cert->nc;
950 GENERAL_SUBTREE *subtree;
956 for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->permittedSubtrees); i++) {
958 subtree = sk_GENERAL_SUBTREE_value(nc->permittedSubtrees, i);
959 if (subtree->minimum || subtree->maximum) {
960 *error = X509_V_ERR_SUBTREE_MINMAX;
963 if ((vname = x509_constraints_name_new()) == NULL) {
964 *error = X509_V_ERR_OUT_OF_MEM;
967 if (x509_constraints_validate(subtree->base, vname, error) ==
969 x509_constraints_name_free(vname);
972 if (vname->type == 0) {
973 x509_constraints_name_free(vname);
976 if (!x509_constraints_names_add(permitted, vname)) {
977 x509_constraints_name_free(vname);
978 *error = X509_V_ERR_OUT_OF_MEM;
983 for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->excludedSubtrees); i++) {
984 subtree = sk_GENERAL_SUBTREE_value(nc->excludedSubtrees, i);
985 if (subtree->minimum || subtree->maximum) {
986 *error = X509_V_ERR_SUBTREE_MINMAX;
989 if ((vname = x509_constraints_name_new()) == NULL) {
990 *error = X509_V_ERR_OUT_OF_MEM;
993 if (x509_constraints_validate(subtree->base, vname, error) ==
995 x509_constraints_name_free(vname);
998 if (vname->type == 0) {
999 x509_constraints_name_free(vname);
1002 if (!x509_constraints_names_add(excluded, vname)) {
1003 x509_constraints_name_free(vname);
1004 *error = X509_V_ERR_OUT_OF_MEM;
1013 * Match a validated name in "name" against a validated constraint in
1014 * "constraint" return 1 if then name matches, 0 otherwise.
1017 x509_constraints_match(struct x509_constraints_name *name,
1018 struct x509_constraints_name *constraint)
1020 if (name->type != constraint->type)
1022 if (name->type == GEN_DNS)
1023 return x509_constraints_sandns(name->name, strlen(name->name),
1024 constraint->name, strlen(constraint->name));
1025 if (name->type == GEN_URI)
1026 return x509_constraints_domain(name->name, strlen(name->name),
1027 constraint->name, strlen(constraint->name));
1028 if (name->type == GEN_IPADD) {
1029 size_t nlen = name->af == AF_INET ? 4 : 16;
1030 size_t clen = name->af == AF_INET ? 8 : 32;
1031 if (name->af != AF_INET && name->af != AF_INET6)
1033 if (constraint->af != AF_INET && constraint->af != AF_INET6)
1035 if (name->af != constraint->af)
1037 return x509_constraints_ipaddr(name->address, nlen,
1038 constraint->address, clen);
1040 if (name->type == GEN_EMAIL) {
1041 if (constraint->local) {
1042 /* mailbox local and domain parts must exactly match */
1043 return (strcmp(name->local, constraint->local) == 0 &&
1044 strcmp(name->name, constraint->name) == 0);
1046 /* otherwise match the constraint to the domain part */
1047 return x509_constraints_domain(name->name, strlen(name->name),
1048 constraint->name, strlen(constraint->name));
1050 if (name->type == GEN_DIRNAME)
1051 return x509_constraints_dirname(name->der, name->der_len,
1052 constraint->der, constraint->der_len);
1057 * Make sure every name in names does not match any excluded
1058 * constraints, and does match at least one permitted constraint if
1059 * any are present. Returns 1 if ok, 0, and sets error if not.
1062 x509_constraints_check(struct x509_constraints_names *names,
1063 struct x509_constraints_names *permitted,
1064 struct x509_constraints_names *excluded, int *error)
1068 for (i = 0; i < names->names_count; i++) {
1069 int permitted_seen = 0;
1070 int permitted_matched = 0;
1072 for (j = 0; j < excluded->names_count; j++) {
1073 if (x509_constraints_match(names->names[i],
1074 excluded->names[j])) {
1075 *error = X509_V_ERR_EXCLUDED_VIOLATION;
1079 for (j = 0; j < permitted->names_count; j++) {
1080 if (permitted->names[j]->type == names->names[i]->type)
1082 if (x509_constraints_match(names->names[i],
1083 permitted->names[j])) {
1084 permitted_matched++;
1088 if (permitted_seen && !permitted_matched) {
1089 *error = X509_V_ERR_PERMITTED_VIOLATION;
1097 * Walk a validated chain of X509 certs, starting at the leaf, and
1098 * validate the name constraints in the chain. Intended for use with
1099 * the legacy X509 validtion code in x509_vfy.c
1101 * returns 1 if the constraints are ok, 0 otherwise, setting error and
1105 x509_constraints_chain(STACK_OF(X509) *chain, int *error, int *depth)
1107 int chain_length, verify_err = X509_V_ERR_UNSPECIFIED, i = 0;
1108 struct x509_constraints_names *names = NULL;
1109 struct x509_constraints_names *excluded = NULL;
1110 struct x509_constraints_names *permitted = NULL;
1111 size_t constraints_count = 0;
1114 if (chain == NULL || (chain_length = sk_X509_num(chain)) == 0)
1116 if (chain_length == 1)
1118 if ((names = x509_constraints_names_new()) == NULL) {
1119 verify_err = X509_V_ERR_OUT_OF_MEM;
1123 if ((cert = sk_X509_value(chain, 0)) == NULL)
1125 if (!x509_constraints_extract_names(names, cert, 1, &verify_err))
1127 for (i = 1; i < chain_length; i++) {
1128 if ((cert = sk_X509_value(chain, i)) == NULL)
1130 if (cert->nc != NULL) {
1132 x509_constraints_names_new()) == NULL) {
1133 verify_err = X509_V_ERR_OUT_OF_MEM;
1137 x509_constraints_names_new()) == NULL) {
1138 verify_err = X509_V_ERR_OUT_OF_MEM;
1141 if (!x509_constraints_extract_constraints(cert,
1142 permitted, excluded, &verify_err))
1144 constraints_count += permitted->names_count;
1145 constraints_count += excluded->names_count;
1146 if (constraints_count >
1147 X509_VERIFY_MAX_CHAIN_CONSTRAINTS) {
1148 verify_err = X509_V_ERR_OUT_OF_MEM;
1151 if (!x509_constraints_check(names, permitted, excluded,
1154 x509_constraints_names_free(excluded);
1156 x509_constraints_names_free(permitted);
1159 if (!x509_constraints_extract_names(names, cert, 0,
1162 if (names->names_count > X509_VERIFY_MAX_CHAIN_NAMES) {
1163 verify_err = X509_V_ERR_OUT_OF_MEM;
1168 x509_constraints_names_free(names);
1172 *error = verify_err;
1174 x509_constraints_names_free(excluded);
1175 x509_constraints_names_free(permitted);
1176 x509_constraints_names_free(names);