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