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 <alloca.h> 30 #include <unistd.h> 31 #include <strings.h> 32 #include <stdlib.h> 33 #include <libintl.h> 34 #include <locale.h> 35 #include <limits.h> 36 #include <libgen.h> 37 #include <errno.h> 38 #include <ctype.h> 39 #include <wanbootutil.h> 40 #include <sys/sysmacros.h> 41 #include <sys/socket.h> 42 #include <sys/types.h> 43 #include <sys/stat.h> 44 #include <sys/wanboot_impl.h> 45 #include <netinet/in.h> 46 #include <arpa/inet.h> 47 48 /* Return codes */ 49 #define KEYMGMT_SUCCESS 0 50 #define KEYMGMT_ERROR 1 51 52 /* Suboption. */ 53 #define TYPE 0 54 55 static char *opts[] = { "type", NULL }; 56 57 /* 58 * This routine is used to parse the suboptions of '-o' option. 59 * 60 * The option should be of the form: type=<3des|aes|sha1|rsa> 61 * 62 * This routine will pass the value of the suboption back in the 63 * supplied arguments, 'ka'. 64 * 65 * Returns: 66 * KEYMGMT_SUCCESS or KEYMGMT_ERROR. 67 */ 68 static int 69 process_option(char *arg, wbku_key_attr_t *ka) 70 { 71 char *value; 72 wbku_retcode_t ret; 73 74 while (*arg != '\0') { 75 switch (getsubopt(&arg, opts, &value)) { 76 case TYPE: 77 /* 78 * Key type. 79 */ 80 ret = wbku_str_to_keyattr(value, ka, WBKU_ANY_KEY); 81 if (ret != WBKU_SUCCESS) { 82 wbku_printerr("%s\n", wbku_retmsg(ret)); 83 return (KEYMGMT_ERROR); 84 } 85 break; 86 default: 87 wbku_printerr("%s is not a valid option\n", value); 88 return (KEYMGMT_ERROR); 89 } 90 } 91 92 /* 93 * Success. 94 */ 95 return (KEYMGMT_SUCCESS); 96 } 97 98 /* 99 * This routine extracts a key of type 'ka' from the keystore named 100 * 'keystore_name' and writes it the the file identified by 'name'. 101 * 102 * Returns: 103 * KEYMGMT_SUCCESS or KEYMGMT_ERROR. 104 */ 105 static int 106 process_extract(const char *keystore_name, const char *name, 107 wbku_key_attr_t *ka) 108 { 109 size_t i; 110 uint8_t ex_key[WANBOOT_MAXKEYLEN]; 111 FILE *keystore_fp; 112 FILE *fp; 113 wbku_retcode_t ret; 114 115 /* 116 * Open the keystore for reading. 117 */ 118 if ((keystore_fp = fopen(keystore_name, "r")) == NULL) { 119 wbku_printerr("Cannot open %s", keystore_name); 120 return (KEYMGMT_ERROR); 121 } 122 123 /* 124 * Find the client key. 125 */ 126 ret = wbku_find_key(keystore_fp, NULL, ka, ex_key, B_FALSE); 127 if (ret != WBKU_SUCCESS) { 128 if (ret == WBKU_NOKEY) { 129 wbku_printerr("The client %s key does not exist\n", 130 ka->ka_str); 131 } else { 132 wbku_printerr("%s\n", wbku_retmsg(ret)); 133 } 134 (void) fclose(keystore_fp); 135 return (KEYMGMT_ERROR); 136 } 137 (void) fclose(keystore_fp); 138 139 /* 140 * Open the output file. 141 */ 142 if ((fp = fopen(name, "w")) == NULL) { 143 wbku_printerr("Cannot open %s", name); 144 (void) fclose(keystore_fp); 145 return (KEYMGMT_ERROR); 146 } 147 148 /* 149 * Dump the key to the output file. 150 */ 151 i = fwrite(ex_key, sizeof (uint8_t), ka->ka_len, fp); 152 if (i != ka->ka_len) { 153 wbku_printerr("Error writing to %s", name); 154 (void) fclose(fp); 155 return (KEYMGMT_ERROR); 156 } 157 (void) fclose(fp); 158 159 /* 160 * Success. 161 */ 162 return (KEYMGMT_SUCCESS); 163 } 164 165 /* 166 * There is a key which needs to be removed from the keystore. Given basic 167 * information about the key to be deleted, go through the keystore and 168 * remove it. The steps are: 169 * 1) create a temp file in the same directory as the keystore. 170 * 2) copy the existing keystore to the temp file, omitting the key being 171 * removed. 172 * 3) shuffle files. Close the keystore and move it aside. Close the 173 * temp file and move in to the keystore. 174 * 175 * Returns: 176 * B_TRUE on success 177 * B_FALSE on error 178 */ 179 static boolean_t 180 compress_keystore(const char *keystore_name, FILE *fp, 181 const wbku_key_attr_t *ka) 182 { 183 char *tmp_path; 184 FILE *tmp_fp; 185 int tmp_fd; 186 int len; 187 wbku_retcode_t ret; 188 189 /* 190 * Allocate storage for the temporary path from the stack. 191 */ 192 len = strlen(keystore_name) + sizeof (".XXXXXX"); 193 tmp_path = alloca(len); 194 (void) snprintf(tmp_path, len, "%s.XXXXXX", keystore_name); 195 196 /* 197 * Make the temp working file where a new store will be created. 198 */ 199 if ((tmp_fd = mkstemp(tmp_path)) == -1) { 200 wbku_printerr("Error creating %s\n", tmp_path); 201 return (B_FALSE); 202 } 203 204 /* 205 * Need to reference this file as a stream. 206 */ 207 if ((tmp_fp = fdopen(tmp_fd, "w")) == NULL) { 208 wbku_printerr("Error opening %s", tmp_path); 209 (void) close(tmp_fd); 210 (void) unlink(tmp_path); 211 return (B_FALSE); 212 } 213 214 /* 215 * Copy the existing keystore to the temp one, omitting the 216 * key being deleted. 217 */ 218 ret = wbku_delete_key(fp, tmp_fp, ka); 219 (void) fclose(tmp_fp); 220 if (ret != WBKU_SUCCESS) { 221 wbku_printerr("%s\n", wbku_retmsg(ret)); 222 (void) unlink(tmp_path); 223 return (B_FALSE); 224 } 225 226 /* 227 * Shuffle files. 228 */ 229 if (rename(tmp_path, keystore_name) == -1) { 230 wbku_printerr("Error moving new keystore file from %s to %s", 231 tmp_path, keystore_name); 232 (void) unlink(tmp_path); 233 return (B_FALSE); 234 } 235 236 return (B_TRUE); 237 } 238 239 /* 240 * This routine reads a key of type 'ka' from the file identified 'name' and 241 * inserts it into the keystore named 'keystore_name'. 242 * 243 * Returns: 244 * KEYMGMT_SUCCESS or KEYMGMT_ERROR. 245 */ 246 static int 247 process_insert(const char *keystore_name, const char *name, 248 wbku_key_attr_t *ka) 249 { 250 int fd; 251 FILE *keystore_fp = NULL; 252 FILE *fp; 253 fpos_t pos; 254 uint8_t rd_key[WANBOOT_MAXKEYLEN]; 255 int inlen; 256 boolean_t newfile = B_TRUE; 257 wbku_retcode_t ret; 258 259 /* 260 * If the file already exists, then open the file for update. 261 * Otherwise, create it and open it for writing. 262 */ 263 fd = open(keystore_name, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR); 264 if (fd < 0) { 265 if (errno == EEXIST) { 266 keystore_fp = fopen(keystore_name, "r+"); 267 newfile = B_FALSE; 268 } 269 } else { 270 if ((keystore_fp = fdopen(fd, "w")) == NULL) { 271 (void) close(fd); 272 } 273 } 274 275 if (keystore_fp == NULL) { 276 wbku_printerr("Cannot open %s", keystore_name); 277 return (KEYMGMT_ERROR); 278 } 279 280 /* 281 * Open the input file. 282 */ 283 fp = fopen(name, "r"); 284 if (fp == NULL) { 285 wbku_printerr("Cannot open %s", name); 286 (void) fclose(keystore_fp); 287 return (KEYMGMT_ERROR); 288 } 289 290 /* 291 * Read the key from the file. 292 */ 293 inlen = fread(rd_key, sizeof (uint8_t), ka->ka_maxlen, fp); 294 if (inlen == 0 && ferror(fp) != 0) { 295 wbku_printerr("Error reading %s", name); 296 (void) fclose(fp); 297 (void) fclose(keystore_fp); 298 return (KEYMGMT_ERROR); 299 } 300 (void) fclose(fp); 301 302 if ((inlen < ka->ka_minlen) || (inlen > ka->ka_maxlen)) { 303 wbku_printerr("Key length is not valid\n"); 304 (void) fclose(keystore_fp); 305 return (KEYMGMT_ERROR); 306 } 307 308 /* 309 * If the keystore exists, search for a key of the type 310 * being inserted. If found, note its file position. 311 */ 312 ret = WBKU_NOKEY; 313 if (!newfile) { 314 ret = wbku_find_key(keystore_fp, &pos, ka, NULL, B_FALSE); 315 if (ret != WBKU_SUCCESS && ret != WBKU_NOKEY) { 316 wbku_printerr("%s\n", wbku_retmsg(ret)); 317 (void) fclose(keystore_fp); 318 return (KEYMGMT_ERROR); 319 } 320 321 /* 322 * Unfortuantely, RSA keys have variable lengths. If 323 * the one being inserted is a different length than 324 * than the one that already exists in the file, then 325 * the key must be removed from the keystore and then 326 * readded. 327 */ 328 if (ret == WBKU_SUCCESS && inlen != ka->ka_len) { 329 if (!compress_keystore(keystore_name, 330 keystore_fp, ka)) { 331 wbku_printerr("Insertion required compression" 332 " of keystore, but compression failed\n" 333 "Key was not inserted\n"); 334 (void) fclose(keystore_fp); 335 return (KEYMGMT_ERROR); 336 } 337 338 /* 339 * The original keystore is history. Close the 340 * stream and open a stream to the new keystore. 341 */ 342 (void) fclose(keystore_fp); 343 keystore_fp = fopen(keystore_name, "r+"); 344 if (keystore_fp == NULL) { 345 wbku_printerr("Cannot open %s", keystore_name); 346 return (KEYMGMT_ERROR); 347 } 348 349 /* Force new key to end of file */ 350 ret = WBKU_NOKEY; 351 } 352 } 353 ka->ka_len = inlen; 354 355 /* 356 * If wbku_find_key() did not find the key position for us, 357 * then we should set position to the end of the file. 358 */ 359 if (ret == WBKU_NOKEY && (fseek(keystore_fp, 0, SEEK_END) != 0 || 360 fgetpos(keystore_fp, &pos) != 0)) { 361 wbku_printerr("Internal error"); 362 (void) fclose(keystore_fp); 363 return (KEYMGMT_ERROR); 364 } 365 366 /* 367 * Write the key to the keystore. 368 */ 369 ret = wbku_write_key(keystore_fp, &pos, ka, rd_key, B_FALSE); 370 (void) fclose(keystore_fp); 371 if (ret != WBKU_SUCCESS) { 372 wbku_printerr("%s\n", wbku_retmsg(ret)); 373 return (KEYMGMT_ERROR); 374 } 375 376 (void) printf(gettext("The client's %s key has been set\n"), 377 ka->ka_str); 378 /* 379 * Success. 380 */ 381 return (KEYMGMT_SUCCESS); 382 } 383 384 /* 385 * Prints usage(). 386 */ 387 static void 388 usage(const char *cmd) 389 { 390 (void) fprintf(stderr, gettext("Usage: %s" 391 " -i -k <key_file> -s <keystore> -o type=<%s|%s|%s|%s>\n" 392 " %s -x -f <out_file> -s <keystore> -o" 393 " type=<%s|%s|%s|%s>\n"), 394 cmd, WBKU_KW_3DES, WBKU_KW_AES_128, WBKU_KW_HMAC_SHA1, WBKU_KW_RSA, 395 cmd, WBKU_KW_3DES, WBKU_KW_AES_128, WBKU_KW_HMAC_SHA1, WBKU_KW_RSA); 396 } 397 398 /* 399 * This program is used to insert and extract WAN boot encryption and 400 * hash keys into and from keystores. The paths to the keystores are 401 * provided by the user as are the input and output files. 402 * 403 * Note: 404 * This program assumes all keys being inserted or extracted 405 * are client keys. There is no way for a user to insert or 406 * extract a master key using this program. 407 * 408 * We do not do any file locking scheme. This means that if two 409 * keymgmt commands are run concurrently, results can be disastrous. 410 * 411 * Returns: 412 * KEYMGMT_SUCCESS or KEYMGMT_ERROR. 413 */ 414 int 415 main(int argc, char **argv) 416 { 417 int c; 418 boolean_t is_insert = B_FALSE; 419 boolean_t is_extract = B_FALSE; 420 char *keystore_name = NULL; 421 char *filename = NULL; 422 wbku_key_attr_t ka; 423 int ret; 424 425 /* 426 * Do the necessary magic for localization support. 427 */ 428 (void) setlocale(LC_ALL, ""); 429 #if !defined(TEXT_DOMAIN) 430 #define TEXT_DOMAIN "SYS_TEST" 431 #endif 432 (void) textdomain(TEXT_DOMAIN); 433 434 /* 435 * Initialize program name for use by wbku_printerr(). 436 */ 437 wbku_errinit(argv[0]); 438 439 /* 440 * At the very least, we'll need one arg. 441 */ 442 if (argc < 2) { 443 usage(argv[0]); 444 return (KEYMGMT_ERROR); 445 } 446 447 /* 448 * Parse the options. 449 */ 450 ka.ka_type = WBKU_KEY_UNKNOWN; 451 while ((c = getopt(argc, argv, "ixf:k:s:o:")) != EOF) { 452 switch (c) { 453 case 'i': 454 is_insert = B_TRUE; 455 break; 456 case 'x': 457 is_extract = B_TRUE; 458 break; 459 case 'o': 460 /* 461 * Suboptions. 462 */ 463 if (process_option(optarg, &ka) != KEYMGMT_SUCCESS) { 464 usage(argv[0]); 465 return (KEYMGMT_ERROR); 466 } 467 break; 468 case 's': 469 /* 470 * Keystore path. 471 */ 472 keystore_name = optarg; 473 break; 474 case 'f': 475 /* 476 * Input file. 477 */ 478 if (is_insert || filename != NULL) { 479 usage(argv[0]); 480 return (KEYMGMT_ERROR); 481 } 482 filename = optarg; 483 break; 484 case 'k': 485 /* 486 * Input file. 487 */ 488 if (is_extract || filename != NULL) { 489 usage(argv[0]); 490 return (KEYMGMT_ERROR); 491 } 492 filename = optarg; 493 break; 494 default: 495 usage(argv[0]); 496 return (KEYMGMT_ERROR); 497 } 498 } 499 500 /* 501 * Must be inserting or extracting a key and we must have a 502 * key type, keystore filename and an input or output filename. 503 */ 504 if ((is_insert == is_extract) || keystore_name == NULL || 505 filename == NULL || ka.ka_type == WBKU_KEY_UNKNOWN) { 506 usage(argv[0]); 507 return (KEYMGMT_ERROR); 508 } 509 510 /* 511 * Insert or extract the key. 512 */ 513 if (is_insert) { 514 ret = process_insert(keystore_name, filename, &ka); 515 } else { 516 ret = process_extract(keystore_name, filename, &ka); 517 } 518 519 return (ret); 520 } 521