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