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 */
53slot_data_t *slots;
54CK_SLOT_ID metaslot_keystore_slotid;
55static CK_ULONG num_slots;
56static CK_ULONG objtok_slotnum;
57static CK_ULONG softtoken_slotnum;
58static boolean_t write_protected;
59
60/* protects the "metaslotLoggedIn" variable */
61static pthread_mutex_t metaslotLoggedIn_mutex = PTHREAD_MUTEX_INITIALIZER;
62static 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 */
70CK_RV
71meta_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
119clean_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 */
136void
137meta_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 */
212void
213meta_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
345skip_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
360CK_ULONG
361get_keystore_slotnum()
362{
363	return (objtok_slotnum);
364}
365
366CK_ULONG
367get_softtoken_slotnum()
368{
369	return (softtoken_slotnum);
370}
371
372CK_SLOT_ID
373meta_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
382CK_ULONG
383meta_slotManager_get_slotcount()
384{
385	return (num_slots);
386}
387
388boolean_t
389meta_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 */
400static slot_session_t *
401get_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 */
439CK_RV
440meta_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 */
528void
529meta_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 */
582boolean_t
583metaslot_logged_in()
584{
585	return (metaslotLoggedIn);
586}
587
588void
589metaslot_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