/* * 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 (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * Copyright 2010 Nexenta Systems, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pkcs11Global.h" #include "pkcs11Conf.h" #include "pkcs11Slot.h" #include "metaGlobal.h" /* * Fastpath is used when there is only one slot available from a single provider * plugged into the framework this is the common case. * These globals are used to track the function pointers and policy when * the fast-path is activated. * This will need to be revisted if per-slot policy is ever * implemented. */ boolean_t purefastpath = B_FALSE; boolean_t policyfastpath = B_FALSE; CK_FUNCTION_LIST_PTR fast_funcs = NULL; CK_SLOT_ID fast_slot = 0; boolean_t metaslot_enabled = B_FALSE; boolean_t metaslot_auto_key_migrate = B_FALSE; metaslot_config_t metaslot_config; void (*Tmp_GetThreshold)(void *) = NULL; cipher_mechs_threshold_t meta_mechs_threshold[MAX_NUM_THRESHOLD]; static const char *conf_err = "See cryptoadm(8). Skipping this plug-in."; /* * Set up metaslot for the framework using either user configuration * or system wide configuration options * * Also sets up the global "slottable" to have the first slot be metaslot. */ static CK_RV setup_metaslot(uentry_t *metaslot_entry) { CK_RV rv; CK_MECHANISM_TYPE_PTR prov_pol_mechs = NULL; pkcs11_slot_t *cur_slot; /* process policies for mechanisms */ if ((metaslot_entry) && (metaslot_entry->count > 0)) { rv = pkcs11_mech_parse(metaslot_entry->policylist, &prov_pol_mechs, metaslot_entry->count); if (rv == CKR_HOST_MEMORY) { cryptoerror(LOG_ERR, "libpkcs11: Could not parse configuration," "out of memory. Cannot continue parsing " "%s.\n", _PATH_PKCS11_CONF); return (rv); } else if (rv == CKR_MECHANISM_INVALID) { /* * Configuration file is corrupted for metaslot */ cryptoerror(LOG_ERR, "libpkcs11: Policy invalid or corrupted " "for metaslot. Use cryptoadm(8) to fix " "this. Disabling metaslot functionality.\n"); metaslot_enabled = B_FALSE; return (rv); } } /* * Check for metaslot policy. If all mechanisms are * disabled, disable metaslot since there is nothing * interesting for it to do */ if ((metaslot_entry) && (metaslot_entry->flag_enabledlist) && (prov_pol_mechs == NULL)) { metaslot_enabled = B_FALSE; return (rv); } /* * save system wide value for metaslot's keystore. * If either slot description or token label is specified by * the user, the system wide value for both is ignored. */ if ((metaslot_entry) && (!metaslot_config.keystore_token_specified) && (!metaslot_config.keystore_slot_specified)) { /* * blank_str is used for comparing with token label, * and slot description, make sure it is better than * the larger of both */ char blank_str[TOKEN_LABEL_SIZE + SLOT_DESCRIPTION_SIZE]; bzero(blank_str, sizeof (blank_str)); if (memcmp(metaslot_entry->metaslot_ks_token, blank_str, TOKEN_LABEL_SIZE) != 0) { metaslot_config.keystore_token_specified = B_TRUE; (void) strlcpy( (char *)metaslot_config.keystore_token, (const char *)metaslot_entry->metaslot_ks_token, TOKEN_LABEL_SIZE); } if (memcmp(metaslot_entry->metaslot_ks_slot, blank_str, SLOT_DESCRIPTION_SIZE) != 0) { metaslot_config.keystore_slot_specified = B_TRUE; (void) strlcpy( (char *)metaslot_config.keystore_slot, (const char *)metaslot_entry->metaslot_ks_slot, SLOT_DESCRIPTION_SIZE); } } /* check system-wide value for auto_key_migrate */ if (metaslot_config.auto_key_migrate_specified) { /* take user's specified value */ metaslot_auto_key_migrate = metaslot_config.auto_key_migrate; } else { if (metaslot_entry) { /* use system-wide default */ metaslot_auto_key_migrate = metaslot_entry->flag_metaslot_auto_key_migrate; } else { /* * there's no system wide metaslot entry, * default auto_key_migrate to true */ metaslot_auto_key_migrate = B_TRUE; } } /* Make first slotID be 0, for metaslot. */ slottable->st_first = 0; /* Set up the slottable entry for metaslot */ slottable->st_slots[0] = NULL; cur_slot = calloc(1, sizeof (pkcs11_slot_t)); if (cur_slot == NULL) { rv = CKR_HOST_MEMORY; return (rv); } cur_slot->sl_wfse_state = WFSE_CLEAR; cur_slot->sl_enabledpol = B_FALSE; cur_slot->sl_no_wfse = B_FALSE; (void) pthread_mutex_init(&cur_slot->sl_mutex, NULL); /* * The metaslot entry was prealloc'd by * pkcs11_slottable_increase() */ (void) pthread_mutex_lock(&slottable->st_mutex); slottable->st_slots[0] = cur_slot; (void) pthread_mutex_unlock(&slottable->st_mutex); (void) pthread_mutex_lock(&cur_slot->sl_mutex); cur_slot->sl_id = METASLOT_SLOTID; cur_slot->sl_func_list = &metaslot_functionList; if (metaslot_entry) { cur_slot->sl_enabledpol = metaslot_entry->flag_enabledlist; cur_slot->sl_pol_count = metaslot_entry->count; } else { /* if no metaslot entry, assume all mechs are enabled */ cur_slot->sl_enabledpol = B_FALSE; cur_slot->sl_pol_count = 0; } cur_slot->sl_pol_mechs = prov_pol_mechs; cur_slot->sl_dldesc = NULL; /* not applicable */ cur_slot->sl_prov_id = 0; (void) pthread_mutex_unlock(&cur_slot->sl_mutex); /* Call the meta_Initialize() to initialize metaslot */ rv = meta_Initialize(NULL); if (rv != CKR_OK) { cryptoerror(LOG_ERR, "libpkcs11: Can't initialize metaslot (%s)", pkcs11_strerror(rv)); goto cleanup; } return (CKR_OK); cleanup: metaslot_enabled = B_FALSE; slottable->st_slots[0] = NULL; if (cur_slot) { (void) pthread_mutex_destroy(&cur_slot->sl_mutex); free(cur_slot); } return (rv); } /* * For each provider found in pkcs11.conf: expand $ISA if necessary, * verify the module is signed, load the provider, find all of its * slots, and store the function list and disabled policy. * * This function requires that the uentrylist_t and pkcs11_slottable_t * already have memory allocated, and that the uentrylist_t is already * populated with provider and policy information. * * pInitArgs can be set to NULL, but is normally the same value * the framework's C_Initialize() was called with. * * Unless metaslot is explicitly disabled, it is setup when all other * providers are loaded. */ CK_RV pkcs11_slot_mapping(uentrylist_t *pplist, CK_VOID_PTR pInitArgs) { CK_RV rv = CKR_OK; CK_RV prov_rv; /* Provider's return code */ CK_INFO prov_info; CK_RV (*Tmp_C_GetFunctionList)(CK_FUNCTION_LIST_PTR_PTR); CK_FUNCTION_LIST_PTR prov_funcs = NULL; /* Provider's function list */ CK_ULONG prov_slot_count; /* Number of slots */ CK_SLOT_ID slot_id; /* slotID assigned for framework */ CK_SLOT_ID_PTR prov_slots = NULL; /* Provider's slot list */ /* Enabled or Disabled policy */ CK_MECHANISM_TYPE_PTR prov_pol_mechs = NULL; void *dldesc = NULL; char *isa, *fullpath = NULL, *dl_error; uentrylist_t *phead; uint_t prov_count = 0; pkcs11_slot_t *cur_slot; CK_ULONG i; size_t len; uentry_t *metaslot_entry = NULL; /* number of slots in the framework, not including metaslot */ uint_t slot_count = 0; phead = pplist; /* Loop through all of the provider listed in pkcs11.conf */ while (phead != NULL) { if (!strcasecmp(phead->puent->name, "metaslot")) { /* * Skip standard processing for metaslot * entry since it is not an actual library * that can be dlopened. * It will be initialized later. */ if (metaslot_entry != NULL) { cryptoerror(LOG_ERR, "libpkcs11: multiple entries for metaslot " "detected. All but the first entry will " "be ignored"); } else { metaslot_entry = phead->puent; } goto contparse; } if (!strcasecmp(phead->puent->name, FIPS_KEYWORD)) { /* * Skip standard processing for fips-140 * entry since it is not an actual library * that can be dlopened. */ goto contparse; } /* Check for Instruction Set Architecture indicator */ if ((isa = strstr(phead->puent->name, PKCS11_ISA)) != NULL) { /* Substitute the architecture dependent path */ len = strlen(phead->puent->name) - strlen(PKCS11_ISA) + strlen(PKCS11_ISA_DIR) + 1; if ((fullpath = (char *)malloc(len)) == NULL) { cryptoerror(LOG_ERR, "libpksc11: parsing %s, out of memory. " "Cannot continue parsing.", _PATH_PKCS11_CONF); rv = CKR_HOST_MEMORY; goto conferror; } *isa = '\000'; isa += strlen(PKCS11_ISA); (void) snprintf(fullpath, len, "%s%s%s", phead->puent->name, PKCS11_ISA_DIR, isa); } else if ((fullpath = strdup(phead->puent->name)) == 0) { cryptoerror(LOG_ERR, "libpkcs11: parsing %s, out of memory. " "Cannot continue parsing.", _PATH_PKCS11_CONF); rv = CKR_HOST_MEMORY; goto conferror; } /* * Open the provider. We assume all of our plugins have * their symbols properly defined, so the use of RTLD_NOW * to flush out errors immediately is not necessary. * * Note that for proper operation, all plugins must be * built with direct bindings enabled. */ dldesc = dlopen(fullpath, RTLD_LAZY); /* * If we failed to load it, we will just skip this * provider and move on to the next one. */ if (dldesc == NULL) { dl_error = dlerror(); cryptoerror(LOG_ERR, "libpkcs11: Cannot load PKCS#11 library %s. " "dlerror: %s. %s", fullpath, dl_error != NULL ? dl_error : "Unknown", conf_err); goto contparse; } /* Get the pointer to provider's C_GetFunctionList() */ Tmp_C_GetFunctionList = (CK_RV(*)())dlsym(dldesc, "C_GetFunctionList"); /* * If we failed to get the pointer to C_GetFunctionList(), * skip this provider and continue to the next one. */ if (Tmp_C_GetFunctionList == NULL) { cryptoerror(LOG_ERR, "libpkcs11: Could not dlsym() C_GetFunctionList() " "for %s. May not be a PKCS#11 library. %s", fullpath, conf_err); (void) dlclose(dldesc); goto contparse; } /* Get the provider's function list */ prov_rv = Tmp_C_GetFunctionList(&prov_funcs); /* * If we failed to get the provider's function list, * skip this provider and continue to the next one. */ if (prov_rv != CKR_OK) { cryptoerror(LOG_ERR, "libpkcs11: Could not get function list for %s. " "%s Error: %s.", fullpath, conf_err, pkcs11_strerror(prov_rv)); (void) dlclose(dldesc); goto contparse; } /* Initialize this provider */ prov_rv = prov_funcs->C_Initialize(pInitArgs); /* * If we failed to initialize this provider, * skip this provider and continue to the next one. */ if ((prov_rv != CKR_OK) && (prov_rv != CKR_CRYPTOKI_ALREADY_INITIALIZED)) { cryptoerror(LOG_ERR, "libpkcs11: Could not initialize %s. " "%s Error: %s.", fullpath, conf_err, pkcs11_strerror(prov_rv)); (void) dlclose(dldesc); goto contparse; } /* * Make sure this provider is implementing the same * major version, and at least the same minor version * that we are. */ prov_rv = prov_funcs->C_GetInfo(&prov_info); /* * If we can't verify that we are implementing the * same major version, or if it is definitely not the same * version, we need to skip this provider. */ if ((prov_rv != CKR_OK) || (prov_info.cryptokiVersion.major != CRYPTOKI_VERSION_MAJOR)) { if (prov_rv != CKR_OK) { cryptoerror(LOG_ERR, "libpkcs11: Could not verify version of " "%s. %s Error: %s.", fullpath, conf_err, pkcs11_strerror(prov_rv)); } else { cryptoerror(LOG_ERR, "libpkcs11: Only CRYPTOKI major version " "%d is supported. %s is major " "version %d. %s", CRYPTOKI_VERSION_MAJOR, fullpath, prov_info.cryptokiVersion.major, conf_err); } (void) prov_funcs->C_Finalize(NULL); (void) dlclose(dldesc); goto contparse; } /* * Warn the administrator (at debug) that a provider with * a significantly older or newer version of * CRYPTOKI is being used. It should not cause * problems, but logging a warning makes it easier * to debug later. */ if ((prov_info.cryptokiVersion.minor < CRYPTOKI_VERSION_WARN_MINOR) || (prov_info.cryptokiVersion.minor > CRYPTOKI_VERSION_MINOR)) { cryptoerror(LOG_DEBUG, "libpkcs11: %s CRYPTOKI minor version, %d, may " "not be compatible with minor version %d.", fullpath, prov_info.cryptokiVersion.minor, CRYPTOKI_VERSION_MINOR); } /* * Find out how many slots this provider has, * call with tokenPresent set to FALSE so all * potential slots are returned. */ prov_rv = prov_funcs->C_GetSlotList(FALSE, NULL, &prov_slot_count); /* * If the call failed, or if no slots are returned, * then skip this provider and continue to next one. */ if (prov_rv != CKR_OK) { cryptoerror(LOG_ERR, "libpksc11: Could not get slot list from %s. " "%s Error: %s.", fullpath, conf_err, pkcs11_strerror(prov_rv)); (void) prov_funcs->C_Finalize(NULL); (void) dlclose(dldesc); goto contparse; } if (prov_slot_count == 0) { cryptodebug("libpkcs11: No slots presented from %s. " "Skipping this plug-in at this time.\n", fullpath); (void) prov_funcs->C_Finalize(NULL); (void) dlclose(dldesc); goto contparse; } /* Allocate memory for the slot list */ prov_slots = calloc(prov_slot_count, sizeof (CK_SLOT_ID)); if (prov_slots == NULL) { cryptoerror(LOG_ERR, "libpkcs11: Could not allocate memory for " "plug-in slots. Cannot continue parsing %s\n", _PATH_PKCS11_CONF); rv = CKR_HOST_MEMORY; goto conferror; } /* Get slot list from provider */ prov_rv = prov_funcs->C_GetSlotList(FALSE, prov_slots, &prov_slot_count); /* if second call fails, drop this provider */ if (prov_rv != CKR_OK) { cryptoerror(LOG_ERR, "libpkcs11: Second call to C_GetSlotList() for %s " "failed. %s Error: %s.", fullpath, conf_err, pkcs11_strerror(prov_rv)); (void) prov_funcs->C_Finalize(NULL); (void) dlclose(dldesc); goto contparse; } /* * Parse the list of disabled or enabled mechanisms, will * apply to each of the provider's slots. */ if (phead->puent->count > 0) { rv = pkcs11_mech_parse(phead->puent->policylist, &prov_pol_mechs, phead->puent->count); if (rv == CKR_HOST_MEMORY) { cryptoerror(LOG_ERR, "libpkcs11: Could not parse configuration," "out of memory. Cannot continue parsing " "%s.", _PATH_PKCS11_CONF); goto conferror; } else if (rv == CKR_MECHANISM_INVALID) { /* * Configuration file is corrupted for this * provider. */ cryptoerror(LOG_ERR, "libpkcs11: Policy invalid or corrupted " "for %s. Use cryptoadm(8) to fix " "this. Skipping this plug-in.", fullpath); (void) prov_funcs->C_Finalize(NULL); (void) dlclose(dldesc); goto contparse; } } /* Allocate memory in our slottable for these slots */ rv = pkcs11_slottable_increase(prov_slot_count); /* * If any error is returned, it will be memory related, * so we need to abort the attempt at filling the * slottable. */ if (rv != CKR_OK) { cryptoerror(LOG_ERR, "libpkcs11: slottable could not increase. " "Cannot continue parsing %s.", _PATH_PKCS11_CONF); goto conferror; } /* Configure information for each new slot */ for (i = 0; i < prov_slot_count; i++) { /* allocate slot in framework */ rv = pkcs11_slot_allocate(&slot_id); if (rv != CKR_OK) { cryptoerror(LOG_ERR, "libpkcs11: Could not allocate " "new slot. Cannot continue parsing %s.", _PATH_PKCS11_CONF); goto conferror; } slot_count++; cur_slot = slottable->st_slots[slot_id]; (void) pthread_mutex_lock(&cur_slot->sl_mutex); cur_slot->sl_id = prov_slots[i]; cur_slot->sl_func_list = prov_funcs; cur_slot->sl_enabledpol = phead->puent->flag_enabledlist; cur_slot->sl_pol_mechs = prov_pol_mechs; cur_slot->sl_pol_count = phead->puent->count; cur_slot->sl_norandom = phead->puent->flag_norandom; cur_slot->sl_dldesc = dldesc; cur_slot->sl_prov_id = prov_count + 1; (void) pthread_mutex_unlock(&cur_slot->sl_mutex); } /* * Get the pointer to private interface _SUNW_GetThreshold() * in pkcs11_kernel. */ if (Tmp_GetThreshold == NULL) { Tmp_GetThreshold = (void(*)())dlsym(dldesc, "_SUNW_GetThreshold"); /* Get the threshold values for the supported mechs */ if (Tmp_GetThreshold != NULL) { (void) memset(meta_mechs_threshold, 0, sizeof (meta_mechs_threshold)); Tmp_GetThreshold(meta_mechs_threshold); } } /* Set and reset values to process next provider */ prov_count++; contparse: prov_slot_count = 0; Tmp_C_GetFunctionList = NULL; prov_funcs = NULL; dldesc = NULL; if (fullpath != NULL) { free(fullpath); fullpath = NULL; } if (prov_slots != NULL) { free(prov_slots); prov_slots = NULL; } phead = phead->next; } if (slot_count == 0) { /* * there's no other slot in the framework, * there is nothing to do */ goto config_complete; } /* determine if metaslot should be enabled */ /* * Check to see if any environment variable is defined * by the user for configuring metaslot. Users' * setting always take precedence over the system wide * setting. So, we will first check for any user's * defined env variables before looking at the system-wide * configuration. */ get_user_metaslot_config(); /* no metaslot entry in /etc/crypto/pkcs11.conf */ if (!metaslot_entry) { /* * If user env variable indicates metaslot should be enabled, * but there's no entry in /etc/crypto/pkcs11.conf for * metaslot at all, will respect the user's defined value */ if ((metaslot_config.enabled_specified) && (metaslot_config.enabled)) { metaslot_enabled = B_TRUE; } } else { if (!metaslot_config.enabled_specified) { /* * take system wide value if * it is not specified by user */ metaslot_enabled = metaslot_entry->flag_metaslot_enabled; } else { metaslot_enabled = metaslot_config.enabled; } } /* * * As long as the user or system configuration file does not * disable metaslot, it will be enabled regardless of the * number of slots plugged into the framework. Therefore, * metaslot is enabled even when there's only one slot * plugged into the framework. This is necessary for * presenting a consistent token label view to applications. * * However, for the case where there is only 1 slot plugged into * the framework, we can use "fastpath". * * "fastpath" will pass all of the application's requests * directly to the underlying provider. Only when policy is in * effect will we need to keep slotID around. * * When metaslot is enabled, and fastpath is enabled, * all the metaslot processing will be skipped. * When there is only 1 slot, there's * really not much metaslot can do in terms of combining functionality * of different slots, and object migration. * */ /* check to see if fastpath can be used */ if (slottable->st_last == slottable->st_first) { cur_slot = slottable->st_slots[slottable->st_first]; (void) pthread_mutex_lock(&cur_slot->sl_mutex); if ((cur_slot->sl_pol_count == 0) && (!cur_slot->sl_enabledpol) && (!cur_slot->sl_norandom)) { /* No policy is in effect, don't need slotid */ fast_funcs = cur_slot->sl_func_list; purefastpath = B_TRUE; } else { fast_funcs = cur_slot->sl_func_list; fast_slot = slottable->st_first; policyfastpath = B_TRUE; } (void) pthread_mutex_unlock(&cur_slot->sl_mutex); } if ((purefastpath || policyfastpath) && (!metaslot_enabled)) { goto config_complete; } /* * If we get here, there are more than 2 slots in the framework, * we need to set up metaslot if it is enabled */ if (metaslot_enabled) { rv = setup_metaslot(metaslot_entry); if (rv != CKR_OK) { goto conferror; } } config_complete: return (CKR_OK); conferror: /* * This cleanup code is only exercised when a major, * unrecoverable error like "out of memory". */ if (prov_funcs != NULL) { (void) prov_funcs->C_Finalize(NULL); } if (dldesc != NULL) { (void) dlclose(dldesc); } if (fullpath != NULL) { free(fullpath); fullpath = NULL; } if (prov_slots != NULL) { free(prov_slots); prov_slots = NULL; } return (rv); } /* * pkcs11_mech_parse will take hex mechanism ids, as a list of * strings, and convert them to CK_MECHANISM_TYPE_PTR. */ CK_RV pkcs11_mech_parse(umechlist_t *str_list, CK_MECHANISM_TYPE_PTR *mech_list, int mech_count) { CK_MECHANISM_TYPE_PTR tmp_list; umechlist_t *shead = str_list; tmp_list = malloc(mech_count * sizeof (CK_MECHANISM_TYPE)); if (tmp_list == NULL) { cryptoerror(LOG_ERR, "libpkcs11: parsing %s, out of memory. " "Cannot continue.", _PATH_PKCS11_CONF); return (CKR_HOST_MEMORY); } *mech_list = tmp_list; /* * The following will loop mech_count times, as there are * exactly mech_count items in the str_list. */ while (shead != NULL) { CK_MECHANISM_TYPE cur_mech; errno = 0; /* * "name" is a hexadecimal number, preceded by 0x. */ cur_mech = strtoul(shead->name, NULL, 16); if ((cur_mech == 0) && ((errno == EINVAL) || (errno == ERANGE))) { free(mech_list); return (CKR_MECHANISM_INVALID); } *tmp_list = (CK_MECHANISM_TYPE)cur_mech; tmp_list++; shead = shead->next; } return (CKR_OK); } /* * pkcs11_is_dismech is provided a slotid and a mechanism. * If mech is not disabled, then return B_FALSE. */ boolean_t pkcs11_is_dismech(CK_SLOT_ID slotid, CK_MECHANISM_TYPE mech) { ulong_t i; boolean_t enabled_pol; CK_MECHANISM_TYPE_PTR pol_mechs; ulong_t pol_count; /* Find the associated slot and get the mech policy info */ (void) pthread_mutex_lock(&slottable->st_slots[slotid]->sl_mutex); enabled_pol = slottable->st_slots[slotid]->sl_enabledpol; pol_mechs = slottable->st_slots[slotid]->sl_pol_mechs; pol_count = slottable->st_slots[slotid]->sl_pol_count; (void) pthread_mutex_unlock(&slottable->st_slots[slotid]->sl_mutex); /* Check for policy */ if ((!enabled_pol) && (pol_mechs == NULL)) { /* no policy */ return (B_FALSE); } else if (pol_mechs == NULL) { /* * We have an empty enabled list, which means no * mechanisms are exempted from this policy: all * are disabled. */ return (B_TRUE); } for (i = 0; i < pol_count; i++) { /* * If it matches, return status based on this * being and enabled or a disabled list of mechs. */ if (pol_mechs[i] == mech) { return (enabled_pol ? B_FALSE : B_TRUE); } } /* mech was not found in list */ return (enabled_pol ? B_TRUE : B_FALSE); }