diff options
Diffstat (limited to 'usr/src/psm/stand/boot/sparc/common/wbcli.c')
| -rw-r--r-- | usr/src/psm/stand/boot/sparc/common/wbcli.c | 1425 |
1 files changed, 1425 insertions, 0 deletions
diff --git a/usr/src/psm/stand/boot/sparc/common/wbcli.c b/usr/src/psm/stand/boot/sparc/common/wbcli.c new file mode 100644 index 0000000000..f6d6d9a00a --- /dev/null +++ b/usr/src/psm/stand/boot/sparc/common/wbcli.c @@ -0,0 +1,1425 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* EXPORT DELETE START */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/salib.h> +#include <sys/promif.h> +#include <sys/wanboot_impl.h> +#include <netinet/in.h> +#include <parseURL.h> +#include <bootlog.h> +#include <sys/socket.h> +#include <netinet/inetutil.h> +#include <netinet/dhcp.h> +#include <dhcp_impl.h> +#include <lib/inet/mac.h> +#include <lib/inet/ipv4.h> +#include <lib/inet/dhcpv4.h> +#include <lib/sock/sock_test.h> +#include <sys/sunos_dhcp_class.h> +#include <aes.h> +#include <des3.h> +#include <hmac_sha1.h> +#include <netdb.h> +#include <wanboot_conf.h> +#include <bootinfo.h> +/* EXPORT DELETE END */ + +#include "wbcli.h" + +/* EXPORT DELETE START */ + +#define skipspace(p) while (isspace(*(p))) ++p + +#define skiptext(p) while (*(p) != '\0' && !isspace(*(p)) && \ + *(p) != '=' && *(p) != ',') ++p + +#define PROMPT "boot> " +#define TEST_PROMPT "boot-test> " + +#define CLI_SET 0 +#define CLI_FAIL (-1) +#define CLI_EXIT (-2) +#define CLI_CONT (-3) + +#define CLF_CMD 0x00000001 /* builtin command */ +#define CLF_ARG 0x00000002 /* boot argument directive */ + +#define CLF_IF 0x00000100 /* interface parameter */ +#define CLF_BM 0x00000200 /* bootmisc parameter */ + +#define CLF_VALSET 0x00010000 /* value set, may be null */ +#define CLF_HIDDEN 0x00020000 /* don't show its value (key) */ +#define CLF_VALMOD 0x00040000 /* value modified by the user */ + +/* + * Macros for use in managing the flags in the cli_list[]. + * The conventions we follow are: + * + * CLF_VALSET is cleared if a value is removed from varptr + * CLF_VALSET is set if a value has been placed in varptr + * (that value need not be vetted) + * CLF_HIDDEN is set if a value must not be exposed to the user + * CLF_HIDDEN is cleared if a value can be exposed to the user + * CLF_VALMOD is cleared if a value in varptr has not been modified + * CLF_VALMOD is set if a value in varptr has been modified by + * the user + */ +#ifdef DEBUG +#define CLF_SETVAL(var) { \ + (((var)->flags) |= CLF_VALSET); \ + printf("set %s\n", var->varname);\ + } + +#define CLF_ISSET(var) (printf("%s\n", \ + (((var)->flags) & CLF_VALSET) != 0 \ + ? "is set" : "not set"), \ + ((((var)->flags) & CLF_VALSET) != 0)) + +#define CLF_CLRHIDDEN(var) { \ + (((var)->flags) &= ~CLF_HIDDEN); \ + printf("unhide %s\n", var->varname); \ + } + +#define CLF_ISHIDDEN(var) (printf("%s\n", \ + (((var)->flags) & CLF_HIDDEN) != 0 \ + ? "is hidden" : "not hidden"), \ + ((((var)->flags) & CLF_HIDDEN) != 0)) + +#define CLF_MODVAL(var) { \ + (((var)->flags) |= \ + (CLF_VALMOD | CLF_VALSET)); \ + printf("modified %s\n", var->varname);\ + } + +#define CLF_ISMOD(var) (printf("%s\n", \ + (((var)->flags) & CLF_VALMOD) != 0 \ + ? "is set" : "not set"), \ + ((((var)->flags) & CLF_VALMOD) != 0)) +#else /* DEBUG */ + +#define CLF_SETVAL(var) (((var)->flags) |= CLF_VALSET) +#define CLF_ISSET(var) ((((var)->flags) & CLF_VALSET) != 0) +#define CLF_CLRHIDDEN(var) (((var)->flags) &= ~CLF_HIDDEN) +#define CLF_ISHIDDEN(var) ((((var)->flags) & CLF_HIDDEN) != 0) +#define CLF_MODVAL(var) (((var)->flags) |= (CLF_VALMOD | CLF_VALSET)) +#define CLF_ISMOD(var) ((((var)->flags) & CLF_VALMOD) != 0) + +#endif /* DEBUG */ + +/* + * The width of the widest varname below - currently "subnet_mask". + */ +#define VAR_MAXWIDTH strlen(BI_SUBNET_MASK) + +struct cli_ent; +typedef int claction_t(struct cli_ent *, char *, boolean_t); + +typedef struct cli_ent { + char *varname; + claction_t *action; + int flags; + void *varptr; + uint_t varlen; + uint_t varmax; +} cli_ent_t; + +static cli_ent_t *find_cli_ent(char *varstr); + +static char cmdbuf[2048]; /* interpreter buffer */ +static char hostip[INET_ADDRSTRLEN]; +static char subnet[INET_ADDRSTRLEN]; +static char router[INET_ADDRSTRLEN]; +static char hostname[MAXHOSTNAMELEN]; +static char httpproxy[INET_ADDRSTRLEN + 5]; /* a.b.c.d:p */ +static char bootserverURL[URL_MAX_STRLEN + 1]; +static unsigned char clientid[WB_MAX_CID_LEN]; +static unsigned char aeskey[AES_128_KEY_SIZE]; +static unsigned char des3key[DES3_KEY_SIZE]; +static unsigned char sha1key[WANBOOT_HMAC_KEY_SIZE]; +static boolean_t args_specified_prompt = B_FALSE; + +extern bc_handle_t bc_handle; +extern int getchar(void); + +static claction_t clcid, clkey, clip, clstr, clurl, clhp; +static claction_t clhelp, cllist, clprompt, cldhcp, cltest, clgo, clexit; + +static cli_ent_t cli_list[] = { + /* + * Commands/bootargs: + */ + { "test", cltest, CLF_ARG, + NULL, 0, 0 }, + { "dhcp", cldhcp, CLF_ARG, + NULL, 0, 0 }, + { "prompt", clprompt, CLF_CMD | CLF_ARG, + NULL, 0, 0 }, + { "list", cllist, CLF_CMD, + NULL, 0, 0 }, + { "help", clhelp, CLF_CMD, + NULL, 0, 0 }, + { "go", clgo, CLF_CMD, + NULL, 0, 0 }, + { "exit", clexit, CLF_CMD, + NULL, 0, 0 }, + + /* + * Interface: + */ + { BI_HOST_IP, clip, CLF_IF, + hostip, 0, sizeof (hostip) }, + { BI_SUBNET_MASK, clip, CLF_IF, + subnet, 0, sizeof (subnet) }, + { BI_ROUTER_IP, clip, CLF_IF, + router, 0, sizeof (router) }, + { BI_HOSTNAME, clstr, CLF_IF, + hostname, 0, sizeof (hostname) }, + { BI_HTTP_PROXY, clhp, CLF_IF, + httpproxy, 0, sizeof (httpproxy) }, + { BI_CLIENT_ID, clcid, CLF_IF, + clientid, 0, sizeof (clientid) }, + + /* + * Bootmisc: + */ + { BI_AES_KEY, clkey, CLF_BM | CLF_HIDDEN, + aeskey, 0, sizeof (aeskey) }, + { BI_3DES_KEY, clkey, CLF_BM | CLF_HIDDEN, + des3key, 0, sizeof (des3key) }, + { BI_SHA1_KEY, clkey, CLF_BM | CLF_HIDDEN, + sha1key, 0, sizeof (sha1key) }, + { BI_BOOTSERVER, clurl, CLF_BM, + bootserverURL, 0, sizeof (bootserverURL) }, +}; + +static int num_cli_ent = (sizeof (cli_list) / sizeof (cli_ent_t)); + +/* + * Fetch a line from the user, handling backspace appropriately. + */ +static int +editline(char *buf, int count) +{ + int i = 0; + char c; + + while (i < count - 1) { + c = getchar(); + if (c == '\n') { + break; + } else if (c == '\b') { + /* Clear for backspace. */ + if (i > 0) + i--; + continue; + } else { + buf[i++] = c; + } + } + buf[i] = '\0'; + return (i); +} + +/* + * Assign a client-id to cliptr, or output cliptr's value as a client-id. + * On assignment the value is specified in valstr, either in hexascii or + * as a quoted string; on output its value is printed in hexascii. + */ +static int +clcid(cli_ent_t *cliptr, char *valstr, boolean_t out) +{ + uint_t len, vmax; + boolean_t hexascii = B_TRUE; + char buffer[2 * WB_MAX_CID_LEN + 1]; + + if (out) { + len = cliptr->varlen * 2 + 1; + (void) octet_to_hexascii(cliptr->varptr, cliptr->varlen, + buffer, &len); + printf("%s", buffer); + return (CLI_CONT); + } else { + len = strlen(valstr); + vmax = cliptr->varmax - 1; /* space for the prefix */ + + /* + * Check whether the value is a quoted string; if so, strip + * the quotes and note that it's not in hexascii. + */ + if ((valstr[0] == '"' || valstr[0] == '\'') && + valstr[len-1] == valstr[0]) { + hexascii = B_FALSE; + ++valstr; + len -= 2; + valstr[len] = '\0'; + } else { + /* + * If the value contains any non-hex digits assume + * that it's not in hexascii. + */ + char *p; + + for (p = valstr; *p != '\0'; ++p) { + if (!isxdigit(*p)) { + hexascii = B_FALSE; + break; + } + } + } + + if (hexascii) { + if (len > vmax * 2 || + hexascii_to_octet(valstr, len, + (char *)(cliptr->varptr), &vmax) != 0) { + return (CLI_FAIL); + } + cliptr->varlen = vmax; + } else { + if (len > vmax) { + return (CLI_FAIL); + } + bcopy(valstr, cliptr->varptr, len); + cliptr->varlen = len; + } + + return (CLI_SET); + } +} + +/* + * Assign a key to cliptr, or output cliptr's value as a key. + * On assignment the value is specified in valstr in hexascii; + * on output its value is printed in hexascii, provided the key + * was entered at the interpreter (not obtained from OBP and + * thus hidden). + */ +static int +clkey(cli_ent_t *cliptr, char *valstr, boolean_t out) +{ + uint_t len, vmax; + + if (out) { + char buffer[2 * WANBOOT_MAXKEYLEN + 1]; + + if (!CLF_ISHIDDEN(cliptr)) { + len = cliptr->varlen * 2 + 1; + (void) octet_to_hexascii(cliptr->varptr, + cliptr->varlen, buffer, &len); + printf("%s", buffer); + } else { + printf("*HIDDEN*"); + } + return (CLI_CONT); + } else { + len = strlen(valstr); + vmax = cliptr->varmax; + if (len != vmax * 2 || hexascii_to_octet(valstr, len, + cliptr->varptr, &vmax) != 0) { + return (CLI_FAIL); + } + cliptr->varlen = vmax; + CLF_CLRHIDDEN(cliptr); + return (CLI_SET); + } +} + +/* + * Assign an IP address to cliptr, or output cliptr's value as an + * IP address. On assignment the value is specified in valstr in + * dotted-decimal format; on output its value is printed in dotted- + * decimal format. + */ +static int +clip(cli_ent_t *cliptr, char *valstr, boolean_t out) +{ + uint_t len; + + if (out) { + printf("%s", (char *)cliptr->varptr); + return (CLI_CONT); + } + + if (inet_addr(valstr) == (in_addr_t)-1 || + (len = strlen(valstr)) >= cliptr->varmax) { + return (CLI_FAIL); + } + + (void) strcpy(cliptr->varptr, valstr); + cliptr->varlen = len + 1; + return (CLI_SET); +} + +/* + * Assign an arbitrary string to cliptr, or output cliptr's value as a string. + */ +static int +clstr(cli_ent_t *cliptr, char *valstr, boolean_t out) +{ + uint_t len; + + if (out) { + printf("%s", (char *)cliptr->varptr); + return (CLI_CONT); + } else { + if ((len = strlen(valstr)) >= cliptr->varmax) { + return (CLI_FAIL); + } else { + (void) strcpy(cliptr->varptr, valstr); + cliptr->varlen = len + 1; + return (CLI_SET); + } + } +} + +/* + * Assign a URL to cliptr (having verified the format), or output cliptr's + * value as a URL. The host must be specified in dotted-decimal, and the + * scheme must not be https. + */ +static int +clurl(cli_ent_t *cliptr, char *valstr, boolean_t out) +{ + url_t u; + uint_t len; + + if (out) { + printf("%s", (char *)cliptr->varptr); + return (CLI_CONT); + } + + if (url_parse(valstr, &u) != URL_PARSE_SUCCESS || + u.https || inet_addr(u.hport.hostname) == (in_addr_t)-1 || + (len = strlen(valstr)) >= cliptr->varmax) { + return (CLI_FAIL); + } + + (void) strcpy(cliptr->varptr, valstr); + cliptr->varlen = len + 1; + return (CLI_SET); +} + +/* + * Assign a hostport to cliptr (having verified the format), or output cliptr's + * value as a hostport. The host must be specified in dotted-decimal. + */ +static int +clhp(cli_ent_t *cliptr, char *valstr, boolean_t out) +{ + url_hport_t u; + uint_t len; + + if (out) { + printf("%s", (char *)cliptr->varptr); + return (CLI_CONT); + } + + if (url_parse_hostport(valstr, &u, URL_DFLT_PROXY_PORT) != + URL_PARSE_SUCCESS || + inet_addr(u.hostname) == (in_addr_t)-1 || + (len = strlen(valstr)) >= cliptr->varmax) { + return (CLI_FAIL); + } + + (void) strcpy(cliptr->varptr, valstr); + cliptr->varlen = len + 1; + return (CLI_SET); +} + +/* + * Exit the interpreter and return to the booter. + */ +/*ARGSUSED*/ +static int +clgo(cli_ent_t *cliptr, char *valstr, boolean_t out) +{ + return (CLI_EXIT); +} + +/* + * Exit the interpreter and return to OBP. + */ +/*ARGSUSED*/ +static int +clexit(cli_ent_t *cliptr, char *valstr, boolean_t out) +{ + prom_exit_to_mon(); + /*NOTREACHED*/ + return (CLI_EXIT); +} + +/* + * Provide simple help information. + */ +/*ARGSUSED*/ +static int +clhelp(cli_ent_t *cliptr, char *valstr, boolean_t out) +{ + printf("var=val - set variable\n"); + printf("var= - unset variable\n"); + printf("var - print variable\n"); + printf("list - list variables and their values\n"); + printf("prompt - prompt for unset variables\n"); + printf("go - continue booting\n"); + printf("exit - quit boot interpreter and return to OBP\n"); + + return (CLI_CONT); +} + +/* + * List variables and their current values. + */ +/*ARGSUSED*/ +static int +cllist(cli_ent_t *cliptr, char *valstr, boolean_t out) +{ + int wanted = (int)valstr; + int i; + + wanted &= ~(CLF_CMD | CLF_ARG); + + for (cliptr = cli_list; cliptr < &cli_list[num_cli_ent]; cliptr++) { + if ((cliptr->flags & (CLF_CMD | CLF_ARG)) != 0 || + (cliptr->flags & wanted) == 0) { + continue; + } + printf("%s: ", cliptr->varname); + /* + * Line the values up - space to the width of the widest + * varname + 1 for the ':'. + */ + for (i = VAR_MAXWIDTH + 1 - strlen(cliptr->varname); + i > 0; --i) { + printf(" "); + } + + if (CLF_ISSET(cliptr) || CLF_ISHIDDEN(cliptr)) { + (void) cliptr->action(cliptr, NULL, B_TRUE); + printf("\n"); + } else { + printf("UNSET\n"); + } + } + + return (CLI_CONT); +} + +/* + * Prompt for wanted values. + */ +/*ARGSUSED*/ +static int +clprompt(cli_ent_t *cliptr, char *valstr, boolean_t out) +{ + char *p; + int wanted = (int)valstr; + + /* + * If processing boot arguments, simply note the fact that clprompt() + * should be invoked later when other parameters may be supplied. + */ + if ((wanted & CLF_ARG) != 0) { + args_specified_prompt = B_TRUE; + return (CLI_CONT); + } + wanted &= ~(CLF_CMD | CLF_ARG); + + for (cliptr = cli_list; cliptr < &cli_list[num_cli_ent]; ++cliptr) { + if ((cliptr->flags & wanted) == 0) { + continue; + } + + printf("%s", cliptr->varname); + if (CLF_ISSET(cliptr)) { + printf(" ["); + (void) cliptr->action(cliptr, NULL, B_TRUE); + printf("]"); + } + printf("? "); + (void) editline(cmdbuf, sizeof (cmdbuf)); + printf("\n"); + + p = cmdbuf; + skipspace(p); + if (*p == '\0') { /* nothing there */ + continue; + } + + /* Get valstr and nul terminate */ + valstr = p; + ++p; + skiptext(p); + *p = '\0'; + + /* If empty value, do nothing */ + if (strlen(valstr) == 0) { + continue; + } + + switch (cliptr->action(cliptr, valstr, B_FALSE)) { + case CLI_SET: + CLF_MODVAL(cliptr); + break; + case CLI_FAIL: + printf("Incorrect format, parameter unchanged!\n"); + break; + case CLI_EXIT: + return (CLI_EXIT); + case CLI_CONT: + break; + } + } + + return (CLI_CONT); +} + +/* + * If the PROM has done DHCP, bind the interface; otherwise do the full + * DHCP packet exchange. + */ +/*ARGSUSED*/ +static int +cldhcp(cli_ent_t *cliptr, char *valstr, boolean_t out) +{ + static boolean_t first_time = B_TRUE; + static int ret = CLI_CONT; + + if (first_time) { + /* + * Set DHCP's idea of the client_id from our cached value. + */ + cliptr = find_cli_ent(BI_CLIENT_ID); + if (CLF_ISMOD(cliptr)) { + dhcp_set_client_id(cliptr->varptr, cliptr->varlen); + } + + bootlog("wanboot", BOOTLOG_INFO, "Starting DHCP configuration"); + + (void) ipv4_setpromiscuous(B_TRUE); + if (dhcp() == 0) { + bootlog("wanboot", BOOTLOG_INFO, + "DHCP configuration succeeded"); + } else { + bootlog("wanboot", BOOTLOG_CRIT, + "DHCP configuration failed"); + ret = CLI_FAIL; + } + (void) ipv4_setpromiscuous(B_FALSE); + + first_time = B_FALSE; + } + + return (ret); +} + +/* + * Invoke the socket test interpreter (for testing purposes only). + */ +/*ARGSUSED*/ +static int +cltest(cli_ent_t *cliptr, char *valstr, boolean_t out) +{ + (void) ipv4_setpromiscuous(B_FALSE); + printf("\n"); + for (;;) { + printf(TEST_PROMPT); + if (editline(cmdbuf, sizeof (cmdbuf)) > 0) { + printf("\n"); + (void) st_interpret(cmdbuf); + } else { + prom_exit_to_mon(); + /* NOTREACHED */ + } + } + + /* NOTREACHED */ + return (CLI_CONT); +} + +/* + * Return the cliptr corresponding to the named variable. + */ +static cli_ent_t * +find_cli_ent(char *varstr) +{ + cli_ent_t *cliptr; + + for (cliptr = cli_list; cliptr < &cli_list[num_cli_ent]; ++cliptr) { + if (strcmp(varstr, cliptr->varname) == 0) { + return (cliptr); + } + } + + return (NULL); +} + +/* + * Evaluate the commands provided by the user (either as "-o" boot arguments + * or interactively at the boot interpreter). + */ +static int +cli_eval_buf(char *inbuf, int wanted) +{ + char *p, *varstr, *end_varstr, *valstr, *end_valstr; + boolean_t assign; + cli_ent_t *cliptr; + + for (p = inbuf; *p != '\0'; ) { + skipspace(p); + + /* If nothing more on line, go get the next one */ + if (*p == '\0') { + break; + } else if (*p == ',') { /* orphan ',' ? */ + ++p; + continue; + } + + /* Get ptrs to start & end of variable */ + varstr = p; + ++p; + skiptext(p); + end_varstr = p; + skipspace(p); + + /* See if we're doing an assignment */ + valstr = NULL; + if (*p != '=') { /* nope, just printing */ + assign = B_FALSE; + } else { + assign = B_TRUE; + ++p; /* past '=' */ + skipspace(p); + + /* Assigning something? (else clear variable) */ + if (*p != '\0' && *p != ',') { + /* Get ptrs to start & end of valstr */ + valstr = p; + ++p; + skiptext(p); + end_valstr = p; + skipspace(p); + } + } + + /* Skip ',' delimiter if present */ + if (*p == ',') { + ++p; + } + + /* Nul-terminate varstr and valstr (if appropriate) */ + *end_varstr = '\0'; + if (valstr != NULL) { + *end_valstr = '\0'; + } + + if ((cliptr = find_cli_ent(varstr)) == NULL) { + printf("Unknown variable '%s'; ignored\n", varstr); + continue; + } + + /* + * It's an error to specify a parameter which can only be a + * boot argument (and not a command) when not processing the + * boot arguments. + */ + if ((cliptr->flags & (CLF_CMD | CLF_ARG)) == CLF_ARG && + (wanted & CLF_ARG) == 0) { + printf("'%s' may only be specified as a " + "boot argument; ignored\n", varstr); + continue; + } + + /* + * When doing an assignment, verify that it's not a command + * or argument name, and that it is permissible in the current + * context. An 'empty' assignment (var=) is treated the same + * as a null assignment (var=""). + * + * If processing the boot arguments, it is an error to not + * assign a value to a non-argument parameter. + */ + if (assign) { + if ((cliptr->flags & (CLF_CMD | CLF_ARG)) != 0) { + printf("'%s' is a command and cannot " + "be assigned\n", varstr); + return (CLI_FAIL); + } + if ((cliptr->flags & wanted) == 0) { + printf("'%s' cannot be assigned\n", varstr); + return (CLI_FAIL); + } + + if (valstr == NULL) { + cliptr->varlen = 0; + CLF_MODVAL(cliptr); + continue; + } + } else if ((wanted & CLF_ARG) != 0 && + (cliptr->flags & (CLF_CMD | CLF_ARG)) == 0) { + printf("'%s' must be assigned when specified in " + " the boot arguments\n", varstr); + return (CLI_FAIL); + } + + /* + * Pass 'wanted' to command-handling functions, in particular + * clprompt() and cllist(). + */ + if ((cliptr->flags & CLF_CMD) != 0) { + valstr = (char *)wanted; + } + + /* + * Call the parameter's action function. + */ + switch (cliptr->action(cliptr, valstr, !assign)) { + case CLI_SET: + CLF_MODVAL(cliptr); + break; + case CLI_FAIL: + printf("Incorrect format: variable '%s' not set\n", + cliptr->varname); + break; + case CLI_EXIT: + return (CLI_EXIT); + case CLI_CONT: + if (!assign) { + printf("\n"); + } + break; + } + } + + return (CLI_CONT); +} + +static void +cli_interpret(int wanted) +{ + printf("\n"); + do { + printf(PROMPT); + (void) editline(cmdbuf, sizeof (cmdbuf)); + printf("\n"); + + } while (cli_eval_buf(cmdbuf, wanted) != CLI_EXIT); +} + +#if defined(__sparcv9) +/* + * This routine queries the PROM to see what encryption keys exist. + */ +static void +get_prom_encr_keys() +{ + cli_ent_t *cliptr; + char encr_key[WANBOOT_MAXKEYLEN]; + int keylen; + int status; + int ret; + + /* + * At the top of the priority list, we have AES. + */ + ret = prom_get_security_key(WANBOOT_AES_128_KEY_NAME, encr_key, + WANBOOT_MAXKEYLEN, &keylen, &status); + if ((ret == 0) && (status == 0) && (keylen == AES_128_KEY_SIZE)) { + cliptr = find_cli_ent(BI_AES_KEY); + bcopy(encr_key, cliptr->varptr, AES_128_KEY_SIZE); + cliptr->varlen = AES_128_KEY_SIZE; + CLF_MODVAL(cliptr); + } + + /* + * Next, 3DES. + */ + ret = prom_get_security_key(WANBOOT_DES3_KEY_NAME, encr_key, + WANBOOT_MAXKEYLEN, &keylen, &status); + if ((ret == 0) && (status == 0) && (keylen == DES3_KEY_SIZE)) { + cliptr = find_cli_ent(BI_3DES_KEY); + bcopy(encr_key, cliptr->varptr, DES3_KEY_SIZE); + cliptr->varlen = DES3_KEY_SIZE; + CLF_MODVAL(cliptr); + } +} + +/* + * This routine queries the PROM to see what hashing keys exist. + */ +static void +get_prom_hash_keys() +{ + cli_ent_t *cliptr; + char hash_key[WANBOOT_HMAC_KEY_SIZE]; + int keylen; + int status; + int ret; + + /* + * The only supported key thus far is SHA1. + */ + ret = prom_get_security_key(WANBOOT_HMAC_SHA1_KEY_NAME, hash_key, + WANBOOT_HMAC_KEY_SIZE, &keylen, &status); + if ((ret == 0) && (status == 0) && (keylen == WANBOOT_HMAC_KEY_SIZE)) { + cliptr = find_cli_ent(BI_SHA1_KEY); + bcopy(hash_key, cliptr->varptr, WANBOOT_HMAC_KEY_SIZE); + cliptr->varlen = WANBOOT_HMAC_KEY_SIZE; + CLF_MODVAL(cliptr); + } +} +#endif /* defined(__sparcv9) */ + +/* + * For the given parameter type(s), get values from bootinfo and cache in + * the local variables used by the "boot>" interpreter. + */ +static void +bootinfo_defaults(int which) +{ + cli_ent_t *cliptr; + + for (cliptr = cli_list; cliptr < &cli_list[num_cli_ent]; ++cliptr) { + if ((cliptr->flags & which) != 0 && !CLF_ISSET(cliptr)) { + size_t len = cliptr->varmax; + + if (bootinfo_get(cliptr->varname, cliptr->varptr, + &len, NULL) == BI_E_SUCCESS) { + cliptr->varlen = len; + CLF_SETVAL(cliptr); + } + } + } +} + +/* + * For the given parameter type(s), store values entered at the "boot>" + * interpreter back into bootinfo. + */ +static void +update_bootinfo(int which) +{ + cli_ent_t *cliptr; + + for (cliptr = cli_list; cliptr < &cli_list[num_cli_ent]; ++cliptr) { + if ((cliptr->flags & which) != 0 && CLF_ISMOD(cliptr)) { + (void) bootinfo_put(cliptr->varname, + cliptr->varptr, cliptr->varlen, 0); + } + } +} + +/* + * Return the net-config-strategy: "dhcp", "manual" or "rarp" + */ +static char * +net_config_strategy(void) +{ + static char ncs[8]; /* "dhcp" or "manual" */ + size_t len = sizeof (ncs); + + if (ncs[0] == '\0' && + bootinfo_get(BI_NET_CONFIG_STRATEGY, ncs, &len, NULL) != + BI_E_SUCCESS) { + /* + * Support for old PROMs: create the net-config-strategy + * property under /chosen with an appropriate value. If we + * have a bootp-response (not interested in its value, just + * its presence) then we did DHCP; otherwise configuration + * is manual. + */ + if (bootinfo_get(BI_BOOTP_RESPONSE, NULL, NULL, + NULL) == BI_E_BUF2SMALL) { + (void) strcpy(ncs, "dhcp"); + } else { + (void) strcpy(ncs, "manual"); + } + (void) bootinfo_put(BI_NET_CONFIG_STRATEGY, ncs, strlen(ncs), + BI_R_CHOSEN); + + bootlog("wanboot", BOOTLOG_INFO, + "Default net-config-strategy: %s", ncs); + } + + return (ncs); +} + +/* + * If there is no client-id property published in /chosen (by the PROM or the + * boot interpreter) provide a default client-id based on the MAC address of + * the client. + * As specified in RFC2132 (section 9.14), this is prefixed with a byte + * which specifies the ARP hardware type defined in RFC1700 (for Ethernet, + * this should be 1). + */ +static void +generate_default_clientid(void) +{ + char clid[WB_MAX_CID_LEN]; + size_t len = sizeof (clid); + + if (bootinfo_get(BI_CLIENT_ID, clid, &len, NULL) != BI_E_SUCCESS) { + len = mac_get_addr_len() + 1; /* include hwtype */ + + if (len > sizeof (clid)) { + return; + } + + clid[0] = mac_arp_type(mac_get_type()); + bcopy(mac_get_addr_buf(), &clid[1], len - 1); + + (void) bootinfo_put(BI_CLIENT_ID, clid, len, 0); + } +} + +/* + * Determine the URL of the boot server from the 'file' parameter to OBP, + * the SbootURI or BootFile DHCP options, or the 'bootserver' value entered + * either as a "-o" argument or at the interpreter. + */ +static void +determine_bootserver_url(void) +{ + char bs[URL_MAX_STRLEN + 1]; + size_t len; + url_t url; + + if (bootinfo_get(BI_BOOTSERVER, bs, &len, NULL) != BI_E_SUCCESS) { + /* + * If OBP has published a network-boot-file property in + * /chosen (or there is a DHCP BootFile or SbootURI vendor + * option) and it's a URL, construct the bootserver URL + * from it. + */ + len = URL_MAX_STRLEN; + if (bootinfo_get(BI_NETWORK_BOOT_FILE, bs, &len, NULL) != + BI_E_SUCCESS) { + len = URL_MAX_STRLEN; + if (bootinfo_get(BI_BOOTFILE, bs, &len, NULL) != + BI_E_SUCCESS) { + return; + } + } + if (url_parse(bs, &url) == URL_PARSE_SUCCESS) { + (void) bootinfo_put(BI_BOOTSERVER, bs, len, 0); + } + } +} + +/* + * Provide a classful subnet mask based on the client's IP address. + */ +static in_addr_t +generate_classful_subnet(in_addr_t client_ipaddr) +{ + struct in_addr subnetmask; + char *netstr; + + if (IN_CLASSA(client_ipaddr)) { + subnetmask.s_addr = IN_CLASSA_NET; + } else if (IN_CLASSB(client_ipaddr)) { + subnetmask.s_addr = IN_CLASSB_NET; + } else { + subnetmask.s_addr = IN_CLASSC_NET; + } + + netstr = inet_ntoa(subnetmask); + (void) bootinfo_put(BI_SUBNET_MASK, netstr, strlen(netstr) + 1, 0); + + return (subnetmask.s_addr); +} + +/* + * Informational output to the user (if interactive) or the bootlogger. + */ +static void +info(const char *msg, boolean_t interactive) +{ + if (interactive) { + printf("%s\n", msg); + } else { + bootlog("wanboot", BOOTLOG_INFO, "%s", msg); + } +} + +/* + * Determine whether we have sufficient information to proceed with booting, + * either for configuring the interface and downloading the bootconf file, + * or for downloading the miniroot. + */ +static int +config_incomplete(int why, boolean_t interactive) +{ + boolean_t error = B_FALSE; + char buf[URL_MAX_STRLEN + 1]; + size_t len; + char *urlstr; + url_t u; + struct hostent *hp; + in_addr_t client_ipaddr, ipaddr, bsnet, pxnet; + static in_addr_t subnetmask, clnet; + static boolean_t have_router = B_FALSE; + static boolean_t have_proxy = B_FALSE; + boolean_t have_root_server = B_FALSE; + boolean_t have_boot_logger = B_FALSE; + in_addr_t rsnet, blnet; + + /* + * Note that 'have_router', 'have_proxy', 'subnetmask', and 'clnet' + * are static, so that their values (gathered when checking the + * interface configuration) may be used again when checking the boot + * configuration. + */ + if (why == CLF_IF) { + /* + * A valid host IP address is an absolute requirement. + */ + len = sizeof (buf); + if (bootinfo_get(BI_HOST_IP, buf, &len, NULL) == BI_E_SUCCESS) { + if ((client_ipaddr = inet_addr(buf)) == (in_addr_t)-1) { + info("host-ip invalid!", interactive); + error = B_TRUE; + } + } else { + info("host-ip not set!", interactive); + error = B_TRUE; + } + + /* + * If a subnet mask was provided, use it; otherwise infer it. + */ + len = sizeof (buf); + if (bootinfo_get(BI_SUBNET_MASK, buf, &len, NULL) == + BI_E_SUCCESS) { + if ((subnetmask = inet_addr(buf)) == (in_addr_t)-1) { + info("subnet-mask invalid!", interactive); + error = B_TRUE; + } + } else { + info("Defaulting to classful subnetting", interactive); + + subnetmask = generate_classful_subnet(client_ipaddr); + } + clnet = client_ipaddr & subnetmask; + + /* + * A legal bootserver URL is also an absolute requirement. + */ + len = sizeof (buf); + if (bootinfo_get(BI_BOOTSERVER, buf, &len, NULL) == + BI_E_SUCCESS) { + if (url_parse(buf, &u) != URL_PARSE_SUCCESS || + u.https || + (ipaddr = inet_addr(u.hport.hostname)) == + (in_addr_t)-1) { + info("bootserver not legal URL!", interactive); + error = B_TRUE; + } else { + bsnet = ipaddr & subnetmask; + } + } else { + info("bootserver not specified!", interactive); + error = B_TRUE; + } + + /* + * Is there a correctly-defined router? + */ + len = sizeof (buf); + if (bootinfo_get(BI_ROUTER_IP, buf, &len, NULL) == + BI_E_SUCCESS) { + if ((ipaddr = inet_addr(buf)) == (in_addr_t)-1) { + info("router-ip invalid!", interactive); + error = B_TRUE; + } else if (clnet != (ipaddr & subnetmask)) { + info("router not on local subnet!", + interactive); + error = B_TRUE; + } else { + have_router = B_TRUE; + } + } + + /* + * Is there a correctly-defined proxy? + */ + len = sizeof (buf); + if (bootinfo_get(BI_HTTP_PROXY, buf, &len, NULL) == + BI_E_SUCCESS) { + url_hport_t u; + + if (url_parse_hostport(buf, &u, URL_DFLT_PROXY_PORT) != + URL_PARSE_SUCCESS || + (ipaddr = inet_addr(u.hostname)) == (in_addr_t)-1) { + info("http-proxy port invalid!", interactive); + error = B_TRUE; + } else { + /* + * The proxy is only of use to us if it's on + * our local subnet, or if a router has been + * specified (which should hopefully allow us + * to access the proxy). + */ + pxnet = ipaddr & subnetmask; + have_proxy = (have_router || pxnet == clnet); + } + } + + /* + * If there is no router and no proxy (either on the local + * subnet or reachable via a router), then the bootserver + * URL must be on the local net. + */ + if (!error && !have_router && !have_proxy && bsnet != clnet) { + info("bootserver URL not on local subnet", + interactive); + error = B_TRUE; + } + } else { + /* + * There must be a correctly-defined root_server URL. + */ + if ((urlstr = bootconf_get(&bc_handle, + BC_ROOT_SERVER)) == NULL) { + info("no root_server URL!", interactive); + error = B_TRUE; + } else if (url_parse(urlstr, &u) != URL_PARSE_SUCCESS) { + info("root_server not legal URL!", interactive); + error = B_TRUE; + } else if ((hp = gethostbyname(u.hport.hostname)) == NULL) { + info("cannot resolve root_server hostname!", + interactive); + error = B_TRUE; + } else { + rsnet = *(in_addr_t *)hp->h_addr & subnetmask; + have_root_server = B_TRUE; + } + + /* + * Is there a correctly-defined (non-empty) boot_logger URL? + */ + if ((urlstr = bootconf_get(&bc_handle, + BC_BOOT_LOGGER)) != NULL) { + if (url_parse(urlstr, &u) != URL_PARSE_SUCCESS) { + info("boot_logger not legal URL!", interactive); + error = B_TRUE; + } else if ((hp = gethostbyname(u.hport.hostname)) == + NULL) { + info("cannot resolve boot_logger hostname!", + interactive); + error = B_TRUE; + } else { + blnet = *(in_addr_t *)hp->h_addr & subnetmask; + have_boot_logger = B_TRUE; + } + } + + /* + * If there is no router and no proxy (either on the local + * subnet or reachable via a router), then the root_server + * URL (and the boot_logger URL if specified) must be on the + * local net. + */ + if (!error && !have_router && !have_proxy) { + if (have_root_server && rsnet != clnet) { + info("root_server URL not on local subnet", + interactive); + error = B_TRUE; + } + if (have_boot_logger && blnet != clnet) { + info("boot_logger URL not on local subnet", + interactive); + error = B_TRUE; + } + } + } + + return (error); +} + +/* + * Actually setup our network interface with the values derived from the + * PROM, DHCP or interactively from the user. + */ +static void +setup_interface() +{ + char str[MAXHOSTNAMELEN]; /* will accomodate an IP too */ + size_t len; + struct in_addr in_addr; + + len = sizeof (str); + if (bootinfo_get(BI_HOST_IP, str, &len, NULL) == BI_E_SUCCESS && + (in_addr.s_addr = inet_addr(str)) != (in_addr_t)-1) { + in_addr.s_addr = htonl(in_addr.s_addr); + ipv4_setipaddr(&in_addr); + } + + len = sizeof (str); + if (bootinfo_get(BI_SUBNET_MASK, str, &len, NULL) == BI_E_SUCCESS && + (in_addr.s_addr = inet_addr(str)) != (in_addr_t)-1) { + in_addr.s_addr = htonl(in_addr.s_addr); + ipv4_setnetmask(&in_addr); + } + + len = sizeof (str); + if (bootinfo_get(BI_ROUTER_IP, str, &len, NULL) == BI_E_SUCCESS && + (in_addr.s_addr = inet_addr(str)) != (in_addr_t)-1) { + in_addr.s_addr = htonl(in_addr.s_addr); + ipv4_setdefaultrouter(&in_addr); + (void) ipv4_route(IPV4_ADD_ROUTE, RT_DEFAULT, NULL, &in_addr); + } + + len = sizeof (str); + if (bootinfo_get(BI_HOSTNAME, str, &len, NULL) == BI_E_SUCCESS) { + (void) sethostname(str, len); + } +} + +/* EXPORT DELETE END */ +boolean_t +wanboot_init_interface(char *boot_arguments) +{ +/* EXPORT DELETE START */ + boolean_t interactive; + int which; + +#if defined(__sparcv9) + /* + * Get the keys from PROM before we allow the user + * to override them from the CLI. + */ + get_prom_encr_keys(); + get_prom_hash_keys(); +#endif /* defined(__sparcv9) */ + + /* + * If there is already a bootp-response property under + * /chosen then the PROM must have done DHCP for us; + * invoke dhcp() to 'bind' the interface. + */ + if (bootinfo_get(BI_BOOTP_RESPONSE, NULL, NULL, NULL) == + BI_E_BUF2SMALL) { + (void) cldhcp(NULL, NULL, 0); + } + + /* + * Obtain default interface values from bootinfo. + */ + bootinfo_defaults(CLF_IF); + + /* + * Process the boot arguments (following the "-o" option). + */ + if (boot_arguments != NULL) { + (void) cli_eval_buf(boot_arguments, + (CLF_ARG | CLF_IF | CLF_BM)); + } + + /* + * Stash away any interface/bootmisc parameter values we got + * from either the PROM or the boot arguments. + */ + update_bootinfo(CLF_IF | CLF_BM); + + /* + * If we don't already have a value for bootserver, try to + * deduce one. Refresh wbcli's idea of these values. + */ + determine_bootserver_url(); + bootinfo_defaults(CLF_BM); + + /* + * Check that the information we have collected thus far is sufficient. + */ + interactive = args_specified_prompt; + + if (interactive) { + /* + * Drop into the boot interpreter to allow the input + * of keys, bootserver and bootmisc, and in the case + * that net-config-strategy == "manual" the interface + * parameters. + */ + which = CLF_BM | CLF_CMD; + if (strcmp(net_config_strategy(), "manual") == 0) + which |= CLF_IF; + + do { + cli_interpret(which); + update_bootinfo(CLF_IF | CLF_BM); + } while (config_incomplete(CLF_IF, interactive)); + } else { + /* + * The user is not to be given the opportunity to + * enter further values; fail. + */ + if (config_incomplete(CLF_IF, interactive)) { + bootlog("wanboot", BOOTLOG_CRIT, + "interface incorrectly configured"); + return (B_FALSE); + } + } + + /* + * If a wanboot-enabled PROM hasn't processed client-id in + * network-boot-arguments, or no value for client-id has been + * specified to the boot interpreter, then provide a default + * client-id based on our MAC address. + */ + generate_default_clientid(); + + /* + * If net-config-strategy == "manual" then we must setup + * the interface now; if "dhcp" then it will already have + * been setup. + */ + if (strcmp(net_config_strategy(), "manual") == 0) + setup_interface(); +/* EXPORT DELETE END */ + return (B_TRUE); +} + +boolean_t +wanboot_verify_config(void) +{ +/* EXPORT DELETE START */ + /* + * Check that the wanboot.conf file defines a valid root_server + * URL, and check that, if given, the boot_logger URL is valid. + */ + if (config_incomplete(0, B_FALSE)) { + bootlog("wanboot", BOOTLOG_CRIT, + "incomplete boot configuration"); + return (B_FALSE); + } +/* EXPORT DELETE END */ + return (B_TRUE); +} |
