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