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