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
process_option(char * arg,char ** net,char ** cid,wbku_key_attr_t * ka)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
isxstring(const char * buf)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
create_client_filename(char * filename,size_t len,const char * net,const char * cid,boolean_t create)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
gen_key(const wbku_key_attr_t * ka,uint8_t * rand_key)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
master_gen_key(wbku_key_attr_t * ka)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
client_gen_key(const char * filename,wbku_key_attr_t * ka,const char * net,const char * cid)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
keydump(const char * key,int keylen)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
display_key(const char * filename,wbku_key_attr_t * ka,boolean_t master)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
usage(const char * cmd)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
main(int argc,char ** argv)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