xref: /onnv-gate/usr/src/psm/stand/boot/sparc/common/wanboot.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include <sys/types.h>
30*0Sstevel@tonic-gate /* EXPORT DELETE START */
31*0Sstevel@tonic-gate #include <sys/promif.h>
32*0Sstevel@tonic-gate #include <sys/obpdefs.h>
33*0Sstevel@tonic-gate #include <sys/bootvfs.h>
34*0Sstevel@tonic-gate #include <sys/bootconf.h>
35*0Sstevel@tonic-gate #include <netinet/in.h>
36*0Sstevel@tonic-gate #include <sys/wanboot_impl.h>
37*0Sstevel@tonic-gate #include <boot_http.h>
38*0Sstevel@tonic-gate #include <aes.h>
39*0Sstevel@tonic-gate #include <des3.h>
40*0Sstevel@tonic-gate #include <cbc.h>
41*0Sstevel@tonic-gate #include <hmac_sha1.h>
42*0Sstevel@tonic-gate #include <sys/sha1.h>
43*0Sstevel@tonic-gate #include <sys/sha1_consts.h>
44*0Sstevel@tonic-gate #include <bootlog.h>
45*0Sstevel@tonic-gate #include <parseURL.h>
46*0Sstevel@tonic-gate #include <netboot_paths.h>
47*0Sstevel@tonic-gate #include <netinet/inetutil.h>
48*0Sstevel@tonic-gate #include <sys/salib.h>
49*0Sstevel@tonic-gate #include <inet/mac.h>
50*0Sstevel@tonic-gate #include <inet/ipv4.h>
51*0Sstevel@tonic-gate #include <dhcp_impl.h>
52*0Sstevel@tonic-gate #include <inet/dhcpv4.h>
53*0Sstevel@tonic-gate #include <bootinfo.h>
54*0Sstevel@tonic-gate #include <wanboot_conf.h>
55*0Sstevel@tonic-gate #include "boot_plat.h"
56*0Sstevel@tonic-gate #include "ramdisk.h"
57*0Sstevel@tonic-gate #include "wbcli.h"
58*0Sstevel@tonic-gate 
59*0Sstevel@tonic-gate /*
60*0Sstevel@tonic-gate  * Types of downloads
61*0Sstevel@tonic-gate  */
62*0Sstevel@tonic-gate #define	MINIINFO	"miniinfo"
63*0Sstevel@tonic-gate #define	MINIROOT	"miniroot"
64*0Sstevel@tonic-gate #define	WANBOOTFS	"wanbootfs"
65*0Sstevel@tonic-gate 
66*0Sstevel@tonic-gate #define	WANBOOT_RETRY_NOMAX	-1
67*0Sstevel@tonic-gate #define	WANBOOT_RETRY_ROOT_MAX	50
68*0Sstevel@tonic-gate #define	WANBOOT_RETRY_MAX	5
69*0Sstevel@tonic-gate #define	WANBOOT_RETRY_SECS	5
70*0Sstevel@tonic-gate #define	WANBOOT_RETRY_MAX_SECS	30
71*0Sstevel@tonic-gate 
72*0Sstevel@tonic-gate /*
73*0Sstevel@tonic-gate  * Our read requests should timeout after 25 seconds
74*0Sstevel@tonic-gate  */
75*0Sstevel@tonic-gate #define	SOCKET_READ_TIMEOUT	25
76*0Sstevel@tonic-gate 
77*0Sstevel@tonic-gate /*
78*0Sstevel@tonic-gate  * Experimentation has shown that an 8K download buffer is optimal
79*0Sstevel@tonic-gate  */
80*0Sstevel@tonic-gate static char	buffer[8192];
81*0Sstevel@tonic-gate 
82*0Sstevel@tonic-gate bc_handle_t	bc_handle;
83*0Sstevel@tonic-gate 
84*0Sstevel@tonic-gate extern int	determine_fstype_and_mountroot(char *);
85*0Sstevel@tonic-gate extern uint64_t	get_ticks(void);
86*0Sstevel@tonic-gate 
87*0Sstevel@tonic-gate /*
88*0Sstevel@tonic-gate  * The following is used to determine whether the certs and private key
89*0Sstevel@tonic-gate  * files will be in PEM format or PKCS12 format.  'use_p12' is zero
90*0Sstevel@tonic-gate  * to use PEM format, and 1 when PKCS12 format is to be used.  It is
91*0Sstevel@tonic-gate  * done this way, as a global, so that it can be patched if needs be
92*0Sstevel@tonic-gate  * using the OBP debugger.
93*0Sstevel@tonic-gate  */
94*0Sstevel@tonic-gate uint32_t	use_p12 = 1;
95*0Sstevel@tonic-gate 
96*0Sstevel@tonic-gate #define	CONTENT_LENGTH		"Content-Length"
97*0Sstevel@tonic-gate 
98*0Sstevel@tonic-gate #define	NONCELEN	(2 * HMAC_DIGEST_LEN) /* two hex nibbles/byte */
99*0Sstevel@tonic-gate #define	WANBOOTFS_NONCE_FILE	"/nonce"
100*0Sstevel@tonic-gate 
101*0Sstevel@tonic-gate static char nonce[NONCELEN + 1];
102*0Sstevel@tonic-gate 
103*0Sstevel@tonic-gate enum URLtype {
104*0Sstevel@tonic-gate 	URLtype_wanbootfs = 0,
105*0Sstevel@tonic-gate 	URLtype_miniroot = 1
106*0Sstevel@tonic-gate };
107*0Sstevel@tonic-gate 
108*0Sstevel@tonic-gate static char *URLtoCGIcontent[] = {
109*0Sstevel@tonic-gate 	"bootfs",
110*0Sstevel@tonic-gate 	"rootfs"
111*0Sstevel@tonic-gate };
112*0Sstevel@tonic-gate #define	CGIcontent(urltype)	URLtoCGIcontent[urltype]
113*0Sstevel@tonic-gate 
114*0Sstevel@tonic-gate /* Encryption algorithms */
115*0Sstevel@tonic-gate typedef enum {
116*0Sstevel@tonic-gate 	ENCR_NONE,
117*0Sstevel@tonic-gate 	ENCR_3DES,
118*0Sstevel@tonic-gate 	ENCR_AES
119*0Sstevel@tonic-gate } encr_type_t;
120*0Sstevel@tonic-gate 
121*0Sstevel@tonic-gate /* Hash algorithms */
122*0Sstevel@tonic-gate typedef enum {
123*0Sstevel@tonic-gate 	HASH_NONE,
124*0Sstevel@tonic-gate 	HASH_HMAC_SHA1
125*0Sstevel@tonic-gate } hash_type_t;
126*0Sstevel@tonic-gate 
127*0Sstevel@tonic-gate /*
128*0Sstevel@tonic-gate  * Keys ...
129*0Sstevel@tonic-gate  */
130*0Sstevel@tonic-gate static encr_type_t	encr_type = ENCR_NONE;
131*0Sstevel@tonic-gate static unsigned char	*g_encr_key = NULL;
132*0Sstevel@tonic-gate 
133*0Sstevel@tonic-gate static hash_type_t	hash_type = HASH_NONE;
134*0Sstevel@tonic-gate static unsigned char	*g_hash_key = NULL;
135*0Sstevel@tonic-gate 
136*0Sstevel@tonic-gate void
137*0Sstevel@tonic-gate print_errors(const char *func, http_handle_t handle)
138*0Sstevel@tonic-gate {
139*0Sstevel@tonic-gate 	char const *msg;
140*0Sstevel@tonic-gate 	ulong_t err;
141*0Sstevel@tonic-gate 	uint_t src;
142*0Sstevel@tonic-gate 
143*0Sstevel@tonic-gate 	while ((err = http_get_lasterr(handle, &src)) != 0) {
144*0Sstevel@tonic-gate 		msg = http_errorstr(src, err);
145*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_ALERT,
146*0Sstevel@tonic-gate 		    "%s: errsrc %u, err %lu (0x%lx)", func, src, err, err);
147*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_ALERT, "%s", msg);
148*0Sstevel@tonic-gate 	}
149*0Sstevel@tonic-gate }
150*0Sstevel@tonic-gate 
151*0Sstevel@tonic-gate /*
152*0Sstevel@tonic-gate  * This routine is called by a consumer to determine whether or not a
153*0Sstevel@tonic-gate  * retry should be attempted. If a retry is in order (depends upon the
154*0Sstevel@tonic-gate  * 'retry_cnt' and 'retry_max' arguments), then this routine will print a
155*0Sstevel@tonic-gate  * message indicating this is the case and will determine an appropriate
156*0Sstevel@tonic-gate  * "sleep" time before retrying. The "sleep" time will depend upon the
157*0Sstevel@tonic-gate  * 'retry_cnt' and will max out at WANBOOT_RETRY_MAX_SECS.
158*0Sstevel@tonic-gate  *
159*0Sstevel@tonic-gate  * Returns:
160*0Sstevel@tonic-gate  *	 B_TRUE  = retry is in order
161*0Sstevel@tonic-gate  *	 B_FALSE = retry limit exceeded
162*0Sstevel@tonic-gate  */
163*0Sstevel@tonic-gate boolean_t
164*0Sstevel@tonic-gate wanboot_retry(int retry_cnt, int retry_max)
165*0Sstevel@tonic-gate {
166*0Sstevel@tonic-gate 	unsigned int seconds;
167*0Sstevel@tonic-gate 
168*0Sstevel@tonic-gate 	if (retry_max == WANBOOT_RETRY_NOMAX || retry_cnt <= retry_max) {
169*0Sstevel@tonic-gate 		seconds = WANBOOT_RETRY_SECS * retry_cnt;
170*0Sstevel@tonic-gate 		if (seconds > WANBOOT_RETRY_MAX_SECS) {
171*0Sstevel@tonic-gate 			seconds = WANBOOT_RETRY_MAX_SECS;
172*0Sstevel@tonic-gate 		}
173*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_INFO,
174*0Sstevel@tonic-gate 		    "Will retry in %d seconds ...", seconds);
175*0Sstevel@tonic-gate 		(void) sleep(seconds);
176*0Sstevel@tonic-gate 		return (B_TRUE);
177*0Sstevel@tonic-gate 	} else {
178*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_INFO,
179*0Sstevel@tonic-gate 		    "Maximum retries exceeded.");
180*0Sstevel@tonic-gate 		return (B_FALSE);
181*0Sstevel@tonic-gate 	}
182*0Sstevel@tonic-gate }
183*0Sstevel@tonic-gate 
184*0Sstevel@tonic-gate /*
185*0Sstevel@tonic-gate  * Determine which encryption algorithm the client is configured to use.
186*0Sstevel@tonic-gate  * WAN boot determines which key to use by order of priority.  That is
187*0Sstevel@tonic-gate  * multiple encryption keys may exist in the PROM, but the first one found
188*0Sstevel@tonic-gate  * (while searching in a preferred order) is the one that will be used.
189*0Sstevel@tonic-gate  */
190*0Sstevel@tonic-gate static void
191*0Sstevel@tonic-gate init_encryption(void)
192*0Sstevel@tonic-gate {
193*0Sstevel@tonic-gate 	static unsigned char	key[WANBOOT_MAXKEYLEN];
194*0Sstevel@tonic-gate 	size_t			len = sizeof (key);
195*0Sstevel@tonic-gate 
196*0Sstevel@tonic-gate 	if (bootinfo_get(BI_AES_KEY, (char *)&key, &len, NULL) ==
197*0Sstevel@tonic-gate 	    BI_E_SUCCESS) {
198*0Sstevel@tonic-gate 		encr_type = ENCR_AES;
199*0Sstevel@tonic-gate 		g_encr_key = key;
200*0Sstevel@tonic-gate 	} else if (bootinfo_get(BI_3DES_KEY, (char *)&key, &len, NULL) ==
201*0Sstevel@tonic-gate 	    BI_E_SUCCESS) {
202*0Sstevel@tonic-gate 		encr_type = ENCR_3DES;
203*0Sstevel@tonic-gate 		g_encr_key = key;
204*0Sstevel@tonic-gate 	}
205*0Sstevel@tonic-gate }
206*0Sstevel@tonic-gate 
207*0Sstevel@tonic-gate /*
208*0Sstevel@tonic-gate  * Determine whether the client is configured to use hashing.
209*0Sstevel@tonic-gate  */
210*0Sstevel@tonic-gate static void
211*0Sstevel@tonic-gate init_hashing(void)
212*0Sstevel@tonic-gate {
213*0Sstevel@tonic-gate 	static unsigned char	key[WANBOOT_HMAC_KEY_SIZE];
214*0Sstevel@tonic-gate 	size_t			len = sizeof (key);
215*0Sstevel@tonic-gate 
216*0Sstevel@tonic-gate 	if (bootinfo_get(BI_SHA1_KEY, (char *)&key, &len, NULL) ==
217*0Sstevel@tonic-gate 	    BI_E_SUCCESS) {
218*0Sstevel@tonic-gate 		hash_type = HASH_HMAC_SHA1;
219*0Sstevel@tonic-gate 		g_hash_key = key;
220*0Sstevel@tonic-gate 	}
221*0Sstevel@tonic-gate }
222*0Sstevel@tonic-gate 
223*0Sstevel@tonic-gate /*
224*0Sstevel@tonic-gate  * Read some CPU-specific rapidly-varying data (assumed to be of length
225*0Sstevel@tonic-gate  * sizeof (hrtime_t) in the non-SPARC case), and digestify it to further
226*0Sstevel@tonic-gate  * randomize the output.
227*0Sstevel@tonic-gate  */
228*0Sstevel@tonic-gate char *
229*0Sstevel@tonic-gate generate_nonce(void)
230*0Sstevel@tonic-gate {
231*0Sstevel@tonic-gate 	uint64_t	t;
232*0Sstevel@tonic-gate 	SHA1_CTX	c;
233*0Sstevel@tonic-gate 	unsigned char	digest[HMAC_DIGEST_LEN];
234*0Sstevel@tonic-gate 	uint_t		nlen = sizeof (nonce);
235*0Sstevel@tonic-gate 
236*0Sstevel@tonic-gate 	int		err;
237*0Sstevel@tonic-gate 
238*0Sstevel@tonic-gate 	/*
239*0Sstevel@tonic-gate 	 * Read SPARC %tick register or x86 TSC
240*0Sstevel@tonic-gate 	 */
241*0Sstevel@tonic-gate 	t = get_ticks();
242*0Sstevel@tonic-gate 	SHA1Init(&c);
243*0Sstevel@tonic-gate 	SHA1Update(&c, (const uint8_t *)&t, sizeof (t));
244*0Sstevel@tonic-gate 	SHA1Final(digest, &c);
245*0Sstevel@tonic-gate 
246*0Sstevel@tonic-gate 	err = octet_to_hexascii(digest, sizeof (digest), nonce, &nlen);
247*0Sstevel@tonic-gate 	if (err != 0) {
248*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
249*0Sstevel@tonic-gate 		    "cannot convert nonce to ASCII: error %d", err);
250*0Sstevel@tonic-gate 		return (NULL);
251*0Sstevel@tonic-gate 	}
252*0Sstevel@tonic-gate 	nonce[NONCELEN] = '\0';
253*0Sstevel@tonic-gate 	return (nonce);
254*0Sstevel@tonic-gate }
255*0Sstevel@tonic-gate 
256*0Sstevel@tonic-gate /*
257*0Sstevel@tonic-gate  * Given a server URL, builds a URL to request one of the wanboot
258*0Sstevel@tonic-gate  * datastreams.
259*0Sstevel@tonic-gate  *
260*0Sstevel@tonic-gate  * Returns:
261*0Sstevel@tonic-gate  *	-1 = Non-recoverable error
262*0Sstevel@tonic-gate  *	 0 = Success
263*0Sstevel@tonic-gate  */
264*0Sstevel@tonic-gate static int
265*0Sstevel@tonic-gate build_request_url(url_t *req_url, enum URLtype ut, const url_t *server_url)
266*0Sstevel@tonic-gate {
267*0Sstevel@tonic-gate 	char		clid[WB_MAX_CID_LEN];
268*0Sstevel@tonic-gate 	size_t		clen;
269*0Sstevel@tonic-gate 	char		wid[WB_MAX_CID_LEN * 2 + 1];
270*0Sstevel@tonic-gate 	uint_t		wlen;
271*0Sstevel@tonic-gate 	struct in_addr	ip;
272*0Sstevel@tonic-gate 	struct in_addr	mask;
273*0Sstevel@tonic-gate 	char		*netstr;
274*0Sstevel@tonic-gate 	char		*ppath;
275*0Sstevel@tonic-gate 	size_t		plen;
276*0Sstevel@tonic-gate 	const char	reqstr[] = "/?CONTENT=%s&IP=%s&CID=%s";
277*0Sstevel@tonic-gate 
278*0Sstevel@tonic-gate 	/*
279*0Sstevel@tonic-gate 	 * Initialize the request
280*0Sstevel@tonic-gate 	 */
281*0Sstevel@tonic-gate 	*req_url = *server_url;
282*0Sstevel@tonic-gate 
283*0Sstevel@tonic-gate 	/*
284*0Sstevel@tonic-gate 	 * Build the network number string
285*0Sstevel@tonic-gate 	 */
286*0Sstevel@tonic-gate 	ipv4_getipaddr(&ip);
287*0Sstevel@tonic-gate 	ipv4_getnetmask(&mask);
288*0Sstevel@tonic-gate 	ip.s_addr = ip.s_addr & mask.s_addr;
289*0Sstevel@tonic-gate 	netstr = inet_ntoa(ip);
290*0Sstevel@tonic-gate 
291*0Sstevel@tonic-gate 	/*
292*0Sstevel@tonic-gate 	 * Get the wan id
293*0Sstevel@tonic-gate 	 */
294*0Sstevel@tonic-gate 	clen = sizeof (clid);
295*0Sstevel@tonic-gate 	if (bootinfo_get(BI_CLIENT_ID, clid, &clen, NULL) != BI_E_SUCCESS) {
296*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
297*0Sstevel@tonic-gate 		    "Cannot retrieve the client ID");
298*0Sstevel@tonic-gate 		return (-1);
299*0Sstevel@tonic-gate 	}
300*0Sstevel@tonic-gate 	wlen = sizeof (wid);
301*0Sstevel@tonic-gate 	(void) octet_to_hexascii(clid, clen, wid, &wlen);
302*0Sstevel@tonic-gate 
303*0Sstevel@tonic-gate 	/*
304*0Sstevel@tonic-gate 	 * Build the request, making sure that the length of the
305*0Sstevel@tonic-gate 	 * constructed URL falls within the supported maximum.
306*0Sstevel@tonic-gate 	 */
307*0Sstevel@tonic-gate 	plen = strlen(req_url->abspath);
308*0Sstevel@tonic-gate 	ppath = req_url->abspath + plen;
309*0Sstevel@tonic-gate 	if (snprintf(ppath, URL_MAX_PATHLEN - plen, reqstr,
310*0Sstevel@tonic-gate 	    CGIcontent(ut), netstr, wid) >= URL_MAX_PATHLEN - plen) {
311*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
312*0Sstevel@tonic-gate 		    "The URL path length of the %s request is greater than "
313*0Sstevel@tonic-gate 		    "the maximum of %d", CGIcontent(ut), URL_MAX_PATHLEN);
314*0Sstevel@tonic-gate 		return (-1);
315*0Sstevel@tonic-gate 	}
316*0Sstevel@tonic-gate 
317*0Sstevel@tonic-gate 	/*
318*0Sstevel@tonic-gate 	 * If the URL type requires a nonce, then supply it.
319*0Sstevel@tonic-gate 	 * It will be returned in the reply to detect attempted
320*0Sstevel@tonic-gate 	 * replays.
321*0Sstevel@tonic-gate 	 */
322*0Sstevel@tonic-gate 	if (ut == URLtype_wanbootfs) {
323*0Sstevel@tonic-gate 		char	*n = generate_nonce();
324*0Sstevel@tonic-gate 
325*0Sstevel@tonic-gate 		if (n != NULL) {
326*0Sstevel@tonic-gate 			plen += strlen("&NONCE=") + NONCELEN;
327*0Sstevel@tonic-gate 			if (plen > URL_MAX_PATHLEN)
328*0Sstevel@tonic-gate 				return (-1);
329*0Sstevel@tonic-gate 			(void) strcat(req_url->abspath, "&NONCE=");
330*0Sstevel@tonic-gate 			(void) strcat(req_url->abspath, n);
331*0Sstevel@tonic-gate 		}
332*0Sstevel@tonic-gate 	}
333*0Sstevel@tonic-gate 
334*0Sstevel@tonic-gate 	return (0);
335*0Sstevel@tonic-gate }
336*0Sstevel@tonic-gate 
337*0Sstevel@tonic-gate /*
338*0Sstevel@tonic-gate  * This routine reads data from an HTTP connection into a buffer.
339*0Sstevel@tonic-gate  *
340*0Sstevel@tonic-gate  * Returns:
341*0Sstevel@tonic-gate  *	 0 = Success
342*0Sstevel@tonic-gate  *	 1 = HTTP download error
343*0Sstevel@tonic-gate  */
344*0Sstevel@tonic-gate static int
345*0Sstevel@tonic-gate read_bytes(http_handle_t handle, char *buffer, size_t cnt)
346*0Sstevel@tonic-gate {
347*0Sstevel@tonic-gate 	int len;
348*0Sstevel@tonic-gate 	size_t i;
349*0Sstevel@tonic-gate 
350*0Sstevel@tonic-gate 	for (i = 0; i < cnt; i += len) {
351*0Sstevel@tonic-gate 		len = http_read_body(handle, &buffer[i], cnt - i);
352*0Sstevel@tonic-gate 		if (len <= 0) {
353*0Sstevel@tonic-gate 			print_errors("http_read_body", handle);
354*0Sstevel@tonic-gate 			return (1);
355*0Sstevel@tonic-gate 		}
356*0Sstevel@tonic-gate 	}
357*0Sstevel@tonic-gate 	return (0);
358*0Sstevel@tonic-gate }
359*0Sstevel@tonic-gate 
360*0Sstevel@tonic-gate /*
361*0Sstevel@tonic-gate  * This routine compares two hash digests, one computed by the server and
362*0Sstevel@tonic-gate  * the other computed by the client to verify that a transmitted message
363*0Sstevel@tonic-gate  * was received without corruption.
364*0Sstevel@tonic-gate  *
365*0Sstevel@tonic-gate  * Notes:
366*0Sstevel@tonic-gate  *	The client only computes a digest if it is configured with a
367*0Sstevel@tonic-gate  *	hash key. If it is not, then the server should not have a hash
368*0Sstevel@tonic-gate  *	key for the client either and therefore should have sent a
369*0Sstevel@tonic-gate  *	zero filled digest.
370*0Sstevel@tonic-gate  *
371*0Sstevel@tonic-gate  * Returns:
372*0Sstevel@tonic-gate  *	 B_TRUE  = digest was verified
373*0Sstevel@tonic-gate  *	 B_FALSE = digest did not verify
374*0Sstevel@tonic-gate  */
375*0Sstevel@tonic-gate static boolean_t
376*0Sstevel@tonic-gate verify_digests(const char *what, unsigned char *cdigest, unsigned char *sdigest)
377*0Sstevel@tonic-gate {
378*0Sstevel@tonic-gate 	static char	null_digest[HMAC_DIGEST_LEN];
379*0Sstevel@tonic-gate 
380*0Sstevel@tonic-gate 	if (bcmp(sdigest, cdigest, HMAC_DIGEST_LEN) != 0) {
381*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
382*0Sstevel@tonic-gate 		    "%s: invalid hash digest", what);
383*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
384*0Sstevel@tonic-gate 		    "This may signify a client/server key mismatch");
385*0Sstevel@tonic-gate 		if (bcmp(sdigest, null_digest, HMAC_DIGEST_LEN) == 0) {
386*0Sstevel@tonic-gate 			bootlog("wanboot", BOOTLOG_CRIT,
387*0Sstevel@tonic-gate 			    "(client has key but wrong signature_type?)");
388*0Sstevel@tonic-gate 		} else if (bcmp(cdigest, null_digest, HMAC_DIGEST_LEN) == 0) {
389*0Sstevel@tonic-gate 			bootlog("wanboot", BOOTLOG_CRIT,
390*0Sstevel@tonic-gate 			    "(signature_type specified but no client key?)");
391*0Sstevel@tonic-gate 		}
392*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
393*0Sstevel@tonic-gate 		    "or possible corruption of the image in transit");
394*0Sstevel@tonic-gate 		return (B_FALSE);
395*0Sstevel@tonic-gate 	}
396*0Sstevel@tonic-gate 
397*0Sstevel@tonic-gate 	return (B_TRUE);
398*0Sstevel@tonic-gate }
399*0Sstevel@tonic-gate 
400*0Sstevel@tonic-gate /*
401*0Sstevel@tonic-gate  * This routine reads the part of a multipart message that contains a
402*0Sstevel@tonic-gate  * hash digest. Errors in reading the digest are differentiated from
403*0Sstevel@tonic-gate  * other kinds of errors so that the caller can decide whether or
404*0Sstevel@tonic-gate  * not a retry is worthwhile.
405*0Sstevel@tonic-gate  *
406*0Sstevel@tonic-gate  * Note:
407*0Sstevel@tonic-gate  *	The hash digest can either be an HMAC digest or it can be
408*0Sstevel@tonic-gate  *	a zero length message (representing no hash digest).
409*0Sstevel@tonic-gate  *
410*0Sstevel@tonic-gate  * Returns:
411*0Sstevel@tonic-gate  *	-1 = Non-recoverable error
412*0Sstevel@tonic-gate  *	 0 = Success
413*0Sstevel@tonic-gate  *	 1 = HTTP download error
414*0Sstevel@tonic-gate  */
415*0Sstevel@tonic-gate static int
416*0Sstevel@tonic-gate read_digest(const char *what, http_handle_t handle, unsigned char *sdigest)
417*0Sstevel@tonic-gate {
418*0Sstevel@tonic-gate 	char *lenstr;
419*0Sstevel@tonic-gate 	size_t digest_size;
420*0Sstevel@tonic-gate 
421*0Sstevel@tonic-gate 	/*
422*0Sstevel@tonic-gate 	 * Process the HMAC digest header.
423*0Sstevel@tonic-gate 	 */
424*0Sstevel@tonic-gate 	if (http_process_part_headers(handle, NULL) != 0) {
425*0Sstevel@tonic-gate 		print_errors("http_process_part_headers", handle);
426*0Sstevel@tonic-gate 		return (1);
427*0Sstevel@tonic-gate 	}
428*0Sstevel@tonic-gate 	lenstr = http_get_header_value(handle, CONTENT_LENGTH);
429*0Sstevel@tonic-gate 	if (lenstr == NULL) {
430*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_ALERT,
431*0Sstevel@tonic-gate 			"%s: error getting digest length", what);
432*0Sstevel@tonic-gate 		return (1);
433*0Sstevel@tonic-gate 	}
434*0Sstevel@tonic-gate 	digest_size = (size_t)strtol(lenstr, NULL, 10);
435*0Sstevel@tonic-gate 	free(lenstr);
436*0Sstevel@tonic-gate 
437*0Sstevel@tonic-gate 	/*
438*0Sstevel@tonic-gate 	 * Validate the HMAC digest length.
439*0Sstevel@tonic-gate 	 */
440*0Sstevel@tonic-gate 	if (digest_size != HMAC_DIGEST_LEN) {
441*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
442*0Sstevel@tonic-gate 			"%s: error validating response - invalid digest size",
443*0Sstevel@tonic-gate 			what);
444*0Sstevel@tonic-gate 		return (-1);
445*0Sstevel@tonic-gate 	}
446*0Sstevel@tonic-gate 
447*0Sstevel@tonic-gate 	/*
448*0Sstevel@tonic-gate 	 * Read the HMAC digest.
449*0Sstevel@tonic-gate 	 */
450*0Sstevel@tonic-gate 	if (read_bytes(handle, (char *)sdigest, digest_size) != 0) {
451*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_ALERT,
452*0Sstevel@tonic-gate 			"%s: error reading digest", what);
453*0Sstevel@tonic-gate 		return (1);
454*0Sstevel@tonic-gate 	}
455*0Sstevel@tonic-gate 
456*0Sstevel@tonic-gate 	return (0);
457*0Sstevel@tonic-gate }
458*0Sstevel@tonic-gate 
459*0Sstevel@tonic-gate /*
460*0Sstevel@tonic-gate  * This routine reads data from an HTTP connection and writes the data
461*0Sstevel@tonic-gate  * to a ramdisk. It also, optionally computes a hash digest of the processed
462*0Sstevel@tonic-gate  * data. This routine may be called to continue writing a previously aborted
463*0Sstevel@tonic-gate  * write. If this is the case, then the offset will be non-zero and the write
464*0Sstevel@tonic-gate  * pointer into the ramdisk will be positioned correctly by the caller.
465*0Sstevel@tonic-gate  *
466*0Sstevel@tonic-gate  * Returns:
467*0Sstevel@tonic-gate  *	-1 = Non-recoverable error
468*0Sstevel@tonic-gate  *	 0 = Success
469*0Sstevel@tonic-gate  *	 1 = HTTP download error
470*0Sstevel@tonic-gate  */
471*0Sstevel@tonic-gate static int
472*0Sstevel@tonic-gate write_msg_to_ramdisk(const char *what, int fd, http_handle_t handle,
473*0Sstevel@tonic-gate     size_t ramdisk_size, off_t *offset, SHA1_CTX *sha)
474*0Sstevel@tonic-gate {
475*0Sstevel@tonic-gate 	int len;
476*0Sstevel@tonic-gate 	long nleft;
477*0Sstevel@tonic-gate 	static int bootlog_message_interval;
478*0Sstevel@tonic-gate 	static int bootlog_progress;
479*0Sstevel@tonic-gate 	int ret;
480*0Sstevel@tonic-gate 
481*0Sstevel@tonic-gate 	/*
482*0Sstevel@tonic-gate 	 * Read the data and write it to the ramdisk.
483*0Sstevel@tonic-gate 	 */
484*0Sstevel@tonic-gate 	if (*offset == 0) {
485*0Sstevel@tonic-gate 		bootlog_progress = 0;
486*0Sstevel@tonic-gate 		bootlog_message_interval = ramdisk_size / sizeof (buffer);
487*0Sstevel@tonic-gate 		if (bootlog_message_interval < 500)
488*0Sstevel@tonic-gate 			bootlog_message_interval /= 5;
489*0Sstevel@tonic-gate 		else
490*0Sstevel@tonic-gate 			bootlog_message_interval /= 50;
491*0Sstevel@tonic-gate 
492*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_VERBOSE,
493*0Sstevel@tonic-gate 		    "Reading %s file system (%ld kB)",
494*0Sstevel@tonic-gate 		    what, ramdisk_size / 1024);
495*0Sstevel@tonic-gate 	} else {
496*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_VERBOSE,
497*0Sstevel@tonic-gate 		    "Continuing read of %s file system (%ld kB)",
498*0Sstevel@tonic-gate 		    what, ramdisk_size / 1024);
499*0Sstevel@tonic-gate 	}
500*0Sstevel@tonic-gate 	for (ret = 0; ret == 0 && *offset < ramdisk_size; *offset += len) {
501*0Sstevel@tonic-gate 		nleft = ramdisk_size - *offset;
502*0Sstevel@tonic-gate 
503*0Sstevel@tonic-gate 		if (nleft > sizeof (buffer))
504*0Sstevel@tonic-gate 			nleft = sizeof (buffer);
505*0Sstevel@tonic-gate 
506*0Sstevel@tonic-gate 		len = http_read_body(handle, buffer, nleft);
507*0Sstevel@tonic-gate 		if (len <= 0) {
508*0Sstevel@tonic-gate 			print_errors("http_read_body", handle);
509*0Sstevel@tonic-gate 			/*
510*0Sstevel@tonic-gate 			 * In the case of a partial failure, http_read_body()
511*0Sstevel@tonic-gate 			 * returns into 'len', 1 - the number of bytes read.
512*0Sstevel@tonic-gate 			 * So, a -65 means 64 bytes read and an error occurred.
513*0Sstevel@tonic-gate 			 */
514*0Sstevel@tonic-gate 			if (len != 0) {
515*0Sstevel@tonic-gate 				len = -(len + 1);
516*0Sstevel@tonic-gate 			}
517*0Sstevel@tonic-gate 			ret = 1;
518*0Sstevel@tonic-gate 		}
519*0Sstevel@tonic-gate 		if (sha != NULL) {
520*0Sstevel@tonic-gate 			HMACUpdate(sha, (uchar_t *)buffer, (size_t)len);
521*0Sstevel@tonic-gate 		}
522*0Sstevel@tonic-gate 		if (prom_write(fd, buffer, (size_t)len, 0, 0) != (ssize_t)len) {
523*0Sstevel@tonic-gate 			bootlog("wanboot", BOOTLOG_CRIT,
524*0Sstevel@tonic-gate 			    "%s: write to ramdisk failed", what);
525*0Sstevel@tonic-gate 			ret = -1;
526*0Sstevel@tonic-gate 			continue;
527*0Sstevel@tonic-gate 		}
528*0Sstevel@tonic-gate 		if (bootlog_progress == bootlog_message_interval) {
529*0Sstevel@tonic-gate 			bootlog("wanboot", BOOTLOG_PROGRESS,
530*0Sstevel@tonic-gate 			    "%s: Read %ld of %ld kB (%ld%%)", what,
531*0Sstevel@tonic-gate 			    *offset / 1024, ramdisk_size / 1024,
532*0Sstevel@tonic-gate 			    *offset * 100 / ramdisk_size);
533*0Sstevel@tonic-gate 			bootlog_progress = 0;
534*0Sstevel@tonic-gate 		} else {
535*0Sstevel@tonic-gate 			bootlog_progress++;
536*0Sstevel@tonic-gate 		}
537*0Sstevel@tonic-gate 	}
538*0Sstevel@tonic-gate 	if (ret == 0) {
539*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_PROGRESS,
540*0Sstevel@tonic-gate 		    "%s: Read %ld of %ld kB (%ld%%)", what,
541*0Sstevel@tonic-gate 		    *offset / 1024, ramdisk_size / 1024,
542*0Sstevel@tonic-gate 		    *offset * 100 / ramdisk_size);
543*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_INFO, "%s: Download complete", what);
544*0Sstevel@tonic-gate 	}
545*0Sstevel@tonic-gate 	return (ret);
546*0Sstevel@tonic-gate }
547*0Sstevel@tonic-gate 
548*0Sstevel@tonic-gate /*
549*0Sstevel@tonic-gate  * This routine is called with a bootinfo parameter name.  If the parameter
550*0Sstevel@tonic-gate  * has a value it should be a URL, and this will be used to initialize the
551*0Sstevel@tonic-gate  * http_url structure.
552*0Sstevel@tonic-gate  *
553*0Sstevel@tonic-gate  * Returns:
554*0Sstevel@tonic-gate  *	-1 = Non-recoverable error
555*0Sstevel@tonic-gate  *	 0 = Success
556*0Sstevel@tonic-gate  *	 1 = DHCP option not set
557*0Sstevel@tonic-gate  */
558*0Sstevel@tonic-gate static int
559*0Sstevel@tonic-gate get_url(char *name, url_t *url)
560*0Sstevel@tonic-gate {
561*0Sstevel@tonic-gate 	char	buf[URL_MAX_STRLEN];
562*0Sstevel@tonic-gate 	size_t	len;
563*0Sstevel@tonic-gate 	int	ret;
564*0Sstevel@tonic-gate 
565*0Sstevel@tonic-gate 	bzero(buf, sizeof (buf));
566*0Sstevel@tonic-gate 	len = sizeof (buf) - 1;
567*0Sstevel@tonic-gate 	if (bootinfo_get(name, buf, &len, NULL) != BI_E_SUCCESS || len == 0) {
568*0Sstevel@tonic-gate 		return (1);
569*0Sstevel@tonic-gate 	}
570*0Sstevel@tonic-gate 
571*0Sstevel@tonic-gate 	/*
572*0Sstevel@tonic-gate 	 * Parse the URL.
573*0Sstevel@tonic-gate 	 */
574*0Sstevel@tonic-gate 	ret = url_parse(buf, url);
575*0Sstevel@tonic-gate 	if (ret != URL_PARSE_SUCCESS) {
576*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
577*0Sstevel@tonic-gate 		    "Unable to parse URL %s", buf);
578*0Sstevel@tonic-gate 		return (-1);
579*0Sstevel@tonic-gate 	}
580*0Sstevel@tonic-gate 
581*0Sstevel@tonic-gate 	return (0);
582*0Sstevel@tonic-gate }
583*0Sstevel@tonic-gate 
584*0Sstevel@tonic-gate /*
585*0Sstevel@tonic-gate  * This routine initiates an HTTP request and returns a handle so that
586*0Sstevel@tonic-gate  * the caller can process the response.
587*0Sstevel@tonic-gate  *
588*0Sstevel@tonic-gate  * Notes:
589*0Sstevel@tonic-gate  *	Requests may be either secure or not. If the request is secure, then
590*0Sstevel@tonic-gate  *	this routine assumes that a wanboot file system exists and
591*0Sstevel@tonic-gate  *	uses its contents to provide the HTTP library with the information
592*0Sstevel@tonic-gate  *	that will be required by SSL.
593*0Sstevel@tonic-gate  *
594*0Sstevel@tonic-gate  *	In order to facilitate transmission retries, this routine supports
595*0Sstevel@tonic-gate  *	range requests. A caller may request a range by providing a non-zero
596*0Sstevel@tonic-gate  *	offset. In which case, a range request is made that ranges from the
597*0Sstevel@tonic-gate  *	offet to the end of the file.
598*0Sstevel@tonic-gate  *
599*0Sstevel@tonic-gate  *	If the client is configured to use an HTTP proxy, then this routine
600*0Sstevel@tonic-gate  *	will make the HTTP library aware of the proxy.
601*0Sstevel@tonic-gate  *
602*0Sstevel@tonic-gate  *	Any HTTP errors encountered in downloading or processing the message
603*0Sstevel@tonic-gate  *	are not deemed unrecoverable errors. The caller can simply try the
604*0Sstevel@tonic-gate  *	request once again.
605*0Sstevel@tonic-gate  *
606*0Sstevel@tonic-gate  * Returns:
607*0Sstevel@tonic-gate  *	-1 = Non-recoverable error
608*0Sstevel@tonic-gate  *	 0 = Success
609*0Sstevel@tonic-gate  *	 1 = HTTP download error
610*0Sstevel@tonic-gate  */
611*0Sstevel@tonic-gate static int
612*0Sstevel@tonic-gate establish_http_connection(const char *what, http_handle_t *handlep,
613*0Sstevel@tonic-gate     url_t *url, off_t offset)
614*0Sstevel@tonic-gate {
615*0Sstevel@tonic-gate 	static boolean_t	is_auth_file_init = B_FALSE;
616*0Sstevel@tonic-gate 	static boolean_t	is_proxy_init = B_FALSE;
617*0Sstevel@tonic-gate 	static boolean_t	proxy_exists = B_FALSE;
618*0Sstevel@tonic-gate 	static url_hport_t	proxy_hp;
619*0Sstevel@tonic-gate 	http_respinfo_t		*resp;
620*0Sstevel@tonic-gate 	char			buf[URL_MAX_STRLEN];
621*0Sstevel@tonic-gate 	size_t			len = sizeof (buf) - 1;
622*0Sstevel@tonic-gate 	int			ret;
623*0Sstevel@tonic-gate 
624*0Sstevel@tonic-gate 	/* Check for HTTP proxy */
625*0Sstevel@tonic-gate 	if (!is_proxy_init &&
626*0Sstevel@tonic-gate 	    bootinfo_get(BI_HTTP_PROXY, buf, &len, NULL) == BI_E_SUCCESS &&
627*0Sstevel@tonic-gate 	    strlen(buf) > 0) {
628*0Sstevel@tonic-gate 		/*
629*0Sstevel@tonic-gate 		 * Parse the hostport.
630*0Sstevel@tonic-gate 		 */
631*0Sstevel@tonic-gate 		ret = url_parse_hostport(buf, &proxy_hp, URL_DFLT_PROXY_PORT);
632*0Sstevel@tonic-gate 		if (ret == URL_PARSE_SUCCESS) {
633*0Sstevel@tonic-gate 			proxy_exists = B_TRUE;
634*0Sstevel@tonic-gate 		} else {
635*0Sstevel@tonic-gate 			bootlog("wanboot", BOOTLOG_CRIT,
636*0Sstevel@tonic-gate 			    "%s is not set to a valid hostport value",
637*0Sstevel@tonic-gate 			    BI_HTTP_PROXY);
638*0Sstevel@tonic-gate 			return (-1);
639*0Sstevel@tonic-gate 		}
640*0Sstevel@tonic-gate 		is_proxy_init = B_TRUE;
641*0Sstevel@tonic-gate 	}
642*0Sstevel@tonic-gate 
643*0Sstevel@tonic-gate 	http_set_p12_format(use_p12);
644*0Sstevel@tonic-gate 
645*0Sstevel@tonic-gate 	/*
646*0Sstevel@tonic-gate 	 * Initialize the handle that will be used for the request.
647*0Sstevel@tonic-gate 	 */
648*0Sstevel@tonic-gate 	*handlep = http_srv_init(url);
649*0Sstevel@tonic-gate 	if (*handlep == NULL) {
650*0Sstevel@tonic-gate 		print_errors("http_srv_init", NULL);
651*0Sstevel@tonic-gate 		return (-1);
652*0Sstevel@tonic-gate 	}
653*0Sstevel@tonic-gate 
654*0Sstevel@tonic-gate 	/*
655*0Sstevel@tonic-gate 	 * Is the request a secure one? If it is, then we need to do further
656*0Sstevel@tonic-gate 	 * setup. Search the wanboot file system for files that will be
657*0Sstevel@tonic-gate 	 * needed by SSL.
658*0Sstevel@tonic-gate 	 */
659*0Sstevel@tonic-gate 	if (url->https) {
660*0Sstevel@tonic-gate 		char		*cas;
661*0Sstevel@tonic-gate 		boolean_t	client_authentication = B_FALSE;
662*0Sstevel@tonic-gate 
663*0Sstevel@tonic-gate 		if (http_set_random_file(*handlep, "/dev/urandom") < 0) {
664*0Sstevel@tonic-gate 			print_errors("http_set_random_file", *handlep);
665*0Sstevel@tonic-gate 			(void) http_srv_close(*handlep);
666*0Sstevel@tonic-gate 			return (-1);
667*0Sstevel@tonic-gate 		}
668*0Sstevel@tonic-gate 
669*0Sstevel@tonic-gate 		/*
670*0Sstevel@tonic-gate 		 * We only need to initialize the CA once as it is not handle
671*0Sstevel@tonic-gate 		 * specific.
672*0Sstevel@tonic-gate 		 */
673*0Sstevel@tonic-gate 		if (!is_auth_file_init) {
674*0Sstevel@tonic-gate 			if (http_set_certificate_authority_file(NB_CA_CERT_PATH)
675*0Sstevel@tonic-gate 			    < 0) {
676*0Sstevel@tonic-gate 				print_errors(
677*0Sstevel@tonic-gate 				    "http_set_certificate_authority_file",
678*0Sstevel@tonic-gate 				    *handlep);
679*0Sstevel@tonic-gate 				(void) http_srv_close(*handlep);
680*0Sstevel@tonic-gate 				return (-1);
681*0Sstevel@tonic-gate 			}
682*0Sstevel@tonic-gate 
683*0Sstevel@tonic-gate 			is_auth_file_init = B_TRUE;
684*0Sstevel@tonic-gate 		}
685*0Sstevel@tonic-gate 
686*0Sstevel@tonic-gate 		/*
687*0Sstevel@tonic-gate 		 * The client certificate and key will not exist unless
688*0Sstevel@tonic-gate 		 * client authentication has been configured. If it is
689*0Sstevel@tonic-gate 		 * configured then the webserver will have added these
690*0Sstevel@tonic-gate 		 * files to the wanboot file system and the HTTP library
691*0Sstevel@tonic-gate 		 * needs to be made aware of their existence.
692*0Sstevel@tonic-gate 		 */
693*0Sstevel@tonic-gate 		if ((cas = bootconf_get(&bc_handle,
694*0Sstevel@tonic-gate 		    BC_CLIENT_AUTHENTICATION)) != NULL &&
695*0Sstevel@tonic-gate 		    strcmp(cas, "yes") == 0) {
696*0Sstevel@tonic-gate 			client_authentication = B_TRUE;
697*0Sstevel@tonic-gate 
698*0Sstevel@tonic-gate 			if (http_set_client_certificate_file(*handlep,
699*0Sstevel@tonic-gate 			    NB_CLIENT_CERT_PATH) < 0) {
700*0Sstevel@tonic-gate 				print_errors("http_set_client_certificate_file",
701*0Sstevel@tonic-gate 				    *handlep);
702*0Sstevel@tonic-gate 				(void) http_srv_close(*handlep);
703*0Sstevel@tonic-gate 				return (-1);
704*0Sstevel@tonic-gate 			}
705*0Sstevel@tonic-gate 
706*0Sstevel@tonic-gate 			if (http_set_private_key_file(*handlep,
707*0Sstevel@tonic-gate 			    NB_CLIENT_KEY_PATH) < 0) {
708*0Sstevel@tonic-gate 				print_errors("http_set_private_key_file",
709*0Sstevel@tonic-gate 				    *handlep);
710*0Sstevel@tonic-gate 				(void) http_srv_close(*handlep);
711*0Sstevel@tonic-gate 				return (-1);
712*0Sstevel@tonic-gate 			}
713*0Sstevel@tonic-gate 		}
714*0Sstevel@tonic-gate 
715*0Sstevel@tonic-gate 		/*
716*0Sstevel@tonic-gate 		 * We do not really need to set this unless client
717*0Sstevel@tonic-gate 		 * authentication is configured or unless pkcs12 files
718*0Sstevel@tonic-gate 		 * are used.
719*0Sstevel@tonic-gate 		 */
720*0Sstevel@tonic-gate 		if ((client_authentication || use_p12) &&
721*0Sstevel@tonic-gate 		    http_set_password(*handlep, WANBOOT_PASSPHRASE) < 0) {
722*0Sstevel@tonic-gate 			print_errors("http_set_password", *handlep);
723*0Sstevel@tonic-gate 			(void) http_srv_close(*handlep);
724*0Sstevel@tonic-gate 			return (-1);
725*0Sstevel@tonic-gate 		}
726*0Sstevel@tonic-gate 	}
727*0Sstevel@tonic-gate 
728*0Sstevel@tonic-gate 	/*
729*0Sstevel@tonic-gate 	 * If the client is using a proxy, tell the library.
730*0Sstevel@tonic-gate 	 */
731*0Sstevel@tonic-gate 	if (proxy_exists) {
732*0Sstevel@tonic-gate 		if (http_set_proxy(*handlep, &proxy_hp) != 0) {
733*0Sstevel@tonic-gate 			print_errors("http_set_proxy", *handlep);
734*0Sstevel@tonic-gate 			(void) http_srv_close(*handlep);
735*0Sstevel@tonic-gate 			return (-1);
736*0Sstevel@tonic-gate 		}
737*0Sstevel@tonic-gate 	}
738*0Sstevel@tonic-gate 
739*0Sstevel@tonic-gate 	(void) http_set_socket_read_timeout(*handlep, SOCKET_READ_TIMEOUT);
740*0Sstevel@tonic-gate 
741*0Sstevel@tonic-gate 	/*
742*0Sstevel@tonic-gate 	 * Ok, connect to the webserver.
743*0Sstevel@tonic-gate 	 */
744*0Sstevel@tonic-gate 	if (http_srv_connect(*handlep) == -1) {
745*0Sstevel@tonic-gate 		print_errors("http_srv_connect", *handlep);
746*0Sstevel@tonic-gate 		(void) http_srv_close(*handlep);
747*0Sstevel@tonic-gate 		return (1);
748*0Sstevel@tonic-gate 	}
749*0Sstevel@tonic-gate 
750*0Sstevel@tonic-gate 	/*
751*0Sstevel@tonic-gate 	 * If the offset is 0, then we assume that we want the entire
752*0Sstevel@tonic-gate 	 * message. If the offset is not 0, then we assume that we are
753*0Sstevel@tonic-gate 	 * retrying a previously interrupted transfer and thus we make
754*0Sstevel@tonic-gate 	 * a range request.
755*0Sstevel@tonic-gate 	 */
756*0Sstevel@tonic-gate 	if (offset == 0) {
757*0Sstevel@tonic-gate 		if ((ret = http_get_request(*handlep, url->abspath)) == 0) {
758*0Sstevel@tonic-gate 			bootlog("wanboot", BOOTLOG_VERBOSE,
759*0Sstevel@tonic-gate 			    "%s: http_get_request: sent", what);
760*0Sstevel@tonic-gate 		} else {
761*0Sstevel@tonic-gate 			print_errors("http_get_request", *handlep);
762*0Sstevel@tonic-gate 			(void) http_srv_close(*handlep);
763*0Sstevel@tonic-gate 			return (1);
764*0Sstevel@tonic-gate 		}
765*0Sstevel@tonic-gate 	} else {
766*0Sstevel@tonic-gate 		if ((ret = http_get_range_request(*handlep, url->abspath,
767*0Sstevel@tonic-gate 		    offset, 0)) == 0) {
768*0Sstevel@tonic-gate 			bootlog("wanboot", BOOTLOG_VERBOSE,
769*0Sstevel@tonic-gate 			    "%s: http_get_range_request: sent", what);
770*0Sstevel@tonic-gate 		} else {
771*0Sstevel@tonic-gate 			print_errors("http_get_range_request", *handlep);
772*0Sstevel@tonic-gate 			(void) http_srv_close(*handlep);
773*0Sstevel@tonic-gate 			return (1);
774*0Sstevel@tonic-gate 		}
775*0Sstevel@tonic-gate 	}
776*0Sstevel@tonic-gate 
777*0Sstevel@tonic-gate 	/*
778*0Sstevel@tonic-gate 	 * Tell the library to read in the response headers.
779*0Sstevel@tonic-gate 	 */
780*0Sstevel@tonic-gate 	ret = http_process_headers(*handlep, &resp);
781*0Sstevel@tonic-gate 	if (ret == -1) {
782*0Sstevel@tonic-gate 		print_errors("http_process_headers", *handlep);
783*0Sstevel@tonic-gate 		(void) http_srv_close(*handlep);
784*0Sstevel@tonic-gate 		return (1);
785*0Sstevel@tonic-gate 	}
786*0Sstevel@tonic-gate 
787*0Sstevel@tonic-gate 	/*
788*0Sstevel@tonic-gate 	 * Check for a valid response code.
789*0Sstevel@tonic-gate 	 */
790*0Sstevel@tonic-gate 	if ((offset == 0 && resp->code != 200) ||
791*0Sstevel@tonic-gate 	    (offset != 0 && resp->code != 206)) {
792*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_ALERT,
793*0Sstevel@tonic-gate 			"%s: Request returned code %d", what, resp->code);
794*0Sstevel@tonic-gate 		if (resp->statusmsg != NULL && resp->statusmsg[0] != '\0')
795*0Sstevel@tonic-gate 			bootlog("wanboot", BOOTLOG_ALERT,
796*0Sstevel@tonic-gate 				"%s", resp->statusmsg);
797*0Sstevel@tonic-gate 		http_free_respinfo(resp);
798*0Sstevel@tonic-gate 		(void) http_srv_close(*handlep);
799*0Sstevel@tonic-gate 		return (1);
800*0Sstevel@tonic-gate 	}
801*0Sstevel@tonic-gate 	http_free_respinfo(resp);
802*0Sstevel@tonic-gate 
803*0Sstevel@tonic-gate 	/*
804*0Sstevel@tonic-gate 	 * Success.
805*0Sstevel@tonic-gate 	 */
806*0Sstevel@tonic-gate 	return (0);
807*0Sstevel@tonic-gate }
808*0Sstevel@tonic-gate 
809*0Sstevel@tonic-gate /*
810*0Sstevel@tonic-gate  * This routine is called by get_miniinfo() to receive the reply
811*0Sstevel@tonic-gate  * to the request for the miniroot metadata. The reply is a two
812*0Sstevel@tonic-gate  * part multipart message. The first part of the message contains
813*0Sstevel@tonic-gate  * the miniroot file size. The second part of the message contains
814*0Sstevel@tonic-gate  * a hash digest of the miniroot as computed by the server. This
815*0Sstevel@tonic-gate  * routine receives both message parts and returns them to the caller.
816*0Sstevel@tonic-gate  *
817*0Sstevel@tonic-gate  * Notes:
818*0Sstevel@tonic-gate  *	If the miniroot is going to be downloaded securely or if the
819*0Sstevel@tonic-gate  *	the server has no hash key for the client, then the hash digest
820*0Sstevel@tonic-gate  *	downloaded contains all zeros.
821*0Sstevel@tonic-gate  *
822*0Sstevel@tonic-gate  *	Any HTTP errors encountered in downloading or processing the message
823*0Sstevel@tonic-gate  *	are not deemed unrecoverable errors. That is, get_miniinfo()
824*0Sstevel@tonic-gate  *	tries re-requesting the message and tries processing it again.
825*0Sstevel@tonic-gate  *
826*0Sstevel@tonic-gate  * Returns:
827*0Sstevel@tonic-gate  *	-1 = Non-recoverable error
828*0Sstevel@tonic-gate  *	 0 = Success
829*0Sstevel@tonic-gate  *	 1 = HTTP download error
830*0Sstevel@tonic-gate  */
831*0Sstevel@tonic-gate static int
832*0Sstevel@tonic-gate process_miniinfo(http_handle_t handle, size_t *mini_size,
833*0Sstevel@tonic-gate     unsigned char *sdigest)
834*0Sstevel@tonic-gate {
835*0Sstevel@tonic-gate 	char	*lenstr;
836*0Sstevel@tonic-gate 	size_t	cnt;
837*0Sstevel@tonic-gate 
838*0Sstevel@tonic-gate 	/*
839*0Sstevel@tonic-gate 	 * Process the file size header.
840*0Sstevel@tonic-gate 	 */
841*0Sstevel@tonic-gate 	if (http_process_part_headers(handle, NULL) != 0) {
842*0Sstevel@tonic-gate 		print_errors("http_process_part_headers", handle);
843*0Sstevel@tonic-gate 		return (1);
844*0Sstevel@tonic-gate 	}
845*0Sstevel@tonic-gate 	lenstr = http_get_header_value(handle, CONTENT_LENGTH);
846*0Sstevel@tonic-gate 	if (lenstr == NULL) {
847*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_ALERT, "%s: error getting length "
848*0Sstevel@tonic-gate 		    "of first part of multipart message", MINIINFO);
849*0Sstevel@tonic-gate 		return (1);
850*0Sstevel@tonic-gate 	}
851*0Sstevel@tonic-gate 	cnt = (size_t)strtol(lenstr, NULL, 10);
852*0Sstevel@tonic-gate 	free(lenstr);
853*0Sstevel@tonic-gate 	if (cnt == 0 || cnt >= sizeof (buffer)) {
854*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_ALERT, "%s: length of first part "
855*0Sstevel@tonic-gate 		    "of multipart message not a legal size", MINIINFO);
856*0Sstevel@tonic-gate 		return (1);
857*0Sstevel@tonic-gate 	}
858*0Sstevel@tonic-gate 
859*0Sstevel@tonic-gate 	if (read_bytes(handle, buffer, cnt) != 0) {
860*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_ALERT,
861*0Sstevel@tonic-gate 		    "%s: error reading miniroot size", MINIINFO);
862*0Sstevel@tonic-gate 		return (1);
863*0Sstevel@tonic-gate 	}
864*0Sstevel@tonic-gate 	buffer[cnt] = '\0';
865*0Sstevel@tonic-gate 
866*0Sstevel@tonic-gate 	*mini_size = (size_t)strtol(buffer, NULL, 10);
867*0Sstevel@tonic-gate 	if (*mini_size == 0) {
868*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_ALERT, "%s: body of first part "
869*0Sstevel@tonic-gate 		    "of multipart message not a legal size", MINIINFO);
870*0Sstevel@tonic-gate 		return (1);
871*0Sstevel@tonic-gate 	}
872*0Sstevel@tonic-gate 
873*0Sstevel@tonic-gate 	return (read_digest(MINIINFO, handle, sdigest));
874*0Sstevel@tonic-gate }
875*0Sstevel@tonic-gate 
876*0Sstevel@tonic-gate /*
877*0Sstevel@tonic-gate  * This routine is called by get_miniroot() to retrieve the miniroot
878*0Sstevel@tonic-gate  * metadata (miniroot size and a hash digest). This routine sends an
879*0Sstevel@tonic-gate  * HTTP GET request to the webserver to request the download of the
880*0Sstevel@tonic-gate  * miniroot metadata and relies on process_miniinfo() to receive the
881*0Sstevel@tonic-gate  * reply, process it and ultimately return to it the miniroot size and
882*0Sstevel@tonic-gate  * the hash digest.
883*0Sstevel@tonic-gate  *
884*0Sstevel@tonic-gate  * Note:
885*0Sstevel@tonic-gate  *	Any HTTP errors encountered in downloading or processing the message
886*0Sstevel@tonic-gate  *	are not deemed unrecoverable errors. That is, get_miniinfo() should
887*0Sstevel@tonic-gate  *	try re-requesting the message and try processing again.
888*0Sstevel@tonic-gate  *
889*0Sstevel@tonic-gate  * Returns:
890*0Sstevel@tonic-gate  *	-1 = Non-recoverable error
891*0Sstevel@tonic-gate  *	 0 = Success
892*0Sstevel@tonic-gate  */
893*0Sstevel@tonic-gate int
894*0Sstevel@tonic-gate get_miniinfo(const url_t *server_url, size_t *mini_size,
895*0Sstevel@tonic-gate     unsigned char *sdigest)
896*0Sstevel@tonic-gate {
897*0Sstevel@tonic-gate 	http_handle_t	handle;
898*0Sstevel@tonic-gate 	url_t		req_url;
899*0Sstevel@tonic-gate 	int		retry_cnt = 0;
900*0Sstevel@tonic-gate 	int		retry_max = WANBOOT_RETRY_MAX;
901*0Sstevel@tonic-gate 	int		ret;
902*0Sstevel@tonic-gate 
903*0Sstevel@tonic-gate 	/*
904*0Sstevel@tonic-gate 	 * Build the URL to request the miniroot info.
905*0Sstevel@tonic-gate 	 */
906*0Sstevel@tonic-gate 	if (build_request_url(&req_url, URLtype_miniroot, server_url) == -1) {
907*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
908*0Sstevel@tonic-gate 		    "Can't build the URL to make the %s request",
909*0Sstevel@tonic-gate 		    CGIcontent(URLtype_miniroot));
910*0Sstevel@tonic-gate 		return (-1);
911*0Sstevel@tonic-gate 	}
912*0Sstevel@tonic-gate 
913*0Sstevel@tonic-gate 	/*
914*0Sstevel@tonic-gate 	 * Go get the miniroot info. If we fail reading the
915*0Sstevel@tonic-gate 	 * response we re-request the info in its entirety.
916*0Sstevel@tonic-gate 	 */
917*0Sstevel@tonic-gate 	bootlog("wanboot", BOOTLOG_VERBOSE, "Downloading miniroot info");
918*0Sstevel@tonic-gate 
919*0Sstevel@tonic-gate 	do {
920*0Sstevel@tonic-gate 		if ((ret = establish_http_connection(MINIINFO, &handle,
921*0Sstevel@tonic-gate 		    &req_url, 0)) < 0) {
922*0Sstevel@tonic-gate 			break;
923*0Sstevel@tonic-gate 		} else if (ret > 0) {
924*0Sstevel@tonic-gate 			if (wanboot_retry(++retry_cnt, retry_max)) {
925*0Sstevel@tonic-gate 				continue;
926*0Sstevel@tonic-gate 			} else {
927*0Sstevel@tonic-gate 				break;
928*0Sstevel@tonic-gate 			}
929*0Sstevel@tonic-gate 		}
930*0Sstevel@tonic-gate 
931*0Sstevel@tonic-gate 		if ((ret = process_miniinfo(handle, mini_size,
932*0Sstevel@tonic-gate 			sdigest)) > 0) {
933*0Sstevel@tonic-gate 			if (!wanboot_retry(++retry_cnt, retry_max)) {
934*0Sstevel@tonic-gate 				(void) http_srv_close(handle);
935*0Sstevel@tonic-gate 				break;
936*0Sstevel@tonic-gate 			}
937*0Sstevel@tonic-gate 		}
938*0Sstevel@tonic-gate 
939*0Sstevel@tonic-gate 		(void) http_srv_close(handle);
940*0Sstevel@tonic-gate 
941*0Sstevel@tonic-gate 	} while (ret > 0);
942*0Sstevel@tonic-gate 
943*0Sstevel@tonic-gate 	/*
944*0Sstevel@tonic-gate 	 * Success.
945*0Sstevel@tonic-gate 	 */
946*0Sstevel@tonic-gate 	if (ret == 0) {
947*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_VERBOSE,
948*0Sstevel@tonic-gate 		    "Miniroot info download successful");
949*0Sstevel@tonic-gate 		return (0);
950*0Sstevel@tonic-gate 	} else {
951*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
952*0Sstevel@tonic-gate 		    "Miniroot info download aborted");
953*0Sstevel@tonic-gate 		return (-1);
954*0Sstevel@tonic-gate 	}
955*0Sstevel@tonic-gate }
956*0Sstevel@tonic-gate 
957*0Sstevel@tonic-gate /*
958*0Sstevel@tonic-gate  * This routine is called by get_miniroot() to receive the reply to
959*0Sstevel@tonic-gate  * the request for the miniroot download. The miniroot is written
960*0Sstevel@tonic-gate  * to ramdisk as it is received and a hash digest is optionally computed
961*0Sstevel@tonic-gate  * as it does so. The miniroot is downloaded as one large message.
962*0Sstevel@tonic-gate  * Because the message is so large, this routine is prepared to deal
963*0Sstevel@tonic-gate  * with errors in the middle of download. If an error occurs during
964*0Sstevel@tonic-gate  * download, then this message processes all received data up to the
965*0Sstevel@tonic-gate  * point of the error and returns to get_miniroot() an error signifying
966*0Sstevel@tonic-gate  * that a download error has occurred. Presumably, get_miniroot()
967*0Sstevel@tonic-gate  * re-requests the remaining part of the miniroot not yet processed and
968*0Sstevel@tonic-gate  * calls this routine back to process the reply. When this routine
969*0Sstevel@tonic-gate  * returns succesfully, it returns a devpath to the ramdisk and the
970*0Sstevel@tonic-gate  * computed hash (if computed).
971*0Sstevel@tonic-gate  *
972*0Sstevel@tonic-gate  * Note:
973*0Sstevel@tonic-gate  *	In order to facilitate reentry, the ramdisk is left open
974*0Sstevel@tonic-gate  *	and the original miniroot_size and HMAC handle are kept
975*0Sstevel@tonic-gate  *	static.
976*0Sstevel@tonic-gate  *
977*0Sstevel@tonic-gate  * Returns:
978*0Sstevel@tonic-gate  *	-1 = Non-recoverable error
979*0Sstevel@tonic-gate  *	 0 = Success
980*0Sstevel@tonic-gate  *	 1 = HTTP download error
981*0Sstevel@tonic-gate  */
982*0Sstevel@tonic-gate static int
983*0Sstevel@tonic-gate process_miniroot(http_handle_t handle, hash_type_t htype,
984*0Sstevel@tonic-gate     size_t length, char **devpath, off_t *offset, unsigned char *cdigest)
985*0Sstevel@tonic-gate {
986*0Sstevel@tonic-gate 	static SHA1_CTX	sha;
987*0Sstevel@tonic-gate 	static size_t	miniroot_size;
988*0Sstevel@tonic-gate 	static int	fd = -1;
989*0Sstevel@tonic-gate 	int		ret;
990*0Sstevel@tonic-gate 
991*0Sstevel@tonic-gate 	if (fd == -1) {
992*0Sstevel@tonic-gate 		if (htype == HASH_HMAC_SHA1) {
993*0Sstevel@tonic-gate 			bootlog("wanboot", BOOTLOG_INFO,
994*0Sstevel@tonic-gate 			    "%s: Authentication will use HMAC-SHA1", MINIROOT);
995*0Sstevel@tonic-gate 			HMACInit(&sha, g_hash_key, WANBOOT_HMAC_KEY_SIZE);
996*0Sstevel@tonic-gate 		}
997*0Sstevel@tonic-gate 
998*0Sstevel@tonic-gate 		miniroot_size = length;
999*0Sstevel@tonic-gate 
1000*0Sstevel@tonic-gate 		fd = create_ramdisk(RD_ROOTFS, miniroot_size, devpath);
1001*0Sstevel@tonic-gate 	}
1002*0Sstevel@tonic-gate 
1003*0Sstevel@tonic-gate 	if (prom_seek(fd, *offset) == -1) {
1004*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
1005*0Sstevel@tonic-gate 			"%s: prom_seek error", MINIROOT);
1006*0Sstevel@tonic-gate 		return (-1);
1007*0Sstevel@tonic-gate 	}
1008*0Sstevel@tonic-gate 
1009*0Sstevel@tonic-gate 	if ((ret = write_msg_to_ramdisk(MINIROOT, fd, handle, miniroot_size,
1010*0Sstevel@tonic-gate 	    offset, (htype == HASH_NONE) ? NULL : &sha)) != 0) {
1011*0Sstevel@tonic-gate 		if (ret < 0) {
1012*0Sstevel@tonic-gate 			/*
1013*0Sstevel@tonic-gate 			 * Reentry not supported.
1014*0Sstevel@tonic-gate 			 */
1015*0Sstevel@tonic-gate 			(void) prom_close(fd);
1016*0Sstevel@tonic-gate 		}
1017*0Sstevel@tonic-gate 		return (ret);
1018*0Sstevel@tonic-gate 	}
1019*0Sstevel@tonic-gate 
1020*0Sstevel@tonic-gate 	if (htype != HASH_NONE) {
1021*0Sstevel@tonic-gate 		HMACFinal(&sha, g_hash_key, WANBOOT_HMAC_KEY_SIZE, cdigest);
1022*0Sstevel@tonic-gate 	}
1023*0Sstevel@tonic-gate 
1024*0Sstevel@tonic-gate 	(void) prom_close(fd);
1025*0Sstevel@tonic-gate 
1026*0Sstevel@tonic-gate 	return (0);
1027*0Sstevel@tonic-gate }
1028*0Sstevel@tonic-gate 
1029*0Sstevel@tonic-gate /*
1030*0Sstevel@tonic-gate  * This routine retrieves the miniroot from the webserver. The miniroot
1031*0Sstevel@tonic-gate  * is retrieved in two steps. First a request is made to the server
1032*0Sstevel@tonic-gate  * to retrieve miniroot metadata (miniroot size and a hash digest).
1033*0Sstevel@tonic-gate  * The second request actually results in the download of the miniroot.
1034*0Sstevel@tonic-gate  *
1035*0Sstevel@tonic-gate  * This routine relies on get_miniinfo() to make and process
1036*0Sstevel@tonic-gate  * the request for the miniroot metadata and returns the
1037*0Sstevel@tonic-gate  * miniroot size and the hash digest of the miniroot as computed by
1038*0Sstevel@tonic-gate  * the server.
1039*0Sstevel@tonic-gate  *
1040*0Sstevel@tonic-gate  * If get_miniinfo() returns successfully, then this routine sends
1041*0Sstevel@tonic-gate  * an HTTP GET request to the webserver to request download of the
1042*0Sstevel@tonic-gate  * miniroot. This routine relies on process_miniroot() to receive
1043*0Sstevel@tonic-gate  * the reply, process it and ultimately return to it a device path to
1044*0Sstevel@tonic-gate  * a ramdisk containing the miniroot and a client computed hash digest.
1045*0Sstevel@tonic-gate  * This routine verifies that the client computed hash digest matches
1046*0Sstevel@tonic-gate  * the one retrieved by get_miniinfo().
1047*0Sstevel@tonic-gate  *
1048*0Sstevel@tonic-gate  * If an error occurs in the transfer of the miniroot from the server
1049*0Sstevel@tonic-gate  * to the client, then the client re-requests the download of the
1050*0Sstevel@tonic-gate  * miniroot using a range request and only requests the part of the
1051*0Sstevel@tonic-gate  * miniroot not previously downloaded and written to ramdisk. The
1052*0Sstevel@tonic-gate  * process_miniroot() routine has the intelligence to recognize that
1053*0Sstevel@tonic-gate  * it is processing a range request. Errors not related to the actual
1054*0Sstevel@tonic-gate  * message download are deemed unrecoverable.
1055*0Sstevel@tonic-gate  *
1056*0Sstevel@tonic-gate  * Note:
1057*0Sstevel@tonic-gate  *	If the client request for the miniroot is a secure request or
1058*0Sstevel@tonic-gate  *	if the server is not configured with a hash key for the client,
1059*0Sstevel@tonic-gate  *	then the hash digest downloaded from the server will contain
1060*0Sstevel@tonic-gate  *	all zeros. This routine verifies that the server and client are
1061*0Sstevel@tonic-gate  *	in-sync with respect to the need for hash verification.
1062*0Sstevel@tonic-gate  *
1063*0Sstevel@tonic-gate  * Returns:
1064*0Sstevel@tonic-gate  *	-1 = Non-recoverable error
1065*0Sstevel@tonic-gate  *	 0 = Success
1066*0Sstevel@tonic-gate  */
1067*0Sstevel@tonic-gate int
1068*0Sstevel@tonic-gate get_miniroot(char **devpath)
1069*0Sstevel@tonic-gate {
1070*0Sstevel@tonic-gate 	http_handle_t	handle;
1071*0Sstevel@tonic-gate 	unsigned char	cdigest[HMAC_DIGEST_LEN];
1072*0Sstevel@tonic-gate 	unsigned char	sdigest[HMAC_DIGEST_LEN];
1073*0Sstevel@tonic-gate 	char		*urlstr;
1074*0Sstevel@tonic-gate 	url_t		server_url;
1075*0Sstevel@tonic-gate 	size_t		mini_size;
1076*0Sstevel@tonic-gate 	off_t		offset;
1077*0Sstevel@tonic-gate 	int		plen;
1078*0Sstevel@tonic-gate 	int		retry_cnt = 0;
1079*0Sstevel@tonic-gate 	int		retry_max = WANBOOT_RETRY_ROOT_MAX;
1080*0Sstevel@tonic-gate 	int		ret;
1081*0Sstevel@tonic-gate 
1082*0Sstevel@tonic-gate 	/*
1083*0Sstevel@tonic-gate 	 * Get the miniroot URL.
1084*0Sstevel@tonic-gate 	 */
1085*0Sstevel@tonic-gate 	if ((urlstr = bootconf_get(&bc_handle, BC_ROOT_SERVER)) == NULL) {
1086*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
1087*0Sstevel@tonic-gate 		    "Missing root_server URL");
1088*0Sstevel@tonic-gate 		return (-1);
1089*0Sstevel@tonic-gate 	} else if (url_parse(urlstr, &server_url) != URL_PARSE_SUCCESS) {
1090*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
1091*0Sstevel@tonic-gate 		    "Unable to parse URL %s", urlstr);
1092*0Sstevel@tonic-gate 		return (-1);
1093*0Sstevel@tonic-gate 	}
1094*0Sstevel@tonic-gate 
1095*0Sstevel@tonic-gate 	/*
1096*0Sstevel@tonic-gate 	 * We must get the miniroot info before we can request
1097*0Sstevel@tonic-gate 	 * the miniroot itself.
1098*0Sstevel@tonic-gate 	 */
1099*0Sstevel@tonic-gate 	if (get_miniinfo(&server_url, &mini_size, sdigest) != 0) {
1100*0Sstevel@tonic-gate 		return (-1);
1101*0Sstevel@tonic-gate 	}
1102*0Sstevel@tonic-gate 
1103*0Sstevel@tonic-gate 	plen = sizeof (server_url.abspath);
1104*0Sstevel@tonic-gate 	if ((urlstr = bootconf_get(&bc_handle, BC_ROOT_FILE)) == NULL ||
1105*0Sstevel@tonic-gate 	    strlcpy(server_url.abspath, urlstr, plen) >= plen) {
1106*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
1107*0Sstevel@tonic-gate 		    "Cannot retrieve the miniroot path");
1108*0Sstevel@tonic-gate 		return (-1);
1109*0Sstevel@tonic-gate 	}
1110*0Sstevel@tonic-gate 
1111*0Sstevel@tonic-gate 	/*
1112*0Sstevel@tonic-gate 	 * Go get the miniroot. If we fail reading the response
1113*0Sstevel@tonic-gate 	 * then we re-request only the range we have yet to read,
1114*0Sstevel@tonic-gate 	 * unless the error was "unrecoverable" in which case we
1115*0Sstevel@tonic-gate 	 * re-request the entire file system.
1116*0Sstevel@tonic-gate 	 */
1117*0Sstevel@tonic-gate 	bootlog("wanboot", BOOTLOG_VERBOSE, "Downloading miniroot");
1118*0Sstevel@tonic-gate 
1119*0Sstevel@tonic-gate 	bzero(cdigest, sizeof (cdigest));
1120*0Sstevel@tonic-gate 	offset = 0;
1121*0Sstevel@tonic-gate 	do {
1122*0Sstevel@tonic-gate 		if ((ret = establish_http_connection(MINIROOT, &handle,
1123*0Sstevel@tonic-gate 		    &server_url, offset)) < 0) {
1124*0Sstevel@tonic-gate 			break;
1125*0Sstevel@tonic-gate 		} else if (ret > 0) {
1126*0Sstevel@tonic-gate 			if (wanboot_retry(++retry_cnt, retry_max)) {
1127*0Sstevel@tonic-gate 				continue;
1128*0Sstevel@tonic-gate 			} else {
1129*0Sstevel@tonic-gate 				break;
1130*0Sstevel@tonic-gate 			}
1131*0Sstevel@tonic-gate 		}
1132*0Sstevel@tonic-gate 
1133*0Sstevel@tonic-gate 		if ((ret = process_miniroot(handle,
1134*0Sstevel@tonic-gate 		    server_url.https ? HASH_NONE : hash_type,
1135*0Sstevel@tonic-gate 		    mini_size, devpath, &offset, cdigest)) > 0) {
1136*0Sstevel@tonic-gate 			if (!wanboot_retry(++retry_cnt, retry_max)) {
1137*0Sstevel@tonic-gate 				(void) http_srv_close(handle);
1138*0Sstevel@tonic-gate 				break;
1139*0Sstevel@tonic-gate 			}
1140*0Sstevel@tonic-gate 		}
1141*0Sstevel@tonic-gate 
1142*0Sstevel@tonic-gate 		(void) http_srv_close(handle);
1143*0Sstevel@tonic-gate 
1144*0Sstevel@tonic-gate 	} while (ret > 0);
1145*0Sstevel@tonic-gate 
1146*0Sstevel@tonic-gate 	/*
1147*0Sstevel@tonic-gate 	 * Validate the computed digest against the one received.
1148*0Sstevel@tonic-gate 	 */
1149*0Sstevel@tonic-gate 	if (ret != 0 || !verify_digests(MINIROOT, cdigest, sdigest)) {
1150*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
1151*0Sstevel@tonic-gate 		    "Miniroot download aborted");
1152*0Sstevel@tonic-gate 		return (-1);
1153*0Sstevel@tonic-gate 	}
1154*0Sstevel@tonic-gate 
1155*0Sstevel@tonic-gate 	bootlog("wanboot", BOOTLOG_VERBOSE, "Miniroot download successful");
1156*0Sstevel@tonic-gate 	return (0);
1157*0Sstevel@tonic-gate }
1158*0Sstevel@tonic-gate 
1159*0Sstevel@tonic-gate /*
1160*0Sstevel@tonic-gate  * This routine is called to finish the decryption process.
1161*0Sstevel@tonic-gate  * Its purpose is to free the resources allocated by the
1162*0Sstevel@tonic-gate  * encryption init routines.
1163*0Sstevel@tonic-gate  */
1164*0Sstevel@tonic-gate static void
1165*0Sstevel@tonic-gate encr_fini(encr_type_t etype, void *eh)
1166*0Sstevel@tonic-gate {
1167*0Sstevel@tonic-gate 	switch (etype) {
1168*0Sstevel@tonic-gate 	case ENCR_3DES:
1169*0Sstevel@tonic-gate 		des3_fini(eh);
1170*0Sstevel@tonic-gate 		break;
1171*0Sstevel@tonic-gate 	case ENCR_AES:
1172*0Sstevel@tonic-gate 		aes_fini(eh);
1173*0Sstevel@tonic-gate 		break;
1174*0Sstevel@tonic-gate 	default:
1175*0Sstevel@tonic-gate 		break;
1176*0Sstevel@tonic-gate 	}
1177*0Sstevel@tonic-gate }
1178*0Sstevel@tonic-gate 
1179*0Sstevel@tonic-gate /*
1180*0Sstevel@tonic-gate  * This routine is called by process_wanbootfs() to read the encrypted
1181*0Sstevel@tonic-gate  * file system from ramdisk and decrypt it. This routine will rewrite
1182*0Sstevel@tonic-gate  * the file system back to ramdisk in place. The method of decryption
1183*0Sstevel@tonic-gate  * (algorithm) will have already been determined by process_wanbootfs()
1184*0Sstevel@tonic-gate  * and the cbc_handle passed to this routine will already have been
1185*0Sstevel@tonic-gate  * initialized appropriately.
1186*0Sstevel@tonic-gate  *
1187*0Sstevel@tonic-gate  * Returns:
1188*0Sstevel@tonic-gate  *	-1 = Non-recoverable error
1189*0Sstevel@tonic-gate  *	 0 = Success
1190*0Sstevel@tonic-gate  */
1191*0Sstevel@tonic-gate static int
1192*0Sstevel@tonic-gate decrypt_wanbootfs(int fd, cbc_handle_t *ch, uint8_t *iv,
1193*0Sstevel@tonic-gate     size_t block_size, size_t wanbootfs_size)
1194*0Sstevel@tonic-gate {
1195*0Sstevel@tonic-gate 	size_t total;
1196*0Sstevel@tonic-gate 	size_t len;
1197*0Sstevel@tonic-gate 	size_t nleft;
1198*0Sstevel@tonic-gate 	size_t max_read_size;
1199*0Sstevel@tonic-gate 
1200*0Sstevel@tonic-gate 	max_read_size = (sizeof (buffer) / block_size) * block_size;
1201*0Sstevel@tonic-gate 	for (total = 0; total < wanbootfs_size; total += len) {
1202*0Sstevel@tonic-gate 		if (prom_seek(fd, total) == -1) {
1203*0Sstevel@tonic-gate 			bootlog("wanboot", BOOTLOG_CRIT,
1204*0Sstevel@tonic-gate 			    "%s: prom_seek error", WANBOOTFS);
1205*0Sstevel@tonic-gate 			return (-1);
1206*0Sstevel@tonic-gate 		}
1207*0Sstevel@tonic-gate 		nleft = wanbootfs_size - total;
1208*0Sstevel@tonic-gate 		if (nleft > max_read_size)
1209*0Sstevel@tonic-gate 			nleft = max_read_size;
1210*0Sstevel@tonic-gate 		len = prom_read(fd, buffer, nleft, 0, 0);
1211*0Sstevel@tonic-gate 		if (len != nleft) {
1212*0Sstevel@tonic-gate 			bootlog("wanboot", BOOTLOG_CRIT,
1213*0Sstevel@tonic-gate 			    "%s: prom_read error", WANBOOTFS);
1214*0Sstevel@tonic-gate 			return (-1);
1215*0Sstevel@tonic-gate 		}
1216*0Sstevel@tonic-gate 		if (!cbc_decrypt(ch, (uint8_t *)buffer, len, iv)) {
1217*0Sstevel@tonic-gate 			bootlog("wanboot", BOOTLOG_CRIT,
1218*0Sstevel@tonic-gate 			    "%s: cbc decrypt error", WANBOOTFS);
1219*0Sstevel@tonic-gate 			return (-1);
1220*0Sstevel@tonic-gate 		}
1221*0Sstevel@tonic-gate 		if (prom_seek(fd, total) == -1) {
1222*0Sstevel@tonic-gate 			bootlog("wanboot", BOOTLOG_CRIT,
1223*0Sstevel@tonic-gate 			    "%s: prom_seek error", WANBOOTFS);
1224*0Sstevel@tonic-gate 			return (-1);
1225*0Sstevel@tonic-gate 		}
1226*0Sstevel@tonic-gate 		if (prom_write(fd, buffer, len, 0, 0) != len) {
1227*0Sstevel@tonic-gate 			bootlog("wanboot", BOOTLOG_CRIT,
1228*0Sstevel@tonic-gate 			    "%s: prom_write error", WANBOOTFS);
1229*0Sstevel@tonic-gate 			return (-1);
1230*0Sstevel@tonic-gate 		}
1231*0Sstevel@tonic-gate 	}
1232*0Sstevel@tonic-gate 	return (0);
1233*0Sstevel@tonic-gate }
1234*0Sstevel@tonic-gate 
1235*0Sstevel@tonic-gate /*
1236*0Sstevel@tonic-gate  * This routine is called by get_wanbootfs() to receive the reply to
1237*0Sstevel@tonic-gate  * the request for the wanboot file system. The reply is a multipart message.
1238*0Sstevel@tonic-gate  * The first part of the message is the file system (which may or may
1239*0Sstevel@tonic-gate  * not be encrypted).  If encrypted, then the first block of the message
1240*0Sstevel@tonic-gate  * part is the CBC IV value used by the server to encrypt the remaining
1241*0Sstevel@tonic-gate  * part of the message part and is used by the client to decrypt it. The
1242*0Sstevel@tonic-gate  * second message part is a hash digest of the first part (the file
1243*0Sstevel@tonic-gate  * system) as computed by the server. If no hash key is configured
1244*0Sstevel@tonic-gate  * for the client, then the hash digest simply contains all zeros. This
1245*0Sstevel@tonic-gate  * routine receives both message parts. The file system is written to ramdisk
1246*0Sstevel@tonic-gate  * as it is received and simultaneously computes a hash digest (if a hash
1247*0Sstevel@tonic-gate  * key exists). Once the entire part is received, if the file system is
1248*0Sstevel@tonic-gate  * encrypted, it is read from ramdisk, decrypted and rewritten back to
1249*0Sstevel@tonic-gate  * ramdisk. The server computed hash digest is then read and along with the
1250*0Sstevel@tonic-gate  * ramdisk device path and the client computed hash digest is returned to the
1251*0Sstevel@tonic-gate  * caller.
1252*0Sstevel@tonic-gate  *
1253*0Sstevel@tonic-gate  * Notes:
1254*0Sstevel@tonic-gate  *	In order to decrypt the file system and to compute the client
1255*0Sstevel@tonic-gate  *	hash digest, an encryption key and a hash key is retrieved from
1256*0Sstevel@tonic-gate  *	the PROM (or the wanboot interpreter). The non-existence of these
1257*0Sstevel@tonic-gate  *	keys has implications on how the message response is processed and
1258*0Sstevel@tonic-gate  *	it is assumed that the server is configured identically.
1259*0Sstevel@tonic-gate  *
1260*0Sstevel@tonic-gate  *	Any HTTP errors encountered in downloading or processing the message
1261*0Sstevel@tonic-gate  *	are not deemed unrecoverable errors. That is, get_wanbootfs() will
1262*0Sstevel@tonic-gate  *	try re-requesting the message and will try processing it again.
1263*0Sstevel@tonic-gate  *
1264*0Sstevel@tonic-gate  * Returns:
1265*0Sstevel@tonic-gate  *	-1 = Non-recoverable error
1266*0Sstevel@tonic-gate  *	 0 = Success
1267*0Sstevel@tonic-gate  *	 1 = HTTP download error
1268*0Sstevel@tonic-gate  */
1269*0Sstevel@tonic-gate static int
1270*0Sstevel@tonic-gate process_wanbootfs(http_handle_t handle, char **devpath,
1271*0Sstevel@tonic-gate     unsigned char *cdigest, unsigned char *sdigest)
1272*0Sstevel@tonic-gate {
1273*0Sstevel@tonic-gate 	/* iv[] must be sized to store the largest possible encryption block */
1274*0Sstevel@tonic-gate 	uint8_t		iv[WANBOOT_MAXBLOCKLEN];
1275*0Sstevel@tonic-gate 	cbc_handle_t	ch;
1276*0Sstevel@tonic-gate 	void		*eh;
1277*0Sstevel@tonic-gate 	SHA1_CTX	sha;
1278*0Sstevel@tonic-gate 	char		*lenstr;
1279*0Sstevel@tonic-gate 	size_t		wanbootfs_size;
1280*0Sstevel@tonic-gate 	size_t		block_size;
1281*0Sstevel@tonic-gate 	off_t		offset;
1282*0Sstevel@tonic-gate 	static int	fd = -1;
1283*0Sstevel@tonic-gate 	int		ret;
1284*0Sstevel@tonic-gate 
1285*0Sstevel@tonic-gate 	switch (hash_type) {
1286*0Sstevel@tonic-gate 	case HASH_HMAC_SHA1:
1287*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_INFO,
1288*0Sstevel@tonic-gate 		    "%s: Authentication will use HMAC-SHA1", WANBOOTFS);
1289*0Sstevel@tonic-gate 		HMACInit(&sha, g_hash_key, WANBOOT_HMAC_KEY_SIZE);
1290*0Sstevel@tonic-gate 		break;
1291*0Sstevel@tonic-gate 	case HASH_NONE:
1292*0Sstevel@tonic-gate 		break;
1293*0Sstevel@tonic-gate 	default:
1294*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
1295*0Sstevel@tonic-gate 		    "%s: unrecognized hash type", WANBOOTFS);
1296*0Sstevel@tonic-gate 		return (-1);
1297*0Sstevel@tonic-gate 	}
1298*0Sstevel@tonic-gate 
1299*0Sstevel@tonic-gate 	switch (encr_type) {
1300*0Sstevel@tonic-gate 	case ENCR_3DES:
1301*0Sstevel@tonic-gate 		bootlog("wanboot",
1302*0Sstevel@tonic-gate 		    BOOTLOG_INFO, "%s: Decryption will use 3DES", WANBOOTFS);
1303*0Sstevel@tonic-gate 		if (des3_init(&eh) != 0) {
1304*0Sstevel@tonic-gate 			return (-1);
1305*0Sstevel@tonic-gate 		}
1306*0Sstevel@tonic-gate 		block_size = DES3_BLOCK_SIZE;
1307*0Sstevel@tonic-gate 		des3_key(eh, g_encr_key);
1308*0Sstevel@tonic-gate 		cbc_makehandle(&ch, eh, DES3_KEY_SIZE, block_size,
1309*0Sstevel@tonic-gate 		    DES3_IV_SIZE, des3_encrypt, des3_decrypt);
1310*0Sstevel@tonic-gate 
1311*0Sstevel@tonic-gate 		break;
1312*0Sstevel@tonic-gate 	case ENCR_AES:
1313*0Sstevel@tonic-gate 		bootlog("wanboot",
1314*0Sstevel@tonic-gate 		    BOOTLOG_INFO, "%s: Decryption will use AES", WANBOOTFS);
1315*0Sstevel@tonic-gate 		if (aes_init(&eh) != 0) {
1316*0Sstevel@tonic-gate 			return (-1);
1317*0Sstevel@tonic-gate 		}
1318*0Sstevel@tonic-gate 		block_size = AES_BLOCK_SIZE;
1319*0Sstevel@tonic-gate 		aes_key(eh, g_encr_key, AES_128_KEY_SIZE);
1320*0Sstevel@tonic-gate 		cbc_makehandle(&ch, eh, AES_128_KEY_SIZE, block_size,
1321*0Sstevel@tonic-gate 		    AES_IV_SIZE, aes_encrypt, aes_decrypt);
1322*0Sstevel@tonic-gate 		break;
1323*0Sstevel@tonic-gate 	case ENCR_NONE:
1324*0Sstevel@tonic-gate 		break;
1325*0Sstevel@tonic-gate 	default:
1326*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
1327*0Sstevel@tonic-gate 		    "%s: unrecognized encryption type", WANBOOTFS);
1328*0Sstevel@tonic-gate 		return (-1);
1329*0Sstevel@tonic-gate 	}
1330*0Sstevel@tonic-gate 
1331*0Sstevel@tonic-gate 	/*
1332*0Sstevel@tonic-gate 	 * Process the header.
1333*0Sstevel@tonic-gate 	 */
1334*0Sstevel@tonic-gate 	if (http_process_part_headers(handle, NULL) != 0) {
1335*0Sstevel@tonic-gate 		print_errors("http_process_part_headers", handle);
1336*0Sstevel@tonic-gate 		return (1);
1337*0Sstevel@tonic-gate 	}
1338*0Sstevel@tonic-gate 	lenstr = http_get_header_value(handle, CONTENT_LENGTH);
1339*0Sstevel@tonic-gate 	if (lenstr == NULL) {
1340*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_ALERT, "%s: error getting length "
1341*0Sstevel@tonic-gate 		    "of first part of multipart message", WANBOOTFS);
1342*0Sstevel@tonic-gate 		return (1);
1343*0Sstevel@tonic-gate 	}
1344*0Sstevel@tonic-gate 	wanbootfs_size = (size_t)strtol(lenstr, NULL, 10);
1345*0Sstevel@tonic-gate 	free(lenstr);
1346*0Sstevel@tonic-gate 	if (wanbootfs_size == 0) {
1347*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_ALERT, "%s: length of first part "
1348*0Sstevel@tonic-gate 		    "of multipart message not a legal size", WANBOOTFS);
1349*0Sstevel@tonic-gate 		return (1);
1350*0Sstevel@tonic-gate 	}
1351*0Sstevel@tonic-gate 
1352*0Sstevel@tonic-gate 	/*
1353*0Sstevel@tonic-gate 	 * If encrypted, then read the iv.
1354*0Sstevel@tonic-gate 	 */
1355*0Sstevel@tonic-gate 	if (encr_type != ENCR_NONE) {
1356*0Sstevel@tonic-gate 		if (read_bytes(handle, (char *)iv, block_size) != 0) {
1357*0Sstevel@tonic-gate 			bootlog("wanboot", BOOTLOG_ALERT,
1358*0Sstevel@tonic-gate 			    "%s: error reading hash iv", WANBOOTFS);
1359*0Sstevel@tonic-gate 			return (1);
1360*0Sstevel@tonic-gate 		}
1361*0Sstevel@tonic-gate 		wanbootfs_size -= block_size;
1362*0Sstevel@tonic-gate 		if (hash_type != HASH_NONE) {
1363*0Sstevel@tonic-gate 			HMACUpdate(&sha, (uchar_t *)iv, block_size);
1364*0Sstevel@tonic-gate 		}
1365*0Sstevel@tonic-gate 	}
1366*0Sstevel@tonic-gate 
1367*0Sstevel@tonic-gate 	/*
1368*0Sstevel@tonic-gate 	 * We can only create the ramdisk once. So, if we've
1369*0Sstevel@tonic-gate 	 * already created it, then it means we've re-entered
1370*0Sstevel@tonic-gate 	 * this routine from an earlier partial failure. Use
1371*0Sstevel@tonic-gate 	 * the already existing ramdisk and seek back to the
1372*0Sstevel@tonic-gate 	 * beginning of the file.
1373*0Sstevel@tonic-gate 	 */
1374*0Sstevel@tonic-gate 	if (fd == -1) {
1375*0Sstevel@tonic-gate 		fd = create_ramdisk(RD_BOOTFS, wanbootfs_size, devpath);
1376*0Sstevel@tonic-gate 	}
1377*0Sstevel@tonic-gate 
1378*0Sstevel@tonic-gate 	offset = 0;
1379*0Sstevel@tonic-gate 	if (prom_seek(fd, offset) == -1) {
1380*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
1381*0Sstevel@tonic-gate 			"%s: prom_seek error", WANBOOTFS);
1382*0Sstevel@tonic-gate 		return (-1);
1383*0Sstevel@tonic-gate 	}
1384*0Sstevel@tonic-gate 
1385*0Sstevel@tonic-gate 	if ((ret = write_msg_to_ramdisk(WANBOOTFS, fd, handle, wanbootfs_size,
1386*0Sstevel@tonic-gate 	    &offset, (hash_type == HASH_NONE) ? NULL : &sha)) != 0) {
1387*0Sstevel@tonic-gate 		if (ret < 0) {
1388*0Sstevel@tonic-gate 			/*
1389*0Sstevel@tonic-gate 			 * Reentry not supported.
1390*0Sstevel@tonic-gate 			 */
1391*0Sstevel@tonic-gate 			(void) prom_close(fd);
1392*0Sstevel@tonic-gate 		}
1393*0Sstevel@tonic-gate 		return (ret);
1394*0Sstevel@tonic-gate 	}
1395*0Sstevel@tonic-gate 
1396*0Sstevel@tonic-gate 	if (hash_type != HASH_NONE) {
1397*0Sstevel@tonic-gate 		HMACFinal(&sha, g_hash_key, WANBOOT_HMAC_KEY_SIZE, cdigest);
1398*0Sstevel@tonic-gate 	}
1399*0Sstevel@tonic-gate 
1400*0Sstevel@tonic-gate 	/*
1401*0Sstevel@tonic-gate 	 * If encrypted, then decrypt it.
1402*0Sstevel@tonic-gate 	 */
1403*0Sstevel@tonic-gate 	if (encr_type != ENCR_NONE) {
1404*0Sstevel@tonic-gate 		ret = decrypt_wanbootfs(fd, &ch, iv, block_size,
1405*0Sstevel@tonic-gate 		    wanbootfs_size);
1406*0Sstevel@tonic-gate 		if (ret != 0) {
1407*0Sstevel@tonic-gate 			encr_fini(encr_type, eh);
1408*0Sstevel@tonic-gate 			(void) prom_close(fd);
1409*0Sstevel@tonic-gate 			return (-1);
1410*0Sstevel@tonic-gate 		}
1411*0Sstevel@tonic-gate 		encr_fini(encr_type, eh);
1412*0Sstevel@tonic-gate 	}
1413*0Sstevel@tonic-gate 
1414*0Sstevel@tonic-gate 	(void) prom_close(fd);
1415*0Sstevel@tonic-gate 
1416*0Sstevel@tonic-gate 	return (read_digest(WANBOOTFS, handle, sdigest));
1417*0Sstevel@tonic-gate }
1418*0Sstevel@tonic-gate 
1419*0Sstevel@tonic-gate /*
1420*0Sstevel@tonic-gate  * This routine sends an HTTP GET request to the webserver to
1421*0Sstevel@tonic-gate  * request the wanboot file system for the client. The server
1422*0Sstevel@tonic-gate  * will reply by sending a multipart message. This routine will rely
1423*0Sstevel@tonic-gate  * on process_wanbootfs() to receive the multipart message, process it
1424*0Sstevel@tonic-gate  * and ultimately return to it a device path to a ramdisk containing
1425*0Sstevel@tonic-gate  * the wanboot file system, a client computed hash digest and a
1426*0Sstevel@tonic-gate  * server computed hash digest. This routine will verify that the
1427*0Sstevel@tonic-gate  * client computed hash digest matches the one sent by the server. This
1428*0Sstevel@tonic-gate  * routine will also verify that the nonce received in the reply matches
1429*0Sstevel@tonic-gate  * the one sent in the request.
1430*0Sstevel@tonic-gate  *
1431*0Sstevel@tonic-gate  * If an error occurs in the transfer of the message from the server
1432*0Sstevel@tonic-gate  * to the client, then the client re-requests the download in its
1433*0Sstevel@tonic-gate  * entirety. Errors not related to the actual message download are
1434*0Sstevel@tonic-gate  * deemed unrecoverable.
1435*0Sstevel@tonic-gate  *
1436*0Sstevel@tonic-gate  * Returns:
1437*0Sstevel@tonic-gate  *	-1 = Non-recoverable error
1438*0Sstevel@tonic-gate  *	 0 = Success
1439*0Sstevel@tonic-gate  */
1440*0Sstevel@tonic-gate int
1441*0Sstevel@tonic-gate get_wanbootfs(const url_t *server_url)
1442*0Sstevel@tonic-gate {
1443*0Sstevel@tonic-gate 	http_handle_t	handle;
1444*0Sstevel@tonic-gate 	unsigned char	cdigest[HMAC_DIGEST_LEN];
1445*0Sstevel@tonic-gate 	unsigned char	sdigest[HMAC_DIGEST_LEN];
1446*0Sstevel@tonic-gate 	url_t		req_url;
1447*0Sstevel@tonic-gate 	char		*devpath;
1448*0Sstevel@tonic-gate 	int		ret;
1449*0Sstevel@tonic-gate 	int		fd;
1450*0Sstevel@tonic-gate 	char		buf[NONCELEN + 1];
1451*0Sstevel@tonic-gate 	int		retry_cnt = 0;
1452*0Sstevel@tonic-gate 	int		retry_max = WANBOOT_RETRY_MAX;
1453*0Sstevel@tonic-gate 
1454*0Sstevel@tonic-gate 	/*
1455*0Sstevel@tonic-gate 	 * Build the URL to request the wanboot file system. This URL
1456*0Sstevel@tonic-gate 	 * will include the CGI script name and the IP, CID, and
1457*0Sstevel@tonic-gate 	 * NONCE parameters.
1458*0Sstevel@tonic-gate 	 */
1459*0Sstevel@tonic-gate 	if (build_request_url(&req_url, URLtype_wanbootfs, server_url) == -1) {
1460*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
1461*0Sstevel@tonic-gate 		    "Can't build the URL to make the %s request",
1462*0Sstevel@tonic-gate 		    CGIcontent(URLtype_wanbootfs));
1463*0Sstevel@tonic-gate 		return (-1);
1464*0Sstevel@tonic-gate 	}
1465*0Sstevel@tonic-gate 
1466*0Sstevel@tonic-gate 	/*
1467*0Sstevel@tonic-gate 	 * Go get the wanboot file system. If we fail reading the
1468*0Sstevel@tonic-gate 	 * response we re-request the entire file system.
1469*0Sstevel@tonic-gate 	 */
1470*0Sstevel@tonic-gate 	bootlog("wanboot", BOOTLOG_VERBOSE, "Downloading wanboot file system");
1471*0Sstevel@tonic-gate 
1472*0Sstevel@tonic-gate 	bzero(cdigest, sizeof (cdigest));
1473*0Sstevel@tonic-gate 	do {
1474*0Sstevel@tonic-gate 		if ((ret = establish_http_connection(WANBOOTFS, &handle,
1475*0Sstevel@tonic-gate 			&req_url, 0)) < 0) {
1476*0Sstevel@tonic-gate 			break;
1477*0Sstevel@tonic-gate 		} else if (ret > 0) {
1478*0Sstevel@tonic-gate 			if (wanboot_retry(++retry_cnt, retry_max)) {
1479*0Sstevel@tonic-gate 				continue;
1480*0Sstevel@tonic-gate 			} else {
1481*0Sstevel@tonic-gate 				break;
1482*0Sstevel@tonic-gate 			}
1483*0Sstevel@tonic-gate 		}
1484*0Sstevel@tonic-gate 
1485*0Sstevel@tonic-gate 		if ((ret = process_wanbootfs(handle, &devpath,
1486*0Sstevel@tonic-gate 			cdigest, sdigest)) > 0) {
1487*0Sstevel@tonic-gate 			if (!wanboot_retry(++retry_cnt, retry_max)) {
1488*0Sstevel@tonic-gate 				(void) http_srv_close(handle);
1489*0Sstevel@tonic-gate 				break;
1490*0Sstevel@tonic-gate 			}
1491*0Sstevel@tonic-gate 		}
1492*0Sstevel@tonic-gate 
1493*0Sstevel@tonic-gate 		(void) http_srv_close(handle);
1494*0Sstevel@tonic-gate 
1495*0Sstevel@tonic-gate 	} while (ret > 0);
1496*0Sstevel@tonic-gate 
1497*0Sstevel@tonic-gate 	/*
1498*0Sstevel@tonic-gate 	 * Validate the computed digest against the one received.
1499*0Sstevel@tonic-gate 	 */
1500*0Sstevel@tonic-gate 	if (ret != 0 ||
1501*0Sstevel@tonic-gate 		!verify_digests(WANBOOTFS, cdigest, sdigest)) {
1502*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
1503*0Sstevel@tonic-gate 		    "The wanboot file system download aborted");
1504*0Sstevel@tonic-gate 		return (-1);
1505*0Sstevel@tonic-gate 	}
1506*0Sstevel@tonic-gate 
1507*0Sstevel@tonic-gate 	/*
1508*0Sstevel@tonic-gate 	 * Mount the wanboot file system.
1509*0Sstevel@tonic-gate 	 */
1510*0Sstevel@tonic-gate 	if (determine_fstype_and_mountroot(devpath) != VFS_SUCCESS) {
1511*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
1512*0Sstevel@tonic-gate 		    "Could not mount the wanboot filesystem.");
1513*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
1514*0Sstevel@tonic-gate 		    "This may signify a client/server key mismatch");
1515*0Sstevel@tonic-gate 		if (encr_type != ENCR_NONE) {
1516*0Sstevel@tonic-gate 			bootlog("wanboot", BOOTLOG_CRIT,
1517*0Sstevel@tonic-gate 			    "(client has key but wrong encryption_type?)");
1518*0Sstevel@tonic-gate 		} else {
1519*0Sstevel@tonic-gate 			bootlog("wanboot", BOOTLOG_CRIT,
1520*0Sstevel@tonic-gate 			    "(encryption_type specified but no client key?)");
1521*0Sstevel@tonic-gate 		}
1522*0Sstevel@tonic-gate 		return (-1);
1523*0Sstevel@tonic-gate 	}
1524*0Sstevel@tonic-gate 	bootlog("wanboot", BOOTLOG_VERBOSE,
1525*0Sstevel@tonic-gate 	    "The wanboot file system has been mounted");
1526*0Sstevel@tonic-gate 
1527*0Sstevel@tonic-gate 	/*
1528*0Sstevel@tonic-gate 	 * The wanboot file system should contain a nonce. Read it
1529*0Sstevel@tonic-gate 	 * and compare it against the nonce sent in the request.
1530*0Sstevel@tonic-gate 	 */
1531*0Sstevel@tonic-gate 	if ((fd = open(WANBOOTFS_NONCE_FILE, O_RDONLY)) == -1) {
1532*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
1533*0Sstevel@tonic-gate 			"No nonce found in the wanboot file system");
1534*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
1535*0Sstevel@tonic-gate 		    "The wanboot file system download aborted");
1536*0Sstevel@tonic-gate 		return (-1);
1537*0Sstevel@tonic-gate 	}
1538*0Sstevel@tonic-gate 
1539*0Sstevel@tonic-gate 	if (read(fd, buf, NONCELEN) != NONCELEN ||
1540*0Sstevel@tonic-gate 	    bcmp(nonce, buf, NONCELEN) != 0) {
1541*0Sstevel@tonic-gate 		(void) close(fd);
1542*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
1543*0Sstevel@tonic-gate 			"Invalid nonce found in the wanboot file system");
1544*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
1545*0Sstevel@tonic-gate 		    "The wanboot file system download aborted");
1546*0Sstevel@tonic-gate 		return (-1);
1547*0Sstevel@tonic-gate 	}
1548*0Sstevel@tonic-gate 
1549*0Sstevel@tonic-gate 	(void) close(fd);
1550*0Sstevel@tonic-gate 
1551*0Sstevel@tonic-gate 	bootlog("wanboot", BOOTLOG_VERBOSE,
1552*0Sstevel@tonic-gate 	    "The wanboot file system download was successful");
1553*0Sstevel@tonic-gate 	return (0);
1554*0Sstevel@tonic-gate }
1555*0Sstevel@tonic-gate 
1556*0Sstevel@tonic-gate static boolean_t
1557*0Sstevel@tonic-gate init_netdev(char *bpath)
1558*0Sstevel@tonic-gate {
1559*0Sstevel@tonic-gate 	dnode_t		anode;
1560*0Sstevel@tonic-gate 	int		proplen;
1561*0Sstevel@tonic-gate 	static char	netalias[OBP_MAXPATHLEN];
1562*0Sstevel@tonic-gate 
1563*0Sstevel@tonic-gate 	/*
1564*0Sstevel@tonic-gate 	 * Wanboot will either have loaded over the network (in which case
1565*0Sstevel@tonic-gate 	 * bpath will name a network device), or from CD-ROM or disk.  In
1566*0Sstevel@tonic-gate 	 * both cases ensure that the 'net' alias corresponds to a network
1567*0Sstevel@tonic-gate 	 * device, and that if a network boot was performed that it is
1568*0Sstevel@tonic-gate 	 * identical to bpath.  This is so that the interface name can always
1569*0Sstevel@tonic-gate 	 * be determined for CD-ROM or disk boots, and for manually-configured
1570*0Sstevel@tonic-gate 	 * network boots.  The latter restriction may be relaxed in the future.
1571*0Sstevel@tonic-gate 	 */
1572*0Sstevel@tonic-gate 	anode = prom_alias_node();
1573*0Sstevel@tonic-gate 	if ((proplen = prom_getproplen(anode, "net")) > 0 &&
1574*0Sstevel@tonic-gate 	    proplen < sizeof (netalias)) {
1575*0Sstevel@tonic-gate 		(void) prom_getprop(anode, "net", (caddr_t)netalias);
1576*0Sstevel@tonic-gate 
1577*0Sstevel@tonic-gate 		if (is_netdev(netalias)) {
1578*0Sstevel@tonic-gate 			char	*p;
1579*0Sstevel@tonic-gate 
1580*0Sstevel@tonic-gate 			/*
1581*0Sstevel@tonic-gate 			 * Strip device arguments from netalias[].
1582*0Sstevel@tonic-gate 			 */
1583*0Sstevel@tonic-gate 			if ((p = strchr(netalias, ':')) != NULL) {
1584*0Sstevel@tonic-gate 				*p = '\0';
1585*0Sstevel@tonic-gate 			}
1586*0Sstevel@tonic-gate 
1587*0Sstevel@tonic-gate 			/*
1588*0Sstevel@tonic-gate 			 * If bpath is a network device path, then v2path
1589*0Sstevel@tonic-gate 			 * will be a copy of this sans device arguments.
1590*0Sstevel@tonic-gate 			 */
1591*0Sstevel@tonic-gate 			if (!is_netdev(bpath) ||
1592*0Sstevel@tonic-gate 			    strcmp(v2path, netalias) == 0) {
1593*0Sstevel@tonic-gate 				/*
1594*0Sstevel@tonic-gate 				 * Stash the netdev_path bootprop value, then
1595*0Sstevel@tonic-gate 				 * initialize the hardware and return success.
1596*0Sstevel@tonic-gate 				 */
1597*0Sstevel@tonic-gate 				netdev_path = netalias;
1598*0Sstevel@tonic-gate 				mac_init(netalias);
1599*0Sstevel@tonic-gate 				return (B_TRUE);
1600*0Sstevel@tonic-gate 			}
1601*0Sstevel@tonic-gate 
1602*0Sstevel@tonic-gate 			bootlog("wanboot", BOOTLOG_CRIT,
1603*0Sstevel@tonic-gate 			    "wanboot requires that the 'net' alias refers to ");
1604*0Sstevel@tonic-gate 			bootlog("wanboot", BOOTLOG_CRIT,
1605*0Sstevel@tonic-gate 			    "the network device path from which it loaded");
1606*0Sstevel@tonic-gate 			return (B_FALSE);
1607*0Sstevel@tonic-gate 		}
1608*0Sstevel@tonic-gate 	}
1609*0Sstevel@tonic-gate 
1610*0Sstevel@tonic-gate 	/*
1611*0Sstevel@tonic-gate 	 * If we haven't established a device path for a network interface,
1612*0Sstevel@tonic-gate 	 * then we're doomed.
1613*0Sstevel@tonic-gate 	 */
1614*0Sstevel@tonic-gate 	bootlog("wanboot", BOOTLOG_CRIT,
1615*0Sstevel@tonic-gate 	    "No network device available for wanboot!");
1616*0Sstevel@tonic-gate 	bootlog("wanboot", BOOTLOG_CRIT,
1617*0Sstevel@tonic-gate 	    "(Ensure that the 'net' alias is set correctly)");
1618*0Sstevel@tonic-gate 	return (B_FALSE);
1619*0Sstevel@tonic-gate }
1620*0Sstevel@tonic-gate 
1621*0Sstevel@tonic-gate /*
1622*0Sstevel@tonic-gate  * This implementation of bootprog() is used solely by wanboot.
1623*0Sstevel@tonic-gate  *
1624*0Sstevel@tonic-gate  * The basic algorithm is as follows:
1625*0Sstevel@tonic-gate  *
1626*0Sstevel@tonic-gate  * - The wanboot options (those specified using the "-o" flag) are processed,
1627*0Sstevel@tonic-gate  *   and if necessary the wanboot interpreter is invoked to collect other
1628*0Sstevel@tonic-gate  *   options.
1629*0Sstevel@tonic-gate  *
1630*0Sstevel@tonic-gate  * - The wanboot filesystem (containing certificates, wanboot.conf file, etc.)
1631*0Sstevel@tonic-gate  *   is then downloaded into the bootfs ramdisk, which is mounted for use
1632*0Sstevel@tonic-gate  *   by OpenSSL, access to wanboot.conf, etc.
1633*0Sstevel@tonic-gate  *
1634*0Sstevel@tonic-gate  * - The wanboot miniroot is downloaded over http/https into the rootfs
1635*0Sstevel@tonic-gate  *   ramdisk.  The bootfs filesystem is unmounted, and the rootfs filesystem
1636*0Sstevel@tonic-gate  *   is mounted.
1637*0Sstevel@tonic-gate  */
1638*0Sstevel@tonic-gate /* EXPORT DELETE END */
1639*0Sstevel@tonic-gate /*ARGSUSED*/
1640*0Sstevel@tonic-gate int
1641*0Sstevel@tonic-gate bootprog(char *bpath, char *bargs, boolean_t user_specified_filename)
1642*0Sstevel@tonic-gate {
1643*0Sstevel@tonic-gate /* EXPORT DELETE START */
1644*0Sstevel@tonic-gate 	char		*miniroot_path;
1645*0Sstevel@tonic-gate 	url_t		server_url;
1646*0Sstevel@tonic-gate 	int		ret;
1647*0Sstevel@tonic-gate 
1648*0Sstevel@tonic-gate 	if (!init_netdev(bpath)) {
1649*0Sstevel@tonic-gate 		return (-1);
1650*0Sstevel@tonic-gate 	}
1651*0Sstevel@tonic-gate 
1652*0Sstevel@tonic-gate 	if (!bootinfo_init()) {
1653*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT, "Cannot initialize bootinfo");
1654*0Sstevel@tonic-gate 		return (-1);
1655*0Sstevel@tonic-gate 	}
1656*0Sstevel@tonic-gate 
1657*0Sstevel@tonic-gate 	/*
1658*0Sstevel@tonic-gate 	 * Get default values from PROM, etc., process any boot arguments
1659*0Sstevel@tonic-gate 	 * (specified with the "-o" option), and initialize the interface.
1660*0Sstevel@tonic-gate 	 */
1661*0Sstevel@tonic-gate 	if (!wanboot_init_interface(wanboot_arguments)) {
1662*0Sstevel@tonic-gate 		return (-1);
1663*0Sstevel@tonic-gate 	}
1664*0Sstevel@tonic-gate 
1665*0Sstevel@tonic-gate 	/*
1666*0Sstevel@tonic-gate 	 * Determine which encryption and hashing algorithms the client
1667*0Sstevel@tonic-gate 	 * is configured to use.
1668*0Sstevel@tonic-gate 	 */
1669*0Sstevel@tonic-gate 	init_encryption();
1670*0Sstevel@tonic-gate 	init_hashing();
1671*0Sstevel@tonic-gate 
1672*0Sstevel@tonic-gate 	/*
1673*0Sstevel@tonic-gate 	 * Get the bootserver value.  Should be of the form:
1674*0Sstevel@tonic-gate 	 *	http://host[:port]/abspath.
1675*0Sstevel@tonic-gate 	 */
1676*0Sstevel@tonic-gate 	ret = get_url(BI_BOOTSERVER, &server_url);
1677*0Sstevel@tonic-gate 	if (ret != 0) {
1678*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
1679*0Sstevel@tonic-gate 		    "Unable to retrieve the bootserver URL");
1680*0Sstevel@tonic-gate 		return (-1);
1681*0Sstevel@tonic-gate 	}
1682*0Sstevel@tonic-gate 
1683*0Sstevel@tonic-gate 	/*
1684*0Sstevel@tonic-gate 	 * Get the wanboot file system and mount it. Contains metdata
1685*0Sstevel@tonic-gate 	 * needed by wanboot.
1686*0Sstevel@tonic-gate 	 */
1687*0Sstevel@tonic-gate 	if (get_wanbootfs(&server_url) != 0) {
1688*0Sstevel@tonic-gate 		return (-1);
1689*0Sstevel@tonic-gate 	}
1690*0Sstevel@tonic-gate 
1691*0Sstevel@tonic-gate 	/*
1692*0Sstevel@tonic-gate 	 * Check that there is a valid wanboot.conf file in the wanboot
1693*0Sstevel@tonic-gate 	 * file system.
1694*0Sstevel@tonic-gate 	 */
1695*0Sstevel@tonic-gate 	if (bootconf_init(&bc_handle, NULL) != BC_E_NOERROR) {
1696*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
1697*0Sstevel@tonic-gate 		    "wanboot.conf error (code=%d)", bc_handle.bc_error_code);
1698*0Sstevel@tonic-gate 		return (-1);
1699*0Sstevel@tonic-gate 	}
1700*0Sstevel@tonic-gate 
1701*0Sstevel@tonic-gate 	/*
1702*0Sstevel@tonic-gate 	 * Set the time
1703*0Sstevel@tonic-gate 	 */
1704*0Sstevel@tonic-gate 	init_boot_time();
1705*0Sstevel@tonic-gate 
1706*0Sstevel@tonic-gate 	/*
1707*0Sstevel@tonic-gate 	 * Verify that URLs in wanboot.conf can be reached, etc.
1708*0Sstevel@tonic-gate 	 */
1709*0Sstevel@tonic-gate 	if (!wanboot_verify_config()) {
1710*0Sstevel@tonic-gate 		return (-1);
1711*0Sstevel@tonic-gate 	}
1712*0Sstevel@tonic-gate 
1713*0Sstevel@tonic-gate 	/*
1714*0Sstevel@tonic-gate 	 * Retrieve the miniroot.
1715*0Sstevel@tonic-gate 	 */
1716*0Sstevel@tonic-gate 	if (get_miniroot(&miniroot_path) != 0) {
1717*0Sstevel@tonic-gate 		return (-1);
1718*0Sstevel@tonic-gate 	}
1719*0Sstevel@tonic-gate 
1720*0Sstevel@tonic-gate 	/*
1721*0Sstevel@tonic-gate 	 * We don't need the wanboot file system mounted anymore and
1722*0Sstevel@tonic-gate 	 * should unmount it so that we can mount the miniroot.
1723*0Sstevel@tonic-gate 	 */
1724*0Sstevel@tonic-gate 	(void) unmountroot();
1725*0Sstevel@tonic-gate 
1726*0Sstevel@tonic-gate 	/*
1727*0Sstevel@tonic-gate 	 * Mount the miniroot.
1728*0Sstevel@tonic-gate 	 */
1729*0Sstevel@tonic-gate 	if (determine_fstype_and_mountroot(miniroot_path) != VFS_SUCCESS) {
1730*0Sstevel@tonic-gate 		bootlog("wanboot", BOOTLOG_CRIT,
1731*0Sstevel@tonic-gate 		    "Could not mount miniroot filesystem");
1732*0Sstevel@tonic-gate 		return (-1);
1733*0Sstevel@tonic-gate 	}
1734*0Sstevel@tonic-gate 	bootlog("wanboot", BOOTLOG_VERBOSE, "The miniroot has been mounted");
1735*0Sstevel@tonic-gate 
1736*0Sstevel@tonic-gate 	v2path = "/ramdisk-rootfs:a";
1737*0Sstevel@tonic-gate 	bootlog("wanboot", BOOTLOG_VERBOSE, "device path '%s'", v2path);
1738*0Sstevel@tonic-gate 
1739*0Sstevel@tonic-gate 	/*
1740*0Sstevel@tonic-gate 	 * kernname (default-name) might have changed if mountroot() called
1741*0Sstevel@tonic-gate 	 * boot_nfs_mountroot(), and it called set_default_filename().
1742*0Sstevel@tonic-gate 	 */
1743*0Sstevel@tonic-gate 	if (! user_specified_filename)
1744*0Sstevel@tonic-gate 		(void) strcpy(filename, kernname);
1745*0Sstevel@tonic-gate 
1746*0Sstevel@tonic-gate 	bootlog("wanboot", BOOTLOG_VERBOSE,
1747*0Sstevel@tonic-gate 	    "standalone = `%s', args = `%s'", filename, bargs);
1748*0Sstevel@tonic-gate 
1749*0Sstevel@tonic-gate 	set_client_bootargs(filename, bargs);
1750*0Sstevel@tonic-gate 
1751*0Sstevel@tonic-gate 	/*
1752*0Sstevel@tonic-gate 	 * We're done with the mac interface that was initialized by
1753*0Sstevel@tonic-gate 	 * mac_init() inside init_netdev().
1754*0Sstevel@tonic-gate 	 */
1755*0Sstevel@tonic-gate 	mac_fini();
1756*0Sstevel@tonic-gate 
1757*0Sstevel@tonic-gate 	bootconf_end(&bc_handle);
1758*0Sstevel@tonic-gate 	bootinfo_end();
1759*0Sstevel@tonic-gate 
1760*0Sstevel@tonic-gate /* EXPORT DELETE END */
1761*0Sstevel@tonic-gate 	return (0);
1762*0Sstevel@tonic-gate }
1763