wbcli.c revision 694c35faa87b858ecdadfe4fc592615f4eefbb07
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/wanboot_impl.h>
#include <parseURL.h>
#include <bootlog.h>
#include <netinet/inetutil.h>
#include <dhcp_impl.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>
#include "wbcli.h"
*(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)
/*
* 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) { \
}
? "is set" : "not set"), \
#define CLF_CLRHIDDEN(var) { \
}
? "is hidden" : "not hidden"), \
#define CLF_MODVAL(var) { \
(CLF_VALMOD | CLF_VALSET)); \
}
? "is set" : "not set"), \
#else /* DEBUG */
#endif /* DEBUG */
/*
* The width of the widest varname below - currently "subnet_mask".
*/
struct cli_ent;
typedef struct cli_ent {
char *varname;
int flags;
void *varptr;
} cli_ent_t;
static char hostip[INET_ADDRSTRLEN];
static char subnet[INET_ADDRSTRLEN];
static char router[INET_ADDRSTRLEN];
static char hostname[MAXHOSTNAMELEN];
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];
extern bc_handle_t bc_handle;
extern int getchar(void);
/*
*/
NULL, 0, 0 },
NULL, 0, 0 },
NULL, 0, 0 },
NULL, 0, 0 },
NULL, 0, 0 },
NULL, 0, 0 },
NULL, 0, 0 },
/*
* Interface:
*/
/*
* Bootmisc:
*/
bootserverURL, 0, sizeof (bootserverURL) },
};
/*
* Fetch a line from the user, handling backspace appropriately.
*/
static int
{
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
{
if (out) {
return (CLI_CONT);
} else {
/*
* Check whether the value is a quoted string; if so, strip
* the quotes and note that it's not in hexascii.
*/
++valstr;
len -= 2;
} 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)) {
break;
}
}
}
if (hexascii) {
return (CLI_FAIL);
}
} else {
return (CLI_FAIL);
}
}
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
{
if (out) {
if (!CLF_ISHIDDEN(cliptr)) {
} else {
printf("*HIDDEN*");
}
return (CLI_CONT);
} else {
return (CLI_FAIL);
}
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
{
if (out) {
return (CLI_CONT);
}
return (CLI_FAIL);
}
return (CLI_SET);
}
/*
* Assign an arbitrary string to cliptr, or output cliptr's value as a string.
*/
static int
{
if (out) {
return (CLI_CONT);
} else {
return (CLI_FAIL);
} else {
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
{
url_t u;
if (out) {
return (CLI_CONT);
}
return (CLI_FAIL);
}
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
{
url_hport_t u;
if (out) {
return (CLI_CONT);
}
return (CLI_FAIL);
}
return (CLI_SET);
}
/*
* Exit the interpreter and return to the booter.
*/
/*ARGSUSED*/
static int
{
return (CLI_EXIT);
}
/*
* Exit the interpreter and return to OBP.
*/
/*ARGSUSED*/
static int
{
/*NOTREACHED*/
return (CLI_EXIT);
}
/*
* Provide simple help information.
*/
/*ARGSUSED*/
static int
{
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
{
int i;
continue;
}
/*
* Line the values up - space to the width of the widest
* varname + 1 for the ':'.
*/
i > 0; --i) {
printf(" ");
}
printf("\n");
} else {
printf("UNSET\n");
}
}
return (CLI_CONT);
}
/*
* Prompt for wanted values.
*/
/*ARGSUSED*/
static int
{
char *p;
/*
* If processing boot arguments, simply note the fact that clprompt()
* should be invoked later when other parameters may be supplied.
*/
return (CLI_CONT);
}
continue;
}
printf(" [");
printf("]");
}
printf("? ");
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 */
continue;
}
case CLI_SET:
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
{
if (first_time) {
/*
* Set DHCP's idea of the client_id from our cached value.
*/
}
(void) ipv4_setpromiscuous(B_TRUE);
if (dhcp() == 0) {
"DHCP configuration succeeded");
} else {
"DHCP configuration failed");
}
(void) ipv4_setpromiscuous(B_FALSE);
}
return (ret);
}
/*
* Invoke the socket test interpreter (for testing purposes only).
*/
/*ARGSUSED*/
static int
{
(void) ipv4_setpromiscuous(B_FALSE);
printf("\n");
for (;;) {
printf("\n");
(void) st_interpret(cmdbuf);
} else {
/* NOTREACHED */
}
}
/* NOTREACHED */
return (CLI_CONT);
}
/*
* Return the cliptr corresponding to the named variable.
*/
static cli_ent_t *
find_cli_ent(char *varstr)
{
return (cliptr);
}
}
return (NULL);
}
/*
* Evaluate the commands provided by the user (either as "-o" boot arguments
* or interactively at the boot interpreter).
*/
static int
{
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 */
if (*p != '=') { /* nope, just printing */
} else {
++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';
*end_valstr = '\0';
}
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.
*/
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) {
printf("'%s' is a command and cannot "
"be assigned\n", varstr);
return (CLI_FAIL);
}
return (CLI_FAIL);
}
continue;
}
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().
*/
/* use uintptr_t to suppress the gcc warning */
}
/*
* Call the parameter's action function.
*/
case CLI_SET:
break;
case CLI_FAIL:
printf("Incorrect format: variable '%s' not set\n",
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("\n");
}
#if defined(__sparcv9)
/*
* This routine queries the PROM to see what encryption keys exist.
*/
static void
{
char encr_key[WANBOOT_MAXKEYLEN];
int keylen;
int status;
int ret;
/*
* At the top of the priority list, we have AES.
*/
}
/*
* Next, 3DES.
*/
}
}
/*
* This routine queries the PROM to see what hashing keys exist.
*/
static void
{
char hash_key[WANBOOT_HMAC_KEY_SIZE];
int keylen;
int status;
int ret;
/*
* The only supported key thus far is SHA1.
*/
}
}
#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)
{
}
}
}
}
/*
* For the given parameter type(s), store values entered at the "boot>"
* interpreter back into bootinfo.
*/
static void
update_bootinfo(int which)
{
}
}
}
/*
* Return the net-config-strategy: "dhcp", "manual" or "rarp"
*/
static char *
net_config_strategy(void)
{
if (ncs[0] == '\0' &&
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.
*/
NULL) == BI_E_BUF2SMALL) {
} else {
}
"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
{
char clid[WB_MAX_CID_LEN];
return;
}
}
}
/*
* 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)
{
/*
* 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.
*/
BI_E_SUCCESS) {
BI_E_SUCCESS) {
return;
}
}
}
}
}
/*
* Provide a classful subnet mask based on the client's IP address.
*/
static in_addr_t
{
struct in_addr subnetmask;
char *netstr;
if (IN_CLASSA(client_ipaddr)) {
} else if (IN_CLASSB(client_ipaddr)) {
} else if (IN_CLASSC(client_ipaddr)) {
} else {
}
return (subnetmask.s_addr);
}
/*
* Informational output to the user (if interactive) or the bootlogger.
*/
static void
{
if (interactive) {
} else {
}
}
/*
* 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
{
char *urlstr;
url_t u;
/*
* 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.
*/
/*
* A valid host IP address is an absolute requirement.
*/
}
} else {
}
/*
* If a subnet mask was provided, use it; otherwise infer it.
*/
BI_E_SUCCESS) {
}
} else {
}
/*
* A legal bootserver URL is also an absolute requirement.
*/
BI_E_SUCCESS) {
u.https ||
(in_addr_t)-1) {
} else {
}
} else {
}
/*
* Is there a correctly-defined router?
*/
BI_E_SUCCESS) {
info("router not on local subnet!",
} else {
}
}
/*
* Is there a correctly-defined proxy?
*/
BI_E_SUCCESS) {
url_hport_t u;
} 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).
*/
}
}
/*
* 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.
*/
info("bootserver URL not on local subnet",
}
} else {
/*
* There must be a correctly-defined root_server URL.
*/
BC_ROOT_SERVER)) == NULL) {
info("cannot resolve root_server hostname!",
} else {
}
/*
* Is there a correctly-defined (non-empty) boot_logger URL?
*/
BC_BOOT_LOGGER)) != NULL) {
NULL) {
info("cannot resolve boot_logger hostname!",
} else {
}
}
/*
* 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.
*/
info("root_server URL not on local subnet",
}
info("boot_logger URL not on local subnet",
}
}
}
return (error);
}
/*
* Actually setup our network interface with the values derived from the
* PROM, DHCP or interactively from the user.
*/
static void
{
}
}
}
}
}
{
int which;
#if defined(__sparcv9)
/*
* Get the keys from PROM before we allow the user
* to override them from the CLI.
*/
#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.
*/
}
/*
* Obtain default interface values from bootinfo.
*/
/*
* Process the boot arguments (following the "-o" option).
*/
if (boot_arguments != NULL) {
(void) cli_eval_buf(boot_arguments,
}
/*
* from either the PROM or the boot arguments.
*/
/*
* If we don't already have a value for bootserver, try to
* deduce one. Refresh wbcli's idea of these values.
*/
/*
* Check that the information we have collected thus far is sufficient.
*/
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.
*/
do {
} else {
/*
* The user is not to be given the opportunity to
* enter further values; fail.
*/
"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.
*/
/*
* If net-config-strategy == "manual" then we must setup
* the interface now; if "dhcp" then it will already have
* been setup.
*/
return (B_TRUE);
}
wanboot_verify_config(void)
{
/*
* 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)) {
"incomplete boot configuration");
return (B_FALSE);
}
return (B_TRUE);
}