xref: /illumos-gate/usr/src/lib/pkcs11/pkcs11_softtoken/common/softObject.c (revision d35aa001d65a0260475aa7ded6ca159c6ba3fe64)
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  * Copyright 2020 Joyent, Inc.
26  * Copyright 2023 RackTop Systems, Inc.
27  */
28 #include <pthread.h>
29 #include <stdlib.h>
30 #include <security/cryptoki.h>
31 #include "softGlobal.h"
32 #include "softObject.h"
33 #include "softSession.h"
34 #include "softKeystore.h"
35 #include "softKeystoreUtil.h"
36 
37 
38 CK_RV
39 C_CreateObject(CK_SESSION_HANDLE hSession,
40     CK_ATTRIBUTE_PTR pTemplate,
41     CK_ULONG ulCount,
42     CK_OBJECT_HANDLE_PTR phObject)
43 {
44 
45 	CK_RV rv;
46 	soft_session_t *session_p;
47 	boolean_t lock_held = B_FALSE;
48 
49 	if (!softtoken_initialized)
50 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
51 
52 	/*
53 	 * Obtain the session pointer. Also, increment the session
54 	 * reference count.
55 	 */
56 	rv = handle2session(hSession, &session_p);
57 	if (rv != CKR_OK)
58 		return (rv);
59 
60 	if ((pTemplate == NULL) || (ulCount == 0) ||
61 	    (phObject == NULL)) {
62 		rv = CKR_ARGUMENTS_BAD;
63 		goto clean_exit;
64 	}
65 
66 	/* Create a new object. */
67 	rv = soft_add_object(pTemplate, ulCount, phObject, session_p);
68 
69 clean_exit:
70 	/*
71 	 * Decrement the session reference count.
72 	 * We do not hold the session lock.
73 	 */
74 	SES_REFRELE(session_p, lock_held);
75 	return (rv);
76 }
77 
78 CK_RV
79 C_CopyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
80     CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount,
81     CK_OBJECT_HANDLE_PTR phNewObject)
82 {
83 
84 	CK_RV rv;
85 	soft_session_t *session_p;
86 	boolean_t lock_held = B_FALSE;
87 	soft_object_t *old_object, *new_object = NULL;
88 	ulong_t i;
89 
90 	if (!softtoken_initialized)
91 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
92 
93 	/*
94 	 * Obtain the session pointer. Also, increment the session
95 	 * reference count.
96 	 */
97 	rv = handle2session(hSession, &session_p);
98 	if (rv != CKR_OK)
99 		return (rv);
100 
101 	/* Check arguments */
102 	if (((ulCount > 0) && (pTemplate == NULL)) ||
103 	    (phNewObject == NULL)) {
104 		rv = CKR_ARGUMENTS_BAD;
105 		goto clean_exit;
106 	}
107 
108 	/* Obtain the object pointer. */
109 	HANDLE2OBJECT(hObject, old_object, rv);
110 	if (rv != CKR_OK) {
111 		goto clean_exit;
112 	}
113 
114 	/*
115 	 * Copy the old object to a new object.
116 	 * The 3rd argument with SOFT_COPY_OBJ value indicates that
117 	 * everything in the object will be duplicated for C_CopyObject.
118 	 * The 4th argument has the session pointer that will be
119 	 * saved in the new copy of the session object.
120 	 */
121 	(void) pthread_mutex_lock(&old_object->object_mutex);
122 	rv = soft_copy_object(old_object, &new_object, SOFT_COPY_OBJECT,
123 	    session_p);
124 
125 	if ((rv != CKR_OK) || (new_object == NULL)) {
126 		/* Most likely we ran out of space. */
127 		(void) pthread_mutex_unlock(&old_object->object_mutex);
128 		goto clean_exit1;
129 	}
130 
131 	/* No need to hold the lock on the old object. */
132 	(void) pthread_mutex_unlock(&old_object->object_mutex);
133 
134 	/* Modifiy the objects if requested */
135 	for (i = 0; i < ulCount; i++) {
136 		/* Set the requested attribute into the new object. */
137 		rv = soft_set_attribute(new_object, &pTemplate[i], B_TRUE);
138 		if (rv != CKR_OK) {
139 			goto fail;
140 		}
141 	}
142 
143 	rv = soft_pin_expired_check(new_object);
144 	if (rv != CKR_OK) {
145 		goto fail;
146 	}
147 
148 	/*
149 	 * Does the new object violate the creation rule or access rule?
150 	 */
151 	rv = soft_object_write_access_check(session_p, new_object);
152 	if (rv != CKR_OK) {
153 		goto fail;
154 	}
155 
156 	/*
157 	 * If the new object is a token object, it will be added
158 	 * to token object list and write to disk.
159 	 */
160 	if (IS_TOKEN_OBJECT(new_object)) {
161 		new_object->version = 1;
162 		/*
163 		 * Write to the keystore file.
164 		 */
165 		rv = soft_put_object_to_keystore(new_object);
166 		if (rv != CKR_OK) {
167 			goto fail;
168 		}
169 
170 		new_object->session_handle = CK_INVALID_HANDLE;
171 		/*
172 		 * Add the newly created token object to the global
173 		 * token object list in the slot struct.
174 		 */
175 		soft_add_token_object_to_slot(new_object);
176 		OBJ_REFRELE(old_object);
177 		SES_REFRELE(session_p, lock_held);
178 		*phNewObject = set_objecthandle(new_object);
179 
180 		return (CKR_OK);
181 	}
182 
183 	*phNewObject = set_objecthandle(new_object);
184 
185 	/* Insert new object into this session's object list */
186 	soft_add_object_to_session(new_object, session_p);
187 
188 	/*
189 	 * Decrement the session reference count.
190 	 * We do not hold the session lock.
191 	 */
192 	OBJ_REFRELE(old_object);
193 	SES_REFRELE(session_p, lock_held);
194 
195 	return (rv);
196 
197 fail:
198 	soft_cleanup_object(new_object);
199 	free(new_object);
200 
201 clean_exit1:
202 	OBJ_REFRELE(old_object);
203 clean_exit:
204 	SES_REFRELE(session_p, lock_held);
205 	return (rv);
206 }
207 
208 CK_RV
209 C_DestroyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject)
210 {
211 
212 	CK_RV rv;
213 	soft_object_t *object_p;
214 	soft_session_t *session_p;
215 	boolean_t lock_held = B_FALSE;
216 	CK_SESSION_HANDLE creating_session;
217 
218 
219 	if (!softtoken_initialized)
220 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
221 
222 	rv = handle2session(hSession, &session_p);
223 	if (rv != CKR_OK)
224 		return (rv);
225 
226 	/* Obtain the object pointer. */
227 	HANDLE2OBJECT_DESTROY(hObject, object_p, rv);
228 	if (rv != CKR_OK) {
229 		SES_REFRELE(session_p, lock_held);
230 		return (rv);
231 	}
232 
233 	/* Obtain the session handle which object belongs to. */
234 	creating_session = object_p->session_handle;
235 
236 	if (creating_session == 0) {
237 		/*
238 		 * This is a token object to be deleted.
239 		 * For token object, there is no creating session concept,
240 		 * therefore, creating_session is always NULL.
241 		 */
242 		rv = soft_pin_expired_check(object_p);
243 		if (rv != CKR_OK) {
244 			SES_REFRELE(session_p, lock_held);
245 			return (rv);
246 		}
247 
248 		rv = soft_object_write_access_check(session_p, object_p);
249 		if (rv != CKR_OK) {
250 			SES_REFRELE(session_p, lock_held);
251 			return (rv);
252 		}
253 
254 		/*
255 		 * Set OBJECT_IS_DELETING flag so any access to this
256 		 * object will be rejected.
257 		 */
258 		(void) pthread_mutex_lock(&object_p->object_mutex);
259 		if (object_p->obj_delete_sync & OBJECT_IS_DELETING) {
260 			(void) pthread_mutex_unlock(&object_p->object_mutex);
261 			SES_REFRELE(session_p, lock_held);
262 			return (CKR_OBJECT_HANDLE_INVALID);
263 		}
264 		object_p->obj_delete_sync |= OBJECT_IS_DELETING;
265 		(void) pthread_mutex_unlock(&object_p->object_mutex);
266 		SES_REFRELE(session_p, lock_held);
267 
268 		/*
269 		 * Delete a token object by calling soft_delete_token_object()
270 		 * with the second argument B_TRUE indicating to delete the
271 		 * object from keystore and the third argument B_FALSE
272 		 * indicating that the caller does not hold the slot mutex.
273 		 */
274 		soft_delete_token_object(object_p, B_TRUE, B_FALSE);
275 		return (CKR_OK);
276 	}
277 
278 	/*
279 	 * Switch to the creating_session, which actually holds the object.
280 	 * If we use the wrong session in the call to soft_delete_object(),
281 	 * deletion will silently fail, and we'll leak memory until
282 	 * C_CloseSession is called (which, if metaslot is active, may be
283 	 * never).
284 	 */
285 	if (hSession != creating_session) {
286 		SES_REFRELE(session_p, lock_held);
287 		rv = handle2session(creating_session, &session_p);
288 		if (rv != CKR_OK)
289 			return (rv);
290 	}
291 
292 	/*
293 	 * Set OBJECT_IS_DELETING flag so any access to this
294 	 * object will be rejected.
295 	 */
296 	(void) pthread_mutex_lock(&object_p->object_mutex);
297 	if (object_p->obj_delete_sync & OBJECT_IS_DELETING) {
298 		(void) pthread_mutex_unlock(&object_p->object_mutex);
299 		SES_REFRELE(session_p, lock_held);
300 		return (CKR_OBJECT_HANDLE_INVALID);
301 	}
302 	object_p->obj_delete_sync |= OBJECT_IS_DELETING;
303 	(void) pthread_mutex_unlock(&object_p->object_mutex);
304 
305 	/*
306 	 * Delete an object by calling soft_delete_object()
307 	 * with a FALSE boolean argument indicating that
308 	 * the caller does not hold the session lock.
309 	 */
310 	soft_delete_object(session_p, object_p, B_FALSE, B_FALSE);
311 
312 	/*
313 	 * Decrement the session reference count.
314 	 * We do not hold the session lock.
315 	 */
316 	SES_REFRELE(session_p, lock_held);
317 
318 	return (rv);
319 }
320 
321 
322 CK_RV
323 C_GetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
324     CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount)
325 {
326 
327 	CK_RV rv = CKR_OK, rv1 = CKR_OK;
328 	soft_object_t *object_p;
329 	soft_session_t *session_p;
330 	boolean_t lock_held = B_FALSE;
331 	ulong_t i;
332 
333 	if (!softtoken_initialized)
334 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
335 
336 	/*
337 	 * Obtain the session pointer. Also, increment the session
338 	 * reference count.
339 	 */
340 	rv = handle2session(hSession, &session_p);
341 	if (rv != CKR_OK)
342 		return (rv);
343 
344 	if ((pTemplate == NULL) || (ulCount == 0)) {
345 		/*
346 		 * Decrement the session reference count.
347 		 * We do not hold the session lock.
348 		 */
349 		SES_REFRELE(session_p, lock_held);
350 		return (CKR_ARGUMENTS_BAD);
351 	}
352 
353 	/* Obtain the object pointer. */
354 	HANDLE2OBJECT(hObject, object_p, rv);
355 	if (rv != CKR_OK) {
356 		/*
357 		 * Decrement the session reference count.
358 		 * We do not hold the session lock.
359 		 */
360 		SES_REFRELE(session_p, lock_held);
361 		return (rv);
362 	}
363 
364 	if (IS_TOKEN_OBJECT(object_p)) {
365 
366 		rv = soft_keystore_load_latest_object(object_p);
367 		if (rv != CKR_OK) {
368 			OBJ_REFRELE(object_p);
369 			SES_REFRELE(session_p, lock_held);
370 			return (rv);
371 		}
372 	}
373 
374 	/* Acquire the lock on the object. */
375 	(void) pthread_mutex_lock(&object_p->object_mutex);
376 
377 	for (i = 0; i < ulCount; i++) {
378 		/*
379 		 * Get the value of each attribute in the template.
380 		 * (We must process EVERY attribute in the template.)
381 		 */
382 		rv = soft_get_attribute(object_p, &pTemplate[i]);
383 		if (rv != CKR_OK)
384 			/* At least we catch some type of error. */
385 			rv1 = rv;
386 	}
387 
388 	/* Release the object lock */
389 	(void) pthread_mutex_unlock(&object_p->object_mutex);
390 
391 	/*
392 	 * Decrement the session reference count.
393 	 * We do not hold the session lock.
394 	 */
395 	OBJ_REFRELE(object_p);
396 	SES_REFRELE(session_p, lock_held);
397 
398 	rv = rv1;
399 	return (rv);
400 }
401 
402 
403 CK_RV
404 C_SetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
405     CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount)
406 {
407 	CK_RV rv = CKR_OK;
408 	soft_object_t *object_p;
409 	soft_object_t *new_object = NULL;
410 	soft_session_t *session_p;
411 	boolean_t lock_held = B_FALSE;
412 	ulong_t i;
413 
414 	if (!softtoken_initialized)
415 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
416 
417 	/*
418 	 * Obtain the session pointer. Also, increment the session
419 	 * reference count.
420 	 */
421 	rv = handle2session(hSession, &session_p);
422 	if (rv != CKR_OK)
423 		return (rv);
424 
425 	if ((pTemplate == NULL) || (ulCount == 0)) {
426 		/*
427 		 * Decrement the session reference count.
428 		 * We do not hold the session lock.
429 		 */
430 		SES_REFRELE(session_p, lock_held);
431 		return (CKR_ARGUMENTS_BAD);
432 	}
433 
434 	/* Obtain the object pointer. */
435 	HANDLE2OBJECT(hObject, object_p, rv);
436 	if (rv != CKR_OK) {
437 		/*
438 		 * Decrement the session reference count.
439 		 * We do not hold the session lock.
440 		 */
441 		SES_REFRELE(session_p, lock_held);
442 		return (rv);
443 	}
444 
445 	if (object_p->bool_attr_mask & NOT_MODIFIABLE_BOOL_ON) {
446 		rv = CKR_ATTRIBUTE_READ_ONLY;
447 		goto fail_1;
448 	}
449 
450 	/*
451 	 * Start working on the object, so we need to set the write lock so that
452 	 * no one can write to it but still can read it.
453 	 */
454 	if (IS_TOKEN_OBJECT(object_p)) {
455 		rv = soft_keystore_load_latest_object(object_p);
456 		if (rv != CKR_OK) {
457 			goto fail_1;
458 		}
459 	}
460 
461 	/*
462 	 * Copy the old object to a new object. We work on the copied
463 	 * version because in case of error we still keep the old one
464 	 * intact.
465 	 * The 3rd argument with SOFT_SET_ATTR_VALUE value indicates that
466 	 * not everything will be duplicated for C_SetAttributeValue.
467 	 * Information not duplicated are those attributes that are not
468 	 * modifiable.
469 	 */
470 	(void) pthread_mutex_lock(&object_p->object_mutex);
471 	rv = soft_copy_object(object_p, &new_object, SOFT_SET_ATTR_VALUE, NULL);
472 
473 	if ((rv != CKR_OK) || (new_object == NULL)) {
474 		/* Most likely we ran out of space. */
475 		(void) pthread_mutex_unlock(&object_p->object_mutex);
476 		/*
477 		 * Decrement the session reference count.
478 		 * We do not hold the session lock.
479 		 */
480 		goto fail_1;
481 	}
482 
483 	/*
484 	 * No need to hold the lock on the old object, because we
485 	 * will be working on the new scratch object.
486 	 */
487 	(void) pthread_mutex_unlock(&object_p->object_mutex);
488 
489 	rv = soft_object_write_access_check(session_p, new_object);
490 	if (rv != CKR_OK) {
491 		goto fail;
492 	}
493 
494 	for (i = 0; i < ulCount; i++) {
495 		/* Set the requested attribute into the new object. */
496 		rv = soft_set_attribute(new_object, &pTemplate[i], B_FALSE);
497 
498 		if (rv != CKR_OK) {
499 			goto fail;
500 		}
501 	}
502 
503 	/*
504 	 * We've successfully set all the requested attributes.
505 	 * Merge the new object with the old object, then destory
506 	 * the new one. The reason to do the merging is because we
507 	 * have to keep the original object handle (address of object).
508 	 */
509 	(void) pthread_mutex_lock(&object_p->object_mutex);
510 
511 	soft_merge_object(object_p, new_object);
512 
513 	/*
514 	 * The object has been modified, so we write it back to keystore.
515 	 */
516 	if (IS_TOKEN_OBJECT(object_p)) {
517 		object_p->version++;
518 		rv = soft_modify_object_to_keystore(object_p);
519 	}
520 
521 	(void) pthread_mutex_unlock(&object_p->object_mutex);
522 	free(new_object);
523 
524 	/*
525 	 * Decrement the session reference count.
526 	 * We do not hold the session lock.
527 	 */
528 	OBJ_REFRELE(object_p);
529 	SES_REFRELE(session_p, lock_held);
530 	return (rv);
531 
532 fail:
533 	soft_cleanup_object(new_object);
534 	free(new_object);
535 
536 fail_1:
537 	OBJ_REFRELE(object_p);
538 	SES_REFRELE(session_p, lock_held);
539 
540 	return (rv);
541 }
542 
543 /*ARGSUSED*/
544 CK_RV
545 C_GetObjectSize(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
546     CK_ULONG_PTR pulSize)
547 {
548 	if (!softtoken_initialized)
549 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
550 
551 	return (CKR_FUNCTION_NOT_SUPPORTED);
552 }
553 
554 CK_RV
555 C_FindObjectsInit(CK_SESSION_HANDLE sh, CK_ATTRIBUTE_PTR pTemplate,
556     CK_ULONG ulCount)
557 {
558 
559 	CK_RV		rv;
560 	soft_session_t	*session_p;
561 	boolean_t lock_held = B_TRUE;
562 
563 	if (!softtoken_initialized)
564 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
565 
566 	/*
567 	 * Obtain the session pointer. Also, increment the session
568 	 * reference count.
569 	 */
570 	rv = handle2session(sh, &session_p);
571 	if (rv != CKR_OK)
572 		return (rv);
573 
574 	/* Check the arguments */
575 	if ((ulCount > 0) && (pTemplate == NULL)) {
576 		/* decrement the session count, we do not hold the lock */
577 		lock_held = B_FALSE;
578 		SES_REFRELE(session_p, lock_held);
579 		return (CKR_ARGUMENTS_BAD);
580 	}
581 
582 	/* Acquire the session lock */
583 	(void) pthread_mutex_lock(&session_p->session_mutex);
584 
585 	/* Check to see if find operation is already active */
586 	if (session_p->find_objects.flags & CRYPTO_OPERATION_ACTIVE) {
587 		/* decrement the session count, and unlock the mutex */
588 		SES_REFRELE(session_p, lock_held);
589 		return (CKR_OPERATION_ACTIVE);
590 	} else {
591 		/*
592 		 * This active flag will remain ON until application calls
593 		 * C_FindObjectsFinal.
594 		 */
595 		session_p->find_objects.flags = CRYPTO_OPERATION_ACTIVE;
596 	}
597 
598 	(void) pthread_mutex_unlock(&session_p->session_mutex);
599 
600 	rv = soft_find_objects_init(session_p,  pTemplate, ulCount);
601 
602 	if (rv != CKR_OK) {
603 		(void) pthread_mutex_lock(&session_p->session_mutex);
604 		session_p->find_objects.flags = 0;
605 		(void) pthread_mutex_unlock(&session_p->session_mutex);
606 	}
607 
608 	/* decrement the session count, and unlock the mutex */
609 	lock_held = B_FALSE;
610 	SES_REFRELE(session_p, lock_held);
611 	return (rv);
612 }
613 
614 CK_RV
615 C_FindObjects(CK_SESSION_HANDLE sh,
616     CK_OBJECT_HANDLE_PTR phObject,
617     CK_ULONG ulMaxObjectCount,
618     CK_ULONG_PTR pulObjectCount)
619 {
620 	soft_session_t	*session_p;
621 	CK_RV rv = CKR_OK;
622 	boolean_t lock_held = B_TRUE;
623 
624 	if (!softtoken_initialized)
625 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
626 
627 	/*
628 	 * Obtain the session pointer. Also, increment the session
629 	 * reference count.
630 	 */
631 	rv = handle2session(sh, &session_p);
632 	if (rv != CKR_OK)
633 		return (rv);
634 
635 	/* check for invalid arguments */
636 	if (((phObject == NULL) && (ulMaxObjectCount != 0)) ||
637 	    (pulObjectCount == NULL)) {
638 		/* decrement the session count, we do not hold the lock */
639 		lock_held = B_FALSE;
640 		SES_REFRELE(session_p, lock_held);
641 		return (CKR_ARGUMENTS_BAD);
642 	}
643 
644 	if (ulMaxObjectCount == 0) {
645 		/* don't need to do anything, just return */
646 		*pulObjectCount = 0;
647 		/* decrement the session count, we do not hold the lock */
648 		lock_held = B_FALSE;
649 		SES_REFRELE(session_p, lock_held);
650 		return (CKR_OK);
651 	}
652 
653 	/* Acquire the session lock */
654 	(void) pthread_mutex_lock(&session_p->session_mutex);
655 
656 	/* Check to see if find operation is active */
657 	if (!(session_p->find_objects.flags & CRYPTO_OPERATION_ACTIVE)) {
658 		SES_REFRELE(session_p, lock_held);
659 		return (CKR_OPERATION_NOT_INITIALIZED);
660 	}
661 
662 	soft_find_objects(session_p, phObject, ulMaxObjectCount,
663 	    pulObjectCount);
664 
665 	/* decrement the session count, and release the lock */
666 	SES_REFRELE(session_p, lock_held);
667 	return (rv);
668 }
669 
670 CK_RV
671 C_FindObjectsFinal(CK_SESSION_HANDLE sh)
672 {
673 	soft_session_t	*session_p;
674 	CK_RV rv;
675 	boolean_t lock_held = B_TRUE;
676 
677 	if (!softtoken_initialized)
678 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
679 
680 	/*
681 	 * Obtain the session pointer. Also, increment the session
682 	 * reference count.
683 	 */
684 	rv = handle2session(sh, &session_p);
685 	if (rv != CKR_OK)
686 		return (rv);
687 
688 	/* Acquire the session lock */
689 	(void) pthread_mutex_lock(&session_p->session_mutex);
690 
691 	/* Check to see if find operation is active */
692 	if (!(session_p->find_objects.flags & CRYPTO_OPERATION_ACTIVE)) {
693 		SES_REFRELE(session_p, lock_held);
694 		return (CKR_OPERATION_NOT_INITIALIZED);
695 	}
696 
697 	soft_find_objects_final(session_p);
698 
699 	/* decrement the session count, and release the lock */
700 	SES_REFRELE(session_p, lock_held);
701 	return (rv);
702 }
703