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
39static 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 */
49int
50crypto_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
96bail:
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 */
105int
106crypto_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 */
197static int
198init_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 */
265void
266undo_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