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