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 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * Functions for dealing with provider sessions
28 */
29
30 #include <string.h>
31 #include <cryptoutil.h>
32 #include "metaGlobal.h"
33 #include "pkcs11Session.h"
34 #include "pkcs11Global.h"
35
36
37 /*
38 * This is just a **WILD** guess for the maximum idle sessions to
39 * keep for each slot. This number should probably be adjusted
40 * when there's more data from actual application use
41 */
42 #define MAX_IDLE_SESSIONS 100
43
44 /*
45 * The following 5 variables are initialized at the time metaslot
46 * is initialized. They are not modified after they are initialized
47 *
48 * During initialization time, they are protected by the "initmutex"
49 * defined in metaGeneral.c
50 */
51 slot_data_t *slots;
52 CK_SLOT_ID metaslot_keystore_slotid;
53 static CK_ULONG num_slots;
54 static CK_ULONG objtok_slotnum;
55 static CK_ULONG softtoken_slotnum;
56 static boolean_t write_protected;
57
58 /* protects the "metaslotLoggedIn" variable */
59 static pthread_mutex_t metaslotLoggedIn_mutex = PTHREAD_MUTEX_INITIALIZER;
60 static boolean_t metaslotLoggedIn;
61
62 /*
63 * meta_slotManager_initialize
64 *
65 * Called from C_Initialize. Allocates and initializes the storage needed
66 * by the slot manager.
67 */
68 CK_RV
meta_slotManager_initialize()69 meta_slotManager_initialize() {
70 CK_ULONG slot_count = 0;
71 CK_RV rv;
72 CK_SLOT_ID i;
73
74 /* Initialize the static variables */
75 write_protected = B_FALSE;
76 metaslotLoggedIn = B_FALSE;
77
78 /*
79 * Count the number of slots in the framework.
80 * We start at ((slottable->st_first) + 1) instead of
81 * slottable->st_first because when we are here, metaslot is
82 * enabled, and st_first is always metaslot, which doesn't
83 * need to be counted.
84 */
85 for (i = (slottable->st_first) + 1; i <= slottable->st_last; i++) {
86 slot_count++;
87 }
88
89 /*
90 * This shouldn't happen, because there should at least
91 * be 1 other slot besides metaslot.
92 */
93 if (slot_count < 1) {
94 rv = CKR_FUNCTION_FAILED;
95 goto clean_exit;
96 }
97
98 slots = calloc(slot_count, sizeof (slot_data_t));
99 if (slots == NULL) {
100 rv = CKR_HOST_MEMORY;
101 goto clean_exit;
102 }
103
104 /*
105 * Store the slot IDs. Adjust for the fact that the first slot is
106 * actually us (metaslot).
107 */
108 for (i = 0; i < slot_count; i++) {
109 slots[i].fw_st_id = i + 1;
110 (void) pthread_rwlock_init(
111 &(slots[i].tokenobject_list_lock), NULL);
112 }
113 num_slots = slot_count;
114
115 return (CKR_OK);
116
117 clean_exit:
118 if (slots) {
119 free(slots);
120 slots = NULL;
121 }
122
123 num_slots = 0;
124
125 return (rv);
126 }
127
128
129 /*
130 * meta_slotManager_finalize
131 *
132 * Called from C_Finalize. Deallocates any storage held by the slot manager.
133 */
134 void
meta_slotManager_finalize()135 meta_slotManager_finalize() {
136 CK_ULONG slot;
137
138 /* If no slots to free, return */
139 if (slots == NULL)
140 return;
141 /*
142 * No need to lock pool, we assume all meta sessions are closed.
143 *
144 * Close all sessions in the idle and persist list.
145 * The active list is empty. It doesn't need to be checked.
146 */
147
148 for (slot = 0; slot < num_slots; slot++) {
149 slot_session_t *session, *next_session;
150
151 /*
152 * The slotobjects associated with the session should have
153 * been closed when the metaobjects were closed. Thus, no
154 * need to do anything here.
155 */
156
157 session = slots[slot].session_pool.idle_list_head;
158 while (session) {
159 next_session = session->next;
160 (void) FUNCLIST(session->fw_st_id)->C_CloseSession(
161 session->hSession);
162 (void) pthread_rwlock_destroy(
163 &session->object_list_lock);
164 free(session);
165 session = next_session;
166 }
167
168 session = slots[slot].session_pool.persist_list_head;
169 while (session) {
170 next_session = session->next;
171 (void) FUNCLIST(session->fw_st_id)->C_CloseSession(
172 session->hSession);
173 (void) pthread_rwlock_destroy(
174 &session->object_list_lock);
175 free(session);
176 session = next_session;
177 }
178
179 (void) pthread_rwlock_destroy(
180 &slots[slot].tokenobject_list_lock);
181 }
182
183 free(slots);
184 slots = NULL;
185 num_slots = 0;
186 }
187
188
189 /*
190 * meta_slotManager_find_object_token()
191 *
192 * Called from meta_Initialize. Searches for the "object token," which is used
193 * for storing token objects and loging into.
194 *
195 * We do the search using the following algorithm.
196 *
197 * If either ${METASLOT_OBJECTSTORE_SLOT} or ${METASLOT_OBJECTSTORE_TOKEN}
198 * environment variable is defined, the value of the defined variable(s)
199 * will be used for the match. All token and slot values defined system-wide
200 * will be ignored.
201 *
202 * If neither variables above are defined, the system-wide values defined
203 * in pkcs11.conf are used.
204 *
205 * If neither environment variables or system-wide values are defined,
206 * or if none of the existing slots/tokens match the defined
207 * values, the first slot after metaslot will be used as the default.
208 *
209 */
210 void
meta_slotManager_find_object_token()211 meta_slotManager_find_object_token() {
212 CK_ULONG slot;
213 boolean_t found = B_FALSE;
214 CK_RV rv;
215 unsigned int num_match_needed = 0;
216 CK_SLOT_INFO slotinfo;
217 CK_TOKEN_INFO tokeninfo;
218
219 if (metaslot_config.keystore_token_specified) {
220 num_match_needed++;
221 }
222
223 if (metaslot_config.keystore_slot_specified) {
224 num_match_needed++;
225 }
226
227 if (num_match_needed == 0) {
228 goto skip_search;
229 }
230
231 for (slot = 0; slot < num_slots; slot++) {
232 unsigned int num_matched = 0;
233 boolean_t have_tokeninfo = B_FALSE;
234 CK_SLOT_ID true_id, fw_st_id;
235
236 fw_st_id = slots[slot].fw_st_id;
237 true_id = TRUEID(fw_st_id);
238
239 (void) memset(&slotinfo, 0, sizeof (CK_SLOT_INFO));
240 rv = FUNCLIST(fw_st_id)->C_GetSlotInfo(true_id,
241 &slotinfo);
242 if (rv != CKR_OK)
243 continue;
244
245 if (strncmp((char *)SOFT_SLOT_DESCRIPTION,
246 (char *)slotinfo.slotDescription,
247 SLOT_DESCRIPTION_SIZE) == 0) {
248 softtoken_slotnum = slot;
249 }
250
251 if (metaslot_config.keystore_slot_specified) {
252
253 unsigned char *slot;
254 size_t slot_str_len;
255
256 rv = FUNCLIST(fw_st_id)->C_GetSlotInfo(true_id,
257 &slotinfo);
258 if (rv != CKR_OK)
259 continue;
260
261 /*
262 * pad slot description from user/system configuration
263 * with spaces
264 */
265 slot = metaslot_config.keystore_slot;
266 slot_str_len = strlen((char *)slot);
267 (void) memset(slot + slot_str_len, ' ',
268 SLOT_DESCRIPTION_SIZE - slot_str_len);
269
270 /*
271 * The PKCS#11 strings are not null-terminated, so,
272 * we just compare SLOT_DESCRIPTION_SIZE bytes
273 */
274 if (strncmp((char *)slot,
275 (char *)slotinfo.slotDescription,
276 SLOT_DESCRIPTION_SIZE) == 0) {
277 num_matched++;
278 }
279 }
280
281 if (metaslot_config.keystore_token_specified) {
282 unsigned char *token;
283 size_t token_str_len;
284
285 rv = FUNCLIST(fw_st_id)->C_GetTokenInfo(true_id,
286 &tokeninfo);
287
288 if (rv != CKR_OK) {
289 continue;
290 }
291
292 have_tokeninfo = B_TRUE;
293
294 /*
295 * pad slot description from user/system configuration
296 * with spaces
297 */
298 token = metaslot_config.keystore_token;
299 token_str_len = strlen((char *)token);
300 (void) memset(token + token_str_len, ' ',
301 TOKEN_LABEL_SIZE - token_str_len);
302
303 /*
304 * The PKCS#11 strings are not null-terminated.
305 * So, just compare TOKEN_LABEL_SIZE bytes
306 */
307 if (strncmp((char *)token, (char *)tokeninfo.label,
308 TOKEN_LABEL_SIZE) == 0) {
309 num_matched++;
310 }
311 }
312
313 if (num_match_needed == num_matched) {
314 /* match is found */
315
316 if (!have_tokeninfo) {
317 rv = FUNCLIST(fw_st_id)->C_GetTokenInfo(true_id,
318 &tokeninfo);
319 if (rv != CKR_OK) {
320 continue;
321 }
322 }
323
324
325 if (tokeninfo.flags & CKF_WRITE_PROTECTED) {
326 /*
327 * Currently this is the only time that
328 * the write_protected state is set, and
329 * it is never cleared. The token could
330 * clear (or set!) this flag later on.
331 * We might want to adjust the state
332 * of metaslot, but there's know way to know
333 * when a token changes this flag.
334 */
335 write_protected = B_TRUE;
336 }
337
338 found = B_TRUE;
339 break;
340 }
341 }
342
343 skip_search:
344 if (found) {
345 objtok_slotnum = slot;
346 } else {
347 /*
348 * if slot and/or token is not defined for the keystore,
349 * just use the first available slot as keystore
350 */
351 objtok_slotnum = 0;
352 }
353 slots[objtok_slotnum].session_pool.keep_one_alive = B_TRUE;
354 metaslot_keystore_slotid = slots[objtok_slotnum].fw_st_id;
355 }
356
357
358 CK_ULONG
get_keystore_slotnum()359 get_keystore_slotnum()
360 {
361 return (objtok_slotnum);
362 }
363
364 CK_ULONG
get_softtoken_slotnum()365 get_softtoken_slotnum()
366 {
367 return (softtoken_slotnum);
368 }
369
370 CK_SLOT_ID
meta_slotManager_get_framework_table_id(CK_ULONG slotnum)371 meta_slotManager_get_framework_table_id(CK_ULONG slotnum)
372 {
373 /*
374 * This is only used internally, and so the slotnum should always
375 * be valid.
376 */
377 return (slots[slotnum].fw_st_id);
378 }
379
380 CK_ULONG
meta_slotManager_get_slotcount()381 meta_slotManager_get_slotcount()
382 {
383 return (num_slots);
384 }
385
386 boolean_t
meta_slotManager_token_write_protected()387 meta_slotManager_token_write_protected()
388 {
389 return (write_protected);
390 }
391
392 /*
393 * Find a session in the given list that matches the specified flags.
394 * If such a session is found, it will be removed from the list, and
395 * returned to the caller. If such a session is not found, will
396 * return NULL
397 */
398 static slot_session_t *
get_session(slot_session_t ** session_list,CK_FLAGS flags)399 get_session(slot_session_t **session_list, CK_FLAGS flags)
400 {
401
402 slot_session_t *tmp_session;
403
404 tmp_session = *session_list;
405
406 while (tmp_session != NULL) {
407 if (tmp_session->session_flags == flags) {
408 break;
409 } else {
410 tmp_session = tmp_session->next;
411 }
412
413 }
414
415 if (tmp_session == NULL) {
416 /* no match */
417 return (NULL);
418 }
419
420 /* Remove from list */
421 REMOVE_FROM_LIST(*session_list, tmp_session);
422 return (tmp_session);
423 }
424
425 /*
426 * meta_get_slot_session
427 *
428 * Call to get a session with a specific slot/token.
429 *
430 * NOTE - We assume the slot allows an unlimited number of sessions. We
431 * could look at what's reported in the token info, but that information is
432 * not always set. It's also unclear when we should (A) wait for one to become
433 * available, (B) skip the slot for now or (C) return a fatal error. The
434 * extra complexity is not worth it.
435 *
436 */
437 CK_RV
meta_get_slot_session(CK_ULONG slotnum,slot_session_t ** session,CK_FLAGS flags)438 meta_get_slot_session(CK_ULONG slotnum, slot_session_t **session,
439 CK_FLAGS flags) {
440 session_pool_t *pool;
441 slot_session_t *new_session, *tmp_session;
442 CK_RV rv;
443 CK_SLOT_ID fw_st_id, true_id;
444
445 if (slotnum >= num_slots) {
446 return (CKR_SLOT_ID_INVALID);
447 }
448
449 pool = &slots[slotnum].session_pool;
450
451 /*
452 * Try to reuse an existing session.
453 */
454
455 (void) pthread_mutex_lock(&pool->list_lock);
456
457 if (pool->idle_list_head != NULL) {
458 tmp_session = get_session(&(pool->idle_list_head), flags);
459 if (tmp_session != NULL) {
460 /* Add to active list */
461 INSERT_INTO_LIST(pool->active_list_head, tmp_session);
462 *session = tmp_session;
463 pool->num_idle_sessions--;
464 (void) pthread_mutex_unlock(&pool->list_lock);
465 return (CKR_OK);
466 }
467 }
468
469 if (pool->persist_list_head != NULL) {
470 tmp_session = get_session(&(pool->persist_list_head), flags);
471 if (tmp_session != NULL) {
472 /* Add to active list */
473 INSERT_INTO_LIST(pool->active_list_head, tmp_session);
474 *session = tmp_session;
475 (void) pthread_mutex_unlock(&pool->list_lock);
476 return (CKR_OK);
477 }
478 }
479 (void) pthread_mutex_unlock(&pool->list_lock);
480
481 fw_st_id = slots[slotnum].fw_st_id;
482 true_id = TRUEID(fw_st_id);
483
484 new_session = calloc(1, sizeof (slot_session_t));
485 if (new_session == NULL) {
486 return (CKR_HOST_MEMORY);
487 }
488
489 /* initialize slotsession */
490 new_session->slotnum = slotnum;
491 new_session->fw_st_id = fw_st_id;
492 new_session->object_list_head = NULL;
493 new_session->session_flags = flags;
494 (void) pthread_rwlock_init(&new_session->object_list_lock, NULL);
495
496 rv = FUNCLIST(fw_st_id)->C_OpenSession(true_id, flags, NULL, NULL,
497 &new_session->hSession);
498
499 if (rv == CKR_TOKEN_WRITE_PROTECTED) {
500 /* Retry with a RO session. */
501 new_session->session_flags &= ~CKF_SERIAL_SESSION;
502 rv = FUNCLIST(fw_st_id)->C_OpenSession(true_id,
503 new_session->session_flags, NULL, NULL,
504 &new_session->hSession);
505 }
506
507 if (rv != CKR_OK) {
508 free(new_session);
509 return (CKR_FUNCTION_FAILED);
510 }
511
512 /* Insert session into active list */
513 (void) pthread_mutex_lock(&pool->list_lock);
514 INSERT_INTO_LIST(pool->active_list_head, new_session);
515 (void) pthread_mutex_unlock(&pool->list_lock);
516 *session = new_session;
517 return (CKR_OK);
518 }
519
520
521 /*
522 * meta_release_slot_session
523 *
524 * Call to release a session obtained via meta_get_slot_session()
525 */
526 void
meta_release_slot_session(slot_session_t * session)527 meta_release_slot_session(slot_session_t *session) {
528 session_pool_t *pool;
529 boolean_t must_retain, can_close = B_FALSE;
530 boolean_t this_is_last_session = B_FALSE;
531
532 pool = &slots[session->slotnum].session_pool;
533
534 /* Note that the active_list must have >= 1 entry (this session) */
535 if (pool->persist_list_head == NULL &&
536 pool->idle_list_head == NULL &&
537 pool->active_list_head->next == NULL)
538 this_is_last_session = B_TRUE;
539
540 /*
541 * If the session has session objects, we need to retain it. Also
542 * retain it if it's the only session holding login state (or handles
543 * to public token objects)
544 */
545 must_retain = session->object_list_head != NULL ||
546 (pool->keep_one_alive && this_is_last_session);
547
548 if ((!must_retain) && (pool->num_idle_sessions > MAX_IDLE_SESSIONS)) {
549 can_close = B_TRUE;
550 }
551
552 (void) pthread_mutex_lock(&pool->list_lock);
553 /* remove from active list */
554 REMOVE_FROM_LIST(pool->active_list_head, session);
555
556 if (must_retain) {
557 /* insert into persist list */
558 INSERT_INTO_LIST(pool->persist_list_head, session);
559 (void) pthread_mutex_unlock(&pool->list_lock);
560 return;
561 } else if (!can_close) {
562 /* insert into idle list */
563 INSERT_INTO_LIST(pool->idle_list_head, session);
564 pool->num_idle_sessions++;
565 (void) pthread_mutex_unlock(&pool->list_lock);
566 return;
567 }
568
569 (void) pthread_mutex_unlock(&pool->list_lock);
570
571 (void) FUNCLIST(session->fw_st_id)->C_CloseSession(session->hSession);
572
573 (void) pthread_rwlock_destroy(&session->object_list_lock);
574 free(session);
575 }
576
577 /*
578 * Returns whether metaslot has directly logged in
579 */
580 boolean_t
metaslot_logged_in()581 metaslot_logged_in()
582 {
583 return (metaslotLoggedIn);
584 }
585
586 void
metaslot_set_logged_in_flag(boolean_t value)587 metaslot_set_logged_in_flag(boolean_t value)
588 {
589 (void) pthread_mutex_lock(&metaslotLoggedIn_mutex);
590 metaslotLoggedIn = value;
591 (void) pthread_mutex_unlock(&metaslotLoggedIn_mutex);
592 }
593