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