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
host_dns(const char * s,struct addr vec[MAX_SERVERS_DNS])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 *
url2host(const char * host,short * port,char ** path)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
parse_ocsp_time(ASN1_GENERALIZEDTIME * gt)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 *
read_cacerts(const char * file,const char * dir)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
STACK_OF(X509)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 *
cert_from_chain(STACK_OF (X509)* fullchain)293 cert_from_chain(STACK_OF(X509) *fullchain)
294 {
295 return sk_X509_value(fullchain, 0);
296 }
297
298 static const X509 *
issuer_from_chain(STACK_OF (X509)* fullchain)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 *
ocsp_request_new_from_cert(const char * cadir,char * file,int nonce)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
validate_response(char * buf,size_t size,ocsp_request * request,X509_STORE * store,char * host,char * file)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
usage(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
main(int argc,char ** argv)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