1*381ee599Stb /* $OpenBSD: validate.c,v 1.78 2024/11/12 09:23:07 tb Exp $ */ 29a7e9e7fSjob /* 39a7e9e7fSjob * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 49a7e9e7fSjob * 59a7e9e7fSjob * Permission to use, copy, modify, and distribute this software for any 69a7e9e7fSjob * purpose with or without fee is hereby granted, provided that the above 79a7e9e7fSjob * copyright notice and this permission notice appear in all copies. 89a7e9e7fSjob * 99a7e9e7fSjob * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 109a7e9e7fSjob * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 119a7e9e7fSjob * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 129a7e9e7fSjob * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 139a7e9e7fSjob * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 149a7e9e7fSjob * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 159a7e9e7fSjob * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 169a7e9e7fSjob */ 179a7e9e7fSjob 189a7e9e7fSjob #include <arpa/inet.h> 199a7e9e7fSjob #include <assert.h> 208c2eb288Sclaudio #include <ctype.h> 219a7e9e7fSjob #include <err.h> 2267d45509Sclaudio #include <fcntl.h> 239a7e9e7fSjob #include <inttypes.h> 249a7e9e7fSjob #include <stdlib.h> 259a7e9e7fSjob #include <string.h> 269a7e9e7fSjob #include <unistd.h> 279a7e9e7fSjob 289a7e9e7fSjob #include "extern.h" 299a7e9e7fSjob 30c4a9443cSclaudio extern ASN1_OBJECT *certpol_oid; 31c4a9443cSclaudio 329a7e9e7fSjob /* 339a7e9e7fSjob * Walk up the chain of certificates trying to match our AS number to 349a7e9e7fSjob * one of the allocations in that chain. 35a079bbf8Sclaudio * Returns 1 if covered or 0 if not. 369a7e9e7fSjob */ 37a079bbf8Sclaudio static int 38a079bbf8Sclaudio valid_as(struct auth *a, uint32_t min, uint32_t max) 399a7e9e7fSjob { 409a7e9e7fSjob int c; 419a7e9e7fSjob 42a079bbf8Sclaudio if (a == NULL) 43a079bbf8Sclaudio return 0; 449a7e9e7fSjob 459a7e9e7fSjob /* Does this certificate cover our AS number? */ 46*381ee599Stb c = as_check_covered(min, max, a->cert->ases, a->cert->num_ases); 479a7e9e7fSjob if (c > 0) 48a079bbf8Sclaudio return 1; 4986cba02dSclaudio else if (c < 0) 5086cba02dSclaudio return 0; 519a7e9e7fSjob 5260c4be4aStb /* If it inherits, walk up the chain. */ 53335482abStb return valid_as(a->issuer, min, max); 549a7e9e7fSjob } 559a7e9e7fSjob 569a7e9e7fSjob /* 579a7e9e7fSjob * Walk up the chain of certificates (really just the last one, but in 586822deefStb * the case of inheritance, the ones before) making sure that our IP 599a7e9e7fSjob * prefix is covered in the first non-inheriting specification. 60a079bbf8Sclaudio * Returns 1 if covered or 0 if not. 619a7e9e7fSjob */ 62a079bbf8Sclaudio static int 63a079bbf8Sclaudio valid_ip(struct auth *a, enum afi afi, 64a079bbf8Sclaudio const unsigned char *min, const unsigned char *max) 659a7e9e7fSjob { 669a7e9e7fSjob int c; 679a7e9e7fSjob 68a079bbf8Sclaudio if (a == NULL) 69a079bbf8Sclaudio return 0; 709a7e9e7fSjob 719a7e9e7fSjob /* Does this certificate cover our IP prefix? */ 72*381ee599Stb c = ip_addr_check_covered(afi, min, max, a->cert->ips, 73*381ee599Stb a->cert->num_ips); 749a7e9e7fSjob if (c > 0) 75a079bbf8Sclaudio return 1; 769a7e9e7fSjob else if (c < 0) 77a079bbf8Sclaudio return 0; 789a7e9e7fSjob 7960c4be4aStb /* If it inherits, walk up the chain. */ 80335482abStb return valid_ip(a->issuer, afi, min, max); 819a7e9e7fSjob } 829a7e9e7fSjob 839a7e9e7fSjob /* 849a7e9e7fSjob * Validate a non-TA certificate: make sure its IP and AS resources are 859a7e9e7fSjob * fully covered by those in the authority key (which must exist). 86a079bbf8Sclaudio * Returns 1 if valid, 0 otherwise. 879a7e9e7fSjob */ 88a079bbf8Sclaudio int 8924069af1Sclaudio valid_cert(const char *fn, struct auth *a, const struct cert *cert) 909a7e9e7fSjob { 919a7e9e7fSjob size_t i; 929a7e9e7fSjob uint32_t min, max; 939a7e9e7fSjob 94*381ee599Stb for (i = 0; i < cert->num_ases; i++) { 95*381ee599Stb if (cert->ases[i].type == CERT_AS_INHERIT) 969a7e9e7fSjob continue; 97782a58ffSjob 98*381ee599Stb if (cert->ases[i].type == CERT_AS_ID) { 99*381ee599Stb min = cert->ases[i].id; 100*381ee599Stb max = cert->ases[i].id; 101782a58ffSjob } else { 102*381ee599Stb min = cert->ases[i].range.min; 103*381ee599Stb max = cert->ases[i].range.max; 104782a58ffSjob } 105782a58ffSjob 106a079bbf8Sclaudio if (valid_as(a, min, max)) 1079a7e9e7fSjob continue; 108782a58ffSjob 109*381ee599Stb as_warn(fn, "RFC 6487: uncovered resource", &cert->ases[i]); 110a079bbf8Sclaudio return 0; 1119a7e9e7fSjob } 1129a7e9e7fSjob 113*381ee599Stb for (i = 0; i < cert->num_ips; i++) { 114ea3b4836Sclaudio if (cert->ips[i].type == CERT_IP_INHERIT) 115ea3b4836Sclaudio continue; 116782a58ffSjob 117a079bbf8Sclaudio if (valid_ip(a, cert->ips[i].afi, cert->ips[i].min, 118a079bbf8Sclaudio cert->ips[i].max)) 1199a7e9e7fSjob continue; 120782a58ffSjob 1214032f119Stb ip_warn(fn, "RFC 6487: uncovered resource", &cert->ips[i]); 122a079bbf8Sclaudio return 0; 1239a7e9e7fSjob } 1249a7e9e7fSjob 125a079bbf8Sclaudio return 1; 1269a7e9e7fSjob } 1279a7e9e7fSjob 1289a7e9e7fSjob /* 12936f16a56Sjob * Validate our ROA: check that the prefixes (ipAddrBlocks) are contained. 130a079bbf8Sclaudio * Returns 1 if valid, 0 otherwise. 1319a7e9e7fSjob */ 132a079bbf8Sclaudio int 13399dbdb7fStb valid_roa(const char *fn, struct cert *cert, struct roa *roa) 1349a7e9e7fSjob { 1359a7e9e7fSjob size_t i; 1369a7e9e7fSjob char buf[64]; 1379a7e9e7fSjob 138*381ee599Stb for (i = 0; i < roa->num_ips; i++) { 13999dbdb7fStb if (ip_addr_check_covered(roa->ips[i].afi, roa->ips[i].min, 140*381ee599Stb roa->ips[i].max, cert->ips, cert->num_ips) > 0) 1419a7e9e7fSjob continue; 14299dbdb7fStb 14399dbdb7fStb ip_addr_print(&roa->ips[i].addr, roa->ips[i].afi, buf, 14499dbdb7fStb sizeof(buf)); 14599dbdb7fStb warnx("%s: RFC 6482: uncovered IP: %s", fn, buf); 146a079bbf8Sclaudio return 0; 1479a7e9e7fSjob } 1489a7e9e7fSjob 149a079bbf8Sclaudio return 1; 1509a7e9e7fSjob } 15167d45509Sclaudio 15267d45509Sclaudio /* 153d4be4cdeSjob * Validate our SPL: check that the asID is contained in the end-entity 154d4be4cdeSjob * certificate's resources. 155d4be4cdeSjob * Returns 1 if valid, 0 otherwise. 156d4be4cdeSjob */ 157d4be4cdeSjob int 158d4be4cdeSjob valid_spl(const char *fn, struct cert *cert, struct spl *spl) 159d4be4cdeSjob { 160*381ee599Stb if (as_check_covered(spl->asid, spl->asid, cert->ases, 161*381ee599Stb cert->num_ases) > 0) 162d4be4cdeSjob return 1; 163d4be4cdeSjob 164d4be4cdeSjob warnx("%s: SPL: uncovered ASID: %u", fn, spl->asid); 165d4be4cdeSjob 166d4be4cdeSjob return 0; 167d4be4cdeSjob } 168d4be4cdeSjob 169d4be4cdeSjob /* 17067d45509Sclaudio * Validate a file by verifying the SHA256 hash of that file. 17187c7c78dSclaudio * The file to check is passed as a file descriptor. 17287c7c78dSclaudio * Returns 1 if hash matched, 0 otherwise. Closes fd when done. 17367d45509Sclaudio */ 17467d45509Sclaudio int 17587c7c78dSclaudio valid_filehash(int fd, const char *hash, size_t hlen) 17667d45509Sclaudio { 17767d45509Sclaudio SHA256_CTX ctx; 17867d45509Sclaudio char filehash[SHA256_DIGEST_LENGTH]; 17967d45509Sclaudio char buffer[8192]; 18067d45509Sclaudio ssize_t nr; 18167d45509Sclaudio 18267d45509Sclaudio if (hlen != sizeof(filehash)) 18367d45509Sclaudio errx(1, "bad hash size"); 18467d45509Sclaudio 18587c7c78dSclaudio if (fd == -1) 18667d45509Sclaudio return 0; 18767d45509Sclaudio 18867d45509Sclaudio SHA256_Init(&ctx); 18967d45509Sclaudio while ((nr = read(fd, buffer, sizeof(buffer))) > 0) 19067d45509Sclaudio SHA256_Update(&ctx, buffer, nr); 19167d45509Sclaudio close(fd); 19267d45509Sclaudio SHA256_Final(filehash, &ctx); 19387c7c78dSclaudio 19467d45509Sclaudio if (memcmp(hash, filehash, sizeof(filehash)) != 0) 19567d45509Sclaudio return 0; 19623bc08f8Sclaudio return 1; 19723bc08f8Sclaudio } 19867d45509Sclaudio 19923bc08f8Sclaudio /* 20023bc08f8Sclaudio * Same as above but with a buffer instead of a fd. 20123bc08f8Sclaudio */ 20223bc08f8Sclaudio int 20323bc08f8Sclaudio valid_hash(unsigned char *buf, size_t len, const char *hash, size_t hlen) 20423bc08f8Sclaudio { 20523bc08f8Sclaudio char filehash[SHA256_DIGEST_LENGTH]; 20623bc08f8Sclaudio 20723bc08f8Sclaudio if (hlen != sizeof(filehash)) 20823bc08f8Sclaudio errx(1, "bad hash size"); 20923bc08f8Sclaudio 21023bc08f8Sclaudio if (buf == NULL || len == 0) 21123bc08f8Sclaudio return 0; 21223bc08f8Sclaudio 21323bc08f8Sclaudio if (!EVP_Digest(buf, len, filehash, NULL, EVP_sha256(), NULL)) 21423bc08f8Sclaudio errx(1, "EVP_Digest failed"); 21523bc08f8Sclaudio 21623bc08f8Sclaudio if (memcmp(hash, filehash, sizeof(filehash)) != 0) 21723bc08f8Sclaudio return 0; 21867d45509Sclaudio return 1; 21967d45509Sclaudio } 2208c2eb288Sclaudio 2218c2eb288Sclaudio /* 222203dfefcStb * Validate that a filename only contains characters from the POSIX portable 223203dfefcStb * filename character set [A-Za-z0-9._-], see IEEE Std 1003.1-2013, 3.278. 224203dfefcStb */ 225203dfefcStb int 226203dfefcStb valid_filename(const char *fn, size_t len) 227203dfefcStb { 228203dfefcStb const unsigned char *c; 229203dfefcStb size_t i; 230203dfefcStb 231203dfefcStb for (c = fn, i = 0; i < len; i++, c++) 232203dfefcStb if (!isalnum(*c) && *c != '-' && *c != '_' && *c != '.') 233203dfefcStb return 0; 234203dfefcStb return 1; 235203dfefcStb } 236203dfefcStb 237203dfefcStb /* 2388c2eb288Sclaudio * Validate a URI to make sure it is pure ASCII and does not point backwards 2398c2eb288Sclaudio * or doing some other silly tricks. To enforce the protocol pass either 2408c2eb288Sclaudio * https:// or rsync:// as proto, if NULL is passed no protocol is enforced. 2418c2eb288Sclaudio * Returns 1 if valid, 0 otherwise. 2428c2eb288Sclaudio */ 2438c2eb288Sclaudio int 2448c2eb288Sclaudio valid_uri(const char *uri, size_t usz, const char *proto) 2458c2eb288Sclaudio { 2468c2eb288Sclaudio size_t s; 2478c2eb288Sclaudio 2481c699626Sbeck if (usz > MAX_URI_LENGTH) 24958f449faSjob return 0; 25058f449faSjob 2518c2eb288Sclaudio for (s = 0; s < usz; s++) 2528c2eb288Sclaudio if (!isalnum((unsigned char)uri[s]) && 2538c2eb288Sclaudio !ispunct((unsigned char)uri[s])) 2548c2eb288Sclaudio return 0; 2558c2eb288Sclaudio 2568c2eb288Sclaudio if (proto != NULL) { 2578c2eb288Sclaudio s = strlen(proto); 2583b405428Stb if (s >= usz) 2593b405428Stb return 0; 2608c2eb288Sclaudio if (strncasecmp(uri, proto, s) != 0) 2618c2eb288Sclaudio return 0; 2628c2eb288Sclaudio } 2638c2eb288Sclaudio 2648c2eb288Sclaudio /* do not allow files or directories to start with a '.' */ 2658c2eb288Sclaudio if (strstr(uri, "/.") != NULL) 2668c2eb288Sclaudio return 0; 2678c2eb288Sclaudio 2688c2eb288Sclaudio return 1; 2698c2eb288Sclaudio } 27093d9375cSclaudio 27193d9375cSclaudio /* 27293d9375cSclaudio * Validate that a URI has the same host as the URI passed in proto. 27393d9375cSclaudio * Returns 1 if valid, 0 otherwise. 27493d9375cSclaudio */ 27593d9375cSclaudio int 27693d9375cSclaudio valid_origin(const char *uri, const char *proto) 27793d9375cSclaudio { 27893d9375cSclaudio const char *to; 27993d9375cSclaudio 28093d9375cSclaudio /* extract end of host from proto URI */ 28193d9375cSclaudio to = strstr(proto, "://"); 28293d9375cSclaudio if (to == NULL) 28393d9375cSclaudio return 0; 28493d9375cSclaudio to += strlen("://"); 28593d9375cSclaudio if ((to = strchr(to, '/')) == NULL) 28693d9375cSclaudio return 0; 28793d9375cSclaudio 28893d9375cSclaudio /* compare hosts including the / for the start of the path section */ 28993d9375cSclaudio if (strncasecmp(uri, proto, to - proto + 1) != 0) 29093d9375cSclaudio return 0; 29193d9375cSclaudio 29293d9375cSclaudio return 1; 29393d9375cSclaudio } 294c4a9443cSclaudio 295c4a9443cSclaudio /* 296967224c8Stb * Walk the tree of known valid CA certificates until we find a certificate that 297967224c8Stb * doesn't inherit. Build a chain of intermediates and use the non-inheriting 298967224c8Stb * certificate as a trusted root by virtue of X509_V_FLAG_PARTIAL_CHAIN. The 299967224c8Stb * RFC 3779 path validation needs a non-inheriting trust root to ensure that 300967224c8Stb * all delegated resources are covered. 301c4a9443cSclaudio */ 302c4a9443cSclaudio static void 303967224c8Stb build_chain(const struct auth *a, STACK_OF(X509) **intermediates, 304967224c8Stb STACK_OF(X509) **root) 305c4a9443cSclaudio { 306967224c8Stb *intermediates = NULL; 307967224c8Stb *root = NULL; 308c4a9443cSclaudio 309f8e924bcStb /* XXX - this should be removed, but filemode relies on it. */ 310f8e924bcStb if (a == NULL) 311f8e924bcStb return; 312f8e924bcStb 313967224c8Stb if ((*intermediates = sk_X509_new_null()) == NULL) 314967224c8Stb err(1, "sk_X509_new_null"); 315967224c8Stb if ((*root = sk_X509_new_null()) == NULL) 316c4a9443cSclaudio err(1, "sk_X509_new_null"); 317335482abStb for (; a != NULL; a = a->issuer) { 318c4a9443cSclaudio assert(a->cert->x509 != NULL); 319967224c8Stb if (!a->any_inherits) { 320967224c8Stb if (!sk_X509_push(*root, a->cert->x509)) 321967224c8Stb errx(1, "sk_X509_push"); 322967224c8Stb break; 323967224c8Stb } 324967224c8Stb if (!sk_X509_push(*intermediates, a->cert->x509)) 325c4a9443cSclaudio errx(1, "sk_X509_push"); 326c4a9443cSclaudio } 327967224c8Stb assert(sk_X509_num(*root) == 1); 328c4a9443cSclaudio } 329c4a9443cSclaudio 330c4a9443cSclaudio /* 331c4a9443cSclaudio * Add the CRL based on the certs SKI value. 332c4a9443cSclaudio * No need to insert any other CRL since those were already checked. 333c4a9443cSclaudio */ 334c4a9443cSclaudio static void 335c4a9443cSclaudio build_crls(const struct crl *crl, STACK_OF(X509_CRL) **crls) 336c4a9443cSclaudio { 337c4a9443cSclaudio *crls = NULL; 338c4a9443cSclaudio 339c4a9443cSclaudio if (crl == NULL) 340c4a9443cSclaudio return; 341c4a9443cSclaudio if ((*crls = sk_X509_CRL_new_null()) == NULL) 342c4a9443cSclaudio errx(1, "sk_X509_CRL_new_null"); 343c4a9443cSclaudio if (!sk_X509_CRL_push(*crls, crl->x509_crl)) 344c4a9443cSclaudio err(1, "sk_X509_CRL_push"); 345c4a9443cSclaudio } 346c4a9443cSclaudio 347c4a9443cSclaudio /* 34865827fe9Stb * Attempt to upgrade the generic 'certificate revoked' message to include 34965827fe9Stb * a timestamp. 35065827fe9Stb */ 35165827fe9Stb static void 35265827fe9Stb pretty_revocation_time(X509 *x509, X509_CRL *crl, const char **errstr) 35365827fe9Stb { 35465827fe9Stb static char buf[64]; 35565827fe9Stb X509_REVOKED *revoked; 35665827fe9Stb const ASN1_TIME *atime; 35765827fe9Stb time_t t; 35865827fe9Stb 35965827fe9Stb if (X509_CRL_get0_by_cert(crl, &revoked, x509) != 1) 36065827fe9Stb return; 36165827fe9Stb if ((atime = X509_REVOKED_get0_revocationDate(revoked)) == NULL) 36265827fe9Stb return; 36365827fe9Stb if (!x509_get_time(atime, &t)) 36465827fe9Stb return; 36565827fe9Stb 36665827fe9Stb snprintf(buf, sizeof(buf), "certificate revoked on %s", time2str(t)); 36765827fe9Stb *errstr = buf; 36865827fe9Stb } 36965827fe9Stb 37065827fe9Stb /* 371e0cb527bSclaudio * Validate the X509 certificate. Returns 1 for valid certificates, 372e0cb527bSclaudio * returns 0 if there is a verify error and sets *errstr to the error 373e0cb527bSclaudio * returned by X509_verify_cert_error_string(). 374c4a9443cSclaudio */ 375c4a9443cSclaudio int 376c4a9443cSclaudio valid_x509(char *file, X509_STORE_CTX *store_ctx, X509 *x509, struct auth *a, 377fd7a2857Sclaudio struct crl *crl, const char **errstr) 378c4a9443cSclaudio { 379c4a9443cSclaudio X509_VERIFY_PARAM *params; 380c4a9443cSclaudio ASN1_OBJECT *cp_oid; 381967224c8Stb STACK_OF(X509) *intermediates, *root; 382c4a9443cSclaudio STACK_OF(X509_CRL) *crls = NULL; 383c4a9443cSclaudio unsigned long flags; 384fd7a2857Sclaudio int error; 385c4a9443cSclaudio 386fd7a2857Sclaudio *errstr = NULL; 387967224c8Stb build_chain(a, &intermediates, &root); 388c4a9443cSclaudio build_crls(crl, &crls); 389c4a9443cSclaudio 390c4a9443cSclaudio assert(store_ctx != NULL); 391c4a9443cSclaudio assert(x509 != NULL); 392c4a9443cSclaudio if (!X509_STORE_CTX_init(store_ctx, NULL, x509, NULL)) 393c0528901Stb err(1, "X509_STORE_CTX_init"); 394c4a9443cSclaudio 395c4a9443cSclaudio if ((params = X509_STORE_CTX_get0_param(store_ctx)) == NULL) 396c0528901Stb errx(1, "X509_STORE_CTX_get0_param"); 397c4a9443cSclaudio if ((cp_oid = OBJ_dup(certpol_oid)) == NULL) 398c0528901Stb err(1, "OBJ_dup"); 399c4a9443cSclaudio if (!X509_VERIFY_PARAM_add0_policy(params, cp_oid)) 400c0528901Stb err(1, "X509_VERIFY_PARAM_add0_policy"); 4010876134dSclaudio X509_VERIFY_PARAM_set_time(params, get_current_time()); 402c4a9443cSclaudio 403c4a9443cSclaudio flags = X509_V_FLAG_CRL_CHECK; 404967224c8Stb flags |= X509_V_FLAG_PARTIAL_CHAIN; 405bc7ae01aStb flags |= X509_V_FLAG_POLICY_CHECK; 406c4a9443cSclaudio flags |= X509_V_FLAG_EXPLICIT_POLICY; 407c4a9443cSclaudio flags |= X509_V_FLAG_INHIBIT_MAP; 408c4a9443cSclaudio X509_STORE_CTX_set_flags(store_ctx, flags); 409c4a9443cSclaudio X509_STORE_CTX_set_depth(store_ctx, MAX_CERT_DEPTH); 410967224c8Stb /* 411967224c8Stb * See the comment above build_chain() for details on what's happening 412967224c8Stb * here. The nomenclature in this API is dubious and poorly documented. 413967224c8Stb */ 414967224c8Stb X509_STORE_CTX_set0_untrusted(store_ctx, intermediates); 415967224c8Stb X509_STORE_CTX_set0_trusted_stack(store_ctx, root); 416c4a9443cSclaudio X509_STORE_CTX_set0_crls(store_ctx, crls); 417c4a9443cSclaudio 418c4a9443cSclaudio if (X509_verify_cert(store_ctx) <= 0) { 419fd7a2857Sclaudio error = X509_STORE_CTX_get_error(store_ctx); 420fd7a2857Sclaudio *errstr = X509_verify_cert_error_string(error); 42165827fe9Stb if (filemode && error == X509_V_ERR_CERT_REVOKED) 42265827fe9Stb pretty_revocation_time(x509, crl->x509_crl, errstr); 423c4a9443cSclaudio X509_STORE_CTX_cleanup(store_ctx); 424967224c8Stb sk_X509_free(intermediates); 425967224c8Stb sk_X509_free(root); 426c4a9443cSclaudio sk_X509_CRL_free(crls); 427c4a9443cSclaudio return 0; 428c4a9443cSclaudio } 429c4a9443cSclaudio 430c4a9443cSclaudio X509_STORE_CTX_cleanup(store_ctx); 431967224c8Stb sk_X509_free(intermediates); 432967224c8Stb sk_X509_free(root); 433c4a9443cSclaudio sk_X509_CRL_free(crls); 434c4a9443cSclaudio return 1; 435c4a9443cSclaudio } 436e6c729cdSjob 437e6c729cdSjob /* 438e6c729cdSjob * Validate our RSC: check that all items in the ResourceBlock are contained. 439e6c729cdSjob * Returns 1 if valid, 0 otherwise. 440e6c729cdSjob */ 441e6c729cdSjob int 44299dbdb7fStb valid_rsc(const char *fn, struct cert *cert, struct rsc *rsc) 443e6c729cdSjob { 444e6c729cdSjob size_t i; 445e6c729cdSjob uint32_t min, max; 446e6c729cdSjob 447*381ee599Stb for (i = 0; i < rsc->num_ases; i++) { 448*381ee599Stb if (rsc->ases[i].type == CERT_AS_ID) { 449*381ee599Stb min = rsc->ases[i].id; 450*381ee599Stb max = rsc->ases[i].id; 451782a58ffSjob } else { 452*381ee599Stb min = rsc->ases[i].range.min; 453*381ee599Stb max = rsc->ases[i].range.max; 454782a58ffSjob } 455e6c729cdSjob 456*381ee599Stb if (as_check_covered(min, max, cert->ases, cert->num_ases) > 0) 457e6c729cdSjob continue; 458e6c729cdSjob 459*381ee599Stb as_warn(fn, "RSC ResourceBlock uncovered", &rsc->ases[i]); 460e6c729cdSjob return 0; 461e6c729cdSjob } 462e6c729cdSjob 463*381ee599Stb for (i = 0; i < rsc->num_ips; i++) { 46499dbdb7fStb if (ip_addr_check_covered(rsc->ips[i].afi, rsc->ips[i].min, 465*381ee599Stb rsc->ips[i].max, cert->ips, cert->num_ips) > 0) 466e6c729cdSjob continue; 467e6c729cdSjob 4684032f119Stb ip_warn(fn, "RSC ResourceBlock uncovered", &rsc->ips[i]); 469e6c729cdSjob return 0; 470e6c729cdSjob } 471e6c729cdSjob 472e6c729cdSjob return 1; 473e6c729cdSjob } 4748e5b9839Stb 4758e5b9839Stb int 476e1686bd7Sjob valid_econtent_version(const char *fn, const ASN1_INTEGER *aint, 477e1686bd7Sjob uint64_t expected) 4788e5b9839Stb { 479331e816cStb uint64_t version; 4808e5b9839Stb 48176b14750Stb if (aint == NULL) { 48276b14750Stb if (expected == 0) 4838e5b9839Stb return 1; 48476b14750Stb warnx("%s: unexpected version 0", fn); 48576b14750Stb return 0; 48676b14750Stb } 4878e5b9839Stb 488331e816cStb if (!ASN1_INTEGER_get_uint64(&version, aint)) { 489331e816cStb warnx("%s: ASN1_INTEGER_get_uint64 failed", fn); 4908e5b9839Stb return 0; 4918e5b9839Stb } 4928e5b9839Stb 493e1686bd7Sjob if (version == 0) { 4948e5b9839Stb warnx("%s: incorrect encoding for version 0", fn); 4958e5b9839Stb return 0; 496e1686bd7Sjob } 497e1686bd7Sjob 498e1686bd7Sjob if (version != expected) { 499e1686bd7Sjob warnx("%s: unexpected version (expected %llu, got %llu)", fn, 500e1686bd7Sjob (unsigned long long)expected, (unsigned long long)version); 5018e5b9839Stb return 0; 5028e5b9839Stb } 503e1686bd7Sjob 504e1686bd7Sjob return 1; 5058e5b9839Stb } 506a29ddfd5Sjob 507a29ddfd5Sjob /* 508a29ddfd5Sjob * Validate the ASPA: check that the customerASID is contained. 509a29ddfd5Sjob * Returns 1 if valid, 0 otherwise. 510a29ddfd5Sjob */ 511a29ddfd5Sjob int 512a29ddfd5Sjob valid_aspa(const char *fn, struct cert *cert, struct aspa *aspa) 513a29ddfd5Sjob { 514a29ddfd5Sjob 515a29ddfd5Sjob if (as_check_covered(aspa->custasid, aspa->custasid, 516*381ee599Stb cert->ases, cert->num_ases) > 0) 517a29ddfd5Sjob return 1; 518a29ddfd5Sjob 519a29ddfd5Sjob warnx("%s: ASPA: uncovered Customer ASID: %u", fn, aspa->custasid); 520a29ddfd5Sjob 521a29ddfd5Sjob return 0; 522a29ddfd5Sjob } 523ef3f6f56Sjob 524ef3f6f56Sjob /* 525ef3f6f56Sjob * Validate Geofeed prefixes: check that the prefixes are contained. 526ef3f6f56Sjob * Returns 1 if valid, 0 otherwise. 527ef3f6f56Sjob */ 528ef3f6f56Sjob int 529ef3f6f56Sjob valid_geofeed(const char *fn, struct cert *cert, struct geofeed *g) 530ef3f6f56Sjob { 531ef3f6f56Sjob size_t i; 532ef3f6f56Sjob char buf[64]; 533ef3f6f56Sjob 534*381ee599Stb for (i = 0; i < g->num_geoips; i++) { 535ef3f6f56Sjob if (ip_addr_check_covered(g->geoips[i].ip->afi, 536ef3f6f56Sjob g->geoips[i].ip->min, g->geoips[i].ip->max, cert->ips, 537*381ee599Stb cert->num_ips) > 0) 538ef3f6f56Sjob continue; 539ef3f6f56Sjob 540ef3f6f56Sjob ip_addr_print(&g->geoips[i].ip->ip, g->geoips[i].ip->afi, buf, 541ef3f6f56Sjob sizeof(buf)); 542ef3f6f56Sjob warnx("%s: Geofeed: uncovered IP: %s", fn, buf); 543ef3f6f56Sjob return 0; 544ef3f6f56Sjob } 545ef3f6f56Sjob 546ef3f6f56Sjob return 1; 547ef3f6f56Sjob } 54845735addSclaudio 54945735addSclaudio /* 55045735addSclaudio * Validate whether a given string is a valid UUID. 55145735addSclaudio * Returns 1 if valid, 0 otherwise. 55245735addSclaudio */ 55345735addSclaudio int 55445735addSclaudio valid_uuid(const char *s) 55545735addSclaudio { 55645735addSclaudio int n = 0; 55745735addSclaudio 55845735addSclaudio while (1) { 55945735addSclaudio switch (n) { 56045735addSclaudio case 8: 56145735addSclaudio case 13: 56245735addSclaudio case 18: 56345735addSclaudio case 23: 56445735addSclaudio if (s[n] != '-') 56545735addSclaudio return 0; 56645735addSclaudio break; 56745735addSclaudio /* Check UUID is version 4 */ 56845735addSclaudio case 14: 56945735addSclaudio if (s[n] != '4') 57045735addSclaudio return 0; 57145735addSclaudio break; 57245735addSclaudio /* Check UUID variant is 1 */ 57345735addSclaudio case 19: 57445735addSclaudio if (s[n] != '8' && s[n] != '9' && s[n] != 'a' && 57545735addSclaudio s[n] != 'A' && s[n] != 'b' && s[n] != 'B') 57645735addSclaudio return 0; 57745735addSclaudio break; 57845735addSclaudio case 36: 57945735addSclaudio return s[n] == '\0'; 58045735addSclaudio default: 58145735addSclaudio if (!isxdigit((unsigned char)s[n])) 58245735addSclaudio return 0; 58345735addSclaudio break; 58445735addSclaudio } 58545735addSclaudio n++; 58645735addSclaudio } 58745735addSclaudio } 58845735addSclaudio 589ec1cc732Sjob static int 590ec1cc732Sjob valid_ca_pkey_rsa(const char *fn, EVP_PKEY *pkey) 591ae36eebeSjob { 5929a67f0c9Stb const RSA *rsa; 593ae36eebeSjob const BIGNUM *rsa_e; 594ae36eebeSjob int key_bits; 595ae36eebeSjob 596ae36eebeSjob if ((key_bits = EVP_PKEY_bits(pkey)) != 2048) { 597ae36eebeSjob warnx("%s: RFC 7935: expected 2048-bit modulus, got %d bits", 598ae36eebeSjob fn, key_bits); 599ae36eebeSjob return 0; 600ae36eebeSjob } 601ae36eebeSjob 602ae36eebeSjob if ((rsa = EVP_PKEY_get0_RSA(pkey)) == NULL) { 603ae36eebeSjob warnx("%s: failed to extract RSA public key", fn); 604ae36eebeSjob return 0; 605ae36eebeSjob } 606ae36eebeSjob 607ae36eebeSjob if ((rsa_e = RSA_get0_e(rsa)) == NULL) { 608ae36eebeSjob warnx("%s: failed to get RSA exponent", fn); 609ae36eebeSjob return 0; 610ae36eebeSjob } 611ae36eebeSjob 612ae36eebeSjob if (!BN_is_word(rsa_e, 65537)) { 613ae36eebeSjob warnx("%s: incorrect exponent (e) in RSA public key", fn); 614ae36eebeSjob return 0; 615ae36eebeSjob } 616ae36eebeSjob 617ae36eebeSjob return 1; 618ae36eebeSjob } 619ec1cc732Sjob 620ec1cc732Sjob static int 621ec1cc732Sjob valid_ca_pkey_ec(const char *fn, EVP_PKEY *pkey) 622ec1cc732Sjob { 6239a67f0c9Stb const EC_KEY *ec; 624ec1cc732Sjob const EC_GROUP *group; 625ec1cc732Sjob int nid; 626ec1cc732Sjob const char *cname; 627ec1cc732Sjob 628ec1cc732Sjob if ((ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL) { 629ec1cc732Sjob warnx("%s: failed to extract ECDSA public key", fn); 630ec1cc732Sjob return 0; 631ec1cc732Sjob } 632ec1cc732Sjob 633ec1cc732Sjob if ((group = EC_KEY_get0_group(ec)) == NULL) { 634ec1cc732Sjob warnx("%s: EC_KEY_get0_group failed", fn); 635ec1cc732Sjob return 0; 636ec1cc732Sjob } 637ec1cc732Sjob 638ec1cc732Sjob nid = EC_GROUP_get_curve_name(group); 639ec1cc732Sjob if (nid != NID_X9_62_prime256v1) { 640ec1cc732Sjob if ((cname = EC_curve_nid2nist(nid)) == NULL) 64178de3577Stb cname = nid2str(nid); 642ec1cc732Sjob warnx("%s: Expected P-256, got %s", fn, cname); 643ec1cc732Sjob return 0; 644ec1cc732Sjob } 645ec1cc732Sjob 646ec1cc732Sjob if (!EC_KEY_check_key(ec)) { 647ec1cc732Sjob warnx("%s: EC_KEY_check_key failed", fn); 648ec1cc732Sjob return 0; 649ec1cc732Sjob } 650ec1cc732Sjob 651ec1cc732Sjob return 1; 652ec1cc732Sjob } 653ec1cc732Sjob 654ec1cc732Sjob int 655ec1cc732Sjob valid_ca_pkey(const char *fn, EVP_PKEY *pkey) 656ec1cc732Sjob { 657ec1cc732Sjob if (pkey == NULL) { 658ec1cc732Sjob warnx("%s: failure, pkey is NULL", fn); 659ec1cc732Sjob return 0; 660ec1cc732Sjob } 661ec1cc732Sjob 662ec1cc732Sjob if (EVP_PKEY_base_id(pkey) == EVP_PKEY_RSA) 663ec1cc732Sjob return valid_ca_pkey_rsa(fn, pkey); 664ec1cc732Sjob 665ec1cc732Sjob if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC) 666ec1cc732Sjob return valid_ca_pkey_ec(fn, pkey); 667ec1cc732Sjob 668ec1cc732Sjob warnx("%s: unsupported public key algorithm", fn); 669ec1cc732Sjob return 0; 670ec1cc732Sjob } 671