Home | History | Annotate | Download | only in common
      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