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
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 	/* 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
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
361 get_keystore_slotnum()
362 {
363 	return (objtok_slotnum);
364 }
365 
366 CK_ULONG
367 get_softtoken_slotnum()
368 {
369 	return (softtoken_slotnum);
370 }
371 
372 CK_SLOT_ID
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
383 meta_slotManager_get_slotcount()
384 {
385 	return (num_slots);
386 }
387 
388 boolean_t
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 *
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
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
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
583 metaslot_logged_in()
584 {
585 	return (metaslotLoggedIn);
586 }
587 
588 void
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