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