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 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include "mt.h"
30 #include "rpc_mt.h"
31 #include <stdio.h>
32 #include <atomic.h>
33 #include <sys/errno.h>
34 #include <dlfcn.h>
35 #include <rpc/rpc.h>
36 
37 #define	RPCSEC	"rpcsec.so.1"
38 
39 typedef struct {
40 	AUTH		*(*rpc_gss_seccreate)();
41 	bool_t		(*rpc_gss_set_defaults)();
42 	bool_t		(*rpc_gss_get_principal_name)();
43 	char		**(*rpc_gss_get_mechanisms)();
44 	char		**(*rpc_gss_get_mech_info)();
45 	bool_t		(*rpc_gss_get_versions)();
46 	bool_t		(*rpc_gss_is_installed)();
47 	bool_t		(*rpc_gss_set_svc_name)();
48 	bool_t		(*rpc_gss_set_callback)();
49 	bool_t		(*rpc_gss_getcred)();
50 	bool_t		(*rpc_gss_mech_to_oid)();
51 	bool_t		(*rpc_gss_qop_to_num)();
52 	enum auth_stat	(*__svcrpcsec_gss)();
53 	bool_t		(*__rpc_gss_wrap)();
54 	bool_t		(*__rpc_gss_unwrap)();
55 	int		(*rpc_gss_max_data_length)();
56 	int		(*rpc_gss_svc_max_data_length)();
57 	void		(*rpc_gss_get_error)();
58 } rpcgss_calls_t;
59 
60 static rpcgss_calls_t calls;
61 static mutex_t rpcgss_calls_mutex = DEFAULTMUTEX;
62 static bool_t initialized = FALSE;
63 
64 static bool_t
65 rpcgss_calls_init(void)
66 {
67 	void	*handle;
68 	bool_t	ret = FALSE;
69 
70 	if (initialized) {
71 		membar_consumer();
72 		return (TRUE);
73 	}
74 	(void) mutex_lock(&rpcgss_calls_mutex);
75 	if (initialized) {
76 		(void) mutex_unlock(&rpcgss_calls_mutex);
77 		membar_consumer();
78 		return (TRUE);
79 	}
80 
81 	if ((handle = dlopen(RPCSEC, RTLD_LAZY)) == NULL)
82 		goto done;
83 
84 	if ((calls.rpc_gss_seccreate = (AUTH *(*)()) dlsym(handle,
85 					"__rpc_gss_seccreate")) == NULL)
86 		goto done;
87 	if ((calls.rpc_gss_set_defaults = (bool_t (*)()) dlsym(handle,
88 					"__rpc_gss_set_defaults")) == NULL)
89 		goto done;
90 	if ((calls.rpc_gss_get_principal_name = (bool_t (*)()) dlsym(handle,
91 				"__rpc_gss_get_principal_name")) == NULL)
92 		goto done;
93 	if ((calls.rpc_gss_get_mechanisms = (char **(*)()) dlsym(handle,
94 					"__rpc_gss_get_mechanisms")) == NULL)
95 		goto done;
96 	if ((calls.rpc_gss_get_mech_info = (char **(*)()) dlsym(handle,
97 					"__rpc_gss_get_mech_info")) == NULL)
98 		goto done;
99 	if ((calls.rpc_gss_get_versions = (bool_t (*)()) dlsym(handle,
100 					"__rpc_gss_get_versions")) == NULL)
101 		goto done;
102 	if ((calls.rpc_gss_is_installed = (bool_t (*)()) dlsym(handle,
103 					"__rpc_gss_is_installed")) == NULL)
104 		goto done;
105 	if ((calls.rpc_gss_set_svc_name = (bool_t (*)()) dlsym(handle,
106 					"__rpc_gss_set_svc_name")) == NULL)
107 		goto done;
108 	if ((calls.rpc_gss_set_callback = (bool_t (*)()) dlsym(handle,
109 					"__rpc_gss_set_callback")) == NULL)
110 		goto done;
111 	if ((calls.rpc_gss_getcred = (bool_t (*)()) dlsym(handle,
112 					"__rpc_gss_getcred")) == NULL)
113 		goto done;
114 	if ((calls.rpc_gss_mech_to_oid = (bool_t (*)()) dlsym(handle,
115 					"__rpc_gss_mech_to_oid")) == NULL)
116 		goto done;
117 
118 	if ((calls.rpc_gss_qop_to_num = (bool_t (*)()) dlsym(handle,
119 					"__rpc_gss_qop_to_num")) == NULL)
120 		goto done;
121 	if ((calls.__svcrpcsec_gss = (enum auth_stat (*)()) dlsym(handle,
122 					"__svcrpcsec_gss")) == NULL)
123 		goto done;
124 	if ((calls.__rpc_gss_wrap = (bool_t (*)()) dlsym(handle,
125 					"__rpc_gss_wrap")) == NULL)
126 		goto done;
127 	if ((calls.__rpc_gss_unwrap = (bool_t (*)()) dlsym(handle,
128 					"__rpc_gss_unwrap")) == NULL)
129 		goto done;
130 	if ((calls.rpc_gss_max_data_length = (int (*)()) dlsym(handle,
131 					"__rpc_gss_max_data_length")) == NULL)
132 		goto done;
133 	if ((calls.rpc_gss_svc_max_data_length = (int (*)()) dlsym(handle,
134 				"__rpc_gss_svc_max_data_length")) == NULL)
135 		goto done;
136 	if ((calls.rpc_gss_get_error = (void (*)()) dlsym(handle,
137 					"__rpc_gss_get_error")) == NULL)
138 		goto done;
139 	ret = TRUE;
140 done:
141 	if (!ret) {
142 		if (handle != NULL)
143 			(void) dlclose(handle);
144 	}
145 	membar_producer();
146 	initialized = ret;
147 	(void) mutex_unlock(&rpcgss_calls_mutex);
148 	return (ret);
149 }
150 
151 AUTH *
152 rpc_gss_seccreate(
153 	CLIENT			*clnt,		/* associated client handle */
154 	char			*principal,	/* server service principal */
155 	char			*mechanism,	/* security mechanism */
156 	rpc_gss_service_t	service_type,	/* security service */
157 	char			*qop,		/* requested QOP */
158 	rpc_gss_options_req_t	*options_req,	/* requested options */
159 	rpc_gss_options_ret_t	*options_ret)	/* returned options */
160 {
161 	if (!rpcgss_calls_init())
162 		return (NULL);
163 	return ((*calls.rpc_gss_seccreate)(clnt, principal, mechanism,
164 				service_type, qop, options_req, options_ret));
165 }
166 
167 bool_t
168 rpc_gss_set_defaults(AUTH *auth, rpc_gss_service_t service, char *qop)
169 {
170 	if (!rpcgss_calls_init())
171 		return (FALSE);
172 	return ((*calls.rpc_gss_set_defaults)(auth, service, qop));
173 }
174 
175 bool_t
176 rpc_gss_get_principal_name(
177 	rpc_gss_principal_t	*principal,
178 	char			*mechanism,
179 	char			*user_name,
180 	char			*node,
181 	char			*secdomain)
182 {
183 	if (!rpcgss_calls_init())
184 		return (FALSE);
185 	return ((*calls.rpc_gss_get_principal_name)(principal, mechanism,
186 					user_name, node, secdomain));
187 }
188 
189 char **
190 rpc_gss_get_mechanisms(void)
191 {
192 	if (!rpcgss_calls_init())
193 		return (NULL);
194 	return ((*calls.rpc_gss_get_mechanisms)());
195 }
196 
197 char **
198 rpc_gss_get_mech_info(char *mechanism, rpc_gss_service_t *service)
199 {
200 	if (!rpcgss_calls_init())
201 		return (NULL);
202 	return ((*calls.rpc_gss_get_mech_info)(mechanism, service));
203 }
204 
205 bool_t
206 rpc_gss_get_versions(uint_t *vers_hi, uint_t *vers_lo)
207 {
208 	if (!rpcgss_calls_init())
209 		return (FALSE);
210 	return ((*calls.rpc_gss_get_versions)(vers_hi, vers_lo));
211 }
212 
213 bool_t
214 rpc_gss_is_installed(char *mechanism)
215 {
216 	if (!rpcgss_calls_init())
217 		return (FALSE);
218 	return ((*calls.rpc_gss_is_installed)(mechanism));
219 }
220 
221 bool_t
222 rpc_gss_set_svc_name(
223 	char			*principal, /* server service principal name */
224 	char			*mechanism,
225 	uint_t			req_time,
226 	uint_t			program,
227 	uint_t			version)
228 {
229 	if (!rpcgss_calls_init())
230 		return (FALSE);
231 	return ((*calls.rpc_gss_set_svc_name)(principal, mechanism, req_time,
232 						program, version));
233 }
234 
235 bool_t
236 rpc_gss_set_callback(rpc_gss_callback_t *cb)
237 {
238 	if (!rpcgss_calls_init())
239 		return (FALSE);
240 	return ((*calls.rpc_gss_set_callback)(cb));
241 }
242 
243 bool_t
244 rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
245 					rpc_gss_ucred_t **ucred, void **cookie)
246 {
247 	if (!rpcgss_calls_init())
248 		return (FALSE);
249 	return ((*calls.rpc_gss_getcred)(req, rcred, ucred, cookie));
250 }
251 
252 bool_t
253 rpc_gss_mech_to_oid(char *mech, rpc_gss_OID *oid)
254 {
255 	if (!rpcgss_calls_init())
256 		return (FALSE);
257 	return ((*calls.rpc_gss_mech_to_oid)(mech, oid));
258 }
259 
260 bool_t
261 rpc_gss_qop_to_num(char *qop, char *mech, uint_t *num)
262 {
263 	if (!rpcgss_calls_init())
264 		return (FALSE);
265 	return ((*calls.rpc_gss_qop_to_num)(qop, mech, num));
266 }
267 
268 enum auth_stat
269 __svcrpcsec_gss(struct svc_req *rqst, struct rpc_msg *msg, bool_t *no_dispatch)
270 {
271 	if (!rpcgss_calls_init())
272 		return (AUTH_FAILED);
273 	return ((*calls.__svcrpcsec_gss)(rqst, msg, no_dispatch));
274 }
275 
276 bool_t
277 __rpc_gss_wrap(AUTH *auth, char *buf, uint_t buflen, XDR *out_xdrs,
278 					bool_t (*xdr_func)(), caddr_t xdr_ptr)
279 {
280 	if (!rpcgss_calls_init())
281 		return (FALSE);
282 	return ((*calls.__rpc_gss_wrap)(auth, buf, buflen, out_xdrs,
283 							xdr_func, xdr_ptr));
284 }
285 
286 bool_t
287 __rpc_gss_unwrap(AUTH *auth, XDR *in_xdrs, bool_t (*xdr_func)(),
288 								caddr_t xdr_ptr)
289 {
290 	if (!rpcgss_calls_init())
291 		return (FALSE);
292 	return ((*calls.__rpc_gss_unwrap)(auth, in_xdrs, xdr_func, xdr_ptr));
293 }
294 
295 int
296 rpc_gss_max_data_length(AUTH *rpcgss_handle, int max_tp_unit_len)
297 {
298 	if (!rpcgss_calls_init())
299 		return (0);
300 	return ((*calls.rpc_gss_max_data_length)(rpcgss_handle,
301 					max_tp_unit_len));
302 }
303 
304 int
305 rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
306 {
307 	if (!rpcgss_calls_init())
308 		return (0);
309 	return ((*calls.rpc_gss_svc_max_data_length)(req, max_tp_unit_len));
310 }
311 
312 void
313 rpc_gss_get_error(rpc_gss_error_t *error)
314 {
315 	if (!rpcgss_calls_init()) {
316 		error->rpc_gss_error = RPC_GSS_ER_SYSTEMERROR;
317 		error->system_error = ENOTSUP;
318 		return;
319 	}
320 	(*calls.rpc_gss_get_error)(error);
321 }
322