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 != NULL) {
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