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