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 <stdlib.h>
30 #include <string.h>
31 #include "metaGlobal.h"
32 
33 /*
34  * The list and the list lock are global for two external uses:
35  * 1) C_CloseAllSessions need to close the head (repeatedly,
36  *    until no more sessions exist).
37  * 2) meta_object_find_by_handle needs to walk all sessions,
38  *    searching each session object list for matching objects.
39  */
40 pthread_rwlock_t meta_sessionlist_lock;
41 meta_session_t *meta_sessionlist_head;
42 
43 /*
44  * The following 2 variables are used for tracking the number of
45  * sessions and number of rw sessios that are currently open
46  *
47  * They are being manipulated in the metaSession.c file, and being
48  * referenced in the metaSlotToken.c file
49  */
50 CK_ULONG num_meta_sessions;
51 CK_ULONG num_rw_meta_sessions;
52 
53 
54 
55 static pthread_rwlock_t meta_sessionclose_lock;
56 
57 
58 /*
59  * meta_sessionManager_initialize
60  *
61  * Called from meta_Initialize.  Initializes all the variables used
62  * by the session manager.
63  */
64 CK_RV
65 meta_sessionManager_initialize()
66 {
67 
68 	if (pthread_rwlock_init(&meta_sessionlist_lock, NULL) != 0) {
69 		return (CKR_FUNCTION_FAILED);
70 	}
71 
72 	if (pthread_rwlock_init(&meta_sessionclose_lock, NULL) != 0) {
73 		(void) pthread_rwlock_destroy(&meta_sessionlist_lock);
74 		return (CKR_FUNCTION_FAILED);
75 	}
76 
77 	meta_sessionlist_head = NULL;
78 	num_meta_sessions = 0;
79 	num_rw_meta_sessions = 0;
80 
81 	return (CKR_OK);
82 }
83 
84 /*
85  * meta_sessionManager_finalize
86  *
87  * Close all sessions, and destroy all the locks
88  */
89 void
90 meta_sessionManager_finalize()
91 {
92 	/*
93 	 * Close any remaining metasessions, can just simply call
94 	 * meta_CloseAllSessions.  The METASLOT_SLOTID argument is
95 	 * not used, but need to be passed in.
96 	 */
97 	(void) meta_CloseAllSessions(METASLOT_SLOTID);
98 
99 	(void) pthread_rwlock_destroy(&meta_sessionclose_lock);
100 
101 	(void) pthread_rwlock_destroy(&meta_sessionlist_lock);
102 }
103 
104 /*
105  * meta_handle2session
106  *
107  * Convert a CK_SESSION_HANDLE to the corresponding metasession. If
108  * successful, a reader-lock on the session will be held to indicate
109  * that it's in use. Call REFRELEASE() when finished.
110  *
111  */
112 CK_RV
113 meta_handle2session(CK_SESSION_HANDLE hSession, meta_session_t **session)
114 {
115 	meta_session_t *tmp_session = (meta_session_t *)(hSession);
116 
117 	/* Check for bad args (eg CK_INVALID_HANDLE, which is 0/NULL). */
118 	if (tmp_session == NULL) {
119 		*session = NULL;
120 		return (CKR_SESSION_HANDLE_INVALID);
121 	}
122 
123 
124 	/* Lock to ensure the magic-check + read-lock is atomic. */
125 	(void) pthread_rwlock_rdlock(&meta_sessionclose_lock);
126 
127 	if (tmp_session->magic_marker != METASLOT_SESSION_MAGIC) {
128 		(void) pthread_rwlock_unlock(&meta_sessionclose_lock);
129 		*session = NULL;
130 		return (CKR_SESSION_HANDLE_INVALID);
131 	}
132 	(void) pthread_rwlock_unlock(&meta_sessionclose_lock);
133 
134 	/*
135 	 * sessions can only be used by a single thread at a time.
136 	 * So, we need to get a write-lock.
137 	 */
138 	(void) pthread_rwlock_wrlock(&tmp_session->session_lock);
139 
140 	/* Make sure this session is not in the process of being deleted */
141 	(void) pthread_mutex_lock(&tmp_session->isClosingSession_lock);
142 	if (tmp_session->isClosingSession) {
143 		(void) pthread_mutex_unlock(
144 		    &tmp_session->isClosingSession_lock);
145 		(void) pthread_rwlock_unlock(&tmp_session->session_lock);
146 		return (CKR_SESSION_HANDLE_INVALID);
147 	}
148 	(void) pthread_mutex_unlock(&tmp_session->isClosingSession_lock);
149 
150 	*session = tmp_session;
151 	return (CKR_OK);
152 }
153 
154 
155 /*
156  * meta_session_alloc
157  */
158 CK_RV
159 meta_session_alloc(meta_session_t **session)
160 {
161 	meta_session_t *new_session;
162 
163 	/* Allocate memory for the session. */
164 	new_session = calloc(1, sizeof (meta_session_t));
165 	if (new_session == NULL)
166 		return (CKR_HOST_MEMORY);
167 
168 	(new_session->mech_support_info).supporting_slots
169 	    = malloc(meta_slotManager_get_slotcount() * sizeof (mechinfo_t *));
170 	if ((new_session->mech_support_info).supporting_slots == NULL) {
171 		free(new_session);
172 		return (CKR_HOST_MEMORY);
173 	}
174 	(new_session->mech_support_info).num_supporting_slots = 0;
175 
176 	new_session->magic_marker = METASLOT_SESSION_MAGIC;
177 	(void) pthread_rwlock_init(&new_session->session_lock, NULL);
178 	(void) pthread_mutex_init(&new_session->isClosingSession_lock, NULL);
179 	(void) pthread_rwlock_init(&new_session->object_list_lock, NULL);
180 
181 	*session = new_session;
182 	return (CKR_OK);
183 }
184 
185 
186 /*
187  * meta_session_activate
188  *
189  * Create and add a session to the list of active meta sessions.
190  */
191 CK_RV
192 meta_session_activate(meta_session_t *session)
193 {
194 	CK_RV rv = CKR_OK;
195 
196 	/* Add session to the list of sessions. */
197 	(void) pthread_rwlock_wrlock(&meta_sessionlist_lock);
198 	INSERT_INTO_LIST(meta_sessionlist_head, session);
199 	(void) pthread_rwlock_unlock(&meta_sessionlist_lock);
200 
201 	return (rv);
202 }
203 
204 /*
205  * meta_session_deactivate
206  *
207  *
208  */
209 CK_RV
210 meta_session_deactivate(meta_session_t *session,
211     boolean_t have_sessionlist_lock)
212 {
213 	boolean_t isLastSession = B_FALSE;
214 	meta_object_t *object;
215 
216 	/* Safely resolve attempts of concurrent-close */
217 	(void) pthread_mutex_lock(&session->isClosingSession_lock);
218 	if (session->isClosingSession) {
219 		/* Lost a delete race. */
220 		(void) pthread_mutex_unlock(&session->isClosingSession_lock);
221 		REFRELEASE(session);
222 		return (CKR_SESSION_HANDLE_INVALID);
223 	}
224 	session->isClosingSession = B_TRUE;
225 	(void) pthread_mutex_unlock(&session->isClosingSession_lock);
226 
227 	/*
228 	 * Remove session from the session list. Once removed, it will not
229 	 * be possible for another thread to begin using the session.
230 	 */
231 	(void) pthread_rwlock_wrlock(&meta_sessionclose_lock);
232 	if (!have_sessionlist_lock) {
233 		(void) pthread_rwlock_wrlock(&meta_sessionlist_lock);
234 	}
235 
236 	session->magic_marker = METASLOT_SESSION_BADMAGIC;
237 	REMOVE_FROM_LIST(meta_sessionlist_head, session);
238 	if (meta_sessionlist_head == NULL) {
239 		isLastSession = B_TRUE;
240 	}
241 	if (!have_sessionlist_lock) {
242 		(void) pthread_rwlock_unlock(&meta_sessionlist_lock);
243 	}
244 	(void) pthread_rwlock_unlock(&meta_sessionclose_lock);
245 
246 	(void) pthread_rwlock_unlock(&session->session_lock);
247 
248 	/* Cleanup any in-progress operations. */
249 	if (session->op1.type != OP_UNUSED) {
250 		meta_operation_cleanup(session, session->op1.type, FALSE);
251 	}
252 
253 	if (session->op1.session != NULL) {
254 		meta_release_slot_session(session->op1.session);
255 		session->op1.session = NULL;
256 	}
257 
258 	/* Remove all the session metaobjects created in this session. */
259 	/* Basically, emulate C_DestroyObject, including safety h2s */
260 	while ((object = session->object_list_head) != NULL) {
261 		CK_RV rv;
262 
263 		rv = meta_handle2object((CK_OBJECT_HANDLE)object, &object);
264 		if (rv != CKR_OK) {
265 			/* Can only happen if someone else just closed it. */
266 			continue;
267 		}
268 
269 		rv = meta_object_deactivate(object, B_FALSE, B_TRUE);
270 		if (rv != CKR_OK) {
271 			continue;
272 		}
273 
274 		rv = meta_object_dealloc(object, B_FALSE);
275 		if (rv != CKR_OK) {
276 			continue;
277 		}
278 
279 	}
280 
281 	if ((isLastSession) && (metaslot_logged_in())) {
282 		slot_session_t *slotsessp;
283 		CK_RV rv;
284 
285 		rv = meta_get_slot_session(get_keystore_slotnum(), &slotsessp,
286 		    session->session_flags);
287 		if (rv != CKR_OK)
288 			return (rv);
289 		rv = FUNCLIST(slotsessp->fw_st_id)->C_Logout(
290 		    slotsessp->hSession);
291 
292 		meta_release_slot_session(slotsessp);
293 
294 		/* if C_Logout fails, just ignore the error */
295 		metaslot_set_logged_in_flag(B_FALSE);
296 
297 		if (rv != CKR_OK)
298 			return (rv);
299 
300 		/* need to deactivate all the PRIVATE token objects */
301 		rv = meta_token_object_deactivate(PRIVATE_TOKEN);
302 		if (rv != CKR_OK) {
303 			return (rv);
304 		}
305 	}
306 
307 	return (CKR_OK);
308 }
309 
310 
311 /*
312  * meta_session_dealloc
313  *
314  * Release the resources held by a metasession. If the session has been
315  * activated, it must be deactivated first.
316  */
317 void
318 meta_session_dealloc(meta_session_t *session)
319 {
320 	if ((session->find_objs_info).matched_objs) {
321 		free((session->find_objs_info).matched_objs);
322 	}
323 
324 	free((session->mech_support_info).supporting_slots);
325 
326 	/*
327 	 * If there were active operations, cleanup the slot session so that
328 	 * it can be reused (otherwise provider might complain that an
329 	 * operation is active).
330 	 */
331 	if (session->op1.type != OP_UNUSED)
332 		meta_operation_cleanup(session, session->op1.type, FALSE);
333 
334 	/* Final object cleanup. */
335 	(void) pthread_rwlock_destroy(&session->session_lock);
336 	(void) pthread_mutex_destroy(&session->isClosingSession_lock);
337 	(void) pthread_rwlock_destroy(&session->object_list_lock);
338 
339 	meta_session_delay_free(session);
340 }
341 
342 /*
343  * This function adds the to-be-freed meta session to a linked list.
344  * When the number of sessions queued in the linked list reaches the
345  * maximum threshold MAX_SESSION_TO_BE_FREED, it will free the first
346  * session (FIFO) in the list.
347  */
348 void
349 meta_session_delay_free(meta_session_t *sp)
350 {
351 	meta_session_t *tmp;
352 
353 	(void) pthread_mutex_lock(&ses_delay_freed.ses_to_be_free_mutex);
354 
355 	/* Add the newly deleted session at the end of the list */
356 	sp->next = NULL;
357 	if (ses_delay_freed.first == NULL) {
358 		ses_delay_freed.last = sp;
359 		ses_delay_freed.first = sp;
360 	} else {
361 		ses_delay_freed.last->next = sp;
362 		ses_delay_freed.last = sp;
363 	}
364 
365 	if (++ses_delay_freed.count >= MAX_SESSION_TO_BE_FREED) {
366 		/*
367 		 * Free the first session in the list only if
368 		 * the total count reaches maximum threshold.
369 		 */
370 		ses_delay_freed.count--;
371 		tmp = ses_delay_freed.first->next;
372 		free(ses_delay_freed.first);
373 		ses_delay_freed.first = tmp;
374 	}
375 	(void) pthread_mutex_unlock(&ses_delay_freed.ses_to_be_free_mutex);
376 }
377