xref: /illumos-gate/usr/src/lib/pkcs11/libpkcs11/common/metaSlotManager.c (revision bdfc6d18da790deeec2e0eb09c625902defe2498)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Functions for dealing with provider sessions
31  */
32 
33 #include <string.h>
34 #include <cryptoutil.h>
35 #include "metaGlobal.h"
36 #include "pkcs11Session.h"
37 #include "pkcs11Global.h"
38 
39 
40 /*
41  * This is just a **WILD** guess for the maximum idle sessions to
42  * keep for each slot.  This number should probably be adjusted
43  * when there's more data from actual application use
44  */
45 #define	MAX_IDLE_SESSIONS	100
46 
47 /*
48  * The following 5 variables are initialized at the time metaslot
49  * is initialized.  They are not modified after they are initialized
50  *
51  * During initialization time, they are protected by the "initmutex"
52  * defined in metaGeneral.c
53  */
54 slot_data_t *slots;
55 CK_SLOT_ID metaslot_keystore_slotid;
56 static CK_ULONG num_slots;
57 static CK_ULONG objtok_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
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
137 meta_slotManager_finalize() {
138 	CK_ULONG slot;
139 
140 	/*
141 	 * No need to lock pool, we assume all meta sessions are closed.
142 	 *
143 	 * Close all sessions in the idle and persist list.
144 	 * The active list is empty.  It doesn't need to be checked.
145 	 */
146 
147 	for (slot = 0; slot < num_slots; slot++) {
148 		slot_session_t *session, *next_session;
149 
150 		/*
151 		 * The slotobjects associated with the session should have
152 		 * been closed when the metaobjects were closed. Thus, no
153 		 * need to do anything here.
154 		 */
155 
156 		session = slots[slot].session_pool.idle_list_head;
157 		while (session) {
158 			next_session = session->next;
159 			(void) FUNCLIST(session->fw_st_id)->C_CloseSession(
160 			    session->hSession);
161 			(void) pthread_rwlock_destroy(
162 				&session->object_list_lock);
163 			free(session);
164 			session = next_session;
165 		}
166 
167 		session = slots[slot].session_pool.persist_list_head;
168 		while (session) {
169 			next_session = session->next;
170 			(void) FUNCLIST(session->fw_st_id)->C_CloseSession(
171 			    session->hSession);
172 			(void) pthread_rwlock_destroy(
173 				&session->object_list_lock);
174 			free(session);
175 			session = next_session;
176 		}
177 
178 		(void) pthread_rwlock_destroy(
179 			&slots[slot].tokenobject_list_lock);
180 	}
181 
182 	free(slots);
183 	slots = NULL;
184 }
185 
186 
187 /*
188  * meta_slotManager_find_object_token()
189  *
190  * Called from meta_Initialize. Searches for the "object token," which is used
191  * for storing token objects and loging into.
192  *
193  * We do the search using the following algorithm.
194  *
195  * If either ${METASLOT_OBJECTSTORE_SLOT} or ${METASLOT_OBJECTSTORE_TOKEN}
196  * environment variable is defined, the value of the defined variable(s)
197  * will be used for the match.  All token and slot values defined system-wide
198  * will be ignored.
199  *
200  * If neither variables above are defined, the system-wide values defined
201  * in pkcs11.conf are used.
202  *
203  * If neither environment variables or system-wide values are defined,
204  * or if none of the existing slots/tokens match the defined
205  * values, the first slot after metaslot will be used as the default.
206  *
207  */
208 void
209 meta_slotManager_find_object_token() {
210 	CK_ULONG slot;
211 	boolean_t found = B_FALSE;
212 	CK_RV rv;
213 	unsigned int num_match_needed = 0;
214 	CK_SLOT_INFO slotinfo;
215 	CK_TOKEN_INFO tokeninfo;
216 
217 	if (metaslot_config.keystore_token_specified) {
218 		num_match_needed++;
219 	}
220 
221 	if (metaslot_config.keystore_slot_specified) {
222 		num_match_needed++;
223 	}
224 
225 	if (num_match_needed == 0) {
226 		goto skip_search;
227 	}
228 
229 	for (slot = 0; slot < num_slots; slot++) {
230 		unsigned int num_matched = 0;
231 		boolean_t have_tokeninfo = B_FALSE;
232 		CK_SLOT_ID true_id, fw_st_id;
233 
234 		fw_st_id = slots[slot].fw_st_id;
235 		true_id = TRUEID(fw_st_id);
236 
237 		if (metaslot_config.keystore_slot_specified) {
238 
239 			unsigned char *slot;
240 			size_t slot_str_len;
241 
242 			rv = FUNCLIST(fw_st_id)->C_GetSlotInfo(true_id,
243 			    &slotinfo);
244 			if (rv != CKR_OK)
245 				continue;
246 
247 			/*
248 			 * pad slot description from user/system configuration
249 			 * with spaces
250 			 */
251 			slot = metaslot_config.keystore_slot;
252 			slot_str_len = strlen((char *)slot);
253 			(void) memset(slot + slot_str_len, ' ',
254 			    SLOT_DESCRIPTION_SIZE - slot_str_len);
255 
256 			/*
257 			 * The PKCS#11 strings are not null-terminated, so,
258 			 * we just compare SLOT_DESCRIPTION_SIZE bytes
259 			 */
260 			if (strncmp((char *)slot,
261 			    (char *)slotinfo.slotDescription,
262 			    SLOT_DESCRIPTION_SIZE) == 0) {
263 				num_matched++;
264 			}
265 		}
266 
267 		if (metaslot_config.keystore_token_specified) {
268 			unsigned char *token;
269 			size_t token_str_len;
270 
271 			rv = FUNCLIST(fw_st_id)->C_GetTokenInfo(true_id,
272 			    &tokeninfo);
273 
274 			if (rv != CKR_OK) {
275 				continue;
276 			}
277 
278 			have_tokeninfo = B_TRUE;
279 
280 			/*
281 			 * pad slot description from user/system configuration
282 			 * with spaces
283 			 */
284 			token = metaslot_config.keystore_token;
285 			token_str_len = strlen((char *)token);
286 			(void) memset(token + token_str_len, ' ',
287 			    TOKEN_LABEL_SIZE - token_str_len);
288 
289 			/*
290 			 * The PKCS#11 strings are not null-terminated.
291 			 * So, just compare TOKEN_LABEL_SIZE bytes
292 			 */
293 			if (strncmp((char *)token, (char *)tokeninfo.label,
294 			    TOKEN_LABEL_SIZE) == 0) {
295 				num_matched++;
296 			}
297 		}
298 
299 		if (num_match_needed == num_matched) {
300 			/* match is found */
301 
302 			if (!have_tokeninfo) {
303 				rv = FUNCLIST(fw_st_id)->C_GetTokenInfo(true_id,
304 				    &tokeninfo);
305 				if (rv != CKR_OK) {
306 					continue;
307 				}
308 			}
309 
310 
311 			if (tokeninfo.flags & CKF_WRITE_PROTECTED) {
312 				/*
313 				 * Currently this is the only time that
314 				 * the write_protected state is set, and
315 				 * it is never cleared. The token could
316 				 * clear (or set!) this flag later on.
317 				 * We might want to adjust the state
318 				 * of metaslot, but there's know way to know
319 				 * when a token changes this flag.
320 				 */
321 				write_protected = B_TRUE;
322 			}
323 
324 			found = B_TRUE;
325 			break;
326 		}
327 	}
328 
329 skip_search:
330 	if (found) {
331 		objtok_slotnum = slot;
332 	} else {
333 		/*
334 		 * if slot and/or token is not defined for the keystore,
335 		 * just use the first available slot as keystore
336 		 */
337 		objtok_slotnum = 0;
338 	}
339 	slots[objtok_slotnum].session_pool.keep_one_alive = B_TRUE;
340 	metaslot_keystore_slotid = slots[objtok_slotnum].fw_st_id;
341 }
342 
343 
344 CK_ULONG
345 get_keystore_slotnum()
346 {
347 	return (objtok_slotnum);
348 }
349 
350 
351 CK_SLOT_ID
352 meta_slotManager_get_framework_table_id(CK_ULONG slotnum)
353 {
354 	/*
355 	 * This is only used internally, and so the slotnum should always
356 	 * be valid.
357 	 */
358 	return (slots[slotnum].fw_st_id);
359 }
360 
361 CK_ULONG
362 meta_slotManager_get_slotcount()
363 {
364 	return (num_slots);
365 }
366 
367 boolean_t
368 meta_slotManager_token_write_protected()
369 {
370 	return (write_protected);
371 }
372 
373 /*
374  * Find a session in the given list that matches the specified flags.
375  * If such a session is found, it will be removed from the list, and
376  * returned to the caller.  If such a session is not found, will
377  * return NULL
378  */
379 static slot_session_t *
380 get_session(slot_session_t **session_list, CK_FLAGS flags)
381 {
382 
383 	slot_session_t *tmp_session;
384 
385 	tmp_session = *session_list;
386 
387 	while (tmp_session != NULL) {
388 		if (tmp_session->session_flags == flags) {
389 			break;
390 		} else {
391 			tmp_session = tmp_session->next;
392 		}
393 
394 	}
395 
396 	if (tmp_session == NULL) {
397 		/* no match */
398 		return (NULL);
399 	}
400 
401 	/* Remove from list */
402 	REMOVE_FROM_LIST(*session_list, tmp_session);
403 	return (tmp_session);
404 }
405 
406 /*
407  * meta_get_slot_session
408  *
409  * Call to get a session with a specific slot/token.
410  *
411  * NOTE - We assume the slot allows an unlimited number of sessions. We
412  * could look at what's reported in the token info, but that information is
413  * not always set. It's also unclear when we should (A) wait for one to become
414  * available, (B) skip the slot for now or (C) return a fatal error. The
415  * extra complexity is not worth it.
416  *
417  */
418 CK_RV
419 meta_get_slot_session(CK_ULONG slotnum, slot_session_t **session,
420     CK_FLAGS flags) {
421 	session_pool_t *pool;
422 	slot_session_t *new_session, *tmp_session;
423 	CK_RV rv;
424 	CK_SLOT_ID fw_st_id, true_id;
425 
426 	if (slotnum >= num_slots) {
427 		return (CKR_SLOT_ID_INVALID);
428 	}
429 
430 	pool = &slots[slotnum].session_pool;
431 
432 	/*
433 	 * Try to reuse an existing session.
434 	 */
435 
436 	(void) pthread_mutex_lock(&pool->list_lock);
437 
438 	if (pool->idle_list_head != NULL) {
439 		tmp_session = get_session(&(pool->idle_list_head), flags);
440 		if (tmp_session != NULL) {
441 			/* Add to active list */
442 			INSERT_INTO_LIST(pool->active_list_head, tmp_session);
443 			*session = tmp_session;
444 			pool->num_idle_sessions--;
445 			(void) pthread_mutex_unlock(&pool->list_lock);
446 			return (CKR_OK);
447 		}
448 	}
449 
450 	if (pool->persist_list_head != NULL) {
451 		tmp_session = get_session(&(pool->persist_list_head), flags);
452 		if (tmp_session != NULL) {
453 			/* Add to active list */
454 			INSERT_INTO_LIST(pool->active_list_head, tmp_session);
455 			*session = tmp_session;
456 			(void) pthread_mutex_unlock(&pool->list_lock);
457 			return (CKR_OK);
458 		}
459 	}
460 	(void) pthread_mutex_unlock(&pool->list_lock);
461 
462 	fw_st_id = slots[slotnum].fw_st_id;
463 	true_id = TRUEID(fw_st_id);
464 
465 	new_session = calloc(1, sizeof (slot_session_t));
466 	if (new_session == NULL) {
467 		return (CKR_HOST_MEMORY);
468 	}
469 
470 	/* initialize slotsession */
471 	new_session->slotnum = slotnum;
472 	new_session->fw_st_id = fw_st_id;
473 	new_session->object_list_head = NULL;
474 	new_session->session_flags = flags;
475 	(void) pthread_rwlock_init(&new_session->object_list_lock, NULL);
476 
477 	rv = FUNCLIST(fw_st_id)->C_OpenSession(true_id, flags, NULL, NULL,
478 	    &new_session->hSession);
479 
480 	if (rv == CKR_TOKEN_WRITE_PROTECTED) {
481 		/* Retry with a RO session. */
482 		new_session->session_flags &= ~CKF_SERIAL_SESSION;
483 		rv = FUNCLIST(fw_st_id)->C_OpenSession(true_id,
484 		    new_session->session_flags, NULL, NULL,
485 		    &new_session->hSession);
486 	}
487 
488 	if (rv != CKR_OK) {
489 		free(new_session);
490 		return (CKR_FUNCTION_FAILED);
491 	}
492 
493 	/* Insert session into active list */
494 	(void) pthread_mutex_lock(&pool->list_lock);
495 	INSERT_INTO_LIST(pool->active_list_head, new_session);
496 	(void) pthread_mutex_unlock(&pool->list_lock);
497 	*session = new_session;
498 	return (CKR_OK);
499 }
500 
501 
502 /*
503  * meta_release_slot_session
504  *
505  * Call to release a session obtained via meta_get_slot_session()
506  */
507 void
508 meta_release_slot_session(slot_session_t *session) {
509 	session_pool_t *pool;
510 	boolean_t must_retain, can_close = B_FALSE;
511 	boolean_t this_is_last_session = B_FALSE;
512 
513 	pool = &slots[session->slotnum].session_pool;
514 
515 	/* Note that the active_list must have >= 1 entry (this session) */
516 	if (pool->persist_list_head == NULL &&
517 	    pool->idle_list_head == NULL &&
518 	    pool->active_list_head->next == NULL)
519 		this_is_last_session = B_TRUE;
520 
521 	/*
522 	 * If the session has session objects, we need to retain it. Also
523 	 * retain it if it's the only session holding login state (or handles
524 	 * to public token objects)
525 	 */
526 	must_retain = session->object_list_head != NULL ||
527 	    (pool->keep_one_alive && this_is_last_session);
528 
529 	if ((!must_retain) && (pool->num_idle_sessions > MAX_IDLE_SESSIONS)) {
530 		can_close = B_TRUE;
531 	}
532 
533 	(void) pthread_mutex_lock(&pool->list_lock);
534 	/* remove from active list */
535 	REMOVE_FROM_LIST(pool->active_list_head, session);
536 
537 	if (must_retain) {
538 		/* insert into persist list */
539 		INSERT_INTO_LIST(pool->persist_list_head, session);
540 		(void) pthread_mutex_unlock(&pool->list_lock);
541 		return;
542 	} else if (!can_close) {
543 		/* insert into idle list */
544 		INSERT_INTO_LIST(pool->idle_list_head, session);
545 		pool->num_idle_sessions++;
546 		(void) pthread_mutex_unlock(&pool->list_lock);
547 		return;
548 	}
549 
550 	(void) pthread_mutex_unlock(&pool->list_lock);
551 
552 	(void) FUNCLIST(session->fw_st_id)->C_CloseSession(session->hSession);
553 
554 	(void) pthread_rwlock_destroy(&session->object_list_lock);
555 	free(session);
556 }
557 
558 /*
559  * Returns whether metaslot has directly logged in
560  */
561 boolean_t
562 metaslot_logged_in()
563 {
564 	return (metaslotLoggedIn);
565 }
566 
567 void
568 metaslot_set_logged_in_flag(boolean_t value)
569 {
570 	(void) pthread_mutex_lock(&metaslotLoggedIn_mutex);
571 	metaslotLoggedIn = value;
572 	(void) pthread_mutex_unlock(&metaslotLoggedIn_mutex);
573 }
574