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