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  * Copyright 2012 Milan Jurik. All rights reserved.
25  */
26 
27 #include <string.h>
28 #include <stdlib.h>
29 #include <strings.h>
30 #include "metaGlobal.h"
31 #include "metaAttrMasters.h"
32 
33 static void
34 find_attribute(CK_ATTRIBUTE_TYPE attrtype, generic_attr_t *attributes,
35 	size_t num_attributes, generic_attr_t **found_attribute);
36 
37 /*
38  * get_master_attributes_by_object
39  *
40  * Returns an (statically allocated) set of object attributes, as determined by
41  * class and keytype of the supplied object.  The attributes are only
42  * initialized to default values.
43  */
44 CK_RV
45 get_master_attributes_by_object(slot_session_t *session,
46     slot_object_t *slot_object, generic_attr_t **attributes,
47     size_t *num_attributes)
48 {
49 	CK_RV rv;
50 	CK_ATTRIBUTE attr;
51 	CK_OBJECT_CLASS class;
52 	CK_ULONG subtype = CK_UNAVAILABLE_INFORMATION;
53 
54 	/* first get the class */
55 	attr.type = CKA_CLASS;
56 	attr.pValue = &class;
57 	attr.ulValueLen = sizeof (class);
58 	rv = FUNCLIST(session->fw_st_id)->C_GetAttributeValue(
59 	    session->hSession, slot_object->hObject, &attr, 1);
60 	if (rv != CKR_OK) {
61 		return (rv);
62 	}
63 
64 	attr.pValue = &subtype;
65 	attr.ulValueLen = sizeof (subtype);
66 	switch (class) {
67 		case CKO_CERTIFICATE:
68 			attr.type = CKA_CERTIFICATE_TYPE;
69 			break;
70 		case CKO_HW_FEATURE:
71 			attr.type = CKA_HW_FEATURE_TYPE;
72 			break;
73 		case CKO_PUBLIC_KEY:
74 		case CKO_PRIVATE_KEY:
75 		case CKO_SECRET_KEY:
76 		case CKO_DOMAIN_PARAMETERS:
77 			attr.type = CKA_KEY_TYPE;
78 			break;
79 		case CKO_DATA:
80 			goto get_attr;
81 		default:
82 			/* should never be here */
83 			return (CKR_ATTRIBUTE_VALUE_INVALID);
84 	}
85 	rv = FUNCLIST(session->fw_st_id)->C_GetAttributeValue(
86 	    session->hSession, slot_object->hObject, &attr, 1);
87 	if (rv != CKR_OK) {
88 		return (rv);
89 	}
90 
91 get_attr:
92 	rv = get_master_attributes_by_type(class, subtype,
93 	    attributes, num_attributes);
94 
95 	return (rv);
96 }
97 
98 /*
99  * get_master_attributes_by_template
100  *
101  * Returns an (statically allocated) set of object attributes, as determined by
102  * the supplied object template. The template is only used to determine the
103  * class/subclass of the object. The attributes are only initialized to
104  * default values.
105  */
106 CK_RV
107 get_master_attributes_by_template(
108 	CK_ATTRIBUTE *template, CK_ULONG template_size,
109 	generic_attr_t **attributes, size_t *num_attributes)
110 {
111 	CK_OBJECT_CLASS class;
112 	CK_ULONG subtype = CK_UNAVAILABLE_INFORMATION;
113 	boolean_t found;
114 
115 	found = get_template_ulong(CKA_CLASS, template, template_size, &class);
116 	if (!found) {
117 		return (CKR_TEMPLATE_INCOMPLETE);
118 	}
119 
120 	switch (class) {
121 		case CKO_CERTIFICATE:
122 			found = get_template_ulong(CKA_CERTIFICATE_TYPE,
123 			    template, template_size, &subtype);
124 			break;
125 		case CKO_HW_FEATURE:
126 			found = get_template_ulong(CKA_HW_FEATURE_TYPE,
127 			    template, template_size, &subtype);
128 			break;
129 		case CKO_PUBLIC_KEY:
130 		case CKO_PRIVATE_KEY:
131 		case CKO_SECRET_KEY:
132 		case CKO_DOMAIN_PARAMETERS:
133 			found = get_template_ulong(CKA_KEY_TYPE,
134 			    template, template_size, &subtype);
135 			break;
136 		case CKO_DATA:
137 			/* CKO_DATA has no subtype, just pretend it is found  */
138 			found = B_TRUE;
139 			break;
140 		default:
141 			/* unknown object class */
142 			return (CKR_ATTRIBUTE_VALUE_INVALID);
143 	}
144 
145 	if (!found) {
146 		return (CKR_TEMPLATE_INCOMPLETE);
147 	}
148 
149 	return (get_master_attributes_by_type(class, subtype,
150 	    attributes, num_attributes));
151 }
152 
153 /*
154  * get_master_template_by_type
155  *
156  * Returns an (statically allocated) set of object attributes, as determined
157  * by the specified class and subtype. The attributes are initialized to default
158  * values.
159  */
160 CK_RV
161 get_master_template_by_type(CK_OBJECT_CLASS class, CK_ULONG subtype,
162     generic_attr_t **attributes, size_t *num_attributes)
163 {
164 	generic_attr_t *master_template = NULL;
165 	size_t master_template_size = 0;
166 
167 	switch (class) {
168 	case CKO_HW_FEATURE:
169 		switch (subtype) {
170 		case CKO_HW_FEATURE:
171 			master_template = (generic_attr_t *)OBJ_HW_CLOCK;
172 			master_template_size = sizeof (OBJ_HW_CLOCK);
173 			break;
174 
175 		case CKH_MONOTONIC_COUNTER:
176 			master_template = (generic_attr_t *)OBJ_HW_MONOTONIC;
177 			master_template_size = sizeof (OBJ_HW_MONOTONIC);
178 			break;
179 
180 		default:
181 			/* Unsupported. */
182 			break;
183 		}
184 		break;
185 
186 	case CKO_DATA:
187 		/* Objects of this class have no subtype. */
188 		master_template = (generic_attr_t *)OBJ_DATA;
189 		master_template_size = sizeof (OBJ_DATA);
190 		break;
191 
192 	case CKO_CERTIFICATE:
193 		switch (subtype) {
194 		case CKC_X_509:
195 			master_template = (generic_attr_t *)OBJ_CERT_X509;
196 			master_template_size = sizeof (OBJ_CERT_X509);
197 			break;
198 
199 		case CKC_X_509_ATTR_CERT:
200 			master_template = (generic_attr_t *)OBJ_CERT_X509ATTR;
201 			master_template_size = sizeof (OBJ_CERT_X509ATTR);
202 			break;
203 
204 		default:
205 			/* Unsupported. */
206 			break;
207 		}
208 		break;
209 
210 	case CKO_PUBLIC_KEY:
211 		switch (subtype) {
212 		case CKK_RSA:
213 			master_template = (generic_attr_t *)OBJ_PUBKEY_RSA;
214 			master_template_size = sizeof (OBJ_PUBKEY_RSA);
215 			break;
216 
217 		case CKK_DSA:
218 			master_template = (generic_attr_t *)OBJ_PUBKEY_DSA;
219 			master_template_size = sizeof (OBJ_PUBKEY_DSA);
220 			break;
221 
222 		case CKK_EC:
223 			master_template = (generic_attr_t *)OBJ_PUBKEY_EC;
224 			master_template_size = sizeof (OBJ_PUBKEY_EC);
225 			break;
226 
227 		case CKK_DH:
228 			master_template = (generic_attr_t *)OBJ_PUBKEY_DH;
229 			master_template_size = sizeof (OBJ_PUBKEY_DH);
230 			break;
231 
232 		case CKK_X9_42_DH:
233 			master_template = (generic_attr_t *)OBJ_PUBKEY_X942DH;
234 			master_template_size = sizeof (OBJ_PUBKEY_X942DH);
235 			break;
236 
237 		case CKK_KEA:
238 			master_template = (generic_attr_t *)OBJ_PUBKEY_KEA;
239 			master_template_size = sizeof (OBJ_PUBKEY_KEA);
240 			break;
241 
242 		default:
243 			/* Unsupported. */
244 			break;
245 		}
246 		break;
247 
248 	case CKO_PRIVATE_KEY:
249 		switch (subtype) {
250 		case CKK_RSA:
251 			master_template = (generic_attr_t *)OBJ_PRIVKEY_RSA;
252 			master_template_size = sizeof (OBJ_PRIVKEY_RSA);
253 			break;
254 
255 		case CKK_DSA:
256 			master_template = (generic_attr_t *)OBJ_PRIVKEY_DSA;
257 			master_template_size = sizeof (OBJ_PRIVKEY_DSA);
258 			break;
259 
260 		case CKK_EC:
261 			master_template = (generic_attr_t *)OBJ_PRIVKEY_EC;
262 			master_template_size = sizeof (OBJ_PRIVKEY_EC);
263 			break;
264 
265 		case CKK_DH:
266 			master_template = (generic_attr_t *)OBJ_PRIVKEY_DH;
267 			master_template_size = sizeof (OBJ_PRIVKEY_DH);
268 			break;
269 
270 		case CKK_X9_42_DH:
271 			master_template = (generic_attr_t *)OBJ_PRIVKEY_X942DH;
272 			master_template_size = sizeof (OBJ_PRIVKEY_X942DH);
273 			break;
274 
275 		case CKK_KEA:
276 			master_template = (generic_attr_t *)OBJ_PRIVKEY_KEA;
277 			master_template_size = sizeof (OBJ_PRIVKEY_KEA);
278 			break;
279 
280 		default:
281 			/* Unsupported. */
282 			break;
283 		}
284 		break;
285 
286 	case CKO_SECRET_KEY:
287 		/*
288 		 * The only difference between secret keys is that some
289 		 * are valiable length (eg CKK_AES), while others are not
290 		 * (eg CKK_DES) -- and do not have a CKA_VALUE_LEN attribute.
291 		 *
292 		 * FUTURE(?): Consider using obj_seckey_withlen for unknown
293 		 * keytypes. This is the most likely choice, as new algorithms
294 		 * seem to support variable length keys. That's not the default
295 		 * now, because if people have implemented new key types with
296 		 * different attribute sets (like the mess of public/private
297 		 * key types), then incorrect behaviour would result. It's
298 		 * easier to relax this restriction than to tighten it (which
299 		 * would introduce a regression to anyone relying on this
300 		 * working for unknown key types).
301 		 *
302 		 */
303 		switch (subtype) {
304 		case CKK_DES:
305 		case CKK_DES2:
306 		case CKK_DES3:
307 		case CKK_IDEA:
308 		case CKK_CDMF:
309 		case CKK_SKIPJACK:
310 		case CKK_BATON:
311 		case CKK_JUNIPER:
312 			master_template = (generic_attr_t *)OBJ_SECKEY;
313 			master_template_size = sizeof (OBJ_SECKEY);
314 			break;
315 
316 		case CKK_GENERIC_SECRET:
317 		case CKK_RC2:
318 		case CKK_RC4:
319 		case CKK_RC5:
320 		case CKK_AES:
321 		case CKK_BLOWFISH:
322 		case CKK_CAST:
323 		case CKK_CAST3:
324 		case CKK_CAST128:
325 			master_template = (generic_attr_t *)OBJ_SECKEY_WITHLEN;
326 			master_template_size = sizeof (OBJ_SECKEY_WITHLEN);
327 			break;
328 
329 		default:
330 			/* Unsupported. */
331 			break;
332 		}
333 		break;
334 
335 	case CKO_DOMAIN_PARAMETERS:
336 		switch (subtype) {
337 		case CKK_DSA:
338 			master_template = (generic_attr_t *)OBJ_DOM_DSA;
339 			master_template_size = sizeof (OBJ_DOM_DSA);
340 			break;
341 
342 		case CKK_DH:
343 			master_template = (generic_attr_t *)OBJ_DOM_DH;
344 			master_template_size = sizeof (OBJ_DOM_DH);
345 			break;
346 
347 		case CKK_X9_42_DH:
348 			master_template = (generic_attr_t *)OBJ_DOM_X942DH;
349 			master_template_size = sizeof (OBJ_DOM_X942DH);
350 			break;
351 
352 		default:
353 			/* Unsupported. */
354 			break;
355 		}
356 		break;
357 
358 	default:
359 		/* Unsupported. */
360 		break;
361 	}
362 
363 	/* Requested object is unknown or invalid. */
364 	if (master_template == NULL)
365 		return (CKR_ATTRIBUTE_VALUE_INVALID);
366 	else {
367 		*attributes = master_template;
368 		*num_attributes = master_template_size;
369 		return (CKR_OK);
370 	}
371 }
372 
373 
374 /*
375  * get_master_attributes_by_type
376  *
377  * Returns an (statically allocated) set of object attributes, as determined by
378  * the specified class and subtype. The attributes are initialized to default
379  * values.
380  */
381 CK_RV
382 get_master_attributes_by_type(CK_OBJECT_CLASS class, CK_ULONG subtype,
383 	generic_attr_t **attributes, size_t *num_attributes)
384 {
385 	CK_RV rv;
386 	generic_attr_t *master_template = NULL;
387 	generic_attr_t *new_attributes;
388 	size_t i, num_new_attributes, master_template_size = 0;
389 
390 	/* Determine the appropriate master template needed. */
391 	rv = get_master_template_by_type(class, subtype,
392 	    &master_template, &master_template_size);
393 	if (rv != CKR_OK)
394 		return (rv);
395 
396 	/* Duplicate the master template. */
397 	new_attributes = malloc(master_template_size);
398 	if (new_attributes == NULL)
399 		return (CKR_HOST_MEMORY);
400 
401 	(void) memcpy(new_attributes, master_template, master_template_size);
402 	num_new_attributes = master_template_size / sizeof (generic_attr_t);
403 
404 	/* Set the pointer in the appropriate storage area. */
405 	for (i = 0; i < num_new_attributes; i++) {
406 		generic_attr_t *attr;
407 
408 		attr = new_attributes + i;
409 
410 		switch (attr->attribute.ulValueLen) {
411 			case (sizeof (CK_ULONG)):
412 				attr->attribute.pValue = &attr->generic_ulong;
413 				break;
414 			case (sizeof (CK_BBOOL)):
415 				attr->attribute.pValue = &attr->generic_bbool;
416 				break;
417 			default:
418 				attr->attribute.pValue = attr->generic_data;
419 				break;
420 		}
421 
422 	}
423 
424 	/* Secret keys share a common template, so set the key type here. */
425 	if (class == CKO_SECRET_KEY) {
426 		/* Keytype / subtype is always the second attribute. */
427 		new_attributes[1].generic_ulong = subtype;
428 	}
429 
430 	*attributes = new_attributes;
431 	*num_attributes = num_new_attributes;
432 
433 	return (CKR_OK);
434 }
435 
436 
437 /*
438  * get_master_attributes_by_duplication
439  *
440  * Returns an (statically allocated) set of object attributes, as copied from an
441  * existing set of attributes. The new attributes inherit the values from
442  * the old attributes.
443  */
444 CK_RV
445 get_master_attributes_by_duplication(
446 	generic_attr_t *src_attrs, size_t num_src_attrs,
447 	generic_attr_t **dst_attrs, size_t *num_dst_attrs)
448 {
449 	CK_RV rv = CKR_OK;
450 	generic_attr_t *new_attrs, *src, *dst;
451 	size_t i;
452 
453 	new_attrs = malloc(sizeof (generic_attr_t) * num_src_attrs);
454 	if (new_attrs == NULL)
455 		return (CKR_HOST_MEMORY);
456 
457 	for (i = 0; i < num_src_attrs; i++) {
458 		src = src_attrs + i;
459 		dst = new_attrs + i;
460 
461 		*dst = *src;
462 
463 		/* Adjust pointers in dst so that they don't point to src. */
464 
465 		if (src->isMalloced) {
466 			dst->attribute.pValue =
467 			    malloc(src->attribute.ulValueLen);
468 
469 			if (dst->attribute.pValue == NULL) {
470 				/*
471 				 * Continue on error, so that the cleanup
472 				 * routine doesn't see pointers to src_attrs.
473 				 */
474 				dst->attribute.ulValueLen = 0;
475 				rv = CKR_HOST_MEMORY;
476 				continue;
477 			}
478 		} else if (src->attribute.pValue == &src->generic_bbool) {
479 			dst->attribute.pValue = &dst->generic_bbool;
480 		} else if (src->attribute.pValue == &src->generic_ulong) {
481 			dst->attribute.pValue = &dst->generic_ulong;
482 		} else if (src->attribute.pValue == &src->generic_data) {
483 			dst->attribute.pValue = &dst->generic_data;
484 		} else {
485 			/* This shouldn't happen. */
486 			dst->attribute.pValue = NULL;
487 			dst->attribute.ulValueLen = 0;
488 			rv = CKR_GENERAL_ERROR;
489 			num_src_attrs = i + 1;
490 			break;
491 		}
492 
493 		(void) memcpy(dst->attribute.pValue, src->attribute.pValue,
494 		    src->attribute.ulValueLen);
495 	}
496 
497 	if (rv != CKR_OK) {
498 		dealloc_attributes(new_attrs, num_src_attrs);
499 	} else {
500 		*dst_attrs = new_attrs;
501 		*num_dst_attrs = num_src_attrs;
502 	}
503 
504 	return (rv);
505 }
506 
507 
508 /*
509  * dealloc_attributes
510  *
511  * Deallocates the storage used for a set of attributes. The attribute
512  * values are zeroed out before being free'd.
513  */
514 void
515 dealloc_attributes(generic_attr_t *attributes, size_t num_attributes)
516 {
517 	size_t i;
518 	generic_attr_t *attr;
519 
520 	for (i = 0; i < num_attributes; i++) {
521 		attr = attributes + i;
522 
523 		/*
524 		 * Zero-out any attribute values. We could do this just for
525 		 * attributes with isSensitive == True, but it's not much
526 		 * extra work to just do them all. [Most attributes are just
527 		 * 1 or 4 bytes]
528 		 */
529 		bzero(attr->attribute.pValue, attr->attribute.ulValueLen);
530 
531 		if (attr->isMalloced)
532 			free(attr->attribute.pValue);
533 	}
534 
535 	free(attributes);
536 }
537 
538 
539 /*
540  * attribute_set_value
541  *
542  * Sets the value of the specified attribute. Any portion of the old value
543  * which will not be overwritten by the new value is zeroed out.
544  */
545 CK_RV
546 attribute_set_value(CK_ATTRIBUTE *new_attr,
547 	generic_attr_t *attributes, size_t num_attributes)
548 {
549 	generic_attr_t *attr = NULL;
550 
551 	if (new_attr == NULL)
552 		return (CKR_TEMPLATE_INCOMPLETE);
553 	else if (new_attr->pValue == NULL) {
554 		return (CKR_ATTRIBUTE_VALUE_INVALID);
555 	}
556 
557 	find_attribute(new_attr->type, attributes, num_attributes, &attr);
558 	if (attr == NULL) {
559 		return (CKR_ATTRIBUTE_TYPE_INVALID);
560 	}
561 
562 	/* Store the new value. */
563 	if (attr->attribute.ulValueLen >= new_attr->ulValueLen) {
564 		/* Existing storage is sufficient to store new value. */
565 
566 		/* bzero() out any data that won't be overwritten. */
567 		bzero((char *)attr->attribute.pValue + new_attr->ulValueLen,
568 		    attr->attribute.ulValueLen - new_attr->ulValueLen);
569 
570 	} else if (new_attr->ulValueLen <= sizeof (attr->generic_data)) {
571 		/* Use generic storage to avoid a malloc. */
572 
573 		bzero(attr->attribute.pValue, attr->attribute.ulValueLen);
574 		if (attr->isMalloced) {
575 			/*
576 			 * If app sets a large value (triggering a malloc),
577 			 * then sets a tiny value, and finally again sets
578 			 * a large value (phew!) we could end up here.
579 			 *
580 			 * FUTURE?: Store the original malloc size, so that
581 			 * we can regrow the value up to the original size.
582 			 * This might avoid some heap churn for pathalogic
583 			 * applications.
584 			 */
585 			free(attr->attribute.pValue);
586 			attr->isMalloced = B_FALSE;
587 		}
588 
589 		attr->attribute.pValue = attr->generic_data;
590 
591 	} else {
592 		/* Need to allocate storage for the new value. */
593 		void *newStorage;
594 
595 		newStorage = malloc(new_attr->ulValueLen);
596 		if (newStorage == NULL)
597 			return (CKR_HOST_MEMORY);
598 		bzero(attr->attribute.pValue, attr->attribute.ulValueLen);
599 		attr->attribute.pValue = newStorage;
600 		attr->isMalloced = B_TRUE;
601 	}
602 
603 	(void) memcpy(attr->attribute.pValue, new_attr->pValue,
604 	    new_attr->ulValueLen);
605 	attr->attribute.ulValueLen = new_attr->ulValueLen;
606 	attr->hasValueForClone = B_TRUE;
607 
608 	return (CKR_OK);
609 }
610 
611 
612 /*
613  * find_attribute
614  *
615  * Passes a pointer to the requested attribute, or NULL if not found.
616  */
617 static void
618 find_attribute(CK_ATTRIBUTE_TYPE attrtype, generic_attr_t *attributes,
619 	size_t num_attributes, generic_attr_t **found_attribute)
620 {
621 	generic_attr_t *attr;
622 	boolean_t found = B_FALSE;
623 	size_t i;
624 
625 	/* Find the requested attribute. */
626 	for (i = 0, attr = attributes; i < num_attributes; i++, attr++) {
627 		if (attr->attribute.type == attrtype) {
628 			found = B_TRUE;
629 			break;
630 		}
631 	}
632 
633 	*found_attribute = found ? attr : NULL;
634 }
635 
636 
637 /*
638  * get_template_ulong
639  *
640  * Look for the specified ulong-size attribute, and retrieve its value. The
641  * return value specifies if the attribute was found (or not).
642  */
643 boolean_t
644 get_template_ulong(CK_ATTRIBUTE_TYPE type, CK_ATTRIBUTE *attributes,
645 	CK_ULONG num_attributes, CK_ULONG *result)
646 {
647 	boolean_t found = B_FALSE;
648 	CK_ULONG i;
649 
650 	for (i = 0; i < num_attributes; i++) {
651 		if (attributes[i].type == type) {
652 			CK_ULONG *value = attributes[i].pValue;
653 
654 			*result = *value;
655 			found = B_TRUE;
656 			break;
657 		}
658 	}
659 
660 	return (found);
661 }
662 
663 
664 /*
665  * get_template_boolean
666  *
667  * Look for the specified boolean attribute, and retrieve its value. The
668  * return value specifies if the attribute was found (or not).
669  */
670 boolean_t
671 get_template_boolean(CK_ATTRIBUTE_TYPE type, CK_ATTRIBUTE *attributes,
672 	CK_ULONG num_attributes, boolean_t *result)
673 {
674 	boolean_t found = B_FALSE;
675 	CK_ULONG i;
676 
677 	for (i = 0; i < num_attributes; i++) {
678 		if (attributes[i].type == type) {
679 			CK_BBOOL *value = attributes[i].pValue;
680 
681 			if (*value == CK_FALSE)
682 				*result = B_FALSE;
683 			else
684 				*result = B_TRUE;
685 
686 			found = B_TRUE;
687 			break;
688 		}
689 	}
690 
691 	return (found);
692 }
693 
694 /*
695  * set_template_boolean
696  *
697  * Look for the specified boolean attribute, and set its value.
698  *
699  * if 'local' is true, it sets the pointer to the value in the template a new
700  * location.  There should be no memory leak created by this because we are
701  * only doing this to booleans which should not be malloc'ed.
702  *
703  * if 'local' is false, it sets its value.
704  *
705  * The return value specifies if the attribute was found (or not).
706  */
707 int
708 set_template_boolean(CK_ATTRIBUTE_TYPE type, CK_ATTRIBUTE *attributes,
709 	CK_ULONG num_attributes, boolean_t local, CK_BBOOL *value)
710 {
711 	int i;
712 
713 	for (i = 0; i < num_attributes; i++) {
714 		if (attributes[i].type == type) {
715 			if (local)
716 				attributes[i].pValue = value;
717 			else
718 				*((CK_BBOOL *)attributes[i].pValue) = *value;
719 
720 			return (i);
721 		}
722 	}
723 
724 	return (-1);
725 }
726