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