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