1 /* $OpenBSD: ocspcheck.c,v 1.29 2021/02/09 16:55:51 claudio Exp $ */ 2 3 /* 4 * Copyright (c) 2017,2020 Bob Beck <beck@openbsd.org> 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 AUTHORS DISCLAIM ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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 <arpa/inet.h> 20 #include <netinet/in.h> 21 #include <sys/socket.h> 22 #include <sys/stat.h> 23 24 #include <err.h> 25 #include <fcntl.h> 26 #include <limits.h> 27 #include <netdb.h> 28 #include <poll.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <time.h> 33 #include <unistd.h> 34 35 #include <openssl/err.h> 36 #include <openssl/ocsp.h> 37 #include <openssl/ssl.h> 38 39 #include "http.h" 40 41 #define MAXAGE_SEC (14*24*60*60) 42 #define JITTER_SEC (60) 43 #define OCSP_MAX_RESPONSE_SIZE (20480) 44 45 typedef struct ocsp_request { 46 STACK_OF(X509) *fullchain; 47 OCSP_REQUEST *req; 48 char *url; 49 unsigned char *data; 50 size_t size; 51 int nonce; 52 } ocsp_request; 53 54 int verbose; 55 #define vspew(fmt, ...) \ 56 do { if (verbose >= 1) fprintf(stderr, fmt, __VA_ARGS__); } while (0) 57 #define dspew(fmt, ...) \ 58 do { if (verbose >= 2) fprintf(stderr, fmt, __VA_ARGS__); } while (0) 59 60 #define MAX_SERVERS_DNS 8 61 62 struct addr { 63 int family; /* 4 for PF_INET, 6 for PF_INET6 */ 64 char ip[INET6_ADDRSTRLEN]; 65 }; 66 67 static ssize_t 68 host_dns(const char *s, struct addr vec[MAX_SERVERS_DNS]) 69 { 70 struct addrinfo hints, *res0, *res; 71 int error; 72 ssize_t vecsz; 73 struct sockaddr *sa; 74 75 memset(&hints, 0, sizeof(hints)); 76 hints.ai_family = PF_UNSPEC; 77 hints.ai_socktype = SOCK_DGRAM; /* DUMMY */ 78 79 error = getaddrinfo(s, NULL, &hints, &res0); 80 81 if (error == EAI_AGAIN || 82 #ifdef EAI_NODATA 83 error == EAI_NODATA || 84 #endif 85 error == EAI_NONAME) 86 return 0; 87 88 if (error) { 89 warnx("%s: parse error: %s", s, gai_strerror(error)); 90 return -1; 91 } 92 93 for (vecsz = 0, res = res0; 94 res != NULL && vecsz < MAX_SERVERS_DNS; 95 res = res->ai_next) { 96 if (res->ai_family != AF_INET && 97 res->ai_family != AF_INET6) 98 continue; 99 100 sa = res->ai_addr; 101 102 if (res->ai_family == AF_INET) { 103 vec[vecsz].family = 4; 104 inet_ntop(AF_INET, 105 &(((struct sockaddr_in *)sa)->sin_addr), 106 vec[vecsz].ip, INET6_ADDRSTRLEN); 107 } else { 108 vec[vecsz].family = 6; 109 inet_ntop(AF_INET6, 110 &(((struct sockaddr_in6 *)sa)->sin6_addr), 111 vec[vecsz].ip, INET6_ADDRSTRLEN); 112 } 113 114 dspew("DNS returns %s for %s\n", vec[vecsz].ip, s); 115 vecsz++; 116 } 117 118 freeaddrinfo(res0); 119 return vecsz; 120 } 121 122 /* 123 * Extract the domain and port from a URL. 124 * The url must be formatted as schema://address[/stuff]. 125 * This returns NULL on failure. 126 */ 127 static char * 128 url2host(const char *host, short *port, char **path) 129 { 130 char *url, *ep; 131 132 /* We only understand HTTP and HTTPS. */ 133 134 if (strncmp(host, "https://", 8) == 0) { 135 *port = 443; 136 if ((url = strdup(host + 8)) == NULL) { 137 warn("strdup"); 138 return (NULL); 139 } 140 } else if (strncmp(host, "http://", 7) == 0) { 141 *port = 80; 142 if ((url = strdup(host + 7)) == NULL) { 143 warn("strdup"); 144 return (NULL); 145 } 146 } else { 147 warnx("%s: unknown schema", host); 148 return (NULL); 149 } 150 151 /* Terminate path part. */ 152 153 if ((ep = strchr(url, '/')) != NULL) { 154 *path = strdup(ep); 155 *ep = '\0'; 156 } else 157 *path = strdup("/"); 158 159 if (*path == NULL) { 160 warn("strdup"); 161 free(url); 162 return (NULL); 163 } 164 165 /* Check to see if there is a port in the url */ 166 if ((ep = strchr(url, ':')) != NULL) { 167 const char *errstr; 168 short pp; 169 pp = strtonum(ep + 1, 1, SHRT_MAX, &errstr); 170 if (errstr != NULL) { 171 warnx("error parsing port from '%s': %s", url, errstr); 172 free(url); 173 free(*path); 174 return NULL; 175 } 176 *port = pp; 177 *ep = '\0'; 178 } 179 180 return (url); 181 } 182 183 static time_t 184 parse_ocsp_time(ASN1_GENERALIZEDTIME *gt) 185 { 186 struct tm tm; 187 time_t rv = -1; 188 189 if (gt == NULL) 190 return -1; 191 /* RFC 6960 specifies that all times in OCSP must be GENERALIZEDTIME */ 192 if (ASN1_time_parse(gt->data, gt->length, &tm, 193 V_ASN1_GENERALIZEDTIME) == -1) 194 return -1; 195 if ((rv = timegm(&tm)) == -1) 196 return -1; 197 return rv; 198 } 199 200 static X509_STORE * 201 read_cacerts(const char *file, const char *dir) 202 { 203 X509_STORE *store = NULL; 204 X509_LOOKUP *lookup; 205 206 if (file == NULL && dir == NULL) { 207 warnx("No CA certs to load"); 208 goto end; 209 } 210 if ((store = X509_STORE_new()) == NULL) { 211 warnx("Malloc failed"); 212 goto end; 213 } 214 if (file != NULL) { 215 if ((lookup = X509_STORE_add_lookup(store, 216 X509_LOOKUP_file())) == NULL) { 217 warnx("Unable to load CA cert file"); 218 goto end; 219 } 220 if (!X509_LOOKUP_load_file(lookup, file, X509_FILETYPE_PEM)) { 221 warnx("Unable to load CA certs from file %s", file); 222 goto end; 223 } 224 } 225 if (dir != NULL) { 226 if ((lookup = X509_STORE_add_lookup(store, 227 X509_LOOKUP_hash_dir())) == NULL) { 228 warnx("Unable to load CA cert directory"); 229 goto end; 230 } 231 if (!X509_LOOKUP_add_dir(lookup, dir, X509_FILETYPE_PEM)) { 232 warnx("Unable to load CA certs from directory %s", dir); 233 goto end; 234 } 235 } 236 return store; 237 238 end: 239 X509_STORE_free(store); 240 return NULL; 241 } 242 243 static STACK_OF(X509) * 244 read_fullchain(const char *file, int *count) 245 { 246 int i; 247 BIO *bio; 248 STACK_OF(X509_INFO) *xis = NULL; 249 X509_INFO *xi; 250 STACK_OF(X509) *rv = NULL; 251 252 *count = 0; 253 254 if ((bio = BIO_new_file(file, "r")) == NULL) { 255 warn("Unable to read a certificate from %s", file); 256 goto end; 257 } 258 if ((xis = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL)) == NULL) { 259 warnx("Unable to read PEM format from %s", file); 260 goto end; 261 } 262 if (sk_X509_INFO_num(xis) <= 0) { 263 warnx("No certificates in file %s", file); 264 goto end; 265 } 266 if ((rv = sk_X509_new_null()) == NULL) { 267 warnx("malloc failed"); 268 goto end; 269 } 270 271 for (i = 0; i < sk_X509_INFO_num(xis); i++) { 272 xi = sk_X509_INFO_value(xis, i); 273 if (xi->x509 == NULL) 274 continue; 275 if (!sk_X509_push(rv, xi->x509)) { 276 warnx("unable to build x509 chain"); 277 sk_X509_pop_free(rv, X509_free); 278 rv = NULL; 279 goto end; 280 } 281 xi->x509 = NULL; 282 (*count)++; 283 } 284 end: 285 BIO_free(bio); 286 sk_X509_INFO_pop_free(xis, X509_INFO_free); 287 return rv; 288 } 289 290 static inline X509 * 291 cert_from_chain(STACK_OF(X509) *fullchain) 292 { 293 return sk_X509_value(fullchain, 0); 294 } 295 296 static const X509 * 297 issuer_from_chain(STACK_OF(X509) *fullchain) 298 { 299 const X509 *cert; 300 X509_NAME *issuer_name; 301 302 cert = cert_from_chain(fullchain); 303 if ((issuer_name = X509_get_issuer_name(cert)) == NULL) 304 return NULL; 305 306 return X509_find_by_subject(fullchain, issuer_name); 307 } 308 309 static ocsp_request * 310 ocsp_request_new_from_cert(const char *cadir, char *file, int nonce) 311 { 312 X509 *cert; 313 int count = 0; 314 OCSP_CERTID *id = NULL; 315 ocsp_request *request = NULL; 316 const EVP_MD *cert_id_md = NULL; 317 const X509 *issuer; 318 STACK_OF(OPENSSL_STRING) *urls = NULL; 319 320 if ((request = calloc(1, sizeof(ocsp_request))) == NULL) { 321 warn("malloc"); 322 goto err; 323 } 324 325 if ((request->req = OCSP_REQUEST_new()) == NULL) 326 goto err; 327 328 request->fullchain = read_fullchain(file, &count); 329 if (cadir == NULL) { 330 /* Drop rpath from pledge, we don't need to read anymore */ 331 if (pledge("stdio inet dns", NULL) == -1) 332 err(1, "pledge"); 333 } 334 if (request->fullchain == NULL) { 335 warnx("Unable to read cert chain from file %s", file); 336 goto err; 337 } 338 if (count <= 1) { 339 warnx("File %s does not contain a cert chain", file); 340 goto err; 341 } 342 if ((cert = cert_from_chain(request->fullchain)) == NULL) { 343 warnx("No certificate found in %s", file); 344 goto err; 345 } 346 if ((issuer = issuer_from_chain(request->fullchain)) == NULL) { 347 warnx("Unable to find issuer for cert in %s", file); 348 goto err; 349 } 350 351 urls = X509_get1_ocsp(cert); 352 if (urls == NULL || sk_OPENSSL_STRING_num(urls) <= 0) { 353 warnx("Certificate in %s contains no OCSP url", file); 354 goto err; 355 } 356 if ((request->url = strdup(sk_OPENSSL_STRING_value(urls, 0))) == NULL) 357 goto err; 358 X509_email_free(urls); 359 urls = NULL; 360 361 cert_id_md = EVP_sha1(); /* XXX. This sucks but OCSP is poopy */ 362 if ((id = OCSP_cert_to_id(cert_id_md, cert, issuer)) == NULL) { 363 warnx("Unable to get certificate id from cert in %s", file); 364 goto err; 365 } 366 if (OCSP_request_add0_id(request->req, id) == NULL) { 367 warnx("Unable to add certificate id to request"); 368 goto err; 369 } 370 id = NULL; 371 372 request->nonce = nonce; 373 if (request->nonce) 374 OCSP_request_add1_nonce(request->req, NULL, -1); 375 376 if ((request->size = i2d_OCSP_REQUEST(request->req, 377 &request->data)) <= 0) { 378 warnx("Unable to encode ocsp request"); 379 goto err; 380 } 381 if (request->data == NULL) { 382 warnx("Unable to allocte memory"); 383 goto err; 384 } 385 return request; 386 387 err: 388 if (request != NULL) { 389 sk_X509_pop_free(request->fullchain, X509_free); 390 free(request->url); 391 OCSP_REQUEST_free(request->req); 392 free(request->data); 393 } 394 X509_email_free(urls); 395 OCSP_CERTID_free(id); 396 free(request); 397 return NULL; 398 } 399 400 401 int 402 validate_response(char *buf, size_t size, ocsp_request *request, 403 X509_STORE *store, char *host, char *file) 404 { 405 ASN1_GENERALIZEDTIME *revtime = NULL, *thisupd = NULL, *nextupd = NULL; 406 const unsigned char **p = (const unsigned char **)&buf; 407 int status, cert_status = 0, crl_reason = 0; 408 time_t now, rev_t = -1, this_t, next_t; 409 OCSP_RESPONSE *resp = NULL; 410 OCSP_BASICRESP *bresp = NULL; 411 OCSP_CERTID *cid = NULL; 412 const X509 *cert, *issuer; 413 int ret = 0; 414 415 if ((cert = cert_from_chain(request->fullchain)) == NULL) { 416 warnx("No certificate found in %s", file); 417 goto err; 418 } 419 if ((issuer = issuer_from_chain(request->fullchain)) == NULL) { 420 warnx("Unable to find certificate issuer for cert in %s", file); 421 goto err; 422 } 423 if ((cid = OCSP_cert_to_id(NULL, cert, issuer)) == NULL) { 424 warnx("Unable to get issuer cert/CID in %s", file); 425 goto err; 426 } 427 428 if ((resp = d2i_OCSP_RESPONSE(NULL, p, size)) == NULL) { 429 warnx("OCSP response unserializable from host %s", host); 430 goto err; 431 } 432 433 if ((bresp = OCSP_response_get1_basic(resp)) == NULL) { 434 warnx("Failed to load OCSP response from %s", host); 435 goto err; 436 } 437 438 if (OCSP_basic_verify(bresp, request->fullchain, store, 439 OCSP_TRUSTOTHER) != 1) { 440 warnx("OCSP verify failed from %s", host); 441 goto err; 442 } 443 dspew("OCSP response signature validated from %s\n", host); 444 445 status = OCSP_response_status(resp); 446 if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { 447 warnx("OCSP Failure: code %d (%s) from host %s", 448 status, OCSP_response_status_str(status), host); 449 goto err; 450 } 451 dspew("OCSP response status %d from host %s\n", status, host); 452 453 /* Check the nonce if we sent one */ 454 455 if (request->nonce) { 456 if (OCSP_check_nonce(request->req, bresp) <= 0) { 457 warnx("No OCSP nonce, or mismatch, from host %s", host); 458 goto err; 459 } 460 } 461 462 if (OCSP_resp_find_status(bresp, cid, &cert_status, &crl_reason, 463 &revtime, &thisupd, &nextupd) != 1) { 464 warnx("OCSP verify failed: no result for cert"); 465 goto err; 466 } 467 468 if (revtime && (rev_t = parse_ocsp_time(revtime)) == -1) { 469 warnx("Unable to parse revocation time in OCSP reply"); 470 goto err; 471 } 472 /* 473 * Belt and suspenders, Treat it as revoked if there is either 474 * a revocation time, or status revoked. 475 */ 476 if (rev_t != -1 || cert_status == V_OCSP_CERTSTATUS_REVOKED) { 477 warnx("Invalid OCSP reply: certificate is revoked"); 478 if (rev_t != -1) 479 warnx("Certificate revoked at: %s", ctime(&rev_t)); 480 goto err; 481 } 482 if ((this_t = parse_ocsp_time(thisupd)) == -1) { 483 warnx("unable to parse this update time in OCSP reply"); 484 goto err; 485 } 486 if ((next_t = parse_ocsp_time(nextupd)) == -1) { 487 warnx("unable to parse next update time in OCSP reply"); 488 goto err; 489 } 490 491 /* Don't allow this update to precede next update */ 492 if (this_t >= next_t) { 493 warnx("Invalid OCSP reply: this update >= next update"); 494 goto err; 495 } 496 497 now = time(NULL); 498 /* 499 * Check that this update is not more than JITTER seconds 500 * in the future. 501 */ 502 if (this_t > now + JITTER_SEC) { 503 warnx("Invalid OCSP reply: this update is in the future at %s", 504 ctime(&this_t)); 505 goto err; 506 } 507 508 /* 509 * Check that this update is not more than MAXSEC 510 * in the past. 511 */ 512 if (this_t < now - MAXAGE_SEC) { 513 warnx("Invalid OCSP reply: this update is too old %s", 514 ctime(&this_t)); 515 goto err; 516 } 517 518 /* 519 * Check that next update is still valid 520 */ 521 if (next_t < now - JITTER_SEC) { 522 warnx("Invalid OCSP reply: reply has expired at %s", 523 ctime(&next_t)); 524 goto err; 525 } 526 527 vspew("OCSP response validated from %s\n", host); 528 vspew(" This Update: %s", ctime(&this_t)); 529 vspew(" Next Update: %s", ctime(&next_t)); 530 ret = 1; 531 err: 532 OCSP_RESPONSE_free(resp); 533 OCSP_BASICRESP_free(bresp); 534 OCSP_CERTID_free(cid); 535 return ret; 536 } 537 538 static void 539 usage(void) 540 { 541 fprintf(stderr, 542 "usage: ocspcheck [-Nv] [-C CAfile] [-i staplefile] " 543 "[-o staplefile] file\n"); 544 exit(1); 545 } 546 547 int 548 main(int argc, char **argv) 549 { 550 const char *cafile = NULL, *cadir = NULL; 551 char *host = NULL, *path = NULL, *certfile = NULL, *outfile = NULL, 552 *instaple = NULL, *infile = NULL; 553 struct addr addrs[MAX_SERVERS_DNS] = {{0}}; 554 struct source sources[MAX_SERVERS_DNS]; 555 int i, ch, staplefd = -1, infd = -1, nonce = 1; 556 ocsp_request *request = NULL; 557 size_t rescount, httphsz = 0, instaplesz = 0; 558 struct httphead *httph = NULL; 559 struct httpget *hget; 560 X509_STORE *castore; 561 ssize_t written, w; 562 short port; 563 564 while ((ch = getopt(argc, argv, "C:i:No:v")) != -1) { 565 switch (ch) { 566 case 'C': 567 cafile = optarg; 568 break; 569 case 'N': 570 nonce = 0; 571 break; 572 case 'o': 573 outfile = optarg; 574 break; 575 case 'i': 576 infile = optarg; 577 break; 578 case 'v': 579 verbose++; 580 break; 581 default: 582 usage(); 583 } 584 } 585 argc -= optind; 586 argv += optind; 587 588 if (argc != 1 || (certfile = argv[0]) == NULL) 589 usage(); 590 591 if (outfile != NULL) { 592 if (strcmp(outfile, "-") == 0) 593 staplefd = STDOUT_FILENO; 594 else 595 staplefd = open(outfile, O_WRONLY|O_CREAT, 596 S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH); 597 if (staplefd < 0) 598 err(1, "Unable to open output file %s", outfile); 599 } 600 601 if (infile != NULL) { 602 if (strcmp(infile, "-") == 0) 603 infd = STDIN_FILENO; 604 else 605 infd = open(infile, O_RDONLY); 606 if (infd < 0) 607 err(1, "Unable to open input file %s", infile); 608 nonce = 0; /* Can't validate a nonce on a saved reply */ 609 } 610 611 if (cafile == NULL) { 612 if (access(X509_get_default_cert_file(), R_OK) == 0) 613 cafile = X509_get_default_cert_file(); 614 if (access(X509_get_default_cert_dir(), F_OK) == 0) 615 cadir = X509_get_default_cert_dir(); 616 } 617 618 if (cafile != NULL) { 619 if (unveil(cafile, "r") == -1) 620 err(1, "unveil"); 621 } 622 if (cadir != NULL) { 623 if (unveil(cadir, "r") == -1) 624 err(1, "unveil"); 625 } 626 if (unveil(certfile, "r") == -1) 627 err(1, "unveil"); 628 629 if (pledge("stdio inet rpath dns", NULL) == -1) 630 err(1, "pledge"); 631 632 /* 633 * Load our certificate and keystore, and build up an 634 * OCSP request based on the full certificate chain 635 * we have been given to check. 636 */ 637 if ((castore = read_cacerts(cafile, cadir)) == NULL) 638 exit(1); 639 if ((request = ocsp_request_new_from_cert(cadir, certfile, nonce)) 640 == NULL) 641 exit(1); 642 643 dspew("Built an %zu byte ocsp request\n", request->size); 644 645 if ((host = url2host(request->url, &port, &path)) == NULL) 646 errx(1, "Invalid OCSP url %s from %s", request->url, 647 certfile); 648 649 if (infd == -1) { 650 /* Get a new OCSP response from the indicated server */ 651 652 vspew("Using %s to host %s, port %d, path %s\n", 653 port == 443 ? "https" : "http", host, port, path); 654 655 rescount = host_dns(host, addrs); 656 for (i = 0; i < rescount; i++) { 657 sources[i].ip = addrs[i].ip; 658 sources[i].family = addrs[i].family; 659 } 660 661 /* 662 * Do an HTTP post to send our request to the OCSP 663 * server, and hopefully get an answer back 664 */ 665 hget = http_get(sources, rescount, host, port, path, 666 request->data, request->size); 667 if (hget == NULL) 668 errx(1, "http_get"); 669 /* 670 * Pledge minimally before fiddling with libcrypto init 671 * routines and parsing untrusted input from someone's OCSP 672 * server. 673 */ 674 if (cadir == NULL) { 675 if (pledge("stdio", NULL) == -1) 676 err(1, "pledge"); 677 } else { 678 if (pledge("stdio rpath", NULL) == -1) 679 err(1, "pledge"); 680 } 681 682 dspew("Server at %s returns:\n", host); 683 for (i = 0; i < httphsz; i++) 684 dspew(" [%s]=[%s]\n", httph[i].key, httph[i].val); 685 dspew(" [Body]=[%zu bytes]\n", hget->bodypartsz); 686 if (hget->bodypartsz <= 0) 687 errx(1, "No body in reply from %s", host); 688 689 if (hget->code != 200) 690 errx(1, "http reply code %d from %s", hget->code, host); 691 692 /* 693 * Validate the OCSP response we got back 694 */ 695 OPENSSL_add_all_algorithms_noconf(); 696 if (!validate_response(hget->bodypart, hget->bodypartsz, 697 request, castore, host, certfile)) 698 exit(1); 699 instaple = hget->bodypart; 700 instaplesz = hget->bodypartsz; 701 } else { 702 size_t nr = 0; 703 instaplesz = 0; 704 705 /* 706 * Pledge minimally before fiddling with libcrypto init 707 */ 708 if (cadir == NULL) { 709 if (pledge("stdio", NULL) == -1) 710 err(1, "pledge"); 711 } else { 712 if (pledge("stdio rpath", NULL) == -1) 713 err(1, "pledge"); 714 } 715 716 dspew("Using ocsp response saved in %s:\n", infile); 717 718 /* Use the existing OCSP response saved in infd */ 719 instaple = calloc(OCSP_MAX_RESPONSE_SIZE, 1); 720 if (instaple) { 721 while ((nr = read(infd, instaple + instaplesz, 722 OCSP_MAX_RESPONSE_SIZE - instaplesz)) != -1 && 723 nr != 0) 724 instaplesz += nr; 725 } 726 if (instaplesz == 0) 727 exit(1); 728 /* 729 * Validate the OCSP staple we read in. 730 */ 731 OPENSSL_add_all_algorithms_noconf(); 732 if (!validate_response(instaple, instaplesz, 733 request, castore, host, certfile)) 734 exit(1); 735 } 736 737 /* 738 * If we have been given a place to save a staple, 739 * write out the DER format response to the staplefd 740 */ 741 if (staplefd >= 0) { 742 while (ftruncate(staplefd, 0) < 0) { 743 if (errno == EINVAL) 744 break; 745 if (errno != EINTR && errno != EAGAIN) 746 err(1, "Write of OCSP response failed"); 747 } 748 written = 0; 749 while (written < instaplesz) { 750 w = write(staplefd, instaple + written, 751 instaplesz - written); 752 if (w == -1) { 753 if (errno != EINTR && errno != EAGAIN) 754 err(1, "Write of OCSP response failed"); 755 } else 756 written += w; 757 } 758 close(staplefd); 759 } 760 exit(0); 761 } 762