/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * Mechanism Manager - centralized knowledge of mechanisms. * * The core of the mechmanager is the "mechlist" data structure. It contains * information about all mechanisms available from providers that have been * exposed to the application. * * Each element in the array represents a particular mechanism type. The * array is sorted by type, so that searching by mechanism can be done * quickly. Each element also contains the mechanism data for each slot. * * The mechlist is constructed on an as-needed basis, entries are not added * until the application triggers an action that requires an entry to be * added (or updated). * */ #include #include #include "pkcs11Conf.h" #include "metaGlobal.h" /* Global data... */ #define INITIAL_MECHLIST_SIZE 256 typedef struct mechliststruct { CK_MECHANISM_TYPE type; mechinfo_t *slots; } mechlist_t; static pthread_rwlock_t mechlist_lock = PTHREAD_RWLOCK_INITIALIZER; static mechlist_t *mechlist; static unsigned long num_mechs; static unsigned long true_mechlist_size; /* Prototypes... */ static CK_RV meta_mechManager_update_mech(CK_MECHANISM_TYPE, boolean_t); static CK_RV meta_mechManager_update_slot(CK_ULONG); static CK_RV update_slotmech(CK_MECHANISM_TYPE, CK_ULONG, unsigned long); static CK_RV meta_mechManager_allocmechs(CK_MECHANISM_TYPE *, unsigned long, unsigned long *); static boolean_t find_mech_index(CK_MECHANISM_TYPE, unsigned long *); static int qsort_mechtypes(const void *, const void *); /* * meta_mechManager_initialize * * Called from C_Initialize. Allocates and initializes storage needed * by the slot manager. */ CK_RV meta_mechManager_initialize() { /* The mechlist can dynamically grow, but let's preallocate space. */ mechlist = calloc(INITIAL_MECHLIST_SIZE, sizeof (mechlist_t)); if (mechlist == NULL) return (CKR_HOST_MEMORY); true_mechlist_size = INITIAL_MECHLIST_SIZE; num_mechs = 0; return (CKR_OK); } /* * meta_mechManager_finalize * * Called from C_Finalize. Deallocates any storage held by the slot manager. */ void meta_mechManager_finalize() { int i; /* No need to lock list, we assume all sessions are closed. */ for (i = 0; i < num_mechs; i++) { free(mechlist[i].slots); } free(mechlist); mechlist = NULL; num_mechs = 0; true_mechlist_size = 0; } /* * meta_mechManager_get_mechs * * Get list of all available mechanisms. * * Follows PKCS#11 semantics, where list may be NULL to only request a * count of available mechanisms. */ CK_RV meta_mechManager_get_mechs(CK_MECHANISM_TYPE *list, CK_ULONG *listsize) { CK_RV rv = CKR_OK; CK_ULONG num_found = 0; CK_ULONG slotnum, num_slots; unsigned long i; /* get number of slots */ num_slots = meta_slotManager_get_slotcount(); /* * Update slot info. Ignore any errors. * * NOTE: Due to the PKCS#11 convention of calling C_GetMechanismList * twice (once to get the count, again to get the actual list), this * is somewhat inefficient... However, I don't see an easy way to fix * that without impacting other cases (eg, when the first call contains * an "optimistic" pre-allocated buffer). */ for (slotnum = 0; slotnum < num_slots; slotnum++) { (void) meta_mechManager_update_slot(slotnum); } /* * Count the number of mechanisms. We can't just use num_mechs, * because some mechs may not currently be supported on any slot. * Also, it may not be allowed based on the mechanism policy. */ (void) pthread_rwlock_rdlock(&mechlist_lock); for (i = 0; i < num_mechs; i++) { CK_ULONG j; boolean_t supported; if (pkcs11_is_dismech(METASLOT_FRAMEWORK_ID, mechlist[i].type)) { /* skip mechs disabled by policy */ continue; } supported = FALSE; for (j = 0; j < num_slots; j++) { if (!mechlist[i].slots[j].initialized) continue; if (mechlist[i].slots[j].supported) { supported = B_TRUE; break; } } if (supported) { num_found++; if (list && *listsize >= num_found) { list[num_found - 1] = mechlist[i].type; } } } (void) pthread_rwlock_unlock(&mechlist_lock); if (num_found > *listsize) rv = CKR_BUFFER_TOO_SMALL; *listsize = num_found; return (rv); } /* * meta_mechManager_get_slots * * Get list of all slots supporting the specified mechanism. * * The "mech_support_info" argument should have allocated enough * space to accomodate the list of slots that supports the * specified mechanism. The "num_supporting_slots" field * in the "mech_support_info" structure will indicate how * many slots are found to support the mechanism. * * If any error occurred in getting the list, info in * mech_support_info argument is not updated. * */ CK_RV meta_mechManager_get_slots(mech_support_info_t *mech_support_info, boolean_t force_update, CK_MECHANISM_INFO *mech_info) { CK_RV rv; boolean_t found; CK_ULONG i, num_slots; unsigned long index, num_found = 0; CK_MECHANISM_INFO info; rv = meta_mechManager_update_mech(mech_support_info->mech, force_update); if (rv != CKR_OK) { return (rv); } (void) pthread_rwlock_rdlock(&mechlist_lock); found = find_mech_index(mech_support_info->mech, &index); if (!found) { goto finish; } num_slots = meta_slotManager_get_slotcount(); for (i = 0; i < num_slots; i++) { if (!mechlist[index].slots[i].initialized || !mechlist[index].slots[i].supported) continue; if (mech_info) { info = mechlist[index].slots[i].mechanism_info; if (!(info.flags & mech_info->flags)) { continue; } } num_found++; (mech_support_info->supporting_slots)[num_found - 1] = &mechlist[index].slots[i]; } finish: (void) pthread_rwlock_unlock(&mechlist_lock); if (num_found == 0) { rv = CKR_MECHANISM_INVALID; } else { mech_support_info->num_supporting_slots = num_found; } return (rv); } /* * meta_mechManager_update_mech * * Updates a mechanism in the mechlist. If the mechanism is not * listed, all providers will be queried. If the mechanism * is present, but not initialized for some providers, those providers * will be queried. Existing entries will not be updated unless the * force_refresh flag is set. * * The force_refresh flag is used by C_GetMechanismInfo, to force an * update. Updates are not forced during the common usage by operations * [eg C_EncryptInit] to avoid poor performance. */ static CK_RV meta_mechManager_update_mech(CK_MECHANISM_TYPE mech, boolean_t force_refresh) { CK_RV rv; CK_ULONG slot, num_slots; unsigned long index = 0; boolean_t found; /* Ensure list contains the mechanism. */ rv = meta_mechManager_allocmechs(&mech, 1, &index); if (rv != CKR_OK) return (rv); (void) pthread_rwlock_wrlock(&mechlist_lock); /* * We didn't retain a lock after the first search, so it's possible * that the mechlist was updated. Search again, but use the last * index as a hint to quickly find the mechanism. */ found = find_mech_index(mech, &index); if (!found) { /* Shouldn't happen - entries are not removed from list. */ rv = CKR_GENERAL_ERROR; goto finish; } num_slots = meta_slotManager_get_slotcount(); for (slot = 0; slot < num_slots; slot++) { if (force_refresh || !mechlist[index].slots[slot].initialized) { rv = update_slotmech(mech, slot, index); if (rv != CKR_OK) { /* Ignore error and continue with next slot. */ rv = CKR_OK; } } } finish: (void) pthread_rwlock_unlock(&mechlist_lock); return (rv); } /* * meta_mechManager_update_slot * * Updates a slot in the mechlist. Called by C_GetMechanismList * [by way of meta_mechManager_get_mechs()]. Unlike * meta_mechManager_get_slots(), the context is always to force a refresh * of the mechlist. * */ static CK_RV meta_mechManager_update_slot(CK_ULONG slotnum) { unsigned long index = 0; CK_MECHANISM_TYPE *slot_mechlist = NULL, *tmp_slot_mechlist = NULL; CK_ULONG slot_mechlistsize, mechnum, tmp_mechlistsize; CK_RV rv; boolean_t found; CK_SLOT_ID fw_st_id, true_id; int i; fw_st_id = meta_slotManager_get_framework_table_id(slotnum); true_id = TRUEID(fw_st_id); /* First, get the count. */ rv = FUNCLIST(fw_st_id)->C_GetMechanismList(true_id, NULL, &slot_mechlistsize); if (rv != CKR_OK) { goto finish; } tmp_slot_mechlist = malloc( slot_mechlistsize * sizeof (CK_MECHANISM_TYPE)); if (tmp_slot_mechlist == NULL) { rv = CKR_HOST_MEMORY; goto finish; } /* Next, get the actual list. */ rv = FUNCLIST(fw_st_id)->C_GetMechanismList(true_id, tmp_slot_mechlist, &slot_mechlistsize); if (rv != CKR_OK) { goto finish; } /* * filter the list of mechanisms returned by the underlying slot * to remove any mechanisms that are explicitly disabled * in the configuration file. */ slot_mechlist = malloc(slot_mechlistsize * sizeof (CK_MECHANISM_TYPE)); if (slot_mechlist == NULL) { rv = CKR_HOST_MEMORY; goto finish; } tmp_mechlistsize = 0; for (i = 0; i < slot_mechlistsize; i++) { /* filter out the disabled mechanisms */ if (pkcs11_is_dismech(fw_st_id, tmp_slot_mechlist[i])) { continue; } slot_mechlist[tmp_mechlistsize] = tmp_slot_mechlist[i]; tmp_mechlistsize++; } slot_mechlistsize = tmp_mechlistsize; /* Sort the mechanisms by value. */ qsort(slot_mechlist, slot_mechlistsize, sizeof (CK_MECHANISM_TYPE), qsort_mechtypes); /* Ensure list contains the mechanisms. */ rv = meta_mechManager_allocmechs(slot_mechlist, slot_mechlistsize, &index); if (rv != CKR_OK) goto finish; /* Update the mechanism info. */ (void) pthread_rwlock_wrlock(&mechlist_lock); for (mechnum = 0; mechnum < slot_mechlistsize; mechnum++) { found = find_mech_index(slot_mechlist[mechnum], &index); if (!found) { /* This shouldn't happen. */ rv = CKR_GENERAL_ERROR; goto finish; } rv = update_slotmech(slot_mechlist[mechnum], slotnum, index); if (rv != CKR_OK) { /* Ignore error, make best effort to finish update. */ rv = CKR_OK; continue; } } (void) pthread_rwlock_unlock(&mechlist_lock); finish: if (slot_mechlist) { free(slot_mechlist); } if (tmp_slot_mechlist) { free(tmp_slot_mechlist); } return (rv); } /* * update_slotmech * * Updates the information for a particular mechanism for a particular slot. * (ie, slotlist[foo].slots[bar]) * * It is assumed that the caller to this function (all of which are * in this file) holds the write-lock to "mechlist_lock". * */ static CK_RV update_slotmech(CK_MECHANISM_TYPE mech, CK_ULONG slotnum, unsigned long index) { CK_RV rv = CKR_OK; CK_MECHANISM_INFO info; CK_SLOT_ID fw_st_id, true_id; mechlist[index].slots[slotnum].slotnum = slotnum; fw_st_id = meta_slotManager_get_framework_table_id(slotnum); true_id = TRUEID(fw_st_id); /* * Check if the specified mechanism is in the disabled list * of the specified slot. If so, we can immediately conclude * that it is not supported by the specified slot. */ if (pkcs11_is_dismech(fw_st_id, mech)) { /* * we mark this as initialized so that we won't try * to do this check later */ mechlist[index].slots[slotnum].initialized = B_TRUE; mechlist[index].slots[slotnum].supported = B_FALSE; bzero(&mechlist[index].slots[slotnum].mechanism_info, sizeof (CK_MECHANISM_INFO)); goto finish; } rv = FUNCLIST(fw_st_id)->C_GetMechanismInfo(true_id, mech, &info); if (rv == CKR_OK) { mechlist[index].slots[slotnum].initialized = B_TRUE; mechlist[index].slots[slotnum].supported = B_TRUE; mechlist[index].slots[slotnum].mechanism_info = info; } else { /* record that the mechanism isn't supported for the slot */ mechlist[index].slots[slotnum].initialized = B_TRUE; mechlist[index].slots[slotnum].supported = B_FALSE; bzero(&mechlist[index].slots[slotnum].mechanism_info, sizeof (CK_MECHANISM_INFO)); } finish: return (rv); } /* * meta_mechManager_allocmechs * * Ensures that all of the specified mechanisms are present in the * mechlist. If a mechanism is not present, an uninitialized entry is * added for it. * * The returned index can be used by the caller as a hint to where the * first mechanism was located. */ static CK_RV meta_mechManager_allocmechs(CK_MECHANISM_TYPE *new_mechs, unsigned long num_new_mechs, unsigned long *index_hint) { CK_RV rv = CKR_OK; unsigned long i, index = 0; boolean_t found; /* The optimistic assumption is that the mech is already present. */ (void) pthread_rwlock_rdlock(&mechlist_lock); for (i = 0; i < num_new_mechs; i++) { found = find_mech_index(new_mechs[i], &index); if (i == 0) *index_hint = index; if (!found) break; } (void) pthread_rwlock_unlock(&mechlist_lock); if (found) { return (CKR_OK); } /* * We stopped searching when the first unknown mech was found. Now * obtain a write-lock, and continue from where we left off, inserting * unknown mechanisms. */ (void) pthread_rwlock_wrlock(&mechlist_lock); for (; i < num_new_mechs; i++) { found = find_mech_index(new_mechs[i], &index); if (!found) { mechinfo_t *new_mechinfos; new_mechinfos = calloc(meta_slotManager_get_slotcount(), sizeof (mechinfo_t)); if (new_mechinfos == NULL) { rv = CKR_HOST_MEMORY; goto finish; } /* * If the current storage for the mechlist is too * small, allocate a new list twice as large. */ if (num_mechs == true_mechlist_size) { mechlist_t *newmechlist; newmechlist = realloc(mechlist, 2 * true_mechlist_size * sizeof (mechlist_t)); if (newmechlist == NULL) { rv = CKR_HOST_MEMORY; free(new_mechinfos); goto finish; } mechlist = newmechlist; true_mechlist_size *= 2; } /* Shift existing entries to make space. */ (void) memmove(&mechlist[index+1], &mechlist[index], (num_mechs - index) * sizeof (mechlist_t)); num_mechs++; mechlist[index].type = new_mechs[i]; mechlist[index].slots = new_mechinfos; } } finish: (void) pthread_rwlock_unlock(&mechlist_lock); return (rv); } /* * find_mech_index * * Performs a search of mechlist for the specified mechanism, and * returns if the mechanism was found or not. The value of the "index" * argument will be where the mech is (if found), or where it should * be (if not found). * * The current value of "index" will be used as a starting point, if the * caller already knows where the mechanism is likely to be. * * The caller is assumed to have a lock on the mechlist, preventing it * from being changed while searching (also to ensure the returned index * will remain valid until the list is unlocked). * * FUTURE: convert to binary search [from O(N) to a O(log(N))]. * * NOTES: * 1) This function assumes that mechMap is a sorted list. */ static boolean_t find_mech_index(CK_MECHANISM_TYPE mechanism, unsigned long *index) { boolean_t found = B_FALSE; unsigned long i; for (i = 0; i < num_mechs; i++) { if (mechlist[i].type == mechanism) { found = B_TRUE; break; } if (mechlist[i].type > mechanism) break; } *index = i; return (found); } static int qsort_mechtypes(const void *arg1, const void *arg2) { CK_MECHANISM_TYPE mech1 = *((CK_MECHANISM_TYPE *)arg1); CK_MECHANISM_TYPE mech2 = *((CK_MECHANISM_TYPE *)arg2); if (mech1 > mech2) return (1); if (mech1 < mech2) return (-1); return (0); } /* * Check if the specified mechanism is supported by the specified slot. * The result is returned in the "supports" argument. If the "slot_info" * argument is not NULL, it will be filled with information about * the slot. */ CK_RV meta_mechManager_slot_supports_mech(CK_MECHANISM_TYPE mechanism, CK_ULONG slotnum, boolean_t *supports, mechinfo_t **slot_info, boolean_t force_update, CK_MECHANISM_INFO *mech_info) { boolean_t found; CK_RV rv; unsigned long index; CK_MECHANISM_INFO info; *supports = B_FALSE; rv = meta_mechManager_update_mech(mechanism, force_update); if (rv != CKR_OK) return (rv); (void) pthread_rwlock_rdlock(&mechlist_lock); found = find_mech_index(mechanism, &index); if (!found) { goto finish; } if ((mechlist[index].slots[slotnum].initialized) && (mechlist[index].slots[slotnum].supported)) { if (mech_info) { info = mechlist[index].slots[slotnum].mechanism_info; if (!(info.flags & mech_info->flags)) { goto finish; } } *supports = B_TRUE; if (slot_info) { *slot_info = &(mechlist[index].slots[slotnum]); } } finish: (void) pthread_rwlock_unlock(&mechlist_lock); return (rv); }