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