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