1 /* $Id: netproc.c,v 1.22 2019/02/01 10:16:04 benno 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 if (strncmp(host, "https://", 8) == 0) { 98 *port = 443; 99 if ((url = strdup(host + 8)) == NULL) { 100 warn("strdup"); 101 return NULL; 102 } 103 } else if (strncmp(host, "http://", 7) == 0) { 104 *port = 80; 105 if ((url = strdup(host + 7)) == NULL) { 106 warn("strdup"); 107 return NULL; 108 } 109 } else { 110 warnx("%s: unknown schema", host); 111 return NULL; 112 } 113 114 /* Terminate path part. */ 115 if ((ep = strchr(url, '/')) != NULL) { 116 *path = strdup(ep); 117 *ep = '\0'; 118 } else 119 *path = strdup(""); 120 121 if (*path == NULL) { 122 warn("strdup"); 123 free(url); 124 return NULL; 125 } 126 127 return url; 128 } 129 130 /* 131 * Contact dnsproc and resolve a host. 132 * Place the answers in "v" and return the number of answers, which can 133 * be at most MAX_SERVERS_DNS. 134 * Return <0 on failure. 135 */ 136 static ssize_t 137 urlresolve(int fd, const char *host, struct source *v) 138 { 139 char *addr; 140 size_t i, sz; 141 long lval; 142 143 if (writeop(fd, COMM_DNS, DNS_LOOKUP) <= 0) 144 return -1; 145 else if (writestr(fd, COMM_DNSQ, host) <= 0) 146 return -1; 147 else if ((lval = readop(fd, COMM_DNSLEN)) < 0) 148 return -1; 149 150 sz = lval; 151 assert(sz <= MAX_SERVERS_DNS); 152 153 for (i = 0; i < sz; i++) { 154 memset(&v[i], 0, sizeof(struct source)); 155 if ((lval = readop(fd, COMM_DNSF)) < 0) 156 goto err; 157 else if (lval != 4 && lval != 6) 158 goto err; 159 else if ((addr = readstr(fd, COMM_DNSA)) == NULL) 160 goto err; 161 v[i].family = lval; 162 v[i].ip = addr; 163 } 164 165 return sz; 166 err: 167 for (i = 0; i < sz; i++) 168 free(v[i].ip); 169 return -1; 170 } 171 172 /* 173 * Send a "regular" HTTP GET message to "addr" and stuff the response 174 * into the connection buffer. 175 * Return the HTTP error code or <0 on failure. 176 */ 177 static long 178 nreq(struct conn *c, const char *addr) 179 { 180 struct httpget *g; 181 struct source src[MAX_SERVERS_DNS]; 182 struct httphead *st; 183 char *host, *path; 184 short port; 185 size_t srcsz; 186 ssize_t ssz; 187 long code; 188 int redirects = 0; 189 190 if ((host = url2host(addr, &port, &path)) == NULL) 191 return -1; 192 193 again: 194 if ((ssz = urlresolve(c->dfd, host, src)) < 0) { 195 free(host); 196 free(path); 197 return -1; 198 } 199 srcsz = ssz; 200 201 g = http_get(src, srcsz, host, port, path, NULL, 0); 202 free(host); 203 free(path); 204 if (g == NULL) 205 return -1; 206 207 switch (g->code) { 208 case 301: 209 case 302: 210 case 303: 211 case 307: 212 case 308: 213 redirects++; 214 if (redirects > 3) { 215 warnx("too many redirects"); 216 http_get_free(g); 217 return -1; 218 } 219 220 if ((st = http_head_get("Location", g->head, g->headsz)) == 221 NULL) { 222 warnx("redirect without location header"); 223 return -1; 224 } 225 226 dodbg("Location: %s", st->val); 227 host = url2host(st->val, &port, &path); 228 http_get_free(g); 229 if (host == NULL) 230 return -1; 231 goto again; 232 break; 233 default: 234 code = g->code; 235 break; 236 } 237 238 /* Copy the body part into our buffer. */ 239 free(c->buf.buf); 240 c->buf.sz = g->bodypartsz; 241 c->buf.buf = malloc(c->buf.sz); 242 if (c->buf.buf == NULL) { 243 warn("malloc"); 244 code = -1; 245 } else 246 memcpy(c->buf.buf, g->bodypart, c->buf.sz); 247 http_get_free(g); 248 return code; 249 } 250 251 /* 252 * Create and send a signed communication to the ACME server. 253 * Stuff the response into the communication buffer. 254 * Return <0 on failure on the HTTP error code otherwise. 255 */ 256 static long 257 sreq(struct conn *c, const char *addr, const char *req) 258 { 259 struct httpget *g; 260 struct source src[MAX_SERVERS_DNS]; 261 char *host, *path, *nonce, *reqsn; 262 short port; 263 struct httphead *h; 264 ssize_t ssz; 265 long code; 266 267 if ((host = url2host(c->na, &port, &path)) == NULL) 268 return -1; 269 270 if ((ssz = urlresolve(c->dfd, host, src)) < 0) { 271 free(host); 272 free(path); 273 return -1; 274 } 275 276 g = http_get(src, (size_t)ssz, host, port, path, NULL, 0); 277 free(host); 278 free(path); 279 if (g == NULL) 280 return -1; 281 282 h = http_head_get("Replay-Nonce", g->head, g->headsz); 283 if (h == NULL) { 284 warnx("%s: no replay nonce", c->na); 285 http_get_free(g); 286 return -1; 287 } else if ((nonce = strdup(h->val)) == NULL) { 288 warn("strdup"); 289 http_get_free(g); 290 return -1; 291 } 292 http_get_free(g); 293 294 /* 295 * Send the nonce and request payload to the acctproc. 296 * This will create the proper JSON object we need. 297 */ 298 if (writeop(c->fd, COMM_ACCT, ACCT_SIGN) <= 0) { 299 free(nonce); 300 return -1; 301 } else if (writestr(c->fd, COMM_PAY, req) <= 0) { 302 free(nonce); 303 return -1; 304 } else if (writestr(c->fd, COMM_NONCE, nonce) <= 0) { 305 free(nonce); 306 return -1; 307 } 308 free(nonce); 309 310 /* Now read back the signed payload. */ 311 if ((reqsn = readstr(c->fd, COMM_REQ)) == NULL) 312 return -1; 313 314 /* Now send the signed payload to the CA. */ 315 if ((host = url2host(addr, &port, &path)) == NULL) { 316 free(reqsn); 317 return -1; 318 } else if ((ssz = urlresolve(c->dfd, host, src)) < 0) { 319 free(host); 320 free(path); 321 free(reqsn); 322 return -1; 323 } 324 325 g = http_get(src, (size_t)ssz, host, port, path, reqsn, strlen(reqsn)); 326 327 free(host); 328 free(path); 329 free(reqsn); 330 if (g == NULL) 331 return -1; 332 333 /* Stuff response into parse buffer. */ 334 code = g->code; 335 336 free(c->buf.buf); 337 c->buf.sz = g->bodypartsz; 338 c->buf.buf = malloc(c->buf.sz); 339 if (c->buf.buf == NULL) { 340 warn("malloc"); 341 code = -1; 342 } else 343 memcpy(c->buf.buf, g->bodypart, c->buf.sz); 344 http_get_free(g); 345 return code; 346 } 347 348 /* 349 * Send to the CA that we want to authorise a new account. 350 * This only happens once for a new account key. 351 * Returns non-zero on success. 352 */ 353 static int 354 donewreg(struct conn *c, const struct capaths *p) 355 { 356 int rc = 0; 357 char *req; 358 long lc; 359 360 dodbg("%s: new-reg", p->newreg); 361 362 if ((req = json_fmt_newreg(p->agreement)) == NULL) 363 warnx("json_fmt_newreg"); 364 else if ((lc = sreq(c, p->newreg, req)) < 0) 365 warnx("%s: bad comm", p->newreg); 366 else if (lc != 200 && lc != 201) 367 warnx("%s: bad HTTP: %ld", p->newreg, lc); 368 else if (c->buf.buf == NULL || c->buf.sz == 0) 369 warnx("%s: empty response", p->newreg); 370 else 371 rc = 1; 372 373 if (rc == 0 || verbose > 1) 374 buf_dump(&c->buf); 375 free(req); 376 return rc; 377 } 378 379 /* 380 * Request a challenge for the given domain name. 381 * This must be called for each name "alt". 382 * On non-zero exit, fills in "chng" with the challenge. 383 */ 384 static int 385 dochngreq(struct conn *c, const char *alt, struct chng *chng, 386 const struct capaths *p) 387 { 388 int rc = 0; 389 char *req; 390 long lc; 391 struct jsmnn *j = NULL; 392 393 dodbg("%s: req-auth: %s", p->newauthz, alt); 394 395 if ((req = json_fmt_newauthz(alt)) == NULL) 396 warnx("json_fmt_newauthz"); 397 else if ((lc = sreq(c, p->newauthz, req)) < 0) 398 warnx("%s: bad comm", p->newauthz); 399 else if (lc != 200 && lc != 201) 400 warnx("%s: bad HTTP: %ld", p->newauthz, lc); 401 else if ((j = json_parse(c->buf.buf, c->buf.sz)) == NULL) 402 warnx("%s: bad JSON object", p->newauthz); 403 else if (!json_parse_challenge(j, chng)) 404 warnx("%s: bad challenge", p->newauthz); 405 else 406 rc = 1; 407 408 if (rc == 0 || verbose > 1) 409 buf_dump(&c->buf); 410 json_free(j); 411 free(req); 412 return rc; 413 } 414 415 /* 416 * Tell the CA that a challenge response is in place. 417 */ 418 static int 419 dochngresp(struct conn *c, const struct chng *chng, const char *th) 420 { 421 int rc = 0; 422 long lc; 423 char *req; 424 425 dodbg("%s: challenge", chng->uri); 426 427 if ((req = json_fmt_challenge(chng->token, th)) == NULL) 428 warnx("json_fmt_challenge"); 429 else if ((lc = sreq(c, chng->uri, req)) < 0) 430 warnx("%s: bad comm", chng->uri); 431 else if (lc != 200 && lc != 201 && lc != 202) 432 warnx("%s: bad HTTP: %ld", chng->uri, lc); 433 else 434 rc = 1; 435 436 if (rc == 0 || verbose > 1) 437 buf_dump(&c->buf); 438 free(req); 439 return rc; 440 } 441 442 /* 443 * Check with the CA whether a challenge has been processed. 444 * Note: we'll only do this a limited number of times, and pause for a 445 * time between checks, but this happens in the caller. 446 */ 447 static int 448 dochngcheck(struct conn *c, struct chng *chng) 449 { 450 enum chngstatus cc; 451 long lc; 452 struct jsmnn *j; 453 454 dodbg("%s: status", chng->uri); 455 456 if ((lc = nreq(c, chng->uri)) < 0) { 457 warnx("%s: bad comm", chng->uri); 458 return 0; 459 } else if (lc != 200 && lc != 201 && lc != 202) { 460 warnx("%s: bad HTTP: %ld", chng->uri, lc); 461 buf_dump(&c->buf); 462 return 0; 463 } else if ((j = json_parse(c->buf.buf, c->buf.sz)) == NULL) { 464 warnx("%s: bad JSON object", chng->uri); 465 buf_dump(&c->buf); 466 return 0; 467 } else if ((cc = json_parse_response(j)) == -1) { 468 warnx("%s: bad response", chng->uri); 469 buf_dump(&c->buf); 470 json_free(j); 471 return 0; 472 } else if (cc > 0) 473 chng->status = CHNG_VALID; 474 475 json_free(j); 476 return 1; 477 } 478 479 static int 480 dorevoke(struct conn *c, const char *addr, const char *cert) 481 { 482 char *req; 483 int rc = 0; 484 long lc = 0; 485 486 dodbg("%s: revocation", addr); 487 488 if ((req = json_fmt_revokecert(cert)) == NULL) 489 warnx("json_fmt_revokecert"); 490 else if ((lc = sreq(c, addr, req)) < 0) 491 warnx("%s: bad comm", addr); 492 else if (lc != 200 && lc != 201 && lc != 409) 493 warnx("%s: bad HTTP: %ld", addr, lc); 494 else 495 rc = 1; 496 497 if (lc == 409) 498 warnx("%s: already revoked", addr); 499 500 if (rc == 0 || verbose > 1) 501 buf_dump(&c->buf); 502 free(req); 503 return rc; 504 } 505 506 /* 507 * Submit our certificate to the CA. 508 * This, upon success, will return the signed CA. 509 */ 510 static int 511 docert(struct conn *c, const char *addr, const char *cert) 512 { 513 char *req; 514 int rc = 0; 515 long lc; 516 517 dodbg("%s: certificate", addr); 518 519 if ((req = json_fmt_newcert(cert)) == NULL) 520 warnx("json_fmt_newcert"); 521 else if ((lc = sreq(c, addr, req)) < 0) 522 warnx("%s: bad comm", addr); 523 else if (lc != 200 && lc != 201) 524 warnx("%s: bad HTTP: %ld", addr, lc); 525 else if (c->buf.sz == 0 || c->buf.buf == NULL) 526 warnx("%s: empty response", addr); 527 else 528 rc = 1; 529 530 if (rc == 0 || verbose > 1) 531 buf_dump(&c->buf); 532 free(req); 533 return rc; 534 } 535 536 /* 537 * Look up directories from the certificate authority. 538 */ 539 static int 540 dodirs(struct conn *c, const char *addr, struct capaths *paths) 541 { 542 struct jsmnn *j = NULL; 543 long lc; 544 int rc = 0; 545 546 dodbg("%s: directories", addr); 547 548 if ((lc = nreq(c, addr)) < 0) 549 warnx("%s: bad comm", addr); 550 else if (lc != 200 && lc != 201) 551 warnx("%s: bad HTTP: %ld", addr, lc); 552 else if ((j = json_parse(c->buf.buf, c->buf.sz)) == NULL) 553 warnx("json_parse"); 554 else if (!json_parse_capaths(j, paths)) 555 warnx("%s: bad CA paths", addr); 556 else 557 rc = 1; 558 559 if (rc == 0 || verbose > 1) 560 buf_dump(&c->buf); 561 json_free(j); 562 return rc; 563 } 564 565 /* 566 * Request the full chain certificate. 567 */ 568 static int 569 dofullchain(struct conn *c, const char *addr) 570 { 571 int rc = 0; 572 long lc; 573 574 dodbg("%s: full chain", addr); 575 576 if ((lc = nreq(c, addr)) < 0) 577 warnx("%s: bad comm", addr); 578 else if (lc != 200 && lc != 201) 579 warnx("%s: bad HTTP: %ld", addr, lc); 580 else 581 rc = 1; 582 583 if (rc == 0 || verbose > 1) 584 buf_dump(&c->buf); 585 return rc; 586 } 587 588 /* 589 * Communicate with the ACME server. 590 * We need the certificate we want to upload and our account key information. 591 */ 592 int 593 netproc(int kfd, int afd, int Cfd, int cfd, int dfd, int rfd, 594 int newacct, int revocate, struct authority_c *authority, 595 const char *const *alts, size_t altsz) 596 { 597 int rc = 0; 598 size_t i, done = 0; 599 char *cert = NULL, *thumb = NULL, *url = NULL; 600 struct conn c; 601 struct capaths paths; 602 struct chng *chngs = NULL; 603 long lval; 604 605 memset(&paths, 0, sizeof(struct capaths)); 606 memset(&c, 0, sizeof(struct conn)); 607 608 if (unveil(tls_default_ca_cert_file(), "r") == -1) { 609 warn("unveil"); 610 goto out; 611 } 612 613 if (pledge("stdio inet rpath", NULL) == -1) { 614 warn("pledge"); 615 goto out; 616 } 617 618 if (http_init() == -1) { 619 warn("http_init"); 620 goto out; 621 } 622 623 if (pledge("stdio inet", NULL) == -1) { 624 warn("pledge"); 625 goto out; 626 } 627 628 /* 629 * Wait until the acctproc, keyproc, and revokeproc have started up and 630 * are ready to serve us data. 631 * Then check whether revokeproc indicates that the certificate on file 632 * (if any) can be updated. 633 */ 634 if ((lval = readop(afd, COMM_ACCT_STAT)) == 0) { 635 rc = 1; 636 goto out; 637 } else if (lval != ACCT_READY) { 638 warnx("unknown operation from acctproc"); 639 goto out; 640 } 641 642 if ((lval = readop(kfd, COMM_KEY_STAT)) == 0) { 643 rc = 1; 644 goto out; 645 } else if (lval != KEY_READY) { 646 warnx("unknown operation from keyproc"); 647 goto out; 648 } 649 650 if ((lval = readop(rfd, COMM_REVOKE_RESP)) == 0) { 651 rc = 1; 652 goto out; 653 } else if (lval != REVOKE_EXP && lval != REVOKE_OK) { 654 warnx("unknown operation from revokeproc"); 655 goto out; 656 } 657 658 /* If our certificate is up-to-date, return now. */ 659 if (lval == REVOKE_OK) { 660 rc = 1; 661 goto out; 662 } 663 664 /* Allocate main state. */ 665 chngs = calloc(altsz, sizeof(struct chng)); 666 if (chngs == NULL) { 667 warn("calloc"); 668 goto out; 669 } 670 671 c.dfd = dfd; 672 c.fd = afd; 673 c.na = authority->api; 674 675 /* 676 * Look up the API urls of the ACME server. 677 */ 678 if (!dodirs(&c, c.na, &paths)) 679 goto out; 680 681 /* 682 * If we're meant to revoke, then wait for revokeproc to send us 683 * the certificate (if it's found at all). 684 * Following that, submit the request to the CA then notify the 685 * certproc, which will in turn notify the fileproc. 686 */ 687 if (revocate) { 688 if ((cert = readstr(rfd, COMM_CSR)) == NULL) 689 goto out; 690 if (!dorevoke(&c, paths.revokecert, cert)) 691 goto out; 692 else if (writeop(cfd, COMM_CSR_OP, CERT_REVOKE) > 0) 693 rc = 1; 694 goto out; 695 } 696 697 /* If new, register with the CA server. */ 698 if (newacct && ! donewreg(&c, &paths)) 699 goto out; 700 701 /* Pre-authorise all domains with CA server. */ 702 for (i = 0; i < altsz; i++) 703 if (!dochngreq(&c, alts[i], &chngs[i], &paths)) 704 goto out; 705 706 /* 707 * We now have our challenges. 708 * We need to ask the acctproc for the thumbprint. 709 * We'll combine this to the challenge to create our response, 710 * which will be orchestrated by the chngproc. 711 */ 712 if (writeop(afd, COMM_ACCT, ACCT_THUMBPRINT) <= 0) 713 goto out; 714 else if ((thumb = readstr(afd, COMM_THUMB)) == NULL) 715 goto out; 716 717 /* We'll now ask chngproc to build the challenge. */ 718 for (i = 0; i < altsz; i++) { 719 if (writeop(Cfd, COMM_CHNG_OP, CHNG_SYN) <= 0) 720 goto out; 721 else if (writestr(Cfd, COMM_THUMB, thumb) <= 0) 722 goto out; 723 else if (writestr(Cfd, COMM_TOK, chngs[i].token) <= 0) 724 goto out; 725 726 /* Read that the challenge has been made. */ 727 if (readop(Cfd, COMM_CHNG_ACK) != CHNG_ACK) 728 goto out; 729 730 /* Write to the CA that it's ready. */ 731 if (!dochngresp(&c, &chngs[i], thumb)) 732 goto out; 733 } 734 735 /* 736 * We now wait on the ACME server for each domain. 737 * Connect to the server (assume it's the same server) once 738 * every five seconds. 739 */ 740 for (;;) { 741 for (i = 0; i < altsz; i++) { 742 doddbg("%s: done %lu, altsz %lu, i %lu, status %d", 743 __func__, done, altsz, i, chngs[i].status); 744 745 if (chngs[i].status == CHNG_VALID) 746 continue; 747 748 if (chngs[i].retry++ >= RETRY_MAX) { 749 warnx("%s: too many tries", chngs[i].uri); 750 goto out; 751 } 752 753 sleep(RETRY_DELAY); 754 if (dochngcheck(&c, &chngs[i])) { 755 if (chngs[i].status == CHNG_VALID) 756 done++; 757 continue; 758 } else 759 goto out; 760 } 761 762 if (done == altsz) 763 break; 764 } 765 766 /* 767 * Write our acknowledgement that the challenges are over. 768 * The challenge process will remove all of the files. 769 */ 770 if (writeop(Cfd, COMM_CHNG_OP, CHNG_STOP) <= 0) 771 goto out; 772 773 /* Wait to receive the certificate itself. */ 774 if ((cert = readstr(kfd, COMM_CERT)) == NULL) 775 goto out; 776 777 /* 778 * Otherwise, submit the CA for signing, download the signed 779 * copy, and ship that into the certificate process for copying. 780 */ 781 if (!docert(&c, paths.newcert, cert)) 782 goto out; 783 else if (writeop(cfd, COMM_CSR_OP, CERT_UPDATE) <= 0) 784 goto out; 785 else if (writebuf(cfd, COMM_CSR, c.buf.buf, c.buf.sz) <= 0) 786 goto out; 787 788 /* 789 * Read back the issuer from the certproc. 790 * Then contact the issuer to get the certificate chain. 791 * Write this chain directly back to the certproc. 792 */ 793 if ((url = readstr(cfd, COMM_ISSUER)) == NULL) 794 goto out; 795 else if (!dofullchain(&c, url)) 796 goto out; 797 else if (writebuf(cfd, COMM_CHAIN, c.buf.buf, c.buf.sz) <= 0) 798 goto out; 799 800 rc = 1; 801 out: 802 close(cfd); 803 close(kfd); 804 close(afd); 805 close(Cfd); 806 close(dfd); 807 close(rfd); 808 free(cert); 809 free(url); 810 free(thumb); 811 free(c.buf.buf); 812 if (chngs != NULL) 813 for (i = 0; i < altsz; i++) 814 json_free_challenge(&chngs[i]); 815 free(chngs); 816 json_free_capaths(&paths); 817 return rc; 818 } 819