xref: /illumos-gate/usr/src/lib/pkcs11/libpkcs11/common/metaObject.c (revision 355b4669e025ff377602b6fc7caaf30dbc218371)
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 /*
30  * Object Management Functions
31  * (as defined in PKCS#11 spec section 11.7)
32  */
33 
34 #include <strings.h>
35 #include "metaGlobal.h"
36 #include <stdio.h>
37 
38 #define	FIND_OBJ_BUF_SIZE	512	/* size of buf used for C_FindObjects */
39 
40 /*
41  * Argument related return codes. Will return to the caller immediately,
42  * and not try the operation on another slot.
43  */
44 static CK_RV stop_rv[] = {
45 	CKR_ARGUMENTS_BAD,
46 	CKR_ATTRIBUTE_TYPE_INVALID,
47 	CKR_DOMAIN_PARAMS_INVALID,
48 	CKR_TEMPLATE_INCOMPLETE
49 };
50 static int num_stop_rv = sizeof (stop_rv) / sizeof (CK_RV);
51 
52 /*
53  * Return codes that are related to a specific slot.
54  * Will try to perform the operation in the next available slot.
55  * If all attempts failed, will return the error code from the first slot.
56  *
57  * This list is here for reference only, it is commented out because
58  * it doesn't need to be used by the code at this point.
59  *
60  * static CK_RV try_again_rv[] = {
61  * 	CKR_DEVICE_ERROR,
62  * 	CKR_DEVICE_MEMORY,
63  * 	CKR_DEVICE_REMOVED,
64  * 	CKR_FUNCTION_FAILED,
65  * 	CKR_GENERAL_ERROR,
66  * 	CKR_HOST_MEMORY,
67  * 	CKR_TEMPLATE_INCONSISTENT,
68  * 	CKR_ATTRIBUTE_READ_ONLY,
69  * 	CKR_ATTRIBUTE_VALUE_INVALID
70  * };
71  * static int num_try_again_rv = sizeof (try_again_rv) / sizeof (CK_RV);
72  */
73 
74 /*
75  * We should never get these return codes because
76  * MetaSlot is the one that actually created the
77  * sessions.  When we get these errors in C_CreateObject,
78  * will try to create the object in the next available slot.
79  * If all attempts failed, will return CKR_FUNCTION_FAILED
80  * to the caller.
81  */
82 static CK_RV other_rv[] = {
83 	CKR_CRYPTOKI_NOT_INITIALIZED,
84 	CKR_SESSION_CLOSED,
85 	CKR_SESSION_HANDLE_INVALID,
86 	CKR_SESSION_READ_ONLY
87 };
88 static int num_other_rv = sizeof (other_rv) / sizeof (CK_RV);
89 
90 /*
91  * This function is only used by the C_CreateObject and C_CopyObject.
92  *
93  * It is used to determine if the operation should be tried on another slot
94  * based on the return code
95  */
96 static boolean_t
97 try_again(CK_RV rv)
98 {
99 	int i;
100 
101 	for (i = 0; i < num_stop_rv; i++) {
102 		if (rv == stop_rv[i]) {
103 			return (B_FALSE);
104 		}
105 	}
106 	return (B_TRUE);
107 }
108 
109 
110 /*
111  * meta_CreateObject
112  *
113  */
114 CK_RV
115 meta_CreateObject(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate,
116     CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject)
117 {
118 	CK_RV rv;
119 	meta_session_t *session;
120 	slot_session_t *slot_session = NULL;
121 	meta_object_t *object = NULL;
122 	slot_object_t *slot_object = NULL;
123 
124 	if (pTemplate == NULL || ulCount < 1 || phObject == NULL)
125 		return (CKR_ARGUMENTS_BAD);
126 
127 	rv = meta_handle2session(hSession, &session);
128 	if (rv != CKR_OK)
129 		return (rv);
130 
131 	rv = meta_object_alloc(session, &object);
132 	if (rv != CKR_OK)
133 		goto cleanup;
134 
135 	/*
136 	 * Create a clone of the object
137 	 */
138 	rv = meta_slot_object_alloc(&slot_object);
139 	if (rv != CKR_OK)
140 		goto cleanup;
141 
142 	(void) get_template_boolean(CKA_TOKEN, pTemplate, ulCount,
143 	    &(object->isToken));
144 
145 	/* Can't create token objects in a read-only session. */
146 	if ((IS_READ_ONLY_SESSION(session->session_flags)) && object->isToken) {
147 		rv = CKR_SESSION_READ_ONLY;
148 		goto cleanup;
149 	}
150 
151 	if (object->isToken) {
152 		CK_OBJECT_HANDLE hNewObject;
153 		CK_ULONG keystore_slotnum;
154 
155 		keystore_slotnum = get_keystore_slotnum();
156 
157 		rv = meta_get_slot_session(keystore_slotnum, &slot_session,
158 		    session->session_flags);
159 		if (rv != CKR_OK)
160 			goto cleanup;
161 
162 		object->tried_create_clone[keystore_slotnum] = B_TRUE;
163 		rv = FUNCLIST(slot_session->fw_st_id)->C_CreateObject(
164 		    slot_session->hSession, pTemplate, ulCount, &hNewObject);
165 		if (rv != CKR_OK) {
166 			goto cleanup;
167 		}
168 
169 		slot_object->hObject = hNewObject;
170 
171 		/*
172 		 * Store slot object and forget about it. If we fail later in
173 		 * this function, let the metaobject cleanup handle it.
174 		 */
175 		object->clones[keystore_slotnum] = slot_object;
176 		object->master_clone_slotnum = keystore_slotnum;
177 		meta_slot_object_activate(slot_object, slot_session, B_TRUE);
178 		slot_object = NULL;
179 
180 		meta_release_slot_session(slot_session);
181 		slot_session = NULL;
182 
183 		/*
184 		 * record if it is a private object or not.
185 		 * if CKA_PRIVATE=true is not specified, it is not
186 		 * a private object
187 		 */
188 		(void) get_template_boolean(CKA_PRIVATE, pTemplate, ulCount,
189 		    &(object->isPrivate));
190 	} else {
191 
192 		CK_OBJECT_HANDLE hNewObject;
193 
194 		/*
195 		 * Create a clone of the object in the first available slot.
196 		 *
197 		 * If creating a clone in a specific slot failed, it will
198 		 * either stop and return the error to the user, or try
199 		 * again in the next available slot until it succeeds.  The
200 		 * decision to stop or continue is made based on the return
201 		 * code.
202 		 */
203 
204 		CK_ULONG slot_num, num_slots;
205 		CK_RV first_rv;
206 
207 		num_slots = meta_slotManager_get_slotcount();
208 
209 		for (slot_num = 0; slot_num < num_slots; slot_num++) {
210 			rv = meta_get_slot_session(slot_num, &slot_session,
211 			    session->session_flags);
212 			if (rv != CKR_OK) {
213 				goto cleanup;
214 			}
215 
216 			object->tried_create_clone[slot_num] = B_TRUE;
217 			rv = FUNCLIST(slot_session->fw_st_id)->C_CreateObject(
218 			    slot_session->hSession, pTemplate, ulCount,
219 			    &hNewObject);
220 			if (rv == CKR_OK) {
221 				break;
222 			}
223 
224 			if (!try_again(rv)) {
225 				goto cleanup;
226 			}
227 
228 			if (slot_num == 0) {
229 				/* save first rv for other errors */
230 				first_rv = rv;
231 			}
232 
233 			meta_release_slot_session(slot_session);
234 			slot_session = NULL;
235 
236 		}
237 
238 		if (rv == CKR_OK) {
239 
240 			slot_object->hObject = hNewObject;
241 			object->clones[slot_num] = slot_object;
242 			object->master_clone_slotnum = slot_num;
243 			meta_slot_object_activate(slot_object, slot_session,
244 			    B_FALSE);
245 			slot_object = NULL;
246 			meta_release_slot_session(slot_session);
247 			slot_session = NULL;
248 		} else {
249 			/*
250 			 * return either first error code or
251 			 * CKR_FUNCTION_FAILED depending on the failure
252 			 */
253 			int i;
254 			for (i = 0; i < num_other_rv; i++) {
255 				if (rv == other_rv[i]) {
256 					rv = CKR_FUNCTION_FAILED;
257 					goto cleanup;
258 				}
259 			}
260 			/* need to return first rv */
261 			rv = first_rv;
262 			goto cleanup;
263 		}
264 	}
265 
266 	/* Get the CKA_EXTRACTABLE and CKA_SENSITIVE attribute */
267 
268 	/* assume object is extractable unless indicated otherwise */
269 	object->isExtractable = B_TRUE;
270 	(void) get_template_boolean(CKA_EXTRACTABLE, pTemplate, ulCount,
271 	    &(object->isExtractable));
272 
273 	/* assume object is not sensitive unless indicated otherwise */
274 	object->isSensitive = B_FALSE;
275 	(void) get_template_boolean(CKA_SENSITIVE, pTemplate, ulCount,
276 	    &(object->isSensitive));
277 
278 	/*
279 	 * always keep a copy of the template for C_CreateObject,
280 	 * so clones can be created on other slots if necessary.
281 	 * This is done even when the CKA_EXTRACTABLE=FALSE flag
282 	 * is set for the object.  The supplied template is
283 	 * "owned" by metaslot.  The application should not be
284 	 * penalized just because metaslot choose to try creating
285 	 * the object in a slot that's not capable of performing
286 	 * any future operation.
287 	 */
288 	rv = get_master_attributes_by_template(pTemplate, ulCount,
289 		&object->attributes, &object->num_attributes);
290 	if (rv == CKR_OK) {
291 		CK_ULONG i;
292 		for (i = 0; i < ulCount; i++) {
293 			rv = attribute_set_value(&(pTemplate[i]),
294 			    object->attributes, object->num_attributes);
295 		}
296 	}
297 
298 	meta_object_activate(object);
299 	*phObject = (CK_OBJECT_HANDLE) object;
300 
301 	REFRELEASE(session);
302 
303 	return (CKR_OK);
304 
305 cleanup:
306 	if (slot_object)
307 		meta_slot_object_dealloc(slot_object);
308 	if (slot_session)
309 		meta_release_slot_session(slot_session);
310 	if (object)
311 		(void) meta_object_dealloc(object, B_TRUE);
312 
313 	REFRELEASE(session);
314 
315 	return (rv);
316 }
317 
318 
319 /*
320  * meta_CopyObject
321  *
322  */
323 CK_RV
324 meta_CopyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
325     CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount,
326     CK_OBJECT_HANDLE_PTR phNewObject)
327 {
328 	CK_RV rv, first_rv;
329 	meta_session_t *session;
330 	meta_object_t *src_object, *dst_object = NULL;
331 	slot_session_t *slot_session = NULL;
332 	slot_object_t *dst_slot_object = NULL;
333 	CK_ULONG i;
334 	slot_object_t *src_slot_object;
335 	CK_ULONG slotnum, num_slots;
336 	boolean_t found;
337 
338 	if (pTemplate == NULL && ulCount != 0)
339 		return (CKR_ARGUMENTS_BAD);
340 	if (phNewObject == NULL)
341 		return (CKR_ARGUMENTS_BAD);
342 
343 	rv = meta_handle2session(hSession, &session);
344 	if (rv != CKR_OK)
345 		return (rv);
346 
347 	rv = meta_handle2object(hObject, &src_object);
348 	if (rv != CKR_OK) {
349 		REFRELEASE(session);
350 		return (rv);
351 	}
352 
353 	rv = meta_object_alloc(session, &dst_object);
354 	if (rv != CKR_OK)
355 		goto finish;
356 
357 	found = get_template_boolean(CKA_TOKEN,
358 	    pTemplate, ulCount, &(dst_object->isToken));
359 	if (!found) {
360 		dst_object->isToken = src_object->isToken;
361 	}
362 
363 	/* Can't create token objects in a read-only session. */
364 	if ((IS_READ_ONLY_SESSION(session->session_flags)) &&
365 	    (dst_object->isToken)) {
366 		rv = CKR_SESSION_READ_ONLY;
367 		goto finish;
368 	}
369 
370 	if (dst_object->isToken) {
371 
372 		/*
373 		 * if the dst object is a token object, and the source
374 		 * object is not, the source object needs to be extractable.
375 		 * Otherwise, the source object needs to reside in the
376 		 * token object slot
377 		 */
378 		if ((!src_object->isExtractable) &&
379 		    (src_object->master_clone_slotnum
380 		    != get_keystore_slotnum())) {
381 			rv = CKR_FUNCTION_FAILED;
382 			goto finish;
383 		}
384 
385 		/* determine if dst is going to be private object or not */
386 		found = get_template_boolean(CKA_PRIVATE,
387 		    pTemplate, ulCount, &(dst_object->isPrivate));
388 		if (!found) {
389 			/* will be the same as the source object */
390 			dst_object->isPrivate = src_object->isPrivate;
391 		}
392 
393 		slotnum = get_keystore_slotnum();
394 	} else {
395 
396 		/* try create the obj in the same slot as the source obj */
397 		slotnum = src_object->master_clone_slotnum;
398 	}
399 
400 	rv = meta_slot_object_alloc(&dst_slot_object);
401 	if (rv != CKR_OK)
402 		goto finish;
403 
404 	rv = meta_get_slot_session(slotnum, &slot_session,
405 	    session->session_flags);
406 	if (rv != CKR_OK)
407 		goto finish;
408 
409 	rv = meta_object_get_clone(src_object, slotnum,
410 		slot_session, &src_slot_object);
411 	if (rv != CKR_OK)
412 		goto finish;
413 
414 	dst_object->tried_create_clone[slotnum] = B_TRUE;
415 	rv = FUNCLIST(slot_session->fw_st_id)->C_CopyObject(
416 	    slot_session->hSession, src_slot_object->hObject, pTemplate,
417 	    ulCount, &(dst_slot_object->hObject));
418 
419 	if (rv != CKR_OK) {
420 		if (dst_object->isToken) {
421 			/*
422 			 * token obj can only be created in the
423 			 * token slot.  No need to try anywhere else
424 			 */
425 			goto finish;
426 		}
427 		if ((!src_object->isExtractable) ||
428 		    ((src_object->isSensitive) && (src_object->isToken) &&
429 		    (!metaslot_auto_key_migrate))) {
430 			/* source object isn't clonable in another slot */
431 			goto finish;
432 		}
433 
434 		if (!try_again(rv)) {
435 			goto finish;
436 		}
437 
438 		first_rv = rv;
439 
440 		meta_release_slot_session(slot_session);
441 		slot_session = NULL;
442 
443 		num_slots = meta_slotManager_get_slotcount();
444 
445 		/* Try operation on other slots if the object is clonable */
446 		for (slotnum = 0; slotnum < num_slots; slotnum++) {
447 
448 			if (slotnum == src_object->master_clone_slotnum) {
449 				/* already tried, don't need to try again */
450 				continue;
451 			}
452 
453 			rv = meta_get_slot_session(slotnum, &slot_session,
454 			    session->session_flags);
455 			if (rv != CKR_OK) {
456 				goto finish;
457 			}
458 
459 			rv = meta_object_get_clone(src_object, slotnum,
460 				slot_session, &src_slot_object);
461 			if (rv != CKR_OK)
462 				goto finish;
463 
464 			dst_object->tried_create_clone[slotnum] = B_TRUE;
465 
466 			rv = FUNCLIST(slot_session->fw_st_id)->C_CopyObject(
467 			    slot_session->hSession, src_slot_object->hObject,
468 			    pTemplate, ulCount, &dst_slot_object->hObject);
469 
470 			if (rv == CKR_OK) {
471 				break;
472 			}
473 
474 			if (!try_again(rv)) {
475 				goto finish;
476 			}
477 			meta_release_slot_session(slot_session);
478 			slot_session = NULL;
479 		}
480 	}
481 
482 	if (rv == CKR_OK) {
483 
484 		rv = meta_object_get_attr(slot_session,
485 		    dst_slot_object->hObject, dst_object);
486 		if (rv != CKR_OK) {
487 			goto finish;
488 		}
489 
490 		if (src_object->attributes != NULL) {
491 
492 			/* Keep a copy of the template for the future */
493 
494 			/*
495 			 * Don't allow attributes to change while
496 			 * we look at them.
497 			 */
498 			(void) pthread_rwlock_rdlock(
499 			    &src_object->attribute_lock);
500 
501 			rv = get_master_attributes_by_duplication(
502 			    src_object->attributes,
503 			    src_object->num_attributes,
504 			    &dst_object->attributes,
505 			    &dst_object->num_attributes);
506 
507 			(void) pthread_rwlock_unlock(
508 			    &src_object->attribute_lock);
509 
510 			if (rv != CKR_OK)
511 				goto finish;
512 
513 			for (i = 0; i < ulCount; i++) {
514 				rv = attribute_set_value(pTemplate + i,
515 				    dst_object->attributes,
516 				    dst_object->num_attributes);
517 
518 				if (rv != CKR_OK)
519 					goto finish;
520 			}
521 		}
522 
523 		meta_slot_object_activate(dst_slot_object,
524 			slot_session, dst_object->isToken);
525 
526 		dst_object->clones[slotnum] = dst_slot_object;
527 		dst_object->master_clone_slotnum = slotnum;
528 		dst_slot_object = NULL; /* for error cleanup */
529 
530 		meta_release_slot_session(slot_session);
531 		slot_session = NULL; /* for error cleanup */
532 
533 	} else {
534 		/*
535 		 * return either first error code or
536 		 * CKR_FUNCTION_FAILED depending on the failure
537 		 */
538 		int j;
539 		for (j = 0; j < num_other_rv; j++) {
540 			if (rv == other_rv[j]) {
541 				rv = CKR_FUNCTION_FAILED;
542 				goto finish;
543 			}
544 		}
545 		/* need to return first rv */
546 		rv = first_rv;
547 		goto finish;
548 	}
549 	meta_object_activate(dst_object);
550 	*phNewObject = (CK_OBJECT_HANDLE) dst_object;
551 
552 finish:
553 	if (rv != CKR_OK) {
554 		if (dst_slot_object)
555 			meta_slot_object_dealloc(dst_slot_object);
556 
557 		if (dst_object)
558 			(void) meta_object_dealloc(dst_object, B_TRUE);
559 
560 		if (slot_session)
561 			meta_release_slot_session(slot_session);
562 	}
563 
564 	OBJRELEASE(src_object);
565 	REFRELEASE(session);
566 
567 	return (rv);
568 }
569 
570 
571 /*
572  * meta_DestroyObject
573  *
574  * This function destroys an object by first removing it from the
575  * list of valid objects for a given session (if session object) or
576  * the global token object list.  And then, calling C_DestroyObject
577  * on all the slots on which we have created a clone of this object.
578  */
579 CK_RV
580 meta_DestroyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject)
581 {
582 	CK_RV rv;
583 	meta_session_t *session;
584 	meta_object_t *object;
585 
586 	rv = meta_handle2session(hSession, &session);
587 	if (rv != CKR_OK)
588 		return (rv);
589 
590 	rv = meta_handle2object(hObject, &object);
591 	if (rv != CKR_OK) {
592 		REFRELEASE(session);
593 		return (rv);
594 	}
595 
596 	/* Can't delete token objects from a read-only session. */
597 	if ((IS_READ_ONLY_SESSION(session->session_flags)) && object->isToken) {
598 		OBJRELEASE(object);
599 		REFRELEASE(session);
600 		return (CKR_SESSION_READ_ONLY);
601 	}
602 
603 	/* Remove object from list of valid meta_objects */
604 	rv = meta_object_deactivate(object, B_FALSE, B_TRUE);
605 
606 	/*
607 	 * Actually call C_DestroyObject on all the slots on which we have
608 	 * created a clone of this object.
609 	 */
610 	if (rv == CKR_OK)
611 		rv = meta_object_dealloc(object, B_TRUE);
612 
613 	REFRELEASE(session);
614 
615 	return (rv);
616 }
617 
618 
619 /*
620  * meta_GetObjectSize
621  *
622  * NOTES:
623  * 1) Because the "size" is so poorly defined in the spec, we have deemed
624  *    it useless and won't support it. This is especially true for the
625  *    metaslot, because the mulitple providers it uses may each interpret
626  *    the size differently.
627  */
628 /* ARGSUSED */
629 CK_RV
630 meta_GetObjectSize(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
631     CK_ULONG_PTR pulSize)
632 {
633 	return (CKR_FUNCTION_NOT_SUPPORTED);
634 }
635 
636 
637 /*
638  * meta_GetAttributeValue
639  *
640  */
641 CK_RV
642 meta_GetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
643     CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount)
644 {
645 	CK_RV rv;
646 	meta_session_t *session;
647 	meta_object_t *object;
648 	CK_ULONG slotnum;
649 	slot_session_t *slot_session;
650 
651 	if (pTemplate == NULL || ulCount < 1)
652 		return (CKR_ARGUMENTS_BAD);
653 
654 	rv = meta_handle2session(hSession, &session);
655 	if (rv != CKR_OK)
656 		return (rv);
657 
658 	rv = meta_handle2object(hObject, &object);
659 	if (rv != CKR_OK) {
660 		REFRELEASE(session);
661 		return (rv);
662 	}
663 
664 	slotnum = object->master_clone_slotnum;
665 
666 	rv = meta_get_slot_session(slotnum, &slot_session,
667 	    session->session_flags);
668 	if (rv == CKR_OK) {
669 		rv = FUNCLIST(slot_session->fw_st_id)->C_GetAttributeValue(
670 		    slot_session->hSession, object->clones[slotnum]->hObject,
671 		    pTemplate, ulCount);
672 
673 		meta_release_slot_session(slot_session);
674 	}
675 
676 	OBJRELEASE(object);
677 	REFRELEASE(session);
678 
679 	return (rv);
680 
681 }
682 
683 
684 /*
685  * meta_SetAttributeValue
686  *
687  * Call C_SetAttributeValue on all the clones.  If the operation fails on
688  * all clones, return the failure.
689  *
690  * If the operation fails on some clones and not the others, delete all the
691  * clones that have failed the operation.  If any of the deleted clone is the
692  * master clone, use one of the remaining clone as the master clone.
693  *
694  * If the operation is successful and the master template already exists,
695  * update the master template with new values.
696  */
697 CK_RV
698 meta_SetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
699     CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount)
700 {
701 	CK_RV rv = CKR_OK, save_rv = CKR_OK;
702 	meta_session_t *session;
703 	meta_object_t *object;
704 	CK_ULONG slotnum, num_slots;
705 	/* Keep track of which slot's SetAttributeValue failed */
706 	boolean_t *clone_failed_op = NULL;
707 	int num_clones = 0, num_clones_failed = 0;
708 	slot_session_t *slot_session;
709 	slot_object_t *slot_object;
710 	boolean_t need_update_master_clone = B_FALSE;
711 
712 	if (pTemplate == NULL || ulCount < 1)
713 		return (CKR_ARGUMENTS_BAD);
714 
715 	rv = meta_handle2session(hSession, &session);
716 	if (rv != CKR_OK)
717 		return (rv);
718 
719 	rv = meta_handle2object(hObject, &object);
720 	if (rv != CKR_OK) {
721 		REFRELEASE(session);
722 		return (rv);
723 	}
724 
725 	if ((IS_READ_ONLY_SESSION(session->session_flags)) &&
726 	    (object->isToken)) {
727 		rv = CKR_SESSION_READ_ONLY;
728 		goto finish;
729 	}
730 
731 	if ((!object->isExtractable) && (object->attributes == NULL)) {
732 		/*
733 		 * object has no clone, just need to do the operation
734 		 * in the master clone slot
735 		 */
736 		slot_session_t *slot_session;
737 		slotnum = object->master_clone_slotnum;
738 
739 		rv = meta_get_slot_session(slotnum, &slot_session,
740 		    session->session_flags);
741 		if (rv == CKR_OK) {
742 			rv = FUNCLIST(slot_session->fw_st_id)->\
743 			    C_SetAttributeValue(slot_session->hSession,
744 			    object->clones[slotnum]->hObject, pTemplate,
745 			    ulCount);
746 
747 			meta_release_slot_session(slot_session);
748 		}
749 		goto finish;
750 	}
751 
752 
753 	num_slots = meta_slotManager_get_slotcount();
754 
755 	/*
756 	 * object might have clones, need to do operation in all clones
757 	 *
758 	 * If the C_SetAttributeValue() call fails in a clone, the
759 	 * clone that failed the operation can not be deleted right
760 	 * away.  The clone with the failed operation is recorded, and
761 	 * the deletion will happen in a separate loop.
762 	 *
763 	 * This is necessary because if ALL the clones failed
764 	 * C_SetAttributeVAlue(), then, the app's call to C_SetAttributeValue()
765 	 * is considered failed, and there shouldn't be any changes to the
766 	 * object, none of the clones should be deleted.
767 	 * On the other hand, if C_SetAttributeValue() fails in some clones
768 	 * and succeeds in other clones, the C_SetAttributeValue() operation
769 	 * is considered successful, and those clones that failed the
770 	 * operation is deleted.
771 	 */
772 	clone_failed_op = calloc(num_slots, sizeof (boolean_t));
773 	if (clone_failed_op == NULL) {
774 		rv = CKR_HOST_MEMORY;
775 		goto finish;
776 	}
777 	for (slotnum = 0; slotnum < num_slots; slotnum++) {
778 		if (object->clones[slotnum] != NULL) {
779 			num_clones++;
780 			rv = meta_get_slot_session(slotnum, &slot_session,
781 			    session->session_flags);
782 			if (rv != CKR_OK) {
783 				goto finish;
784 			}
785 
786 			rv = FUNCLIST(slot_session->fw_st_id)->\
787 			    C_SetAttributeValue(slot_session->hSession,
788 			    object->clones[slotnum]->hObject, pTemplate,
789 			    ulCount);
790 
791 			if (rv != CKR_OK) {
792 				num_clones_failed++;
793 				clone_failed_op[slotnum] = B_TRUE;
794 				if (save_rv == CKR_OK) {
795 					save_rv = rv;
796 				}
797 			}
798 			meta_release_slot_session(slot_session);
799 		}
800 	}
801 
802 	if (num_clones_failed == num_clones) {
803 		/* all operations failed */
804 		rv = save_rv;
805 		goto finish;
806 	}
807 
808 	if (num_clones_failed > 0) {
809 		/*
810 		 * C_SetAttributeValue in some of the clones failed.
811 		 * Find out which ones failed, and delete the clones
812 		 * in those failed slots
813 		 */
814 		for (slotnum = 0; slotnum < num_slots; slotnum++) {
815 			if (clone_failed_op[slotnum]) {
816 
817 				slot_object_t *clone = object->clones[slotnum];
818 
819 				rv = meta_get_slot_session(slotnum,
820 				    &slot_session, session->session_flags);
821 				if (rv == CKR_OK) {
822 					(void) FUNCLIST(
823 					    slot_session->fw_st_id)->
824 					    C_DestroyObject(
825 					    slot_session->hSession,
826 					    clone->hObject);
827 
828 					meta_release_slot_session(slot_session);
829 
830 				}
831 
832 				meta_slot_object_deactivate(clone);
833 				meta_slot_object_dealloc(clone);
834 				object->clones[slotnum] = NULL;
835 
836 				if (slotnum == object->master_clone_slotnum) {
837 					need_update_master_clone = B_TRUE;
838 				}
839 			}
840 		}
841 
842 		if (need_update_master_clone) {
843 			/* make first available clone the master */
844 			for (slotnum = 0; slotnum < num_slots; slotnum++) {
845 				if (object->clones[slotnum]) {
846 					object->master_clone_slotnum = slotnum;
847 					need_update_master_clone = B_FALSE;
848 					break;
849 				}
850 			}
851 
852 		}
853 		if (need_update_master_clone) {
854 			/*
855 			 * something is very wrong, can't continue
856 			 * it should never be this case.
857 			 */
858 			rv = CKR_FUNCTION_FAILED;
859 			goto finish;
860 		}
861 		rv = CKR_OK;
862 	}
863 
864 	/*
865 	 * Update the attribute information we keep in our metaslot object
866 	 */
867 	slot_object = object->clones[object->master_clone_slotnum];
868 	rv = meta_get_slot_session(object->master_clone_slotnum,
869 	    &slot_session, session->session_flags);
870 	if (rv == CKR_OK) {
871 		(void) meta_object_get_attr(slot_session,
872 		    slot_object->hObject, object);
873 		meta_release_slot_session(slot_session);
874 	}
875 
876 	/* if there's a copy of the attributes, keep it up to date */
877 	if (object->attributes != NULL) {
878 
879 		CK_ULONG i;
880 
881 		/* Make sure no one else is looking at attributes. */
882 		(void) pthread_rwlock_wrlock(&object->attribute_lock);
883 
884 		for (i = 0; i < ulCount; i++) {
885 			(void) attribute_set_value(pTemplate + i,
886 			    object->attributes, object->num_attributes);
887 
888 		}
889 		(void) pthread_rwlock_unlock(&object->attribute_lock);
890 	}
891 
892 finish:
893 	if (clone_failed_op) {
894 		free(clone_failed_op);
895 	}
896 	OBJRELEASE(object);
897 	REFRELEASE(session);
898 
899 	return (rv);
900 }
901 
902 static boolean_t
903 meta_object_in_list(meta_object_t *obj, meta_object_t **objs_list, int num_objs)
904 {
905 	int i;
906 
907 	for (i = 0; i < num_objs; i++) {
908 		if (objs_list[i] == obj) {
909 			return (B_TRUE);
910 		}
911 	}
912 	return (B_FALSE);
913 }
914 
915 static CK_RV
916 add_to_search_result(meta_object_t *object, find_objs_info_t *info,
917     int *num_results_alloc)
918 {
919 	/*
920 	 * allocate space for storing results if the currently
921 	 * allocated space is not enough
922 	 */
923 	if (*num_results_alloc <= info->num_matched_objs) {
924 		*num_results_alloc += FIND_OBJ_BUF_SIZE;
925 		info->matched_objs = realloc(info->matched_objs,
926 		    sizeof (meta_object_t *) * (*num_results_alloc));
927 		if (info->matched_objs == NULL) {
928 			return (CKR_HOST_MEMORY);
929 		}
930 	}
931 	(info->matched_objs)[(info->num_matched_objs)++] = object;
932 	return (CKR_OK);
933 }
934 
935 static CK_RV
936 process_find_results(CK_OBJECT_HANDLE *results, CK_ULONG num_results,
937     int *num_results_allocated, find_objs_info_t *info, CK_ULONG slotnum,
938     boolean_t token_only, slot_session_t *slot_session,
939     meta_session_t *session)
940 {
941 	CK_ULONG i;
942 	meta_object_t *object;
943 	CK_RV rv;
944 
945 	for (i = 0; i < num_results; i++) {
946 
947 		object = meta_object_find_by_handle(results[i], slotnum,
948 		    token_only);
949 
950 		/*
951 		 * a token object is found from the keystore,
952 		 * need to create a meta object for it
953 		 */
954 		if (object == NULL) {
955 			slot_object_t *slot_object;
956 
957 			rv = meta_object_alloc(session, &object);
958 			if (rv != CKR_OK) {
959 				return (rv);
960 			}
961 
962 			rv = meta_slot_object_alloc(&slot_object);
963 			if (rv != CKR_OK) {
964 				(void) meta_object_dealloc(object, B_TRUE);
965 				return (rv);
966 			}
967 
968 			slot_object->hObject = results[i];
969 			object->master_clone_slotnum = slotnum;
970 			object->clones[slotnum] = slot_object;
971 
972 			/* get in the attributes we keep in meta_object */
973 
974 			rv = meta_object_get_attr(slot_session,
975 			    slot_object->hObject, object);
976 			if (rv != CKR_OK) {
977 				(void) meta_object_dealloc(object, B_TRUE);
978 				return (rv);
979 			}
980 
981 			meta_slot_object_activate(slot_object, slot_session,
982 			    B_TRUE);
983 			meta_object_activate(object);
984 			slot_object = NULL;
985 		}
986 
987 		if (!meta_object_in_list(object, info->matched_objs,
988 		    info->num_matched_objs)) {
989 			rv = add_to_search_result(object, info,
990 			    num_results_allocated);
991 			if (rv != CKR_OK) {
992 				return (rv);
993 			}
994 		}
995 	}
996 	return (CKR_OK);
997 }
998 
999 static CK_RV
1000 meta_search_for_objects(meta_session_t *session, find_objs_info_t *info,
1001     slot_session_t *slot_session, CK_ATTRIBUTE_PTR pTemplate,
1002     CK_ULONG ulCount, CK_ULONG slotnum, boolean_t token_only,
1003     int *num_results_alloc)
1004 {
1005 	CK_ULONG tmp_num_results;
1006 	CK_OBJECT_HANDLE tmp_results[FIND_OBJ_BUF_SIZE];
1007 	CK_SESSION_HANDLE hSession = slot_session->hSession;
1008 	CK_RV rv;
1009 	CK_SLOT_ID fw_st_id = slot_session->fw_st_id;
1010 
1011 	rv = FUNCLIST(fw_st_id)->C_FindObjectsInit(hSession,
1012 	    pTemplate, ulCount);
1013 
1014 	if (rv != CKR_OK) {
1015 		return (rv);
1016 	}
1017 
1018 	tmp_num_results = 0;
1019 	rv = FUNCLIST(fw_st_id)->C_FindObjects(hSession, tmp_results,
1020 	    FIND_OBJ_BUF_SIZE, &tmp_num_results);
1021 	if (rv != CKR_OK) {
1022 		return (rv);
1023 	}
1024 
1025 	rv = process_find_results(tmp_results, tmp_num_results,
1026 	    num_results_alloc, info, slotnum, token_only,
1027 	    slot_session, session);
1028 	if (rv != CKR_OK) {
1029 		return (rv);
1030 	}
1031 
1032 	while (tmp_num_results == FIND_OBJ_BUF_SIZE) {
1033 		/* might be more results, need to call C_FindObjects again */
1034 		rv = FUNCLIST(fw_st_id)->C_FindObjects(hSession, tmp_results,
1035 		    FIND_OBJ_BUF_SIZE, &tmp_num_results);
1036 		if (rv != CKR_OK) {
1037 			return (rv);
1038 		}
1039 
1040 		rv = process_find_results(tmp_results, tmp_num_results,
1041 		    num_results_alloc, info, slotnum, token_only,
1042 		    slot_session, session);
1043 		if (rv != CKR_OK) {
1044 			return (rv);
1045 		}
1046 	}
1047 
1048 	rv = FUNCLIST(fw_st_id)->C_FindObjectsFinal(hSession);
1049 	return (rv);
1050 }
1051 
1052 
1053 /*
1054  * meta_FindObjectsInit
1055  *
1056  * This function actually will do ALL the work of searching for objects
1057  * that match all requirements specified in the template.
1058  *
1059  * Objects that matched the template will be stored in the
1060  * session's data structure.  When the subsequent C_FindObjects()
1061  * calls are made, results saved will be returned.
1062  *
1063  */
1064 CK_RV
1065 meta_FindObjectsInit(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate,
1066     CK_ULONG ulCount)
1067 {
1068 	CK_RV rv;
1069 	meta_session_t *session;
1070 	CK_ULONG slot_num = 0;
1071 	boolean_t have_token_attr, tokenTrue = B_FALSE;
1072 	slot_session_t *slot_find_session = NULL;
1073 	int num_results_allocated = 0;
1074 	CK_ULONG keystore_slotnum;
1075 
1076 	rv = meta_handle2session(hSession, &session);
1077 	if (rv != CKR_OK)
1078 		return (rv);
1079 
1080 	if ((session->find_objs_info).op_active) {
1081 		REFRELEASE(session);
1082 		return (CKR_OPERATION_ACTIVE);
1083 	}
1084 
1085 	(session->find_objs_info).op_active = B_TRUE;
1086 
1087 	REFRELEASE(session);
1088 
1089 	/* see if the template indicates token object only or not */
1090 	have_token_attr = get_template_boolean(CKA_TOKEN, pTemplate, ulCount,
1091 	    &tokenTrue);
1092 
1093 	keystore_slotnum = get_keystore_slotnum();
1094 
1095 	if (have_token_attr && tokenTrue) {
1096 
1097 
1098 		/*
1099 		 * only interested in token objects, just need to search
1100 		 * token object slot
1101 		 */
1102 		rv = meta_get_slot_session(keystore_slotnum,
1103 			&slot_find_session, session->session_flags);
1104 		if (rv != CKR_OK)  {
1105 			goto finish;
1106 		}
1107 		rv = meta_search_for_objects(session,
1108 		    &(session->find_objs_info), slot_find_session, pTemplate,
1109 		    ulCount, keystore_slotnum, B_TRUE, &num_results_allocated);
1110 		if (rv != CKR_OK) {
1111 			goto finish;
1112 		}
1113 	} else {
1114 		CK_ULONG num_slots = meta_slotManager_get_slotcount();
1115 		for (slot_num = 0; slot_num < num_slots; slot_num++) {
1116 			rv = meta_get_slot_session(slot_num,
1117 			    &slot_find_session, session->session_flags);
1118 			if (rv != CKR_OK) {
1119 				goto finish;
1120 			}
1121 
1122 			/*
1123 			 * if the slot is NOT the token object slot, and
1124 			 * CKA_TOKEN is not specified, need to specified
1125 			 * it to be false explicitly.  This will prevent
1126 			 * us from using token objects that doesn't
1127 			 * belong to the token slot in the case that
1128 			 * more than one slot supports token objects.
1129 			 */
1130 
1131 			if ((slot_num != keystore_slotnum) &&
1132 			    (!have_token_attr)) {
1133 				CK_BBOOL false = FALSE;
1134 				CK_ATTRIBUTE_PTR newTemplate;
1135 
1136 				newTemplate = malloc((ulCount + 1) *
1137 				    sizeof (CK_ATTRIBUTE));
1138 				if (newTemplate == NULL) {
1139 					rv = CKR_HOST_MEMORY;
1140 					goto finish;
1141 				}
1142 				(void) memcpy(newTemplate + 1, pTemplate,
1143 				    ulCount * sizeof (CK_ATTRIBUTE));
1144 				newTemplate[0].type = CKA_TOKEN;
1145 				newTemplate[0].pValue = &false;
1146 				newTemplate[0].ulValueLen = sizeof (false);
1147 
1148 				rv = meta_search_for_objects(session,
1149 				    &(session->find_objs_info),
1150 				    slot_find_session, newTemplate,
1151 				    ulCount+1, slot_num, B_FALSE,
1152 				    &num_results_allocated);
1153 				free(newTemplate);
1154 			} else {
1155 				rv = meta_search_for_objects(session,
1156 				    &(session->find_objs_info),
1157 				    slot_find_session, pTemplate, ulCount,
1158 				    slot_num, B_FALSE,
1159 				    &num_results_allocated);
1160 			}
1161 
1162 			if (rv != CKR_OK) {
1163 				goto finish;
1164 			}
1165 			meta_release_slot_session(slot_find_session);
1166 			slot_find_session = NULL;
1167 		}
1168 	}
1169 
1170 finish:
1171 	if (slot_find_session != NULL) {
1172 		meta_release_slot_session(slot_find_session);
1173 	}
1174 	if (rv != CKR_OK) {
1175 		(void) pthread_rwlock_wrlock(&session->session_lock);
1176 		if (((session->find_objs_info).matched_objs) != NULL) {
1177 			free((session->find_objs_info).matched_objs);
1178 		}
1179 		bzero(&(session->find_objs_info), sizeof (find_objs_info_t));
1180 		(void) pthread_rwlock_unlock(&(session->session_lock));
1181 	}
1182 
1183 	return (rv);
1184 }
1185 
1186 /*
1187  * meta_FindObjects
1188  *
1189  * This function actually doesn't do any real work in search for the
1190  * matching object.  All the work is done in FindObjectsInit().  This
1191  * function will only return the matching objects store in the session's
1192  * "find_objs_info" variable.
1193  *
1194  */
1195 CK_RV
1196 meta_FindObjects(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE_PTR phObject,
1197     CK_ULONG ulMaxObjectCount, CK_ULONG_PTR pulObjectCount)
1198 {
1199 	CK_RV rv;
1200 	find_objs_info_t *info;
1201 	CK_ULONG num_objs_found = 0;
1202 	meta_object_t *obj;
1203 	meta_session_t *session;
1204 	int i;
1205 
1206 	rv = meta_handle2session(hSession, &session);
1207 	if (rv != CKR_OK)
1208 		return (rv);
1209 
1210 	info = &(session->find_objs_info);
1211 
1212 	if (!(info->op_active)) {
1213 		REFRELEASE(session);
1214 		return (CKR_OPERATION_NOT_INITIALIZED);
1215 	}
1216 
1217 	for (i = info->next_result_index;
1218 	    ((num_objs_found < ulMaxObjectCount) &&
1219 	    (i < info->num_matched_objs));
1220 	    i++) {
1221 		obj = info->matched_objs[i];
1222 		if (obj != NULL) {
1223 			/* sanity check to see if object is still valid */
1224 			(void) pthread_rwlock_rdlock(&obj->object_lock);
1225 			if (obj->magic_marker == METASLOT_OBJECT_MAGIC) {
1226 				phObject[num_objs_found++] =
1227 				    (CK_OBJECT_HANDLE)obj;
1228 			}
1229 			(void) pthread_rwlock_unlock(&obj->object_lock);
1230 		}
1231 	}
1232 	info->next_result_index = i;
1233 	*pulObjectCount	= num_objs_found;
1234 	REFRELEASE(session);
1235 	return (rv);
1236 }
1237 
1238 
1239 /*
1240  * meta_FindObjectsFinal
1241  *
1242  */
1243 CK_RV
1244 meta_FindObjectsFinal(CK_SESSION_HANDLE hSession)
1245 {
1246 	CK_RV rv;
1247 	find_objs_info_t *info;
1248 	meta_session_t *session;
1249 
1250 	rv = meta_handle2session(hSession, &session);
1251 	if (rv != CKR_OK)
1252 		return (rv);
1253 
1254 	info = &(session->find_objs_info);
1255 
1256 	if (!info->op_active) {
1257 		REFRELEASE(session);
1258 		return (CKR_OPERATION_NOT_INITIALIZED);
1259 	}
1260 
1261 	if (info->matched_objs) {
1262 		free(info->matched_objs);
1263 	}
1264 
1265 	bzero(info, sizeof (find_objs_info_t));
1266 	REFRELEASE(session);
1267 	return (rv);
1268 }
1269