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