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_OK) {
109 		if (handle != NULL) {
110 			handle->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN;
111 			handle->lasterr.errcode = ck_rv;
112 		}
113 		return (KMF_ERR_INTERNAL);
114 	}
115 
116 	if (tmp_count == 0) {
117 		*slot_list = NULL_PTR;
118 		*slot_count = 0;
119 		return (KMF_OK);
120 	}
121 
122 	/* Allocate initial space for the slot list. */
123 	if ((tmp_list = (CK_SLOT_ID_PTR) malloc(tmp_count *
124 	    sizeof (CK_SLOT_ID))) == NULL)
125 		return (KMF_ERR_MEMORY);
126 
127 	/* Then get the slot list itself. */
128 	for (;;) {
129 		ck_rv = C_GetSlotList(1, tmp_list, &tmp_count);
130 		if (ck_rv == CKR_OK) {
131 			*slot_list = tmp_list;
132 			*slot_count = tmp_count;
133 			kmf_rv = KMF_OK;
134 			break;
135 		}
136 
137 		if (ck_rv != CKR_BUFFER_TOO_SMALL) {
138 			free(tmp_list);
139 			if (handle != NULL) {
140 				handle->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN;
141 				handle->lasterr.errcode = ck_rv;
142 			}
143 			kmf_rv = KMF_ERR_INTERNAL;
144 			break;
145 		}
146 
147 		/*
148 		 * If the number of slots grew, try again. This
149 		 * is to be consistent with pktool in ONNV.
150 		 */
151 		if ((tmp2_list = (CK_SLOT_ID_PTR) realloc(tmp_list,
152 		    tmp_count * sizeof (CK_SLOT_ID))) == NULL) {
153 			free(tmp_list);
154 			kmf_rv = KMF_ERR_MEMORY;
155 			break;
156 		}
157 		tmp_list = tmp2_list;
158 	}
159 
160 	return (kmf_rv);
161 }
162 
163 /*
164  * Returns pointer to either null-terminator or next unescaped colon.  The
165  * string to be extracted starts at the beginning and goes until one character
166  * before this pointer.  If NULL is returned, the string itself is NULL.
167  */
168 static char *
169 find_unescaped_colon(char *str)
170 {
171 	char *end;
172 
173 	if (str == NULL)
174 		return (NULL);
175 
176 	while ((end = strchr(str, ':')) != NULL) {
177 		if (end != str && *(end-1) != '\\')
178 			return (end);
179 		str = end + 1;		/* could point to null-terminator */
180 	}
181 	if (end == NULL)
182 		end = strchr(str, '\0');
183 	return (end);
184 }
185 
186 /*
187  * Compresses away any characters escaped with backslash from given string.
188  * The string is altered in-place.  Example, "ab\:\\e" becomes "ab:\e".
189  */
190 static void
191 unescape_str(char *str)
192 {
193 	boolean_t	escaped = B_FALSE;
194 	char		*mark;
195 
196 	if (str == NULL)
197 		return;
198 
199 	for (mark = str; *str != '\0'; str++) {
200 		if (*str != '\\' || escaped == B_TRUE) {
201 			*mark++ = *str;
202 			escaped = B_FALSE;
203 		} else {
204 			escaped = B_TRUE;
205 		}
206 	}
207 	*mark = '\0';
208 }
209 
210 
211 /*
212  * Given a colon-separated token specifier, this functions splits it into
213  * its label, manufacturer ID (if any), and serial number (if any).  Literal
214  * colons within the label/manuf/serial can be escaped with a backslash.
215  * Fields can left blank and trailing colons can be omitted, however leading
216  * colons are required as placeholders.  For example, these are equivalent:
217  *	(a) "lbl", "lbl:", "lbl::"	(b) "lbl:man", "lbl:man:"
218  * but these are not:
219  *	(c) "man", ":man"	(d) "ser", "::ser"
220  * Furthermore, the token label is required always.
221  *
222  * The buffer containing the token specifier is altered by replacing the
223  * colons to null-terminators, and pointers returned are pointers into this
224  * string.  No new memory is allocated.
225  */
226 static int
227 parse_token_spec(char *token_spec, char **token_name, char **manuf_id,
228 	char **serial_no)
229 {
230 	char	*mark;
231 
232 	if (token_spec == NULL || *token_spec == '\0') {
233 		return (-1);
234 	}
235 
236 	*token_name = NULL;
237 	*manuf_id = NULL;
238 	*serial_no = NULL;
239 
240 	/* Token label (required) */
241 	mark = find_unescaped_colon(token_spec);
242 	*token_name = token_spec;
243 	if (*mark != '\0')
244 		*mark++ = '\0';		/* mark points to next field, if any */
245 	unescape_str(*token_name);
246 
247 	if (*(*token_name) == '\0') {	/* token label is required */
248 		return (-1);
249 	}
250 
251 	if (*mark == '\0' || *(mark+1) == '\0')		/* no more fields */
252 		return (0);
253 	token_spec = mark;
254 
255 	/* Manufacturer identifier (optional) */
256 	mark = find_unescaped_colon(token_spec);
257 	*manuf_id = token_spec;
258 	if (*mark != '\0')
259 		*mark++ = '\0';		/* mark points to next field, if any */
260 	unescape_str(*manuf_id);
261 
262 	if (*mark == '\0' || *(mark+1) == '\0')		/* no more fields */
263 		return (0);
264 	token_spec = mark;
265 
266 	/* Serial number (optional) */
267 	mark = find_unescaped_colon(token_spec);
268 	*serial_no = token_spec;
269 	if (*mark != '\0')
270 		*mark++ = '\0';		/* null-terminate, just in case */
271 	unescape_str(*serial_no);
272 
273 	return (0);
274 }
275 
276 /*
277  * Find slots that match a token identifier.  Token labels take the
278  * form of:
279  *	token_name:manufacturer:serial_number
280  * manufacterer and serial number are optional.  If used, the fields
281  * are delimited by the colon ':' character.
282  */
283 KMF_RETURN
284 KMF_PK11TokenLookup(KMF_HANDLE_T handle, char *label, CK_SLOT_ID *slot_id)
285 {
286 	KMF_RETURN	kmf_rv = KMF_OK;
287 	CK_RV		rv;
288 	CK_SLOT_ID_PTR	slot_list = NULL;
289 	CK_TOKEN_INFO	token_info;
290 	CK_ULONG	slot_count = 0;
291 	int		i;
292 	uint_t		len, max_sz;
293 	boolean_t 	metaslot_status_enabled;
294 	boolean_t 	metaslot_migrate_enabled;
295 	char	*metaslot_slot_info;
296 	char	*metaslot_token_info;
297 	char	*tmplabel = NULL;
298 	char	*token_name = NULL;
299 	char	*manuf_id = NULL;
300 	char	*serial_no = NULL;
301 	boolean_t	tok_match = B_FALSE,
302 			man_match = B_FALSE,
303 			ser_match = B_FALSE;
304 
305 	if (slot_id == NULL || label == NULL || !strlen(label))
306 		return (KMF_ERR_BAD_PARAMETER);
307 
308 	if (handle == NULL) {
309 		rv = C_Initialize(NULL);
310 		if ((rv != CKR_OK) &&
311 		    (rv != CKR_CRYPTOKI_ALREADY_INITIALIZED)) {
312 			return (KMF_ERR_UNINITIALIZED);
313 		}
314 	}
315 
316 	/*
317 	 * Parse token specifier into token_name, manuf_id, serial_no.
318 	 * Token_name is required; manuf_id and serial_no are optional.
319 	 */
320 	tmplabel = strdup(label);
321 	if (tmplabel == NULL)
322 		return (KMF_ERR_MEMORY);
323 
324 	if (parse_token_spec(tmplabel, &token_name, &manuf_id,
325 		&serial_no) < 0) {
326 		free(tmplabel);
327 		return (KMF_ERR_BAD_PARAMETER);
328 	}
329 
330 	/* Get a list of all slots with tokens present. */
331 	kmf_rv = kmf_get_token_slots(handle, &slot_list, &slot_count);
332 	if (kmf_rv != KMF_OK) {
333 		free(tmplabel);
334 		return (kmf_rv);
335 	}
336 
337 	/* If there are no such slots, the desired token won't be found. */
338 	if (slot_count == 0) {
339 		free(tmplabel);
340 		return (KMF_ERR_TOKEN_NOT_PRESENT);
341 	}
342 
343 	/* Search the slot list for the token. */
344 	for (i = 0; i < slot_count; i++) {
345 		if (C_GetTokenInfo(slot_list[i], &token_info) != CKR_OK) {
346 			continue;
347 		}
348 
349 		/* See if the token label matches. */
350 		len = strlen(token_name);
351 		max_sz = sizeof (token_info.label);
352 		if (memcmp_pad_max(&(token_info.label), max_sz, token_name,
353 			len, max_sz) == 0)
354 			tok_match = B_TRUE;
355 		/*
356 		 * If manufacturer id was given, see if it actually matches.
357 		 * If no manufacturer id was given, assume match is true.
358 		 */
359 		if (manuf_id) {
360 			len = strlen(manuf_id);
361 			max_sz = sizeof ((char *)(token_info.manufacturerID));
362 			if (memcmp_pad_max(&(token_info.manufacturerID), max_sz,
363 			    manuf_id, len, max_sz) == 0)
364 				man_match = B_TRUE;
365 		} else {
366 			man_match = B_TRUE;
367 		}
368 
369 		/*
370 		 * If serial number was given, see if it actually matches.
371 		 * If no serial number was given, assume match is true.
372 		 */
373 		if (serial_no) {
374 			len = strlen(serial_no);
375 			max_sz = sizeof ((char *)(token_info.serialNumber));
376 			if (memcmp_pad_max(&(token_info.serialNumber), max_sz,
377 			    serial_no, len, max_sz) == 0)
378 				ser_match = B_TRUE;
379 		} else {
380 			ser_match = B_TRUE;
381 		}
382 
383 		if (tok_match && man_match && ser_match)
384 			break;		/* found it! */
385 	}
386 
387 	if (i < slot_count) {
388 		/* found the desired token from the slotlist */
389 		*slot_id = slot_list[i];
390 		free(slot_list);
391 		free(tmplabel);
392 		return (KMF_OK);
393 	}
394 
395 	/*
396 	 * If we didn't find the token from the slotlist, check if this token
397 	 * is the one currently hidden by the metaslot. If that's case,
398 	 * we can just use the metaslot, the slot 0.
399 	 */
400 	kmf_rv = get_metaslot_info(&metaslot_status_enabled,
401 	    &metaslot_migrate_enabled, &metaslot_slot_info,
402 	    &metaslot_token_info);
403 	if (kmf_rv) {
404 		/*
405 		 * Failed to get the metaslot info.  This usually means that
406 		 * metaslot is disabled from the system.
407 		 */
408 		kmf_rv = KMF_ERR_TOKEN_NOT_PRESENT;
409 	} else {
410 		max_sz = strlen(metaslot_token_info);
411 		if (memcmp_pad_max(metaslot_token_info, max_sz, token_name, len,
412 		    max_sz) == 0) {
413 			*slot_id = slot_list[0];
414 		} else {
415 			kmf_rv = KMF_ERR_TOKEN_NOT_PRESENT;
416 		}
417 		free(metaslot_slot_info);
418 		free(metaslot_token_info);
419 	}
420 
421 	free(slot_list);
422 	free(tmplabel);
423 	return (kmf_rv);
424 }
425 
426 KMF_RETURN
427 KMF_SetTokenPin(KMF_HANDLE_T handle, KMF_SETPIN_PARAMS *params,
428 	KMF_CREDENTIAL *newpin)
429 {
430 	KMF_RETURN rv = KMF_OK;
431 	KMF_PLUGIN *plugin;
432 
433 	CLEAR_ERROR(handle, rv);
434 	if (rv != KMF_OK)
435 		return (rv);
436 
437 	if (params == NULL || newpin == NULL)
438 		return (KMF_ERR_BAD_PARAMETER);
439 
440 	/*
441 	 * If setting PKCS#11 token look for the slot.
442 	 */
443 	if (params->kstype == KMF_KEYSTORE_PK11TOKEN) {
444 		rv = KMF_PK11TokenLookup(NULL, params->tokenname,
445 			&params->pkcs11parms.slot);
446 		if (rv != KMF_OK)
447 			return (rv);
448 	}
449 
450 	plugin = FindPlugin(handle, params->kstype);
451 	if (plugin == NULL)
452 		return (KMF_ERR_PLUGIN_NOTFOUND);
453 	if (plugin->funclist->SetTokenPin == NULL)
454 		return (KMF_ERR_FUNCTION_NOT_FOUND);
455 
456 	rv = plugin->funclist->SetTokenPin(handle, params, newpin);
457 
458 	return (rv);
459 }
460 
461 /*
462  *
463  * Name: KMF_SelectToken
464  *
465  * Description:
466  *   This function enables the user of PKCS#11 plugin to select a
467  *   particular PKCS#11 token. Valid token label are required in order to
468  *   successfully complete this function.
469  *   All subsequent KMF APIs, which specify PKCS#11 keystore as
470  *   the backend, will be performed at the selected token.
471  *
472  * Parameters:
473  *   label(input) - pointer to the token label
474  *
475  * Returns:
476  *   A KMF_RETURN value indicating success or specifying a particular
477  *   error condition.
478  *   The value KMF_OK indicates success. All other values represent
479  *   an error condition.
480  *
481  */
482 KMF_RETURN
483 KMF_SelectToken(KMF_HANDLE_T handle, char *label,
484 	int readonly)
485 {
486 	KMF_RETURN kmf_rv = KMF_OK;
487 	CK_RV ck_rv = CKR_OK;
488 	CK_SLOT_ID slot_id;
489 	CK_SESSION_HANDLE hSession;
490 	CK_FLAGS 	openflags;
491 
492 	CLEAR_ERROR(handle, kmf_rv);
493 	if (kmf_rv != KMF_OK)
494 		return (kmf_rv);
495 
496 	if (label == NULL) {
497 		return (KMF_ERR_BAD_PARAMETER);
498 	}
499 
500 	kmf_rv = init_pk11();
501 	if (kmf_rv != KMF_OK) {
502 		return (kmf_rv);
503 	}
504 
505 	/* Only one token can be active per thread */
506 	if (handle->pk11handle != NULL) {
507 		return (KMF_ERR_TOKEN_SELECTED);
508 	}
509 
510 	/* Find the token with matching label */
511 	kmf_rv = KMF_PK11TokenLookup(handle, label, &slot_id);
512 	if (kmf_rv != KMF_OK) {
513 		return (kmf_rv);
514 	}
515 
516 	openflags = CKF_SERIAL_SESSION;
517 	if (!readonly)
518 		openflags |= CKF_RW_SESSION;
519 
520 	/* Open a session then log the user into the token */
521 	ck_rv = C_OpenSession(slot_id, openflags, NULL, NULL, &hSession);
522 	if (ck_rv != CKR_OK) {
523 		handle->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN;
524 		handle->lasterr.errcode = ck_rv;
525 		return (KMF_ERR_INTERNAL);
526 	}
527 
528 	handle->pk11handle = hSession;
529 
530 	return (kmf_rv);
531 }
532 
533 CK_SESSION_HANDLE
534 KMF_GetPK11Handle(KMF_HANDLE_T kmfh)
535 {
536 	return (kmfh->pk11handle);
537 }
538