xref: /illumos-gate/usr/src/uts/common/fs/nfs/nfs_auth.c (revision 4764d912222e53f8386bae7bf491f5780fd102ec)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/param.h>
29 #include <sys/errno.h>
30 #include <sys/vfs.h>
31 #include <sys/vnode.h>
32 #include <sys/cred.h>
33 #include <sys/cmn_err.h>
34 #include <sys/systm.h>
35 #include <sys/kmem.h>
36 #include <sys/pathname.h>
37 #include <sys/utsname.h>
38 #include <sys/debug.h>
39 #include <sys/door.h>
40 #include <sys/sdt.h>
41 
42 #include <rpc/types.h>
43 #include <rpc/auth.h>
44 #include <rpc/clnt.h>
45 
46 #include <nfs/nfs.h>
47 #include <nfs/export.h>
48 #include <nfs/nfs_clnt.h>
49 #include <nfs/auth.h>
50 
51 #define	EQADDR(a1, a2)  \
52 	(bcmp((char *)(a1)->buf, (char *)(a2)->buf, (a1)->len) == 0 && \
53 	(a1)->len == (a2)->len)
54 
55 static struct knetconfig auth_knconf;
56 static servinfo_t svp;
57 static clinfo_t ci;
58 
59 static struct kmem_cache *exi_cache_handle;
60 static void exi_cache_reclaim(void *);
61 static void exi_cache_trim(struct exportinfo *exi);
62 
63 int nfsauth_cache_hit;
64 int nfsauth_cache_miss;
65 int nfsauth_cache_reclaim;
66 
67 /*
68  * Number of seconds to wait for an NFSAUTH upcall.
69  */
70 static int nfsauth_timeout = 20;
71 
72 /*
73  * mountd is a server-side only daemon. This will need to be
74  * revisited if the NFS server is ever made zones-aware.
75  */
76 kmutex_t	mountd_lock;
77 door_handle_t   mountd_dh;
78 
79 void
80 mountd_args(uint_t did)
81 {
82 	mutex_enter(&mountd_lock);
83 	if (mountd_dh)
84 		door_ki_rele(mountd_dh);
85 	mountd_dh = door_ki_lookup(did);
86 	mutex_exit(&mountd_lock);
87 }
88 
89 void
90 nfsauth_init(void)
91 {
92 	/*
93 	 * mountd can be restarted by smf(5). We need to make sure
94 	 * the updated door handle will safely make it to mountd_dh
95 	 */
96 	mutex_init(&mountd_lock, NULL, MUTEX_DEFAULT, NULL);
97 
98 	/*
99 	 * Allocate nfsauth cache handle
100 	 */
101 	exi_cache_handle = kmem_cache_create("exi_cache_handle",
102 		sizeof (struct auth_cache), 0, NULL, NULL,
103 		exi_cache_reclaim, NULL, NULL, 0);
104 }
105 
106 /*
107  * Finalization routine for nfsauth. It is important to call this routine
108  * before destroying the exported_lock.
109  */
110 void
111 nfsauth_fini(void)
112 {
113 	/*
114 	 * Deallocate nfsauth cache handle
115 	 */
116 	kmem_cache_destroy(exi_cache_handle);
117 }
118 
119 /*
120  * Convert the address in a netbuf to
121  * a hash index for the auth_cache table.
122  */
123 static int
124 hash(struct netbuf *a)
125 {
126 	int i, h = 0;
127 
128 	for (i = 0; i < a->len; i++)
129 		h ^= a->buf[i];
130 
131 	return (h & (AUTH_TABLESIZE - 1));
132 }
133 
134 /*
135  * Mask out the components of an
136  * address that do not identify
137  * a host. For socket addresses the
138  * masking gets rid of the port number.
139  */
140 static void
141 addrmask(struct netbuf *addr, struct netbuf *mask)
142 {
143 	int i;
144 
145 	for (i = 0; i < addr->len; i++)
146 		addr->buf[i] &= mask->buf[i];
147 }
148 
149 /*
150  * nfsauth4_access is used for NFS V4 auth checking. Besides doing
151  * the common nfsauth_access(), it will check if the client can
152  * have a limited access to this vnode even if the security flavor
153  * used does not meet the policy.
154  */
155 int
156 nfsauth4_access(struct exportinfo *exi, vnode_t *vp, struct svc_req *req)
157 {
158 	int access;
159 
160 	access = nfsauth_access(exi, req);
161 
162 	/*
163 	 * There are cases that the server needs to allow the client
164 	 * to have a limited view.
165 	 *
166 	 * e.g.
167 	 * /export is shared as "sec=sys,rw=dfs-test-4,sec=krb5,rw"
168 	 * /export/home is shared as "sec=sys,rw"
169 	 *
170 	 * When the client mounts /export with sec=sys, the client
171 	 * would get a limited view with RO access on /export to see
172 	 * "home" only because the client is allowed to access
173 	 * /export/home with auth_sys.
174 	 */
175 	if (access & NFSAUTH_DENIED || access & NFSAUTH_WRONGSEC) {
176 		/*
177 		 * Allow ro permission with LIMITED view if there is a
178 		 * sub-dir exported under vp.
179 		 */
180 		if (has_visible(exi, vp)) {
181 			return (NFSAUTH_LIMITED);
182 		}
183 	}
184 
185 	return (access);
186 }
187 
188 static void
189 sys_log(const char *msg)
190 {
191 	static time_t	tstamp = 0;
192 	time_t		now;
193 
194 	/*
195 	 * msg is shown (at most) once per minute
196 	 */
197 	now = gethrestime_sec();
198 	if ((tstamp + 60) < now) {
199 		tstamp = now;
200 		cmn_err(CE_WARN, msg);
201 	}
202 }
203 
204 /*
205  * Get the access information from the cache or callup to the mountd
206  * to get and cache the access information in the kernel.
207  */
208 int
209 nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor)
210 {
211 	struct netbuf		  addr;
212 	struct netbuf		 *claddr;
213 	struct auth_cache	**head;
214 	struct auth_cache	 *ap;
215 	int			  access;
216 	varg_t			  varg = {0};
217 	nfsauth_res_t		  res = {0};
218 	XDR			  xdrs_a;
219 	XDR			  xdrs_r;
220 	size_t			  absz;
221 	caddr_t			  abuf;
222 	size_t			  rbsz = (size_t)(BYTES_PER_XDR_UNIT * 2);
223 	char			  result[BYTES_PER_XDR_UNIT * 2] = {0};
224 	caddr_t			  rbuf = (caddr_t)&result;
225 	int			  last = 0;
226 	door_arg_t		  da;
227 	door_info_t		  di;
228 	door_handle_t		  dh;
229 	uint_t			  ntries = 0;
230 
231 	/*
232 	 * Now check whether this client already
233 	 * has an entry for this flavor in the cache
234 	 * for this export.
235 	 * Get the caller's address, mask off the
236 	 * parts of the address that do not identify
237 	 * the host (port number, etc), and then hash
238 	 * it to find the chain of cache entries.
239 	 */
240 
241 	claddr = svc_getrpccaller(req->rq_xprt);
242 	addr = *claddr;
243 	addr.buf = kmem_alloc(addr.len, KM_SLEEP);
244 	bcopy(claddr->buf, addr.buf, claddr->len);
245 	addrmask(&addr, svc_getaddrmask(req->rq_xprt));
246 	head = &exi->exi_cache[hash(&addr)];
247 
248 	rw_enter(&exi->exi_cache_lock, RW_READER);
249 	for (ap = *head; ap; ap = ap->auth_next) {
250 		if (EQADDR(&addr, &ap->auth_addr) && flavor == ap->auth_flavor)
251 			break;
252 	}
253 	if (ap) {				/* cache hit */
254 		access = ap->auth_access;
255 		ap->auth_time = gethrestime_sec();
256 		nfsauth_cache_hit++;
257 	}
258 
259 	rw_exit(&exi->exi_cache_lock);
260 
261 	if (ap) {
262 		kmem_free(addr.buf, addr.len);
263 		return (access);
264 	}
265 
266 	nfsauth_cache_miss++;
267 
268 	/*
269 	 * No entry in the cache for this client/flavor
270 	 * so we need to call the nfsauth service in the
271 	 * mount daemon.
272 	 */
273 retry:
274 	mutex_enter(&mountd_lock);
275 	dh = mountd_dh;
276 	if (dh)
277 		door_ki_hold(dh);
278 	mutex_exit(&mountd_lock);
279 
280 	if (dh == NULL) {
281 		/*
282 		 * The rendezvous point has not been established yet !
283 		 * This could mean that either mountd(1m) has not yet
284 		 * been started or that _this_ routine nuked the door
285 		 * handle after receiving an EINTR for a REVOKED door.
286 		 *
287 		 * Returning NFSAUTH_DROP will cause the NFS client
288 		 * to retransmit the request, so let's try to be more
289 		 * rescillient and attempt for ntries before we bail.
290 		 */
291 		if (++ntries % NFSAUTH_DR_TRYCNT) {
292 			delay(hz);
293 			goto retry;
294 		}
295 		sys_log("nfsauth: mountd has not established door");
296 		kmem_free(addr.buf, addr.len);
297 		return (NFSAUTH_DROP);
298 	}
299 	ntries = 0;
300 	varg.vers = V_PROTO;
301 	varg.arg_u.arg.cmd = NFSAUTH_ACCESS;
302 	varg.arg_u.arg.areq.req_client.n_len = addr.len;
303 	varg.arg_u.arg.areq.req_client.n_bytes = addr.buf;
304 	varg.arg_u.arg.areq.req_netid = svc_getnetid(req->rq_xprt);
305 	varg.arg_u.arg.areq.req_path = exi->exi_export.ex_path;
306 	varg.arg_u.arg.areq.req_flavor = flavor;
307 
308 	/*
309 	 * Setup the XDR stream for encoding the arguments. Notice that
310 	 * in addition to the args having variable fields (req_netid and
311 	 * req_path), the argument data structure is itself versioned,
312 	 * so we need to make sure we can size the arguments buffer
313 	 * appropriately to encode all the args. If we can't get sizing
314 	 * info _or_ properly encode the arguments, there's really no
315 	 * point in continuting, so we fail the request.
316 	 */
317 	DTRACE_PROBE1(nfsserv__func__nfsauth__varg, varg_t *, &varg);
318 	if ((absz = xdr_sizeof(xdr_varg, (void *)&varg)) == 0) {
319 		door_ki_rele(dh);
320 		kmem_free(addr.buf, addr.len);
321 		return (NFSAUTH_DENIED);
322 	}
323 	abuf = (caddr_t)kmem_alloc(absz, KM_SLEEP);
324 	xdrmem_create(&xdrs_a, abuf, absz, XDR_ENCODE);
325 	if (!xdr_varg(&xdrs_a, &varg)) {
326 		door_ki_rele(dh);
327 		goto fail;
328 	}
329 	XDR_DESTROY(&xdrs_a);
330 
331 	/*
332 	 * The result (nfsauth_res_t) is always two int's, so we don't
333 	 * have to dynamically size (or allocate) the results buffer.
334 	 * Now that we've got what we need, we prep the door arguments
335 	 * and place the call.
336 	 */
337 	da.data_ptr = (char *)abuf;
338 	da.data_size = absz;
339 	da.desc_ptr = NULL;
340 	da.desc_num = 0;
341 	da.rbuf = (char *)rbuf;
342 	da.rsize = rbsz;
343 
344 	switch (door_ki_upcall_limited(dh, &da, NULL, SIZE_MAX, 0)) {
345 		case 0:				/* Success */
346 			if (da.data_ptr != da.rbuf && da.data_size == 0) {
347 				/*
348 				 * The door_return that contained the data
349 				 * failed ! We're here because of the 2nd
350 				 * door_return (w/o data) such that we can
351 				 * get control of the thread (and exit
352 				 * gracefully).
353 				 */
354 				DTRACE_PROBE1(nfsserv__func__nfsauth__door__nil,
355 				    door_arg_t *, &da);
356 				door_ki_rele(dh);
357 				goto fail;
358 
359 			} else if (rbuf != da.rbuf) {
360 				/*
361 				 * The only time this should be true
362 				 * is iff userland wanted to hand us
363 				 * a bigger response than what we
364 				 * expect; that should not happen
365 				 * (nfsauth_res_t is only 2 int's),
366 				 * but we check nevertheless.
367 				 */
368 				rbuf = da.rbuf;
369 				rbsz = da.rsize;
370 
371 			} else if (rbsz > da.data_size) {
372 				/*
373 				 * We were expecting two int's; but if
374 				 * userland fails in encoding the XDR
375 				 * stream, we detect that here, since
376 				 * the mountd forces down only one byte
377 				 * in such scenario.
378 				 */
379 				door_ki_rele(dh);
380 				goto fail;
381 			}
382 			door_ki_rele(dh);
383 			break;
384 
385 		case EAGAIN:
386 			/*
387 			 * Server out of resources; back off for a bit
388 			 */
389 			door_ki_rele(dh);
390 			kmem_free(abuf, absz);
391 			delay(hz);
392 			goto retry;
393 			/* NOTREACHED */
394 
395 		case EINTR:
396 			if (!door_ki_info(dh, &di)) {
397 				if (di.di_attributes & DOOR_REVOKED) {
398 					/*
399 					 * The server barfed and revoked
400 					 * the (existing) door on us; we
401 					 * want to wait to give smf(5) a
402 					 * chance to restart mountd(1m)
403 					 * and establish a new door handle.
404 					 */
405 					mutex_enter(&mountd_lock);
406 					if (dh == mountd_dh)
407 						mountd_dh = NULL;
408 					mutex_exit(&mountd_lock);
409 					door_ki_rele(dh);
410 					kmem_free(abuf, absz);
411 					delay(hz);
412 					goto retry;
413 				}
414 				/*
415 				 * If the door was _not_ revoked on us,
416 				 * then more than likely we took an INTR,
417 				 * so we need to fail the operation.
418 				 */
419 				door_ki_rele(dh);
420 				goto fail;
421 			}
422 			/*
423 			 * The only failure that can occur from getting
424 			 * the door info is EINVAL, so we let the code
425 			 * below handle it.
426 			 */
427 			/* FALLTHROUGH */
428 
429 		case EBADF:
430 		case EINVAL:
431 		default:
432 			/*
433 			 * If we have a stale door handle, give smf a last
434 			 * chance to start it by sleeping for a little bit.
435 			 * If we're still hosed, we'll fail the call.
436 			 *
437 			 * Since we're going to reacquire the door handle
438 			 * upon the retry, we opt to sleep for a bit and
439 			 * _not_ to clear mountd_dh. If mountd restarted
440 			 * and was able to set mountd_dh, we should see
441 			 * the new instance; if not, we won't get caught
442 			 * up in the retry/DELAY loop.
443 			 */
444 			door_ki_rele(dh);
445 			if (!last) {
446 				delay(hz);
447 				last++;
448 				goto retry;
449 			}
450 			sys_log("nfsauth: stale mountd door handle");
451 			goto fail;
452 	}
453 
454 	/*
455 	 * No door errors encountered; setup the XDR stream for decoding
456 	 * the results. If we fail to decode the results, we've got no
457 	 * other recourse than to fail the request.
458 	 */
459 	xdrmem_create(&xdrs_r, rbuf, rbsz, XDR_DECODE);
460 	if (!xdr_nfsauth_res(&xdrs_r, &res))
461 		goto fail;
462 	XDR_DESTROY(&xdrs_r);
463 
464 	DTRACE_PROBE1(nfsserv__func__nfsauth__results, nfsauth_res_t *, &res);
465 	switch (res.stat) {
466 		case NFSAUTH_DR_OKAY:
467 			access = res.ares.auth_perm;
468 			kmem_free(abuf, absz);
469 			break;
470 
471 		case NFSAUTH_DR_EFAIL:
472 		case NFSAUTH_DR_DECERR:
473 		case NFSAUTH_DR_BADCMD:
474 		default:
475 fail:
476 			kmem_free(addr.buf, addr.len);
477 			kmem_free(abuf, absz);
478 			return (NFSAUTH_DENIED);
479 			/* NOTREACHED */
480 	}
481 
482 	/*
483 	 * Now cache the result on the cache chain
484 	 * for this export (if there's enough memory)
485 	 */
486 	ap = kmem_cache_alloc(exi_cache_handle, KM_NOSLEEP);
487 	if (ap) {
488 		ap->auth_addr = addr;
489 		ap->auth_flavor = flavor;
490 		ap->auth_access = access;
491 		ap->auth_time = gethrestime_sec();
492 		rw_enter(&exi->exi_cache_lock, RW_WRITER);
493 		ap->auth_next = *head;
494 		*head = ap;
495 		rw_exit(&exi->exi_cache_lock);
496 	} else {
497 		kmem_free(addr.buf, addr.len);
498 	}
499 
500 	return (access);
501 }
502 
503 /*
504  * Check if the requesting client has access to the filesystem with
505  * a given nfs flavor number which is an explicitly shared flavor.
506  */
507 int
508 nfsauth4_secinfo_access(struct exportinfo *exi, struct svc_req *req,
509 			int flavor, int perm)
510 {
511 	int access;
512 
513 	if (! (perm & M_4SEC_EXPORTED)) {
514 		return (NFSAUTH_DENIED);
515 	}
516 
517 	/*
518 	 * Optimize if there are no lists
519 	 */
520 	if ((perm & M_ROOT) == 0) {
521 		perm &= ~M_4SEC_EXPORTED;
522 		if (perm == M_RO)
523 			return (NFSAUTH_RO);
524 		if (perm == M_RW)
525 			return (NFSAUTH_RW);
526 	}
527 
528 	access = nfsauth_cache_get(exi, req, flavor);
529 
530 	return (access);
531 }
532 
533 int
534 nfsauth_access(struct exportinfo *exi, struct svc_req *req)
535 {
536 	int access, mapaccess;
537 	struct secinfo *sp;
538 	int i, flavor, perm;
539 	int authnone_entry = -1;
540 
541 	/*
542 	 *  Get the nfs flavor number from xprt.
543 	 */
544 	flavor = (int)(uintptr_t)req->rq_xprt->xp_cookie;
545 
546 	/*
547 	 * First check the access restrictions on the filesystem.  If
548 	 * there are no lists associated with this flavor then there's no
549 	 * need to make an expensive call to the nfsauth service or to
550 	 * cache anything.
551 	 */
552 
553 	sp = exi->exi_export.ex_secinfo;
554 	for (i = 0; i < exi->exi_export.ex_seccnt; i++) {
555 		if (flavor != sp[i].s_secinfo.sc_nfsnum) {
556 			if (sp[i].s_secinfo.sc_nfsnum == AUTH_NONE)
557 				authnone_entry = i;
558 			continue;
559 		}
560 		break;
561 	}
562 
563 	mapaccess = 0;
564 
565 	if (i >= exi->exi_export.ex_seccnt) {
566 		/*
567 		 * Flavor not found, but use AUTH_NONE if it exists
568 		 */
569 		if (authnone_entry == -1)
570 			return (NFSAUTH_DENIED);
571 		flavor = AUTH_NONE;
572 		mapaccess = NFSAUTH_MAPNONE;
573 		i = authnone_entry;
574 	}
575 
576 	/*
577 	 * If the flavor is in the ex_secinfo list, but not an explicitly
578 	 * shared flavor by the user, it is a result of the nfsv4 server
579 	 * namespace setup. We will grant an RO permission similar for
580 	 * a pseudo node except that this node is a shared one.
581 	 *
582 	 * e.g. flavor in (flavor) indicates that it is not explictly
583 	 *	shared by the user:
584 	 *
585 	 *		/	(sys, krb5)
586 	 *		|
587 	 *		export  #share -o sec=sys (krb5)
588 	 *		|
589 	 *		secure  #share -o sec=krb5
590 	 *
591 	 *	In this case, when a krb5 request coming in to access
592 	 *	/export, RO permission is granted.
593 	 */
594 	if (!(sp[i].s_flags & M_4SEC_EXPORTED))
595 		return (mapaccess | NFSAUTH_RO);
596 
597 	/*
598 	 * Optimize if there are no lists
599 	 */
600 	perm = sp[i].s_flags;
601 	if ((perm & M_ROOT) == 0) {
602 		perm &= ~M_4SEC_EXPORTED;
603 		if (perm == M_RO)
604 			return (mapaccess | NFSAUTH_RO);
605 		if (perm == M_RW)
606 			return (mapaccess | NFSAUTH_RW);
607 	}
608 
609 	access = nfsauth_cache_get(exi, req, flavor);
610 
611 	return (access | mapaccess);
612 }
613 
614 /*
615  * Free the nfsauth cache for a given export
616  */
617 void
618 nfsauth_cache_free(struct exportinfo *exi)
619 {
620 	int i;
621 	struct auth_cache *p, *next;
622 
623 	for (i = 0; i < AUTH_TABLESIZE; i++) {
624 		for (p = exi->exi_cache[i]; p; p = next) {
625 			kmem_free(p->auth_addr.buf, p->auth_addr.len);
626 			next = p->auth_next;
627 			kmem_cache_free(exi_cache_handle, (void *)p);
628 		}
629 	}
630 }
631 
632 /*
633  * Called by the kernel memory allocator when
634  * memory is low. Free unused cache entries.
635  * If that's not enough, the VM system will
636  * call again for some more.
637  */
638 /*ARGSUSED*/
639 void
640 exi_cache_reclaim(void *cdrarg)
641 {
642 	int i;
643 	struct exportinfo *exi;
644 
645 	rw_enter(&exported_lock, RW_READER);
646 
647 	for (i = 0; i < EXPTABLESIZE; i++) {
648 		for (exi = exptable[i]; exi; exi = exi->exi_hash) {
649 			exi_cache_trim(exi);
650 		}
651 	}
652 	nfsauth_cache_reclaim++;
653 
654 	rw_exit(&exported_lock);
655 }
656 
657 /*
658  * Don't reclaim entries until they've been
659  * in the cache for at least exi_cache_time
660  * seconds.
661  */
662 time_t exi_cache_time = 60 * 60;
663 
664 void
665 exi_cache_trim(struct exportinfo *exi)
666 {
667 	struct auth_cache *p;
668 	struct auth_cache *prev, *next;
669 	int i;
670 	time_t stale_time;
671 
672 	stale_time = gethrestime_sec() - exi_cache_time;
673 
674 	rw_enter(&exi->exi_cache_lock, RW_WRITER);
675 
676 	for (i = 0; i < AUTH_TABLESIZE; i++) {
677 
678 		/*
679 		 * Free entries that have not been
680 		 * used for exi_cache_time seconds.
681 		 */
682 		prev = NULL;
683 		for (p = exi->exi_cache[i]; p; p = next) {
684 			next = p->auth_next;
685 			if (p->auth_time > stale_time) {
686 				prev = p;
687 				continue;
688 			}
689 
690 			kmem_free(p->auth_addr.buf, p->auth_addr.len);
691 			kmem_cache_free(exi_cache_handle, (void *)p);
692 			if (prev == NULL)
693 				exi->exi_cache[i] = next;
694 			else
695 				prev->auth_next = next;
696 		}
697 	}
698 
699 	rw_exit(&exi->exi_cache_lock);
700 }
701