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