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