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