1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
|
// SPDX-License-Identifier: CDDL-1.0
/*
* 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 https://opensource.org/licenses/CDDL-1.0.
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* This file is part of the core Kernel Cryptographic Framework.
* It implements the SPI functions exported to cryptographic
* providers.
*/
#include <sys/zfs_context.h>
#include <sys/crypto/common.h>
#include <sys/crypto/impl.h>
#include <sys/crypto/sched_impl.h>
#include <sys/crypto/spi.h>
static int init_prov_mechs(const crypto_provider_info_t *,
kcf_provider_desc_t *);
/*
* This routine is used to add cryptographic providers to the KEF framework.
* Providers pass a crypto_provider_info structure to crypto_register_provider()
* and get back a handle. The crypto_provider_info structure contains a
* list of mechanisms supported by the provider and an ops vector containing
* provider entry points. Providers call this routine in their _init() routine.
*/
int
crypto_register_provider(const crypto_provider_info_t *info,
crypto_kcf_provider_handle_t *handle)
{
kcf_provider_desc_t *prov_desc = NULL;
int ret = CRYPTO_ARGUMENTS_BAD;
/*
* Allocate and initialize a new provider descriptor. We also
* hold it and release it when done.
*/
prov_desc = kcf_alloc_provider_desc();
KCF_PROV_REFHOLD(prov_desc);
/* copy provider description string */
prov_desc->pd_description = info->pi_provider_description;
/* Change from Illumos: the ops vector is persistent. */
prov_desc->pd_ops_vector = info->pi_ops_vector;
/* process the mechanisms supported by the provider */
if ((ret = init_prov_mechs(info, prov_desc)) != CRYPTO_SUCCESS)
goto bail;
/*
* Add provider to providers tables, also sets the descriptor
* pd_prov_id field.
*/
if ((ret = kcf_prov_tab_add_provider(prov_desc)) != CRYPTO_SUCCESS) {
undo_register_provider(prov_desc, B_FALSE);
goto bail;
}
/*
* The global queue is used for providers. We handle ordering
* of multi-part requests in the taskq routine. So, it is safe to
* have multiple threads for the taskq. We pass TASKQ_PREPOPULATE flag
* to keep some entries cached to improve performance.
*/
mutex_enter(&prov_desc->pd_lock);
prov_desc->pd_state = KCF_PROV_READY;
mutex_exit(&prov_desc->pd_lock);
*handle = prov_desc->pd_kcf_prov_handle;
ret = CRYPTO_SUCCESS;
bail:
KCF_PROV_REFRELE(prov_desc);
return (ret);
}
/*
* This routine is used to notify the framework when a provider is being
* removed. Providers call this routine in their _fini() routine.
*/
int
crypto_unregister_provider(crypto_kcf_provider_handle_t handle)
{
uint_t mech_idx;
kcf_provider_desc_t *desc;
kcf_prov_state_t saved_state;
/* lookup provider descriptor */
if ((desc = kcf_prov_tab_lookup((crypto_provider_id_t)handle)) == NULL)
return (CRYPTO_UNKNOWN_PROVIDER);
mutex_enter(&desc->pd_lock);
/*
* Check if any other thread is disabling or removing
* this provider. We return if this is the case.
*/
if (desc->pd_state >= KCF_PROV_DISABLED) {
mutex_exit(&desc->pd_lock);
/* Release reference held by kcf_prov_tab_lookup(). */
KCF_PROV_REFRELE(desc);
return (CRYPTO_BUSY);
}
saved_state = desc->pd_state;
desc->pd_state = KCF_PROV_REMOVED;
/*
* Check if this provider is currently being used.
* pd_irefcnt is the number of holds from the internal
* structures. We add one to account for the above lookup.
*/
if (desc->pd_refcnt > desc->pd_irefcnt + 1) {
desc->pd_state = saved_state;
mutex_exit(&desc->pd_lock);
/* Release reference held by kcf_prov_tab_lookup(). */
KCF_PROV_REFRELE(desc);
/*
* The administrator will presumably stop the clients,
* thus removing the holds, when they get the busy
* return value. Any retry will succeed then.
*/
return (CRYPTO_BUSY);
}
mutex_exit(&desc->pd_lock);
/* remove the provider from the mechanisms tables */
for (mech_idx = 0; mech_idx < desc->pd_mech_list_count;
mech_idx++) {
kcf_remove_mech_provider(
desc->pd_mechanisms[mech_idx].cm_mech_name, desc);
}
/* remove provider from providers table */
if (kcf_prov_tab_rem_provider((crypto_provider_id_t)handle) !=
CRYPTO_SUCCESS) {
/* Release reference held by kcf_prov_tab_lookup(). */
KCF_PROV_REFRELE(desc);
return (CRYPTO_UNKNOWN_PROVIDER);
}
/* Release reference held by kcf_prov_tab_lookup(). */
KCF_PROV_REFRELE(desc);
/*
* Wait till the existing requests complete.
*/
mutex_enter(&desc->pd_lock);
while (desc->pd_state != KCF_PROV_FREED)
cv_wait(&desc->pd_remove_cv, &desc->pd_lock);
mutex_exit(&desc->pd_lock);
/*
* This is the only place where kcf_free_provider_desc()
* is called directly. KCF_PROV_REFRELE() should free the
* structure in all other places.
*/
ASSERT(desc->pd_state == KCF_PROV_FREED &&
desc->pd_refcnt == 0);
kcf_free_provider_desc(desc);
return (CRYPTO_SUCCESS);
}
/*
* Process the mechanism info structures specified by the provider
* during registration. A NULL crypto_provider_info_t indicates
* an already initialized provider descriptor.
*
* Returns CRYPTO_SUCCESS on success, CRYPTO_ARGUMENTS if one
* of the specified mechanisms was malformed, or CRYPTO_HOST_MEMORY
* if the table of mechanisms is full.
*/
static int
init_prov_mechs(const crypto_provider_info_t *info, kcf_provider_desc_t *desc)
{
uint_t mech_idx;
uint_t cleanup_idx;
int err = CRYPTO_SUCCESS;
kcf_prov_mech_desc_t *pmd;
int desc_use_count = 0;
/*
* Copy the mechanism list from the provider info to the provider
* descriptor. desc->pd_mechanisms has an extra crypto_mech_info_t
* element if the provider has random_ops since we keep an internal
* mechanism, SUN_RANDOM, in this case.
*/
if (info != NULL) {
ASSERT(info->pi_mechanisms != NULL);
desc->pd_mech_list_count = info->pi_mech_list_count;
desc->pd_mechanisms = info->pi_mechanisms;
}
/*
* For each mechanism support by the provider, add the provider
* to the corresponding KCF mechanism mech_entry chain.
*/
for (mech_idx = 0; mech_idx < desc->pd_mech_list_count; mech_idx++) {
if ((err = kcf_add_mech_provider(mech_idx, desc, &pmd)) !=
KCF_SUCCESS)
break;
if (pmd == NULL)
continue;
/* The provider will be used for this mechanism */
desc_use_count++;
}
/*
* Don't allow multiple providers with disabled mechanisms
* to register. Subsequent enabling of mechanisms will result in
* an unsupported configuration, i.e. multiple providers
* per mechanism.
*/
if (desc_use_count == 0)
return (CRYPTO_ARGUMENTS_BAD);
if (err == KCF_SUCCESS)
return (CRYPTO_SUCCESS);
/*
* An error occurred while adding the mechanism, cleanup
* and bail.
*/
for (cleanup_idx = 0; cleanup_idx < mech_idx; cleanup_idx++) {
kcf_remove_mech_provider(
desc->pd_mechanisms[cleanup_idx].cm_mech_name, desc);
}
if (err == KCF_MECH_TAB_FULL)
return (CRYPTO_HOST_MEMORY);
return (CRYPTO_ARGUMENTS_BAD);
}
/*
* Utility routine called from failure paths in crypto_register_provider()
* and from crypto_load_soft_disabled().
*/
void
undo_register_provider(kcf_provider_desc_t *desc, boolean_t remove_prov)
{
uint_t mech_idx;
/* remove the provider from the mechanisms tables */
for (mech_idx = 0; mech_idx < desc->pd_mech_list_count;
mech_idx++) {
kcf_remove_mech_provider(
desc->pd_mechanisms[mech_idx].cm_mech_name, desc);
}
/* remove provider from providers table */
if (remove_prov)
(void) kcf_prov_tab_rem_provider(desc->pd_prov_id);
}
|