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