xref: /illumos-gate/usr/src/lib/pkcs11/libpkcs11/common/metaObject.c (revision f48205be61a214698b763ff550ab9e657525104c)
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 	/*
152 	 * Set to true (private object) if template has CKA_PRIVATE=true;
153 	 * otherwise, it is false (public object).
154 	 */
155 	(void) get_template_boolean(CKA_PRIVATE, pTemplate, ulCount,
156 	    &(object->isPrivate));
157 
158 	/* Assume object is extractable unless template has otherwise */
159 	object->isExtractable = B_TRUE;
160 	(void) get_template_boolean(CKA_EXTRACTABLE, pTemplate, ulCount,
161 	    &(object->isExtractable));
162 
163 	/*
164 	 * Set to true (sensitive object) if template has CKA_SENSITIVE=true;
165 	 * otherwise, it is false.
166 	 */
167 	(void) get_template_boolean(CKA_SENSITIVE, pTemplate, ulCount,
168 	    &(object->isSensitive));
169 
170 	/*
171 	 * Check if this can be a FreeObject.
172 	 *
173 	 * For creating objects, this check is mostly for preventing
174 	 * non-keystore hardware from creating CKA_PRIVATE objects without
175 	 * logging in.
176 	 */
177 
178 	if (meta_freeobject_check(session, object, NULL, pTemplate, ulCount,
179 		NULL)) {
180 		/*
181 		 * Make sure we are logged into the keystore if this is a
182 		 * private freetoken object.
183 		 */
184 		if (object->isPrivate && !metaslot_logged_in())
185 			return (CKR_USER_NOT_LOGGED_IN);
186 
187 		if (!meta_freeobject_set(object, pTemplate, ulCount, B_TRUE))
188 			goto cleanup;
189 	}
190 
191 	/* Can't create token objects in a read-only session. */
192 	if ((IS_READ_ONLY_SESSION(session->session_flags)) && object->isToken) {
193 		rv = CKR_SESSION_READ_ONLY;
194 		goto cleanup;
195 	}
196 
197 	keystore_slotnum = get_keystore_slotnum();
198 
199 	if (object->isToken || object->isFreeToken == FREE_ENABLED) {
200 
201 		/*
202 		 * If this is a token object or a FreeToken then create it
203 		 * on the keystore slot.
204 		 */
205 
206 		slot_num = keystore_slotnum;
207 		rv = meta_get_slot_session(slot_num, &slot_session,
208 		    session->session_flags);
209 		if (rv != CKR_OK)
210 			goto cleanup;
211 
212 		object->tried_create_clone[slot_num] = B_TRUE;
213 		rv = FUNCLIST(slot_session->fw_st_id)->C_CreateObject(
214 			slot_session->hSession, pTemplate, ulCount,
215 			    &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 	}
382 
383 	/* Can't create token objects in a read-only session. */
384 	if ((IS_READ_ONLY_SESSION(session->session_flags)) &&
385 	    (dst_object->isToken)) {
386 		rv = CKR_SESSION_READ_ONLY;
387 		goto finish;
388 	}
389 
390 	if (dst_object->isToken) {
391 
392 		/*
393 		 * if the dst object is a token object, and the source
394 		 * object is not, the source object needs to be extractable.
395 		 * Otherwise, the source object needs to reside in the
396 		 * token object slot
397 		 */
398 		if ((!src_object->isExtractable) &&
399 		    (src_object->master_clone_slotnum
400 		    != get_keystore_slotnum())) {
401 			rv = CKR_FUNCTION_FAILED;
402 			goto finish;
403 		}
404 
405 		/* determine if dst is going to be private object or not */
406 		found = get_template_boolean(CKA_PRIVATE,
407 		    pTemplate, ulCount, &(dst_object->isPrivate));
408 		if (!found) {
409 			/* will be the same as the source object */
410 			dst_object->isPrivate = src_object->isPrivate;
411 		}
412 
413 		slotnum = get_keystore_slotnum();
414 	} else {
415 
416 		/* try create the obj in the same slot as the source obj */
417 		slotnum = src_object->master_clone_slotnum;
418 	}
419 
420 	rv = meta_slot_object_alloc(&dst_slot_object);
421 	if (rv != CKR_OK)
422 		goto finish;
423 
424 	rv = meta_get_slot_session(slotnum, &slot_session,
425 	    session->session_flags);
426 	if (rv != CKR_OK)
427 		goto finish;
428 
429 	rv = meta_object_get_clone(src_object, slotnum,
430 		slot_session, &src_slot_object);
431 	if (rv != CKR_OK)
432 		goto finish;
433 
434 	dst_object->tried_create_clone[slotnum] = B_TRUE;
435 	rv = FUNCLIST(slot_session->fw_st_id)->C_CopyObject(
436 	    slot_session->hSession, src_slot_object->hObject, pTemplate,
437 	    ulCount, &(dst_slot_object->hObject));
438 
439 	if (rv != CKR_OK) {
440 		if (dst_object->isToken) {
441 			/*
442 			 * token obj can only be created in the
443 			 * token slot.  No need to try anywhere else
444 			 */
445 			goto finish;
446 		}
447 		if ((!src_object->isExtractable) ||
448 		    ((src_object->isSensitive) && (src_object->isToken) &&
449 		    (!metaslot_auto_key_migrate))) {
450 			/* source object isn't clonable in another slot */
451 			goto finish;
452 		}
453 
454 		if (!try_again(rv)) {
455 			goto finish;
456 		}
457 
458 		first_rv = rv;
459 
460 		meta_release_slot_session(slot_session);
461 		slot_session = NULL;
462 
463 		num_slots = meta_slotManager_get_slotcount();
464 
465 		/* Try operation on other slots if the object is clonable */
466 		for (slotnum = 0; slotnum < num_slots; slotnum++) {
467 
468 			if (slotnum == src_object->master_clone_slotnum) {
469 				/* already tried, don't need to try again */
470 				continue;
471 			}
472 
473 			rv = meta_get_slot_session(slotnum, &slot_session,
474 			    session->session_flags);
475 			if (rv != CKR_OK) {
476 				goto finish;
477 			}
478 
479 			rv = meta_object_get_clone(src_object, slotnum,
480 				slot_session, &src_slot_object);
481 			if (rv != CKR_OK)
482 				goto finish;
483 
484 			dst_object->tried_create_clone[slotnum] = B_TRUE;
485 
486 			rv = FUNCLIST(slot_session->fw_st_id)->C_CopyObject(
487 			    slot_session->hSession, src_slot_object->hObject,
488 			    pTemplate, ulCount, &dst_slot_object->hObject);
489 
490 			if (rv == CKR_OK) {
491 				break;
492 			}
493 
494 			if (!try_again(rv)) {
495 				goto finish;
496 			}
497 			meta_release_slot_session(slot_session);
498 			slot_session = NULL;
499 		}
500 	}
501 
502 	if (rv == CKR_OK) {
503 
504 		rv = meta_object_get_attr(slot_session,
505 		    dst_slot_object->hObject, dst_object);
506 		if (rv != CKR_OK) {
507 			goto finish;
508 		}
509 
510 		if (src_object->attributes != NULL) {
511 
512 			/* Keep a copy of the template for the future */
513 
514 			/*
515 			 * Don't allow attributes to change while
516 			 * we look at them.
517 			 */
518 			(void) pthread_rwlock_rdlock(
519 			    &src_object->attribute_lock);
520 
521 			rv = get_master_attributes_by_duplication(
522 			    src_object->attributes,
523 			    src_object->num_attributes,
524 			    &dst_object->attributes,
525 			    &dst_object->num_attributes);
526 
527 			(void) pthread_rwlock_unlock(
528 			    &src_object->attribute_lock);
529 
530 			if (rv != CKR_OK)
531 				goto finish;
532 
533 			for (i = 0; i < ulCount; i++) {
534 				rv = attribute_set_value(pTemplate + i,
535 				    dst_object->attributes,
536 				    dst_object->num_attributes);
537 
538 				if (rv != CKR_OK)
539 					goto finish;
540 			}
541 		}
542 
543 		meta_slot_object_activate(dst_slot_object,
544 			slot_session, dst_object->isToken);
545 
546 		dst_object->clones[slotnum] = dst_slot_object;
547 		dst_object->master_clone_slotnum = slotnum;
548 		dst_slot_object = NULL; /* for error cleanup */
549 
550 		meta_release_slot_session(slot_session);
551 		slot_session = NULL; /* for error cleanup */
552 
553 	} else {
554 		/*
555 		 * return either first error code or
556 		 * CKR_FUNCTION_FAILED depending on the failure
557 		 */
558 		int j;
559 		for (j = 0; j < num_other_rv; j++) {
560 			if (rv == other_rv[j]) {
561 				rv = CKR_FUNCTION_FAILED;
562 				goto finish;
563 			}
564 		}
565 		/* need to return first rv */
566 		rv = first_rv;
567 		goto finish;
568 	}
569 	meta_object_activate(dst_object);
570 	*phNewObject = (CK_OBJECT_HANDLE) dst_object;
571 
572 finish:
573 	if (rv != CKR_OK) {
574 		if (dst_slot_object)
575 			meta_slot_object_dealloc(dst_slot_object);
576 
577 		if (dst_object)
578 			(void) meta_object_dealloc(dst_object, B_TRUE);
579 
580 		if (slot_session)
581 			meta_release_slot_session(slot_session);
582 	}
583 
584 	OBJRELEASE(src_object);
585 	REFRELEASE(session);
586 
587 	return (rv);
588 }
589 
590 
591 /*
592  * meta_DestroyObject
593  *
594  * This function destroys an object by first removing it from the
595  * list of valid objects for a given session (if session object) or
596  * the global token object list.  And then, calling C_DestroyObject
597  * on all the slots on which we have created a clone of this object.
598  */
599 CK_RV
600 meta_DestroyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject)
601 {
602 	CK_RV rv;
603 	meta_session_t *session;
604 	meta_object_t *object;
605 
606 	rv = meta_handle2session(hSession, &session);
607 	if (rv != CKR_OK)
608 		return (rv);
609 
610 	rv = meta_handle2object(hObject, &object);
611 	if (rv != CKR_OK) {
612 		REFRELEASE(session);
613 		return (rv);
614 	}
615 
616 	/* Can't delete token objects from a read-only session. */
617 	if ((IS_READ_ONLY_SESSION(session->session_flags)) && object->isToken) {
618 		OBJRELEASE(object);
619 		REFRELEASE(session);
620 		return (CKR_SESSION_READ_ONLY);
621 	}
622 
623 	/* Remove object from list of valid meta_objects */
624 	rv = meta_object_deactivate(object, B_FALSE, B_TRUE);
625 
626 	/*
627 	 * Actually call C_DestroyObject on all the slots on which we have
628 	 * created a clone of this object.
629 	 */
630 	if (rv == CKR_OK)
631 		rv = meta_object_dealloc(object, B_TRUE);
632 
633 	REFRELEASE(session);
634 
635 	return (rv);
636 }
637 
638 
639 /*
640  * meta_GetObjectSize
641  *
642  * NOTES:
643  * 1) Because the "size" is so poorly defined in the spec, we have deemed
644  *    it useless and won't support it. This is especially true for the
645  *    metaslot, because the mulitple providers it uses may each interpret
646  *    the size differently.
647  */
648 /* ARGSUSED */
649 CK_RV
650 meta_GetObjectSize(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
651     CK_ULONG_PTR pulSize)
652 {
653 	return (CKR_FUNCTION_NOT_SUPPORTED);
654 }
655 
656 
657 /*
658  * meta_GetAttributeValue
659  *
660  */
661 CK_RV
662 meta_GetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
663     CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount)
664 {
665 	CK_RV rv;
666 	meta_session_t *session;
667 	meta_object_t *object;
668 	CK_ULONG slotnum;
669 	slot_session_t *slot_session;
670 
671 	if (pTemplate == NULL || ulCount < 1)
672 		return (CKR_ARGUMENTS_BAD);
673 
674 	rv = meta_handle2session(hSession, &session);
675 	if (rv != CKR_OK)
676 		return (rv);
677 
678 	rv = meta_handle2object(hObject, &object);
679 	if (rv != CKR_OK) {
680 		REFRELEASE(session);
681 		return (rv);
682 	}
683 
684 	slotnum = object->master_clone_slotnum;
685 
686 	rv = meta_get_slot_session(slotnum, &slot_session,
687 	    session->session_flags);
688 	if (rv == CKR_OK) {
689 		rv = FUNCLIST(slot_session->fw_st_id)->C_GetAttributeValue(
690 		    slot_session->hSession, object->clones[slotnum]->hObject,
691 		    pTemplate, ulCount);
692 
693 		meta_release_slot_session(slot_session);
694 	}
695 
696 	OBJRELEASE(object);
697 	REFRELEASE(session);
698 
699 	return (rv);
700 
701 }
702 
703 
704 /*
705  * meta_SetAttributeValue
706  *
707  * Call C_SetAttributeValue on all the clones.  If the operation fails on
708  * all clones, return the failure.
709  *
710  * If the operation fails on some clones and not the others, delete all the
711  * clones that have failed the operation.  If any of the deleted clone is the
712  * master clone, use one of the remaining clone as the master clone.
713  *
714  * If the operation is successful and the master template already exists,
715  * update the master template with new values.
716  */
717 CK_RV
718 meta_SetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
719     CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount)
720 {
721 	CK_RV rv = CKR_OK, save_rv = CKR_OK;
722 	meta_session_t *session;
723 	meta_object_t *object;
724 	CK_ULONG slotnum, num_slots;
725 	/* Keep track of which slot's SetAttributeValue failed */
726 	boolean_t *clone_failed_op = NULL;
727 	int num_clones = 0, num_clones_failed = 0;
728 	slot_session_t *slot_session;
729 	slot_object_t *slot_object;
730 	boolean_t need_update_master_clone = B_FALSE;
731 
732 	if (pTemplate == NULL || ulCount < 1)
733 		return (CKR_ARGUMENTS_BAD);
734 
735 	rv = meta_handle2session(hSession, &session);
736 	if (rv != CKR_OK)
737 		return (rv);
738 
739 	rv = meta_handle2object(hObject, &object);
740 	if (rv != CKR_OK) {
741 		REFRELEASE(session);
742 		return (rv);
743 	}
744 
745 	if ((IS_READ_ONLY_SESSION(session->session_flags)) &&
746 	    (object->isToken)) {
747 		rv = CKR_SESSION_READ_ONLY;
748 		goto finish;
749 	}
750 
751 	if ((!object->isExtractable) && (object->attributes == NULL)) {
752 		/*
753 		 * object has no clone, just need to do the operation
754 		 * in the master clone slot
755 		 */
756 		slot_session_t *slot_session;
757 		slotnum = object->master_clone_slotnum;
758 
759 		rv = meta_get_slot_session(slotnum, &slot_session,
760 		    session->session_flags);
761 		if (rv == CKR_OK) {
762 			rv = FUNCLIST(slot_session->fw_st_id)->\
763 			    C_SetAttributeValue(slot_session->hSession,
764 			    object->clones[slotnum]->hObject, pTemplate,
765 			    ulCount);
766 
767 			meta_release_slot_session(slot_session);
768 		}
769 		goto finish;
770 	}
771 
772 
773 	num_slots = meta_slotManager_get_slotcount();
774 
775 	/*
776 	 * object might have clones, need to do operation in all clones
777 	 *
778 	 * If the C_SetAttributeValue() call fails in a clone, the
779 	 * clone that failed the operation can not be deleted right
780 	 * away.  The clone with the failed operation is recorded, and
781 	 * the deletion will happen in a separate loop.
782 	 *
783 	 * This is necessary because if ALL the clones failed
784 	 * C_SetAttributeVAlue(), then, the app's call to C_SetAttributeValue()
785 	 * is considered failed, and there shouldn't be any changes to the
786 	 * object, none of the clones should be deleted.
787 	 * On the other hand, if C_SetAttributeValue() fails in some clones
788 	 * and succeeds in other clones, the C_SetAttributeValue() operation
789 	 * is considered successful, and those clones that failed the
790 	 * operation is deleted.
791 	 */
792 	clone_failed_op = calloc(num_slots, sizeof (boolean_t));
793 	if (clone_failed_op == NULL) {
794 		rv = CKR_HOST_MEMORY;
795 		goto finish;
796 	}
797 	for (slotnum = 0; slotnum < num_slots; slotnum++) {
798 		if (object->clones[slotnum] != NULL) {
799 			num_clones++;
800 			rv = meta_get_slot_session(slotnum, &slot_session,
801 			    session->session_flags);
802 			if (rv != CKR_OK) {
803 				goto finish;
804 			}
805 
806 			rv = FUNCLIST(slot_session->fw_st_id)->\
807 			    C_SetAttributeValue(slot_session->hSession,
808 			    object->clones[slotnum]->hObject, pTemplate,
809 			    ulCount);
810 
811 			if (rv != CKR_OK) {
812 				num_clones_failed++;
813 				clone_failed_op[slotnum] = B_TRUE;
814 				if (save_rv == CKR_OK) {
815 					save_rv = rv;
816 				}
817 			}
818 			meta_release_slot_session(slot_session);
819 		}
820 	}
821 
822 	if (num_clones_failed == num_clones) {
823 		/* all operations failed */
824 		rv = save_rv;
825 		goto finish;
826 	}
827 
828 	if (num_clones_failed > 0) {
829 		/*
830 		 * C_SetAttributeValue in some of the clones failed.
831 		 * Find out which ones failed, and delete the clones
832 		 * in those failed slots
833 		 */
834 		for (slotnum = 0; slotnum < num_slots; slotnum++) {
835 			if (clone_failed_op[slotnum]) {
836 
837 				slot_object_t *clone = object->clones[slotnum];
838 
839 				rv = meta_get_slot_session(slotnum,
840 				    &slot_session, session->session_flags);
841 				if (rv == CKR_OK) {
842 					(void) FUNCLIST(
843 					    slot_session->fw_st_id)->
844 					    C_DestroyObject(
845 					    slot_session->hSession,
846 					    clone->hObject);
847 
848 					meta_release_slot_session(slot_session);
849 
850 				}
851 
852 				meta_slot_object_deactivate(clone);
853 				meta_slot_object_dealloc(clone);
854 				object->clones[slotnum] = NULL;
855 
856 				if (slotnum == object->master_clone_slotnum) {
857 					need_update_master_clone = B_TRUE;
858 				}
859 			}
860 		}
861 
862 		if (need_update_master_clone) {
863 			/* make first available clone the master */
864 			for (slotnum = 0; slotnum < num_slots; slotnum++) {
865 				if (object->clones[slotnum]) {
866 					object->master_clone_slotnum = slotnum;
867 					need_update_master_clone = B_FALSE;
868 					break;
869 				}
870 			}
871 
872 		}
873 		if (need_update_master_clone) {
874 			/*
875 			 * something is very wrong, can't continue
876 			 * it should never be this case.
877 			 */
878 			rv = CKR_FUNCTION_FAILED;
879 			goto finish;
880 		}
881 		rv = CKR_OK;
882 	}
883 
884 	/*
885 	 * Update the attribute information we keep in our metaslot object
886 	 */
887 	slot_object = object->clones[object->master_clone_slotnum];
888 	rv = meta_get_slot_session(object->master_clone_slotnum,
889 	    &slot_session, session->session_flags);
890 	if (rv == CKR_OK) {
891 		(void) meta_object_get_attr(slot_session,
892 		    slot_object->hObject, object);
893 		meta_release_slot_session(slot_session);
894 	}
895 
896 	/* if there's a copy of the attributes, keep it up to date */
897 	if (object->attributes != NULL) {
898 
899 		CK_ULONG i;
900 
901 		/* Make sure no one else is looking at attributes. */
902 		(void) pthread_rwlock_wrlock(&object->attribute_lock);
903 
904 		for (i = 0; i < ulCount; i++) {
905 			(void) attribute_set_value(pTemplate + i,
906 			    object->attributes, object->num_attributes);
907 
908 		}
909 		(void) pthread_rwlock_unlock(&object->attribute_lock);
910 	}
911 
912 finish:
913 	if (clone_failed_op) {
914 		free(clone_failed_op);
915 	}
916 	OBJRELEASE(object);
917 	REFRELEASE(session);
918 
919 	return (rv);
920 }
921 
922 static boolean_t
923 meta_object_in_list(meta_object_t *obj, meta_object_t **objs_list, int num_objs)
924 {
925 	int i;
926 
927 	for (i = 0; i < num_objs; i++) {
928 		if (objs_list[i] == obj) {
929 			return (B_TRUE);
930 		}
931 	}
932 	return (B_FALSE);
933 }
934 
935 static CK_RV
936 add_to_search_result(meta_object_t *object, find_objs_info_t *info,
937     int *num_results_alloc)
938 {
939 	/*
940 	 * allocate space for storing results if the currently
941 	 * allocated space is not enough
942 	 */
943 	if (*num_results_alloc <= info->num_matched_objs) {
944 		*num_results_alloc += FIND_OBJ_BUF_SIZE;
945 		info->matched_objs = realloc(info->matched_objs,
946 		    sizeof (meta_object_t *) * (*num_results_alloc));
947 		if (info->matched_objs == NULL) {
948 			return (CKR_HOST_MEMORY);
949 		}
950 	}
951 	(info->matched_objs)[(info->num_matched_objs)++] = object;
952 	return (CKR_OK);
953 }
954 
955 static CK_RV
956 process_find_results(CK_OBJECT_HANDLE *results, CK_ULONG num_results,
957     int *num_results_allocated, find_objs_info_t *info, CK_ULONG slotnum,
958     boolean_t token_only, slot_session_t *slot_session,
959     meta_session_t *session)
960 {
961 	CK_ULONG i;
962 	meta_object_t *object;
963 	CK_RV rv;
964 
965 	for (i = 0; i < num_results; i++) {
966 
967 		object = meta_object_find_by_handle(results[i], slotnum,
968 		    token_only);
969 
970 		/*
971 		 * a token object is found from the keystore,
972 		 * need to create a meta object for it
973 		 */
974 		if (object == NULL) {
975 			slot_object_t *slot_object;
976 
977 			rv = meta_object_alloc(session, &object);
978 			if (rv != CKR_OK) {
979 				return (rv);
980 			}
981 
982 			rv = meta_slot_object_alloc(&slot_object);
983 			if (rv != CKR_OK) {
984 				(void) meta_object_dealloc(object, B_TRUE);
985 				return (rv);
986 			}
987 
988 			slot_object->hObject = results[i];
989 			object->master_clone_slotnum = slotnum;
990 			object->clones[slotnum] = slot_object;
991 
992 			/* get in the attributes we keep in meta_object */
993 
994 			rv = meta_object_get_attr(slot_session,
995 			    slot_object->hObject, object);
996 			if (rv != CKR_OK) {
997 				(void) meta_object_dealloc(object, B_TRUE);
998 				return (rv);
999 			}
1000 
1001 			meta_slot_object_activate(slot_object, slot_session,
1002 			    B_TRUE);
1003 			meta_object_activate(object);
1004 			slot_object = NULL;
1005 		}
1006 
1007 		if (!meta_object_in_list(object, info->matched_objs,
1008 		    info->num_matched_objs)) {
1009 			rv = add_to_search_result(object, info,
1010 			    num_results_allocated);
1011 			if (rv != CKR_OK) {
1012 				return (rv);
1013 			}
1014 		}
1015 	}
1016 	return (CKR_OK);
1017 }
1018 
1019 static CK_RV
1020 meta_search_for_objects(meta_session_t *session, find_objs_info_t *info,
1021     slot_session_t *slot_session, CK_ATTRIBUTE_PTR pTemplate,
1022     CK_ULONG ulCount, CK_ULONG slotnum, boolean_t token_only,
1023     int *num_results_alloc)
1024 {
1025 	CK_ULONG tmp_num_results;
1026 	CK_OBJECT_HANDLE tmp_results[FIND_OBJ_BUF_SIZE];
1027 	CK_SESSION_HANDLE hSession = slot_session->hSession;
1028 	CK_RV rv;
1029 	CK_SLOT_ID fw_st_id = slot_session->fw_st_id;
1030 
1031 	rv = FUNCLIST(fw_st_id)->C_FindObjectsInit(hSession,
1032 	    pTemplate, ulCount);
1033 
1034 	if (rv != CKR_OK) {
1035 		return (rv);
1036 	}
1037 
1038 	tmp_num_results = 0;
1039 	rv = FUNCLIST(fw_st_id)->C_FindObjects(hSession, tmp_results,
1040 	    FIND_OBJ_BUF_SIZE, &tmp_num_results);
1041 	if (rv != CKR_OK) {
1042 		return (rv);
1043 	}
1044 
1045 	rv = process_find_results(tmp_results, tmp_num_results,
1046 	    num_results_alloc, info, slotnum, token_only,
1047 	    slot_session, session);
1048 	if (rv != CKR_OK) {
1049 		return (rv);
1050 	}
1051 
1052 	while (tmp_num_results == FIND_OBJ_BUF_SIZE) {
1053 		/* might be more results, need to call C_FindObjects again */
1054 		rv = FUNCLIST(fw_st_id)->C_FindObjects(hSession, tmp_results,
1055 		    FIND_OBJ_BUF_SIZE, &tmp_num_results);
1056 		if (rv != CKR_OK) {
1057 			return (rv);
1058 		}
1059 
1060 		rv = process_find_results(tmp_results, tmp_num_results,
1061 		    num_results_alloc, info, slotnum, token_only,
1062 		    slot_session, session);
1063 		if (rv != CKR_OK) {
1064 			return (rv);
1065 		}
1066 	}
1067 
1068 	rv = FUNCLIST(fw_st_id)->C_FindObjectsFinal(hSession);
1069 	return (rv);
1070 }
1071 
1072 
1073 /*
1074  * meta_FindObjectsInit
1075  *
1076  * This function actually will do ALL the work of searching for objects
1077  * that match all requirements specified in the template.
1078  *
1079  * Objects that matched the template will be stored in the
1080  * session's data structure.  When the subsequent C_FindObjects()
1081  * calls are made, results saved will be returned.
1082  *
1083  */
1084 CK_RV
1085 meta_FindObjectsInit(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate,
1086     CK_ULONG ulCount)
1087 {
1088 	CK_RV rv;
1089 	meta_session_t *session;
1090 	CK_ULONG slot_num = 0;
1091 	boolean_t have_token_attr, tokenTrue = B_FALSE;
1092 	slot_session_t *slot_find_session = NULL;
1093 	int num_results_allocated = 0;
1094 	CK_ULONG keystore_slotnum;
1095 
1096 	rv = meta_handle2session(hSession, &session);
1097 	if (rv != CKR_OK)
1098 		return (rv);
1099 
1100 	if ((session->find_objs_info).op_active) {
1101 		REFRELEASE(session);
1102 		return (CKR_OPERATION_ACTIVE);
1103 	}
1104 
1105 	(session->find_objs_info).op_active = B_TRUE;
1106 
1107 	REFRELEASE(session);
1108 
1109 	/* see if the template indicates token object only or not */
1110 	have_token_attr = get_template_boolean(CKA_TOKEN, pTemplate, ulCount,
1111 	    &tokenTrue);
1112 
1113 	keystore_slotnum = get_keystore_slotnum();
1114 
1115 	if (have_token_attr && tokenTrue) {
1116 
1117 
1118 		/*
1119 		 * only interested in token objects, just need to search
1120 		 * token object slot
1121 		 */
1122 		rv = meta_get_slot_session(keystore_slotnum,
1123 			&slot_find_session, session->session_flags);
1124 		if (rv != CKR_OK)  {
1125 			goto finish;
1126 		}
1127 		rv = meta_search_for_objects(session,
1128 		    &(session->find_objs_info), slot_find_session, pTemplate,
1129 		    ulCount, keystore_slotnum, B_TRUE, &num_results_allocated);
1130 		if (rv != CKR_OK) {
1131 			goto finish;
1132 		}
1133 	} else {
1134 		CK_ULONG num_slots = meta_slotManager_get_slotcount();
1135 		for (slot_num = 0; slot_num < num_slots; slot_num++) {
1136 			rv = meta_get_slot_session(slot_num,
1137 			    &slot_find_session, session->session_flags);
1138 			if (rv != CKR_OK) {
1139 				goto finish;
1140 			}
1141 
1142 			/*
1143 			 * if the slot is NOT the token object slot, and
1144 			 * CKA_TOKEN is not specified, need to specified
1145 			 * it to be false explicitly.  This will prevent
1146 			 * us from using token objects that doesn't
1147 			 * belong to the token slot in the case that
1148 			 * more than one slot supports token objects.
1149 			 */
1150 
1151 			if ((slot_num != keystore_slotnum) &&
1152 			    (!have_token_attr)) {
1153 				CK_BBOOL false = FALSE;
1154 				CK_ATTRIBUTE_PTR newTemplate;
1155 
1156 				newTemplate = malloc((ulCount + 1) *
1157 				    sizeof (CK_ATTRIBUTE));
1158 				if (newTemplate == NULL) {
1159 					rv = CKR_HOST_MEMORY;
1160 					goto finish;
1161 				}
1162 				(void) memcpy(newTemplate + 1, pTemplate,
1163 				    ulCount * sizeof (CK_ATTRIBUTE));
1164 				newTemplate[0].type = CKA_TOKEN;
1165 				newTemplate[0].pValue = &false;
1166 				newTemplate[0].ulValueLen = sizeof (false);
1167 
1168 				rv = meta_search_for_objects(session,
1169 				    &(session->find_objs_info),
1170 				    slot_find_session, newTemplate,
1171 				    ulCount+1, slot_num, B_FALSE,
1172 				    &num_results_allocated);
1173 				free(newTemplate);
1174 			} else {
1175 				rv = meta_search_for_objects(session,
1176 				    &(session->find_objs_info),
1177 				    slot_find_session, pTemplate, ulCount,
1178 				    slot_num, B_FALSE,
1179 				    &num_results_allocated);
1180 			}
1181 
1182 			if (rv != CKR_OK) {
1183 				goto finish;
1184 			}
1185 			meta_release_slot_session(slot_find_session);
1186 			slot_find_session = NULL;
1187 		}
1188 	}
1189 
1190 finish:
1191 	if (slot_find_session != NULL) {
1192 		meta_release_slot_session(slot_find_session);
1193 	}
1194 	if (rv != CKR_OK) {
1195 		(void) pthread_rwlock_wrlock(&session->session_lock);
1196 		if (((session->find_objs_info).matched_objs) != NULL) {
1197 			free((session->find_objs_info).matched_objs);
1198 		}
1199 		bzero(&(session->find_objs_info), sizeof (find_objs_info_t));
1200 		(void) pthread_rwlock_unlock(&(session->session_lock));
1201 	}
1202 
1203 	return (rv);
1204 }
1205 
1206 /*
1207  * meta_FindObjects
1208  *
1209  * This function actually doesn't do any real work in search for the
1210  * matching object.  All the work is done in FindObjectsInit().  This
1211  * function will only return the matching objects store in the session's
1212  * "find_objs_info" variable.
1213  *
1214  */
1215 CK_RV
1216 meta_FindObjects(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE_PTR phObject,
1217     CK_ULONG ulMaxObjectCount, CK_ULONG_PTR pulObjectCount)
1218 {
1219 	CK_RV rv;
1220 	find_objs_info_t *info;
1221 	CK_ULONG num_objs_found = 0;
1222 	meta_object_t *obj;
1223 	meta_session_t *session;
1224 	int i;
1225 
1226 	rv = meta_handle2session(hSession, &session);
1227 	if (rv != CKR_OK)
1228 		return (rv);
1229 
1230 	info = &(session->find_objs_info);
1231 
1232 	if (!(info->op_active)) {
1233 		REFRELEASE(session);
1234 		return (CKR_OPERATION_NOT_INITIALIZED);
1235 	}
1236 
1237 	for (i = info->next_result_index;
1238 	    ((num_objs_found < ulMaxObjectCount) &&
1239 	    (i < info->num_matched_objs));
1240 	    i++) {
1241 		obj = info->matched_objs[i];
1242 		if (obj != NULL) {
1243 			/* sanity check to see if object is still valid */
1244 			(void) pthread_rwlock_rdlock(&obj->object_lock);
1245 			if (obj->magic_marker == METASLOT_OBJECT_MAGIC) {
1246 				phObject[num_objs_found++] =
1247 				    (CK_OBJECT_HANDLE)obj;
1248 			}
1249 			(void) pthread_rwlock_unlock(&obj->object_lock);
1250 		}
1251 	}
1252 	info->next_result_index = i;
1253 	*pulObjectCount	= num_objs_found;
1254 	REFRELEASE(session);
1255 	return (rv);
1256 }
1257 
1258 
1259 /*
1260  * meta_FindObjectsFinal
1261  *
1262  */
1263 CK_RV
1264 meta_FindObjectsFinal(CK_SESSION_HANDLE hSession)
1265 {
1266 	CK_RV rv;
1267 	find_objs_info_t *info;
1268 	meta_session_t *session;
1269 
1270 	rv = meta_handle2session(hSession, &session);
1271 	if (rv != CKR_OK)
1272 		return (rv);
1273 
1274 	info = &(session->find_objs_info);
1275 
1276 	if (!info->op_active) {
1277 		REFRELEASE(session);
1278 		return (CKR_OPERATION_NOT_INITIALIZED);
1279 	}
1280 
1281 	if (info->matched_objs) {
1282 		free(info->matched_objs);
1283 	}
1284 
1285 	bzero(info, sizeof (find_objs_info_t));
1286 	REFRELEASE(session);
1287 	return (rv);
1288 }
1289