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