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 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * Functions for accessing the wanboot.conf(4) file. 30 */ 31 32 #include <stdio.h> 33 #include <string.h> 34 #include <sys/types.h> 35 #include <parseURL.h> 36 #include <netboot_paths.h> 37 #include <wanboot_conf.h> 38 39 /* 40 * Parser helper macros: 41 */ 42 #define is_whitespace(c) ((c) == ' ' || (c) == '\t') 43 #define skip_whitespace(p) while (is_whitespace(*(p))) ++p 44 45 /* 46 * Table of valid wanboot.conf(4) names: 47 */ 48 static const char *bootconf_names[] = { 49 BC_BOOT_FILE, 50 BC_ROOT_SERVER, 51 BC_ROOT_FILE, 52 BC_ENCRYPTION_TYPE, 53 BC_SIGNATURE_TYPE, 54 BC_CLIENT_AUTHENTICATION, 55 BC_SERVER_AUTHENTICATION, 56 BC_BOOT_LOGGER, 57 BC_RESOLVE_HOSTS, 58 BC_SYSTEM_CONF, 59 NULL 60 }; 61 62 /* 63 * Check whether 'name' is valid within wanboot.conf(4). 64 */ 65 static boolean_t 66 valid_name(const char *name) 67 { 68 int i; 69 70 for (i = 0; bootconf_names[i] != NULL; ++i) { 71 if (strcmp(name, bootconf_names[i]) == 0) { 72 return (B_TRUE); 73 } 74 } 75 76 return (B_FALSE); 77 } 78 79 /* 80 * parse_bootconf() parses a wanboot.conf(4) file and, if there are no 81 * errors, creates an nvpair list of the name-value pairs defined therein. 82 * 83 * Lines must be blank or of the form: 84 * [name=value] [# comment] 85 * 86 * Returns: 87 * B_TRUE - success 88 * B_FALSE - error (return code in handle->bc_error_code, line number 89 * on which the error occurred in handle->bc_error_pos) 90 */ 91 static boolean_t 92 parse_bootconf(bc_handle_t *handle, const char *bootconf) 93 { 94 FILE *fp = NULL; 95 nvlist_t *nvl = NULL; 96 char line[BC_MAX_LINE_LENGTH]; 97 98 if ((fp = fopen(bootconf, "r")) == NULL) { 99 handle->bc_error_code = BC_E_ACCESS; 100 goto cleanup; 101 } 102 103 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) { 104 handle->bc_error_code = BC_E_NVLIST; 105 goto cleanup; 106 } 107 108 while (fgets(line, sizeof (line), fp) != NULL) { 109 int i; 110 char *p = line; 111 char *ks, *ke, *vs, *ve; 112 char quote; 113 114 ++(handle->bc_error_pos); 115 116 /* 117 * Strip off the '\n' at the end of the line. 118 */ 119 if ((i = strlen(line)) < 1) { 120 handle->bc_error_code = BC_E_IOERR; 121 goto cleanup; 122 } else if (line[i - 1] != '\n') { 123 handle->bc_error_code = BC_E_TOO_LONG; 124 goto cleanup; 125 } 126 line[i - 1] = '\0'; 127 128 /* 129 * Skip leading whitespace. 130 */ 131 skip_whitespace(p); 132 133 /* 134 * Blank line/comment-only line? 135 */ 136 if (*p == '\0' || *p == '#') { 137 continue; 138 } 139 140 /* 141 * Get start and end pointers to the 'name'. 142 */ 143 ks = p; 144 while (!is_whitespace(*p) && *p != '=') { 145 ++p; 146 } 147 ke = p; 148 149 /* 150 * Must be of the form "name=value"; skip leading and 151 * trailing whitespace. 152 */ 153 skip_whitespace(p); 154 if (*p == '=') { 155 ++p; /* skip '=' */ 156 skip_whitespace(p); 157 } else { 158 handle->bc_error_code = BC_E_SYNTAX; 159 goto cleanup; 160 } 161 162 /* 163 * The 'value' may be quoted. 164 */ 165 if (*p == '"' || *p == '\'') { 166 quote = *p; 167 ++p; /* skip '"' */ 168 } else { 169 quote = '\0'; 170 } 171 172 /* 173 * Get start and end pointers to the 'value' string. 174 * Note that 'value' may be the empty string. 175 */ 176 vs = p; 177 if (quote != '\0' || *p != '#') { 178 while (*p != '\0' && *p != quote) { 179 /* 180 * White space that is not part of a quoted 181 * value signals end of value. 182 */ 183 if (is_whitespace(*p) && quote == '\0') { 184 break; 185 } 186 ++p; 187 } 188 } 189 ve = p; 190 191 /* 192 * If 'value' string was quoted, ensure that there is a 193 * balancing close-quote and skip it. 194 */ 195 if (quote != '\0') { 196 if (*p == quote) { 197 ++p; 198 } else { 199 handle->bc_error_code = BC_E_SYNTAX; 200 goto cleanup; 201 } 202 } 203 204 /* 205 * Verify line is well-formed; the rest of the line should 206 * be blank or comment. 207 */ 208 skip_whitespace(p); 209 if (*p != '\0' && *p != '#') { 210 handle->bc_error_code = BC_E_SYNTAX; 211 goto cleanup; 212 } 213 214 /* 215 * Nul-terminate both the 'name' and the 'value' string. 216 */ 217 *ke = '\0'; 218 *ve = '\0'; 219 220 /* 221 * Check that this is a valid parameter name. 222 */ 223 if (!valid_name(ks)) { 224 handle->bc_error_code = BC_E_UNKNOWN_NAME; 225 goto cleanup; 226 } 227 228 /* 229 * Add the name-value pair to the nvpair list. 230 */ 231 if (nvlist_add_string(nvl, ks, vs) != 0) { 232 handle->bc_error_code = BC_E_NVLIST; 233 goto cleanup; 234 } 235 } 236 237 /* 238 * Verify that we didn't exit the parsing loop because of an 239 * input error. 240 */ 241 if (ferror(fp)) { 242 handle->bc_error_code = BC_E_IOERR; 243 goto cleanup; 244 } 245 246 cleanup: 247 /* 248 * Close the file if open and free the nvlist if an error occurred. 249 */ 250 if (fp != NULL && fclose(fp) != 0) { 251 handle->bc_error_code = BC_E_IOERR; 252 } 253 if (handle->bc_error_code != BC_E_NOERROR) { 254 if (nvl != NULL) { 255 nvlist_free(nvl); 256 } 257 return (B_FALSE); 258 } 259 260 /* 261 * All is well. 262 */ 263 handle->bc_nvl = nvl; 264 265 return (B_TRUE); 266 } 267 268 /* 269 * valid_encryption() validitate the encryption type value 270 * 271 * Returns: 272 * B_TRUE - success 273 * B_FALSE - error (return code in handle->bc_error_code) 274 */ 275 static boolean_t 276 valid_encryption(bc_handle_t *handle, boolean_t *is_encrypted) 277 { 278 nvlist_t *nvl = handle->bc_nvl; 279 char *strval; 280 281 /* 282 * Until proven otherwise, encryption is not enabled. 283 */ 284 *is_encrypted = B_FALSE; 285 286 /* 287 * If encryption_type was specified then it must be either 288 * "3des", "aes" or "". 289 */ 290 if (nvlist_lookup_string(nvl, BC_ENCRYPTION_TYPE, &strval) == 0) { 291 if (strlen(strval) > 0) { 292 if (strcmp(strval, BC_ENCRYPTION_3DES) != 0 && 293 strcmp(strval, BC_ENCRYPTION_AES) != 0) { 294 handle->bc_error_code = BC_E_ENCRYPTION_ILLEGAL; 295 return (B_FALSE); 296 } 297 *is_encrypted = B_TRUE; 298 } 299 } 300 return (B_TRUE); 301 } 302 303 /* 304 * valid_signature() validates the signature type value 305 * 306 * Returns: 307 * B_TRUE - success 308 * B_FALSE - error (return code in handle->bc_error_code) 309 */ 310 static boolean_t 311 valid_signature(bc_handle_t *handle, boolean_t *is_signed) 312 { 313 nvlist_t *nvl = handle->bc_nvl; 314 char *strval; 315 316 /* 317 * Until proven otherwise, signing is not enabled. 318 */ 319 *is_signed = B_FALSE; 320 321 /* 322 * If signature_type was specified then it must be either 323 * "sha1" or "". 324 */ 325 if (nvlist_lookup_string(nvl, BC_SIGNATURE_TYPE, &strval) == 0) { 326 if (strlen(strval) > 0) { 327 if (strcmp(strval, BC_SIGNATURE_SHA1) != 0) { 328 handle->bc_error_code = BC_E_SIGNATURE_ILLEGAL; 329 return (B_FALSE); 330 } 331 *is_signed = B_TRUE; 332 } 333 } 334 335 return (B_TRUE); 336 } 337 338 /* 339 * valid_client_authentication() validates the client authentication value 340 * 341 * Returns: 342 * B_TRUE - success 343 * B_FALSE - error (return code in handle->bc_error_code) 344 */ 345 static boolean_t 346 valid_client_authentication(bc_handle_t *handle, boolean_t *is_authenticated) 347 { 348 nvlist_t *nvl = handle->bc_nvl; 349 char *strval; 350 351 /* 352 * Until proven otherwise, authentication is not enabled. 353 */ 354 *is_authenticated = B_FALSE; 355 356 /* 357 * If client_authentication was specified then it must be either 358 * "yes" or "no". 359 */ 360 if (nvlist_lookup_string(nvl, BC_CLIENT_AUTHENTICATION, &strval) == 0) { 361 if (strcmp(strval, BC_YES) == 0) { 362 *is_authenticated = B_TRUE; 363 } else if (strcmp(strval, BC_NO) != 0) { 364 handle->bc_error_code = BC_E_CLIENT_AUTH_ILLEGAL; 365 return (B_FALSE); 366 } 367 } 368 369 return (B_TRUE); 370 } 371 372 /* 373 * valid_server_authentication() validates the server authentication value 374 * 375 * Returns: 376 * B_TRUE - success 377 * B_FALSE - error (return code in handle->bc_error_code) 378 */ 379 static boolean_t 380 valid_server_authentication(bc_handle_t *handle, boolean_t *is_authenticated) 381 { 382 nvlist_t *nvl = handle->bc_nvl; 383 char *strval; 384 385 /* 386 * Until proven otherwise, authentication is not enabled. 387 */ 388 *is_authenticated = B_FALSE; 389 390 /* 391 * If server_authentication was specified then it must be either 392 * "yes" or"no". 393 */ 394 if (nvlist_lookup_string(nvl, BC_SERVER_AUTHENTICATION, &strval) == 0) { 395 if (strcmp(strval, BC_YES) == 0) { 396 *is_authenticated = B_TRUE; 397 } else if (strcmp(strval, BC_NO) != 0) { 398 handle->bc_error_code = BC_E_SERVER_AUTH_ILLEGAL; 399 return (B_FALSE); 400 } 401 } 402 403 return (B_TRUE); 404 } 405 406 /* 407 * valid_root_server() validates the root server and root file values 408 * 409 * Returns: 410 * B_TRUE - success 411 * B_FALSE - error (return code in handle->bc_error_code) 412 */ 413 static boolean_t 414 valid_root_server(bc_handle_t *handle, boolean_t *is_https) 415 { 416 nvlist_t *nvl = handle->bc_nvl; 417 char *strval; 418 url_t url; 419 420 /* 421 * Until proven otherwise, assume not https. 422 */ 423 *is_https = B_FALSE; 424 425 /* 426 * Check whether a root_server URL was specified, and if so whether 427 * it is a secure URL (of the form https://...). 428 */ 429 if (nvlist_lookup_string(nvl, BC_ROOT_SERVER, &strval) == 0) { 430 if (url_parse(strval, &url) != URL_PARSE_SUCCESS) { 431 handle->bc_error_code = BC_E_ROOT_SERVER_BAD; 432 return (B_FALSE); 433 } 434 *is_https = url.https; 435 436 /* 437 * Ensure that a root_file was also specified. 438 */ 439 if (nvlist_lookup_string(nvl, BC_ROOT_FILE, &strval) != 0 || 440 strlen(strval) == 0) { 441 handle->bc_error_code = BC_E_ROOT_FILE_ABSENT; 442 return (B_FALSE); 443 } 444 } else { 445 handle->bc_error_code = BC_E_ROOT_SERVER_ABSENT; 446 return (B_FALSE); 447 } 448 449 return (B_TRUE); 450 } 451 452 /* 453 * valid_boot_logger() validates the boot_logger value 454 * 455 * Returns: 456 * B_TRUE - success 457 * B_FALSE - error (return code in handle->bc_error_code) 458 */ 459 static boolean_t 460 valid_boot_logger(bc_handle_t *handle, boolean_t *is_https) 461 { 462 nvlist_t *nvl = handle->bc_nvl; 463 char *strval; 464 url_t url; 465 466 /* 467 * Until proven otherwise, assume not https. 468 */ 469 *is_https = B_FALSE; 470 471 /* 472 * If boot_logger was specified, make sure that it is a valid URL. 473 */ 474 if (nvlist_lookup_string(nvl, BC_BOOT_LOGGER, &strval) == 0 && 475 strlen(strval) > 0) { 476 if (url_parse(strval, &url) != URL_PARSE_SUCCESS) { 477 handle->bc_error_code = BC_E_BOOT_LOGGER_BAD; 478 return (B_FALSE); 479 } 480 *is_https = url.https; 481 } 482 483 return (B_TRUE); 484 } 485 486 /* 487 * validate_bootconf() checks the consistency of the nvpair list representation 488 * of a wanboot.conf(4) file as returned by the parse_bootconf() function. 489 * 490 * Returns: 491 * B_TRUE - success 492 * B_FALSE - error (return code in handle->bc_error_code) 493 */ 494 static boolean_t 495 validate_bootconf(bc_handle_t *handle) 496 { 497 boolean_t is_encrypted; 498 boolean_t is_signed; 499 boolean_t client_is_authenticated; 500 boolean_t server_is_authenticated; 501 boolean_t rootserver_is_https; 502 boolean_t bootlogger_is_https; 503 504 /* 505 * Check to make sure option values are valid. 506 */ 507 if (!valid_encryption(handle, &is_encrypted) || 508 !valid_signature(handle, &is_signed) || 509 !valid_client_authentication(handle, &client_is_authenticated) || 510 !valid_server_authentication(handle, &server_is_authenticated) || 511 !valid_root_server(handle, &rootserver_is_https) || 512 !valid_boot_logger(handle, &bootlogger_is_https)) 513 return (B_FALSE); 514 515 /* 516 * Now do consistency checking between bootconf settings. 517 */ 518 if (is_encrypted && !is_signed) { 519 handle->bc_error_code = BC_E_ENCRYPTED_NOT_SIGNED; 520 return (B_FALSE); 521 } 522 if (client_is_authenticated) { 523 if (!(is_encrypted && is_signed)) { 524 handle->bc_error_code = BC_E_CLIENT_AUTH_NOT_ENCRYPTED; 525 return (B_FALSE); 526 } 527 528 if (!server_is_authenticated) { 529 handle->bc_error_code = BC_E_CLIENT_AUTH_NOT_SERVER; 530 return (B_FALSE); 531 } 532 } 533 if (server_is_authenticated) { 534 if (!is_signed) { 535 handle->bc_error_code = BC_E_SERVER_AUTH_NOT_SIGNED; 536 return (B_FALSE); 537 } 538 539 if (!rootserver_is_https) { 540 handle->bc_error_code = BC_E_SERVER_AUTH_NOT_HTTPS; 541 return (B_FALSE); 542 } 543 } else if (rootserver_is_https) { 544 handle->bc_error_code = BC_E_SERVER_AUTH_NOT_HTTP; 545 return (B_FALSE); 546 } else if (bootlogger_is_https) { 547 handle->bc_error_code = BC_E_BOOTLOGGER_AUTH_NOT_HTTP; 548 return (B_FALSE); 549 } 550 551 return (B_TRUE); 552 } 553 554 555 /* 556 * bootconf_end() cleans up once we're done accessing the nvpair list 557 * representation of wanboot.conf(4). 558 */ 559 void 560 bootconf_end(bc_handle_t *handle) 561 { 562 if (handle->bc_nvl != NULL) { 563 nvlist_free(handle->bc_nvl); 564 handle->bc_nvl = NULL; 565 } 566 } 567 568 /* 569 * bootconf_init() must be called to initialize 'handle' before bootconf_get() 570 * can be used to access values from the wanboot.conf(4) file. 571 */ 572 int 573 bootconf_init(bc_handle_t *handle, const char *bootconf) 574 { 575 /* 576 * Initalise the handle's fields to sensible values. 577 */ 578 handle->bc_nvl = NULL; 579 handle->bc_error_code = BC_E_NOERROR; 580 handle->bc_error_pos = 0; 581 582 /* 583 * Provide a default path for the bootconf file if none was given. 584 */ 585 if (bootconf == NULL) { 586 bootconf = NB_WANBOOT_CONF_PATH; 587 } 588 589 /* 590 * Check that we can successfully parse and validate the file. 591 */ 592 if (parse_bootconf(handle, bootconf) && validate_bootconf(handle)) { 593 return (BC_SUCCESS); 594 } 595 596 /* 597 * Parse/validate error; free any allocated resources. 598 */ 599 bootconf_end(handle); 600 601 return (BC_FAILURE); 602 } 603 604 /* 605 * bootconf_get() returns the value of a parameter in the wanboot.conf(4) file. 606 * 607 * Returns: 608 * != NULL - the given value 609 * == NULL - value not found or is empty 610 */ 611 char * 612 bootconf_get(bc_handle_t *handle, const char *name) 613 { 614 char *strval; 615 616 /* 617 * Look up the name in bc_nvl and return its value if found. 618 */ 619 if (handle->bc_nvl != NULL && 620 nvlist_lookup_string(handle->bc_nvl, (char *)name, &strval) == 0) { 621 return (strlen(strval) == 0 ? NULL : strval); 622 } 623 624 return (NULL); 625 } 626