xref: /illumos-gate/usr/src/lib/pkcs11/libpkcs11/common/metaAttrManager.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
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
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 			break;
83 		default:
84 			/* should never be here */
85 			return (CKR_ATTRIBUTE_VALUE_INVALID);
86 	}
87 	rv = FUNCLIST(session->fw_st_id)->C_GetAttributeValue(
88 	    session->hSession, slot_object->hObject, &attr, 1);
89 	if (rv != CKR_OK) {
90 		return (rv);
91 	}
92 
93 get_attr:
94 	rv = get_master_attributes_by_type(class, subtype,
95 	    attributes, num_attributes);
96 
97 	return (rv);
98 }
99 
100 /*
101  * get_master_attributes_by_template
102  *
103  * Returns an (statically allocated) set of object attributes, as determined by
104  * the supplied object template. The template is only used to determine the
105  * class/subclass of the object. The attributes are only initialized to
106  * default values.
107  */
108 CK_RV
109 get_master_attributes_by_template(
110 	CK_ATTRIBUTE *template, CK_ULONG template_size,
111 	generic_attr_t **attributes, size_t *num_attributes)
112 {
113 	CK_OBJECT_CLASS class;
114 	CK_ULONG subtype = CK_UNAVAILABLE_INFORMATION;
115 	boolean_t found;
116 
117 	found = get_template_ulong(CKA_CLASS, template, template_size, &class);
118 	if (!found) {
119 		return (CKR_TEMPLATE_INCOMPLETE);
120 	}
121 
122 	switch (class) {
123 		case CKO_CERTIFICATE:
124 			found = get_template_ulong(CKA_CERTIFICATE_TYPE,
125 			    template, template_size, &subtype);
126 			break;
127 		case CKO_HW_FEATURE:
128 			found = get_template_ulong(CKA_HW_FEATURE_TYPE,
129 			    template, template_size, &subtype);
130 			break;
131 		case CKO_PUBLIC_KEY:
132 		case CKO_PRIVATE_KEY:
133 		case CKO_SECRET_KEY:
134 		case CKO_DOMAIN_PARAMETERS:
135 			found = get_template_ulong(CKA_KEY_TYPE,
136 			    template, template_size, &subtype);
137 			break;
138 		case CKO_DATA:
139 			/* CKO_DATA has no subtype, just pretend it is found  */
140 			found = B_TRUE;
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
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
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
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
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 		bzero(attr->attribute.pValue, attr->attribute.ulValueLen);
531 
532 		if (attr->isMalloced)
533 			free(attr->attribute.pValue);
534 	}
535 
536 	free(attributes);
537 }
538 
539 
540 /*
541  * attribute_set_value
542  *
543  * Sets the value of the specified attribute. Any portion of the old value
544  * which will not be overwritten by the new value is zeroed out.
545  */
546 CK_RV
547 attribute_set_value(CK_ATTRIBUTE *new_attr,
548 	generic_attr_t *attributes, size_t num_attributes)
549 {
550 	generic_attr_t *attr = NULL;
551 
552 	if (new_attr == NULL)
553 		return (CKR_TEMPLATE_INCOMPLETE);
554 	else if (new_attr->pValue == NULL) {
555 		return (CKR_ATTRIBUTE_VALUE_INVALID);
556 	}
557 
558 	find_attribute(new_attr->type, attributes, num_attributes, &attr);
559 	if (attr == NULL) {
560 		return (CKR_ATTRIBUTE_TYPE_INVALID);
561 	}
562 
563 	/* Store the new value. */
564 	if (attr->attribute.ulValueLen >= new_attr->ulValueLen) {
565 		/* Existing storage is sufficient to store new value. */
566 
567 		/* bzero() out any data that won't be overwritten. */
568 		bzero((char *)attr->attribute.pValue + new_attr->ulValueLen,
569 			attr->attribute.ulValueLen - new_attr->ulValueLen);
570 
571 	} else if (new_attr->ulValueLen <= sizeof (attr->generic_data)) {
572 		/* Use generic storage to avoid a malloc. */
573 
574 		bzero(attr->attribute.pValue, attr->attribute.ulValueLen);
575 		if (attr->isMalloced) {
576 			/*
577 			 * If app sets a large value (triggering a malloc),
578 			 * then sets a tiny value, and finally again sets
579 			 * a large value (phew!) we could end up here.
580 			 *
581 			 * FUTURE?: Store the original malloc size, so that
582 			 * we can regrow the value up to the original size.
583 			 * This might avoid some heap churn for pathalogic
584 			 * applications.
585 			 */
586 			free(attr->attribute.pValue);
587 			attr->isMalloced = B_FALSE;
588 		}
589 
590 		attr->attribute.pValue = attr->generic_data;
591 
592 	} else {
593 		/* Need to allocate storage for the new value. */
594 		void *newStorage;
595 
596 		newStorage = malloc(new_attr->ulValueLen);
597 		if (newStorage == NULL)
598 			return (CKR_HOST_MEMORY);
599 		bzero(attr->attribute.pValue, attr->attribute.ulValueLen);
600 		attr->attribute.pValue = newStorage;
601 		attr->isMalloced = B_TRUE;
602 	}
603 
604 	(void) memcpy(attr->attribute.pValue, new_attr->pValue,
605 		new_attr->ulValueLen);
606 	attr->attribute.ulValueLen = new_attr->ulValueLen;
607 	attr->hasValueForClone = B_TRUE;
608 
609 	return (CKR_OK);
610 }
611 
612 
613 /*
614  * find_attribute
615  *
616  * Passes a pointer to the requested attribute, or NULL if not found.
617  */
618 static void
619 find_attribute(CK_ATTRIBUTE_TYPE attrtype, generic_attr_t *attributes,
620 	size_t num_attributes, generic_attr_t **found_attribute)
621 {
622 	generic_attr_t *attr;
623 	boolean_t found = B_FALSE;
624 	size_t i;
625 
626 	/* Find the requested attribute. */
627 	for (i = 0, attr = attributes; i < num_attributes; i++, attr++) {
628 		if (attr->attribute.type == attrtype) {
629 			found = B_TRUE;
630 			break;
631 		}
632 	}
633 
634 	*found_attribute = found ? attr : NULL;
635 }
636 
637 
638 /*
639  * get_template_ulong
640  *
641  * Look for the specified ulong-size attribute, and retrieve its value. The
642  * return value specifies if the attribute was found (or not).
643  */
644 boolean_t
645 get_template_ulong(CK_ATTRIBUTE_TYPE type, CK_ATTRIBUTE *attributes,
646 	CK_ULONG num_attributes, CK_ULONG *result)
647 {
648 	boolean_t found = B_FALSE;
649 	CK_ULONG i;
650 
651 	for (i = 0; i < num_attributes; i++) {
652 		if (attributes[i].type == type) {
653 			CK_ULONG *value = attributes[i].pValue;
654 
655 			*result = *value;
656 			found = B_TRUE;
657 			break;
658 		}
659 	}
660 
661 	return (found);
662 }
663 
664 
665 /*
666  * get_template_boolean
667  *
668  * Look for the specified boolean attribute, and retrieve its value. The
669  * return value specifies if the attribute was found (or not).
670  */
671 boolean_t
672 get_template_boolean(CK_ATTRIBUTE_TYPE type, CK_ATTRIBUTE *attributes,
673 	CK_ULONG num_attributes, boolean_t *result)
674 {
675 	boolean_t found = B_FALSE;
676 	CK_ULONG i;
677 
678 	for (i = 0; i < num_attributes; i++) {
679 		if (attributes[i].type == type) {
680 			CK_BBOOL *value = attributes[i].pValue;
681 
682 			if (*value == CK_FALSE)
683 				*result = B_FALSE;
684 			else
685 				*result = B_TRUE;
686 
687 			found = B_TRUE;
688 			break;
689 		}
690 	}
691 
692 	return (found);
693 }
694 
695 /*
696  * set_template_boolean
697  *
698  * Look for the specified boolean attribute, and set its value.
699  *
700  * if 'local' is true, it sets the pointer to the value in the template a new
701  * location.  There should be no memory leak created by this because we are
702  * only doing this to booleans which should not be malloc'ed.
703  *
704  * if 'local' is false, it sets its value.
705  *
706  * The return value specifies if the attribute was found (or not).
707  */
708 int
709 set_template_boolean(CK_ATTRIBUTE_TYPE type, CK_ATTRIBUTE *attributes,
710 	CK_ULONG num_attributes, boolean_t local, CK_BBOOL *value)
711 {
712 	int i;
713 
714 	for (i = 0; i < num_attributes; i++) {
715 		if (attributes[i].type == type) {
716 			if (local)
717 				attributes[i].pValue = value;
718 			else
719 				*((CK_BBOOL *)attributes[i].pValue) = *value;
720 
721 			return (i);
722 		}
723 	}
724 
725 	return (-1);
726 }
727