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