xref: /illumos-gate/usr/src/lib/pkcs11/pkcs11_kernel/common/kernelSession.c (revision 1f5207b7604fb44407eb4342aff613f7c4508508)
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 #include <pthread.h>
27 #include <errno.h>
28 #include <security/cryptoki.h>
29 #include <sys/crypto/ioctl.h>
30 #include "kernelGlobal.h"
31 #include "kernelSession.h"
32 #include "kernelSlot.h"
33 #include "kernelEmulate.h"
34 
35 CK_RV
36 C_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication,
37     CK_NOTIFY Notify, CK_SESSION_HANDLE_PTR phSession)
38 {
39 	CK_RV rv = CKR_OK;
40 	kernel_slot_t	*pslot;
41 
42 	if (!kernel_initialized)
43 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
44 
45 	/*
46 	 * For legacy reasons, the CKF_SERIAL_SESSION bit must always
47 	 * be set.
48 	 */
49 	if (!(flags & CKF_SERIAL_SESSION))
50 		return (CKR_SESSION_PARALLEL_NOT_SUPPORTED);
51 
52 	if (phSession == NULL)
53 		return (CKR_ARGUMENTS_BAD);
54 
55 	if (slotID >= slot_count) {
56 		return (CKR_SLOT_ID_INVALID);
57 	}
58 
59 	/*
60 	 * Acquire the slot lock to protect sl_state and sl_sess_list.
61 	 * These two fields need to be protected atomically, even though
62 	 * "sl_sess_list" is updated in kernel_add_session().
63 	 */
64 	pslot = slot_table[slotID];
65 	(void) pthread_mutex_lock(&pslot->sl_mutex);
66 
67 	/* If SO is logged in the slot, only the RW session is allowed. */
68 	if ((pslot->sl_state == CKU_SO) && !(flags & CKF_RW_SESSION)) {
69 		(void) pthread_mutex_unlock(&pslot->sl_mutex);
70 		return (CKR_SESSION_READ_WRITE_SO_EXISTS);
71 	}
72 
73 	/* Create a new session */
74 	rv = kernel_add_session(slotID, flags, pApplication, Notify,
75 	    phSession);
76 	(void) pthread_mutex_unlock(&pslot->sl_mutex);
77 	return (rv);
78 }
79 
80 CK_RV
81 C_CloseSession(CK_SESSION_HANDLE hSession)
82 {
83 	CK_RV rv;
84 
85 	kernel_session_t *session_p;
86 	boolean_t ses_lock_held = B_FALSE;
87 
88 	if (!kernel_initialized)
89 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
90 
91 	/*
92 	 * Obtain the session pointer. Also, increment the session
93 	 * reference count.
94 	 */
95 	rv = handle2session(hSession, &session_p);
96 	if (rv != CKR_OK)
97 		return (rv);
98 
99 	(void) pthread_mutex_lock(&session_p->session_mutex);
100 	ses_lock_held = B_TRUE;
101 
102 	/*
103 	 * Set SESSION_IS_CLOSING flag so any access to this
104 	 * session will be rejected.
105 	 */
106 	if (session_p->ses_close_sync & SESSION_IS_CLOSING) {
107 		REFRELE(session_p, ses_lock_held);
108 		return (CKR_SESSION_CLOSED);
109 	}
110 	session_p->ses_close_sync |= SESSION_IS_CLOSING;
111 
112 	/*
113 	 * Decrement the session reference count.
114 	 * We hold the session lock, and REFRELE()
115 	 * will release the session lock for us.
116 	 */
117 	REFRELE(session_p, ses_lock_held);
118 
119 	/*
120 	 * Delete a session by calling kernel_delete_session() with
121 	 * a session pointer and two boolean arguments. The 3rd argument
122 	 * boolean value FALSE indicates that the caller does not
123 	 * hold the slot lock.  The 4th argument boolean value B_FALSE
124 	 * indicates that we want to delete all the objects completely.
125 	 *
126 	 * kernel_delete_session() will reset SESSION_IS_CLOSING
127 	 * flag after it is done.
128 	 */
129 	kernel_delete_session(session_p->ses_slotid, session_p, B_FALSE,
130 	    B_FALSE);
131 	return (rv);
132 }
133 
134 
135 CK_RV
136 C_CloseAllSessions(CK_SLOT_ID slotID)
137 {
138 	if (!kernel_initialized)
139 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
140 
141 	/* Delete all the sessions and release the allocated resources */
142 	kernel_delete_all_sessions(slotID, B_FALSE);
143 
144 	return (CKR_OK);
145 }
146 
147 /*
148  * Utility routine to get CK_STATE value for a session.
149  * The caller should not be holding the session lock.
150  */
151 static CK_STATE
152 get_ses_state(kernel_session_t *session_p)
153 {
154 	CK_STATE state;
155 	kernel_slot_t *pslot;
156 
157 	pslot = slot_table[session_p->ses_slotid];
158 	(void) pthread_mutex_lock(&pslot->sl_mutex);
159 
160 	if (pslot->sl_state == CKU_PUBLIC) {
161 		state = (session_p->ses_RO) ?
162 		    CKS_RO_PUBLIC_SESSION : CKS_RW_PUBLIC_SESSION;
163 	} else if (pslot->sl_state == CKU_USER) {
164 		state = (session_p->ses_RO) ?
165 		    CKS_RO_USER_FUNCTIONS : CKS_RW_USER_FUNCTIONS;
166 	} else if (pslot->sl_state == CKU_SO) {
167 		state = CKS_RW_SO_FUNCTIONS;
168 	}
169 
170 	(void) pthread_mutex_unlock(&pslot->sl_mutex);
171 
172 	return (state);
173 }
174 
175 
176 CK_RV
177 C_GetSessionInfo(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo)
178 {
179 	kernel_session_t *session_p;
180 	CK_RV rv;
181 	boolean_t ses_lock_held = B_FALSE;
182 
183 	if (!kernel_initialized)
184 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
185 
186 	if (pInfo == NULL)
187 		return (CKR_ARGUMENTS_BAD);
188 
189 	/*
190 	 * Obtain the session pointer. Also, increment the session
191 	 * reference count.
192 	 */
193 	rv = handle2session(hSession, &session_p);
194 	if (rv != CKR_OK)
195 		return (rv);
196 
197 	/* Provide information for the specified session */
198 	pInfo->slotID = session_p->ses_slotid;
199 	pInfo->flags = session_p->flags;
200 	pInfo->ulDeviceError = 0;
201 	pInfo->state = get_ses_state(session_p);
202 
203 	/*
204 	 * Decrement the session reference count.
205 	 */
206 	REFRELE(session_p, ses_lock_held);
207 
208 	return (CKR_OK);
209 }
210 
211 /*
212  * Save the state in pOperationState. The data format is:
213  * 1. Total length (including this field)
214  * 2. session state
215  * 3. crypto_active_op_t structure
216  * 4. digest_buf_t's data buffer contents
217  */
218 static CK_RV
219 kernel_get_operationstate(kernel_session_t *session_p, CK_STATE ses_state,
220     CK_BYTE_PTR pOperationState, CK_ULONG_PTR pulOperationStateLen)
221 {
222 	int op_data_len = 0;
223 	CK_BYTE_PTR dst;
224 	digest_buf_t *bufp;
225 
226 	if (!(session_p->digest.flags & CRYPTO_EMULATE)) {
227 		/*
228 		 * Return CKR_OPERATION_NOT_INITIALIZED if the slot
229 		 * is capable of C_GetOperationState(). Return
230 		 * CKR_FUNCTION_NOT_SUPPORTED otherwise.
231 		 *
232 		 * We return these codes because some clients
233 		 * check the return code to determine if C_GetOperationState()
234 		 * is supported.
235 		 */
236 		if (slot_table[session_p->ses_slotid]->sl_flags &
237 		    CRYPTO_LIMITED_HASH_SUPPORT)
238 			return (CKR_OPERATION_NOT_INITIALIZED);
239 		else
240 			return (CKR_FUNCTION_NOT_SUPPORTED);
241 	}
242 
243 	/*
244 	 * XXX Need to support this case in future.
245 	 * This is the case where we exceeded SLOT_HASH_MAX_INDATA_LEN and
246 	 * hence started using libmd. SLOT_HASH_MAX_INDATA_LEN is at least
247 	 * 64K for current crypto framework providers and web servers
248 	 * do not need to clone digests that big for SSL operations.
249 	 */
250 	if (session_p->digest.flags & CRYPTO_EMULATE_USING_SW) {
251 		return (CKR_STATE_UNSAVEABLE);
252 	}
253 
254 	/* Check to see if this is an unsupported operation. */
255 	if (session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE ||
256 	    session_p->decrypt.flags & CRYPTO_OPERATION_ACTIVE ||
257 	    session_p->sign.flags & CRYPTO_OPERATION_ACTIVE ||
258 	    session_p->verify.flags & CRYPTO_OPERATION_ACTIVE) {
259 		return (CKR_STATE_UNSAVEABLE);
260 	}
261 
262 	/* Check to see if digest operation is active. */
263 	if (!(session_p->digest.flags & CRYPTO_OPERATION_ACTIVE)) {
264 		return (CKR_OPERATION_NOT_INITIALIZED);
265 	}
266 
267 	bufp = session_p->digest.context;
268 
269 	op_data_len =  sizeof (int);
270 	op_data_len +=  sizeof (CK_STATE);
271 	op_data_len += sizeof (crypto_active_op_t);
272 	op_data_len += bufp->indata_len;
273 
274 	if (pOperationState == NULL_PTR) {
275 		*pulOperationStateLen = op_data_len;
276 		return (CKR_OK);
277 	} else {
278 		if (*pulOperationStateLen < op_data_len) {
279 			*pulOperationStateLen = op_data_len;
280 			return (CKR_BUFFER_TOO_SMALL);
281 		}
282 	}
283 
284 	dst = pOperationState;
285 
286 	/* Save total length */
287 	bcopy(&op_data_len, dst, sizeof (int));
288 	dst += sizeof (int);
289 
290 	/* Save session state */
291 	bcopy(&ses_state, dst, sizeof (CK_STATE));
292 	dst += sizeof (CK_STATE);
293 
294 	/* Save crypto_active_op_t */
295 	bcopy(&session_p->digest, dst, sizeof (crypto_active_op_t));
296 	dst += sizeof (crypto_active_op_t);
297 
298 	/* Save the data buffer */
299 	bcopy(bufp->buf, dst, bufp->indata_len);
300 
301 	*pulOperationStateLen = op_data_len;
302 	return (CKR_OK);
303 }
304 
305 CK_RV
306 C_GetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState,
307     CK_ULONG_PTR pulOperationStateLen)
308 {
309 	CK_RV rv;
310 	CK_STATE ses_state;
311 	kernel_session_t *session_p;
312 	boolean_t ses_lock_held = B_TRUE;
313 
314 	if (!kernel_initialized)
315 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
316 
317 	if (pulOperationStateLen == NULL_PTR)
318 		return (CKR_ARGUMENTS_BAD);
319 
320 	/*
321 	 * Obtain the session pointer. Also, increment the session
322 	 * reference count.
323 	 */
324 	rv = handle2session(hSession, &session_p);
325 	if (rv != CKR_OK)
326 		return (rv);
327 
328 	ses_state = get_ses_state(session_p);
329 
330 	(void) pthread_mutex_lock(&session_p->session_mutex);
331 	rv = kernel_get_operationstate(session_p, ses_state,
332 	    pOperationState, pulOperationStateLen);
333 
334 	REFRELE(session_p, ses_lock_held);
335 	return (rv);
336 }
337 
338 /*
339  * Restore the state from pOperationState. The data format is:
340  * 1. Total length (including this field)
341  * 2. session state
342  * 3. crypto_active_op_t structure
343  * 4. digest_buf_t's data buffer contents
344  */
345 static CK_RV
346 kernel_set_operationstate(kernel_session_t *session_p, CK_STATE ses_state,
347     CK_BYTE_PTR pOperationState, CK_ULONG ulOperationStateLen,
348     CK_OBJECT_HANDLE hEncryptionKey, CK_OBJECT_HANDLE hAuthenticationKey)
349 {
350 	CK_RV rv;
351 	CK_BYTE_PTR src;
352 	CK_STATE src_ses_state;
353 	int expected_len, indata_len;
354 	digest_buf_t *bufp;
355 	crypto_active_op_t tmp_op;
356 
357 	if ((hAuthenticationKey != 0) || (hEncryptionKey != 0))
358 		return (CKR_KEY_NOT_NEEDED);
359 
360 	src = pOperationState;
361 
362 	/* Get total length field */
363 	bcopy(src, &expected_len, sizeof (int));
364 	if (ulOperationStateLen < expected_len)
365 		return (CKR_SAVED_STATE_INVALID);
366 
367 	/* compute the data buffer length */
368 	indata_len = expected_len - sizeof (int) -
369 	    sizeof (CK_STATE) - sizeof (crypto_active_op_t);
370 	if (indata_len > SLOT_HASH_MAX_INDATA_LEN(session_p))
371 		return (CKR_SAVED_STATE_INVALID);
372 	src += sizeof (int);
373 
374 	/* Get session state */
375 	bcopy(src, &src_ses_state, sizeof (CK_STATE));
376 	if (ses_state != src_ses_state)
377 		return (CKR_SAVED_STATE_INVALID);
378 	src += sizeof (CK_STATE);
379 
380 	/*
381 	 * Restore crypto_active_op_t. We need to use a temporary
382 	 * buffer to avoid modifying the source session's buffer.
383 	 */
384 	bcopy(src, &tmp_op, sizeof (crypto_active_op_t));
385 	if (tmp_op.flags & CRYPTO_EMULATE_USING_SW)
386 		return (CKR_SAVED_STATE_INVALID);
387 	session_p->digest.mech = tmp_op.mech;
388 	session_p->digest.flags = tmp_op.flags;
389 	src += sizeof (crypto_active_op_t);
390 
391 	/* This routine reuses the session's existing buffer if possible */
392 	rv = emulate_buf_init(session_p, indata_len, OP_DIGEST);
393 	if (rv != CKR_OK)
394 		return (rv);
395 	bufp = session_p->digest.context;
396 	bufp->indata_len = indata_len;
397 
398 	/* Restore the data buffer */
399 	bcopy(src, bufp->buf, bufp->indata_len);
400 
401 	return (CKR_OK);
402 }
403 
404 
405 CK_RV
406 C_SetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState,
407     CK_ULONG ulOperationStateLen, CK_OBJECT_HANDLE hEncryptionKey,
408     CK_OBJECT_HANDLE hAuthenticationKey)
409 {
410 	CK_RV rv;
411 	CK_STATE ses_state;
412 	kernel_session_t *session_p;
413 	boolean_t ses_lock_held = B_TRUE;
414 
415 	if (!kernel_initialized)
416 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
417 
418 	if ((pOperationState == NULL_PTR) ||
419 	    (ulOperationStateLen == 0))
420 		return (CKR_ARGUMENTS_BAD);
421 
422 	rv = handle2session(hSession, &session_p);
423 	if (rv != CKR_OK)
424 		return (rv);
425 
426 	ses_state = get_ses_state(session_p);
427 
428 	(void) pthread_mutex_lock(&session_p->session_mutex);
429 
430 	rv = kernel_set_operationstate(session_p, ses_state,
431 	    pOperationState, ulOperationStateLen,
432 	    hEncryptionKey, hAuthenticationKey);
433 
434 	REFRELE(session_p, ses_lock_held);
435 	return (rv);
436 }
437 
438 
439 CK_RV
440 C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType,
441     CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen)
442 {
443 	CK_RV	rv = CKR_OK;
444 	kernel_session_t *session_p;
445 	kernel_slot_t	*pslot;
446 	boolean_t ses_lock_held = B_FALSE;
447 	crypto_login_t  c_login;
448 	int r;
449 
450 	if (!kernel_initialized)
451 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
452 
453 	if ((userType != CKU_SO) && (userType != CKU_USER)) {
454 		return (CKR_USER_TYPE_INVALID);
455 	}
456 
457 	/*
458 	 * Obtain the session pointer. Also, increment the session
459 	 * reference count.
460 	 */
461 	rv = handle2session(hSession, &session_p);
462 	if (rv != CKR_OK)
463 		return (rv);
464 
465 	/* Acquire the slot lock */
466 	pslot = slot_table[session_p->ses_slotid];
467 	(void) pthread_mutex_lock(&pslot->sl_mutex);
468 
469 	/* Check if the slot is logged in already */
470 	if ((pslot->sl_state == CKU_USER) || (pslot->sl_state == CKU_SO)) {
471 		rv = CKR_USER_ALREADY_LOGGED_IN;
472 		goto clean_exit;
473 	}
474 
475 	/* To login as SO, every session in this slot needs to be R/W */
476 	if (userType == CKU_SO) {
477 		kernel_session_t  *sp;
478 		boolean_t	found;
479 
480 		found = B_FALSE;
481 		sp = pslot->sl_sess_list;
482 		while (sp) {
483 			/*
484 			 * Need not to lock individual sessions before
485 			 * accessing their "ses_RO" and "next" fields,
486 			 * because they are always accessed under the
487 			 * slot's mutex protection.
488 			 */
489 			if (sp->ses_RO) {
490 				found = B_TRUE;
491 				break;
492 			}
493 			sp = sp->next;
494 		}
495 
496 		if (found) {
497 			rv = CKR_SESSION_READ_ONLY_EXISTS;
498 			goto clean_exit;
499 		}
500 	}
501 
502 	/* Now make the ioctl call; no need to acquire the session lock. */
503 	c_login.co_session = session_p->k_session;
504 	c_login.co_user_type = userType;
505 	c_login.co_pin_len = ulPinLen;
506 	c_login.co_pin = (char *)pPin;
507 
508 	while ((r = ioctl(kernel_fd, CRYPTO_LOGIN, &c_login)) < 0) {
509 		if (errno != EINTR)
510 			break;
511 	}
512 	if (r < 0) {
513 		rv = CKR_FUNCTION_FAILED;
514 	} else {
515 		rv = crypto2pkcs11_error_number(c_login.co_return_value);
516 	}
517 
518 	if (rv == CKR_OK) {
519 		/* Set the slot's session state. */
520 		pslot->sl_state = userType;
521 	}
522 
523 clean_exit:
524 
525 	REFRELE(session_p, ses_lock_held);
526 	(void) pthread_mutex_unlock(&pslot->sl_mutex);
527 	return (rv);
528 }
529 
530 
531 CK_RV
532 C_Logout(CK_SESSION_HANDLE hSession)
533 {
534 	CK_RV	rv = CKR_OK;
535 	kernel_session_t *session_p;
536 	kernel_slot_t	*pslot;
537 	boolean_t ses_lock_held = B_FALSE;
538 	crypto_logout_t  c_logout;
539 	int r;
540 
541 	if (!kernel_initialized)
542 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
543 
544 	/*
545 	 * Obtain the session pointer. Also, increment the session
546 	 * reference count.
547 	 */
548 	rv = handle2session(hSession, &session_p);
549 	if (rv != CKR_OK)
550 		return (rv);
551 
552 	/* Acquire the slot lock. */
553 	pslot = slot_table[session_p->ses_slotid];
554 	(void) pthread_mutex_lock(&pslot->sl_mutex);
555 
556 	/* Check if the user or SO was logged in  */
557 	if (pslot->sl_state == CKU_PUBLIC) {
558 		rv = CKR_USER_NOT_LOGGED_IN;
559 		goto clean_exit;
560 	}
561 
562 	/* Now make the ioctl call. No need to acquire the session lock. */
563 	c_logout.cl_session = session_p->k_session;
564 	while ((r = ioctl(kernel_fd, CRYPTO_LOGOUT, &c_logout)) < 0) {
565 		if (errno != EINTR)
566 			break;
567 	}
568 	if (r < 0) {
569 		rv = CKR_FUNCTION_FAILED;
570 	} else {
571 		rv = crypto2pkcs11_error_number(c_logout.cl_return_value);
572 	}
573 
574 	if (rv != CKR_OK) {
575 		goto clean_exit;
576 	}
577 
578 	/*
579 	 * If this slot was logged in as USER previously, we need to clean up
580 	 * all private object wrappers in library for this slot.
581 	 */
582 	kernel_cleanup_pri_objects_in_slot(pslot, session_p);
583 
584 	if (rv == CKR_OK) {
585 		/* Reset the slot's session state. */
586 		pslot->sl_state = CKU_PUBLIC;
587 	}
588 
589 clean_exit:
590 	REFRELE(session_p, ses_lock_held);
591 	(void) pthread_mutex_unlock(&pslot->sl_mutex);
592 	return (rv);
593 }
594