8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / usr.lib / wanboot / wanboot-cgi / wanboot-cgi.c
blobc8f391031af82d79ae0f093861441a8c96b8f8b1
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2012 Milan Jurik. All rights reserved.
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>
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>
56 #include <p12aux.h>
58 #include <parseURL.h>
60 * These can be replaced with wanbootutil.h once the openssl interfaces
61 * are moved to libwanboot.
63 #include <wanboot/key_util.h>
64 #include <wanboot/key_xdr.h>
65 #include <hmac_sha1.h>
67 #include <netboot_paths.h>
68 #include <wanboot_conf.h>
71 * Exit status:
73 #define WBCGI_STATUS_OK 0
74 #define WBCGI_STATUS_ERR 1
76 #define WBCGI_FILE_EXISTS(file, statbuf) \
77 (stat(file, &statbuf) == 0 && S_ISREG(statbuf.st_mode))
79 #define WBCGI_DIR_EXISTS(dir, statbuf) \
80 (stat(dir, &statbuf) == 0 && S_ISDIR(statbuf.st_mode))
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"
87 #define WBCGI_DEV_URANDOM "/dev/urandom"
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"
94 #define WBCGI_CNSTR "CN="
95 #define WBCGI_CNSTR_LEN (sizeof (WBCGI_CNSTR) - 1)
96 #define WBCGI_NAMESEP ",/\n\r"
98 #define WBCGI_MAXBUF 256
101 * Possible return values from netboot_ftw():
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 */
109 * getsubopt() is used to map one of the contents[] keywords
110 * to one of these types
112 #define WBCGI_CONTENT_ERROR -1
113 #define WBCGI_CONTENT_BOOTFILE 0
114 #define WBCGI_CONTENT_BOOTFS 1
115 #define WBCGI_CONTENT_ROOTFS 2
117 static char *contents[] =
118 { "bootfile", "bootfs", "rootfs", NULL };
121 * getsubopt() is used to parse the query string for
122 * the keywords defined by queryopts[]
124 #define WBCGI_QUERYOPT_CONTENT 0
125 #define WBCGI_QUERYOPT_NET 1
126 #define WBCGI_QUERYOPT_CID 2
127 #define WBCGI_QUERYOPT_NONCE 3
129 static char *queryopts[] =
130 { "CONTENT", "IP", "CID", "NONCE", NULL };
132 static bc_handle_t bc_handle;
135 static char *
136 status_msg(int status)
138 char *msg;
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;
155 return (msg);
158 static void
159 print_status(int status, const char *spec_msg)
161 if (spec_msg == NULL) {
162 spec_msg = "";
165 (void) fprintf(stdout, "Status: %d %s %s%s", status,
166 status_msg(status), spec_msg, WBCGI_CRNL);
169 static char *
170 make_path(const char *root, const char *suffix)
172 char path[MAXPATHLEN];
173 char *ptr = NULL;
174 int chars;
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)");
182 return (ptr);
185 static void
186 free_path(char **pathp)
188 if (*pathp != NULL) {
189 free(*pathp);
190 *pathp = NULL;
194 static char *
195 gen_tmppath(const char *prefix, const char *net, const char *cid)
197 pid_t pid;
198 time_t secs;
199 int chars;
200 char path[MAXPATHLEN];
201 char *ptr = NULL;
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)");
210 return (ptr);
214 * File I/O stuff:
216 static boolean_t
217 write_buffer(int fd, const void *buffer, size_t buflen)
219 size_t nwritten;
220 ssize_t nbytes;
221 const char *buf = buffer;
223 for (nwritten = 0; nwritten < buflen; nwritten += nbytes) {
224 nbytes = write(fd, &buf[nwritten], buflen - nwritten);
225 if (nbytes <= 0) {
226 return (B_FALSE);
230 return (B_TRUE);
233 static boolean_t
234 write_file(int ofd, const char *filename, size_t size)
236 boolean_t ret = B_TRUE;
237 int ifd;
238 char buf[1024];
239 size_t rlen;
240 ssize_t wlen;
242 if ((ifd = open(filename, O_RDONLY)) < 0) {
243 return (B_FALSE);
246 for (; size != 0; size -= wlen) {
247 rlen = (size < sizeof (buf)) ? size : sizeof (buf);
249 if ((wlen = read(ifd, buf, rlen)) < 0 ||
250 !write_buffer(ofd, buf, wlen)) {
251 ret = B_FALSE;
252 break;
255 (void) close(ifd);
257 return (ret);
260 static boolean_t
261 copy_file(const char *src, const char *dest)
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;
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;
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;
289 mflags |= MAP_FIXED;
291 validsize = MIN(chunksize, nbytes - nwritten);
292 if (!write_buffer(wfd, buf, validsize)) {
293 (void) munmap(buf, chunksize);
294 goto cleanup;
298 if (buf != NULL) {
299 (void) munmap(buf, chunksize);
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);
313 if (rfd != -1) {
314 (void) close(rfd);
316 if (wfd != -1) {
317 (void) close(wfd);
320 return (ret);
323 static boolean_t
324 create_nonce(const char *noncepath, const char *nonce)
326 boolean_t ret = B_TRUE;
327 int fd;
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;
335 if (fd != -1) {
336 (void) close(fd);
339 return (ret);
342 static boolean_t
343 create_timestamp(const char *timestamppath, const char *timestamp)
345 boolean_t ret = B_TRUE;
346 int fd;
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;
354 if (fd != -1) {
355 (void) close(fd);
358 return (ret);
361 static boolean_t
362 create_urandom(const char *urandompath)
364 boolean_t ret = B_TRUE;
365 int fd;
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;
373 if (fd != -1) {
374 (void) close(fd);
377 return (ret);
380 static boolean_t
381 create_null_hash(const char *hashpath)
383 boolean_t ret = B_TRUE;
384 int fd;
385 static char null_hash[HMAC_DIGEST_LEN];
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;
393 if (fd != -1) {
394 (void) close(fd);
397 return (ret);
401 static char *
402 determine_doc_root(void)
404 char *doc_root;
407 * If DOCUMENT_ROOT is valid, use that.
409 if ((doc_root = getenv("DOCUMENT_ROOT")) == NULL ||
410 strlen(doc_root) == 0) {
412 * No DOCUMENT_ROOT - try PATH_TRANSLATED.
414 if ((doc_root = getenv("PATH_TRANSLATED")) == NULL ||
415 strlen(doc_root) == 0) {
417 * Can't determine the document root.
419 return (NULL);
423 return (doc_root);
426 static boolean_t
427 get_request_info(int *contentp, char **netp, char **cidp, char **noncep,
428 char **docrootp)
430 char *method;
431 char *query_string;
432 char *value;
433 char *junk;
434 int i;
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);
442 if ((query_string = getenv("QUERY_STRING")) == NULL) {
443 print_status(400, "(empty query string)");
444 return (B_FALSE);
447 for (i = 0; i < strlen(query_string); i++) {
448 if (query_string[i] == '&') {
449 query_string[i] = ',';
453 *contentp = WBCGI_CONTENT_ERROR;
454 *netp = *cidp = *noncep = NULL;
456 if ((*docrootp = determine_doc_root()) == NULL) {
457 print_status(400, "(unable to determine document root)");
458 return (B_FALSE);
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);
481 switch (*contentp) {
482 default:
483 print_status(400, "(missing or illegal CONTENT)");
484 return (B_FALSE);
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);
492 break;
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);
501 break;
504 return (B_TRUE);
507 static boolean_t
508 encrypt_payload(const char *payload, const char *encr_payload,
509 const char *keyfile, const char *encryption_type)
511 struct stat sbuf;
512 int chars;
513 char cmd[MAXPATHLEN];
514 FILE *fp;
515 int status;
516 char msg[WBCGI_MAXBUF];
518 if (!WBCGI_FILE_EXISTS(payload, sbuf)) {
519 print_status(500, "(encrypt_payload: missing payload)");
520 return (B_FALSE);
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);
531 if ((fp = popen(cmd, "w")) == NULL) {
532 print_status(500, "(encrypt_payload: missing/file error)");
533 return (B_FALSE);
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);
542 if (!WBCGI_FILE_EXISTS(encr_payload, sbuf)) {
543 print_status(500, "(encrypt_payload: bad encrypted file)");
544 return (B_FALSE);
547 return (B_TRUE);
550 static boolean_t
551 hash_payload(const char *payload, const char *payload_hash,
552 const char *keyfile)
554 struct stat sbuf;
555 int chars;
556 char cmd[MAXPATHLEN];
557 FILE *fp;
558 int status;
559 char msg[WBCGI_MAXBUF];
561 if (!WBCGI_FILE_EXISTS(payload, sbuf)) {
562 print_status(500, "(hash_payload: missing payload)");
563 return (B_FALSE);
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);
573 if ((fp = popen(cmd, "w")) == NULL) {
574 print_status(500, "(hash_payload: missing/file error)");
575 return (B_FALSE);
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);
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);
590 return (B_TRUE);
593 static boolean_t
594 extract_keystore(const char *path, const char *keystorepath)
596 struct stat sbuf;
597 int chars;
598 char cmd[MAXPATHLEN];
599 FILE *fp;
600 int status;
601 char msg[WBCGI_MAXBUF];
603 if (!WBCGI_FILE_EXISTS(path, sbuf)) {
604 print_status(500, "(extract_keystore: missing keystore)");
605 return (B_FALSE);
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);
616 if ((fp = popen(cmd, "w")) == NULL) {
617 print_status(500, "(extract_keystore: missing/file error)");
618 return (B_FALSE);
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);
627 if (!WBCGI_FILE_EXISTS(keystorepath, sbuf)) {
628 print_status(500, "(extract_keystore: failed to create)");
629 return (B_FALSE);
632 return (B_TRUE);
635 static boolean_t
636 mkisofs(const char *image_dir, const char *image)
638 struct stat sbuf;
639 int chars;
640 char cmd[MAXPATHLEN];
641 FILE *fp;
642 int status;
643 char msg[WBCGI_MAXBUF];
645 if (!WBCGI_DIR_EXISTS(image_dir, sbuf)) {
646 print_status(500, "(mksiofs: missing image_dir)");
647 return (B_FALSE);
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);
657 if ((fp = popen(cmd, "w")) == NULL) {
658 print_status(500, "(mkisofs: missing/file error)");
659 return (B_FALSE);
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);
668 if (!WBCGI_FILE_EXISTS(image, sbuf)) {
669 print_status(500, "(mksiofs: failed to create image)");
670 return (B_FALSE);
673 return (B_TRUE);
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:
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>
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.
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.
697 * Returns:
698 * WBCGI_FTW_DONE, WBCGI_FTW_CBOK or WBCGI_FTW_CBERR.
700 static int
701 netboot_ftw(const char *filename, const char *net, const char *cid,
702 int (*cb)(const char *, void *arg), void *arg)
704 char ckpath[4][MAXPATHLEN];
705 int ret;
706 struct stat buf;
707 int i = 0;
709 if (snprintf(ckpath[i++], MAXPATHLEN, "%s%s", NB_NETBOOT_ROOT, filename)
710 >= MAXPATHLEN)
711 return (WBCGI_FTW_CBERR);
713 if (net != NULL && snprintf(ckpath[i++], MAXPATHLEN, "%s%s/%s",
714 NB_NETBOOT_ROOT, net, filename) >= MAXPATHLEN)
715 return (WBCGI_FTW_CBERR);
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);
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);
729 * Loop through hierarchy and check for file existence.
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);
738 return (WBCGI_FTW_DONE);
741 /*ARGSUSED*/
742 static int
743 noact_cb(const char *path, void *arg)
745 return (WBCGI_FTW_CBOK);
748 static int
749 set_pathname(const char *path, void *pathname)
751 *(char **)pathname = strdup((char *)path);
752 return (WBCGI_FTW_CBOK);
755 static int
756 create_keystore(const char *path, void *keystorepath)
758 if (!extract_keystore(path, (char *)keystorepath)) {
759 return (WBCGI_FTW_CBERR);
761 return (WBCGI_FTW_CBOK);
764 static int
765 copy_certstore(const char *path, void *certstorepath)
767 if (!copy_file(path, (char *)certstorepath)) {
768 return (WBCGI_FTW_CBERR);
770 return (WBCGI_FTW_CBOK);
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.
778 static int
779 build_trustfile(const char *path, void *truststorepath)
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;
796 if (!WBCGI_FILE_EXISTS(path, i_st)) {
797 goto cleanup;
800 if (WBCGI_FILE_EXISTS((char *)truststorepath, o_st)) {
802 * If we are inadvertantly writing to the input file.
803 * return success.
804 * XXX Pete: how can this happen, and why success?
806 if (i_st.st_ino == o_st.st_ino) {
807 ret = WBCGI_FTW_CBCONT;
808 goto cleanup;
810 if ((wfp = fopen((char *)truststorepath, "r+")) == NULL) {
811 goto cleanup;
814 * Read what's already there, so that new information
815 * can be added.
817 if ((p12 = d2i_PKCS12_fp(wfp, NULL)) == NULL) {
818 errtype = 1;
819 goto cleanup;
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;
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);
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.
847 if ((wfd = open((char *)truststorepath,
848 O_CREAT|O_EXCL|O_RDWR, 0700)) < 0) {
849 goto cleanup;
851 if ((wfp = fdopen(wfd, "w+")) == NULL) {
852 goto cleanup;
854 o_anchors = sk_X509_new_null();
855 if (o_anchors == NULL) {
856 goto cleanup;
860 if ((rfp = fopen(path, "r")) == NULL) {
861 goto cleanup;
863 if ((p12 = d2i_PKCS12_fp(rfp, NULL)) == NULL) {
864 errtype = 1;
865 goto cleanup;
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;
873 PKCS12_free(p12);
874 p12 = NULL;
877 * Merge the two stacks of pkcs12 certs.
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);
886 * Create the pkcs12 structure from the modified input stack and
887 * then write out that structure.
889 p12 = sunw_PKCS12_create((const char *)WANBOOT_PASSPHRASE, NULL, NULL,
890 o_anchors);
891 if (p12 == NULL) {
892 goto cleanup;
894 rewind(wfp);
895 if (i2d_PKCS12_fp(wfp, p12) == 0) {
896 goto cleanup;
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);
911 if (chars > 0 && chars <= sizeof (message)) {
912 print_status(500, message);
913 } else {
914 print_status(500, NULL);
917 if (rfp != NULL) {
918 (void) fclose(rfp);
920 if (wfp != NULL) {
921 /* Will also close wfd */
922 (void) fclose(wfp);
924 if (p12 != NULL) {
925 PKCS12_free(p12);
927 if (i_anchors != NULL) {
928 sk_X509_pop_free(i_anchors, X509_free);
930 if (o_anchors != NULL) {
931 sk_X509_pop_free(o_anchors, X509_free);
934 return (ret);
937 static boolean_t
938 check_key_type(const char *keyfile, const char *keytype, int flag)
940 boolean_t ret = B_FALSE;
941 FILE *key_fp = NULL;
942 wbku_key_attr_t ka;
945 * Map keytype into the ka structure
947 if (wbku_str_to_keyattr(keytype, &ka, flag) != WBKU_SUCCESS) {
948 goto cleanup;
952 * Open the key file for reading.
954 if ((key_fp = fopen(keyfile, "r")) == NULL) {
955 goto cleanup;
959 * Find the valid client key, if it exists.
961 if (wbku_find_key(key_fp, NULL, &ka, NULL, B_FALSE) != WBKU_SUCCESS) {
962 goto cleanup;
965 ret = B_TRUE;
966 cleanup:
967 if (key_fp != NULL) {
968 (void) fclose(key_fp);
971 return (ret);
974 static boolean_t
975 resolve_hostname(const char *hostname, nvlist_t *nvl, boolean_t may_be_crap)
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];
985 * Initialize cached nodename
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);
994 (void) strcpy(myname, un.nodename);
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.
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)");
1012 return (may_be_crap);
1014 (void) memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
1015 cp = inet_ntoa(sin.sin_addr);
1018 if (nvlist_add_string(nvl, (char *)hostname, cp) != 0) {
1019 print_status(500, "(error adding hostname to nvlist)");
1020 return (B_FALSE);
1023 return (B_TRUE);
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.
1032 static boolean_t
1033 one_name(const char *namestr, nvlist_t *nvl)
1035 boolean_t ret = B_TRUE;
1036 char *p;
1037 char *q;
1038 char c;
1040 if (namestr != NULL &&
1041 (p = strstr(namestr, WBCGI_CNSTR)) != NULL) {
1042 p += WBCGI_CNSTR_LEN;
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);
1054 return (ret);
1058 * Loop through the certificates in a file
1060 static int
1061 get_hostnames(const char *path, void *nvl)
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;
1074 if ((rfp = fopen(path, "r")) == NULL) {
1075 goto cleanup;
1078 if ((p12 = d2i_PKCS12_fp(rfp, NULL)) == NULL) {
1079 errtype = 1;
1080 goto cleanup;
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;
1089 PKCS12_free(p12);
1090 p12 = NULL;
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;
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);
1111 if (chars > 0 && chars <= sizeof (message)) {
1112 print_status(500, message);
1113 } else {
1114 print_status(500, NULL);
1117 if (rfp != NULL) {
1118 (void) fclose(rfp);
1120 if (p12 != NULL) {
1121 PKCS12_free(p12);
1123 if (certs != NULL) {
1124 sk_X509_pop_free(certs, X509_free);
1127 return (ret);
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.
1134 static boolean_t
1135 create_hostsfile(const char *hostsfile, const char *net, const char *cid)
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 };
1147 * Allocate nvlist handle to store our hostname/IP pairs.
1149 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
1150 print_status(500, "(error allocating hostname nvlist)");
1151 goto cleanup;
1155 * Extract and resolve hostnames from CNs.
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;
1165 * Extract and resolve hostnames from any URLs in bootconf.
1167 for (i = 0; bc_urls[i] != NULL; ++i) {
1168 char *urlstr;
1169 url_t url;
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;
1181 * If there is a resolve-hosts list in bootconf, resolve those
1182 * hostnames too.
1184 if ((hostslist = bootconf_get(&bc_handle, BC_RESOLVE_HOSTS)) != NULL) {
1185 char *hostname;
1187 for (hostname = strtok(hostslist, ","); hostname != NULL;
1188 hostname = strtok(NULL, ",")) {
1189 if (!resolve_hostname(hostname, nvl, B_FALSE)) {
1190 goto cleanup;
1196 * Now write the hostname/IP pairs gathered to the hosts file.
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;
1204 for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
1205 nvp = nvlist_next_nvpair(nvl, nvp)) {
1206 char *hostname;
1207 char *ipstr;
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;
1215 if (fprintf(hostfp, "%s\t%s\n", ipstr, hostname) < 0) {
1216 print_status(500, "(error writing hosts file)");
1217 goto cleanup;
1221 ret = B_TRUE;
1222 cleanup:
1223 if (nvl != NULL) {
1224 nvlist_free(nvl);
1226 if (hostfp != NULL) {
1228 * hostfd is automatically closed as well.
1230 (void) fclose(hostfp);
1233 return (ret);
1236 static boolean_t
1237 bootfile_payload(const char *docroot, char **bootpathp)
1239 boolean_t ret = B_FALSE;
1240 char *boot_file;
1241 struct stat sbuf;
1243 if ((boot_file = bootconf_get(&bc_handle, BC_BOOT_FILE)) == NULL) {
1244 print_status(500, "(boot_file must be specified)");
1245 goto cleanup;
1247 if ((*bootpathp = make_path(docroot, boot_file)) == NULL) {
1248 goto cleanup;
1250 if (!WBCGI_FILE_EXISTS(*bootpathp, sbuf)) {
1251 print_status(500, "(boot_file missing)");
1252 goto cleanup;
1255 ret = B_TRUE;
1256 cleanup:
1257 return (ret);
1261 * Create the wanboot file system whose contents are determined by the
1262 * security configuration specified in bootconf.
1264 static boolean_t
1265 wanbootfs_payload(const char *net, const char *cid, const char *nonce,
1266 const char *bootconf, char **wanbootfs_imagep)
1268 int ret = B_FALSE;
1270 char *server_authentication;
1271 char *client_authentication;
1272 char *scf;
1274 char *bootfs_dir = NULL;
1275 char *bootfs_etc_dir = NULL;
1276 char *bootfs_etc_inet_dir = NULL;
1277 char *bootfs_dev_dir = NULL;
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;
1291 boolean_t authenticate_client;
1292 boolean_t authenticate_server;
1294 struct stat sbuf;
1297 * Initialize SSL stuff.
1299 sunw_crypto_init();
1302 * Get the security strategy values.
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);
1314 * Make a temporary directory structure for the wanboot file system.
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;
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;
1330 if (authenticate_client) {
1332 * Add the client private key.
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;
1342 * Add the client certificate.
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;
1352 if (authenticate_client || authenticate_server) {
1354 * Add the trustfile; at least one truststore must exist.
1356 if ((truststorepath = make_path(bootfs_dir,
1357 NB_CA_CERT)) == NULL) {
1358 goto cleanup;
1360 if (netboot_ftw(NB_CA_CERT, net, cid,
1361 noact_cb, NULL) != WBCGI_FTW_CBOK) {
1362 print_status(500, "(truststore not found)");
1364 if (netboot_ftw(NB_CA_CERT, net, cid,
1365 build_trustfile, truststorepath) == WBCGI_FTW_CBERR) {
1366 goto cleanup;
1370 * Create the /dev/urandom file.
1372 if ((urandompath = make_path(bootfs_dev_dir,
1373 "urandom")) == NULL ||
1374 !create_urandom(urandompath)) {
1375 goto cleanup;
1380 * Add the wanboot.conf(4) file.
1382 if ((bootconfpath = make_path(bootfs_dir, NB_WANBOOT_CONF)) == NULL ||
1383 !copy_file(bootconf, bootconfpath)) {
1384 goto cleanup;
1388 * Add the system_conf file if present.
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;
1396 if ((systemconfpath = make_path(bootfs_dir,
1397 NB_SYSTEM_CONF)) == NULL ||
1398 !copy_file(systemconf, systemconfpath)) {
1399 goto cleanup;
1404 * Create the /nonce file.
1406 if ((noncepath = make_path(bootfs_dir, "nonce")) == NULL ||
1407 !create_nonce(noncepath, nonce)) {
1408 goto cleanup;
1412 * Create an /etc/inet/hosts file by extracting hostnames from CN,
1413 * URLs in bootconf and resolve-hosts in bootconf.
1415 if ((hostspath = make_path(bootfs_etc_inet_dir, "hosts")) == NULL ||
1416 !create_hostsfile(hostspath, net, cid)) {
1417 goto cleanup;
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.
1425 if ((etc_hostspath = make_path(bootfs_etc_dir, "hosts")) == NULL ||
1426 !copy_file(hostspath, etc_hostspath)) {
1427 goto cleanup;
1431 * Create the /timestamp file.
1433 if ((timestamppath = make_path(bootfs_dir, "timestamp")) == NULL ||
1434 !create_timestamp(timestamppath, "timestamp")) {
1435 goto cleanup;
1439 * Create an HSFS file system for the directory.
1441 if ((*wanbootfs_imagep = gen_tmppath("wanbootfs", net, cid)) == NULL ||
1442 !mkisofs(bootfs_dir, *wanbootfs_imagep)) {
1443 goto cleanup;
1446 ret = B_TRUE;
1447 cleanup:
1449 * Clean up temporary files and directories.
1451 if (keystorepath != NULL &&
1452 WBCGI_FILE_EXISTS(keystorepath, sbuf)) {
1453 (void) unlink(keystorepath);
1455 if (certstorepath != NULL &&
1456 WBCGI_FILE_EXISTS(certstorepath, sbuf)) {
1457 (void) unlink(certstorepath);
1459 if (truststorepath != NULL &&
1460 WBCGI_FILE_EXISTS(truststorepath, sbuf)) {
1461 (void) unlink(truststorepath);
1463 if (bootconfpath != NULL &&
1464 WBCGI_FILE_EXISTS(bootconfpath, sbuf)) {
1465 (void) unlink(bootconfpath);
1467 if (systemconfpath != NULL &&
1468 WBCGI_FILE_EXISTS(systemconfpath, sbuf)) {
1469 (void) unlink(systemconfpath);
1471 if (urandompath != NULL &&
1472 WBCGI_FILE_EXISTS(urandompath, sbuf)) {
1473 (void) unlink(urandompath);
1475 if (noncepath != NULL &&
1476 WBCGI_FILE_EXISTS(noncepath, sbuf)) {
1477 (void) unlink(noncepath);
1479 if (hostspath != NULL &&
1480 WBCGI_FILE_EXISTS(hostspath, sbuf)) {
1481 (void) unlink(hostspath);
1483 if (etc_hostspath != NULL &&
1484 WBCGI_FILE_EXISTS(etc_hostspath, sbuf)) {
1485 (void) unlink(etc_hostspath);
1487 if (timestamppath != NULL &&
1488 WBCGI_FILE_EXISTS(timestamppath, sbuf)) {
1489 (void) unlink(timestamppath);
1492 if (bootfs_etc_inet_dir != NULL &&
1493 WBCGI_DIR_EXISTS(bootfs_etc_inet_dir, sbuf)) {
1494 (void) rmdir(bootfs_etc_inet_dir);
1496 if (bootfs_etc_dir != NULL &&
1497 WBCGI_DIR_EXISTS(bootfs_etc_dir, sbuf)) {
1498 (void) rmdir(bootfs_etc_dir);
1500 if (bootfs_dev_dir != NULL &&
1501 WBCGI_DIR_EXISTS(bootfs_dev_dir, sbuf)) {
1502 (void) rmdir(bootfs_dev_dir);
1504 if (bootfs_dir != NULL &&
1505 WBCGI_DIR_EXISTS(bootfs_dir, sbuf)) {
1506 (void) rmdir(bootfs_dir);
1510 * Free allocated memory.
1512 free_path(&bootfs_dir);
1513 free_path(&bootfs_etc_dir);
1514 free_path(&bootfs_etc_inet_dir);
1515 free_path(&bootfs_dev_dir);
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);
1529 return (ret);
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)
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;
1545 if ((root_server = bootconf_get(&bc_handle, BC_ROOT_SERVER)) == NULL) {
1546 print_status(500, "(root_server must be specified)");
1547 goto cleanup;
1549 if (url_parse(root_server, &url) != URL_PARSE_SUCCESS) {
1550 print_status(500, "(root_server URL is invalid)");
1552 *https_rootserverp = url.https;
1554 if ((root_file = bootconf_get(&bc_handle, BC_ROOT_FILE)) == NULL) {
1555 print_status(500, "(rootfile must be specified)");
1556 goto cleanup;
1558 if ((*rootpathp = make_path(docroot, root_file)) == NULL) {
1559 goto cleanup;
1561 if (!WBCGI_FILE_EXISTS(*rootpathp, sbuf)) {
1562 print_status(500, "(root filesystem image missing)");
1563 goto cleanup;
1566 if ((*rootinfop = gen_tmppath("mrinfo", net, cid)) == NULL) {
1567 goto cleanup;
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;
1578 ret = B_TRUE;
1579 cleanup:
1580 if (fd != -1) {
1581 (void) close(fd);
1584 return (ret);
1587 static boolean_t
1588 deliver_payload(const char *payload, const char *payload_hash)
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;
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);
1607 * Multi-part header.
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);
1618 * Multi-part header for part one.
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);
1628 * Multi-part header for part two.
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);
1638 * End-of-parts Trailer.
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);
1648 * Message header.
1650 msglen = payload_buf.st_size + hash_buf.st_size +
1651 strlen(multi_header1) + strlen(multi_header2) + strlen(multi_end);
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);
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.
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);
1674 return (B_TRUE);
1678 /*ARGSUSED*/
1680 main(int argc, char **argv)
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;
1703 * Process the query string.
1705 if (!get_request_info(&content, &net, &cid, &nonce, &docroot)) {
1706 goto cleanup;
1710 * Sanity check that the netboot directory exists.
1712 if (!WBCGI_DIR_EXISTS(NB_NETBOOT_ROOT, sbuf)) {
1713 print_status(500, "(" NB_NETBOOT_ROOT " does not exist)");
1714 goto cleanup;
1718 * Get absolute bootconf pathname.
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;
1727 * Initialize bc_handle from the given wanboot.conf file.
1729 if (bootconf_init(&bc_handle, bootconf) != BC_SUCCESS) {
1730 char message[WBCGI_MAXBUF];
1731 int chars;
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;
1743 * Get and check signature and encryption types,
1744 * presence of helper utilities, keystore, etc.
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;
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;
1757 if (!check_key_type(keyfile, signature_type, WBKU_HASH_KEY)) {
1758 print_status(500, "(hash key not found)");
1759 goto cleanup;
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;
1768 if (!WBCGI_FILE_EXISTS(WBCGI_ENCR_PATH, sbuf)) {
1769 print_status(500, "(encr utility not found)");
1770 goto cleanup;
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;
1777 if (!check_key_type(keyfile, encryption_type, WBKU_ENCR_KEY)) {
1778 print_status(500, "(encr key not found)");
1779 goto cleanup;
1784 * Determine/create our payload.
1786 switch (content) {
1787 case WBCGI_CONTENT_BOOTFILE:
1788 if (!bootfile_payload(docroot, &bootpath)) {
1789 goto cleanup;
1791 payload = bootpath;
1793 break;
1795 case WBCGI_CONTENT_BOOTFS:
1796 if (!wanbootfs_payload(net, cid, nonce,
1797 bootconf, &wanbootfs_image)) {
1798 goto cleanup;
1800 payload = wanbootfs_image;
1802 break;
1804 case WBCGI_CONTENT_ROOTFS:
1805 if (!miniroot_payload(net, cid, docroot,
1806 &rootpath, &miniroot_info, &https_rootserver)) {
1807 goto cleanup;
1809 payload = rootpath;
1811 break;
1815 * Encrypt the payload if necessary.
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;
1824 if (!encrypt_payload(payload, encr_payload, keyfile,
1825 encryption_type)) {
1826 goto cleanup;
1829 payload = encr_payload;
1833 * Compute the hash (actual or null).
1835 if ((payload_hash = gen_tmppath("hash", net, cid)) == NULL) {
1836 goto cleanup;
1839 if (signature_type != NULL &&
1840 (content != WBCGI_CONTENT_ROOTFS || !https_rootserver)) {
1841 if (!hash_payload(payload, payload_hash, keyfile)) {
1842 goto cleanup;
1844 } else {
1845 if (!create_null_hash(payload_hash)) {
1846 goto cleanup;
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.
1855 if (content == WBCGI_CONTENT_ROOTFS) {
1856 payload = miniroot_info;
1860 * Finally, deliver the payload and hash as a multipart message.
1862 if (!deliver_payload(payload, payload_hash)) {
1863 goto cleanup;
1866 ret = WBCGI_STATUS_OK;
1867 cleanup:
1869 * Clean up temporary files.
1871 if (wanbootfs_image != NULL &&
1872 WBCGI_FILE_EXISTS(wanbootfs_image, sbuf)) {
1873 (void) unlink(wanbootfs_image);
1875 if (miniroot_info != NULL &&
1876 WBCGI_FILE_EXISTS(miniroot_info, sbuf)) {
1877 (void) unlink(miniroot_info);
1879 if (encr_payload != NULL &&
1880 WBCGI_FILE_EXISTS(encr_payload, sbuf)) {
1881 (void) unlink(encr_payload);
1883 if (payload_hash != NULL &&
1884 WBCGI_FILE_EXISTS(payload_hash, sbuf)) {
1885 (void) unlink(payload_hash);
1889 * Free up any allocated strings.
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);
1900 bootconf_end(&bc_handle);
1902 return (ret);