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