1*39b34cbdStb /* $OpenBSD: filemode.c,v 1.57 2024/12/16 13:53:37 tb Exp $ */ 2c4a9443cSclaudio /* 3c4a9443cSclaudio * Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org> 4c4a9443cSclaudio * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 5c4a9443cSclaudio * 6c4a9443cSclaudio * Permission to use, copy, modify, and distribute this software for any 7c4a9443cSclaudio * purpose with or without fee is hereby granted, provided that the above 8c4a9443cSclaudio * copyright notice and this permission notice appear in all copies. 9c4a9443cSclaudio * 10c4a9443cSclaudio * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11c4a9443cSclaudio * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12c4a9443cSclaudio * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13c4a9443cSclaudio * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14c4a9443cSclaudio * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15c4a9443cSclaudio * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16c4a9443cSclaudio * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17c4a9443cSclaudio */ 18c4a9443cSclaudio 19c4a9443cSclaudio #include <sys/queue.h> 20c4a9443cSclaudio #include <sys/tree.h> 21c4a9443cSclaudio #include <sys/types.h> 22c4a9443cSclaudio 23c4a9443cSclaudio #include <assert.h> 24c4a9443cSclaudio #include <err.h> 25c4a9443cSclaudio #include <fcntl.h> 26c4a9443cSclaudio #include <poll.h> 27c4a9443cSclaudio #include <stdio.h> 28c4a9443cSclaudio #include <stdlib.h> 29c4a9443cSclaudio #include <string.h> 30c4a9443cSclaudio #include <limits.h> 31c4a9443cSclaudio #include <unistd.h> 32c4a9443cSclaudio #include <imsg.h> 33c4a9443cSclaudio 34c4a9443cSclaudio #include <openssl/asn1.h> 35c4a9443cSclaudio #include <openssl/err.h> 36c4a9443cSclaudio #include <openssl/evp.h> 37d61b9ff1Sjob #include <openssl/pem.h> 38c4a9443cSclaudio #include <openssl/x509.h> 39c4a9443cSclaudio #include <openssl/x509v3.h> 40c4a9443cSclaudio 41c4a9443cSclaudio #include "extern.h" 427aabcda0Sclaudio #include "json.h" 43c4a9443cSclaudio 44318f0572Sjob extern BN_CTX *bn_ctx; 45318f0572Sjob 46c4a9443cSclaudio static X509_STORE_CTX *ctx; 47c4a9443cSclaudio static struct auth_tree auths = RB_INITIALIZER(&auths); 48c4a9443cSclaudio static struct crl_tree crlt = RB_INITIALIZER(&crlt); 49c4a9443cSclaudio 50c4a9443cSclaudio struct tal *talobj[TALSZ_MAX]; 51c4a9443cSclaudio 520bc420b9Sclaudio struct uripath { 530bc420b9Sclaudio RB_ENTRY(uripath) entry; 540bc420b9Sclaudio const char *uri; 550bc420b9Sclaudio struct cert *cert; 560bc420b9Sclaudio }; 570bc420b9Sclaudio 580bc420b9Sclaudio static RB_HEAD(uripath_tree, uripath) uritree; 590bc420b9Sclaudio 600bc420b9Sclaudio static inline int 610bc420b9Sclaudio uripathcmp(const struct uripath *a, const struct uripath *b) 620bc420b9Sclaudio { 630bc420b9Sclaudio return strcmp(a->uri, b->uri); 640bc420b9Sclaudio } 650bc420b9Sclaudio 660bc420b9Sclaudio RB_PROTOTYPE(uripath_tree, uripath, entry, uripathcmp); 670bc420b9Sclaudio 680bc420b9Sclaudio static void 690bc420b9Sclaudio uripath_add(const char *uri, struct cert *cert) 700bc420b9Sclaudio { 710bc420b9Sclaudio struct uripath *up; 720bc420b9Sclaudio 730bc420b9Sclaudio if ((up = calloc(1, sizeof(*up))) == NULL) 740bc420b9Sclaudio err(1, NULL); 750bc420b9Sclaudio if ((up->uri = strdup(uri)) == NULL) 760bc420b9Sclaudio err(1, NULL); 770bc420b9Sclaudio up->cert = cert; 780bc420b9Sclaudio if (RB_INSERT(uripath_tree, &uritree, up) != NULL) 790bc420b9Sclaudio errx(1, "corrupt AIA lookup tree"); 800bc420b9Sclaudio } 810bc420b9Sclaudio 820bc420b9Sclaudio static struct cert * 830bc420b9Sclaudio uripath_lookup(const char *uri) 840bc420b9Sclaudio { 850bc420b9Sclaudio struct uripath needle = { .uri = uri }; 860bc420b9Sclaudio struct uripath *up; 870bc420b9Sclaudio 880bc420b9Sclaudio up = RB_FIND(uripath_tree, &uritree, &needle); 890bc420b9Sclaudio if (up == NULL) 900bc420b9Sclaudio return NULL; 910bc420b9Sclaudio return up->cert; 920bc420b9Sclaudio } 930bc420b9Sclaudio 940bc420b9Sclaudio RB_GENERATE(uripath_tree, uripath, entry, uripathcmp); 950bc420b9Sclaudio 96c4a9443cSclaudio /* 97c4a9443cSclaudio * Use the X509 CRL Distribution Points to locate the CRL needed for 98c4a9443cSclaudio * verification. 99c4a9443cSclaudio */ 100c4a9443cSclaudio static void 101c4a9443cSclaudio parse_load_crl(char *uri) 102c4a9443cSclaudio { 103c4a9443cSclaudio struct crl *crl; 104c4a9443cSclaudio char *f; 105c4a9443cSclaudio size_t flen; 106c4a9443cSclaudio 107c4a9443cSclaudio if (uri == NULL) 108c4a9443cSclaudio return; 1090610060dSjob if (strncmp(uri, RSYNC_PROTO, RSYNC_PROTO_LEN) != 0) { 110c4a9443cSclaudio warnx("bad CRL distribution point URI %s", uri); 111c4a9443cSclaudio return; 112c4a9443cSclaudio } 1130610060dSjob uri += RSYNC_PROTO_LEN; 114c4a9443cSclaudio 115c4a9443cSclaudio f = load_file(uri, &flen); 116c4a9443cSclaudio if (f == NULL) { 117c4a9443cSclaudio warn("parse file %s", uri); 118c4a9443cSclaudio return; 119c4a9443cSclaudio } 120c4a9443cSclaudio 121c4a9443cSclaudio crl = crl_parse(uri, f, flen); 122c4a9443cSclaudio if (crl != NULL && !crl_insert(&crlt, crl)) 123c4a9443cSclaudio crl_free(crl); 124c4a9443cSclaudio 125c4a9443cSclaudio free(f); 126c4a9443cSclaudio } 127c4a9443cSclaudio 128c4a9443cSclaudio /* 129c4a9443cSclaudio * Parse the cert pointed at by the AIA URI while doing that also load 130c4a9443cSclaudio * the CRL of this cert. While the CRL is validated the returned cert 131c4a9443cSclaudio * is not. The caller needs to make sure it is validated once all 132c4a9443cSclaudio * necessary certs were loaded. Returns NULL on failure. 133c4a9443cSclaudio */ 134c4a9443cSclaudio static struct cert * 135c4a9443cSclaudio parse_load_cert(char *uri) 136c4a9443cSclaudio { 137c4a9443cSclaudio struct cert *cert = NULL; 138c4a9443cSclaudio char *f; 139c4a9443cSclaudio size_t flen; 140c4a9443cSclaudio 141c4a9443cSclaudio if (uri == NULL) 142c4a9443cSclaudio return NULL; 143c4a9443cSclaudio 1440610060dSjob if (strncmp(uri, RSYNC_PROTO, RSYNC_PROTO_LEN) != 0) { 145c4a9443cSclaudio warnx("bad authority information access URI %s", uri); 146c4a9443cSclaudio return NULL; 147c4a9443cSclaudio } 1480610060dSjob uri += RSYNC_PROTO_LEN; 149c4a9443cSclaudio 150c4a9443cSclaudio f = load_file(uri, &flen); 151c4a9443cSclaudio if (f == NULL) { 152c4a9443cSclaudio warn("parse file %s", uri); 153c4a9443cSclaudio goto done; 154c4a9443cSclaudio } 155c4a9443cSclaudio 156c4a9443cSclaudio cert = cert_parse_pre(uri, f, flen); 157c4a9443cSclaudio free(f); 158c4a9443cSclaudio 159c4a9443cSclaudio if (cert == NULL) 160c4a9443cSclaudio goto done; 161c4a9443cSclaudio if (cert->purpose != CERT_PURPOSE_CA) { 162eb6f3761Stb warnx("AIA reference to %s in %s", 163eb6f3761Stb purpose2str(cert->purpose), uri); 164c4a9443cSclaudio goto done; 165c4a9443cSclaudio } 166c4a9443cSclaudio /* try to load the CRL of this cert */ 167c4a9443cSclaudio parse_load_crl(cert->crl); 168c4a9443cSclaudio 169c4a9443cSclaudio return cert; 170c4a9443cSclaudio 171c4a9443cSclaudio done: 172c4a9443cSclaudio cert_free(cert); 173c4a9443cSclaudio return NULL; 174c4a9443cSclaudio } 175c4a9443cSclaudio 176c4a9443cSclaudio /* 177c4a9443cSclaudio * Build the certificate chain by using the Authority Information Access. 178c4a9443cSclaudio * This requires that the TA are already validated and added to the auths 179c4a9443cSclaudio * tree. Once the TA is located in the chain the chain is validated in 180c4a9443cSclaudio * reverse order. 181c4a9443cSclaudio */ 1820bc420b9Sclaudio static struct auth * 183c4a9443cSclaudio parse_load_certchain(char *uri) 184c4a9443cSclaudio { 185ad462a11Sclaudio struct cert *stack[MAX_CERT_DEPTH] = { 0 }; 186c4a9443cSclaudio char *filestack[MAX_CERT_DEPTH]; 187c4a9443cSclaudio struct cert *cert; 188ad462a11Sclaudio struct crl *crl; 189ad462a11Sclaudio struct auth *a; 190fd7a2857Sclaudio const char *errstr; 191ad462a11Sclaudio int i; 192c4a9443cSclaudio 193c4a9443cSclaudio for (i = 0; i < MAX_CERT_DEPTH; i++) { 1940bc420b9Sclaudio if ((cert = uripath_lookup(uri)) != NULL) { 1950bc420b9Sclaudio a = auth_find(&auths, cert->certid); 1961d8c6443Stb if (a == NULL) { 1971d8c6443Stb warnx("failed to find issuer for %s", uri); 1981d8c6443Stb goto fail; 1991d8c6443Stb } 2000bc420b9Sclaudio break; 2010bc420b9Sclaudio } 202ad462a11Sclaudio filestack[i] = uri; 203ad462a11Sclaudio stack[i] = cert = parse_load_cert(uri); 204ad462a11Sclaudio if (cert == NULL || cert->purpose != CERT_PURPOSE_CA) { 2050bc420b9Sclaudio warnx("failed to build authority chain: %s", uri); 206ad462a11Sclaudio goto fail; 207c4a9443cSclaudio } 208c4a9443cSclaudio uri = cert->aia; 209c4a9443cSclaudio } 210c4a9443cSclaudio 211c4a9443cSclaudio if (i >= MAX_CERT_DEPTH) { 212c4a9443cSclaudio warnx("authority chain exceeds max depth of %d", 213c4a9443cSclaudio MAX_CERT_DEPTH); 214ad462a11Sclaudio goto fail; 215c4a9443cSclaudio } 216c4a9443cSclaudio 217c4a9443cSclaudio /* TA found play back the stack and add all certs */ 2180bc420b9Sclaudio for (; i > 0; i--) { 2190bc420b9Sclaudio cert = stack[i - 1]; 2200bc420b9Sclaudio uri = filestack[i - 1]; 221c4a9443cSclaudio 222ad462a11Sclaudio crl = crl_get(&crlt, a); 223fd7a2857Sclaudio if (!valid_x509(uri, ctx, cert->x509, a, crl, &errstr) || 224fd7a2857Sclaudio !valid_cert(uri, a, cert)) { 225fd7a2857Sclaudio if (errstr != NULL) 226fd7a2857Sclaudio warnx("%s: %s", uri, errstr); 227ad462a11Sclaudio goto fail; 228fd7a2857Sclaudio } 229ad462a11Sclaudio cert->talid = a->cert->talid; 2300bc420b9Sclaudio a = auth_insert(uri, &auths, cert, a); 2310bc420b9Sclaudio uripath_add(uri, cert); 23268dbd033Stb stack[i - 1] = NULL; 233c4a9443cSclaudio } 234ad462a11Sclaudio 2350bc420b9Sclaudio return a; 236ad462a11Sclaudio fail: 237ad462a11Sclaudio for (i = 0; i < MAX_CERT_DEPTH; i++) 238ad462a11Sclaudio cert_free(stack[i]); 2390bc420b9Sclaudio return NULL; 240c4a9443cSclaudio } 241c4a9443cSclaudio 242c4a9443cSclaudio static void 243c4a9443cSclaudio parse_load_ta(struct tal *tal) 244c4a9443cSclaudio { 245ad462a11Sclaudio const char *filename; 246ad462a11Sclaudio struct cert *cert; 247ad462a11Sclaudio unsigned char *f = NULL; 248ad462a11Sclaudio char *file; 2490bc420b9Sclaudio size_t flen, i; 250c4a9443cSclaudio 251c4a9443cSclaudio /* does not matter which URI, all end with same filename */ 252ad462a11Sclaudio filename = strrchr(tal->uri[0], '/'); 253ad462a11Sclaudio assert(filename); 254c4a9443cSclaudio 255ad462a11Sclaudio if (asprintf(&file, "ta/%s%s", tal->descr, filename) == -1) 256c4a9443cSclaudio err(1, NULL); 257c4a9443cSclaudio 258ad462a11Sclaudio f = load_file(file, &flen); 259c4a9443cSclaudio if (f == NULL) { 260ad462a11Sclaudio warn("parse file %s", file); 261ad462a11Sclaudio goto out; 262c4a9443cSclaudio } 263c4a9443cSclaudio 264ad462a11Sclaudio /* Extract certificate data. */ 265ad462a11Sclaudio cert = cert_parse_pre(file, f, flen); 266ad462a11Sclaudio cert = ta_parse(file, cert, tal->pkey, tal->pkeysz); 267ad462a11Sclaudio if (cert == NULL) 268ad462a11Sclaudio goto out; 269ad462a11Sclaudio 270ad462a11Sclaudio cert->talid = tal->id; 2710bc420b9Sclaudio auth_insert(file, &auths, cert, NULL); 27230a08502Stb for (i = 0; i < tal->num_uris; i++) { 2730bc420b9Sclaudio if (strncasecmp(tal->uri[i], RSYNC_PROTO, RSYNC_PROTO_LEN) != 0) 2740bc420b9Sclaudio continue; 2750bc420b9Sclaudio /* Add all rsync uri since any of them could be used as AIA. */ 2760bc420b9Sclaudio uripath_add(tal->uri[i], cert); 2770bc420b9Sclaudio } 278ad462a11Sclaudio 279ad462a11Sclaudio out: 280ad462a11Sclaudio free(file); 281c4a9443cSclaudio free(f); 282c4a9443cSclaudio } 283c4a9443cSclaudio 284c4a9443cSclaudio static struct tal * 285c4a9443cSclaudio find_tal(struct cert *cert) 286c4a9443cSclaudio { 287c4a9443cSclaudio EVP_PKEY *pk, *opk; 288c4a9443cSclaudio struct tal *tal; 289c4a9443cSclaudio int i; 290c4a9443cSclaudio 291c4a9443cSclaudio if ((opk = X509_get0_pubkey(cert->x509)) == NULL) 292c4a9443cSclaudio return NULL; 293c4a9443cSclaudio 294c4a9443cSclaudio for (i = 0; i < TALSZ_MAX; i++) { 295c4a9443cSclaudio const unsigned char *pkey; 296c4a9443cSclaudio 297c4a9443cSclaudio if (talobj[i] == NULL) 298c4a9443cSclaudio break; 299c4a9443cSclaudio tal = talobj[i]; 300c4a9443cSclaudio pkey = tal->pkey; 301c4a9443cSclaudio pk = d2i_PUBKEY(NULL, &pkey, tal->pkeysz); 302c4a9443cSclaudio if (pk == NULL) 303c4a9443cSclaudio continue; 304c4a9443cSclaudio if (EVP_PKEY_cmp(pk, opk) == 1) { 305c4a9443cSclaudio EVP_PKEY_free(pk); 306c4a9443cSclaudio return tal; 307c4a9443cSclaudio } 308c4a9443cSclaudio EVP_PKEY_free(pk); 309c4a9443cSclaudio } 310c4a9443cSclaudio return NULL; 311c4a9443cSclaudio } 312c4a9443cSclaudio 313bb47adf3Sjob static void 3149c1f5d6bSjob print_signature_path(const char *crl, const char *aia, const struct auth *a) 315bb47adf3Sjob { 316bb47adf3Sjob if (crl != NULL) 3179c1f5d6bSjob printf("Signature path: %s\n", crl); 318a6002f6aStb if (a != NULL && a->cert != NULL && a->cert->mft != NULL) 31984418253Sjob printf(" %s\n", a->cert->mft); 320bb47adf3Sjob if (aia != NULL) 321bb47adf3Sjob printf(" %s\n", aia); 322bb47adf3Sjob 323335482abStb for (; a != NULL; a = a->issuer) { 324bb47adf3Sjob if (a->cert->crl != NULL) 325bb47adf3Sjob printf(" %s\n", a->cert->crl); 326335482abStb if (a->issuer != NULL && a->issuer->cert != NULL && 327335482abStb a->issuer->cert->mft != NULL) 32884418253Sjob printf(" %s\n", 329335482abStb a->issuer->cert->mft); 330bb47adf3Sjob if (a->cert->aia != NULL) 331bb47adf3Sjob printf(" %s\n", a->cert->aia); 332bb47adf3Sjob } 333bb47adf3Sjob } 334bb47adf3Sjob 335c4a9443cSclaudio /* 336c4a9443cSclaudio * Parse file passed with -f option. 337c4a9443cSclaudio */ 338c4a9443cSclaudio static void 339c4a9443cSclaudio proc_parser_file(char *file, unsigned char *buf, size_t len) 340c4a9443cSclaudio { 341c4a9443cSclaudio static int num; 342c4a9443cSclaudio X509 *x509 = NULL; 3439649a735Sjob struct aspa *aspa = NULL; 344c4a9443cSclaudio struct cert *cert = NULL; 345c4a9443cSclaudio struct crl *crl = NULL; 3469649a735Sjob struct gbr *gbr = NULL; 3479649a735Sjob struct geofeed *geofeed = NULL; 348c4a9443cSclaudio struct mft *mft = NULL; 349c4a9443cSclaudio struct roa *roa = NULL; 35004834fbdSjob struct rsc *rsc = NULL; 351d4be4cdeSjob struct spl *spl = NULL; 352ee2a33daSjob struct tak *tak = NULL; 3539649a735Sjob struct tal *tal = NULL; 3540bc420b9Sclaudio char *aia = NULL; 355a6235ec6Sjob char *crl_uri = NULL; 356490bf478Stb time_t *notbefore = NULL, *expires = NULL, *notafter = NULL; 357490bf478Stb time_t now; 358a6002f6aStb struct auth *a = NULL; 359a6235ec6Sjob struct crl *c; 3607aabcda0Sclaudio const char *errstr = NULL, *valid; 361a6235ec6Sjob int status = 0; 362d0b565d2Sjob char filehash[SHA256_DIGEST_LENGTH]; 363d0b565d2Sjob char *hash; 364c4a9443cSclaudio enum rtype type; 365c4a9443cSclaudio int is_ta = 0; 366c4a9443cSclaudio 367490bf478Stb now = get_current_time(); 368490bf478Stb 3697aabcda0Sclaudio if (outformats & FORMAT_JSON) { 3707aabcda0Sclaudio json_do_start(stdout); 3717aabcda0Sclaudio } else { 3727aabcda0Sclaudio if (num++ > 0) 373c4a9443cSclaudio printf("--\n"); 374c4a9443cSclaudio } 375c4a9443cSclaudio 3760610060dSjob if (strncmp(file, RSYNC_PROTO, RSYNC_PROTO_LEN) == 0) { 3770610060dSjob file += RSYNC_PROTO_LEN; 378c4a9443cSclaudio buf = load_file(file, &len); 379c4a9443cSclaudio if (buf == NULL) { 380c4a9443cSclaudio warn("parse file %s", file); 381c4a9443cSclaudio return; 382c4a9443cSclaudio } 383c4a9443cSclaudio } 384c4a9443cSclaudio 385d0b565d2Sjob if (!EVP_Digest(buf, len, filehash, NULL, EVP_sha256(), NULL)) 386d0b565d2Sjob errx(1, "EVP_Digest failed in %s", __func__); 387d0b565d2Sjob 388d0b565d2Sjob if (base64_encode(filehash, sizeof(filehash), &hash) == -1) 389d0b565d2Sjob errx(1, "base64_encode failed in %s", __func__); 390d0b565d2Sjob 391d0b565d2Sjob if (outformats & FORMAT_JSON) { 3927aabcda0Sclaudio json_do_string("file", file); 3937aabcda0Sclaudio json_do_string("hash_id", hash); 394d0b565d2Sjob } else { 395c4a9443cSclaudio printf("File: %s\n", file); 396d0b565d2Sjob printf("Hash identifier: %s\n", hash); 397d0b565d2Sjob } 398d0b565d2Sjob 399d0b565d2Sjob free(hash); 400c4a9443cSclaudio 401c4a9443cSclaudio type = rtype_from_file_extension(file); 402c4a9443cSclaudio 403c4a9443cSclaudio switch (type) { 4049649a735Sjob case RTYPE_ASPA: 4050636c4d0Stb aspa = aspa_parse(&x509, file, -1, buf, len); 4069649a735Sjob if (aspa == NULL) 4079649a735Sjob break; 4089649a735Sjob aia = aspa->aia; 4099c1f5d6bSjob expires = &aspa->expires; 410490bf478Stb notbefore = &aspa->notbefore; 4119c1f5d6bSjob notafter = &aspa->notafter; 4129649a735Sjob break; 413c4a9443cSclaudio case RTYPE_CER: 414c4a9443cSclaudio cert = cert_parse_pre(file, buf, len); 415c4a9443cSclaudio if (cert == NULL) 416c4a9443cSclaudio break; 4177b62aa6fStb is_ta = (cert->purpose == CERT_PURPOSE_TA); 418c4a9443cSclaudio if (!is_ta) 419c4a9443cSclaudio cert = cert_parse(file, cert); 420c4a9443cSclaudio if (cert == NULL) 421c4a9443cSclaudio break; 422c4a9443cSclaudio aia = cert->aia; 423c4a9443cSclaudio x509 = cert->x509; 424c4a9443cSclaudio if (X509_up_ref(x509) == 0) 425c4a9443cSclaudio errx(1, "%s: X509_up_ref failed", __func__); 426894936b4Sjob expires = &cert->expires; 427490bf478Stb notbefore = &cert->notbefore; 428894936b4Sjob notafter = &cert->notafter; 429c4a9443cSclaudio break; 430c4a9443cSclaudio case RTYPE_CRL: 431c4a9443cSclaudio crl = crl_parse(file, buf, len); 432c4a9443cSclaudio if (crl == NULL) 433c4a9443cSclaudio break; 434c4a9443cSclaudio crl_print(crl); 435c4a9443cSclaudio break; 436c4a9443cSclaudio case RTYPE_MFT: 4370636c4d0Stb mft = mft_parse(&x509, file, -1, buf, len); 438c4a9443cSclaudio if (mft == NULL) 439c4a9443cSclaudio break; 440c4a9443cSclaudio aia = mft->aia; 441894936b4Sjob expires = &mft->expires; 442490bf478Stb notbefore = &mft->thisupdate; 443894936b4Sjob notafter = &mft->nextupdate; 444c4a9443cSclaudio break; 445c4a9443cSclaudio case RTYPE_GBR: 4460636c4d0Stb gbr = gbr_parse(&x509, file, -1, buf, len); 447c4a9443cSclaudio if (gbr == NULL) 448c4a9443cSclaudio break; 449c4a9443cSclaudio aia = gbr->aia; 450894936b4Sjob expires = &gbr->expires; 451490bf478Stb notbefore = &gbr->notbefore; 452894936b4Sjob notafter = &gbr->notafter; 453c4a9443cSclaudio break; 4549649a735Sjob case RTYPE_GEOFEED: 4550636c4d0Stb geofeed = geofeed_parse(&x509, file, -1, buf, len); 4569649a735Sjob if (geofeed == NULL) 457c4a9443cSclaudio break; 4589649a735Sjob aia = geofeed->aia; 459894936b4Sjob expires = &geofeed->expires; 460490bf478Stb notbefore = &geofeed->notbefore; 461894936b4Sjob notafter = &geofeed->notafter; 4629649a735Sjob break; 4639649a735Sjob case RTYPE_ROA: 4640636c4d0Stb roa = roa_parse(&x509, file, -1, buf, len); 4659649a735Sjob if (roa == NULL) 4669649a735Sjob break; 4679649a735Sjob aia = roa->aia; 4689c1f5d6bSjob expires = &roa->expires; 469490bf478Stb notbefore = &roa->notbefore; 4709c1f5d6bSjob notafter = &roa->notafter; 471c4a9443cSclaudio break; 47204834fbdSjob case RTYPE_RSC: 4730636c4d0Stb rsc = rsc_parse(&x509, file, -1, buf, len); 47404834fbdSjob if (rsc == NULL) 47504834fbdSjob break; 47604834fbdSjob aia = rsc->aia; 477894936b4Sjob expires = &rsc->expires; 478490bf478Stb notbefore = &rsc->notbefore; 479894936b4Sjob notafter = &rsc->notafter; 48004834fbdSjob break; 481d4be4cdeSjob case RTYPE_SPL: 482d4be4cdeSjob spl = spl_parse(&x509, file, -1, buf, len); 483d4be4cdeSjob if (spl == NULL) 484d4be4cdeSjob break; 485d4be4cdeSjob aia = spl->aia; 486d4be4cdeSjob expires = &spl->expires; 487490bf478Stb notbefore = &spl->notbefore; 488d4be4cdeSjob notafter = &spl->notafter; 489d4be4cdeSjob break; 490ee2a33daSjob case RTYPE_TAK: 4910636c4d0Stb tak = tak_parse(&x509, file, -1, buf, len); 492ee2a33daSjob if (tak == NULL) 493ee2a33daSjob break; 494ee2a33daSjob aia = tak->aia; 495894936b4Sjob expires = &tak->expires; 496490bf478Stb notbefore = &tak->notbefore; 497894936b4Sjob notafter = &tak->notafter; 498ee2a33daSjob break; 4999649a735Sjob case RTYPE_TAL: 5009649a735Sjob tal = tal_parse(file, buf, len); 5019649a735Sjob if (tal == NULL) 502ef3f6f56Sjob break; 5039649a735Sjob tal_print(tal); 504ef3f6f56Sjob break; 505c4a9443cSclaudio default: 506c4a9443cSclaudio printf("%s: unsupported file type\n", file); 507c4a9443cSclaudio break; 508c4a9443cSclaudio } 509c4a9443cSclaudio 510c4a9443cSclaudio if (aia != NULL) { 511c4a9443cSclaudio x509_get_crl(x509, file, &crl_uri); 512c4a9443cSclaudio parse_load_crl(crl_uri); 5130bc420b9Sclaudio a = parse_load_certchain(aia); 514c4a9443cSclaudio c = crl_get(&crlt, a); 515c4a9443cSclaudio 516fd7a2857Sclaudio if ((status = valid_x509(file, ctx, x509, a, c, &errstr))) { 517a29ddfd5Sjob switch (type) { 518a29ddfd5Sjob case RTYPE_ASPA: 519a29ddfd5Sjob status = aspa->valid; 520a29ddfd5Sjob break; 521ef3f6f56Sjob case RTYPE_GEOFEED: 522ef3f6f56Sjob status = geofeed->valid; 523ef3f6f56Sjob break; 5249649a735Sjob case RTYPE_ROA: 5259649a735Sjob status = roa->valid; 5269649a735Sjob break; 5279649a735Sjob case RTYPE_RSC: 5289649a735Sjob status = rsc->valid; 5299649a735Sjob break; 530d4be4cdeSjob case RTYPE_SPL: 531d4be4cdeSjob status = spl->valid; 532a29ddfd5Sjob default: 533a29ddfd5Sjob break; 534a29ddfd5Sjob } 535e6c729cdSjob } 536891d6bceSjob if (status && cert == NULL) { 537891d6bceSjob struct cert *eecert; 538891d6bceSjob 539891d6bceSjob eecert = cert_parse_ee_cert(file, a->cert->talid, x509); 540891d6bceSjob if (eecert == NULL) 541891d6bceSjob status = 0; 542891d6bceSjob cert_free(eecert); 543891d6bceSjob } else if (status) { 544891d6bceSjob cert->talid = a->cert->talid; 545ce2ba085Sjob constraints_validate(file, cert); 546891d6bceSjob } 547c4a9443cSclaudio } else if (is_ta) { 548c2f5584bStb expires = NULL; 549c2f5584bStb notafter = NULL; 550c4a9443cSclaudio if ((tal = find_tal(cert)) != NULL) { 551c4a9443cSclaudio cert = ta_parse(file, cert, tal->pkey, tal->pkeysz); 552a6235ec6Sjob status = (cert != NULL); 553c2f5584bStb if (status) { 554c2f5584bStb expires = &cert->expires; 555c2f5584bStb notafter = &cert->notafter; 556c2f5584bStb } 557c4a9443cSclaudio if (outformats & FORMAT_JSON) 5587aabcda0Sclaudio json_do_string("tal", tal->descr); 559c4a9443cSclaudio else 560a6235ec6Sjob printf("TAL: %s\n", 561bb47adf3Sjob tal->descr); 562c4a9443cSclaudio tal = NULL; 563c4a9443cSclaudio } else { 564c4a9443cSclaudio cert_free(cert); 565c4a9443cSclaudio cert = NULL; 566a6235ec6Sjob status = 0; 567a6235ec6Sjob } 568a6235ec6Sjob } 569a6235ec6Sjob 5709c1f5d6bSjob if (expires != NULL) { 571*39b34cbdStb if ((status && aia != NULL) || is_ta) 5729c1f5d6bSjob *expires = x509_find_expires(*notafter, a, &crlt); 5739c1f5d6bSjob 5749c1f5d6bSjob switch (type) { 5759c1f5d6bSjob case RTYPE_ASPA: 5769c1f5d6bSjob aspa_print(x509, aspa); 5779c1f5d6bSjob break; 578f09fce12Sjob case RTYPE_CER: 579f09fce12Sjob cert_print(cert); 580f09fce12Sjob break; 581894936b4Sjob case RTYPE_GBR: 582894936b4Sjob gbr_print(x509, gbr); 583894936b4Sjob break; 584894936b4Sjob case RTYPE_GEOFEED: 585894936b4Sjob geofeed_print(x509, geofeed); 586894936b4Sjob break; 587894936b4Sjob case RTYPE_MFT: 588894936b4Sjob mft_print(x509, mft); 589894936b4Sjob break; 5909c1f5d6bSjob case RTYPE_ROA: 5919c1f5d6bSjob roa_print(x509, roa); 5929c1f5d6bSjob break; 593894936b4Sjob case RTYPE_RSC: 594894936b4Sjob rsc_print(x509, rsc); 595894936b4Sjob break; 596d4be4cdeSjob case RTYPE_SPL: 597d4be4cdeSjob spl_print(x509, spl); 598d4be4cdeSjob break; 599894936b4Sjob case RTYPE_TAK: 600894936b4Sjob tak_print(x509, tak); 601894936b4Sjob break; 6029c1f5d6bSjob default: 6039c1f5d6bSjob break; 6049c1f5d6bSjob } 6059c1f5d6bSjob } 6069c1f5d6bSjob 607490bf478Stb if (status) { 608490bf478Stb if (notbefore != NULL && *notbefore > now) 609490bf478Stb valid = "Not yet valid"; 610490bf478Stb else if (notafter != NULL && *notafter < now) 611490bf478Stb valid = "Expired"; 612490bf478Stb else if (expires != NULL && *expires < now) 613490bf478Stb valid = "Signature path expired"; 614490bf478Stb else 6157aabcda0Sclaudio valid = "OK"; 616490bf478Stb } else if (aia == NULL) 6177aabcda0Sclaudio valid = "N/A"; 6187aabcda0Sclaudio else 6197aabcda0Sclaudio valid = "Failed"; 6207aabcda0Sclaudio 6217aabcda0Sclaudio if (outformats & FORMAT_JSON) { 6227aabcda0Sclaudio json_do_string("validation", valid); 6237aabcda0Sclaudio if (errstr != NULL) 6247aabcda0Sclaudio json_do_string("error", errstr); 6257aabcda0Sclaudio } else { 6267aabcda0Sclaudio printf("Validation: %s", valid); 627a6235ec6Sjob if (errstr != NULL) 628a6235ec6Sjob printf(", %s", errstr); 629c4a9443cSclaudio } 630c4a9443cSclaudio 631c4a9443cSclaudio if (outformats & FORMAT_JSON) 6327aabcda0Sclaudio json_do_finish(); 633d61b9ff1Sjob else { 6341e618320Sjob printf("\n"); 6351e618320Sjob 636a6002f6aStb if (aia != NULL && status) { 6379c1f5d6bSjob print_signature_path(crl_uri, aia, a); 6389c1f5d6bSjob if (expires != NULL) 6399c1f5d6bSjob printf("Signature path expires: %s\n", 6409c1f5d6bSjob time2str(*expires)); 6419c1f5d6bSjob } 6421e618320Sjob 643d61b9ff1Sjob if (x509 == NULL) 644d61b9ff1Sjob goto out; 645d61b9ff1Sjob if (type == RTYPE_TAL || type == RTYPE_CRL) 646d61b9ff1Sjob goto out; 647d61b9ff1Sjob 648d61b9ff1Sjob if (verbose) { 649d61b9ff1Sjob if (!X509_print_fp(stdout, x509)) 650d61b9ff1Sjob errx(1, "X509_print_fp"); 651d61b9ff1Sjob } 652d61b9ff1Sjob 653d53c6c47Sjob if (verbose > 1) { 654d61b9ff1Sjob if (!PEM_write_X509(stdout, x509)) 655d61b9ff1Sjob errx(1, "PEM_write_X509"); 656d61b9ff1Sjob } 657d61b9ff1Sjob } 658d61b9ff1Sjob 659d61b9ff1Sjob out: 660a6235ec6Sjob free(crl_uri); 661c4a9443cSclaudio X509_free(x509); 6629649a735Sjob aspa_free(aspa); 663c4a9443cSclaudio cert_free(cert); 664c4a9443cSclaudio crl_free(crl); 6659649a735Sjob gbr_free(gbr); 6669649a735Sjob geofeed_free(geofeed); 667c4a9443cSclaudio mft_free(mft); 668c4a9443cSclaudio roa_free(roa); 669827b2096Sjob rsc_free(rsc); 670ee2a33daSjob tak_free(tak); 6719649a735Sjob tal_free(tal); 672c4a9443cSclaudio } 673c4a9443cSclaudio 674c4a9443cSclaudio /* 675c4a9443cSclaudio * Process a file request, in general don't send anything back. 676c4a9443cSclaudio */ 677c4a9443cSclaudio static void 678c4a9443cSclaudio parse_file(struct entityq *q, struct msgbuf *msgq) 679c4a9443cSclaudio { 680c4a9443cSclaudio struct entity *entp; 681c4a9443cSclaudio struct ibuf *b; 682c4a9443cSclaudio struct tal *tal; 68308ac1330Sjob time_t dummy = 0; 684c4a9443cSclaudio 685c4a9443cSclaudio while ((entp = TAILQ_FIRST(q)) != NULL) { 686c4a9443cSclaudio TAILQ_REMOVE(q, entp, entries); 687c4a9443cSclaudio 688c4a9443cSclaudio switch (entp->type) { 689c4a9443cSclaudio case RTYPE_FILE: 690c4a9443cSclaudio proc_parser_file(entp->file, entp->data, entp->datasz); 691c4a9443cSclaudio break; 692c4a9443cSclaudio case RTYPE_TAL: 693c4a9443cSclaudio if ((tal = tal_parse(entp->file, entp->data, 694c4a9443cSclaudio entp->datasz)) == NULL) 695c4a9443cSclaudio errx(1, "%s: could not parse tal file", 696c4a9443cSclaudio entp->file); 697c4a9443cSclaudio tal->id = entp->talid; 698c4a9443cSclaudio talobj[tal->id] = tal; 699c4a9443cSclaudio parse_load_ta(tal); 700c4a9443cSclaudio break; 701c4a9443cSclaudio default: 702c4a9443cSclaudio errx(1, "unhandled entity type %d", entp->type); 703c4a9443cSclaudio } 704c4a9443cSclaudio 705c4a9443cSclaudio b = io_new_buffer(); 706c4a9443cSclaudio io_simple_buffer(b, &entp->type, sizeof(entp->type)); 70739c0924aSclaudio io_simple_buffer(b, &entp->repoid, sizeof(entp->repoid)); 7081fc2657fSclaudio io_simple_buffer(b, &entp->talid, sizeof(entp->talid)); 709c4a9443cSclaudio io_str_buffer(b, entp->file); 71008ac1330Sjob io_simple_buffer(b, &dummy, sizeof(dummy)); 711c4a9443cSclaudio io_close_buffer(msgq, b); 712c4a9443cSclaudio entity_free(entp); 713c4a9443cSclaudio } 714c4a9443cSclaudio } 715c4a9443cSclaudio 716c4a9443cSclaudio /* 717c4a9443cSclaudio * Process responsible for parsing and validating content. 718c4a9443cSclaudio * All this process does is wait to be told about a file to parse, then 719c4a9443cSclaudio * it parses it and makes sure that the data being returned is fully 720c4a9443cSclaudio * validated and verified. 721c4a9443cSclaudio * The process will exit cleanly only when fd is closed. 722c4a9443cSclaudio */ 723c4a9443cSclaudio void 724c4a9443cSclaudio proc_filemode(int fd) 725c4a9443cSclaudio { 726c4a9443cSclaudio struct entityq q; 727c4a9443cSclaudio struct pollfd pfd; 72825d36c5cSclaudio struct msgbuf *msgq; 729c4a9443cSclaudio struct entity *entp; 730c4a9443cSclaudio struct ibuf *b, *inbuf = NULL; 731c4a9443cSclaudio 732c4a9443cSclaudio /* Only allow access to the cache directory. */ 733c4a9443cSclaudio if (unveil(".", "r") == -1) 734c4a9443cSclaudio err(1, "unveil cachedir"); 735c4a9443cSclaudio if (pledge("stdio rpath", NULL) == -1) 736c4a9443cSclaudio err(1, "pledge"); 737c4a9443cSclaudio 738c4a9443cSclaudio ERR_load_crypto_strings(); 739c4a9443cSclaudio OpenSSL_add_all_ciphers(); 740c4a9443cSclaudio OpenSSL_add_all_digests(); 741c4a9443cSclaudio x509_init_oid(); 742891d6bceSjob constraints_parse(); 743c4a9443cSclaudio 744c4a9443cSclaudio if ((ctx = X509_STORE_CTX_new()) == NULL) 745c0528901Stb err(1, "X509_STORE_CTX_new"); 746318f0572Sjob if ((bn_ctx = BN_CTX_new()) == NULL) 747318f0572Sjob err(1, "BN_CTX_new"); 748318f0572Sjob 749c4a9443cSclaudio TAILQ_INIT(&q); 750c4a9443cSclaudio 751b5fa5d51Sclaudio if ((msgq = msgbuf_new_reader(sizeof(size_t), io_parse_hdr, NULL)) == 752b5fa5d51Sclaudio NULL) 75325d36c5cSclaudio err(1, NULL); 754c4a9443cSclaudio pfd.fd = fd; 755c4a9443cSclaudio 756c4a9443cSclaudio for (;;) { 757c4a9443cSclaudio pfd.events = POLLIN; 75825d36c5cSclaudio if (msgbuf_queuelen(msgq) > 0) 759c4a9443cSclaudio pfd.events |= POLLOUT; 760c4a9443cSclaudio 761c4a9443cSclaudio if (poll(&pfd, 1, INFTIM) == -1) { 762c4a9443cSclaudio if (errno == EINTR) 763c4a9443cSclaudio continue; 764c4a9443cSclaudio err(1, "poll"); 765c4a9443cSclaudio } 766c4a9443cSclaudio if ((pfd.revents & (POLLERR|POLLNVAL))) 767c4a9443cSclaudio errx(1, "poll: bad descriptor"); 768c4a9443cSclaudio 769c4a9443cSclaudio /* If the parent closes, return immediately. */ 770c4a9443cSclaudio 771c4a9443cSclaudio if ((pfd.revents & POLLHUP)) 772c4a9443cSclaudio break; 773c4a9443cSclaudio 774c4a9443cSclaudio if ((pfd.revents & POLLIN)) { 775b5fa5d51Sclaudio switch (ibuf_read(fd, msgq)) { 776b5fa5d51Sclaudio case -1: 777b5fa5d51Sclaudio err(1, "ibuf_read"); 778b5fa5d51Sclaudio case 0: 779b5fa5d51Sclaudio errx(1, "ibuf_read: connection closed"); 780b5fa5d51Sclaudio } 781b5fa5d51Sclaudio while ((b = io_buf_get(msgq)) != NULL) { 782c4a9443cSclaudio entp = calloc(1, sizeof(struct entity)); 783c4a9443cSclaudio if (entp == NULL) 784c4a9443cSclaudio err(1, NULL); 785c4a9443cSclaudio entity_read_req(b, entp); 786c4a9443cSclaudio TAILQ_INSERT_TAIL(&q, entp, entries); 787c4a9443cSclaudio ibuf_free(b); 788c4a9443cSclaudio } 789c4a9443cSclaudio } 790c4a9443cSclaudio 791c4a9443cSclaudio if (pfd.revents & POLLOUT) { 79225d36c5cSclaudio if (msgbuf_write(fd, msgq) == -1) { 7939aadc625Sclaudio if (errno == EPIPE) 794c4a9443cSclaudio errx(1, "write: connection closed"); 7959aadc625Sclaudio else 796c4a9443cSclaudio err(1, "write"); 797c4a9443cSclaudio } 798c4a9443cSclaudio } 799c4a9443cSclaudio 80025d36c5cSclaudio parse_file(&q, msgq); 801c4a9443cSclaudio } 802c4a9443cSclaudio 80325d36c5cSclaudio msgbuf_free(msgq); 804c4a9443cSclaudio while ((entp = TAILQ_FIRST(&q)) != NULL) { 805c4a9443cSclaudio TAILQ_REMOVE(&q, entp, entries); 806c4a9443cSclaudio entity_free(entp); 807c4a9443cSclaudio } 808c4a9443cSclaudio 809b8017900Sjob auth_tree_free(&auths); 810b8017900Sjob crl_tree_free(&crlt); 811b8017900Sjob 812c4a9443cSclaudio X509_STORE_CTX_free(ctx); 813318f0572Sjob BN_CTX_free(bn_ctx); 814318f0572Sjob 815b8017900Sjob ibuf_free(inbuf); 816c4a9443cSclaudio 817c4a9443cSclaudio exit(0); 818c4a9443cSclaudio } 819