1 /* $OpenBSD: x509.c,v 1.88 2024/05/29 13:26:24 tb Exp $ */ 2 /* 3 * Copyright (c) 2022 Theo Buehler <tb@openbsd.org> 4 * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org> 5 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <err.h> 21 #include <stdlib.h> 22 #include <string.h> 23 #include <unistd.h> 24 25 #include <openssl/evp.h> 26 #include <openssl/x509v3.h> 27 28 #include "extern.h" 29 30 ASN1_OBJECT *certpol_oid; /* id-cp-ipAddr-asNumber cert policy */ 31 ASN1_OBJECT *carepo_oid; /* 1.3.6.1.5.5.7.48.5 (caRepository) */ 32 ASN1_OBJECT *manifest_oid; /* 1.3.6.1.5.5.7.48.10 (rpkiManifest) */ 33 ASN1_OBJECT *signedobj_oid; /* 1.3.6.1.5.5.7.48.11 (signedObject) */ 34 ASN1_OBJECT *notify_oid; /* 1.3.6.1.5.5.7.48.13 (rpkiNotify) */ 35 ASN1_OBJECT *roa_oid; /* id-ct-routeOriginAuthz CMS content type */ 36 ASN1_OBJECT *mft_oid; /* id-ct-rpkiManifest CMS content type */ 37 ASN1_OBJECT *gbr_oid; /* id-ct-rpkiGhostbusters CMS content type */ 38 ASN1_OBJECT *bgpsec_oid; /* id-kp-bgpsec-router Key Purpose */ 39 ASN1_OBJECT *cnt_type_oid; /* pkcs-9 id-contentType */ 40 ASN1_OBJECT *msg_dgst_oid; /* pkcs-9 id-messageDigest */ 41 ASN1_OBJECT *sign_time_oid; /* pkcs-9 id-signingTime */ 42 ASN1_OBJECT *rsc_oid; /* id-ct-signedChecklist */ 43 ASN1_OBJECT *aspa_oid; /* id-ct-ASPA */ 44 ASN1_OBJECT *tak_oid; /* id-ct-SignedTAL */ 45 ASN1_OBJECT *geofeed_oid; /* id-ct-geofeedCSVwithCRLF */ 46 ASN1_OBJECT *spl_oid; /* id-ct-signedPrefixList */ 47 48 static const struct { 49 const char *oid; 50 ASN1_OBJECT **ptr; 51 } oid_table[] = { 52 { 53 .oid = "1.3.6.1.5.5.7.14.2", 54 .ptr = &certpol_oid, 55 }, 56 { 57 .oid = "1.3.6.1.5.5.7.48.5", 58 .ptr = &carepo_oid, 59 }, 60 { 61 .oid = "1.3.6.1.5.5.7.48.10", 62 .ptr = &manifest_oid, 63 }, 64 { 65 .oid = "1.3.6.1.5.5.7.48.11", 66 .ptr = &signedobj_oid, 67 }, 68 { 69 .oid = "1.3.6.1.5.5.7.48.13", 70 .ptr = ¬ify_oid, 71 }, 72 { 73 .oid = "1.2.840.113549.1.9.16.1.24", 74 .ptr = &roa_oid, 75 }, 76 { 77 .oid = "1.2.840.113549.1.9.16.1.26", 78 .ptr = &mft_oid, 79 }, 80 { 81 .oid = "1.2.840.113549.1.9.16.1.35", 82 .ptr = &gbr_oid, 83 }, 84 { 85 .oid = "1.3.6.1.5.5.7.3.30", 86 .ptr = &bgpsec_oid, 87 }, 88 { 89 .oid = "1.2.840.113549.1.9.3", 90 .ptr = &cnt_type_oid, 91 }, 92 { 93 .oid = "1.2.840.113549.1.9.4", 94 .ptr = &msg_dgst_oid, 95 }, 96 { 97 .oid = "1.2.840.113549.1.9.5", 98 .ptr = &sign_time_oid, 99 }, 100 { 101 .oid = "1.2.840.113549.1.9.16.1.47", 102 .ptr = &geofeed_oid, 103 }, 104 { 105 .oid = "1.2.840.113549.1.9.16.1.48", 106 .ptr = &rsc_oid, 107 }, 108 { 109 .oid = "1.2.840.113549.1.9.16.1.49", 110 .ptr = &aspa_oid, 111 }, 112 { 113 .oid = "1.2.840.113549.1.9.16.1.50", 114 .ptr = &tak_oid, 115 }, 116 { 117 .oid = "1.2.840.113549.1.9.16.1.51", 118 .ptr = &spl_oid, 119 }, 120 }; 121 122 void 123 x509_init_oid(void) 124 { 125 size_t i; 126 127 for (i = 0; i < sizeof(oid_table) / sizeof(oid_table[0]); i++) { 128 *oid_table[i].ptr = OBJ_txt2obj(oid_table[i].oid, 1); 129 if (*oid_table[i].ptr == NULL) 130 errx(1, "OBJ_txt2obj for %s failed", oid_table[i].oid); 131 } 132 } 133 134 /* 135 * Parse X509v3 authority key identifier (AKI), RFC 6487 sec. 4.8.3. 136 * Returns the AKI or NULL if it could not be parsed. 137 * The AKI is formatted as a hex string. 138 */ 139 int 140 x509_get_aki(X509 *x, const char *fn, char **aki) 141 { 142 const unsigned char *d; 143 AUTHORITY_KEYID *akid; 144 ASN1_OCTET_STRING *os; 145 int dsz, crit, rc = 0; 146 147 *aki = NULL; 148 akid = X509_get_ext_d2i(x, NID_authority_key_identifier, &crit, NULL); 149 if (akid == NULL) { 150 if (crit != -1) { 151 warnx("%s: RFC 6487 section 4.8.3: error parsing AKI", 152 fn); 153 return 0; 154 } 155 return 1; 156 } 157 if (crit != 0) { 158 warnx("%s: RFC 6487 section 4.8.3: " 159 "AKI: extension not non-critical", fn); 160 goto out; 161 } 162 if (akid->issuer != NULL || akid->serial != NULL) { 163 warnx("%s: RFC 6487 section 4.8.3: AKI: " 164 "authorityCertIssuer or authorityCertSerialNumber present", 165 fn); 166 goto out; 167 } 168 169 os = akid->keyid; 170 if (os == NULL) { 171 warnx("%s: RFC 6487 section 4.8.3: AKI: " 172 "Key Identifier missing", fn); 173 goto out; 174 } 175 176 d = os->data; 177 dsz = os->length; 178 179 if (dsz != SHA_DIGEST_LENGTH) { 180 warnx("%s: RFC 6487 section 4.8.2: AKI: " 181 "want %d bytes SHA1 hash, have %d bytes", 182 fn, SHA_DIGEST_LENGTH, dsz); 183 goto out; 184 } 185 186 *aki = hex_encode(d, dsz); 187 rc = 1; 188 out: 189 AUTHORITY_KEYID_free(akid); 190 return rc; 191 } 192 193 /* 194 * Validate the X509v3 subject key identifier (SKI), RFC 6487 section 4.8.2: 195 * "The SKI is a SHA-1 hash of the value of the DER-encoded ASN.1 BIT STRING of 196 * the Subject Public Key, as described in Section 4.2.1.2 of RFC 5280." 197 * Returns the SKI formatted as hex string, or NULL if it couldn't be parsed. 198 */ 199 int 200 x509_get_ski(X509 *x, const char *fn, char **ski) 201 { 202 ASN1_OCTET_STRING *os; 203 unsigned char md[EVP_MAX_MD_SIZE]; 204 unsigned int md_len = EVP_MAX_MD_SIZE; 205 int crit, rc = 0; 206 207 *ski = NULL; 208 os = X509_get_ext_d2i(x, NID_subject_key_identifier, &crit, NULL); 209 if (os == NULL) { 210 if (crit != -1) { 211 warnx("%s: RFC 6487 section 4.8.2: error parsing SKI", 212 fn); 213 return 0; 214 } 215 return 1; 216 } 217 if (crit != 0) { 218 warnx("%s: RFC 6487 section 4.8.2: " 219 "SKI: extension not non-critical", fn); 220 goto out; 221 } 222 223 if (!X509_pubkey_digest(x, EVP_sha1(), md, &md_len)) { 224 warnx("%s: X509_pubkey_digest", fn); 225 goto out; 226 } 227 228 if (os->length < 0 || md_len != (size_t)os->length) { 229 warnx("%s: RFC 6487 section 4.8.2: SKI: " 230 "want %u bytes SHA1 hash, have %d bytes", 231 fn, md_len, os->length); 232 goto out; 233 } 234 235 if (memcmp(os->data, md, md_len) != 0) { 236 warnx("%s: SKI does not match SHA1 hash of SPK", fn); 237 goto out; 238 } 239 240 *ski = hex_encode(md, md_len); 241 rc = 1; 242 out: 243 ASN1_OCTET_STRING_free(os); 244 return rc; 245 } 246 247 /* 248 * Check the certificate's purpose: CA or BGPsec Router. 249 * Return a member of enum cert_purpose. 250 */ 251 enum cert_purpose 252 x509_get_purpose(X509 *x, const char *fn) 253 { 254 BASIC_CONSTRAINTS *bc = NULL; 255 EXTENDED_KEY_USAGE *eku = NULL; 256 int crit; 257 enum cert_purpose purpose = CERT_PURPOSE_INVALID; 258 259 if (X509_check_ca(x) == 1) { 260 bc = X509_get_ext_d2i(x, NID_basic_constraints, &crit, NULL); 261 if (bc == NULL) { 262 if (crit != -1) 263 warnx("%s: RFC 6487 section 4.8.1: " 264 "error parsing basic constraints", fn); 265 else 266 warnx("%s: RFC 6487 section 4.8.1: " 267 "missing basic constraints", fn); 268 goto out; 269 } 270 if (crit != 1) { 271 warnx("%s: RFC 6487 section 4.8.1: Basic Constraints " 272 "must be marked critical", fn); 273 goto out; 274 } 275 if (bc->pathlen != NULL) { 276 warnx("%s: RFC 6487 section 4.8.1: Path Length " 277 "Constraint must be absent", fn); 278 goto out; 279 } 280 purpose = CERT_PURPOSE_CA; 281 goto out; 282 } 283 284 if (X509_get_extension_flags(x) & EXFLAG_BCONS) { 285 warnx("%s: Basic Constraints ext in non-CA cert", fn); 286 goto out; 287 } 288 289 eku = X509_get_ext_d2i(x, NID_ext_key_usage, &crit, NULL); 290 if (eku == NULL) { 291 if (crit != -1) 292 warnx("%s: error parsing EKU", fn); 293 else 294 warnx("%s: EKU: extension missing", fn); 295 goto out; 296 } 297 if (crit != 0) { 298 warnx("%s: EKU: extension must not be marked critical", fn); 299 goto out; 300 } 301 if (sk_ASN1_OBJECT_num(eku) != 1) { 302 warnx("%s: EKU: expected 1 purpose, have %d", fn, 303 sk_ASN1_OBJECT_num(eku)); 304 goto out; 305 } 306 307 if (OBJ_cmp(bgpsec_oid, sk_ASN1_OBJECT_value(eku, 0)) == 0) { 308 purpose = CERT_PURPOSE_BGPSEC_ROUTER; 309 goto out; 310 } 311 312 out: 313 BASIC_CONSTRAINTS_free(bc); 314 EXTENDED_KEY_USAGE_free(eku); 315 return purpose; 316 } 317 318 /* 319 * Extract Subject Public Key Info (SPKI) from BGPsec X.509 Certificate. 320 * Returns NULL on failure, on success return the SPKI as base64 encoded pubkey 321 */ 322 char * 323 x509_get_pubkey(X509 *x, const char *fn) 324 { 325 EVP_PKEY *pkey; 326 EC_KEY *eckey; 327 int nid; 328 const char *cname; 329 uint8_t *pubkey = NULL; 330 char *res = NULL; 331 int len; 332 333 pkey = X509_get0_pubkey(x); 334 if (pkey == NULL) { 335 warnx("%s: X509_get0_pubkey failed in %s", fn, __func__); 336 goto out; 337 } 338 if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) { 339 warnx("%s: Expected EVP_PKEY_EC, got %d", fn, 340 EVP_PKEY_base_id(pkey)); 341 goto out; 342 } 343 344 eckey = EVP_PKEY_get0_EC_KEY(pkey); 345 if (eckey == NULL) { 346 warnx("%s: Incorrect key type", fn); 347 goto out; 348 } 349 350 nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey)); 351 if (nid != NID_X9_62_prime256v1) { 352 if ((cname = EC_curve_nid2nist(nid)) == NULL) 353 cname = nid2str(nid); 354 warnx("%s: Expected P-256, got %s", fn, cname); 355 goto out; 356 } 357 358 if (!EC_KEY_check_key(eckey)) { 359 warnx("%s: EC_KEY_check_key failed in %s", fn, __func__); 360 goto out; 361 } 362 363 len = i2d_PUBKEY(pkey, &pubkey); 364 if (len <= 0) { 365 warnx("%s: i2d_PUBKEY failed in %s", fn, __func__); 366 goto out; 367 } 368 369 if (base64_encode(pubkey, len, &res) == -1) 370 errx(1, "base64_encode failed in %s", __func__); 371 372 out: 373 free(pubkey); 374 return res; 375 } 376 377 /* 378 * Compute the SKI of an RSA public key in an X509_PUBKEY using SHA-1. 379 * Returns allocated hex-encoded SKI on success, NULL on failure. 380 */ 381 char * 382 x509_pubkey_get_ski(X509_PUBKEY *pubkey, const char *fn) 383 { 384 ASN1_OBJECT *obj; 385 const unsigned char *der; 386 int der_len, nid; 387 unsigned char md[EVP_MAX_MD_SIZE]; 388 unsigned int md_len = EVP_MAX_MD_SIZE; 389 390 if (!X509_PUBKEY_get0_param(&obj, &der, &der_len, NULL, pubkey)) { 391 warnx("%s: X509_PUBKEY_get0_param failed", fn); 392 return NULL; 393 } 394 395 if ((nid = OBJ_obj2nid(obj)) != NID_rsaEncryption) { 396 warnx("%s: RFC 7935: wrong signature algorithm %s, want %s", 397 fn, nid2str(nid), LN_rsaEncryption); 398 return NULL; 399 } 400 401 if (!EVP_Digest(der, der_len, md, &md_len, EVP_sha1(), NULL)) { 402 warnx("%s: EVP_Digest failed", fn); 403 return NULL; 404 } 405 406 return hex_encode(md, md_len); 407 } 408 409 /* 410 * Parse the Authority Information Access (AIA) extension 411 * See RFC 6487, section 4.8.7 for details. 412 * Returns NULL on failure, on success returns the AIA URI 413 * (which has to be freed after use). 414 */ 415 int 416 x509_get_aia(X509 *x, const char *fn, char **aia) 417 { 418 ACCESS_DESCRIPTION *ad; 419 AUTHORITY_INFO_ACCESS *info; 420 int crit, rc = 0; 421 422 *aia = NULL; 423 info = X509_get_ext_d2i(x, NID_info_access, &crit, NULL); 424 if (info == NULL) { 425 if (crit != -1) { 426 warnx("%s: RFC 6487 section 4.8.7: error parsing AIA", 427 fn); 428 return 0; 429 } 430 return 1; 431 } 432 433 if (crit != 0) { 434 warnx("%s: RFC 6487 section 4.8.7: " 435 "AIA: extension not non-critical", fn); 436 goto out; 437 } 438 439 if ((X509_get_extension_flags(x) & EXFLAG_SS) != 0) { 440 warnx("%s: RFC 6487 section 4.8.7: AIA must be absent from " 441 "a self-signed certificate", fn); 442 goto out; 443 } 444 445 if (sk_ACCESS_DESCRIPTION_num(info) != 1) { 446 warnx("%s: RFC 6487 section 4.8.7: AIA: " 447 "want 1 element, have %d", fn, 448 sk_ACCESS_DESCRIPTION_num(info)); 449 goto out; 450 } 451 452 ad = sk_ACCESS_DESCRIPTION_value(info, 0); 453 if (OBJ_obj2nid(ad->method) != NID_ad_ca_issuers) { 454 warnx("%s: RFC 6487 section 4.8.7: AIA: " 455 "expected caIssuers, have %d", fn, OBJ_obj2nid(ad->method)); 456 goto out; 457 } 458 459 if (!x509_location(fn, "AIA: caIssuers", NULL, ad->location, aia)) 460 goto out; 461 462 rc = 1; 463 464 out: 465 AUTHORITY_INFO_ACCESS_free(info); 466 return rc; 467 } 468 469 /* 470 * Parse the Subject Information Access (SIA) extension 471 * See RFC 6487, section 4.8.8 for details. 472 * Returns NULL on failure, on success returns the SIA signedObject URI 473 * (which has to be freed after use). 474 */ 475 int 476 x509_get_sia(X509 *x, const char *fn, char **sia) 477 { 478 ACCESS_DESCRIPTION *ad; 479 AUTHORITY_INFO_ACCESS *info; 480 ASN1_OBJECT *oid; 481 int i, crit, rsync_found = 0; 482 483 *sia = NULL; 484 485 info = X509_get_ext_d2i(x, NID_sinfo_access, &crit, NULL); 486 if (info == NULL) { 487 if (crit != -1) { 488 warnx("%s: error parsing SIA", fn); 489 return 0; 490 } 491 return 1; 492 } 493 494 if (crit != 0) { 495 warnx("%s: RFC 6487 section 4.8.8: " 496 "SIA: extension not non-critical", fn); 497 goto out; 498 } 499 500 for (i = 0; i < sk_ACCESS_DESCRIPTION_num(info); i++) { 501 ad = sk_ACCESS_DESCRIPTION_value(info, i); 502 oid = ad->method; 503 504 /* 505 * XXX: RFC 6487 4.8.8.2 states that the accessMethod MUST be 506 * signedObject. However, rpkiNotify accessMethods currently 507 * exist in the wild. Consider removing this special case. 508 * See also https://www.rfc-editor.org/errata/eid7239. 509 */ 510 if (OBJ_cmp(oid, notify_oid) == 0) { 511 if (verbose > 1) 512 warnx("%s: RFC 6487 section 4.8.8.2: SIA should" 513 " not contain rpkiNotify accessMethod", fn); 514 continue; 515 } 516 if (OBJ_cmp(oid, signedobj_oid) != 0) { 517 char buf[128]; 518 519 OBJ_obj2txt(buf, sizeof(buf), oid, 0); 520 warnx("%s: RFC 6487 section 4.8.8.2: unexpected" 521 " accessMethod: %s", fn, buf); 522 goto out; 523 } 524 525 /* Don't fail on non-rsync URI, so check this afterward. */ 526 if (!x509_location(fn, "SIA: signedObject", NULL, ad->location, 527 sia)) 528 goto out; 529 530 if (rsync_found) 531 continue; 532 533 if (strncasecmp(*sia, RSYNC_PROTO, RSYNC_PROTO_LEN) == 0) { 534 const char *p = *sia + RSYNC_PROTO_LEN; 535 size_t fnlen, plen; 536 537 rsync_found = 1; 538 539 if (filemode) 540 continue; 541 542 fnlen = strlen(fn); 543 plen = strlen(p); 544 545 if (fnlen < plen || strcmp(p, fn + fnlen - plen) != 0) { 546 warnx("%s: mismatch between pathname and SIA " 547 "(%s)", fn, *sia); 548 goto out; 549 } 550 551 continue; 552 } 553 554 free(*sia); 555 *sia = NULL; 556 } 557 558 if (!rsync_found) { 559 warnx("%s: RFC 6487 section 4.8.8.2: " 560 "SIA without rsync accessLocation", fn); 561 goto out; 562 } 563 564 AUTHORITY_INFO_ACCESS_free(info); 565 return 1; 566 567 out: 568 free(*sia); 569 *sia = NULL; 570 AUTHORITY_INFO_ACCESS_free(info); 571 return 0; 572 } 573 574 /* 575 * Extract the notBefore of a certificate. 576 */ 577 int 578 x509_get_notbefore(X509 *x, const char *fn, time_t *tt) 579 { 580 const ASN1_TIME *at; 581 582 at = X509_get0_notBefore(x); 583 if (at == NULL) { 584 warnx("%s: X509_get0_notBefore failed", fn); 585 return 0; 586 } 587 if (!x509_get_time(at, tt)) { 588 warnx("%s: ASN1_TIME_to_tm failed", fn); 589 return 0; 590 } 591 return 1; 592 } 593 594 /* 595 * Extract the notAfter from a certificate. 596 */ 597 int 598 x509_get_notafter(X509 *x, const char *fn, time_t *tt) 599 { 600 const ASN1_TIME *at; 601 602 at = X509_get0_notAfter(x); 603 if (at == NULL) { 604 warnx("%s: X509_get0_notafter failed", fn); 605 return 0; 606 } 607 if (!x509_get_time(at, tt)) { 608 warnx("%s: ASN1_TIME_to_tm failed", fn); 609 return 0; 610 } 611 return 1; 612 } 613 614 /* 615 * Check whether all RFC 3779 extensions are set to inherit. 616 * Return 1 if both AS & IP are set to inherit. 617 * Return 0 on failure (such as missing extensions or no inheritance). 618 */ 619 int 620 x509_inherits(X509 *x) 621 { 622 STACK_OF(IPAddressFamily) *addrblk = NULL; 623 ASIdentifiers *asidentifiers = NULL; 624 const IPAddressFamily *af; 625 int crit, i, rc = 0; 626 627 addrblk = X509_get_ext_d2i(x, NID_sbgp_ipAddrBlock, &crit, NULL); 628 if (addrblk == NULL) { 629 if (crit != -1) 630 warnx("error parsing ipAddrBlock"); 631 goto out; 632 } 633 634 /* 635 * Check by hand, since X509v3_addr_inherits() success only means that 636 * at least one address family inherits, not all of them. 637 */ 638 for (i = 0; i < sk_IPAddressFamily_num(addrblk); i++) { 639 af = sk_IPAddressFamily_value(addrblk, i); 640 if (af->ipAddressChoice->type != IPAddressChoice_inherit) 641 goto out; 642 } 643 644 asidentifiers = X509_get_ext_d2i(x, NID_sbgp_autonomousSysNum, NULL, 645 NULL); 646 if (asidentifiers == NULL) { 647 if (crit != -1) 648 warnx("error parsing asIdentifiers"); 649 goto out; 650 } 651 652 /* We need to have AS numbers and don't want RDIs. */ 653 if (asidentifiers->asnum == NULL || asidentifiers->rdi != NULL) 654 goto out; 655 if (!X509v3_asid_inherits(asidentifiers)) 656 goto out; 657 658 rc = 1; 659 out: 660 ASIdentifiers_free(asidentifiers); 661 sk_IPAddressFamily_pop_free(addrblk, IPAddressFamily_free); 662 return rc; 663 } 664 665 /* 666 * Check whether at least one RFC 3779 extension is set to inherit. 667 * Return 1 if an inherit element is encountered in AS or IP. 668 * Return 0 otherwise. 669 */ 670 int 671 x509_any_inherits(X509 *x) 672 { 673 STACK_OF(IPAddressFamily) *addrblk = NULL; 674 ASIdentifiers *asidentifiers = NULL; 675 int crit, rc = 0; 676 677 addrblk = X509_get_ext_d2i(x, NID_sbgp_ipAddrBlock, &crit, NULL); 678 if (addrblk == NULL && crit != -1) 679 warnx("error parsing ipAddrBlock"); 680 if (X509v3_addr_inherits(addrblk)) 681 rc = 1; 682 683 asidentifiers = X509_get_ext_d2i(x, NID_sbgp_autonomousSysNum, &crit, 684 NULL); 685 if (asidentifiers == NULL && crit != -1) 686 warnx("error parsing asIdentifiers"); 687 if (X509v3_asid_inherits(asidentifiers)) 688 rc = 1; 689 690 ASIdentifiers_free(asidentifiers); 691 sk_IPAddressFamily_pop_free(addrblk, IPAddressFamily_free); 692 return rc; 693 } 694 695 /* 696 * Parse the very specific subset of information in the CRL distribution 697 * point extension. 698 * See RFC 6487, section 4.8.6 for details. 699 * Returns NULL on failure, the crl URI on success which has to be freed 700 * after use. 701 */ 702 int 703 x509_get_crl(X509 *x, const char *fn, char **crl) 704 { 705 CRL_DIST_POINTS *crldp; 706 DIST_POINT *dp; 707 GENERAL_NAMES *names; 708 GENERAL_NAME *name; 709 int i, crit, rsync_found = 0; 710 711 *crl = NULL; 712 crldp = X509_get_ext_d2i(x, NID_crl_distribution_points, &crit, NULL); 713 if (crldp == NULL) { 714 if (crit != -1) { 715 warnx("%s: RFC 6487 section 4.8.6: failed to parse " 716 "CRL distribution points", fn); 717 return 0; 718 } 719 return 1; 720 } 721 722 if (crit != 0) { 723 warnx("%s: RFC 6487 section 4.8.6: " 724 "CRL distribution point: extension not non-critical", fn); 725 goto out; 726 } 727 728 if (sk_DIST_POINT_num(crldp) != 1) { 729 warnx("%s: RFC 6487 section 4.8.6: CRL: " 730 "want 1 element, have %d", fn, 731 sk_DIST_POINT_num(crldp)); 732 goto out; 733 } 734 735 dp = sk_DIST_POINT_value(crldp, 0); 736 if (dp->CRLissuer != NULL) { 737 warnx("%s: RFC 6487 section 4.8.6: CRL CRLIssuer field" 738 " disallowed", fn); 739 goto out; 740 } 741 if (dp->reasons != NULL) { 742 warnx("%s: RFC 6487 section 4.8.6: CRL Reasons field" 743 " disallowed", fn); 744 goto out; 745 } 746 if (dp->distpoint == NULL) { 747 warnx("%s: RFC 6487 section 4.8.6: CRL: " 748 "no distribution point name", fn); 749 goto out; 750 } 751 if (dp->distpoint->dpname != NULL) { 752 warnx("%s: RFC 6487 section 4.8.6: nameRelativeToCRLIssuer" 753 " disallowed", fn); 754 goto out; 755 } 756 /* Need to hardcode the alternative 0 due to missing macros or enum. */ 757 if (dp->distpoint->type != 0) { 758 warnx("%s: RFC 6487 section 4.8.6: CRL DistributionPointName:" 759 " expected fullName, have %d", fn, dp->distpoint->type); 760 goto out; 761 } 762 763 names = dp->distpoint->name.fullname; 764 for (i = 0; i < sk_GENERAL_NAME_num(names); i++) { 765 name = sk_GENERAL_NAME_value(names, i); 766 767 /* Don't fail on non-rsync URI, so check this afterward. */ 768 if (!x509_location(fn, "CRL distribution point", NULL, name, 769 crl)) 770 goto out; 771 772 if (strncasecmp(*crl, RSYNC_PROTO, RSYNC_PROTO_LEN) == 0) { 773 rsync_found = 1; 774 goto out; 775 } 776 777 free(*crl); 778 *crl = NULL; 779 } 780 781 warnx("%s: RFC 6487 section 4.8.6: no rsync URI " 782 "in CRL distributionPoint", fn); 783 784 out: 785 CRL_DIST_POINTS_free(crldp); 786 return rsync_found; 787 } 788 789 /* 790 * Convert passed ASN1_TIME to time_t *t. 791 * Returns 1 on success and 0 on failure. 792 */ 793 int 794 x509_get_time(const ASN1_TIME *at, time_t *t) 795 { 796 struct tm tm; 797 798 *t = 0; 799 memset(&tm, 0, sizeof(tm)); 800 /* Fail instead of silently falling back to the current time. */ 801 if (at == NULL) 802 return 0; 803 if (!ASN1_TIME_to_tm(at, &tm)) 804 return 0; 805 if ((*t = timegm(&tm)) == -1) 806 errx(1, "timegm failed"); 807 return 1; 808 } 809 810 /* 811 * Extract and validate an accessLocation, RFC 6487, 4.8 and RFC 8182, 3.2. 812 * Returns 0 on failure and 1 on success. 813 */ 814 int 815 x509_location(const char *fn, const char *descr, const char *proto, 816 GENERAL_NAME *location, char **out) 817 { 818 ASN1_IA5STRING *uri; 819 820 if (location->type != GEN_URI) { 821 warnx("%s: RFC 6487 section 4.8: %s not URI", fn, descr); 822 return 0; 823 } 824 825 uri = location->d.uniformResourceIdentifier; 826 827 if (!valid_uri(uri->data, uri->length, proto)) { 828 warnx("%s: RFC 6487 section 4.8: %s bad location", fn, descr); 829 return 0; 830 } 831 832 if (*out != NULL) { 833 warnx("%s: RFC 6487 section 4.8: multiple %s specified, " 834 "using the first one", fn, descr); 835 return 1; 836 } 837 838 if ((*out = strndup(uri->data, uri->length)) == NULL) 839 err(1, NULL); 840 841 return 1; 842 } 843 844 /* 845 * Check that the subject only contains commonName and serialNumber. 846 * Return 0 on failure. 847 */ 848 int 849 x509_valid_subject(const char *fn, const X509 *x) 850 { 851 const X509_NAME *xn; 852 const X509_NAME_ENTRY *ne; 853 const ASN1_OBJECT *ao; 854 const ASN1_STRING *as; 855 int cn = 0, sn = 0; 856 int i, nid; 857 858 if ((xn = X509_get_subject_name(x)) == NULL) { 859 warnx("%s: X509_get_subject_name", fn); 860 return 0; 861 } 862 863 for (i = 0; i < X509_NAME_entry_count(xn); i++) { 864 if ((ne = X509_NAME_get_entry(xn, i)) == NULL) { 865 warnx("%s: X509_NAME_get_entry", fn); 866 return 0; 867 } 868 if ((ao = X509_NAME_ENTRY_get_object(ne)) == NULL) { 869 warnx("%s: X509_NAME_ENTRY_get_object", fn); 870 return 0; 871 } 872 873 nid = OBJ_obj2nid(ao); 874 switch (nid) { 875 case NID_commonName: 876 if (cn++ > 0) { 877 warnx("%s: duplicate commonName in subject", 878 fn); 879 return 0; 880 } 881 if ((as = X509_NAME_ENTRY_get_data(ne)) == NULL) { 882 warnx("%s: X509_NAME_ENTRY_get_data failed", 883 fn); 884 return 0; 885 } 886 /* 887 * The following check can be enabled after AFRINIC re-issues CA certs. 888 * https://lists.afrinic.net/pipermail/dbwg/2023-March/000436.html 889 */ 890 #if 0 891 if (ASN1_STRING_type(as) != V_ASN1_PRINTABLESTRING) { 892 warnx("%s: RFC 6487 section 4.5: commonName is" 893 " not PrintableString", fn); 894 return 0; 895 } 896 #endif 897 break; 898 case NID_serialNumber: 899 if (sn++ > 0) { 900 warnx("%s: duplicate serialNumber in subject", 901 fn); 902 return 0; 903 } 904 break; 905 case NID_undef: 906 warnx("%s: OBJ_obj2nid failed", fn); 907 return 0; 908 default: 909 warnx("%s: RFC 6487 section 4.5: unexpected attribute" 910 " %s", fn, nid2str(nid)); 911 return 0; 912 } 913 } 914 915 if (cn == 0) { 916 warnx("%s: RFC 6487 section 4.5: subject missing commonName", 917 fn); 918 return 0; 919 } 920 921 return 1; 922 } 923 924 /* 925 * Convert an ASN1_INTEGER into a hexstring, enforcing that it is non-negative 926 * and representable by at most 20 octets (RFC 5280, section 4.1.2.2). 927 * Returned string needs to be freed by the caller. 928 */ 929 char * 930 x509_convert_seqnum(const char *fn, const ASN1_INTEGER *i) 931 { 932 BIGNUM *seqnum = NULL; 933 char *s = NULL; 934 935 if (i == NULL) 936 goto out; 937 938 if (ASN1_STRING_length(i) > 20) { 939 warnx("%s: %s: want 20 octets or fewer, have more.", 940 __func__, fn); 941 goto out; 942 } 943 944 seqnum = ASN1_INTEGER_to_BN(i, NULL); 945 if (seqnum == NULL) { 946 warnx("%s: ASN1_INTEGER_to_BN error", fn); 947 goto out; 948 } 949 950 if (BN_is_negative(seqnum)) { 951 warnx("%s: %s: want positive integer, have negative.", 952 __func__, fn); 953 goto out; 954 } 955 956 s = BN_bn2hex(seqnum); 957 if (s == NULL) 958 warnx("%s: BN_bn2hex error", fn); 959 960 out: 961 BN_free(seqnum); 962 return s; 963 } 964 965 /* 966 * Find the closest expiry moment by walking the chain of authorities. 967 */ 968 time_t 969 x509_find_expires(time_t notafter, struct auth *a, struct crl_tree *crlt) 970 { 971 struct crl *crl; 972 time_t expires; 973 974 expires = notafter; 975 976 for (; a != NULL; a = a->issuer) { 977 if (expires > a->cert->notafter) 978 expires = a->cert->notafter; 979 crl = crl_get(crlt, a); 980 if (crl != NULL && expires > crl->nextupdate) 981 expires = crl->nextupdate; 982 } 983 984 return expires; 985 } 986