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