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