filemode.c revision 1.21
1/* $OpenBSD: filemode.c,v 1.21 2023/03/07 14:49:32 job 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 <assert.h> 24#include <err.h> 25#include <fcntl.h> 26#include <poll.h> 27#include <stdio.h> 28#include <stdlib.h> 29#include <string.h> 30#include <limits.h> 31#include <unistd.h> 32#include <imsg.h> 33 34#include <openssl/asn1.h> 35#include <openssl/err.h> 36#include <openssl/evp.h> 37#include <openssl/pem.h> 38#include <openssl/x509.h> 39#include <openssl/x509v3.h> 40 41#include "extern.h" 42 43extern int verbose; 44 45static X509_STORE_CTX *ctx; 46static struct auth_tree auths = RB_INITIALIZER(&auths); 47static struct crl_tree crlt = RB_INITIALIZER(&crlt); 48 49struct tal *talobj[TALSZ_MAX]; 50 51/* 52 * Use the X509 CRL Distribution Points to locate the CRL needed for 53 * verification. 54 */ 55static void 56parse_load_crl(char *uri) 57{ 58 struct crl *crl; 59 char *f; 60 size_t flen; 61 62 if (uri == NULL) 63 return; 64 if (strncmp(uri, "rsync://", strlen("rsync://")) != 0) { 65 warnx("bad CRL distribution point URI %s", uri); 66 return; 67 } 68 uri += strlen("rsync://"); 69 70 f = load_file(uri, &flen); 71 if (f == NULL) { 72 warn("parse file %s", uri); 73 return; 74 } 75 76 crl = crl_parse(uri, f, flen); 77 if (crl != NULL && !crl_insert(&crlt, crl)) 78 crl_free(crl); 79 80 free(f); 81} 82 83/* 84 * Parse the cert pointed at by the AIA URI while doing that also load 85 * the CRL of this cert. While the CRL is validated the returned cert 86 * is not. The caller needs to make sure it is validated once all 87 * necessary certs were loaded. Returns NULL on failure. 88 */ 89static struct cert * 90parse_load_cert(char *uri) 91{ 92 struct cert *cert = NULL; 93 char *f; 94 size_t flen; 95 96 if (uri == NULL) 97 return NULL; 98 99 if (strncmp(uri, "rsync://", strlen("rsync://")) != 0) { 100 warnx("bad authority information access URI %s", uri); 101 return NULL; 102 } 103 uri += strlen("rsync://"); 104 105 f = load_file(uri, &flen); 106 if (f == NULL) { 107 warn("parse file %s", uri); 108 goto done; 109 } 110 111 cert = cert_parse_pre(uri, f, flen); 112 free(f); 113 114 if (cert == NULL) 115 goto done; 116 if (cert->purpose != CERT_PURPOSE_CA) { 117 warnx("AIA reference to bgpsec cert %s", uri); 118 goto done; 119 } 120 /* try to load the CRL of this cert */ 121 parse_load_crl(cert->crl); 122 123 return cert; 124 125 done: 126 cert_free(cert); 127 return NULL; 128} 129 130/* 131 * Build the certificate chain by using the Authority Information Access. 132 * This requires that the TA are already validated and added to the auths 133 * tree. Once the TA is located in the chain the chain is validated in 134 * reverse order. 135 */ 136static void 137parse_load_certchain(char *uri) 138{ 139 struct cert *stack[MAX_CERT_DEPTH] = { 0 }; 140 char *filestack[MAX_CERT_DEPTH]; 141 struct cert *cert; 142 struct crl *crl; 143 struct auth *a; 144 const char *errstr; 145 int i; 146 147 for (i = 0; i < MAX_CERT_DEPTH; i++) { 148 filestack[i] = uri; 149 stack[i] = cert = parse_load_cert(uri); 150 if (cert == NULL || cert->purpose != CERT_PURPOSE_CA) { 151 warnx("failed to build authority chain"); 152 goto fail; 153 } 154 if (auth_find(&auths, cert->ski) != NULL) { 155 assert(i == 0); 156 goto fail; 157 } 158 if ((a = auth_find(&auths, cert->aki)) != NULL) 159 break; /* found chain to TA */ 160 uri = cert->aia; 161 } 162 163 if (i >= MAX_CERT_DEPTH) { 164 warnx("authority chain exceeds max depth of %d", 165 MAX_CERT_DEPTH); 166 goto fail; 167 } 168 169 /* TA found play back the stack and add all certs */ 170 for (; i >= 0; i--) { 171 cert = stack[i]; 172 uri = filestack[i]; 173 174 crl = crl_get(&crlt, a); 175 if (!valid_x509(uri, ctx, cert->x509, a, crl, &errstr) || 176 !valid_cert(uri, a, cert)) { 177 if (errstr != NULL) 178 warnx("%s: %s", uri, errstr); 179 goto fail; 180 } 181 cert->talid = a->cert->talid; 182 a = auth_insert(&auths, cert, a); 183 stack[i] = NULL; 184 } 185 186 return; 187fail: 188 for (i = 0; i < MAX_CERT_DEPTH; i++) 189 cert_free(stack[i]); 190} 191 192static void 193parse_load_ta(struct tal *tal) 194{ 195 const char *filename; 196 struct cert *cert; 197 unsigned char *f = NULL; 198 char *file; 199 size_t flen; 200 201 /* does not matter which URI, all end with same filename */ 202 filename = strrchr(tal->uri[0], '/'); 203 assert(filename); 204 205 if (asprintf(&file, "ta/%s%s", tal->descr, filename) == -1) 206 err(1, NULL); 207 208 f = load_file(file, &flen); 209 if (f == NULL) { 210 warn("parse file %s", file); 211 goto out; 212 } 213 214 /* Extract certificate data. */ 215 cert = cert_parse_pre(file, f, flen); 216 cert = ta_parse(file, cert, tal->pkey, tal->pkeysz); 217 if (cert == NULL) 218 goto out; 219 220 cert->talid = tal->id; 221 222 if (!valid_ta(file, &auths, cert)) 223 cert_free(cert); 224 else 225 auth_insert(&auths, cert, NULL); 226out: 227 free(file); 228 free(f); 229} 230 231static struct tal * 232find_tal(struct cert *cert) 233{ 234 EVP_PKEY *pk, *opk; 235 struct tal *tal; 236 int i; 237 238 if ((opk = X509_get0_pubkey(cert->x509)) == NULL) 239 return NULL; 240 241 for (i = 0; i < TALSZ_MAX; i++) { 242 const unsigned char *pkey; 243 244 if (talobj[i] == NULL) 245 break; 246 tal = talobj[i]; 247 pkey = tal->pkey; 248 pk = d2i_PUBKEY(NULL, &pkey, tal->pkeysz); 249 if (pk == NULL) 250 continue; 251 if (EVP_PKEY_cmp(pk, opk) == 1) { 252 EVP_PKEY_free(pk); 253 return tal; 254 } 255 EVP_PKEY_free(pk); 256 } 257 return NULL; 258} 259 260static void 261print_certification_path(const char *crl, const char *aia, const struct auth *a) 262{ 263 if (crl != NULL) 264 printf("Certification path: %s\n", crl); 265 if (aia != NULL) 266 printf(" %s\n", aia); 267 268 for (; a != NULL; a = a->parent) { 269 if (a->cert->crl != NULL) 270 printf(" %s\n", a->cert->crl); 271 if (a->cert->aia != NULL) 272 printf(" %s\n", a->cert->aia); 273 } 274} 275 276/* 277 * Parse file passed with -f option. 278 */ 279static void 280proc_parser_file(char *file, unsigned char *buf, size_t len) 281{ 282 static int num; 283 X509 *x509 = NULL; 284 struct cert *cert = NULL; 285 struct crl *crl = NULL; 286 struct mft *mft = NULL; 287 struct roa *roa = NULL; 288 struct gbr *gbr = NULL; 289 struct tal *tal = NULL; 290 struct rsc *rsc = NULL; 291 struct aspa *aspa = NULL; 292 struct tak *tak = NULL; 293 struct geofeed *geofeed = NULL; 294 char *aia = NULL, *aki = NULL; 295 char filehash[SHA256_DIGEST_LENGTH]; 296 char *hash; 297 enum rtype type; 298 int is_ta = 0; 299 300 if (num++ > 0) { 301 if ((outformats & FORMAT_JSON) == 0) 302 printf("--\n"); 303 } 304 305 if (strncmp(file, "rsync://", strlen("rsync://")) == 0) { 306 file += strlen("rsync://"); 307 buf = load_file(file, &len); 308 if (buf == NULL) { 309 warn("parse file %s", file); 310 return; 311 } 312 } 313 314 if (!EVP_Digest(buf, len, filehash, NULL, EVP_sha256(), NULL)) 315 errx(1, "EVP_Digest failed in %s", __func__); 316 317 if (base64_encode(filehash, sizeof(filehash), &hash) == -1) 318 errx(1, "base64_encode failed in %s", __func__); 319 320 if (outformats & FORMAT_JSON) { 321 printf("{\n\t\"file\": \"%s\",\n", file); 322 printf("\t\"hash_id\": \"%s\",\n", hash); 323 } else { 324 printf("File: %s\n", file); 325 printf("Hash identifier: %s\n", hash); 326 } 327 328 free(hash); 329 330 type = rtype_from_file_extension(file); 331 332 switch (type) { 333 case RTYPE_CER: 334 cert = cert_parse_pre(file, buf, len); 335 if (cert == NULL) 336 break; 337 is_ta = X509_get_extension_flags(cert->x509) & EXFLAG_SS; 338 if (!is_ta) 339 cert = cert_parse(file, cert); 340 if (cert == NULL) 341 break; 342 cert_print(cert); 343 aia = cert->aia; 344 aki = cert->aki; 345 x509 = cert->x509; 346 if (X509_up_ref(x509) == 0) 347 errx(1, "%s: X509_up_ref failed", __func__); 348 break; 349 case RTYPE_CRL: 350 crl = crl_parse(file, buf, len); 351 if (crl == NULL) 352 break; 353 crl_print(crl); 354 break; 355 case RTYPE_MFT: 356 mft = mft_parse(&x509, file, buf, len); 357 if (mft == NULL) 358 break; 359 mft_print(x509, mft); 360 aia = mft->aia; 361 aki = mft->aki; 362 break; 363 case RTYPE_ROA: 364 roa = roa_parse(&x509, file, buf, len); 365 if (roa == NULL) 366 break; 367 roa_print(x509, roa); 368 aia = roa->aia; 369 aki = roa->aki; 370 break; 371 case RTYPE_GBR: 372 gbr = gbr_parse(&x509, file, buf, len); 373 if (gbr == NULL) 374 break; 375 gbr_print(x509, gbr); 376 aia = gbr->aia; 377 aki = gbr->aki; 378 break; 379 case RTYPE_TAL: 380 tal = tal_parse(file, buf, len); 381 if (tal == NULL) 382 break; 383 tal_print(tal); 384 break; 385 case RTYPE_RSC: 386 rsc = rsc_parse(&x509, file, buf, len); 387 if (rsc == NULL) 388 break; 389 rsc_print(x509, rsc); 390 aia = rsc->aia; 391 aki = rsc->aki; 392 break; 393 case RTYPE_ASPA: 394 aspa = aspa_parse(&x509, file, buf, len); 395 if (aspa == NULL) 396 break; 397 aspa_print(x509, aspa); 398 aia = aspa->aia; 399 aki = aspa->aki; 400 break; 401 case RTYPE_TAK: 402 tak = tak_parse(&x509, file, buf, len); 403 if (tak == NULL) 404 break; 405 tak_print(x509, tak); 406 aia = tak->aia; 407 aki = tak->aki; 408 break; 409 case RTYPE_GEOFEED: 410 geofeed = geofeed_parse(&x509, file, buf, len); 411 if (geofeed == NULL) 412 break; 413 geofeed_print(x509, geofeed); 414 aia = geofeed->aia; 415 aki = geofeed->aki; 416 break; 417 default: 418 printf("%s: unsupported file type\n", file); 419 break; 420 } 421 422 if (outformats & FORMAT_JSON) 423 printf("\t\"validation\": \""); 424 else { 425 if (type == RTYPE_CRL) 426 printf("Validation: N/A\n"); 427 else 428 printf("Validation: "); 429 } 430 431 if (aia != NULL) { 432 struct auth *a; 433 struct crl *c; 434 const char *errstr; 435 char *crl_uri; 436 int status; 437 438 x509_get_crl(x509, file, &crl_uri); 439 parse_load_crl(crl_uri); 440 if (auth_find(&auths, aki) == NULL) 441 parse_load_certchain(aia); 442 a = auth_find(&auths, aki); 443 c = crl_get(&crlt, a); 444 445 if ((status = valid_x509(file, ctx, x509, a, c, &errstr))) { 446 switch (type) { 447 case RTYPE_ROA: 448 status = roa->valid; 449 break; 450 case RTYPE_RSC: 451 status = rsc->valid; 452 break; 453 case RTYPE_ASPA: 454 status = aspa->valid; 455 break; 456 case RTYPE_GEOFEED: 457 status = geofeed->valid; 458 break; 459 default: 460 break; 461 } 462 } 463 if (status) { 464 if ((outformats & FORMAT_JSON) == 0) 465 printf(" "); 466 printf("OK"); 467 if ((outformats & FORMAT_JSON) == 0) { 468 printf("\n"); 469 print_certification_path(crl_uri, aia, a); 470 } 471 } else { 472 if ((outformats & FORMAT_JSON) == 0) 473 printf(" "); 474 printf("Failed"); 475 if (errstr != NULL) 476 printf(", %s", errstr); 477 if ((outformats & FORMAT_JSON) == 0) 478 printf("\n"); 479 } 480 free(crl_uri); 481 } else if (is_ta) { 482 if ((tal = find_tal(cert)) != NULL) { 483 cert = ta_parse(file, cert, tal->pkey, tal->pkeysz); 484 if ((outformats & FORMAT_JSON) == 0) 485 printf(" "); 486 if (cert != NULL) 487 printf("OK"); 488 else 489 printf("Failed"); 490 if (outformats & FORMAT_JSON) 491 printf("\",\n\t\"tal\": \"%s", tal->descr); 492 else 493 printf("\nTAL: %s\n", 494 tal->descr); 495 tal = NULL; 496 } else { 497 cert_free(cert); 498 cert = NULL; 499 printf("Failed"); 500 } 501 } 502 503 if (outformats & FORMAT_JSON) 504 printf("\"\n}\n"); 505 else { 506 if (x509 == NULL) 507 goto out; 508 if (type == RTYPE_TAL || type == RTYPE_CRL) 509 goto out; 510 511 if (verbose) { 512 if (!X509_print_fp(stdout, x509)) 513 errx(1, "X509_print_fp"); 514 } 515 516 if (verbose > 1) { 517 if (!PEM_write_X509(stdout, x509)) 518 errx(1, "PEM_write_X509"); 519 } 520 } 521 522 out: 523 X509_free(x509); 524 cert_free(cert); 525 crl_free(crl); 526 mft_free(mft); 527 roa_free(roa); 528 gbr_free(gbr); 529 tal_free(tal); 530 rsc_free(rsc); 531 aspa_free(aspa); 532 tak_free(tak); 533 geofeed_free(geofeed); 534} 535 536/* 537 * Process a file request, in general don't send anything back. 538 */ 539static void 540parse_file(struct entityq *q, struct msgbuf *msgq) 541{ 542 struct entity *entp; 543 struct ibuf *b; 544 struct tal *tal; 545 546 while ((entp = TAILQ_FIRST(q)) != NULL) { 547 TAILQ_REMOVE(q, entp, entries); 548 549 switch (entp->type) { 550 case RTYPE_FILE: 551 proc_parser_file(entp->file, entp->data, entp->datasz); 552 break; 553 case RTYPE_TAL: 554 if ((tal = tal_parse(entp->file, entp->data, 555 entp->datasz)) == NULL) 556 errx(1, "%s: could not parse tal file", 557 entp->file); 558 tal->id = entp->talid; 559 talobj[tal->id] = tal; 560 parse_load_ta(tal); 561 break; 562 default: 563 errx(1, "unhandled entity type %d", entp->type); 564 } 565 566 b = io_new_buffer(); 567 io_simple_buffer(b, &entp->type, sizeof(entp->type)); 568 io_simple_buffer(b, &entp->repoid, sizeof(entp->repoid)); 569 io_str_buffer(b, entp->file); 570 io_close_buffer(msgq, b); 571 entity_free(entp); 572 } 573} 574 575/* 576 * Process responsible for parsing and validating content. 577 * All this process does is wait to be told about a file to parse, then 578 * it parses it and makes sure that the data being returned is fully 579 * validated and verified. 580 * The process will exit cleanly only when fd is closed. 581 */ 582void 583proc_filemode(int fd) 584{ 585 struct entityq q; 586 struct msgbuf msgq; 587 struct pollfd pfd; 588 struct entity *entp; 589 struct ibuf *b, *inbuf = NULL; 590 591 /* Only allow access to the cache directory. */ 592 if (unveil(".", "r") == -1) 593 err(1, "unveil cachedir"); 594 if (pledge("stdio rpath", NULL) == -1) 595 err(1, "pledge"); 596 597 ERR_load_crypto_strings(); 598 OpenSSL_add_all_ciphers(); 599 OpenSSL_add_all_digests(); 600 x509_init_oid(); 601 602 if ((ctx = X509_STORE_CTX_new()) == NULL) 603 cryptoerrx("X509_STORE_CTX_new"); 604 TAILQ_INIT(&q); 605 606 msgbuf_init(&msgq); 607 msgq.fd = fd; 608 609 pfd.fd = fd; 610 611 for (;;) { 612 pfd.events = POLLIN; 613 if (msgq.queued) 614 pfd.events |= POLLOUT; 615 616 if (poll(&pfd, 1, INFTIM) == -1) { 617 if (errno == EINTR) 618 continue; 619 err(1, "poll"); 620 } 621 if ((pfd.revents & (POLLERR|POLLNVAL))) 622 errx(1, "poll: bad descriptor"); 623 624 /* If the parent closes, return immediately. */ 625 626 if ((pfd.revents & POLLHUP)) 627 break; 628 629 if ((pfd.revents & POLLIN)) { 630 b = io_buf_read(fd, &inbuf); 631 if (b != NULL) { 632 entp = calloc(1, sizeof(struct entity)); 633 if (entp == NULL) 634 err(1, NULL); 635 entity_read_req(b, entp); 636 TAILQ_INSERT_TAIL(&q, entp, entries); 637 ibuf_free(b); 638 } 639 } 640 641 if (pfd.revents & POLLOUT) { 642 switch (msgbuf_write(&msgq)) { 643 case 0: 644 errx(1, "write: connection closed"); 645 case -1: 646 err(1, "write"); 647 } 648 } 649 650 parse_file(&q, &msgq); 651 } 652 653 msgbuf_clear(&msgq); 654 while ((entp = TAILQ_FIRST(&q)) != NULL) { 655 TAILQ_REMOVE(&q, entp, entries); 656 entity_free(entp); 657 } 658 659 auth_tree_free(&auths); 660 crl_tree_free(&crlt); 661 662 X509_STORE_CTX_free(ctx); 663 ibuf_free(inbuf); 664 665 exit(0); 666} 667