xref: /illumos-gate/usr/src/lib/pkcs11/pkcs11_kernel/common/kernelSession.c (revision bea83d026ee1bd1b2a2419e1d0232f107a5d7d9b)
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 #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 	/*
229 	 * We return CKR_FUNCTION_NOT_SUPPORTED because some clients
230 	 * check for this code and work around the lack of this routine.
231 	 */
232 	if (!(session_p->digest.flags & CRYPTO_EMULATE)) {
233 		return (CKR_FUNCTION_NOT_SUPPORTED);
234 	}
235 
236 	/*
237 	 * XXX Need to support this case in future.
238 	 * This is the case where we exceeded SLOT_MAX_INDATA_LEN and
239 	 * hence started using libmd. SLOT_MAX_INDATA_LEN is at least
240 	 * 64K for current crypto framework providers and web servers
241 	 * do not need to clone digests that big for SSL operations.
242 	 */
243 	if (session_p->digest.flags & CRYPTO_EMULATE_USING_SW) {
244 		return (CKR_STATE_UNSAVEABLE);
245 	}
246 
247 	/* Check to see if this is an unsupported operation. */
248 	if (session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE ||
249 	    session_p->decrypt.flags & CRYPTO_OPERATION_ACTIVE ||
250 	    session_p->sign.flags & CRYPTO_OPERATION_ACTIVE ||
251 	    session_p->verify.flags & CRYPTO_OPERATION_ACTIVE) {
252 		return (CKR_STATE_UNSAVEABLE);
253 	}
254 
255 	/* Check to see if digest operation is active. */
256 	if (!(session_p->digest.flags & CRYPTO_OPERATION_ACTIVE)) {
257 		return (CKR_OPERATION_NOT_INITIALIZED);
258 	}
259 
260 	bufp = session_p->digest.context;
261 
262 	op_data_len =  sizeof (int);
263 	op_data_len +=  sizeof (CK_STATE);
264 	op_data_len += sizeof (crypto_active_op_t);
265 	op_data_len += bufp->indata_len;
266 
267 	if (pOperationState == NULL_PTR) {
268 		*pulOperationStateLen = op_data_len;
269 		return (CKR_OK);
270 	} else {
271 		if (*pulOperationStateLen < op_data_len) {
272 			*pulOperationStateLen = op_data_len;
273 			return (CKR_BUFFER_TOO_SMALL);
274 		}
275 	}
276 
277 	dst = pOperationState;
278 
279 	/* Save total length */
280 	bcopy(&op_data_len, dst, sizeof (int));
281 	dst += sizeof (int);
282 
283 	/* Save session state */
284 	bcopy(&ses_state, dst, sizeof (CK_STATE));
285 	dst += sizeof (CK_STATE);
286 
287 	/* Save crypto_active_op_t */
288 	bcopy(&session_p->digest, dst, sizeof (crypto_active_op_t));
289 	dst += sizeof (crypto_active_op_t);
290 
291 	/* Save the data buffer */
292 	bcopy(bufp->buf, dst, bufp->indata_len);
293 
294 	*pulOperationStateLen = op_data_len;
295 	return (CKR_OK);
296 }
297 
298 CK_RV
299 C_GetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState,
300     CK_ULONG_PTR pulOperationStateLen)
301 {
302 	CK_RV rv;
303 	CK_STATE ses_state;
304 	kernel_session_t *session_p;
305 	boolean_t ses_lock_held = B_TRUE;
306 
307 	if (!kernel_initialized)
308 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
309 
310 	if (pulOperationStateLen == NULL_PTR)
311 		return (CKR_ARGUMENTS_BAD);
312 
313 	/*
314 	 * Obtain the session pointer. Also, increment the session
315 	 * reference count.
316 	 */
317 	rv = handle2session(hSession, &session_p);
318 	if (rv != CKR_OK)
319 		return (rv);
320 
321 	ses_state = get_ses_state(session_p);
322 
323 	(void) pthread_mutex_lock(&session_p->session_mutex);
324 	rv = kernel_get_operationstate(session_p, ses_state,
325 	    pOperationState, pulOperationStateLen);
326 
327 	REFRELE(session_p, ses_lock_held);
328 	return (rv);
329 }
330 
331 /*
332  * Restore the state from pOperationState. The data format is:
333  * 1. Total length (including this field)
334  * 2. session state
335  * 3. crypto_active_op_t structure
336  * 4. digest_buf_t's data buffer contents
337  */
338 static CK_RV
339 kernel_set_operationstate(kernel_session_t *session_p, CK_STATE ses_state,
340     CK_BYTE_PTR pOperationState, CK_ULONG ulOperationStateLen,
341     CK_OBJECT_HANDLE hEncryptionKey, CK_OBJECT_HANDLE hAuthenticationKey)
342 {
343 	CK_RV rv;
344 	CK_BYTE_PTR src;
345 	CK_STATE src_ses_state;
346 	int expected_len, indata_len;
347 	digest_buf_t *bufp;
348 	crypto_active_op_t tmp_op;
349 
350 	if ((hAuthenticationKey != 0) || (hEncryptionKey != 0))
351 		return (CKR_KEY_NOT_NEEDED);
352 
353 	src = pOperationState;
354 
355 	/* Get total length field */
356 	bcopy(src, &expected_len, sizeof (int));
357 	if (ulOperationStateLen < expected_len)
358 		return (CKR_SAVED_STATE_INVALID);
359 
360 	/* compute the data buffer length */
361 	indata_len = expected_len - sizeof (int) -
362 	    sizeof (CK_STATE) - sizeof (crypto_active_op_t);
363 	if (indata_len > SLOT_MAX_INDATA_LEN(session_p))
364 		return (CKR_SAVED_STATE_INVALID);
365 	src += sizeof (int);
366 
367 	/* Get session state */
368 	bcopy(src, &src_ses_state, sizeof (CK_STATE));
369 	if (ses_state != src_ses_state)
370 		return (CKR_SAVED_STATE_INVALID);
371 	src += sizeof (CK_STATE);
372 
373 	/*
374 	 * Restore crypto_active_op_t. We need to use a temporary
375 	 * buffer to avoid modifying the source session's buffer.
376 	 */
377 	bcopy(src, &tmp_op, sizeof (crypto_active_op_t));
378 	if (tmp_op.flags & CRYPTO_EMULATE_USING_SW)
379 		return (CKR_SAVED_STATE_INVALID);
380 	session_p->digest.mech = tmp_op.mech;
381 	session_p->digest.flags = tmp_op.flags;
382 	src += sizeof (crypto_active_op_t);
383 
384 	/* This routine reuses the session's existing buffer if possible */
385 	rv = emulate_buf_init(session_p, indata_len, OP_DIGEST);
386 	if (rv != CKR_OK)
387 		return (rv);
388 	bufp = session_p->digest.context;
389 	bufp->indata_len = indata_len;
390 
391 	/* Restore the data buffer */
392 	bcopy(src, bufp->buf, bufp->indata_len);
393 
394 	return (CKR_OK);
395 }
396 
397 
398 CK_RV
399 C_SetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState,
400     CK_ULONG ulOperationStateLen, CK_OBJECT_HANDLE hEncryptionKey,
401     CK_OBJECT_HANDLE hAuthenticationKey)
402 {
403 	CK_RV rv;
404 	CK_STATE ses_state;
405 	kernel_session_t *session_p;
406 	boolean_t ses_lock_held = B_TRUE;
407 
408 	if (!kernel_initialized)
409 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
410 
411 	if ((pOperationState == NULL_PTR) ||
412 	    (ulOperationStateLen == 0))
413 		return (CKR_ARGUMENTS_BAD);
414 
415 	rv = handle2session(hSession, &session_p);
416 	if (rv != CKR_OK)
417 		return (rv);
418 
419 	ses_state = get_ses_state(session_p);
420 
421 	(void) pthread_mutex_lock(&session_p->session_mutex);
422 
423 	rv = kernel_set_operationstate(session_p, ses_state,
424 	    pOperationState, ulOperationStateLen,
425 	    hEncryptionKey, hAuthenticationKey);
426 
427 	REFRELE(session_p, ses_lock_held);
428 	return (rv);
429 }
430 
431 
432 CK_RV
433 C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType,
434     CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen)
435 {
436 	CK_RV	rv = CKR_OK;
437 	kernel_session_t *session_p;
438 	kernel_slot_t	*pslot;
439 	boolean_t ses_lock_held = B_FALSE;
440 	crypto_login_t  c_login;
441 	int r;
442 
443 	if (!kernel_initialized)
444 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
445 
446 	if ((userType != CKU_SO) && (userType != CKU_USER)) {
447 		return (CKR_USER_TYPE_INVALID);
448 	}
449 
450 	/*
451 	 * Obtain the session pointer. Also, increment the session
452 	 * reference count.
453 	 */
454 	rv = handle2session(hSession, &session_p);
455 	if (rv != CKR_OK)
456 		return (rv);
457 
458 	/* Acquire the slot lock */
459 	pslot = slot_table[session_p->ses_slotid];
460 	(void) pthread_mutex_lock(&pslot->sl_mutex);
461 
462 	/* Check if the slot is logged in already */
463 	if ((pslot->sl_state == CKU_USER) || (pslot->sl_state == CKU_SO)) {
464 		rv = CKR_USER_ALREADY_LOGGED_IN;
465 		goto clean_exit;
466 	}
467 
468 	/* To login as SO, every session in this slot needs to be R/W */
469 	if (userType == CKU_SO) {
470 		kernel_session_t  *sp;
471 		boolean_t	found;
472 
473 		found = B_FALSE;
474 		sp = pslot->sl_sess_list;
475 		while (sp) {
476 			/*
477 			 * Need not to lock individual sessions before
478 			 * accessing their "ses_RO" and "next" fields,
479 			 * because they are always accessed under the
480 			 * slot's mutex protection.
481 			 */
482 			if (sp->ses_RO) {
483 				found = B_TRUE;
484 				break;
485 			}
486 			sp = sp->next;
487 		}
488 
489 		if (found) {
490 			rv = CKR_SESSION_READ_ONLY_EXISTS;
491 			goto clean_exit;
492 		}
493 	}
494 
495 	/* Now make the ioctl call; no need to acquire the session lock. */
496 	c_login.co_session = session_p->k_session;
497 	c_login.co_user_type = userType;
498 	c_login.co_pin_len = ulPinLen;
499 	c_login.co_pin = (char *)pPin;
500 
501 	while ((r = ioctl(kernel_fd, CRYPTO_LOGIN, &c_login)) < 0) {
502 		if (errno != EINTR)
503 			break;
504 	}
505 	if (r < 0) {
506 		rv = CKR_FUNCTION_FAILED;
507 	} else {
508 		rv = crypto2pkcs11_error_number(c_login.co_return_value);
509 	}
510 
511 	if (rv == CKR_OK) {
512 		/* Set the slot's session state. */
513 		pslot->sl_state = userType;
514 	}
515 
516 clean_exit:
517 
518 	REFRELE(session_p, ses_lock_held);
519 	(void) pthread_mutex_unlock(&pslot->sl_mutex);
520 	return (rv);
521 }
522 
523 
524 CK_RV
525 C_Logout(CK_SESSION_HANDLE hSession)
526 {
527 	CK_RV	rv = CKR_OK;
528 	kernel_session_t *session_p;
529 	kernel_slot_t	*pslot;
530 	boolean_t ses_lock_held = B_FALSE;
531 	crypto_logout_t  c_logout;
532 	int r;
533 
534 	if (!kernel_initialized)
535 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
536 
537 	/*
538 	 * Obtain the session pointer. Also, increment the session
539 	 * reference count.
540 	 */
541 	rv = handle2session(hSession, &session_p);
542 	if (rv != CKR_OK)
543 		return (rv);
544 
545 	/* Acquire the slot lock. */
546 	pslot = slot_table[session_p->ses_slotid];
547 	(void) pthread_mutex_lock(&pslot->sl_mutex);
548 
549 	/* Check if the user or SO was logged in  */
550 	if (pslot->sl_state == CKU_PUBLIC) {
551 		rv = CKR_USER_NOT_LOGGED_IN;
552 		goto clean_exit;
553 	}
554 
555 	/* Now make the ioctl call. No need to acquire the session lock. */
556 	c_logout.cl_session = session_p->k_session;
557 	while ((r = ioctl(kernel_fd, CRYPTO_LOGOUT, &c_logout)) < 0) {
558 		if (errno != EINTR)
559 			break;
560 	}
561 	if (r < 0) {
562 		rv = CKR_FUNCTION_FAILED;
563 	} else {
564 		rv = crypto2pkcs11_error_number(c_logout.cl_return_value);
565 	}
566 
567 	if (rv != CKR_OK) {
568 		goto clean_exit;
569 	}
570 
571 	/*
572 	 * If this slot was logged in as USER previously, we need to clean up
573 	 * all private object wrappers in library for this slot.
574 	 */
575 	kernel_cleanup_pri_objects_in_slot(pslot, session_p);
576 
577 	if (rv == CKR_OK) {
578 		/* Reset the slot's session state. */
579 		pslot->sl_state = CKU_PUBLIC;
580 	}
581 
582 clean_exit:
583 	REFRELE(session_p, ses_lock_held);
584 	(void) pthread_mutex_unlock(&pslot->sl_mutex);
585 	return (rv);
586 }
587