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