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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  *
21  *
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <security/cryptoki.h>
33 #include <kmfapi.h>
34 #include <kmfapiP.h>
35 #include <cryptoutil.h>
36 
37 /*
38  * memcmp_pad_max() is a specialized version of memcmp() which
39  * compares two pieces of data up to a maximum length.  If the
40  * the two data match up the maximum length, they are considered
41  * matching.  Trailing blanks do not cause the match to fail if
42  * one of the data is shorted.
43  *
44  * Examples of matches:
45  *	"one"           |
46  *	"one      "     |
47  *	                ^maximum length
48  *
49  *	"Number One     |  X"	(X is beyond maximum length)
50  *	"Number One   " |
51  *	                ^maximum length
52  *
53  * Examples of mismatches:
54  *	" one"
55  *	"one"
56  *
57  *	"Number One    X|"
58  *	"Number One     |"
59  *	                ^maximum length
60  */
61 static int
62 memcmp_pad_max(void *d1, uint_t d1_len, void *d2, uint_t d2_len, uint_t max_sz)
63 {
64 	uint_t		len, extra_len;
65 	char		*marker;
66 
67 	/* No point in comparing anything beyond max_sz */
68 	if (d1_len > max_sz)
69 		d1_len = max_sz;
70 	if (d2_len > max_sz)
71 		d2_len = max_sz;
72 
73 	/* Find shorter of the two data. */
74 	if (d1_len <= d2_len) {
75 		len = d1_len;
76 		extra_len = d2_len;
77 		marker = d2;
78 	} else {	/* d1_len > d2_len */
79 		len = d2_len;
80 		extra_len = d1_len;
81 		marker = d1;
82 	}
83 
84 	/* Have a match in the shortest length of data? */
85 	if (memcmp(d1, d2, len) != 0)
86 		/* CONSTCOND */
87 		return (1);
88 
89 	/* If the rest of longer data is nulls or blanks, call it a match. */
90 	while (len < extra_len)
91 		if (!isspace(marker[len++]))
92 			/* CONSTCOND */
93 			return (1);
94 	return (0);
95 }
96 
97 static KMF_RETURN
98 kmf_get_token_slots(KMF_HANDLE *handle, CK_SLOT_ID_PTR *slot_list,
99     CK_ULONG *slot_count)
100 {
101 
102 	KMF_RETURN	kmf_rv = KMF_OK;
103 	CK_RV		ck_rv = CKR_OK;
104 	CK_ULONG	tmp_count = 0;
105 	CK_SLOT_ID_PTR	tmp_list = NULL_PTR, tmp2_list = NULL_PTR;
106 
107 	ck_rv = C_GetSlotList(1, NULL_PTR, &tmp_count);
108 	if (ck_rv == CKR_CRYPTOKI_NOT_INITIALIZED) {
109 		ck_rv = C_Initialize(NULL);
110 		if ((ck_rv != CKR_OK) &&
111 		    (ck_rv != CKR_CRYPTOKI_ALREADY_INITIALIZED))
112 			return (KMF_ERR_UNINITIALIZED);
113 		if (ck_rv == CKR_CRYPTOKI_ALREADY_INITIALIZED)
114 			ck_rv = CKR_OK;
115 
116 		ck_rv = C_GetSlotList(1, NULL_PTR, &tmp_count);
117 	}
118 	if (ck_rv != CKR_OK) {
119 		if (handle != NULL) {
120 			handle->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN;
121 			handle->lasterr.errcode = ck_rv;
122 		}
123 		return (KMF_ERR_INTERNAL);
124 	}
125 
126 	if (tmp_count == 0) {
127 		*slot_list = NULL_PTR;
128 		*slot_count = 0;
129 		return (KMF_OK);
130 	}
131 
132 	/* Allocate initial space for the slot list. */
133 	if ((tmp_list = (CK_SLOT_ID_PTR) malloc(tmp_count *
134 	    sizeof (CK_SLOT_ID))) == NULL)
135 		return (KMF_ERR_MEMORY);
136 
137 	/* Then get the slot list itself. */
138 	for (;;) {
139 		ck_rv = C_GetSlotList(1, tmp_list, &tmp_count);
140 		if (ck_rv == CKR_OK) {
141 			*slot_list = tmp_list;
142 			*slot_count = tmp_count;
143 			kmf_rv = KMF_OK;
144 			break;
145 		}
146 
147 		if (ck_rv != CKR_BUFFER_TOO_SMALL) {
148 			free(tmp_list);
149 			if (handle != NULL) {
150 				handle->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN;
151 				handle->lasterr.errcode = ck_rv;
152 			}
153 			kmf_rv = KMF_ERR_INTERNAL;
154 			break;
155 		}
156 
157 		/*
158 		 * If the number of slots grew, try again. This
159 		 * is to be consistent with pktool in ONNV.
160 		 */
161 		if ((tmp2_list = (CK_SLOT_ID_PTR) realloc(tmp_list,
162 		    tmp_count * sizeof (CK_SLOT_ID))) == NULL) {
163 			free(tmp_list);
164 			kmf_rv = KMF_ERR_MEMORY;
165 			break;
166 		}
167 		tmp_list = tmp2_list;
168 	}
169 
170 	return (kmf_rv);
171 }
172 
173 /*
174  * Returns pointer to either null-terminator or next unescaped colon.  The
175  * string to be extracted starts at the beginning and goes until one character
176  * before this pointer.  If NULL is returned, the string itself is NULL.
177  */
178 static char *
179 find_unescaped_colon(char *str)
180 {
181 	char *end;
182 
183 	if (str == NULL)
184 		return (NULL);
185 
186 	while ((end = strchr(str, ':')) != NULL) {
187 		if (end != str && *(end-1) != '\\')
188 			return (end);
189 		str = end + 1;		/* could point to null-terminator */
190 	}
191 	if (end == NULL)
192 		end = strchr(str, '\0');
193 	return (end);
194 }
195 
196 /*
197  * Compresses away any characters escaped with backslash from given string.
198  * The string is altered in-place.  Example, "ab\:\\e" becomes "ab:\e".
199  */
200 static void
201 unescape_str(char *str)
202 {
203 	boolean_t	escaped = B_FALSE;
204 	char		*mark;
205 
206 	if (str == NULL)
207 		return;
208 
209 	for (mark = str; *str != '\0'; str++) {
210 		if (*str != '\\' || escaped == B_TRUE) {
211 			*mark++ = *str;
212 			escaped = B_FALSE;
213 		} else {
214 			escaped = B_TRUE;
215 		}
216 	}
217 	*mark = '\0';
218 }
219 
220 
221 /*
222  * Given a colon-separated token specifier, this functions splits it into
223  * its label, manufacturer ID (if any), and serial number (if any).  Literal
224  * colons within the label/manuf/serial can be escaped with a backslash.
225  * Fields can left blank and trailing colons can be omitted, however leading
226  * colons are required as placeholders.  For example, these are equivalent:
227  *	(a) "lbl", "lbl:", "lbl::"	(b) "lbl:man", "lbl:man:"
228  * but these are not:
229  *	(c) "man", ":man"	(d) "ser", "::ser"
230  * Furthermore, the token label is required always.
231  *
232  * The buffer containing the token specifier is altered by replacing the
233  * colons to null-terminators, and pointers returned are pointers into this
234  * string.  No new memory is allocated.
235  */
236 static int
237 parse_token_spec(char *token_spec, char **token_name, char **manuf_id,
238 	char **serial_no)
239 {
240 	char	*mark;
241 
242 	if (token_spec == NULL || *token_spec == '\0') {
243 		return (-1);
244 	}
245 
246 	*token_name = NULL;
247 	*manuf_id = NULL;
248 	*serial_no = NULL;
249 
250 	/* Token label (required) */
251 	mark = find_unescaped_colon(token_spec);
252 	*token_name = token_spec;
253 	if (*mark != '\0')
254 		*mark++ = '\0';		/* mark points to next field, if any */
255 	unescape_str(*token_name);
256 
257 	if (*(*token_name) == '\0') {	/* token label is required */
258 		return (-1);
259 	}
260 
261 	if (*mark == '\0' || *(mark+1) == '\0')		/* no more fields */
262 		return (0);
263 	token_spec = mark;
264 
265 	/* Manufacturer identifier (optional) */
266 	mark = find_unescaped_colon(token_spec);
267 	*manuf_id = token_spec;
268 	if (*mark != '\0')
269 		*mark++ = '\0';		/* mark points to next field, if any */
270 	unescape_str(*manuf_id);
271 
272 	if (*mark == '\0' || *(mark+1) == '\0')		/* no more fields */
273 		return (0);
274 	token_spec = mark;
275 
276 	/* Serial number (optional) */
277 	mark = find_unescaped_colon(token_spec);
278 	*serial_no = token_spec;
279 	if (*mark != '\0')
280 		*mark++ = '\0';		/* null-terminate, just in case */
281 	unescape_str(*serial_no);
282 
283 	return (0);
284 }
285 
286 /*
287  * Find slots that match a token identifier.  Token labels take the
288  * form of:
289  *	token_name:manufacturer:serial_number
290  * manufacterer and serial number are optional.  If used, the fields
291  * are delimited by the colon ':' character.
292  */
293 KMF_RETURN
294 kmf_pk11_token_lookup(KMF_HANDLE_T handle, char *label, CK_SLOT_ID *slot_id)
295 {
296 	KMF_RETURN	kmf_rv = KMF_OK;
297 	CK_RV		rv;
298 	CK_SLOT_ID_PTR	slot_list = NULL;
299 	CK_TOKEN_INFO	token_info;
300 	CK_ULONG	slot_count = 0;
301 	int		i;
302 	uint_t		len, max_sz;
303 	boolean_t 	metaslot_status_enabled;
304 	boolean_t 	metaslot_migrate_enabled;
305 	char	*metaslot_slot_info;
306 	char	*metaslot_token_info;
307 	char	*tmplabel = NULL;
308 	char	*token_name = NULL;
309 	char	*manuf_id = NULL;
310 	char	*serial_no = NULL;
311 	boolean_t	tok_match = B_FALSE;
312 	boolean_t	man_match = B_FALSE;
313 	boolean_t	ser_match = B_FALSE;
314 
315 	if (slot_id == NULL || label == NULL || !strlen(label))
316 		return (KMF_ERR_BAD_PARAMETER);
317 
318 	if (handle == NULL) {
319 		rv = C_Initialize(NULL);
320 		if ((rv != CKR_OK) &&
321 		    (rv != CKR_CRYPTOKI_ALREADY_INITIALIZED)) {
322 			return (KMF_ERR_UNINITIALIZED);
323 		}
324 	}
325 
326 	/*
327 	 * Parse token specifier into token_name, manuf_id, serial_no.
328 	 * Token_name is required; manuf_id and serial_no are optional.
329 	 */
330 	tmplabel = strdup(label);
331 	if (tmplabel == NULL)
332 		return (KMF_ERR_MEMORY);
333 
334 	if (parse_token_spec(tmplabel, &token_name, &manuf_id,
335 	    &serial_no) < 0) {
336 		free(tmplabel);
337 		return (KMF_ERR_BAD_PARAMETER);
338 	}
339 
340 	/* Get a list of all slots with tokens present. */
341 	kmf_rv = kmf_get_token_slots(handle, &slot_list, &slot_count);
342 	if (kmf_rv != KMF_OK) {
343 		free(tmplabel);
344 		return (kmf_rv);
345 	}
346 
347 	/* If there are no such slots, the desired token won't be found. */
348 	if (slot_count == 0) {
349 		free(tmplabel);
350 		return (KMF_ERR_TOKEN_NOT_PRESENT);
351 	}
352 
353 	/* Search the slot list for the token. */
354 	for (i = 0; i < slot_count; i++) {
355 		if (C_GetTokenInfo(slot_list[i], &token_info) != CKR_OK) {
356 			continue;
357 		}
358 
359 		/* See if the token label matches. */
360 		len = strlen(token_name);
361 		max_sz = sizeof (token_info.label);
362 		if (memcmp_pad_max(&(token_info.label), max_sz, token_name,
363 		    len, max_sz) == 0)
364 			tok_match = B_TRUE;
365 		/*
366 		 * If manufacturer id was given, see if it actually matches.
367 		 * If no manufacturer id was given, assume match is true.
368 		 */
369 		if (manuf_id) {
370 			len = strlen(manuf_id);
371 			max_sz = sizeof ((char *)(token_info.manufacturerID));
372 			if (memcmp_pad_max(&(token_info.manufacturerID), max_sz,
373 			    manuf_id, len, max_sz) == 0)
374 				man_match = B_TRUE;
375 		} else {
376 			man_match = B_TRUE;
377 		}
378 
379 		/*
380 		 * If serial number was given, see if it actually matches.
381 		 * If no serial number was given, assume match is true.
382 		 */
383 		if (serial_no) {
384 			len = strlen(serial_no);
385 			max_sz = sizeof ((char *)(token_info.serialNumber));
386 			if (memcmp_pad_max(&(token_info.serialNumber), max_sz,
387 			    serial_no, len, max_sz) == 0)
388 				ser_match = B_TRUE;
389 		} else {
390 			ser_match = B_TRUE;
391 		}
392 
393 		if (tok_match && man_match && ser_match)
394 			break;		/* found it! */
395 	}
396 
397 	if (i < slot_count) {
398 		/* found the desired token from the slotlist */
399 		*slot_id = slot_list[i];
400 		free(slot_list);
401 		free(tmplabel);
402 		return (KMF_OK);
403 	}
404 
405 	/*
406 	 * If we didn't find the token from the slotlist, check if this token
407 	 * is the one currently hidden by the metaslot. If that's case,
408 	 * we can just use the metaslot, the slot 0.
409 	 */
410 	kmf_rv = get_metaslot_info(&metaslot_status_enabled,
411 	    &metaslot_migrate_enabled, &metaslot_slot_info,
412 	    &metaslot_token_info);
413 	if (kmf_rv) {
414 		/*
415 		 * Failed to get the metaslot info.  This usually means that
416 		 * metaslot is disabled from the system.
417 		 */
418 		kmf_rv = KMF_ERR_TOKEN_NOT_PRESENT;
419 	} else {
420 		max_sz = strlen(metaslot_token_info);
421 		if (memcmp_pad_max(metaslot_token_info, max_sz, token_name, len,
422 		    max_sz) == 0) {
423 			*slot_id = slot_list[0];
424 		} else {
425 			kmf_rv = KMF_ERR_TOKEN_NOT_PRESENT;
426 		}
427 		free(metaslot_slot_info);
428 		free(metaslot_token_info);
429 	}
430 
431 	free(slot_list);
432 	free(tmplabel);
433 	return (kmf_rv);
434 }
435 
436 KMF_RETURN
437 kmf_set_token_pin(KMF_HANDLE_T handle,
438 	int num_attr,
439 	KMF_ATTRIBUTE *attrlist)
440 {
441 	KMF_RETURN ret = KMF_OK;
442 	KMF_PLUGIN *plugin;
443 	KMF_ATTRIBUTE_TESTER required_attrs[] = {
444 		{KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)},
445 		{KMF_CREDENTIAL_ATTR, FALSE, sizeof (KMF_CREDENTIAL),
446 			sizeof (KMF_CREDENTIAL)},
447 		{KMF_NEWPIN_ATTR, FALSE, sizeof (KMF_CREDENTIAL),
448 			sizeof (KMF_CREDENTIAL)},
449 	};
450 
451 	int num_req_attrs = sizeof (required_attrs) /
452 	    sizeof (KMF_ATTRIBUTE_TESTER);
453 	uint32_t len;
454 	KMF_KEYSTORE_TYPE kstype;
455 
456 	if (handle == NULL)
457 		return (KMF_ERR_BAD_PARAMETER);
458 
459 	CLEAR_ERROR(handle, ret);
460 	if (ret != KMF_OK)
461 		return (ret);
462 
463 	ret = test_attributes(num_req_attrs, required_attrs,
464 	    0, NULL, num_attr, attrlist);
465 	if (ret != KMF_OK)
466 		return (ret);
467 
468 	len = sizeof (kstype);
469 	ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, num_attr,
470 	    &kstype, &len);
471 	if (ret != KMF_OK)
472 		return (ret);
473 
474 	plugin = FindPlugin(handle, kstype);
475 	if (plugin != NULL) {
476 		if (plugin->funclist->SetTokenPin != NULL)
477 			return (plugin->funclist->SetTokenPin(handle, num_attr,
478 			    attrlist));
479 		else
480 			return (KMF_ERR_FUNCTION_NOT_FOUND);
481 	}
482 	return (KMF_ERR_PLUGIN_NOTFOUND);
483 }
484 
485 /*
486  * Name: kmf_select_token
487  *
488  * Description:
489  *   This function enables the user of PKCS#11 plugin to select a
490  *   particular PKCS#11 token. Valid token label are required in order to
491  *   successfully complete this function.
492  *   All subsequent KMF APIs, which specify PKCS#11 keystore as
493  *   the backend, will be performed at the selected token.
494  *
495  * Parameters:
496  *   label(input) - pointer to the token label
497  *
498  * Returns:
499  *   A KMF_RETURN value indicating success or specifying a particular
500  *   error condition.
501  *   The value KMF_OK indicates success. All other values represent
502  *   an error condition.
503  */
504 KMF_RETURN
505 kmf_select_token(KMF_HANDLE_T handle, char *label, int readonly)
506 {
507 	KMF_RETURN kmf_rv = KMF_OK;
508 	CK_RV ck_rv = CKR_OK;
509 	CK_SLOT_ID slot_id;
510 	CK_SESSION_HANDLE hSession;
511 	CK_FLAGS 	openflags;
512 
513 	CLEAR_ERROR(handle, kmf_rv);
514 	if (kmf_rv != KMF_OK)
515 		return (kmf_rv);
516 
517 	if (label == NULL) {
518 		return (KMF_ERR_BAD_PARAMETER);
519 	}
520 
521 	kmf_rv = init_pk11();
522 	if (kmf_rv != KMF_OK) {
523 		return (kmf_rv);
524 	}
525 
526 	/* Only one token can be active per thread */
527 	if (handle->pk11handle != NULL) {
528 		return (KMF_ERR_TOKEN_SELECTED);
529 	}
530 
531 	/* Find the token with matching label */
532 	kmf_rv = kmf_pk11_token_lookup(handle, label, &slot_id);
533 	if (kmf_rv != KMF_OK) {
534 		return (kmf_rv);
535 	}
536 
537 	openflags = CKF_SERIAL_SESSION;
538 	if (!readonly)
539 		openflags |= CKF_RW_SESSION;
540 
541 	/* Open a session then log the user into the token */
542 	ck_rv = C_OpenSession(slot_id, openflags, NULL, NULL, &hSession);
543 	if (ck_rv != CKR_OK) {
544 		handle->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN;
545 		handle->lasterr.errcode = ck_rv;
546 		return (KMF_ERR_INTERNAL);
547 	}
548 
549 	handle->pk11handle = hSession;
550 
551 	return (kmf_rv);
552 }
553 
554 CK_SESSION_HANDLE
555 kmf_get_pk11_handle(KMF_HANDLE_T kmfh)
556 {
557 	return (kmfh->pk11handle);
558 }
559