Path: blob/main/sys/contrib/openzfs/module/icp/spi/kcf_spi.c
48534 views
// SPDX-License-Identifier: CDDL-1.01/*2* CDDL HEADER START3*4* The contents of this file are subject to the terms of the5* 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.LICENSE9* or https://opensource.org/licenses/CDDL-1.0.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*/21/*22* Copyright 2008 Sun Microsystems, Inc. All rights reserved.23* Use is subject to license terms.24*/2526/*27* This file is part of the core Kernel Cryptographic Framework.28* It implements the SPI functions exported to cryptographic29* providers.30*/313233#include <sys/zfs_context.h>34#include <sys/crypto/common.h>35#include <sys/crypto/impl.h>36#include <sys/crypto/sched_impl.h>37#include <sys/crypto/spi.h>3839static int init_prov_mechs(const crypto_provider_info_t *,40kcf_provider_desc_t *);4142/*43* This routine is used to add cryptographic providers to the KEF framework.44* Providers pass a crypto_provider_info structure to crypto_register_provider()45* and get back a handle. The crypto_provider_info structure contains a46* list of mechanisms supported by the provider and an ops vector containing47* provider entry points. Providers call this routine in their _init() routine.48*/49int50crypto_register_provider(const crypto_provider_info_t *info,51crypto_kcf_provider_handle_t *handle)52{53kcf_provider_desc_t *prov_desc = NULL;54int ret = CRYPTO_ARGUMENTS_BAD;5556/*57* Allocate and initialize a new provider descriptor. We also58* hold it and release it when done.59*/60prov_desc = kcf_alloc_provider_desc();61KCF_PROV_REFHOLD(prov_desc);6263/* copy provider description string */64prov_desc->pd_description = info->pi_provider_description;6566/* Change from Illumos: the ops vector is persistent. */67prov_desc->pd_ops_vector = info->pi_ops_vector;6869/* process the mechanisms supported by the provider */70if ((ret = init_prov_mechs(info, prov_desc)) != CRYPTO_SUCCESS)71goto bail;7273/*74* Add provider to providers tables, also sets the descriptor75* pd_prov_id field.76*/77if ((ret = kcf_prov_tab_add_provider(prov_desc)) != CRYPTO_SUCCESS) {78undo_register_provider(prov_desc, B_FALSE);79goto bail;80}8182/*83* The global queue is used for providers. We handle ordering84* of multi-part requests in the taskq routine. So, it is safe to85* have multiple threads for the taskq. We pass TASKQ_PREPOPULATE flag86* to keep some entries cached to improve performance.87*/8889mutex_enter(&prov_desc->pd_lock);90prov_desc->pd_state = KCF_PROV_READY;91mutex_exit(&prov_desc->pd_lock);9293*handle = prov_desc->pd_kcf_prov_handle;94ret = CRYPTO_SUCCESS;9596bail:97KCF_PROV_REFRELE(prov_desc);98return (ret);99}100101/*102* This routine is used to notify the framework when a provider is being103* removed. Providers call this routine in their _fini() routine.104*/105int106crypto_unregister_provider(crypto_kcf_provider_handle_t handle)107{108uint_t mech_idx;109kcf_provider_desc_t *desc;110kcf_prov_state_t saved_state;111112/* lookup provider descriptor */113if ((desc = kcf_prov_tab_lookup((crypto_provider_id_t)handle)) == NULL)114return (CRYPTO_UNKNOWN_PROVIDER);115116mutex_enter(&desc->pd_lock);117/*118* Check if any other thread is disabling or removing119* this provider. We return if this is the case.120*/121if (desc->pd_state >= KCF_PROV_DISABLED) {122mutex_exit(&desc->pd_lock);123/* Release reference held by kcf_prov_tab_lookup(). */124KCF_PROV_REFRELE(desc);125return (CRYPTO_BUSY);126}127128saved_state = desc->pd_state;129desc->pd_state = KCF_PROV_REMOVED;130131/*132* Check if this provider is currently being used.133* pd_irefcnt is the number of holds from the internal134* structures. We add one to account for the above lookup.135*/136if (desc->pd_refcnt > desc->pd_irefcnt + 1) {137desc->pd_state = saved_state;138mutex_exit(&desc->pd_lock);139/* Release reference held by kcf_prov_tab_lookup(). */140KCF_PROV_REFRELE(desc);141/*142* The administrator will presumably stop the clients,143* thus removing the holds, when they get the busy144* return value. Any retry will succeed then.145*/146return (CRYPTO_BUSY);147}148mutex_exit(&desc->pd_lock);149150/* remove the provider from the mechanisms tables */151for (mech_idx = 0; mech_idx < desc->pd_mech_list_count;152mech_idx++) {153kcf_remove_mech_provider(154desc->pd_mechanisms[mech_idx].cm_mech_name, desc);155}156157/* remove provider from providers table */158if (kcf_prov_tab_rem_provider((crypto_provider_id_t)handle) !=159CRYPTO_SUCCESS) {160/* Release reference held by kcf_prov_tab_lookup(). */161KCF_PROV_REFRELE(desc);162return (CRYPTO_UNKNOWN_PROVIDER);163}164165/* Release reference held by kcf_prov_tab_lookup(). */166KCF_PROV_REFRELE(desc);167168/*169* Wait till the existing requests complete.170*/171mutex_enter(&desc->pd_lock);172while (desc->pd_state != KCF_PROV_FREED)173cv_wait(&desc->pd_remove_cv, &desc->pd_lock);174mutex_exit(&desc->pd_lock);175176/*177* This is the only place where kcf_free_provider_desc()178* is called directly. KCF_PROV_REFRELE() should free the179* structure in all other places.180*/181ASSERT(desc->pd_state == KCF_PROV_FREED &&182desc->pd_refcnt == 0);183kcf_free_provider_desc(desc);184185return (CRYPTO_SUCCESS);186}187188/*189* Process the mechanism info structures specified by the provider190* during registration. A NULL crypto_provider_info_t indicates191* an already initialized provider descriptor.192*193* Returns CRYPTO_SUCCESS on success, CRYPTO_ARGUMENTS if one194* of the specified mechanisms was malformed, or CRYPTO_HOST_MEMORY195* if the table of mechanisms is full.196*/197static int198init_prov_mechs(const crypto_provider_info_t *info, kcf_provider_desc_t *desc)199{200uint_t mech_idx;201uint_t cleanup_idx;202int err = CRYPTO_SUCCESS;203kcf_prov_mech_desc_t *pmd;204int desc_use_count = 0;205206/*207* Copy the mechanism list from the provider info to the provider208* descriptor. desc->pd_mechanisms has an extra crypto_mech_info_t209* element if the provider has random_ops since we keep an internal210* mechanism, SUN_RANDOM, in this case.211*/212if (info != NULL) {213ASSERT(info->pi_mechanisms != NULL);214desc->pd_mech_list_count = info->pi_mech_list_count;215desc->pd_mechanisms = info->pi_mechanisms;216}217218/*219* For each mechanism support by the provider, add the provider220* to the corresponding KCF mechanism mech_entry chain.221*/222for (mech_idx = 0; mech_idx < desc->pd_mech_list_count; mech_idx++) {223if ((err = kcf_add_mech_provider(mech_idx, desc, &pmd)) !=224KCF_SUCCESS)225break;226227if (pmd == NULL)228continue;229230/* The provider will be used for this mechanism */231desc_use_count++;232}233234/*235* Don't allow multiple providers with disabled mechanisms236* to register. Subsequent enabling of mechanisms will result in237* an unsupported configuration, i.e. multiple providers238* per mechanism.239*/240if (desc_use_count == 0)241return (CRYPTO_ARGUMENTS_BAD);242243if (err == KCF_SUCCESS)244return (CRYPTO_SUCCESS);245246/*247* An error occurred while adding the mechanism, cleanup248* and bail.249*/250for (cleanup_idx = 0; cleanup_idx < mech_idx; cleanup_idx++) {251kcf_remove_mech_provider(252desc->pd_mechanisms[cleanup_idx].cm_mech_name, desc);253}254255if (err == KCF_MECH_TAB_FULL)256return (CRYPTO_HOST_MEMORY);257258return (CRYPTO_ARGUMENTS_BAD);259}260261/*262* Utility routine called from failure paths in crypto_register_provider()263* and from crypto_load_soft_disabled().264*/265void266undo_register_provider(kcf_provider_desc_t *desc, boolean_t remove_prov)267{268uint_t mech_idx;269270/* remove the provider from the mechanisms tables */271for (mech_idx = 0; mech_idx < desc->pd_mech_list_count;272mech_idx++) {273kcf_remove_mech_provider(274desc->pd_mechanisms[mech_idx].cm_mech_name, desc);275}276277/* remove provider from providers table */278if (remove_prov)279(void) kcf_prov_tab_rem_provider(desc->pd_prov_id);280}281282283