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(&timestamppath);
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 }