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 * 39 x509_constraints_name_new(void) 40 { 41 return (calloc(1, sizeof(struct x509_constraints_name))); 42 } 43 44 void 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 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 * 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 * 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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