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
39typedef 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
60static rpcgss_calls_t calls;
61static mutex_t rpcgss_calls_mutex = DEFAULTMUTEX;
62static bool_t initialized = FALSE;
63
64static bool_t
65rpcgss_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;
140done:
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
151AUTH *
152rpc_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
167bool_t
168rpc_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
175bool_t
176rpc_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
189char **
190rpc_gss_get_mechanisms(void)
191{
192	if (!rpcgss_calls_init())
193		return (NULL);
194	return ((*calls.rpc_gss_get_mechanisms)());
195}
196
197char **
198rpc_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
205bool_t
206rpc_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
213bool_t
214rpc_gss_is_installed(char *mechanism)
215{
216	if (!rpcgss_calls_init())
217		return (FALSE);
218	return ((*calls.rpc_gss_is_installed)(mechanism));
219}
220
221bool_t
222rpc_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
235bool_t
236rpc_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
243bool_t
244rpc_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
252bool_t
253rpc_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
260bool_t
261rpc_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
268enum 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
276bool_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
286bool_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
295int
296rpc_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
304int
305rpc_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
312void
313rpc_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