xref: /titanic_44/usr/src/uts/common/fs/nfs/nfs_auth.c (revision 6dfee4834394825da35b977ca71cdc965bc7b6a4)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 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 <sys/param.h>
30 #include <sys/errno.h>
31 #include <sys/vfs.h>
32 #include <sys/vnode.h>
33 #include <sys/cred.h>
34 #include <sys/cmn_err.h>
35 #include <sys/systm.h>
36 #include <sys/kmem.h>
37 #include <sys/pathname.h>
38 #include <sys/utsname.h>
39 #include <sys/debug.h>
40 
41 #include <rpc/types.h>
42 #include <rpc/auth.h>
43 #include <rpc/clnt.h>
44 
45 #include <nfs/nfs.h>
46 #include <nfs/export.h>
47 #include <nfs/nfs_clnt.h>
48 #include <rpcsvc/nfsauth_prot.h>
49 
50 #define	EQADDR(a1, a2)  \
51 	(bcmp((char *)(a1)->buf, (char *)(a2)->buf, (a1)->len) == 0 && \
52 	(a1)->len == (a2)->len)
53 
54 static struct knetconfig auth_knconf;
55 static servinfo_t svp;
56 static clinfo_t ci;
57 
58 static struct kmem_cache *exi_cache_handle;
59 static void exi_cache_reclaim(void *);
60 static void exi_cache_trim(struct exportinfo *exi);
61 
62 int nfsauth_cache_hit;
63 int nfsauth_cache_miss;
64 int nfsauth_cache_reclaim;
65 
66 /*
67  * Number of seconds to wait for an NFSAUTH upcall.
68  */
69 static int nfsauth_timeout = 20;
70 
71 void
72 nfsauth_init(void)
73 {
74 	vnode_t *kvp;
75 	int error;
76 	char addrbuf[SYS_NMLN+16];
77 
78 	/*
79 	 * Setup netconfig.
80 	 * Assume a connectionless loopback transport.
81 	 */
82 	if ((error = lookupname("/dev/ticotsord", UIO_SYSSPACE, FOLLOW,
83 		NULLVPP, &kvp)) != 0) {
84 		cmn_err(CE_CONT, "nfsauth: lookupname: %d\n", error);
85 		return;
86 	}
87 
88 	auth_knconf.knc_rdev = kvp->v_rdev;
89 	auth_knconf.knc_protofmly = NC_LOOPBACK;
90 	auth_knconf.knc_semantics = NC_TPI_COTS_ORD;
91 	VN_RELE(kvp);
92 
93 	(void) strcpy(addrbuf, utsname.nodename);
94 	(void) strcat(addrbuf, ".nfsauth");
95 
96 	svp.sv_knconf = &auth_knconf;
97 	svp.sv_addr.buf = kmem_alloc(strlen(addrbuf)+1, KM_SLEEP);
98 	(void) strcpy(svp.sv_addr.buf, addrbuf);
99 	svp.sv_addr.len = (uint_t)strlen(addrbuf);
100 	svp.sv_addr.maxlen = svp.sv_addr.len;
101 	svp.sv_secdata = kmem_alloc(sizeof (struct sec_data), KM_SLEEP);
102 	svp.sv_secdata->rpcflavor = AUTH_LOOPBACK;
103 	svp.sv_secdata->data = NULL;
104 
105 	ci.cl_prog = NFSAUTH_PROG;
106 	ci.cl_vers = NFSAUTH_VERS;
107 	ci.cl_readsize = 0;
108 	ci.cl_retrans = 1;
109 	ci.cl_flags = 0x0;
110 
111 	/*
112 	 * Allocate nfsauth cache handle
113 	 */
114 	exi_cache_handle = kmem_cache_create("exi_cache_handle",
115 		sizeof (struct auth_cache), 0, NULL, NULL,
116 		exi_cache_reclaim, NULL, NULL, 0);
117 }
118 
119 /*
120  * Finalization routine for nfsauth. It is important to call this routine
121  * before destroying the exported_lock.
122  */
123 void
124 nfsauth_fini(void)
125 {
126 	/*
127 	 * Deallocate nfsauth cache handle
128 	 */
129 	kmem_cache_destroy(exi_cache_handle);
130 }
131 
132 static int
133 nfsauth_clget(CLIENT **newcl, struct chtab **chp)
134 {
135 	return (clget(&ci, &svp, CRED(), newcl, chp));
136 }
137 
138 /*
139  * Convert the address in a netbuf to
140  * a hash index for the auth_cache table.
141  */
142 static int
143 hash(struct netbuf *a)
144 {
145 	int i, h = 0;
146 
147 	for (i = 0; i < a->len; i++)
148 		h ^= a->buf[i];
149 
150 	return (h & (AUTH_TABLESIZE - 1));
151 }
152 
153 /*
154  * Mask out the components of an
155  * address that do not identify
156  * a host. For socket addresses the
157  * masking gets rid of the port number.
158  */
159 static void
160 addrmask(struct netbuf *addr, struct netbuf *mask)
161 {
162 	int i;
163 
164 	for (i = 0; i < addr->len; i++)
165 		addr->buf[i] &= mask->buf[i];
166 }
167 
168 /*
169  * nfsauth4_access is used for NFS V4 auth checking. Besides doing
170  * the common nfsauth_access(), it will check if the client can
171  * have a limited access to this vnode even if the security flavor
172  * used does not meet the policy.
173  */
174 int
175 nfsauth4_access(struct exportinfo *exi, vnode_t *vp, struct svc_req *req)
176 {
177 	int access;
178 
179 	access = nfsauth_access(exi, req);
180 
181 	/*
182 	 * There are cases that the server needs to allow the client
183 	 * to have a limited view.
184 	 *
185 	 * e.g.
186 	 * /export is shared as "sec=sys,rw=dfs-test-4,sec=krb5,rw"
187 	 * /export/home is shared as "sec=sys,rw"
188 	 *
189 	 * When the client mounts /export with sec=sys, the client
190 	 * would get a limited view with RO access on /export to see
191 	 * "home" only because the client is allowed to access
192 	 * /export/home with auth_sys.
193 	 */
194 	if (access & NFSAUTH_DENIED || access & NFSAUTH_WRONGSEC) {
195 		/*
196 		 * Allow ro permission with LIMITED view if there is a
197 		 * sub-dir exported under vp.
198 		 */
199 		if (has_visible(exi, vp)) {
200 			return (NFSAUTH_LIMITED);
201 		}
202 	}
203 
204 	return (access);
205 }
206 
207 /*
208  * Get the access information from the cache or callup to the mountd
209  * to get and cache the access information in the kernel.
210  */
211 int
212 nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor)
213 {
214 	struct netbuf addr, *claddr;
215 	struct auth_cache **head, *ap;
216 	CLIENT *clnt;
217 	struct chtab *ch;
218 	struct auth_req request;
219 	struct auth_res result;
220 	enum clnt_stat rpcstat;
221 	int access;
222 	struct timeval timout;
223 	static time_t exi_msg = 0;
224 	time_t now;
225 
226 	/*
227 	 * Now check whether this client already
228 	 * has an entry for this flavor in the cache
229 	 * for this export.
230 	 * Get the caller's address, mask off the
231 	 * parts of the address that do not identify
232 	 * the host (port number, etc), and then hash
233 	 * it to find the chain of cache entries.
234 	 */
235 
236 	claddr = svc_getrpccaller(req->rq_xprt);
237 	addr = *claddr;
238 	addr.buf = mem_alloc(addr.len);
239 	bcopy(claddr->buf, addr.buf, claddr->len);
240 	addrmask(&addr, svc_getaddrmask(req->rq_xprt));
241 	head = &exi->exi_cache[hash(&addr)];
242 
243 	rw_enter(&exi->exi_cache_lock, RW_READER);
244 	for (ap = *head; ap; ap = ap->auth_next) {
245 		if (EQADDR(&addr, &ap->auth_addr) && flavor == ap->auth_flavor)
246 			break;
247 	}
248 	if (ap) {				/* cache hit */
249 		access = ap->auth_access;
250 		ap->auth_time = gethrestime_sec();
251 		nfsauth_cache_hit++;
252 	}
253 
254 	rw_exit(&exi->exi_cache_lock);
255 
256 	if (ap) {
257 		kmem_free(addr.buf, addr.len);
258 		return (access);
259 	}
260 
261 	nfsauth_cache_miss++;
262 
263 	/*
264 	 * No entry in the cache for this client/flavor
265 	 * so we need to call the nfsauth service in the
266 	 * mount daemon.
267 	 */
268 
269 	if (nfsauth_clget(&clnt, &ch)) {
270 		kmem_free(addr.buf, addr.len);
271 		return (NFSAUTH_DROP);
272 	}
273 
274 	timout.tv_sec = nfsauth_timeout;
275 	timout.tv_usec = 0;
276 
277 	request.req_client.n_len = addr.len;
278 	request.req_client.n_bytes = addr.buf;
279 	request.req_netid = svc_getnetid(req->rq_xprt);
280 	request.req_path = exi->exi_export.ex_path;
281 	request.req_flavor = flavor;
282 
283 	rpcstat = clnt_call(clnt, NFSAUTH_ACCESS,
284 		(xdrproc_t)xdr_auth_req, (caddr_t)&request,
285 		(xdrproc_t)xdr_auth_res, (caddr_t)&result,
286 		timout);
287 
288 	switch (rpcstat) {
289 	case RPC_SUCCESS:
290 		access = result.auth_perm;
291 		break;
292 	case RPC_INTR:
293 		break;
294 	case RPC_TIMEDOUT:
295 		/*
296 		 * Show messages no more than once per minute
297 		 */
298 		now = gethrestime_sec();
299 		if ((exi_msg + 60) < now) {
300 			exi_msg = now;
301 			cmn_err(CE_WARN, "nfsauth: mountd not responding");
302 		}
303 		break;
304 	default:
305 		/*
306 		 * Show messages no more than once per minute
307 		 */
308 		now = gethrestime_sec();
309 		if ((exi_msg + 60) < now) {
310 			char *errmsg;
311 
312 			exi_msg = now;
313 			errmsg = clnt_sperror(clnt, "nfsauth upcall failed");
314 			cmn_err(CE_WARN, errmsg);
315 			kmem_free(errmsg, MAXPATHLEN);
316 		}
317 		break;
318 	}
319 
320 	clfree(clnt, ch);
321 	if (rpcstat != RPC_SUCCESS) {
322 		kmem_free(addr.buf, addr.len);
323 		return (NFSAUTH_DROP);
324 	}
325 
326 	/*
327 	 * Now cache the result on the cache chain
328 	 * for this export (if there's enough memory)
329 	 */
330 	ap = kmem_cache_alloc(exi_cache_handle, KM_NOSLEEP);
331 	if (ap) {
332 		ap->auth_addr = addr;
333 		ap->auth_flavor = flavor;
334 		ap->auth_access = access;
335 		ap->auth_time = gethrestime_sec();
336 		rw_enter(&exi->exi_cache_lock, RW_WRITER);
337 		ap->auth_next = *head;
338 		*head = ap;
339 		rw_exit(&exi->exi_cache_lock);
340 	} else {
341 		kmem_free(addr.buf, addr.len);
342 	}
343 
344 	return (access);
345 }
346 
347 /*
348  * Check if the requesting client has access to the filesystem with
349  * a given nfs flavor number which is an explicitly shared flavor.
350  */
351 int
352 nfsauth4_secinfo_access(struct exportinfo *exi, struct svc_req *req,
353 			int flavor, int perm)
354 {
355 	int access;
356 
357 	if (! (perm & M_4SEC_EXPORTED)) {
358 		return (NFSAUTH_DENIED);
359 	}
360 
361 	/*
362 	 * Optimize if there are no lists
363 	 */
364 	if ((perm & M_ROOT) == 0) {
365 		perm &= ~M_4SEC_EXPORTED;
366 		if (perm == M_RO)
367 			return (NFSAUTH_RO);
368 		if (perm == M_RW)
369 			return (NFSAUTH_RW);
370 	}
371 
372 	access = nfsauth_cache_get(exi, req, flavor);
373 
374 	return (access);
375 }
376 
377 int
378 nfsauth_access(struct exportinfo *exi, struct svc_req *req)
379 {
380 	int access, mapaccess;
381 	struct secinfo *sp;
382 	int i, flavor, perm;
383 	int authnone_entry = -1;
384 
385 	/*
386 	 *  Get the nfs flavor number from xprt.
387 	 */
388 	flavor = (int)(uintptr_t)req->rq_xprt->xp_cookie;
389 
390 	/*
391 	 * First check the access restrictions on the filesystem.  If
392 	 * there are no lists associated with this flavor then there's no
393 	 * need to make an expensive call to the nfsauth service or to
394 	 * cache anything.
395 	 */
396 
397 	sp = exi->exi_export.ex_secinfo;
398 	for (i = 0; i < exi->exi_export.ex_seccnt; i++) {
399 		if (flavor != sp[i].s_secinfo.sc_nfsnum) {
400 			if (sp[i].s_secinfo.sc_nfsnum == AUTH_NONE)
401 				authnone_entry = i;
402 			continue;
403 		}
404 		break;
405 	}
406 
407 	mapaccess = 0;
408 
409 	if (i >= exi->exi_export.ex_seccnt) {
410 		/*
411 		 * Flavor not found, but use AUTH_NONE if it exists
412 		 */
413 		if (authnone_entry == -1)
414 			return (NFSAUTH_DENIED);
415 		flavor = AUTH_NONE;
416 		mapaccess = NFSAUTH_MAPNONE;
417 		i = authnone_entry;
418 	}
419 
420 	/*
421 	 * If the flavor is in the ex_secinfo list, but not an explicitly
422 	 * shared flavor by the user, it is a result of the nfsv4 server
423 	 * namespace setup. We will grant an RO permission similar for
424 	 * a pseudo node except that this node is a shared one.
425 	 *
426 	 * e.g. flavor in (flavor) indicates that it is not explictly
427 	 *	shared by the user:
428 	 *
429 	 *		/	(sys, krb5)
430 	 *		|
431 	 *		export  #share -o sec=sys (krb5)
432 	 *		|
433 	 *		secure  #share -o sec=krb5
434 	 *
435 	 *	In this case, when a krb5 request coming in to access
436 	 *	/export, RO permission is granted.
437 	 */
438 	if (!(sp[i].s_flags & M_4SEC_EXPORTED))
439 		return (mapaccess | NFSAUTH_RO);
440 
441 	/*
442 	 * Optimize if there are no lists
443 	 */
444 	perm = sp[i].s_flags;
445 	if ((perm & M_ROOT) == 0) {
446 		perm &= ~M_4SEC_EXPORTED;
447 		if (perm == M_RO)
448 			return (mapaccess | NFSAUTH_RO);
449 		if (perm == M_RW)
450 			return (mapaccess | NFSAUTH_RW);
451 	}
452 
453 	access = nfsauth_cache_get(exi, req, flavor);
454 
455 	return (access | mapaccess);
456 }
457 
458 /*
459  * Free the nfsauth cache for a given export
460  */
461 void
462 nfsauth_cache_free(struct exportinfo *exi)
463 {
464 	int i;
465 	struct auth_cache *p, *next;
466 
467 	for (i = 0; i < AUTH_TABLESIZE; i++) {
468 		for (p = exi->exi_cache[i]; p; p = next) {
469 			kmem_free(p->auth_addr.buf, p->auth_addr.len);
470 			next = p->auth_next;
471 			kmem_cache_free(exi_cache_handle, (void *)p);
472 		}
473 	}
474 }
475 
476 /*
477  * Called by the kernel memory allocator when
478  * memory is low. Free unused cache entries.
479  * If that's not enough, the VM system will
480  * call again for some more.
481  */
482 /*ARGSUSED*/
483 void
484 exi_cache_reclaim(void *cdrarg)
485 {
486 	int i;
487 	struct exportinfo *exi;
488 
489 	rw_enter(&exported_lock, RW_READER);
490 
491 	for (i = 0; i < EXPTABLESIZE; i++) {
492 		for (exi = exptable[i]; exi; exi = exi->exi_hash) {
493 			exi_cache_trim(exi);
494 		}
495 	}
496 	nfsauth_cache_reclaim++;
497 
498 	rw_exit(&exported_lock);
499 }
500 
501 /*
502  * Don't reclaim entries until they've been
503  * in the cache for at least exi_cache_time
504  * seconds.
505  */
506 time_t exi_cache_time = 60 * 60;
507 
508 void
509 exi_cache_trim(struct exportinfo *exi)
510 {
511 	struct auth_cache *p;
512 	struct auth_cache *prev, *next;
513 	int i;
514 	time_t stale_time;
515 
516 	stale_time = gethrestime_sec() - exi_cache_time;
517 
518 	rw_enter(&exi->exi_cache_lock, RW_WRITER);
519 
520 	for (i = 0; i < AUTH_TABLESIZE; i++) {
521 
522 		/*
523 		 * Free entries that have not been
524 		 * used for exi_cache_time seconds.
525 		 */
526 		prev = NULL;
527 		for (p = exi->exi_cache[i]; p; p = next) {
528 			next = p->auth_next;
529 			if (p->auth_time > stale_time) {
530 				prev = p;
531 				continue;
532 			}
533 
534 			kmem_free(p->auth_addr.buf, p->auth_addr.len);
535 			kmem_cache_free(exi_cache_handle, (void *)p);
536 			if (prev == NULL)
537 				exi->exi_cache[i] = next;
538 			else
539 				prev->auth_next = next;
540 		}
541 	}
542 
543 	rw_exit(&exi->exi_cache_lock);
544 }
545