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 #include <pthread.h>
30 #include <errno.h>
31 #include <security/cryptoki.h>
32 #include <sys/crypto/ioctl.h>
33 #include "kernelGlobal.h"
34 #include "kernelSession.h"
35 #include "kernelSlot.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 CK_RV
150 C_GetSessionInfo(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo)
151 {
152 	kernel_session_t *session_p;
153 	CK_RV rv;
154 	boolean_t ses_lock_held = B_FALSE;
155 	kernel_slot_t	*pslot;
156 
157 	if (!kernel_initialized)
158 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
159 
160 	if (pInfo == NULL)
161 		return (CKR_ARGUMENTS_BAD);
162 
163 	/*
164 	 * Obtain the session pointer. Also, increment the session
165 	 * reference count.
166 	 */
167 	rv = handle2session(hSession, &session_p);
168 	if (rv != CKR_OK)
169 		return (rv);
170 
171 	/* Provide information for the specified session */
172 	pInfo->slotID = session_p->ses_slotid;
173 	pInfo->flags = session_p->flags;
174 	pInfo->ulDeviceError = 0;
175 
176 	pslot = slot_table[session_p->ses_slotid];
177 	(void) pthread_mutex_lock(&pslot->sl_mutex);
178 
179 	if (pslot->sl_state == CKU_PUBLIC) {
180 		pInfo->state = (session_p->ses_RO) ?
181 		    CKS_RO_PUBLIC_SESSION : CKS_RW_PUBLIC_SESSION;
182 	} else if (pslot->sl_state == CKU_USER) {
183 		pInfo->state = (session_p->ses_RO) ?
184 		    CKS_RO_USER_FUNCTIONS : CKS_RW_USER_FUNCTIONS;
185 	} else if (pslot->sl_state == CKU_SO) {
186 		pInfo->state = CKS_RW_SO_FUNCTIONS;
187 	}
188 
189 	(void) pthread_mutex_unlock(&pslot->sl_mutex);
190 
191 	/*
192 	 * Decrement the session reference count.
193 	 */
194 	REFRELE(session_p, ses_lock_held);
195 
196 	return (CKR_OK);
197 }
198 
199 
200 /*ARGSUSED*/
201 CK_RV
202 C_GetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState,
203     CK_ULONG_PTR pulOperationStateLen)
204 {
205 	if (!kernel_initialized)
206 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
207 
208 	return (CKR_FUNCTION_NOT_SUPPORTED);
209 }
210 
211 
212 /*ARGSUSED*/
213 CK_RV
214 C_SetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState,
215     CK_ULONG ulOperationStateLen, CK_OBJECT_HANDLE hEncryptionKey,
216     CK_OBJECT_HANDLE hAuthenticationKey)
217 {
218 	if (!kernel_initialized)
219 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
220 
221 	return (CKR_FUNCTION_NOT_SUPPORTED);
222 }
223 
224 
225 CK_RV
226 C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType,
227     CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen)
228 {
229 	CK_RV	rv = CKR_OK;
230 	kernel_session_t *session_p;
231 	kernel_slot_t	*pslot;
232 	boolean_t ses_lock_held = B_FALSE;
233 	crypto_login_t  c_login;
234 	int r;
235 
236 	if (!kernel_initialized)
237 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
238 
239 	if ((userType != CKU_SO) && (userType != CKU_USER)) {
240 		return (CKR_USER_TYPE_INVALID);
241 	}
242 
243 	/*
244 	 * Obtain the session pointer. Also, increment the session
245 	 * reference count.
246 	 */
247 	rv = handle2session(hSession, &session_p);
248 	if (rv != CKR_OK)
249 		return (rv);
250 
251 	/* Acquire the slot lock */
252 	pslot = slot_table[session_p->ses_slotid];
253 	(void) pthread_mutex_lock(&pslot->sl_mutex);
254 
255 	/* Check if the slot is logged in already */
256 	if ((pslot->sl_state == CKU_USER) || (pslot->sl_state == CKU_SO)) {
257 		rv = CKR_USER_ALREADY_LOGGED_IN;
258 		goto clean_exit;
259 	}
260 
261 	/* To login as SO, every session in this slot needs to be R/W */
262 	if (userType == CKU_SO) {
263 		kernel_session_t  *sp;
264 		boolean_t	found;
265 
266 		found = B_FALSE;
267 		sp = pslot->sl_sess_list;
268 		while (sp) {
269 			/*
270 			 * Need not to lock individual sessions before
271 			 * accessing their "ses_RO" and "next" fields,
272 			 * because they are always accessed under the
273 			 * slot's mutex protection.
274 			 */
275 			if (sp->ses_RO) {
276 				found = B_TRUE;
277 				break;
278 			}
279 			sp = sp->next;
280 		}
281 
282 		if (found) {
283 			rv = CKR_SESSION_READ_ONLY_EXISTS;
284 			goto clean_exit;
285 		}
286 	}
287 
288 	/* Now make the ioctl call; no need to acquire the session lock. */
289 	c_login.co_session = session_p->k_session;
290 	c_login.co_user_type = userType;
291 	c_login.co_pin_len = ulPinLen;
292 	c_login.co_pin = (char *)pPin;
293 
294 	while ((r = ioctl(kernel_fd, CRYPTO_LOGIN, &c_login)) < 0) {
295 		if (errno != EINTR)
296 			break;
297 	}
298 	if (r < 0) {
299 		rv = CKR_FUNCTION_FAILED;
300 	} else {
301 		rv = crypto2pkcs11_error_number(c_login.co_return_value);
302 	}
303 
304 	if (rv == CKR_OK) {
305 		/* Set the slot's session state. */
306 		pslot->sl_state = userType;
307 	}
308 
309 clean_exit:
310 
311 	REFRELE(session_p, ses_lock_held);
312 	(void) pthread_mutex_unlock(&pslot->sl_mutex);
313 	return (rv);
314 }
315 
316 
317 CK_RV
318 C_Logout(CK_SESSION_HANDLE hSession)
319 {
320 	CK_RV	rv = CKR_OK;
321 	kernel_session_t *session_p;
322 	kernel_slot_t	*pslot;
323 	boolean_t ses_lock_held = B_FALSE;
324 	crypto_logout_t  c_logout;
325 	int r;
326 
327 	if (!kernel_initialized)
328 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
329 
330 	/*
331 	 * Obtain the session pointer. Also, increment the session
332 	 * reference count.
333 	 */
334 	rv = handle2session(hSession, &session_p);
335 	if (rv != CKR_OK)
336 		return (rv);
337 
338 	/* Acquire the slot lock. */
339 	pslot = slot_table[session_p->ses_slotid];
340 	(void) pthread_mutex_lock(&pslot->sl_mutex);
341 
342 	/* Check if the user or SO was logged in  */
343 	if (pslot->sl_state == CKU_PUBLIC) {
344 		rv = CKR_USER_NOT_LOGGED_IN;
345 		goto clean_exit;
346 	}
347 
348 	/* Now make the ioctl call. No need to acquire the session lock. */
349 	c_logout.cl_session = session_p->k_session;
350 	while ((r = ioctl(kernel_fd, CRYPTO_LOGOUT, &c_logout)) < 0) {
351 		if (errno != EINTR)
352 			break;
353 	}
354 	if (r < 0) {
355 		rv = CKR_FUNCTION_FAILED;
356 	} else {
357 		rv = crypto2pkcs11_error_number(c_logout.cl_return_value);
358 	}
359 
360 	if (rv != CKR_OK) {
361 		goto clean_exit;
362 	}
363 
364 	/*
365 	 * If this slot was logged in as USER previously, we need to clean up
366 	 * all private object wrappers in library for this slot.
367 	 */
368 	kernel_cleanup_pri_objects_in_slot(pslot, session_p);
369 
370 	if (rv == CKR_OK) {
371 		/* Reset the slot's session state. */
372 		pslot->sl_state = CKU_PUBLIC;
373 	}
374 
375 clean_exit:
376 	REFRELE(session_p, ses_lock_held);
377 	(void) pthread_mutex_unlock(&pslot->sl_mutex);
378 	return (rv);
379 }
380