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