1 /* $OpenBSD: ocspcheck.c,v 1.30 2021/07/12 15:09:21 beck 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
host_dns(const char * s,struct addr vec[MAX_SERVERS_DNS])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 *
url2host(const char * host,short * port,char ** path)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
parse_ocsp_time(ASN1_GENERALIZEDTIME * gt)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 *
read_cacerts(const char * file,const char * dir)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
STACK_OF(X509)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 *
cert_from_chain(STACK_OF (X509)* fullchain)291 cert_from_chain(STACK_OF(X509) *fullchain)
292 {
293 return sk_X509_value(fullchain, 0);
294 }
295
296 static const X509 *
issuer_from_chain(STACK_OF (X509)* fullchain)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 *
ocsp_request_new_from_cert(const char * cadir,char * file,int nonce)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
validate_response(char * buf,size_t size,ocsp_request * request,X509_STORE * store,char * host,char * file)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
usage(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
main(int argc,char ** argv)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 %s", cafile);
621 }
622 if (cadir != NULL) {
623 if (unveil(cadir, "r") == -1)
624 err(1, "unveil %s", cadir);
625 }
626 if (unveil(certfile, "r") == -1)
627 err(1, "unveil %s", certfile);
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