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