1 /* $OpenBSD: parser.c,v 1.82 2023/01/06 16:06:43 claudio Exp $ */ 2 /* 3 * Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org> 4 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/queue.h> 20 #include <sys/tree.h> 21 #include <sys/types.h> 22 23 #include <err.h> 24 #include <fcntl.h> 25 #include <poll.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <limits.h> 30 #include <unistd.h> 31 #include <imsg.h> 32 33 #include <openssl/asn1.h> 34 #include <openssl/err.h> 35 #include <openssl/evp.h> 36 #include <openssl/x509.h> 37 #include <openssl/x509v3.h> 38 39 #include "extern.h" 40 41 static X509_STORE_CTX *ctx; 42 static struct auth_tree auths = RB_INITIALIZER(&auths); 43 static struct crl_tree crlt = RB_INITIALIZER(&crlt); 44 45 struct parse_repo { 46 RB_ENTRY(parse_repo) entry; 47 char *path; 48 char *validpath; 49 unsigned int id; 50 }; 51 52 static RB_HEAD(repo_tree, parse_repo) repos = RB_INITIALIZER(&repos); 53 54 static inline int 55 repocmp(struct parse_repo *a, struct parse_repo *b) 56 { 57 return a->id - b->id; 58 } 59 60 RB_GENERATE_STATIC(repo_tree, parse_repo, entry, repocmp); 61 62 static struct parse_repo * 63 repo_get(unsigned int id) 64 { 65 struct parse_repo needle = { .id = id }; 66 67 return RB_FIND(repo_tree, &repos, &needle); 68 } 69 70 static void 71 repo_add(unsigned int id, char *path, char *validpath) 72 { 73 struct parse_repo *rp; 74 75 if ((rp = calloc(1, sizeof(*rp))) == NULL) 76 err(1, NULL); 77 rp->id = id; 78 if (path != NULL) 79 if ((rp->path = strdup(path)) == NULL) 80 err(1, NULL); 81 if (validpath != NULL) 82 if ((rp->validpath = strdup(validpath)) == NULL) 83 err(1, NULL); 84 85 if (RB_INSERT(repo_tree, &repos, rp) != NULL) 86 errx(1, "repository already added: id %d, %s", id, path); 87 } 88 89 /* 90 * Build access path to file based on repoid, path, location and file values. 91 */ 92 static char * 93 parse_filepath(unsigned int repoid, const char *path, const char *file, 94 enum location loc) 95 { 96 struct parse_repo *rp; 97 char *fn, *repopath; 98 99 /* build file path based on repoid, entity path and filename */ 100 rp = repo_get(repoid); 101 if (rp == NULL) 102 errx(1, "build file path: repository %u missing", repoid); 103 104 if (loc == DIR_VALID) 105 repopath = rp->validpath; 106 else 107 repopath = rp->path; 108 109 if (repopath == NULL) 110 return NULL; 111 112 if (path == NULL) { 113 if (asprintf(&fn, "%s/%s", repopath, file) == -1) 114 err(1, NULL); 115 } else { 116 if (asprintf(&fn, "%s/%s/%s", repopath, path, file) == -1) 117 err(1, NULL); 118 } 119 return fn; 120 } 121 122 /* 123 * Parse and validate a ROA. 124 * This is standard stuff. 125 * Returns the roa on success, NULL on failure. 126 */ 127 static struct roa * 128 proc_parser_roa(char *file, const unsigned char *der, size_t len) 129 { 130 struct roa *roa; 131 struct auth *a; 132 struct crl *crl; 133 X509 *x509; 134 const char *errstr; 135 136 if ((roa = roa_parse(&x509, file, der, len)) == NULL) 137 return NULL; 138 139 a = valid_ski_aki(file, &auths, roa->ski, roa->aki); 140 crl = crl_get(&crlt, a); 141 142 if (!valid_x509(file, ctx, x509, a, crl, &errstr)) { 143 warnx("%s: %s", file, errstr); 144 X509_free(x509); 145 roa_free(roa); 146 return NULL; 147 } 148 X509_free(x509); 149 150 roa->talid = a->cert->talid; 151 152 /* 153 * Check CRL to figure out the soonest transitive expiry moment 154 */ 155 if (crl != NULL && roa->expires > crl->expires) 156 roa->expires = crl->expires; 157 158 /* 159 * Scan the cert tree to figure out the soonest transitive 160 * expiry moment 161 */ 162 for (; a != NULL; a = a->parent) { 163 if (roa->expires > a->cert->expires) 164 roa->expires = a->cert->expires; 165 } 166 167 return roa; 168 } 169 170 /* 171 * Check all files and their hashes in a MFT structure. 172 * Return zero on failure, non-zero on success. 173 */ 174 static int 175 proc_parser_mft_check(const char *fn, struct mft *p) 176 { 177 const enum location loc[2] = { DIR_TEMP, DIR_VALID }; 178 size_t i; 179 int rc = 1; 180 char *path; 181 182 for (i = 0; i < p->filesz; i++) { 183 struct mftfile *m = &p->files[i]; 184 int try, fd = -1, noent = 0, valid = 0; 185 for (try = 0; try < 2 && !valid; try++) { 186 if ((path = parse_filepath(p->repoid, p->path, m->file, 187 loc[try])) == NULL) 188 continue; 189 fd = open(path, O_RDONLY); 190 if (fd == -1 && errno == ENOENT) 191 noent++; 192 free(path); 193 194 /* remember which path was checked */ 195 m->location = loc[try]; 196 valid = valid_filehash(fd, m->hash, sizeof(m->hash)); 197 } 198 199 if (!valid) { 200 /* silently skip not-existing unknown files */ 201 if (m->type == RTYPE_INVALID && noent == 2) 202 continue; 203 warnx("%s: bad message digest for %s", fn, m->file); 204 rc = 0; 205 continue; 206 } 207 } 208 209 return rc; 210 } 211 212 /* 213 * Load the correct CRL using the info from the MFT. 214 */ 215 static struct crl * 216 parse_load_crl_from_mft(struct entity *entp, struct mft *mft, enum location loc) 217 { 218 struct crl *crl = NULL; 219 unsigned char *f = NULL; 220 char *fn = NULL; 221 size_t flen; 222 223 while (1) { 224 fn = parse_filepath(entp->repoid, entp->path, mft->crl, loc); 225 if (fn == NULL) 226 goto next; 227 228 f = load_file(fn, &flen); 229 if (f == NULL && errno != ENOENT) 230 warn("parse file %s", fn); 231 if (f == NULL) 232 goto next; 233 if (!valid_hash(f, flen, mft->crlhash, sizeof(mft->crlhash))) 234 goto next; 235 crl = crl_parse(fn, f, flen); 236 237 next: 238 free(f); 239 free(fn); 240 f = NULL; 241 fn = NULL; 242 243 if (crl != NULL) 244 return crl; 245 if (loc == DIR_TEMP) 246 loc = DIR_VALID; 247 else 248 return NULL; 249 } 250 } 251 252 /* 253 * Parse and validate a manifest file. Skip checking the fileandhash 254 * this is done in the post check. After this step we know the mft is 255 * valid and can be compared. 256 * Return the mft on success or NULL on failure. 257 */ 258 static struct mft * 259 proc_parser_mft_pre(char *file, const unsigned char *der, size_t len, 260 struct entity *entp, enum location loc, struct crl **crl, 261 const char **errstr) 262 { 263 struct mft *mft; 264 X509 *x509; 265 struct auth *a; 266 267 *crl = NULL; 268 *errstr = NULL; 269 if ((mft = mft_parse(&x509, file, der, len)) == NULL) 270 return NULL; 271 *crl = parse_load_crl_from_mft(entp, mft, loc); 272 273 a = valid_ski_aki(file, &auths, mft->ski, mft->aki); 274 if (!valid_x509(file, ctx, x509, a, *crl, errstr)) { 275 X509_free(x509); 276 mft_free(mft); 277 crl_free(*crl); 278 *crl = NULL; 279 return NULL; 280 } 281 X509_free(x509); 282 283 mft->repoid = entp->repoid; 284 return mft; 285 } 286 287 /* 288 * Do the end of manifest validation. 289 * Return the mft on success or NULL on failure. 290 */ 291 static struct mft * 292 proc_parser_mft_post(char *file, struct mft *mft, const char *path, 293 const char *errstr) 294 { 295 /* check that now is not before from */ 296 time_t now = time(NULL); 297 298 if (mft == NULL) { 299 if (errstr == NULL) 300 errstr = "no valid mft available"; 301 warnx("%s: %s", file, errstr); 302 return NULL; 303 } 304 305 /* check that now is not before from */ 306 if (now < mft->valid_since) { 307 warnx("%s: mft not yet valid %s", file, 308 time2str(mft->valid_since)); 309 mft->stale = 1; 310 } 311 /* check that now is not after until */ 312 if (now > mft->valid_until) { 313 warnx("%s: mft expired on %s", file, 314 time2str(mft->valid_until)); 315 mft->stale = 1; 316 } 317 318 if (path != NULL) 319 if ((mft->path = strdup(path)) == NULL) 320 err(1, NULL); 321 322 if (!mft->stale) 323 if (!proc_parser_mft_check(file, mft)) { 324 mft_free(mft); 325 return NULL; 326 } 327 328 return mft; 329 } 330 331 /* 332 * Load the most recent MFT by opening both options and comparing the two. 333 */ 334 static char * 335 proc_parser_mft(struct entity *entp, struct mft **mp) 336 { 337 struct mft *mft1 = NULL, *mft2 = NULL; 338 struct crl *crl, *crl1 = NULL, *crl2 = NULL; 339 char *f, *file, *file1, *file2; 340 const char *err1, *err2; 341 size_t flen; 342 343 *mp = NULL; 344 file1 = parse_filepath(entp->repoid, entp->path, entp->file, DIR_VALID); 345 file2 = parse_filepath(entp->repoid, entp->path, entp->file, DIR_TEMP); 346 347 if (file1 != NULL) { 348 f = load_file(file1, &flen); 349 if (f == NULL && errno != ENOENT) 350 warn("parse file %s", file1); 351 mft1 = proc_parser_mft_pre(file1, f, flen, entp, DIR_VALID, 352 &crl1, &err1); 353 free(f); 354 } 355 if (file2 != NULL) { 356 f = load_file(file2, &flen); 357 if (f == NULL && errno != ENOENT) 358 warn("parse file %s", file2); 359 mft2 = proc_parser_mft_pre(file2, f, flen, entp, DIR_TEMP, 360 &crl2, &err2); 361 free(f); 362 } 363 364 /* overload error from temp file if it is set */ 365 if (mft1 == NULL && mft2 == NULL) 366 if (err2 != NULL) 367 err1 = err2; 368 369 if (mft_compare(mft1, mft2) == 1) { 370 mft_free(mft2); 371 crl_free(crl2); 372 free(file2); 373 *mp = proc_parser_mft_post(file1, mft1, entp->path, err1); 374 crl = crl1; 375 file = file1; 376 } else { 377 mft_free(mft1); 378 crl_free(crl1); 379 free(file1); 380 *mp = proc_parser_mft_post(file2, mft2, entp->path, err2); 381 crl = crl2; 382 file = file2; 383 } 384 385 if (*mp != NULL) { 386 if (!crl_insert(&crlt, crl)) { 387 warnx("%s: duplicate AKI %s", file, crl->aki); 388 crl_free(crl); 389 } 390 } else { 391 crl_free(crl); 392 } 393 return file; 394 } 395 396 /* 397 * Certificates are from manifests (has a digest and is signed with 398 * another certificate) Parse the certificate, make sure its 399 * signatures are valid (with CRLs), then validate the RPKI content. 400 * This returns a certificate (which must not be freed) or NULL on 401 * parse failure. 402 */ 403 static struct cert * 404 proc_parser_cert(char *file, const unsigned char *der, size_t len) 405 { 406 struct cert *cert; 407 struct crl *crl; 408 struct auth *a; 409 const char *errstr = NULL; 410 411 /* Extract certificate data. */ 412 413 cert = cert_parse_pre(file, der, len); 414 cert = cert_parse(file, cert); 415 if (cert == NULL) 416 return NULL; 417 418 a = valid_ski_aki(file, &auths, cert->ski, cert->aki); 419 crl = crl_get(&crlt, a); 420 421 if (!valid_x509(file, ctx, cert->x509, a, crl, &errstr) || 422 !valid_cert(file, a, cert)) { 423 if (errstr != NULL) 424 warnx("%s: %s", file, errstr); 425 cert_free(cert); 426 return NULL; 427 } 428 429 cert->talid = a->cert->talid; 430 431 /* 432 * Add validated CA certs to the RPKI auth tree. 433 */ 434 if (cert->purpose == CERT_PURPOSE_CA) 435 auth_insert(&auths, cert, a); 436 437 return cert; 438 } 439 440 /* 441 * Root certificates come from TALs (has a pkey and is self-signed). 442 * Parse the certificate, ensure that its public key matches the 443 * known public key from the TAL, and then validate the RPKI 444 * content. 445 * 446 * This returns a certificate (which must not be freed) or NULL on 447 * parse failure. 448 */ 449 static struct cert * 450 proc_parser_root_cert(char *file, const unsigned char *der, size_t len, 451 unsigned char *pkey, size_t pkeysz, int talid) 452 { 453 struct cert *cert; 454 455 /* Extract certificate data. */ 456 457 cert = cert_parse_pre(file, der, len); 458 cert = ta_parse(file, cert, pkey, pkeysz); 459 if (cert == NULL) 460 return NULL; 461 462 if (!valid_ta(file, &auths, cert)) { 463 warnx("%s: certificate not a valid ta", file); 464 cert_free(cert); 465 return NULL; 466 } 467 468 cert->talid = talid; 469 470 /* 471 * Add valid roots to the RPKI auth tree. 472 */ 473 auth_insert(&auths, cert, NULL); 474 475 return cert; 476 } 477 478 /* 479 * Parse a ghostbuster record 480 */ 481 static void 482 proc_parser_gbr(char *file, const unsigned char *der, size_t len) 483 { 484 struct gbr *gbr; 485 X509 *x509; 486 struct crl *crl; 487 struct auth *a; 488 const char *errstr; 489 490 if ((gbr = gbr_parse(&x509, file, der, len)) == NULL) 491 return; 492 493 a = valid_ski_aki(file, &auths, gbr->ski, gbr->aki); 494 crl = crl_get(&crlt, a); 495 496 /* return value can be ignored since nothing happens here */ 497 if (!valid_x509(file, ctx, x509, a, crl, &errstr)) 498 warnx("%s: %s", file, errstr); 499 500 X509_free(x509); 501 gbr_free(gbr); 502 } 503 504 /* 505 * Parse an ASPA object 506 */ 507 static struct aspa * 508 proc_parser_aspa(char *file, const unsigned char *der, size_t len) 509 { 510 struct aspa *aspa; 511 struct auth *a; 512 struct crl *crl; 513 X509 *x509; 514 const char *errstr; 515 516 if ((aspa = aspa_parse(&x509, file, der, len)) == NULL) 517 return NULL; 518 519 a = valid_ski_aki(file, &auths, aspa->ski, aspa->aki); 520 crl = crl_get(&crlt, a); 521 522 if (!valid_x509(file, ctx, x509, a, crl, &errstr)) { 523 warnx("%s: %s", file, errstr); 524 X509_free(x509); 525 aspa_free(aspa); 526 return NULL; 527 } 528 X509_free(x509); 529 530 aspa->talid = a->cert->talid; 531 532 if (crl != NULL && aspa->expires > crl->expires) 533 aspa->expires = crl->expires; 534 535 for (; a != NULL; a = a->parent) { 536 if (aspa->expires > a->cert->expires) 537 aspa->expires = a->cert->expires; 538 } 539 540 return aspa; 541 } 542 543 /* 544 * Parse a TAK object. 545 */ 546 static struct tak * 547 proc_parser_tak(char *file, const unsigned char *der, size_t len) 548 { 549 struct tak *tak; 550 X509 *x509; 551 struct crl *crl; 552 struct auth *a; 553 const char *errstr; 554 int rc = 0; 555 556 if ((tak = tak_parse(&x509, file, der, len)) == NULL) 557 return NULL; 558 559 a = valid_ski_aki(file, &auths, tak->ski, tak->aki); 560 crl = crl_get(&crlt, a); 561 562 if (!valid_x509(file, ctx, x509, a, crl, &errstr)) { 563 warnx("%s: %s", file, errstr); 564 goto out; 565 } 566 567 /* TAK EE must be signed by self-signed CA */ 568 if (a->parent != NULL) 569 goto out; 570 571 tak->talid = a->cert->talid; 572 rc = 1; 573 out: 574 if (rc == 0) { 575 tak_free(tak); 576 tak = NULL; 577 } 578 X509_free(x509); 579 return tak; 580 } 581 582 /* 583 * Load the file specified by the entity information. 584 */ 585 static char * 586 parse_load_file(struct entity *entp, unsigned char **f, size_t *flen) 587 { 588 char *file; 589 590 file = parse_filepath(entp->repoid, entp->path, entp->file, 591 entp->location); 592 if (file == NULL) 593 errx(1, "no path to file"); 594 595 *f = load_file(file, flen); 596 if (*f == NULL) 597 warn("parse file %s", file); 598 599 return file; 600 } 601 602 /* 603 * Process an entity and responing to parent process. 604 */ 605 static void 606 parse_entity(struct entityq *q, struct msgbuf *msgq) 607 { 608 struct entity *entp; 609 struct tal *tal; 610 struct cert *cert; 611 struct mft *mft; 612 struct roa *roa; 613 struct aspa *aspa; 614 struct ibuf *b; 615 unsigned char *f; 616 size_t flen; 617 char *file; 618 int c; 619 620 while ((entp = TAILQ_FIRST(q)) != NULL) { 621 TAILQ_REMOVE(q, entp, entries); 622 623 /* handle RTYPE_REPO first */ 624 if (entp->type == RTYPE_REPO) { 625 repo_add(entp->repoid, entp->path, entp->file); 626 entity_free(entp); 627 continue; 628 } 629 630 /* pass back at least type, repoid and filename */ 631 b = io_new_buffer(); 632 io_simple_buffer(b, &entp->type, sizeof(entp->type)); 633 io_simple_buffer(b, &entp->repoid, sizeof(entp->repoid)); 634 635 file = NULL; 636 f = NULL; 637 switch (entp->type) { 638 case RTYPE_TAL: 639 io_str_buffer(b, entp->file); 640 if ((tal = tal_parse(entp->file, entp->data, 641 entp->datasz)) == NULL) 642 errx(1, "%s: could not parse tal file", 643 entp->file); 644 tal->id = entp->talid; 645 tal_buffer(b, tal); 646 tal_free(tal); 647 break; 648 case RTYPE_CER: 649 file = parse_load_file(entp, &f, &flen); 650 io_str_buffer(b, file); 651 if (entp->data != NULL) 652 cert = proc_parser_root_cert(file, 653 f, flen, entp->data, entp->datasz, 654 entp->talid); 655 else 656 cert = proc_parser_cert(file, f, flen); 657 c = (cert != NULL); 658 io_simple_buffer(b, &c, sizeof(int)); 659 if (cert != NULL) { 660 cert->repoid = entp->repoid; 661 cert_buffer(b, cert); 662 } 663 /* 664 * The parsed certificate data "cert" is now 665 * managed in the "auths" table, so don't free 666 * it here. 667 */ 668 break; 669 case RTYPE_CRL: 670 /* 671 * CRLs are already loaded with the MFT so nothing 672 * really needs to be done here. 673 */ 674 file = parse_filepath(entp->repoid, entp->path, 675 entp->file, entp->location); 676 io_str_buffer(b, file); 677 break; 678 case RTYPE_MFT: 679 file = proc_parser_mft(entp, &mft); 680 io_str_buffer(b, file); 681 c = (mft != NULL); 682 io_simple_buffer(b, &c, sizeof(int)); 683 if (mft != NULL) 684 mft_buffer(b, mft); 685 mft_free(mft); 686 break; 687 case RTYPE_ROA: 688 file = parse_load_file(entp, &f, &flen); 689 io_str_buffer(b, file); 690 roa = proc_parser_roa(file, f, flen); 691 c = (roa != NULL); 692 io_simple_buffer(b, &c, sizeof(int)); 693 if (roa != NULL) 694 roa_buffer(b, roa); 695 roa_free(roa); 696 break; 697 case RTYPE_GBR: 698 file = parse_load_file(entp, &f, &flen); 699 io_str_buffer(b, file); 700 proc_parser_gbr(file, f, flen); 701 break; 702 case RTYPE_ASPA: 703 file = parse_load_file(entp, &f, &flen); 704 io_str_buffer(b, file); 705 aspa = proc_parser_aspa(file, f, flen); 706 c = (aspa != NULL); 707 io_simple_buffer(b, &c, sizeof(int)); 708 if (aspa != NULL) 709 aspa_buffer(b, aspa); 710 aspa_free(aspa); 711 break; 712 case RTYPE_TAK: 713 file = parse_load_file(entp, &f, &flen); 714 io_str_buffer(b, file); 715 proc_parser_tak(file, f, flen); 716 break; 717 default: 718 file = parse_filepath(entp->repoid, entp->path, 719 entp->file, entp->location); 720 io_str_buffer(b, file); 721 warnx("%s: unhandled type %d", file, entp->type); 722 break; 723 } 724 725 free(f); 726 free(file); 727 io_close_buffer(msgq, b); 728 entity_free(entp); 729 } 730 } 731 732 /* 733 * Process responsible for parsing and validating content. 734 * All this process does is wait to be told about a file to parse, then 735 * it parses it and makes sure that the data being returned is fully 736 * validated and verified. 737 * The process will exit cleanly only when fd is closed. 738 */ 739 void 740 proc_parser(int fd) 741 { 742 struct entityq q; 743 struct msgbuf msgq; 744 struct pollfd pfd; 745 struct entity *entp; 746 struct ibuf *b, *inbuf = NULL; 747 748 /* Only allow access to the cache directory. */ 749 if (unveil(".", "r") == -1) 750 err(1, "unveil cachedir"); 751 if (pledge("stdio rpath", NULL) == -1) 752 err(1, "pledge"); 753 754 ERR_load_crypto_strings(); 755 OpenSSL_add_all_ciphers(); 756 OpenSSL_add_all_digests(); 757 x509_init_oid(); 758 759 if ((ctx = X509_STORE_CTX_new()) == NULL) 760 cryptoerrx("X509_STORE_CTX_new"); 761 762 TAILQ_INIT(&q); 763 764 msgbuf_init(&msgq); 765 msgq.fd = fd; 766 767 pfd.fd = fd; 768 769 for (;;) { 770 pfd.events = POLLIN; 771 if (msgq.queued) 772 pfd.events |= POLLOUT; 773 774 if (poll(&pfd, 1, INFTIM) == -1) { 775 if (errno == EINTR) 776 continue; 777 err(1, "poll"); 778 } 779 if ((pfd.revents & (POLLERR|POLLNVAL))) 780 errx(1, "poll: bad descriptor"); 781 782 /* If the parent closes, return immediately. */ 783 784 if ((pfd.revents & POLLHUP)) 785 break; 786 787 if ((pfd.revents & POLLIN)) { 788 b = io_buf_read(fd, &inbuf); 789 if (b != NULL) { 790 entp = calloc(1, sizeof(struct entity)); 791 if (entp == NULL) 792 err(1, NULL); 793 entity_read_req(b, entp); 794 TAILQ_INSERT_TAIL(&q, entp, entries); 795 ibuf_free(b); 796 } 797 } 798 799 if (pfd.revents & POLLOUT) { 800 switch (msgbuf_write(&msgq)) { 801 case 0: 802 errx(1, "write: connection closed"); 803 case -1: 804 err(1, "write"); 805 } 806 } 807 808 parse_entity(&q, &msgq); 809 } 810 811 while ((entp = TAILQ_FIRST(&q)) != NULL) { 812 TAILQ_REMOVE(&q, entp, entries); 813 entity_free(entp); 814 } 815 816 auth_tree_free(&auths); 817 crl_tree_free(&crlt); 818 819 X509_STORE_CTX_free(ctx); 820 msgbuf_clear(&msgq); 821 822 ibuf_free(inbuf); 823 824 exit(0); 825 } 826