xref: /onnv-gate/usr/src/lib/pkcs11/pkcs11_kms/common/kmsSessionUtil.c (revision 12720:3db6e0082404)
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  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
22  */
23 
24 #include <pthread.h>
25 #include <syslog.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <security/cryptoki.h>
30 #include "kmsGlobal.h"
31 #include "kmsSession.h"
32 #include "kmsSlot.h"
33 #include "kmsKeystoreUtil.h"
34 
35 static pthread_mutex_t delete_sessions_mutex = PTHREAD_MUTEX_INITIALIZER;
36 CK_ULONG kms_session_cnt = 0;
37 CK_ULONG kms_session_rw_cnt = 0;
38 
39 /*
40  * Delete all the sessions. First, obtain the slot lock.
41  * Then start to delete one session at a time.  The boolean wrapper_only
42  * argument indicates that whether the caller only wants to clean up the
43  * session wrappers and the object wrappers in the library.
44  * - When this function is called by C_CloseAllSessions or indirectly by
45  *   C_Finalize, wrapper_only is FALSE.
46  * - When this function is called by cleanup_child, wrapper_only is TRUE.
47  */
48 void
kms_delete_all_sessions(boolean_t wrapper_only)49 kms_delete_all_sessions(boolean_t wrapper_only)
50 {
51 	kms_session_t *session_p;
52 	kms_slot_t *pslot;
53 
54 	(void) pthread_mutex_lock(&delete_sessions_mutex);
55 
56 	pslot = get_slotinfo();
57 
58 	/*
59 	 * Delete all the sessions in the slot's session list.
60 	 * The routine kms_delete_session() updates the linked list.
61 	 * So, we do not need to maintain the list here.
62 	 */
63 	for (;;) {
64 		(void) pthread_mutex_lock(&pslot->sl_mutex);
65 		if (pslot->sl_sess_list == NULL)
66 			break;
67 
68 		session_p = pslot->sl_sess_list;
69 		/*
70 		 * Set SESSION_IS_CLOSING flag so any access to this
71 		 * session will be rejected.
72 		 */
73 		(void) pthread_mutex_lock(&session_p->session_mutex);
74 		if (session_p->ses_close_sync & SESSION_IS_CLOSING) {
75 			(void) pthread_mutex_unlock(&session_p->session_mutex);
76 			continue;
77 		}
78 		session_p->ses_close_sync |= SESSION_IS_CLOSING;
79 		(void) pthread_mutex_unlock(&session_p->session_mutex);
80 
81 		(void) pthread_mutex_unlock(&pslot->sl_mutex);
82 		kms_delete_session(session_p, B_FALSE, wrapper_only);
83 	}
84 	(void) pthread_mutex_unlock(&pslot->sl_mutex);
85 	(void) pthread_mutex_unlock(&delete_sessions_mutex);
86 }
87 
88 static int
labelcmp(const void * a,const void * b)89 labelcmp(const void *a, const void *b)
90 {
91 	objlabel_t *obja = (objlabel_t *)a;
92 	objlabel_t *objb = (objlabel_t *)b;
93 	int n;
94 
95 	if (obja == NULL || obja->label == NULL)
96 		return (-1);
97 	if (objb == NULL || objb->label == NULL)
98 		return (1);
99 
100 	n = strcmp((char *)obja->label, (char *)objb->label);
101 	if (n == 0)
102 		return (0);
103 	else if (n < 0)
104 		return (-1);
105 	return (1);
106 }
107 
108 /*
109  * Create a new session struct, and add it to the slot's session list.
110  *
111  * This function is called by C_OpenSession(), which hold the slot lock.
112  */
113 CK_RV
kms_add_session(CK_SLOT_ID slotID,CK_FLAGS flags,CK_VOID_PTR pApplication,CK_NOTIFY notify,CK_ULONG * sessionhandle_p)114 kms_add_session(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication,
115 	CK_NOTIFY notify, CK_ULONG *sessionhandle_p)
116 {
117 	kms_session_t *new_sp = NULL;
118 	kms_slot_t	*pslot;
119 	CK_RV rv;
120 
121 	/* Allocate a new session struct */
122 	new_sp = calloc(1, sizeof (kms_session_t));
123 	if (new_sp == NULL) {
124 		return (CKR_HOST_MEMORY);
125 	}
126 
127 	new_sp->magic_marker = KMSTOKEN_SESSION_MAGIC;
128 	new_sp->pApplication = pApplication;
129 	new_sp->Notify = notify;
130 	new_sp->flags = flags;
131 	new_sp->ses_RO = (flags & CKF_RW_SESSION) ? B_FALSE : B_TRUE;
132 	new_sp->ses_slotid = slotID;
133 	new_sp->object_list = NULL;
134 	new_sp->ses_refcnt = 0;
135 	new_sp->ses_close_sync = 0;
136 
137 	avl_create(&new_sp->objlabel_tree, labelcmp, sizeof (objlabel_t),
138 	    KMSOFFSETOF(objlabel_t, nodep));
139 
140 	rv = kms_reload_labels(new_sp);
141 	if (rv != CKR_OK) {
142 		free(new_sp);
143 		return (rv);
144 	}
145 
146 	/* Initialize the lock for the newly created session */
147 	if (pthread_mutex_init(&new_sp->session_mutex, NULL) != 0) {
148 		free(new_sp);
149 		return (CKR_CANT_LOCK);
150 	}
151 
152 	pslot = get_slotinfo();
153 
154 	(void) pthread_mutex_init(&new_sp->ses_free_mutex, NULL);
155 	(void) pthread_cond_init(&new_sp->ses_free_cond, NULL);
156 
157 	/* Insert the new session in front of the slot's session list */
158 	if (pslot->sl_sess_list == NULL) {
159 		pslot->sl_sess_list = new_sp;
160 		new_sp->prev = NULL;
161 		new_sp->next = NULL;
162 	} else {
163 		pslot->sl_sess_list->prev = new_sp;
164 		new_sp->next = pslot->sl_sess_list;
165 		new_sp->prev = NULL;
166 		pslot->sl_sess_list = new_sp;
167 	}
168 
169 	/* Type casting the address of a session struct to a session handle */
170 	*sessionhandle_p =  (CK_ULONG)new_sp;
171 	return (CKR_OK);
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 kms_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 void
kms_delete_session(kms_session_t * session_p,boolean_t slot_lock_held,boolean_t wrapper_only)186 kms_delete_session(kms_session_t *session_p,
187     boolean_t slot_lock_held, boolean_t wrapper_only)
188 {
189 	kms_slot_t	*pslot;
190 	kms_object_t *objp;
191 	kms_object_t *objp1;
192 
193 	/*
194 	 * Check to see if the caller holds the lock on the global
195 	 * session list. If not, we need to acquire that lock in
196 	 * order to proceed.
197 	 */
198 	pslot = get_slotinfo();
199 	if (!slot_lock_held) {
200 		/* Acquire the slot lock */
201 		(void) pthread_mutex_lock(&pslot->sl_mutex);
202 	}
203 
204 	/*
205 	 * Remove the session from the slot's session list first.
206 	 */
207 	if (pslot->sl_sess_list == session_p) {
208 		/* Session is the first one in the list */
209 		if (session_p->next) {
210 			pslot->sl_sess_list = session_p->next;
211 			session_p->next->prev = NULL;
212 		} else {
213 			/* Session is the only one in the list */
214 			pslot->sl_sess_list = NULL;
215 		}
216 	} else {
217 		/* Session is not the first one in the list */
218 		if (session_p->next) {
219 			/* Session is in the middle of the list */
220 			session_p->prev->next = session_p->next;
221 			session_p->next->prev = session_p->prev;
222 		} else {
223 			/* Session is the last one in the list */
224 			session_p->prev->next = NULL;
225 		}
226 	}
227 
228 	if (!slot_lock_held) {
229 		/*
230 		 * If the slot lock is obtained by
231 		 * this function, then release that lock after
232 		 * removing the session from session linked list.
233 		 * We want the releasing of the objects of the
234 		 * session, and freeing of the session itself to
235 		 * be done without holding the slot's session list
236 		 * lock.
237 		 */
238 		(void) pthread_mutex_unlock(&pslot->sl_mutex);
239 	}
240 
241 	/* Acquire the individual session lock */
242 	(void) pthread_mutex_lock(&session_p->session_mutex);
243 
244 	/*
245 	 * Make sure another thread hasn't freed the session.
246 	 */
247 	if (session_p->magic_marker != KMSTOKEN_SESSION_MAGIC) {
248 		(void) pthread_mutex_unlock(&session_p->session_mutex);
249 		return;
250 	}
251 
252 	/*
253 	 * The deletion of a session must be blocked when the session reference
254 	 * count is not zero. This means that if the thread that is attempting
255 	 * to close the session must wait until the prior operations on this
256 	 * session are finished.
257 	 *
258 	 * Unless we are being forced to shut everything down, this only
259 	 * happens if the library's _fini() is running not if someone
260 	 * explicitly called C_Finalize().
261 	 */
262 	(void) pthread_mutex_lock(&session_p->ses_free_mutex);
263 
264 	if (wrapper_only) {
265 		session_p->ses_refcnt = 0;
266 	}
267 
268 	while (session_p->ses_refcnt != 0) {
269 		/*
270 		 * We set the SESSION_REFCNT_WAITING flag before we put
271 		 * this closing thread in a wait state, so other non-closing
272 		 * operation thread will wake it up only when
273 		 * the session reference count becomes zero and this flag
274 		 * is set.
275 		 */
276 		session_p->ses_close_sync |= SESSION_REFCNT_WAITING;
277 		(void) pthread_mutex_unlock(&session_p->session_mutex);
278 		(void) pthread_cond_wait(&session_p->ses_free_cond,
279 		    &session_p->ses_free_mutex);
280 		(void) pthread_mutex_lock(&session_p->session_mutex);
281 	}
282 
283 	session_p->ses_close_sync &= ~SESSION_REFCNT_WAITING;
284 
285 	/* Mark session as no longer valid. */
286 	session_p->magic_marker = 0;
287 
288 	(void) pthread_mutex_unlock(&session_p->ses_free_mutex);
289 	(void) pthread_mutex_destroy(&session_p->ses_free_mutex);
290 	(void) pthread_cond_destroy(&session_p->ses_free_cond);
291 
292 	/*
293 	 * Remove all the objects created in this session, waiting
294 	 * until each object's refcnt is 0.
295 	 */
296 	kms_delete_all_objects_in_session(session_p, wrapper_only);
297 
298 	/* Close the KMS agent profile. */
299 	KMS_UnloadProfile(&session_p->kmsProfile);
300 
301 	/* Reset SESSION_IS_CLOSING flag. */
302 	session_p->ses_close_sync &= ~SESSION_IS_CLOSING;
303 
304 	kms_clear_label_list(&session_p->objlabel_tree);
305 	avl_destroy(&session_p->objlabel_tree);
306 
307 	(void) pthread_mutex_unlock(&session_p->session_mutex);
308 	/* Destroy the individual session lock */
309 	(void) pthread_mutex_destroy(&session_p->session_mutex);
310 
311 	kms_session_delay_free(session_p);
312 
313 	/*
314 	 * If there is no more session remained in this slot, reset the slot's
315 	 * session state to CKU_PUBLIC.  Also, clean up all the token object
316 	 * wrappers in the library for this slot.
317 	 */
318 	/* Acquire the slot lock if lock is not held */
319 	if (!slot_lock_held) {
320 		(void) pthread_mutex_lock(&pslot->sl_mutex);
321 	}
322 
323 	if (pslot->sl_sess_list == NULL) {
324 		/* Reset the session auth state. */
325 		pslot->sl_state = CKU_PUBLIC;
326 
327 		/* Clean up token object wrappers. */
328 		objp = pslot->sl_tobj_list;
329 		while (objp) {
330 			objp1 = objp->next;
331 			(void) pthread_mutex_destroy(&objp->object_mutex);
332 			(void) kms_cleanup_object(objp);
333 			free(objp);
334 			objp = objp1;
335 		}
336 		pslot->sl_tobj_list = NULL;
337 	}
338 
339 	/* Release the slot lock if lock is not held */
340 	if (!slot_lock_held) {
341 		(void) pthread_mutex_unlock(&pslot->sl_mutex);
342 	}
343 }
344 
345 /*
346  * This function is used to type cast a session handle to a pointer to
347  * the session struct. Also, it does the following things:
348  * 1) Check to see if the session struct is tagged with a session
349  *    magic number. This is to detect when an application passes
350  *    a bogus session pointer.
351  * 2) Acquire the locks on the designated session.
352  * 3) Check to see if the session is in the closing state that another
353  *    thread is performing.
354  * 4) Increment the session reference count by one. This is to prevent
355  *    this session from being closed by other thread.
356  * 5) Release the locks on the designated session.
357  */
358 CK_RV
handle2session(CK_SESSION_HANDLE hSession,kms_session_t ** session_p)359 handle2session(CK_SESSION_HANDLE hSession, kms_session_t **session_p)
360 {
361 	kms_session_t *sp = (kms_session_t *)(hSession);
362 	CK_RV rv;
363 
364 	if ((sp == NULL) ||
365 	    (sp->magic_marker != KMSTOKEN_SESSION_MAGIC)) {
366 		return (CKR_SESSION_HANDLE_INVALID);
367 	} else {
368 		(void) pthread_mutex_lock(&sp->session_mutex);
369 		if (sp->ses_close_sync & SESSION_IS_CLOSING) {
370 			rv = CKR_SESSION_CLOSED;
371 		} else {
372 			/* Increment session ref count. */
373 			sp->ses_refcnt++;
374 			rv = CKR_OK;
375 		}
376 		(void) pthread_mutex_unlock(&sp->session_mutex);
377 	}
378 
379 	if (rv == CKR_OK)
380 		*session_p = sp;
381 
382 	return (rv);
383 }
384 
385 /*
386  * This function adds the to-be-freed session to a linked list.
387  * When the number of sessions queued in the linked list reaches the
388  * maximum threshold MAX_SES_TO_BE_FREED, it will free the first
389  * session (FIFO) in the list.
390  */
391 void
kms_session_delay_free(kms_session_t * sp)392 kms_session_delay_free(kms_session_t *sp)
393 {
394 	kms_session_t *tmp;
395 
396 	(void) pthread_mutex_lock(&ses_delay_freed.ses_to_be_free_mutex);
397 
398 	/* Add the newly deleted session at the end of the list */
399 	sp->next = NULL;
400 	if (ses_delay_freed.first == NULL) {
401 		ses_delay_freed.last = sp;
402 		ses_delay_freed.first = sp;
403 	} else {
404 		ses_delay_freed.last->next = sp;
405 		ses_delay_freed.last = sp;
406 	}
407 
408 	if (++ses_delay_freed.count >= MAX_SES_TO_BE_FREED) {
409 		/*
410 		 * Free the first session in the list only if
411 		 * the total count reaches maximum threshold.
412 		 */
413 		ses_delay_freed.count--;
414 		tmp = ses_delay_freed.first->next;
415 		free(ses_delay_freed.first);
416 		ses_delay_freed.first = tmp;
417 	}
418 	(void) pthread_mutex_unlock(&ses_delay_freed.ses_to_be_free_mutex);
419 }
420 
421 /*
422  * Acquire all slots' mutexes and all their sessions' mutexes.
423  * Order:
424  * 1. delete_sessions_mutex
425  * for each slot:
426  *  2. pslot->sl_mutex
427  *  for each session:
428  *   3. session_p->session_mutex
429  *   4. session_p->ses_free_mutex
430  */
431 void
kms_acquire_all_slots_mutexes()432 kms_acquire_all_slots_mutexes()
433 {
434 	kms_slot_t *pslot;
435 	kms_session_t *session_p;
436 
437 	(void) pthread_mutex_lock(&delete_sessions_mutex);
438 
439 	pslot = get_slotinfo();
440 	(void) pthread_mutex_lock(&pslot->sl_mutex);
441 
442 	/* Iterate through sessions acquiring all mutexes */
443 	session_p = pslot->sl_sess_list;
444 	while (session_p) {
445 		struct object *objp;
446 
447 		(void) pthread_mutex_lock(&session_p->session_mutex);
448 		(void) pthread_mutex_lock(&session_p->ses_free_mutex);
449 
450 		objp = session_p->object_list;
451 		while (objp) {
452 			(void) pthread_mutex_lock(&objp->object_mutex);
453 			objp = objp->next;
454 		}
455 
456 		session_p = session_p->next;
457 	}
458 }
459 
460 /* Release in opposite order to kms_acquire_all_slots_mutexes(). */
461 void
kms_release_all_slots_mutexes()462 kms_release_all_slots_mutexes()
463 {
464 	kms_slot_t *pslot;
465 	kms_session_t *session_p;
466 
467 	pslot = get_slotinfo();
468 
469 	/* Iterate through sessions releasing all mutexes */
470 	session_p = pslot->sl_sess_list;
471 	while (session_p) {
472 		struct object *objp;
473 
474 		objp = session_p->object_list;
475 		while (objp) {
476 			(void) pthread_mutex_unlock(&objp->object_mutex);
477 			objp = objp->next;
478 		}
479 
480 		(void) pthread_mutex_unlock(&session_p->ses_free_mutex);
481 		(void) pthread_mutex_unlock(&session_p->session_mutex);
482 		session_p = session_p->next;
483 	}
484 
485 	/*
486 	 * acquired in "acquire_all_slots_mutexes" which only
487 	 * happens just prior to a fork.
488 	 */
489 	(void) pthread_mutex_unlock(&pslot->sl_mutex);
490 	(void) pthread_mutex_unlock(&delete_sessions_mutex);
491 }
492