xref: /titanic_50/usr/src/lib/libwanbootutil/common/key_util.c (revision 2a8164df8a5f42c8a00f10c67d7bc84f80ae9c41)
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 <strings.h>
29 #include <stdarg.h>
30 #include <errno.h>
31 #include <libintl.h>
32 #include <sys/wanboot_impl.h>
33 
34 #include "key_xdr.h"
35 #include "key_util.h"
36 
37 /*
38  * Size of 'empty' pkcs12 key file (with no key in it) plus 1
39  * This is the minimum length for our RSA keys, because we
40  * only use RSA keys that are stored in PKCS12 format.
41  */
42 #define	PKCS12_MIN_LEN	76
43 
44 /*
45  *  Program name to be used by wbku_printerr()
46  */
47 static const char *wbku_pname = NULL;
48 
49 /*
50  * Note: must be kept in sync with codes in <key_util.h>
51  */
52 static char *wbku_retmsgs[WBKU_NRET] = {
53 /* 0 WBKU_SUCCESS */		"Success",
54 /* 1 WBKU_INTERNAL_ERR */	"Internal error",
55 /* 2 WBKU_WRITE_ERR */		"Keystore write error",
56 /* 3 WBKU_NOKEY */		"Key does not exist in keystore",
57 /* 4 WBKU_BAD_KEYTYPE */	"Invalid keytype specified"
58 };
59 
60 /*
61  * Initialize library for calls to wbku_printerr().
62  */
63 void
64 wbku_errinit(const char *arg0)
65 {
66 	wbku_pname = strrchr(arg0, '/');
67 
68 	if (wbku_pname == NULL)
69 		wbku_pname = arg0;
70 	else
71 		wbku_pname++;
72 }
73 
74 /*
75  * Print an error message to standard error and optionally
76  * append a system error.
77  */
78 /*PRINTFLIKE1*/
79 void
80 wbku_printerr(const char *format, ...)
81 {
82 	int err = errno;
83 	va_list	ap;
84 
85 	if (wbku_pname != NULL)
86 		(void) fprintf(stderr, "%s: ", wbku_pname);
87 
88 	/*
89 	 * Note that gettext() is used in order to obtain the
90 	 * message from the consumer's domain.
91 	 */
92 	va_start(ap, format);
93 	(void) vfprintf(stderr, gettext(format), ap);
94 	va_end(ap);
95 
96 	if (strchr(format, '\n') == NULL)
97 		(void) fprintf(stderr, ": %s\n", strerror(err));
98 }
99 
100 /*
101  * Return the appropriate message for a given WBKU return code.
102  */
103 const char *
104 wbku_retmsg(wbku_retcode_t retcode)
105 {
106 	if ((retcode < WBKU_SUCCESS) || (retcode >= WBKU_NRET))
107 		return (dgettext(TEXT_DOMAIN, "<unknown code>"));
108 
109 	return (dgettext(TEXT_DOMAIN, wbku_retmsgs[retcode]));
110 }
111 
112 /*
113  * This routine is a simple helper routine that initializes a
114  * wbku_key_attr_t object.
115  */
116 static void
117 wbku_keyattr_init(wbku_key_attr_t *attr, wbku_key_type_t type, uint_t atype,
118     uint_t len, uint_t minlen, uint_t maxlen,
119     char *str, char *oid, boolean_t (*keycheck)(const uint8_t *))
120 {
121 	attr->ka_type = type;
122 	attr->ka_atype = atype;
123 	attr->ka_len = len;
124 	attr->ka_minlen = minlen;
125 	attr->ka_maxlen = maxlen;
126 	attr->ka_str = str;
127 	attr->ka_oid = oid;
128 	attr->ka_keycheck = keycheck;
129 }
130 
131 
132 /*
133  * This routine is used to build a key attribute structure of the type
134  * defined by 'str' and 'flag'. This structure, 'attr', is the common
135  * structure used by the utilities that defines the attributes of a
136  * specific key type.
137  *
138  * Returns:
139  *	WBKU_SUCCESS or WBKU_BAD_KEYTYPE.
140  */
141 wbku_retcode_t
142 wbku_str_to_keyattr(const char *str, wbku_key_attr_t *attr, uint_t flag)
143 {
144 	if (str == NULL)
145 		return (WBKU_BAD_KEYTYPE);
146 
147 	if (flag & WBKU_ENCR_KEY) {
148 		if (strcmp(str, WBKU_KW_3DES) == 0) {
149 			wbku_keyattr_init(attr, WBKU_KEY_3DES,
150 			    WBKU_ENCR_KEY, DES3_KEY_SIZE, DES3_KEY_SIZE,
151 			    DES3_KEY_SIZE, "3DES", WBKU_DES3_OID,
152 			    des3_keycheck);
153 			return (WBKU_SUCCESS);
154 		}
155 		if (strcmp(str, WBKU_KW_AES_128) == 0) {
156 			wbku_keyattr_init(attr, WBKU_KEY_AES_128,
157 			    WBKU_ENCR_KEY, AES_128_KEY_SIZE, AES_128_KEY_SIZE,
158 			    AES_128_KEY_SIZE, "AES", WBKU_AES_128_OID, NULL);
159 			return (WBKU_SUCCESS);
160 		}
161 		if (strcmp(str, WBKU_KW_RSA) == 0) {
162 			wbku_keyattr_init(attr, WBKU_KEY_RSA,
163 			    WBKU_ENCR_KEY, 0, PKCS12_MIN_LEN,
164 			    WBKU_MAX_KEYLEN, "RSA", WBKU_RSA_OID, NULL);
165 			return (WBKU_SUCCESS);
166 		}
167 	}
168 	if (flag & WBKU_HASH_KEY) {
169 		if (strcmp(str, WBKU_KW_HMAC_SHA1) == 0) {
170 			wbku_keyattr_init(attr, WBKU_KEY_HMAC_SHA1,
171 			    WBKU_HASH_KEY, WANBOOT_HMAC_KEY_SIZE,
172 			    WANBOOT_HMAC_KEY_SIZE, WANBOOT_HMAC_KEY_SIZE,
173 			    "HMAC/SHA1", WBKU_HMAC_SHA1_OID, NULL);
174 			return (WBKU_SUCCESS);
175 		}
176 	}
177 	return (WBKU_BAD_KEYTYPE);
178 }
179 
180 /*
181  * This routine is used to search a key file (whose handle, fp, has been
182  * initialized by the caller) for the key of type 'ka'. The search is further
183  * constrained by the 'master' argument which is used to signify that the
184  * key being searched for is the master key.
185  *
186  * This routine may be used for a number of purposes:
187  *  - Check for the existence of key of type foo.
188  *  - Get the value for the key of type foo.
189  *  - Return the file position of the key of type foo.
190  *
191  * To faciliate the uses above, both 'ppos' and 'ekey' will only be
192  * returned if they are not NULL pointers.
193  *
194  * Returns:
195  *	WBKU_SUCCESS, WBKU_INTERNAL_ERR or WBKU_NOKEY.
196  */
197 wbku_retcode_t
198 wbku_find_key(FILE *fp, fpos_t *ppos, wbku_key_attr_t *ka, uint8_t *ekey,
199     boolean_t master)
200 {
201 	fpos_t pos;
202 	XDR xdrs;
203 	wbku_key keyobj;
204 	int keyno;
205 	int ret;
206 
207 	/*
208 	 * Always, start at the beginning.
209 	 */
210 	rewind(fp);
211 
212 	/*
213 	 * Initialize the XDR stream.
214 	 */
215 	xdrs.x_ops = NULL;
216 	xdrstdio_create(&xdrs, fp, XDR_DECODE);
217 	if (xdrs.x_ops == NULL) {
218 		return (WBKU_INTERNAL_ERR);
219 	}
220 
221 	/*
222 	 * The XDR routines may examine the content of the keyobj
223 	 * structure to determine whether or not to provide memory
224 	 * resources. Since XDR does not provide an init routine
225 	 * for XDR generated objects, it seems that the safest thing
226 	 * to do is to bzero() the object as a means of initialization.
227 	 */
228 	bzero(&keyobj, sizeof (keyobj));
229 
230 	/*
231 	 * Read a key and check to see if matches the criteria.
232 	 */
233 	for (keyno = 0; !feof(fp); keyno++) {
234 
235 		/*
236 		 * Returning the file position is conditional.
237 		 */
238 		if (ppos != NULL) {
239 			if (fgetpos(fp, &pos) != 0) {
240 				ret = WBKU_INTERNAL_ERR;
241 				break;
242 			}
243 		}
244 
245 		/*
246 		 * Read the key. Unfortuantely, XDR does not provide
247 		 * the ability to tell an EOF from some other IO error.
248 		 * Therefore, a faliure to read is assumed to be EOF.
249 		 */
250 		if (!xdr_wbku_key(&xdrs, &keyobj)) {
251 			ret = WBKU_NOKEY;
252 			break;
253 		}
254 
255 		/*
256 		 * Check this key against the criteria.
257 		 */
258 		if ((strcmp(keyobj.wk_oid, ka->ka_oid) == 0) &&
259 		    (keyobj.wk_master == master)) {
260 
261 			ka->ka_len = keyobj.wk_key_len;
262 
263 			/*
264 			 * Conditionally return the key value and file
265 			 * position.
266 			 */
267 			if (ekey != NULL) {
268 				(void) memcpy(ekey, keyobj.wk_key_val,
269 				    ka->ka_len);
270 			}
271 			if (ppos != NULL) {
272 				*ppos = pos;
273 			}
274 
275 			xdr_free(xdr_wbku_key, (char *)&keyobj);
276 			ret = WBKU_SUCCESS;
277 			break;
278 		}
279 		xdr_free(xdr_wbku_key, (char *)&keyobj);
280 	}
281 
282 	xdr_destroy(&xdrs);
283 	return (ret);
284 }
285 
286 /*
287  * This routine writes a key object to the key file at the location
288  * specified by the caller.
289  *
290  * Returns:
291  *	WBKU_SUCCESS, WBKU_INTERNAL_ERR or WBKU_WRITE_ERR.
292  */
293 wbku_retcode_t
294 wbku_write_key(FILE *fp, const fpos_t *ppos, const wbku_key_attr_t *ka,
295     uint8_t *rand_key, boolean_t master)
296 {
297 	XDR xdrs;
298 	wbku_key keyobj;
299 
300 	/*
301 	 * Set the file position as specified by the caller.
302 	 */
303 	if (fsetpos(fp, ppos) != 0) {
304 		return (WBKU_INTERNAL_ERR);
305 	}
306 
307 	/*
308 	 * Initialize the XDR stream.
309 	 */
310 	xdrs.x_ops = NULL;
311 	xdrstdio_create(&xdrs, fp, XDR_ENCODE);
312 	if (xdrs.x_ops == NULL) {
313 		return (WBKU_INTERNAL_ERR);
314 	}
315 
316 	/*
317 	 * Build the key object.
318 	 */
319 	keyobj.wk_master = master;
320 	keyobj.wk_oid = ka->ka_oid;
321 	keyobj.wk_key_len = ka->ka_len;
322 	keyobj.wk_key_val = (char *)rand_key;
323 
324 	/*
325 	 * Write it.
326 	 */
327 	if (!xdr_wbku_key(&xdrs, &keyobj)) {
328 		xdr_free(xdr_wbku_key, (char *)&keyobj);
329 		xdr_destroy(&xdrs);
330 		return (WBKU_WRITE_ERR);
331 	}
332 
333 	/*
334 	 * Free the stream and return success.
335 	 */
336 	xdr_destroy(&xdrs);
337 	return (WBKU_SUCCESS);
338 }
339 
340 /*
341  * This routine reads the contents of one keystore file and copies it to
342  * another, omitting the key of the type defined by 'ka'.
343  *
344  * Returns:
345  *	WBKU_SUCCESS, WBKU_INTERNAL_ERR or WBKU_WRITE_ERR.
346  */
347 wbku_retcode_t
348 wbku_delete_key(FILE *from_fp, FILE *to_fp, const wbku_key_attr_t *ka)
349 {
350 	XDR from_xdrs;
351 	XDR to_xdrs;
352 	wbku_key keyobj;
353 	int keyno;
354 	int ret;
355 
356 	/*
357 	 * Always, start at the beginning.
358 	 */
359 	rewind(from_fp);
360 	rewind(to_fp);
361 
362 	/*
363 	 * Initialize the XDR streams.
364 	 */
365 	from_xdrs.x_ops = NULL;
366 	xdrstdio_create(&from_xdrs, from_fp, XDR_DECODE);
367 	if (from_xdrs.x_ops == NULL) {
368 		return (WBKU_INTERNAL_ERR);
369 	}
370 
371 	to_xdrs.x_ops = NULL;
372 	xdrstdio_create(&to_xdrs, to_fp, XDR_ENCODE);
373 	if (to_xdrs.x_ops == NULL) {
374 		xdr_destroy(&from_xdrs);
375 		return (WBKU_INTERNAL_ERR);
376 	}
377 
378 	/*
379 	 * The XDR routines may examine the content of the keyobj
380 	 * structure to determine whether or not to provide memory
381 	 * resources. Since XDR does not provide an init routine
382 	 * for XDR generated objects, it seems that the safest thing
383 	 * to do is to bzero() the object as a means of initialization.
384 	 */
385 	bzero(&keyobj, sizeof (keyobj));
386 
387 	/*
388 	 * Read a key and check to see if matches the criteria.
389 	 */
390 	ret = WBKU_SUCCESS;
391 	for (keyno = 0; !feof(from_fp); keyno++) {
392 
393 		/*
394 		 * Read the key. Unfortuantely, XDR does not provide
395 		 * the ability to tell an EOF from some other IO error.
396 		 * Therefore, a faliure to read is assumed to be EOF.
397 		 */
398 		if (!xdr_wbku_key(&from_xdrs, &keyobj)) {
399 			break;
400 		}
401 
402 		/*
403 		 * If this isn't the key to skip, then write it.
404 		 */
405 		if (strcmp(keyobj.wk_oid, ka->ka_oid) != 0) {
406 			/*
407 			 * Write this to the copy.
408 			 */
409 			if (!xdr_wbku_key(&to_xdrs, &keyobj)) {
410 				xdr_free(xdr_wbku_key, (char *)&keyobj);
411 				ret = WBKU_WRITE_ERR;
412 				break;
413 			}
414 
415 		}
416 
417 		xdr_free(xdr_wbku_key, (char *)&keyobj);
418 	}
419 
420 	xdr_destroy(&from_xdrs);
421 	xdr_destroy(&to_xdrs);
422 
423 	return (ret);
424 }
425