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
40static int
41cbs_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
60struct x509_constraints_name *
61x509_constraints_name_new(void)
62{
63	return (calloc(1, sizeof(struct x509_constraints_name)));
64}
65
66void
67x509_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
75void
76x509_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
84struct x509_constraints_name *
85x509_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
110struct x509_constraints_names *
111x509_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
123void
124x509_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
134void
135x509_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
144int
145x509_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
163struct x509_constraints_names *
164x509_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 */
200static int
201x509_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
266int
267x509_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
286int
287x509_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
305static inline int
306local_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 */
322int
323x509_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
467int
468x509_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 */
508int
509x509_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
573int
574x509_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 */
601int
602x509_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
629int
630x509_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 */
665int
666x509_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 */
687int
688x509_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 */
703int
704x509_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 */
759int
760x509_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 */
963int
964x509_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
1070int
1071x509_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 */
1135int
1136x509_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 */
1180int
1181x509_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 */
1223int
1224x509_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