xref: /illumos-gate/usr/src/lib/pkcs11/libpkcs11/common/metaSlotManager.c (revision 533affcbc7fc4d0c8132976ea454aaa715fe2307)
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
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
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
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
359 get_keystore_slotnum()
360 {
361 	return (objtok_slotnum);
362 }
363 
364 CK_ULONG
365 get_softtoken_slotnum()
366 {
367 	return (softtoken_slotnum);
368 }
369 
370 CK_SLOT_ID
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
381 meta_slotManager_get_slotcount()
382 {
383 	return (num_slots);
384 }
385 
386 boolean_t
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 *
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
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
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
581 metaslot_logged_in()
582 {
583 	return (metaslotLoggedIn);
584 }
585 
586 void
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