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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Session Management Functions
31  * (as defined in PKCS#11 spec spection 11.6)
32  */
33 
34 #include <string.h>
35 #include "metaGlobal.h"
36 
37 extern meta_session_t *meta_sessionlist_head;
38 extern pthread_rwlock_t meta_sessionlist_lock;
39 extern CK_ULONG num_meta_sessions;
40 extern CK_ULONG num_rw_meta_sessions;
41 
42 /*
43  * meta_OpenSession
44  *
45  * NOTES:
46  * 1) The pApplication and Notify args are not used, as the metaslot does not
47  *    support application callbacks.
48  * 2) the slotID argument is not checked or used because this function
49  *    is only called from the framework.
50  */
51 /* ARGSUSED */
52 CK_RV
53 meta_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication,
54     CK_NOTIFY Notify, CK_SESSION_HANDLE_PTR phSession)
55 {
56 	meta_session_t *new_session;
57 	CK_RV rv;
58 
59 	if (!metaslot_enabled) {
60 		return (CKR_SLOT_ID_INVALID);
61 	}
62 
63 	if (phSession == NULL) {
64 		return (CKR_ARGUMENTS_BAD);
65 	}
66 
67 	/* Check for any unknown flags. */
68 	if (flags & ~(CKF_SERIAL_SESSION | CKF_RW_SESSION)) {
69 		return (CKR_ARGUMENTS_BAD);
70 	}
71 
72 	if (!(flags & CKF_SERIAL_SESSION)) {
73 		return (CKR_SESSION_PARALLEL_NOT_SUPPORTED);
74 	}
75 
76 	if (meta_slotManager_token_write_protected() &&
77 	    (flags & CKF_RW_SESSION)) {
78 		return (CKR_TOKEN_WRITE_PROTECTED);
79 	}
80 
81 	rv = meta_session_alloc(&new_session);
82 	if (rv != CKR_OK)
83 		return (rv);
84 
85 	new_session->session_flags = flags;
86 
87 	rv = meta_session_activate(new_session);
88 	if (rv != CKR_OK) {
89 		meta_session_dealloc(new_session);
90 		return (rv);
91 	}
92 
93 	*phSession = (CK_SESSION_HANDLE) new_session;
94 
95 	num_meta_sessions++;
96 	if (flags & CKF_RW_SESSION) {
97 		num_rw_meta_sessions++;
98 	}
99 
100 	return (CKR_OK);
101 }
102 
103 
104 /*
105  * meta_CloseSession
106  *
107  */
108 CK_RV
109 meta_CloseSession(CK_SESSION_HANDLE hSession)
110 {
111 	CK_RV rv;
112 	meta_session_t *session;
113 	CK_FLAGS flags;
114 
115 	rv = meta_handle2session(hSession, &session);
116 	if (rv != CKR_OK)
117 		return (rv);
118 
119 	/* save info about session flags before they are destroyed */
120 	flags = session->session_flags;
121 
122 	rv = meta_session_deactivate(session, B_FALSE);
123 
124 	if (rv == CKR_OK)
125 		meta_session_dealloc(session);
126 
127 	num_meta_sessions--;
128 	if (flags & CKF_RW_SESSION) {
129 		num_rw_meta_sessions--;
130 	}
131 
132 	return (rv);
133 }
134 
135 
136 /*
137  * meta_CloseAllSessions
138  *
139  * This is a simple loop that closes the sessionlist head (resulting in a
140  * new list head) until the list is empty.
141  *
142  */
143 CK_RV
144 meta_CloseAllSessions(CK_SLOT_ID slotID)
145 {
146 	CK_RV rv;
147 	meta_session_t *session;
148 
149 	if (!metaslot_enabled) {
150 		return (CKR_SLOT_ID_INVALID);
151 	}
152 
153 	if (slotID != METASLOT_SLOTID)
154 		return (CKR_SLOT_ID_INVALID);
155 
156 	(void) pthread_rwlock_wrlock(&meta_sessionlist_lock);
157 	while ((session = meta_sessionlist_head) != NULL) {
158 		rv = meta_handle2session((CK_SESSION_HANDLE)session, &session);
159 		if (rv != CKR_OK) {
160 			/*NOTREACHED*/
161 			(void) pthread_rwlock_unlock(&meta_sessionlist_lock);
162 			return (CKR_FUNCTION_FAILED);
163 		}
164 
165 		(void) meta_session_deactivate(session, B_TRUE);
166 		meta_session_dealloc(session);
167 	}
168 	(void) pthread_rwlock_unlock(&meta_sessionlist_lock);
169 
170 	/* All open sessions should be closed, just reset the variables */
171 	num_meta_sessions = 0;
172 	num_rw_meta_sessions = 0;
173 
174 	return (CKR_OK);
175 }
176 
177 
178 /*
179  * meta_GetSessionInfo
180  *
181  */
182 CK_RV
183 meta_GetSessionInfo(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo)
184 {
185 	CK_RV rv;
186 	meta_session_t *session;
187 
188 	if (pInfo == NULL)
189 		return (CKR_ARGUMENTS_BAD);
190 
191 	rv = meta_handle2session(hSession, &session);
192 	if (rv != CKR_OK)
193 		return (rv);
194 
195 	pInfo->slotID = METASLOT_SLOTID;
196 	pInfo->flags = session->session_flags;
197 
198 	if (metaslot_logged_in()) {
199 		if (IS_READ_ONLY_SESSION(session->session_flags)) {
200 			pInfo->state = CKS_RO_USER_FUNCTIONS;
201 		} else {
202 			pInfo->state = CKS_RW_USER_FUNCTIONS;
203 		}
204 	} else {
205 		if (IS_READ_ONLY_SESSION(session->session_flags)) {
206 			pInfo->state = CKS_RO_PUBLIC_SESSION;
207 		} else {
208 			pInfo->state = CKS_RW_PUBLIC_SESSION;
209 		}
210 	}
211 
212 	pInfo->ulDeviceError = 0;
213 
214 	REFRELEASE(session);
215 
216 	return (CKR_OK);
217 }
218 
219 CK_RV
220 meta_getopstatelen(meta_session_t *session, CK_ULONG *out_length)
221 {
222 	CK_RV rv = CKR_OK;
223 	slot_session_t *slot_session;
224 	CK_ULONG length;
225 
226 	*out_length = sizeof (meta_opstate_t);
227 	if (session->op1.type != OP_UNUSED) {
228 		slot_session = session->op1.session;
229 		rv = FUNCLIST(slot_session->fw_st_id)->C_GetOperationState(
230 		    slot_session->hSession, NULL, &length);
231 		if (rv == CKR_OK)
232 			*out_length += length;
233 	}
234 	return (rv);
235 }
236 
237 /*
238  * meta_GetOperationState
239  *
240  */
241 CK_RV
242 meta_GetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState,
243     CK_ULONG_PTR pulOperationStateLen)
244 {
245 	CK_RV rv;
246 	meta_session_t *session;
247 	slot_session_t *slot_session = NULL;
248 	meta_opstate_t opstate;
249 
250 	if (pulOperationStateLen == NULL)
251 		return (CKR_ARGUMENTS_BAD);
252 
253 	rv = meta_handle2session(hSession, &session);
254 	if (rv != CKR_OK)
255 		return (rv);
256 
257 	/*
258 	 * If no operation is active, then bail out.
259 	 */
260 	if (session->op1.type == OP_UNUSED) {
261 		rv = CKR_OPERATION_NOT_INITIALIZED;
262 		goto endgetopstate;
263 	}
264 
265 	/*
266 	 * If the caller did not give an OpState buffer,
267 	 * shortcut and just return the size needed to hold
268 	 * a metaslot OpState record later.
269 	 * The actual size of the returned state will be the
270 	 * sizeof(meta_opstate_t) + SIZE (op1 state),
271 	 * so we have to get the size of
272 	 * the operation states now.
273 	 */
274 	if (pOperationState == NULL) {
275 		rv = meta_getopstatelen(session, pulOperationStateLen);
276 		REFRELEASE(session);
277 		return (rv);
278 	}
279 
280 	/*
281 	 * To be here, the caller must have supplied an
282 	 * already initialized meta_opstate_t pointer.
283 	 * Use it to get the real state info from the operation(s).
284 	 *
285 	 * The format of the Metaslot Opstate record:
286 	 * {
287 	 *    struct metaopstate
288 	 *    [ op1 state data ]
289 	 * }
290 	 */
291 
292 	/*
293 	 * If the buffer is not even big enough for the metaslot
294 	 * opstate data, return error and set the returned
295 	 * state length to indicate the minimum needed.
296 	 */
297 	if (*pulOperationStateLen < sizeof (meta_opstate_t)) {
298 		rv = meta_getopstatelen(session, pulOperationStateLen);
299 		/*
300 		 * Remap the error so the caller knows that they
301 		 * used an invalid buffer size in the first place.
302 		 */
303 		if (rv == CKR_OK)
304 			rv = CKR_BUFFER_TOO_SMALL;
305 		goto endgetopstate;
306 	}
307 
308 	(void) memset(&opstate, 0, sizeof (meta_opstate_t));
309 	opstate.magic_marker = METASLOT_OPSTATE_MAGIC;
310 
311 	if (session->op1.type != OP_UNUSED) {
312 		slot_session = session->op1.session;
313 		opstate.state[0].op_type = session->op1.type;
314 		opstate.state[0].op_slotnum = slot_session->slotnum;
315 		opstate.state[0].op_state_len = *pulOperationStateLen -
316 				sizeof (meta_opstate_t);
317 		rv = FUNCLIST(slot_session->fw_st_id)->C_GetOperationState(
318 		    slot_session->hSession,
319 		    pOperationState + sizeof (meta_opstate_t),
320 		    &(opstate.state[0].op_state_len));
321 
322 		if (rv == CKR_BUFFER_TOO_SMALL) {
323 			/*
324 			 * This should not happen, but if it does,
325 			 * recalculate the entire size needed
326 			 * and return the error.
327 			 */
328 			rv = meta_getopstatelen(session, pulOperationStateLen);
329 			if (rv == CKR_OK)
330 				rv = CKR_BUFFER_TOO_SMALL;
331 		}
332 
333 		if (rv != CKR_OK)
334 			goto endgetopstate;
335 	}
336 
337 endgetopstate:
338 	if (rv == CKR_OK && pOperationState != NULL) {
339 		(void) memcpy(pOperationState, (void *)&opstate,
340 			sizeof (meta_opstate_t));
341 
342 		*pulOperationStateLen = sizeof (meta_opstate_t) +
343 			opstate.state[0].op_state_len;
344 	}
345 
346 	REFRELEASE(session);
347 	return (rv);
348 }
349 
350 static CK_RV
351 meta_set_opstate(slot_session_t *slot_session,
352 		meta_object_t *meta_enc_key,
353 		meta_object_t *meta_auth_key,
354 		struct opstate_data *state,
355 		CK_BYTE *databuf)
356 {
357 	CK_RV rv;
358 	static CK_ULONG encrypt_optypes = (OP_ENCRYPT | OP_DECRYPT);
359 	static CK_ULONG sign_optypes = (OP_SIGN | OP_VERIFY |
360 			OP_SIGNRECOVER | OP_VERIFYRECOVER);
361 	slot_object_t *enc_key_obj = NULL, *auth_key_obj = NULL;
362 
363 	if (state->op_type & encrypt_optypes) {
364 		rv = meta_object_get_clone(meta_enc_key,
365 			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,
373 			slot_session->slotnum,
374 			slot_session, &auth_key_obj);
375 		if (rv != CKR_OK) {
376 			return (rv);
377 		}
378 	}
379 
380 	/*
381 	 * Check to see if the keys are needed to restore the
382 	 * state on the first operation.
383 	 */
384 	rv = FUNCLIST(slot_session->fw_st_id)->C_SetOperationState(
385 	    slot_session->hSession, databuf, state->op_state_len,
386 	    enc_key_obj ? enc_key_obj->hObject : CK_INVALID_HANDLE,
387 	    auth_key_obj ? auth_key_obj->hObject : CK_INVALID_HANDLE);
388 	/*
389 	 * If the operation did not need a key, try again.
390 	 */
391 	if (rv == CKR_KEY_NOT_NEEDED) {
392 		rv = FUNCLIST(slot_session->fw_st_id)->C_SetOperationState(
393 		    slot_session->hSession, databuf, state->op_state_len,
394 		    CK_INVALID_HANDLE, CK_INVALID_HANDLE);
395 		/*
396 		 * Strange case... If the first try returned
397 		 * KEY_NOT_NEEDED, and this one returns KEY_NEEDED,
398 		 * we want to remap the return so the caller sees
399 		 * the original "CKR_KEY_NOT_NEEDED" return value.
400 		 * This ensures that a correct caller will retry
401 		 * without the unnecessary key argument and this
402 		 * 2nd attempt will not happen again.
403 		 */
404 		if (rv == CKR_KEY_NEEDED) {
405 			rv  = CKR_KEY_NOT_NEEDED;
406 		}
407 	}
408 
409 	return (rv);
410 }
411 
412 /*
413  * meta_SetOperationState
414  *
415  */
416 CK_RV
417 meta_SetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState,
418     CK_ULONG ulOperationStateLen, CK_OBJECT_HANDLE hEncryptionKey,
419     CK_OBJECT_HANDLE hAuthenticationKey)
420 {
421 	CK_RV rv = CKR_OK;
422 	meta_session_t *session;
423 	slot_session_t *slot_session = NULL;
424 	meta_opstate_t opstate;
425 	meta_object_t *meta_enc_key = NULL, *meta_auth_key = NULL;
426 
427 	/*
428 	 * Make sure the opstate info buffer is big enough to be valid.
429 	 */
430 	if (ulOperationStateLen < sizeof (meta_opstate_t) ||
431 	    pOperationState == NULL)
432 		return (CKR_ARGUMENTS_BAD);
433 
434 	/* Copy the opstate info into the structure */
435 	(void) memcpy(&opstate, pOperationState, sizeof (meta_opstate_t));
436 
437 	/* verify that a metaslot operation state was supplied */
438 	if (opstate.magic_marker != METASLOT_OPSTATE_MAGIC)
439 		return (CKR_SAVED_STATE_INVALID);
440 
441 	/*
442 	 * Now, check the size again to make sure the "real" state
443 	 * data is present.  Length of state provided must be exact.
444 	 */
445 	if (ulOperationStateLen != (sizeof (meta_opstate_t) +
446 		opstate.state[0].op_state_len))
447 		return (CKR_SAVED_STATE_INVALID);
448 
449 	rv = meta_handle2session(hSession, &session);
450 	if (rv != CKR_OK)
451 		return (rv);
452 
453 	if (hEncryptionKey != CK_INVALID_HANDLE) {
454 		rv = meta_handle2object(hEncryptionKey, &meta_enc_key);
455 		if (rv != CKR_OK)
456 			goto cleanup;
457 	}
458 	if (hAuthenticationKey != CK_INVALID_HANDLE) {
459 		rv = meta_handle2object(hAuthenticationKey, &meta_auth_key);
460 		if (rv != CKR_OK)
461 			goto cleanup;
462 	}
463 
464 	if (opstate.state[0].op_type != OP_UNUSED) {
465 		if (session->op1.type != OP_UNUSED)
466 			meta_operation_cleanup(session, session->op1.type,
467 				B_FALSE);
468 
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 		session->op1.type = opstate.state[0].op_type;
475 		session->op1.session = slot_session;
476 
477 		rv = meta_set_opstate(slot_session,
478 			meta_enc_key, meta_auth_key,
479 			&(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