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