1 /* $OpenBSD: x509.c,v 1.75 2023/11/16 11:10:59 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 *bin_sign_time_oid; /* pkcs-9 id-aa-binarySigningTime */ 43 ASN1_OBJECT *rsc_oid; /* id-ct-signedChecklist */ 44 ASN1_OBJECT *aspa_oid; /* id-ct-ASPA */ 45 ASN1_OBJECT *tak_oid; /* id-ct-SignedTAL */ 46 ASN1_OBJECT *geofeed_oid; /* id-ct-geofeedCSVwithCRLF */ 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.2.46", 102 .ptr = &bin_sign_time_oid, 103 }, 104 { 105 .oid = "1.2.840.113549.1.9.16.1.47", 106 .ptr = &geofeed_oid, 107 }, 108 { 109 .oid = "1.2.840.113549.1.9.16.1.48", 110 .ptr = &rsc_oid, 111 }, 112 { 113 .oid = "1.2.840.113549.1.9.16.1.49", 114 .ptr = &aspa_oid, 115 }, 116 { 117 .oid = "1.2.840.113549.1.9.16.1.50", 118 .ptr = &tak_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 * Parse X509v3 subject key identifier (SKI), RFC 6487 sec. 4.8.2. 195 * The SKI must be the SHA1 hash of the Subject Public Key. 196 * Returns the SKI formatted as hex string, or NULL if it couldn't be parsed. 197 */ 198 int 199 x509_get_ski(X509 *x, const char *fn, char **ski) 200 { 201 const unsigned char *d, *spk; 202 ASN1_OCTET_STRING *os; 203 X509_PUBKEY *pubkey; 204 unsigned char spkd[SHA_DIGEST_LENGTH]; 205 int crit, dsz, spkz, 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 d = os->data; 224 dsz = os->length; 225 226 if (dsz != SHA_DIGEST_LENGTH) { 227 warnx("%s: RFC 6487 section 4.8.2: SKI: " 228 "want %d bytes SHA1 hash, have %d bytes", 229 fn, SHA_DIGEST_LENGTH, dsz); 230 goto out; 231 } 232 233 if ((pubkey = X509_get_X509_PUBKEY(x)) == NULL) { 234 warnx("%s: X509_get_X509_PUBKEY", fn); 235 goto out; 236 } 237 if (!X509_PUBKEY_get0_param(NULL, &spk, &spkz, NULL, pubkey)) { 238 warnx("%s: X509_PUBKEY_get0_param", fn); 239 goto out; 240 } 241 242 if (!EVP_Digest(spk, spkz, spkd, NULL, EVP_sha1(), NULL)) { 243 warnx("%s: EVP_Digest failed", fn); 244 goto out; 245 } 246 247 if (memcmp(spkd, d, dsz) != 0) { 248 warnx("%s: SKI does not match SHA1 hash of SPK", fn); 249 goto out; 250 } 251 252 *ski = hex_encode(d, dsz); 253 rc = 1; 254 out: 255 ASN1_OCTET_STRING_free(os); 256 return rc; 257 } 258 259 /* 260 * Check the certificate's purpose: CA or BGPsec Router. 261 * Return a member of enum cert_purpose. 262 */ 263 enum cert_purpose 264 x509_get_purpose(X509 *x, const char *fn) 265 { 266 BASIC_CONSTRAINTS *bc = NULL; 267 EXTENDED_KEY_USAGE *eku = NULL; 268 int crit; 269 enum cert_purpose purpose = CERT_PURPOSE_INVALID; 270 271 if (X509_check_ca(x) == 1) { 272 bc = X509_get_ext_d2i(x, NID_basic_constraints, &crit, NULL); 273 if (bc == NULL) { 274 if (crit != -1) 275 warnx("%s: RFC 6487 section 4.8.1: " 276 "error parsing basic constraints", fn); 277 else 278 warnx("%s: RFC 6487 section 4.8.1: " 279 "missing basic constraints", fn); 280 goto out; 281 } 282 if (crit != 1) { 283 warnx("%s: RFC 6487 section 4.8.1: Basic Constraints " 284 "must be marked critical", fn); 285 goto out; 286 } 287 if (bc->pathlen != NULL) { 288 warnx("%s: RFC 6487 section 4.8.1: Path Length " 289 "Constraint must be absent", fn); 290 goto out; 291 } 292 purpose = CERT_PURPOSE_CA; 293 goto out; 294 } 295 296 if (X509_get_extension_flags(x) & EXFLAG_BCONS) { 297 warnx("%s: Basic Constraints ext in non-CA cert", fn); 298 goto out; 299 } 300 301 eku = X509_get_ext_d2i(x, NID_ext_key_usage, &crit, NULL); 302 if (eku == NULL) { 303 if (crit != -1) 304 warnx("%s: error parsing EKU", fn); 305 else 306 warnx("%s: EKU: extension missing", fn); 307 goto out; 308 } 309 if (crit != 0) { 310 warnx("%s: EKU: extension must not be marked critical", fn); 311 goto out; 312 } 313 if (sk_ASN1_OBJECT_num(eku) != 1) { 314 warnx("%s: EKU: expected 1 purpose, have %d", fn, 315 sk_ASN1_OBJECT_num(eku)); 316 goto out; 317 } 318 319 if (OBJ_cmp(bgpsec_oid, sk_ASN1_OBJECT_value(eku, 0)) == 0) { 320 purpose = CERT_PURPOSE_BGPSEC_ROUTER; 321 goto out; 322 } 323 324 out: 325 BASIC_CONSTRAINTS_free(bc); 326 EXTENDED_KEY_USAGE_free(eku); 327 return purpose; 328 } 329 330 /* 331 * Extract Subject Public Key Info (SPKI) from BGPsec X.509 Certificate. 332 * Returns NULL on failure, on success return the SPKI as base64 encoded pubkey 333 */ 334 char * 335 x509_get_pubkey(X509 *x, const char *fn) 336 { 337 EVP_PKEY *pkey; 338 EC_KEY *eckey; 339 int nid; 340 const char *cname; 341 uint8_t *pubkey = NULL; 342 char *res = NULL; 343 int len; 344 345 pkey = X509_get0_pubkey(x); 346 if (pkey == NULL) { 347 warnx("%s: X509_get0_pubkey failed in %s", fn, __func__); 348 goto out; 349 } 350 if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) { 351 warnx("%s: Expected EVP_PKEY_EC, got %d", fn, 352 EVP_PKEY_base_id(pkey)); 353 goto out; 354 } 355 356 eckey = EVP_PKEY_get0_EC_KEY(pkey); 357 if (eckey == NULL) { 358 warnx("%s: Incorrect key type", fn); 359 goto out; 360 } 361 362 nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey)); 363 if (nid != NID_X9_62_prime256v1) { 364 if ((cname = EC_curve_nid2nist(nid)) == NULL) 365 cname = OBJ_nid2sn(nid); 366 warnx("%s: Expected P-256, got %s", fn, cname); 367 goto out; 368 } 369 370 if (!EC_KEY_check_key(eckey)) { 371 warnx("%s: EC_KEY_check_key failed in %s", fn, __func__); 372 goto out; 373 } 374 375 len = i2d_PUBKEY(pkey, &pubkey); 376 if (len <= 0) { 377 warnx("%s: i2d_PUBKEY failed in %s", fn, __func__); 378 goto out; 379 } 380 381 if (base64_encode(pubkey, len, &res) == -1) 382 errx(1, "base64_encode failed in %s", __func__); 383 384 out: 385 free(pubkey); 386 return res; 387 } 388 389 /* 390 * Parse the Authority Information Access (AIA) extension 391 * See RFC 6487, section 4.8.7 for details. 392 * Returns NULL on failure, on success returns the AIA URI 393 * (which has to be freed after use). 394 */ 395 int 396 x509_get_aia(X509 *x, const char *fn, char **aia) 397 { 398 ACCESS_DESCRIPTION *ad; 399 AUTHORITY_INFO_ACCESS *info; 400 int crit, rc = 0; 401 402 *aia = NULL; 403 info = X509_get_ext_d2i(x, NID_info_access, &crit, NULL); 404 if (info == NULL) { 405 if (crit != -1) { 406 warnx("%s: RFC 6487 section 4.8.7: error parsing AIA", 407 fn); 408 return 0; 409 } 410 return 1; 411 } 412 413 if (crit != 0) { 414 warnx("%s: RFC 6487 section 4.8.7: " 415 "AIA: extension not non-critical", fn); 416 goto out; 417 } 418 419 if ((X509_get_extension_flags(x) & EXFLAG_SS) != 0) { 420 warnx("%s: RFC 6487 section 4.8.7: AIA must be absent from " 421 "a self-signed certificate", fn); 422 goto out; 423 } 424 425 if (sk_ACCESS_DESCRIPTION_num(info) != 1) { 426 warnx("%s: RFC 6487 section 4.8.7: AIA: " 427 "want 1 element, have %d", fn, 428 sk_ACCESS_DESCRIPTION_num(info)); 429 goto out; 430 } 431 432 ad = sk_ACCESS_DESCRIPTION_value(info, 0); 433 if (OBJ_obj2nid(ad->method) != NID_ad_ca_issuers) { 434 warnx("%s: RFC 6487 section 4.8.7: AIA: " 435 "expected caIssuers, have %d", fn, OBJ_obj2nid(ad->method)); 436 goto out; 437 } 438 439 if (!x509_location(fn, "AIA: caIssuers", NULL, ad->location, aia)) 440 goto out; 441 442 rc = 1; 443 444 out: 445 AUTHORITY_INFO_ACCESS_free(info); 446 return rc; 447 } 448 449 /* 450 * Parse the Subject Information Access (SIA) extension 451 * See RFC 6487, section 4.8.8 for details. 452 * Returns NULL on failure, on success returns the SIA signedObject URI 453 * (which has to be freed after use). 454 */ 455 int 456 x509_get_sia(X509 *x, const char *fn, char **sia) 457 { 458 ACCESS_DESCRIPTION *ad; 459 AUTHORITY_INFO_ACCESS *info; 460 ASN1_OBJECT *oid; 461 int i, crit, rsync_found = 0; 462 463 *sia = NULL; 464 465 info = X509_get_ext_d2i(x, NID_sinfo_access, &crit, NULL); 466 if (info == NULL) { 467 if (crit != -1) { 468 warnx("%s: error parsing SIA", fn); 469 return 0; 470 } 471 return 1; 472 } 473 474 if (crit != 0) { 475 warnx("%s: RFC 6487 section 4.8.8: " 476 "SIA: extension not non-critical", fn); 477 goto out; 478 } 479 480 for (i = 0; i < sk_ACCESS_DESCRIPTION_num(info); i++) { 481 ad = sk_ACCESS_DESCRIPTION_value(info, i); 482 oid = ad->method; 483 484 /* 485 * XXX: RFC 6487 4.8.8.2 states that the accessMethod MUST be 486 * signedObject. However, rpkiNotify accessMethods currently 487 * exist in the wild. Consider removing this special case. 488 * See also https://www.rfc-editor.org/errata/eid7239. 489 */ 490 if (OBJ_cmp(oid, notify_oid) == 0) { 491 if (verbose > 1) 492 warnx("%s: RFC 6487 section 4.8.8.2: SIA should" 493 " not contain rpkiNotify accessMethod", fn); 494 continue; 495 } 496 if (OBJ_cmp(oid, signedobj_oid) != 0) { 497 char buf[128]; 498 499 OBJ_obj2txt(buf, sizeof(buf), oid, 0); 500 warnx("%s: RFC 6487 section 4.8.8.2: unexpected" 501 " accessMethod: %s", fn, buf); 502 goto out; 503 } 504 505 /* Don't fail on non-rsync URI, so check this afterward. */ 506 if (!x509_location(fn, "SIA: signedObject", NULL, ad->location, 507 sia)) 508 goto out; 509 510 if (rsync_found) 511 continue; 512 513 if (strncasecmp(*sia, "rsync://", 8) == 0) { 514 rsync_found = 1; 515 continue; 516 } 517 518 free(*sia); 519 *sia = NULL; 520 } 521 522 if (!rsync_found) { 523 warnx("%s: RFC 6487 section 4.8.8.2: " 524 "SIA without rsync accessLocation", fn); 525 goto out; 526 } 527 528 AUTHORITY_INFO_ACCESS_free(info); 529 return 1; 530 531 out: 532 free(*sia); 533 *sia = NULL; 534 AUTHORITY_INFO_ACCESS_free(info); 535 return 0; 536 } 537 538 /* 539 * Extract the notBefore of a certificate. 540 */ 541 int 542 x509_get_notbefore(X509 *x, const char *fn, time_t *tt) 543 { 544 const ASN1_TIME *at; 545 546 at = X509_get0_notBefore(x); 547 if (at == NULL) { 548 warnx("%s: X509_get0_notBefore failed", fn); 549 return 0; 550 } 551 if (!x509_get_time(at, tt)) { 552 warnx("%s: ASN1_TIME_to_tm failed", fn); 553 return 0; 554 } 555 return 1; 556 } 557 558 /* 559 * Extract the notAfter from a certificate. 560 */ 561 int 562 x509_get_notafter(X509 *x, const char *fn, time_t *tt) 563 { 564 const ASN1_TIME *at; 565 566 at = X509_get0_notAfter(x); 567 if (at == NULL) { 568 warnx("%s: X509_get0_notafter failed", fn); 569 return 0; 570 } 571 if (!x509_get_time(at, tt)) { 572 warnx("%s: ASN1_TIME_to_tm failed", fn); 573 return 0; 574 } 575 return 1; 576 } 577 578 /* 579 * Check whether all RFC 3779 extensions are set to inherit. 580 * Return 1 if both AS & IP are set to inherit. 581 * Return 0 on failure (such as missing extensions or no inheritance). 582 */ 583 int 584 x509_inherits(X509 *x) 585 { 586 STACK_OF(IPAddressFamily) *addrblk = NULL; 587 ASIdentifiers *asidentifiers = NULL; 588 const IPAddressFamily *af; 589 int crit, i, rc = 0; 590 591 addrblk = X509_get_ext_d2i(x, NID_sbgp_ipAddrBlock, &crit, NULL); 592 if (addrblk == NULL) { 593 if (crit != -1) 594 warnx("error parsing ipAddrBlock"); 595 goto out; 596 } 597 598 /* 599 * Check by hand, since X509v3_addr_inherits() success only means that 600 * at least one address family inherits, not all of them. 601 */ 602 for (i = 0; i < sk_IPAddressFamily_num(addrblk); i++) { 603 af = sk_IPAddressFamily_value(addrblk, i); 604 if (af->ipAddressChoice->type != IPAddressChoice_inherit) 605 goto out; 606 } 607 608 asidentifiers = X509_get_ext_d2i(x, NID_sbgp_autonomousSysNum, NULL, 609 NULL); 610 if (asidentifiers == NULL) { 611 if (crit != -1) 612 warnx("error parsing asIdentifiers"); 613 goto out; 614 } 615 616 /* We need to have AS numbers and don't want RDIs. */ 617 if (asidentifiers->asnum == NULL || asidentifiers->rdi != NULL) 618 goto out; 619 if (!X509v3_asid_inherits(asidentifiers)) 620 goto out; 621 622 rc = 1; 623 out: 624 ASIdentifiers_free(asidentifiers); 625 sk_IPAddressFamily_pop_free(addrblk, IPAddressFamily_free); 626 return rc; 627 } 628 629 /* 630 * Check whether at least one RFC 3779 extension is set to inherit. 631 * Return 1 if an inherit element is encountered in AS or IP. 632 * Return 0 otherwise. 633 */ 634 int 635 x509_any_inherits(X509 *x) 636 { 637 STACK_OF(IPAddressFamily) *addrblk = NULL; 638 ASIdentifiers *asidentifiers = NULL; 639 int crit, rc = 0; 640 641 addrblk = X509_get_ext_d2i(x, NID_sbgp_ipAddrBlock, &crit, NULL); 642 if (addrblk == NULL && crit != -1) 643 warnx("error parsing ipAddrBlock"); 644 if (X509v3_addr_inherits(addrblk)) 645 rc = 1; 646 647 asidentifiers = X509_get_ext_d2i(x, NID_sbgp_autonomousSysNum, &crit, 648 NULL); 649 if (asidentifiers == NULL && crit != -1) 650 warnx("error parsing asIdentifiers"); 651 if (X509v3_asid_inherits(asidentifiers)) 652 rc = 1; 653 654 ASIdentifiers_free(asidentifiers); 655 sk_IPAddressFamily_pop_free(addrblk, IPAddressFamily_free); 656 return rc; 657 } 658 659 /* 660 * Parse the very specific subset of information in the CRL distribution 661 * point extension. 662 * See RFC 6487, section 4.8.6 for details. 663 * Returns NULL on failure, the crl URI on success which has to be freed 664 * after use. 665 */ 666 int 667 x509_get_crl(X509 *x, const char *fn, char **crl) 668 { 669 CRL_DIST_POINTS *crldp; 670 DIST_POINT *dp; 671 GENERAL_NAMES *names; 672 GENERAL_NAME *name; 673 int i, crit, rsync_found = 0; 674 675 *crl = NULL; 676 crldp = X509_get_ext_d2i(x, NID_crl_distribution_points, &crit, NULL); 677 if (crldp == NULL) { 678 if (crit != -1) { 679 warnx("%s: RFC 6487 section 4.8.6: failed to parse " 680 "CRL distribution points", fn); 681 return 0; 682 } 683 return 1; 684 } 685 686 if (crit != 0) { 687 warnx("%s: RFC 6487 section 4.8.6: " 688 "CRL distribution point: extension not non-critical", fn); 689 goto out; 690 } 691 692 if (sk_DIST_POINT_num(crldp) != 1) { 693 warnx("%s: RFC 6487 section 4.8.6: CRL: " 694 "want 1 element, have %d", fn, 695 sk_DIST_POINT_num(crldp)); 696 goto out; 697 } 698 699 dp = sk_DIST_POINT_value(crldp, 0); 700 if (dp->CRLissuer != NULL) { 701 warnx("%s: RFC 6487 section 4.8.6: CRL CRLIssuer field" 702 " disallowed", fn); 703 goto out; 704 } 705 if (dp->reasons != NULL) { 706 warnx("%s: RFC 6487 section 4.8.6: CRL Reasons field" 707 " disallowed", fn); 708 goto out; 709 } 710 if (dp->distpoint == NULL) { 711 warnx("%s: RFC 6487 section 4.8.6: CRL: " 712 "no distribution point name", fn); 713 goto out; 714 } 715 if (dp->distpoint->dpname != NULL) { 716 warnx("%s: RFC 6487 section 4.8.6: nameRelativeToCRLIssuer" 717 " disallowed", fn); 718 goto out; 719 } 720 if (dp->distpoint->type != 0) { 721 warnx("%s: RFC 6487 section 4.8.6: CRL: " 722 "expected GEN_OTHERNAME, have %d", fn, dp->distpoint->type); 723 goto out; 724 } 725 726 names = dp->distpoint->name.fullname; 727 for (i = 0; i < sk_GENERAL_NAME_num(names); i++) { 728 name = sk_GENERAL_NAME_value(names, i); 729 730 /* Don't fail on non-rsync URI, so check this afterward. */ 731 if (!x509_location(fn, "CRL distribution point", NULL, name, 732 crl)) 733 goto out; 734 735 if (strncasecmp(*crl, "rsync://", 8) == 0) { 736 rsync_found = 1; 737 goto out; 738 } 739 740 free(*crl); 741 *crl = NULL; 742 } 743 744 warnx("%s: RFC 6487 section 4.8.6: no rsync URI " 745 "in CRL distributionPoint", fn); 746 747 out: 748 CRL_DIST_POINTS_free(crldp); 749 return rsync_found; 750 } 751 752 /* 753 * Parse X509v3 authority key identifier (AKI) from the CRL. 754 * This is matched against the string from x509_get_ski() above. 755 * Returns the AKI or NULL if it could not be parsed. 756 * The AKI is formatted as a hex string. 757 */ 758 char * 759 x509_crl_get_aki(X509_CRL *crl, const char *fn) 760 { 761 const unsigned char *d; 762 AUTHORITY_KEYID *akid; 763 ASN1_OCTET_STRING *os; 764 int dsz, crit; 765 char *res = NULL; 766 767 akid = X509_CRL_get_ext_d2i(crl, NID_authority_key_identifier, &crit, 768 NULL); 769 if (akid == NULL) { 770 warnx("%s: RFC 6487 section 4.8.3: AKI: extension missing", fn); 771 return NULL; 772 } 773 if (crit != 0) { 774 warnx("%s: RFC 6487 section 4.8.3: " 775 "AKI: extension not non-critical", fn); 776 goto out; 777 } 778 if (akid->issuer != NULL || akid->serial != NULL) { 779 warnx("%s: RFC 6487 section 4.8.3: AKI: " 780 "authorityCertIssuer or authorityCertSerialNumber present", 781 fn); 782 goto out; 783 } 784 785 os = akid->keyid; 786 if (os == NULL) { 787 warnx("%s: RFC 6487 section 4.8.3: AKI: " 788 "Key Identifier missing", fn); 789 goto out; 790 } 791 792 d = os->data; 793 dsz = os->length; 794 795 if (dsz != SHA_DIGEST_LENGTH) { 796 warnx("%s: RFC 6487 section 4.8.2: AKI: " 797 "want %d bytes SHA1 hash, have %d bytes", 798 fn, SHA_DIGEST_LENGTH, dsz); 799 goto out; 800 } 801 802 res = hex_encode(d, dsz); 803 out: 804 AUTHORITY_KEYID_free(akid); 805 return res; 806 } 807 808 /* 809 * Retrieve CRL Number extension. Returns a printable hexadecimal representation 810 * of the number which has to be freed after use. 811 */ 812 char * 813 x509_crl_get_number(X509_CRL *crl, const char *fn) 814 { 815 ASN1_INTEGER *aint; 816 int crit; 817 char *res = NULL; 818 819 aint = X509_CRL_get_ext_d2i(crl, NID_crl_number, &crit, NULL); 820 if (aint == NULL) { 821 warnx("%s: RFC 6487 section 5: CRL Number missing", fn); 822 return NULL; 823 } 824 if (crit != 0) { 825 warnx("%s: RFC 5280, section 5.2.3: " 826 "CRL Number not non-critical", fn); 827 goto out; 828 } 829 830 /* This checks that the number is non-negative and <= 20 bytes. */ 831 res = x509_convert_seqnum(fn, aint); 832 833 out: 834 ASN1_INTEGER_free(aint); 835 return res; 836 } 837 838 /* 839 * Convert passed ASN1_TIME to time_t *t. 840 * Returns 1 on success and 0 on failure. 841 */ 842 int 843 x509_get_time(const ASN1_TIME *at, time_t *t) 844 { 845 struct tm tm; 846 847 *t = 0; 848 memset(&tm, 0, sizeof(tm)); 849 /* Fail instead of silently falling back to the current time. */ 850 if (at == NULL) 851 return 0; 852 if (!ASN1_TIME_to_tm(at, &tm)) 853 return 0; 854 if ((*t = timegm(&tm)) == -1) 855 errx(1, "timegm failed"); 856 return 1; 857 } 858 859 /* 860 * Extract and validate an accessLocation, RFC 6487, 4.8 and RFC 8182, 3.2. 861 * Returns 0 on failure and 1 on success. 862 */ 863 int 864 x509_location(const char *fn, const char *descr, const char *proto, 865 GENERAL_NAME *location, char **out) 866 { 867 ASN1_IA5STRING *uri; 868 869 if (location->type != GEN_URI) { 870 warnx("%s: RFC 6487 section 4.8: %s not URI", fn, descr); 871 return 0; 872 } 873 874 uri = location->d.uniformResourceIdentifier; 875 876 if (!valid_uri(uri->data, uri->length, proto)) { 877 warnx("%s: RFC 6487 section 4.8: %s bad location", fn, descr); 878 return 0; 879 } 880 881 if (*out != NULL) { 882 warnx("%s: RFC 6487 section 4.8: multiple %s specified, " 883 "using the first one", fn, descr); 884 return 1; 885 } 886 887 if ((*out = strndup(uri->data, uri->length)) == NULL) 888 err(1, NULL); 889 890 return 1; 891 } 892 893 /* 894 * Check that the subject only contains commonName and serialNumber. 895 * Return 0 on failure. 896 */ 897 int 898 x509_valid_subject(const char *fn, const X509 *x) 899 { 900 const X509_NAME *xn; 901 const X509_NAME_ENTRY *ne; 902 const ASN1_OBJECT *ao; 903 const ASN1_STRING *as; 904 int cn = 0, sn = 0; 905 int i, nid; 906 907 if ((xn = X509_get_subject_name(x)) == NULL) { 908 warnx("%s: X509_get_subject_name", fn); 909 return 0; 910 } 911 912 for (i = 0; i < X509_NAME_entry_count(xn); i++) { 913 if ((ne = X509_NAME_get_entry(xn, i)) == NULL) { 914 warnx("%s: X509_NAME_get_entry", fn); 915 return 0; 916 } 917 if ((ao = X509_NAME_ENTRY_get_object(ne)) == NULL) { 918 warnx("%s: X509_NAME_ENTRY_get_object", fn); 919 return 0; 920 } 921 922 nid = OBJ_obj2nid(ao); 923 switch (nid) { 924 case NID_commonName: 925 if (cn++ > 0) { 926 warnx("%s: duplicate commonName in subject", 927 fn); 928 return 0; 929 } 930 if ((as = X509_NAME_ENTRY_get_data(ne)) == NULL) { 931 warnx("%s: X509_NAME_ENTRY_get_data failed", 932 fn); 933 return 0; 934 } 935 /* 936 * The following check can be enabled after AFRINIC re-issues CA certs. 937 * https://lists.afrinic.net/pipermail/dbwg/2023-March/000436.html 938 */ 939 #if 0 940 if (ASN1_STRING_type(as) != V_ASN1_PRINTABLESTRING) { 941 warnx("%s: RFC 6487 section 4.5: commonName is" 942 " not PrintableString", fn); 943 return 0; 944 } 945 #endif 946 break; 947 case NID_serialNumber: 948 if (sn++ > 0) { 949 warnx("%s: duplicate serialNumber in subject", 950 fn); 951 return 0; 952 } 953 break; 954 case NID_undef: 955 warnx("%s: OBJ_obj2nid failed", fn); 956 return 0; 957 default: 958 warnx("%s: RFC 6487 section 4.5: unexpected attribute " 959 "%s", fn, OBJ_nid2sn(nid)); 960 return 0; 961 } 962 } 963 964 if (cn == 0) { 965 warnx("%s: RFC 6487 section 4.5: subject missing commonName", 966 fn); 967 return 0; 968 } 969 970 return 1; 971 } 972 973 /* 974 * Convert an ASN1_INTEGER into a hexstring. 975 * Returned string needs to be freed by the caller. 976 */ 977 char * 978 x509_convert_seqnum(const char *fn, const ASN1_INTEGER *i) 979 { 980 BIGNUM *seqnum = NULL; 981 char *s = NULL; 982 983 if (i == NULL) 984 goto out; 985 986 seqnum = ASN1_INTEGER_to_BN(i, NULL); 987 if (seqnum == NULL) { 988 warnx("%s: ASN1_INTEGER_to_BN error", fn); 989 goto out; 990 } 991 992 if (BN_is_negative(seqnum)) { 993 warnx("%s: %s: want positive integer, have negative.", 994 __func__, fn); 995 goto out; 996 } 997 998 if (BN_num_bytes(seqnum) > 20) { 999 warnx("%s: %s: want 20 octets or fewer, have more.", 1000 __func__, fn); 1001 goto out; 1002 } 1003 1004 s = BN_bn2hex(seqnum); 1005 if (s == NULL) 1006 warnx("%s: BN_bn2hex error", fn); 1007 1008 out: 1009 BN_free(seqnum); 1010 return s; 1011 } 1012 1013 /* 1014 * Find the closest expiry moment by walking the chain of authorities. 1015 */ 1016 time_t 1017 x509_find_expires(time_t notafter, struct auth *a, struct crl_tree *crlt) 1018 { 1019 struct crl *crl; 1020 time_t expires; 1021 1022 expires = notafter; 1023 1024 for (; a != NULL; a = a->parent) { 1025 if (expires > a->cert->notafter) 1026 expires = a->cert->notafter; 1027 crl = crl_get(crlt, a); 1028 if (crl != NULL && expires > crl->nextupdate) 1029 expires = crl->nextupdate; 1030 } 1031 1032 return expires; 1033 } 1034