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