xref: /openbsd-src/usr.sbin/acme-client/netproc.c (revision f2270df8abda6ff4ad6a750f5b036a15b3ba6bad)
1*f2270df8Sflorian /*	$Id: netproc.c,v 1.37 2024/10/10 09:39:35 florian Exp $ */
2de579d12Sflorian /*
3de579d12Sflorian  * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
4de579d12Sflorian  *
5de579d12Sflorian  * Permission to use, copy, modify, and distribute this software for any
6de579d12Sflorian  * purpose with or without fee is hereby granted, provided that the above
7de579d12Sflorian  * copyright notice and this permission notice appear in all copies.
8de579d12Sflorian  *
9de579d12Sflorian  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
10de579d12Sflorian  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11de579d12Sflorian  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
12de579d12Sflorian  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13de579d12Sflorian  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14de579d12Sflorian  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15de579d12Sflorian  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16de579d12Sflorian  */
17de579d12Sflorian 
18de579d12Sflorian #include <assert.h>
19de579d12Sflorian #include <ctype.h>
20de579d12Sflorian #include <err.h>
21de579d12Sflorian #include <errno.h>
22*f2270df8Sflorian #include <stdio.h>
23de579d12Sflorian #include <stdlib.h>
24de579d12Sflorian #include <string.h>
25de579d12Sflorian #include <unistd.h>
26aad8b57bSjsing #include <tls.h>
2742c2cc51Sflorian #include <vis.h>
28de579d12Sflorian 
29de579d12Sflorian #include "http.h"
30de579d12Sflorian #include "extern.h"
31383e31e9Sbenno #include "parse.h"
32de579d12Sflorian 
33de579d12Sflorian #define	RETRY_DELAY 5
34de579d12Sflorian #define RETRY_MAX 10
35de579d12Sflorian 
36de579d12Sflorian /*
37b6c74373Sbenno  * Buffer used when collecting the results of an http transfer.
38de579d12Sflorian  */
39de579d12Sflorian struct	buf {
40de579d12Sflorian 	char	*buf; /* binary buffer */
41de579d12Sflorian 	size_t	 sz; /* length of buffer */
42de579d12Sflorian };
43de579d12Sflorian 
44de579d12Sflorian /*
45b6c74373Sbenno  * Used for communication with other processes.
46de579d12Sflorian  */
47de579d12Sflorian struct	conn {
487b00f4e9Sflorian 	const char	  *newnonce; /* nonce authority */
497b00f4e9Sflorian 	char		  *kid; /* kid when account exists */
50de579d12Sflorian 	int		   fd; /* acctproc handle */
51de579d12Sflorian 	int		   dfd; /* dnsproc handle */
527b00f4e9Sflorian 	struct buf	   buf; /* http body buffer */
53de579d12Sflorian };
54de579d12Sflorian 
55de579d12Sflorian /*
56de579d12Sflorian  * If something goes wrong (or we're tracing output), we dump the
57de579d12Sflorian  * current transfer's data as a debug message.
58de579d12Sflorian  */
59de579d12Sflorian static void
60de579d12Sflorian buf_dump(const struct buf *buf)
61de579d12Sflorian {
62de579d12Sflorian 	char	*nbuf;
63de579d12Sflorian 
647cd8f039Sjsing 	if (buf->sz == 0)
65de579d12Sflorian 		return;
66b98581f5Sflorian 	/* must be at least 4 * srclen + 1 long */
67b98581f5Sflorian 	if ((nbuf = calloc(buf->sz + 1, 4)) == NULL)
68b98581f5Sflorian 		err(EXIT_FAILURE, "calloc");
69b98581f5Sflorian 	strvisx(nbuf, buf->buf, buf->sz, VIS_SAFE);
70b98581f5Sflorian 	dodbg("transfer buffer: [%s] (%zu bytes)", nbuf, buf->sz);
71de579d12Sflorian 	free(nbuf);
72de579d12Sflorian }
73de579d12Sflorian 
74de579d12Sflorian /*
75de579d12Sflorian  * Extract the domain and port from a URL.
76de579d12Sflorian  * The url must be formatted as schema://address[/stuff].
77de579d12Sflorian  * This returns NULL on failure.
78de579d12Sflorian  */
79de579d12Sflorian static char *
80de579d12Sflorian url2host(const char *host, short *port, char **path)
81de579d12Sflorian {
82de579d12Sflorian 	char	*url, *ep;
83de579d12Sflorian 
84de579d12Sflorian 	/* We only understand HTTP and HTTPS. */
857cd8f039Sjsing 	if (strncmp(host, "https://", 8) == 0) {
86de579d12Sflorian 		*port = 443;
877cd8f039Sjsing 		if ((url = strdup(host + 8)) == NULL) {
88de579d12Sflorian 			warn("strdup");
8934335c11Sjsing 			return NULL;
90de579d12Sflorian 		}
917cd8f039Sjsing 	} else if (strncmp(host, "http://", 7) == 0) {
92de579d12Sflorian 		*port = 80;
937cd8f039Sjsing 		if ((url = strdup(host + 7)) == NULL) {
94de579d12Sflorian 			warn("strdup");
9534335c11Sjsing 			return NULL;
96de579d12Sflorian 		}
97de579d12Sflorian 	} else {
98de579d12Sflorian 		warnx("%s: unknown schema", host);
9934335c11Sjsing 		return NULL;
100de579d12Sflorian 	}
101de579d12Sflorian 
102de579d12Sflorian 	/* Terminate path part. */
1037cd8f039Sjsing 	if ((ep = strchr(url, '/')) != NULL) {
104de579d12Sflorian 		*path = strdup(ep);
105de579d12Sflorian 		*ep = '\0';
106de579d12Sflorian 	} else
107de579d12Sflorian 		*path = strdup("");
108de579d12Sflorian 
1097cd8f039Sjsing 	if (*path == NULL) {
110de579d12Sflorian 		warn("strdup");
111de579d12Sflorian 		free(url);
11234335c11Sjsing 		return NULL;
113de579d12Sflorian 	}
114de579d12Sflorian 
11534335c11Sjsing 	return url;
116de579d12Sflorian }
117de579d12Sflorian 
118de579d12Sflorian /*
119de579d12Sflorian  * Contact dnsproc and resolve a host.
120de579d12Sflorian  * Place the answers in "v" and return the number of answers, which can
121de579d12Sflorian  * be at most MAX_SERVERS_DNS.
122de579d12Sflorian  * Return <0 on failure.
123de579d12Sflorian  */
124de579d12Sflorian static ssize_t
125de579d12Sflorian urlresolve(int fd, const char *host, struct source *v)
126de579d12Sflorian {
127de579d12Sflorian 	char		*addr;
128de579d12Sflorian 	size_t		 i, sz;
129de579d12Sflorian 	long		 lval;
130de579d12Sflorian 
131de579d12Sflorian 	if (writeop(fd, COMM_DNS, DNS_LOOKUP) <= 0)
13234335c11Sjsing 		return -1;
133de579d12Sflorian 	else if (writestr(fd, COMM_DNSQ, host) <= 0)
13434335c11Sjsing 		return -1;
135de579d12Sflorian 	else if ((lval = readop(fd, COMM_DNSLEN)) < 0)
13634335c11Sjsing 		return -1;
137de579d12Sflorian 
138de579d12Sflorian 	sz = lval;
139de579d12Sflorian 	assert(sz <= MAX_SERVERS_DNS);
140de579d12Sflorian 
141de579d12Sflorian 	for (i = 0; i < sz; i++) {
142de579d12Sflorian 		memset(&v[i], 0, sizeof(struct source));
143de579d12Sflorian 		if ((lval = readop(fd, COMM_DNSF)) < 0)
144de579d12Sflorian 			goto err;
1457cd8f039Sjsing 		else if (lval != 4 && lval != 6)
146de579d12Sflorian 			goto err;
1477cd8f039Sjsing 		else if ((addr = readstr(fd, COMM_DNSA)) == NULL)
148de579d12Sflorian 			goto err;
149de579d12Sflorian 		v[i].family = lval;
150de579d12Sflorian 		v[i].ip = addr;
151de579d12Sflorian 	}
152de579d12Sflorian 
15334335c11Sjsing 	return sz;
154de579d12Sflorian err:
155de579d12Sflorian 	for (i = 0; i < sz; i++)
156de579d12Sflorian 		free(v[i].ip);
15734335c11Sjsing 	return -1;
158de579d12Sflorian }
159de579d12Sflorian 
160de579d12Sflorian /*
161de579d12Sflorian  * Send a "regular" HTTP GET message to "addr" and stuff the response
162de579d12Sflorian  * into the connection buffer.
163de579d12Sflorian  * Return the HTTP error code or <0 on failure.
164de579d12Sflorian  */
165de579d12Sflorian static long
166de579d12Sflorian nreq(struct conn *c, const char *addr)
167de579d12Sflorian {
168de579d12Sflorian 	struct httpget	*g;
169de579d12Sflorian 	struct source	 src[MAX_SERVERS_DNS];
1705dc748e0Sflorian 	struct httphead *st;
171de579d12Sflorian 	char		*host, *path;
172de579d12Sflorian 	short		 port;
173de579d12Sflorian 	size_t		 srcsz;
174de579d12Sflorian 	ssize_t		 ssz;
175de579d12Sflorian 	long		 code;
1765dc748e0Sflorian 	int		 redirects = 0;
177de579d12Sflorian 
1787cd8f039Sjsing 	if ((host = url2host(addr, &port, &path)) == NULL)
17934335c11Sjsing 		return -1;
180de579d12Sflorian 
1815dc748e0Sflorian again:
182de579d12Sflorian 	if ((ssz = urlresolve(c->dfd, host, src)) < 0) {
183de579d12Sflorian 		free(host);
184de579d12Sflorian 		free(path);
18534335c11Sjsing 		return -1;
186de579d12Sflorian 	}
187de579d12Sflorian 	srcsz = ssz;
188de579d12Sflorian 
1897b00f4e9Sflorian 	g = http_get(src, srcsz, host, port, path, 0, NULL, 0);
190de579d12Sflorian 	free(host);
191de579d12Sflorian 	free(path);
1927cd8f039Sjsing 	if (g == NULL)
19334335c11Sjsing 		return -1;
194de579d12Sflorian 
1955dc748e0Sflorian 	switch (g->code) {
1965dc748e0Sflorian 	case 301:
1975dc748e0Sflorian 	case 302:
1985dc748e0Sflorian 	case 303:
1995dc748e0Sflorian 	case 307:
2005dc748e0Sflorian 	case 308:
2015dc748e0Sflorian 		redirects++;
2025dc748e0Sflorian 		if (redirects > 3) {
2035dc748e0Sflorian 			warnx("too many redirects");
2045dc748e0Sflorian 			http_get_free(g);
2055dc748e0Sflorian 			return -1;
2065dc748e0Sflorian 		}
2075dc748e0Sflorian 
2085dc748e0Sflorian 		if ((st = http_head_get("Location", g->head, g->headsz)) ==
2095dc748e0Sflorian 		    NULL) {
2105dc748e0Sflorian 			warnx("redirect without location header");
211f44c78d3Smbuhl 			http_get_free(g);
2125dc748e0Sflorian 			return -1;
2135dc748e0Sflorian 		}
2145dc748e0Sflorian 
2155dc748e0Sflorian 		host = url2host(st->val, &port, &path);
2165dc748e0Sflorian 		http_get_free(g);
2175dc748e0Sflorian 		if (host == NULL)
2185dc748e0Sflorian 			return -1;
2195dc748e0Sflorian 		goto again;
2205dc748e0Sflorian 		break;
2215dc748e0Sflorian 	default:
222de579d12Sflorian 		code = g->code;
2235dc748e0Sflorian 		break;
2245dc748e0Sflorian 	}
225de579d12Sflorian 
226de579d12Sflorian 	/* Copy the body part into our buffer. */
227de579d12Sflorian 	free(c->buf.buf);
228de579d12Sflorian 	c->buf.sz = g->bodypartsz;
229de579d12Sflorian 	c->buf.buf = malloc(c->buf.sz);
2307cd8f039Sjsing 	if (c->buf.buf == NULL) {
231de579d12Sflorian 		warn("malloc");
2322963b436Sflorian 		code = -1;
2332963b436Sflorian 	} else
2342963b436Sflorian 		memcpy(c->buf.buf, g->bodypart, c->buf.sz);
2352963b436Sflorian 	http_get_free(g);
23634335c11Sjsing 	return code;
237de579d12Sflorian }
238de579d12Sflorian 
239de579d12Sflorian /*
240de579d12Sflorian  * Create and send a signed communication to the ACME server.
241de579d12Sflorian  * Stuff the response into the communication buffer.
242de579d12Sflorian  * Return <0 on failure on the HTTP error code otherwise.
243de579d12Sflorian  */
244de579d12Sflorian static long
2457b00f4e9Sflorian sreq(struct conn *c, const char *addr, int kid, const char *req, char **loc)
246de579d12Sflorian {
247de579d12Sflorian 	struct httpget	*g;
248de579d12Sflorian 	struct source	 src[MAX_SERVERS_DNS];
249de579d12Sflorian 	char		*host, *path, *nonce, *reqsn;
250de579d12Sflorian 	short		 port;
251de579d12Sflorian 	struct httphead	*h;
252de579d12Sflorian 	ssize_t		 ssz;
253de579d12Sflorian 	long		 code;
254de579d12Sflorian 
2557b00f4e9Sflorian 	if ((host = url2host(c->newnonce, &port, &path)) == NULL)
25634335c11Sjsing 		return -1;
257de579d12Sflorian 
258de579d12Sflorian 	if ((ssz = urlresolve(c->dfd, host, src)) < 0) {
259de579d12Sflorian 		free(host);
260de579d12Sflorian 		free(path);
26134335c11Sjsing 		return -1;
262de579d12Sflorian 	}
263de579d12Sflorian 
2647b00f4e9Sflorian 	g = http_get(src, (size_t)ssz, host, port, path, 1, NULL, 0);
265de579d12Sflorian 	free(host);
266de579d12Sflorian 	free(path);
2677cd8f039Sjsing 	if (g == NULL)
26834335c11Sjsing 		return -1;
269de579d12Sflorian 
270de579d12Sflorian 	h = http_head_get("Replay-Nonce", g->head, g->headsz);
2717cd8f039Sjsing 	if (h == NULL) {
2727b00f4e9Sflorian 		warnx("%s: no replay nonce", c->newnonce);
273de579d12Sflorian 		http_get_free(g);
27434335c11Sjsing 		return -1;
2757cd8f039Sjsing 	} else if ((nonce = strdup(h->val)) == NULL) {
276de579d12Sflorian 		warn("strdup");
277de579d12Sflorian 		http_get_free(g);
27834335c11Sjsing 		return -1;
279de579d12Sflorian 	}
280de579d12Sflorian 	http_get_free(g);
281de579d12Sflorian 
282de579d12Sflorian 	/*
2837b00f4e9Sflorian 	 * Send the url, nonce and request payload to the acctproc.
284de579d12Sflorian 	 * This will create the proper JSON object we need.
285de579d12Sflorian 	 */
2867b00f4e9Sflorian 	if (writeop(c->fd, COMM_ACCT, kid ? ACCT_KID_SIGN : ACCT_SIGN) <= 0) {
287de579d12Sflorian 		free(nonce);
28834335c11Sjsing 		return -1;
289de579d12Sflorian 	} else if (writestr(c->fd, COMM_PAY, req) <= 0) {
290de579d12Sflorian 		free(nonce);
29134335c11Sjsing 		return -1;
292de579d12Sflorian 	} else if (writestr(c->fd, COMM_NONCE, nonce) <= 0) {
293de579d12Sflorian 		free(nonce);
29434335c11Sjsing 		return -1;
2957b00f4e9Sflorian 	} else if (writestr(c->fd, COMM_URL, addr) <= 0) {
2967b00f4e9Sflorian 		free(nonce);
2977b00f4e9Sflorian 		return -1;
298de579d12Sflorian 	}
299de579d12Sflorian 	free(nonce);
300de579d12Sflorian 
3017b00f4e9Sflorian 	if (kid && writestr(c->fd, COMM_KID, c->kid) <= 0)
3027b00f4e9Sflorian 		return -1;
3037b00f4e9Sflorian 
304de579d12Sflorian 	/* Now read back the signed payload. */
3057cd8f039Sjsing 	if ((reqsn = readstr(c->fd, COMM_REQ)) == NULL)
30634335c11Sjsing 		return -1;
307de579d12Sflorian 
308de579d12Sflorian 	/* Now send the signed payload to the CA. */
3097cd8f039Sjsing 	if ((host = url2host(addr, &port, &path)) == NULL) {
310de579d12Sflorian 		free(reqsn);
31134335c11Sjsing 		return -1;
312de579d12Sflorian 	} else if ((ssz = urlresolve(c->dfd, host, src)) < 0) {
313de579d12Sflorian 		free(host);
314de579d12Sflorian 		free(path);
315de579d12Sflorian 		free(reqsn);
31634335c11Sjsing 		return -1;
317de579d12Sflorian 	}
318de579d12Sflorian 
3197b00f4e9Sflorian 	g = http_get(src, (size_t)ssz, host, port, path, 0, reqsn,
3207b00f4e9Sflorian 	    strlen(reqsn));
321de579d12Sflorian 
322de579d12Sflorian 	free(host);
323de579d12Sflorian 	free(path);
324de579d12Sflorian 	free(reqsn);
3257cd8f039Sjsing 	if (g == NULL)
32634335c11Sjsing 		return -1;
327de579d12Sflorian 
328de579d12Sflorian 	/* Stuff response into parse buffer. */
329de579d12Sflorian 	code = g->code;
330de579d12Sflorian 
331de579d12Sflorian 	free(c->buf.buf);
332de579d12Sflorian 	c->buf.sz = g->bodypartsz;
333de579d12Sflorian 	c->buf.buf = malloc(c->buf.sz);
3347cd8f039Sjsing 	if (c->buf.buf == NULL) {
335de579d12Sflorian 		warn("malloc");
3362963b436Sflorian 		code = -1;
3372963b436Sflorian 	} else
3382963b436Sflorian 		memcpy(c->buf.buf, g->bodypart, c->buf.sz);
3397b00f4e9Sflorian 
3407b00f4e9Sflorian 	if (loc != NULL) {
3417b00f4e9Sflorian 		free(*loc);
3427b00f4e9Sflorian 		*loc = NULL;
3437b00f4e9Sflorian 		h = http_head_get("Location", g->head, g->headsz);
3447b00f4e9Sflorian 		/* error checking done by caller */
3457b00f4e9Sflorian 		if (h != NULL)
3467b00f4e9Sflorian 			*loc = strdup(h->val);
3477b00f4e9Sflorian 	}
3487b00f4e9Sflorian 
3492963b436Sflorian 	http_get_free(g);
35034335c11Sjsing 	return code;
351de579d12Sflorian }
352de579d12Sflorian 
353de579d12Sflorian /*
354de579d12Sflorian  * Send to the CA that we want to authorise a new account.
355de579d12Sflorian  * This only happens once for a new account key.
356de579d12Sflorian  * Returns non-zero on success.
357de579d12Sflorian  */
358de579d12Sflorian static int
3596736ff2bSflorian donewacc(struct conn *c, const struct capaths *p, const char *contact)
360de579d12Sflorian {
361ec77e55dSflorian 	struct jsmnn	*j = NULL;
3627bce6888Sderaadt 	int		 rc = 0;
363de0ff358Ssthen 	char		*req, *detail, *error = NULL, *accturi = NULL;
364de579d12Sflorian 	long		 lc;
365de579d12Sflorian 
3666736ff2bSflorian 	if ((req = json_fmt_newacc(contact)) == NULL)
3677b00f4e9Sflorian 		warnx("json_fmt_newacc");
3687b00f4e9Sflorian 	else if ((lc = sreq(c, p->newaccount, 0, req, &c->kid)) < 0)
3697b00f4e9Sflorian 		warnx("%s: bad comm", p->newaccount);
370ec77e55dSflorian 	else if (lc == 400) {
371ec77e55dSflorian 		if ((j = json_parse(c->buf.buf, c->buf.sz)) == NULL)
372ec77e55dSflorian 			warnx("%s: bad JSON object", p->newaccount);
373ec77e55dSflorian 		else {
374ec77e55dSflorian 			detail = json_getstr(j, "detail");
375ec77e55dSflorian 			if (detail != NULL && stravis(&error, detail, VIS_SAFE)
376ec77e55dSflorian 			    != -1) {
377ec77e55dSflorian 				warnx("%s", error);
378ec77e55dSflorian 				free(error);
379ec77e55dSflorian 			}
380ec77e55dSflorian 		}
381ec77e55dSflorian 	} else if (lc != 200 && lc != 201)
3827b00f4e9Sflorian 		warnx("%s: bad HTTP: %ld", p->newaccount, lc);
3837cd8f039Sjsing 	else if (c->buf.buf == NULL || c->buf.sz == 0)
3847b00f4e9Sflorian 		warnx("%s: empty response", p->newaccount);
385de579d12Sflorian 	else
386de579d12Sflorian 		rc = 1;
387de579d12Sflorian 
388de0ff358Ssthen 	if (c->kid != NULL) {
389de0ff358Ssthen 		if (stravis(&accturi, c->kid, VIS_SAFE) != -1)
390*f2270df8Sflorian 			printf("account key: %s\n", accturi);
391de0ff358Ssthen 		free(accturi);
392de0ff358Ssthen 	}
393de0ff358Ssthen 
3947cd8f039Sjsing 	if (rc == 0 || verbose > 1)
395de579d12Sflorian 		buf_dump(&c->buf);
396de579d12Sflorian 	free(req);
39734335c11Sjsing 	return rc;
398de579d12Sflorian }
399de579d12Sflorian 
400de579d12Sflorian /*
4017b00f4e9Sflorian  * Check if our account already exists, if not create it.
4027b00f4e9Sflorian  * Populates conn->kid.
4037b00f4e9Sflorian  * Returns non-zero on success.
4047b00f4e9Sflorian  */
4057b00f4e9Sflorian static int
4066736ff2bSflorian dochkacc(struct conn *c, const struct capaths *p, const char *contact)
4077b00f4e9Sflorian {
4087b00f4e9Sflorian 	int		 rc = 0;
409de0ff358Ssthen 	char		*req, *accturi = NULL;
4107b00f4e9Sflorian 	long		 lc;
4117b00f4e9Sflorian 
4127b00f4e9Sflorian 	if ((req = json_fmt_chkacc()) == NULL)
4137b00f4e9Sflorian 		warnx("json_fmt_chkacc");
4147b00f4e9Sflorian 	else if ((lc = sreq(c, p->newaccount, 0, req, &c->kid)) < 0)
4157b00f4e9Sflorian 		warnx("%s: bad comm", p->newaccount);
4167b00f4e9Sflorian 	else if (lc != 200 && lc != 400)
4177b00f4e9Sflorian 		warnx("%s: bad HTTP: %ld", p->newaccount, lc);
4187b00f4e9Sflorian 	else if (c->buf.buf == NULL || c->buf.sz == 0)
4197b00f4e9Sflorian 		warnx("%s: empty response", p->newaccount);
4207b00f4e9Sflorian 	else if (lc == 400)
4216736ff2bSflorian 		rc = donewacc(c, p, contact);
4227b00f4e9Sflorian 	else
4237b00f4e9Sflorian 		rc = 1;
4247b00f4e9Sflorian 
4257b00f4e9Sflorian 	if (c->kid == NULL)
4267b00f4e9Sflorian 		rc = 0;
427de0ff358Ssthen 	else {
428de0ff358Ssthen 		if (stravis(&accturi, c->kid, VIS_SAFE) != -1)
429de0ff358Ssthen 			dodbg("account key: %s", accturi);
430de0ff358Ssthen 		free(accturi);
431de0ff358Ssthen 	}
4327b00f4e9Sflorian 
4337b00f4e9Sflorian 	if (rc == 0 || verbose > 1)
4347b00f4e9Sflorian 		buf_dump(&c->buf);
4357b00f4e9Sflorian 	free(req);
4367b00f4e9Sflorian 	return rc;
4377b00f4e9Sflorian }
4387b00f4e9Sflorian 
4397b00f4e9Sflorian /*
4407b00f4e9Sflorian  * Submit a new order for a certificate.
4417b00f4e9Sflorian  */
4427b00f4e9Sflorian static int
4437b00f4e9Sflorian doneworder(struct conn *c, const char *const *alts, size_t altsz,
4447b00f4e9Sflorian     struct order *order, const struct capaths *p)
4457b00f4e9Sflorian {
4467b00f4e9Sflorian 	struct jsmnn	*j = NULL;
4477b00f4e9Sflorian 	int		 rc = 0;
4487b00f4e9Sflorian 	char		*req;
4497b00f4e9Sflorian 	long		 lc;
4507b00f4e9Sflorian 
4517b00f4e9Sflorian 	if ((req = json_fmt_neworder(alts, altsz)) == NULL)
4527b00f4e9Sflorian 		warnx("json_fmt_neworder");
4537b00f4e9Sflorian 	else if ((lc = sreq(c, p->neworder, 1, req, &order->uri)) < 0)
4547b00f4e9Sflorian 		warnx("%s: bad comm", p->neworder);
4557b00f4e9Sflorian 	else if (lc != 201)
4567b00f4e9Sflorian 		warnx("%s: bad HTTP: %ld", p->neworder, lc);
4577b00f4e9Sflorian 	else if ((j = json_parse(c->buf.buf, c->buf.sz)) == NULL)
4587b00f4e9Sflorian 		warnx("%s: bad JSON object", p->neworder);
4597b00f4e9Sflorian 	else if (!json_parse_order(j, order))
4607b00f4e9Sflorian 		warnx("%s: bad order", p->neworder);
4617b00f4e9Sflorian 	else if (order->status == ORDER_INVALID)
4627b00f4e9Sflorian 		warnx("%s: order invalid", p->neworder);
4637b00f4e9Sflorian 	else
4647b00f4e9Sflorian 		rc = 1;
4657b00f4e9Sflorian 
4667b00f4e9Sflorian 	if (rc == 0 || verbose > 1)
4677b00f4e9Sflorian 		buf_dump(&c->buf);
4687b00f4e9Sflorian 
4697b00f4e9Sflorian 	free(req);
4707b00f4e9Sflorian 	json_free(j);
4717b00f4e9Sflorian 	return rc;
4727b00f4e9Sflorian }
4737b00f4e9Sflorian 
4747b00f4e9Sflorian /*
4757b00f4e9Sflorian  * Update order status
4767b00f4e9Sflorian  */
4777b00f4e9Sflorian static int
4787b00f4e9Sflorian doupdorder(struct conn *c, struct order *order)
4797b00f4e9Sflorian {
4807b00f4e9Sflorian 	struct jsmnn	*j = NULL;
4817b00f4e9Sflorian 	int		 rc = 0;
4827b00f4e9Sflorian 	long		 lc;
4837b00f4e9Sflorian 
4847b00f4e9Sflorian 	if ((lc = sreq(c, order->uri, 1, "", NULL)) < 0)
4857b00f4e9Sflorian 		warnx("%s: bad comm", order->uri);
4867b00f4e9Sflorian 	else if (lc != 200)
4877b00f4e9Sflorian 		warnx("%s: bad HTTP: %ld", order->uri, lc);
4887b00f4e9Sflorian 	else if ((j = json_parse(c->buf.buf, c->buf.sz)) == NULL)
4897b00f4e9Sflorian 		warnx("%s: bad JSON object", order->uri);
4907b00f4e9Sflorian 	else if (!json_parse_upd_order(j, order))
4917b00f4e9Sflorian 		warnx("%s: bad order", order->uri);
4927b00f4e9Sflorian 	else
4937b00f4e9Sflorian 		rc = 1;
4947b00f4e9Sflorian 
4957b00f4e9Sflorian 	if (rc == 0 || verbose > 1)
4967b00f4e9Sflorian 		buf_dump(&c->buf);
4977b00f4e9Sflorian 
4987b00f4e9Sflorian 	json_free(j);
4997b00f4e9Sflorian 	return rc;
5007b00f4e9Sflorian }
5017b00f4e9Sflorian 
5027b00f4e9Sflorian /*
503de579d12Sflorian  * Request a challenge for the given domain name.
5048a38e73eSbenno  * This must be called for each name "alt".
505de579d12Sflorian  * On non-zero exit, fills in "chng" with the challenge.
506de579d12Sflorian  */
507de579d12Sflorian static int
5087b00f4e9Sflorian dochngreq(struct conn *c, const char *auth, struct chng *chng)
509de579d12Sflorian {
5107bce6888Sderaadt 	int		 rc = 0;
511de579d12Sflorian 	long		 lc;
5127bce6888Sderaadt 	struct jsmnn	*j = NULL;
513de579d12Sflorian 
5147b00f4e9Sflorian 	dodbg("%s: %s", __func__, auth);
515de579d12Sflorian 
5167b00f4e9Sflorian 	if ((lc = sreq(c, auth, 1, "", NULL)) < 0)
5177b00f4e9Sflorian 		warnx("%s: bad comm", auth);
5187b00f4e9Sflorian 	else if (lc != 200)
5197b00f4e9Sflorian 		warnx("%s: bad HTTP: %ld", auth, lc);
5207cd8f039Sjsing 	else if ((j = json_parse(c->buf.buf, c->buf.sz)) == NULL)
5217b00f4e9Sflorian 		warnx("%s: bad JSON object", auth);
522de579d12Sflorian 	else if (!json_parse_challenge(j, chng))
5237b00f4e9Sflorian 		warnx("%s: bad challenge", auth);
524de579d12Sflorian 	else
525de579d12Sflorian 		rc = 1;
526de579d12Sflorian 
5277cd8f039Sjsing 	if (rc == 0 || verbose > 1)
528de579d12Sflorian 		buf_dump(&c->buf);
529de579d12Sflorian 	json_free(j);
53034335c11Sjsing 	return rc;
531de579d12Sflorian }
532de579d12Sflorian 
533de579d12Sflorian /*
5348a38e73eSbenno  * Tell the CA that a challenge response is in place.
535de579d12Sflorian  */
536de579d12Sflorian static int
5377b00f4e9Sflorian dochngresp(struct conn *c, const struct chng *chng)
538de579d12Sflorian {
5397bce6888Sderaadt 	int	 rc = 0;
540de579d12Sflorian 	long	 lc;
541de579d12Sflorian 
542de579d12Sflorian 	dodbg("%s: challenge", chng->uri);
543de579d12Sflorian 
5447b00f4e9Sflorian 	if ((lc = sreq(c, chng->uri, 1, "{}", NULL)) < 0)
545de579d12Sflorian 		warnx("%s: bad comm", chng->uri);
5467cd8f039Sjsing 	else if (lc != 200 && lc != 201 && lc != 202)
547de579d12Sflorian 		warnx("%s: bad HTTP: %ld", chng->uri, lc);
548de579d12Sflorian 	else
549de579d12Sflorian 		rc = 1;
550de579d12Sflorian 
5517cd8f039Sjsing 	if (rc == 0 || verbose > 1)
552de579d12Sflorian 		buf_dump(&c->buf);
5537b00f4e9Sflorian 	return rc;
5547b00f4e9Sflorian }
5557b00f4e9Sflorian 
5567b00f4e9Sflorian /*
5577b00f4e9Sflorian  * Submit our csr to the CA.
5587b00f4e9Sflorian  */
5597b00f4e9Sflorian static int
5607b00f4e9Sflorian docert(struct conn *c, const char *uri, const char *csr)
5617b00f4e9Sflorian {
5627b00f4e9Sflorian 	char	*req;
5637b00f4e9Sflorian 	int	 rc = 0;
5647b00f4e9Sflorian 	long	 lc;
5657b00f4e9Sflorian 
5667b00f4e9Sflorian 	dodbg("%s: certificate", uri);
5677b00f4e9Sflorian 
5687b00f4e9Sflorian 	if ((req = json_fmt_newcert(csr)) == NULL)
5697b00f4e9Sflorian 		warnx("json_fmt_newcert");
5707b00f4e9Sflorian 	else if ((lc = sreq(c, uri, 1, req, NULL)) < 0)
5717b00f4e9Sflorian 		warnx("%s: bad comm", uri);
5727b00f4e9Sflorian 	else if (lc != 200)
5737b00f4e9Sflorian 		warnx("%s: bad HTTP: %ld", uri, lc);
5747b00f4e9Sflorian 	else if (c->buf.sz == 0 || c->buf.buf == NULL)
5757b00f4e9Sflorian 		warnx("%s: empty response", uri);
5767b00f4e9Sflorian 	else
5777b00f4e9Sflorian 		rc = 1;
5787b00f4e9Sflorian 
5797b00f4e9Sflorian 	if (rc == 0 || verbose > 1)
5807b00f4e9Sflorian 		buf_dump(&c->buf);
581de579d12Sflorian 	free(req);
58234335c11Sjsing 	return rc;
583de579d12Sflorian }
584de579d12Sflorian 
585de579d12Sflorian /*
5867b00f4e9Sflorian  * Get certificate from CA
587de579d12Sflorian  */
588de579d12Sflorian static int
5897b00f4e9Sflorian dogetcert(struct conn *c, const char *uri)
590de579d12Sflorian {
5917b00f4e9Sflorian 	int	 rc = 0;
592de579d12Sflorian 	long	 lc;
593de579d12Sflorian 
5947b00f4e9Sflorian 	dodbg("%s: certificate", uri);
595de579d12Sflorian 
5967b00f4e9Sflorian 	if ((lc = sreq(c, uri, 1, "", NULL)) < 0)
5977b00f4e9Sflorian 		warnx("%s: bad comm", uri);
5987b00f4e9Sflorian 	else if (lc != 200)
5997b00f4e9Sflorian 		warnx("%s: bad HTTP: %ld", uri, lc);
6007b00f4e9Sflorian 	else if (c->buf.sz == 0 || c->buf.buf == NULL)
6017b00f4e9Sflorian 		warnx("%s: empty response", uri);
6027b00f4e9Sflorian 	else
6037b00f4e9Sflorian 		rc = 1;
604de579d12Sflorian 
6057b00f4e9Sflorian 	if (rc == 0 || verbose > 1)
6067b00f4e9Sflorian 		buf_dump(&c->buf);
6077b00f4e9Sflorian 
6087b00f4e9Sflorian 	return rc;
609de579d12Sflorian }
610de579d12Sflorian 
611de579d12Sflorian static int
612de579d12Sflorian dorevoke(struct conn *c, const char *addr, const char *cert)
613de579d12Sflorian {
614de579d12Sflorian 	char		*req;
6157bce6888Sderaadt 	int		 rc = 0;
6167bce6888Sderaadt 	long		 lc = 0;
617de579d12Sflorian 
618de579d12Sflorian 	dodbg("%s: revocation", addr);
619de579d12Sflorian 
6207cd8f039Sjsing 	if ((req = json_fmt_revokecert(cert)) == NULL)
621de579d12Sflorian 		warnx("json_fmt_revokecert");
6227b00f4e9Sflorian 	else if ((lc = sreq(c, addr, 1, req, NULL)) < 0)
623de579d12Sflorian 		warnx("%s: bad comm", addr);
6247cd8f039Sjsing 	else if (lc != 200 && lc != 201 && lc != 409)
625de579d12Sflorian 		warnx("%s: bad HTTP: %ld", addr, lc);
626de579d12Sflorian 	else
627de579d12Sflorian 		rc = 1;
628de579d12Sflorian 
6297cd8f039Sjsing 	if (lc == 409)
630de579d12Sflorian 		warnx("%s: already revoked", addr);
631de579d12Sflorian 
6327cd8f039Sjsing 	if (rc == 0 || verbose > 1)
633de579d12Sflorian 		buf_dump(&c->buf);
634de579d12Sflorian 	free(req);
63534335c11Sjsing 	return rc;
636de579d12Sflorian }
637de579d12Sflorian 
638de579d12Sflorian /*
639de579d12Sflorian  * Look up directories from the certificate authority.
640de579d12Sflorian  */
641de579d12Sflorian static int
642de579d12Sflorian dodirs(struct conn *c, const char *addr, struct capaths *paths)
643de579d12Sflorian {
6447bce6888Sderaadt 	struct jsmnn	*j = NULL;
645de579d12Sflorian 	long		 lc;
6467bce6888Sderaadt 	int		 rc = 0;
647de579d12Sflorian 
648de579d12Sflorian 	dodbg("%s: directories", addr);
649de579d12Sflorian 
650de579d12Sflorian 	if ((lc = nreq(c, addr)) < 0)
651de579d12Sflorian 		warnx("%s: bad comm", addr);
6527cd8f039Sjsing 	else if (lc != 200 && lc != 201)
653de579d12Sflorian 		warnx("%s: bad HTTP: %ld", addr, lc);
6547cd8f039Sjsing 	else if ((j = json_parse(c->buf.buf, c->buf.sz)) == NULL)
655de579d12Sflorian 		warnx("json_parse");
656de579d12Sflorian 	else if (!json_parse_capaths(j, paths))
657de579d12Sflorian 		warnx("%s: bad CA paths", addr);
658de579d12Sflorian 	else
659de579d12Sflorian 		rc = 1;
660de579d12Sflorian 
6617cd8f039Sjsing 	if (rc == 0 || verbose > 1)
662de579d12Sflorian 		buf_dump(&c->buf);
663de579d12Sflorian 	json_free(j);
66434335c11Sjsing 	return rc;
665de579d12Sflorian }
666de579d12Sflorian 
667de579d12Sflorian /*
6688a38e73eSbenno  * Communicate with the ACME server.
6698a38e73eSbenno  * We need the certificate we want to upload and our account key information.
670de579d12Sflorian  */
671de579d12Sflorian int
672de579d12Sflorian netproc(int kfd, int afd, int Cfd, int cfd, int dfd, int rfd,
6737b00f4e9Sflorian     int revocate, struct authority_c *authority,
6740b28b247Sflorian     const char *const *alts, size_t altsz)
675de579d12Sflorian {
6767bce6888Sderaadt 	int		 rc = 0;
6777b00f4e9Sflorian 	size_t		 i;
678c5ad7108Stb 	char		*cert = NULL, *thumb = NULL, *error = NULL;
679de579d12Sflorian 	struct conn	 c;
680de579d12Sflorian 	struct capaths	 paths;
6817b00f4e9Sflorian 	struct order	 order;
6827bce6888Sderaadt 	struct chng	*chngs = NULL;
683de579d12Sflorian 	long		 lval;
684de579d12Sflorian 
685de579d12Sflorian 	memset(&paths, 0, sizeof(struct capaths));
686de579d12Sflorian 	memset(&c, 0, sizeof(struct conn));
687de579d12Sflorian 
688fb0a89eeStedu 	if (unveil(tls_default_ca_cert_file(), "r") == -1) {
689bc5a8259Sbeck 		warn("unveil %s", tls_default_ca_cert_file());
69082b65d8aSderaadt 		goto out;
69182b65d8aSderaadt 	}
69282b65d8aSderaadt 
69325ca385bSjsing 	if (pledge("stdio inet rpath", NULL) == -1) {
69425ca385bSjsing 		warn("pledge");
69525ca385bSjsing 		goto out;
69625ca385bSjsing 	}
69725ca385bSjsing 
69825ca385bSjsing 	if (http_init() == -1) {
69925ca385bSjsing 		warn("http_init");
70025ca385bSjsing 		goto out;
70125ca385bSjsing 	}
70225ca385bSjsing 
703ec0d8c8bSderaadt 	if (pledge("stdio inet", NULL) == -1) {
704ec0d8c8bSderaadt 		warn("pledge");
705de579d12Sflorian 		goto out;
706ec0d8c8bSderaadt 	}
707de579d12Sflorian 
708de579d12Sflorian 	/*
7098a38e73eSbenno 	 * Wait until the acctproc, keyproc, and revokeproc have started up and
7108a38e73eSbenno 	 * are ready to serve us data.
7118a38e73eSbenno 	 * Then check whether revokeproc indicates that the certificate on file
7128a38e73eSbenno 	 * (if any) can be updated.
713de579d12Sflorian 	 */
7147cd8f039Sjsing 	if ((lval = readop(afd, COMM_ACCT_STAT)) == 0) {
715de579d12Sflorian 		rc = 1;
716de579d12Sflorian 		goto out;
7177cd8f039Sjsing 	} else if (lval != ACCT_READY) {
718de579d12Sflorian 		warnx("unknown operation from acctproc");
719de579d12Sflorian 		goto out;
720de579d12Sflorian 	}
721de579d12Sflorian 
7227cd8f039Sjsing 	if ((lval = readop(kfd, COMM_KEY_STAT)) == 0) {
723de579d12Sflorian 		rc = 1;
724de579d12Sflorian 		goto out;
7257cd8f039Sjsing 	} else if (lval != KEY_READY) {
726de579d12Sflorian 		warnx("unknown operation from keyproc");
727de579d12Sflorian 		goto out;
728de579d12Sflorian 	}
729de579d12Sflorian 
7307cd8f039Sjsing 	if ((lval = readop(rfd, COMM_REVOKE_RESP)) == 0) {
731de579d12Sflorian 		rc = 1;
732de579d12Sflorian 		goto out;
7337cd8f039Sjsing 	} else if (lval != REVOKE_EXP && lval != REVOKE_OK) {
734de579d12Sflorian 		warnx("unknown operation from revokeproc");
735de579d12Sflorian 		goto out;
736de579d12Sflorian 	}
737de579d12Sflorian 
738de579d12Sflorian 	/* If our certificate is up-to-date, return now. */
7397cd8f039Sjsing 	if (lval == REVOKE_OK) {
740de579d12Sflorian 		rc = 1;
741de579d12Sflorian 		goto out;
742de579d12Sflorian 	}
743de579d12Sflorian 
744de579d12Sflorian 	c.dfd = dfd;
745de579d12Sflorian 	c.fd = afd;
746de579d12Sflorian 
747de579d12Sflorian 	/*
7488a38e73eSbenno 	 * Look up the API urls of the ACME server.
749de579d12Sflorian 	 */
7507b00f4e9Sflorian 	if (!dodirs(&c, authority->api, &paths))
7517b00f4e9Sflorian 		goto out;
7527b00f4e9Sflorian 
7537b00f4e9Sflorian 	c.newnonce = paths.newnonce;
7547b00f4e9Sflorian 
7557b00f4e9Sflorian 	/* Check if our account already exists or create it. */
7566736ff2bSflorian 	if (!dochkacc(&c, &paths, authority->contact))
757de579d12Sflorian 		goto out;
758de579d12Sflorian 
759de579d12Sflorian 	/*
760de579d12Sflorian 	 * If we're meant to revoke, then wait for revokeproc to send us
761de579d12Sflorian 	 * the certificate (if it's found at all).
762de579d12Sflorian 	 * Following that, submit the request to the CA then notify the
763de579d12Sflorian 	 * certproc, which will in turn notify the fileproc.
7647b00f4e9Sflorian 	 * XXX currently we can only sign with the account key, the RFC
7657b00f4e9Sflorian 	 * also mentions signing with the privat key of the cert itself.
766de579d12Sflorian 	 */
767ee27a5e1Sderaadt 	if (revocate) {
7687cd8f039Sjsing 		if ((cert = readstr(rfd, COMM_CSR)) == NULL)
769de579d12Sflorian 			goto out;
770de579d12Sflorian 		if (!dorevoke(&c, paths.revokecert, cert))
771de579d12Sflorian 			goto out;
772de579d12Sflorian 		else if (writeop(cfd, COMM_CSR_OP, CERT_REVOKE) > 0)
773de579d12Sflorian 			rc = 1;
774de579d12Sflorian 		goto out;
775de579d12Sflorian 	}
776de579d12Sflorian 
7777b00f4e9Sflorian 	memset(&order, 0, sizeof(order));
7787b00f4e9Sflorian 
7797b00f4e9Sflorian 	if (!doneworder(&c, alts, altsz, &order, &paths))
780de579d12Sflorian 		goto out;
781de579d12Sflorian 
7827b00f4e9Sflorian 	chngs = calloc(order.authsz, sizeof(struct chng));
7837b00f4e9Sflorian 	if (chngs == NULL) {
7847b00f4e9Sflorian 		warn("calloc");
785de579d12Sflorian 		goto out;
7867b00f4e9Sflorian 	}
787de579d12Sflorian 
788de579d12Sflorian 	/*
7897b00f4e9Sflorian 	 * Get thumbprint from acctproc. We will need it to construct
7907b00f4e9Sflorian 	 * a response to the challenge
791de579d12Sflorian 	 */
792de579d12Sflorian 	if (writeop(afd, COMM_ACCT, ACCT_THUMBPRINT) <= 0)
793de579d12Sflorian 		goto out;
7947cd8f039Sjsing 	else if ((thumb = readstr(afd, COMM_THUMB)) == NULL)
795de579d12Sflorian 		goto out;
796de579d12Sflorian 
7977b00f4e9Sflorian 	while(order.status != ORDER_VALID && order.status != ORDER_INVALID) {
7987b00f4e9Sflorian 		switch (order.status) {
7997b00f4e9Sflorian 		case ORDER_INVALID:
8007b00f4e9Sflorian 			warnx("order invalid");
8017b00f4e9Sflorian 			goto out;
8027b00f4e9Sflorian 		case ORDER_VALID:
8037b00f4e9Sflorian 			rc = 1;
8047b00f4e9Sflorian 			continue;
8057b00f4e9Sflorian 		case ORDER_PENDING:
8067b00f4e9Sflorian 			if (order.authsz < 1) {
8077b00f4e9Sflorian 				warnx("order is in state pending but no "
8087b00f4e9Sflorian 				    "authorizations know");
8097b00f4e9Sflorian 				goto out;
8107b00f4e9Sflorian 			}
8117b00f4e9Sflorian 			for (i = 0; i < order.authsz; i++) {
8127b00f4e9Sflorian 				if (!dochngreq(&c, order.auths[i], &chngs[i]))
8137b00f4e9Sflorian 					goto out;
8147b00f4e9Sflorian 
8157b00f4e9Sflorian 				dodbg("challenge, token: %s, uri: %s, status: "
8167b00f4e9Sflorian 				    "%d", chngs[i].token, chngs[i].uri,
8177b00f4e9Sflorian 				    chngs[i].status);
8187b00f4e9Sflorian 
81942c2cc51Sflorian 				if (chngs[i].status == CHNG_VALID ||
82042c2cc51Sflorian 				    chngs[i].status == CHNG_INVALID)
8217b00f4e9Sflorian 					continue;
8227b00f4e9Sflorian 
8237b00f4e9Sflorian 				if (chngs[i].retry++ >= RETRY_MAX) {
8247b00f4e9Sflorian 					warnx("%s: too many tries",
8257b00f4e9Sflorian 					    chngs[i].uri);
8267b00f4e9Sflorian 					goto out;
8277b00f4e9Sflorian 				}
8287b00f4e9Sflorian 
829de579d12Sflorian 				if (writeop(Cfd, COMM_CHNG_OP, CHNG_SYN) <= 0)
830de579d12Sflorian 					goto out;
831de579d12Sflorian 				else if (writestr(Cfd, COMM_THUMB, thumb) <= 0)
832de579d12Sflorian 					goto out;
8337b00f4e9Sflorian 				else if (writestr(Cfd, COMM_TOK,
8347b00f4e9Sflorian 				    chngs[i].token) <= 0)
835de579d12Sflorian 					goto out;
836de579d12Sflorian 
837de579d12Sflorian 				/* Read that the challenge has been made. */
8387cd8f039Sjsing 				if (readop(Cfd, COMM_CHNG_ACK) != CHNG_ACK)
839de579d12Sflorian 					goto out;
840de579d12Sflorian 
841e1b5cdf1Sflorian 			}
842de579d12Sflorian 			/* Write to the CA that it's ready. */
843e1b5cdf1Sflorian 			for (i = 0; i < order.authsz; i++) {
844e1b5cdf1Sflorian 				if (chngs[i].status == CHNG_VALID ||
845e1b5cdf1Sflorian 				    chngs[i].status == CHNG_INVALID)
846e1b5cdf1Sflorian 					continue;
8477b00f4e9Sflorian 				if (!dochngresp(&c, &chngs[i]))
848de579d12Sflorian 					goto out;
849de579d12Sflorian 			}
850e26d00f9Sbenno 			break;
8517b00f4e9Sflorian 		case ORDER_READY:
852de579d12Sflorian 			/*
8537b00f4e9Sflorian 			 * Write our acknowledgement that the challenges are
8547b00f4e9Sflorian 			 * over.
855de579d12Sflorian 			 * The challenge process will remove all of the files.
856de579d12Sflorian 			 */
857de579d12Sflorian 			if (writeop(Cfd, COMM_CHNG_OP, CHNG_STOP) <= 0)
858de579d12Sflorian 				goto out;
859de579d12Sflorian 
860de579d12Sflorian 			/* Wait to receive the certificate itself. */
8617cd8f039Sjsing 			if ((cert = readstr(kfd, COMM_CERT)) == NULL)
862de579d12Sflorian 				goto out;
8637b00f4e9Sflorian 			if (!docert(&c, order.finalize, cert))
8647b00f4e9Sflorian 				goto out;
8657b00f4e9Sflorian 			break;
8667b00f4e9Sflorian 		default:
8677b00f4e9Sflorian 			warnx("unhandled status: %d", order.status);
8687b00f4e9Sflorian 			goto out;
8697b00f4e9Sflorian 		}
8707b00f4e9Sflorian 		if (!doupdorder(&c, &order))
8717b00f4e9Sflorian 			goto out;
872de579d12Sflorian 
8737b00f4e9Sflorian 		dodbg("order.status %d", order.status);
8747b00f4e9Sflorian 		if (order.status == ORDER_PENDING)
8757b00f4e9Sflorian 			sleep(RETRY_DELAY);
8767b00f4e9Sflorian 	}
8777b00f4e9Sflorian 
87842c2cc51Sflorian 	if (order.status != ORDER_VALID) {
87942c2cc51Sflorian 		for (i = 0; i < order.authsz; i++) {
88042c2cc51Sflorian 			dochngreq(&c, order.auths[i], &chngs[i]);
88142c2cc51Sflorian 			if (chngs[i].error != NULL) {
88242c2cc51Sflorian 				if (stravis(&error, chngs[i].error, VIS_SAFE)
88342c2cc51Sflorian 				    != -1) {
88442c2cc51Sflorian 					warnx("%s", error);
88542c2cc51Sflorian 					free(error);
88642c2cc51Sflorian 					error = NULL;
88742c2cc51Sflorian 				}
88842c2cc51Sflorian 			}
88942c2cc51Sflorian 		}
8907b00f4e9Sflorian 		goto out;
89142c2cc51Sflorian 	}
8927b00f4e9Sflorian 
8937b00f4e9Sflorian 	if (order.certificate == NULL) {
8947b00f4e9Sflorian 		warnx("no certificate url received");
8957b00f4e9Sflorian 		goto out;
8967b00f4e9Sflorian 	}
8977b00f4e9Sflorian 
8987b00f4e9Sflorian 	if (!dogetcert(&c, order.certificate))
899de579d12Sflorian 		goto out;
900de579d12Sflorian 	else if (writeop(cfd, COMM_CSR_OP, CERT_UPDATE) <= 0)
901de579d12Sflorian 		goto out;
902de579d12Sflorian 	else if (writebuf(cfd, COMM_CSR, c.buf.buf, c.buf.sz) <= 0)
903de579d12Sflorian 		goto out;
904de579d12Sflorian 	rc = 1;
905de579d12Sflorian out:
906de579d12Sflorian 	close(cfd);
907de579d12Sflorian 	close(kfd);
908de579d12Sflorian 	close(afd);
909de579d12Sflorian 	close(Cfd);
910de579d12Sflorian 	close(dfd);
911de579d12Sflorian 	close(rfd);
912de579d12Sflorian 	free(cert);
913de579d12Sflorian 	free(thumb);
9147b00f4e9Sflorian 	free(c.kid);
915de579d12Sflorian 	free(c.buf.buf);
9167cd8f039Sjsing 	if (chngs != NULL)
9174e6b438aSflorian 		for (i = 0; i < order.authsz; i++)
918de579d12Sflorian 			json_free_challenge(&chngs[i]);
919de579d12Sflorian 	free(chngs);
920de579d12Sflorian 	json_free_capaths(&paths);
92134335c11Sjsing 	return rc;
922de579d12Sflorian }
923