1 /* $Id: netproc.c,v 1.19 2018/11/29 14:25:07 tedu 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 27 #include "http.h" 28 #include "extern.h" 29 #include "parse.h" 30 31 #define RETRY_DELAY 5 32 #define RETRY_MAX 10 33 34 /* 35 * Buffer used when collecting the results of a CURL transfer. 36 */ 37 struct buf { 38 char *buf; /* binary buffer */ 39 size_t sz; /* length of buffer */ 40 }; 41 42 /* 43 * Used for CURL communications. 44 */ 45 struct conn { 46 const char *na; /* nonce authority */ 47 int fd; /* acctproc handle */ 48 int dfd; /* dnsproc handle */ 49 struct buf buf; /* transfer buffer */ 50 }; 51 52 /* 53 * If something goes wrong (or we're tracing output), we dump the 54 * current transfer's data as a debug message. 55 * Make sure that print all non-printable characters as question marks 56 * so that we don't spam the console. 57 * Also, consolidate white-space. 58 * This of course will ruin string literals, but the intent here is just 59 * to show the message, not to replicate it. 60 */ 61 static void 62 buf_dump(const struct buf *buf) 63 { 64 size_t i; 65 int j; 66 char *nbuf; 67 68 if (buf->sz == 0) 69 return; 70 if ((nbuf = malloc(buf->sz)) == NULL) 71 err(EXIT_FAILURE, "malloc"); 72 73 for (j = 0, i = 0; i < buf->sz; i++) 74 if (isspace((int)buf->buf[i])) { 75 nbuf[j++] = ' '; 76 while (isspace((int)buf->buf[i])) 77 i++; 78 i--; 79 } else 80 nbuf[j++] = isprint((int)buf->buf[i]) ? 81 buf->buf[i] : '?'; 82 dodbg("transfer buffer: [%.*s] (%zu bytes)", j, nbuf, buf->sz); 83 free(nbuf); 84 } 85 86 /* 87 * Extract the domain and port from a URL. 88 * The url must be formatted as schema://address[/stuff]. 89 * This returns NULL on failure. 90 */ 91 static char * 92 url2host(const char *host, short *port, char **path) 93 { 94 char *url, *ep; 95 96 /* We only understand HTTP and HTTPS. */ 97 98 if (strncmp(host, "https://", 8) == 0) { 99 *port = 443; 100 if ((url = strdup(host + 8)) == NULL) { 101 warn("strdup"); 102 return NULL; 103 } 104 } else if (strncmp(host, "http://", 7) == 0) { 105 *port = 80; 106 if ((url = strdup(host + 7)) == NULL) { 107 warn("strdup"); 108 return NULL; 109 } 110 } else { 111 warnx("%s: unknown schema", host); 112 return NULL; 113 } 114 115 /* Terminate path part. */ 116 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, 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 return -1; 226 } 227 228 dodbg("Location: %s", st->val); 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 242 free(c->buf.buf); 243 c->buf.sz = g->bodypartsz; 244 c->buf.buf = malloc(c->buf.sz); 245 if (c->buf.buf == NULL) { 246 warn("malloc"); 247 code = -1; 248 } else 249 memcpy(c->buf.buf, g->bodypart, c->buf.sz); 250 http_get_free(g); 251 return code; 252 } 253 254 /* 255 * Create and send a signed communication to the ACME server. 256 * Stuff the response into the communication buffer. 257 * Return <0 on failure on the HTTP error code otherwise. 258 */ 259 static long 260 sreq(struct conn *c, const char *addr, const char *req) 261 { 262 struct httpget *g; 263 struct source src[MAX_SERVERS_DNS]; 264 char *host, *path, *nonce, *reqsn; 265 short port; 266 struct httphead *h; 267 ssize_t ssz; 268 long code; 269 270 if ((host = url2host(c->na, &port, &path)) == NULL) 271 return -1; 272 273 if ((ssz = urlresolve(c->dfd, host, src)) < 0) { 274 free(host); 275 free(path); 276 return -1; 277 } 278 279 g = http_get(src, (size_t)ssz, host, port, path, NULL, 0); 280 free(host); 281 free(path); 282 if (g == NULL) 283 return -1; 284 285 h = http_head_get("Replay-Nonce", g->head, g->headsz); 286 if (h == NULL) { 287 warnx("%s: no replay nonce", c->na); 288 http_get_free(g); 289 return -1; 290 } else if ((nonce = strdup(h->val)) == NULL) { 291 warn("strdup"); 292 http_get_free(g); 293 return -1; 294 } 295 http_get_free(g); 296 297 /* 298 * Send the nonce and request payload to the acctproc. 299 * This will create the proper JSON object we need. 300 */ 301 302 if (writeop(c->fd, COMM_ACCT, ACCT_SIGN) <= 0) { 303 free(nonce); 304 return -1; 305 } else if (writestr(c->fd, COMM_PAY, req) <= 0) { 306 free(nonce); 307 return -1; 308 } else if (writestr(c->fd, COMM_NONCE, nonce) <= 0) { 309 free(nonce); 310 return -1; 311 } 312 free(nonce); 313 314 /* Now read back the signed payload. */ 315 316 if ((reqsn = readstr(c->fd, COMM_REQ)) == NULL) 317 return -1; 318 319 /* Now send the signed payload to the CA. */ 320 321 if ((host = url2host(addr, &port, &path)) == NULL) { 322 free(reqsn); 323 return -1; 324 } else if ((ssz = urlresolve(c->dfd, host, src)) < 0) { 325 free(host); 326 free(path); 327 free(reqsn); 328 return -1; 329 } 330 331 g = http_get(src, (size_t)ssz, host, port, path, reqsn, strlen(reqsn)); 332 333 free(host); 334 free(path); 335 free(reqsn); 336 if (g == NULL) 337 return -1; 338 339 /* Stuff response into parse buffer. */ 340 341 code = g->code; 342 343 free(c->buf.buf); 344 c->buf.sz = g->bodypartsz; 345 c->buf.buf = malloc(c->buf.sz); 346 if (c->buf.buf == NULL) { 347 warn("malloc"); 348 code = -1; 349 } else 350 memcpy(c->buf.buf, g->bodypart, c->buf.sz); 351 http_get_free(g); 352 return code; 353 } 354 355 /* 356 * Send to the CA that we want to authorise a new account. 357 * This only happens once for a new account key. 358 * Returns non-zero on success. 359 */ 360 static int 361 donewreg(struct conn *c, const struct capaths *p) 362 { 363 int rc = 0; 364 char *req; 365 long lc; 366 367 dodbg("%s: new-reg", p->newreg); 368 369 if ((req = json_fmt_newreg(p->agreement)) == NULL) 370 warnx("json_fmt_newreg"); 371 else if ((lc = sreq(c, p->newreg, req)) < 0) 372 warnx("%s: bad comm", p->newreg); 373 else if (lc != 200 && lc != 201) 374 warnx("%s: bad HTTP: %ld", p->newreg, lc); 375 else if (c->buf.buf == NULL || c->buf.sz == 0) 376 warnx("%s: empty response", p->newreg); 377 else 378 rc = 1; 379 380 if (rc == 0 || verbose > 1) 381 buf_dump(&c->buf); 382 free(req); 383 return rc; 384 } 385 386 /* 387 * Request a challenge for the given domain name. 388 * This must happen for each name "alt". 389 * On non-zero exit, fills in "chng" with the challenge. 390 */ 391 static int 392 dochngreq(struct conn *c, const char *alt, struct chng *chng, 393 const struct capaths *p) 394 { 395 int rc = 0; 396 char *req; 397 long lc; 398 struct jsmnn *j = NULL; 399 400 dodbg("%s: req-auth: %s", p->newauthz, alt); 401 402 if ((req = json_fmt_newauthz(alt)) == NULL) 403 warnx("json_fmt_newauthz"); 404 else if ((lc = sreq(c, p->newauthz, req)) < 0) 405 warnx("%s: bad comm", p->newauthz); 406 else if (lc != 200 && lc != 201) 407 warnx("%s: bad HTTP: %ld", p->newauthz, lc); 408 else if ((j = json_parse(c->buf.buf, c->buf.sz)) == NULL) 409 warnx("%s: bad JSON object", p->newauthz); 410 else if (!json_parse_challenge(j, chng)) 411 warnx("%s: bad challenge", p->newauthz); 412 else 413 rc = 1; 414 415 if (rc == 0 || verbose > 1) 416 buf_dump(&c->buf); 417 json_free(j); 418 free(req); 419 return rc; 420 } 421 422 /* 423 * Note to the CA that a challenge response is in place. 424 */ 425 static int 426 dochngresp(struct conn *c, const struct chng *chng, const char *th) 427 { 428 int rc = 0; 429 long lc; 430 char *req; 431 432 dodbg("%s: challenge", chng->uri); 433 434 if ((req = json_fmt_challenge(chng->token, th)) == NULL) 435 warnx("json_fmt_challenge"); 436 else if ((lc = sreq(c, chng->uri, req)) < 0) 437 warnx("%s: bad comm", chng->uri); 438 else if (lc != 200 && lc != 201 && lc != 202) 439 warnx("%s: bad HTTP: %ld", chng->uri, lc); 440 else 441 rc = 1; 442 443 if (rc == 0 || verbose > 1) 444 buf_dump(&c->buf); 445 free(req); 446 return rc; 447 } 448 449 /* 450 * Check with the CA whether a challenge has been processed. 451 * Note: we'll only do this a limited number of times, and pause for a 452 * time between checks, but this happens in the caller. 453 */ 454 static int 455 dochngcheck(struct conn *c, struct chng *chng) 456 { 457 int cc; 458 long lc; 459 struct jsmnn *j; 460 461 dodbg("%s: status", chng->uri); 462 463 if ((lc = nreq(c, chng->uri)) < 0) { 464 warnx("%s: bad comm", chng->uri); 465 return 0; 466 } else if (lc != 200 && lc != 201 && lc != 202) { 467 warnx("%s: bad HTTP: %ld", chng->uri, lc); 468 buf_dump(&c->buf); 469 return 0; 470 } else if ((j = json_parse(c->buf.buf, c->buf.sz)) == NULL) { 471 warnx("%s: bad JSON object", chng->uri); 472 buf_dump(&c->buf); 473 return 0; 474 } else if ((cc = json_parse_response(j)) == -1) { 475 warnx("%s: bad response", chng->uri); 476 buf_dump(&c->buf); 477 json_free(j); 478 return 0; 479 } else if (cc > 0) 480 chng->status = 1; 481 482 json_free(j); 483 return 1; 484 } 485 486 static int 487 dorevoke(struct conn *c, const char *addr, const char *cert) 488 { 489 char *req; 490 int rc = 0; 491 long lc = 0; 492 493 dodbg("%s: revocation", addr); 494 495 if ((req = json_fmt_revokecert(cert)) == NULL) 496 warnx("json_fmt_revokecert"); 497 else if ((lc = sreq(c, addr, req)) < 0) 498 warnx("%s: bad comm", addr); 499 else if (lc != 200 && lc != 201 && lc != 409) 500 warnx("%s: bad HTTP: %ld", addr, lc); 501 else 502 rc = 1; 503 504 if (lc == 409) 505 warnx("%s: already revoked", addr); 506 507 if (rc == 0 || verbose > 1) 508 buf_dump(&c->buf); 509 free(req); 510 return rc; 511 } 512 513 /* 514 * Submit our certificate to the CA. 515 * This, upon success, will return the signed CA. 516 */ 517 static int 518 docert(struct conn *c, const char *addr, const char *cert) 519 { 520 char *req; 521 int rc = 0; 522 long lc; 523 524 dodbg("%s: certificate", addr); 525 526 if ((req = json_fmt_newcert(cert)) == NULL) 527 warnx("json_fmt_newcert"); 528 else if ((lc = sreq(c, addr, req)) < 0) 529 warnx("%s: bad comm", addr); 530 else if (lc != 200 && lc != 201) 531 warnx("%s: bad HTTP: %ld", addr, lc); 532 else if (c->buf.sz == 0 || c->buf.buf == NULL) 533 warnx("%s: empty response", addr); 534 else 535 rc = 1; 536 537 if (rc == 0 || verbose > 1) 538 buf_dump(&c->buf); 539 free(req); 540 return rc; 541 } 542 543 /* 544 * Look up directories from the certificate authority. 545 */ 546 static int 547 dodirs(struct conn *c, const char *addr, struct capaths *paths) 548 { 549 struct jsmnn *j = NULL; 550 long lc; 551 int rc = 0; 552 553 dodbg("%s: directories", addr); 554 555 if ((lc = nreq(c, addr)) < 0) 556 warnx("%s: bad comm", addr); 557 else if (lc != 200 && lc != 201) 558 warnx("%s: bad HTTP: %ld", addr, lc); 559 else if ((j = json_parse(c->buf.buf, c->buf.sz)) == NULL) 560 warnx("json_parse"); 561 else if (!json_parse_capaths(j, paths)) 562 warnx("%s: bad CA paths", addr); 563 else 564 rc = 1; 565 566 if (rc == 0 || verbose > 1) 567 buf_dump(&c->buf); 568 json_free(j); 569 return rc; 570 } 571 572 /* 573 * Request the full chain certificate. 574 */ 575 static int 576 dofullchain(struct conn *c, const char *addr) 577 { 578 int rc = 0; 579 long lc; 580 581 dodbg("%s: full chain", addr); 582 583 if ((lc = nreq(c, addr)) < 0) 584 warnx("%s: bad comm", addr); 585 else if (lc != 200 && lc != 201) 586 warnx("%s: bad HTTP: %ld", addr, lc); 587 else 588 rc = 1; 589 590 if (rc == 0 || verbose > 1) 591 buf_dump(&c->buf); 592 return rc; 593 } 594 595 /* 596 * Here we communicate with the ACME server. 597 * For this, we'll need the certificate we want to upload and our 598 * account key information. 599 */ 600 int 601 netproc(int kfd, int afd, int Cfd, int cfd, int dfd, int rfd, 602 int newacct, int revocate, struct authority_c *authority, 603 const char *const *alts,size_t altsz) 604 { 605 int rc = 0; 606 size_t i; 607 char *cert = NULL, *thumb = NULL, *url = NULL; 608 struct conn c; 609 struct capaths paths; 610 struct chng *chngs = NULL; 611 long lval; 612 613 memset(&paths, 0, sizeof(struct capaths)); 614 memset(&c, 0, sizeof(struct conn)); 615 616 if (unveil(tls_default_ca_cert_file(), "r") == -1) { 617 warn("unveil"); 618 goto out; 619 } 620 621 if (pledge("stdio inet rpath", NULL) == -1) { 622 warn("pledge"); 623 goto out; 624 } 625 626 if (http_init() == -1) { 627 warn("http_init"); 628 goto out; 629 } 630 631 if (pledge("stdio inet", NULL) == -1) { 632 warn("pledge"); 633 goto out; 634 } 635 636 /* 637 * Wait until the acctproc, keyproc, and revokeproc have started 638 * up and are ready to serve us data. 639 * There's no point in running if these don't work. 640 * Then check whether revokeproc indicates that the certificate 641 * on file (if any) can be updated. 642 */ 643 644 if ((lval = readop(afd, COMM_ACCT_STAT)) == 0) { 645 rc = 1; 646 goto out; 647 } else if (lval != ACCT_READY) { 648 warnx("unknown operation from acctproc"); 649 goto out; 650 } 651 652 if ((lval = readop(kfd, COMM_KEY_STAT)) == 0) { 653 rc = 1; 654 goto out; 655 } else if (lval != KEY_READY) { 656 warnx("unknown operation from keyproc"); 657 goto out; 658 } 659 660 if ((lval = readop(rfd, COMM_REVOKE_RESP)) == 0) { 661 rc = 1; 662 goto out; 663 } else if (lval != REVOKE_EXP && lval != REVOKE_OK) { 664 warnx("unknown operation from revokeproc"); 665 goto out; 666 } 667 668 /* If our certificate is up-to-date, return now. */ 669 670 if (lval == REVOKE_OK) { 671 rc = 1; 672 goto out; 673 } 674 675 /* Allocate main state. */ 676 677 chngs = calloc(altsz, sizeof(struct chng)); 678 if (chngs == NULL) { 679 warn("calloc"); 680 goto out; 681 } 682 683 c.dfd = dfd; 684 c.fd = afd; 685 c.na = authority->api; 686 687 /* 688 * Look up the domain of the ACME server. 689 * We'll use this ourselves instead of having libcurl do the DNS 690 * resolution itself. 691 */ 692 if (!dodirs(&c, c.na, &paths)) 693 goto out; 694 695 /* 696 * If we're meant to revoke, then wait for revokeproc to send us 697 * the certificate (if it's found at all). 698 * Following that, submit the request to the CA then notify the 699 * certproc, which will in turn notify the fileproc. 700 */ 701 702 if (revocate) { 703 if ((cert = readstr(rfd, COMM_CSR)) == NULL) 704 goto out; 705 if (!dorevoke(&c, paths.revokecert, cert)) 706 goto out; 707 else if (writeop(cfd, COMM_CSR_OP, CERT_REVOKE) > 0) 708 rc = 1; 709 goto out; 710 } 711 712 /* If new, register with the CA server. */ 713 714 if (newacct && ! donewreg(&c, &paths)) 715 goto out; 716 717 /* Pre-authorise all domains with CA server. */ 718 719 for (i = 0; i < altsz; i++) 720 if (!dochngreq(&c, alts[i], &chngs[i], &paths)) 721 goto out; 722 723 /* 724 * We now have our challenges. 725 * We need to ask the acctproc for the thumbprint. 726 * We'll combine this to the challenge to create our response, 727 * which will be orchestrated by the chngproc. 728 */ 729 730 if (writeop(afd, COMM_ACCT, ACCT_THUMBPRINT) <= 0) 731 goto out; 732 else if ((thumb = readstr(afd, COMM_THUMB)) == NULL) 733 goto out; 734 735 /* We'll now ask chngproc to build the challenge. */ 736 737 for (i = 0; i < altsz; i++) { 738 if (writeop(Cfd, COMM_CHNG_OP, CHNG_SYN) <= 0) 739 goto out; 740 else if (writestr(Cfd, COMM_THUMB, thumb) <= 0) 741 goto out; 742 else if (writestr(Cfd, COMM_TOK, chngs[i].token) <= 0) 743 goto out; 744 745 /* Read that the challenge has been made. */ 746 747 if (readop(Cfd, COMM_CHNG_ACK) != CHNG_ACK) 748 goto out; 749 750 /* Write to the CA that it's ready. */ 751 752 if (!dochngresp(&c, &chngs[i], thumb)) 753 goto out; 754 } 755 756 /* 757 * We now wait on the ACME server for each domain. 758 * Connect to the server (assume it's the same server) once 759 * every five seconds. 760 */ 761 762 for (i = 0; i < altsz; i++) { 763 if (chngs[i].status == 1) 764 continue; 765 766 if (chngs[i].retry++ >= RETRY_MAX) { 767 warnx("%s: too many tries", chngs[i].uri); 768 goto out; 769 } 770 771 /* Sleep before every attempt. */ 772 sleep(RETRY_DELAY); 773 if (!dochngcheck(&c, &chngs[i])) 774 goto out; 775 } 776 777 /* 778 * Write our acknowledgement that the challenges are over. 779 * The challenge process will remove all of the files. 780 */ 781 782 if (writeop(Cfd, COMM_CHNG_OP, CHNG_STOP) <= 0) 783 goto out; 784 785 /* Wait to receive the certificate itself. */ 786 787 if ((cert = readstr(kfd, COMM_CERT)) == NULL) 788 goto out; 789 790 /* 791 * Otherwise, submit the CA for signing, download the signed 792 * copy, and ship that into the certificate process for copying. 793 */ 794 795 if (!docert(&c, paths.newcert, cert)) 796 goto out; 797 else if (writeop(cfd, COMM_CSR_OP, CERT_UPDATE) <= 0) 798 goto out; 799 else if (writebuf(cfd, COMM_CSR, c.buf.buf, c.buf.sz) <= 0) 800 goto out; 801 802 /* 803 * Read back the issuer from the certproc. 804 * Then contact the issuer to get the certificate chain. 805 * Write this chain directly back to the certproc. 806 */ 807 808 if ((url = readstr(cfd, COMM_ISSUER)) == NULL) 809 goto out; 810 else if (!dofullchain(&c, url)) 811 goto out; 812 else if (writebuf(cfd, COMM_CHAIN, c.buf.buf, c.buf.sz) <= 0) 813 goto out; 814 815 rc = 1; 816 out: 817 close(cfd); 818 close(kfd); 819 close(afd); 820 close(Cfd); 821 close(dfd); 822 close(rfd); 823 free(cert); 824 free(url); 825 free(thumb); 826 free(c.buf.buf); 827 if (chngs != NULL) 828 for (i = 0; i < altsz; i++) 829 json_free_challenge(&chngs[i]); 830 free(chngs); 831 json_free_capaths(&paths); 832 return rc; 833 } 834