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