xref: /openbsd-src/lib/libcrypto/x509/x509_constraints.c (revision 3374c67d44f9b75b98444cbf63020f777792342e)
1 /* $OpenBSD: x509_constraints.c,v 1.31 2022/12/26 07:18:53 jmc Exp $ */
2 /*
3  * Copyright (c) 2020 Bob Beck <beck@openbsd.org>
4  *
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.
8  *
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.
16  */
17 
18 #include <ctype.h>
19 #include <errno.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <time.h>
23 #include <unistd.h>
24 
25 #include <sys/socket.h>
26 #include <arpa/inet.h>
27 
28 #include <openssl/safestack.h>
29 #include <openssl/x509.h>
30 #include <openssl/x509v3.h>
31 
32 #include "bytestring.h"
33 #include "x509_internal.h"
34 
35 /* RFC 2821 section 4.5.3.1 */
36 #define LOCAL_PART_MAX_LEN (size_t)64
37 #define DOMAIN_PART_MAX_LEN (size_t)255
38 #define MAX_IP_ADDRESS_LENGTH (size_t)46
39 
40 static int
41 cbs_is_ip_address(CBS *cbs)
42 {
43 	struct sockaddr_in6 sin6;
44 	struct sockaddr_in sin4;
45 	char *name = NULL;
46 	int ret = 0;
47 
48 	if (CBS_len(cbs) > MAX_IP_ADDRESS_LENGTH)
49 		return 0;
50 	if (!CBS_strdup(cbs, &name))
51 		return 0;
52 	if (inet_pton(AF_INET, name, &sin4) == 1 ||
53 	    inet_pton(AF_INET6, name, &sin6) == 1)
54 		ret = 1;
55 
56 	free(name);
57 	return ret;
58 }
59 
60 struct x509_constraints_name *
61 x509_constraints_name_new(void)
62 {
63 	return (calloc(1, sizeof(struct x509_constraints_name)));
64 }
65 
66 void
67 x509_constraints_name_clear(struct x509_constraints_name *name)
68 {
69 	free(name->name);
70 	free(name->local);
71 	free(name->der);
72 	memset(name, 0, sizeof(*name));
73 }
74 
75 void
76 x509_constraints_name_free(struct x509_constraints_name *name)
77 {
78 	if (name == NULL)
79 		return;
80 	x509_constraints_name_clear(name);
81 	free(name);
82 }
83 
84 struct x509_constraints_name *
85 x509_constraints_name_dup(struct x509_constraints_name *name)
86 {
87 	struct x509_constraints_name *new;
88 
89 	if ((new = x509_constraints_name_new()) == NULL)
90 		goto err;
91 	new->type = name->type;
92 	new->af = name->af;
93 	new->der_len = name->der_len;
94 	if (name->der_len > 0) {
95 		if ((new->der = malloc(name->der_len)) == NULL)
96 			goto err;
97 		memcpy(new->der, name->der, name->der_len);
98 	}
99 	if (name->name != NULL && (new->name = strdup(name->name)) == NULL)
100 		goto err;
101 	if (name->local != NULL && (new->local = strdup(name->local)) == NULL)
102 		goto err;
103 	memcpy(new->address, name->address, sizeof(name->address));
104 	return new;
105  err:
106 	x509_constraints_name_free(new);
107 	return NULL;
108 }
109 
110 struct x509_constraints_names *
111 x509_constraints_names_new(size_t names_max)
112 {
113 	struct x509_constraints_names *new;
114 
115 	if ((new = calloc(1, sizeof(struct x509_constraints_names))) == NULL)
116 		return NULL;
117 
118 	new->names_max = names_max;
119 
120 	return new;
121 }
122 
123 void
124 x509_constraints_names_clear(struct x509_constraints_names *names)
125 {
126 	size_t i;
127 
128 	for (i = 0; i < names->names_count; i++)
129 		x509_constraints_name_free(names->names[i]);
130 	free(names->names);
131 	memset(names, 0, sizeof(*names));
132 }
133 
134 void
135 x509_constraints_names_free(struct x509_constraints_names *names)
136 {
137 	if (names == NULL)
138 		return;
139 
140 	x509_constraints_names_clear(names);
141 	free(names);
142 }
143 
144 int
145 x509_constraints_names_add(struct x509_constraints_names *names,
146     struct x509_constraints_name *name)
147 {
148 	if (names->names_count >= names->names_max)
149 		return 0;
150 	if (names->names_count == names->names_len) {
151 		struct x509_constraints_name **tmp;
152 		if ((tmp = recallocarray(names->names, names->names_len,
153 		    names->names_len + 32, sizeof(*tmp))) == NULL)
154 			return 0;
155 		names->names_len += 32;
156 		names->names = tmp;
157 	}
158 	names->names[names->names_count] = name;
159 	names->names_count++;
160 	return 1;
161 }
162 
163 struct x509_constraints_names *
164 x509_constraints_names_dup(struct x509_constraints_names *names)
165 {
166 	struct x509_constraints_names *new = NULL;
167 	struct x509_constraints_name *name = NULL;
168 	size_t i;
169 
170 	if (names == NULL)
171 		return NULL;
172 
173 	if ((new = x509_constraints_names_new(names->names_max)) == NULL)
174 		goto err;
175 
176 	for (i = 0; i < names->names_count; i++) {
177 		if ((name = x509_constraints_name_dup(names->names[i])) == NULL)
178 			goto err;
179 		if (!x509_constraints_names_add(new, name))
180 			goto err;
181 	}
182 
183 	return new;
184  err:
185 	x509_constraints_names_free(new);
186 	x509_constraints_name_free(name);
187 	return NULL;
188 }
189 
190 /*
191  * Validate that the name contains only a hostname consisting of RFC
192  * 5890 compliant A-labels (see RFC 6066 section 3). This is more
193  * permissive to allow for a leading '.'  for a subdomain based
194  * constraint, as well as allowing for '_' which is commonly accepted
195  * by nonconformant DNS implementations.
196  *
197  * if "wildcards" is set it allows '*' to occur in the string at the end of a
198  * component.
199  */
200 static int
201 x509_constraints_valid_domain_internal(CBS *cbs, int wildcards)
202 {
203 	int first, component = 0;
204 	uint8_t prev, c = 0;
205 	size_t i, len;
206 	CBS copy;
207 
208 	CBS_dup(cbs, &copy);
209 
210 	len = CBS_len(cbs);
211 
212 	if (len > DOMAIN_PART_MAX_LEN)
213 		return 0;
214 	for (i = 0; i < len; i++) {
215 		prev = c;
216 		if (!CBS_get_u8(&copy, &c))
217 			return 0;
218 
219 		first = (i == 0);
220 
221 		/* Everything has to be ASCII, with no NUL byte */
222 		if (!isascii(c) || c == '\0')
223 			return 0;
224 		/* It must be alphanumeric, a '-', '.', '_' or '*' */
225 		if (!isalnum(c) && c != '-' && c != '.' && c != '_' && c != '*')
226 			return 0;
227 
228 		/* if it is a '*', fail if not wildcards */
229 		if (!wildcards && c == '*')
230 			return 0;
231 
232 		/* '-' must not start a component or be at the end. */
233 		if (c == '-' && (component == 0 || i == len - 1))
234 			return 0;
235 
236 		/*
237 		 * '.' must not be at the end. It may be first overall
238 		 * but must not otherwise start a component.
239 		 */
240 		if (c == '.' && ((component == 0 && !first) || i == len - 1))
241 			return 0;
242 
243 		if (c == '.') {
244 			/* Components can not end with a dash. */
245 			if (prev == '-')
246 				return 0;
247 			/* Start new component */
248 			component = 0;
249 			continue;
250 		}
251 		/*
252 		 * Wildcards can only occur at the end of a component.
253 		 * c*.com is valid, c*c.com is not.
254 		 */
255 		if (prev == '*')
256 			return 0;
257 
258 		/* Components must be 63 chars or less. */
259 		if (++component > 63)
260 			return 0;
261 	}
262 
263 	return 1;
264 }
265 
266 int
267 x509_constraints_valid_host(CBS *cbs)
268 {
269 	uint8_t first;
270 
271 	if (!CBS_peek_u8(cbs, &first))
272 		return 0;
273 	if (first == '.')
274 		return 0; /* leading . not allowed in a host name */
275 	if (cbs_is_ip_address(cbs))
276 		return 0;
277 
278 	return x509_constraints_valid_domain_internal(cbs, 0);
279 }
280 
281 int
282 x509_constraints_valid_sandns(CBS *cbs)
283 {
284 	uint8_t first;
285 
286 	if (!CBS_peek_u8(cbs, &first))
287 		return 0;
288 	if (first == '.')
289 		return 0; /* leading . not allowed in a SAN DNS name */
290 	/*
291 	 * A domain may not be less than two characters, so you
292 	 * can't wildcard a single domain of less than that
293 	 */
294 	if (CBS_len(cbs) < 4 && first == '*')
295 		return 0;
296 
297 	return x509_constraints_valid_domain_internal(cbs, 1);
298 }
299 
300 static inline int
301 local_part_ok(char c)
302 {
303 	return (('0' <= c && c <= '9') || ('a' <= c && c <= 'z') ||
304 	    ('A' <= c && c <= 'Z') || c == '!' || c == '#' || c == '$' ||
305 	    c == '%' || c == '&' || c == '\'' || c == '*' || c == '+' ||
306 	    c == '-' || c == '/' || c == '=' || c == '?' || c == '^' ||
307 	    c == '_' || c == '`' || c == '{' || c == '|' || c == '}' ||
308 	    c == '~' || c == '.');
309 }
310 
311 /*
312  * Parse "candidate" as an RFC 2821 mailbox.
313  * Returns 0 if candidate is not a valid mailbox or if an error occurs.
314  * Returns 1 if candidate is a mailbox and adds newly allocated
315  * local and domain parts of the mailbox to "name->local" and name->name"
316  */
317 int
318 x509_constraints_parse_mailbox(CBS *candidate,
319     struct x509_constraints_name *name)
320 {
321 	char working[DOMAIN_PART_MAX_LEN + 1] = { 0 };
322 	char *candidate_local = NULL;
323 	char *candidate_domain = NULL;
324 	CBS domain_cbs;
325 	size_t i, len, wi = 0;
326 	int accept = 0;
327 	int quoted = 0;
328 	CBS copy;
329 
330 	/* XXX This should not be necessary - revisit and remove */
331 	if (candidate == NULL)
332 		return 0;
333 
334 	CBS_dup(candidate, &copy);
335 
336 	if ((len = CBS_len(&copy)) == 0)
337 		return 0;
338 
339 	/* It can't be bigger than the local part, domain part and the '@' */
340 	if (len > LOCAL_PART_MAX_LEN + DOMAIN_PART_MAX_LEN + 1)
341 		return 0;
342 
343 	for (i = 0; i < len; i++) {
344 		char c;
345 		if (!CBS_get_u8(&copy, &c))
346 			goto bad;
347 		/* non ascii, cr, lf, or nul is never allowed */
348 		if (!isascii(c) || c == '\r' || c == '\n' || c == '\0')
349 			goto bad;
350 		if (i == 0) {
351 			/* local part is quoted part */
352 			if (c == '"')
353 				quoted = 1;
354 			/* can not start with a . */
355 			if (c == '.')
356 				goto bad;
357 		}
358 		if (accept) {
359 			if (wi >= DOMAIN_PART_MAX_LEN)
360 				goto bad;
361 			working[wi++] = c;
362 			accept = 0;
363 			continue;
364 		}
365 		if (candidate_local != NULL) {
366 			/* We are looking for the domain part */
367 			if (wi >= DOMAIN_PART_MAX_LEN)
368 				goto bad;
369 			working[wi++] = c;
370 			if (i == len - 1) {
371 				if (wi == 0)
372 					goto bad;
373 				if (candidate_domain != NULL)
374 					goto bad;
375 				candidate_domain = strdup(working);
376 				if (candidate_domain == NULL)
377 					goto bad;
378 			}
379 			continue;
380 		}
381 		/* We are looking for the local part */
382 		if (wi >= LOCAL_PART_MAX_LEN)
383 			break;
384 
385 		if (quoted) {
386 			if (c == '\\') {
387 				accept = 1;
388 				continue;
389 			}
390 			if (c == '"' && i != 0) {
391 				uint8_t next;
392 				/* end the quoted part. @ must be next */
393 				if (!CBS_peek_u8(&copy, &next))
394 					goto bad;
395 				if (next != '@')
396 					goto bad;
397 				quoted = 0;
398 			}
399 			/*
400 			 * XXX Go strangely permits sp but forbids ht
401 			 * mimic that for now
402 			 */
403 			if (c == 9)
404 				goto bad;
405 			if (wi >= LOCAL_PART_MAX_LEN)
406 				goto bad;
407 			working[wi++] = c;
408 			continue; /* all's good inside our quoted string */
409 		}
410 		if (c == '@') {
411 			if (wi == 0)
412 				goto bad;
413 			if (candidate_local != NULL)
414 				goto bad;
415 			candidate_local = strdup(working);
416 			if (candidate_local == NULL)
417 				goto bad;
418 			memset(working, 0, sizeof(working));
419 			wi = 0;
420 			continue;
421 		}
422 		if (c == '\\') {
423 			uint8_t next;
424 			/*
425 			 * RFC 2821 hints these can happen outside of
426 			 * quoted string. Don't include the \ but
427 			 * next character must be ok.
428 			 */
429 			if (!CBS_peek_u8(&copy, &next))
430 				goto bad;
431 			if (!local_part_ok(next))
432 				goto bad;
433 			accept = 1;
434 		}
435 		if (!local_part_ok(c))
436 			goto bad;
437 		if (wi >= LOCAL_PART_MAX_LEN)
438 			goto bad;
439 		working[wi++] = c;
440 	}
441 	if (candidate_local == NULL || candidate_domain == NULL)
442 		goto bad;
443 	CBS_init(&domain_cbs, candidate_domain, strlen(candidate_domain));
444 	if (!x509_constraints_valid_host(&domain_cbs))
445 		goto bad;
446 
447 	if (name != NULL) {
448 		name->local = candidate_local;
449 		name->name = candidate_domain;
450 		name->type = GEN_EMAIL;
451 	} else {
452 		free(candidate_local);
453 		free(candidate_domain);
454 	}
455 	return 1;
456  bad:
457 	free(candidate_local);
458 	free(candidate_domain);
459 	return 0;
460 }
461 
462 int
463 x509_constraints_valid_domain_constraint(CBS *cbs)
464 {
465 	uint8_t first;
466 
467 	if (CBS_len(cbs) == 0)
468 		return 1;	/* empty constraints match */
469 
470 	/*
471 	 * A domain may not be less than two characters, so you
472 	 * can't match a single domain of less than that
473 	 */
474 	if (CBS_len(cbs) < 3) {
475 		if (!CBS_peek_u8(cbs, &first))
476 			return 0;
477 		if (first == '.')
478 			return 0;
479 	}
480 	return x509_constraints_valid_domain_internal(cbs, 0);
481 }
482 
483 /*
484  * Extract the host part of a URI. On failure to parse a valid host part of the
485  * URI, 0 is returned indicating an invalid URI. If the host part parses as
486  * valid, or is not present, 1 is returned indicating a possibly valid URI.
487  *
488  * In the case of a valid URI, *hostpart will be set to a copy of the host part
489  * of the URI, or the empty string if no URI is present. If memory allocation
490  * fails *hostpart will be set to NULL, even though we returned 1. It is the
491  * caller's responsibility to indicate an error for memory allocation failure,
492  * and the callers responsibility to free *hostpart.
493  *
494  * RFC 3986:
495  * the authority part of a uri starts with // and is terminated with
496  * the next '/', '?', '#' or end of the URI.
497  *
498  * The authority itself contains [userinfo '@'] host [: port]
499  *
500  * so the host starts at the start or after the '@', and ends
501  * with end of URI, '/', '?', "#', or ':'.
502  */
503 int
504 x509_constraints_uri_host(uint8_t *uri, size_t len, char **hostpart)
505 {
506 	size_t i, hostlen = 0;
507 	uint8_t *authority = NULL;
508 	char *host = NULL;
509 	CBS host_cbs;
510 
511 	/*
512 	 * Find first '//'. there must be at least a '//' and
513 	 * something else.
514 	 */
515 	if (len < 3)
516 		return 0;
517 	for (i = 0; i < len - 1; i++) {
518 		if (!isascii(uri[i]))
519 			return 0;
520 		if (uri[i] == '/' && uri[i + 1] == '/') {
521 			authority = uri + i + 2;
522 			break;
523 		}
524 	}
525 	if (authority == NULL) {
526 		/*
527 		 * There is no authority, so no host part in this
528 		 * URI. This might be ok or might not, but it must
529 		 * fail if we run into a name constraint later, so
530 		 * we indicate that we have a URI with an empty
531 		 * host part, and succeed.
532 		 */
533 		if (hostpart != NULL)
534 			*hostpart = strdup("");
535 		return 1;
536 	}
537 	for (i = authority - uri; i < len; i++) {
538 		if (!isascii(uri[i]))
539 			return 0;
540 		/* it has a userinfo part */
541 		if (uri[i] == '@') {
542 			hostlen = 0;
543 			/* it can only have one */
544 			if (host != NULL)
545 				break;
546 			/* start after the userinfo part */
547 			host = uri + i + 1;
548 			continue;
549 		}
550 		/* did we find the end? */
551 		if (uri[i] == ':' || uri[i] == '/' || uri[i] == '?' ||
552 		    uri[i] == '#')
553 			break;
554 		hostlen++;
555 	}
556 	if (hostlen == 0)
557 		return 0;
558 	if (host == NULL)
559 		host = authority;
560 	CBS_init(&host_cbs, host, hostlen);
561 	if (!x509_constraints_valid_host(&host_cbs))
562 		return 0;
563 	if (hostpart != NULL && !CBS_strdup(&host_cbs, hostpart))
564 		return 0;
565 	return 1;
566 }
567 
568 int
569 x509_constraints_sandns(char *sandns, size_t dlen, char *constraint, size_t len)
570 {
571 	char *suffix;
572 
573 	if (len == 0)
574 		return 1; /* an empty constraint matches everything */
575 
576 	/* match the end of the domain */
577 	if (dlen < len)
578 		return 0;
579 	suffix = sandns + (dlen - len);
580 	return (strncasecmp(suffix, constraint, len) == 0);
581 }
582 
583 /*
584  * Validate a pre-validated domain of length dlen against a pre-validated
585  * constraint of length len.
586  *
587  * returns 1 if the domain and constraint match.
588  * returns 0 otherwise.
589  *
590  * an empty constraint matches everything.
591  * constraint will be matched against the domain as a suffix if it
592  * starts with a '.'.
593  * domain will be matched against the constraint as a suffix if it
594  * starts with a '.'.
595  */
596 int
597 x509_constraints_domain(char *domain, size_t dlen, char *constraint, size_t len)
598 {
599 	if (len == 0)
600 		return 1; /* an empty constraint matches everything */
601 
602 	if (constraint[0] == '.') {
603 		/* match the end of the domain */
604 		char *suffix;
605 		if (dlen < len)
606 			return 0;
607 		suffix = domain + (dlen - len);
608 		return (strncasecmp(suffix, constraint, len) == 0);
609 	}
610 	if (domain[0] == '.') {
611 		/* match the end of the constraint */
612 		char *suffix;
613 		if (len < dlen)
614 			return 0;
615 		suffix = constraint + (len - dlen);
616 		return (strncasecmp(suffix, domain, dlen) == 0);
617 	}
618 	/* otherwise we must exactly match the constraint */
619 	if (dlen != len)
620 		return 0;
621 	return (strncasecmp(domain, constraint, len) == 0);
622 }
623 
624 int
625 x509_constraints_uri(uint8_t *uri, size_t ulen, uint8_t *constraint,
626     size_t len,
627     int *error)
628 {
629 	int ret = 0;
630 	char *hostpart = NULL;
631 	CBS cbs;
632 
633 	CBS_init(&cbs, constraint, len);
634 	if (!x509_constraints_uri_host(uri, ulen, &hostpart)) {
635 		*error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
636 		goto err;
637 	}
638 	if (hostpart == NULL) {
639 		*error = X509_V_ERR_OUT_OF_MEM;
640 		goto err;
641 	}
642 	if (!x509_constraints_valid_domain_constraint(&cbs)) {
643 		*error = X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX;
644 		goto err;
645 	}
646 	ret = x509_constraints_domain(hostpart, strlen(hostpart), constraint,
647 	    len);
648  err:
649 	free(hostpart);
650 	return ret;
651 }
652 
653 /*
654  * Verify a validated address of size alen with a validated constraint
655  * of size constraint_len. returns 1 if matching, 0 if not.
656  * Addresses are assumed to be pre-validated for a length of 4 and 8
657  * respectively for ipv4 addresses and constraints, and a length of
658  * 16 and 32 respectively for ipv6 address constraints by the caller.
659  */
660 int
661 x509_constraints_ipaddr(uint8_t *address, size_t alen, uint8_t *constraint,
662     size_t len)
663 {
664 	uint8_t *mask;
665 	size_t i;
666 
667 	if (alen * 2 != len)
668 		return 0;
669 
670 	mask = constraint + alen;
671 	for (i = 0; i < alen; i++) {
672 		if ((address[i] & mask[i]) != (constraint[i] & mask[i]))
673 			return 0;
674 	}
675 	return 1;
676 }
677 
678 /*
679  * Verify a canonicalized der encoded constraint dirname
680  * a canonicalized der encoded constraint.
681  */
682 int
683 x509_constraints_dirname(uint8_t *dirname, size_t dlen,
684     uint8_t *constraint, size_t len)
685 {
686 	/*
687 	 * The constraint must be a prefix in DER format, so it can't be
688 	 * longer than the name it is checked against.
689 	 */
690 	if (len > dlen)
691 		return 0;
692 	return (memcmp(constraint, dirname, len) == 0);
693 }
694 
695 /*
696  * De-obfuscate a GENERAL_NAME into useful bytes for a name or constraint.
697  */
698 int
699 x509_constraints_general_to_bytes(GENERAL_NAME *name, uint8_t **bytes,
700     size_t *len)
701 {
702 	*bytes = NULL;
703 	*len = 0;
704 
705 	if (name->type == GEN_DNS) {
706 		ASN1_IA5STRING *aname = name->d.dNSName;
707 
708 		*bytes = aname->data;
709 		*len = aname->length;
710 
711 		return name->type;
712 	}
713 	if (name->type == GEN_EMAIL) {
714 		ASN1_IA5STRING *aname = name->d.rfc822Name;
715 
716 		*bytes = aname->data;
717 		*len = aname->length;
718 
719 		return name->type;
720 	}
721 	if (name->type == GEN_URI) {
722 		ASN1_IA5STRING *aname = name->d.uniformResourceIdentifier;
723 
724 		*bytes = aname->data;
725 		*len = aname->length;
726 
727 		return name->type;
728 	}
729 	if (name->type == GEN_DIRNAME) {
730 		X509_NAME *dname = name->d.directoryName;
731 
732 		if (!dname->modified || i2d_X509_NAME(dname, NULL) >= 0) {
733 			*bytes = dname->canon_enc;
734 			*len = dname->canon_enclen;
735 
736 			return name->type;
737 		}
738 	}
739 	if (name->type == GEN_IPADD) {
740 		*bytes = name->d.ip->data;
741 		*len = name->d.ip->length;
742 
743 		return name->type;
744 	}
745 
746 	return 0;
747 }
748 
749 /*
750  * Extract the relevant names for constraint checking from "cert",
751  * validate them, and add them to the list of cert names for "chain".
752  * returns 1 on success sets error and returns 0 on failure.
753  */
754 int
755 x509_constraints_extract_names(struct x509_constraints_names *names,
756     X509 *cert, int is_leaf, int *error)
757 {
758 	struct x509_constraints_name *vname = NULL;
759 	X509_NAME *subject_name;
760 	GENERAL_NAME *name;
761 	ssize_t i = 0;
762 	int name_type, include_cn = is_leaf, include_email = is_leaf;
763 
764 	/* first grab the altnames */
765 	while ((name = sk_GENERAL_NAME_value(cert->altname, i++)) != NULL) {
766 		uint8_t *bytes = NULL;
767 		size_t len = 0;
768 		CBS cbs;
769 
770 		if ((vname = x509_constraints_name_new()) == NULL) {
771 			*error = X509_V_ERR_OUT_OF_MEM;
772 			goto err;
773 		}
774 
775 		name_type = x509_constraints_general_to_bytes(name, &bytes,
776 		    &len);
777 		CBS_init(&cbs, bytes, len);
778 		switch (name_type) {
779 		case GEN_DNS:
780 			if (!x509_constraints_valid_sandns(&cbs)) {
781 				*error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
782 				goto err;
783 			}
784 			if (!CBS_strdup(&cbs, &vname->name)) {
785 				*error = X509_V_ERR_OUT_OF_MEM;
786 				goto err;
787 			}
788 			vname->type = GEN_DNS;
789 			include_cn = 0; /* Don't use cn from subject */
790 			break;
791 		case GEN_EMAIL:
792 			if (!x509_constraints_parse_mailbox(&cbs, vname)) {
793 				*error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
794 				goto err;
795 			}
796 			vname->type = GEN_EMAIL;
797 			include_email = 0; /* Don't use email from subject */
798 			break;
799 		case GEN_URI:
800 			if (!x509_constraints_uri_host(bytes, len,
801 			    &vname->name)) {
802 				*error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
803 				goto err;
804 			}
805 			if (vname->name == NULL) {
806 				*error = X509_V_ERR_OUT_OF_MEM;
807 				goto err;
808 			}
809 			vname->type = GEN_URI;
810 			break;
811 		case GEN_DIRNAME:
812 			if (len == 0) {
813 				*error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
814 				goto err;
815 			}
816 			if (bytes == NULL || ((vname->der = malloc(len)) ==
817 			    NULL)) {
818 				*error = X509_V_ERR_OUT_OF_MEM;
819 				goto err;
820 			}
821 			memcpy(vname->der, bytes, len);
822 			vname->der_len = len;
823 			vname->type = GEN_DIRNAME;
824 			break;
825 		case GEN_IPADD:
826 			if (len == 4)
827 				vname->af = AF_INET;
828 			if (len == 16)
829 				vname->af = AF_INET6;
830 			if (vname->af != AF_INET && vname->af != AF_INET6) {
831 				*error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
832 				goto err;
833 			}
834 			memcpy(vname->address, bytes, len);
835 			vname->type = GEN_IPADD;
836 			break;
837 		default:
838 			/* Ignore this name */
839 			x509_constraints_name_free(vname);
840 			vname = NULL;
841 			continue;
842 		}
843 		if (!x509_constraints_names_add(names, vname)) {
844 			*error = X509_V_ERR_OUT_OF_MEM;
845 			goto err;
846 		}
847 		vname = NULL;
848 	}
849 
850 	x509_constraints_name_free(vname);
851 	vname = NULL;
852 
853 	subject_name = X509_get_subject_name(cert);
854 	if (X509_NAME_entry_count(subject_name) > 0) {
855 		X509_NAME_ENTRY *email;
856 		X509_NAME_ENTRY *cn;
857 		/*
858 		 * This cert has a non-empty subject, so we must add
859 		 * the subject as a dirname to be compared against
860 		 * any dirname constraints
861 		 */
862 		if ((subject_name->modified &&
863 		    i2d_X509_NAME(subject_name, NULL) < 0) ||
864 		    (vname = x509_constraints_name_new()) == NULL ||
865 		    (vname->der = malloc(subject_name->canon_enclen)) == NULL) {
866 			*error = X509_V_ERR_OUT_OF_MEM;
867 			goto err;
868 		}
869 
870 		memcpy(vname->der, subject_name->canon_enc,
871 		    subject_name->canon_enclen);
872 		vname->der_len = subject_name->canon_enclen;
873 		vname->type = GEN_DIRNAME;
874 		if (!x509_constraints_names_add(names, vname)) {
875 			*error = X509_V_ERR_OUT_OF_MEM;
876 			goto err;
877 		}
878 		vname = NULL;
879 		/*
880 		 * Get any email addresses from the subject line, and
881 		 * add them as mbox names to be compared against any
882 		 * email constraints
883 		 */
884 		while (include_email &&
885 		    (i = X509_NAME_get_index_by_NID(subject_name,
886 		     NID_pkcs9_emailAddress, i)) >= 0) {
887 			ASN1_STRING *aname;
888 			CBS cbs;
889 			if ((email = X509_NAME_get_entry(subject_name, i)) ==
890 			    NULL ||
891 			    (aname = X509_NAME_ENTRY_get_data(email)) == NULL) {
892 				*error = X509_V_ERR_OUT_OF_MEM;
893 				goto err;
894 			}
895 			CBS_init(&cbs, aname->data, aname->length);
896 			if ((vname = x509_constraints_name_new()) == NULL) {
897 				*error = X509_V_ERR_OUT_OF_MEM;
898 				goto err;
899 			}
900 			if (!x509_constraints_parse_mailbox(&cbs, vname)) {
901 				*error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
902 				goto err;
903 			}
904 			vname->type = GEN_EMAIL;
905 			if (!x509_constraints_names_add(names, vname)) {
906 				*error = X509_V_ERR_OUT_OF_MEM;
907 				goto err;
908 			}
909 			vname = NULL;
910 		}
911 		/*
912 		 * Include the CN as a hostname to be checked against
913 		 * name constraints if it looks like a hostname.
914 		 */
915 		while (include_cn &&
916 		    (i = X509_NAME_get_index_by_NID(subject_name,
917 		     NID_commonName, i)) >= 0) {
918 			CBS cbs;
919 			ASN1_STRING *aname;
920 			if ((cn = X509_NAME_get_entry(subject_name, i)) ==
921 			    NULL ||
922 			    (aname = X509_NAME_ENTRY_get_data(cn)) == NULL) {
923 				*error = X509_V_ERR_OUT_OF_MEM;
924 				goto err;
925 			}
926 			CBS_init(&cbs, aname->data, aname->length);
927 			if (!x509_constraints_valid_host(&cbs))
928 				continue; /* ignore it if not a hostname */
929 			if ((vname = x509_constraints_name_new()) == NULL) {
930 				*error = X509_V_ERR_OUT_OF_MEM;
931 				goto err;
932 			}
933 			if (!CBS_strdup(&cbs, &vname->name)) {
934 				*error = X509_V_ERR_OUT_OF_MEM;
935 				goto err;
936 			}
937 			vname->type = GEN_DNS;
938 			if (!x509_constraints_names_add(names, vname)) {
939 				*error = X509_V_ERR_OUT_OF_MEM;
940 				goto err;
941 			}
942 			vname = NULL;
943 		}
944 	}
945 	return 1;
946  err:
947 	x509_constraints_name_free(vname);
948 	return 0;
949 }
950 
951 /*
952  * Validate a constraint in a general name, putting the relevant data
953  * into "name" if valid. returns 0, and sets error if the constraint is
954  * not valid. returns 1 if the constraint validated. name->type will be
955  * set to a valid type if there is constraint data in name, or unmodified
956  * if the GENERAL_NAME had a valid type but was ignored.
957  */
958 int
959 x509_constraints_validate(GENERAL_NAME *constraint,
960     struct x509_constraints_name **out_name, int *out_error)
961 {
962 	uint8_t next, *bytes = NULL;
963 	size_t len = 0;
964 	struct x509_constraints_name *name;
965 	int error = X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX;
966 	int name_type;
967 	CBS cbs;
968 
969 	if (out_name == NULL || *out_name != NULL)
970 		return 0;
971 
972 	if (out_error != NULL)
973 		*out_error = 0;
974 
975 	if ((name = x509_constraints_name_new()) == NULL) {
976 		error = X509_V_ERR_OUT_OF_MEM;
977 		goto err;
978 	}
979 
980 	name_type = x509_constraints_general_to_bytes(constraint, &bytes, &len);
981 	CBS_init(&cbs, bytes, len);
982 	switch (name_type) {
983 	case GEN_DIRNAME:
984 		if (len == 0)
985 			goto err; /* XXX The RFCs are delightfully vague */
986 		if (bytes == NULL || (name->der = malloc(len)) == NULL) {
987 			error = X509_V_ERR_OUT_OF_MEM;
988 			goto err;
989 		}
990 		memcpy(name->der, bytes, len);
991 		name->der_len = len;
992 		name->type = GEN_DIRNAME;
993 		break;
994 	case GEN_DNS:
995 		if (!x509_constraints_valid_domain_constraint(&cbs))
996 			goto err;
997 		if ((name->name = strndup(bytes, len)) == NULL) {
998 			error = X509_V_ERR_OUT_OF_MEM;
999 			goto err;
1000 		}
1001 		name->type = GEN_DNS;
1002 		break;
1003 	case GEN_EMAIL:
1004 		if (len > 0 && memchr(bytes + 1, '@', len - 1) != NULL) {
1005 			if (!x509_constraints_parse_mailbox(&cbs, name))
1006 				goto err;
1007 			break;
1008 		}
1009 		/*
1010 		 * Mail constraints of the form @domain.com are accepted by
1011 		 * OpenSSL and Microsoft.
1012 		 */
1013 		if (CBS_len(&cbs) > 0) {
1014 			if (!CBS_peek_u8(&cbs, &next))
1015 				goto err;
1016 			if (next == '@') {
1017 				if (!CBS_skip(&cbs, 1))
1018 					goto err;
1019 			}
1020 		}
1021 		if (!x509_constraints_valid_domain_constraint(&cbs))
1022 			goto err;
1023 		if (!CBS_strdup(&cbs, &name->name)) {
1024 			error = X509_V_ERR_OUT_OF_MEM;
1025 			goto err;
1026 		}
1027 		name->type = GEN_EMAIL;
1028 		break;
1029 	case GEN_IPADD:
1030 		/* Constraints are ip then mask */
1031 		if (len == 8)
1032 			name->af = AF_INET;
1033 		else if (len == 32)
1034 			name->af = AF_INET6;
1035 		else
1036 			goto err;
1037 		memcpy(&name->address[0], bytes, len);
1038 		name->type = GEN_IPADD;
1039 		break;
1040 	case GEN_URI:
1041 		if (!x509_constraints_valid_domain_constraint(&cbs))
1042 			goto err;
1043 		if ((name->name = strndup(bytes, len)) == NULL) {
1044 			error = X509_V_ERR_OUT_OF_MEM;
1045 			goto err;
1046 		}
1047 		name->type = GEN_URI;
1048 		break;
1049 	default:
1050 		break;
1051 	}
1052 
1053 	*out_name = name;
1054 
1055 	return 1;
1056 
1057  err:
1058 	x509_constraints_name_free(name);
1059 	if (out_error != NULL)
1060 		*out_error = error;
1061 
1062 	return 0;
1063 }
1064 
1065 int
1066 x509_constraints_extract_constraints(X509 *cert,
1067     struct x509_constraints_names *permitted,
1068     struct x509_constraints_names *excluded,
1069     int *error)
1070 {
1071 	struct x509_constraints_name *vname = NULL;
1072 	NAME_CONSTRAINTS *nc = cert->nc;
1073 	GENERAL_SUBTREE *subtree;
1074 	int i;
1075 
1076 	if (nc == NULL)
1077 		return 1;
1078 
1079 	for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->permittedSubtrees); i++) {
1080 		subtree = sk_GENERAL_SUBTREE_value(nc->permittedSubtrees, i);
1081 		if (subtree->minimum || subtree->maximum) {
1082 			*error = X509_V_ERR_SUBTREE_MINMAX;
1083 			return 0;
1084 		}
1085 		if (!x509_constraints_validate(subtree->base, &vname, error))
1086 			return 0;
1087 		if (vname->type == 0) {
1088 			x509_constraints_name_free(vname);
1089 			vname = NULL;
1090 			continue;
1091 		}
1092 		if (!x509_constraints_names_add(permitted, vname)) {
1093 			x509_constraints_name_free(vname);
1094 			vname = NULL;
1095 			*error = X509_V_ERR_OUT_OF_MEM;
1096 			return 0;
1097 		}
1098 		vname = NULL;
1099 	}
1100 
1101 	for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->excludedSubtrees); i++) {
1102 		subtree = sk_GENERAL_SUBTREE_value(nc->excludedSubtrees, i);
1103 		if (subtree->minimum || subtree->maximum) {
1104 			*error = X509_V_ERR_SUBTREE_MINMAX;
1105 			return 0;
1106 		}
1107 		if (!x509_constraints_validate(subtree->base, &vname, error))
1108 			return 0;
1109 		if (vname->type == 0) {
1110 			x509_constraints_name_free(vname);
1111 			vname = NULL;
1112 			continue;
1113 		}
1114 		if (!x509_constraints_names_add(excluded, vname)) {
1115 			x509_constraints_name_free(vname);
1116 			vname = NULL;
1117 			*error = X509_V_ERR_OUT_OF_MEM;
1118 			return 0;
1119 		}
1120 		vname = NULL;
1121 	}
1122 
1123 	return 1;
1124 }
1125 
1126 /*
1127  * Match a validated name in "name" against a validated constraint in
1128  * "constraint" return 1 if then name matches, 0 otherwise.
1129  */
1130 int
1131 x509_constraints_match(struct x509_constraints_name *name,
1132     struct x509_constraints_name *constraint)
1133 {
1134 	if (name->type != constraint->type)
1135 		return 0;
1136 	if (name->type == GEN_DNS)
1137 		return x509_constraints_sandns(name->name, strlen(name->name),
1138 		    constraint->name, strlen(constraint->name));
1139 	if (name->type == GEN_URI)
1140 		return x509_constraints_domain(name->name, strlen(name->name),
1141 		    constraint->name, strlen(constraint->name));
1142 	if (name->type == GEN_IPADD) {
1143 		size_t nlen = name->af == AF_INET ? 4 : 16;
1144 		size_t clen = name->af == AF_INET ? 8 : 32;
1145 		if (name->af != AF_INET && name->af != AF_INET6)
1146 			return 0;
1147 		if (constraint->af != AF_INET && constraint->af != AF_INET6)
1148 			return 0;
1149 		if (name->af != constraint->af)
1150 			return 0;
1151 		return x509_constraints_ipaddr(name->address, nlen,
1152 		    constraint->address, clen);
1153 	}
1154 	if (name->type == GEN_EMAIL) {
1155 		if (constraint->local) {
1156 			/* mailbox local and domain parts must exactly match */
1157 			return (strcmp(name->local, constraint->local) == 0 &&
1158 			    strcmp(name->name, constraint->name) == 0);
1159 		}
1160 		/* otherwise match the constraint to the domain part */
1161 		return x509_constraints_domain(name->name, strlen(name->name),
1162 		    constraint->name, strlen(constraint->name));
1163 	}
1164 	if (name->type == GEN_DIRNAME)
1165 		return x509_constraints_dirname(name->der, name->der_len,
1166 		    constraint->der, constraint->der_len);
1167 	return 0;
1168 }
1169 
1170 /*
1171  * Make sure every name in names does not match any excluded
1172  * constraints, and does match at least one permitted constraint if
1173  * any are present. Returns 1 if ok, 0, and sets error if not.
1174  */
1175 int
1176 x509_constraints_check(struct x509_constraints_names *names,
1177     struct x509_constraints_names *permitted,
1178     struct x509_constraints_names *excluded, int *error)
1179 {
1180 	size_t i, j;
1181 
1182 	for (i = 0; i < names->names_count; i++) {
1183 		int permitted_seen = 0;
1184 		int permitted_matched = 0;
1185 
1186 		for (j = 0; j < excluded->names_count; j++) {
1187 			if (x509_constraints_match(names->names[i],
1188 			    excluded->names[j])) {
1189 				*error = X509_V_ERR_EXCLUDED_VIOLATION;
1190 				return 0;
1191 			}
1192 		}
1193 		for (j = 0; j < permitted->names_count; j++) {
1194 			if (permitted->names[j]->type == names->names[i]->type)
1195 				permitted_seen++;
1196 			if (x509_constraints_match(names->names[i],
1197 			    permitted->names[j])) {
1198 				permitted_matched++;
1199 				break;
1200 			}
1201 		}
1202 		if (permitted_seen && !permitted_matched) {
1203 			*error = X509_V_ERR_PERMITTED_VIOLATION;
1204 			return 0;
1205 		}
1206 	}
1207 	return 1;
1208 }
1209 
1210 /*
1211  * Walk a validated chain of X509 certs, starting at the leaf, and
1212  * validate the name constraints in the chain. Intended for use with
1213  * the legacy X509 validation code in x509_vfy.c
1214  *
1215  * returns 1 if the constraints are ok, 0 otherwise, setting error and
1216  * depth
1217  */
1218 int
1219 x509_constraints_chain(STACK_OF(X509) *chain, int *error, int *depth)
1220 {
1221 	int chain_length, verify_err = X509_V_ERR_UNSPECIFIED, i = 0;
1222 	struct x509_constraints_names *names = NULL;
1223 	struct x509_constraints_names *excluded = NULL;
1224 	struct x509_constraints_names *permitted = NULL;
1225 	size_t constraints_count = 0;
1226 	X509 *cert;
1227 
1228 	if (chain == NULL || (chain_length = sk_X509_num(chain)) == 0)
1229 		goto err;
1230 	if (chain_length == 1)
1231 		return 1;
1232 	if ((names = x509_constraints_names_new(
1233 	    X509_VERIFY_MAX_CHAIN_NAMES)) == NULL) {
1234 		verify_err = X509_V_ERR_OUT_OF_MEM;
1235 		goto err;
1236 	}
1237 
1238 	if ((cert = sk_X509_value(chain, 0)) == NULL)
1239 		goto err;
1240 	if (!x509_constraints_extract_names(names, cert, 1, &verify_err))
1241 		goto err;
1242 	for (i = 1; i < chain_length; i++) {
1243 		if ((cert = sk_X509_value(chain, i)) == NULL)
1244 			goto err;
1245 		if (cert->nc != NULL) {
1246 			if ((permitted = x509_constraints_names_new(
1247 			    X509_VERIFY_MAX_CHAIN_CONSTRAINTS)) == NULL) {
1248 				verify_err = X509_V_ERR_OUT_OF_MEM;
1249 				goto err;
1250 			}
1251 			if ((excluded = x509_constraints_names_new(
1252 			    X509_VERIFY_MAX_CHAIN_CONSTRAINTS)) == NULL) {
1253 				verify_err = X509_V_ERR_OUT_OF_MEM;
1254 				goto err;
1255 			}
1256 			if (!x509_constraints_extract_constraints(cert,
1257 			    permitted, excluded, &verify_err))
1258 				goto err;
1259 			constraints_count += permitted->names_count;
1260 			constraints_count += excluded->names_count;
1261 			if (constraints_count >
1262 			    X509_VERIFY_MAX_CHAIN_CONSTRAINTS) {
1263 				verify_err = X509_V_ERR_OUT_OF_MEM;
1264 				goto err;
1265 			}
1266 			if (!x509_constraints_check(names, permitted, excluded,
1267 			    &verify_err))
1268 				goto err;
1269 			x509_constraints_names_free(excluded);
1270 			excluded = NULL;
1271 			x509_constraints_names_free(permitted);
1272 			permitted = NULL;
1273 		}
1274 		if (!x509_constraints_extract_names(names, cert, 0,
1275 		    &verify_err))
1276 			goto err;
1277 	}
1278 
1279 	x509_constraints_names_free(names);
1280 	return 1;
1281 
1282  err:
1283 	*error = verify_err;
1284 	*depth = i;
1285 	x509_constraints_names_free(excluded);
1286 	x509_constraints_names_free(permitted);
1287 	x509_constraints_names_free(names);
1288 	return 0;
1289 }
1290