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 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*
     27  * Session Management Functions
     28  * (as defined in PKCS#11 spec spection 11.6)
     29  */
     30 
     31 #include <string.h>
     32 #include "metaGlobal.h"
     33 
     34 extern meta_session_t *meta_sessionlist_head;
     35 extern pthread_rwlock_t meta_sessionlist_lock;
     36 extern CK_ULONG num_meta_sessions;
     37 extern CK_ULONG num_rw_meta_sessions;
     38 
     39 /*
     40  * meta_OpenSession
     41  *
     42  * NOTES:
     43  * 1) The pApplication and Notify args are not used, as the metaslot does not
     44  *    support application callbacks.
     45  * 2) the slotID argument is not checked or used because this function
     46  *    is only called from the framework.
     47  */
     48 /* ARGSUSED */
     49 CK_RV
     50 meta_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication,
     51     CK_NOTIFY Notify, CK_SESSION_HANDLE_PTR phSession)
     52 {
     53 	meta_session_t *new_session;
     54 	CK_RV rv;
     55 
     56 	if (!metaslot_enabled) {
     57 		return (CKR_SLOT_ID_INVALID);
     58 	}
     59 
     60 	if (phSession == NULL) {
     61 		return (CKR_ARGUMENTS_BAD);
     62 	}
     63 
     64 	/* Check for any unknown flags. */
     65 	if (flags & ~(CKF_SERIAL_SESSION | CKF_RW_SESSION)) {
     66 		return (CKR_ARGUMENTS_BAD);
     67 	}
     68 
     69 	if (!(flags & CKF_SERIAL_SESSION)) {
     70 		return (CKR_SESSION_PARALLEL_NOT_SUPPORTED);
     71 	}
     72 
     73 	if (meta_slotManager_token_write_protected() &&
     74 	    (flags & CKF_RW_SESSION)) {
     75 		return (CKR_TOKEN_WRITE_PROTECTED);
     76 	}
     77 
     78 	rv = meta_session_alloc(&new_session);
     79 	if (rv != CKR_OK)
     80 		return (rv);
     81 
     82 	new_session->session_flags = flags;
     83 
     84 	rv = meta_session_activate(new_session);
     85 	if (rv != CKR_OK) {
     86 		meta_session_dealloc(new_session);
     87 		return (rv);
     88 	}
     89 
     90 	*phSession = (CK_SESSION_HANDLE) new_session;
     91 
     92 	num_meta_sessions++;
     93 	if (flags & CKF_RW_SESSION) {
     94 		num_rw_meta_sessions++;
     95 	}
     96 
     97 	return (CKR_OK);
     98 }
     99 
    100 
    101 /*
    102  * meta_CloseSession
    103  *
    104  */
    105 CK_RV
    106 meta_CloseSession(CK_SESSION_HANDLE hSession)
    107 {
    108 	CK_RV rv;
    109 	meta_session_t *session;
    110 	CK_FLAGS flags;
    111 
    112 	rv = meta_handle2session(hSession, &session);
    113 	if (rv != CKR_OK)
    114 		return (rv);
    115 
    116 	/* save info about session flags before they are destroyed */
    117 	flags = session->session_flags;
    118 
    119 	rv = meta_session_deactivate(session, B_FALSE);
    120 
    121 	if (rv == CKR_OK)
    122 		meta_session_dealloc(session);
    123 
    124 	num_meta_sessions--;
    125 	if (flags & CKF_RW_SESSION) {
    126 		num_rw_meta_sessions--;
    127 	}
    128 
    129 	return (rv);
    130 }
    131 
    132 
    133 /*
    134  * meta_CloseAllSessions
    135  *
    136  * This is a simple loop that closes the sessionlist head (resulting in a
    137  * new list head) until the list is empty.
    138  *
    139  */
    140 CK_RV
    141 meta_CloseAllSessions(CK_SLOT_ID slotID)
    142 {
    143 	CK_RV rv;
    144 	meta_session_t *session;
    145 
    146 	if (!metaslot_enabled) {
    147 		return (CKR_SLOT_ID_INVALID);
    148 	}
    149 
    150 	if (slotID != METASLOT_SLOTID)
    151 		return (CKR_SLOT_ID_INVALID);
    152 
    153 	(void) pthread_rwlock_wrlock(&meta_sessionlist_lock);
    154 	while ((session = meta_sessionlist_head) != NULL) {
    155 		rv = meta_handle2session((CK_SESSION_HANDLE)session, &session);
    156 		if (rv != CKR_OK) {
    157 			/*NOTREACHED*/
    158 			(void) pthread_rwlock_unlock(&meta_sessionlist_lock);
    159 			return (CKR_FUNCTION_FAILED);
    160 		}
    161 
    162 		(void) meta_session_deactivate(session, B_TRUE);
    163 		meta_session_dealloc(session);
    164 	}
    165 	(void) pthread_rwlock_unlock(&meta_sessionlist_lock);
    166 
    167 	/* All open sessions should be closed, just reset the variables */
    168 	num_meta_sessions = 0;
    169 	num_rw_meta_sessions = 0;
    170 
    171 	return (CKR_OK);
    172 }
    173 
    174 
    175 /*
    176  * meta_GetSessionInfo
    177  *
    178  */
    179 CK_RV
    180 meta_GetSessionInfo(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo)
    181 {
    182 	CK_RV rv;
    183 	meta_session_t *session;
    184 
    185 	if (pInfo == NULL)
    186 		return (CKR_ARGUMENTS_BAD);
    187 
    188 	rv = meta_handle2session(hSession, &session);
    189 	if (rv != CKR_OK)
    190 		return (rv);
    191 
    192 	pInfo->slotID = METASLOT_SLOTID;
    193 	pInfo->flags = session->session_flags;
    194 
    195 	if (metaslot_logged_in()) {
    196 		if (IS_READ_ONLY_SESSION(session->session_flags)) {
    197 			pInfo->state = CKS_RO_USER_FUNCTIONS;
    198 		} else {
    199 			pInfo->state = CKS_RW_USER_FUNCTIONS;
    200 		}
    201 	} else {
    202 		if (IS_READ_ONLY_SESSION(session->session_flags)) {
    203 			pInfo->state = CKS_RO_PUBLIC_SESSION;
    204 		} else {
    205 			pInfo->state = CKS_RW_PUBLIC_SESSION;
    206 		}
    207 	}
    208 
    209 	pInfo->ulDeviceError = 0;
    210 
    211 	REFRELEASE(session);
    212 
    213 	return (CKR_OK);
    214 }
    215 
    216 CK_RV
    217 meta_getopstatelen(meta_session_t *session, CK_ULONG *out_length)
    218 {
    219 	CK_RV rv = CKR_OK;
    220 	slot_session_t *slot_session;
    221 	CK_ULONG length;
    222 
    223 	*out_length = sizeof (meta_opstate_t);
    224 	if (session->op1.type != 0) {
    225 		slot_session = session->op1.session;
    226 		rv = FUNCLIST(slot_session->fw_st_id)->C_GetOperationState(
    227 		    slot_session->hSession, NULL, &length);
    228 		if (rv == CKR_OK)
    229 			*out_length += length;
    230 	}
    231 	return (rv);
    232 }
    233 
    234 /*
    235  * meta_GetOperationState
    236  *
    237  */
    238 CK_RV
    239 meta_GetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState,
    240     CK_ULONG_PTR pulOperationStateLen)
    241 {
    242 	CK_RV rv;
    243 	meta_session_t *session;
    244 	slot_session_t *slot_session = NULL;
    245 	meta_opstate_t opstate;
    246 
    247 	if (pulOperationStateLen == NULL)
    248 		return (CKR_ARGUMENTS_BAD);
    249 
    250 	rv = meta_handle2session(hSession, &session);
    251 	if (rv != CKR_OK)
    252 		return (rv);
    253 
    254 	/*
    255 	 * If no operation is active, then bail out.
    256 	 */
    257 	if (session->op1.type == 0) {
    258 		rv = CKR_OPERATION_NOT_INITIALIZED;
    259 		goto endgetopstate;
    260 	}
    261 
    262 	/*
    263 	 * If the caller did not give an OpState buffer,
    264 	 * shortcut and just return the size needed to hold
    265 	 * a metaslot OpState record later.
    266 	 * The actual size of the returned state will be the
    267 	 * sizeof(meta_opstate_t) + SIZE (op1 state),
    268 	 * so we have to get the size of
    269 	 * the operation states now.
    270 	 */
    271 	if (pOperationState == NULL) {
    272 		rv = meta_getopstatelen(session, pulOperationStateLen);
    273 		REFRELEASE(session);
    274 		return (rv);
    275 	}
    276 
    277 	/*
    278 	 * To be here, the caller must have supplied an
    279 	 * already initialized meta_opstate_t pointer.
    280 	 * Use it to get the real state info from the operation(s).
    281 	 *
    282 	 * The format of the Metaslot Opstate record:
    283 	 * {
    284 	 *    struct metaopstate
    285 	 *    [ op1 state data ]
    286 	 * }
    287 	 */
    288 
    289 	/*
    290 	 * If the buffer is not even big enough for the metaslot
    291 	 * opstate data, return error and set the returned
    292 	 * state length to indicate the minimum needed.
    293 	 */
    294 	if (*pulOperationStateLen < sizeof (meta_opstate_t)) {
    295 		rv = meta_getopstatelen(session, pulOperationStateLen);
    296 		/*
    297 		 * Remap the error so the caller knows that they
    298 		 * used an invalid buffer size in the first place.
    299 		 */
    300 		if (rv == CKR_OK)
    301 			rv = CKR_BUFFER_TOO_SMALL;
    302 		goto endgetopstate;
    303 	}
    304 
    305 	(void) memset(&opstate, 0, sizeof (meta_opstate_t));
    306 	opstate.magic_marker = METASLOT_OPSTATE_MAGIC;
    307 
    308 	if (session->op1.type != 0) {
    309 		slot_session = session->op1.session;
    310 		opstate.state[0].op_type = session->op1.type;
    311 		opstate.state[0].op_slotnum = slot_session->slotnum;
    312 		opstate.state[0].op_state_len = *pulOperationStateLen -
    313 		    sizeof (meta_opstate_t);
    314 		opstate.state[0].op_init_app = session->init.app;
    315 		opstate.state[0].op_init_done = session->init.done;
    316 		rv = FUNCLIST(slot_session->fw_st_id)->C_GetOperationState(
    317 		    slot_session->hSession,
    318 		    pOperationState + sizeof (meta_opstate_t),
    319 		    &(opstate.state[0].op_state_len));
    320 
    321 		if (rv == CKR_BUFFER_TOO_SMALL) {
    322 			/*
    323 			 * This should not happen, but if it does,
    324 			 * recalculate the entire size needed
    325 			 * and return the error.
    326 			 */
    327 			rv = meta_getopstatelen(session, pulOperationStateLen);
    328 			if (rv == CKR_OK)
    329 				rv = CKR_BUFFER_TOO_SMALL;
    330 		}
    331 
    332 		if (rv != CKR_OK)
    333 			goto endgetopstate;
    334 	}
    335 
    336 endgetopstate:
    337 	if (rv == CKR_OK && pOperationState != NULL) {
    338 		(void) memcpy(pOperationState, (void *)&opstate,
    339 		    sizeof (meta_opstate_t));
    340 
    341 		*pulOperationStateLen = sizeof (meta_opstate_t) +
    342 		    opstate.state[0].op_state_len;
    343 	}
    344 
    345 	REFRELEASE(session);
    346 	return (rv);
    347 }
    348 
    349 static CK_RV
    350 meta_set_opstate(slot_session_t *slot_session,
    351 		meta_object_t *meta_enc_key,
    352 		meta_object_t *meta_auth_key,
    353 		struct opstate_data *state,
    354 		CK_BYTE *databuf)
    355 {
    356 	CK_RV rv;
    357 	static CK_ULONG encrypt_optypes = (CKF_ENCRYPT | CKF_DECRYPT);
    358 	static CK_ULONG sign_optypes = (CKF_SIGN | CKF_VERIFY |
    359 	    CKF_SIGN_RECOVER | CKF_VERIFY_RECOVER);
    360 	slot_object_t *enc_key_obj = NULL, *auth_key_obj = NULL;
    361 
    362 	if (state->op_type & encrypt_optypes) {
    363 		rv = meta_object_get_clone(meta_enc_key, slot_session->slotnum,
    364 		    slot_session, &enc_key_obj);
    365 		if (rv != CKR_OK) {
    366 			return (rv);
    367 		}
    368 	}
    369 	if (state->op_type & sign_optypes) {
    370 		rv = meta_object_get_clone(meta_auth_key, slot_session->slotnum,
    371 		    slot_session, &auth_key_obj);
    372 		if (rv != CKR_OK) {
    373 			return (rv);
    374 		}
    375 	}
    376 
    377 	/*
    378 	 * Check to see if the keys are needed to restore the
    379 	 * state on the first operation.
    380 	 */
    381 	rv = FUNCLIST(slot_session->fw_st_id)->C_SetOperationState(
    382 	    slot_session->hSession, databuf, state->op_state_len,
    383 	    enc_key_obj ? enc_key_obj->hObject : CK_INVALID_HANDLE,
    384 	    auth_key_obj ? auth_key_obj->hObject : CK_INVALID_HANDLE);
    385 	/*
    386 	 * If the operation did not need a key, try again.
    387 	 */
    388 	if (rv == CKR_KEY_NOT_NEEDED) {
    389 		rv = FUNCLIST(slot_session->fw_st_id)->C_SetOperationState(
    390 		    slot_session->hSession, databuf, state->op_state_len,
    391 		    CK_INVALID_HANDLE, CK_INVALID_HANDLE);
    392 		/*
    393 		 * Strange case... If the first try returned
    394 		 * KEY_NOT_NEEDED, and this one returns KEY_NEEDED,
    395 		 * we want to remap the return so the caller sees
    396 		 * the original "CKR_KEY_NOT_NEEDED" return value.
    397 		 * This ensures that a correct caller will retry
    398 		 * without the unnecessary key argument and this
    399 		 * 2nd attempt will not happen again.
    400 		 */
    401 		if (rv == CKR_KEY_NEEDED) {
    402 			rv  = CKR_KEY_NOT_NEEDED;
    403 		}
    404 	}
    405 
    406 	return (rv);
    407 }
    408 
    409 /*
    410  * meta_SetOperationState
    411  *
    412  */
    413 CK_RV
    414 meta_SetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState,
    415     CK_ULONG ulOperationStateLen, CK_OBJECT_HANDLE hEncryptionKey,
    416     CK_OBJECT_HANDLE hAuthenticationKey)
    417 {
    418 	CK_RV rv = CKR_OK;
    419 	meta_session_t *session;
    420 	slot_session_t *slot_session = NULL;
    421 	meta_opstate_t opstate;
    422 	meta_object_t *meta_enc_key = NULL, *meta_auth_key = NULL;
    423 
    424 	/*
    425 	 * Make sure the opstate info buffer is big enough to be valid.
    426 	 */
    427 	if (ulOperationStateLen < sizeof (meta_opstate_t) ||
    428 	    pOperationState == NULL)
    429 		return (CKR_ARGUMENTS_BAD);
    430 
    431 	/* Copy the opstate info into the structure */
    432 	(void) memcpy(&opstate, pOperationState, sizeof (meta_opstate_t));
    433 
    434 	/* verify that a metaslot operation state was supplied */
    435 	if (opstate.magic_marker != METASLOT_OPSTATE_MAGIC)
    436 		return (CKR_SAVED_STATE_INVALID);
    437 
    438 	/*
    439 	 * Now, check the size again to make sure the "real" state
    440 	 * data is present.  Length of state provided must be exact.
    441 	 */
    442 	if (ulOperationStateLen != (sizeof (meta_opstate_t) +
    443 	    opstate.state[0].op_state_len))
    444 		return (CKR_SAVED_STATE_INVALID);
    445 
    446 	rv = meta_handle2session(hSession, &session);
    447 	if (rv != CKR_OK)
    448 		return (rv);
    449 
    450 	if (hEncryptionKey != CK_INVALID_HANDLE) {
    451 		rv = meta_handle2object(hEncryptionKey, &meta_enc_key);
    452 		if (rv != CKR_OK)
    453 			goto cleanup;
    454 	}
    455 	if (hAuthenticationKey != CK_INVALID_HANDLE) {
    456 		rv = meta_handle2object(hAuthenticationKey, &meta_auth_key);
    457 		if (rv != CKR_OK)
    458 			goto cleanup;
    459 	}
    460 
    461 	if (opstate.state[0].op_type != 0) {
    462 		if (session->op1.type != 0)
    463 			meta_operation_cleanup(session, session->op1.type,
    464 			    B_FALSE);
    465 
    466 		if (session->op1.session != NULL) {
    467 			slot_session = session->op1.session;
    468 		} else {
    469 			rv = meta_get_slot_session(opstate.state[0].op_slotnum,
    470 			    &slot_session, session->session_flags);
    471 			if (rv != CKR_OK)
    472 				goto cleanup;
    473 		}
    474 
    475 		session->op1.type = opstate.state[0].op_type;
    476 		session->op1.session = slot_session;
    477 		session->init.app = opstate.state[0].op_init_app;
    478 		session->init.done = opstate.state[0].op_init_done;
    479 
    480 		rv = meta_set_opstate(slot_session, meta_enc_key,
    481 		    meta_auth_key, &(opstate.state[0]),
    482 		    pOperationState + sizeof (meta_opstate_t));
    483 
    484 		if (rv != CKR_OK) {
    485 			meta_operation_cleanup(session, session->op1.type,
    486 			    FALSE);
    487 			goto cleanup;
    488 		}
    489 	}
    490 
    491 cleanup:
    492 	if (meta_enc_key != NULL)
    493 		OBJRELEASE(meta_enc_key);
    494 	if (meta_auth_key != NULL)
    495 		OBJRELEASE(meta_auth_key);
    496 	REFRELEASE(session);
    497 	return (rv);
    498 }
    499 
    500 /*
    501  * meta_Login
    502  *
    503  * This allows the user to login to the object token. The metaslot itself
    504  * does not have any kind of PIN.
    505  *
    506  */
    507 CK_RV
    508 meta_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType,
    509     CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen)
    510 {
    511 	CK_RV rv;
    512 	meta_session_t *session;
    513 	slot_session_t *login_session = NULL;
    514 	CK_TOKEN_INFO token_info;
    515 	CK_SLOT_ID true_id, fw_st_id;
    516 
    517 	rv = meta_handle2session(hSession, &session);
    518 	if (rv != CKR_OK)
    519 		return (rv);
    520 
    521 	if (metaslot_logged_in()) {
    522 		rv = CKR_USER_ALREADY_LOGGED_IN;
    523 		goto finish;
    524 	}
    525 
    526 	/* Note: CKU_SO is not supported. */
    527 	if (userType != CKU_USER) {
    528 		rv = CKR_USER_TYPE_INVALID;
    529 		goto finish;
    530 	}
    531 
    532 	rv = meta_get_slot_session(get_keystore_slotnum(), &login_session,
    533 	    session->session_flags);
    534 	if (rv != CKR_OK)
    535 		goto finish;
    536 
    537 
    538 	fw_st_id = login_session->fw_st_id;
    539 	rv = FUNCLIST(fw_st_id)->C_Login(login_session->hSession, userType,
    540 	    pPin, ulPinLen);
    541 
    542 	if (rv != CKR_OK) {
    543 		goto finish;
    544 	}
    545 
    546 	/*
    547 	 * Note:
    548 	 *
    549 	 * For some slots (eg: the pkcs11_softtoken.so), C_Login()
    550 	 * returning OK don't mean that the login is truely
    551 	 * successful.  For pkcs11_softtoken.so, the CKF_USER_PIN_TO_BE_CHANGED
    552 	 * is set to indicate that the pin needs to be changed, and
    553 	 * the login is not really successful.  We will check
    554 	 * that flag for this special condition.  Checking for
    555 	 * this flag shouldn't be harmful for other slots that doesn't
    556 	 * behave like pkcs11_softtoken.so.
    557 	 */
    558 
    559 	true_id = TRUEID(fw_st_id);
    560 	rv = FUNCLIST(fw_st_id)->C_GetTokenInfo(true_id, &token_info);
    561 	if (rv != CKR_OK) {
    562 		goto finish;
    563 	}
    564 
    565 	metaslot_set_logged_in_flag(B_TRUE);
    566 	if (token_info.flags & CKF_USER_PIN_TO_BE_CHANGED) {
    567 		metaslot_set_logged_in_flag(B_FALSE);
    568 	}
    569 finish:
    570 	if (login_session)
    571 		meta_release_slot_session(login_session);
    572 
    573 	REFRELEASE(session);
    574 
    575 	return (rv);
    576 }
    577 
    578 /*
    579  * meta_Logout
    580  *
    581  */
    582 CK_RV
    583 meta_Logout(CK_SESSION_HANDLE hSession)
    584 {
    585 	CK_RV rv = CKR_OK;
    586 	meta_session_t *session;
    587 	slot_session_t *logout_session = NULL;
    588 
    589 	rv = meta_handle2session(hSession, &session);
    590 	if (rv != CKR_OK)
    591 		return (rv);
    592 
    593 	if (!metaslot_logged_in()) {
    594 		rv = CKR_USER_NOT_LOGGED_IN;
    595 		goto finish;
    596 	}
    597 
    598 	rv = meta_get_slot_session(get_keystore_slotnum(), &logout_session,
    599 	    session->session_flags);
    600 	if (rv != CKR_OK)
    601 		goto finish;
    602 
    603 	rv = FUNCLIST(logout_session->fw_st_id)->C_Logout(
    604 	    logout_session->hSession);
    605 
    606 	/* If the C_Logout fails, just ignore the error. */
    607 	metaslot_set_logged_in_flag(B_FALSE);
    608 	(void) meta_token_object_deactivate(PRIVATE_TOKEN);
    609 
    610 finish:
    611 	if (logout_session)
    612 		meta_release_slot_session(logout_session);
    613 
    614 	REFRELEASE(session);
    615 
    616 	return (rv);
    617 }
    618