dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / pkcs11 / libpkcs11 / common / metaMechManager.c
blob988f1132a5ee1a683d23eb34baa8785d43443b57
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI"
29 * Mechanism Manager - centralized knowledge of mechanisms.
31 * The core of the mechmanager is the "mechlist" data structure. It contains
32 * information about all mechanisms available from providers that have been
33 * exposed to the application.
35 * Each element in the array represents a particular mechanism type. The
36 * array is sorted by type, so that searching by mechanism can be done
37 * quickly. Each element also contains the mechanism data for each slot.
39 * The mechlist is constructed on an as-needed basis, entries are not added
40 * until the application triggers an action that requires an entry to be
41 * added (or updated).
45 #include <string.h>
46 #include <strings.h>
47 #include "pkcs11Conf.h"
48 #include "metaGlobal.h"
51 /* Global data... */
53 #define INITIAL_MECHLIST_SIZE 256
55 typedef struct mechliststruct {
56 CK_MECHANISM_TYPE type;
57 mechinfo_t *slots;
58 } mechlist_t;
60 static pthread_rwlock_t mechlist_lock = PTHREAD_RWLOCK_INITIALIZER;
61 static mechlist_t *mechlist;
62 static unsigned long num_mechs;
63 static unsigned long true_mechlist_size;
66 /* Prototypes... */
67 static CK_RV meta_mechManager_update_mech(CK_MECHANISM_TYPE, boolean_t);
68 static CK_RV meta_mechManager_update_slot(CK_ULONG);
69 static CK_RV update_slotmech(CK_MECHANISM_TYPE, CK_ULONG, unsigned long);
70 static CK_RV meta_mechManager_allocmechs(CK_MECHANISM_TYPE *, unsigned long,
71 unsigned long *);
72 static boolean_t find_mech_index(CK_MECHANISM_TYPE, unsigned long *);
73 static int qsort_mechtypes(const void *, const void *);
77 * meta_mechManager_initialize
79 * Called from C_Initialize. Allocates and initializes storage needed
80 * by the slot manager.
82 CK_RV
83 meta_mechManager_initialize()
85 /* The mechlist can dynamically grow, but let's preallocate space. */
86 mechlist = calloc(INITIAL_MECHLIST_SIZE, sizeof (mechlist_t));
87 if (mechlist == NULL)
88 return (CKR_HOST_MEMORY);
90 true_mechlist_size = INITIAL_MECHLIST_SIZE;
91 num_mechs = 0;
93 return (CKR_OK);
98 * meta_mechManager_finalize
100 * Called from C_Finalize. Deallocates any storage held by the slot manager.
102 void
103 meta_mechManager_finalize()
105 int i;
107 /* No need to lock list, we assume all sessions are closed. */
108 for (i = 0; i < num_mechs; i++) {
109 free(mechlist[i].slots);
112 free(mechlist);
113 mechlist = NULL;
114 num_mechs = 0;
115 true_mechlist_size = 0;
120 * meta_mechManager_get_mechs
122 * Get list of all available mechanisms.
124 * Follows PKCS#11 semantics, where list may be NULL to only request a
125 * count of available mechanisms.
127 CK_RV
128 meta_mechManager_get_mechs(CK_MECHANISM_TYPE *list, CK_ULONG *listsize)
130 CK_RV rv = CKR_OK;
131 CK_ULONG num_found = 0;
132 CK_ULONG slotnum, num_slots;
133 unsigned long i;
135 /* get number of slots */
136 num_slots = meta_slotManager_get_slotcount();
139 * Update slot info. Ignore any errors.
141 * NOTE: Due to the PKCS#11 convention of calling C_GetMechanismList
142 * twice (once to get the count, again to get the actual list), this
143 * is somewhat inefficient... However, I don't see an easy way to fix
144 * that without impacting other cases (eg, when the first call contains
145 * an "optimistic" pre-allocated buffer).
147 for (slotnum = 0; slotnum < num_slots; slotnum++) {
148 (void) meta_mechManager_update_slot(slotnum);
153 * Count the number of mechanisms. We can't just use num_mechs,
154 * because some mechs may not currently be supported on any slot.
155 * Also, it may not be allowed based on the mechanism policy.
158 (void) pthread_rwlock_rdlock(&mechlist_lock);
159 for (i = 0; i < num_mechs; i++) {
160 CK_ULONG j;
161 boolean_t supported;
163 if (pkcs11_is_dismech(METASLOT_FRAMEWORK_ID,
164 mechlist[i].type)) {
165 /* skip mechs disabled by policy */
166 continue;
169 supported = FALSE;
170 for (j = 0; j < num_slots; j++) {
171 if (!mechlist[i].slots[j].initialized)
172 continue;
174 if (mechlist[i].slots[j].supported) {
175 supported = B_TRUE;
176 break;
180 if (supported) {
181 num_found++;
183 if (list && *listsize >= num_found) {
184 list[num_found - 1] = mechlist[i].type;
188 (void) pthread_rwlock_unlock(&mechlist_lock);
190 if (num_found > *listsize)
191 rv = CKR_BUFFER_TOO_SMALL;
193 *listsize = num_found;
195 return (rv);
200 * meta_mechManager_get_slots
202 * Get list of all slots supporting the specified mechanism.
204 * The "mech_support_info" argument should have allocated enough
205 * space to accomodate the list of slots that supports the
206 * specified mechanism. The "num_supporting_slots" field
207 * in the "mech_support_info" structure will indicate how
208 * many slots are found to support the mechanism.
210 * If any error occurred in getting the list, info in
211 * mech_support_info argument is not updated.
214 CK_RV
215 meta_mechManager_get_slots(mech_support_info_t *mech_support_info,
216 boolean_t force_update, CK_MECHANISM_INFO *mech_info)
218 CK_RV rv;
219 boolean_t found;
220 CK_ULONG i, num_slots;
221 unsigned long index, num_found = 0;
222 CK_MECHANISM_INFO info;
224 rv = meta_mechManager_update_mech(mech_support_info->mech,
225 force_update);
226 if (rv != CKR_OK) {
227 return (rv);
230 (void) pthread_rwlock_rdlock(&mechlist_lock);
232 found = find_mech_index(mech_support_info->mech, &index);
233 if (!found) {
234 goto finish;
237 num_slots = meta_slotManager_get_slotcount();
238 for (i = 0; i < num_slots; i++) {
239 if (!mechlist[index].slots[i].initialized ||
240 !mechlist[index].slots[i].supported)
241 continue;
243 if (mech_info) {
244 info = mechlist[index].slots[i].mechanism_info;
245 if (!(info.flags & mech_info->flags)) {
246 continue;
250 num_found++;
251 (mech_support_info->supporting_slots)[num_found - 1]
252 = &mechlist[index].slots[i];
255 finish:
256 (void) pthread_rwlock_unlock(&mechlist_lock);
258 if (num_found == 0) {
259 rv = CKR_MECHANISM_INVALID;
260 } else {
261 mech_support_info->num_supporting_slots = num_found;
264 return (rv);
269 * meta_mechManager_update_mech
271 * Updates a mechanism in the mechlist. If the mechanism is not
272 * listed, all providers will be queried. If the mechanism
273 * is present, but not initialized for some providers, those providers
274 * will be queried. Existing entries will not be updated unless the
275 * force_refresh flag is set.
277 * The force_refresh flag is used by C_GetMechanismInfo, to force an
278 * update. Updates are not forced during the common usage by operations
279 * [eg C_EncryptInit] to avoid poor performance.
281 static CK_RV
282 meta_mechManager_update_mech(CK_MECHANISM_TYPE mech, boolean_t force_refresh)
284 CK_RV rv;
285 CK_ULONG slot, num_slots;
286 unsigned long index = 0;
287 boolean_t found;
289 /* Ensure list contains the mechanism. */
290 rv = meta_mechManager_allocmechs(&mech, 1, &index);
291 if (rv != CKR_OK)
292 return (rv);
294 (void) pthread_rwlock_wrlock(&mechlist_lock);
296 * We didn't retain a lock after the first search, so it's possible
297 * that the mechlist was updated. Search again, but use the last
298 * index as a hint to quickly find the mechanism.
300 found = find_mech_index(mech, &index);
301 if (!found) {
302 /* Shouldn't happen - entries are not removed from list. */
303 rv = CKR_GENERAL_ERROR;
304 goto finish;
307 num_slots = meta_slotManager_get_slotcount();
308 for (slot = 0; slot < num_slots; slot++) {
309 if (force_refresh || !mechlist[index].slots[slot].initialized) {
310 rv = update_slotmech(mech, slot, index);
311 if (rv != CKR_OK) {
312 /* Ignore error and continue with next slot. */
313 rv = CKR_OK;
318 finish:
319 (void) pthread_rwlock_unlock(&mechlist_lock);
321 return (rv);
326 * meta_mechManager_update_slot
328 * Updates a slot in the mechlist. Called by C_GetMechanismList
329 * [by way of meta_mechManager_get_mechs()]. Unlike
330 * meta_mechManager_get_slots(), the context is always to force a refresh
331 * of the mechlist.
334 static CK_RV
335 meta_mechManager_update_slot(CK_ULONG slotnum)
337 unsigned long index = 0;
338 CK_MECHANISM_TYPE *slot_mechlist = NULL, *tmp_slot_mechlist = NULL;
339 CK_ULONG slot_mechlistsize, mechnum, tmp_mechlistsize;
340 CK_RV rv;
341 boolean_t found;
342 CK_SLOT_ID fw_st_id, true_id;
343 int i;
345 fw_st_id = meta_slotManager_get_framework_table_id(slotnum);
346 true_id = TRUEID(fw_st_id);
348 /* First, get the count. */
349 rv = FUNCLIST(fw_st_id)->C_GetMechanismList(true_id, NULL,
350 &slot_mechlistsize);
351 if (rv != CKR_OK) {
352 goto finish;
355 tmp_slot_mechlist = malloc(
356 slot_mechlistsize * sizeof (CK_MECHANISM_TYPE));
357 if (tmp_slot_mechlist == NULL) {
358 rv = CKR_HOST_MEMORY;
359 goto finish;
362 /* Next, get the actual list. */
363 rv = FUNCLIST(fw_st_id)->C_GetMechanismList(true_id,
364 tmp_slot_mechlist, &slot_mechlistsize);
365 if (rv != CKR_OK) {
366 goto finish;
370 * filter the list of mechanisms returned by the underlying slot
371 * to remove any mechanisms that are explicitly disabled
372 * in the configuration file.
374 slot_mechlist = malloc(slot_mechlistsize * sizeof (CK_MECHANISM_TYPE));
375 if (slot_mechlist == NULL) {
376 rv = CKR_HOST_MEMORY;
377 goto finish;
380 tmp_mechlistsize = 0;
381 for (i = 0; i < slot_mechlistsize; i++) {
382 /* filter out the disabled mechanisms */
383 if (pkcs11_is_dismech(fw_st_id, tmp_slot_mechlist[i])) {
384 continue;
387 slot_mechlist[tmp_mechlistsize] = tmp_slot_mechlist[i];
388 tmp_mechlistsize++;
390 slot_mechlistsize = tmp_mechlistsize;
392 /* Sort the mechanisms by value. */
393 qsort(slot_mechlist, slot_mechlistsize, sizeof (CK_MECHANISM_TYPE),
394 qsort_mechtypes);
396 /* Ensure list contains the mechanisms. */
397 rv = meta_mechManager_allocmechs(slot_mechlist, slot_mechlistsize,
398 &index);
399 if (rv != CKR_OK)
400 goto finish;
402 /* Update the mechanism info. */
403 (void) pthread_rwlock_wrlock(&mechlist_lock);
404 for (mechnum = 0; mechnum < slot_mechlistsize; mechnum++) {
405 found = find_mech_index(slot_mechlist[mechnum], &index);
406 if (!found) {
407 /* This shouldn't happen. */
408 rv = CKR_GENERAL_ERROR;
409 goto finish;
412 rv = update_slotmech(slot_mechlist[mechnum], slotnum, index);
413 if (rv != CKR_OK) {
414 /* Ignore error, make best effort to finish update. */
415 rv = CKR_OK;
416 continue;
419 (void) pthread_rwlock_unlock(&mechlist_lock);
421 finish:
422 if (slot_mechlist) {
423 free(slot_mechlist);
426 if (tmp_slot_mechlist) {
427 free(tmp_slot_mechlist);
430 return (rv);
435 * update_slotmech
437 * Updates the information for a particular mechanism for a particular slot.
438 * (ie, slotlist[foo].slots[bar])
440 * It is assumed that the caller to this function (all of which are
441 * in this file) holds the write-lock to "mechlist_lock".
444 static CK_RV
445 update_slotmech(CK_MECHANISM_TYPE mech, CK_ULONG slotnum,
446 unsigned long index)
448 CK_RV rv = CKR_OK;
449 CK_MECHANISM_INFO info;
450 CK_SLOT_ID fw_st_id, true_id;
452 mechlist[index].slots[slotnum].slotnum = slotnum;
453 fw_st_id = meta_slotManager_get_framework_table_id(slotnum);
454 true_id = TRUEID(fw_st_id);
457 * Check if the specified mechanism is in the disabled list
458 * of the specified slot. If so, we can immediately conclude
459 * that it is not supported by the specified slot.
461 if (pkcs11_is_dismech(fw_st_id, mech)) {
463 * we mark this as initialized so that we won't try
464 * to do this check later
466 mechlist[index].slots[slotnum].initialized = B_TRUE;
467 mechlist[index].slots[slotnum].supported = B_FALSE;
468 bzero(&mechlist[index].slots[slotnum].mechanism_info,
469 sizeof (CK_MECHANISM_INFO));
470 goto finish;
473 rv = FUNCLIST(fw_st_id)->C_GetMechanismInfo(true_id, mech, &info);
474 if (rv == CKR_OK) {
475 mechlist[index].slots[slotnum].initialized = B_TRUE;
476 mechlist[index].slots[slotnum].supported = B_TRUE;
477 mechlist[index].slots[slotnum].mechanism_info = info;
478 } else {
479 /* record that the mechanism isn't supported for the slot */
480 mechlist[index].slots[slotnum].initialized = B_TRUE;
481 mechlist[index].slots[slotnum].supported = B_FALSE;
482 bzero(&mechlist[index].slots[slotnum].mechanism_info,
483 sizeof (CK_MECHANISM_INFO));
486 finish:
487 return (rv);
492 * meta_mechManager_allocmechs
494 * Ensures that all of the specified mechanisms are present in the
495 * mechlist. If a mechanism is not present, an uninitialized entry is
496 * added for it.
498 * The returned index can be used by the caller as a hint to where the
499 * first mechanism was located.
501 static CK_RV
502 meta_mechManager_allocmechs(CK_MECHANISM_TYPE *new_mechs,
503 unsigned long num_new_mechs, unsigned long *index_hint)
505 CK_RV rv = CKR_OK;
506 unsigned long i, index = 0;
507 boolean_t found;
509 /* The optimistic assumption is that the mech is already present. */
510 (void) pthread_rwlock_rdlock(&mechlist_lock);
511 for (i = 0; i < num_new_mechs; i++) {
512 found = find_mech_index(new_mechs[i], &index);
514 if (i == 0)
515 *index_hint = index;
517 if (!found)
518 break;
520 (void) pthread_rwlock_unlock(&mechlist_lock);
522 if (found) {
523 return (CKR_OK);
527 * We stopped searching when the first unknown mech was found. Now
528 * obtain a write-lock, and continue from where we left off, inserting
529 * unknown mechanisms.
532 (void) pthread_rwlock_wrlock(&mechlist_lock);
533 for (; i < num_new_mechs; i++) {
534 found = find_mech_index(new_mechs[i], &index);
536 if (!found) {
537 mechinfo_t *new_mechinfos;
539 new_mechinfos = calloc(meta_slotManager_get_slotcount(),
540 sizeof (mechinfo_t));
541 if (new_mechinfos == NULL) {
542 rv = CKR_HOST_MEMORY;
543 goto finish;
547 * If the current storage for the mechlist is too
548 * small, allocate a new list twice as large.
550 if (num_mechs == true_mechlist_size) {
551 mechlist_t *newmechlist;
553 newmechlist = reallocarray(mechlist,
554 2 * true_mechlist_size,
555 sizeof (mechlist_t));
557 if (newmechlist == NULL) {
558 rv = CKR_HOST_MEMORY;
559 free(new_mechinfos);
560 goto finish;
563 mechlist = newmechlist;
564 true_mechlist_size *= 2;
567 /* Shift existing entries to make space. */
568 (void) memmove(&mechlist[index+1], &mechlist[index],
569 (num_mechs - index) * sizeof (mechlist_t));
570 num_mechs++;
572 mechlist[index].type = new_mechs[i];
573 mechlist[index].slots = new_mechinfos;
577 finish:
578 (void) pthread_rwlock_unlock(&mechlist_lock);
580 return (rv);
585 * find_mech_index
587 * Performs a search of mechlist for the specified mechanism, and
588 * returns if the mechanism was found or not. The value of the "index"
589 * argument will be where the mech is (if found), or where it should
590 * be (if not found).
592 * The current value of "index" will be used as a starting point, if the
593 * caller already knows where the mechanism is likely to be.
595 * The caller is assumed to have a lock on the mechlist, preventing it
596 * from being changed while searching (also to ensure the returned index
597 * will remain valid until the list is unlocked).
599 * FUTURE: convert to binary search [from O(N) to a O(log(N))].
601 * NOTES:
602 * 1) This function assumes that mechMap is a sorted list.
604 static boolean_t
605 find_mech_index(CK_MECHANISM_TYPE mechanism, unsigned long *index)
607 boolean_t found = B_FALSE;
608 unsigned long i;
610 for (i = 0; i < num_mechs; i++) {
612 if (mechlist[i].type == mechanism) {
613 found = B_TRUE;
614 break;
617 if (mechlist[i].type > mechanism)
618 break;
621 *index = i;
623 return (found);
626 static int
627 qsort_mechtypes(const void *arg1, const void *arg2)
629 CK_MECHANISM_TYPE mech1 = *((CK_MECHANISM_TYPE *)arg1);
630 CK_MECHANISM_TYPE mech2 = *((CK_MECHANISM_TYPE *)arg2);
632 if (mech1 > mech2)
633 return (1);
634 if (mech1 < mech2)
635 return (-1);
636 return (0);
640 * Check if the specified mechanism is supported by the specified slot.
641 * The result is returned in the "supports" argument. If the "slot_info"
642 * argument is not NULL, it will be filled with information about
643 * the slot.
645 CK_RV
646 meta_mechManager_slot_supports_mech(CK_MECHANISM_TYPE mechanism,
647 CK_ULONG slotnum, boolean_t *supports, mechinfo_t **slot_info,
648 boolean_t force_update, CK_MECHANISM_INFO *mech_info)
651 boolean_t found;
652 CK_RV rv;
653 unsigned long index;
654 CK_MECHANISM_INFO info;
656 *supports = B_FALSE;
658 rv = meta_mechManager_update_mech(mechanism, force_update);
659 if (rv != CKR_OK)
660 return (rv);
662 (void) pthread_rwlock_rdlock(&mechlist_lock);
664 found = find_mech_index(mechanism, &index);
665 if (!found) {
666 goto finish;
669 if ((mechlist[index].slots[slotnum].initialized) &&
670 (mechlist[index].slots[slotnum].supported)) {
671 if (mech_info) {
672 info = mechlist[index].slots[slotnum].mechanism_info;
673 if (!(info.flags & mech_info->flags)) {
674 goto finish;
677 *supports = B_TRUE;
678 if (slot_info) {
679 *slot_info = &(mechlist[index].slots[slotnum]);
683 finish:
684 (void) pthread_rwlock_unlock(&mechlist_lock);
686 return (rv);