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 #include <stdio.h> 29 #include <ctype.h> 30 #include <unistd.h> 31 #include <strings.h> 32 #include <libintl.h> 33 #include <locale.h> 34 #include <limits.h> 35 #include <libgen.h> 36 #include <errno.h> 37 #include <assert.h> 38 #include <wanbootutil.h> 39 #include <sys/sysmacros.h> 40 #include <sys/socket.h> 41 #include <sys/types.h> 42 #include <sys/stat.h> 43 #include <sys/wanboot_impl.h> 44 #include <netinet/in.h> 45 #include <arpa/inet.h> 46 47 /* Return codes */ 48 #define KEYGEN_SUCCESS 0 49 #define KEYGEN_ERROR 1 50 51 /* Defaults */ 52 static char default_net[] = "0.0.0.0"; 53 static char default_cid[] = "00000000000000"; 54 55 /* Suboption. */ 56 #define NET 0 57 #define CID 1 58 #define TYPE 2 59 60 static char *opts[] = { "net", "cid", "type", NULL }; 61 62 /* 63 * This routine is used to parse the suboptions of '-o' option. 64 * 65 * The option should be of the form: 66 * net=<addr>,cid=<cid>,type=<3des|aes|sha1|rsa> 67 * 68 * This routine will pass the values of each of the suboptions back in the 69 * supplied arguments, 'net', 'cid' and 'ka'. 70 * 71 * Returns: 72 * KEYGEN_SUCCESS or KEYGEN_ERROR. 73 */ 74 static int 75 process_option(char *arg, char **net, char **cid, wbku_key_attr_t *ka) 76 { 77 char *value; 78 wbku_retcode_t ret; 79 80 while (*arg != '\0') { 81 switch (getsubopt(&arg, opts, &value)) { 82 case NET: 83 /* 84 * Network number. 85 */ 86 *net = value; 87 break; 88 case CID: 89 /* 90 * Client ID. 91 */ 92 *cid = value; 93 break; 94 case TYPE: 95 /* 96 * Key type. 97 */ 98 ret = wbku_str_to_keyattr(value, ka, WBKU_ANY_KEY); 99 if (ret != WBKU_SUCCESS) { 100 wbku_printerr("%s\n", wbku_retmsg(ret)); 101 return (KEYGEN_ERROR); 102 } 103 break; 104 default: 105 wbku_printerr("%s is not a valid option\n", value); 106 return (KEYGEN_ERROR); 107 } 108 } 109 110 /* 111 * Sanity checks 112 */ 113 if (*net != NULL && **net == '\0') { 114 wbku_printerr("Missing net option value\n"); 115 return (KEYGEN_ERROR); 116 } 117 if (*cid != NULL && **cid == '\0') { 118 wbku_printerr("Missing cid option value\n"); 119 return (KEYGEN_ERROR); 120 } 121 if (*cid != NULL && *net == NULL) { 122 wbku_printerr( 123 "The cid option requires net option specification\n"); 124 return (KEYGEN_ERROR); 125 } 126 if (ka->ka_type == WBKU_KEY_UNKNOWN) { 127 wbku_printerr("Missing key type option value\n"); 128 return (KEYGEN_ERROR); 129 } 130 131 return (KEYGEN_SUCCESS); 132 } 133 134 /* 135 * This routine parses a buffer to determine whether or not it 136 * contains a hexascii string. If the buffer contains any characters 137 * that are not hexascii, then it is not a hexascii string. Since 138 * this function is used to validate a CID value (which is then used 139 * to identify a directory in the filesystem), no evaluation of the 140 * string is performed. That is, hex strings are not padded (e.g. "A" 141 * is not padded to "0A"). 142 * 143 * Returns: 144 * B_TRUE or B_FALSE 145 */ 146 static boolean_t 147 isxstring(const char *buf) 148 { 149 if ((strlen(buf) % 2) != 0) { 150 return (B_FALSE); 151 } 152 153 for (; *buf != '\0'; ++buf) { 154 if (!isxdigit(*buf)) { 155 return (B_FALSE); 156 } 157 } 158 return (B_TRUE); 159 } 160 161 /* 162 * This routine uses the 'net' and the 'cid' to generate the client's 163 * keystore filename and, if requested, creates the directory path to 164 * the file if any of the directories do not exist. If directory path 165 * creation is not requested and any of the directories do not exist, 166 * then an error is returned. 167 * 168 * Returns: 169 * KEYGEN_SUCCESS or KEYGEN_ERROR. 170 */ 171 static int 172 create_client_filename(char *filename, size_t len, const char *net, 173 const char *cid, boolean_t create) 174 { 175 struct in_addr addr; 176 size_t size; 177 178 if (net == NULL) { 179 size = snprintf(filename, len, "%s", CLIENT_KEY_DIR); 180 } else if (inet_pton(AF_INET, net, &addr) != 1) { 181 wbku_printerr("%s is not a valid network address\n", net); 182 return (KEYGEN_ERROR); 183 } else if (cid == NULL) { 184 size = snprintf(filename, len, "%s/%s", CLIENT_KEY_DIR, net); 185 } else if (!isxstring(cid)) { 186 wbku_printerr( 187 "%s must be an even number of hexadecimal characters\n", 188 cid); 189 return (KEYGEN_ERROR); 190 } else { 191 size = snprintf(filename, len, "%s/%s/%s", CLIENT_KEY_DIR, 192 net, cid); 193 } 194 195 /* 196 * Shouldn't be a problem, but make sure buffer was big enough. 197 */ 198 if (size >= len) { 199 wbku_printerr("Keystore path too long\n"); 200 return (KEYGEN_ERROR); 201 } 202 203 /* 204 * If directory creation is allowed, then try to create it. 205 * If the directory already exists, then march on. 206 */ 207 if (create) { 208 if (mkdirp(filename, S_IRWXU) == -1 && errno != EEXIST) { 209 wbku_printerr("Cannot create client keystore"); 210 return (KEYGEN_ERROR); 211 } 212 } 213 214 /* 215 * Append the filename. 216 */ 217 if (strlcat(filename, "/keystore", len) >= len) { 218 wbku_printerr("Keystore path too long\n"); 219 return (KEYGEN_ERROR); 220 } 221 222 return (KEYGEN_SUCCESS); 223 } 224 225 /* 226 * This routine generates a random key of the type defined by 'ka'. 227 * The key value is returned in 'rand_key' and the buffer pointed to 228 * by 'rand_key' is assumed to be of the correct size. 229 * 230 * Note: 231 * If 'ka' has a non-NULL keycheck value, then the routine will 232 * generate randon keys until a non-weak key is generated. 233 * 234 * Returns: 235 * KEYGEN_SUCCESS or KEYGEN_ERROR. 236 */ 237 static int 238 gen_key(const wbku_key_attr_t *ka, uint8_t *rand_key) 239 { 240 /* 241 * Generate key, until non-weak key generated. 242 */ 243 for (;;) { 244 if (wbio_nread_rand(rand_key, ka->ka_len) != 0) { 245 wbku_printerr("Cannot generate random number"); 246 return (KEYGEN_ERROR); 247 } 248 249 if (ka->ka_keycheck == NULL || ka->ka_keycheck(rand_key)) { 250 return (KEYGEN_SUCCESS); 251 } 252 } 253 } 254 255 /* 256 * This routine generates a random master key of the type (currently only 257 * HMAC SHA1 supported) defined by 'ka' and stores it in the master key 258 * file. 259 * 260 * Returns: 261 * KEYGEN_SUCCESS or KEYGEN_ERROR. 262 */ 263 static int 264 master_gen_key(wbku_key_attr_t *ka) 265 { 266 uint8_t mas_key[WANBOOT_HMAC_KEY_SIZE]; 267 int fd; 268 FILE *fp = NULL; 269 fpos_t pos; 270 wbku_retcode_t ret; 271 boolean_t exists = B_FALSE; 272 273 /* 274 * If the file already exists (possibly via keymgmt), then open 275 * the file for update. Otherwise create it and open it for 276 * for writing. 277 */ 278 fd = open(MASTER_KEY_FILE, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR); 279 if (fd < 0) { 280 if (errno == EEXIST) { 281 fp = fopen(MASTER_KEY_FILE, "r+"); 282 exists = B_TRUE; 283 } 284 } else { 285 if ((fp = fdopen(fd, "w")) == NULL) { 286 (void) close(fd); 287 } 288 } 289 290 if (fp == NULL) { 291 wbku_printerr("Cannot open master keystore", MASTER_KEY_FILE); 292 return (KEYGEN_ERROR); 293 } 294 295 /* 296 * If the file already exists, then see if a master key already 297 * exists. We will not overwrite it if it does. 298 */ 299 ret = WBKU_NOKEY; 300 if (exists) { 301 ret = wbku_find_key(fp, NULL, ka, NULL, B_TRUE); 302 if (ret != WBKU_NOKEY) { 303 if (ret == WBKU_SUCCESS) { 304 wbku_printerr("The master %s key already " 305 "exists and will not be overwritten\n", 306 ka->ka_str); 307 } else { 308 wbku_printerr("%s\n", wbku_retmsg(ret)); 309 } 310 (void) fclose(fp); 311 return (KEYGEN_ERROR); 312 } 313 } 314 315 /* 316 * If wbku_find_key() did not find the key position for us 317 * (expected behavior), then we should set position to 318 * the end of the file. 319 */ 320 if (ret == WBKU_NOKEY && 321 (fseek(fp, 0, SEEK_END) != 0 || fgetpos(fp, &pos) != 0)) { 322 wbku_printerr("Internal error"); 323 (void) fclose(fp); 324 return (KEYGEN_ERROR); 325 } 326 327 /* 328 * Generate a key and write it. 329 */ 330 if (gen_key(ka, mas_key) != KEYGEN_SUCCESS) { 331 (void) fclose(fp); 332 return (KEYGEN_ERROR); 333 } 334 335 ret = wbku_write_key(fp, &pos, ka, mas_key, B_TRUE); 336 (void) fclose(fp); 337 if (ret != WBKU_SUCCESS) { 338 wbku_printerr("%s\n", wbku_retmsg(ret)); 339 return (KEYGEN_ERROR); 340 } 341 342 (void) printf(gettext("The master %s key has been generated\n"), 343 ka->ka_str); 344 return (KEYGEN_SUCCESS); 345 } 346 347 /* 348 * This routine generates a random client key of the type 349 * defined by 'ka' and stores it in the client keystore. 350 * file. 351 * 352 * Returns: 353 * KEYGEN_SUCCESS or KEYGEN_ERROR. 354 */ 355 static int 356 client_gen_key(const char *filename, wbku_key_attr_t *ka, const char *net, 357 const char *cid) 358 { 359 int fd; 360 FILE *cli_fp = NULL; 361 FILE *mas_fp; 362 fpos_t pos; 363 uint8_t cli_key[WANBOOT_MAXKEYLEN]; 364 uint8_t mas_key[WANBOOT_HMAC_KEY_SIZE]; 365 SHA1_CTX ctx; 366 char cid_buf[PATH_MAX]; 367 boolean_t exists = B_FALSE; 368 wbku_retcode_t ret; 369 370 /* 371 * If the file already exists (possibly via keymgmt), then open 372 * the file for update. Otherwise create it and open it for 373 * for writing. 374 */ 375 fd = open(filename, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR); 376 if (fd < 0) { 377 if (errno == EEXIST) { 378 cli_fp = fopen(filename, "r+"); 379 exists = B_TRUE; 380 } 381 } else { 382 if ((cli_fp = fdopen(fd, "w")) == NULL) { 383 (void) close(fd); 384 } 385 } 386 387 if (cli_fp == NULL) { 388 wbku_printerr("Cannot open client keystore"); 389 return (KEYGEN_ERROR); 390 } 391 392 /* 393 * Generate the key. Encryption keys can be generated by simply 394 * calling gen_key(). An HMAC SHA1 key will be generated by 395 * hashing the master key. 396 */ 397 switch (ka->ka_type) { 398 case WBKU_KEY_3DES: 399 case WBKU_KEY_AES_128: 400 if (gen_key(ka, cli_key) != KEYGEN_SUCCESS) { 401 (void) fclose(cli_fp); 402 return (KEYGEN_ERROR); 403 } 404 break; 405 case WBKU_KEY_HMAC_SHA1: 406 /* 407 * Follow RFC 3118 Appendix A's algorithm to generate 408 * the HMAC/SHA1 client key. 409 */ 410 411 /* 412 * Open the master keystore for reading only. 413 */ 414 if ((mas_fp = fopen(MASTER_KEY_FILE, "r")) == NULL) { 415 wbku_printerr("Cannot open master keystore"); 416 (void) fclose(cli_fp); 417 return (KEYGEN_ERROR); 418 } 419 420 /* 421 * Find the master key. 422 */ 423 ret = wbku_find_key(mas_fp, NULL, ka, mas_key, B_TRUE); 424 if (ret != WBKU_SUCCESS) { 425 if (ret == WBKU_NOKEY) { 426 wbku_printerr("Cannot create a client key " 427 "without first creating a master key\n"); 428 } else { 429 wbku_printerr("%s\n", wbku_retmsg(ret)); 430 } 431 (void) fclose(mas_fp); 432 (void) fclose(cli_fp); 433 return (KEYGEN_ERROR); 434 } 435 (void) fclose(mas_fp); 436 437 /* 438 * Now generate the client's unique ID buffer. 439 */ 440 if (strlcpy(cid_buf, net, PATH_MAX) >= PATH_MAX || 441 strlcat(cid_buf, cid, PATH_MAX) >= PATH_MAX) { 442 wbku_printerr("Unique id for client is too big\n"); 443 (void) fclose(cli_fp); 444 return (KEYGEN_ERROR); 445 } 446 447 /* 448 * Hash the buffer to create the client key. 449 */ 450 HMACInit(&ctx, mas_key, WANBOOT_HMAC_KEY_SIZE); 451 HMACUpdate(&ctx, (uint8_t *)cid_buf, strlen(cid_buf)); 452 HMACFinal(&ctx, mas_key, WANBOOT_HMAC_KEY_SIZE, cli_key); 453 454 break; 455 case WBKU_KEY_RSA: 456 wbku_printerr("Cannot generate RSA key using keygen\n"); 457 (void) fclose(cli_fp); 458 return (KEYGEN_ERROR); 459 default: 460 wbku_printerr("Internal error\n"); 461 (void) fclose(cli_fp); 462 return (KEYGEN_ERROR); 463 } 464 465 /* 466 * Look to see if a client key of this type exists and if 467 * it does note its position in the file. 468 */ 469 ret = WBKU_NOKEY; 470 if (exists) { 471 ret = wbku_find_key(cli_fp, &pos, ka, NULL, B_FALSE); 472 if (ret != WBKU_SUCCESS && ret != WBKU_NOKEY) { 473 wbku_printerr("%s\n", wbku_retmsg(ret)); 474 (void) fclose(cli_fp); 475 return (KEYGEN_ERROR); 476 } 477 } 478 479 /* 480 * If wbku_find_key() did not find the key position for us, 481 * then we should set position to the end of the file. 482 */ 483 if (ret == WBKU_NOKEY && 484 (fseek(cli_fp, 0, SEEK_END) != 0 || fgetpos(cli_fp, &pos) != 0)) { 485 wbku_printerr("Internal error"); 486 (void) fclose(cli_fp); 487 return (KEYGEN_ERROR); 488 } 489 490 /* 491 * Write the key. 492 */ 493 ret = wbku_write_key(cli_fp, &pos, ka, cli_key, B_FALSE); 494 if (ret != WBKU_SUCCESS) { 495 wbku_printerr("%s\n", wbku_retmsg(ret)); 496 (void) fclose(cli_fp); 497 return (KEYGEN_ERROR); 498 } 499 (void) fclose(cli_fp); 500 501 (void) printf(gettext("A new client %s key has been generated\n"), 502 ka->ka_str); 503 504 return (KEYGEN_SUCCESS); 505 } 506 507 /* 508 * This routine is used to print a hexascii version of a key. 509 * The hexascii version of the key will be twice the length 510 * of 'datalen'. 511 */ 512 static void 513 keydump(const char *key, int keylen) 514 { 515 uint16_t *p16; 516 517 assert(IS_P2ALIGNED(key, sizeof (uint16_t))); 518 /*LINTED aligned*/ 519 for (p16 = (uint16_t *)key; keylen > 0; keylen -= 2) { 520 (void) printf("%04x", htons(*p16++)); 521 } 522 (void) printf("\n"); 523 } 524 525 /* 526 * This routine is used to print a key of the type 527 * described by 'ka'. If 'master' is true, then the 528 * key to display is the master key. Otherwise, it's a 529 * client key. 530 * 531 * Returns: 532 * KEYGEN_SUCCESS or KEYGEN_ERROR. 533 */ 534 static int 535 display_key(const char *filename, wbku_key_attr_t *ka, boolean_t master) 536 { 537 uint8_t key[WANBOOT_MAXKEYLEN]; 538 FILE *fp; 539 wbku_retcode_t ret; 540 541 /* 542 * Open the keystore for reading only. 543 */ 544 if ((fp = fopen(filename, "r")) == NULL) { 545 wbku_printerr("Cannot open keystore"); 546 return (KEYGEN_ERROR); 547 } 548 549 /* 550 * Find the key. 551 */ 552 ret = wbku_find_key(fp, NULL, ka, key, master); 553 if (ret != WBKU_SUCCESS) { 554 if (ret == WBKU_NOKEY) { 555 wbku_printerr("The %s %s key does not exist\n", 556 (master ? "master" : "client"), ka->ka_str); 557 } else { 558 wbku_printerr("%s\n", wbku_retmsg(ret)); 559 } 560 (void) fclose(fp); 561 return (KEYGEN_ERROR); 562 } 563 (void) fclose(fp); 564 565 /* 566 * Dump the key in hex. 567 */ 568 keydump((char *)key, ka->ka_len); 569 570 return (KEYGEN_SUCCESS); 571 } 572 573 /* 574 * Prints usage(). 575 */ 576 static void 577 usage(const char *cmd) 578 { 579 (void) fprintf(stderr, gettext("Usage: %s [-m | -c " 580 "-o net=<addr>,cid=<cid>,type=<%s|%s|%s>]\n" 581 " %s -d [-m | -c -o net=<addr>,cid=<cid>," 582 "type=<%s|%s|%s|%s>]\n"), 583 cmd, WBKU_KW_3DES, WBKU_KW_AES_128, WBKU_KW_HMAC_SHA1, 584 cmd, WBKU_KW_3DES, WBKU_KW_AES_128, WBKU_KW_HMAC_SHA1, WBKU_KW_RSA); 585 } 586 587 /* 588 * This program is used to generate and display WAN boot encryption and 589 * hash keys. The paths to the keystores are predetermined. That is, the 590 * master keystore (used to store a master HMAC SHA1 key) will always 591 * reside in the default location, MASTER_KEY_FILE. The client keystores 592 * will always reside in default locations that are computed using their 593 * network number and cid values. 594 * 595 * Note: 596 * The master keystore can store client keys too. This program 597 * cannot be used to insert client keys into the master keystore. 598 * However, it must not corrupt any client keystore inserted into 599 * the file by other means (keymgmt). 600 * 601 * We do not do any file locking scheme. This means that if two 602 * keygen commands are run concurrently, results can be disastrous. 603 * 604 * Returns: 605 * KEYGEN_SUCCESS or KEYGEN_ERROR. 606 */ 607 int 608 main(int argc, char **argv) 609 { 610 char filename[PATH_MAX]; 611 char *filenamep; 612 int c; 613 boolean_t is_client = B_FALSE; 614 boolean_t is_master = B_FALSE; 615 boolean_t display = B_FALSE; 616 char *net = NULL; 617 char *cid = NULL; 618 wbku_key_attr_t ka; 619 wbku_retcode_t ret; 620 621 /* 622 * Do the necessary magic for localization support. 623 */ 624 (void) setlocale(LC_ALL, ""); 625 #if !defined(TEXT_DOMAIN) 626 #define TEXT_DOMAIN "SYS_TEST" 627 #endif 628 (void) textdomain(TEXT_DOMAIN); 629 630 /* 631 * Initialize program name for use by wbku_printerr(). 632 */ 633 wbku_errinit(argv[0]); 634 635 /* 636 * At the very least, we'll need one arg. 637 */ 638 if (argc < 2) { 639 usage(argv[0]); 640 return (KEYGEN_ERROR); 641 } 642 643 /* 644 * Parse the options. 645 */ 646 ka.ka_type = WBKU_KEY_UNKNOWN; 647 while ((c = getopt(argc, argv, "dcmo:")) != EOF) { 648 switch (c) { 649 case 'd': 650 /* 651 * Display a key. 652 */ 653 display = B_TRUE; 654 break; 655 case 'o': 656 /* 657 * Suboptions. 658 */ 659 if (process_option(optarg, &net, &cid, &ka) != 0) { 660 usage(argv[0]); 661 return (KEYGEN_ERROR); 662 } 663 break; 664 case 'c': 665 is_client = B_TRUE; 666 break; 667 case 'm': 668 is_master = B_TRUE; 669 break; 670 default: 671 usage(argv[0]); 672 return (KEYGEN_ERROR); 673 } 674 } 675 676 /* 677 * Must be operating on a master or client key and if 678 * it's a client key, then type must have been given. 679 */ 680 if ((is_client == is_master) || 681 (is_client && ka.ka_type == WBKU_KEY_UNKNOWN)) { 682 usage(argv[0]); 683 return (KEYGEN_ERROR); 684 } 685 686 /* 687 * If operating on the master key, then it is an HMAC SHA1 688 * key. Build the correct 'ka'. If we're working on a client 689 * key, the 'ka' was already built as part of option parsing. 690 */ 691 if (is_master) { 692 ret = wbku_str_to_keyattr(WBKU_KW_HMAC_SHA1, &ka, 693 WBKU_HASH_KEY); 694 if (ret != WBKU_SUCCESS) { 695 wbku_printerr("Internal error\n"); 696 return (KEYGEN_ERROR); 697 } 698 filenamep = MASTER_KEY_FILE; 699 } else { 700 /* 701 * Build the path to the client keystore. 702 */ 703 if (create_client_filename(filename, sizeof (filename), net, 704 cid, !display) != KEYGEN_SUCCESS) { 705 return (KEYGEN_ERROR); 706 } 707 filenamep = filename; 708 } 709 710 /* 711 * If display chosen, go do it. 712 */ 713 if (display) { 714 return (display_key(filenamep, &ka, is_master)); 715 } 716 717 /* 718 * Can't generate RSA key here. 719 */ 720 if (ka.ka_type == WBKU_KEY_RSA) { 721 wbku_printerr("keygen cannot create RSA key\n"); 722 return (KEYGEN_ERROR); 723 } 724 725 /* 726 * If generating a master key, go do it. 727 */ 728 if (is_master) { 729 return (master_gen_key(&ka)); 730 } 731 732 /* 733 * Must be generating a client key, go do it. 734 */ 735 if (net == NULL) { 736 net = default_net; 737 } 738 if (cid == NULL) { 739 cid = default_cid; 740 } 741 if (client_gen_key(filename, &ka, net, cid) != KEYGEN_SUCCESS) { 742 return (KEYGEN_ERROR); 743 } 744 745 return (KEYGEN_SUCCESS); 746 } 747