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