10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*5648Ssetje * Common Development and Distribution License (the "License"). 6*5648Ssetje * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*5648Ssetje * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate #include <sys/types.h> 290Sstevel@tonic-gate /* EXPORT DELETE START */ 300Sstevel@tonic-gate #include <sys/promif.h> 310Sstevel@tonic-gate #include <sys/obpdefs.h> 320Sstevel@tonic-gate #include <sys/bootvfs.h> 330Sstevel@tonic-gate #include <sys/bootconf.h> 340Sstevel@tonic-gate #include <netinet/in.h> 350Sstevel@tonic-gate #include <sys/wanboot_impl.h> 360Sstevel@tonic-gate #include <boot_http.h> 370Sstevel@tonic-gate #include <aes.h> 380Sstevel@tonic-gate #include <des3.h> 390Sstevel@tonic-gate #include <cbc.h> 400Sstevel@tonic-gate #include <hmac_sha1.h> 410Sstevel@tonic-gate #include <sys/sha1.h> 420Sstevel@tonic-gate #include <sys/sha1_consts.h> 430Sstevel@tonic-gate #include <bootlog.h> 440Sstevel@tonic-gate #include <parseURL.h> 450Sstevel@tonic-gate #include <netboot_paths.h> 460Sstevel@tonic-gate #include <netinet/inetutil.h> 470Sstevel@tonic-gate #include <sys/salib.h> 480Sstevel@tonic-gate #include <inet/mac.h> 490Sstevel@tonic-gate #include <inet/ipv4.h> 500Sstevel@tonic-gate #include <dhcp_impl.h> 510Sstevel@tonic-gate #include <inet/dhcpv4.h> 520Sstevel@tonic-gate #include <bootinfo.h> 530Sstevel@tonic-gate #include <wanboot_conf.h> 540Sstevel@tonic-gate #include "boot_plat.h" 550Sstevel@tonic-gate #include "ramdisk.h" 560Sstevel@tonic-gate #include "wbcli.h" 570Sstevel@tonic-gate 580Sstevel@tonic-gate /* 590Sstevel@tonic-gate * Types of downloads 600Sstevel@tonic-gate */ 610Sstevel@tonic-gate #define MINIINFO "miniinfo" 620Sstevel@tonic-gate #define MINIROOT "miniroot" 630Sstevel@tonic-gate #define WANBOOTFS "wanbootfs" 640Sstevel@tonic-gate 650Sstevel@tonic-gate #define WANBOOT_RETRY_NOMAX -1 660Sstevel@tonic-gate #define WANBOOT_RETRY_ROOT_MAX 50 670Sstevel@tonic-gate #define WANBOOT_RETRY_MAX 5 680Sstevel@tonic-gate #define WANBOOT_RETRY_SECS 5 690Sstevel@tonic-gate #define WANBOOT_RETRY_MAX_SECS 30 700Sstevel@tonic-gate 710Sstevel@tonic-gate /* 720Sstevel@tonic-gate * Our read requests should timeout after 25 seconds 730Sstevel@tonic-gate */ 740Sstevel@tonic-gate #define SOCKET_READ_TIMEOUT 25 750Sstevel@tonic-gate 760Sstevel@tonic-gate /* 770Sstevel@tonic-gate * Experimentation has shown that an 8K download buffer is optimal 780Sstevel@tonic-gate */ 79*5648Ssetje #define HTTP_XFER_SIZE 8192 80*5648Ssetje static char buffer[HTTP_XFER_SIZE]; 810Sstevel@tonic-gate 820Sstevel@tonic-gate bc_handle_t bc_handle; 830Sstevel@tonic-gate 840Sstevel@tonic-gate extern int determine_fstype_and_mountroot(char *); 850Sstevel@tonic-gate extern uint64_t get_ticks(void); 860Sstevel@tonic-gate 870Sstevel@tonic-gate /* 880Sstevel@tonic-gate * The following is used to determine whether the certs and private key 890Sstevel@tonic-gate * files will be in PEM format or PKCS12 format. 'use_p12' is zero 900Sstevel@tonic-gate * to use PEM format, and 1 when PKCS12 format is to be used. It is 910Sstevel@tonic-gate * done this way, as a global, so that it can be patched if needs be 920Sstevel@tonic-gate * using the OBP debugger. 930Sstevel@tonic-gate */ 940Sstevel@tonic-gate uint32_t use_p12 = 1; 950Sstevel@tonic-gate 960Sstevel@tonic-gate #define CONTENT_LENGTH "Content-Length" 970Sstevel@tonic-gate 980Sstevel@tonic-gate #define NONCELEN (2 * HMAC_DIGEST_LEN) /* two hex nibbles/byte */ 990Sstevel@tonic-gate #define WANBOOTFS_NONCE_FILE "/nonce" 1000Sstevel@tonic-gate 1010Sstevel@tonic-gate static char nonce[NONCELEN + 1]; 1020Sstevel@tonic-gate 1030Sstevel@tonic-gate enum URLtype { 1040Sstevel@tonic-gate URLtype_wanbootfs = 0, 1050Sstevel@tonic-gate URLtype_miniroot = 1 1060Sstevel@tonic-gate }; 1070Sstevel@tonic-gate 1080Sstevel@tonic-gate static char *URLtoCGIcontent[] = { 1090Sstevel@tonic-gate "bootfs", 1100Sstevel@tonic-gate "rootfs" 1110Sstevel@tonic-gate }; 1120Sstevel@tonic-gate #define CGIcontent(urltype) URLtoCGIcontent[urltype] 1130Sstevel@tonic-gate 1140Sstevel@tonic-gate /* Encryption algorithms */ 1150Sstevel@tonic-gate typedef enum { 1160Sstevel@tonic-gate ENCR_NONE, 1170Sstevel@tonic-gate ENCR_3DES, 1180Sstevel@tonic-gate ENCR_AES 1190Sstevel@tonic-gate } encr_type_t; 1200Sstevel@tonic-gate 1210Sstevel@tonic-gate /* Hash algorithms */ 1220Sstevel@tonic-gate typedef enum { 1230Sstevel@tonic-gate HASH_NONE, 1240Sstevel@tonic-gate HASH_HMAC_SHA1 1250Sstevel@tonic-gate } hash_type_t; 1260Sstevel@tonic-gate 1270Sstevel@tonic-gate /* 1280Sstevel@tonic-gate * Keys ... 1290Sstevel@tonic-gate */ 1300Sstevel@tonic-gate static encr_type_t encr_type = ENCR_NONE; 1310Sstevel@tonic-gate static unsigned char *g_encr_key = NULL; 1320Sstevel@tonic-gate 1330Sstevel@tonic-gate static hash_type_t hash_type = HASH_NONE; 1340Sstevel@tonic-gate static unsigned char *g_hash_key = NULL; 1350Sstevel@tonic-gate 1360Sstevel@tonic-gate void 1370Sstevel@tonic-gate print_errors(const char *func, http_handle_t handle) 1380Sstevel@tonic-gate { 1390Sstevel@tonic-gate char const *msg; 1400Sstevel@tonic-gate ulong_t err; 1410Sstevel@tonic-gate uint_t src; 1420Sstevel@tonic-gate 1430Sstevel@tonic-gate while ((err = http_get_lasterr(handle, &src)) != 0) { 1440Sstevel@tonic-gate msg = http_errorstr(src, err); 1450Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_ALERT, 1460Sstevel@tonic-gate "%s: errsrc %u, err %lu (0x%lx)", func, src, err, err); 1470Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_ALERT, "%s", msg); 1480Sstevel@tonic-gate } 1490Sstevel@tonic-gate } 1500Sstevel@tonic-gate 1510Sstevel@tonic-gate /* 1520Sstevel@tonic-gate * This routine is called by a consumer to determine whether or not a 1530Sstevel@tonic-gate * retry should be attempted. If a retry is in order (depends upon the 1540Sstevel@tonic-gate * 'retry_cnt' and 'retry_max' arguments), then this routine will print a 1550Sstevel@tonic-gate * message indicating this is the case and will determine an appropriate 1560Sstevel@tonic-gate * "sleep" time before retrying. The "sleep" time will depend upon the 1570Sstevel@tonic-gate * 'retry_cnt' and will max out at WANBOOT_RETRY_MAX_SECS. 1580Sstevel@tonic-gate * 1590Sstevel@tonic-gate * Returns: 1600Sstevel@tonic-gate * B_TRUE = retry is in order 1610Sstevel@tonic-gate * B_FALSE = retry limit exceeded 1620Sstevel@tonic-gate */ 1630Sstevel@tonic-gate boolean_t 1640Sstevel@tonic-gate wanboot_retry(int retry_cnt, int retry_max) 1650Sstevel@tonic-gate { 1660Sstevel@tonic-gate unsigned int seconds; 1670Sstevel@tonic-gate 1680Sstevel@tonic-gate if (retry_max == WANBOOT_RETRY_NOMAX || retry_cnt <= retry_max) { 1690Sstevel@tonic-gate seconds = WANBOOT_RETRY_SECS * retry_cnt; 1700Sstevel@tonic-gate if (seconds > WANBOOT_RETRY_MAX_SECS) { 1710Sstevel@tonic-gate seconds = WANBOOT_RETRY_MAX_SECS; 1720Sstevel@tonic-gate } 1730Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_INFO, 1740Sstevel@tonic-gate "Will retry in %d seconds ...", seconds); 1750Sstevel@tonic-gate (void) sleep(seconds); 1760Sstevel@tonic-gate return (B_TRUE); 1770Sstevel@tonic-gate } else { 1780Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_INFO, 1790Sstevel@tonic-gate "Maximum retries exceeded."); 1800Sstevel@tonic-gate return (B_FALSE); 1810Sstevel@tonic-gate } 1820Sstevel@tonic-gate } 1830Sstevel@tonic-gate 1840Sstevel@tonic-gate /* 1850Sstevel@tonic-gate * Determine which encryption algorithm the client is configured to use. 1860Sstevel@tonic-gate * WAN boot determines which key to use by order of priority. That is 1870Sstevel@tonic-gate * multiple encryption keys may exist in the PROM, but the first one found 1880Sstevel@tonic-gate * (while searching in a preferred order) is the one that will be used. 1890Sstevel@tonic-gate */ 1900Sstevel@tonic-gate static void 1910Sstevel@tonic-gate init_encryption(void) 1920Sstevel@tonic-gate { 1930Sstevel@tonic-gate static unsigned char key[WANBOOT_MAXKEYLEN]; 1940Sstevel@tonic-gate size_t len = sizeof (key); 1950Sstevel@tonic-gate 1960Sstevel@tonic-gate if (bootinfo_get(BI_AES_KEY, (char *)&key, &len, NULL) == 1970Sstevel@tonic-gate BI_E_SUCCESS) { 1980Sstevel@tonic-gate encr_type = ENCR_AES; 1990Sstevel@tonic-gate g_encr_key = key; 2000Sstevel@tonic-gate } else if (bootinfo_get(BI_3DES_KEY, (char *)&key, &len, NULL) == 2010Sstevel@tonic-gate BI_E_SUCCESS) { 2020Sstevel@tonic-gate encr_type = ENCR_3DES; 2030Sstevel@tonic-gate g_encr_key = key; 2040Sstevel@tonic-gate } 2050Sstevel@tonic-gate } 2060Sstevel@tonic-gate 2070Sstevel@tonic-gate /* 2080Sstevel@tonic-gate * Determine whether the client is configured to use hashing. 2090Sstevel@tonic-gate */ 2100Sstevel@tonic-gate static void 2110Sstevel@tonic-gate init_hashing(void) 2120Sstevel@tonic-gate { 2130Sstevel@tonic-gate static unsigned char key[WANBOOT_HMAC_KEY_SIZE]; 2140Sstevel@tonic-gate size_t len = sizeof (key); 2150Sstevel@tonic-gate 2160Sstevel@tonic-gate if (bootinfo_get(BI_SHA1_KEY, (char *)&key, &len, NULL) == 2170Sstevel@tonic-gate BI_E_SUCCESS) { 2180Sstevel@tonic-gate hash_type = HASH_HMAC_SHA1; 2190Sstevel@tonic-gate g_hash_key = key; 2200Sstevel@tonic-gate } 2210Sstevel@tonic-gate } 2220Sstevel@tonic-gate 2230Sstevel@tonic-gate /* 2240Sstevel@tonic-gate * Read some CPU-specific rapidly-varying data (assumed to be of length 2250Sstevel@tonic-gate * sizeof (hrtime_t) in the non-SPARC case), and digestify it to further 2260Sstevel@tonic-gate * randomize the output. 2270Sstevel@tonic-gate */ 2280Sstevel@tonic-gate char * 2290Sstevel@tonic-gate generate_nonce(void) 2300Sstevel@tonic-gate { 2310Sstevel@tonic-gate uint64_t t; 2320Sstevel@tonic-gate SHA1_CTX c; 2330Sstevel@tonic-gate unsigned char digest[HMAC_DIGEST_LEN]; 2340Sstevel@tonic-gate uint_t nlen = sizeof (nonce); 2350Sstevel@tonic-gate 2360Sstevel@tonic-gate int err; 2370Sstevel@tonic-gate 2380Sstevel@tonic-gate /* 2390Sstevel@tonic-gate * Read SPARC %tick register or x86 TSC 2400Sstevel@tonic-gate */ 2410Sstevel@tonic-gate t = get_ticks(); 2420Sstevel@tonic-gate SHA1Init(&c); 2430Sstevel@tonic-gate SHA1Update(&c, (const uint8_t *)&t, sizeof (t)); 2440Sstevel@tonic-gate SHA1Final(digest, &c); 2450Sstevel@tonic-gate 2460Sstevel@tonic-gate err = octet_to_hexascii(digest, sizeof (digest), nonce, &nlen); 2470Sstevel@tonic-gate if (err != 0) { 2480Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 2490Sstevel@tonic-gate "cannot convert nonce to ASCII: error %d", err); 2500Sstevel@tonic-gate return (NULL); 2510Sstevel@tonic-gate } 2520Sstevel@tonic-gate nonce[NONCELEN] = '\0'; 2530Sstevel@tonic-gate return (nonce); 2540Sstevel@tonic-gate } 2550Sstevel@tonic-gate 2560Sstevel@tonic-gate /* 2570Sstevel@tonic-gate * Given a server URL, builds a URL to request one of the wanboot 2580Sstevel@tonic-gate * datastreams. 2590Sstevel@tonic-gate * 2600Sstevel@tonic-gate * Returns: 2610Sstevel@tonic-gate * -1 = Non-recoverable error 2620Sstevel@tonic-gate * 0 = Success 2630Sstevel@tonic-gate */ 2640Sstevel@tonic-gate static int 2650Sstevel@tonic-gate build_request_url(url_t *req_url, enum URLtype ut, const url_t *server_url) 2660Sstevel@tonic-gate { 2670Sstevel@tonic-gate char clid[WB_MAX_CID_LEN]; 2680Sstevel@tonic-gate size_t clen; 2690Sstevel@tonic-gate char wid[WB_MAX_CID_LEN * 2 + 1]; 2700Sstevel@tonic-gate uint_t wlen; 2710Sstevel@tonic-gate struct in_addr ip; 2720Sstevel@tonic-gate struct in_addr mask; 2730Sstevel@tonic-gate char *netstr; 2740Sstevel@tonic-gate char *ppath; 2750Sstevel@tonic-gate size_t plen; 2760Sstevel@tonic-gate const char reqstr[] = "/?CONTENT=%s&IP=%s&CID=%s"; 2770Sstevel@tonic-gate 2780Sstevel@tonic-gate /* 2790Sstevel@tonic-gate * Initialize the request 2800Sstevel@tonic-gate */ 2810Sstevel@tonic-gate *req_url = *server_url; 2820Sstevel@tonic-gate 2830Sstevel@tonic-gate /* 2840Sstevel@tonic-gate * Build the network number string 2850Sstevel@tonic-gate */ 2860Sstevel@tonic-gate ipv4_getipaddr(&ip); 2870Sstevel@tonic-gate ipv4_getnetmask(&mask); 2880Sstevel@tonic-gate ip.s_addr = ip.s_addr & mask.s_addr; 2890Sstevel@tonic-gate netstr = inet_ntoa(ip); 2900Sstevel@tonic-gate 2910Sstevel@tonic-gate /* 2920Sstevel@tonic-gate * Get the wan id 2930Sstevel@tonic-gate */ 2940Sstevel@tonic-gate clen = sizeof (clid); 2950Sstevel@tonic-gate if (bootinfo_get(BI_CLIENT_ID, clid, &clen, NULL) != BI_E_SUCCESS) { 2960Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 2970Sstevel@tonic-gate "Cannot retrieve the client ID"); 2980Sstevel@tonic-gate return (-1); 2990Sstevel@tonic-gate } 3000Sstevel@tonic-gate wlen = sizeof (wid); 3010Sstevel@tonic-gate (void) octet_to_hexascii(clid, clen, wid, &wlen); 3020Sstevel@tonic-gate 3030Sstevel@tonic-gate /* 3040Sstevel@tonic-gate * Build the request, making sure that the length of the 3050Sstevel@tonic-gate * constructed URL falls within the supported maximum. 3060Sstevel@tonic-gate */ 3070Sstevel@tonic-gate plen = strlen(req_url->abspath); 3080Sstevel@tonic-gate ppath = req_url->abspath + plen; 3090Sstevel@tonic-gate if (snprintf(ppath, URL_MAX_PATHLEN - plen, reqstr, 3100Sstevel@tonic-gate CGIcontent(ut), netstr, wid) >= URL_MAX_PATHLEN - plen) { 3110Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 3120Sstevel@tonic-gate "The URL path length of the %s request is greater than " 3130Sstevel@tonic-gate "the maximum of %d", CGIcontent(ut), URL_MAX_PATHLEN); 3140Sstevel@tonic-gate return (-1); 3150Sstevel@tonic-gate } 3160Sstevel@tonic-gate 3170Sstevel@tonic-gate /* 3180Sstevel@tonic-gate * If the URL type requires a nonce, then supply it. 3190Sstevel@tonic-gate * It will be returned in the reply to detect attempted 3200Sstevel@tonic-gate * replays. 3210Sstevel@tonic-gate */ 3220Sstevel@tonic-gate if (ut == URLtype_wanbootfs) { 3230Sstevel@tonic-gate char *n = generate_nonce(); 3240Sstevel@tonic-gate 3250Sstevel@tonic-gate if (n != NULL) { 3260Sstevel@tonic-gate plen += strlen("&NONCE=") + NONCELEN; 3270Sstevel@tonic-gate if (plen > URL_MAX_PATHLEN) 3280Sstevel@tonic-gate return (-1); 3290Sstevel@tonic-gate (void) strcat(req_url->abspath, "&NONCE="); 3300Sstevel@tonic-gate (void) strcat(req_url->abspath, n); 3310Sstevel@tonic-gate } 3320Sstevel@tonic-gate } 3330Sstevel@tonic-gate 3340Sstevel@tonic-gate return (0); 3350Sstevel@tonic-gate } 3360Sstevel@tonic-gate 3370Sstevel@tonic-gate /* 3380Sstevel@tonic-gate * This routine reads data from an HTTP connection into a buffer. 3390Sstevel@tonic-gate * 3400Sstevel@tonic-gate * Returns: 3410Sstevel@tonic-gate * 0 = Success 3420Sstevel@tonic-gate * 1 = HTTP download error 3430Sstevel@tonic-gate */ 3440Sstevel@tonic-gate static int 3450Sstevel@tonic-gate read_bytes(http_handle_t handle, char *buffer, size_t cnt) 3460Sstevel@tonic-gate { 3470Sstevel@tonic-gate int len; 3480Sstevel@tonic-gate size_t i; 3490Sstevel@tonic-gate 3500Sstevel@tonic-gate for (i = 0; i < cnt; i += len) { 3510Sstevel@tonic-gate len = http_read_body(handle, &buffer[i], cnt - i); 3520Sstevel@tonic-gate if (len <= 0) { 3530Sstevel@tonic-gate print_errors("http_read_body", handle); 3540Sstevel@tonic-gate return (1); 3550Sstevel@tonic-gate } 3560Sstevel@tonic-gate } 3570Sstevel@tonic-gate return (0); 3580Sstevel@tonic-gate } 3590Sstevel@tonic-gate 3600Sstevel@tonic-gate /* 3610Sstevel@tonic-gate * This routine compares two hash digests, one computed by the server and 3620Sstevel@tonic-gate * the other computed by the client to verify that a transmitted message 3630Sstevel@tonic-gate * was received without corruption. 3640Sstevel@tonic-gate * 3650Sstevel@tonic-gate * Notes: 3660Sstevel@tonic-gate * The client only computes a digest if it is configured with a 3670Sstevel@tonic-gate * hash key. If it is not, then the server should not have a hash 3680Sstevel@tonic-gate * key for the client either and therefore should have sent a 3690Sstevel@tonic-gate * zero filled digest. 3700Sstevel@tonic-gate * 3710Sstevel@tonic-gate * Returns: 3720Sstevel@tonic-gate * B_TRUE = digest was verified 3730Sstevel@tonic-gate * B_FALSE = digest did not verify 3740Sstevel@tonic-gate */ 3750Sstevel@tonic-gate static boolean_t 3760Sstevel@tonic-gate verify_digests(const char *what, unsigned char *cdigest, unsigned char *sdigest) 3770Sstevel@tonic-gate { 3780Sstevel@tonic-gate static char null_digest[HMAC_DIGEST_LEN]; 3790Sstevel@tonic-gate 3800Sstevel@tonic-gate if (bcmp(sdigest, cdigest, HMAC_DIGEST_LEN) != 0) { 3810Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 3820Sstevel@tonic-gate "%s: invalid hash digest", what); 3830Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 3840Sstevel@tonic-gate "This may signify a client/server key mismatch"); 3850Sstevel@tonic-gate if (bcmp(sdigest, null_digest, HMAC_DIGEST_LEN) == 0) { 3860Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 3870Sstevel@tonic-gate "(client has key but wrong signature_type?)"); 3880Sstevel@tonic-gate } else if (bcmp(cdigest, null_digest, HMAC_DIGEST_LEN) == 0) { 3890Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 3900Sstevel@tonic-gate "(signature_type specified but no client key?)"); 3910Sstevel@tonic-gate } 3920Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 3930Sstevel@tonic-gate "or possible corruption of the image in transit"); 3940Sstevel@tonic-gate return (B_FALSE); 3950Sstevel@tonic-gate } 3960Sstevel@tonic-gate 3970Sstevel@tonic-gate return (B_TRUE); 3980Sstevel@tonic-gate } 3990Sstevel@tonic-gate 4000Sstevel@tonic-gate /* 4010Sstevel@tonic-gate * This routine reads the part of a multipart message that contains a 4020Sstevel@tonic-gate * hash digest. Errors in reading the digest are differentiated from 4030Sstevel@tonic-gate * other kinds of errors so that the caller can decide whether or 4040Sstevel@tonic-gate * not a retry is worthwhile. 4050Sstevel@tonic-gate * 4060Sstevel@tonic-gate * Note: 4070Sstevel@tonic-gate * The hash digest can either be an HMAC digest or it can be 4080Sstevel@tonic-gate * a zero length message (representing no hash digest). 4090Sstevel@tonic-gate * 4100Sstevel@tonic-gate * Returns: 4110Sstevel@tonic-gate * -1 = Non-recoverable error 4120Sstevel@tonic-gate * 0 = Success 4130Sstevel@tonic-gate * 1 = HTTP download error 4140Sstevel@tonic-gate */ 4150Sstevel@tonic-gate static int 4160Sstevel@tonic-gate read_digest(const char *what, http_handle_t handle, unsigned char *sdigest) 4170Sstevel@tonic-gate { 4180Sstevel@tonic-gate char *lenstr; 4190Sstevel@tonic-gate size_t digest_size; 4200Sstevel@tonic-gate 4210Sstevel@tonic-gate /* 4220Sstevel@tonic-gate * Process the HMAC digest header. 4230Sstevel@tonic-gate */ 4240Sstevel@tonic-gate if (http_process_part_headers(handle, NULL) != 0) { 4250Sstevel@tonic-gate print_errors("http_process_part_headers", handle); 4260Sstevel@tonic-gate return (1); 4270Sstevel@tonic-gate } 4280Sstevel@tonic-gate lenstr = http_get_header_value(handle, CONTENT_LENGTH); 4290Sstevel@tonic-gate if (lenstr == NULL) { 4300Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_ALERT, 431*5648Ssetje "%s: error getting digest length", what); 4320Sstevel@tonic-gate return (1); 4330Sstevel@tonic-gate } 4340Sstevel@tonic-gate digest_size = (size_t)strtol(lenstr, NULL, 10); 4350Sstevel@tonic-gate free(lenstr); 4360Sstevel@tonic-gate 4370Sstevel@tonic-gate /* 4380Sstevel@tonic-gate * Validate the HMAC digest length. 4390Sstevel@tonic-gate */ 4400Sstevel@tonic-gate if (digest_size != HMAC_DIGEST_LEN) { 4410Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 442*5648Ssetje "%s: error validating response - invalid digest size", 443*5648Ssetje what); 4440Sstevel@tonic-gate return (-1); 4450Sstevel@tonic-gate } 4460Sstevel@tonic-gate 4470Sstevel@tonic-gate /* 4480Sstevel@tonic-gate * Read the HMAC digest. 4490Sstevel@tonic-gate */ 4500Sstevel@tonic-gate if (read_bytes(handle, (char *)sdigest, digest_size) != 0) { 4510Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_ALERT, 452*5648Ssetje "%s: error reading digest", what); 4530Sstevel@tonic-gate return (1); 4540Sstevel@tonic-gate } 4550Sstevel@tonic-gate 4560Sstevel@tonic-gate return (0); 4570Sstevel@tonic-gate } 4580Sstevel@tonic-gate 4590Sstevel@tonic-gate /* 4600Sstevel@tonic-gate * This routine reads data from an HTTP connection and writes the data 4610Sstevel@tonic-gate * to a ramdisk. It also, optionally computes a hash digest of the processed 4620Sstevel@tonic-gate * data. This routine may be called to continue writing a previously aborted 4630Sstevel@tonic-gate * write. If this is the case, then the offset will be non-zero and the write 4640Sstevel@tonic-gate * pointer into the ramdisk will be positioned correctly by the caller. 4650Sstevel@tonic-gate * 4660Sstevel@tonic-gate * Returns: 4670Sstevel@tonic-gate * -1 = Non-recoverable error 4680Sstevel@tonic-gate * 0 = Success 4690Sstevel@tonic-gate * 1 = HTTP download error 4700Sstevel@tonic-gate */ 4710Sstevel@tonic-gate static int 472*5648Ssetje write_msg_to_ramdisk(const char *what, caddr_t addr, http_handle_t handle, 4730Sstevel@tonic-gate size_t ramdisk_size, off_t *offset, SHA1_CTX *sha) 4740Sstevel@tonic-gate { 4750Sstevel@tonic-gate int len; 4760Sstevel@tonic-gate long nleft; 4770Sstevel@tonic-gate static int bootlog_message_interval; 4780Sstevel@tonic-gate static int bootlog_progress; 4790Sstevel@tonic-gate int ret; 4800Sstevel@tonic-gate 4810Sstevel@tonic-gate /* 4820Sstevel@tonic-gate * Read the data and write it to the ramdisk. 4830Sstevel@tonic-gate */ 4840Sstevel@tonic-gate if (*offset == 0) { 4850Sstevel@tonic-gate bootlog_progress = 0; 4860Sstevel@tonic-gate bootlog_message_interval = ramdisk_size / sizeof (buffer); 4870Sstevel@tonic-gate if (bootlog_message_interval < 500) 4880Sstevel@tonic-gate bootlog_message_interval /= 5; 4890Sstevel@tonic-gate else 4900Sstevel@tonic-gate bootlog_message_interval /= 50; 4910Sstevel@tonic-gate 4920Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_VERBOSE, 4930Sstevel@tonic-gate "Reading %s file system (%ld kB)", 4940Sstevel@tonic-gate what, ramdisk_size / 1024); 4950Sstevel@tonic-gate } else { 4960Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_VERBOSE, 4970Sstevel@tonic-gate "Continuing read of %s file system (%ld kB)", 4980Sstevel@tonic-gate what, ramdisk_size / 1024); 4990Sstevel@tonic-gate } 500*5648Ssetje for (ret = 0; ret == 0 && *offset < ramdisk_size; 501*5648Ssetje *offset += len, addr += len) { 5020Sstevel@tonic-gate nleft = ramdisk_size - *offset; 5030Sstevel@tonic-gate 5040Sstevel@tonic-gate if (nleft > sizeof (buffer)) 5050Sstevel@tonic-gate nleft = sizeof (buffer); 5060Sstevel@tonic-gate 507*5648Ssetje len = http_read_body(handle, addr, nleft); 5080Sstevel@tonic-gate if (len <= 0) { 5090Sstevel@tonic-gate print_errors("http_read_body", handle); 5100Sstevel@tonic-gate /* 5110Sstevel@tonic-gate * In the case of a partial failure, http_read_body() 5120Sstevel@tonic-gate * returns into 'len', 1 - the number of bytes read. 5130Sstevel@tonic-gate * So, a -65 means 64 bytes read and an error occurred. 5140Sstevel@tonic-gate */ 5150Sstevel@tonic-gate if (len != 0) { 5160Sstevel@tonic-gate len = -(len + 1); 5170Sstevel@tonic-gate } 5180Sstevel@tonic-gate ret = 1; 5190Sstevel@tonic-gate } 5200Sstevel@tonic-gate if (sha != NULL) { 521*5648Ssetje HMACUpdate(sha, (uchar_t *)addr, (size_t)len); 5220Sstevel@tonic-gate } 5230Sstevel@tonic-gate if (bootlog_progress == bootlog_message_interval) { 5240Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_PROGRESS, 5250Sstevel@tonic-gate "%s: Read %ld of %ld kB (%ld%%)", what, 5260Sstevel@tonic-gate *offset / 1024, ramdisk_size / 1024, 5270Sstevel@tonic-gate *offset * 100 / ramdisk_size); 5280Sstevel@tonic-gate bootlog_progress = 0; 5290Sstevel@tonic-gate } else { 5300Sstevel@tonic-gate bootlog_progress++; 5310Sstevel@tonic-gate } 5320Sstevel@tonic-gate } 5330Sstevel@tonic-gate if (ret == 0) { 5340Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_PROGRESS, 5350Sstevel@tonic-gate "%s: Read %ld of %ld kB (%ld%%)", what, 5360Sstevel@tonic-gate *offset / 1024, ramdisk_size / 1024, 5370Sstevel@tonic-gate *offset * 100 / ramdisk_size); 5380Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_INFO, "%s: Download complete", what); 5390Sstevel@tonic-gate } 5400Sstevel@tonic-gate return (ret); 5410Sstevel@tonic-gate } 5420Sstevel@tonic-gate 5430Sstevel@tonic-gate /* 5440Sstevel@tonic-gate * This routine is called with a bootinfo parameter name. If the parameter 5450Sstevel@tonic-gate * has a value it should be a URL, and this will be used to initialize the 5460Sstevel@tonic-gate * http_url structure. 5470Sstevel@tonic-gate * 5480Sstevel@tonic-gate * Returns: 5490Sstevel@tonic-gate * -1 = Non-recoverable error 5500Sstevel@tonic-gate * 0 = Success 5510Sstevel@tonic-gate * 1 = DHCP option not set 5520Sstevel@tonic-gate */ 5530Sstevel@tonic-gate static int 5540Sstevel@tonic-gate get_url(char *name, url_t *url) 5550Sstevel@tonic-gate { 5560Sstevel@tonic-gate char buf[URL_MAX_STRLEN]; 5570Sstevel@tonic-gate size_t len; 5580Sstevel@tonic-gate int ret; 5590Sstevel@tonic-gate 5600Sstevel@tonic-gate bzero(buf, sizeof (buf)); 5610Sstevel@tonic-gate len = sizeof (buf) - 1; 5620Sstevel@tonic-gate if (bootinfo_get(name, buf, &len, NULL) != BI_E_SUCCESS || len == 0) { 5630Sstevel@tonic-gate return (1); 5640Sstevel@tonic-gate } 5650Sstevel@tonic-gate 5660Sstevel@tonic-gate /* 5670Sstevel@tonic-gate * Parse the URL. 5680Sstevel@tonic-gate */ 5690Sstevel@tonic-gate ret = url_parse(buf, url); 5700Sstevel@tonic-gate if (ret != URL_PARSE_SUCCESS) { 5710Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 5720Sstevel@tonic-gate "Unable to parse URL %s", buf); 5730Sstevel@tonic-gate return (-1); 5740Sstevel@tonic-gate } 5750Sstevel@tonic-gate 5760Sstevel@tonic-gate return (0); 5770Sstevel@tonic-gate } 5780Sstevel@tonic-gate 5790Sstevel@tonic-gate /* 5800Sstevel@tonic-gate * This routine initiates an HTTP request and returns a handle so that 5810Sstevel@tonic-gate * the caller can process the response. 5820Sstevel@tonic-gate * 5830Sstevel@tonic-gate * Notes: 5840Sstevel@tonic-gate * Requests may be either secure or not. If the request is secure, then 5850Sstevel@tonic-gate * this routine assumes that a wanboot file system exists and 5860Sstevel@tonic-gate * uses its contents to provide the HTTP library with the information 5870Sstevel@tonic-gate * that will be required by SSL. 5880Sstevel@tonic-gate * 5890Sstevel@tonic-gate * In order to facilitate transmission retries, this routine supports 5900Sstevel@tonic-gate * range requests. A caller may request a range by providing a non-zero 5910Sstevel@tonic-gate * offset. In which case, a range request is made that ranges from the 5920Sstevel@tonic-gate * offet to the end of the file. 5930Sstevel@tonic-gate * 5940Sstevel@tonic-gate * If the client is configured to use an HTTP proxy, then this routine 5950Sstevel@tonic-gate * will make the HTTP library aware of the proxy. 5960Sstevel@tonic-gate * 5970Sstevel@tonic-gate * Any HTTP errors encountered in downloading or processing the message 5980Sstevel@tonic-gate * are not deemed unrecoverable errors. The caller can simply try the 5990Sstevel@tonic-gate * request once again. 6000Sstevel@tonic-gate * 6010Sstevel@tonic-gate * Returns: 6020Sstevel@tonic-gate * -1 = Non-recoverable error 6030Sstevel@tonic-gate * 0 = Success 6040Sstevel@tonic-gate * 1 = HTTP download error 6050Sstevel@tonic-gate */ 6060Sstevel@tonic-gate static int 6070Sstevel@tonic-gate establish_http_connection(const char *what, http_handle_t *handlep, 6081279Svh115876 url_t *url, offset_t offset) 6090Sstevel@tonic-gate { 6100Sstevel@tonic-gate static boolean_t is_auth_file_init = B_FALSE; 6110Sstevel@tonic-gate static boolean_t is_proxy_init = B_FALSE; 6120Sstevel@tonic-gate static boolean_t proxy_exists = B_FALSE; 6130Sstevel@tonic-gate static url_hport_t proxy_hp; 6140Sstevel@tonic-gate http_respinfo_t *resp; 6150Sstevel@tonic-gate char buf[URL_MAX_STRLEN]; 6160Sstevel@tonic-gate size_t len = sizeof (buf) - 1; 6170Sstevel@tonic-gate int ret; 6180Sstevel@tonic-gate 6190Sstevel@tonic-gate /* Check for HTTP proxy */ 6200Sstevel@tonic-gate if (!is_proxy_init && 6210Sstevel@tonic-gate bootinfo_get(BI_HTTP_PROXY, buf, &len, NULL) == BI_E_SUCCESS && 6220Sstevel@tonic-gate strlen(buf) > 0) { 6230Sstevel@tonic-gate /* 6240Sstevel@tonic-gate * Parse the hostport. 6250Sstevel@tonic-gate */ 6260Sstevel@tonic-gate ret = url_parse_hostport(buf, &proxy_hp, URL_DFLT_PROXY_PORT); 6270Sstevel@tonic-gate if (ret == URL_PARSE_SUCCESS) { 6280Sstevel@tonic-gate proxy_exists = B_TRUE; 6290Sstevel@tonic-gate } else { 6300Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 6310Sstevel@tonic-gate "%s is not set to a valid hostport value", 6320Sstevel@tonic-gate BI_HTTP_PROXY); 6330Sstevel@tonic-gate return (-1); 6340Sstevel@tonic-gate } 6350Sstevel@tonic-gate is_proxy_init = B_TRUE; 6360Sstevel@tonic-gate } 6370Sstevel@tonic-gate 6380Sstevel@tonic-gate http_set_p12_format(use_p12); 6390Sstevel@tonic-gate 6400Sstevel@tonic-gate /* 6410Sstevel@tonic-gate * Initialize the handle that will be used for the request. 6420Sstevel@tonic-gate */ 6430Sstevel@tonic-gate *handlep = http_srv_init(url); 6440Sstevel@tonic-gate if (*handlep == NULL) { 6450Sstevel@tonic-gate print_errors("http_srv_init", NULL); 6460Sstevel@tonic-gate return (-1); 6470Sstevel@tonic-gate } 6480Sstevel@tonic-gate 6490Sstevel@tonic-gate /* 6500Sstevel@tonic-gate * Is the request a secure one? If it is, then we need to do further 6510Sstevel@tonic-gate * setup. Search the wanboot file system for files that will be 6520Sstevel@tonic-gate * needed by SSL. 6530Sstevel@tonic-gate */ 6540Sstevel@tonic-gate if (url->https) { 6550Sstevel@tonic-gate char *cas; 6560Sstevel@tonic-gate boolean_t client_authentication = B_FALSE; 6570Sstevel@tonic-gate 6580Sstevel@tonic-gate if (http_set_random_file(*handlep, "/dev/urandom") < 0) { 6590Sstevel@tonic-gate print_errors("http_set_random_file", *handlep); 6600Sstevel@tonic-gate (void) http_srv_close(*handlep); 6610Sstevel@tonic-gate return (-1); 6620Sstevel@tonic-gate } 6630Sstevel@tonic-gate 6640Sstevel@tonic-gate /* 6650Sstevel@tonic-gate * We only need to initialize the CA once as it is not handle 6660Sstevel@tonic-gate * specific. 6670Sstevel@tonic-gate */ 6680Sstevel@tonic-gate if (!is_auth_file_init) { 6690Sstevel@tonic-gate if (http_set_certificate_authority_file(NB_CA_CERT_PATH) 6700Sstevel@tonic-gate < 0) { 6710Sstevel@tonic-gate print_errors( 6720Sstevel@tonic-gate "http_set_certificate_authority_file", 6730Sstevel@tonic-gate *handlep); 6740Sstevel@tonic-gate (void) http_srv_close(*handlep); 6750Sstevel@tonic-gate return (-1); 6760Sstevel@tonic-gate } 6770Sstevel@tonic-gate 6780Sstevel@tonic-gate is_auth_file_init = B_TRUE; 6790Sstevel@tonic-gate } 6800Sstevel@tonic-gate 6810Sstevel@tonic-gate /* 6820Sstevel@tonic-gate * The client certificate and key will not exist unless 6830Sstevel@tonic-gate * client authentication has been configured. If it is 6840Sstevel@tonic-gate * configured then the webserver will have added these 6850Sstevel@tonic-gate * files to the wanboot file system and the HTTP library 6860Sstevel@tonic-gate * needs to be made aware of their existence. 6870Sstevel@tonic-gate */ 6880Sstevel@tonic-gate if ((cas = bootconf_get(&bc_handle, 6890Sstevel@tonic-gate BC_CLIENT_AUTHENTICATION)) != NULL && 6900Sstevel@tonic-gate strcmp(cas, "yes") == 0) { 6910Sstevel@tonic-gate client_authentication = B_TRUE; 6920Sstevel@tonic-gate 6930Sstevel@tonic-gate if (http_set_client_certificate_file(*handlep, 6940Sstevel@tonic-gate NB_CLIENT_CERT_PATH) < 0) { 6950Sstevel@tonic-gate print_errors("http_set_client_certificate_file", 6960Sstevel@tonic-gate *handlep); 6970Sstevel@tonic-gate (void) http_srv_close(*handlep); 6980Sstevel@tonic-gate return (-1); 6990Sstevel@tonic-gate } 7000Sstevel@tonic-gate 7010Sstevel@tonic-gate if (http_set_private_key_file(*handlep, 7020Sstevel@tonic-gate NB_CLIENT_KEY_PATH) < 0) { 7030Sstevel@tonic-gate print_errors("http_set_private_key_file", 7040Sstevel@tonic-gate *handlep); 7050Sstevel@tonic-gate (void) http_srv_close(*handlep); 7060Sstevel@tonic-gate return (-1); 7070Sstevel@tonic-gate } 7080Sstevel@tonic-gate } 7090Sstevel@tonic-gate 7100Sstevel@tonic-gate /* 7110Sstevel@tonic-gate * We do not really need to set this unless client 7120Sstevel@tonic-gate * authentication is configured or unless pkcs12 files 7130Sstevel@tonic-gate * are used. 7140Sstevel@tonic-gate */ 7150Sstevel@tonic-gate if ((client_authentication || use_p12) && 7160Sstevel@tonic-gate http_set_password(*handlep, WANBOOT_PASSPHRASE) < 0) { 7170Sstevel@tonic-gate print_errors("http_set_password", *handlep); 7180Sstevel@tonic-gate (void) http_srv_close(*handlep); 7190Sstevel@tonic-gate return (-1); 7200Sstevel@tonic-gate } 7210Sstevel@tonic-gate } 7220Sstevel@tonic-gate 7230Sstevel@tonic-gate /* 7240Sstevel@tonic-gate * If the client is using a proxy, tell the library. 7250Sstevel@tonic-gate */ 7260Sstevel@tonic-gate if (proxy_exists) { 7270Sstevel@tonic-gate if (http_set_proxy(*handlep, &proxy_hp) != 0) { 7280Sstevel@tonic-gate print_errors("http_set_proxy", *handlep); 7290Sstevel@tonic-gate (void) http_srv_close(*handlep); 7300Sstevel@tonic-gate return (-1); 7310Sstevel@tonic-gate } 7320Sstevel@tonic-gate } 7330Sstevel@tonic-gate 7340Sstevel@tonic-gate (void) http_set_socket_read_timeout(*handlep, SOCKET_READ_TIMEOUT); 7350Sstevel@tonic-gate 7360Sstevel@tonic-gate /* 7370Sstevel@tonic-gate * Ok, connect to the webserver. 7380Sstevel@tonic-gate */ 7390Sstevel@tonic-gate if (http_srv_connect(*handlep) == -1) { 7400Sstevel@tonic-gate print_errors("http_srv_connect", *handlep); 7410Sstevel@tonic-gate (void) http_srv_close(*handlep); 7420Sstevel@tonic-gate return (1); 7430Sstevel@tonic-gate } 7440Sstevel@tonic-gate 7450Sstevel@tonic-gate /* 7460Sstevel@tonic-gate * If the offset is 0, then we assume that we want the entire 7470Sstevel@tonic-gate * message. If the offset is not 0, then we assume that we are 7480Sstevel@tonic-gate * retrying a previously interrupted transfer and thus we make 7490Sstevel@tonic-gate * a range request. 7500Sstevel@tonic-gate */ 7510Sstevel@tonic-gate if (offset == 0) { 7520Sstevel@tonic-gate if ((ret = http_get_request(*handlep, url->abspath)) == 0) { 7530Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_VERBOSE, 7540Sstevel@tonic-gate "%s: http_get_request: sent", what); 7550Sstevel@tonic-gate } else { 7560Sstevel@tonic-gate print_errors("http_get_request", *handlep); 7570Sstevel@tonic-gate (void) http_srv_close(*handlep); 7580Sstevel@tonic-gate return (1); 7590Sstevel@tonic-gate } 7600Sstevel@tonic-gate } else { 7610Sstevel@tonic-gate if ((ret = http_get_range_request(*handlep, url->abspath, 7620Sstevel@tonic-gate offset, 0)) == 0) { 7630Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_VERBOSE, 7640Sstevel@tonic-gate "%s: http_get_range_request: sent", what); 7650Sstevel@tonic-gate } else { 7660Sstevel@tonic-gate print_errors("http_get_range_request", *handlep); 7670Sstevel@tonic-gate (void) http_srv_close(*handlep); 7680Sstevel@tonic-gate return (1); 7690Sstevel@tonic-gate } 7700Sstevel@tonic-gate } 7710Sstevel@tonic-gate 7720Sstevel@tonic-gate /* 7730Sstevel@tonic-gate * Tell the library to read in the response headers. 7740Sstevel@tonic-gate */ 7750Sstevel@tonic-gate ret = http_process_headers(*handlep, &resp); 7760Sstevel@tonic-gate if (ret == -1) { 7770Sstevel@tonic-gate print_errors("http_process_headers", *handlep); 7780Sstevel@tonic-gate (void) http_srv_close(*handlep); 7790Sstevel@tonic-gate return (1); 7800Sstevel@tonic-gate } 7810Sstevel@tonic-gate 7820Sstevel@tonic-gate /* 7830Sstevel@tonic-gate * Check for a valid response code. 7840Sstevel@tonic-gate */ 7850Sstevel@tonic-gate if ((offset == 0 && resp->code != 200) || 7860Sstevel@tonic-gate (offset != 0 && resp->code != 206)) { 7870Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_ALERT, 788*5648Ssetje "%s: Request returned code %d", what, resp->code); 7890Sstevel@tonic-gate if (resp->statusmsg != NULL && resp->statusmsg[0] != '\0') 7900Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_ALERT, 791*5648Ssetje "%s", resp->statusmsg); 7920Sstevel@tonic-gate http_free_respinfo(resp); 7930Sstevel@tonic-gate (void) http_srv_close(*handlep); 7940Sstevel@tonic-gate return (1); 7950Sstevel@tonic-gate } 7960Sstevel@tonic-gate http_free_respinfo(resp); 7970Sstevel@tonic-gate 7980Sstevel@tonic-gate /* 7990Sstevel@tonic-gate * Success. 8000Sstevel@tonic-gate */ 8010Sstevel@tonic-gate return (0); 8020Sstevel@tonic-gate } 8030Sstevel@tonic-gate 8040Sstevel@tonic-gate /* 8050Sstevel@tonic-gate * This routine is called by get_miniinfo() to receive the reply 8060Sstevel@tonic-gate * to the request for the miniroot metadata. The reply is a two 8070Sstevel@tonic-gate * part multipart message. The first part of the message contains 8080Sstevel@tonic-gate * the miniroot file size. The second part of the message contains 8090Sstevel@tonic-gate * a hash digest of the miniroot as computed by the server. This 8100Sstevel@tonic-gate * routine receives both message parts and returns them to the caller. 8110Sstevel@tonic-gate * 8120Sstevel@tonic-gate * Notes: 8130Sstevel@tonic-gate * If the miniroot is going to be downloaded securely or if the 8140Sstevel@tonic-gate * the server has no hash key for the client, then the hash digest 8150Sstevel@tonic-gate * downloaded contains all zeros. 8160Sstevel@tonic-gate * 8170Sstevel@tonic-gate * Any HTTP errors encountered in downloading or processing the message 8180Sstevel@tonic-gate * are not deemed unrecoverable errors. That is, get_miniinfo() 8190Sstevel@tonic-gate * tries re-requesting the message and tries processing it again. 8200Sstevel@tonic-gate * 8210Sstevel@tonic-gate * Returns: 8220Sstevel@tonic-gate * -1 = Non-recoverable error 8230Sstevel@tonic-gate * 0 = Success 8240Sstevel@tonic-gate * 1 = HTTP download error 8250Sstevel@tonic-gate */ 8260Sstevel@tonic-gate static int 8270Sstevel@tonic-gate process_miniinfo(http_handle_t handle, size_t *mini_size, 8280Sstevel@tonic-gate unsigned char *sdigest) 8290Sstevel@tonic-gate { 8300Sstevel@tonic-gate char *lenstr; 8310Sstevel@tonic-gate size_t cnt; 8320Sstevel@tonic-gate 8330Sstevel@tonic-gate /* 8340Sstevel@tonic-gate * Process the file size header. 8350Sstevel@tonic-gate */ 8360Sstevel@tonic-gate if (http_process_part_headers(handle, NULL) != 0) { 8370Sstevel@tonic-gate print_errors("http_process_part_headers", handle); 8380Sstevel@tonic-gate return (1); 8390Sstevel@tonic-gate } 8400Sstevel@tonic-gate lenstr = http_get_header_value(handle, CONTENT_LENGTH); 8410Sstevel@tonic-gate if (lenstr == NULL) { 8420Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_ALERT, "%s: error getting length " 8430Sstevel@tonic-gate "of first part of multipart message", MINIINFO); 8440Sstevel@tonic-gate return (1); 8450Sstevel@tonic-gate } 8460Sstevel@tonic-gate cnt = (size_t)strtol(lenstr, NULL, 10); 8470Sstevel@tonic-gate free(lenstr); 8480Sstevel@tonic-gate if (cnt == 0 || cnt >= sizeof (buffer)) { 8490Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_ALERT, "%s: length of first part " 8500Sstevel@tonic-gate "of multipart message not a legal size", MINIINFO); 8510Sstevel@tonic-gate return (1); 8520Sstevel@tonic-gate } 8530Sstevel@tonic-gate 8540Sstevel@tonic-gate if (read_bytes(handle, buffer, cnt) != 0) { 8550Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_ALERT, 8560Sstevel@tonic-gate "%s: error reading miniroot size", MINIINFO); 8570Sstevel@tonic-gate return (1); 8580Sstevel@tonic-gate } 8590Sstevel@tonic-gate buffer[cnt] = '\0'; 8600Sstevel@tonic-gate 8610Sstevel@tonic-gate *mini_size = (size_t)strtol(buffer, NULL, 10); 8620Sstevel@tonic-gate if (*mini_size == 0) { 8630Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_ALERT, "%s: body of first part " 8640Sstevel@tonic-gate "of multipart message not a legal size", MINIINFO); 8650Sstevel@tonic-gate return (1); 8660Sstevel@tonic-gate } 8670Sstevel@tonic-gate 8680Sstevel@tonic-gate return (read_digest(MINIINFO, handle, sdigest)); 8690Sstevel@tonic-gate } 8700Sstevel@tonic-gate 8710Sstevel@tonic-gate /* 8720Sstevel@tonic-gate * This routine is called by get_miniroot() to retrieve the miniroot 8730Sstevel@tonic-gate * metadata (miniroot size and a hash digest). This routine sends an 8740Sstevel@tonic-gate * HTTP GET request to the webserver to request the download of the 8750Sstevel@tonic-gate * miniroot metadata and relies on process_miniinfo() to receive the 8760Sstevel@tonic-gate * reply, process it and ultimately return to it the miniroot size and 8770Sstevel@tonic-gate * the hash digest. 8780Sstevel@tonic-gate * 8790Sstevel@tonic-gate * Note: 8800Sstevel@tonic-gate * Any HTTP errors encountered in downloading or processing the message 8810Sstevel@tonic-gate * are not deemed unrecoverable errors. That is, get_miniinfo() should 8820Sstevel@tonic-gate * try re-requesting the message and try processing again. 8830Sstevel@tonic-gate * 8840Sstevel@tonic-gate * Returns: 8850Sstevel@tonic-gate * -1 = Non-recoverable error 8860Sstevel@tonic-gate * 0 = Success 8870Sstevel@tonic-gate */ 8880Sstevel@tonic-gate int 8890Sstevel@tonic-gate get_miniinfo(const url_t *server_url, size_t *mini_size, 8900Sstevel@tonic-gate unsigned char *sdigest) 8910Sstevel@tonic-gate { 8920Sstevel@tonic-gate http_handle_t handle; 8930Sstevel@tonic-gate url_t req_url; 8940Sstevel@tonic-gate int retry_cnt = 0; 8950Sstevel@tonic-gate int retry_max = WANBOOT_RETRY_MAX; 8960Sstevel@tonic-gate int ret; 8970Sstevel@tonic-gate 8980Sstevel@tonic-gate /* 8990Sstevel@tonic-gate * Build the URL to request the miniroot info. 9000Sstevel@tonic-gate */ 9010Sstevel@tonic-gate if (build_request_url(&req_url, URLtype_miniroot, server_url) == -1) { 9020Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 9030Sstevel@tonic-gate "Can't build the URL to make the %s request", 9040Sstevel@tonic-gate CGIcontent(URLtype_miniroot)); 9050Sstevel@tonic-gate return (-1); 9060Sstevel@tonic-gate } 9070Sstevel@tonic-gate 9080Sstevel@tonic-gate /* 9090Sstevel@tonic-gate * Go get the miniroot info. If we fail reading the 9100Sstevel@tonic-gate * response we re-request the info in its entirety. 9110Sstevel@tonic-gate */ 9120Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_VERBOSE, "Downloading miniroot info"); 9130Sstevel@tonic-gate 9140Sstevel@tonic-gate do { 9150Sstevel@tonic-gate if ((ret = establish_http_connection(MINIINFO, &handle, 9160Sstevel@tonic-gate &req_url, 0)) < 0) { 9170Sstevel@tonic-gate break; 9180Sstevel@tonic-gate } else if (ret > 0) { 9190Sstevel@tonic-gate if (wanboot_retry(++retry_cnt, retry_max)) { 9200Sstevel@tonic-gate continue; 9210Sstevel@tonic-gate } else { 9220Sstevel@tonic-gate break; 9230Sstevel@tonic-gate } 9240Sstevel@tonic-gate } 9250Sstevel@tonic-gate 9260Sstevel@tonic-gate if ((ret = process_miniinfo(handle, mini_size, 927*5648Ssetje sdigest)) > 0) { 9280Sstevel@tonic-gate if (!wanboot_retry(++retry_cnt, retry_max)) { 9290Sstevel@tonic-gate (void) http_srv_close(handle); 9300Sstevel@tonic-gate break; 9310Sstevel@tonic-gate } 9320Sstevel@tonic-gate } 9330Sstevel@tonic-gate 9340Sstevel@tonic-gate (void) http_srv_close(handle); 9350Sstevel@tonic-gate 9360Sstevel@tonic-gate } while (ret > 0); 9370Sstevel@tonic-gate 9380Sstevel@tonic-gate /* 9390Sstevel@tonic-gate * Success. 9400Sstevel@tonic-gate */ 9410Sstevel@tonic-gate if (ret == 0) { 9420Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_VERBOSE, 9430Sstevel@tonic-gate "Miniroot info download successful"); 9440Sstevel@tonic-gate return (0); 9450Sstevel@tonic-gate } else { 9460Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 9470Sstevel@tonic-gate "Miniroot info download aborted"); 9480Sstevel@tonic-gate return (-1); 9490Sstevel@tonic-gate } 9500Sstevel@tonic-gate } 9510Sstevel@tonic-gate 9520Sstevel@tonic-gate /* 9530Sstevel@tonic-gate * This routine is called by get_miniroot() to receive the reply to 9540Sstevel@tonic-gate * the request for the miniroot download. The miniroot is written 9550Sstevel@tonic-gate * to ramdisk as it is received and a hash digest is optionally computed 9560Sstevel@tonic-gate * as it does so. The miniroot is downloaded as one large message. 9570Sstevel@tonic-gate * Because the message is so large, this routine is prepared to deal 9580Sstevel@tonic-gate * with errors in the middle of download. If an error occurs during 9590Sstevel@tonic-gate * download, then this message processes all received data up to the 9600Sstevel@tonic-gate * point of the error and returns to get_miniroot() an error signifying 9610Sstevel@tonic-gate * that a download error has occurred. Presumably, get_miniroot() 9620Sstevel@tonic-gate * re-requests the remaining part of the miniroot not yet processed and 9630Sstevel@tonic-gate * calls this routine back to process the reply. When this routine 9640Sstevel@tonic-gate * returns succesfully, it returns a devpath to the ramdisk and the 9650Sstevel@tonic-gate * computed hash (if computed). 9660Sstevel@tonic-gate * 9670Sstevel@tonic-gate * Note: 9680Sstevel@tonic-gate * In order to facilitate reentry, the ramdisk is left open 9690Sstevel@tonic-gate * and the original miniroot_size and HMAC handle are kept 9700Sstevel@tonic-gate * static. 9710Sstevel@tonic-gate * 9720Sstevel@tonic-gate * Returns: 9730Sstevel@tonic-gate * -1 = Non-recoverable error 9740Sstevel@tonic-gate * 0 = Success 9750Sstevel@tonic-gate * 1 = HTTP download error 9760Sstevel@tonic-gate */ 9770Sstevel@tonic-gate static int 9780Sstevel@tonic-gate process_miniroot(http_handle_t handle, hash_type_t htype, 9790Sstevel@tonic-gate size_t length, char **devpath, off_t *offset, unsigned char *cdigest) 9800Sstevel@tonic-gate { 9810Sstevel@tonic-gate static SHA1_CTX sha; 9820Sstevel@tonic-gate static size_t miniroot_size; 983*5648Ssetje static caddr_t miniroot_vaddr = NULL; 9840Sstevel@tonic-gate int ret; 9850Sstevel@tonic-gate 986*5648Ssetje if (miniroot_vaddr == NULL) { 9870Sstevel@tonic-gate if (htype == HASH_HMAC_SHA1) { 9880Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_INFO, 9890Sstevel@tonic-gate "%s: Authentication will use HMAC-SHA1", MINIROOT); 9900Sstevel@tonic-gate HMACInit(&sha, g_hash_key, WANBOOT_HMAC_KEY_SIZE); 9910Sstevel@tonic-gate } 9920Sstevel@tonic-gate 9930Sstevel@tonic-gate miniroot_size = length; 9940Sstevel@tonic-gate 995*5648Ssetje miniroot_vaddr = create_ramdisk(RD_ROOTFS, miniroot_size, 996*5648Ssetje devpath); 9970Sstevel@tonic-gate } 9980Sstevel@tonic-gate 999*5648Ssetje miniroot_vaddr += *offset; 1000*5648Ssetje 1001*5648Ssetje if ((ret = write_msg_to_ramdisk(MINIROOT, miniroot_vaddr, handle, 1002*5648Ssetje miniroot_size, offset, (htype == HASH_NONE) ? NULL : &sha)) != 0) { 10030Sstevel@tonic-gate return (ret); 10040Sstevel@tonic-gate } 10050Sstevel@tonic-gate 10060Sstevel@tonic-gate if (htype != HASH_NONE) { 10070Sstevel@tonic-gate HMACFinal(&sha, g_hash_key, WANBOOT_HMAC_KEY_SIZE, cdigest); 10080Sstevel@tonic-gate } 10090Sstevel@tonic-gate 10100Sstevel@tonic-gate return (0); 10110Sstevel@tonic-gate } 10120Sstevel@tonic-gate 10130Sstevel@tonic-gate /* 10140Sstevel@tonic-gate * This routine retrieves the miniroot from the webserver. The miniroot 10150Sstevel@tonic-gate * is retrieved in two steps. First a request is made to the server 10160Sstevel@tonic-gate * to retrieve miniroot metadata (miniroot size and a hash digest). 10170Sstevel@tonic-gate * The second request actually results in the download of the miniroot. 10180Sstevel@tonic-gate * 10190Sstevel@tonic-gate * This routine relies on get_miniinfo() to make and process 10200Sstevel@tonic-gate * the request for the miniroot metadata and returns the 10210Sstevel@tonic-gate * miniroot size and the hash digest of the miniroot as computed by 10220Sstevel@tonic-gate * the server. 10230Sstevel@tonic-gate * 10240Sstevel@tonic-gate * If get_miniinfo() returns successfully, then this routine sends 10250Sstevel@tonic-gate * an HTTP GET request to the webserver to request download of the 10260Sstevel@tonic-gate * miniroot. This routine relies on process_miniroot() to receive 10270Sstevel@tonic-gate * the reply, process it and ultimately return to it a device path to 10280Sstevel@tonic-gate * a ramdisk containing the miniroot and a client computed hash digest. 10290Sstevel@tonic-gate * This routine verifies that the client computed hash digest matches 10300Sstevel@tonic-gate * the one retrieved by get_miniinfo(). 10310Sstevel@tonic-gate * 10320Sstevel@tonic-gate * If an error occurs in the transfer of the miniroot from the server 10330Sstevel@tonic-gate * to the client, then the client re-requests the download of the 10340Sstevel@tonic-gate * miniroot using a range request and only requests the part of the 10350Sstevel@tonic-gate * miniroot not previously downloaded and written to ramdisk. The 10360Sstevel@tonic-gate * process_miniroot() routine has the intelligence to recognize that 10370Sstevel@tonic-gate * it is processing a range request. Errors not related to the actual 10380Sstevel@tonic-gate * message download are deemed unrecoverable. 10390Sstevel@tonic-gate * 10400Sstevel@tonic-gate * Note: 10410Sstevel@tonic-gate * If the client request for the miniroot is a secure request or 10420Sstevel@tonic-gate * if the server is not configured with a hash key for the client, 10430Sstevel@tonic-gate * then the hash digest downloaded from the server will contain 10440Sstevel@tonic-gate * all zeros. This routine verifies that the server and client are 10450Sstevel@tonic-gate * in-sync with respect to the need for hash verification. 10460Sstevel@tonic-gate * 10470Sstevel@tonic-gate * Returns: 10480Sstevel@tonic-gate * -1 = Non-recoverable error 10490Sstevel@tonic-gate * 0 = Success 10500Sstevel@tonic-gate */ 10510Sstevel@tonic-gate int 10520Sstevel@tonic-gate get_miniroot(char **devpath) 10530Sstevel@tonic-gate { 10540Sstevel@tonic-gate http_handle_t handle; 10550Sstevel@tonic-gate unsigned char cdigest[HMAC_DIGEST_LEN]; 10560Sstevel@tonic-gate unsigned char sdigest[HMAC_DIGEST_LEN]; 10570Sstevel@tonic-gate char *urlstr; 10580Sstevel@tonic-gate url_t server_url; 10590Sstevel@tonic-gate size_t mini_size; 10600Sstevel@tonic-gate off_t offset; 10610Sstevel@tonic-gate int plen; 10620Sstevel@tonic-gate int retry_cnt = 0; 10630Sstevel@tonic-gate int retry_max = WANBOOT_RETRY_ROOT_MAX; 10640Sstevel@tonic-gate int ret; 10650Sstevel@tonic-gate 10660Sstevel@tonic-gate /* 10670Sstevel@tonic-gate * Get the miniroot URL. 10680Sstevel@tonic-gate */ 10690Sstevel@tonic-gate if ((urlstr = bootconf_get(&bc_handle, BC_ROOT_SERVER)) == NULL) { 10700Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 10710Sstevel@tonic-gate "Missing root_server URL"); 10720Sstevel@tonic-gate return (-1); 10730Sstevel@tonic-gate } else if (url_parse(urlstr, &server_url) != URL_PARSE_SUCCESS) { 10740Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 10750Sstevel@tonic-gate "Unable to parse URL %s", urlstr); 10760Sstevel@tonic-gate return (-1); 10770Sstevel@tonic-gate } 10780Sstevel@tonic-gate 10790Sstevel@tonic-gate /* 10800Sstevel@tonic-gate * We must get the miniroot info before we can request 10810Sstevel@tonic-gate * the miniroot itself. 10820Sstevel@tonic-gate */ 10830Sstevel@tonic-gate if (get_miniinfo(&server_url, &mini_size, sdigest) != 0) { 10840Sstevel@tonic-gate return (-1); 10850Sstevel@tonic-gate } 10860Sstevel@tonic-gate 10870Sstevel@tonic-gate plen = sizeof (server_url.abspath); 10880Sstevel@tonic-gate if ((urlstr = bootconf_get(&bc_handle, BC_ROOT_FILE)) == NULL || 10890Sstevel@tonic-gate strlcpy(server_url.abspath, urlstr, plen) >= plen) { 10900Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 10910Sstevel@tonic-gate "Cannot retrieve the miniroot path"); 10920Sstevel@tonic-gate return (-1); 10930Sstevel@tonic-gate } 10940Sstevel@tonic-gate 10950Sstevel@tonic-gate /* 10960Sstevel@tonic-gate * Go get the miniroot. If we fail reading the response 10970Sstevel@tonic-gate * then we re-request only the range we have yet to read, 10980Sstevel@tonic-gate * unless the error was "unrecoverable" in which case we 10990Sstevel@tonic-gate * re-request the entire file system. 11000Sstevel@tonic-gate */ 11010Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_VERBOSE, "Downloading miniroot"); 11020Sstevel@tonic-gate 11030Sstevel@tonic-gate bzero(cdigest, sizeof (cdigest)); 11040Sstevel@tonic-gate offset = 0; 11050Sstevel@tonic-gate do { 11060Sstevel@tonic-gate if ((ret = establish_http_connection(MINIROOT, &handle, 11070Sstevel@tonic-gate &server_url, offset)) < 0) { 11080Sstevel@tonic-gate break; 11090Sstevel@tonic-gate } else if (ret > 0) { 11100Sstevel@tonic-gate if (wanboot_retry(++retry_cnt, retry_max)) { 11110Sstevel@tonic-gate continue; 11120Sstevel@tonic-gate } else { 11130Sstevel@tonic-gate break; 11140Sstevel@tonic-gate } 11150Sstevel@tonic-gate } 11160Sstevel@tonic-gate 11170Sstevel@tonic-gate if ((ret = process_miniroot(handle, 11180Sstevel@tonic-gate server_url.https ? HASH_NONE : hash_type, 11190Sstevel@tonic-gate mini_size, devpath, &offset, cdigest)) > 0) { 11200Sstevel@tonic-gate if (!wanboot_retry(++retry_cnt, retry_max)) { 11210Sstevel@tonic-gate (void) http_srv_close(handle); 11220Sstevel@tonic-gate break; 11230Sstevel@tonic-gate } 11240Sstevel@tonic-gate } 11250Sstevel@tonic-gate 11260Sstevel@tonic-gate (void) http_srv_close(handle); 11270Sstevel@tonic-gate 11280Sstevel@tonic-gate } while (ret > 0); 11290Sstevel@tonic-gate 11300Sstevel@tonic-gate /* 11310Sstevel@tonic-gate * Validate the computed digest against the one received. 11320Sstevel@tonic-gate */ 11330Sstevel@tonic-gate if (ret != 0 || !verify_digests(MINIROOT, cdigest, sdigest)) { 11340Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 11350Sstevel@tonic-gate "Miniroot download aborted"); 11360Sstevel@tonic-gate return (-1); 11370Sstevel@tonic-gate } 11380Sstevel@tonic-gate 11390Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_VERBOSE, "Miniroot download successful"); 11400Sstevel@tonic-gate return (0); 11410Sstevel@tonic-gate } 11420Sstevel@tonic-gate 11430Sstevel@tonic-gate /* 11440Sstevel@tonic-gate * This routine is called to finish the decryption process. 11450Sstevel@tonic-gate * Its purpose is to free the resources allocated by the 11460Sstevel@tonic-gate * encryption init routines. 11470Sstevel@tonic-gate */ 11480Sstevel@tonic-gate static void 11490Sstevel@tonic-gate encr_fini(encr_type_t etype, void *eh) 11500Sstevel@tonic-gate { 11510Sstevel@tonic-gate switch (etype) { 11520Sstevel@tonic-gate case ENCR_3DES: 11530Sstevel@tonic-gate des3_fini(eh); 11540Sstevel@tonic-gate break; 11550Sstevel@tonic-gate case ENCR_AES: 11560Sstevel@tonic-gate aes_fini(eh); 11570Sstevel@tonic-gate break; 11580Sstevel@tonic-gate default: 11590Sstevel@tonic-gate break; 11600Sstevel@tonic-gate } 11610Sstevel@tonic-gate } 11620Sstevel@tonic-gate 11630Sstevel@tonic-gate /* 1164*5648Ssetje * This routine is called by process_wanbootfs() to decrypt the encrypted 1165*5648Ssetje * file system from ramdisk in place. The method of decryption 11660Sstevel@tonic-gate * (algorithm) will have already been determined by process_wanbootfs() 11670Sstevel@tonic-gate * and the cbc_handle passed to this routine will already have been 11680Sstevel@tonic-gate * initialized appropriately. 11690Sstevel@tonic-gate * 11700Sstevel@tonic-gate * Returns: 11710Sstevel@tonic-gate * -1 = Non-recoverable error 11720Sstevel@tonic-gate * 0 = Success 11730Sstevel@tonic-gate */ 11740Sstevel@tonic-gate static int 1175*5648Ssetje decrypt_wanbootfs(caddr_t addr, cbc_handle_t *ch, uint8_t *iv, 1176*5648Ssetje size_t wanbootfs_size) 11770Sstevel@tonic-gate { 1178*5648Ssetje if (!cbc_decrypt(ch, (uint8_t *)addr, wanbootfs_size, iv)) { 1179*5648Ssetje bootlog("wanboot", BOOTLOG_CRIT, 1180*5648Ssetje "%s: cbc decrypt error", WANBOOTFS); 1181*5648Ssetje return (-1); 11820Sstevel@tonic-gate } 11830Sstevel@tonic-gate return (0); 11840Sstevel@tonic-gate } 11850Sstevel@tonic-gate 11860Sstevel@tonic-gate /* 11870Sstevel@tonic-gate * This routine is called by get_wanbootfs() to receive the reply to 11880Sstevel@tonic-gate * the request for the wanboot file system. The reply is a multipart message. 11890Sstevel@tonic-gate * The first part of the message is the file system (which may or may 11900Sstevel@tonic-gate * not be encrypted). If encrypted, then the first block of the message 11910Sstevel@tonic-gate * part is the CBC IV value used by the server to encrypt the remaining 11920Sstevel@tonic-gate * part of the message part and is used by the client to decrypt it. The 11930Sstevel@tonic-gate * second message part is a hash digest of the first part (the file 11940Sstevel@tonic-gate * system) as computed by the server. If no hash key is configured 11950Sstevel@tonic-gate * for the client, then the hash digest simply contains all zeros. This 11960Sstevel@tonic-gate * routine receives both message parts. The file system is written to ramdisk 11970Sstevel@tonic-gate * as it is received and simultaneously computes a hash digest (if a hash 11980Sstevel@tonic-gate * key exists). Once the entire part is received, if the file system is 11990Sstevel@tonic-gate * encrypted, it is read from ramdisk, decrypted and rewritten back to 12000Sstevel@tonic-gate * ramdisk. The server computed hash digest is then read and along with the 12010Sstevel@tonic-gate * ramdisk device path and the client computed hash digest is returned to the 12020Sstevel@tonic-gate * caller. 12030Sstevel@tonic-gate * 12040Sstevel@tonic-gate * Notes: 12050Sstevel@tonic-gate * In order to decrypt the file system and to compute the client 12060Sstevel@tonic-gate * hash digest, an encryption key and a hash key is retrieved from 12070Sstevel@tonic-gate * the PROM (or the wanboot interpreter). The non-existence of these 12080Sstevel@tonic-gate * keys has implications on how the message response is processed and 12090Sstevel@tonic-gate * it is assumed that the server is configured identically. 12100Sstevel@tonic-gate * 12110Sstevel@tonic-gate * Any HTTP errors encountered in downloading or processing the message 12120Sstevel@tonic-gate * are not deemed unrecoverable errors. That is, get_wanbootfs() will 12130Sstevel@tonic-gate * try re-requesting the message and will try processing it again. 12140Sstevel@tonic-gate * 12150Sstevel@tonic-gate * Returns: 12160Sstevel@tonic-gate * -1 = Non-recoverable error 12170Sstevel@tonic-gate * 0 = Success 12180Sstevel@tonic-gate * 1 = HTTP download error 12190Sstevel@tonic-gate */ 12200Sstevel@tonic-gate static int 12210Sstevel@tonic-gate process_wanbootfs(http_handle_t handle, char **devpath, 12220Sstevel@tonic-gate unsigned char *cdigest, unsigned char *sdigest) 12230Sstevel@tonic-gate { 12240Sstevel@tonic-gate /* iv[] must be sized to store the largest possible encryption block */ 12250Sstevel@tonic-gate uint8_t iv[WANBOOT_MAXBLOCKLEN]; 12260Sstevel@tonic-gate cbc_handle_t ch; 12270Sstevel@tonic-gate void *eh; 12280Sstevel@tonic-gate SHA1_CTX sha; 12290Sstevel@tonic-gate char *lenstr; 12300Sstevel@tonic-gate size_t wanbootfs_size; 12310Sstevel@tonic-gate size_t block_size; 12320Sstevel@tonic-gate off_t offset; 1233*5648Ssetje static caddr_t bootfs_vaddr = NULL; 12340Sstevel@tonic-gate int ret; 12350Sstevel@tonic-gate 12360Sstevel@tonic-gate switch (hash_type) { 12370Sstevel@tonic-gate case HASH_HMAC_SHA1: 12380Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_INFO, 12390Sstevel@tonic-gate "%s: Authentication will use HMAC-SHA1", WANBOOTFS); 12400Sstevel@tonic-gate HMACInit(&sha, g_hash_key, WANBOOT_HMAC_KEY_SIZE); 12410Sstevel@tonic-gate break; 12420Sstevel@tonic-gate case HASH_NONE: 12430Sstevel@tonic-gate break; 12440Sstevel@tonic-gate default: 12450Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 12460Sstevel@tonic-gate "%s: unrecognized hash type", WANBOOTFS); 12470Sstevel@tonic-gate return (-1); 12480Sstevel@tonic-gate } 12490Sstevel@tonic-gate 12500Sstevel@tonic-gate switch (encr_type) { 12510Sstevel@tonic-gate case ENCR_3DES: 12520Sstevel@tonic-gate bootlog("wanboot", 12530Sstevel@tonic-gate BOOTLOG_INFO, "%s: Decryption will use 3DES", WANBOOTFS); 12540Sstevel@tonic-gate if (des3_init(&eh) != 0) { 12550Sstevel@tonic-gate return (-1); 12560Sstevel@tonic-gate } 12570Sstevel@tonic-gate block_size = DES3_BLOCK_SIZE; 12580Sstevel@tonic-gate des3_key(eh, g_encr_key); 12590Sstevel@tonic-gate cbc_makehandle(&ch, eh, DES3_KEY_SIZE, block_size, 12600Sstevel@tonic-gate DES3_IV_SIZE, des3_encrypt, des3_decrypt); 12610Sstevel@tonic-gate 12620Sstevel@tonic-gate break; 12630Sstevel@tonic-gate case ENCR_AES: 12640Sstevel@tonic-gate bootlog("wanboot", 12650Sstevel@tonic-gate BOOTLOG_INFO, "%s: Decryption will use AES", WANBOOTFS); 12660Sstevel@tonic-gate if (aes_init(&eh) != 0) { 12670Sstevel@tonic-gate return (-1); 12680Sstevel@tonic-gate } 12690Sstevel@tonic-gate block_size = AES_BLOCK_SIZE; 12700Sstevel@tonic-gate aes_key(eh, g_encr_key, AES_128_KEY_SIZE); 12710Sstevel@tonic-gate cbc_makehandle(&ch, eh, AES_128_KEY_SIZE, block_size, 12720Sstevel@tonic-gate AES_IV_SIZE, aes_encrypt, aes_decrypt); 12730Sstevel@tonic-gate break; 12740Sstevel@tonic-gate case ENCR_NONE: 12750Sstevel@tonic-gate break; 12760Sstevel@tonic-gate default: 12770Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 12780Sstevel@tonic-gate "%s: unrecognized encryption type", WANBOOTFS); 12790Sstevel@tonic-gate return (-1); 12800Sstevel@tonic-gate } 12810Sstevel@tonic-gate 12820Sstevel@tonic-gate /* 12830Sstevel@tonic-gate * Process the header. 12840Sstevel@tonic-gate */ 12850Sstevel@tonic-gate if (http_process_part_headers(handle, NULL) != 0) { 12860Sstevel@tonic-gate print_errors("http_process_part_headers", handle); 12870Sstevel@tonic-gate return (1); 12880Sstevel@tonic-gate } 12890Sstevel@tonic-gate lenstr = http_get_header_value(handle, CONTENT_LENGTH); 12900Sstevel@tonic-gate if (lenstr == NULL) { 12910Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_ALERT, "%s: error getting length " 12920Sstevel@tonic-gate "of first part of multipart message", WANBOOTFS); 12930Sstevel@tonic-gate return (1); 12940Sstevel@tonic-gate } 12950Sstevel@tonic-gate wanbootfs_size = (size_t)strtol(lenstr, NULL, 10); 12960Sstevel@tonic-gate free(lenstr); 12970Sstevel@tonic-gate if (wanbootfs_size == 0) { 12980Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_ALERT, "%s: length of first part " 12990Sstevel@tonic-gate "of multipart message not a legal size", WANBOOTFS); 13000Sstevel@tonic-gate return (1); 13010Sstevel@tonic-gate } 13020Sstevel@tonic-gate 13030Sstevel@tonic-gate /* 13040Sstevel@tonic-gate * If encrypted, then read the iv. 13050Sstevel@tonic-gate */ 13060Sstevel@tonic-gate if (encr_type != ENCR_NONE) { 13070Sstevel@tonic-gate if (read_bytes(handle, (char *)iv, block_size) != 0) { 13080Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_ALERT, 13090Sstevel@tonic-gate "%s: error reading hash iv", WANBOOTFS); 13100Sstevel@tonic-gate return (1); 13110Sstevel@tonic-gate } 13120Sstevel@tonic-gate wanbootfs_size -= block_size; 13130Sstevel@tonic-gate if (hash_type != HASH_NONE) { 13140Sstevel@tonic-gate HMACUpdate(&sha, (uchar_t *)iv, block_size); 13150Sstevel@tonic-gate } 13160Sstevel@tonic-gate } 13170Sstevel@tonic-gate 13180Sstevel@tonic-gate /* 13190Sstevel@tonic-gate * We can only create the ramdisk once. So, if we've 13200Sstevel@tonic-gate * already created it, then it means we've re-entered 13210Sstevel@tonic-gate * this routine from an earlier partial failure. Use 13220Sstevel@tonic-gate * the already existing ramdisk and seek back to the 13230Sstevel@tonic-gate * beginning of the file. 13240Sstevel@tonic-gate */ 1325*5648Ssetje if (bootfs_vaddr == NULL) { 1326*5648Ssetje bootfs_vaddr = create_ramdisk(RD_BOOTFS, wanbootfs_size, 1327*5648Ssetje devpath); 13280Sstevel@tonic-gate } 13290Sstevel@tonic-gate 13300Sstevel@tonic-gate offset = 0; 13310Sstevel@tonic-gate 1332*5648Ssetje if ((ret = write_msg_to_ramdisk(WANBOOTFS, bootfs_vaddr, handle, 1333*5648Ssetje wanbootfs_size, &offset, (hash_type == HASH_NONE) ? NULL : &sha)) 1334*5648Ssetje != 0) { 13350Sstevel@tonic-gate return (ret); 13360Sstevel@tonic-gate } 13370Sstevel@tonic-gate 13380Sstevel@tonic-gate if (hash_type != HASH_NONE) { 13390Sstevel@tonic-gate HMACFinal(&sha, g_hash_key, WANBOOT_HMAC_KEY_SIZE, cdigest); 13400Sstevel@tonic-gate } 13410Sstevel@tonic-gate 13420Sstevel@tonic-gate /* 13430Sstevel@tonic-gate * If encrypted, then decrypt it. 13440Sstevel@tonic-gate */ 13450Sstevel@tonic-gate if (encr_type != ENCR_NONE) { 1346*5648Ssetje ret = decrypt_wanbootfs(bootfs_vaddr, &ch, iv, wanbootfs_size); 13470Sstevel@tonic-gate if (ret != 0) { 13480Sstevel@tonic-gate encr_fini(encr_type, eh); 13490Sstevel@tonic-gate return (-1); 13500Sstevel@tonic-gate } 13510Sstevel@tonic-gate encr_fini(encr_type, eh); 13520Sstevel@tonic-gate } 13530Sstevel@tonic-gate 13540Sstevel@tonic-gate return (read_digest(WANBOOTFS, handle, sdigest)); 13550Sstevel@tonic-gate } 13560Sstevel@tonic-gate 13570Sstevel@tonic-gate /* 13580Sstevel@tonic-gate * This routine sends an HTTP GET request to the webserver to 13590Sstevel@tonic-gate * request the wanboot file system for the client. The server 13600Sstevel@tonic-gate * will reply by sending a multipart message. This routine will rely 13610Sstevel@tonic-gate * on process_wanbootfs() to receive the multipart message, process it 13620Sstevel@tonic-gate * and ultimately return to it a device path to a ramdisk containing 13630Sstevel@tonic-gate * the wanboot file system, a client computed hash digest and a 13640Sstevel@tonic-gate * server computed hash digest. This routine will verify that the 13650Sstevel@tonic-gate * client computed hash digest matches the one sent by the server. This 13660Sstevel@tonic-gate * routine will also verify that the nonce received in the reply matches 13670Sstevel@tonic-gate * the one sent in the request. 13680Sstevel@tonic-gate * 13690Sstevel@tonic-gate * If an error occurs in the transfer of the message from the server 13700Sstevel@tonic-gate * to the client, then the client re-requests the download in its 13710Sstevel@tonic-gate * entirety. Errors not related to the actual message download are 13720Sstevel@tonic-gate * deemed unrecoverable. 13730Sstevel@tonic-gate * 13740Sstevel@tonic-gate * Returns: 13750Sstevel@tonic-gate * -1 = Non-recoverable error 13760Sstevel@tonic-gate * 0 = Success 13770Sstevel@tonic-gate */ 13780Sstevel@tonic-gate int 13790Sstevel@tonic-gate get_wanbootfs(const url_t *server_url) 13800Sstevel@tonic-gate { 13810Sstevel@tonic-gate http_handle_t handle; 13820Sstevel@tonic-gate unsigned char cdigest[HMAC_DIGEST_LEN]; 13830Sstevel@tonic-gate unsigned char sdigest[HMAC_DIGEST_LEN]; 13840Sstevel@tonic-gate url_t req_url; 13850Sstevel@tonic-gate char *devpath; 13860Sstevel@tonic-gate int ret; 13870Sstevel@tonic-gate int fd; 13880Sstevel@tonic-gate char buf[NONCELEN + 1]; 13890Sstevel@tonic-gate int retry_cnt = 0; 13900Sstevel@tonic-gate int retry_max = WANBOOT_RETRY_MAX; 13910Sstevel@tonic-gate 13920Sstevel@tonic-gate /* 13930Sstevel@tonic-gate * Build the URL to request the wanboot file system. This URL 13940Sstevel@tonic-gate * will include the CGI script name and the IP, CID, and 13950Sstevel@tonic-gate * NONCE parameters. 13960Sstevel@tonic-gate */ 13970Sstevel@tonic-gate if (build_request_url(&req_url, URLtype_wanbootfs, server_url) == -1) { 13980Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 13990Sstevel@tonic-gate "Can't build the URL to make the %s request", 14000Sstevel@tonic-gate CGIcontent(URLtype_wanbootfs)); 14010Sstevel@tonic-gate return (-1); 14020Sstevel@tonic-gate } 14030Sstevel@tonic-gate 14040Sstevel@tonic-gate /* 14050Sstevel@tonic-gate * Go get the wanboot file system. If we fail reading the 14060Sstevel@tonic-gate * response we re-request the entire file system. 14070Sstevel@tonic-gate */ 14080Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_VERBOSE, "Downloading wanboot file system"); 14090Sstevel@tonic-gate 14100Sstevel@tonic-gate bzero(cdigest, sizeof (cdigest)); 14110Sstevel@tonic-gate do { 14120Sstevel@tonic-gate if ((ret = establish_http_connection(WANBOOTFS, &handle, 1413*5648Ssetje &req_url, 0)) < 0) { 14140Sstevel@tonic-gate break; 14150Sstevel@tonic-gate } else if (ret > 0) { 14160Sstevel@tonic-gate if (wanboot_retry(++retry_cnt, retry_max)) { 14170Sstevel@tonic-gate continue; 14180Sstevel@tonic-gate } else { 14190Sstevel@tonic-gate break; 14200Sstevel@tonic-gate } 14210Sstevel@tonic-gate } 14220Sstevel@tonic-gate 14230Sstevel@tonic-gate if ((ret = process_wanbootfs(handle, &devpath, 1424*5648Ssetje cdigest, sdigest)) > 0) { 14250Sstevel@tonic-gate if (!wanboot_retry(++retry_cnt, retry_max)) { 14260Sstevel@tonic-gate (void) http_srv_close(handle); 14270Sstevel@tonic-gate break; 14280Sstevel@tonic-gate } 14290Sstevel@tonic-gate } 14300Sstevel@tonic-gate 14310Sstevel@tonic-gate (void) http_srv_close(handle); 14320Sstevel@tonic-gate 14330Sstevel@tonic-gate } while (ret > 0); 14340Sstevel@tonic-gate 14350Sstevel@tonic-gate /* 14360Sstevel@tonic-gate * Validate the computed digest against the one received. 14370Sstevel@tonic-gate */ 14380Sstevel@tonic-gate if (ret != 0 || 1439*5648Ssetje !verify_digests(WANBOOTFS, cdigest, sdigest)) { 14400Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 14410Sstevel@tonic-gate "The wanboot file system download aborted"); 14420Sstevel@tonic-gate return (-1); 14430Sstevel@tonic-gate } 14440Sstevel@tonic-gate 14450Sstevel@tonic-gate /* 14460Sstevel@tonic-gate * Mount the wanboot file system. 14470Sstevel@tonic-gate */ 14480Sstevel@tonic-gate if (determine_fstype_and_mountroot(devpath) != VFS_SUCCESS) { 14490Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 14500Sstevel@tonic-gate "Could not mount the wanboot filesystem."); 14510Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 14520Sstevel@tonic-gate "This may signify a client/server key mismatch"); 14530Sstevel@tonic-gate if (encr_type != ENCR_NONE) { 14540Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 14550Sstevel@tonic-gate "(client has key but wrong encryption_type?)"); 14560Sstevel@tonic-gate } else { 14570Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 14580Sstevel@tonic-gate "(encryption_type specified but no client key?)"); 14590Sstevel@tonic-gate } 14600Sstevel@tonic-gate return (-1); 14610Sstevel@tonic-gate } 14620Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_VERBOSE, 14630Sstevel@tonic-gate "The wanboot file system has been mounted"); 14640Sstevel@tonic-gate 14650Sstevel@tonic-gate /* 14660Sstevel@tonic-gate * The wanboot file system should contain a nonce. Read it 14670Sstevel@tonic-gate * and compare it against the nonce sent in the request. 14680Sstevel@tonic-gate */ 14690Sstevel@tonic-gate if ((fd = open(WANBOOTFS_NONCE_FILE, O_RDONLY)) == -1) { 14700Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 1471*5648Ssetje "No nonce found in the wanboot file system"); 14720Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 14730Sstevel@tonic-gate "The wanboot file system download aborted"); 14740Sstevel@tonic-gate return (-1); 14750Sstevel@tonic-gate } 14760Sstevel@tonic-gate 14770Sstevel@tonic-gate if (read(fd, buf, NONCELEN) != NONCELEN || 14780Sstevel@tonic-gate bcmp(nonce, buf, NONCELEN) != 0) { 14790Sstevel@tonic-gate (void) close(fd); 14800Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 1481*5648Ssetje "Invalid nonce found in the wanboot file system"); 14820Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 14830Sstevel@tonic-gate "The wanboot file system download aborted"); 14840Sstevel@tonic-gate return (-1); 14850Sstevel@tonic-gate } 14860Sstevel@tonic-gate 14870Sstevel@tonic-gate (void) close(fd); 14880Sstevel@tonic-gate 14890Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_VERBOSE, 14900Sstevel@tonic-gate "The wanboot file system download was successful"); 14910Sstevel@tonic-gate return (0); 14920Sstevel@tonic-gate } 14930Sstevel@tonic-gate 14940Sstevel@tonic-gate static boolean_t 14950Sstevel@tonic-gate init_netdev(char *bpath) 14960Sstevel@tonic-gate { 1497789Sahrens pnode_t anode; 14980Sstevel@tonic-gate int proplen; 14990Sstevel@tonic-gate static char netalias[OBP_MAXPATHLEN]; 15000Sstevel@tonic-gate 15010Sstevel@tonic-gate /* 15020Sstevel@tonic-gate * Wanboot will either have loaded over the network (in which case 15030Sstevel@tonic-gate * bpath will name a network device), or from CD-ROM or disk. In 15040Sstevel@tonic-gate * both cases ensure that the 'net' alias corresponds to a network 15050Sstevel@tonic-gate * device, and that if a network boot was performed that it is 15060Sstevel@tonic-gate * identical to bpath. This is so that the interface name can always 15070Sstevel@tonic-gate * be determined for CD-ROM or disk boots, and for manually-configured 15080Sstevel@tonic-gate * network boots. The latter restriction may be relaxed in the future. 15090Sstevel@tonic-gate */ 15100Sstevel@tonic-gate anode = prom_alias_node(); 15110Sstevel@tonic-gate if ((proplen = prom_getproplen(anode, "net")) > 0 && 15120Sstevel@tonic-gate proplen < sizeof (netalias)) { 15130Sstevel@tonic-gate (void) prom_getprop(anode, "net", (caddr_t)netalias); 15140Sstevel@tonic-gate 15150Sstevel@tonic-gate if (is_netdev(netalias)) { 15160Sstevel@tonic-gate char *p; 15170Sstevel@tonic-gate 15180Sstevel@tonic-gate /* 15190Sstevel@tonic-gate * Strip device arguments from netalias[]. 15200Sstevel@tonic-gate */ 15210Sstevel@tonic-gate if ((p = strchr(netalias, ':')) != NULL) { 15220Sstevel@tonic-gate *p = '\0'; 15230Sstevel@tonic-gate } 15240Sstevel@tonic-gate 15250Sstevel@tonic-gate /* 15260Sstevel@tonic-gate * If bpath is a network device path, then v2path 15270Sstevel@tonic-gate * will be a copy of this sans device arguments. 15280Sstevel@tonic-gate */ 15290Sstevel@tonic-gate if (!is_netdev(bpath) || 15300Sstevel@tonic-gate strcmp(v2path, netalias) == 0) { 15310Sstevel@tonic-gate /* 15320Sstevel@tonic-gate * Stash the netdev_path bootprop value, then 15330Sstevel@tonic-gate * initialize the hardware and return success. 15340Sstevel@tonic-gate */ 15350Sstevel@tonic-gate netdev_path = netalias; 15360Sstevel@tonic-gate mac_init(netalias); 15370Sstevel@tonic-gate return (B_TRUE); 15380Sstevel@tonic-gate } 15390Sstevel@tonic-gate 15400Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 15410Sstevel@tonic-gate "wanboot requires that the 'net' alias refers to "); 15420Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 15430Sstevel@tonic-gate "the network device path from which it loaded"); 15440Sstevel@tonic-gate return (B_FALSE); 15450Sstevel@tonic-gate } 15460Sstevel@tonic-gate } 15470Sstevel@tonic-gate 15480Sstevel@tonic-gate /* 15490Sstevel@tonic-gate * If we haven't established a device path for a network interface, 15500Sstevel@tonic-gate * then we're doomed. 15510Sstevel@tonic-gate */ 15520Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 15530Sstevel@tonic-gate "No network device available for wanboot!"); 15540Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 15550Sstevel@tonic-gate "(Ensure that the 'net' alias is set correctly)"); 15560Sstevel@tonic-gate return (B_FALSE); 15570Sstevel@tonic-gate } 15580Sstevel@tonic-gate 15590Sstevel@tonic-gate /* 15600Sstevel@tonic-gate * This implementation of bootprog() is used solely by wanboot. 15610Sstevel@tonic-gate * 15620Sstevel@tonic-gate * The basic algorithm is as follows: 15630Sstevel@tonic-gate * 15640Sstevel@tonic-gate * - The wanboot options (those specified using the "-o" flag) are processed, 15650Sstevel@tonic-gate * and if necessary the wanboot interpreter is invoked to collect other 15660Sstevel@tonic-gate * options. 15670Sstevel@tonic-gate * 15680Sstevel@tonic-gate * - The wanboot filesystem (containing certificates, wanboot.conf file, etc.) 15690Sstevel@tonic-gate * is then downloaded into the bootfs ramdisk, which is mounted for use 15700Sstevel@tonic-gate * by OpenSSL, access to wanboot.conf, etc. 15710Sstevel@tonic-gate * 15720Sstevel@tonic-gate * - The wanboot miniroot is downloaded over http/https into the rootfs 15730Sstevel@tonic-gate * ramdisk. The bootfs filesystem is unmounted, and the rootfs filesystem 1574*5648Ssetje * is booted. 15750Sstevel@tonic-gate */ 15760Sstevel@tonic-gate /* EXPORT DELETE END */ 15770Sstevel@tonic-gate /*ARGSUSED*/ 15780Sstevel@tonic-gate int 15790Sstevel@tonic-gate bootprog(char *bpath, char *bargs, boolean_t user_specified_filename) 15800Sstevel@tonic-gate { 15810Sstevel@tonic-gate /* EXPORT DELETE START */ 15820Sstevel@tonic-gate char *miniroot_path; 15830Sstevel@tonic-gate url_t server_url; 15840Sstevel@tonic-gate int ret; 15850Sstevel@tonic-gate 15860Sstevel@tonic-gate if (!init_netdev(bpath)) { 15870Sstevel@tonic-gate return (-1); 15880Sstevel@tonic-gate } 15890Sstevel@tonic-gate 15900Sstevel@tonic-gate if (!bootinfo_init()) { 15910Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, "Cannot initialize bootinfo"); 15920Sstevel@tonic-gate return (-1); 15930Sstevel@tonic-gate } 15940Sstevel@tonic-gate 15950Sstevel@tonic-gate /* 15960Sstevel@tonic-gate * Get default values from PROM, etc., process any boot arguments 15970Sstevel@tonic-gate * (specified with the "-o" option), and initialize the interface. 15980Sstevel@tonic-gate */ 15990Sstevel@tonic-gate if (!wanboot_init_interface(wanboot_arguments)) { 16000Sstevel@tonic-gate return (-1); 16010Sstevel@tonic-gate } 16020Sstevel@tonic-gate 16030Sstevel@tonic-gate /* 16040Sstevel@tonic-gate * Determine which encryption and hashing algorithms the client 16050Sstevel@tonic-gate * is configured to use. 16060Sstevel@tonic-gate */ 16070Sstevel@tonic-gate init_encryption(); 16080Sstevel@tonic-gate init_hashing(); 16090Sstevel@tonic-gate 16100Sstevel@tonic-gate /* 16110Sstevel@tonic-gate * Get the bootserver value. Should be of the form: 16120Sstevel@tonic-gate * http://host[:port]/abspath. 16130Sstevel@tonic-gate */ 16140Sstevel@tonic-gate ret = get_url(BI_BOOTSERVER, &server_url); 16150Sstevel@tonic-gate if (ret != 0) { 16160Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 16170Sstevel@tonic-gate "Unable to retrieve the bootserver URL"); 16180Sstevel@tonic-gate return (-1); 16190Sstevel@tonic-gate } 16200Sstevel@tonic-gate 16210Sstevel@tonic-gate /* 16220Sstevel@tonic-gate * Get the wanboot file system and mount it. Contains metdata 16230Sstevel@tonic-gate * needed by wanboot. 16240Sstevel@tonic-gate */ 16250Sstevel@tonic-gate if (get_wanbootfs(&server_url) != 0) { 16260Sstevel@tonic-gate return (-1); 16270Sstevel@tonic-gate } 16280Sstevel@tonic-gate 16290Sstevel@tonic-gate /* 16300Sstevel@tonic-gate * Check that there is a valid wanboot.conf file in the wanboot 16310Sstevel@tonic-gate * file system. 16320Sstevel@tonic-gate */ 16330Sstevel@tonic-gate if (bootconf_init(&bc_handle, NULL) != BC_E_NOERROR) { 16340Sstevel@tonic-gate bootlog("wanboot", BOOTLOG_CRIT, 16350Sstevel@tonic-gate "wanboot.conf error (code=%d)", bc_handle.bc_error_code); 16360Sstevel@tonic-gate return (-1); 16370Sstevel@tonic-gate } 16380Sstevel@tonic-gate 16390Sstevel@tonic-gate /* 16400Sstevel@tonic-gate * Set the time 16410Sstevel@tonic-gate */ 16420Sstevel@tonic-gate init_boot_time(); 16430Sstevel@tonic-gate 16440Sstevel@tonic-gate /* 16450Sstevel@tonic-gate * Verify that URLs in wanboot.conf can be reached, etc. 16460Sstevel@tonic-gate */ 16470Sstevel@tonic-gate if (!wanboot_verify_config()) { 16480Sstevel@tonic-gate return (-1); 16490Sstevel@tonic-gate } 16500Sstevel@tonic-gate 16510Sstevel@tonic-gate /* 16520Sstevel@tonic-gate * Retrieve the miniroot. 16530Sstevel@tonic-gate */ 16540Sstevel@tonic-gate if (get_miniroot(&miniroot_path) != 0) { 16550Sstevel@tonic-gate return (-1); 16560Sstevel@tonic-gate } 16570Sstevel@tonic-gate 16580Sstevel@tonic-gate /* 16590Sstevel@tonic-gate * We don't need the wanboot file system mounted anymore and 16600Sstevel@tonic-gate * should unmount it so that we can mount the miniroot. 16610Sstevel@tonic-gate */ 16620Sstevel@tonic-gate (void) unmountroot(); 16630Sstevel@tonic-gate 1664*5648Ssetje boot_ramdisk(RD_ROOTFS); 16650Sstevel@tonic-gate 16660Sstevel@tonic-gate /* EXPORT DELETE END */ 16670Sstevel@tonic-gate return (0); 16680Sstevel@tonic-gate } 1669