1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright 2012 Milan Jurik. All rights reserved. 24 */ 25 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <strings.h> 29 #include <string.h> 30 #include <libgen.h> 31 #include <unistd.h> 32 #include <fcntl.h> 33 #include <errno.h> 34 #include <netdb.h> 35 #include <libnvpair.h> 36 #include <sys/types.h> 37 #include <sys/wait.h> 38 #include <sys/stat.h> 39 #include <sys/param.h> 40 #include <sys/sysmacros.h> 41 #include <sys/mman.h> 42 #include <sys/socket.h> 43 #include <sys/utsname.h> 44 #include <sys/wanboot_impl.h> 45 #include <netinet/in.h> 46 #include <arpa/inet.h> 47 48 #include <openssl/crypto.h> 49 #include <openssl/x509.h> 50 #include <openssl/x509v3.h> 51 #include <openssl/pem.h> 52 #include <openssl/pkcs12.h> 53 #include <openssl/evp.h> 54 #include <openssl/err.h> 55 56 #include <p12aux.h> 57 58 #include <parseURL.h> 59 /* 60 * These can be replaced with wanbootutil.h once the openssl interfaces 61 * are moved to libwanboot. 62 */ 63 #include <wanboot/key_util.h> 64 #include <wanboot/key_xdr.h> 65 #include <hmac_sha1.h> 66 67 #include <netboot_paths.h> 68 #include <wanboot_conf.h> 69 70 /* 71 * Exit status: 72 */ 73 #define WBCGI_STATUS_OK 0 74 #define WBCGI_STATUS_ERR 1 75 76 #define WBCGI_FILE_EXISTS(file, statbuf) \ 77 (stat(file, &statbuf) == 0 && S_ISREG(statbuf.st_mode)) 78 79 #define WBCGI_DIR_EXISTS(dir, statbuf) \ 80 (stat(dir, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) 81 82 #define WBCGI_HMAC_PATH "/usr/lib/inet/wanboot/hmac" 83 #define WBCGI_ENCR_PATH "/usr/lib/inet/wanboot/encr" 84 #define WBCGI_KEYMGMT_PATH "/usr/lib/inet/wanboot/keymgmt" 85 #define WBCGI_MKISOFS_PATH "/bin/mkisofs" 86 87 #define WBCGI_DEV_URANDOM "/dev/urandom" 88 89 #define WBCGI_CONTENT_TYPE "Content-Type: " 90 #define WBCGI_CONTENT_LENGTH "Content-Length: " 91 #define WBCGI_WANBOOT_BNDTXT "WANBoot_Part_Boundary" 92 #define WBCGI_CRNL "\r\n" 93 94 #define WBCGI_CNSTR "CN=" 95 #define WBCGI_CNSTR_LEN (sizeof (WBCGI_CNSTR) - 1) 96 #define WBCGI_NAMESEP ",/\n\r" 97 98 #define WBCGI_MAXBUF 256 99 100 /* 101 * Possible return values from netboot_ftw(): 102 */ 103 #define WBCGI_FTW_CBOK 2 /* CB terminated walk OK */ 104 #define WBCGI_FTW_CBCONT 1 /* CB wants walk should continue */ 105 #define WBCGI_FTW_DONE 0 /* Walk terminated without CBERR/CBOK */ 106 #define WBCGI_FTW_CBERR -1 /* CB terminated walk with err */ 107 108 /* 109 * getsubopt() is used to map one of the contents[] keywords 110 * to one of these types 111 */ 112 #define WBCGI_CONTENT_ERROR -1 113 #define WBCGI_CONTENT_BOOTFILE 0 114 #define WBCGI_CONTENT_BOOTFS 1 115 #define WBCGI_CONTENT_ROOTFS 2 116 117 static char *contents[] = 118 { "bootfile", "bootfs", "rootfs", NULL }; 119 120 /* 121 * getsubopt() is used to parse the query string for 122 * the keywords defined by queryopts[] 123 */ 124 #define WBCGI_QUERYOPT_CONTENT 0 125 #define WBCGI_QUERYOPT_NET 1 126 #define WBCGI_QUERYOPT_CID 2 127 #define WBCGI_QUERYOPT_NONCE 3 128 129 static char *queryopts[] = 130 { "CONTENT", "IP", "CID", "NONCE", NULL }; 131 132 static bc_handle_t bc_handle; 133 134 135 static char * 136 status_msg(int status) 137 { 138 char *msg; 139 140 switch (status) { 141 case 400: 142 msg = "Bad Request"; 143 break; 144 case 403: 145 msg = "Forbidden"; 146 break; 147 case 500: 148 msg = "Internal Server Error"; 149 break; 150 default: 151 msg = "Unknown status"; 152 break; 153 } 154 155 return (msg); 156 } 157 158 static void 159 print_status(int status, const char *spec_msg) 160 { 161 if (spec_msg == NULL) { 162 spec_msg = ""; 163 } 164 165 (void) fprintf(stdout, "Status: %d %s %s%s", status, 166 status_msg(status), spec_msg, WBCGI_CRNL); 167 } 168 169 static char * 170 make_path(const char *root, const char *suffix) 171 { 172 char path[MAXPATHLEN]; 173 char *ptr = NULL; 174 int chars; 175 176 if ((chars = snprintf(path, sizeof (path), 177 "%s/%s", root, suffix)) < 0 || chars > sizeof (path) || 178 (ptr = strdup(path)) == NULL) { 179 print_status(500, "(error making path)"); 180 } 181 182 return (ptr); 183 } 184 185 static void 186 free_path(char **pathp) 187 { 188 if (*pathp != NULL) { 189 free(*pathp); 190 *pathp = NULL; 191 } 192 } 193 194 static char * 195 gen_tmppath(const char *prefix, const char *net, const char *cid) 196 { 197 pid_t pid; 198 time_t secs; 199 int chars; 200 char path[MAXPATHLEN]; 201 char *ptr = NULL; 202 203 if ((pid = getpid()) < 0 || (secs = time(NULL)) < 0 || 204 (chars = snprintf(path, sizeof (path), "/tmp/%s_%s_%s_%ld_%ld", 205 prefix, net, cid, pid, secs)) < 0 || chars > sizeof (path) || 206 (ptr = strdup(path)) == NULL) { 207 print_status(500, "(error creating temporary filename)"); 208 } 209 210 return (ptr); 211 } 212 213 /* 214 * File I/O stuff: 215 */ 216 static boolean_t 217 write_buffer(int fd, const void *buffer, size_t buflen) 218 { 219 size_t nwritten; 220 ssize_t nbytes; 221 const char *buf = buffer; 222 223 for (nwritten = 0; nwritten < buflen; nwritten += nbytes) { 224 nbytes = write(fd, &buf[nwritten], buflen - nwritten); 225 if (nbytes <= 0) { 226 return (B_FALSE); 227 } 228 } 229 230 return (B_TRUE); 231 } 232 233 static boolean_t 234 write_file(int ofd, const char *filename, size_t size) 235 { 236 boolean_t ret = B_TRUE; 237 int ifd; 238 char buf[1024]; 239 size_t rlen; 240 ssize_t wlen; 241 242 if ((ifd = open(filename, O_RDONLY)) < 0) { 243 return (B_FALSE); 244 } 245 246 for (; size != 0; size -= wlen) { 247 rlen = (size < sizeof (buf)) ? size : sizeof (buf); 248 249 if ((wlen = read(ifd, buf, rlen)) < 0 || 250 !write_buffer(ofd, buf, wlen)) { 251 ret = B_FALSE; 252 break; 253 } 254 } 255 (void) close(ifd); 256 257 return (ret); 258 } 259 260 static boolean_t 261 copy_file(const char *src, const char *dest) 262 { 263 boolean_t ret = B_FALSE; 264 char message[WBCGI_MAXBUF]; 265 const size_t chunksize = 16 * PAGESIZE; 266 size_t validsize; 267 size_t nwritten = 0; 268 size_t nbytes = 0; 269 off_t roff; 270 int mflags = MAP_PRIVATE; 271 char *buf = NULL; 272 struct stat st; 273 int rfd = -1; 274 int wfd = -1; 275 int chars; 276 277 if ((rfd = open(src, O_RDONLY)) < 0 || 278 (wfd = open(dest, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR)) < 0 || 279 fstat(rfd, &st) == -1) { 280 goto cleanup; 281 } 282 283 for (nbytes = st.st_size, roff = 0; nwritten < nbytes; 284 nwritten += validsize, roff += validsize) { 285 buf = mmap(buf, chunksize, PROT_READ, mflags, rfd, roff); 286 if (buf == MAP_FAILED) { 287 goto cleanup; 288 } 289 mflags |= MAP_FIXED; 290 291 validsize = MIN(chunksize, nbytes - nwritten); 292 if (!write_buffer(wfd, buf, validsize)) { 293 (void) munmap(buf, chunksize); 294 goto cleanup; 295 } 296 297 } 298 if (buf != NULL) { 299 (void) munmap(buf, chunksize); 300 } 301 302 ret = B_TRUE; 303 cleanup: 304 if (ret == B_FALSE) { 305 if ((chars = snprintf(message, sizeof (message), 306 "error copying %s to %s", src, dest)) > 0 && 307 chars <= sizeof (message)) { 308 print_status(500, message); 309 } else { 310 print_status(500, NULL); 311 } 312 } 313 if (rfd != -1) { 314 (void) close(rfd); 315 } 316 if (wfd != -1) { 317 (void) close(wfd); 318 } 319 320 return (ret); 321 } 322 323 static boolean_t 324 create_nonce(const char *noncepath, const char *nonce) 325 { 326 boolean_t ret = B_TRUE; 327 int fd; 328 329 if ((fd = open(noncepath, 330 O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR)) == -1 || 331 !write_buffer(fd, nonce, strlen(nonce))) { 332 print_status(500, "(error creating nonce file)"); 333 ret = B_FALSE; 334 } 335 if (fd != -1) { 336 (void) close(fd); 337 } 338 339 return (ret); 340 } 341 342 static boolean_t 343 create_timestamp(const char *timestamppath, const char *timestamp) 344 { 345 boolean_t ret = B_TRUE; 346 int fd; 347 348 if ((fd = open(timestamppath, 349 O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR)) == -1 || 350 !write_buffer(fd, timestamp, strlen(timestamp))) { 351 print_status(500, "(error creating timestamp file)"); 352 ret = B_FALSE; 353 } 354 if (fd != -1) { 355 (void) close(fd); 356 } 357 358 return (ret); 359 } 360 361 static boolean_t 362 create_urandom(const char *urandompath) 363 { 364 boolean_t ret = B_TRUE; 365 int fd; 366 367 if ((fd = open(urandompath, 368 O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR)) == -1 || 369 !write_file(fd, WBCGI_DEV_URANDOM, 32 * 1024)) { 370 print_status(500, "(error creating urandom file)"); 371 ret = B_FALSE; 372 } 373 if (fd != -1) { 374 (void) close(fd); 375 } 376 377 return (ret); 378 } 379 380 static boolean_t 381 create_null_hash(const char *hashpath) 382 { 383 boolean_t ret = B_TRUE; 384 int fd; 385 static char null_hash[HMAC_DIGEST_LEN]; 386 387 if ((fd = open(hashpath, 388 O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR)) == -1 || 389 !write_buffer(fd, null_hash, sizeof (null_hash))) { 390 print_status(500, "(error creating null hash)"); 391 ret = B_FALSE; 392 } 393 if (fd != -1) { 394 (void) close(fd); 395 } 396 397 return (ret); 398 } 399 400 401 static char * 402 determine_doc_root(void) 403 { 404 char *doc_root; 405 406 /* 407 * If DOCUMENT_ROOT is valid, use that. 408 */ 409 if ((doc_root = getenv("DOCUMENT_ROOT")) == NULL || 410 strlen(doc_root) == 0) { 411 /* 412 * No DOCUMENT_ROOT - try PATH_TRANSLATED. 413 */ 414 if ((doc_root = getenv("PATH_TRANSLATED")) == NULL || 415 strlen(doc_root) == 0) { 416 /* 417 * Can't determine the document root. 418 */ 419 return (NULL); 420 } 421 } 422 423 return (doc_root); 424 } 425 426 static boolean_t 427 get_request_info(int *contentp, char **netp, char **cidp, char **noncep, 428 char **docrootp) 429 { 430 char *method; 431 char *query_string; 432 char *value; 433 char *junk; 434 int i; 435 436 if ((method = getenv("REQUEST_METHOD")) == NULL || 437 strncasecmp(method, "GET", strlen("GET") != 0)) { 438 print_status(403, "(GET method expected)"); 439 return (B_FALSE); 440 } 441 442 if ((query_string = getenv("QUERY_STRING")) == NULL) { 443 print_status(400, "(empty query string)"); 444 return (B_FALSE); 445 } 446 447 for (i = 0; i < strlen(query_string); i++) { 448 if (query_string[i] == '&') { 449 query_string[i] = ','; 450 } 451 } 452 453 *contentp = WBCGI_CONTENT_ERROR; 454 *netp = *cidp = *noncep = NULL; 455 456 if ((*docrootp = determine_doc_root()) == NULL) { 457 print_status(400, "(unable to determine document root)"); 458 return (B_FALSE); 459 } 460 461 while (*query_string != '\0') { 462 switch (getsubopt(&query_string, queryopts, &value)) { 463 case WBCGI_QUERYOPT_CONTENT: 464 *contentp = getsubopt(&value, contents, &junk); 465 break; 466 case WBCGI_QUERYOPT_NET: 467 *netp = value; 468 break; 469 case WBCGI_QUERYOPT_CID: 470 *cidp = value; 471 break; 472 case WBCGI_QUERYOPT_NONCE: 473 *noncep = value; 474 break; 475 default: 476 print_status(400, "(illegal query string)"); 477 return (B_FALSE); 478 } 479 } 480 481 switch (*contentp) { 482 default: 483 print_status(400, "(missing or illegal CONTENT)"); 484 return (B_FALSE); 485 486 case WBCGI_CONTENT_BOOTFS: 487 if (*netp == NULL || *cidp == NULL || *noncep == NULL) { 488 print_status(400, 489 "(CONTENT, IP, CID and NONCE required)"); 490 return (B_FALSE); 491 } 492 break; 493 494 case WBCGI_CONTENT_BOOTFILE: 495 case WBCGI_CONTENT_ROOTFS: 496 if (*netp == NULL || *cidp == NULL || *docrootp == NULL) { 497 print_status(400, 498 "(CONTENT, IP, CID and DOCUMENT_ROOT required)"); 499 return (B_FALSE); 500 } 501 break; 502 } 503 504 return (B_TRUE); 505 } 506 507 static boolean_t 508 encrypt_payload(const char *payload, const char *encr_payload, 509 const char *keyfile, const char *encryption_type) 510 { 511 struct stat sbuf; 512 int chars; 513 char cmd[MAXPATHLEN]; 514 FILE *fp; 515 int status; 516 char msg[WBCGI_MAXBUF]; 517 518 if (!WBCGI_FILE_EXISTS(payload, sbuf)) { 519 print_status(500, "(encrypt_payload: missing payload)"); 520 return (B_FALSE); 521 } 522 523 if ((chars = snprintf(cmd, sizeof (cmd), 524 "%s -o type=%s -k %s < %s > %s", WBCGI_ENCR_PATH, 525 encryption_type, keyfile, payload, encr_payload)) < 0 || 526 chars > sizeof (cmd)) { 527 print_status(500, "(encrypt_payload: buffer overflow)"); 528 return (B_FALSE); 529 } 530 531 if ((fp = popen(cmd, "w")) == NULL) { 532 print_status(500, "(encrypt_payload: missing/file error)"); 533 return (B_FALSE); 534 } 535 if ((status = WEXITSTATUS(pclose(fp))) != 0) { 536 (void) snprintf(msg, sizeof (msg), 537 "(encrypt_payload: failed, status=%d)", status); 538 print_status(500, msg); 539 return (B_FALSE); 540 } 541 542 if (!WBCGI_FILE_EXISTS(encr_payload, sbuf)) { 543 print_status(500, "(encrypt_payload: bad encrypted file)"); 544 return (B_FALSE); 545 } 546 547 return (B_TRUE); 548 } 549 550 static boolean_t 551 hash_payload(const char *payload, const char *payload_hash, 552 const char *keyfile) 553 { 554 struct stat sbuf; 555 int chars; 556 char cmd[MAXPATHLEN]; 557 FILE *fp; 558 int status; 559 char msg[WBCGI_MAXBUF]; 560 561 if (!WBCGI_FILE_EXISTS(payload, sbuf)) { 562 print_status(500, "(hash_payload: missing payload)"); 563 return (B_FALSE); 564 } 565 566 if ((chars = snprintf(cmd, sizeof (cmd), "%s -i %s -k %s > %s", 567 WBCGI_HMAC_PATH, payload, keyfile, payload_hash)) < 0 || 568 chars > sizeof (cmd)) { 569 print_status(500, "(hash_payload: buffer overflow)"); 570 return (B_FALSE); 571 } 572 573 if ((fp = popen(cmd, "w")) == NULL) { 574 print_status(500, "(hash_payload: missing/file error)"); 575 return (B_FALSE); 576 } 577 if ((status = WEXITSTATUS(pclose(fp))) != 0) { 578 (void) snprintf(msg, sizeof (msg), 579 "(hash_payload: failed, status=%d)", status); 580 print_status(500, msg); 581 return (B_FALSE); 582 } 583 584 if (!WBCGI_FILE_EXISTS(payload_hash, sbuf) || 585 sbuf.st_size < HMAC_DIGEST_LEN) { 586 print_status(500, "(hash_payload: bad signature file)"); 587 return (B_FALSE); 588 } 589 590 return (B_TRUE); 591 } 592 593 static boolean_t 594 extract_keystore(const char *path, const char *keystorepath) 595 { 596 struct stat sbuf; 597 int chars; 598 char cmd[MAXPATHLEN]; 599 FILE *fp; 600 int status; 601 char msg[WBCGI_MAXBUF]; 602 603 if (!WBCGI_FILE_EXISTS(path, sbuf)) { 604 print_status(500, "(extract_keystore: missing keystore)"); 605 return (B_FALSE); 606 } 607 608 if ((chars = snprintf(cmd, sizeof (cmd), 609 "%s -x -f %s -s %s -o type=rsa", 610 WBCGI_KEYMGMT_PATH, keystorepath, path)) < 0 || 611 chars > sizeof (cmd)) { 612 print_status(500, "(extract_keystore: buffer overflow)"); 613 return (B_FALSE); 614 } 615 616 if ((fp = popen(cmd, "w")) == NULL) { 617 print_status(500, "(extract_keystore: missing/file error)"); 618 return (B_FALSE); 619 } 620 if ((status = WEXITSTATUS(pclose(fp))) != 0) { 621 (void) snprintf(msg, sizeof (msg), 622 "(extract_keystore: failed, status=%d)", status); 623 print_status(500, msg); 624 return (B_FALSE); 625 } 626 627 if (!WBCGI_FILE_EXISTS(keystorepath, sbuf)) { 628 print_status(500, "(extract_keystore: failed to create)"); 629 return (B_FALSE); 630 } 631 632 return (B_TRUE); 633 } 634 635 static boolean_t 636 mkisofs(const char *image_dir, const char *image) 637 { 638 struct stat sbuf; 639 int chars; 640 char cmd[MAXPATHLEN]; 641 FILE *fp; 642 int status; 643 char msg[WBCGI_MAXBUF]; 644 645 if (!WBCGI_DIR_EXISTS(image_dir, sbuf)) { 646 print_status(500, "(mksiofs: missing image_dir)"); 647 return (B_FALSE); 648 } 649 650 if ((chars = snprintf(cmd, sizeof (cmd), "%s -quiet -o %s -r %s", 651 WBCGI_MKISOFS_PATH, image, image_dir)) < 0 || 652 chars > sizeof (cmd)) { 653 print_status(500, "(mkisofs: buffer overflow)"); 654 return (B_FALSE); 655 } 656 657 if ((fp = popen(cmd, "w")) == NULL) { 658 print_status(500, "(mkisofs: missing/file error)"); 659 return (B_FALSE); 660 } 661 if ((status = WEXITSTATUS(pclose(fp))) != 0) { 662 (void) snprintf(msg, sizeof (msg), 663 "(mkisofs: failed, status=%d)", status); 664 print_status(500, msg); 665 return (B_FALSE); 666 } 667 668 if (!WBCGI_FILE_EXISTS(image, sbuf)) { 669 print_status(500, "(mksiofs: failed to create image)"); 670 return (B_FALSE); 671 } 672 673 return (B_TRUE); 674 } 675 676 /* 677 * This function, when invoked with a file name, optional network and 678 * client ID strings, and callback function will search for the file 679 * in the following locations: 680 * 681 * NB_NETBOOT_ROOT/<network>/<client id>/<file> 682 * NB_NETBOOT_ROOT/<client id>/<file> 683 * NB_NETBOOT_ROOT/<network>/<file> 684 * NB_NETBOOT_ROOT/<file> 685 * 686 * The callback function is invoked each time the file is found until 687 * we have searched all of the above locations or the callback function 688 * returns a value other than WBCGI_FTW_CBCONT. 689 * 690 * Arguments: 691 * filename - Name of file to search for. 692 * net - Optional network number to include in search hierarchy. 693 * cid - Optional client ID to include in search hierarchy. 694 * cb - Callback function to be called when file is found. 695 * arg - Argument to be supplied to the callback funtion. 696 * 697 * Returns: 698 * WBCGI_FTW_DONE, WBCGI_FTW_CBOK or WBCGI_FTW_CBERR. 699 */ 700 static int 701 netboot_ftw(const char *filename, const char *net, const char *cid, 702 int (*cb)(const char *, void *arg), void *arg) 703 { 704 char ckpath[4][MAXPATHLEN]; 705 int ret; 706 struct stat buf; 707 int i = 0; 708 709 if (snprintf(ckpath[i++], MAXPATHLEN, "%s%s", NB_NETBOOT_ROOT, filename) 710 >= MAXPATHLEN) 711 return (WBCGI_FTW_CBERR); 712 713 if (net != NULL && snprintf(ckpath[i++], MAXPATHLEN, "%s%s/%s", 714 NB_NETBOOT_ROOT, net, filename) >= MAXPATHLEN) 715 return (WBCGI_FTW_CBERR); 716 717 if (cid != NULL) { 718 if (snprintf(ckpath[i++], MAXPATHLEN, "%s%s/%s", 719 NB_NETBOOT_ROOT, cid, filename) >= MAXPATHLEN) 720 return (WBCGI_FTW_CBERR); 721 722 if (net != NULL && snprintf(ckpath[i++], MAXPATHLEN, 723 "%s%s/%s/%s", NB_NETBOOT_ROOT, net, cid, filename) >= 724 MAXPATHLEN) 725 return (WBCGI_FTW_CBERR); 726 } 727 728 /* 729 * Loop through hierarchy and check for file existence. 730 */ 731 while (i > 0) { 732 --i; 733 if (WBCGI_FILE_EXISTS(ckpath[i], buf)) { 734 if ((ret = cb(ckpath[i], arg)) != WBCGI_FTW_CBCONT) 735 return (ret); 736 } 737 } 738 return (WBCGI_FTW_DONE); 739 } 740 741 /*ARGSUSED*/ 742 static int 743 noact_cb(const char *path, void *arg) 744 { 745 return (WBCGI_FTW_CBOK); 746 } 747 748 static int 749 set_pathname(const char *path, void *pathname) 750 { 751 *(char **)pathname = strdup((char *)path); 752 return (WBCGI_FTW_CBOK); 753 } 754 755 static int 756 create_keystore(const char *path, void *keystorepath) 757 { 758 if (!extract_keystore(path, (char *)keystorepath)) { 759 return (WBCGI_FTW_CBERR); 760 } 761 return (WBCGI_FTW_CBOK); 762 } 763 764 static int 765 copy_certstore(const char *path, void *certstorepath) 766 { 767 if (!copy_file(path, (char *)certstorepath)) { 768 return (WBCGI_FTW_CBERR); 769 } 770 return (WBCGI_FTW_CBOK); 771 } 772 773 /* 774 * Add the certs found in the trustfile found in path (a trust store) to 775 * the file found at bootfs_dir/truststore. If necessary, create the 776 * output file. 777 */ 778 static int 779 build_trustfile(const char *path, void *truststorepath) 780 { 781 int ret = WBCGI_FTW_CBERR; 782 STACK_OF(X509) *i_anchors = NULL; 783 STACK_OF(X509) *o_anchors = NULL; 784 char message[WBCGI_MAXBUF]; 785 PKCS12 *p12 = NULL; 786 FILE *rfp = NULL; 787 FILE *wfp = NULL; 788 struct stat i_st; 789 struct stat o_st; 790 X509 *x = NULL; 791 int errtype = 0; 792 int wfd = -1; 793 int chars; 794 int i; 795 796 if (!WBCGI_FILE_EXISTS(path, i_st)) { 797 goto cleanup; 798 } 799 800 if (WBCGI_FILE_EXISTS((char *)truststorepath, o_st)) { 801 /* 802 * If we are inadvertantly writing to the input file. 803 * return success. 804 * XXX Pete: how can this happen, and why success? 805 */ 806 if (i_st.st_ino == o_st.st_ino) { 807 ret = WBCGI_FTW_CBCONT; 808 goto cleanup; 809 } 810 if ((wfp = fopen((char *)truststorepath, "r+")) == NULL) { 811 goto cleanup; 812 } 813 /* 814 * Read what's already there, so that new information 815 * can be added. 816 */ 817 if ((p12 = d2i_PKCS12_fp(wfp, NULL)) == NULL) { 818 errtype = 1; 819 goto cleanup; 820 } 821 i = sunw_PKCS12_parse(p12, WANBOOT_PASSPHRASE, DO_NONE, NULL, 822 0, NULL, NULL, NULL, &o_anchors); 823 if (i <= 0) { 824 errtype = 1; 825 goto cleanup; 826 } 827 828 PKCS12_free(p12); 829 p12 = NULL; 830 } else { 831 if (errno != ENOENT) { 832 chars = snprintf(message, sizeof (message), 833 "(error accessing file %s, error %s)", 834 path, strerror(errno)); 835 if (chars > 0 && chars < sizeof (message)) 836 print_status(500, message); 837 else 838 print_status(500, NULL); 839 return (WBCGI_FTW_CBERR); 840 } 841 842 /* 843 * Note: We could copy the file to the new trustfile, but 844 * we can't verify the password that way. Therefore, copy 845 * it by reading it. 846 */ 847 if ((wfd = open((char *)truststorepath, 848 O_CREAT|O_EXCL|O_RDWR, 0700)) < 0) { 849 goto cleanup; 850 } 851 if ((wfp = fdopen(wfd, "w+")) == NULL) { 852 goto cleanup; 853 } 854 o_anchors = sk_X509_new_null(); 855 if (o_anchors == NULL) { 856 goto cleanup; 857 } 858 } 859 860 if ((rfp = fopen(path, "r")) == NULL) { 861 goto cleanup; 862 } 863 if ((p12 = d2i_PKCS12_fp(rfp, NULL)) == NULL) { 864 errtype = 1; 865 goto cleanup; 866 } 867 i = sunw_PKCS12_parse(p12, WANBOOT_PASSPHRASE, DO_NONE, NULL, 0, NULL, 868 NULL, NULL, &i_anchors); 869 if (i <= 0) { 870 errtype = 1; 871 goto cleanup; 872 } 873 PKCS12_free(p12); 874 p12 = NULL; 875 876 /* 877 * Merge the two stacks of pkcs12 certs. 878 */ 879 for (i = 0; i < sk_X509_num(i_anchors); i++) { 880 /* LINTED */ 881 x = sk_X509_delete(i_anchors, i); 882 (void) sk_X509_push(o_anchors, x); 883 } 884 885 /* 886 * Create the pkcs12 structure from the modified input stack and 887 * then write out that structure. 888 */ 889 p12 = sunw_PKCS12_create((const char *)WANBOOT_PASSPHRASE, NULL, NULL, 890 o_anchors); 891 if (p12 == NULL) { 892 goto cleanup; 893 } 894 rewind(wfp); 895 if (i2d_PKCS12_fp(wfp, p12) == 0) { 896 goto cleanup; 897 } 898 899 ret = WBCGI_FTW_CBCONT; 900 cleanup: 901 if (ret == WBCGI_FTW_CBERR) { 902 if (errtype == 1) { 903 chars = snprintf(message, sizeof (message), 904 "(internal PKCS12 error while copying %s to %s)", 905 path, (char *)truststorepath); 906 } else { 907 chars = snprintf(message, sizeof (message), 908 "(error copying %s to %s)", 909 path, (char *)truststorepath); 910 } 911 if (chars > 0 && chars <= sizeof (message)) { 912 print_status(500, message); 913 } else { 914 print_status(500, NULL); 915 } 916 } 917 if (rfp != NULL) { 918 (void) fclose(rfp); 919 } 920 if (wfp != NULL) { 921 /* Will also close wfd */ 922 (void) fclose(wfp); 923 } 924 if (p12 != NULL) { 925 PKCS12_free(p12); 926 } 927 if (i_anchors != NULL) { 928 sk_X509_pop_free(i_anchors, X509_free); 929 } 930 if (o_anchors != NULL) { 931 sk_X509_pop_free(o_anchors, X509_free); 932 } 933 934 return (ret); 935 } 936 937 static boolean_t 938 check_key_type(const char *keyfile, const char *keytype, int flag) 939 { 940 boolean_t ret = B_FALSE; 941 FILE *key_fp = NULL; 942 wbku_key_attr_t ka; 943 944 /* 945 * Map keytype into the ka structure 946 */ 947 if (wbku_str_to_keyattr(keytype, &ka, flag) != WBKU_SUCCESS) { 948 goto cleanup; 949 } 950 951 /* 952 * Open the key file for reading. 953 */ 954 if ((key_fp = fopen(keyfile, "r")) == NULL) { 955 goto cleanup; 956 } 957 958 /* 959 * Find the valid client key, if it exists. 960 */ 961 if (wbku_find_key(key_fp, NULL, &ka, NULL, B_FALSE) != WBKU_SUCCESS) { 962 goto cleanup; 963 } 964 965 ret = B_TRUE; 966 cleanup: 967 if (key_fp != NULL) { 968 (void) fclose(key_fp); 969 } 970 971 return (ret); 972 } 973 974 static boolean_t 975 resolve_hostname(const char *hostname, nvlist_t *nvl, boolean_t may_be_crap) 976 { 977 struct sockaddr_in sin; 978 struct hostent *hp; 979 struct utsname un; 980 static char myname[SYS_NMLN] = { '\0' }; 981 char *cp = NULL; 982 char msg[WBCGI_MAXBUF]; 983 984 /* 985 * Initialize cached nodename 986 */ 987 if (strlen(myname) == 0) { 988 if (uname(&un) == -1) { 989 (void) snprintf(msg, sizeof (msg), 990 "(unable to retrieve uname, errno %d)", errno); 991 print_status(500, msg); 992 return (B_FALSE); 993 } 994 (void) strcpy(myname, un.nodename); 995 } 996 997 /* 998 * If hostname is local node name, return the address this 999 * request came in on, which is supplied as SERVER_ADDR in the 1000 * cgi environment. This ensures we don't send back a possible 1001 * alternate address that may be unreachable from the client's 1002 * network. Otherwise, just resolve with nameservice. 1003 */ 1004 if ((strcmp(hostname, myname) != 0) || 1005 ((cp = getenv("SERVER_ADDR")) == NULL)) { 1006 if (((hp = gethostbyname(hostname)) == NULL) || 1007 (hp->h_addrtype != AF_INET) || 1008 (hp->h_length != sizeof (struct in_addr))) { 1009 if (!may_be_crap) { 1010 print_status(500, "(error resolving hostname)"); 1011 } 1012 return (may_be_crap); 1013 } 1014 (void) memcpy(&sin.sin_addr, hp->h_addr, hp->h_length); 1015 cp = inet_ntoa(sin.sin_addr); 1016 } 1017 1018 if (nvlist_add_string(nvl, (char *)hostname, cp) != 0) { 1019 print_status(500, "(error adding hostname to nvlist)"); 1020 return (B_FALSE); 1021 } 1022 1023 return (B_TRUE); 1024 } 1025 1026 /* 1027 * one_name() is called for each certificate found and is passed the string 1028 * that X509_NAME_oneline() returns. Its job is to find the common name and 1029 * determine whether it is a host name; if it is then a line suitable for 1030 * inclusion in /etc/inet/hosts is written to that file. 1031 */ 1032 static boolean_t 1033 one_name(const char *namestr, nvlist_t *nvl) 1034 { 1035 boolean_t ret = B_TRUE; 1036 char *p; 1037 char *q; 1038 char c; 1039 1040 if (namestr != NULL && 1041 (p = strstr(namestr, WBCGI_CNSTR)) != NULL) { 1042 p += WBCGI_CNSTR_LEN; 1043 1044 if ((q = strpbrk(p, WBCGI_NAMESEP)) != NULL) { 1045 c = *q; 1046 *q = '\0'; 1047 ret = resolve_hostname(p, nvl, B_TRUE); 1048 *q = c; 1049 } else { 1050 ret = resolve_hostname(p, nvl, B_TRUE); 1051 } 1052 } 1053 1054 return (ret); 1055 } 1056 1057 /* 1058 * Loop through the certificates in a file 1059 */ 1060 static int 1061 get_hostnames(const char *path, void *nvl) 1062 { 1063 int ret = WBCGI_FTW_CBERR; 1064 STACK_OF(X509) *certs = NULL; 1065 PKCS12 *p12 = NULL; 1066 char message[WBCGI_MAXBUF]; 1067 char buf[WBCGI_MAXBUF + 1]; 1068 FILE *rfp = NULL; 1069 X509 *x = NULL; 1070 int errtype = 0; 1071 int chars; 1072 int i; 1073 1074 if ((rfp = fopen(path, "r")) == NULL) { 1075 goto cleanup; 1076 } 1077 1078 if ((p12 = d2i_PKCS12_fp(rfp, NULL)) == NULL) { 1079 errtype = 1; 1080 goto cleanup; 1081 } 1082 i = sunw_PKCS12_parse(p12, WANBOOT_PASSPHRASE, DO_NONE, NULL, 0, NULL, 1083 NULL, NULL, &certs); 1084 if (i <= 0) { 1085 errtype = 1; 1086 goto cleanup; 1087 } 1088 1089 PKCS12_free(p12); 1090 p12 = NULL; 1091 1092 for (i = 0; i < sk_X509_num(certs); i++) { 1093 /* LINTED */ 1094 x = sk_X509_value(certs, i); 1095 if (!one_name(sunw_issuer_attrs(x, buf, sizeof (buf) - 1), 1096 nvl)) { 1097 goto cleanup; 1098 } 1099 } 1100 1101 ret = WBCGI_FTW_CBCONT; 1102 cleanup: 1103 if (ret == WBCGI_FTW_CBERR) { 1104 if (errtype == 1) { 1105 chars = snprintf(message, sizeof (message), 1106 "(internal PKCS12 error reading %s)", path); 1107 } else { 1108 chars = snprintf(message, sizeof (message), 1109 "error reading %s", path); 1110 } 1111 if (chars > 0 && chars <= sizeof (message)) { 1112 print_status(500, message); 1113 } else { 1114 print_status(500, NULL); 1115 } 1116 } 1117 if (rfp != NULL) { 1118 (void) fclose(rfp); 1119 } 1120 if (p12 != NULL) { 1121 PKCS12_free(p12); 1122 } 1123 if (certs != NULL) { 1124 sk_X509_pop_free(certs, X509_free); 1125 } 1126 1127 return (ret); 1128 } 1129 1130 /* 1131 * Create a hosts file by extracting hosts from client and truststore 1132 * files. Use the CN. Then we should copy that file to the inet dir. 1133 */ 1134 static boolean_t 1135 create_hostsfile(const char *hostsfile, const char *net, const char *cid) 1136 { 1137 boolean_t ret = B_FALSE; 1138 nvlist_t *nvl; 1139 nvpair_t *nvp; 1140 FILE *hostfp = NULL; 1141 int hostfd = -1; 1142 int i; 1143 char *hostslist; 1144 const char *bc_urls[] = { BC_ROOT_SERVER, BC_BOOT_LOGGER, NULL }; 1145 1146 /* 1147 * Allocate nvlist handle to store our hostname/IP pairs. 1148 */ 1149 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) { 1150 print_status(500, "(error allocating hostname nvlist)"); 1151 goto cleanup; 1152 } 1153 1154 /* 1155 * Extract and resolve hostnames from CNs. 1156 */ 1157 if (netboot_ftw(NB_CLIENT_CERT, net, cid, 1158 get_hostnames, nvl) == WBCGI_FTW_CBERR || 1159 netboot_ftw(NB_CA_CERT, net, cid, 1160 get_hostnames, nvl) == WBCGI_FTW_CBERR) { 1161 goto cleanup; 1162 } 1163 1164 /* 1165 * Extract and resolve hostnames from any URLs in bootconf. 1166 */ 1167 for (i = 0; bc_urls[i] != NULL; ++i) { 1168 char *urlstr; 1169 url_t url; 1170 1171 if ((urlstr = bootconf_get(&bc_handle, bc_urls[i])) != NULL && 1172 url_parse(urlstr, &url) == URL_PARSE_SUCCESS) { 1173 if (!resolve_hostname(url.hport.hostname, 1174 nvl, B_FALSE)) { 1175 goto cleanup; 1176 } 1177 } 1178 } 1179 1180 /* 1181 * If there is a resolve-hosts list in bootconf, resolve those 1182 * hostnames too. 1183 */ 1184 if ((hostslist = bootconf_get(&bc_handle, BC_RESOLVE_HOSTS)) != NULL) { 1185 char *hostname; 1186 1187 for (hostname = strtok(hostslist, ","); hostname != NULL; 1188 hostname = strtok(NULL, ",")) { 1189 if (!resolve_hostname(hostname, nvl, B_FALSE)) { 1190 goto cleanup; 1191 } 1192 } 1193 } 1194 1195 /* 1196 * Now write the hostname/IP pairs gathered to the hosts file. 1197 */ 1198 if ((hostfd = open(hostsfile, 1199 O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR)) == -1 || 1200 (hostfp = fdopen(hostfd, "w+")) == NULL) { 1201 print_status(500, "(error creating hosts file)"); 1202 goto cleanup; 1203 } 1204 for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL; 1205 nvp = nvlist_next_nvpair(nvl, nvp)) { 1206 char *hostname; 1207 char *ipstr; 1208 1209 hostname = nvpair_name(nvp); 1210 if (nvpair_value_string(nvp, &ipstr) != 0) { 1211 print_status(500, "(nvl error writing hosts file)"); 1212 goto cleanup; 1213 } 1214 1215 if (fprintf(hostfp, "%s\t%s\n", ipstr, hostname) < 0) { 1216 print_status(500, "(error writing hosts file)"); 1217 goto cleanup; 1218 } 1219 } 1220 1221 ret = B_TRUE; 1222 cleanup: 1223 if (nvl != NULL) { 1224 nvlist_free(nvl); 1225 } 1226 if (hostfp != NULL) { 1227 /* 1228 * hostfd is automatically closed as well. 1229 */ 1230 (void) fclose(hostfp); 1231 } 1232 1233 return (ret); 1234 } 1235 1236 static boolean_t 1237 bootfile_payload(const char *docroot, char **bootpathp) 1238 { 1239 boolean_t ret = B_FALSE; 1240 char *boot_file; 1241 struct stat sbuf; 1242 1243 if ((boot_file = bootconf_get(&bc_handle, BC_BOOT_FILE)) == NULL) { 1244 print_status(500, "(boot_file must be specified)"); 1245 goto cleanup; 1246 } 1247 if ((*bootpathp = make_path(docroot, boot_file)) == NULL) { 1248 goto cleanup; 1249 } 1250 if (!WBCGI_FILE_EXISTS(*bootpathp, sbuf)) { 1251 print_status(500, "(boot_file missing)"); 1252 goto cleanup; 1253 } 1254 1255 ret = B_TRUE; 1256 cleanup: 1257 return (ret); 1258 } 1259 1260 /* 1261 * Create the wanboot file system whose contents are determined by the 1262 * security configuration specified in bootconf. 1263 */ 1264 static boolean_t 1265 wanbootfs_payload(const char *net, const char *cid, const char *nonce, 1266 const char *bootconf, char **wanbootfs_imagep) 1267 { 1268 int ret = B_FALSE; 1269 1270 char *server_authentication; 1271 char *client_authentication; 1272 char *scf; 1273 1274 char *bootfs_dir = NULL; 1275 char *bootfs_etc_dir = NULL; 1276 char *bootfs_etc_inet_dir = NULL; 1277 char *bootfs_dev_dir = NULL; 1278 1279 char *systemconf = NULL; 1280 char *keystorepath = NULL; 1281 char *certstorepath = NULL; 1282 char *truststorepath = NULL; 1283 char *bootconfpath = NULL; 1284 char *systemconfpath = NULL; 1285 char *urandompath = NULL; 1286 char *noncepath = NULL; 1287 char *hostspath = NULL; 1288 char *etc_hostspath = NULL; 1289 char *timestamppath = NULL; 1290 1291 boolean_t authenticate_client; 1292 boolean_t authenticate_server; 1293 1294 struct stat sbuf; 1295 1296 /* 1297 * Initialize SSL stuff. 1298 */ 1299 sunw_crypto_init(); 1300 1301 /* 1302 * Get the security strategy values. 1303 */ 1304 client_authentication = bootconf_get(&bc_handle, 1305 BC_CLIENT_AUTHENTICATION); 1306 authenticate_client = (client_authentication != NULL && 1307 strcmp(client_authentication, "yes") == 0); 1308 server_authentication = bootconf_get(&bc_handle, 1309 BC_SERVER_AUTHENTICATION); 1310 authenticate_server = (server_authentication != NULL && 1311 strcmp(server_authentication, "yes") == 0); 1312 1313 /* 1314 * Make a temporary directory structure for the wanboot file system. 1315 */ 1316 if ((bootfs_dir = gen_tmppath("bootfs_dir", net, cid)) == NULL || 1317 (bootfs_etc_dir = make_path(bootfs_dir, "etc")) == NULL || 1318 (bootfs_etc_inet_dir = make_path(bootfs_etc_dir, "inet")) == NULL || 1319 (bootfs_dev_dir = make_path(bootfs_dir, "dev")) == NULL) { 1320 goto cleanup; 1321 } 1322 if (mkdirp(bootfs_dir, 0700) || 1323 mkdirp(bootfs_etc_dir, 0700) || 1324 mkdirp(bootfs_etc_inet_dir, 0700) || 1325 mkdirp(bootfs_dev_dir, 0700)) { 1326 print_status(500, "(error creating wanbootfs dir structure)"); 1327 goto cleanup; 1328 } 1329 1330 if (authenticate_client) { 1331 /* 1332 * Add the client private key. 1333 */ 1334 if ((keystorepath = make_path(bootfs_dir, 1335 NB_CLIENT_KEY)) == NULL || 1336 netboot_ftw(NB_CLIENT_KEY, net, cid, 1337 create_keystore, keystorepath) != WBCGI_FTW_CBOK) { 1338 goto cleanup; 1339 } 1340 1341 /* 1342 * Add the client certificate. 1343 */ 1344 if ((certstorepath = make_path(bootfs_dir, 1345 NB_CLIENT_CERT)) == NULL || 1346 netboot_ftw(NB_CLIENT_CERT, net, cid, 1347 copy_certstore, certstorepath) != WBCGI_FTW_CBOK) { 1348 goto cleanup; 1349 } 1350 } 1351 1352 if (authenticate_client || authenticate_server) { 1353 /* 1354 * Add the trustfile; at least one truststore must exist. 1355 */ 1356 if ((truststorepath = make_path(bootfs_dir, 1357 NB_CA_CERT)) == NULL) { 1358 goto cleanup; 1359 } 1360 if (netboot_ftw(NB_CA_CERT, net, cid, 1361 noact_cb, NULL) != WBCGI_FTW_CBOK) { 1362 print_status(500, "(truststore not found)"); 1363 } 1364 if (netboot_ftw(NB_CA_CERT, net, cid, 1365 build_trustfile, truststorepath) == WBCGI_FTW_CBERR) { 1366 goto cleanup; 1367 } 1368 1369 /* 1370 * Create the /dev/urandom file. 1371 */ 1372 if ((urandompath = make_path(bootfs_dev_dir, 1373 "urandom")) == NULL || 1374 !create_urandom(urandompath)) { 1375 goto cleanup; 1376 } 1377 } 1378 1379 /* 1380 * Add the wanboot.conf(4) file. 1381 */ 1382 if ((bootconfpath = make_path(bootfs_dir, NB_WANBOOT_CONF)) == NULL || 1383 !copy_file(bootconf, bootconfpath)) { 1384 goto cleanup; 1385 } 1386 1387 /* 1388 * Add the system_conf file if present. 1389 */ 1390 if ((scf = bootconf_get(&bc_handle, BC_SYSTEM_CONF)) != NULL) { 1391 if (netboot_ftw(scf, net, cid, 1392 set_pathname, &systemconf) != WBCGI_FTW_CBOK) { 1393 print_status(500, "(system_conf file not found)"); 1394 goto cleanup; 1395 } 1396 if ((systemconfpath = make_path(bootfs_dir, 1397 NB_SYSTEM_CONF)) == NULL || 1398 !copy_file(systemconf, systemconfpath)) { 1399 goto cleanup; 1400 } 1401 } 1402 1403 /* 1404 * Create the /nonce file. 1405 */ 1406 if ((noncepath = make_path(bootfs_dir, "nonce")) == NULL || 1407 !create_nonce(noncepath, nonce)) { 1408 goto cleanup; 1409 } 1410 1411 /* 1412 * Create an /etc/inet/hosts file by extracting hostnames from CN, 1413 * URLs in bootconf and resolve-hosts in bootconf. 1414 */ 1415 if ((hostspath = make_path(bootfs_etc_inet_dir, "hosts")) == NULL || 1416 !create_hostsfile(hostspath, net, cid)) { 1417 goto cleanup; 1418 } 1419 1420 /* 1421 * We would like to create a symbolic link etc/hosts -> etc/inet/hosts, 1422 * but unfortunately the HSFS support in the standalone doesn't handle 1423 * symlinks. 1424 */ 1425 if ((etc_hostspath = make_path(bootfs_etc_dir, "hosts")) == NULL || 1426 !copy_file(hostspath, etc_hostspath)) { 1427 goto cleanup; 1428 } 1429 1430 /* 1431 * Create the /timestamp file. 1432 */ 1433 if ((timestamppath = make_path(bootfs_dir, "timestamp")) == NULL || 1434 !create_timestamp(timestamppath, "timestamp")) { 1435 goto cleanup; 1436 } 1437 1438 /* 1439 * Create an HSFS file system for the directory. 1440 */ 1441 if ((*wanbootfs_imagep = gen_tmppath("wanbootfs", net, cid)) == NULL || 1442 !mkisofs(bootfs_dir, *wanbootfs_imagep)) { 1443 goto cleanup; 1444 } 1445 1446 ret = B_TRUE; 1447 cleanup: 1448 /* 1449 * Clean up temporary files and directories. 1450 */ 1451 if (keystorepath != NULL && 1452 WBCGI_FILE_EXISTS(keystorepath, sbuf)) { 1453 (void) unlink(keystorepath); 1454 } 1455 if (certstorepath != NULL && 1456 WBCGI_FILE_EXISTS(certstorepath, sbuf)) { 1457 (void) unlink(certstorepath); 1458 } 1459 if (truststorepath != NULL && 1460 WBCGI_FILE_EXISTS(truststorepath, sbuf)) { 1461 (void) unlink(truststorepath); 1462 } 1463 if (bootconfpath != NULL && 1464 WBCGI_FILE_EXISTS(bootconfpath, sbuf)) { 1465 (void) unlink(bootconfpath); 1466 } 1467 if (systemconfpath != NULL && 1468 WBCGI_FILE_EXISTS(systemconfpath, sbuf)) { 1469 (void) unlink(systemconfpath); 1470 } 1471 if (urandompath != NULL && 1472 WBCGI_FILE_EXISTS(urandompath, sbuf)) { 1473 (void) unlink(urandompath); 1474 } 1475 if (noncepath != NULL && 1476 WBCGI_FILE_EXISTS(noncepath, sbuf)) { 1477 (void) unlink(noncepath); 1478 } 1479 if (hostspath != NULL && 1480 WBCGI_FILE_EXISTS(hostspath, sbuf)) { 1481 (void) unlink(hostspath); 1482 } 1483 if (etc_hostspath != NULL && 1484 WBCGI_FILE_EXISTS(etc_hostspath, sbuf)) { 1485 (void) unlink(etc_hostspath); 1486 } 1487 if (timestamppath != NULL && 1488 WBCGI_FILE_EXISTS(timestamppath, sbuf)) { 1489 (void) unlink(timestamppath); 1490 } 1491 1492 if (bootfs_etc_inet_dir != NULL && 1493 WBCGI_DIR_EXISTS(bootfs_etc_inet_dir, sbuf)) { 1494 (void) rmdir(bootfs_etc_inet_dir); 1495 } 1496 if (bootfs_etc_dir != NULL && 1497 WBCGI_DIR_EXISTS(bootfs_etc_dir, sbuf)) { 1498 (void) rmdir(bootfs_etc_dir); 1499 } 1500 if (bootfs_dev_dir != NULL && 1501 WBCGI_DIR_EXISTS(bootfs_dev_dir, sbuf)) { 1502 (void) rmdir(bootfs_dev_dir); 1503 } 1504 if (bootfs_dir != NULL && 1505 WBCGI_DIR_EXISTS(bootfs_dir, sbuf)) { 1506 (void) rmdir(bootfs_dir); 1507 } 1508 1509 /* 1510 * Free allocated memory. 1511 */ 1512 free_path(&bootfs_dir); 1513 free_path(&bootfs_etc_dir); 1514 free_path(&bootfs_etc_inet_dir); 1515 free_path(&bootfs_dev_dir); 1516 1517 free_path(&systemconf); 1518 free_path(&keystorepath); 1519 free_path(&certstorepath); 1520 free_path(&truststorepath); 1521 free_path(&bootconfpath); 1522 free_path(&systemconfpath); 1523 free_path(&urandompath); 1524 free_path(&noncepath); 1525 free_path(&hostspath); 1526 free_path(&etc_hostspath); 1527 free_path(×tamppath); 1528 1529 return (ret); 1530 } 1531 1532 static boolean_t 1533 miniroot_payload(const char *net, const char *cid, const char *docroot, 1534 char **rootpathp, char **rootinfop, boolean_t *https_rootserverp) 1535 { 1536 boolean_t ret = B_FALSE; 1537 char *root_server; 1538 char *root_file; 1539 url_t url; 1540 struct stat sbuf; 1541 char sizebuf[WBCGI_MAXBUF]; 1542 int chars; 1543 int fd = -1; 1544 1545 if ((root_server = bootconf_get(&bc_handle, BC_ROOT_SERVER)) == NULL) { 1546 print_status(500, "(root_server must be specified)"); 1547 goto cleanup; 1548 } 1549 if (url_parse(root_server, &url) != URL_PARSE_SUCCESS) { 1550 print_status(500, "(root_server URL is invalid)"); 1551 } 1552 *https_rootserverp = url.https; 1553 1554 if ((root_file = bootconf_get(&bc_handle, BC_ROOT_FILE)) == NULL) { 1555 print_status(500, "(rootfile must be specified)"); 1556 goto cleanup; 1557 } 1558 if ((*rootpathp = make_path(docroot, root_file)) == NULL) { 1559 goto cleanup; 1560 } 1561 if (!WBCGI_FILE_EXISTS(*rootpathp, sbuf)) { 1562 print_status(500, "(root filesystem image missing)"); 1563 goto cleanup; 1564 } 1565 1566 if ((*rootinfop = gen_tmppath("mrinfo", net, cid)) == NULL) { 1567 goto cleanup; 1568 } 1569 if ((chars = snprintf(sizebuf, sizeof (sizebuf), "%ld", 1570 sbuf.st_size)) < 0 || chars > sizeof (sizebuf) || 1571 (fd = open(*rootinfop, 1572 O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR)) == -1 || 1573 !write_buffer(fd, sizebuf, strlen(sizebuf))) { 1574 print_status(500, "(error creating miniroot info file)"); 1575 goto cleanup; 1576 } 1577 1578 ret = B_TRUE; 1579 cleanup: 1580 if (fd != -1) { 1581 (void) close(fd); 1582 } 1583 1584 return (ret); 1585 } 1586 1587 static boolean_t 1588 deliver_payload(const char *payload, const char *payload_hash) 1589 { 1590 int fd = fileno(stdout); 1591 struct stat payload_buf, hash_buf; 1592 int chars; 1593 char main_header[WBCGI_MAXBUF]; 1594 char multi_header[WBCGI_MAXBUF]; 1595 char multi_header1[WBCGI_MAXBUF]; 1596 char multi_header2[WBCGI_MAXBUF]; 1597 char multi_end[WBCGI_MAXBUF]; 1598 size_t msglen; 1599 1600 if (!WBCGI_FILE_EXISTS(payload, payload_buf) || 1601 !WBCGI_FILE_EXISTS(payload_hash, hash_buf)) { 1602 print_status(500, "(payload/hash file(s) missing)"); 1603 return (B_FALSE); 1604 } 1605 1606 /* 1607 * Multi-part header. 1608 */ 1609 if ((chars = snprintf(multi_header, sizeof (multi_header), 1610 "%s--%s%s%sapplication/octet-stream%s%s", WBCGI_CRNL, 1611 WBCGI_WANBOOT_BNDTXT, WBCGI_CRNL, WBCGI_CONTENT_TYPE, WBCGI_CRNL, 1612 WBCGI_CONTENT_LENGTH)) < 0 || chars > sizeof (multi_header)) { 1613 print_status(500, "(error creating multi_header)"); 1614 return (B_FALSE); 1615 } 1616 1617 /* 1618 * Multi-part header for part one. 1619 */ 1620 if ((chars = snprintf(multi_header1, sizeof (multi_header1), 1621 "%s%ld%s%s", multi_header, payload_buf.st_size, WBCGI_CRNL, 1622 WBCGI_CRNL)) < 0 || chars > sizeof (multi_header1)) { 1623 print_status(500, "(error creating multi_header1)"); 1624 return (B_FALSE); 1625 } 1626 1627 /* 1628 * Multi-part header for part two. 1629 */ 1630 if ((chars = snprintf(multi_header2, sizeof (multi_header2), 1631 "%s%ld%s%s", multi_header, hash_buf.st_size, WBCGI_CRNL, 1632 WBCGI_CRNL)) < 0 || chars > sizeof (multi_header2)) { 1633 print_status(500, "(error creating multi_header2)"); 1634 return (B_FALSE); 1635 } 1636 1637 /* 1638 * End-of-parts Trailer. 1639 */ 1640 if ((chars = snprintf(multi_end, sizeof (multi_end), 1641 "%s--%s--%s", WBCGI_CRNL, WBCGI_WANBOOT_BNDTXT, 1642 WBCGI_CRNL)) < 0 || chars > sizeof (multi_end)) { 1643 print_status(500, "(error creating multi_end)"); 1644 return (B_FALSE); 1645 } 1646 1647 /* 1648 * Message header. 1649 */ 1650 msglen = payload_buf.st_size + hash_buf.st_size + 1651 strlen(multi_header1) + strlen(multi_header2) + strlen(multi_end); 1652 1653 if ((chars = snprintf(main_header, sizeof (main_header), 1654 "%s%u%s%smultipart/mixed; boundary=%s%s%s", WBCGI_CONTENT_LENGTH, 1655 msglen, WBCGI_CRNL, WBCGI_CONTENT_TYPE, WBCGI_WANBOOT_BNDTXT, 1656 WBCGI_CRNL, WBCGI_CRNL)) < 0 || chars > sizeof (main_header)) { 1657 print_status(500, "(error creating main_header)"); 1658 return (B_FALSE); 1659 } 1660 1661 /* 1662 * Write the message out. If things fall apart during this then 1663 * there's no way to report the error back to the client. 1664 */ 1665 if (!write_buffer(fd, main_header, strlen(main_header)) || 1666 !write_buffer(fd, multi_header1, strlen(multi_header1)) || 1667 !write_file(fd, payload, payload_buf.st_size) || 1668 !write_buffer(fd, multi_header2, strlen(multi_header2)) || 1669 !write_file(fd, payload_hash, hash_buf.st_size) || 1670 !write_buffer(fileno(stdout), multi_end, strlen(multi_end))) { 1671 return (B_FALSE); 1672 } 1673 1674 return (B_TRUE); 1675 } 1676 1677 1678 /*ARGSUSED*/ 1679 int 1680 main(int argc, char **argv) 1681 { 1682 int ret = WBCGI_STATUS_ERR; 1683 struct stat sbuf; 1684 int content; 1685 char *net; 1686 char *cid; 1687 char *nonce; 1688 char *docroot; 1689 char *payload; 1690 char *signature_type; 1691 char *encryption_type; 1692 char *bootconf = NULL; 1693 char *keyfile = NULL; 1694 char *bootpath = NULL; 1695 char *wanbootfs_image = NULL; 1696 char *rootpath = NULL; 1697 char *miniroot_info = NULL; 1698 char *encr_payload = NULL; 1699 char *payload_hash = NULL; 1700 boolean_t https_rootserver; 1701 1702 /* 1703 * Process the query string. 1704 */ 1705 if (!get_request_info(&content, &net, &cid, &nonce, &docroot)) { 1706 goto cleanup; 1707 } 1708 1709 /* 1710 * Sanity check that the netboot directory exists. 1711 */ 1712 if (!WBCGI_DIR_EXISTS(NB_NETBOOT_ROOT, sbuf)) { 1713 print_status(500, "(" NB_NETBOOT_ROOT " does not exist)"); 1714 goto cleanup; 1715 } 1716 1717 /* 1718 * Get absolute bootconf pathname. 1719 */ 1720 if (netboot_ftw(NB_WANBOOT_CONF, net, cid, 1721 set_pathname, &bootconf) != WBCGI_FTW_CBOK) { 1722 print_status(500, "(wanboot.conf not found)"); 1723 goto cleanup; 1724 } 1725 1726 /* 1727 * Initialize bc_handle from the given wanboot.conf file. 1728 */ 1729 if (bootconf_init(&bc_handle, bootconf) != BC_SUCCESS) { 1730 char message[WBCGI_MAXBUF]; 1731 int chars; 1732 1733 chars = snprintf(message, sizeof (message), 1734 "(wanboot.conf error: %s)", bootconf_errmsg(&bc_handle)); 1735 if (chars > 0 && chars < sizeof (message)) 1736 print_status(500, message); 1737 else 1738 print_status(500, "(wanboot.conf error)"); 1739 goto cleanup; 1740 } 1741 1742 /* 1743 * Get and check signature and encryption types, 1744 * presence of helper utilities, keystore, etc. 1745 */ 1746 if ((signature_type = bootconf_get(&bc_handle, 1747 BC_SIGNATURE_TYPE)) != NULL) { 1748 if (!WBCGI_FILE_EXISTS(WBCGI_HMAC_PATH, sbuf)) { 1749 print_status(500, "(hmac utility not found)"); 1750 goto cleanup; 1751 } 1752 if (keyfile == NULL && netboot_ftw(NB_CLIENT_KEY, net, cid, 1753 set_pathname, &keyfile) != WBCGI_FTW_CBOK) { 1754 print_status(500, "(keystore not found)"); 1755 goto cleanup; 1756 } 1757 if (!check_key_type(keyfile, signature_type, WBKU_HASH_KEY)) { 1758 print_status(500, "(hash key not found)"); 1759 goto cleanup; 1760 } 1761 } 1762 if ((encryption_type = bootconf_get(&bc_handle, 1763 BC_ENCRYPTION_TYPE)) != NULL) { 1764 if (signature_type == NULL) { 1765 print_status(500, "(encrypted but not signed)"); 1766 goto cleanup; 1767 } 1768 if (!WBCGI_FILE_EXISTS(WBCGI_ENCR_PATH, sbuf)) { 1769 print_status(500, "(encr utility not found)"); 1770 goto cleanup; 1771 } 1772 if (keyfile == NULL && netboot_ftw(NB_CLIENT_KEY, net, cid, 1773 set_pathname, &keyfile) != WBCGI_FTW_CBOK) { 1774 print_status(500, "(keystore not found)"); 1775 goto cleanup; 1776 } 1777 if (!check_key_type(keyfile, encryption_type, WBKU_ENCR_KEY)) { 1778 print_status(500, "(encr key not found)"); 1779 goto cleanup; 1780 } 1781 } 1782 1783 /* 1784 * Determine/create our payload. 1785 */ 1786 switch (content) { 1787 case WBCGI_CONTENT_BOOTFILE: 1788 if (!bootfile_payload(docroot, &bootpath)) { 1789 goto cleanup; 1790 } 1791 payload = bootpath; 1792 1793 break; 1794 1795 case WBCGI_CONTENT_BOOTFS: 1796 if (!wanbootfs_payload(net, cid, nonce, 1797 bootconf, &wanbootfs_image)) { 1798 goto cleanup; 1799 } 1800 payload = wanbootfs_image; 1801 1802 break; 1803 1804 case WBCGI_CONTENT_ROOTFS: 1805 if (!miniroot_payload(net, cid, docroot, 1806 &rootpath, &miniroot_info, &https_rootserver)) { 1807 goto cleanup; 1808 } 1809 payload = rootpath; 1810 1811 break; 1812 } 1813 1814 /* 1815 * Encrypt the payload if necessary. 1816 */ 1817 if (content != WBCGI_CONTENT_BOOTFILE && 1818 content != WBCGI_CONTENT_ROOTFS && 1819 encryption_type != NULL) { 1820 if ((encr_payload = gen_tmppath("encr", net, cid)) == NULL) { 1821 goto cleanup; 1822 } 1823 1824 if (!encrypt_payload(payload, encr_payload, keyfile, 1825 encryption_type)) { 1826 goto cleanup; 1827 } 1828 1829 payload = encr_payload; 1830 } 1831 1832 /* 1833 * Compute the hash (actual or null). 1834 */ 1835 if ((payload_hash = gen_tmppath("hash", net, cid)) == NULL) { 1836 goto cleanup; 1837 } 1838 1839 if (signature_type != NULL && 1840 (content != WBCGI_CONTENT_ROOTFS || !https_rootserver)) { 1841 if (!hash_payload(payload, payload_hash, keyfile)) { 1842 goto cleanup; 1843 } 1844 } else { 1845 if (!create_null_hash(payload_hash)) { 1846 goto cleanup; 1847 } 1848 } 1849 1850 /* 1851 * For the rootfs the actual payload transmitted is the file 1852 * containing the size of the rootfs (as a string of ascii digits); 1853 * point payload at this instead. 1854 */ 1855 if (content == WBCGI_CONTENT_ROOTFS) { 1856 payload = miniroot_info; 1857 } 1858 1859 /* 1860 * Finally, deliver the payload and hash as a multipart message. 1861 */ 1862 if (!deliver_payload(payload, payload_hash)) { 1863 goto cleanup; 1864 } 1865 1866 ret = WBCGI_STATUS_OK; 1867 cleanup: 1868 /* 1869 * Clean up temporary files. 1870 */ 1871 if (wanbootfs_image != NULL && 1872 WBCGI_FILE_EXISTS(wanbootfs_image, sbuf)) { 1873 (void) unlink(wanbootfs_image); 1874 } 1875 if (miniroot_info != NULL && 1876 WBCGI_FILE_EXISTS(miniroot_info, sbuf)) { 1877 (void) unlink(miniroot_info); 1878 } 1879 if (encr_payload != NULL && 1880 WBCGI_FILE_EXISTS(encr_payload, sbuf)) { 1881 (void) unlink(encr_payload); 1882 } 1883 if (payload_hash != NULL && 1884 WBCGI_FILE_EXISTS(payload_hash, sbuf)) { 1885 (void) unlink(payload_hash); 1886 } 1887 1888 /* 1889 * Free up any allocated strings. 1890 */ 1891 free_path(&bootconf); 1892 free_path(&keyfile); 1893 free_path(&bootpath); 1894 free_path(&wanbootfs_image); 1895 free_path(&rootpath); 1896 free_path(&miniroot_info); 1897 free_path(&encr_payload); 1898 free_path(&payload_hash); 1899 1900 bootconf_end(&bc_handle); 1901 1902 return (ret); 1903 }