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