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