1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/types.h> 29 #include <sys/param.h> 30 #include <sys/salib.h> 31 #include <sys/promif.h> 32 #include <sys/wanboot_impl.h> 33 #include <netinet/in.h> 34 #include <parseURL.h> 35 #include <bootlog.h> 36 #include <sys/socket.h> 37 #include <netinet/inetutil.h> 38 #include <netinet/dhcp.h> 39 #include <dhcp_impl.h> 40 #include <lib/inet/mac.h> 41 #include <lib/inet/ipv4.h> 42 #include <lib/inet/dhcpv4.h> 43 #include <lib/sock/sock_test.h> 44 #include <sys/sunos_dhcp_class.h> 45 #include <aes.h> 46 #include <des3.h> 47 #include <hmac_sha1.h> 48 #include <netdb.h> 49 #include <wanboot_conf.h> 50 #include <bootinfo.h> 51 52 #include "wbcli.h" 53 54 #define skipspace(p) while (isspace(*(p))) ++p 55 56 #define skiptext(p) while (*(p) != '\0' && !isspace(*(p)) && \ 57 *(p) != '=' && *(p) != ',') ++p 58 59 #define PROMPT "boot> " 60 #define TEST_PROMPT "boot-test> " 61 62 #define CLI_SET 0 63 #define CLI_FAIL (-1) 64 #define CLI_EXIT (-2) 65 #define CLI_CONT (-3) 66 67 #define CLF_CMD 0x00000001 /* builtin command */ 68 #define CLF_ARG 0x00000002 /* boot argument directive */ 69 70 #define CLF_IF 0x00000100 /* interface parameter */ 71 #define CLF_BM 0x00000200 /* bootmisc parameter */ 72 73 #define CLF_VALSET 0x00010000 /* value set, may be null */ 74 #define CLF_HIDDEN 0x00020000 /* don't show its value (key) */ 75 #define CLF_VALMOD 0x00040000 /* value modified by the user */ 76 77 /* 78 * Macros for use in managing the flags in the cli_list[]. 79 * The conventions we follow are: 80 * 81 * CLF_VALSET is cleared if a value is removed from varptr 82 * CLF_VALSET is set if a value has been placed in varptr 83 * (that value need not be vetted) 84 * CLF_HIDDEN is set if a value must not be exposed to the user 85 * CLF_HIDDEN is cleared if a value can be exposed to the user 86 * CLF_VALMOD is cleared if a value in varptr has not been modified 87 * CLF_VALMOD is set if a value in varptr has been modified by 88 * the user 89 */ 90 #ifdef DEBUG 91 #define CLF_SETVAL(var) { \ 92 (((var)->flags) |= CLF_VALSET); \ 93 printf("set %s\n", var->varname);\ 94 } 95 96 #define CLF_ISSET(var) (printf("%s\n", \ 97 (((var)->flags) & CLF_VALSET) != 0 \ 98 ? "is set" : "not set"), \ 99 ((((var)->flags) & CLF_VALSET) != 0)) 100 101 #define CLF_CLRHIDDEN(var) { \ 102 (((var)->flags) &= ~CLF_HIDDEN); \ 103 printf("unhide %s\n", var->varname); \ 104 } 105 106 #define CLF_ISHIDDEN(var) (printf("%s\n", \ 107 (((var)->flags) & CLF_HIDDEN) != 0 \ 108 ? "is hidden" : "not hidden"), \ 109 ((((var)->flags) & CLF_HIDDEN) != 0)) 110 111 #define CLF_MODVAL(var) { \ 112 (((var)->flags) |= \ 113 (CLF_VALMOD | CLF_VALSET)); \ 114 printf("modified %s\n", var->varname);\ 115 } 116 117 #define CLF_ISMOD(var) (printf("%s\n", \ 118 (((var)->flags) & CLF_VALMOD) != 0 \ 119 ? "is set" : "not set"), \ 120 ((((var)->flags) & CLF_VALMOD) != 0)) 121 #else /* DEBUG */ 122 123 #define CLF_SETVAL(var) (((var)->flags) |= CLF_VALSET) 124 #define CLF_ISSET(var) ((((var)->flags) & CLF_VALSET) != 0) 125 #define CLF_CLRHIDDEN(var) (((var)->flags) &= ~CLF_HIDDEN) 126 #define CLF_ISHIDDEN(var) ((((var)->flags) & CLF_HIDDEN) != 0) 127 #define CLF_MODVAL(var) (((var)->flags) |= (CLF_VALMOD | CLF_VALSET)) 128 #define CLF_ISMOD(var) ((((var)->flags) & CLF_VALMOD) != 0) 129 130 #endif /* DEBUG */ 131 132 /* 133 * The width of the widest varname below - currently "subnet_mask". 134 */ 135 #define VAR_MAXWIDTH strlen(BI_SUBNET_MASK) 136 137 struct cli_ent; 138 typedef int claction_t(struct cli_ent *, char *, boolean_t); 139 140 typedef struct cli_ent { 141 char *varname; 142 claction_t *action; 143 int flags; 144 void *varptr; 145 uint_t varlen; 146 uint_t varmax; 147 } cli_ent_t; 148 149 static cli_ent_t *find_cli_ent(char *varstr); 150 151 static char cmdbuf[2048]; /* interpreter buffer */ 152 static char hostip[INET_ADDRSTRLEN]; 153 static char subnet[INET_ADDRSTRLEN]; 154 static char router[INET_ADDRSTRLEN]; 155 static char hostname[MAXHOSTNAMELEN]; 156 static char httpproxy[INET_ADDRSTRLEN + 5]; /* a.b.c.d:p */ 157 static char bootserverURL[URL_MAX_STRLEN + 1]; 158 static unsigned char clientid[WB_MAX_CID_LEN]; 159 static unsigned char aeskey[AES_128_KEY_SIZE]; 160 static unsigned char des3key[DES3_KEY_SIZE]; 161 static unsigned char sha1key[WANBOOT_HMAC_KEY_SIZE]; 162 static boolean_t args_specified_prompt = B_FALSE; 163 164 extern bc_handle_t bc_handle; 165 extern int getchar(void); 166 167 static claction_t clcid, clkey, clip, clstr, clurl, clhp; 168 static claction_t clhelp, cllist, clprompt, cldhcp, cltest, clgo, clexit; 169 170 static cli_ent_t cli_list[] = { 171 /* 172 * Commands/bootargs: 173 */ 174 { "test", cltest, CLF_ARG, 175 NULL, 0, 0 }, 176 { "dhcp", cldhcp, CLF_ARG, 177 NULL, 0, 0 }, 178 { "prompt", clprompt, CLF_CMD | CLF_ARG, 179 NULL, 0, 0 }, 180 { "list", cllist, CLF_CMD, 181 NULL, 0, 0 }, 182 { "help", clhelp, CLF_CMD, 183 NULL, 0, 0 }, 184 { "go", clgo, CLF_CMD, 185 NULL, 0, 0 }, 186 { "exit", clexit, CLF_CMD, 187 NULL, 0, 0 }, 188 189 /* 190 * Interface: 191 */ 192 { BI_HOST_IP, clip, CLF_IF, 193 hostip, 0, sizeof (hostip) }, 194 { BI_SUBNET_MASK, clip, CLF_IF, 195 subnet, 0, sizeof (subnet) }, 196 { BI_ROUTER_IP, clip, CLF_IF, 197 router, 0, sizeof (router) }, 198 { BI_HOSTNAME, clstr, CLF_IF, 199 hostname, 0, sizeof (hostname) }, 200 { BI_HTTP_PROXY, clhp, CLF_IF, 201 httpproxy, 0, sizeof (httpproxy) }, 202 { BI_CLIENT_ID, clcid, CLF_IF, 203 clientid, 0, sizeof (clientid) }, 204 205 /* 206 * Bootmisc: 207 */ 208 { BI_AES_KEY, clkey, CLF_BM | CLF_HIDDEN, 209 aeskey, 0, sizeof (aeskey) }, 210 { BI_3DES_KEY, clkey, CLF_BM | CLF_HIDDEN, 211 des3key, 0, sizeof (des3key) }, 212 { BI_SHA1_KEY, clkey, CLF_BM | CLF_HIDDEN, 213 sha1key, 0, sizeof (sha1key) }, 214 { BI_BOOTSERVER, clurl, CLF_BM, 215 bootserverURL, 0, sizeof (bootserverURL) }, 216 }; 217 218 static int num_cli_ent = (sizeof (cli_list) / sizeof (cli_ent_t)); 219 220 /* 221 * Fetch a line from the user, handling backspace appropriately. 222 */ 223 static int 224 editline(char *buf, int count) 225 { 226 int i = 0; 227 char c; 228 229 while (i < count - 1) { 230 c = getchar(); 231 if (c == '\n') { 232 break; 233 } else if (c == '\b') { 234 /* Clear for backspace. */ 235 if (i > 0) 236 i--; 237 continue; 238 } else { 239 buf[i++] = c; 240 } 241 } 242 buf[i] = '\0'; 243 return (i); 244 } 245 246 /* 247 * Assign a client-id to cliptr, or output cliptr's value as a client-id. 248 * On assignment the value is specified in valstr, either in hexascii or 249 * as a quoted string; on output its value is printed in hexascii. 250 */ 251 static int 252 clcid(cli_ent_t *cliptr, char *valstr, boolean_t out) 253 { 254 uint_t len, vmax; 255 boolean_t hexascii = B_TRUE; 256 char buffer[2 * WB_MAX_CID_LEN + 1]; 257 258 if (out) { 259 len = cliptr->varlen * 2 + 1; 260 (void) octet_to_hexascii(cliptr->varptr, cliptr->varlen, 261 buffer, &len); 262 printf("%s", buffer); 263 return (CLI_CONT); 264 } else { 265 len = strlen(valstr); 266 vmax = cliptr->varmax - 1; /* space for the prefix */ 267 268 /* 269 * Check whether the value is a quoted string; if so, strip 270 * the quotes and note that it's not in hexascii. 271 */ 272 if ((valstr[0] == '"' || valstr[0] == '\'') && 273 valstr[len-1] == valstr[0]) { 274 hexascii = B_FALSE; 275 ++valstr; 276 len -= 2; 277 valstr[len] = '\0'; 278 } else { 279 /* 280 * If the value contains any non-hex digits assume 281 * that it's not in hexascii. 282 */ 283 char *p; 284 285 for (p = valstr; *p != '\0'; ++p) { 286 if (!isxdigit(*p)) { 287 hexascii = B_FALSE; 288 break; 289 } 290 } 291 } 292 293 if (hexascii) { 294 if (len > vmax * 2 || 295 hexascii_to_octet(valstr, len, 296 (char *)(cliptr->varptr), &vmax) != 0) { 297 return (CLI_FAIL); 298 } 299 cliptr->varlen = vmax; 300 } else { 301 if (len > vmax) { 302 return (CLI_FAIL); 303 } 304 bcopy(valstr, cliptr->varptr, len); 305 cliptr->varlen = len; 306 } 307 308 return (CLI_SET); 309 } 310 } 311 312 /* 313 * Assign a key to cliptr, or output cliptr's value as a key. 314 * On assignment the value is specified in valstr in hexascii; 315 * on output its value is printed in hexascii, provided the key 316 * was entered at the interpreter (not obtained from OBP and 317 * thus hidden). 318 */ 319 static int 320 clkey(cli_ent_t *cliptr, char *valstr, boolean_t out) 321 { 322 uint_t len, vmax; 323 324 if (out) { 325 char buffer[2 * WANBOOT_MAXKEYLEN + 1]; 326 327 if (!CLF_ISHIDDEN(cliptr)) { 328 len = cliptr->varlen * 2 + 1; 329 (void) octet_to_hexascii(cliptr->varptr, 330 cliptr->varlen, buffer, &len); 331 printf("%s", buffer); 332 } else { 333 printf("*HIDDEN*"); 334 } 335 return (CLI_CONT); 336 } else { 337 len = strlen(valstr); 338 vmax = cliptr->varmax; 339 if (len != vmax * 2 || hexascii_to_octet(valstr, len, 340 cliptr->varptr, &vmax) != 0) { 341 return (CLI_FAIL); 342 } 343 cliptr->varlen = vmax; 344 CLF_CLRHIDDEN(cliptr); 345 return (CLI_SET); 346 } 347 } 348 349 /* 350 * Assign an IP address to cliptr, or output cliptr's value as an 351 * IP address. On assignment the value is specified in valstr in 352 * dotted-decimal format; on output its value is printed in dotted- 353 * decimal format. 354 */ 355 static int 356 clip(cli_ent_t *cliptr, char *valstr, boolean_t out) 357 { 358 uint_t len; 359 360 if (out) { 361 printf("%s", (char *)cliptr->varptr); 362 return (CLI_CONT); 363 } 364 365 if (inet_addr(valstr) == (in_addr_t)-1 || 366 (len = strlen(valstr)) >= cliptr->varmax) { 367 return (CLI_FAIL); 368 } 369 370 (void) strcpy(cliptr->varptr, valstr); 371 cliptr->varlen = len + 1; 372 return (CLI_SET); 373 } 374 375 /* 376 * Assign an arbitrary string to cliptr, or output cliptr's value as a string. 377 */ 378 static int 379 clstr(cli_ent_t *cliptr, char *valstr, boolean_t out) 380 { 381 uint_t len; 382 383 if (out) { 384 printf("%s", (char *)cliptr->varptr); 385 return (CLI_CONT); 386 } else { 387 if ((len = strlen(valstr)) >= cliptr->varmax) { 388 return (CLI_FAIL); 389 } else { 390 (void) strcpy(cliptr->varptr, valstr); 391 cliptr->varlen = len + 1; 392 return (CLI_SET); 393 } 394 } 395 } 396 397 /* 398 * Assign a URL to cliptr (having verified the format), or output cliptr's 399 * value as a URL. The host must be specified in dotted-decimal, and the 400 * scheme must not be https. 401 */ 402 static int 403 clurl(cli_ent_t *cliptr, char *valstr, boolean_t out) 404 { 405 url_t u; 406 uint_t len; 407 408 if (out) { 409 printf("%s", (char *)cliptr->varptr); 410 return (CLI_CONT); 411 } 412 413 if (url_parse(valstr, &u) != URL_PARSE_SUCCESS || 414 u.https || inet_addr(u.hport.hostname) == (in_addr_t)-1 || 415 (len = strlen(valstr)) >= cliptr->varmax) { 416 return (CLI_FAIL); 417 } 418 419 (void) strcpy(cliptr->varptr, valstr); 420 cliptr->varlen = len + 1; 421 return (CLI_SET); 422 } 423 424 /* 425 * Assign a hostport to cliptr (having verified the format), or output cliptr's 426 * value as a hostport. The host must be specified in dotted-decimal. 427 */ 428 static int 429 clhp(cli_ent_t *cliptr, char *valstr, boolean_t out) 430 { 431 url_hport_t u; 432 uint_t len; 433 434 if (out) { 435 printf("%s", (char *)cliptr->varptr); 436 return (CLI_CONT); 437 } 438 439 if (url_parse_hostport(valstr, &u, URL_DFLT_PROXY_PORT) != 440 URL_PARSE_SUCCESS || 441 inet_addr(u.hostname) == (in_addr_t)-1 || 442 (len = strlen(valstr)) >= cliptr->varmax) { 443 return (CLI_FAIL); 444 } 445 446 (void) strcpy(cliptr->varptr, valstr); 447 cliptr->varlen = len + 1; 448 return (CLI_SET); 449 } 450 451 /* 452 * Exit the interpreter and return to the booter. 453 */ 454 /*ARGSUSED*/ 455 static int 456 clgo(cli_ent_t *cliptr, char *valstr, boolean_t out) 457 { 458 return (CLI_EXIT); 459 } 460 461 /* 462 * Exit the interpreter and return to OBP. 463 */ 464 /*ARGSUSED*/ 465 static int 466 clexit(cli_ent_t *cliptr, char *valstr, boolean_t out) 467 { 468 prom_exit_to_mon(); 469 /*NOTREACHED*/ 470 return (CLI_EXIT); 471 } 472 473 /* 474 * Provide simple help information. 475 */ 476 /*ARGSUSED*/ 477 static int 478 clhelp(cli_ent_t *cliptr, char *valstr, boolean_t out) 479 { 480 printf("var=val - set variable\n"); 481 printf("var= - unset variable\n"); 482 printf("var - print variable\n"); 483 printf("list - list variables and their values\n"); 484 printf("prompt - prompt for unset variables\n"); 485 printf("go - continue booting\n"); 486 printf("exit - quit boot interpreter and return to OBP\n"); 487 488 return (CLI_CONT); 489 } 490 491 /* 492 * List variables and their current values. 493 */ 494 /*ARGSUSED*/ 495 static int 496 cllist(cli_ent_t *cliptr, char *valstr, boolean_t out) 497 { 498 int wanted = (int)(uintptr_t)valstr; /* use uintptr_t for gcc */ 499 int i; 500 501 wanted &= ~(CLF_CMD | CLF_ARG); 502 503 for (cliptr = cli_list; cliptr < &cli_list[num_cli_ent]; cliptr++) { 504 if ((cliptr->flags & (CLF_CMD | CLF_ARG)) != 0 || 505 (cliptr->flags & wanted) == 0) { 506 continue; 507 } 508 printf("%s: ", cliptr->varname); 509 /* 510 * Line the values up - space to the width of the widest 511 * varname + 1 for the ':'. 512 */ 513 for (i = VAR_MAXWIDTH + 1 - strlen(cliptr->varname); 514 i > 0; --i) { 515 printf(" "); 516 } 517 518 if (CLF_ISSET(cliptr) || CLF_ISHIDDEN(cliptr)) { 519 (void) cliptr->action(cliptr, NULL, B_TRUE); 520 printf("\n"); 521 } else { 522 printf("UNSET\n"); 523 } 524 } 525 526 return (CLI_CONT); 527 } 528 529 /* 530 * Prompt for wanted values. 531 */ 532 /*ARGSUSED*/ 533 static int 534 clprompt(cli_ent_t *cliptr, char *valstr, boolean_t out) 535 { 536 char *p; 537 int wanted = (int)(uintptr_t)valstr; /* use uintrptr_t for gcc */ 538 539 /* 540 * If processing boot arguments, simply note the fact that clprompt() 541 * should be invoked later when other parameters may be supplied. 542 */ 543 if ((wanted & CLF_ARG) != 0) { 544 args_specified_prompt = B_TRUE; 545 return (CLI_CONT); 546 } 547 wanted &= ~(CLF_CMD | CLF_ARG); 548 549 for (cliptr = cli_list; cliptr < &cli_list[num_cli_ent]; ++cliptr) { 550 if ((cliptr->flags & wanted) == 0) { 551 continue; 552 } 553 554 printf("%s", cliptr->varname); 555 if (CLF_ISSET(cliptr)) { 556 printf(" ["); 557 (void) cliptr->action(cliptr, NULL, B_TRUE); 558 printf("]"); 559 } 560 printf("? "); 561 (void) editline(cmdbuf, sizeof (cmdbuf)); 562 printf("\n"); 563 564 p = cmdbuf; 565 skipspace(p); 566 if (*p == '\0') { /* nothing there */ 567 continue; 568 } 569 570 /* Get valstr and nul terminate */ 571 valstr = p; 572 ++p; 573 skiptext(p); 574 *p = '\0'; 575 576 /* If empty value, do nothing */ 577 if (strlen(valstr) == 0) { 578 continue; 579 } 580 581 switch (cliptr->action(cliptr, valstr, B_FALSE)) { 582 case CLI_SET: 583 CLF_MODVAL(cliptr); 584 break; 585 case CLI_FAIL: 586 printf("Incorrect format, parameter unchanged!\n"); 587 break; 588 case CLI_EXIT: 589 return (CLI_EXIT); 590 case CLI_CONT: 591 break; 592 } 593 } 594 595 return (CLI_CONT); 596 } 597 598 /* 599 * If the PROM has done DHCP, bind the interface; otherwise do the full 600 * DHCP packet exchange. 601 */ 602 /*ARGSUSED*/ 603 static int 604 cldhcp(cli_ent_t *cliptr, char *valstr, boolean_t out) 605 { 606 static boolean_t first_time = B_TRUE; 607 static int ret = CLI_CONT; 608 609 if (first_time) { 610 /* 611 * Set DHCP's idea of the client_id from our cached value. 612 */ 613 cliptr = find_cli_ent(BI_CLIENT_ID); 614 if (CLF_ISMOD(cliptr)) { 615 dhcp_set_client_id(cliptr->varptr, cliptr->varlen); 616 } 617 618 bootlog("wanboot", BOOTLOG_INFO, "Starting DHCP configuration"); 619 620 (void) ipv4_setpromiscuous(B_TRUE); 621 if (dhcp() == 0) { 622 bootlog("wanboot", BOOTLOG_INFO, 623 "DHCP configuration succeeded"); 624 } else { 625 bootlog("wanboot", BOOTLOG_CRIT, 626 "DHCP configuration failed"); 627 ret = CLI_FAIL; 628 } 629 (void) ipv4_setpromiscuous(B_FALSE); 630 631 first_time = B_FALSE; 632 } 633 634 return (ret); 635 } 636 637 /* 638 * Invoke the socket test interpreter (for testing purposes only). 639 */ 640 /*ARGSUSED*/ 641 static int 642 cltest(cli_ent_t *cliptr, char *valstr, boolean_t out) 643 { 644 (void) ipv4_setpromiscuous(B_FALSE); 645 printf("\n"); 646 for (;;) { 647 printf(TEST_PROMPT); 648 if (editline(cmdbuf, sizeof (cmdbuf)) > 0) { 649 printf("\n"); 650 (void) st_interpret(cmdbuf); 651 } else { 652 prom_exit_to_mon(); 653 /* NOTREACHED */ 654 } 655 } 656 657 /* NOTREACHED */ 658 return (CLI_CONT); 659 } 660 661 /* 662 * Return the cliptr corresponding to the named variable. 663 */ 664 static cli_ent_t * 665 find_cli_ent(char *varstr) 666 { 667 cli_ent_t *cliptr; 668 669 for (cliptr = cli_list; cliptr < &cli_list[num_cli_ent]; ++cliptr) { 670 if (strcmp(varstr, cliptr->varname) == 0) { 671 return (cliptr); 672 } 673 } 674 675 return (NULL); 676 } 677 678 /* 679 * Evaluate the commands provided by the user (either as "-o" boot arguments 680 * or interactively at the boot interpreter). 681 */ 682 static int 683 cli_eval_buf(char *inbuf, int wanted) 684 { 685 char *p, *varstr, *end_varstr, *valstr, *end_valstr; 686 boolean_t assign; 687 cli_ent_t *cliptr; 688 689 for (p = inbuf; *p != '\0'; ) { 690 skipspace(p); 691 692 /* If nothing more on line, go get the next one */ 693 if (*p == '\0') { 694 break; 695 } else if (*p == ',') { /* orphan ',' ? */ 696 ++p; 697 continue; 698 } 699 700 /* Get ptrs to start & end of variable */ 701 varstr = p; 702 ++p; 703 skiptext(p); 704 end_varstr = p; 705 skipspace(p); 706 707 /* See if we're doing an assignment */ 708 valstr = NULL; 709 if (*p != '=') { /* nope, just printing */ 710 assign = B_FALSE; 711 } else { 712 assign = B_TRUE; 713 ++p; /* past '=' */ 714 skipspace(p); 715 716 /* Assigning something? (else clear variable) */ 717 if (*p != '\0' && *p != ',') { 718 /* Get ptrs to start & end of valstr */ 719 valstr = p; 720 ++p; 721 skiptext(p); 722 end_valstr = p; 723 skipspace(p); 724 } 725 } 726 727 /* Skip ',' delimiter if present */ 728 if (*p == ',') { 729 ++p; 730 } 731 732 /* Nul-terminate varstr and valstr (if appropriate) */ 733 *end_varstr = '\0'; 734 if (valstr != NULL) { 735 *end_valstr = '\0'; 736 } 737 738 if ((cliptr = find_cli_ent(varstr)) == NULL) { 739 printf("Unknown variable '%s'; ignored\n", varstr); 740 continue; 741 } 742 743 /* 744 * It's an error to specify a parameter which can only be a 745 * boot argument (and not a command) when not processing the 746 * boot arguments. 747 */ 748 if ((cliptr->flags & (CLF_CMD | CLF_ARG)) == CLF_ARG && 749 (wanted & CLF_ARG) == 0) { 750 printf("'%s' may only be specified as a " 751 "boot argument; ignored\n", varstr); 752 continue; 753 } 754 755 /* 756 * When doing an assignment, verify that it's not a command 757 * or argument name, and that it is permissible in the current 758 * context. An 'empty' assignment (var=) is treated the same 759 * as a null assignment (var=""). 760 * 761 * If processing the boot arguments, it is an error to not 762 * assign a value to a non-argument parameter. 763 */ 764 if (assign) { 765 if ((cliptr->flags & (CLF_CMD | CLF_ARG)) != 0) { 766 printf("'%s' is a command and cannot " 767 "be assigned\n", varstr); 768 return (CLI_FAIL); 769 } 770 if ((cliptr->flags & wanted) == 0) { 771 printf("'%s' cannot be assigned\n", varstr); 772 return (CLI_FAIL); 773 } 774 775 if (valstr == NULL) { 776 cliptr->varlen = 0; 777 CLF_MODVAL(cliptr); 778 continue; 779 } 780 } else if ((wanted & CLF_ARG) != 0 && 781 (cliptr->flags & (CLF_CMD | CLF_ARG)) == 0) { 782 printf("'%s' must be assigned when specified in " 783 " the boot arguments\n", varstr); 784 return (CLI_FAIL); 785 } 786 787 /* 788 * Pass 'wanted' to command-handling functions, in particular 789 * clprompt() and cllist(). 790 */ 791 if ((cliptr->flags & CLF_CMD) != 0) { 792 /* use uintptr_t to suppress the gcc warning */ 793 valstr = (char *)(uintptr_t)wanted; 794 } 795 796 /* 797 * Call the parameter's action function. 798 */ 799 switch (cliptr->action(cliptr, valstr, !assign)) { 800 case CLI_SET: 801 CLF_MODVAL(cliptr); 802 break; 803 case CLI_FAIL: 804 printf("Incorrect format: variable '%s' not set\n", 805 cliptr->varname); 806 break; 807 case CLI_EXIT: 808 return (CLI_EXIT); 809 case CLI_CONT: 810 if (!assign) { 811 printf("\n"); 812 } 813 break; 814 } 815 } 816 817 return (CLI_CONT); 818 } 819 820 static void 821 cli_interpret(int wanted) 822 { 823 printf("\n"); 824 do { 825 printf(PROMPT); 826 (void) editline(cmdbuf, sizeof (cmdbuf)); 827 printf("\n"); 828 829 } while (cli_eval_buf(cmdbuf, wanted) != CLI_EXIT); 830 } 831 832 #if defined(__sparcv9) 833 /* 834 * This routine queries the PROM to see what encryption keys exist. 835 */ 836 static void 837 get_prom_encr_keys() 838 { 839 cli_ent_t *cliptr; 840 char encr_key[WANBOOT_MAXKEYLEN]; 841 int keylen; 842 int status; 843 int ret; 844 845 /* 846 * At the top of the priority list, we have AES. 847 */ 848 ret = prom_get_security_key(WANBOOT_AES_128_KEY_NAME, encr_key, 849 WANBOOT_MAXKEYLEN, &keylen, &status); 850 if ((ret == 0) && (status == 0) && (keylen == AES_128_KEY_SIZE)) { 851 cliptr = find_cli_ent(BI_AES_KEY); 852 bcopy(encr_key, cliptr->varptr, AES_128_KEY_SIZE); 853 cliptr->varlen = AES_128_KEY_SIZE; 854 CLF_MODVAL(cliptr); 855 } 856 857 /* 858 * Next, 3DES. 859 */ 860 ret = prom_get_security_key(WANBOOT_DES3_KEY_NAME, encr_key, 861 WANBOOT_MAXKEYLEN, &keylen, &status); 862 if ((ret == 0) && (status == 0) && (keylen == DES3_KEY_SIZE)) { 863 cliptr = find_cli_ent(BI_3DES_KEY); 864 bcopy(encr_key, cliptr->varptr, DES3_KEY_SIZE); 865 cliptr->varlen = DES3_KEY_SIZE; 866 CLF_MODVAL(cliptr); 867 } 868 } 869 870 /* 871 * This routine queries the PROM to see what hashing keys exist. 872 */ 873 static void 874 get_prom_hash_keys() 875 { 876 cli_ent_t *cliptr; 877 char hash_key[WANBOOT_HMAC_KEY_SIZE]; 878 int keylen; 879 int status; 880 int ret; 881 882 /* 883 * The only supported key thus far is SHA1. 884 */ 885 ret = prom_get_security_key(WANBOOT_HMAC_SHA1_KEY_NAME, hash_key, 886 WANBOOT_HMAC_KEY_SIZE, &keylen, &status); 887 if ((ret == 0) && (status == 0) && (keylen == WANBOOT_HMAC_KEY_SIZE)) { 888 cliptr = find_cli_ent(BI_SHA1_KEY); 889 bcopy(hash_key, cliptr->varptr, WANBOOT_HMAC_KEY_SIZE); 890 cliptr->varlen = WANBOOT_HMAC_KEY_SIZE; 891 CLF_MODVAL(cliptr); 892 } 893 } 894 #endif /* defined(__sparcv9) */ 895 896 /* 897 * For the given parameter type(s), get values from bootinfo and cache in 898 * the local variables used by the "boot>" interpreter. 899 */ 900 static void 901 bootinfo_defaults(int which) 902 { 903 cli_ent_t *cliptr; 904 905 for (cliptr = cli_list; cliptr < &cli_list[num_cli_ent]; ++cliptr) { 906 if ((cliptr->flags & which) != 0 && !CLF_ISSET(cliptr)) { 907 size_t len = cliptr->varmax; 908 909 if (bootinfo_get(cliptr->varname, cliptr->varptr, 910 &len, NULL) == BI_E_SUCCESS) { 911 cliptr->varlen = len; 912 CLF_SETVAL(cliptr); 913 } 914 } 915 } 916 } 917 918 /* 919 * For the given parameter type(s), store values entered at the "boot>" 920 * interpreter back into bootinfo. 921 */ 922 static void 923 update_bootinfo(int which) 924 { 925 cli_ent_t *cliptr; 926 927 for (cliptr = cli_list; cliptr < &cli_list[num_cli_ent]; ++cliptr) { 928 if ((cliptr->flags & which) != 0 && CLF_ISMOD(cliptr)) { 929 (void) bootinfo_put(cliptr->varname, 930 cliptr->varptr, cliptr->varlen, 0); 931 } 932 } 933 } 934 935 /* 936 * Return the net-config-strategy: "dhcp", "manual" or "rarp" 937 */ 938 static char * 939 net_config_strategy(void) 940 { 941 static char ncs[8]; /* "dhcp" or "manual" */ 942 size_t len = sizeof (ncs); 943 944 if (ncs[0] == '\0' && 945 bootinfo_get(BI_NET_CONFIG_STRATEGY, ncs, &len, NULL) != 946 BI_E_SUCCESS) { 947 /* 948 * Support for old PROMs: create the net-config-strategy 949 * property under /chosen with an appropriate value. If we 950 * have a bootp-response (not interested in its value, just 951 * its presence) then we did DHCP; otherwise configuration 952 * is manual. 953 */ 954 if (bootinfo_get(BI_BOOTP_RESPONSE, NULL, NULL, 955 NULL) == BI_E_BUF2SMALL) { 956 (void) strcpy(ncs, "dhcp"); 957 } else { 958 (void) strcpy(ncs, "manual"); 959 } 960 (void) bootinfo_put(BI_NET_CONFIG_STRATEGY, ncs, strlen(ncs), 961 BI_R_CHOSEN); 962 963 bootlog("wanboot", BOOTLOG_INFO, 964 "Default net-config-strategy: %s", ncs); 965 } 966 967 return (ncs); 968 } 969 970 /* 971 * If there is no client-id property published in /chosen (by the PROM or the 972 * boot interpreter) provide a default client-id based on the MAC address of 973 * the client. 974 * As specified in RFC2132 (section 9.14), this is prefixed with a byte 975 * which specifies the ARP hardware type defined in RFC1700 (for Ethernet, 976 * this should be 1). 977 */ 978 static void 979 generate_default_clientid(void) 980 { 981 char clid[WB_MAX_CID_LEN]; 982 size_t len = sizeof (clid); 983 984 if (bootinfo_get(BI_CLIENT_ID, clid, &len, NULL) != BI_E_SUCCESS) { 985 len = mac_get_addr_len() + 1; /* include hwtype */ 986 987 if (len > sizeof (clid)) { 988 return; 989 } 990 991 clid[0] = mac_arp_type(mac_get_type()); 992 bcopy(mac_get_addr_buf(), &clid[1], len - 1); 993 994 (void) bootinfo_put(BI_CLIENT_ID, clid, len, 0); 995 } 996 } 997 998 /* 999 * Determine the URL of the boot server from the 'file' parameter to OBP, 1000 * the SbootURI or BootFile DHCP options, or the 'bootserver' value entered 1001 * either as a "-o" argument or at the interpreter. 1002 */ 1003 static void 1004 determine_bootserver_url(void) 1005 { 1006 char bs[URL_MAX_STRLEN + 1]; 1007 size_t len; 1008 url_t url; 1009 1010 if (bootinfo_get(BI_BOOTSERVER, bs, &len, NULL) != BI_E_SUCCESS) { 1011 /* 1012 * If OBP has published a network-boot-file property in 1013 * /chosen (or there is a DHCP BootFile or SbootURI vendor 1014 * option) and it's a URL, construct the bootserver URL 1015 * from it. 1016 */ 1017 len = URL_MAX_STRLEN; 1018 if (bootinfo_get(BI_NETWORK_BOOT_FILE, bs, &len, NULL) != 1019 BI_E_SUCCESS) { 1020 len = URL_MAX_STRLEN; 1021 if (bootinfo_get(BI_BOOTFILE, bs, &len, NULL) != 1022 BI_E_SUCCESS) { 1023 return; 1024 } 1025 } 1026 if (url_parse(bs, &url) == URL_PARSE_SUCCESS) { 1027 (void) bootinfo_put(BI_BOOTSERVER, bs, len, 0); 1028 } 1029 } 1030 } 1031 1032 /* 1033 * Provide a classful subnet mask based on the client's IP address. 1034 */ 1035 static in_addr_t 1036 generate_classful_subnet(in_addr_t client_ipaddr) 1037 { 1038 struct in_addr subnetmask; 1039 char *netstr; 1040 1041 if (IN_CLASSA(client_ipaddr)) { 1042 subnetmask.s_addr = IN_CLASSA_NET; 1043 } else if (IN_CLASSB(client_ipaddr)) { 1044 subnetmask.s_addr = IN_CLASSB_NET; 1045 } else if (IN_CLASSC(client_ipaddr)) { 1046 subnetmask.s_addr = IN_CLASSC_NET; 1047 } else { 1048 subnetmask.s_addr = IN_CLASSE_NET; 1049 } 1050 1051 netstr = inet_ntoa(subnetmask); 1052 (void) bootinfo_put(BI_SUBNET_MASK, netstr, strlen(netstr) + 1, 0); 1053 1054 return (subnetmask.s_addr); 1055 } 1056 1057 /* 1058 * Informational output to the user (if interactive) or the bootlogger. 1059 */ 1060 static void 1061 info(const char *msg, boolean_t interactive) 1062 { 1063 if (interactive) { 1064 printf("%s\n", msg); 1065 } else { 1066 bootlog("wanboot", BOOTLOG_INFO, "%s", msg); 1067 } 1068 } 1069 1070 /* 1071 * Determine whether we have sufficient information to proceed with booting, 1072 * either for configuring the interface and downloading the bootconf file, 1073 * or for downloading the miniroot. 1074 */ 1075 static int 1076 config_incomplete(int why, boolean_t interactive) 1077 { 1078 boolean_t error = B_FALSE; 1079 char buf[URL_MAX_STRLEN + 1]; 1080 size_t len; 1081 char *urlstr; 1082 url_t u; 1083 struct hostent *hp; 1084 in_addr_t client_ipaddr, ipaddr, bsnet, pxnet; 1085 static in_addr_t subnetmask, clnet; 1086 static boolean_t have_router = B_FALSE; 1087 static boolean_t have_proxy = B_FALSE; 1088 boolean_t have_root_server = B_FALSE; 1089 boolean_t have_boot_logger = B_FALSE; 1090 in_addr_t rsnet, blnet; 1091 1092 /* 1093 * Note that 'have_router', 'have_proxy', 'subnetmask', and 'clnet' 1094 * are static, so that their values (gathered when checking the 1095 * interface configuration) may be used again when checking the boot 1096 * configuration. 1097 */ 1098 if (why == CLF_IF) { 1099 /* 1100 * A valid host IP address is an absolute requirement. 1101 */ 1102 len = sizeof (buf); 1103 if (bootinfo_get(BI_HOST_IP, buf, &len, NULL) == BI_E_SUCCESS) { 1104 if ((client_ipaddr = inet_addr(buf)) == (in_addr_t)-1) { 1105 info("host-ip invalid!", interactive); 1106 error = B_TRUE; 1107 } 1108 } else { 1109 info("host-ip not set!", interactive); 1110 error = B_TRUE; 1111 } 1112 1113 /* 1114 * If a subnet mask was provided, use it; otherwise infer it. 1115 */ 1116 len = sizeof (buf); 1117 if (bootinfo_get(BI_SUBNET_MASK, buf, &len, NULL) == 1118 BI_E_SUCCESS) { 1119 if ((subnetmask = inet_addr(buf)) == (in_addr_t)-1) { 1120 info("subnet-mask invalid!", interactive); 1121 error = B_TRUE; 1122 } 1123 } else { 1124 info("Defaulting to classful subnetting", interactive); 1125 1126 subnetmask = generate_classful_subnet(client_ipaddr); 1127 } 1128 clnet = client_ipaddr & subnetmask; 1129 1130 /* 1131 * A legal bootserver URL is also an absolute requirement. 1132 */ 1133 len = sizeof (buf); 1134 if (bootinfo_get(BI_BOOTSERVER, buf, &len, NULL) == 1135 BI_E_SUCCESS) { 1136 if (url_parse(buf, &u) != URL_PARSE_SUCCESS || 1137 u.https || 1138 (ipaddr = inet_addr(u.hport.hostname)) == 1139 (in_addr_t)-1) { 1140 info("bootserver not legal URL!", interactive); 1141 error = B_TRUE; 1142 } else { 1143 bsnet = ipaddr & subnetmask; 1144 } 1145 } else { 1146 info("bootserver not specified!", interactive); 1147 error = B_TRUE; 1148 } 1149 1150 /* 1151 * Is there a correctly-defined router? 1152 */ 1153 len = sizeof (buf); 1154 if (bootinfo_get(BI_ROUTER_IP, buf, &len, NULL) == 1155 BI_E_SUCCESS) { 1156 if ((ipaddr = inet_addr(buf)) == (in_addr_t)-1) { 1157 info("router-ip invalid!", interactive); 1158 error = B_TRUE; 1159 } else if (clnet != (ipaddr & subnetmask)) { 1160 info("router not on local subnet!", 1161 interactive); 1162 error = B_TRUE; 1163 } else { 1164 have_router = B_TRUE; 1165 } 1166 } 1167 1168 /* 1169 * Is there a correctly-defined proxy? 1170 */ 1171 len = sizeof (buf); 1172 if (bootinfo_get(BI_HTTP_PROXY, buf, &len, NULL) == 1173 BI_E_SUCCESS) { 1174 url_hport_t u; 1175 1176 if (url_parse_hostport(buf, &u, URL_DFLT_PROXY_PORT) != 1177 URL_PARSE_SUCCESS || 1178 (ipaddr = inet_addr(u.hostname)) == (in_addr_t)-1) { 1179 info("http-proxy port invalid!", interactive); 1180 error = B_TRUE; 1181 } else { 1182 /* 1183 * The proxy is only of use to us if it's on 1184 * our local subnet, or if a router has been 1185 * specified (which should hopefully allow us 1186 * to access the proxy). 1187 */ 1188 pxnet = ipaddr & subnetmask; 1189 have_proxy = (have_router || pxnet == clnet); 1190 } 1191 } 1192 1193 /* 1194 * If there is no router and no proxy (either on the local 1195 * subnet or reachable via a router), then the bootserver 1196 * URL must be on the local net. 1197 */ 1198 if (!error && !have_router && !have_proxy && bsnet != clnet) { 1199 info("bootserver URL not on local subnet", 1200 interactive); 1201 error = B_TRUE; 1202 } 1203 } else { 1204 /* 1205 * There must be a correctly-defined root_server URL. 1206 */ 1207 if ((urlstr = bootconf_get(&bc_handle, 1208 BC_ROOT_SERVER)) == NULL) { 1209 info("no root_server URL!", interactive); 1210 error = B_TRUE; 1211 } else if (url_parse(urlstr, &u) != URL_PARSE_SUCCESS) { 1212 info("root_server not legal URL!", interactive); 1213 error = B_TRUE; 1214 } else if ((hp = gethostbyname(u.hport.hostname)) == NULL) { 1215 info("cannot resolve root_server hostname!", 1216 interactive); 1217 error = B_TRUE; 1218 } else { 1219 rsnet = *(in_addr_t *)hp->h_addr & subnetmask; 1220 have_root_server = B_TRUE; 1221 } 1222 1223 /* 1224 * Is there a correctly-defined (non-empty) boot_logger URL? 1225 */ 1226 if ((urlstr = bootconf_get(&bc_handle, 1227 BC_BOOT_LOGGER)) != NULL) { 1228 if (url_parse(urlstr, &u) != URL_PARSE_SUCCESS) { 1229 info("boot_logger not legal URL!", interactive); 1230 error = B_TRUE; 1231 } else if ((hp = gethostbyname(u.hport.hostname)) == 1232 NULL) { 1233 info("cannot resolve boot_logger hostname!", 1234 interactive); 1235 error = B_TRUE; 1236 } else { 1237 blnet = *(in_addr_t *)hp->h_addr & subnetmask; 1238 have_boot_logger = B_TRUE; 1239 } 1240 } 1241 1242 /* 1243 * If there is no router and no proxy (either on the local 1244 * subnet or reachable via a router), then the root_server 1245 * URL (and the boot_logger URL if specified) must be on the 1246 * local net. 1247 */ 1248 if (!error && !have_router && !have_proxy) { 1249 if (have_root_server && rsnet != clnet) { 1250 info("root_server URL not on local subnet", 1251 interactive); 1252 error = B_TRUE; 1253 } 1254 if (have_boot_logger && blnet != clnet) { 1255 info("boot_logger URL not on local subnet", 1256 interactive); 1257 error = B_TRUE; 1258 } 1259 } 1260 } 1261 1262 return (error); 1263 } 1264 1265 /* 1266 * Actually setup our network interface with the values derived from the 1267 * PROM, DHCP or interactively from the user. 1268 */ 1269 static void 1270 setup_interface() 1271 { 1272 char str[MAXHOSTNAMELEN]; /* will accomodate an IP too */ 1273 size_t len; 1274 struct in_addr in_addr; 1275 1276 len = sizeof (str); 1277 if (bootinfo_get(BI_HOST_IP, str, &len, NULL) == BI_E_SUCCESS && 1278 (in_addr.s_addr = inet_addr(str)) != (in_addr_t)-1) { 1279 in_addr.s_addr = htonl(in_addr.s_addr); 1280 ipv4_setipaddr(&in_addr); 1281 } 1282 1283 len = sizeof (str); 1284 if (bootinfo_get(BI_SUBNET_MASK, str, &len, NULL) == BI_E_SUCCESS && 1285 (in_addr.s_addr = inet_addr(str)) != (in_addr_t)-1) { 1286 in_addr.s_addr = htonl(in_addr.s_addr); 1287 ipv4_setnetmask(&in_addr); 1288 } 1289 1290 len = sizeof (str); 1291 if (bootinfo_get(BI_ROUTER_IP, str, &len, NULL) == BI_E_SUCCESS && 1292 (in_addr.s_addr = inet_addr(str)) != (in_addr_t)-1) { 1293 in_addr.s_addr = htonl(in_addr.s_addr); 1294 ipv4_setdefaultrouter(&in_addr); 1295 (void) ipv4_route(IPV4_ADD_ROUTE, RT_DEFAULT, NULL, &in_addr); 1296 } 1297 1298 len = sizeof (str); 1299 if (bootinfo_get(BI_HOSTNAME, str, &len, NULL) == BI_E_SUCCESS) { 1300 (void) sethostname(str, len); 1301 } 1302 } 1303 1304 boolean_t 1305 wanboot_init_interface(char *boot_arguments) 1306 { 1307 boolean_t interactive; 1308 int which; 1309 1310 #if defined(__sparcv9) 1311 /* 1312 * Get the keys from PROM before we allow the user 1313 * to override them from the CLI. 1314 */ 1315 get_prom_encr_keys(); 1316 get_prom_hash_keys(); 1317 #endif /* defined(__sparcv9) */ 1318 1319 /* 1320 * If there is already a bootp-response property under 1321 * /chosen then the PROM must have done DHCP for us; 1322 * invoke dhcp() to 'bind' the interface. 1323 */ 1324 if (bootinfo_get(BI_BOOTP_RESPONSE, NULL, NULL, NULL) == 1325 BI_E_BUF2SMALL) { 1326 (void) cldhcp(NULL, NULL, 0); 1327 } 1328 1329 /* 1330 * Obtain default interface values from bootinfo. 1331 */ 1332 bootinfo_defaults(CLF_IF); 1333 1334 /* 1335 * Process the boot arguments (following the "-o" option). 1336 */ 1337 if (boot_arguments != NULL) { 1338 (void) cli_eval_buf(boot_arguments, 1339 (CLF_ARG | CLF_IF | CLF_BM)); 1340 } 1341 1342 /* 1343 * Stash away any interface/bootmisc parameter values we got 1344 * from either the PROM or the boot arguments. 1345 */ 1346 update_bootinfo(CLF_IF | CLF_BM); 1347 1348 /* 1349 * If we don't already have a value for bootserver, try to 1350 * deduce one. Refresh wbcli's idea of these values. 1351 */ 1352 determine_bootserver_url(); 1353 bootinfo_defaults(CLF_BM); 1354 1355 /* 1356 * Check that the information we have collected thus far is sufficient. 1357 */ 1358 interactive = args_specified_prompt; 1359 1360 if (interactive) { 1361 /* 1362 * Drop into the boot interpreter to allow the input 1363 * of keys, bootserver and bootmisc, and in the case 1364 * that net-config-strategy == "manual" the interface 1365 * parameters. 1366 */ 1367 which = CLF_BM | CLF_CMD; 1368 if (strcmp(net_config_strategy(), "manual") == 0) 1369 which |= CLF_IF; 1370 1371 do { 1372 cli_interpret(which); 1373 update_bootinfo(CLF_IF | CLF_BM); 1374 } while (config_incomplete(CLF_IF, interactive)); 1375 } else { 1376 /* 1377 * The user is not to be given the opportunity to 1378 * enter further values; fail. 1379 */ 1380 if (config_incomplete(CLF_IF, interactive)) { 1381 bootlog("wanboot", BOOTLOG_CRIT, 1382 "interface incorrectly configured"); 1383 return (B_FALSE); 1384 } 1385 } 1386 1387 /* 1388 * If a wanboot-enabled PROM hasn't processed client-id in 1389 * network-boot-arguments, or no value for client-id has been 1390 * specified to the boot interpreter, then provide a default 1391 * client-id based on our MAC address. 1392 */ 1393 generate_default_clientid(); 1394 1395 /* 1396 * If net-config-strategy == "manual" then we must setup 1397 * the interface now; if "dhcp" then it will already have 1398 * been setup. 1399 */ 1400 if (strcmp(net_config_strategy(), "manual") == 0) 1401 setup_interface(); 1402 return (B_TRUE); 1403 } 1404 1405 boolean_t 1406 wanboot_verify_config(void) 1407 { 1408 /* 1409 * Check that the wanboot.conf file defines a valid root_server 1410 * URL, and check that, if given, the boot_logger URL is valid. 1411 */ 1412 if (config_incomplete(0, B_FALSE)) { 1413 bootlog("wanboot", BOOTLOG_CRIT, 1414 "incomplete boot configuration"); 1415 return (B_FALSE); 1416 } 1417 return (B_TRUE); 1418 }