xref: /illumos-gate/usr/src/uts/common/rpc/sec/sec_svc.c (revision 2d6eb4a5e0a47d30189497241345dc5466bb68ab)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  *  sec_svc.c, Server-side rpc security interface.
28  */
29 #ifdef _KERNEL
30 #include <sys/param.h>
31 #include <sys/types.h>
32 #include <sys/debug.h>
33 #include <sys/systm.h>
34 #include <rpc/types.h>
35 #include <netinet/in.h>
36 #include <rpc/xdr.h>
37 #include <rpc/auth.h>
38 #include <rpc/clnt.h>
39 #include <rpc/rpc_msg.h>
40 #include <sys/tiuser.h>
41 #include <sys/tihdr.h>
42 #include <sys/t_kuser.h>
43 #include <sys/cmn_err.h>
44 #include <rpc/auth_des.h>
45 #include <rpc/auth_sys.h>
46 #include <rpc/rpcsec_gss.h>
47 #include <rpc/svc_auth.h>
48 #include <rpc/svc.h>
49 #else
50 #include <rpc/rpc.h>
51 #endif
52 
53 
54 enum auth_stat _svcauth_null(struct svc_req *, struct rpc_msg *);
55 
56 /*
57  *  NO-OP server wrap/unwrap svc_authany_ops using no-op svc_authany_wrap().
58  */
59 /* ARGSUSED */
60 static int
svc_authany_wrap(SVCAUTH * auth,XDR * xdrs,xdrproc_t xfunc,caddr_t xwhere)61 svc_authany_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xfunc, caddr_t xwhere)
62 {
63 	return (*xfunc)(xdrs, xwhere);
64 }
65 
66 struct svc_auth_ops svc_authany_ops = {
67 	svc_authany_wrap,
68 	svc_authany_wrap
69 };
70 
71 
72 /*
73  * The call rpc message, msg has been obtained from the wire.  The msg contains
74  * the raw form of credentials and verifiers.  authenticate returns AUTH_OK
75  * if the msg is successfully authenticated.  If AUTH_OK then the routine also
76  * does the following things:
77  * set rqst->rq_xprt->verf to the appropriate response verifier;
78  * sets rqst->rq_client_cred to the "cooked" form of the credentials.
79  *
80  * NB: rqst->rq_cxprt->verf must be pre-alloctaed;
81  * its length is set appropriately.
82  *
83  * The caller still owns and is responsible for msg->u.cmb.cred and
84  * msg->u.cmb.verf.  The authentication system retains ownership of
85  * rqst->rq_client_cred, the cooked credentials.
86  *
87  * There is an assumption that any flavor less than AUTH_NULL is
88  * invalid.
89  */
90 enum auth_stat
sec_svc_msg(struct svc_req * rqst,struct rpc_msg * msg,bool_t * no_dispatch)91 sec_svc_msg(struct svc_req *rqst, struct rpc_msg *msg, bool_t *no_dispatch)
92 {
93 	int cred_flavor;
94 
95 	rqst->rq_cred = msg->rm_call.cb_cred;
96 	rqst->rq_xprt->xp_verf.oa_flavor = _null_auth.oa_flavor;
97 	rqst->rq_xprt->xp_verf.oa_length = 0;
98 	/*
99 	 * Init the xp_auth to be no-op for all the flavors.
100 	 * Flavor specific routines will revise this when appropriate.
101 	 */
102 	rqst->rq_xprt->xp_auth.svc_ah_ops = svc_authany_ops;
103 	rqst->rq_xprt->xp_auth.svc_ah_private = NULL;
104 	*no_dispatch = FALSE;
105 
106 	cred_flavor = rqst->rq_cred.oa_flavor;
107 
108 	switch (cred_flavor) {
109 	case AUTH_NULL:
110 		rqst->rq_xprt->xp_cookie = (void *) AUTH_NULL;
111 		return (_svcauth_null(rqst, msg));
112 
113 	case AUTH_UNIX:
114 		rqst->rq_xprt->xp_cookie = (void *) AUTH_UNIX;
115 		return (_svcauth_unix(rqst, msg));
116 
117 	case AUTH_SHORT:
118 		rqst->rq_xprt->xp_cookie = (void *) AUTH_SHORT;
119 		return (_svcauth_short(rqst, msg));
120 
121 	case AUTH_DES:
122 		rqst->rq_xprt->xp_cookie = (void *) AUTH_DES;
123 		return (_svcauth_des(rqst, msg));
124 
125 	case RPCSEC_GSS:
126 		/*
127 		 * RPCSEC_GSS flavor routine takes an additional
128 		 * boolean parameter that gets set to TRUE when
129 		 * the call is not to be dispatched to the server.
130 		 */
131 		return (__svcrpcsec_gss(rqst, msg, no_dispatch));
132 	}
133 	return (AUTH_REJECTEDCRED);
134 }
135 
136 /*
137  *  sec_svc_getcred() gets unix cred of incoming security rpc requests.
138  *  It also returns the prinicipal name and a cookie which is application
139  *  dependent e.g. for nfs, it is the pseudo flavor.
140  *
141  *  return 0 on failure
142  */
143 int
sec_svc_getcred(struct svc_req * req,cred_t * cr,caddr_t * principal,int * secmod)144 sec_svc_getcred(struct svc_req *req, cred_t *cr, caddr_t *principal,
145 	int *secmod)
146 {
147 	struct authunix_parms *aup;
148 	struct authdes_cred *adc;
149 	int flavor, stat;
150 	rpc_gss_rawcred_t *rcred;
151 	rpc_gss_ucred_t	*ucred;
152 	void *cookie;
153 
154 	stat = 1;
155 	flavor = req->rq_cred.oa_flavor;
156 
157 	*principal = NULL;
158 	switch (flavor) {
159 	case AUTH_UNIX:
160 		*secmod = AUTH_UNIX;
161 		aup = (struct authunix_parms *)req->rq_clntcred;
162 		if (crsetugid(cr, aup->aup_uid, aup->aup_gid) != 0)
163 			(void) crsetugid(cr, UID_NOBODY, GID_NOBODY);
164 		if (crsetgroups(cr, aup->aup_len, aup->aup_gids) != 0)
165 			(void) crsetgroups(cr, 0, NULL);
166 		break;
167 
168 	case AUTH_NONE:
169 		*secmod = AUTH_NONE;
170 		break;
171 
172 	case AUTH_DES:
173 		*secmod = AUTH_DES;
174 		adc = (struct authdes_cred *)req->rq_clntcred;
175 		stat = kauthdes_getucred(adc, cr);
176 		*principal = adc->adc_fullname.name;
177 		break;
178 
179 	case RPCSEC_GSS:
180 		stat = rpc_gss_getcred(req, &rcred, &ucred, &cookie);
181 		*secmod = (int)(uintptr_t)cookie;	/* XX64 */
182 		if (ucred != NULL) {
183 			if (crsetugid(cr, ucred->uid, ucred->gid) != 0 ||
184 			    crsetgroups(cr, ucred->gidlen, ucred->gidlist) != 0)
185 				stat = 0;
186 		} else {
187 			(void) crsetugid(cr, UID_NOBODY, GID_NOBODY);
188 			(void) crsetgroups(cr, 0, NULL);
189 		}
190 		*principal = (caddr_t)rcred->client_principal;
191 		break;
192 
193 	default:
194 		stat = 0;
195 		break;
196 	}
197 
198 	return (stat);
199 }
200 
201 
202 /* ARGSUSED */
203 enum auth_stat
_svcauth_null(struct svc_req * rqst,struct rpc_msg * msg)204 _svcauth_null(struct svc_req *rqst, struct rpc_msg *msg)
205 {
206 	return (AUTH_OK);
207 }
208 
209 
210 /*
211  *  Load root principal names from user space to kernel space.
212  *
213  *  flavor - security flavor
214  *  count - number of principal names to be loaded
215  *  proots - address of the array of root names.
216  *		input is the array address in the user space,
217  *		output is the kernel address.
218  *
219  *  return 0 on failure.
220  */
221 int
sec_svc_loadrootnames(int flavor,int count,caddr_t ** proots,model_t model)222 sec_svc_loadrootnames(int flavor, int count, caddr_t **proots, model_t model)
223 {
224 	caddr_t *roots, *oroots, root;
225 	char netname[MAXNETNAMELEN+1];
226 	struct rpc_gss_principal gsstmp, *gssname;
227 	uint_t i, j;
228 	size_t len, allocsz, oallocsz;
229 
230 #ifdef lint
231 	model = model;
232 #endif
233 
234 	/*
235 	 * Get list of names from user space
236 	 */
237 	allocsz = count * sizeof (caddr_t);
238 	oallocsz = count * SIZEOF_PTR(model);
239 
240 	/*
241 	 * And now copy each individual principal name
242 	 */
243 	switch (flavor) {
244 	case AUTH_DES:
245 		roots = kmem_zalloc(allocsz, KM_SLEEP);
246 		oroots = kmem_alloc(oallocsz, KM_SLEEP);
247 
248 		if (copyin(*proots, oroots, oallocsz))
249 			goto done;
250 
251 		for (i = 0; i < count; i++) {
252 			/*
253 			 * copyinstr copies the complete string (including the
254 			 * NULL) and returns the len with the NULL byte
255 			 * included in the calculation as long as the max
256 			 * length is not exceeded.
257 			 */
258 #ifdef _SYSCALL32_IMPL
259 			if (model != DATAMODEL_NATIVE) {
260 				caddr32_t *tmp;
261 
262 				tmp = (caddr32_t *)oroots;
263 				root = (caddr_t)(uintptr_t)tmp[i];
264 			} else
265 #endif
266 				root = oroots[i];
267 			if (copyinstr(root, netname, sizeof (netname), &len)) {
268 				for (j = 0; j < i; j++) {
269 					if (roots[j] != NULL)
270 						kmem_free(roots[j],
271 						    strlen(roots[j]) + 1);
272 				}
273 				goto done;
274 			}
275 			roots[i] = kmem_alloc(len, KM_SLEEP);
276 			bcopy(netname, roots[i], len);
277 		}
278 		kmem_free(oroots, oallocsz);
279 		*proots = roots;
280 		return (1);
281 
282 	case RPCSEC_GSS:
283 		roots = kmem_alloc(allocsz, KM_SLEEP);
284 		oroots = kmem_alloc(oallocsz, KM_SLEEP);
285 
286 		if (copyin(*proots, oroots, oallocsz))
287 			goto done;
288 
289 		for (i = 0; i < count; i++) {
290 #ifdef _SYSCALL32_IMPL
291 			if (model != DATAMODEL_NATIVE) {
292 				caddr32_t *tmp;
293 
294 				tmp = (caddr32_t *)oroots;
295 				root = (caddr_t)(uintptr_t)tmp[i];
296 			} else
297 #endif
298 				root = oroots[i];
299 
300 			if (copyin(root, &gsstmp, sizeof (gsstmp))) {
301 				kmem_free(oroots, oallocsz);
302 				goto gssfreeup;
303 			}
304 			len = sizeof (gsstmp.len) + gsstmp.len;
305 			gssname = kmem_alloc(len, KM_SLEEP);
306 			if (copyin(root, gssname, len)) {
307 				kmem_free(gssname, len);
308 				kmem_free(oroots, oallocsz);
309 				goto gssfreeup;
310 			}
311 			roots[i] = (caddr_t)gssname;
312 		}
313 		kmem_free(oroots, oallocsz);
314 		*proots = roots;
315 		return (1);
316 
317 	default:
318 		return (0);
319 	}
320 
321 gssfreeup:
322 	for (j = 0; j < i; j++) {
323 		if (roots[j] != NULL) {
324 			gssname = (rpc_gss_principal_t)roots[j];
325 			kmem_free(roots[j], gssname->len +
326 			    sizeof (gssname->len));
327 		}
328 	}
329 done:
330 	kmem_free(roots, allocsz);
331 	return (0);
332 }
333 
334 
335 /*
336  * Figure out everything we allocated in a root principal name list in
337  * order to free it up.
338  */
339 void
sec_svc_freerootnames(int flavor,int count,caddr_t * proots)340 sec_svc_freerootnames(int flavor, int count, caddr_t *proots)
341 {
342 	int i;
343 	rpc_gss_principal_t gssname;
344 
345 	switch (flavor) {
346 	case AUTH_DES:
347 		for (i = 0; i < count; i++)
348 			if (proots[i] != NULL)
349 				kmem_free(proots[i], strlen(proots[i]) + 1);
350 		break;
351 
352 	case RPCSEC_GSS:
353 		for (i = 0; i < count; i++) {
354 			if (proots[i] == NULL)
355 				continue;
356 			gssname = (rpc_gss_principal_t)proots[i];
357 			kmem_free(proots[i], gssname->len + sizeof (int));
358 		}
359 		break;
360 
361 	}
362 	kmem_free(proots, count * sizeof (caddr_t));
363 }
364 
365 /*
366  * Check if the  given principal name is in the root principal list
367  */
368 bool_t
sec_svc_inrootlist(int flavor,caddr_t rootname,int count,caddr_t * roots)369 sec_svc_inrootlist(int flavor, caddr_t rootname, int count, caddr_t *roots)
370 {
371 	int i, tmp_len;
372 	rpc_gss_principal_t gssp, tmp_gssp;
373 	size_t namelen;
374 
375 	switch (flavor) {
376 	case AUTH_DES:
377 		namelen = strlen(rootname) + 1;
378 		for (i = 0; i < count; i++)
379 			if (bcmp(rootname, roots[i], namelen) == 0)
380 				return (TRUE);
381 		break;
382 
383 	case RPCSEC_GSS:
384 		gssp = (rpc_gss_principal_t)rootname;
385 		namelen = gssp->len;
386 		for (i = 0; i < count; i++) {
387 			tmp_gssp = (rpc_gss_principal_t)roots[i];
388 			tmp_len = tmp_gssp->len;
389 			if ((namelen == tmp_len) &&
390 			    (bcmp(&gssp->name[0],
391 			    &tmp_gssp->name[0], namelen) == 0))
392 				return (TRUE);
393 		}
394 		break;
395 	}
396 	return (FALSE);
397 }
398 
399 /*
400  * Miscellaneout "control" functions manipulating global RPC security
401  * attributes for server applications.
402  */
403 bool_t
sec_svc_control(uint_t cmd,void * argp)404 sec_svc_control(uint_t cmd, void *argp)
405 {
406 	bool_t result = FALSE;		/* be paranoid */
407 
408 	switch (cmd) {
409 	case RPC_SVC_SET_GSS_CALLBACK:
410 		result = rpc_gss_set_callback((rpc_gss_callback_t *)argp);
411 		break;
412 	default:
413 		cmn_err(CE_WARN, "sec_svc_control: bad command (%d)", cmd);
414 		result = FALSE;
415 		break;
416 	}
417 
418 	return (result);
419 }
420