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