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