Path: blob/main/sys/contrib/openzfs/module/icp/spi/kcf_spi.c
108698 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/crypto/common.h>34#include <sys/crypto/impl.h>35#include <sys/crypto/sched_impl.h>36#include <sys/crypto/spi.h>3738static int init_prov_mechs(const crypto_provider_info_t *,39kcf_provider_desc_t *);4041/*42* This routine is used to add cryptographic providers to the KEF framework.43* Providers pass a crypto_provider_info structure to crypto_register_provider()44* and get back a handle. The crypto_provider_info structure contains a45* list of mechanisms supported by the provider and an ops vector containing46* provider entry points. Providers call this routine in their _init() routine.47*/48int49crypto_register_provider(const crypto_provider_info_t *info,50crypto_kcf_provider_handle_t *handle)51{52kcf_provider_desc_t *prov_desc = NULL;53int ret = CRYPTO_ARGUMENTS_BAD;5455/*56* Allocate and initialize a new provider descriptor. We also57* hold it and release it when done.58*/59prov_desc = kcf_alloc_provider_desc();60KCF_PROV_REFHOLD(prov_desc);6162/* copy provider description string */63prov_desc->pd_description = info->pi_provider_description;6465/* Change from Illumos: the ops vector is persistent. */66prov_desc->pd_ops_vector = info->pi_ops_vector;6768/* process the mechanisms supported by the provider */69if ((ret = init_prov_mechs(info, prov_desc)) != CRYPTO_SUCCESS)70goto bail;7172/*73* Add provider to providers tables, also sets the descriptor74* pd_prov_id field.75*/76if ((ret = kcf_prov_tab_add_provider(prov_desc)) != CRYPTO_SUCCESS) {77undo_register_provider(prov_desc, B_FALSE);78goto bail;79}8081/*82* The global queue is used for providers. We handle ordering83* of multi-part requests in the taskq routine. So, it is safe to84* have multiple threads for the taskq. We pass TASKQ_PREPOPULATE flag85* to keep some entries cached to improve performance.86*/8788mutex_enter(&prov_desc->pd_lock);89prov_desc->pd_state = KCF_PROV_READY;90mutex_exit(&prov_desc->pd_lock);9192*handle = prov_desc->pd_kcf_prov_handle;93ret = CRYPTO_SUCCESS;9495bail:96KCF_PROV_REFRELE(prov_desc);97return (ret);98}99100/*101* This routine is used to notify the framework when a provider is being102* removed. Providers call this routine in their _fini() routine.103*/104int105crypto_unregister_provider(crypto_kcf_provider_handle_t handle)106{107uint_t mech_idx;108kcf_provider_desc_t *desc;109kcf_prov_state_t saved_state;110111/* lookup provider descriptor */112if ((desc = kcf_prov_tab_lookup((crypto_provider_id_t)handle)) == NULL)113return (CRYPTO_UNKNOWN_PROVIDER);114115mutex_enter(&desc->pd_lock);116/*117* Check if any other thread is disabling or removing118* this provider. We return if this is the case.119*/120if (desc->pd_state >= KCF_PROV_DISABLED) {121mutex_exit(&desc->pd_lock);122/* Release reference held by kcf_prov_tab_lookup(). */123KCF_PROV_REFRELE(desc);124return (CRYPTO_BUSY);125}126127saved_state = desc->pd_state;128desc->pd_state = KCF_PROV_REMOVED;129130/*131* Check if this provider is currently being used.132* pd_irefcnt is the number of holds from the internal133* structures. We add one to account for the above lookup.134*/135if (desc->pd_refcnt > desc->pd_irefcnt + 1) {136desc->pd_state = saved_state;137mutex_exit(&desc->pd_lock);138/* Release reference held by kcf_prov_tab_lookup(). */139KCF_PROV_REFRELE(desc);140/*141* The administrator will presumably stop the clients,142* thus removing the holds, when they get the busy143* return value. Any retry will succeed then.144*/145return (CRYPTO_BUSY);146}147mutex_exit(&desc->pd_lock);148149/* remove the provider from the mechanisms tables */150for (mech_idx = 0; mech_idx < desc->pd_mech_list_count;151mech_idx++) {152kcf_remove_mech_provider(153desc->pd_mechanisms[mech_idx].cm_mech_name, desc);154}155156/* remove provider from providers table */157if (kcf_prov_tab_rem_provider((crypto_provider_id_t)handle) !=158CRYPTO_SUCCESS) {159/* Release reference held by kcf_prov_tab_lookup(). */160KCF_PROV_REFRELE(desc);161return (CRYPTO_UNKNOWN_PROVIDER);162}163164/* Release reference held by kcf_prov_tab_lookup(). */165KCF_PROV_REFRELE(desc);166167/*168* Wait till the existing requests complete.169*/170mutex_enter(&desc->pd_lock);171while (desc->pd_state != KCF_PROV_FREED)172cv_wait(&desc->pd_remove_cv, &desc->pd_lock);173mutex_exit(&desc->pd_lock);174175/*176* This is the only place where kcf_free_provider_desc()177* is called directly. KCF_PROV_REFRELE() should free the178* structure in all other places.179*/180ASSERT(desc->pd_state == KCF_PROV_FREED &&181desc->pd_refcnt == 0);182kcf_free_provider_desc(desc);183184return (CRYPTO_SUCCESS);185}186187/*188* Process the mechanism info structures specified by the provider189* during registration. A NULL crypto_provider_info_t indicates190* an already initialized provider descriptor.191*192* Returns CRYPTO_SUCCESS on success, CRYPTO_ARGUMENTS if one193* of the specified mechanisms was malformed, or CRYPTO_HOST_MEMORY194* if the table of mechanisms is full.195*/196static int197init_prov_mechs(const crypto_provider_info_t *info, kcf_provider_desc_t *desc)198{199uint_t mech_idx;200uint_t cleanup_idx;201int err = CRYPTO_SUCCESS;202kcf_prov_mech_desc_t *pmd;203int desc_use_count = 0;204205/*206* Copy the mechanism list from the provider info to the provider207* descriptor. desc->pd_mechanisms has an extra crypto_mech_info_t208* element if the provider has random_ops since we keep an internal209* mechanism, SUN_RANDOM, in this case.210*/211if (info != NULL) {212ASSERT(info->pi_mechanisms != NULL);213desc->pd_mech_list_count = info->pi_mech_list_count;214desc->pd_mechanisms = info->pi_mechanisms;215}216217/*218* For each mechanism support by the provider, add the provider219* to the corresponding KCF mechanism mech_entry chain.220*/221for (mech_idx = 0; mech_idx < desc->pd_mech_list_count; mech_idx++) {222if ((err = kcf_add_mech_provider(mech_idx, desc, &pmd)) !=223KCF_SUCCESS)224break;225226if (pmd == NULL)227continue;228229/* The provider will be used for this mechanism */230desc_use_count++;231}232233/*234* Don't allow multiple providers with disabled mechanisms235* to register. Subsequent enabling of mechanisms will result in236* an unsupported configuration, i.e. multiple providers237* per mechanism.238*/239if (desc_use_count == 0)240return (CRYPTO_ARGUMENTS_BAD);241242if (err == KCF_SUCCESS)243return (CRYPTO_SUCCESS);244245/*246* An error occurred while adding the mechanism, cleanup247* and bail.248*/249for (cleanup_idx = 0; cleanup_idx < mech_idx; cleanup_idx++) {250kcf_remove_mech_provider(251desc->pd_mechanisms[cleanup_idx].cm_mech_name, desc);252}253254if (err == KCF_MECH_TAB_FULL)255return (CRYPTO_HOST_MEMORY);256257return (CRYPTO_ARGUMENTS_BAD);258}259260/*261* Utility routine called from failure paths in crypto_register_provider()262* and from crypto_load_soft_disabled().263*/264void265undo_register_provider(kcf_provider_desc_t *desc, boolean_t remove_prov)266{267uint_t mech_idx;268269/* remove the provider from the mechanisms tables */270for (mech_idx = 0; mech_idx < desc->pd_mech_list_count;271mech_idx++) {272kcf_remove_mech_provider(273desc->pd_mechanisms[mech_idx].cm_mech_name, desc);274}275276/* remove provider from providers table */277if (remove_prov)278(void) kcf_prov_tab_rem_provider(desc->pd_prov_id);279}280281282