xref: /titanic_52/usr/src/cmd/cmd-inet/usr.lib/wanboot/keymgmt/keymgmt.c (revision 0eb822a1c0c2bea495647510b75f77f0e57633eb)
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