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 <security/cryptoki.h>
31 #include "softGlobal.h"
32 #include "softSession.h"
33 #include "softObject.h"
34 #include "softKeystore.h"
35 #include "softKeystoreUtil.h"
36 
37 
38 CK_RV
39 C_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication,
40     CK_NOTIFY Notify, CK_SESSION_HANDLE_PTR phSession)
41 {
42 
43 	CK_RV rv = CKR_OK;
44 
45 	if (!softtoken_initialized)
46 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
47 
48 	/*
49 	 * For legacy reasons, the CKF_SERIAL_SESSION bit must always
50 	 * be set.
51 	 */
52 	if (!(flags & CKF_SERIAL_SESSION))
53 		return (CKR_SESSION_PARALLEL_NOT_SUPPORTED);
54 
55 	if (slotID != SOFTTOKEN_SLOTID)
56 		return (CKR_SLOT_ID_INVALID);
57 
58 	if (phSession == NULL)
59 		return (CKR_ARGUMENTS_BAD);
60 
61 	/*
62 	 * softtoken has no limit on the number of concurrent sessions
63 	 * that the token allows. No need to check to see if the
64 	 * token has too many sessions already open.
65 	 */
66 
67 	/* Create a new session */
68 	rv = soft_add_session(flags, pApplication, Notify, phSession);
69 
70 	return (rv);
71 
72 }
73 
74 CK_RV
75 C_CloseSession(CK_SESSION_HANDLE hSession)
76 {
77 
78 	CK_RV rv;
79 
80 	soft_session_t *session_p;
81 	boolean_t lock_held = B_TRUE;
82 
83 	if (!softtoken_initialized)
84 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
85 
86 	/*
87 	 * Obtain the session pointer. Also, increment the session
88 	 * reference count.
89 	 */
90 	rv = handle2session(hSession, &session_p);
91 	if (rv != CKR_OK)
92 		return (rv);
93 
94 	(void) pthread_mutex_lock(&session_p->session_mutex);
95 	/*
96 	 * Set SESSION_IS_CLOSING flag so any access to this
97 	 * session will be rejected.
98 	 */
99 	if (session_p->ses_close_sync & SESSION_IS_CLOSING) {
100 		SES_REFRELE(session_p, lock_held);
101 		return (CKR_SESSION_CLOSED);
102 	}
103 	session_p->ses_close_sync |= SESSION_IS_CLOSING;
104 
105 	/*
106 	 * Decrement the session reference count.
107 	 * We hold the session lock, and SES_REFRELE()
108 	 * will release the session lock for us.
109 	 */
110 	SES_REFRELE(session_p, lock_held);
111 
112 	/*
113 	 * Delete a session by calling soft_delete_session() with
114 	 * a session pointer and a boolean arguments. Boolean
115 	 * value FALSE is used to indicate that the caller does not
116 	 * hold the lock on the global session list and also that
117 	 * this is not a forced session close but an explicit request.
118 	 *
119 	 * soft_delete_session() will reset SESSION_IS_CLOSING
120 	 * flag after it is done.
121 	 */
122 	rv = soft_delete_session(session_p, B_FALSE, B_FALSE);
123 
124 	if (soft_session_cnt == 0) {
125 		/* Clean up private token objects from the token object list */
126 		soft_delete_all_in_core_token_objects(PRIVATE_TOKEN);
127 		/*
128 		 * Invalidate public token object handles instead of
129 		 * deleting them.
130 		 */
131 		soft_validate_token_objects(B_FALSE);
132 		(void) pthread_mutex_lock(&soft_giant_mutex);
133 		soft_slot.authenticated = 0;
134 		soft_slot.userpin_change_needed = 0;
135 		(void) pthread_mutex_unlock(&soft_giant_mutex);
136 	}
137 
138 	return (rv);
139 }
140 
141 
142 CK_RV
143 C_CloseAllSessions(CK_SLOT_ID slotID)
144 {
145 
146 	CK_RV rv = CKR_OK;
147 
148 	if (!softtoken_initialized)
149 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
150 
151 	if (slotID != SOFTTOKEN_SLOTID)
152 		return (CKR_SLOT_ID_INVALID);
153 
154 	/* Acquire the global session list lock */
155 	(void) pthread_mutex_lock(&soft_sessionlist_mutex);
156 	/*
157 	 * Set all_sessions_closing flag so any access to any
158 	 * existing sessions will be rejected.
159 	 */
160 	all_sessions_closing = 1;
161 	(void) pthread_mutex_unlock(&soft_sessionlist_mutex);
162 
163 	/* Delete all the sessions and release the allocated resources */
164 	rv = soft_delete_all_sessions(B_FALSE);
165 
166 	/* Clean up private token objects from the token object list */
167 	soft_delete_all_in_core_token_objects(PRIVATE_TOKEN);
168 
169 	/* Invalidate public token object handles instead of deleting them */
170 	soft_validate_token_objects(B_FALSE);
171 
172 	(void) pthread_mutex_lock(&soft_giant_mutex);
173 	soft_slot.authenticated = 0;
174 	soft_slot.userpin_change_needed = 0;
175 	(void) pthread_mutex_unlock(&soft_giant_mutex);
176 
177 	(void) pthread_mutex_lock(&soft_sessionlist_mutex);
178 	/* Reset all_sessions_closing flag. */
179 	all_sessions_closing = 0;
180 	(void) pthread_mutex_unlock(&soft_sessionlist_mutex);
181 
182 	return (rv);
183 }
184 
185 CK_RV
186 C_GetSessionInfo(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo)
187 {
188 
189 	soft_session_t *session_p;
190 	CK_RV rv;
191 	boolean_t lock_held = B_TRUE;
192 
193 	if (!softtoken_initialized)
194 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
195 
196 	/*
197 	 * Obtain the session pointer. Also, increment the session
198 	 * reference count.
199 	 */
200 	rv = handle2session(hSession, &session_p);
201 	if (rv != CKR_OK)
202 		return (rv);
203 
204 	if (pInfo == NULL) {
205 		lock_held = B_FALSE;
206 		rv = CKR_ARGUMENTS_BAD;
207 		goto clean_exit;
208 	}
209 
210 	(void) pthread_mutex_lock(&session_p->session_mutex);
211 
212 	/* Provide information for the specified session */
213 	pInfo->slotID = SOFTTOKEN_SLOTID;
214 	pInfo->state = session_p->state;
215 	pInfo->flags = session_p->flags;
216 	pInfo->ulDeviceError = 0;
217 
218 clean_exit:
219 	/*
220 	 * Decrement the session reference count.
221 	 * We hold the session lock, and SES_REFRELE()
222 	 * will release the session lock for us.
223 	 */
224 	SES_REFRELE(session_p, lock_held);
225 
226 	return (CKR_OK);
227 }
228 
229 
230 CK_RV
231 C_GetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState,
232     CK_ULONG_PTR pulOperationStateLen)
233 {
234 	soft_session_t *session_p;
235 	CK_RV rv;
236 	boolean_t lock_held = B_FALSE;
237 
238 	if (!softtoken_initialized)
239 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
240 
241 	/*
242 	 * Obtain the session pointer. Also, increment the session
243 	 * reference count.
244 	 */
245 	rv = handle2session(hSession, &session_p);
246 	if (rv != CKR_OK)
247 		return (rv);
248 
249 	/*
250 	 * Only check if pulOperationStateLen is NULL_PTR.
251 	 * No need to check if pOperationState is NULL_PTR because
252 	 * application might just ask for the length of buffer to hold
253 	 * the OperationState.
254 	 */
255 	if (pulOperationStateLen == NULL_PTR) {
256 		rv = CKR_ARGUMENTS_BAD;
257 		goto clean_exit;
258 	}
259 
260 	rv = soft_get_operationstate(session_p, pOperationState,
261 	    pulOperationStateLen);
262 
263 clean_exit:
264 	SES_REFRELE(session_p, lock_held);
265 	return (rv);
266 
267 }
268 
269 
270 CK_RV
271 C_SetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState,
272     CK_ULONG ulOperationStateLen, CK_OBJECT_HANDLE hEncryptionKey,
273     CK_OBJECT_HANDLE hAuthenticationKey)
274 {
275 	soft_session_t *session_p;
276 	CK_RV rv;
277 	boolean_t lock_held = B_FALSE;
278 
279 	if (!softtoken_initialized)
280 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
281 
282 	/*
283 	 * Obtain the session pointer. Also, increment the session
284 	 * reference count.
285 	 */
286 	rv = handle2session(hSession, &session_p);
287 	if (rv != CKR_OK)
288 		return (rv);
289 
290 	if ((pOperationState == NULL_PTR) ||
291 	    (ulOperationStateLen == 0)) {
292 		rv = CKR_ARGUMENTS_BAD;
293 		goto clean_exit;
294 	}
295 
296 	rv = soft_set_operationstate(session_p, pOperationState,
297 	    ulOperationStateLen, hEncryptionKey, hAuthenticationKey);
298 
299 clean_exit:
300 	SES_REFRELE(session_p, lock_held);
301 	return (rv);
302 }
303 
304 CK_RV
305 C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, CK_UTF8CHAR_PTR pPin,
306     CK_ULONG ulPinLen)
307 {
308 
309 	soft_session_t *session_p, *sp;
310 	CK_RV rv;
311 	boolean_t lock_held = B_FALSE;
312 
313 	if (!softtoken_initialized)
314 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
315 
316 	/*
317 	 * Obtain the session pointer. Also, increment the session
318 	 * reference count.
319 	 */
320 	rv = handle2session(hSession, &session_p);
321 	if (rv != CKR_OK)
322 		return (rv);
323 
324 	if (!soft_token_present) {
325 		SES_REFRELE(session_p, lock_held);
326 		return (CKR_DEVICE_REMOVED);
327 	}
328 
329 	if (userType != CKU_USER) {
330 		SES_REFRELE(session_p, lock_held);
331 		return (CKR_USER_TYPE_INVALID);
332 	}
333 
334 	if ((ulPinLen < MIN_PIN_LEN) || (ulPinLen > MAX_PIN_LEN)) {
335 		SES_REFRELE(session_p, lock_held);
336 		return (CKR_PIN_LEN_RANGE);
337 	}
338 
339 	if (pPin == NULL_PTR) {
340 		/*
341 		 * We don't support CKF_PROTECTED_AUTHENTICATION_PATH
342 		 */
343 		SES_REFRELE(session_p, lock_held);
344 		return (CKR_ARGUMENTS_BAD);
345 	}
346 
347 	(void) pthread_mutex_lock(&soft_giant_mutex);
348 	if (soft_slot.authenticated) {
349 		(void) pthread_mutex_unlock(&soft_giant_mutex);
350 		SES_REFRELE(session_p, lock_held);
351 		return (CKR_USER_ALREADY_LOGGED_IN);
352 	}
353 
354 	rv = soft_login(pPin, ulPinLen);
355 	if (rv == CKR_OK) {
356 		if (soft_slot.userpin_change_needed) {
357 			/*
358 			 * This is the special case when the PIN is never
359 			 * initialized in the keystore, which will always
360 			 * return CKR_OK with "userpin_change_needed" set.
361 			 */
362 			(void) pthread_mutex_unlock(&soft_giant_mutex);
363 			SES_REFRELE(session_p, lock_held);
364 			return (rv);
365 		}
366 
367 		soft_slot.authenticated = 1;
368 		(void) pthread_mutex_unlock(&soft_giant_mutex);
369 	} else {
370 		(void) pthread_mutex_unlock(&soft_giant_mutex);
371 		SES_REFRELE(session_p, lock_held);
372 		return (rv);
373 	}
374 
375 	/*
376 	 * Load all the private token objects from keystore.
377 	 */
378 	rv = soft_get_token_objects_from_keystore(PRI_TOKENOBJS);
379 	if (rv != CKR_OK) {
380 		SES_REFRELE(session_p, lock_held);
381 		return (rv);
382 	}
383 
384 	/* Acquire the global session list lock */
385 	(void) pthread_mutex_lock(&soft_sessionlist_mutex);
386 
387 	sp = soft_session_list;
388 
389 	while (sp) {
390 		(void) pthread_mutex_lock(&sp->session_mutex);
391 
392 		if (sp->flags & CKF_RW_SESSION) {
393 			sp->state = CKS_RW_USER_FUNCTIONS;
394 		} else {
395 			sp->state = CKS_RO_USER_FUNCTIONS;
396 		}
397 		(void) pthread_mutex_unlock(&sp->session_mutex);
398 		sp = sp->next;
399 	}
400 
401 	(void) pthread_mutex_unlock(&soft_sessionlist_mutex);
402 
403 	SES_REFRELE(session_p, lock_held);
404 	return (rv);
405 
406 }
407 
408 CK_RV
409 C_Logout(CK_SESSION_HANDLE hSession)
410 {
411 
412 	soft_session_t *session_p, *sp;
413 	CK_RV rv;
414 	boolean_t lock_held = B_FALSE;
415 
416 	if (!softtoken_initialized)
417 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
418 
419 	/*
420 	 * Obtain the session pointer. Also, increment the session
421 	 * reference count.
422 	 */
423 	rv = handle2session(hSession, &session_p);
424 	if (rv != CKR_OK)
425 		return (rv);
426 
427 	(void) pthread_mutex_lock(&soft_giant_mutex);
428 	if (!soft_slot.authenticated) {
429 		if (!soft_slot.userpin_change_needed) {
430 			/*
431 			 * Only if the PIN has been initialized in the keystore.
432 			 */
433 			(void) pthread_mutex_unlock(&soft_giant_mutex);
434 			SES_REFRELE(session_p, lock_held);
435 			return (CKR_USER_NOT_LOGGED_IN);
436 		} else {
437 			soft_slot.userpin_change_needed = 0;
438 			(void) pthread_mutex_unlock(&soft_giant_mutex);
439 			SES_REFRELE(session_p, lock_held);
440 			return (CKR_OK);
441 		}
442 	}
443 
444 	soft_logout();
445 	soft_slot.authenticated = 0;
446 	(void) pthread_mutex_unlock(&soft_giant_mutex);
447 
448 	/* Acquire the global session list lock */
449 	(void) pthread_mutex_lock(&soft_sessionlist_mutex);
450 
451 	sp = soft_session_list;
452 
453 	while (sp) {
454 		(void) pthread_mutex_lock(&sp->session_mutex);
455 
456 		if (sp->flags & CKF_RW_SESSION) {
457 			sp->state = CKS_RW_PUBLIC_SESSION;
458 		} else {
459 			sp->state = CKS_RO_PUBLIC_SESSION;
460 		}
461 		(void) pthread_mutex_unlock(&sp->session_mutex);
462 		sp = sp->next;
463 	}
464 
465 	(void) pthread_mutex_unlock(&soft_sessionlist_mutex);
466 
467 	SES_REFRELE(session_p, lock_held);
468 	return (rv);
469 
470 }
471