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 http://www.opensolaris.org/os/licensing.
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/crypto/common.h>
27 #include <sys/crypto/impl.h>
28 #include <sys/crypto/sched_impl.h>
29 
30 void
31 kcf_free_triedlist(kcf_prov_tried_t *list)
32 {
33 	kcf_prov_tried_t *l;
34 
35 	while ((l = list) != NULL) {
36 		list = list->pt_next;
37 		KCF_PROV_REFRELE(l->pt_pd);
38 		kmem_free(l, sizeof (kcf_prov_tried_t));
39 	}
40 }
41 
42 kcf_prov_tried_t *
43 kcf_insert_triedlist(kcf_prov_tried_t **list, kcf_provider_desc_t *pd,
44     int kmflag)
45 {
46 	kcf_prov_tried_t *l;
47 
48 	l = kmem_alloc(sizeof (kcf_prov_tried_t), kmflag);
49 	if (l == NULL)
50 		return (NULL);
51 
52 	l->pt_pd = pd;
53 	l->pt_next = *list;
54 	*list = l;
55 
56 	return (l);
57 }
58 
59 static boolean_t
60 is_in_triedlist(kcf_provider_desc_t *pd, kcf_prov_tried_t *triedl)
61 {
62 	while (triedl != NULL) {
63 		if (triedl->pt_pd == pd)
64 			return (B_TRUE);
65 		triedl = triedl->pt_next;
66 	};
67 
68 	return (B_FALSE);
69 }
70 
71 /*
72  * Return the best provider for the specified mechanism. The provider
73  * is held and it is the caller's responsibility to release it when done.
74  * The fg input argument is used as a search criterion to pick a provider.
75  * A provider has to support this function group to be picked.
76  *
77  * Find the least loaded provider in the list of providers. We do a linear
78  * search to find one. This is fine as we assume there are only a few
79  * number of providers in this list. If this assumption ever changes,
80  * we should revisit this.
81  */
82 kcf_provider_desc_t *
83 kcf_get_mech_provider(crypto_mech_type_t mech_type, kcf_mech_entry_t **mepp,
84     int *error, kcf_prov_tried_t *triedl, crypto_func_group_t fg)
85 {
86 	kcf_provider_desc_t *pd = NULL;
87 	kcf_prov_mech_desc_t *mdesc;
88 	kcf_ops_class_t class;
89 	int index;
90 	kcf_mech_entry_t *me;
91 	const kcf_mech_entry_tab_t *me_tab;
92 
93 	class = KCF_MECH2CLASS(mech_type);
94 	if ((class < KCF_FIRST_OPSCLASS) || (class > KCF_LAST_OPSCLASS)) {
95 		*error = CRYPTO_MECHANISM_INVALID;
96 		return (NULL);
97 	}
98 
99 	me_tab = &kcf_mech_tabs_tab[class];
100 	index = KCF_MECH2INDEX(mech_type);
101 	if ((index < 0) || (index >= me_tab->met_size)) {
102 		*error = CRYPTO_MECHANISM_INVALID;
103 		return (NULL);
104 	}
105 
106 	me = &((me_tab->met_tab)[index]);
107 	if (mepp != NULL)
108 		*mepp = me;
109 
110 	/* Is there a provider? */
111 	if (pd == NULL && (mdesc = me->me_sw_prov) != NULL) {
112 		pd = mdesc->pm_prov_desc;
113 		if (!IS_FG_SUPPORTED(mdesc, fg) ||
114 		    !KCF_IS_PROV_USABLE(pd) ||
115 		    IS_PROVIDER_TRIED(pd, triedl))
116 			pd = NULL;
117 	}
118 
119 	if (pd == NULL) {
120 		/*
121 		 * We do not want to report CRYPTO_MECH_NOT_SUPPORTED, when
122 		 * we are in the "fallback to the next provider" case. Rather
123 		 * we preserve the error, so that the client gets the right
124 		 * error code.
125 		 */
126 		if (triedl == NULL)
127 			*error = CRYPTO_MECH_NOT_SUPPORTED;
128 	} else
129 		KCF_PROV_REFHOLD(pd);
130 
131 	return (pd);
132 }
133