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