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