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 <syslog.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <sys/crypto/ioctl.h>
35 #include <security/cryptoki.h>
36 #include "kernelGlobal.h"
37 #include "kernelSession.h"
38 #include "kernelSlot.h"
39 
40 /*
41  * Delete all the sessions. First, obtain the slot lock.
42  * Then start to delete one session at a time.  The boolean wrapper_only
43  * argument indicates that whether the caller only wants to clean up the
44  * session wrappers and the object wrappers in the library.
45  * - When this function is called by C_CloseAllSessions or indirectly by
46  *   C_Finalize, wrapper_only is FALSE.
47  * - When this function is called by cleanup_child, wrapper_only is TRUE.
48  */
49 CK_RV
50 kernel_delete_all_sessions(CK_SLOT_ID slotID, boolean_t wrapper_only)
51 {
52 	CK_RV rv = CKR_OK;
53 	CK_RV rv1;
54 	kernel_session_t *session_p;
55 	kernel_session_t *session_p1;
56 	kernel_slot_t *pslot;
57 
58 	/* Acquire the slot lock */
59 	pslot = slot_table[slotID];
60 	(void) pthread_mutex_lock(&pslot->sl_mutex);
61 
62 	/*
63 	 * Delete all the sessions in the slot's session list.
64 	 * The routine kernel_delete_session() updates the linked list.
65 	 * So, we do not need to maintain the list here.
66 	 */
67 	session_p = pslot->sl_sess_list;
68 	while (session_p) {
69 		session_p1 = session_p->next;
70 		/*
71 		 * Delete a session by calling kernel_delete_session()
72 		 * with a session pointer and a boolean arguments.
73 		 * Boolean value TRUE is used to indicate that the
74 		 * caller holds the slot lock.
75 		 */
76 		rv1 = kernel_delete_session(slotID, session_p, B_TRUE,
77 		    wrapper_only);
78 
79 		/* Record the very first error code */
80 		if (rv == CKR_OK) {
81 			rv = rv1;
82 		}
83 
84 		session_p = session_p1;
85 	}
86 
87 	/* Release the slot lock */
88 	(void) pthread_mutex_unlock(&pslot->sl_mutex);
89 
90 	return (rv);
91 }
92 
93 
94 /*
95  * Create a new session struct, and add it to the slot's session list.
96  *
97  * This function is called by C_OpenSession(), which hold the slot lock.
98  */
99 CK_RV
100 kernel_add_session(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication,
101 	CK_NOTIFY notify, CK_ULONG *sessionhandle_p)
102 {
103 	CK_RV rv = CKR_OK;
104 	kernel_session_t *new_sp = NULL;
105 	crypto_open_session_t open_session;
106 	kernel_slot_t	*pslot;
107 	int r;
108 
109 	/* Allocate a new session struct */
110 	new_sp = calloc(1, sizeof (kernel_session_t));
111 	if (new_sp == NULL) {
112 		return (CKR_HOST_MEMORY);
113 	}
114 
115 	new_sp->magic_marker = KERNELTOKEN_SESSION_MAGIC;
116 	new_sp->pApplication = pApplication;
117 	new_sp->Notify = notify;
118 	new_sp->flags = flags;
119 	new_sp->ses_RO = (flags & CKF_RW_SESSION) ? B_FALSE : B_TRUE;
120 	new_sp->ses_slotid = slotID;
121 	new_sp->object_list = NULL;
122 	new_sp->ses_refcnt = 0;
123 	new_sp->ses_close_sync = 0;
124 
125 	/* Initialize the lock for the newly created session */
126 	if (pthread_mutex_init(&new_sp->session_mutex, NULL) != 0) {
127 		free(new_sp);
128 		return (CKR_CANT_LOCK);
129 	}
130 
131 	pslot = slot_table[slotID];
132 	open_session.os_provider_id = pslot->sl_provider_id;
133 	open_session.os_flags = flags;
134 	while ((r = ioctl(kernel_fd, CRYPTO_OPEN_SESSION, &open_session)) < 0) {
135 		if (errno != EINTR)
136 			break;
137 	}
138 	if (r < 0) {
139 		rv = CKR_FUNCTION_FAILED;
140 	} else {
141 		rv = crypto2pkcs11_error_number(open_session.os_return_value);
142 	}
143 
144 	if (rv != CKR_OK) {
145 		(void) pthread_mutex_destroy(&new_sp->session_mutex);
146 		free(new_sp);
147 		return (rv);
148 	}
149 
150 	new_sp->k_session = open_session.os_session;
151 
152 	(void) pthread_mutex_init(&new_sp->ses_free_mutex, NULL);
153 	(void) pthread_cond_init(&new_sp->ses_free_cond, NULL);
154 
155 	/* Insert the new session in front of the slot's session list */
156 	if (pslot->sl_sess_list == NULL) {
157 		pslot->sl_sess_list = new_sp;
158 		new_sp->prev = NULL;
159 		new_sp->next = NULL;
160 	} else {
161 		pslot->sl_sess_list->prev = new_sp;
162 		new_sp->next = pslot->sl_sess_list;
163 		new_sp->prev = NULL;
164 		pslot->sl_sess_list = new_sp;
165 	}
166 
167 	/* Type casting the address of a session struct to a session handle */
168 	*sessionhandle_p =  (CK_ULONG)new_sp;
169 
170 	return (CKR_OK);
171 }
172 
173 
174 /*
175  * Delete a session:
176  * - Remove the session from the slot's session list.
177  * - Release all the objects created by the session.
178  *
179  * The boolean argument slot_lock_held is used to indicate that whether
180  * the caller of this function holds the slot lock or not.
181  * - When called by kernel_delete_all_sessions(), which is called by
182  *   C_Finalize() or C_CloseAllSessions() -- slot_lock_held = TRUE.
183  * - When called by C_CloseSession() -- slot_lock_held = FALSE.
184  */
185 CK_RV
186 kernel_delete_session(CK_SLOT_ID slotID, kernel_session_t *session_p,
187     boolean_t slot_lock_held, boolean_t wrapper_only)
188 {
189 	CK_RV rv;
190 	crypto_session_id_t k_session;
191 	crypto_close_session_t close_session;
192 	kernel_slot_t	*pslot;
193 	kernel_object_t *objp;
194 	kernel_object_t *objp1;
195 	int r;
196 
197 	/*
198 	 * Check to see if the caller holds the lock on the global
199 	 * session list. If not, we need to acquire that lock in
200 	 * order to proceed.
201 	 */
202 	pslot = slot_table[slotID];
203 	if (!slot_lock_held) {
204 		/* Acquire the slot lock */
205 		(void) pthread_mutex_lock(&pslot->sl_mutex);
206 	}
207 
208 	/*
209 	 * Remove the session from the slot's session list first.
210 	 */
211 	if (pslot->sl_sess_list == session_p) {
212 		/* Session is the first one in the list */
213 		if (session_p->next) {
214 			pslot->sl_sess_list = session_p->next;
215 			session_p->next->prev = NULL;
216 		} else {
217 			/* Session is the only one in the list */
218 			pslot->sl_sess_list = NULL;
219 		}
220 	} else {
221 		/* Session is not the first one in the list */
222 		if (session_p->next) {
223 			/* Session is in the middle of the list */
224 			session_p->prev->next = session_p->next;
225 			session_p->next->prev = session_p->prev;
226 		} else {
227 			/* Session is the last one in the list */
228 			session_p->prev->next = NULL;
229 		}
230 	}
231 
232 
233 	if (!slot_lock_held) {
234 		/*
235 		 * If the slot lock is obtained by
236 		 * this function, then release that lock after
237 		 * removing the session from session linked list.
238 		 * We want the releasing of the objects of the
239 		 * session, and freeing of the session itself to
240 		 * be done without holding the slot's session list
241 		 * lock.
242 		 */
243 		(void) pthread_mutex_unlock(&pslot->sl_mutex);
244 	}
245 
246 	/* Acquire the individual session lock */
247 	(void) pthread_mutex_lock(&session_p->session_mutex);
248 
249 	/*
250 	 * Make sure another thread hasn't freed the session.
251 	 */
252 	if (session_p->magic_marker != KERNELTOKEN_SESSION_MAGIC) {
253 		(void) pthread_mutex_unlock(&session_p->session_mutex);
254 		return (CKR_OK);
255 	}
256 
257 	/*
258 	 * The deletion of a session must be blocked when the session reference
259 	 * count is not zero. This means that if the thread that is attempting
260 	 * to close the session must wait until the prior operations on this
261 	 * session are finished.
262 	 */
263 	(void) pthread_mutex_lock(&session_p->ses_free_mutex);
264 
265 	while (session_p->ses_refcnt != 0) {
266 		/*
267 		 * We set the SESSION_REFCNT_WAITING flag before we put
268 		 * this closing thread in a wait state, so other non-closing
269 		 * operation thread will wake it up only when
270 		 * the session reference count becomes zero and this flag
271 		 * is set.
272 		 */
273 		session_p->ses_close_sync |= SESSION_REFCNT_WAITING;
274 		(void) pthread_mutex_unlock(&session_p->session_mutex);
275 		(void) pthread_cond_wait(&session_p->ses_free_cond,
276 		    &session_p->ses_free_mutex);
277 		(void) pthread_mutex_lock(&session_p->session_mutex);
278 	}
279 
280 	session_p->ses_close_sync &= ~SESSION_REFCNT_WAITING;
281 
282 	/* Mark session as no longer valid. */
283 	session_p->magic_marker = 0;
284 
285 	(void) pthread_mutex_unlock(&session_p->ses_free_mutex);
286 	(void) pthread_mutex_destroy(&session_p->ses_free_mutex);
287 	(void) pthread_cond_destroy(&session_p->ses_free_cond);
288 
289 	/*
290 	 * Remove all the objects created in this session.
291 	 */
292 	kernel_delete_all_objects_in_session(session_p, wrapper_only);
293 
294 	/* In case application did not call Final */
295 	if (session_p->digest.context != NULL)
296 		free(session_p->digest.context);
297 
298 	if (session_p->encrypt.context != NULL)
299 		free(session_p->encrypt.context);
300 
301 	if (session_p->decrypt.context != NULL)
302 		free(session_p->decrypt.context);
303 
304 	if (session_p->sign.context != NULL)
305 		free(session_p->sign.context);
306 
307 	if (session_p->verify.context != NULL)
308 		free(session_p->verify.context);
309 
310 	k_session = session_p->k_session;
311 
312 	/* Reset SESSION_IS_CLOSING flag. */
313 	session_p->ses_close_sync &= ~SESSION_IS_CLOSING;
314 
315 	(void) pthread_mutex_unlock(&session_p->session_mutex);
316 	/* Destroy the individual session lock */
317 	(void) pthread_mutex_destroy(&session_p->session_mutex);
318 
319 	if (!wrapper_only) {
320 		close_session.cs_session = k_session;
321 		while ((r = ioctl(kernel_fd, CRYPTO_CLOSE_SESSION,
322 		    &close_session)) < 0) {
323 			if (errno != EINTR)
324 				break;
325 		}
326 		if (r < 0) {
327 			rv = CKR_FUNCTION_FAILED;
328 		} else {
329 			rv = crypto2pkcs11_error_number(
330 			    close_session.cs_return_value);
331 		}
332 	}
333 
334 	/*
335 	 * Ignore ioctl return codes. If the library tells the kernel to
336 	 * close a session and the kernel says "I don't know what session
337 	 * you're talking about", there's not much that can be done.  All
338 	 * sessions in the kernel will be closed when the application exits
339 	 * and closes /dev/crypto.
340 	 */
341 	rv = CKR_OK;
342 	free(session_p);
343 
344 	/*
345 	 * If there is no more session remained in this slot, reset the slot's
346 	 * session state to CKU_PUBLIC.  Also, clean up all the token object
347 	 * wrappers in the library for this slot.
348 	 */
349 	if (pslot->sl_sess_list == NULL) {
350 
351 		/* Acquire the slot lock if lock is not held */
352 		if (!slot_lock_held) {
353 			(void) pthread_mutex_lock(&pslot->sl_mutex);
354 		}
355 
356 		/* Reset the session auth. state. */
357 		pslot->sl_state = CKU_PUBLIC;
358 
359 		/* Clean up token object wrappers. */
360 		objp = pslot->sl_tobj_list;
361 		while (objp) {
362 			objp1 = objp->next;
363 			(void) pthread_mutex_destroy(&objp->object_mutex);
364 			(void) free(objp);
365 			objp = objp1;
366 		}
367 		pslot->sl_tobj_list = NULL;
368 
369 		/* Release the slot lock if lock is not held */
370 		if (!slot_lock_held) {
371 			(void) pthread_mutex_unlock(&pslot->sl_mutex);
372 		}
373 	}
374 
375 	return (rv);
376 }
377 
378 
379 /*
380  * This function is used to type cast a session handle to a pointer to
381  * the session struct. Also, it does the following things:
382  * 1) Check to see if the session struct is tagged with a session
383  *    magic number. This is to detect when an application passes
384  *    a bogus session pointer.
385  * 2) Acquire the locks on the designated session and the slot which owns
386  *    this session.
387  * 3) Check to see if the session is in the closing state that another
388  *    thread is performing.
389  * 4) Increment the session reference count by one. This is to prevent
390  *    this session from being closed by other thread.
391  * 5) Release the locks on the designated session and on the slot.
392  */
393 CK_RV
394 handle2session(CK_SESSION_HANDLE hSession, kernel_session_t **session_p)
395 {
396 	kernel_session_t *sp = (kernel_session_t *)(hSession);
397 	CK_RV rv;
398 	kernel_slot_t *pslot;
399 
400 	if ((sp == NULL) ||
401 	    (sp->magic_marker != KERNELTOKEN_SESSION_MAGIC)) {
402 		return (CKR_SESSION_HANDLE_INVALID);
403 	} else {
404 		pslot = slot_table[sp->ses_slotid];
405 		(void) pthread_mutex_lock(&pslot->sl_mutex);
406 		(void) pthread_mutex_lock(&sp->session_mutex);
407 		if (sp->ses_close_sync & SESSION_IS_CLOSING) {
408 			rv = CKR_SESSION_CLOSED;
409 		} else {
410 			/* Increment session ref count. */
411 			sp->ses_refcnt++;
412 			rv = CKR_OK;
413 		}
414 		(void) pthread_mutex_unlock(&sp->session_mutex);
415 		(void) pthread_mutex_unlock(&pslot->sl_mutex);
416 	}
417 
418 	if (rv == CKR_OK)
419 		*session_p = sp;
420 
421 	return (rv);
422 }
423