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