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
meta_OpenSession(CK_SLOT_ID slotID,CK_FLAGS flags,CK_VOID_PTR pApplication,CK_NOTIFY Notify,CK_SESSION_HANDLE_PTR phSession)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
meta_CloseSession(CK_SESSION_HANDLE hSession)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
meta_CloseAllSessions(CK_SLOT_ID slotID)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
meta_GetSessionInfo(CK_SESSION_HANDLE hSession,CK_SESSION_INFO_PTR pInfo)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
meta_getopstatelen(meta_session_t * session,CK_ULONG * out_length)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
meta_GetOperationState(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pOperationState,CK_ULONG_PTR pulOperationStateLen)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
meta_set_opstate(slot_session_t * slot_session,meta_object_t * meta_enc_key,meta_object_t * meta_auth_key,struct opstate_data * state,CK_BYTE * databuf)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
meta_SetOperationState(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pOperationState,CK_ULONG ulOperationStateLen,CK_OBJECT_HANDLE hEncryptionKey,CK_OBJECT_HANDLE hAuthenticationKey)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
meta_Login(CK_SESSION_HANDLE hSession,CK_USER_TYPE userType,CK_UTF8CHAR_PTR pPin,CK_ULONG ulPinLen)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
meta_Logout(CK_SESSION_HANDLE hSession)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