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