1 /* $OpenBSD: filemode.c,v 1.19 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 <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 43 extern int verbose; 44 45 static X509_STORE_CTX *ctx; 46 static struct auth_tree auths = RB_INITIALIZER(&auths); 47 static struct crl_tree crlt = RB_INITIALIZER(&crlt); 48 49 struct tal *talobj[TALSZ_MAX]; 50 51 /* 52 * Use the X509 CRL Distribution Points to locate the CRL needed for 53 * verification. 54 */ 55 static void 56 parse_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 */ 89 static struct cert * 90 parse_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 */ 136 static void 137 parse_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; 187 fail: 188 for (i = 0; i < MAX_CERT_DEPTH; i++) 189 cert_free(stack[i]); 190 } 191 192 static void 193 parse_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); 226 out: 227 free(file); 228 free(f); 229 } 230 231 static struct tal * 232 find_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 260 /* 261 * Parse file passed with -f option. 262 */ 263 static void 264 proc_parser_file(char *file, unsigned char *buf, size_t len) 265 { 266 static int num; 267 X509 *x509 = NULL; 268 struct cert *cert = NULL; 269 struct crl *crl = NULL; 270 struct mft *mft = NULL; 271 struct roa *roa = NULL; 272 struct gbr *gbr = NULL; 273 struct tal *tal = NULL; 274 struct rsc *rsc = NULL; 275 struct aspa *aspa = NULL; 276 struct tak *tak = NULL; 277 struct geofeed *geofeed = NULL; 278 char *aia = NULL, *aki = NULL; 279 char filehash[SHA256_DIGEST_LENGTH]; 280 char *hash; 281 enum rtype type; 282 int is_ta = 0; 283 284 if (num++ > 0) { 285 if ((outformats & FORMAT_JSON) == 0) 286 printf("--\n"); 287 } 288 289 if (strncmp(file, "rsync://", strlen("rsync://")) == 0) { 290 file += strlen("rsync://"); 291 buf = load_file(file, &len); 292 if (buf == NULL) { 293 warn("parse file %s", file); 294 return; 295 } 296 } 297 298 if (!EVP_Digest(buf, len, filehash, NULL, EVP_sha256(), NULL)) 299 errx(1, "EVP_Digest failed in %s", __func__); 300 301 if (base64_encode(filehash, sizeof(filehash), &hash) == -1) 302 errx(1, "base64_encode failed in %s", __func__); 303 304 if (outformats & FORMAT_JSON) { 305 printf("{\n\t\"file\": \"%s\",\n", file); 306 printf("\t\"hash_id\": \"%s\",\n", hash); 307 } else { 308 printf("File: %s\n", file); 309 printf("Hash identifier: %s\n", hash); 310 } 311 312 free(hash); 313 314 type = rtype_from_file_extension(file); 315 316 switch (type) { 317 case RTYPE_CER: 318 cert = cert_parse_pre(file, buf, len); 319 if (cert == NULL) 320 break; 321 is_ta = X509_get_extension_flags(cert->x509) & EXFLAG_SS; 322 if (!is_ta) 323 cert = cert_parse(file, cert); 324 if (cert == NULL) 325 break; 326 cert_print(cert); 327 aia = cert->aia; 328 aki = cert->aki; 329 x509 = cert->x509; 330 if (X509_up_ref(x509) == 0) 331 errx(1, "%s: X509_up_ref failed", __func__); 332 break; 333 case RTYPE_CRL: 334 crl = crl_parse(file, buf, len); 335 if (crl == NULL) 336 break; 337 crl_print(crl); 338 break; 339 case RTYPE_MFT: 340 mft = mft_parse(&x509, file, buf, len); 341 if (mft == NULL) 342 break; 343 mft_print(x509, mft); 344 aia = mft->aia; 345 aki = mft->aki; 346 break; 347 case RTYPE_ROA: 348 roa = roa_parse(&x509, file, buf, len); 349 if (roa == NULL) 350 break; 351 roa_print(x509, roa); 352 aia = roa->aia; 353 aki = roa->aki; 354 break; 355 case RTYPE_GBR: 356 gbr = gbr_parse(&x509, file, buf, len); 357 if (gbr == NULL) 358 break; 359 gbr_print(x509, gbr); 360 aia = gbr->aia; 361 aki = gbr->aki; 362 break; 363 case RTYPE_TAL: 364 tal = tal_parse(file, buf, len); 365 if (tal == NULL) 366 break; 367 tal_print(tal); 368 break; 369 case RTYPE_RSC: 370 rsc = rsc_parse(&x509, file, buf, len); 371 if (rsc == NULL) 372 break; 373 rsc_print(x509, rsc); 374 aia = rsc->aia; 375 aki = rsc->aki; 376 break; 377 case RTYPE_ASPA: 378 aspa = aspa_parse(&x509, file, buf, len); 379 if (aspa == NULL) 380 break; 381 aspa_print(x509, aspa); 382 aia = aspa->aia; 383 aki = aspa->aki; 384 break; 385 case RTYPE_TAK: 386 tak = tak_parse(&x509, file, buf, len); 387 if (tak == NULL) 388 break; 389 tak_print(x509, tak); 390 aia = tak->aia; 391 aki = tak->aki; 392 break; 393 case RTYPE_GEOFEED: 394 geofeed = geofeed_parse(&x509, file, buf, len); 395 if (geofeed == NULL) 396 break; 397 geofeed_print(x509, geofeed); 398 aia = geofeed->aia; 399 aki = geofeed->aki; 400 break; 401 default: 402 printf("%s: unsupported file type\n", file); 403 break; 404 } 405 406 if (outformats & FORMAT_JSON) 407 printf("\t\"validation\": \""); 408 else 409 printf("Validation: "); 410 411 if (aia != NULL) { 412 struct auth *a; 413 struct crl *c; 414 const char *errstr; 415 char *crl_uri; 416 int status; 417 418 x509_get_crl(x509, file, &crl_uri); 419 parse_load_crl(crl_uri); 420 free(crl_uri); 421 if (auth_find(&auths, aki) == NULL) 422 parse_load_certchain(aia); 423 a = auth_find(&auths, aki); 424 c = crl_get(&crlt, a); 425 426 if ((status = valid_x509(file, ctx, x509, a, c, &errstr))) { 427 switch (type) { 428 case RTYPE_ROA: 429 status = roa->valid; 430 break; 431 case RTYPE_RSC: 432 status = rsc->valid; 433 break; 434 case RTYPE_ASPA: 435 status = aspa->valid; 436 break; 437 case RTYPE_GEOFEED: 438 status = geofeed->valid; 439 break; 440 default: 441 break; 442 } 443 } 444 if (status) 445 printf("OK"); 446 else { 447 printf("Failed"); 448 if (errstr != NULL) 449 printf(", %s", errstr); 450 } 451 } else if (is_ta) { 452 if ((tal = find_tal(cert)) != NULL) { 453 cert = ta_parse(file, cert, tal->pkey, tal->pkeysz); 454 if (cert != NULL) 455 printf("OK"); 456 else 457 printf("Failed"); 458 if (outformats & FORMAT_JSON) 459 printf("\",\n\t\"tal\": \"%s", tal->descr); 460 else 461 printf("\nTAL: %s", tal->descr); 462 tal = NULL; 463 } else { 464 cert_free(cert); 465 cert = NULL; 466 printf("Failed"); 467 } 468 } 469 470 if (outformats & FORMAT_JSON) 471 printf("\"\n}\n"); 472 else { 473 printf("\n"); 474 475 if (x509 == NULL) 476 goto out; 477 if (type == RTYPE_TAL || type == RTYPE_CRL) 478 goto out; 479 480 if (verbose) { 481 if (!X509_print_fp(stdout, x509)) 482 errx(1, "X509_print_fp"); 483 } 484 485 if (verbose > 1) { 486 if (!PEM_write_X509(stdout, x509)) 487 errx(1, "PEM_write_X509"); 488 } 489 } 490 491 out: 492 X509_free(x509); 493 cert_free(cert); 494 crl_free(crl); 495 mft_free(mft); 496 roa_free(roa); 497 gbr_free(gbr); 498 tal_free(tal); 499 rsc_free(rsc); 500 aspa_free(aspa); 501 tak_free(tak); 502 geofeed_free(geofeed); 503 } 504 505 /* 506 * Process a file request, in general don't send anything back. 507 */ 508 static void 509 parse_file(struct entityq *q, struct msgbuf *msgq) 510 { 511 struct entity *entp; 512 struct ibuf *b; 513 struct tal *tal; 514 515 while ((entp = TAILQ_FIRST(q)) != NULL) { 516 TAILQ_REMOVE(q, entp, entries); 517 518 switch (entp->type) { 519 case RTYPE_FILE: 520 proc_parser_file(entp->file, entp->data, entp->datasz); 521 break; 522 case RTYPE_TAL: 523 if ((tal = tal_parse(entp->file, entp->data, 524 entp->datasz)) == NULL) 525 errx(1, "%s: could not parse tal file", 526 entp->file); 527 tal->id = entp->talid; 528 talobj[tal->id] = tal; 529 parse_load_ta(tal); 530 break; 531 default: 532 errx(1, "unhandled entity type %d", entp->type); 533 } 534 535 b = io_new_buffer(); 536 io_simple_buffer(b, &entp->type, sizeof(entp->type)); 537 io_simple_buffer(b, &entp->repoid, sizeof(entp->repoid)); 538 io_str_buffer(b, entp->file); 539 io_close_buffer(msgq, b); 540 entity_free(entp); 541 } 542 } 543 544 /* 545 * Process responsible for parsing and validating content. 546 * All this process does is wait to be told about a file to parse, then 547 * it parses it and makes sure that the data being returned is fully 548 * validated and verified. 549 * The process will exit cleanly only when fd is closed. 550 */ 551 void 552 proc_filemode(int fd) 553 { 554 struct entityq q; 555 struct msgbuf msgq; 556 struct pollfd pfd; 557 struct entity *entp; 558 struct ibuf *b, *inbuf = NULL; 559 560 /* Only allow access to the cache directory. */ 561 if (unveil(".", "r") == -1) 562 err(1, "unveil cachedir"); 563 if (pledge("stdio rpath", NULL) == -1) 564 err(1, "pledge"); 565 566 ERR_load_crypto_strings(); 567 OpenSSL_add_all_ciphers(); 568 OpenSSL_add_all_digests(); 569 x509_init_oid(); 570 571 if ((ctx = X509_STORE_CTX_new()) == NULL) 572 cryptoerrx("X509_STORE_CTX_new"); 573 TAILQ_INIT(&q); 574 575 msgbuf_init(&msgq); 576 msgq.fd = fd; 577 578 pfd.fd = fd; 579 580 for (;;) { 581 pfd.events = POLLIN; 582 if (msgq.queued) 583 pfd.events |= POLLOUT; 584 585 if (poll(&pfd, 1, INFTIM) == -1) { 586 if (errno == EINTR) 587 continue; 588 err(1, "poll"); 589 } 590 if ((pfd.revents & (POLLERR|POLLNVAL))) 591 errx(1, "poll: bad descriptor"); 592 593 /* If the parent closes, return immediately. */ 594 595 if ((pfd.revents & POLLHUP)) 596 break; 597 598 if ((pfd.revents & POLLIN)) { 599 b = io_buf_read(fd, &inbuf); 600 if (b != NULL) { 601 entp = calloc(1, sizeof(struct entity)); 602 if (entp == NULL) 603 err(1, NULL); 604 entity_read_req(b, entp); 605 TAILQ_INSERT_TAIL(&q, entp, entries); 606 ibuf_free(b); 607 } 608 } 609 610 if (pfd.revents & POLLOUT) { 611 switch (msgbuf_write(&msgq)) { 612 case 0: 613 errx(1, "write: connection closed"); 614 case -1: 615 err(1, "write"); 616 } 617 } 618 619 parse_file(&q, &msgq); 620 } 621 622 msgbuf_clear(&msgq); 623 while ((entp = TAILQ_FIRST(&q)) != NULL) { 624 TAILQ_REMOVE(&q, entp, entries); 625 entity_free(entp); 626 } 627 628 auth_tree_free(&auths); 629 crl_tree_free(&crlt); 630 631 X509_STORE_CTX_free(ctx); 632 ibuf_free(inbuf); 633 634 exit(0); 635 } 636