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