xref: /openbsd-src/usr.sbin/acme-client/netproc.c (revision aa997e528a848ca5596493c2a801bdd6fb26ae61)
1 /*	$Id: netproc.c,v 1.16 2018/03/14 12:28:25 florian Exp $ */
2 /*
3  * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <assert.h>
19 #include <ctype.h>
20 #include <err.h>
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 
26 #include "http.h"
27 #include "extern.h"
28 #include "parse.h"
29 
30 #define	RETRY_DELAY 5
31 #define RETRY_MAX 10
32 
33 /*
34  * Buffer used when collecting the results of a CURL transfer.
35  */
36 struct	buf {
37 	char	*buf; /* binary buffer */
38 	size_t	 sz; /* length of buffer */
39 };
40 
41 /*
42  * Used for CURL communications.
43  */
44 struct	conn {
45 	const char	  *na; /* nonce authority */
46 	int		   fd; /* acctproc handle */
47 	int		   dfd; /* dnsproc handle */
48 	struct buf	   buf; /* transfer buffer */
49 };
50 
51 /*
52  * If something goes wrong (or we're tracing output), we dump the
53  * current transfer's data as a debug message.
54  * Make sure that print all non-printable characters as question marks
55  * so that we don't spam the console.
56  * Also, consolidate white-space.
57  * This of course will ruin string literals, but the intent here is just
58  * to show the message, not to replicate it.
59  */
60 static void
61 buf_dump(const struct buf *buf)
62 {
63 	size_t	 i;
64 	int	 j;
65 	char	*nbuf;
66 
67 	if (buf->sz == 0)
68 		return;
69 	if ((nbuf = malloc(buf->sz)) == NULL)
70 		err(EXIT_FAILURE, "malloc");
71 
72 	for (j = 0, i = 0; i < buf->sz; i++)
73 		if (isspace((int)buf->buf[i])) {
74 			nbuf[j++] = ' ';
75 			while (isspace((int)buf->buf[i]))
76 				i++;
77 			i--;
78 		} else
79 			nbuf[j++] = isprint((int)buf->buf[i]) ?
80 			    buf->buf[i] : '?';
81 	dodbg("transfer buffer: [%.*s] (%zu bytes)", j, nbuf, buf->sz);
82 	free(nbuf);
83 }
84 
85 /*
86  * Extract the domain and port from a URL.
87  * The url must be formatted as schema://address[/stuff].
88  * This returns NULL on failure.
89  */
90 static char *
91 url2host(const char *host, short *port, char **path)
92 {
93 	char	*url, *ep;
94 
95 	/* We only understand HTTP and HTTPS. */
96 
97 	if (strncmp(host, "https://", 8) == 0) {
98 		*port = 443;
99 		if ((url = strdup(host + 8)) == NULL) {
100 			warn("strdup");
101 			return NULL;
102 		}
103 	} else if (strncmp(host, "http://", 7) == 0) {
104 		*port = 80;
105 		if ((url = strdup(host + 7)) == NULL) {
106 			warn("strdup");
107 			return NULL;
108 		}
109 	} else {
110 		warnx("%s: unknown schema", host);
111 		return NULL;
112 	}
113 
114 	/* Terminate path part. */
115 
116 	if ((ep = strchr(url, '/')) != NULL) {
117 		*path = strdup(ep);
118 		*ep = '\0';
119 	} else
120 		*path = strdup("");
121 
122 	if (*path == NULL) {
123 		warn("strdup");
124 		free(url);
125 		return NULL;
126 	}
127 
128 	return url;
129 }
130 
131 /*
132  * Contact dnsproc and resolve a host.
133  * Place the answers in "v" and return the number of answers, which can
134  * be at most MAX_SERVERS_DNS.
135  * Return <0 on failure.
136  */
137 static ssize_t
138 urlresolve(int fd, const char *host, struct source *v)
139 {
140 	char		*addr;
141 	size_t		 i, sz;
142 	long		 lval;
143 
144 	if (writeop(fd, COMM_DNS, DNS_LOOKUP) <= 0)
145 		return -1;
146 	else if (writestr(fd, COMM_DNSQ, host) <= 0)
147 		return -1;
148 	else if ((lval = readop(fd, COMM_DNSLEN)) < 0)
149 		return -1;
150 
151 	sz = lval;
152 	assert(sz <= MAX_SERVERS_DNS);
153 
154 	for (i = 0; i < sz; i++) {
155 		memset(&v[i], 0, sizeof(struct source));
156 		if ((lval = readop(fd, COMM_DNSF)) < 0)
157 			goto err;
158 		else if (lval != 4 && lval != 6)
159 			goto err;
160 		else if ((addr = readstr(fd, COMM_DNSA)) == NULL)
161 			goto err;
162 		v[i].family = lval;
163 		v[i].ip = addr;
164 	}
165 
166 	return sz;
167 err:
168 	for (i = 0; i < sz; i++)
169 		free(v[i].ip);
170 	return -1;
171 }
172 
173 /*
174  * Send a "regular" HTTP GET message to "addr" and stuff the response
175  * into the connection buffer.
176  * Return the HTTP error code or <0 on failure.
177  */
178 static long
179 nreq(struct conn *c, const char *addr)
180 {
181 	struct httpget	*g;
182 	struct source	 src[MAX_SERVERS_DNS];
183 	struct httphead *st;
184 	char		*host, *path;
185 	short		 port;
186 	size_t		 srcsz;
187 	ssize_t		 ssz;
188 	long		 code;
189 	int		 redirects = 0;
190 
191 	if ((host = url2host(addr, &port, &path)) == NULL)
192 		return -1;
193 
194 again:
195 	if ((ssz = urlresolve(c->dfd, host, src)) < 0) {
196 		free(host);
197 		free(path);
198 		return -1;
199 	}
200 	srcsz = ssz;
201 
202 	g = http_get(src, srcsz, host, port, path, NULL, 0);
203 	free(host);
204 	free(path);
205 	if (g == NULL)
206 		return -1;
207 
208 	switch (g->code) {
209 	case 301:
210 	case 302:
211 	case 303:
212 	case 307:
213 	case 308:
214 		redirects++;
215 		if (redirects > 3) {
216 			warnx("too many redirects");
217 			http_get_free(g);
218 			return -1;
219 		}
220 
221 		if ((st = http_head_get("Location", g->head, g->headsz)) ==
222 		    NULL) {
223 			warnx("redirect without location header");
224 			return -1;
225 		}
226 
227 		dodbg("Location: %s", st->val);
228 		host = url2host(st->val, &port, &path);
229 		http_get_free(g);
230 		if (host == NULL)
231 			return -1;
232 		goto again;
233 		break;
234 	default:
235 		code = g->code;
236 		break;
237 	}
238 
239 	/* Copy the body part into our buffer. */
240 
241 	free(c->buf.buf);
242 	c->buf.sz = g->bodypartsz;
243 	c->buf.buf = malloc(c->buf.sz);
244 	if (c->buf.buf == NULL) {
245 		warn("malloc");
246 		code = -1;
247 	} else
248 		memcpy(c->buf.buf, g->bodypart, c->buf.sz);
249 	http_get_free(g);
250 	return code;
251 }
252 
253 /*
254  * Create and send a signed communication to the ACME server.
255  * Stuff the response into the communication buffer.
256  * Return <0 on failure on the HTTP error code otherwise.
257  */
258 static long
259 sreq(struct conn *c, const char *addr, const char *req)
260 {
261 	struct httpget	*g;
262 	struct source	 src[MAX_SERVERS_DNS];
263 	char		*host, *path, *nonce, *reqsn;
264 	short		 port;
265 	struct httphead	*h;
266 	ssize_t		 ssz;
267 	long		 code;
268 
269 	if ((host = url2host(c->na, &port, &path)) == NULL)
270 		return -1;
271 
272 	if ((ssz = urlresolve(c->dfd, host, src)) < 0) {
273 		free(host);
274 		free(path);
275 		return -1;
276 	}
277 
278 	g = http_get(src, (size_t)ssz, host, port, path, NULL, 0);
279 	free(host);
280 	free(path);
281 	if (g == NULL)
282 		return -1;
283 
284 	h = http_head_get("Replay-Nonce", g->head, g->headsz);
285 	if (h == NULL) {
286 		warnx("%s: no replay nonce", c->na);
287 		http_get_free(g);
288 		return -1;
289 	} else if ((nonce = strdup(h->val)) == NULL) {
290 		warn("strdup");
291 		http_get_free(g);
292 		return -1;
293 	}
294 	http_get_free(g);
295 
296 	/*
297 	 * Send the nonce and request payload to the acctproc.
298 	 * This will create the proper JSON object we need.
299 	 */
300 
301 	if (writeop(c->fd, COMM_ACCT, ACCT_SIGN) <= 0) {
302 		free(nonce);
303 		return -1;
304 	} else if (writestr(c->fd, COMM_PAY, req) <= 0) {
305 		free(nonce);
306 		return -1;
307 	} else if (writestr(c->fd, COMM_NONCE, nonce) <= 0) {
308 		free(nonce);
309 		return -1;
310 	}
311 	free(nonce);
312 
313 	/* Now read back the signed payload. */
314 
315 	if ((reqsn = readstr(c->fd, COMM_REQ)) == NULL)
316 		return -1;
317 
318 	/* Now send the signed payload to the CA. */
319 
320 	if ((host = url2host(addr, &port, &path)) == NULL) {
321 		free(reqsn);
322 		return -1;
323 	} else if ((ssz = urlresolve(c->dfd, host, src)) < 0) {
324 		free(host);
325 		free(path);
326 		free(reqsn);
327 		return -1;
328 	}
329 
330 	g = http_get(src, (size_t)ssz, host, port, path, reqsn, strlen(reqsn));
331 
332 	free(host);
333 	free(path);
334 	free(reqsn);
335 	if (g == NULL)
336 		return -1;
337 
338 	/* Stuff response into parse buffer. */
339 
340 	code = g->code;
341 
342 	free(c->buf.buf);
343 	c->buf.sz = g->bodypartsz;
344 	c->buf.buf = malloc(c->buf.sz);
345 	if (c->buf.buf == NULL) {
346 		warn("malloc");
347 		code = -1;
348 	} else
349 		memcpy(c->buf.buf, g->bodypart, c->buf.sz);
350 	http_get_free(g);
351 	return code;
352 }
353 
354 /*
355  * Send to the CA that we want to authorise a new account.
356  * This only happens once for a new account key.
357  * Returns non-zero on success.
358  */
359 static int
360 donewreg(struct conn *c, const struct capaths *p)
361 {
362 	int		 rc = 0;
363 	char		*req;
364 	long		 lc;
365 
366 	dodbg("%s: new-reg", p->newreg);
367 
368 	if ((req = json_fmt_newreg(p->agreement)) == NULL)
369 		warnx("json_fmt_newreg");
370 	else if ((lc = sreq(c, p->newreg, req)) < 0)
371 		warnx("%s: bad comm", p->newreg);
372 	else if (lc != 200 && lc != 201)
373 		warnx("%s: bad HTTP: %ld", p->newreg, lc);
374 	else if (c->buf.buf == NULL || c->buf.sz == 0)
375 		warnx("%s: empty response", p->newreg);
376 	else
377 		rc = 1;
378 
379 	if (rc == 0 || verbose > 1)
380 		buf_dump(&c->buf);
381 	free(req);
382 	return rc;
383 }
384 
385 /*
386  * Request a challenge for the given domain name.
387  * This must happen for each name "alt".
388  * On non-zero exit, fills in "chng" with the challenge.
389  */
390 static int
391 dochngreq(struct conn *c, const char *alt, struct chng *chng,
392     const struct capaths *p)
393 {
394 	int		 rc = 0;
395 	char		*req;
396 	long		 lc;
397 	struct jsmnn	*j = NULL;
398 
399 	dodbg("%s: req-auth: %s", p->newauthz, alt);
400 
401 	if ((req = json_fmt_newauthz(alt)) == NULL)
402 		warnx("json_fmt_newauthz");
403 	else if ((lc = sreq(c, p->newauthz, req)) < 0)
404 		warnx("%s: bad comm", p->newauthz);
405 	else if (lc != 200 && lc != 201)
406 		warnx("%s: bad HTTP: %ld", p->newauthz, lc);
407 	else if ((j = json_parse(c->buf.buf, c->buf.sz)) == NULL)
408 		warnx("%s: bad JSON object", p->newauthz);
409 	else if (!json_parse_challenge(j, chng))
410 		warnx("%s: bad challenge", p->newauthz);
411 	else
412 		rc = 1;
413 
414 	if (rc == 0 || verbose > 1)
415 		buf_dump(&c->buf);
416 	json_free(j);
417 	free(req);
418 	return rc;
419 }
420 
421 /*
422  * Note to the CA that a challenge response is in place.
423  */
424 static int
425 dochngresp(struct conn *c, const struct chng *chng, const char *th)
426 {
427 	int	 rc = 0;
428 	long	 lc;
429 	char	*req;
430 
431 	dodbg("%s: challenge", chng->uri);
432 
433 	if ((req = json_fmt_challenge(chng->token, th)) == NULL)
434 		warnx("json_fmt_challenge");
435 	else if ((lc = sreq(c, chng->uri, req)) < 0)
436 		warnx("%s: bad comm", chng->uri);
437 	else if (lc != 200 && lc != 201 && lc != 202)
438 		warnx("%s: bad HTTP: %ld", chng->uri, lc);
439 	else
440 		rc = 1;
441 
442 	if (rc == 0 || verbose > 1)
443 		buf_dump(&c->buf);
444 	free(req);
445 	return rc;
446 }
447 
448 /*
449  * Check with the CA whether a challenge has been processed.
450  * Note: we'll only do this a limited number of times, and pause for a
451  * time between checks, but this happens in the caller.
452  */
453 static int
454 dochngcheck(struct conn *c, struct chng *chng)
455 {
456 	int		 cc;
457 	long		 lc;
458 	struct jsmnn	*j;
459 
460 	dodbg("%s: status", chng->uri);
461 
462 	if ((lc = nreq(c, chng->uri)) < 0) {
463 		warnx("%s: bad comm", chng->uri);
464 		return 0;
465 	} else if (lc != 200 && lc != 201 && lc != 202) {
466 		warnx("%s: bad HTTP: %ld", chng->uri, lc);
467 		buf_dump(&c->buf);
468 		return 0;
469 	} else if ((j = json_parse(c->buf.buf, c->buf.sz)) == NULL) {
470 		warnx("%s: bad JSON object", chng->uri);
471 		buf_dump(&c->buf);
472 		return 0;
473 	} else if ((cc = json_parse_response(j)) == -1) {
474 		warnx("%s: bad response", chng->uri);
475 		buf_dump(&c->buf);
476 		json_free(j);
477 		return 0;
478 	} else if (cc > 0)
479 		chng->status = 1;
480 
481 	json_free(j);
482 	return 1;
483 }
484 
485 static int
486 dorevoke(struct conn *c, const char *addr, const char *cert)
487 {
488 	char		*req;
489 	int		 rc = 0;
490 	long		 lc = 0;
491 
492 	dodbg("%s: revocation", addr);
493 
494 	if ((req = json_fmt_revokecert(cert)) == NULL)
495 		warnx("json_fmt_revokecert");
496 	else if ((lc = sreq(c, addr, req)) < 0)
497 		warnx("%s: bad comm", addr);
498 	else if (lc != 200 && lc != 201 && lc != 409)
499 		warnx("%s: bad HTTP: %ld", addr, lc);
500 	else
501 		rc = 1;
502 
503 	if (lc == 409)
504 		warnx("%s: already revoked", addr);
505 
506 	if (rc == 0 || verbose > 1)
507 		buf_dump(&c->buf);
508 	free(req);
509 	return rc;
510 }
511 
512 /*
513  * Submit our certificate to the CA.
514  * This, upon success, will return the signed CA.
515  */
516 static int
517 docert(struct conn *c, const char *addr, const char *cert)
518 {
519 	char	*req;
520 	int	 rc = 0;
521 	long	 lc;
522 
523 	dodbg("%s: certificate", addr);
524 
525 	if ((req = json_fmt_newcert(cert)) == NULL)
526 		warnx("json_fmt_newcert");
527 	else if ((lc = sreq(c, addr, req)) < 0)
528 		warnx("%s: bad comm", addr);
529 	else if (lc != 200 && lc != 201)
530 		warnx("%s: bad HTTP: %ld", addr, lc);
531 	else if (c->buf.sz == 0 || c->buf.buf == NULL)
532 		warnx("%s: empty response", addr);
533 	else
534 		rc = 1;
535 
536 	if (rc == 0 || verbose > 1)
537 		buf_dump(&c->buf);
538 	free(req);
539 	return rc;
540 }
541 
542 /*
543  * Look up directories from the certificate authority.
544  */
545 static int
546 dodirs(struct conn *c, const char *addr, struct capaths *paths)
547 {
548 	struct jsmnn	*j = NULL;
549 	long		 lc;
550 	int		 rc = 0;
551 
552 	dodbg("%s: directories", addr);
553 
554 	if ((lc = nreq(c, addr)) < 0)
555 		warnx("%s: bad comm", addr);
556 	else if (lc != 200 && lc != 201)
557 		warnx("%s: bad HTTP: %ld", addr, lc);
558 	else if ((j = json_parse(c->buf.buf, c->buf.sz)) == NULL)
559 		warnx("json_parse");
560 	else if (!json_parse_capaths(j, paths))
561 		warnx("%s: bad CA paths", addr);
562 	else
563 		rc = 1;
564 
565 	if (rc == 0 || verbose > 1)
566 		buf_dump(&c->buf);
567 	json_free(j);
568 	return rc;
569 }
570 
571 /*
572  * Request the full chain certificate.
573  */
574 static int
575 dofullchain(struct conn *c, const char *addr)
576 {
577 	int	 rc = 0;
578 	long	 lc;
579 
580 	dodbg("%s: full chain", addr);
581 
582 	if ((lc = nreq(c, addr)) < 0)
583 		warnx("%s: bad comm", addr);
584 	else if (lc != 200 && lc != 201)
585 		warnx("%s: bad HTTP: %ld", addr, lc);
586 	else
587 		rc = 1;
588 
589 	if (rc == 0 || verbose > 1)
590 		buf_dump(&c->buf);
591 	return rc;
592 }
593 
594 /*
595  * Here we communicate with the ACME server.
596  * For this, we'll need the certificate we want to upload and our
597  * account key information.
598  */
599 int
600 netproc(int kfd, int afd, int Cfd, int cfd, int dfd, int rfd,
601     int newacct, int revocate, struct authority_c *authority,
602     const char *const *alts,size_t altsz)
603 {
604 	int		 rc = 0;
605 	size_t		 i;
606 	char		*cert = NULL, *thumb = NULL, *url = NULL;
607 	struct conn	 c;
608 	struct capaths	 paths;
609 	struct chng	*chngs = NULL;
610 	long		 lval;
611 
612 	memset(&paths, 0, sizeof(struct capaths));
613 	memset(&c, 0, sizeof(struct conn));
614 
615 	if (pledge("stdio inet rpath", NULL) == -1) {
616 		warn("pledge");
617 		goto out;
618 	}
619 
620 	if (http_init() == -1) {
621 		warn("http_init");
622 		goto out;
623 	}
624 
625 	if (pledge("stdio inet", NULL) == -1) {
626 		warn("pledge");
627 		goto out;
628 	}
629 
630 	/*
631 	 * Wait until the acctproc, keyproc, and revokeproc have started
632 	 * up and are ready to serve us data.
633 	 * There's no point in running if these don't work.
634 	 * Then check whether revokeproc indicates that the certificate
635 	 * on file (if any) can be updated.
636 	 */
637 
638 	if ((lval = readop(afd, COMM_ACCT_STAT)) == 0) {
639 		rc = 1;
640 		goto out;
641 	} else if (lval != ACCT_READY) {
642 		warnx("unknown operation from acctproc");
643 		goto out;
644 	}
645 
646 	if ((lval = readop(kfd, COMM_KEY_STAT)) == 0) {
647 		rc = 1;
648 		goto out;
649 	} else if (lval != KEY_READY) {
650 		warnx("unknown operation from keyproc");
651 		goto out;
652 	}
653 
654 	if ((lval = readop(rfd, COMM_REVOKE_RESP)) == 0) {
655 		rc = 1;
656 		goto out;
657 	} else if (lval != REVOKE_EXP && lval != REVOKE_OK) {
658 		warnx("unknown operation from revokeproc");
659 		goto out;
660 	}
661 
662 	/* If our certificate is up-to-date, return now. */
663 
664 	if (lval == REVOKE_OK) {
665 		rc = 1;
666 		goto out;
667 	}
668 
669 	/* Allocate main state. */
670 
671 	chngs = calloc(altsz, sizeof(struct chng));
672 	if (chngs == NULL) {
673 		warn("calloc");
674 		goto out;
675 	}
676 
677 	c.dfd = dfd;
678 	c.fd = afd;
679 	c.na = authority->api;
680 
681 	/*
682 	 * Look up the domain of the ACME server.
683 	 * We'll use this ourselves instead of having libcurl do the DNS
684 	 * resolution itself.
685 	 */
686 	if (!dodirs(&c, c.na, &paths))
687 		goto out;
688 
689 	/*
690 	 * If we're meant to revoke, then wait for revokeproc to send us
691 	 * the certificate (if it's found at all).
692 	 * Following that, submit the request to the CA then notify the
693 	 * certproc, which will in turn notify the fileproc.
694 	 */
695 
696 	if (revocate) {
697 		if ((cert = readstr(rfd, COMM_CSR)) == NULL)
698 			goto out;
699 		if (!dorevoke(&c, paths.revokecert, cert))
700 			goto out;
701 		else if (writeop(cfd, COMM_CSR_OP, CERT_REVOKE) > 0)
702 			rc = 1;
703 		goto out;
704 	}
705 
706 	/* If new, register with the CA server. */
707 
708 	if (newacct && ! donewreg(&c, &paths))
709 		goto out;
710 
711 	/* Pre-authorise all domains with CA server. */
712 
713 	for (i = 0; i < altsz; i++)
714 		if (!dochngreq(&c, alts[i], &chngs[i], &paths))
715 			goto out;
716 
717 	/*
718 	 * We now have our challenges.
719 	 * We need to ask the acctproc for the thumbprint.
720 	 * We'll combine this to the challenge to create our response,
721 	 * which will be orchestrated by the chngproc.
722 	 */
723 
724 	if (writeop(afd, COMM_ACCT, ACCT_THUMBPRINT) <= 0)
725 		goto out;
726 	else if ((thumb = readstr(afd, COMM_THUMB)) == NULL)
727 		goto out;
728 
729 	/* We'll now ask chngproc to build the challenge. */
730 
731 	for (i = 0; i < altsz; i++) {
732 		if (writeop(Cfd, COMM_CHNG_OP, CHNG_SYN) <= 0)
733 			goto out;
734 		else if (writestr(Cfd, COMM_THUMB, thumb) <= 0)
735 			goto out;
736 		else if (writestr(Cfd, COMM_TOK, chngs[i].token) <= 0)
737 			goto out;
738 
739 		/* Read that the challenge has been made. */
740 
741 		if (readop(Cfd, COMM_CHNG_ACK) != CHNG_ACK)
742 			goto out;
743 
744 		/* Write to the CA that it's ready. */
745 
746 		if (!dochngresp(&c, &chngs[i], thumb))
747 			goto out;
748 	}
749 
750 	/*
751 	 * We now wait on the ACME server for each domain.
752 	 * Connect to the server (assume it's the same server) once
753 	 * every five seconds.
754 	 */
755 
756 	for (i = 0; i < altsz; i++) {
757 		if (chngs[i].status == 1)
758 			continue;
759 
760 		if (chngs[i].retry++ >= RETRY_MAX) {
761 			warnx("%s: too many tries", chngs[i].uri);
762 			goto out;
763 		}
764 
765 		/* Sleep before every attempt. */
766 		sleep(RETRY_DELAY);
767 		if (!dochngcheck(&c, &chngs[i]))
768 			goto out;
769 	}
770 
771 	/*
772 	 * Write our acknowledgement that the challenges are over.
773 	 * The challenge process will remove all of the files.
774 	 */
775 
776 	if (writeop(Cfd, COMM_CHNG_OP, CHNG_STOP) <= 0)
777 		goto out;
778 
779 	/* Wait to receive the certificate itself. */
780 
781 	if ((cert = readstr(kfd, COMM_CERT)) == NULL)
782 		goto out;
783 
784 	/*
785 	 * Otherwise, submit the CA for signing, download the signed
786 	 * copy, and ship that into the certificate process for copying.
787 	 */
788 
789 	if (!docert(&c, paths.newcert, cert))
790 		goto out;
791 	else if (writeop(cfd, COMM_CSR_OP, CERT_UPDATE) <= 0)
792 		goto out;
793 	else if (writebuf(cfd, COMM_CSR, c.buf.buf, c.buf.sz) <= 0)
794 		goto out;
795 
796 	/*
797 	 * Read back the issuer from the certproc.
798 	 * Then contact the issuer to get the certificate chain.
799 	 * Write this chain directly back to the certproc.
800 	 */
801 
802 	if ((url = readstr(cfd, COMM_ISSUER)) == NULL)
803 		goto out;
804 	else if (!dofullchain(&c, url))
805 		goto out;
806 	else if (writebuf(cfd, COMM_CHAIN, c.buf.buf, c.buf.sz) <= 0)
807 		goto out;
808 
809 	rc = 1;
810 out:
811 	close(cfd);
812 	close(kfd);
813 	close(afd);
814 	close(Cfd);
815 	close(dfd);
816 	close(rfd);
817 	free(cert);
818 	free(url);
819 	free(thumb);
820 	free(c.buf.buf);
821 	if (chngs != NULL)
822 		for (i = 0; i < altsz; i++)
823 			json_free_challenge(&chngs[i]);
824 	free(chngs);
825 	json_free_capaths(&paths);
826 	return rc;
827 }
828