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