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