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