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