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