1*b5fa5d51Sclaudio /* $OpenBSD: parser.c,v 1.148 2024/11/21 13:32:27 claudio Exp $ */ 2eae58378Sclaudio /* 3eae58378Sclaudio * Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org> 4eae58378Sclaudio * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 5eae58378Sclaudio * 6eae58378Sclaudio * Permission to use, copy, modify, and distribute this software for any 7eae58378Sclaudio * purpose with or without fee is hereby granted, provided that the above 8eae58378Sclaudio * copyright notice and this permission notice appear in all copies. 9eae58378Sclaudio * 10eae58378Sclaudio * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11eae58378Sclaudio * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12eae58378Sclaudio * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13eae58378Sclaudio * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14eae58378Sclaudio * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15eae58378Sclaudio * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16eae58378Sclaudio * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17eae58378Sclaudio */ 18eae58378Sclaudio 19eae58378Sclaudio #include <sys/queue.h> 20eae58378Sclaudio #include <sys/tree.h> 21eae58378Sclaudio #include <sys/types.h> 22eae58378Sclaudio 23eae58378Sclaudio #include <err.h> 2487c7c78dSclaudio #include <fcntl.h> 25eae58378Sclaudio #include <poll.h> 26eae58378Sclaudio #include <stdio.h> 27eae58378Sclaudio #include <stdlib.h> 28eae58378Sclaudio #include <string.h> 29eae58378Sclaudio #include <limits.h> 30eae58378Sclaudio #include <unistd.h> 31eae58378Sclaudio #include <imsg.h> 32eae58378Sclaudio 33c631bb87Sclaudio #include <openssl/asn1.h> 34eae58378Sclaudio #include <openssl/err.h> 35a1753de6Sclaudio #include <openssl/evp.h> 36c631bb87Sclaudio #include <openssl/x509.h> 37cac2b49eStb #include <openssl/x509v3.h> 38eae58378Sclaudio 39eae58378Sclaudio #include "extern.h" 40eae58378Sclaudio 411d8c6443Stb extern int certid; 421d8c6443Stb 43318f0572Sjob extern BN_CTX *bn_ctx; 44318f0572Sjob 456feb6ad0Sclaudio static X509_STORE_CTX *ctx; 466feb6ad0Sclaudio static struct auth_tree auths = RB_INITIALIZER(&auths); 476feb6ad0Sclaudio static struct crl_tree crlt = RB_INITIALIZER(&crlt); 486feb6ad0Sclaudio 49100ded9eSclaudio struct parse_repo { 50100ded9eSclaudio RB_ENTRY(parse_repo) entry; 51100ded9eSclaudio char *path; 524bccd3c1Sclaudio char *validpath; 53100ded9eSclaudio unsigned int id; 54100ded9eSclaudio }; 55100ded9eSclaudio 56100ded9eSclaudio static RB_HEAD(repo_tree, parse_repo) repos = RB_INITIALIZER(&repos); 57100ded9eSclaudio 58100ded9eSclaudio static inline int 59100ded9eSclaudio repocmp(struct parse_repo *a, struct parse_repo *b) 60100ded9eSclaudio { 61100ded9eSclaudio return a->id - b->id; 62100ded9eSclaudio } 63100ded9eSclaudio 64100ded9eSclaudio RB_GENERATE_STATIC(repo_tree, parse_repo, entry, repocmp); 65100ded9eSclaudio 66100ded9eSclaudio static struct parse_repo * 67100ded9eSclaudio repo_get(unsigned int id) 68100ded9eSclaudio { 69100ded9eSclaudio struct parse_repo needle = { .id = id }; 70100ded9eSclaudio 71100ded9eSclaudio return RB_FIND(repo_tree, &repos, &needle); 72100ded9eSclaudio } 73100ded9eSclaudio 74100ded9eSclaudio static void 754bccd3c1Sclaudio repo_add(unsigned int id, char *path, char *validpath) 76100ded9eSclaudio { 77100ded9eSclaudio struct parse_repo *rp; 78100ded9eSclaudio 794bccd3c1Sclaudio if ((rp = calloc(1, sizeof(*rp))) == NULL) 80100ded9eSclaudio err(1, NULL); 81100ded9eSclaudio rp->id = id; 824bccd3c1Sclaudio if (path != NULL) 83100ded9eSclaudio if ((rp->path = strdup(path)) == NULL) 84100ded9eSclaudio err(1, NULL); 854bccd3c1Sclaudio if (validpath != NULL) 864bccd3c1Sclaudio if ((rp->validpath = strdup(validpath)) == NULL) 874bccd3c1Sclaudio err(1, NULL); 88100ded9eSclaudio 89100ded9eSclaudio if (RB_INSERT(repo_tree, &repos, rp) != NULL) 90100ded9eSclaudio errx(1, "repository already added: id %d, %s", id, path); 91100ded9eSclaudio } 92100ded9eSclaudio 934bccd3c1Sclaudio /* 940bc420b9Sclaudio * Return the issuer by its certificate id, or NULL on failure. 950bc420b9Sclaudio * Make sure the AKI is the same as the AKI listed on the Manifest, 960bc420b9Sclaudio * and that the SKI of the cert matches with the AKI. 970bc420b9Sclaudio */ 980bc420b9Sclaudio static struct auth * 990bc420b9Sclaudio find_issuer(const char *fn, int id, const char *aki, const char *mftaki) 1000bc420b9Sclaudio { 1010bc420b9Sclaudio struct auth *a; 1020bc420b9Sclaudio 1030bc420b9Sclaudio a = auth_find(&auths, id); 1040bc420b9Sclaudio if (a == NULL) { 1051d8c6443Stb if (certid <= CERTID_MAX) 1061d8c6443Stb warnx("%s: RFC 6487: unknown cert with SKI %s", fn, 1071d8c6443Stb aki); 1080bc420b9Sclaudio return NULL; 1090bc420b9Sclaudio } 1100bc420b9Sclaudio 1110bc420b9Sclaudio if (mftaki != NULL) { 1120bc420b9Sclaudio if (strcmp(aki, mftaki) != 0) { 1130bc420b9Sclaudio warnx("%s: AKI %s doesn't match Manifest AKI %s", fn, 1140bc420b9Sclaudio aki, mftaki); 1150bc420b9Sclaudio return NULL; 1160bc420b9Sclaudio } 1170bc420b9Sclaudio } 1180bc420b9Sclaudio 1190bc420b9Sclaudio if (strcmp(aki, a->cert->ski) != 0) { 1200bc420b9Sclaudio warnx("%s: AKI %s doesn't match issuer SKI %s", fn, 1210bc420b9Sclaudio aki, a->cert->ski); 1220bc420b9Sclaudio return NULL; 1230bc420b9Sclaudio } 1240bc420b9Sclaudio 1250bc420b9Sclaudio return a; 1260bc420b9Sclaudio } 1270bc420b9Sclaudio 1280bc420b9Sclaudio /* 129df512fbcSclaudio * Build access path to file based on repoid, path, location and file values. 1304bccd3c1Sclaudio */ 1314bccd3c1Sclaudio static char * 1324bccd3c1Sclaudio parse_filepath(unsigned int repoid, const char *path, const char *file, 133df512fbcSclaudio enum location loc) 1344bccd3c1Sclaudio { 1354bccd3c1Sclaudio struct parse_repo *rp; 1364bccd3c1Sclaudio char *fn, *repopath; 1374bccd3c1Sclaudio 1384bccd3c1Sclaudio /* build file path based on repoid, entity path and filename */ 1394bccd3c1Sclaudio rp = repo_get(repoid); 140df512fbcSclaudio if (rp == NULL) 141441ac56aSclaudio errx(1, "build file path: repository %u missing", repoid); 1424bccd3c1Sclaudio 143df512fbcSclaudio if (loc == DIR_VALID) 1444bccd3c1Sclaudio repopath = rp->validpath; 1454bccd3c1Sclaudio else 1464bccd3c1Sclaudio repopath = rp->path; 1474bccd3c1Sclaudio 1484bccd3c1Sclaudio if (repopath == NULL) 1494bccd3c1Sclaudio return NULL; 1504bccd3c1Sclaudio 1514bccd3c1Sclaudio if (path == NULL) { 1524bccd3c1Sclaudio if (asprintf(&fn, "%s/%s", repopath, file) == -1) 1534bccd3c1Sclaudio err(1, NULL); 1544bccd3c1Sclaudio } else { 155df512fbcSclaudio if (asprintf(&fn, "%s/%s/%s", repopath, path, file) == -1) 1564bccd3c1Sclaudio err(1, NULL); 1574bccd3c1Sclaudio } 1584bccd3c1Sclaudio return fn; 1594bccd3c1Sclaudio } 1604bccd3c1Sclaudio 1614bccd3c1Sclaudio /* 162eae58378Sclaudio * Parse and validate a ROA. 163eae58378Sclaudio * This is standard stuff. 164eae58378Sclaudio * Returns the roa on success, NULL on failure. 165eae58378Sclaudio */ 166eae58378Sclaudio static struct roa * 16732c8d2feSjob proc_parser_roa(char *file, const unsigned char *der, size_t len, 1680636c4d0Stb const struct entity *entp) 169eae58378Sclaudio { 170eae58378Sclaudio struct roa *roa; 171aec10a2aStb X509 *x509 = NULL; 1724bccd3c1Sclaudio struct auth *a; 173d7e95037Stb struct crl *crl; 174fd7a2857Sclaudio const char *errstr; 175eae58378Sclaudio 1760636c4d0Stb if ((roa = roa_parse(&x509, file, entp->talid, der, len)) == NULL) 177aec10a2aStb goto out; 178eae58378Sclaudio 1790bc420b9Sclaudio a = find_issuer(file, entp->certid, roa->aki, entp->mftaki); 180aec10a2aStb if (a == NULL) 181aec10a2aStb goto out; 182c4a9443cSclaudio crl = crl_get(&crlt, a); 183eae58378Sclaudio 184fd7a2857Sclaudio if (!valid_x509(file, ctx, x509, a, crl, &errstr)) { 185fd7a2857Sclaudio warnx("%s: %s", file, errstr); 186aec10a2aStb goto out; 187eae58378Sclaudio } 1884bccd3c1Sclaudio X509_free(x509); 189aec10a2aStb x509 = NULL; 190a66158d7Sjob 19124069af1Sclaudio roa->talid = a->cert->talid; 19224069af1Sclaudio 193534b6674Sjob roa->expires = x509_find_expires(roa->notafter, a, &crlt); 194eae58378Sclaudio 195eae58378Sclaudio return roa; 196aec10a2aStb 197aec10a2aStb out: 198aec10a2aStb roa_free(roa); 199aec10a2aStb X509_free(x509); 200aec10a2aStb 201aec10a2aStb return NULL; 202eae58378Sclaudio } 203eae58378Sclaudio 204eae58378Sclaudio /* 205d4be4cdeSjob * Parse and validate a draft-ietf-sidrops-rpki-prefixlist SPL. 206d4be4cdeSjob * Returns the spl on success, NULL on failure. 207d4be4cdeSjob */ 208d4be4cdeSjob static struct spl * 209d4be4cdeSjob proc_parser_spl(char *file, const unsigned char *der, size_t len, 210d4be4cdeSjob const struct entity *entp) 211d4be4cdeSjob { 212d4be4cdeSjob struct spl *spl; 213aec10a2aStb X509 *x509 = NULL; 214d4be4cdeSjob struct auth *a; 215d4be4cdeSjob struct crl *crl; 216d4be4cdeSjob const char *errstr; 217d4be4cdeSjob 218d4be4cdeSjob if ((spl = spl_parse(&x509, file, entp->talid, der, len)) == NULL) 219aec10a2aStb goto out; 220d4be4cdeSjob 2210bc420b9Sclaudio a = find_issuer(file, entp->certid, spl->aki, entp->mftaki); 222aec10a2aStb if (a == NULL) 223aec10a2aStb goto out; 224d4be4cdeSjob crl = crl_get(&crlt, a); 225d4be4cdeSjob 226d4be4cdeSjob if (!valid_x509(file, ctx, x509, a, crl, &errstr)) { 227d4be4cdeSjob warnx("%s: %s", file, errstr); 228aec10a2aStb goto out; 229d4be4cdeSjob } 230d4be4cdeSjob X509_free(x509); 231aec10a2aStb x509 = NULL; 232d4be4cdeSjob 233d4be4cdeSjob spl->talid = a->cert->talid; 234d4be4cdeSjob 235d4be4cdeSjob spl->expires = x509_find_expires(spl->notafter, a, &crlt); 236d4be4cdeSjob 237d4be4cdeSjob return spl; 238aec10a2aStb 239aec10a2aStb out: 240aec10a2aStb spl_free(spl); 241aec10a2aStb X509_free(x509); 242aec10a2aStb 243aec10a2aStb return NULL; 244d4be4cdeSjob } 245d4be4cdeSjob 246d4be4cdeSjob /* 24787c7c78dSclaudio * Check all files and their hashes in a MFT structure. 24887c7c78dSclaudio * Return zero on failure, non-zero on success. 24987c7c78dSclaudio */ 250487aa6cbSclaudio static int 251487aa6cbSclaudio proc_parser_mft_check(const char *fn, struct mft *p) 25287c7c78dSclaudio { 253df512fbcSclaudio const enum location loc[2] = { DIR_TEMP, DIR_VALID }; 25487c7c78dSclaudio size_t i; 255685326f5Sclaudio int rc = 1; 256685326f5Sclaudio char *path; 25787c7c78dSclaudio 25894674fa3Stb if (p == NULL) 25994674fa3Stb return 0; 26094674fa3Stb 26187c7c78dSclaudio for (i = 0; i < p->filesz; i++) { 262df512fbcSclaudio struct mftfile *m = &p->files[i]; 263df512fbcSclaudio int try, fd = -1, noent = 0, valid = 0; 264df512fbcSclaudio for (try = 0; try < 2 && !valid; try++) { 2654bccd3c1Sclaudio if ((path = parse_filepath(p->repoid, p->path, m->file, 266df512fbcSclaudio loc[try])) == NULL) 267df512fbcSclaudio continue; 26887c7c78dSclaudio fd = open(path, O_RDONLY); 269df512fbcSclaudio if (fd == -1 && errno == ENOENT) 270df512fbcSclaudio noent++; 2714bccd3c1Sclaudio free(path); 2724bccd3c1Sclaudio 273df512fbcSclaudio /* remember which path was checked */ 274df512fbcSclaudio m->location = loc[try]; 275df512fbcSclaudio valid = valid_filehash(fd, m->hash, sizeof(m->hash)); 276df512fbcSclaudio } 277df512fbcSclaudio 278df512fbcSclaudio if (!valid) { 279df512fbcSclaudio /* silently skip not-existing unknown files */ 280df512fbcSclaudio if (m->type == RTYPE_INVALID && noent == 2) 281df512fbcSclaudio continue; 2827822b8bfSjob warnx("%s#%s: bad message digest for %s", fn, 2837822b8bfSjob p->seqnum, m->file); 28487c7c78dSclaudio rc = 0; 285df512fbcSclaudio continue; 28687c7c78dSclaudio } 28787c7c78dSclaudio } 28887c7c78dSclaudio 28987c7c78dSclaudio return rc; 29087c7c78dSclaudio } 29187c7c78dSclaudio 29287c7c78dSclaudio /* 2936fd959f8Stb * Load the CRL from loc using the info from the MFT. 29423bc08f8Sclaudio */ 29523bc08f8Sclaudio static struct crl * 2966fd959f8Stb parse_load_crl_from_mft(struct entity *entp, struct mft *mft, enum location loc, 2976fd959f8Stb char **crlfile) 29823bc08f8Sclaudio { 29923bc08f8Sclaudio struct crl *crl = NULL; 30023bc08f8Sclaudio unsigned char *f = NULL; 30123bc08f8Sclaudio char *fn = NULL; 30223bc08f8Sclaudio size_t flen; 30323bc08f8Sclaudio 3046fd959f8Stb *crlfile = NULL; 3056fd959f8Stb 30623bc08f8Sclaudio fn = parse_filepath(entp->repoid, entp->path, mft->crl, loc); 30723bc08f8Sclaudio if (fn == NULL) 3086fd959f8Stb goto out; 30923bc08f8Sclaudio 31023bc08f8Sclaudio f = load_file(fn, &flen); 3116fd959f8Stb if (f == NULL) { 3126fd959f8Stb if (errno != ENOENT) 31323bc08f8Sclaudio warn("parse file %s", fn); 3146fd959f8Stb goto out; 3156fd959f8Stb } 3166fd959f8Stb 31723bc08f8Sclaudio if (!valid_hash(f, flen, mft->crlhash, sizeof(mft->crlhash))) 3186fd959f8Stb goto out; 3196fd959f8Stb 32023bc08f8Sclaudio crl = crl_parse(fn, f, flen); 3216fd959f8Stb if (crl == NULL) 3226fd959f8Stb goto out; 323fd7a2857Sclaudio 32432c8d2feSjob if (strcmp(crl->aki, mft->aki) != 0) { 32532c8d2feSjob warnx("%s: AKI doesn't match Manifest AKI", fn); 32632c8d2feSjob goto out; 32732c8d2feSjob } 32832c8d2feSjob 329c207abadSjob if ((crl->mftpath = strdup(mft->sia)) == NULL) 330c207abadSjob err(1, NULL); 331c207abadSjob 332e038f1a1Sclaudio *crlfile = fn; 3336fd959f8Stb free(f); 3346fd959f8Stb 33523bc08f8Sclaudio return crl; 3366fd959f8Stb 3376fd959f8Stb out: 33832c8d2feSjob crl_free(crl); 3396fd959f8Stb free(f); 340e038f1a1Sclaudio free(fn); 3416fd959f8Stb 34223bc08f8Sclaudio return NULL; 34323bc08f8Sclaudio } 34423bc08f8Sclaudio 34523bc08f8Sclaudio /* 34605c37678Sjob * Parse and validate a manifest file. 34705c37678Sjob * Don't check the fileandhash, this is done later on. 34805c37678Sjob * Return the mft on success, or NULL on failure. 349eae58378Sclaudio */ 350eae58378Sclaudio static struct mft * 35152b4e3adSjob proc_parser_mft_pre(struct entity *entp, char *file, struct crl **crl, 35252b4e3adSjob char **crlfile, struct mft *cached_mft, const char **errstr) 353eae58378Sclaudio { 354eae58378Sclaudio struct mft *mft; 355eae58378Sclaudio X509 *x509; 356eae58378Sclaudio struct auth *a; 357a4968bd9Stb unsigned char *der; 358a4968bd9Stb size_t len; 3598cad1871Sjob time_t now; 3601039ba60Stb int issued_cmp, seqnum_cmp; 361eae58378Sclaudio 36223bc08f8Sclaudio *crl = NULL; 363e038f1a1Sclaudio *crlfile = NULL; 364fd7a2857Sclaudio *errstr = NULL; 365eae58378Sclaudio 36609ad02f4Sjob if (file == NULL) 367a4968bd9Stb return NULL; 368a4968bd9Stb 36909ad02f4Sjob der = load_file(file, &len); 370a4968bd9Stb if (der == NULL && errno != ENOENT) 37109ad02f4Sjob warn("parse file %s", file); 372a4968bd9Stb 37309ad02f4Sjob if ((mft = mft_parse(&x509, file, entp->talid, der, len)) == NULL) { 374a4968bd9Stb free(der); 375a4968bd9Stb return NULL; 376a4968bd9Stb } 377c5b5cf9aSjob 378b1d56901Sjob if (entp->path != NULL) { 379b1d56901Sjob if ((mft->path = strdup(entp->path)) == NULL) 380b1d56901Sjob err(1, NULL); 381b1d56901Sjob } 382b1d56901Sjob 383c5b5cf9aSjob if (!EVP_Digest(der, len, mft->mfthash, NULL, EVP_sha256(), NULL)) 384c5b5cf9aSjob errx(1, "EVP_Digest failed"); 385c5b5cf9aSjob 386a4968bd9Stb free(der); 387a4968bd9Stb 3886fd959f8Stb *crl = parse_load_crl_from_mft(entp, mft, DIR_TEMP, crlfile); 3896fd959f8Stb if (*crl == NULL) 3906fd959f8Stb *crl = parse_load_crl_from_mft(entp, mft, DIR_VALID, crlfile); 391a4968bd9Stb 3920bc420b9Sclaudio a = find_issuer(file, entp->certid, mft->aki, NULL); 3931d8c6443Stb if (a == NULL) 3941d8c6443Stb goto err; 39509ad02f4Sjob if (!valid_x509(file, ctx, x509, a, *crl, errstr)) 396690a1eadStb goto err; 397690a1eadStb X509_free(x509); 398690a1eadStb x509 = NULL; 399690a1eadStb 400690a1eadStb mft->repoid = entp->repoid; 401690a1eadStb mft->talid = a->cert->talid; 4020bc420b9Sclaudio mft->certid = entp->certid; 403690a1eadStb 4048cad1871Sjob now = get_current_time(); 4058cad1871Sjob /* check that now is not before from */ 4068cad1871Sjob if (now < mft->thisupdate) { 40709ad02f4Sjob warnx("%s: manifest not yet valid %s", file, 4088cad1871Sjob time2str(mft->thisupdate)); 409c6be5ad7Sjob goto err; 4108cad1871Sjob } 4118cad1871Sjob /* check that now is not after until */ 4128cad1871Sjob if (now > mft->nextupdate) { 41309ad02f4Sjob warnx("%s: manifest expired on %s", file, 4148cad1871Sjob time2str(mft->nextupdate)); 415c6be5ad7Sjob goto err; 4168cad1871Sjob } 4178cad1871Sjob 4188cad1871Sjob /* if there is nothing to compare to, return now */ 41986eea840Stb if (cached_mft == NULL) 42086eea840Stb return mft; 42186eea840Stb 42286eea840Stb /* 4231039ba60Stb * Check that the cached manifest is older in the sense that it was 4241039ba60Stb * issued earlier and that it has a smaller sequence number. 42586eea840Stb */ 42686eea840Stb 4271039ba60Stb if ((issued_cmp = mft_compare_issued(mft, cached_mft)) < 0) { 42862c6bfd2Sjob warnx("%s: unexpected manifest issuance date (want >= %lld, " 42909ad02f4Sjob "got %lld)", file, (long long)cached_mft->thisupdate, 4301039ba60Stb (long long)mft->thisupdate); 4311039ba60Stb goto err; 4321039ba60Stb } 433e73085f8Stb if ((seqnum_cmp = mft_compare_seqnum(mft, cached_mft)) < 0) { 43486eea840Stb warnx("%s: unexpected manifest number (want >= #%s, got #%s)", 43509ad02f4Sjob file, cached_mft->seqnum, mft->seqnum); 43686eea840Stb goto err; 43786eea840Stb } 4381039ba60Stb if (issued_cmp > 0 && seqnum_cmp == 0) { 43962c6bfd2Sjob warnx("%s: manifest issued at %lld and %lld with same " 44009ad02f4Sjob "manifest number #%s", file, (long long)mft->thisupdate, 44162c6bfd2Sjob (long long)cached_mft->thisupdate, cached_mft->seqnum); 4421039ba60Stb goto err; 4431039ba60Stb } 4441039ba60Stb if (issued_cmp == 0 && seqnum_cmp > 0) { 44562c6bfd2Sjob warnx("%s: #%s and #%s were issued at same issuance date %lld", 44609ad02f4Sjob file, mft->seqnum, cached_mft->seqnum, 44762c6bfd2Sjob (long long)mft->thisupdate); 4481039ba60Stb goto err; 4491039ba60Stb } 4501039ba60Stb if (issued_cmp == 0 && seqnum_cmp == 0 && memcmp(mft->mfthash, 45186eea840Stb cached_mft->mfthash, SHA256_DIGEST_LENGTH) != 0) { 45262c6bfd2Sjob warnx("%s: misissuance, issuance date %lld and manifest number " 45309ad02f4Sjob "#%s were recycled", file, (long long)mft->thisupdate, 45462c6bfd2Sjob mft->seqnum); 45586eea840Stb goto err; 45686eea840Stb } 45786eea840Stb 458318f0572Sjob if (seqnum_cmp > 0) { 459318f0572Sjob if (mft_seqnum_gap_present(mft, cached_mft)) { 460318f0572Sjob mft->seqnum_gap = 1; 461318f0572Sjob warnx("%s: seqnum gap detected #%s -> #%s", file, 462318f0572Sjob cached_mft->seqnum, mft->seqnum); 463318f0572Sjob } 464318f0572Sjob } 465318f0572Sjob 466690a1eadStb return mft; 467690a1eadStb 468690a1eadStb err: 469f999fe57Sclaudio X509_free(x509); 47023bc08f8Sclaudio mft_free(mft); 47123bc08f8Sclaudio crl_free(*crl); 47223bc08f8Sclaudio *crl = NULL; 473e038f1a1Sclaudio free(*crlfile); 474e038f1a1Sclaudio *crlfile = NULL; 475f999fe57Sclaudio return NULL; 476f999fe57Sclaudio } 477df512fbcSclaudio 478df512fbcSclaudio /* 47923bc08f8Sclaudio * Load the most recent MFT by opening both options and comparing the two. 48023bc08f8Sclaudio */ 48123bc08f8Sclaudio static char * 48208ac1330Sjob proc_parser_mft(struct entity *entp, struct mft **mp, char **crlfile, 48308ac1330Sjob time_t *crlmtime) 48423bc08f8Sclaudio { 48523bc08f8Sclaudio struct mft *mft1 = NULL, *mft2 = NULL; 48652b4e3adSjob struct crl *crl, *crl1 = NULL, *crl2 = NULL; 48752b4e3adSjob char *file, *file1 = NULL, *file2 = NULL; 48852b4e3adSjob char *crl1file = NULL, *crl2file = NULL; 48952b4e3adSjob const char *err1 = NULL, *err2 = NULL; 49023bc08f8Sclaudio 49123bc08f8Sclaudio *mp = NULL; 49208ac1330Sjob *crlmtime = 0; 49323bc08f8Sclaudio 49409ad02f4Sjob file2 = parse_filepath(entp->repoid, entp->path, entp->file, DIR_VALID); 49552b4e3adSjob mft2 = proc_parser_mft_pre(entp, file2, &crl2, &crl2file, NULL, &err2); 49609ad02f4Sjob 49752b4e3adSjob if (!noop) { 49852b4e3adSjob file1 = parse_filepath(entp->repoid, entp->path, entp->file, 49952b4e3adSjob DIR_TEMP); 50052b4e3adSjob mft1 = proc_parser_mft_pre(entp, file1, &crl1, &crl1file, mft2, 50152b4e3adSjob &err1); 50252b4e3adSjob } 50323bc08f8Sclaudio 5044dfb12e7Sjob if (proc_parser_mft_check(file1, mft1)) { 50523bc08f8Sclaudio mft_free(mft2); 50623bc08f8Sclaudio crl_free(crl2); 507e038f1a1Sclaudio free(crl2file); 50823bc08f8Sclaudio free(file2); 509102443c7Sjob 51094674fa3Stb *mp = mft1; 51123bc08f8Sclaudio crl = crl1; 51223bc08f8Sclaudio file = file1; 513e038f1a1Sclaudio *crlfile = crl1file; 51423bc08f8Sclaudio } else { 51594674fa3Stb if (mft1 != NULL && mft2 != NULL) 51694674fa3Stb warnx("%s: failed fetch, continuing with #%s " 51794674fa3Stb "from cache", file2, mft2->seqnum); 51894674fa3Stb 5196aaa3c8dStb if (!proc_parser_mft_check(file2, mft2)) { 520a0d2775aStb mft_free(mft2); 521a0d2775aStb mft2 = NULL; 52294674fa3Stb 523102443c7Sjob if (err2 == NULL) 524102443c7Sjob err2 = err1; 525a0d2775aStb if (err2 == NULL) 52694674fa3Stb err2 = "no valid manifest available"; 5271d8c6443Stb if (certid <= CERTID_MAX) 528a0d2775aStb warnx("%s: %s", file2, err2); 529a0d2775aStb } 530102443c7Sjob 53123bc08f8Sclaudio mft_free(mft1); 53223bc08f8Sclaudio crl_free(crl1); 533e038f1a1Sclaudio free(crl1file); 53423bc08f8Sclaudio free(file1); 535102443c7Sjob 5366aaa3c8dStb *mp = mft2; 53723bc08f8Sclaudio crl = crl2; 53823bc08f8Sclaudio file = file2; 539e038f1a1Sclaudio *crlfile = crl2file; 54023bc08f8Sclaudio } 54123bc08f8Sclaudio 54223bc08f8Sclaudio if (*mp != NULL) { 543c527cc7aSjob *crlmtime = crl->thisupdate; 544fc437288Stb if (crl_insert(&crlt, crl)) 545fc437288Stb crl = NULL; 54623bc08f8Sclaudio } 54723bc08f8Sclaudio crl_free(crl); 548fc437288Stb 54923bc08f8Sclaudio return file; 55023bc08f8Sclaudio } 55123bc08f8Sclaudio 55223bc08f8Sclaudio /* 553f43c4d92Sclaudio * Certificates are from manifests (has a digest and is signed with 554f43c4d92Sclaudio * another certificate) Parse the certificate, make sure its 555f43c4d92Sclaudio * signatures are valid (with CRLs), then validate the RPKI content. 556f43c4d92Sclaudio * This returns a certificate (which must not be freed) or NULL on 557f43c4d92Sclaudio * parse failure. 558f43c4d92Sclaudio */ 559f43c4d92Sclaudio static struct cert * 56032c8d2feSjob proc_parser_cert(char *file, const unsigned char *der, size_t len, 5610bc420b9Sclaudio const struct entity *entp) 562f43c4d92Sclaudio { 563f43c4d92Sclaudio struct cert *cert; 564ad462a11Sclaudio struct crl *crl; 565ad462a11Sclaudio struct auth *a; 566fd7a2857Sclaudio const char *errstr = NULL; 567f43c4d92Sclaudio 568f43c4d92Sclaudio /* Extract certificate data. */ 569f43c4d92Sclaudio 570ba153bd8Sclaudio cert = cert_parse_pre(file, der, len); 571ba153bd8Sclaudio cert = cert_parse(file, cert); 572f43c4d92Sclaudio if (cert == NULL) 573aec10a2aStb goto out; 574f43c4d92Sclaudio 5750bc420b9Sclaudio a = find_issuer(file, entp->certid, cert->aki, entp->mftaki); 576aec10a2aStb if (a == NULL) 577aec10a2aStb goto out; 578ad462a11Sclaudio crl = crl_get(&crlt, a); 579ad462a11Sclaudio 580fd7a2857Sclaudio if (!valid_x509(file, ctx, cert->x509, a, crl, &errstr) || 581ad462a11Sclaudio !valid_cert(file, a, cert)) { 582fd7a2857Sclaudio if (errstr != NULL) 583fd7a2857Sclaudio warnx("%s: %s", file, errstr); 584aec10a2aStb goto out; 585ad462a11Sclaudio } 586ad462a11Sclaudio 587ad462a11Sclaudio cert->talid = a->cert->talid; 588ad462a11Sclaudio 589891d6bceSjob if (cert->purpose == CERT_PURPOSE_BGPSEC_ROUTER) { 590aec10a2aStb if (!constraints_validate(file, cert)) 591aec10a2aStb goto out; 592891d6bceSjob } 593891d6bceSjob 594ad462a11Sclaudio /* 595ad462a11Sclaudio * Add validated CA certs to the RPKI auth tree. 596ad462a11Sclaudio */ 597ad462a11Sclaudio if (cert->purpose == CERT_PURPOSE_CA) 5980bc420b9Sclaudio auth_insert(file, &auths, cert, a); 599ad462a11Sclaudio 600f43c4d92Sclaudio return cert; 601aec10a2aStb 602aec10a2aStb out: 603aec10a2aStb cert_free(cert); 604aec10a2aStb 605aec10a2aStb return NULL; 606f43c4d92Sclaudio } 607f43c4d92Sclaudio 608b12001c0Stb static int 609b12001c0Stb proc_parser_ta_cmp(const struct cert *cert1, const struct cert *cert2) 610eae58378Sclaudio { 611b12001c0Stb if (cert1 == NULL) 612b12001c0Stb return -1; 613b12001c0Stb if (cert2 == NULL) 614b12001c0Stb return 1; 615eae58378Sclaudio 616198a0520Sclaudio /* 61793ab8db6Sjob * The standards don't specify tiebreakers. While RFC 6487 and other 61893ab8db6Sjob * sources advise against backdating, it's explicitly allowed and some 61993ab8db6Sjob * TAs do. Some TAs have also re-issued with new dates and old 62093ab8db6Sjob * serialNumber. 62193ab8db6Sjob * Our tiebreaker logic: a more recent notBefore is taken to mean a 62293ab8db6Sjob * more recent issuance, and thus preferable. Given equal notBefore 62393ab8db6Sjob * values, prefer the TA cert with the narrower validity window. This 62493ab8db6Sjob * hopefully encourages TA operators to reduce egregiously long TA 62593ab8db6Sjob * validity periods. 626198a0520Sclaudio */ 627eae58378Sclaudio 628b12001c0Stb if (cert1->notbefore < cert2->notbefore) 629b12001c0Stb return -1; 630b12001c0Stb if (cert1->notbefore > cert2->notbefore) 631b12001c0Stb return 1; 632b12001c0Stb 63393ab8db6Sjob if (cert1->notafter > cert2->notafter) 63493ab8db6Sjob return -1; 63593ab8db6Sjob if (cert1->notafter < cert2->notafter) 63693ab8db6Sjob return 1; 63793ab8db6Sjob 638b12001c0Stb /* 63918165bc1Stb * Both certs are valid from our perspective. If anything changed, 64018165bc1Stb * prefer the freshly-fetched one. We rely on cert_parse_pre() having 64118165bc1Stb * cached the extensions and thus libcrypto has already computed the 64218165bc1Stb * certs' hashes (SHA-1 for OpenSSL, SHA-512 for LibreSSL). The below 64318165bc1Stb * compares them. 644b12001c0Stb */ 645b12001c0Stb 64618165bc1Stb return X509_cmp(cert1->x509, cert2->x509) != 0; 647b12001c0Stb } 648b12001c0Stb 649b12001c0Stb /* 650b12001c0Stb * Root certificates come from TALs. Inspect and validate both options and 651b12001c0Stb * compare the two. The cert in out_cert must not be freed. Returns the file 652b12001c0Stb * name of the chosen TA. 653b12001c0Stb */ 654b12001c0Stb static char * 655b12001c0Stb proc_parser_root_cert(struct entity *entp, struct cert **out_cert) 656b12001c0Stb { 657b12001c0Stb struct cert *cert1 = NULL, *cert2 = NULL; 658b12001c0Stb char *file1 = NULL, *file2 = NULL; 659b12001c0Stb unsigned char *der = NULL, *pkey = entp->data; 660b12001c0Stb size_t der_len = 0, pkeysz = entp->datasz; 661b12001c0Stb int cmp; 662b12001c0Stb 663b12001c0Stb *out_cert = NULL; 664b12001c0Stb 665b12001c0Stb file2 = parse_filepath(entp->repoid, entp->path, entp->file, DIR_VALID); 666b12001c0Stb der = load_file(file2, &der_len); 667b12001c0Stb cert2 = cert_parse_pre(file2, der, der_len); 668b12001c0Stb free(der); 669b12001c0Stb cert2 = ta_parse(file2, cert2, pkey, pkeysz); 670b12001c0Stb 671b12001c0Stb if (!noop) { 672b12001c0Stb file1 = parse_filepath(entp->repoid, entp->path, entp->file, 673b12001c0Stb DIR_TEMP); 674b12001c0Stb der = load_file(file1, &der_len); 675b12001c0Stb cert1 = cert_parse_pre(file1, der, der_len); 676b12001c0Stb free(der); 677b12001c0Stb cert1 = ta_parse(file1, cert1, pkey, pkeysz); 678b12001c0Stb } 679b12001c0Stb 680b12001c0Stb if ((cmp = proc_parser_ta_cmp(cert1, cert2)) > 0) { 681b12001c0Stb cert_free(cert2); 682b12001c0Stb free(file2); 683b12001c0Stb 684b12001c0Stb cert1->talid = entp->talid; 685b12001c0Stb auth_insert(file1, &auths, cert1, NULL); 686b12001c0Stb 687b12001c0Stb *out_cert = cert1; 688b12001c0Stb return file1; 689b12001c0Stb } else { 690b12001c0Stb if (cmp < 0 && cert1 != NULL && cert2 != NULL) 691b12001c0Stb warnx("%s: cached TA is newer", entp->file); 692b12001c0Stb cert_free(cert1); 693b12001c0Stb free(file1); 694b12001c0Stb 695b12001c0Stb if (cert2 != 0) { 696b12001c0Stb cert2->talid = entp->talid; 697b12001c0Stb auth_insert(file2, &auths, cert2, NULL); 698b12001c0Stb } 699b12001c0Stb 700b12001c0Stb *out_cert = cert2; 701b12001c0Stb return file2; 702b12001c0Stb } 703eae58378Sclaudio } 704eae58378Sclaudio 705eae58378Sclaudio /* 706eae58378Sclaudio * Parse a ghostbuster record 707eae58378Sclaudio */ 70808ac1330Sjob static struct gbr * 70932c8d2feSjob proc_parser_gbr(char *file, const unsigned char *der, size_t len, 7100636c4d0Stb const struct entity *entp) 711eae58378Sclaudio { 712eae58378Sclaudio struct gbr *gbr; 713aec10a2aStb X509 *x509 = NULL; 7144bccd3c1Sclaudio struct crl *crl; 715d7e95037Stb struct auth *a; 716fd7a2857Sclaudio const char *errstr; 717eae58378Sclaudio 7180636c4d0Stb if ((gbr = gbr_parse(&x509, file, entp->talid, der, len)) == NULL) 719aec10a2aStb goto out; 720eae58378Sclaudio 7210bc420b9Sclaudio a = find_issuer(file, entp->certid, gbr->aki, entp->mftaki); 722aec10a2aStb if (a == NULL) 723aec10a2aStb goto out; 724c4a9443cSclaudio crl = crl_get(&crlt, a); 725eae58378Sclaudio 72608ac1330Sjob if (!valid_x509(file, ctx, x509, a, crl, &errstr)) { 727fd7a2857Sclaudio warnx("%s: %s", file, errstr); 728aec10a2aStb goto out; 72908ac1330Sjob } 73008ac1330Sjob X509_free(x509); 731aec10a2aStb x509 = NULL; 73208ac1330Sjob 73308ac1330Sjob gbr->talid = a->cert->talid; 73408ac1330Sjob 73508ac1330Sjob return gbr; 736aec10a2aStb 737aec10a2aStb out: 738aec10a2aStb gbr_free(gbr); 739aec10a2aStb X509_free(x509); 740aec10a2aStb 741aec10a2aStb return NULL; 742eae58378Sclaudio } 743eae58378Sclaudio 74411e5e285Sclaudio /* 745a29ddfd5Sjob * Parse an ASPA object 746a29ddfd5Sjob */ 747a29ddfd5Sjob static struct aspa * 74832c8d2feSjob proc_parser_aspa(char *file, const unsigned char *der, size_t len, 7490636c4d0Stb const struct entity *entp) 750a29ddfd5Sjob { 751a29ddfd5Sjob struct aspa *aspa; 752aec10a2aStb X509 *x509 = NULL; 753a29ddfd5Sjob struct auth *a; 754a29ddfd5Sjob struct crl *crl; 755fd7a2857Sclaudio const char *errstr; 756a29ddfd5Sjob 7570636c4d0Stb if ((aspa = aspa_parse(&x509, file, entp->talid, der, len)) == NULL) 758aec10a2aStb goto out; 759a29ddfd5Sjob 7600bc420b9Sclaudio a = find_issuer(file, entp->certid, aspa->aki, entp->mftaki); 761aec10a2aStb if (a == NULL) 762aec10a2aStb goto out; 763a29ddfd5Sjob crl = crl_get(&crlt, a); 764a29ddfd5Sjob 765fd7a2857Sclaudio if (!valid_x509(file, ctx, x509, a, crl, &errstr)) { 766fd7a2857Sclaudio warnx("%s: %s", file, errstr); 767aec10a2aStb goto out; 768a29ddfd5Sjob } 769a29ddfd5Sjob X509_free(x509); 770aec10a2aStb x509 = NULL; 771a29ddfd5Sjob 772a29ddfd5Sjob aspa->talid = a->cert->talid; 773a29ddfd5Sjob 774534b6674Sjob aspa->expires = x509_find_expires(aspa->notafter, a, &crlt); 775a29ddfd5Sjob 776a29ddfd5Sjob return aspa; 777aec10a2aStb 778aec10a2aStb out: 779aec10a2aStb aspa_free(aspa); 780aec10a2aStb X509_free(x509); 781aec10a2aStb 782aec10a2aStb return NULL; 783a29ddfd5Sjob } 784a29ddfd5Sjob 785a29ddfd5Sjob /* 786ee2a33daSjob * Parse a TAK object. 787ee2a33daSjob */ 788ee2a33daSjob static struct tak * 78932c8d2feSjob proc_parser_tak(char *file, const unsigned char *der, size_t len, 7900636c4d0Stb const struct entity *entp) 791ee2a33daSjob { 792ee2a33daSjob struct tak *tak; 793aec10a2aStb X509 *x509 = NULL; 794ee2a33daSjob struct crl *crl; 795ee2a33daSjob struct auth *a; 796fd7a2857Sclaudio const char *errstr; 797ee2a33daSjob 7980636c4d0Stb if ((tak = tak_parse(&x509, file, entp->talid, der, len)) == NULL) 799aec10a2aStb goto out; 800ee2a33daSjob 8010bc420b9Sclaudio a = find_issuer(file, entp->certid, tak->aki, entp->mftaki); 8021d8c6443Stb if (a == NULL) 8031d8c6443Stb goto out; 804ee2a33daSjob crl = crl_get(&crlt, a); 805ee2a33daSjob 806fd7a2857Sclaudio if (!valid_x509(file, ctx, x509, a, crl, &errstr)) { 807fd7a2857Sclaudio warnx("%s: %s", file, errstr); 808ee2a33daSjob goto out; 809fd7a2857Sclaudio } 810aec10a2aStb X509_free(x509); 811aec10a2aStb x509 = NULL; 812ee2a33daSjob 813ee2a33daSjob /* TAK EE must be signed by self-signed CA */ 814335482abStb if (a->issuer != NULL) 815ee2a33daSjob goto out; 816ee2a33daSjob 817ee2a33daSjob tak->talid = a->cert->talid; 818aec10a2aStb 819ee2a33daSjob return tak; 820aec10a2aStb 821aec10a2aStb out: 822aec10a2aStb tak_free(tak); 823aec10a2aStb X509_free(x509); 824aec10a2aStb 825aec10a2aStb return NULL; 826ee2a33daSjob } 827ee2a33daSjob 828ee2a33daSjob /* 829df512fbcSclaudio * Load the file specified by the entity information. 830df512fbcSclaudio */ 831100ded9eSclaudio static char * 8324bccd3c1Sclaudio parse_load_file(struct entity *entp, unsigned char **f, size_t *flen) 833100ded9eSclaudio { 834df512fbcSclaudio char *file; 835100ded9eSclaudio 836df512fbcSclaudio file = parse_filepath(entp->repoid, entp->path, entp->file, 837df512fbcSclaudio entp->location); 838df512fbcSclaudio if (file == NULL) 839df512fbcSclaudio errx(1, "no path to file"); 8404bccd3c1Sclaudio 841df512fbcSclaudio *f = load_file(file, flen); 842df512fbcSclaudio if (*f == NULL) 843df512fbcSclaudio warn("parse file %s", file); 844df512fbcSclaudio 8454bccd3c1Sclaudio return file; 846100ded9eSclaudio } 8474bccd3c1Sclaudio 848d7e95037Stb /* 8493b77ffafSjob * Process an entity and respond to parent process. 850f43c4d92Sclaudio */ 8516feb6ad0Sclaudio static void 8526feb6ad0Sclaudio parse_entity(struct entityq *q, struct msgbuf *msgq) 8536feb6ad0Sclaudio { 8546feb6ad0Sclaudio struct entity *entp; 8556feb6ad0Sclaudio struct tal *tal; 8566feb6ad0Sclaudio struct cert *cert; 857d3c7e816Sclaudio struct mft *mft; 8586feb6ad0Sclaudio struct roa *roa; 859a29ddfd5Sjob struct aspa *aspa; 86008ac1330Sjob struct gbr *gbr; 86108ac1330Sjob struct tak *tak; 862d4be4cdeSjob struct spl *spl; 8636feb6ad0Sclaudio struct ibuf *b; 864cabf3a3bSclaudio unsigned char *f; 86508ac1330Sjob time_t mtime, crlmtime; 866cabf3a3bSclaudio size_t flen; 867e038f1a1Sclaudio char *file, *crlfile; 8686feb6ad0Sclaudio int c; 8696feb6ad0Sclaudio 8706feb6ad0Sclaudio while ((entp = TAILQ_FIRST(q)) != NULL) { 8716feb6ad0Sclaudio TAILQ_REMOVE(q, entp, entries); 8726feb6ad0Sclaudio 873100ded9eSclaudio /* handle RTYPE_REPO first */ 874100ded9eSclaudio if (entp->type == RTYPE_REPO) { 8754bccd3c1Sclaudio repo_add(entp->repoid, entp->path, entp->file); 876100ded9eSclaudio entity_free(entp); 877100ded9eSclaudio continue; 878100ded9eSclaudio } 8796feb6ad0Sclaudio 8804bccd3c1Sclaudio /* pass back at least type, repoid and filename */ 881100ded9eSclaudio b = io_new_buffer(); 882de22d368Sclaudio io_simple_buffer(b, &entp->type, sizeof(entp->type)); 88339c0924aSclaudio io_simple_buffer(b, &entp->repoid, sizeof(entp->repoid)); 8841fc2657fSclaudio io_simple_buffer(b, &entp->talid, sizeof(entp->talid)); 885de22d368Sclaudio 886df512fbcSclaudio file = NULL; 887df512fbcSclaudio f = NULL; 88808ac1330Sjob mtime = 0; 88908ac1330Sjob crlmtime = 0; 89008ac1330Sjob 8916feb6ad0Sclaudio switch (entp->type) { 8926feb6ad0Sclaudio case RTYPE_TAL: 893df512fbcSclaudio io_str_buffer(b, entp->file); 89408ac1330Sjob io_simple_buffer(b, &mtime, sizeof(mtime)); 89541edc670Sclaudio if ((tal = tal_parse(entp->file, entp->data, 89641edc670Sclaudio entp->datasz)) == NULL) 8976feb6ad0Sclaudio errx(1, "%s: could not parse tal file", 8986feb6ad0Sclaudio entp->file); 899dc508150Sclaudio tal->id = entp->talid; 9006feb6ad0Sclaudio tal_buffer(b, tal); 9016feb6ad0Sclaudio tal_free(tal); 9026feb6ad0Sclaudio break; 9036feb6ad0Sclaudio case RTYPE_CER: 904b12001c0Stb if (entp->data != NULL) { 905b12001c0Stb file = proc_parser_root_cert(entp, &cert); 906b12001c0Stb } else { 907df512fbcSclaudio file = parse_load_file(entp, &f, &flen); 9080bc420b9Sclaudio cert = proc_parser_cert(file, f, flen, entp); 909b12001c0Stb } 910b12001c0Stb io_str_buffer(b, file); 91108ac1330Sjob if (cert != NULL) 91208ac1330Sjob mtime = cert->notbefore; 91308ac1330Sjob io_simple_buffer(b, &mtime, sizeof(mtime)); 9146feb6ad0Sclaudio c = (cert != NULL); 9156feb6ad0Sclaudio io_simple_buffer(b, &c, sizeof(int)); 916c94cf448Sclaudio if (cert != NULL) { 917c94cf448Sclaudio cert->repoid = entp->repoid; 9186feb6ad0Sclaudio cert_buffer(b, cert); 919c94cf448Sclaudio } 9206feb6ad0Sclaudio /* 9216feb6ad0Sclaudio * The parsed certificate data "cert" is now 9226feb6ad0Sclaudio * managed in the "auths" table, so don't free 923df512fbcSclaudio * it here. 9246feb6ad0Sclaudio */ 9256feb6ad0Sclaudio break; 9266feb6ad0Sclaudio case RTYPE_MFT: 92708ac1330Sjob file = proc_parser_mft(entp, &mft, &crlfile, &crlmtime); 928df512fbcSclaudio io_str_buffer(b, file); 92908ac1330Sjob if (mft != NULL) 93008ac1330Sjob mtime = mft->signtime; 93108ac1330Sjob io_simple_buffer(b, &mtime, sizeof(mtime)); 9326feb6ad0Sclaudio c = (mft != NULL); 9336feb6ad0Sclaudio io_simple_buffer(b, &c, sizeof(int)); 9346feb6ad0Sclaudio if (mft != NULL) 9356feb6ad0Sclaudio mft_buffer(b, mft); 936e038f1a1Sclaudio 937e038f1a1Sclaudio /* Push valid CRL together with the MFT. */ 938e038f1a1Sclaudio if (crlfile != NULL) { 939e038f1a1Sclaudio enum rtype type; 940e038f1a1Sclaudio struct ibuf *b2; 941e038f1a1Sclaudio 942e038f1a1Sclaudio b2 = io_new_buffer(); 943e038f1a1Sclaudio type = RTYPE_CRL; 944e038f1a1Sclaudio io_simple_buffer(b2, &type, sizeof(type)); 945e038f1a1Sclaudio io_simple_buffer(b2, &entp->repoid, 946e038f1a1Sclaudio sizeof(entp->repoid)); 9471fc2657fSclaudio io_simple_buffer(b2, &entp->talid, 9481fc2657fSclaudio sizeof(entp->talid)); 949e038f1a1Sclaudio io_str_buffer(b2, crlfile); 95008ac1330Sjob io_simple_buffer(b2, &crlmtime, 95108ac1330Sjob sizeof(crlmtime)); 952e038f1a1Sclaudio free(crlfile); 953e038f1a1Sclaudio 954e038f1a1Sclaudio io_close_buffer(msgq, b2); 955e038f1a1Sclaudio } 9566feb6ad0Sclaudio mft_free(mft); 9576feb6ad0Sclaudio break; 9586feb6ad0Sclaudio case RTYPE_ROA: 959df512fbcSclaudio file = parse_load_file(entp, &f, &flen); 960df512fbcSclaudio io_str_buffer(b, file); 9610636c4d0Stb roa = proc_parser_roa(file, f, flen, entp); 96208ac1330Sjob if (roa != NULL) 96308ac1330Sjob mtime = roa->signtime; 96408ac1330Sjob io_simple_buffer(b, &mtime, sizeof(mtime)); 9656feb6ad0Sclaudio c = (roa != NULL); 9666feb6ad0Sclaudio io_simple_buffer(b, &c, sizeof(int)); 9676feb6ad0Sclaudio if (roa != NULL) 9686feb6ad0Sclaudio roa_buffer(b, roa); 9696feb6ad0Sclaudio roa_free(roa); 9706feb6ad0Sclaudio break; 9716feb6ad0Sclaudio case RTYPE_GBR: 972df512fbcSclaudio file = parse_load_file(entp, &f, &flen); 973df512fbcSclaudio io_str_buffer(b, file); 9740636c4d0Stb gbr = proc_parser_gbr(file, f, flen, entp); 97508ac1330Sjob if (gbr != NULL) 97608ac1330Sjob mtime = gbr->signtime; 97708ac1330Sjob io_simple_buffer(b, &mtime, sizeof(mtime)); 97808ac1330Sjob gbr_free(gbr); 9796feb6ad0Sclaudio break; 980a29ddfd5Sjob case RTYPE_ASPA: 981a29ddfd5Sjob file = parse_load_file(entp, &f, &flen); 982a29ddfd5Sjob io_str_buffer(b, file); 9830636c4d0Stb aspa = proc_parser_aspa(file, f, flen, entp); 98408ac1330Sjob if (aspa != NULL) 98508ac1330Sjob mtime = aspa->signtime; 98608ac1330Sjob io_simple_buffer(b, &mtime, sizeof(mtime)); 987a29ddfd5Sjob c = (aspa != NULL); 988a29ddfd5Sjob io_simple_buffer(b, &c, sizeof(int)); 989a29ddfd5Sjob if (aspa != NULL) 990a29ddfd5Sjob aspa_buffer(b, aspa); 991a29ddfd5Sjob aspa_free(aspa); 992a29ddfd5Sjob break; 993ee2a33daSjob case RTYPE_TAK: 994ee2a33daSjob file = parse_load_file(entp, &f, &flen); 995ee2a33daSjob io_str_buffer(b, file); 9960636c4d0Stb tak = proc_parser_tak(file, f, flen, entp); 99708ac1330Sjob if (tak != NULL) 99808ac1330Sjob mtime = tak->signtime; 99908ac1330Sjob io_simple_buffer(b, &mtime, sizeof(mtime)); 100008ac1330Sjob tak_free(tak); 1001ee2a33daSjob break; 1002d4be4cdeSjob case RTYPE_SPL: 1003d4be4cdeSjob file = parse_load_file(entp, &f, &flen); 1004d4be4cdeSjob io_str_buffer(b, file); 10059463abd5Stb if (experimental) { 1006d4be4cdeSjob spl = proc_parser_spl(file, f, flen, entp); 1007d4be4cdeSjob if (spl != NULL) 1008d4be4cdeSjob mtime = spl->signtime; 10099463abd5Stb } else { 10109463abd5Stb if (verbose > 0) 10119463abd5Stb warnx("%s: skipped", file); 10129463abd5Stb spl = NULL; 10139463abd5Stb } 1014d4be4cdeSjob io_simple_buffer(b, &mtime, sizeof(mtime)); 1015d4be4cdeSjob c = (spl != NULL); 1016d4be4cdeSjob io_simple_buffer(b, &c, sizeof(int)); 1017d4be4cdeSjob if (spl != NULL) 1018d4be4cdeSjob spl_buffer(b, spl); 1019d4be4cdeSjob spl_free(spl); 1020d4be4cdeSjob break; 1021e038f1a1Sclaudio case RTYPE_CRL: 10226feb6ad0Sclaudio default: 102339c0924aSclaudio file = parse_filepath(entp->repoid, entp->path, 102439c0924aSclaudio entp->file, entp->location); 102539c0924aSclaudio io_str_buffer(b, file); 102608ac1330Sjob io_simple_buffer(b, &mtime, sizeof(mtime)); 102739c0924aSclaudio warnx("%s: unhandled type %d", file, entp->type); 102839c0924aSclaudio break; 10296feb6ad0Sclaudio } 10306feb6ad0Sclaudio 1031cabf3a3bSclaudio free(f); 1032100ded9eSclaudio free(file); 10336feb6ad0Sclaudio io_close_buffer(msgq, b); 10346feb6ad0Sclaudio entity_free(entp); 10356feb6ad0Sclaudio } 10366feb6ad0Sclaudio } 10376feb6ad0Sclaudio 1038eae58378Sclaudio /* 1039eae58378Sclaudio * Process responsible for parsing and validating content. 1040eae58378Sclaudio * All this process does is wait to be told about a file to parse, then 1041eae58378Sclaudio * it parses it and makes sure that the data being returned is fully 1042eae58378Sclaudio * validated and verified. 1043eae58378Sclaudio * The process will exit cleanly only when fd is closed. 1044eae58378Sclaudio */ 1045eae58378Sclaudio void 1046eae58378Sclaudio proc_parser(int fd) 1047eae58378Sclaudio { 1048eae58378Sclaudio struct entityq q; 104925d36c5cSclaudio struct msgbuf *msgq; 1050eae58378Sclaudio struct pollfd pfd; 10516feb6ad0Sclaudio struct entity *entp; 10527eb79a4aSclaudio struct ibuf *b, *inbuf = NULL; 1053eae58378Sclaudio 10541db5fd2bSclaudio /* Only allow access to the cache directory. */ 10551db5fd2bSclaudio if (unveil(".", "r") == -1) 10561db5fd2bSclaudio err(1, "unveil cachedir"); 10571db5fd2bSclaudio if (pledge("stdio rpath", NULL) == -1) 10581db5fd2bSclaudio err(1, "pledge"); 10591db5fd2bSclaudio 1060eae58378Sclaudio ERR_load_crypto_strings(); 1061eae58378Sclaudio OpenSSL_add_all_ciphers(); 1062eae58378Sclaudio OpenSSL_add_all_digests(); 10634bccd3c1Sclaudio x509_init_oid(); 1064891d6bceSjob constraints_parse(); 1065eae58378Sclaudio 1066eae58378Sclaudio if ((ctx = X509_STORE_CTX_new()) == NULL) 1067c0528901Stb err(1, "X509_STORE_CTX_new"); 1068318f0572Sjob if ((bn_ctx = BN_CTX_new()) == NULL) 1069318f0572Sjob err(1, "BN_CTX_new"); 1070eae58378Sclaudio 1071eae58378Sclaudio TAILQ_INIT(&q); 1072eae58378Sclaudio 1073*b5fa5d51Sclaudio if ((msgq = msgbuf_new_reader(sizeof(size_t), io_parse_hdr, NULL)) == 1074*b5fa5d51Sclaudio NULL) 107525d36c5cSclaudio err(1, NULL); 1076eae58378Sclaudio 1077eae58378Sclaudio pfd.fd = fd; 1078eae58378Sclaudio 1079eae58378Sclaudio for (;;) { 1080eae58378Sclaudio pfd.events = POLLIN; 108125d36c5cSclaudio if (msgbuf_queuelen(msgq) > 0) 1082eae58378Sclaudio pfd.events |= POLLOUT; 1083eae58378Sclaudio 10847ba5db23Sclaudio if (poll(&pfd, 1, INFTIM) == -1) { 10857ba5db23Sclaudio if (errno == EINTR) 10867ba5db23Sclaudio continue; 1087eae58378Sclaudio err(1, "poll"); 10887ba5db23Sclaudio } 1089eae58378Sclaudio if ((pfd.revents & (POLLERR|POLLNVAL))) 1090eae58378Sclaudio errx(1, "poll: bad descriptor"); 1091eae58378Sclaudio 1092eae58378Sclaudio /* If the parent closes, return immediately. */ 1093eae58378Sclaudio 1094eae58378Sclaudio if ((pfd.revents & POLLHUP)) 1095eae58378Sclaudio break; 1096eae58378Sclaudio 1097eae58378Sclaudio if ((pfd.revents & POLLIN)) { 1098*b5fa5d51Sclaudio switch (ibuf_read(fd, msgq)) { 1099*b5fa5d51Sclaudio case -1: 1100*b5fa5d51Sclaudio err(1, "ibuf_read"); 1101*b5fa5d51Sclaudio case 0: 1102*b5fa5d51Sclaudio errx(1, "ibuf_read: connection closed"); 1103*b5fa5d51Sclaudio } 1104*b5fa5d51Sclaudio while ((b = io_buf_get(msgq)) != NULL) { 1105eae58378Sclaudio entp = calloc(1, sizeof(struct entity)); 1106eae58378Sclaudio if (entp == NULL) 1107eae58378Sclaudio err(1, NULL); 11087eb79a4aSclaudio entity_read_req(b, entp); 1109eae58378Sclaudio TAILQ_INSERT_TAIL(&q, entp, entries); 11107eb79a4aSclaudio ibuf_free(b); 11117eb79a4aSclaudio } 1112eae58378Sclaudio } 1113eae58378Sclaudio 1114eae58378Sclaudio if (pfd.revents & POLLOUT) { 111525d36c5cSclaudio if (msgbuf_write(fd, msgq) == -1) { 11169aadc625Sclaudio if (errno == EPIPE) 1117eae58378Sclaudio errx(1, "write: connection closed"); 11189aadc625Sclaudio else 1119eae58378Sclaudio err(1, "write"); 1120eae58378Sclaudio } 1121eae58378Sclaudio } 1122eae58378Sclaudio 112325d36c5cSclaudio parse_entity(&q, msgq); 1124eae58378Sclaudio } 1125eae58378Sclaudio 1126eae58378Sclaudio while ((entp = TAILQ_FIRST(&q)) != NULL) { 1127eae58378Sclaudio TAILQ_REMOVE(&q, entp, entries); 1128eae58378Sclaudio entity_free(entp); 1129eae58378Sclaudio } 1130eae58378Sclaudio 113191176c18Sjob auth_tree_free(&auths); 113291176c18Sjob crl_tree_free(&crlt); 1133eae58378Sclaudio 1134eae58378Sclaudio X509_STORE_CTX_free(ctx); 1135318f0572Sjob BN_CTX_free(bn_ctx); 1136318f0572Sjob 113725d36c5cSclaudio msgbuf_free(msgq); 113891176c18Sjob ibuf_free(inbuf); 113991176c18Sjob 11401d8c6443Stb if (certid > CERTID_MAX) 11411d8c6443Stb errx(1, "processing incomplete: too many certificates"); 11421d8c6443Stb 11436feb6ad0Sclaudio exit(0); 1144eae58378Sclaudio } 1145