1 /* $OpenBSD: validate.c,v 1.78 2024/11/12 09:23:07 tb Exp $ */ 2 /* 3 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 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 <arpa/inet.h> 19 #include <assert.h> 20 #include <ctype.h> 21 #include <err.h> 22 #include <fcntl.h> 23 #include <inttypes.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <unistd.h> 27 28 #include "extern.h" 29 30 extern ASN1_OBJECT *certpol_oid; 31 32 /* 33 * Walk up the chain of certificates trying to match our AS number to 34 * one of the allocations in that chain. 35 * Returns 1 if covered or 0 if not. 36 */ 37 static int 38 valid_as(struct auth *a, uint32_t min, uint32_t max) 39 { 40 int c; 41 42 if (a == NULL) 43 return 0; 44 45 /* Does this certificate cover our AS number? */ 46 c = as_check_covered(min, max, a->cert->ases, a->cert->num_ases); 47 if (c > 0) 48 return 1; 49 else if (c < 0) 50 return 0; 51 52 /* If it inherits, walk up the chain. */ 53 return valid_as(a->issuer, min, max); 54 } 55 56 /* 57 * Walk up the chain of certificates (really just the last one, but in 58 * the case of inheritance, the ones before) making sure that our IP 59 * prefix is covered in the first non-inheriting specification. 60 * Returns 1 if covered or 0 if not. 61 */ 62 static int 63 valid_ip(struct auth *a, enum afi afi, 64 const unsigned char *min, const unsigned char *max) 65 { 66 int c; 67 68 if (a == NULL) 69 return 0; 70 71 /* Does this certificate cover our IP prefix? */ 72 c = ip_addr_check_covered(afi, min, max, a->cert->ips, 73 a->cert->num_ips); 74 if (c > 0) 75 return 1; 76 else if (c < 0) 77 return 0; 78 79 /* If it inherits, walk up the chain. */ 80 return valid_ip(a->issuer, afi, min, max); 81 } 82 83 /* 84 * Validate a non-TA certificate: make sure its IP and AS resources are 85 * fully covered by those in the authority key (which must exist). 86 * Returns 1 if valid, 0 otherwise. 87 */ 88 int 89 valid_cert(const char *fn, struct auth *a, const struct cert *cert) 90 { 91 size_t i; 92 uint32_t min, max; 93 94 for (i = 0; i < cert->num_ases; i++) { 95 if (cert->ases[i].type == CERT_AS_INHERIT) 96 continue; 97 98 if (cert->ases[i].type == CERT_AS_ID) { 99 min = cert->ases[i].id; 100 max = cert->ases[i].id; 101 } else { 102 min = cert->ases[i].range.min; 103 max = cert->ases[i].range.max; 104 } 105 106 if (valid_as(a, min, max)) 107 continue; 108 109 as_warn(fn, "RFC 6487: uncovered resource", &cert->ases[i]); 110 return 0; 111 } 112 113 for (i = 0; i < cert->num_ips; i++) { 114 if (cert->ips[i].type == CERT_IP_INHERIT) 115 continue; 116 117 if (valid_ip(a, cert->ips[i].afi, cert->ips[i].min, 118 cert->ips[i].max)) 119 continue; 120 121 ip_warn(fn, "RFC 6487: uncovered resource", &cert->ips[i]); 122 return 0; 123 } 124 125 return 1; 126 } 127 128 /* 129 * Validate our ROA: check that the prefixes (ipAddrBlocks) are contained. 130 * Returns 1 if valid, 0 otherwise. 131 */ 132 int 133 valid_roa(const char *fn, struct cert *cert, struct roa *roa) 134 { 135 size_t i; 136 char buf[64]; 137 138 for (i = 0; i < roa->num_ips; i++) { 139 if (ip_addr_check_covered(roa->ips[i].afi, roa->ips[i].min, 140 roa->ips[i].max, cert->ips, cert->num_ips) > 0) 141 continue; 142 143 ip_addr_print(&roa->ips[i].addr, roa->ips[i].afi, buf, 144 sizeof(buf)); 145 warnx("%s: RFC 6482: uncovered IP: %s", fn, buf); 146 return 0; 147 } 148 149 return 1; 150 } 151 152 /* 153 * Validate our SPL: check that the asID is contained in the end-entity 154 * certificate's resources. 155 * Returns 1 if valid, 0 otherwise. 156 */ 157 int 158 valid_spl(const char *fn, struct cert *cert, struct spl *spl) 159 { 160 if (as_check_covered(spl->asid, spl->asid, cert->ases, 161 cert->num_ases) > 0) 162 return 1; 163 164 warnx("%s: SPL: uncovered ASID: %u", fn, spl->asid); 165 166 return 0; 167 } 168 169 /* 170 * Validate a file by verifying the SHA256 hash of that file. 171 * The file to check is passed as a file descriptor. 172 * Returns 1 if hash matched, 0 otherwise. Closes fd when done. 173 */ 174 int 175 valid_filehash(int fd, const char *hash, size_t hlen) 176 { 177 SHA256_CTX ctx; 178 char filehash[SHA256_DIGEST_LENGTH]; 179 char buffer[8192]; 180 ssize_t nr; 181 182 if (hlen != sizeof(filehash)) 183 errx(1, "bad hash size"); 184 185 if (fd == -1) 186 return 0; 187 188 SHA256_Init(&ctx); 189 while ((nr = read(fd, buffer, sizeof(buffer))) > 0) 190 SHA256_Update(&ctx, buffer, nr); 191 close(fd); 192 SHA256_Final(filehash, &ctx); 193 194 if (memcmp(hash, filehash, sizeof(filehash)) != 0) 195 return 0; 196 return 1; 197 } 198 199 /* 200 * Same as above but with a buffer instead of a fd. 201 */ 202 int 203 valid_hash(unsigned char *buf, size_t len, const char *hash, size_t hlen) 204 { 205 char filehash[SHA256_DIGEST_LENGTH]; 206 207 if (hlen != sizeof(filehash)) 208 errx(1, "bad hash size"); 209 210 if (buf == NULL || len == 0) 211 return 0; 212 213 if (!EVP_Digest(buf, len, filehash, NULL, EVP_sha256(), NULL)) 214 errx(1, "EVP_Digest failed"); 215 216 if (memcmp(hash, filehash, sizeof(filehash)) != 0) 217 return 0; 218 return 1; 219 } 220 221 /* 222 * Validate that a filename only contains characters from the POSIX portable 223 * filename character set [A-Za-z0-9._-], see IEEE Std 1003.1-2013, 3.278. 224 */ 225 int 226 valid_filename(const char *fn, size_t len) 227 { 228 const unsigned char *c; 229 size_t i; 230 231 for (c = fn, i = 0; i < len; i++, c++) 232 if (!isalnum(*c) && *c != '-' && *c != '_' && *c != '.') 233 return 0; 234 return 1; 235 } 236 237 /* 238 * Validate a URI to make sure it is pure ASCII and does not point backwards 239 * or doing some other silly tricks. To enforce the protocol pass either 240 * https:// or rsync:// as proto, if NULL is passed no protocol is enforced. 241 * Returns 1 if valid, 0 otherwise. 242 */ 243 int 244 valid_uri(const char *uri, size_t usz, const char *proto) 245 { 246 size_t s; 247 248 if (usz > MAX_URI_LENGTH) 249 return 0; 250 251 for (s = 0; s < usz; s++) 252 if (!isalnum((unsigned char)uri[s]) && 253 !ispunct((unsigned char)uri[s])) 254 return 0; 255 256 if (proto != NULL) { 257 s = strlen(proto); 258 if (s >= usz) 259 return 0; 260 if (strncasecmp(uri, proto, s) != 0) 261 return 0; 262 } 263 264 /* do not allow files or directories to start with a '.' */ 265 if (strstr(uri, "/.") != NULL) 266 return 0; 267 268 return 1; 269 } 270 271 /* 272 * Validate that a URI has the same host as the URI passed in proto. 273 * Returns 1 if valid, 0 otherwise. 274 */ 275 int 276 valid_origin(const char *uri, const char *proto) 277 { 278 const char *to; 279 280 /* extract end of host from proto URI */ 281 to = strstr(proto, "://"); 282 if (to == NULL) 283 return 0; 284 to += strlen("://"); 285 if ((to = strchr(to, '/')) == NULL) 286 return 0; 287 288 /* compare hosts including the / for the start of the path section */ 289 if (strncasecmp(uri, proto, to - proto + 1) != 0) 290 return 0; 291 292 return 1; 293 } 294 295 /* 296 * Walk the tree of known valid CA certificates until we find a certificate that 297 * doesn't inherit. Build a chain of intermediates and use the non-inheriting 298 * certificate as a trusted root by virtue of X509_V_FLAG_PARTIAL_CHAIN. The 299 * RFC 3779 path validation needs a non-inheriting trust root to ensure that 300 * all delegated resources are covered. 301 */ 302 static void 303 build_chain(const struct auth *a, STACK_OF(X509) **intermediates, 304 STACK_OF(X509) **root) 305 { 306 *intermediates = NULL; 307 *root = NULL; 308 309 /* XXX - this should be removed, but filemode relies on it. */ 310 if (a == NULL) 311 return; 312 313 if ((*intermediates = sk_X509_new_null()) == NULL) 314 err(1, "sk_X509_new_null"); 315 if ((*root = sk_X509_new_null()) == NULL) 316 err(1, "sk_X509_new_null"); 317 for (; a != NULL; a = a->issuer) { 318 assert(a->cert->x509 != NULL); 319 if (!a->any_inherits) { 320 if (!sk_X509_push(*root, a->cert->x509)) 321 errx(1, "sk_X509_push"); 322 break; 323 } 324 if (!sk_X509_push(*intermediates, a->cert->x509)) 325 errx(1, "sk_X509_push"); 326 } 327 assert(sk_X509_num(*root) == 1); 328 } 329 330 /* 331 * Add the CRL based on the certs SKI value. 332 * No need to insert any other CRL since those were already checked. 333 */ 334 static void 335 build_crls(const struct crl *crl, STACK_OF(X509_CRL) **crls) 336 { 337 *crls = NULL; 338 339 if (crl == NULL) 340 return; 341 if ((*crls = sk_X509_CRL_new_null()) == NULL) 342 errx(1, "sk_X509_CRL_new_null"); 343 if (!sk_X509_CRL_push(*crls, crl->x509_crl)) 344 err(1, "sk_X509_CRL_push"); 345 } 346 347 /* 348 * Attempt to upgrade the generic 'certificate revoked' message to include 349 * a timestamp. 350 */ 351 static void 352 pretty_revocation_time(X509 *x509, X509_CRL *crl, const char **errstr) 353 { 354 static char buf[64]; 355 X509_REVOKED *revoked; 356 const ASN1_TIME *atime; 357 time_t t; 358 359 if (X509_CRL_get0_by_cert(crl, &revoked, x509) != 1) 360 return; 361 if ((atime = X509_REVOKED_get0_revocationDate(revoked)) == NULL) 362 return; 363 if (!x509_get_time(atime, &t)) 364 return; 365 366 snprintf(buf, sizeof(buf), "certificate revoked on %s", time2str(t)); 367 *errstr = buf; 368 } 369 370 /* 371 * Validate the X509 certificate. Returns 1 for valid certificates, 372 * returns 0 if there is a verify error and sets *errstr to the error 373 * returned by X509_verify_cert_error_string(). 374 */ 375 int 376 valid_x509(char *file, X509_STORE_CTX *store_ctx, X509 *x509, struct auth *a, 377 struct crl *crl, const char **errstr) 378 { 379 X509_VERIFY_PARAM *params; 380 ASN1_OBJECT *cp_oid; 381 STACK_OF(X509) *intermediates, *root; 382 STACK_OF(X509_CRL) *crls = NULL; 383 unsigned long flags; 384 int error; 385 386 *errstr = NULL; 387 build_chain(a, &intermediates, &root); 388 build_crls(crl, &crls); 389 390 assert(store_ctx != NULL); 391 assert(x509 != NULL); 392 if (!X509_STORE_CTX_init(store_ctx, NULL, x509, NULL)) 393 err(1, "X509_STORE_CTX_init"); 394 395 if ((params = X509_STORE_CTX_get0_param(store_ctx)) == NULL) 396 errx(1, "X509_STORE_CTX_get0_param"); 397 if ((cp_oid = OBJ_dup(certpol_oid)) == NULL) 398 err(1, "OBJ_dup"); 399 if (!X509_VERIFY_PARAM_add0_policy(params, cp_oid)) 400 err(1, "X509_VERIFY_PARAM_add0_policy"); 401 X509_VERIFY_PARAM_set_time(params, get_current_time()); 402 403 flags = X509_V_FLAG_CRL_CHECK; 404 flags |= X509_V_FLAG_PARTIAL_CHAIN; 405 flags |= X509_V_FLAG_POLICY_CHECK; 406 flags |= X509_V_FLAG_EXPLICIT_POLICY; 407 flags |= X509_V_FLAG_INHIBIT_MAP; 408 X509_STORE_CTX_set_flags(store_ctx, flags); 409 X509_STORE_CTX_set_depth(store_ctx, MAX_CERT_DEPTH); 410 /* 411 * See the comment above build_chain() for details on what's happening 412 * here. The nomenclature in this API is dubious and poorly documented. 413 */ 414 X509_STORE_CTX_set0_untrusted(store_ctx, intermediates); 415 X509_STORE_CTX_set0_trusted_stack(store_ctx, root); 416 X509_STORE_CTX_set0_crls(store_ctx, crls); 417 418 if (X509_verify_cert(store_ctx) <= 0) { 419 error = X509_STORE_CTX_get_error(store_ctx); 420 *errstr = X509_verify_cert_error_string(error); 421 if (filemode && error == X509_V_ERR_CERT_REVOKED) 422 pretty_revocation_time(x509, crl->x509_crl, errstr); 423 X509_STORE_CTX_cleanup(store_ctx); 424 sk_X509_free(intermediates); 425 sk_X509_free(root); 426 sk_X509_CRL_free(crls); 427 return 0; 428 } 429 430 X509_STORE_CTX_cleanup(store_ctx); 431 sk_X509_free(intermediates); 432 sk_X509_free(root); 433 sk_X509_CRL_free(crls); 434 return 1; 435 } 436 437 /* 438 * Validate our RSC: check that all items in the ResourceBlock are contained. 439 * Returns 1 if valid, 0 otherwise. 440 */ 441 int 442 valid_rsc(const char *fn, struct cert *cert, struct rsc *rsc) 443 { 444 size_t i; 445 uint32_t min, max; 446 447 for (i = 0; i < rsc->num_ases; i++) { 448 if (rsc->ases[i].type == CERT_AS_ID) { 449 min = rsc->ases[i].id; 450 max = rsc->ases[i].id; 451 } else { 452 min = rsc->ases[i].range.min; 453 max = rsc->ases[i].range.max; 454 } 455 456 if (as_check_covered(min, max, cert->ases, cert->num_ases) > 0) 457 continue; 458 459 as_warn(fn, "RSC ResourceBlock uncovered", &rsc->ases[i]); 460 return 0; 461 } 462 463 for (i = 0; i < rsc->num_ips; i++) { 464 if (ip_addr_check_covered(rsc->ips[i].afi, rsc->ips[i].min, 465 rsc->ips[i].max, cert->ips, cert->num_ips) > 0) 466 continue; 467 468 ip_warn(fn, "RSC ResourceBlock uncovered", &rsc->ips[i]); 469 return 0; 470 } 471 472 return 1; 473 } 474 475 int 476 valid_econtent_version(const char *fn, const ASN1_INTEGER *aint, 477 uint64_t expected) 478 { 479 uint64_t version; 480 481 if (aint == NULL) { 482 if (expected == 0) 483 return 1; 484 warnx("%s: unexpected version 0", fn); 485 return 0; 486 } 487 488 if (!ASN1_INTEGER_get_uint64(&version, aint)) { 489 warnx("%s: ASN1_INTEGER_get_uint64 failed", fn); 490 return 0; 491 } 492 493 if (version == 0) { 494 warnx("%s: incorrect encoding for version 0", fn); 495 return 0; 496 } 497 498 if (version != expected) { 499 warnx("%s: unexpected version (expected %llu, got %llu)", fn, 500 (unsigned long long)expected, (unsigned long long)version); 501 return 0; 502 } 503 504 return 1; 505 } 506 507 /* 508 * Validate the ASPA: check that the customerASID is contained. 509 * Returns 1 if valid, 0 otherwise. 510 */ 511 int 512 valid_aspa(const char *fn, struct cert *cert, struct aspa *aspa) 513 { 514 515 if (as_check_covered(aspa->custasid, aspa->custasid, 516 cert->ases, cert->num_ases) > 0) 517 return 1; 518 519 warnx("%s: ASPA: uncovered Customer ASID: %u", fn, aspa->custasid); 520 521 return 0; 522 } 523 524 /* 525 * Validate Geofeed prefixes: check that the prefixes are contained. 526 * Returns 1 if valid, 0 otherwise. 527 */ 528 int 529 valid_geofeed(const char *fn, struct cert *cert, struct geofeed *g) 530 { 531 size_t i; 532 char buf[64]; 533 534 for (i = 0; i < g->num_geoips; i++) { 535 if (ip_addr_check_covered(g->geoips[i].ip->afi, 536 g->geoips[i].ip->min, g->geoips[i].ip->max, cert->ips, 537 cert->num_ips) > 0) 538 continue; 539 540 ip_addr_print(&g->geoips[i].ip->ip, g->geoips[i].ip->afi, buf, 541 sizeof(buf)); 542 warnx("%s: Geofeed: uncovered IP: %s", fn, buf); 543 return 0; 544 } 545 546 return 1; 547 } 548 549 /* 550 * Validate whether a given string is a valid UUID. 551 * Returns 1 if valid, 0 otherwise. 552 */ 553 int 554 valid_uuid(const char *s) 555 { 556 int n = 0; 557 558 while (1) { 559 switch (n) { 560 case 8: 561 case 13: 562 case 18: 563 case 23: 564 if (s[n] != '-') 565 return 0; 566 break; 567 /* Check UUID is version 4 */ 568 case 14: 569 if (s[n] != '4') 570 return 0; 571 break; 572 /* Check UUID variant is 1 */ 573 case 19: 574 if (s[n] != '8' && s[n] != '9' && s[n] != 'a' && 575 s[n] != 'A' && s[n] != 'b' && s[n] != 'B') 576 return 0; 577 break; 578 case 36: 579 return s[n] == '\0'; 580 default: 581 if (!isxdigit((unsigned char)s[n])) 582 return 0; 583 break; 584 } 585 n++; 586 } 587 } 588 589 static int 590 valid_ca_pkey_rsa(const char *fn, EVP_PKEY *pkey) 591 { 592 const RSA *rsa; 593 const BIGNUM *rsa_e; 594 int key_bits; 595 596 if ((key_bits = EVP_PKEY_bits(pkey)) != 2048) { 597 warnx("%s: RFC 7935: expected 2048-bit modulus, got %d bits", 598 fn, key_bits); 599 return 0; 600 } 601 602 if ((rsa = EVP_PKEY_get0_RSA(pkey)) == NULL) { 603 warnx("%s: failed to extract RSA public key", fn); 604 return 0; 605 } 606 607 if ((rsa_e = RSA_get0_e(rsa)) == NULL) { 608 warnx("%s: failed to get RSA exponent", fn); 609 return 0; 610 } 611 612 if (!BN_is_word(rsa_e, 65537)) { 613 warnx("%s: incorrect exponent (e) in RSA public key", fn); 614 return 0; 615 } 616 617 return 1; 618 } 619 620 static int 621 valid_ca_pkey_ec(const char *fn, EVP_PKEY *pkey) 622 { 623 const EC_KEY *ec; 624 const EC_GROUP *group; 625 int nid; 626 const char *cname; 627 628 if ((ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL) { 629 warnx("%s: failed to extract ECDSA public key", fn); 630 return 0; 631 } 632 633 if ((group = EC_KEY_get0_group(ec)) == NULL) { 634 warnx("%s: EC_KEY_get0_group failed", fn); 635 return 0; 636 } 637 638 nid = EC_GROUP_get_curve_name(group); 639 if (nid != NID_X9_62_prime256v1) { 640 if ((cname = EC_curve_nid2nist(nid)) == NULL) 641 cname = nid2str(nid); 642 warnx("%s: Expected P-256, got %s", fn, cname); 643 return 0; 644 } 645 646 if (!EC_KEY_check_key(ec)) { 647 warnx("%s: EC_KEY_check_key failed", fn); 648 return 0; 649 } 650 651 return 1; 652 } 653 654 int 655 valid_ca_pkey(const char *fn, EVP_PKEY *pkey) 656 { 657 if (pkey == NULL) { 658 warnx("%s: failure, pkey is NULL", fn); 659 return 0; 660 } 661 662 if (EVP_PKEY_base_id(pkey) == EVP_PKEY_RSA) 663 return valid_ca_pkey_rsa(fn, pkey); 664 665 if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC) 666 return valid_ca_pkey_ec(fn, pkey); 667 668 warnx("%s: unsupported public key algorithm", fn); 669 return 0; 670 } 671