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