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
get_master_attributes_by_object(slot_session_t * session,slot_object_t * slot_object,generic_attr_t ** attributes,size_t * num_attributes)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
get_master_attributes_by_template(CK_ATTRIBUTE * template,CK_ULONG template_size,generic_attr_t ** attributes,size_t * num_attributes)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
get_master_template_by_type(CK_OBJECT_CLASS class,CK_ULONG subtype,generic_attr_t ** attributes,size_t * num_attributes)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
get_master_attributes_by_type(CK_OBJECT_CLASS class,CK_ULONG subtype,generic_attr_t ** attributes,size_t * num_attributes)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
get_master_attributes_by_duplication(generic_attr_t * src_attrs,size_t num_src_attrs,generic_attr_t ** dst_attrs,size_t * num_dst_attrs)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
dealloc_attributes(generic_attr_t * attributes,size_t num_attributes)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
attribute_set_value(CK_ATTRIBUTE * new_attr,generic_attr_t * attributes,size_t num_attributes)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
find_attribute(CK_ATTRIBUTE_TYPE attrtype,generic_attr_t * attributes,size_t num_attributes,generic_attr_t ** found_attribute)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
get_template_ulong(CK_ATTRIBUTE_TYPE type,CK_ATTRIBUTE * attributes,CK_ULONG num_attributes,CK_ULONG * result)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
get_template_boolean(CK_ATTRIBUTE_TYPE type,CK_ATTRIBUTE * attributes,CK_ULONG num_attributes,boolean_t * result)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
set_template_boolean(CK_ATTRIBUTE_TYPE type,CK_ATTRIBUTE * attributes,CK_ULONG num_attributes,boolean_t local,CK_BBOOL * value)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