xref: /titanic_51/usr/src/uts/common/fs/nfs/nfs_auth.c (revision aafcd32bc33ad660f4567cc47fc6e0e13338006e)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51cc55349Srmesta  * Common Development and Distribution License (the "License").
61cc55349Srmesta  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
2189621fe1SMarcel Telka 
227c478bd9Sstevel@tonic-gate /*
2371da0c32SMarcel Telka  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
2489621fe1SMarcel Telka  * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #include <sys/param.h>
287c478bd9Sstevel@tonic-gate #include <sys/errno.h>
297c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
307c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
317c478bd9Sstevel@tonic-gate #include <sys/cred.h>
327c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
337c478bd9Sstevel@tonic-gate #include <sys/systm.h>
347c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
357c478bd9Sstevel@tonic-gate #include <sys/pathname.h>
367c478bd9Sstevel@tonic-gate #include <sys/utsname.h>
377c478bd9Sstevel@tonic-gate #include <sys/debug.h>
381cc55349Srmesta #include <sys/door.h>
391cc55349Srmesta #include <sys/sdt.h>
403ccecb66SThomas Haynes #include <sys/thread.h>
4171da0c32SMarcel Telka #include <sys/avl.h>
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate #include <rpc/types.h>
447c478bd9Sstevel@tonic-gate #include <rpc/auth.h>
457c478bd9Sstevel@tonic-gate #include <rpc/clnt.h>
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate #include <nfs/nfs.h>
487c478bd9Sstevel@tonic-gate #include <nfs/export.h>
497c478bd9Sstevel@tonic-gate #include <nfs/nfs_clnt.h>
501cc55349Srmesta #include <nfs/auth.h>
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate static struct kmem_cache *exi_cache_handle;
537c478bd9Sstevel@tonic-gate static void exi_cache_reclaim(void *);
547c478bd9Sstevel@tonic-gate static void exi_cache_trim(struct exportinfo *exi);
557c478bd9Sstevel@tonic-gate 
563ccecb66SThomas Haynes extern pri_t minclsyspri;
573ccecb66SThomas Haynes 
5871da0c32SMarcel Telka volatile uint_t nfsauth_cache_hit;
5971da0c32SMarcel Telka volatile uint_t nfsauth_cache_miss;
6071da0c32SMarcel Telka volatile uint_t nfsauth_cache_refresh;
6171da0c32SMarcel Telka volatile uint_t nfsauth_cache_reclaim;
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate /*
643ccecb66SThomas Haynes  * The lifetime of an auth cache entry:
653ccecb66SThomas Haynes  * ------------------------------------
663ccecb66SThomas Haynes  *
673ccecb66SThomas Haynes  * An auth cache entry is created with both the auth_time
683ccecb66SThomas Haynes  * and auth_freshness times set to the current time.
693ccecb66SThomas Haynes  *
703ccecb66SThomas Haynes  * Upon every client access which results in a hit, the
713ccecb66SThomas Haynes  * auth_time will be updated.
723ccecb66SThomas Haynes  *
733ccecb66SThomas Haynes  * If a client access determines that the auth_freshness
743ccecb66SThomas Haynes  * indicates that the entry is STALE, then it will be
753ccecb66SThomas Haynes  * refreshed. Note that this will explicitly reset
763ccecb66SThomas Haynes  * auth_time.
773ccecb66SThomas Haynes  *
783ccecb66SThomas Haynes  * When the REFRESH successfully occurs, then the
793ccecb66SThomas Haynes  * auth_freshness is updated.
803ccecb66SThomas Haynes  *
813ccecb66SThomas Haynes  * There are two ways for an entry to leave the cache:
823ccecb66SThomas Haynes  *
833ccecb66SThomas Haynes  * 1) Purged by an action on the export (remove or changed)
843ccecb66SThomas Haynes  * 2) Memory backpressure from the kernel (check against NFSAUTH_CACHE_TRIM)
853ccecb66SThomas Haynes  *
863ccecb66SThomas Haynes  * For 2) we check the timeout value against auth_time.
877c478bd9Sstevel@tonic-gate  */
883ccecb66SThomas Haynes 
893ccecb66SThomas Haynes /*
903ccecb66SThomas Haynes  * Number of seconds until we mark for refresh an auth cache entry.
913ccecb66SThomas Haynes  */
923ccecb66SThomas Haynes #define	NFSAUTH_CACHE_REFRESH 600
933ccecb66SThomas Haynes 
943ccecb66SThomas Haynes /*
953ccecb66SThomas Haynes  * Number of idle seconds until we yield to backpressure
963ccecb66SThomas Haynes  * to trim a cache entry.
973ccecb66SThomas Haynes  */
983ccecb66SThomas Haynes #define	NFSAUTH_CACHE_TRIM 3600
993ccecb66SThomas Haynes 
1003ccecb66SThomas Haynes /*
1013ccecb66SThomas Haynes  * While we could encapuslate the exi_list inside the
1023ccecb66SThomas Haynes  * exi structure, we can't do that for the auth_list.
1033ccecb66SThomas Haynes  * So, to keep things looking clean, we keep them both
1043ccecb66SThomas Haynes  * in these external lists.
1053ccecb66SThomas Haynes  */
1063ccecb66SThomas Haynes typedef struct refreshq_exi_node {
1073ccecb66SThomas Haynes 	struct exportinfo	*ren_exi;
1083ccecb66SThomas Haynes 	list_t			ren_authlist;
1093ccecb66SThomas Haynes 	list_node_t		ren_node;
1103ccecb66SThomas Haynes } refreshq_exi_node_t;
1113ccecb66SThomas Haynes 
1123ccecb66SThomas Haynes typedef struct refreshq_auth_node {
1133ccecb66SThomas Haynes 	struct auth_cache	*ran_auth;
11471da0c32SMarcel Telka 	char			*ran_netid;
1153ccecb66SThomas Haynes 	list_node_t		ran_node;
1163ccecb66SThomas Haynes } refreshq_auth_node_t;
1173ccecb66SThomas Haynes 
1183ccecb66SThomas Haynes /*
1193ccecb66SThomas Haynes  * Used to manipulate things on the refreshq_queue.
1203ccecb66SThomas Haynes  * Note that the refresh thread will effectively
1213ccecb66SThomas Haynes  * pop a node off of the queue, at which point it
1223ccecb66SThomas Haynes  * will no longer need to hold the mutex.
1233ccecb66SThomas Haynes  */
1243ccecb66SThomas Haynes static kmutex_t refreshq_lock;
1253ccecb66SThomas Haynes static list_t refreshq_queue;
1263ccecb66SThomas Haynes static kcondvar_t refreshq_cv;
1273ccecb66SThomas Haynes 
1283ccecb66SThomas Haynes /*
1293ccecb66SThomas Haynes  * If there is ever a problem with loading the
1303ccecb66SThomas Haynes  * module, then nfsauth_fini() needs to be called
1313ccecb66SThomas Haynes  * to remove state. In that event, since the
1323ccecb66SThomas Haynes  * refreshq thread has been started, they need to
1333ccecb66SThomas Haynes  * work together to get rid of state.
1343ccecb66SThomas Haynes  */
1353ccecb66SThomas Haynes typedef enum nfsauth_refreshq_thread_state {
1363ccecb66SThomas Haynes 	REFRESHQ_THREAD_RUNNING,
1373ccecb66SThomas Haynes 	REFRESHQ_THREAD_FINI_REQ,
1383ccecb66SThomas Haynes 	REFRESHQ_THREAD_HALTED
1393ccecb66SThomas Haynes } nfsauth_refreshq_thread_state_t;
1403ccecb66SThomas Haynes 
1413ccecb66SThomas Haynes nfsauth_refreshq_thread_state_t
1423ccecb66SThomas Haynes refreshq_thread_state = REFRESHQ_THREAD_HALTED;
1433ccecb66SThomas Haynes 
1443ccecb66SThomas Haynes static void nfsauth_free_node(struct auth_cache *);
1453ccecb66SThomas Haynes static void nfsauth_refresh_thread(void);
1467c478bd9Sstevel@tonic-gate 
14771da0c32SMarcel Telka static int nfsauth_cache_compar(const void *, const void *);
14871da0c32SMarcel Telka 
1491cc55349Srmesta /*
1501cc55349Srmesta  * mountd is a server-side only daemon. This will need to be
1511cc55349Srmesta  * revisited if the NFS server is ever made zones-aware.
1521cc55349Srmesta  */
1531cc55349Srmesta kmutex_t	mountd_lock;
1541cc55349Srmesta door_handle_t   mountd_dh;
1551cc55349Srmesta 
1561cc55349Srmesta void
1571cc55349Srmesta mountd_args(uint_t did)
1581cc55349Srmesta {
1591cc55349Srmesta 	mutex_enter(&mountd_lock);
16089621fe1SMarcel Telka 	if (mountd_dh != NULL)
1611cc55349Srmesta 		door_ki_rele(mountd_dh);
1621cc55349Srmesta 	mountd_dh = door_ki_lookup(did);
1631cc55349Srmesta 	mutex_exit(&mountd_lock);
1641cc55349Srmesta }
1651cc55349Srmesta 
1667c478bd9Sstevel@tonic-gate void
1677c478bd9Sstevel@tonic-gate nfsauth_init(void)
1687c478bd9Sstevel@tonic-gate {
1697c478bd9Sstevel@tonic-gate 	/*
1701cc55349Srmesta 	 * mountd can be restarted by smf(5). We need to make sure
1711cc55349Srmesta 	 * the updated door handle will safely make it to mountd_dh
1727c478bd9Sstevel@tonic-gate 	 */
1731cc55349Srmesta 	mutex_init(&mountd_lock, NULL, MUTEX_DEFAULT, NULL);
1747c478bd9Sstevel@tonic-gate 
1753ccecb66SThomas Haynes 	mutex_init(&refreshq_lock, NULL, MUTEX_DEFAULT, NULL);
1763ccecb66SThomas Haynes 	list_create(&refreshq_queue, sizeof (refreshq_exi_node_t),
1773ccecb66SThomas Haynes 	    offsetof(refreshq_exi_node_t, ren_node));
1783ccecb66SThomas Haynes 
1793ccecb66SThomas Haynes 	cv_init(&refreshq_cv, NULL, CV_DEFAULT, NULL);
1803ccecb66SThomas Haynes 
1817c478bd9Sstevel@tonic-gate 	/*
1827c478bd9Sstevel@tonic-gate 	 * Allocate nfsauth cache handle
1837c478bd9Sstevel@tonic-gate 	 */
1847c478bd9Sstevel@tonic-gate 	exi_cache_handle = kmem_cache_create("exi_cache_handle",
1857c478bd9Sstevel@tonic-gate 	    sizeof (struct auth_cache), 0, NULL, NULL,
1867c478bd9Sstevel@tonic-gate 	    exi_cache_reclaim, NULL, NULL, 0);
1873ccecb66SThomas Haynes 
1883ccecb66SThomas Haynes 	refreshq_thread_state = REFRESHQ_THREAD_RUNNING;
1893ccecb66SThomas Haynes 	(void) zthread_create(NULL, 0, nfsauth_refresh_thread,
1903ccecb66SThomas Haynes 	    NULL, 0, minclsyspri);
1917c478bd9Sstevel@tonic-gate }
1927c478bd9Sstevel@tonic-gate 
1937c478bd9Sstevel@tonic-gate /*
1947c478bd9Sstevel@tonic-gate  * Finalization routine for nfsauth. It is important to call this routine
1957c478bd9Sstevel@tonic-gate  * before destroying the exported_lock.
1967c478bd9Sstevel@tonic-gate  */
1977c478bd9Sstevel@tonic-gate void
1987c478bd9Sstevel@tonic-gate nfsauth_fini(void)
1997c478bd9Sstevel@tonic-gate {
2003ccecb66SThomas Haynes 	refreshq_exi_node_t	*ren;
2013ccecb66SThomas Haynes 
2023ccecb66SThomas Haynes 	/*
20371da0c32SMarcel Telka 	 * Prevent the nfsauth_refresh_thread from getting new
2043ccecb66SThomas Haynes 	 * work.
2053ccecb66SThomas Haynes 	 */
2063ccecb66SThomas Haynes 	mutex_enter(&refreshq_lock);
2073ccecb66SThomas Haynes 	if (refreshq_thread_state != REFRESHQ_THREAD_HALTED) {
2083ccecb66SThomas Haynes 		refreshq_thread_state = REFRESHQ_THREAD_FINI_REQ;
2093ccecb66SThomas Haynes 		cv_broadcast(&refreshq_cv);
2103ccecb66SThomas Haynes 
2113ccecb66SThomas Haynes 		/*
2123ccecb66SThomas Haynes 		 * Also, wait for nfsauth_refresh_thread() to exit.
2133ccecb66SThomas Haynes 		 */
2143ccecb66SThomas Haynes 		while (refreshq_thread_state != REFRESHQ_THREAD_HALTED) {
2153ccecb66SThomas Haynes 			cv_wait(&refreshq_cv, &refreshq_lock);
2163ccecb66SThomas Haynes 		}
2173ccecb66SThomas Haynes 	}
21871da0c32SMarcel Telka 	mutex_exit(&refreshq_lock);
2193ccecb66SThomas Haynes 
2203ccecb66SThomas Haynes 	/*
22171da0c32SMarcel Telka 	 * Walk the exi_list and in turn, walk the auth_lists and free all
22271da0c32SMarcel Telka 	 * lists.  In addition, free INVALID auth_cache entries.
2233ccecb66SThomas Haynes 	 */
2243ccecb66SThomas Haynes 	while ((ren = list_remove_head(&refreshq_queue))) {
22571da0c32SMarcel Telka 		refreshq_auth_node_t *ran;
22671da0c32SMarcel Telka 
22771da0c32SMarcel Telka 		while ((ran = list_remove_head(&ren->ren_authlist)) != NULL) {
22871da0c32SMarcel Telka 			struct auth_cache *p = ran->ran_auth;
22971da0c32SMarcel Telka 			if (p->auth_state == NFS_AUTH_INVALID)
23071da0c32SMarcel Telka 				nfsauth_free_node(p);
23171da0c32SMarcel Telka 			strfree(ran->ran_netid);
2323ccecb66SThomas Haynes 			kmem_free(ran, sizeof (refreshq_auth_node_t));
2333ccecb66SThomas Haynes 		}
2343ccecb66SThomas Haynes 
2353ccecb66SThomas Haynes 		list_destroy(&ren->ren_authlist);
2363ccecb66SThomas Haynes 		exi_rele(ren->ren_exi);
2373ccecb66SThomas Haynes 		kmem_free(ren, sizeof (refreshq_exi_node_t));
2383ccecb66SThomas Haynes 	}
2393ccecb66SThomas Haynes 	list_destroy(&refreshq_queue);
2403ccecb66SThomas Haynes 
2413ccecb66SThomas Haynes 	cv_destroy(&refreshq_cv);
2423ccecb66SThomas Haynes 	mutex_destroy(&refreshq_lock);
2433ccecb66SThomas Haynes 
2443ccecb66SThomas Haynes 	mutex_destroy(&mountd_lock);
2453ccecb66SThomas Haynes 
2467c478bd9Sstevel@tonic-gate 	/*
2477c478bd9Sstevel@tonic-gate 	 * Deallocate nfsauth cache handle
2487c478bd9Sstevel@tonic-gate 	 */
2497c478bd9Sstevel@tonic-gate 	kmem_cache_destroy(exi_cache_handle);
2507c478bd9Sstevel@tonic-gate }
2517c478bd9Sstevel@tonic-gate 
2527c478bd9Sstevel@tonic-gate /*
2537c478bd9Sstevel@tonic-gate  * Convert the address in a netbuf to
2547c478bd9Sstevel@tonic-gate  * a hash index for the auth_cache table.
2557c478bd9Sstevel@tonic-gate  */
2567c478bd9Sstevel@tonic-gate static int
2577c478bd9Sstevel@tonic-gate hash(struct netbuf *a)
2587c478bd9Sstevel@tonic-gate {
2597c478bd9Sstevel@tonic-gate 	int i, h = 0;
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate 	for (i = 0; i < a->len; i++)
2627c478bd9Sstevel@tonic-gate 		h ^= a->buf[i];
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate 	return (h & (AUTH_TABLESIZE - 1));
2657c478bd9Sstevel@tonic-gate }
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate /*
2687c478bd9Sstevel@tonic-gate  * Mask out the components of an
2697c478bd9Sstevel@tonic-gate  * address that do not identify
2707c478bd9Sstevel@tonic-gate  * a host. For socket addresses the
2717c478bd9Sstevel@tonic-gate  * masking gets rid of the port number.
2727c478bd9Sstevel@tonic-gate  */
2737c478bd9Sstevel@tonic-gate static void
2747c478bd9Sstevel@tonic-gate addrmask(struct netbuf *addr, struct netbuf *mask)
2757c478bd9Sstevel@tonic-gate {
2767c478bd9Sstevel@tonic-gate 	int i;
2777c478bd9Sstevel@tonic-gate 
2787c478bd9Sstevel@tonic-gate 	for (i = 0; i < addr->len; i++)
2797c478bd9Sstevel@tonic-gate 		addr->buf[i] &= mask->buf[i];
2807c478bd9Sstevel@tonic-gate }
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate /*
2837c478bd9Sstevel@tonic-gate  * nfsauth4_access is used for NFS V4 auth checking. Besides doing
2847c478bd9Sstevel@tonic-gate  * the common nfsauth_access(), it will check if the client can
2857c478bd9Sstevel@tonic-gate  * have a limited access to this vnode even if the security flavor
2867c478bd9Sstevel@tonic-gate  * used does not meet the policy.
2877c478bd9Sstevel@tonic-gate  */
2887c478bd9Sstevel@tonic-gate int
2895cb0d679SMarcel Telka nfsauth4_access(struct exportinfo *exi, vnode_t *vp, struct svc_req *req,
29089621fe1SMarcel Telka     cred_t *cr, uid_t *uid, gid_t *gid, uint_t *ngids, gid_t **gids)
2917c478bd9Sstevel@tonic-gate {
2927c478bd9Sstevel@tonic-gate 	int access;
2937c478bd9Sstevel@tonic-gate 
294*aafcd32bSMarcel Telka 	rw_enter(&exported_lock, RW_READER);
29589621fe1SMarcel Telka 	access = nfsauth_access(exi, req, cr, uid, gid, ngids, gids);
296*aafcd32bSMarcel Telka 	rw_exit(&exported_lock);
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 	/*
2997c478bd9Sstevel@tonic-gate 	 * There are cases that the server needs to allow the client
3007c478bd9Sstevel@tonic-gate 	 * to have a limited view.
3017c478bd9Sstevel@tonic-gate 	 *
3027c478bd9Sstevel@tonic-gate 	 * e.g.
3037c478bd9Sstevel@tonic-gate 	 * /export is shared as "sec=sys,rw=dfs-test-4,sec=krb5,rw"
3047c478bd9Sstevel@tonic-gate 	 * /export/home is shared as "sec=sys,rw"
3057c478bd9Sstevel@tonic-gate 	 *
3067c478bd9Sstevel@tonic-gate 	 * When the client mounts /export with sec=sys, the client
3077c478bd9Sstevel@tonic-gate 	 * would get a limited view with RO access on /export to see
3087c478bd9Sstevel@tonic-gate 	 * "home" only because the client is allowed to access
3097c478bd9Sstevel@tonic-gate 	 * /export/home with auth_sys.
3107c478bd9Sstevel@tonic-gate 	 */
3117c478bd9Sstevel@tonic-gate 	if (access & NFSAUTH_DENIED || access & NFSAUTH_WRONGSEC) {
3127c478bd9Sstevel@tonic-gate 		/*
3137c478bd9Sstevel@tonic-gate 		 * Allow ro permission with LIMITED view if there is a
3147c478bd9Sstevel@tonic-gate 		 * sub-dir exported under vp.
3157c478bd9Sstevel@tonic-gate 		 */
316b89a8333Snatalie li - Sun Microsystems - Irvine United States 		if (has_visible(exi, vp))
3177c478bd9Sstevel@tonic-gate 			return (NFSAUTH_LIMITED);
3187c478bd9Sstevel@tonic-gate 	}
3197c478bd9Sstevel@tonic-gate 
3207c478bd9Sstevel@tonic-gate 	return (access);
3217c478bd9Sstevel@tonic-gate }
3227c478bd9Sstevel@tonic-gate 
3231cc55349Srmesta static void
3241cc55349Srmesta sys_log(const char *msg)
3251cc55349Srmesta {
3261cc55349Srmesta 	static time_t	tstamp = 0;
3271cc55349Srmesta 	time_t		now;
3281cc55349Srmesta 
3291cc55349Srmesta 	/*
3301cc55349Srmesta 	 * msg is shown (at most) once per minute
3311cc55349Srmesta 	 */
3321cc55349Srmesta 	now = gethrestime_sec();
3331cc55349Srmesta 	if ((tstamp + 60) < now) {
3341cc55349Srmesta 		tstamp = now;
3351cc55349Srmesta 		cmn_err(CE_WARN, msg);
3361cc55349Srmesta 	}
3371cc55349Srmesta }
3381cc55349Srmesta 
3397c478bd9Sstevel@tonic-gate /*
3403ccecb66SThomas Haynes  * Callup to the mountd to get access information in the kernel.
3417c478bd9Sstevel@tonic-gate  */
3423ccecb66SThomas Haynes static bool_t
3433ccecb66SThomas Haynes nfsauth_retrieve(struct exportinfo *exi, char *req_netid, int flavor,
3445cb0d679SMarcel Telka     struct netbuf *addr, int *access, uid_t clnt_uid, gid_t clnt_gid,
34589621fe1SMarcel Telka     uint_t clnt_gids_cnt, const gid_t *clnt_gids, uid_t *srv_uid,
34689621fe1SMarcel Telka     gid_t *srv_gid, uint_t *srv_gids_cnt, gid_t **srv_gids)
3477c478bd9Sstevel@tonic-gate {
3481cc55349Srmesta 	varg_t			  varg = {0};
3491cc55349Srmesta 	nfsauth_res_t		  res = {0};
35089621fe1SMarcel Telka 	XDR			  xdrs;
3511cc55349Srmesta 	size_t			  absz;
3521cc55349Srmesta 	caddr_t			  abuf;
3531cc55349Srmesta 	int			  last = 0;
3541cc55349Srmesta 	door_arg_t		  da;
3551cc55349Srmesta 	door_info_t		  di;
3561cc55349Srmesta 	door_handle_t		  dh;
3571cc55349Srmesta 	uint_t			  ntries = 0;
3587c478bd9Sstevel@tonic-gate 
3597c478bd9Sstevel@tonic-gate 	/*
3607c478bd9Sstevel@tonic-gate 	 * No entry in the cache for this client/flavor
3617c478bd9Sstevel@tonic-gate 	 * so we need to call the nfsauth service in the
3627c478bd9Sstevel@tonic-gate 	 * mount daemon.
3637c478bd9Sstevel@tonic-gate 	 */
36489621fe1SMarcel Telka 
36589621fe1SMarcel Telka 	varg.vers = V_PROTO;
36689621fe1SMarcel Telka 	varg.arg_u.arg.cmd = NFSAUTH_ACCESS;
36789621fe1SMarcel Telka 	varg.arg_u.arg.areq.req_client.n_len = addr->len;
36889621fe1SMarcel Telka 	varg.arg_u.arg.areq.req_client.n_bytes = addr->buf;
36989621fe1SMarcel Telka 	varg.arg_u.arg.areq.req_netid = req_netid;
37089621fe1SMarcel Telka 	varg.arg_u.arg.areq.req_path = exi->exi_export.ex_path;
37189621fe1SMarcel Telka 	varg.arg_u.arg.areq.req_flavor = flavor;
37289621fe1SMarcel Telka 	varg.arg_u.arg.areq.req_clnt_uid = clnt_uid;
37389621fe1SMarcel Telka 	varg.arg_u.arg.areq.req_clnt_gid = clnt_gid;
37489621fe1SMarcel Telka 	varg.arg_u.arg.areq.req_clnt_gids.len = clnt_gids_cnt;
37589621fe1SMarcel Telka 	varg.arg_u.arg.areq.req_clnt_gids.val = (gid_t *)clnt_gids;
37689621fe1SMarcel Telka 
37789621fe1SMarcel Telka 	DTRACE_PROBE1(nfsserv__func__nfsauth__varg, varg_t *, &varg);
37889621fe1SMarcel Telka 
37989621fe1SMarcel Telka 	/*
38089621fe1SMarcel Telka 	 * Setup the XDR stream for encoding the arguments. Notice that
38189621fe1SMarcel Telka 	 * in addition to the args having variable fields (req_netid and
38289621fe1SMarcel Telka 	 * req_path), the argument data structure is itself versioned,
38389621fe1SMarcel Telka 	 * so we need to make sure we can size the arguments buffer
38489621fe1SMarcel Telka 	 * appropriately to encode all the args. If we can't get sizing
38589621fe1SMarcel Telka 	 * info _or_ properly encode the arguments, there's really no
38689621fe1SMarcel Telka 	 * point in continuting, so we fail the request.
38789621fe1SMarcel Telka 	 */
38889621fe1SMarcel Telka 	if ((absz = xdr_sizeof(xdr_varg, &varg)) == 0) {
38989621fe1SMarcel Telka 		*access = NFSAUTH_DENIED;
39089621fe1SMarcel Telka 		return (FALSE);
39189621fe1SMarcel Telka 	}
39289621fe1SMarcel Telka 
39389621fe1SMarcel Telka 	abuf = (caddr_t)kmem_alloc(absz, KM_SLEEP);
39489621fe1SMarcel Telka 	xdrmem_create(&xdrs, abuf, absz, XDR_ENCODE);
39589621fe1SMarcel Telka 	if (!xdr_varg(&xdrs, &varg)) {
39689621fe1SMarcel Telka 		XDR_DESTROY(&xdrs);
39789621fe1SMarcel Telka 		goto fail;
39889621fe1SMarcel Telka 	}
39989621fe1SMarcel Telka 	XDR_DESTROY(&xdrs);
40089621fe1SMarcel Telka 
40189621fe1SMarcel Telka 	/*
40289621fe1SMarcel Telka 	 * Prepare the door arguments
40389621fe1SMarcel Telka 	 *
40489621fe1SMarcel Telka 	 * We don't know the size of the message the daemon
40589621fe1SMarcel Telka 	 * will pass back to us.  By setting rbuf to NULL,
40689621fe1SMarcel Telka 	 * we force the door code to allocate a buf of the
40789621fe1SMarcel Telka 	 * appropriate size.  We must set rsize > 0, however,
40889621fe1SMarcel Telka 	 * else the door code acts as if no response was
40989621fe1SMarcel Telka 	 * expected and doesn't pass the data to us.
41089621fe1SMarcel Telka 	 */
41189621fe1SMarcel Telka 	da.data_ptr = (char *)abuf;
41289621fe1SMarcel Telka 	da.data_size = absz;
41389621fe1SMarcel Telka 	da.desc_ptr = NULL;
41489621fe1SMarcel Telka 	da.desc_num = 0;
41589621fe1SMarcel Telka 	da.rbuf = NULL;
41689621fe1SMarcel Telka 	da.rsize = 1;
41789621fe1SMarcel Telka 
4181cc55349Srmesta retry:
4191cc55349Srmesta 	mutex_enter(&mountd_lock);
4201cc55349Srmesta 	dh = mountd_dh;
42189621fe1SMarcel Telka 	if (dh != NULL)
4221cc55349Srmesta 		door_ki_hold(dh);
4231cc55349Srmesta 	mutex_exit(&mountd_lock);
4247c478bd9Sstevel@tonic-gate 
4251cc55349Srmesta 	if (dh == NULL) {
4261cc55349Srmesta 		/*
4271cc55349Srmesta 		 * The rendezvous point has not been established yet!
4281cc55349Srmesta 		 * This could mean that either mountd(1m) has not yet
4291cc55349Srmesta 		 * been started or that _this_ routine nuked the door
4301cc55349Srmesta 		 * handle after receiving an EINTR for a REVOKED door.
4311cc55349Srmesta 		 *
4321cc55349Srmesta 		 * Returning NFSAUTH_DROP will cause the NFS client
4331cc55349Srmesta 		 * to retransmit the request, so let's try to be more
4341cc55349Srmesta 		 * rescillient and attempt for ntries before we bail.
4351cc55349Srmesta 		 */
4361cc55349Srmesta 		if (++ntries % NFSAUTH_DR_TRYCNT) {
4371cc55349Srmesta 			delay(hz);
4381cc55349Srmesta 			goto retry;
4391cc55349Srmesta 		}
4403ccecb66SThomas Haynes 
44189621fe1SMarcel Telka 		kmem_free(abuf, absz);
44289621fe1SMarcel Telka 
4431cc55349Srmesta 		sys_log("nfsauth: mountd has not established door");
4443ccecb66SThomas Haynes 		*access = NFSAUTH_DROP;
4453ccecb66SThomas Haynes 		return (FALSE);
4467c478bd9Sstevel@tonic-gate 	}
4473ccecb66SThomas Haynes 
4481cc55349Srmesta 	ntries = 0;
4497c478bd9Sstevel@tonic-gate 
4507c478bd9Sstevel@tonic-gate 	/*
45189621fe1SMarcel Telka 	 * Now that we've got what we need, place the call.
4527c478bd9Sstevel@tonic-gate 	 */
453323a81d9Sjwadams 	switch (door_ki_upcall_limited(dh, &da, NULL, SIZE_MAX, 0)) {
4541cc55349Srmesta 	case 0:				/* Success */
45589621fe1SMarcel Telka 		door_ki_rele(dh);
45689621fe1SMarcel Telka 
45789621fe1SMarcel Telka 		if (da.data_ptr == NULL && da.data_size == 0) {
4581cc55349Srmesta 			/*
4591cc55349Srmesta 			 * The door_return that contained the data
4601cc55349Srmesta 			 * failed! We're here because of the 2nd
4611cc55349Srmesta 			 * door_return (w/o data) such that we can
4621cc55349Srmesta 			 * get control of the thread (and exit
4631cc55349Srmesta 			 * gracefully).
4641cc55349Srmesta 			 */
4651cc55349Srmesta 			DTRACE_PROBE1(nfsserv__func__nfsauth__door__nil,
4661cc55349Srmesta 			    door_arg_t *, &da);
4671cc55349Srmesta 			goto fail;
4681cc55349Srmesta 		}
46989621fe1SMarcel Telka 
4707c478bd9Sstevel@tonic-gate 		break;
4711cc55349Srmesta 
4721cc55349Srmesta 	case EAGAIN:
4731cc55349Srmesta 		/*
4741cc55349Srmesta 		 * Server out of resources; back off for a bit
4751cc55349Srmesta 		 */
4761cc55349Srmesta 		door_ki_rele(dh);
4771cc55349Srmesta 		delay(hz);
4781cc55349Srmesta 		goto retry;
4791cc55349Srmesta 		/* NOTREACHED */
4801cc55349Srmesta 
4811cc55349Srmesta 	case EINTR:
4821cc55349Srmesta 		if (!door_ki_info(dh, &di)) {
48389621fe1SMarcel Telka 			door_ki_rele(dh);
48489621fe1SMarcel Telka 
4851cc55349Srmesta 			if (di.di_attributes & DOOR_REVOKED) {
4861cc55349Srmesta 				/*
4871cc55349Srmesta 				 * The server barfed and revoked
4881cc55349Srmesta 				 * the (existing) door on us; we
4891cc55349Srmesta 				 * want to wait to give smf(5) a
4901cc55349Srmesta 				 * chance to restart mountd(1m)
4911cc55349Srmesta 				 * and establish a new door handle.
4921cc55349Srmesta 				 */
4931cc55349Srmesta 				mutex_enter(&mountd_lock);
49489621fe1SMarcel Telka 				if (dh == mountd_dh) {
49589621fe1SMarcel Telka 					door_ki_rele(mountd_dh);
4961cc55349Srmesta 					mountd_dh = NULL;
49789621fe1SMarcel Telka 				}
4981cc55349Srmesta 				mutex_exit(&mountd_lock);
4991cc55349Srmesta 				delay(hz);
5001cc55349Srmesta 				goto retry;
5011cc55349Srmesta 			}
5021cc55349Srmesta 			/*
5031cc55349Srmesta 			 * If the door was _not_ revoked on us,
5041cc55349Srmesta 			 * then more than likely we took an INTR,
5051cc55349Srmesta 			 * so we need to fail the operation.
5061cc55349Srmesta 			 */
5071cc55349Srmesta 			goto fail;
5081cc55349Srmesta 		}
5091cc55349Srmesta 		/*
5101cc55349Srmesta 		 * The only failure that can occur from getting
5111cc55349Srmesta 		 * the door info is EINVAL, so we let the code
5121cc55349Srmesta 		 * below handle it.
5131cc55349Srmesta 		 */
5141cc55349Srmesta 		/* FALLTHROUGH */
5151cc55349Srmesta 
5161cc55349Srmesta 	case EBADF:
5171cc55349Srmesta 	case EINVAL:
5187c478bd9Sstevel@tonic-gate 	default:
5197c478bd9Sstevel@tonic-gate 		/*
5201cc55349Srmesta 		 * If we have a stale door handle, give smf a last
5211cc55349Srmesta 		 * chance to start it by sleeping for a little bit.
5221cc55349Srmesta 		 * If we're still hosed, we'll fail the call.
5231cc55349Srmesta 		 *
5241cc55349Srmesta 		 * Since we're going to reacquire the door handle
5251cc55349Srmesta 		 * upon the retry, we opt to sleep for a bit and
5261cc55349Srmesta 		 * _not_ to clear mountd_dh. If mountd restarted
5271cc55349Srmesta 		 * and was able to set mountd_dh, we should see
5281cc55349Srmesta 		 * the new instance; if not, we won't get caught
5291cc55349Srmesta 		 * up in the retry/DELAY loop.
5307c478bd9Sstevel@tonic-gate 		 */
5311cc55349Srmesta 		door_ki_rele(dh);
5321cc55349Srmesta 		if (!last) {
5331cc55349Srmesta 			delay(hz);
5341cc55349Srmesta 			last++;
5351cc55349Srmesta 			goto retry;
5367c478bd9Sstevel@tonic-gate 		}
5371cc55349Srmesta 		sys_log("nfsauth: stale mountd door handle");
5381cc55349Srmesta 		goto fail;
5391cc55349Srmesta 	}
5401cc55349Srmesta 
54189621fe1SMarcel Telka 	ASSERT(da.rbuf != NULL);
54289621fe1SMarcel Telka 
5431cc55349Srmesta 	/*
5441cc55349Srmesta 	 * No door errors encountered; setup the XDR stream for decoding
5451cc55349Srmesta 	 * the results. If we fail to decode the results, we've got no
5461cc55349Srmesta 	 * other recourse than to fail the request.
5471cc55349Srmesta 	 */
54889621fe1SMarcel Telka 	xdrmem_create(&xdrs, da.rbuf, da.rsize, XDR_DECODE);
54989621fe1SMarcel Telka 	if (!xdr_nfsauth_res(&xdrs, &res)) {
55089621fe1SMarcel Telka 		xdr_free(xdr_nfsauth_res, (char *)&res);
55189621fe1SMarcel Telka 		XDR_DESTROY(&xdrs);
55289621fe1SMarcel Telka 		kmem_free(da.rbuf, da.rsize);
5531cc55349Srmesta 		goto fail;
55489621fe1SMarcel Telka 	}
55589621fe1SMarcel Telka 	XDR_DESTROY(&xdrs);
55689621fe1SMarcel Telka 	kmem_free(da.rbuf, da.rsize);
5571cc55349Srmesta 
5581cc55349Srmesta 	DTRACE_PROBE1(nfsserv__func__nfsauth__results, nfsauth_res_t *, &res);
5591cc55349Srmesta 	switch (res.stat) {
5601cc55349Srmesta 		case NFSAUTH_DR_OKAY:
5613ccecb66SThomas Haynes 			*access = res.ares.auth_perm;
5625cb0d679SMarcel Telka 			*srv_uid = res.ares.auth_srv_uid;
5635cb0d679SMarcel Telka 			*srv_gid = res.ares.auth_srv_gid;
56489621fe1SMarcel Telka 			*srv_gids_cnt = res.ares.auth_srv_gids.len;
56589621fe1SMarcel Telka 			*srv_gids = kmem_alloc(*srv_gids_cnt * sizeof (gid_t),
56689621fe1SMarcel Telka 			    KM_SLEEP);
56789621fe1SMarcel Telka 			bcopy(res.ares.auth_srv_gids.val, *srv_gids,
56889621fe1SMarcel Telka 			    *srv_gids_cnt * sizeof (gid_t));
5697c478bd9Sstevel@tonic-gate 			break;
5707c478bd9Sstevel@tonic-gate 
5711cc55349Srmesta 		case NFSAUTH_DR_EFAIL:
5721cc55349Srmesta 		case NFSAUTH_DR_DECERR:
5731cc55349Srmesta 		case NFSAUTH_DR_BADCMD:
5741cc55349Srmesta 		default:
57589621fe1SMarcel Telka 			xdr_free(xdr_nfsauth_res, (char *)&res);
5761cc55349Srmesta fail:
5773ccecb66SThomas Haynes 			*access = NFSAUTH_DENIED;
5781cc55349Srmesta 			kmem_free(abuf, absz);
5793ccecb66SThomas Haynes 			return (FALSE);
5801cc55349Srmesta 			/* NOTREACHED */
5817c478bd9Sstevel@tonic-gate 	}
5827c478bd9Sstevel@tonic-gate 
58389621fe1SMarcel Telka 	xdr_free(xdr_nfsauth_res, (char *)&res);
58489621fe1SMarcel Telka 	kmem_free(abuf, absz);
58589621fe1SMarcel Telka 
5863ccecb66SThomas Haynes 	return (TRUE);
5873ccecb66SThomas Haynes }
5883ccecb66SThomas Haynes 
5893ccecb66SThomas Haynes static void
5903ccecb66SThomas Haynes nfsauth_refresh_thread(void)
5913ccecb66SThomas Haynes {
5923ccecb66SThomas Haynes 	refreshq_exi_node_t	*ren;
5933ccecb66SThomas Haynes 	refreshq_auth_node_t	*ran;
5943ccecb66SThomas Haynes 
5953ccecb66SThomas Haynes 	struct exportinfo	*exi;
5963ccecb66SThomas Haynes 
5973ccecb66SThomas Haynes 	int			access;
5983ccecb66SThomas Haynes 	bool_t			retrieval;
5993ccecb66SThomas Haynes 
6003ccecb66SThomas Haynes 	callb_cpr_t		cprinfo;
6013ccecb66SThomas Haynes 
6023ccecb66SThomas Haynes 	CALLB_CPR_INIT(&cprinfo, &refreshq_lock, callb_generic_cpr,
6033ccecb66SThomas Haynes 	    "nfsauth_refresh");
6043ccecb66SThomas Haynes 
6053ccecb66SThomas Haynes 	for (;;) {
6063ccecb66SThomas Haynes 		mutex_enter(&refreshq_lock);
6073ccecb66SThomas Haynes 		if (refreshq_thread_state != REFRESHQ_THREAD_RUNNING) {
6083ccecb66SThomas Haynes 			/* Keep the hold on the lock! */
6093ccecb66SThomas Haynes 			break;
6103ccecb66SThomas Haynes 		}
6113ccecb66SThomas Haynes 
6123ccecb66SThomas Haynes 		ren = list_remove_head(&refreshq_queue);
6133ccecb66SThomas Haynes 		if (ren == NULL) {
6143ccecb66SThomas Haynes 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
6153ccecb66SThomas Haynes 			cv_wait(&refreshq_cv, &refreshq_lock);
6163ccecb66SThomas Haynes 			CALLB_CPR_SAFE_END(&cprinfo, &refreshq_lock);
6173ccecb66SThomas Haynes 			mutex_exit(&refreshq_lock);
6183ccecb66SThomas Haynes 			continue;
6193ccecb66SThomas Haynes 		}
6203ccecb66SThomas Haynes 		mutex_exit(&refreshq_lock);
6213ccecb66SThomas Haynes 
6223ccecb66SThomas Haynes 		exi = ren->ren_exi;
6233ccecb66SThomas Haynes 		ASSERT(exi != NULL);
6243ccecb66SThomas Haynes 
62535107df5SMarcel Telka 		/*
62635107df5SMarcel Telka 		 * Since the ren was removed from the refreshq_queue above,
62735107df5SMarcel Telka 		 * this is the only thread aware about the ren existence, so we
62835107df5SMarcel Telka 		 * have the exclusive ownership of it and we do not need to
62935107df5SMarcel Telka 		 * protect it by any lock.
63035107df5SMarcel Telka 		 */
6313ccecb66SThomas Haynes 		while ((ran = list_remove_head(&ren->ren_authlist))) {
63271da0c32SMarcel Telka 			uid_t uid;
63371da0c32SMarcel Telka 			gid_t gid;
63489621fe1SMarcel Telka 			uint_t ngids;
63589621fe1SMarcel Telka 			gid_t *gids;
63635107df5SMarcel Telka 			struct auth_cache *p = ran->ran_auth;
63771da0c32SMarcel Telka 			char *netid = ran->ran_netid;
63835107df5SMarcel Telka 
63935107df5SMarcel Telka 			ASSERT(p != NULL);
64071da0c32SMarcel Telka 			ASSERT(netid != NULL);
64135107df5SMarcel Telka 
64271da0c32SMarcel Telka 			kmem_free(ran, sizeof (refreshq_auth_node_t));
6433ccecb66SThomas Haynes 
6443ccecb66SThomas Haynes 			mutex_enter(&p->auth_lock);
6453ccecb66SThomas Haynes 
6463ccecb66SThomas Haynes 			/*
64771da0c32SMarcel Telka 			 * Once the entry goes INVALID, it can not change
64871da0c32SMarcel Telka 			 * state.
64971da0c32SMarcel Telka 			 *
65071da0c32SMarcel Telka 			 * No need to refresh entries also in a case we are
65171da0c32SMarcel Telka 			 * just shutting down.
65271da0c32SMarcel Telka 			 *
65371da0c32SMarcel Telka 			 * In general, there is no need to hold the
65471da0c32SMarcel Telka 			 * refreshq_lock to test the refreshq_thread_state.  We
65571da0c32SMarcel Telka 			 * do hold it at other places because there is some
65671da0c32SMarcel Telka 			 * related thread synchronization (or some other tasks)
65771da0c32SMarcel Telka 			 * close to the refreshq_thread_state check.
65871da0c32SMarcel Telka 			 *
65971da0c32SMarcel Telka 			 * The check for the refreshq_thread_state value here
66071da0c32SMarcel Telka 			 * is purely advisory to allow the faster
66171da0c32SMarcel Telka 			 * nfsauth_refresh_thread() shutdown.  In a case we
66271da0c32SMarcel Telka 			 * will miss such advisory, nothing catastrophic
66371da0c32SMarcel Telka 			 * happens: we will just spin longer here before the
66471da0c32SMarcel Telka 			 * shutdown.
6653ccecb66SThomas Haynes 			 */
66671da0c32SMarcel Telka 			if (p->auth_state == NFS_AUTH_INVALID ||
66771da0c32SMarcel Telka 			    refreshq_thread_state != REFRESHQ_THREAD_RUNNING) {
6683ccecb66SThomas Haynes 				mutex_exit(&p->auth_lock);
66971da0c32SMarcel Telka 
67071da0c32SMarcel Telka 				if (p->auth_state == NFS_AUTH_INVALID)
67171da0c32SMarcel Telka 					nfsauth_free_node(p);
67271da0c32SMarcel Telka 
67371da0c32SMarcel Telka 				strfree(netid);
6743ccecb66SThomas Haynes 
6753ccecb66SThomas Haynes 				continue;
6763ccecb66SThomas Haynes 			}
6773ccecb66SThomas Haynes 
67871da0c32SMarcel Telka 			/*
67971da0c32SMarcel Telka 			 * Make sure the state is valid.  Note that once we
68071da0c32SMarcel Telka 			 * change the state to NFS_AUTH_REFRESHING, no other
68171da0c32SMarcel Telka 			 * thread will be able to work on this entry.
68271da0c32SMarcel Telka 			 */
68371da0c32SMarcel Telka 			ASSERT(p->auth_state == NFS_AUTH_STALE);
68471da0c32SMarcel Telka 
6853ccecb66SThomas Haynes 			p->auth_state = NFS_AUTH_REFRESHING;
6863ccecb66SThomas Haynes 			mutex_exit(&p->auth_lock);
6873ccecb66SThomas Haynes 
6883ccecb66SThomas Haynes 			DTRACE_PROBE2(nfsauth__debug__cache__refresh,
6893ccecb66SThomas Haynes 			    struct exportinfo *, exi,
6903ccecb66SThomas Haynes 			    struct auth_cache *, p);
6913ccecb66SThomas Haynes 
6923ccecb66SThomas Haynes 			/*
6933ccecb66SThomas Haynes 			 * The first caching of the access rights
6943ccecb66SThomas Haynes 			 * is done with the netid pulled out of the
6953ccecb66SThomas Haynes 			 * request from the client. All subsequent
6963ccecb66SThomas Haynes 			 * users of the cache may or may not have
6973ccecb66SThomas Haynes 			 * the same netid. It doesn't matter. So
6983ccecb66SThomas Haynes 			 * when we refresh, we simply use the netid
6993ccecb66SThomas Haynes 			 * of the request which triggered the
7003ccecb66SThomas Haynes 			 * refresh attempt.
7013ccecb66SThomas Haynes 			 */
70271da0c32SMarcel Telka 			retrieval = nfsauth_retrieve(exi, netid,
70371da0c32SMarcel Telka 			    p->auth_flavor, &p->auth_clnt->authc_addr, &access,
7045cb0d679SMarcel Telka 			    p->auth_clnt_uid, p->auth_clnt_gid,
70571da0c32SMarcel Telka 			    p->auth_clnt_ngids, p->auth_clnt_gids, &uid, &gid,
70671da0c32SMarcel Telka 			    &ngids, &gids);
7073ccecb66SThomas Haynes 
7083ccecb66SThomas Haynes 			/*
7093ccecb66SThomas Haynes 			 * This can only be set in one other place
7103ccecb66SThomas Haynes 			 * and the state has to be NFS_AUTH_FRESH.
7113ccecb66SThomas Haynes 			 */
71271da0c32SMarcel Telka 			strfree(netid);
7133ccecb66SThomas Haynes 
71435107df5SMarcel Telka 			mutex_enter(&p->auth_lock);
71535107df5SMarcel Telka 			if (p->auth_state == NFS_AUTH_INVALID) {
71635107df5SMarcel Telka 				mutex_exit(&p->auth_lock);
71771da0c32SMarcel Telka 				nfsauth_free_node(p);
71889621fe1SMarcel Telka 				if (retrieval == TRUE)
71989621fe1SMarcel Telka 					kmem_free(gids, ngids * sizeof (gid_t));
72035107df5SMarcel Telka 			} else {
7213ccecb66SThomas Haynes 				/*
72235107df5SMarcel Telka 				 * If we got an error, do not reset the
7233ccecb66SThomas Haynes 				 * time. This will cause the next access
7243ccecb66SThomas Haynes 				 * check for the client to reschedule this
7253ccecb66SThomas Haynes 				 * node.
7263ccecb66SThomas Haynes 				 */
72735107df5SMarcel Telka 				if (retrieval == TRUE) {
7283ccecb66SThomas Haynes 					p->auth_access = access;
72989621fe1SMarcel Telka 
73071da0c32SMarcel Telka 					p->auth_srv_uid = uid;
73171da0c32SMarcel Telka 					p->auth_srv_gid = gid;
73289621fe1SMarcel Telka 					kmem_free(p->auth_srv_gids,
73389621fe1SMarcel Telka 					    p->auth_srv_ngids * sizeof (gid_t));
73489621fe1SMarcel Telka 					p->auth_srv_ngids = ngids;
73589621fe1SMarcel Telka 					p->auth_srv_gids = gids;
73689621fe1SMarcel Telka 
7373ccecb66SThomas Haynes 					p->auth_freshness = gethrestime_sec();
73835107df5SMarcel Telka 				}
7393ccecb66SThomas Haynes 				p->auth_state = NFS_AUTH_FRESH;
74071da0c32SMarcel Telka 
74171da0c32SMarcel Telka 				cv_broadcast(&p->auth_cv);
7423ccecb66SThomas Haynes 				mutex_exit(&p->auth_lock);
7433ccecb66SThomas Haynes 			}
7443ccecb66SThomas Haynes 		}
7453ccecb66SThomas Haynes 
7463ccecb66SThomas Haynes 		list_destroy(&ren->ren_authlist);
7473ccecb66SThomas Haynes 		exi_rele(ren->ren_exi);
7483ccecb66SThomas Haynes 		kmem_free(ren, sizeof (refreshq_exi_node_t));
7493ccecb66SThomas Haynes 	}
7503ccecb66SThomas Haynes 
7513ccecb66SThomas Haynes 	refreshq_thread_state = REFRESHQ_THREAD_HALTED;
7523ccecb66SThomas Haynes 	cv_broadcast(&refreshq_cv);
7533ccecb66SThomas Haynes 	CALLB_CPR_EXIT(&cprinfo);
7543ccecb66SThomas Haynes 	zthread_exit();
7553ccecb66SThomas Haynes }
7563ccecb66SThomas Haynes 
75771da0c32SMarcel Telka int
75871da0c32SMarcel Telka nfsauth_cache_clnt_compar(const void *v1, const void *v2)
75971da0c32SMarcel Telka {
76071da0c32SMarcel Telka 	int c;
76171da0c32SMarcel Telka 
76271da0c32SMarcel Telka 	const struct auth_cache_clnt *a1 = (const struct auth_cache_clnt *)v1;
76371da0c32SMarcel Telka 	const struct auth_cache_clnt *a2 = (const struct auth_cache_clnt *)v2;
76471da0c32SMarcel Telka 
76571da0c32SMarcel Telka 	if (a1->authc_addr.len < a2->authc_addr.len)
76671da0c32SMarcel Telka 		return (-1);
76771da0c32SMarcel Telka 	if (a1->authc_addr.len > a2->authc_addr.len)
76871da0c32SMarcel Telka 		return (1);
76971da0c32SMarcel Telka 
77071da0c32SMarcel Telka 	c = memcmp(a1->authc_addr.buf, a2->authc_addr.buf, a1->authc_addr.len);
77171da0c32SMarcel Telka 	if (c < 0)
77271da0c32SMarcel Telka 		return (-1);
77371da0c32SMarcel Telka 	if (c > 0)
77471da0c32SMarcel Telka 		return (1);
77571da0c32SMarcel Telka 
77671da0c32SMarcel Telka 	return (0);
77771da0c32SMarcel Telka }
77871da0c32SMarcel Telka 
77971da0c32SMarcel Telka static int
78071da0c32SMarcel Telka nfsauth_cache_compar(const void *v1, const void *v2)
78171da0c32SMarcel Telka {
78271da0c32SMarcel Telka 	const struct auth_cache *a1 = (const struct auth_cache *)v1;
78371da0c32SMarcel Telka 	const struct auth_cache *a2 = (const struct auth_cache *)v2;
78471da0c32SMarcel Telka 
78571da0c32SMarcel Telka 	if (a1->auth_flavor < a2->auth_flavor)
78671da0c32SMarcel Telka 		return (-1);
78771da0c32SMarcel Telka 	if (a1->auth_flavor > a2->auth_flavor)
78871da0c32SMarcel Telka 		return (1);
78971da0c32SMarcel Telka 
79071da0c32SMarcel Telka 	if (a1->auth_clnt_uid < a2->auth_clnt_uid)
79171da0c32SMarcel Telka 		return (-1);
79271da0c32SMarcel Telka 	if (a1->auth_clnt_uid > a2->auth_clnt_uid)
79371da0c32SMarcel Telka 		return (1);
79471da0c32SMarcel Telka 
79571da0c32SMarcel Telka 	if (a1->auth_clnt_gid < a2->auth_clnt_gid)
79671da0c32SMarcel Telka 		return (-1);
79771da0c32SMarcel Telka 	if (a1->auth_clnt_gid > a2->auth_clnt_gid)
79871da0c32SMarcel Telka 		return (1);
79971da0c32SMarcel Telka 
80071da0c32SMarcel Telka 	return (0);
80171da0c32SMarcel Telka }
80271da0c32SMarcel Telka 
8033ccecb66SThomas Haynes /*
8043ccecb66SThomas Haynes  * Get the access information from the cache or callup to the mountd
8053ccecb66SThomas Haynes  * to get and cache the access information in the kernel.
8063ccecb66SThomas Haynes  */
8075cb0d679SMarcel Telka static int
8085cb0d679SMarcel Telka nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor,
80989621fe1SMarcel Telka     cred_t *cr, uid_t *uid, gid_t *gid, uint_t *ngids, gid_t **gids)
8103ccecb66SThomas Haynes {
8110a4b0810SKaren Rochford 	struct netbuf		*taddrmask;
81271da0c32SMarcel Telka 	struct netbuf		addr;	/* temporary copy of client's address */
81371da0c32SMarcel Telka 	const struct netbuf	*claddr;
81471da0c32SMarcel Telka 	avl_tree_t		*tree;
81571da0c32SMarcel Telka 	struct auth_cache	ac;	/* used as a template for avl_find() */
81671da0c32SMarcel Telka 	struct auth_cache_clnt	*c;
81771da0c32SMarcel Telka 	struct auth_cache_clnt	acc;	/* used as a template for avl_find() */
81871da0c32SMarcel Telka 	struct auth_cache	*p = NULL;
8193ccecb66SThomas Haynes 	int			access;
8203ccecb66SThomas Haynes 
8215cb0d679SMarcel Telka 	uid_t			tmpuid;
8225cb0d679SMarcel Telka 	gid_t			tmpgid;
82389621fe1SMarcel Telka 	uint_t			tmpngids;
82489621fe1SMarcel Telka 	gid_t			*tmpgids;
8255cb0d679SMarcel Telka 
82671da0c32SMarcel Telka 	avl_index_t		where;	/* used for avl_find()/avl_insert() */
82771da0c32SMarcel Telka 
8285cb0d679SMarcel Telka 	ASSERT(cr != NULL);
8295cb0d679SMarcel Telka 
8303ccecb66SThomas Haynes 	/*
8313ccecb66SThomas Haynes 	 * Now check whether this client already
8323ccecb66SThomas Haynes 	 * has an entry for this flavor in the cache
8333ccecb66SThomas Haynes 	 * for this export.
8343ccecb66SThomas Haynes 	 * Get the caller's address, mask off the
8353ccecb66SThomas Haynes 	 * parts of the address that do not identify
8363ccecb66SThomas Haynes 	 * the host (port number, etc), and then hash
8373ccecb66SThomas Haynes 	 * it to find the chain of cache entries.
8383ccecb66SThomas Haynes 	 */
8393ccecb66SThomas Haynes 
8403ccecb66SThomas Haynes 	claddr = svc_getrpccaller(req->rq_xprt);
8413ccecb66SThomas Haynes 	addr = *claddr;
8427bbfa3eeSMarcel Telka 	addr.buf = kmem_alloc(addr.maxlen, KM_SLEEP);
8433ccecb66SThomas Haynes 	bcopy(claddr->buf, addr.buf, claddr->len);
84471da0c32SMarcel Telka 
8450a4b0810SKaren Rochford 	SVC_GETADDRMASK(req->rq_xprt, SVC_TATTR_ADDRMASK, (void **)&taddrmask);
8460a4b0810SKaren Rochford 	ASSERT(taddrmask != NULL);
8470a4b0810SKaren Rochford 	addrmask(&addr, taddrmask);
8483ccecb66SThomas Haynes 
84971da0c32SMarcel Telka 	ac.auth_flavor = flavor;
85071da0c32SMarcel Telka 	ac.auth_clnt_uid = crgetuid(cr);
85171da0c32SMarcel Telka 	ac.auth_clnt_gid = crgetgid(cr);
85271da0c32SMarcel Telka 
85371da0c32SMarcel Telka 	acc.authc_addr = addr;
85471da0c32SMarcel Telka 
85571da0c32SMarcel Telka 	tree = exi->exi_cache[hash(&addr)];
85671da0c32SMarcel Telka 
8573ccecb66SThomas Haynes 	rw_enter(&exi->exi_cache_lock, RW_READER);
85871da0c32SMarcel Telka 	c = (struct auth_cache_clnt *)avl_find(tree, &acc, NULL);
85971da0c32SMarcel Telka 
86071da0c32SMarcel Telka 	if (c == NULL) {
86171da0c32SMarcel Telka 		struct auth_cache_clnt *nc;
86271da0c32SMarcel Telka 
86371da0c32SMarcel Telka 		rw_exit(&exi->exi_cache_lock);
86471da0c32SMarcel Telka 
86571da0c32SMarcel Telka 		nc = kmem_alloc(sizeof (*nc), KM_NOSLEEP | KM_NORMALPRI);
86671da0c32SMarcel Telka 		if (nc == NULL)
86771da0c32SMarcel Telka 			goto retrieve;
86871da0c32SMarcel Telka 
86971da0c32SMarcel Telka 		/*
87071da0c32SMarcel Telka 		 * Initialize the new auth_cache_clnt
87171da0c32SMarcel Telka 		 */
87271da0c32SMarcel Telka 		nc->authc_addr = addr;
8737bbfa3eeSMarcel Telka 		nc->authc_addr.buf = kmem_alloc(addr.maxlen,
87471da0c32SMarcel Telka 		    KM_NOSLEEP | KM_NORMALPRI);
8757bbfa3eeSMarcel Telka 		if (addr.maxlen != 0 && nc->authc_addr.buf == NULL) {
87671da0c32SMarcel Telka 			kmem_free(nc, sizeof (*nc));
87771da0c32SMarcel Telka 			goto retrieve;
87871da0c32SMarcel Telka 		}
87971da0c32SMarcel Telka 		bcopy(addr.buf, nc->authc_addr.buf, addr.len);
88071da0c32SMarcel Telka 		rw_init(&nc->authc_lock, NULL, RW_DEFAULT, NULL);
88171da0c32SMarcel Telka 		avl_create(&nc->authc_tree, nfsauth_cache_compar,
88271da0c32SMarcel Telka 		    sizeof (struct auth_cache),
88371da0c32SMarcel Telka 		    offsetof(struct auth_cache, auth_link));
88471da0c32SMarcel Telka 
88571da0c32SMarcel Telka 		rw_enter(&exi->exi_cache_lock, RW_WRITER);
88671da0c32SMarcel Telka 		c = (struct auth_cache_clnt *)avl_find(tree, &acc, &where);
88771da0c32SMarcel Telka 		if (c == NULL) {
88871da0c32SMarcel Telka 			avl_insert(tree, nc, where);
88971da0c32SMarcel Telka 			rw_downgrade(&exi->exi_cache_lock);
89071da0c32SMarcel Telka 			c = nc;
89171da0c32SMarcel Telka 		} else {
89271da0c32SMarcel Telka 			rw_downgrade(&exi->exi_cache_lock);
89371da0c32SMarcel Telka 
89471da0c32SMarcel Telka 			avl_destroy(&nc->authc_tree);
89571da0c32SMarcel Telka 			rw_destroy(&nc->authc_lock);
8967bbfa3eeSMarcel Telka 			kmem_free(nc->authc_addr.buf, nc->authc_addr.maxlen);
89771da0c32SMarcel Telka 			kmem_free(nc, sizeof (*nc));
89871da0c32SMarcel Telka 		}
8993ccecb66SThomas Haynes 	}
9003ccecb66SThomas Haynes 
90171da0c32SMarcel Telka 	ASSERT(c != NULL);
90271da0c32SMarcel Telka 
90371da0c32SMarcel Telka 	rw_enter(&c->authc_lock, RW_READER);
90471da0c32SMarcel Telka 	p = (struct auth_cache *)avl_find(&c->authc_tree, &ac, NULL);
90571da0c32SMarcel Telka 
90671da0c32SMarcel Telka 	if (p == NULL) {
90771da0c32SMarcel Telka 		struct auth_cache *np;
90871da0c32SMarcel Telka 
90971da0c32SMarcel Telka 		rw_exit(&c->authc_lock);
91071da0c32SMarcel Telka 
91171da0c32SMarcel Telka 		np = kmem_cache_alloc(exi_cache_handle,
91271da0c32SMarcel Telka 		    KM_NOSLEEP | KM_NORMALPRI);
91371da0c32SMarcel Telka 		if (np == NULL) {
91471da0c32SMarcel Telka 			rw_exit(&exi->exi_cache_lock);
91571da0c32SMarcel Telka 			goto retrieve;
91671da0c32SMarcel Telka 		}
91771da0c32SMarcel Telka 
91889621fe1SMarcel Telka 		/*
91971da0c32SMarcel Telka 		 * Initialize the new auth_cache
92071da0c32SMarcel Telka 		 */
92171da0c32SMarcel Telka 		np->auth_clnt = c;
92271da0c32SMarcel Telka 		np->auth_flavor = flavor;
92371da0c32SMarcel Telka 		np->auth_clnt_uid = crgetuid(cr);
92471da0c32SMarcel Telka 		np->auth_clnt_gid = crgetgid(cr);
92571da0c32SMarcel Telka 		np->auth_clnt_ngids = 0;
92671da0c32SMarcel Telka 		np->auth_clnt_gids = NULL;
92771da0c32SMarcel Telka 		np->auth_srv_ngids = 0;
92871da0c32SMarcel Telka 		np->auth_srv_gids = NULL;
92971da0c32SMarcel Telka 		np->auth_time = np->auth_freshness = gethrestime_sec();
93071da0c32SMarcel Telka 		np->auth_state = NFS_AUTH_NEW;
93171da0c32SMarcel Telka 		mutex_init(&np->auth_lock, NULL, MUTEX_DEFAULT, NULL);
93271da0c32SMarcel Telka 		cv_init(&np->auth_cv, NULL, CV_DEFAULT, NULL);
93371da0c32SMarcel Telka 
93471da0c32SMarcel Telka 		rw_enter(&c->authc_lock, RW_WRITER);
93571da0c32SMarcel Telka 		rw_exit(&exi->exi_cache_lock);
93671da0c32SMarcel Telka 
93771da0c32SMarcel Telka 		p = (struct auth_cache *)avl_find(&c->authc_tree, &ac, &where);
93871da0c32SMarcel Telka 		if (p == NULL) {
93971da0c32SMarcel Telka 			avl_insert(&c->authc_tree, np, where);
94071da0c32SMarcel Telka 			rw_downgrade(&c->authc_lock);
94171da0c32SMarcel Telka 			p = np;
94271da0c32SMarcel Telka 		} else {
94371da0c32SMarcel Telka 			rw_downgrade(&c->authc_lock);
94471da0c32SMarcel Telka 
94571da0c32SMarcel Telka 			cv_destroy(&np->auth_cv);
94671da0c32SMarcel Telka 			mutex_destroy(&np->auth_lock);
94771da0c32SMarcel Telka 			kmem_cache_free(exi_cache_handle, np);
94871da0c32SMarcel Telka 		}
94971da0c32SMarcel Telka 	} else {
95071da0c32SMarcel Telka 		rw_exit(&exi->exi_cache_lock);
95171da0c32SMarcel Telka 	}
95271da0c32SMarcel Telka 
95371da0c32SMarcel Telka 	mutex_enter(&p->auth_lock);
95471da0c32SMarcel Telka 	rw_exit(&c->authc_lock);
95571da0c32SMarcel Telka 
95671da0c32SMarcel Telka wait:
95771da0c32SMarcel Telka 	/*
95871da0c32SMarcel Telka 	 * If the entry is in the WAITING state then some other thread is just
95971da0c32SMarcel Telka 	 * retrieving the required info.  The entry was either NEW, or the list
96071da0c32SMarcel Telka 	 * of client's supplemental groups is going to be changed (either by
96171da0c32SMarcel Telka 	 * this thread, or by some other thread).  We need to wait until the
96271da0c32SMarcel Telka 	 * nfsauth_retrieve() is done.
96371da0c32SMarcel Telka 	 */
96471da0c32SMarcel Telka 	while (p->auth_state == NFS_AUTH_WAITING)
96571da0c32SMarcel Telka 		cv_wait(&p->auth_cv, &p->auth_lock);
96671da0c32SMarcel Telka 
96771da0c32SMarcel Telka 	/*
96871da0c32SMarcel Telka 	 * Here the entry cannot be in WAITING or INVALID state.
96971da0c32SMarcel Telka 	 */
97071da0c32SMarcel Telka 	ASSERT(p->auth_state != NFS_AUTH_WAITING);
97171da0c32SMarcel Telka 	ASSERT(p->auth_state != NFS_AUTH_INVALID);
97271da0c32SMarcel Telka 
97371da0c32SMarcel Telka 	/*
97471da0c32SMarcel Telka 	 * In a case the client's list of supplemental groups changed (or, the
97571da0c32SMarcel Telka 	 * list is not initialized yet) we need to (re)allocate it and make
97671da0c32SMarcel Telka 	 * sure the auth_cache entry is (re)retrieved.
97789621fe1SMarcel Telka 	 */
97889621fe1SMarcel Telka 	if (p->auth_clnt_ngids != crgetngroups(cr) ||
97989621fe1SMarcel Telka 	    bcmp(p->auth_clnt_gids, crgetgroups(cr),
98071da0c32SMarcel Telka 	    p->auth_clnt_ngids * sizeof (gid_t)) != 0) {
98189621fe1SMarcel Telka 
9823d1d816fSMarcel Telka 		/*
98371da0c32SMarcel Telka 		 * If the refresh thread is just working on this entry then
98471da0c32SMarcel Telka 		 * wait for it so we do not modify the list of supplemental
98571da0c32SMarcel Telka 		 * groups in the middle of its processing.
9863d1d816fSMarcel Telka 		 */
98771da0c32SMarcel Telka 		if (p->auth_state == NFS_AUTH_REFRESHING) {
98871da0c32SMarcel Telka 			p->auth_state = NFS_AUTH_WAITING;
98971da0c32SMarcel Telka 			goto wait;
99089621fe1SMarcel Telka 		}
99189621fe1SMarcel Telka 
9923d1d816fSMarcel Telka 		/*
99371da0c32SMarcel Telka 		 * We won't modify (and use) the STALE entries here since they
99471da0c32SMarcel Telka 		 * are already in the refreshq_queue list.  Such entries will
99571da0c32SMarcel Telka 		 * be updated later.
9963d1d816fSMarcel Telka 		 */
99771da0c32SMarcel Telka 		if (p->auth_state == NFS_AUTH_STALE) {
9983d1d816fSMarcel Telka 			mutex_exit(&p->auth_lock);
99989621fe1SMarcel Telka 
100071da0c32SMarcel Telka 			p = NULL;
100171da0c32SMarcel Telka 
100271da0c32SMarcel Telka 			goto retrieve;
100389621fe1SMarcel Telka 		}
100489621fe1SMarcel Telka 
100571da0c32SMarcel Telka 		p->auth_state = NFS_AUTH_NEW;
100671da0c32SMarcel Telka 
10073d1d816fSMarcel Telka 		/*
100871da0c32SMarcel Telka 		 * If the number of supplemental groups differ, we need to
100971da0c32SMarcel Telka 		 * reallocate first.
10103d1d816fSMarcel Telka 		 */
101171da0c32SMarcel Telka 		if (p->auth_clnt_ngids != crgetngroups(cr)) {
101271da0c32SMarcel Telka 			kmem_free(p->auth_clnt_gids,
101371da0c32SMarcel Telka 			    p->auth_clnt_ngids * sizeof (gid_t));
101471da0c32SMarcel Telka 
101571da0c32SMarcel Telka 			p->auth_clnt_ngids = crgetngroups(cr);
101671da0c32SMarcel Telka 			p->auth_clnt_gids = kmem_alloc(
101771da0c32SMarcel Telka 			    p->auth_clnt_ngids * sizeof (gid_t),
101871da0c32SMarcel Telka 			    KM_NOSLEEP | KM_NORMALPRI);
101971da0c32SMarcel Telka 
102071da0c32SMarcel Telka 			/*
102171da0c32SMarcel Telka 			 * If we failed to preallocate the memory for
102271da0c32SMarcel Telka 			 * supplemental groups, we won't cache the retrieved
102371da0c32SMarcel Telka 			 * data.
102471da0c32SMarcel Telka 			 */
102571da0c32SMarcel Telka 			if (p->auth_clnt_ngids != 0 &&
102609818e88SMarcel Telka 			    p->auth_clnt_gids == NULL) {
102771da0c32SMarcel Telka 				p->auth_clnt_ngids = 0;
102871da0c32SMarcel Telka 				mutex_exit(&p->auth_lock);
102971da0c32SMarcel Telka 
103071da0c32SMarcel Telka 				p = NULL;
10313d1d816fSMarcel Telka 
103289621fe1SMarcel Telka 				goto retrieve;
103389621fe1SMarcel Telka 			}
103409818e88SMarcel Telka 		}
10353d1d816fSMarcel Telka 
10363d1d816fSMarcel Telka 		/*
103771da0c32SMarcel Telka 		 * Fill the client's supplemental groups.
10383d1d816fSMarcel Telka 		 */
103971da0c32SMarcel Telka 		bcopy(crgetgroups(cr), p->auth_clnt_gids,
104071da0c32SMarcel Telka 		    p->auth_clnt_ngids * sizeof (gid_t));
104171da0c32SMarcel Telka 	}
104289621fe1SMarcel Telka 
104371da0c32SMarcel Telka 	/*
104471da0c32SMarcel Telka 	 * If the cache entry is not valid yet, we need to retrieve the
104571da0c32SMarcel Telka 	 * info ourselves.
104671da0c32SMarcel Telka 	 */
104771da0c32SMarcel Telka 	if (p->auth_state == NFS_AUTH_NEW) {
104871da0c32SMarcel Telka 		bool_t res;
104971da0c32SMarcel Telka 		/*
105071da0c32SMarcel Telka 		 * NFS_AUTH_NEW is the default output auth_state value in a
105171da0c32SMarcel Telka 		 * case we failed somewhere below.
105271da0c32SMarcel Telka 		 */
105371da0c32SMarcel Telka 		auth_state_t state = NFS_AUTH_NEW;
105471da0c32SMarcel Telka 
105571da0c32SMarcel Telka 		p->auth_state = NFS_AUTH_WAITING;
105671da0c32SMarcel Telka 		mutex_exit(&p->auth_lock);
10577bbfa3eeSMarcel Telka 		kmem_free(addr.buf, addr.maxlen);
105871da0c32SMarcel Telka 		addr = p->auth_clnt->authc_addr;
105971da0c32SMarcel Telka 
106071da0c32SMarcel Telka 		atomic_inc_uint(&nfsauth_cache_miss);
106171da0c32SMarcel Telka 
106271da0c32SMarcel Telka 		res = nfsauth_retrieve(exi, svc_getnetid(req->rq_xprt), flavor,
106371da0c32SMarcel Telka 		    &addr, &access, crgetuid(cr), crgetgid(cr),
106471da0c32SMarcel Telka 		    crgetngroups(cr), crgetgroups(cr), &tmpuid, &tmpgid,
106571da0c32SMarcel Telka 		    &tmpngids, &tmpgids);
106671da0c32SMarcel Telka 
106771da0c32SMarcel Telka 		p->auth_access = access;
106871da0c32SMarcel Telka 		p->auth_time = p->auth_freshness = gethrestime_sec();
106971da0c32SMarcel Telka 
107071da0c32SMarcel Telka 		if (res == TRUE) {
107171da0c32SMarcel Telka 			if (uid != NULL)
107271da0c32SMarcel Telka 				*uid = tmpuid;
107371da0c32SMarcel Telka 			if (gid != NULL)
107471da0c32SMarcel Telka 				*gid = tmpgid;
107571da0c32SMarcel Telka 			if (ngids != NULL && gids != NULL) {
107671da0c32SMarcel Telka 				*ngids = tmpngids;
107771da0c32SMarcel Telka 				*gids = tmpgids;
107871da0c32SMarcel Telka 
107971da0c32SMarcel Telka 				/*
108071da0c32SMarcel Telka 				 * We need a copy of gids for the
108171da0c32SMarcel Telka 				 * auth_cache entry
108271da0c32SMarcel Telka 				 */
108371da0c32SMarcel Telka 				tmpgids = kmem_alloc(tmpngids * sizeof (gid_t),
108471da0c32SMarcel Telka 				    KM_NOSLEEP | KM_NORMALPRI);
108571da0c32SMarcel Telka 				if (tmpgids != NULL)
108671da0c32SMarcel Telka 					bcopy(*gids, tmpgids,
108771da0c32SMarcel Telka 					    tmpngids * sizeof (gid_t));
108871da0c32SMarcel Telka 			}
108971da0c32SMarcel Telka 
109071da0c32SMarcel Telka 			if (tmpgids != NULL || tmpngids == 0) {
109171da0c32SMarcel Telka 				p->auth_srv_uid = tmpuid;
109271da0c32SMarcel Telka 				p->auth_srv_gid = tmpgid;
109371da0c32SMarcel Telka 				p->auth_srv_ngids = tmpngids;
109471da0c32SMarcel Telka 				p->auth_srv_gids = tmpgids;
109571da0c32SMarcel Telka 
109671da0c32SMarcel Telka 				state = NFS_AUTH_FRESH;
109771da0c32SMarcel Telka 			}
109871da0c32SMarcel Telka 		}
109971da0c32SMarcel Telka 
110071da0c32SMarcel Telka 		/*
110171da0c32SMarcel Telka 		 * Set the auth_state and notify waiters.
110271da0c32SMarcel Telka 		 */
110371da0c32SMarcel Telka 		mutex_enter(&p->auth_lock);
110471da0c32SMarcel Telka 		p->auth_state = state;
110571da0c32SMarcel Telka 		cv_broadcast(&p->auth_cv);
110671da0c32SMarcel Telka 		mutex_exit(&p->auth_lock);
110771da0c32SMarcel Telka 	} else {
110871da0c32SMarcel Telka 		uint_t nach;
110971da0c32SMarcel Telka 		time_t refresh;
11103ccecb66SThomas Haynes 
11113ccecb66SThomas Haynes 		refresh = gethrestime_sec() - p->auth_freshness;
11123ccecb66SThomas Haynes 
111371da0c32SMarcel Telka 		p->auth_time = gethrestime_sec();
111471da0c32SMarcel Telka 
111571da0c32SMarcel Telka 		if (uid != NULL)
111671da0c32SMarcel Telka 			*uid = p->auth_srv_uid;
111771da0c32SMarcel Telka 		if (gid != NULL)
111871da0c32SMarcel Telka 			*gid = p->auth_srv_gid;
111971da0c32SMarcel Telka 		if (ngids != NULL && gids != NULL) {
112071da0c32SMarcel Telka 			*ngids = p->auth_srv_ngids;
112171da0c32SMarcel Telka 			*gids = kmem_alloc(*ngids * sizeof (gid_t), KM_SLEEP);
112271da0c32SMarcel Telka 			bcopy(p->auth_srv_gids, *gids, *ngids * sizeof (gid_t));
112371da0c32SMarcel Telka 		}
112471da0c32SMarcel Telka 
112571da0c32SMarcel Telka 		access = p->auth_access;
112671da0c32SMarcel Telka 
11273ccecb66SThomas Haynes 		if ((refresh > NFSAUTH_CACHE_REFRESH) &&
11283ccecb66SThomas Haynes 		    p->auth_state == NFS_AUTH_FRESH) {
112971da0c32SMarcel Telka 			refreshq_auth_node_t *ran;
113071da0c32SMarcel Telka 			uint_t nacr;
113171da0c32SMarcel Telka 
11323ccecb66SThomas Haynes 			p->auth_state = NFS_AUTH_STALE;
11333ccecb66SThomas Haynes 			mutex_exit(&p->auth_lock);
11343ccecb66SThomas Haynes 
113571da0c32SMarcel Telka 			nacr = atomic_inc_uint_nv(&nfsauth_cache_refresh);
11363ccecb66SThomas Haynes 			DTRACE_PROBE3(nfsauth__debug__cache__stale,
11373ccecb66SThomas Haynes 			    struct exportinfo *, exi,
11383ccecb66SThomas Haynes 			    struct auth_cache *, p,
113971da0c32SMarcel Telka 			    uint_t, nacr);
11403ccecb66SThomas Haynes 
11413ccecb66SThomas Haynes 			ran = kmem_alloc(sizeof (refreshq_auth_node_t),
11423ccecb66SThomas Haynes 			    KM_SLEEP);
11433ccecb66SThomas Haynes 			ran->ran_auth = p;
114471da0c32SMarcel Telka 			ran->ran_netid = strdup(svc_getnetid(req->rq_xprt));
11453ccecb66SThomas Haynes 
11463ccecb66SThomas Haynes 			mutex_enter(&refreshq_lock);
11473ccecb66SThomas Haynes 			/*
11483ccecb66SThomas Haynes 			 * We should not add a work queue
11493ccecb66SThomas Haynes 			 * item if the thread is not
11503ccecb66SThomas Haynes 			 * accepting them.
11513ccecb66SThomas Haynes 			 */
11523ccecb66SThomas Haynes 			if (refreshq_thread_state == REFRESHQ_THREAD_RUNNING) {
115371da0c32SMarcel Telka 				refreshq_exi_node_t *ren;
115471da0c32SMarcel Telka 
11553ccecb66SThomas Haynes 				/*
11563ccecb66SThomas Haynes 				 * Is there an existing exi_list?
11573ccecb66SThomas Haynes 				 */
11583ccecb66SThomas Haynes 				for (ren = list_head(&refreshq_queue);
11593ccecb66SThomas Haynes 				    ren != NULL;
11603ccecb66SThomas Haynes 				    ren = list_next(&refreshq_queue, ren)) {
11613ccecb66SThomas Haynes 					if (ren->ren_exi == exi) {
11623ccecb66SThomas Haynes 						list_insert_tail(
11633ccecb66SThomas Haynes 						    &ren->ren_authlist, ran);
11643ccecb66SThomas Haynes 						break;
11653ccecb66SThomas Haynes 					}
11663ccecb66SThomas Haynes 				}
11673ccecb66SThomas Haynes 
11683ccecb66SThomas Haynes 				if (ren == NULL) {
11693ccecb66SThomas Haynes 					ren = kmem_alloc(
11703ccecb66SThomas Haynes 					    sizeof (refreshq_exi_node_t),
11713ccecb66SThomas Haynes 					    KM_SLEEP);
11723ccecb66SThomas Haynes 
11733ccecb66SThomas Haynes 					exi_hold(exi);
11743ccecb66SThomas Haynes 					ren->ren_exi = exi;
11753ccecb66SThomas Haynes 
11763ccecb66SThomas Haynes 					list_create(&ren->ren_authlist,
11773ccecb66SThomas Haynes 					    sizeof (refreshq_auth_node_t),
11783ccecb66SThomas Haynes 					    offsetof(refreshq_auth_node_t,
11793ccecb66SThomas Haynes 					    ran_node));
11803ccecb66SThomas Haynes 
11813ccecb66SThomas Haynes 					list_insert_tail(&ren->ren_authlist,
11823ccecb66SThomas Haynes 					    ran);
11833ccecb66SThomas Haynes 					list_insert_tail(&refreshq_queue, ren);
11843ccecb66SThomas Haynes 				}
11853ccecb66SThomas Haynes 
11863ccecb66SThomas Haynes 				cv_broadcast(&refreshq_cv);
11873ccecb66SThomas Haynes 			} else {
118871da0c32SMarcel Telka 				strfree(ran->ran_netid);
11893ccecb66SThomas Haynes 				kmem_free(ran, sizeof (refreshq_auth_node_t));
11903ccecb66SThomas Haynes 			}
11913ccecb66SThomas Haynes 
11923ccecb66SThomas Haynes 			mutex_exit(&refreshq_lock);
11933ccecb66SThomas Haynes 		} else {
11943ccecb66SThomas Haynes 			mutex_exit(&p->auth_lock);
11953ccecb66SThomas Haynes 		}
11963ccecb66SThomas Haynes 
119771da0c32SMarcel Telka 		nach = atomic_inc_uint_nv(&nfsauth_cache_hit);
119871da0c32SMarcel Telka 		DTRACE_PROBE2(nfsauth__debug__cache__hit,
119971da0c32SMarcel Telka 		    uint_t, nach,
120071da0c32SMarcel Telka 		    time_t, refresh);
12015cb0d679SMarcel Telka 
12027bbfa3eeSMarcel Telka 		kmem_free(addr.buf, addr.maxlen);
120371da0c32SMarcel Telka 	}
12043ccecb66SThomas Haynes 
12053ccecb66SThomas Haynes 	return (access);
12063ccecb66SThomas Haynes 
120789621fe1SMarcel Telka retrieve:
120871da0c32SMarcel Telka 	/*
120971da0c32SMarcel Telka 	 * Retrieve the required data without caching.
121071da0c32SMarcel Telka 	 */
12113ccecb66SThomas Haynes 
121271da0c32SMarcel Telka 	ASSERT(p == NULL);
12133ccecb66SThomas Haynes 
121471da0c32SMarcel Telka 	atomic_inc_uint(&nfsauth_cache_miss);
121571da0c32SMarcel Telka 
121671da0c32SMarcel Telka 	if (nfsauth_retrieve(exi, svc_getnetid(req->rq_xprt), flavor, &addr,
121771da0c32SMarcel Telka 	    &access, crgetuid(cr), crgetgid(cr), crgetngroups(cr),
121889621fe1SMarcel Telka 	    crgetgroups(cr), &tmpuid, &tmpgid, &tmpngids, &tmpgids)) {
12195cb0d679SMarcel Telka 		if (uid != NULL)
12205cb0d679SMarcel Telka 			*uid = tmpuid;
12215cb0d679SMarcel Telka 		if (gid != NULL)
12225cb0d679SMarcel Telka 			*gid = tmpgid;
122389621fe1SMarcel Telka 		if (ngids != NULL && gids != NULL) {
122489621fe1SMarcel Telka 			*ngids = tmpngids;
122589621fe1SMarcel Telka 			*gids = tmpgids;
12267c478bd9Sstevel@tonic-gate 		} else {
122789621fe1SMarcel Telka 			kmem_free(tmpgids, tmpngids * sizeof (gid_t));
122889621fe1SMarcel Telka 		}
12297c478bd9Sstevel@tonic-gate 	}
12307c478bd9Sstevel@tonic-gate 
12317bbfa3eeSMarcel Telka 	kmem_free(addr.buf, addr.maxlen);
123271da0c32SMarcel Telka 
12337c478bd9Sstevel@tonic-gate 	return (access);
12347c478bd9Sstevel@tonic-gate }
12357c478bd9Sstevel@tonic-gate 
12367c478bd9Sstevel@tonic-gate /*
12377c478bd9Sstevel@tonic-gate  * Check if the requesting client has access to the filesystem with
12387c478bd9Sstevel@tonic-gate  * a given nfs flavor number which is an explicitly shared flavor.
12397c478bd9Sstevel@tonic-gate  */
12407c478bd9Sstevel@tonic-gate int
12417c478bd9Sstevel@tonic-gate nfsauth4_secinfo_access(struct exportinfo *exi, struct svc_req *req,
12425cb0d679SMarcel Telka 			int flavor, int perm, cred_t *cr)
12437c478bd9Sstevel@tonic-gate {
12447c478bd9Sstevel@tonic-gate 	int access;
12457c478bd9Sstevel@tonic-gate 
12467c478bd9Sstevel@tonic-gate 	if (! (perm & M_4SEC_EXPORTED)) {
12477c478bd9Sstevel@tonic-gate 		return (NFSAUTH_DENIED);
12487c478bd9Sstevel@tonic-gate 	}
12497c478bd9Sstevel@tonic-gate 
12507c478bd9Sstevel@tonic-gate 	/*
12517c478bd9Sstevel@tonic-gate 	 * Optimize if there are no lists
12527c478bd9Sstevel@tonic-gate 	 */
12535cb0d679SMarcel Telka 	if ((perm & (M_ROOT | M_NONE | M_MAP)) == 0) {
12547c478bd9Sstevel@tonic-gate 		perm &= ~M_4SEC_EXPORTED;
12557c478bd9Sstevel@tonic-gate 		if (perm == M_RO)
12567c478bd9Sstevel@tonic-gate 			return (NFSAUTH_RO);
12577c478bd9Sstevel@tonic-gate 		if (perm == M_RW)
12587c478bd9Sstevel@tonic-gate 			return (NFSAUTH_RW);
12597c478bd9Sstevel@tonic-gate 	}
12607c478bd9Sstevel@tonic-gate 
126189621fe1SMarcel Telka 	access = nfsauth_cache_get(exi, req, flavor, cr, NULL, NULL, NULL,
126289621fe1SMarcel Telka 	    NULL);
12637c478bd9Sstevel@tonic-gate 
12647c478bd9Sstevel@tonic-gate 	return (access);
12657c478bd9Sstevel@tonic-gate }
12667c478bd9Sstevel@tonic-gate 
12677c478bd9Sstevel@tonic-gate int
12685cb0d679SMarcel Telka nfsauth_access(struct exportinfo *exi, struct svc_req *req, cred_t *cr,
126989621fe1SMarcel Telka     uid_t *uid, gid_t *gid, uint_t *ngids, gid_t **gids)
12707c478bd9Sstevel@tonic-gate {
12717c478bd9Sstevel@tonic-gate 	int access, mapaccess;
12727c478bd9Sstevel@tonic-gate 	struct secinfo *sp;
12737c478bd9Sstevel@tonic-gate 	int i, flavor, perm;
12747c478bd9Sstevel@tonic-gate 	int authnone_entry = -1;
12757c478bd9Sstevel@tonic-gate 
12767c478bd9Sstevel@tonic-gate 	/*
12779e835c76SMarcel Telka 	 * By default root is mapped to anonymous user.
12789e835c76SMarcel Telka 	 * This might get overriden later in nfsauth_cache_get().
12799e835c76SMarcel Telka 	 */
12809e835c76SMarcel Telka 	if (crgetuid(cr) == 0) {
12819e835c76SMarcel Telka 		if (uid != NULL)
12829e835c76SMarcel Telka 			*uid = exi->exi_export.ex_anon;
12839e835c76SMarcel Telka 		if (gid != NULL)
12849e835c76SMarcel Telka 			*gid = exi->exi_export.ex_anon;
12859e835c76SMarcel Telka 	} else {
12869e835c76SMarcel Telka 		if (uid != NULL)
12879e835c76SMarcel Telka 			*uid = crgetuid(cr);
12889e835c76SMarcel Telka 		if (gid != NULL)
12899e835c76SMarcel Telka 			*gid = crgetgid(cr);
12909e835c76SMarcel Telka 	}
12919e835c76SMarcel Telka 
12929e835c76SMarcel Telka 	if (ngids != NULL)
12939e835c76SMarcel Telka 		*ngids = 0;
12949e835c76SMarcel Telka 	if (gids != NULL)
12959e835c76SMarcel Telka 		*gids = NULL;
12969e835c76SMarcel Telka 
12979e835c76SMarcel Telka 	/*
12987c478bd9Sstevel@tonic-gate 	 *  Get the nfs flavor number from xprt.
12997c478bd9Sstevel@tonic-gate 	 */
13007c478bd9Sstevel@tonic-gate 	flavor = (int)(uintptr_t)req->rq_xprt->xp_cookie;
13017c478bd9Sstevel@tonic-gate 
13027c478bd9Sstevel@tonic-gate 	/*
13037c478bd9Sstevel@tonic-gate 	 * First check the access restrictions on the filesystem.  If
13047c478bd9Sstevel@tonic-gate 	 * there are no lists associated with this flavor then there's no
13057c478bd9Sstevel@tonic-gate 	 * need to make an expensive call to the nfsauth service or to
13067c478bd9Sstevel@tonic-gate 	 * cache anything.
13077c478bd9Sstevel@tonic-gate 	 */
13087c478bd9Sstevel@tonic-gate 
13097c478bd9Sstevel@tonic-gate 	sp = exi->exi_export.ex_secinfo;
13107c478bd9Sstevel@tonic-gate 	for (i = 0; i < exi->exi_export.ex_seccnt; i++) {
13117c478bd9Sstevel@tonic-gate 		if (flavor != sp[i].s_secinfo.sc_nfsnum) {
13127c478bd9Sstevel@tonic-gate 			if (sp[i].s_secinfo.sc_nfsnum == AUTH_NONE)
13137c478bd9Sstevel@tonic-gate 				authnone_entry = i;
13147c478bd9Sstevel@tonic-gate 			continue;
13157c478bd9Sstevel@tonic-gate 		}
13167c478bd9Sstevel@tonic-gate 		break;
13177c478bd9Sstevel@tonic-gate 	}
13187c478bd9Sstevel@tonic-gate 
13197c478bd9Sstevel@tonic-gate 	mapaccess = 0;
13207c478bd9Sstevel@tonic-gate 
13217c478bd9Sstevel@tonic-gate 	if (i >= exi->exi_export.ex_seccnt) {
13227c478bd9Sstevel@tonic-gate 		/*
13237c478bd9Sstevel@tonic-gate 		 * Flavor not found, but use AUTH_NONE if it exists
13247c478bd9Sstevel@tonic-gate 		 */
13257c478bd9Sstevel@tonic-gate 		if (authnone_entry == -1)
13267c478bd9Sstevel@tonic-gate 			return (NFSAUTH_DENIED);
13277c478bd9Sstevel@tonic-gate 		flavor = AUTH_NONE;
13287c478bd9Sstevel@tonic-gate 		mapaccess = NFSAUTH_MAPNONE;
13297c478bd9Sstevel@tonic-gate 		i = authnone_entry;
13307c478bd9Sstevel@tonic-gate 	}
13317c478bd9Sstevel@tonic-gate 
13327c478bd9Sstevel@tonic-gate 	/*
13337c478bd9Sstevel@tonic-gate 	 * If the flavor is in the ex_secinfo list, but not an explicitly
13347c478bd9Sstevel@tonic-gate 	 * shared flavor by the user, it is a result of the nfsv4 server
13357c478bd9Sstevel@tonic-gate 	 * namespace setup. We will grant an RO permission similar for
13367c478bd9Sstevel@tonic-gate 	 * a pseudo node except that this node is a shared one.
13377c478bd9Sstevel@tonic-gate 	 *
13387c478bd9Sstevel@tonic-gate 	 * e.g. flavor in (flavor) indicates that it is not explictly
13397c478bd9Sstevel@tonic-gate 	 *	shared by the user:
13407c478bd9Sstevel@tonic-gate 	 *
13417c478bd9Sstevel@tonic-gate 	 *		/	(sys, krb5)
13427c478bd9Sstevel@tonic-gate 	 *		|
13437c478bd9Sstevel@tonic-gate 	 *		export  #share -o sec=sys (krb5)
13447c478bd9Sstevel@tonic-gate 	 *		|
13457c478bd9Sstevel@tonic-gate 	 *		secure  #share -o sec=krb5
13467c478bd9Sstevel@tonic-gate 	 *
13477c478bd9Sstevel@tonic-gate 	 *	In this case, when a krb5 request coming in to access
13487c478bd9Sstevel@tonic-gate 	 *	/export, RO permission is granted.
13497c478bd9Sstevel@tonic-gate 	 */
13507c478bd9Sstevel@tonic-gate 	if (!(sp[i].s_flags & M_4SEC_EXPORTED))
13517c478bd9Sstevel@tonic-gate 		return (mapaccess | NFSAUTH_RO);
13527c478bd9Sstevel@tonic-gate 
13537c478bd9Sstevel@tonic-gate 	/*
135489621fe1SMarcel Telka 	 * Optimize if there are no lists.
135589621fe1SMarcel Telka 	 * We cannot optimize for AUTH_SYS with NGRPS (16) supplemental groups.
13567c478bd9Sstevel@tonic-gate 	 */
13577c478bd9Sstevel@tonic-gate 	perm = sp[i].s_flags;
135889621fe1SMarcel Telka 	if ((perm & (M_ROOT | M_NONE | M_MAP)) == 0 && (ngroups_max <= NGRPS ||
135989621fe1SMarcel Telka 	    flavor != AUTH_SYS || crgetngroups(cr) < NGRPS)) {
13607c478bd9Sstevel@tonic-gate 		perm &= ~M_4SEC_EXPORTED;
13617c478bd9Sstevel@tonic-gate 		if (perm == M_RO)
13627c478bd9Sstevel@tonic-gate 			return (mapaccess | NFSAUTH_RO);
13637c478bd9Sstevel@tonic-gate 		if (perm == M_RW)
13647c478bd9Sstevel@tonic-gate 			return (mapaccess | NFSAUTH_RW);
13657c478bd9Sstevel@tonic-gate 	}
13667c478bd9Sstevel@tonic-gate 
136789621fe1SMarcel Telka 	access = nfsauth_cache_get(exi, req, flavor, cr, uid, gid, ngids, gids);
136889621fe1SMarcel Telka 
136989621fe1SMarcel Telka 	/*
137089621fe1SMarcel Telka 	 * For both NFSAUTH_DENIED and NFSAUTH_WRONGSEC we do not care about
137189621fe1SMarcel Telka 	 * the supplemental groups.
137289621fe1SMarcel Telka 	 */
137389621fe1SMarcel Telka 	if (access & NFSAUTH_DENIED || access & NFSAUTH_WRONGSEC) {
137489621fe1SMarcel Telka 		if (ngids != NULL && gids != NULL) {
137589621fe1SMarcel Telka 			kmem_free(*gids, *ngids * sizeof (gid_t));
137689621fe1SMarcel Telka 			*ngids = 0;
137789621fe1SMarcel Telka 			*gids = NULL;
137889621fe1SMarcel Telka 		}
137989621fe1SMarcel Telka 	}
1380f73f2d50SVallish Vaidyeshwara 
1381f73f2d50SVallish Vaidyeshwara 	/*
1382f73f2d50SVallish Vaidyeshwara 	 * Client's security flavor doesn't match with "ro" or
1383f73f2d50SVallish Vaidyeshwara 	 * "rw" list. Try again using AUTH_NONE if present.
1384f73f2d50SVallish Vaidyeshwara 	 */
1385f73f2d50SVallish Vaidyeshwara 	if ((access & NFSAUTH_WRONGSEC) && (flavor != AUTH_NONE)) {
1386f73f2d50SVallish Vaidyeshwara 		/*
1387f73f2d50SVallish Vaidyeshwara 		 * Have we already encountered AUTH_NONE ?
1388f73f2d50SVallish Vaidyeshwara 		 */
1389f73f2d50SVallish Vaidyeshwara 		if (authnone_entry != -1) {
1390f73f2d50SVallish Vaidyeshwara 			mapaccess = NFSAUTH_MAPNONE;
13915cb0d679SMarcel Telka 			access = nfsauth_cache_get(exi, req, AUTH_NONE, cr,
139289621fe1SMarcel Telka 			    NULL, NULL, NULL, NULL);
1393f73f2d50SVallish Vaidyeshwara 		} else {
1394f73f2d50SVallish Vaidyeshwara 			/*
1395f73f2d50SVallish Vaidyeshwara 			 * Check for AUTH_NONE presence.
1396f73f2d50SVallish Vaidyeshwara 			 */
1397f73f2d50SVallish Vaidyeshwara 			for (; i < exi->exi_export.ex_seccnt; i++) {
1398f73f2d50SVallish Vaidyeshwara 				if (sp[i].s_secinfo.sc_nfsnum == AUTH_NONE) {
1399f73f2d50SVallish Vaidyeshwara 					mapaccess = NFSAUTH_MAPNONE;
1400f73f2d50SVallish Vaidyeshwara 					access = nfsauth_cache_get(exi, req,
140189621fe1SMarcel Telka 					    AUTH_NONE, cr, NULL, NULL, NULL,
140289621fe1SMarcel Telka 					    NULL);
1403f73f2d50SVallish Vaidyeshwara 					break;
1404f73f2d50SVallish Vaidyeshwara 				}
1405f73f2d50SVallish Vaidyeshwara 			}
1406f73f2d50SVallish Vaidyeshwara 		}
1407f73f2d50SVallish Vaidyeshwara 	}
1408f73f2d50SVallish Vaidyeshwara 
1409b89a8333Snatalie li - Sun Microsystems - Irvine United States 	if (access & NFSAUTH_DENIED)
1410b89a8333Snatalie li - Sun Microsystems - Irvine United States 		access = NFSAUTH_DENIED;
14117c478bd9Sstevel@tonic-gate 
14127c478bd9Sstevel@tonic-gate 	return (access | mapaccess);
14137c478bd9Sstevel@tonic-gate }
14147c478bd9Sstevel@tonic-gate 
14153ccecb66SThomas Haynes static void
141671da0c32SMarcel Telka nfsauth_free_clnt_node(struct auth_cache_clnt *p)
141771da0c32SMarcel Telka {
141871da0c32SMarcel Telka 	void *cookie = NULL;
141971da0c32SMarcel Telka 	struct auth_cache *node;
142071da0c32SMarcel Telka 
142171da0c32SMarcel Telka 	while ((node = avl_destroy_nodes(&p->authc_tree, &cookie)) != NULL)
142271da0c32SMarcel Telka 		nfsauth_free_node(node);
142371da0c32SMarcel Telka 	avl_destroy(&p->authc_tree);
142471da0c32SMarcel Telka 
14257bbfa3eeSMarcel Telka 	kmem_free(p->authc_addr.buf, p->authc_addr.maxlen);
142671da0c32SMarcel Telka 	rw_destroy(&p->authc_lock);
142771da0c32SMarcel Telka 
142871da0c32SMarcel Telka 	kmem_free(p, sizeof (*p));
142971da0c32SMarcel Telka }
143071da0c32SMarcel Telka 
143171da0c32SMarcel Telka static void
14323ccecb66SThomas Haynes nfsauth_free_node(struct auth_cache *p)
14333ccecb66SThomas Haynes {
143489621fe1SMarcel Telka 	kmem_free(p->auth_clnt_gids, p->auth_clnt_ngids * sizeof (gid_t));
143589621fe1SMarcel Telka 	kmem_free(p->auth_srv_gids, p->auth_srv_ngids * sizeof (gid_t));
14363ccecb66SThomas Haynes 	mutex_destroy(&p->auth_lock);
143771da0c32SMarcel Telka 	cv_destroy(&p->auth_cv);
143889621fe1SMarcel Telka 	kmem_cache_free(exi_cache_handle, p);
14393ccecb66SThomas Haynes }
14403ccecb66SThomas Haynes 
14413ccecb66SThomas Haynes /*
14427c478bd9Sstevel@tonic-gate  * Free the nfsauth cache for a given export
14437c478bd9Sstevel@tonic-gate  */
14447c478bd9Sstevel@tonic-gate void
14457c478bd9Sstevel@tonic-gate nfsauth_cache_free(struct exportinfo *exi)
14467c478bd9Sstevel@tonic-gate {
14477c478bd9Sstevel@tonic-gate 	int i;
14483ccecb66SThomas Haynes 
14493ccecb66SThomas Haynes 	/*
145071da0c32SMarcel Telka 	 * The only way we got here was with an exi_rele, which means that no
145171da0c32SMarcel Telka 	 * auth cache entry is being refreshed.
14523ccecb66SThomas Haynes 	 */
145371da0c32SMarcel Telka 
145471da0c32SMarcel Telka 	for (i = 0; i < AUTH_TABLESIZE; i++) {
145571da0c32SMarcel Telka 		avl_tree_t *tree = exi->exi_cache[i];
145671da0c32SMarcel Telka 		void *cookie = NULL;
145771da0c32SMarcel Telka 		struct auth_cache_clnt *node;
145871da0c32SMarcel Telka 
145971da0c32SMarcel Telka 		while ((node = avl_destroy_nodes(tree, &cookie)) != NULL)
146071da0c32SMarcel Telka 			nfsauth_free_clnt_node(node);
14617c478bd9Sstevel@tonic-gate 	}
14627c478bd9Sstevel@tonic-gate }
14637c478bd9Sstevel@tonic-gate 
14647c478bd9Sstevel@tonic-gate /*
14657c478bd9Sstevel@tonic-gate  * Called by the kernel memory allocator when
14667c478bd9Sstevel@tonic-gate  * memory is low. Free unused cache entries.
14677c478bd9Sstevel@tonic-gate  * If that's not enough, the VM system will
14687c478bd9Sstevel@tonic-gate  * call again for some more.
14697c478bd9Sstevel@tonic-gate  */
14707c478bd9Sstevel@tonic-gate /*ARGSUSED*/
14717c478bd9Sstevel@tonic-gate void
14727c478bd9Sstevel@tonic-gate exi_cache_reclaim(void *cdrarg)
14737c478bd9Sstevel@tonic-gate {
14747c478bd9Sstevel@tonic-gate 	int i;
14757c478bd9Sstevel@tonic-gate 	struct exportinfo *exi;
14767c478bd9Sstevel@tonic-gate 
14777c478bd9Sstevel@tonic-gate 	rw_enter(&exported_lock, RW_READER);
14787c478bd9Sstevel@tonic-gate 
14797c478bd9Sstevel@tonic-gate 	for (i = 0; i < EXPTABLESIZE; i++) {
14800616fd7fSPavel Filipensky 		for (exi = exptable[i]; exi; exi = exi->fid_hash.next) {
14817c478bd9Sstevel@tonic-gate 			exi_cache_trim(exi);
14827c478bd9Sstevel@tonic-gate 		}
14837c478bd9Sstevel@tonic-gate 	}
14847c478bd9Sstevel@tonic-gate 
14857c478bd9Sstevel@tonic-gate 	rw_exit(&exported_lock);
148671da0c32SMarcel Telka 
148771da0c32SMarcel Telka 	atomic_inc_uint(&nfsauth_cache_reclaim);
14887c478bd9Sstevel@tonic-gate }
14897c478bd9Sstevel@tonic-gate 
14907c478bd9Sstevel@tonic-gate void
14917c478bd9Sstevel@tonic-gate exi_cache_trim(struct exportinfo *exi)
14927c478bd9Sstevel@tonic-gate {
149371da0c32SMarcel Telka 	struct auth_cache_clnt *c;
149471da0c32SMarcel Telka 	struct auth_cache_clnt *nextc;
14957c478bd9Sstevel@tonic-gate 	struct auth_cache *p;
149671da0c32SMarcel Telka 	struct auth_cache *next;
14977c478bd9Sstevel@tonic-gate 	int i;
14987c478bd9Sstevel@tonic-gate 	time_t stale_time;
149971da0c32SMarcel Telka 	avl_tree_t *tree;
15007c478bd9Sstevel@tonic-gate 
15017c478bd9Sstevel@tonic-gate 	for (i = 0; i < AUTH_TABLESIZE; i++) {
15027c478bd9Sstevel@tonic-gate 
150371da0c32SMarcel Telka 		tree = exi->exi_cache[i];
150471da0c32SMarcel Telka 		stale_time = gethrestime_sec() - NFSAUTH_CACHE_TRIM;
150571da0c32SMarcel Telka 
150671da0c32SMarcel Telka 		rw_enter(&exi->exi_cache_lock, RW_READER);
150771da0c32SMarcel Telka 
15087c478bd9Sstevel@tonic-gate 		/*
15097c478bd9Sstevel@tonic-gate 		 * Free entries that have not been
15103ccecb66SThomas Haynes 		 * used for NFSAUTH_CACHE_TRIM seconds.
15117c478bd9Sstevel@tonic-gate 		 */
151271da0c32SMarcel Telka 		for (c = avl_first(tree); c != NULL; c = AVL_NEXT(tree, c)) {
151371da0c32SMarcel Telka 			rw_enter(&c->authc_lock, RW_WRITER);
151471da0c32SMarcel Telka 			for (p = avl_first(&c->authc_tree); p != NULL;
151571da0c32SMarcel Telka 			    p = next) {
151671da0c32SMarcel Telka 				next = AVL_NEXT(&c->authc_tree, p);
151771da0c32SMarcel Telka 
151871da0c32SMarcel Telka 				ASSERT(p->auth_state != NFS_AUTH_INVALID);
151971da0c32SMarcel Telka 
152071da0c32SMarcel Telka 				mutex_enter(&p->auth_lock);
152171da0c32SMarcel Telka 
152271da0c32SMarcel Telka 				/*
152371da0c32SMarcel Telka 				 * We won't trim recently used and/or WAITING
152471da0c32SMarcel Telka 				 * entries.
152571da0c32SMarcel Telka 				 */
152671da0c32SMarcel Telka 				if (p->auth_time > stale_time ||
152771da0c32SMarcel Telka 				    p->auth_state == NFS_AUTH_WAITING) {
152871da0c32SMarcel Telka 					mutex_exit(&p->auth_lock);
15297c478bd9Sstevel@tonic-gate 					continue;
15307c478bd9Sstevel@tonic-gate 				}
15317c478bd9Sstevel@tonic-gate 
15323ccecb66SThomas Haynes 				DTRACE_PROBE1(nfsauth__debug__trim__state,
15333ccecb66SThomas Haynes 				    auth_state_t, p->auth_state);
15343ccecb66SThomas Haynes 
153571da0c32SMarcel Telka 				/*
153671da0c32SMarcel Telka 				 * STALE and REFRESHING entries needs to be
153771da0c32SMarcel Telka 				 * marked INVALID only because they are
153871da0c32SMarcel Telka 				 * referenced by some other structures or
153971da0c32SMarcel Telka 				 * threads.  They will be freed later.
154071da0c32SMarcel Telka 				 */
154171da0c32SMarcel Telka 				if (p->auth_state == NFS_AUTH_STALE ||
154271da0c32SMarcel Telka 				    p->auth_state == NFS_AUTH_REFRESHING) {
15433ccecb66SThomas Haynes 					p->auth_state = NFS_AUTH_INVALID;
15443ccecb66SThomas Haynes 					mutex_exit(&p->auth_lock);
15453ccecb66SThomas Haynes 
154671da0c32SMarcel Telka 					avl_remove(&c->authc_tree, p);
15473ccecb66SThomas Haynes 				} else {
15483ccecb66SThomas Haynes 					mutex_exit(&p->auth_lock);
154971da0c32SMarcel Telka 
155071da0c32SMarcel Telka 					avl_remove(&c->authc_tree, p);
15513ccecb66SThomas Haynes 					nfsauth_free_node(p);
15523ccecb66SThomas Haynes 				}
15537c478bd9Sstevel@tonic-gate 			}
155471da0c32SMarcel Telka 			rw_exit(&c->authc_lock);
155571da0c32SMarcel Telka 		}
155671da0c32SMarcel Telka 
155771da0c32SMarcel Telka 		if (rw_tryupgrade(&exi->exi_cache_lock) == 0) {
155871da0c32SMarcel Telka 			rw_exit(&exi->exi_cache_lock);
155971da0c32SMarcel Telka 			rw_enter(&exi->exi_cache_lock, RW_WRITER);
156071da0c32SMarcel Telka 		}
156171da0c32SMarcel Telka 
156271da0c32SMarcel Telka 		for (c = avl_first(tree); c != NULL; c = nextc) {
156371da0c32SMarcel Telka 			nextc = AVL_NEXT(tree, c);
156471da0c32SMarcel Telka 
156571da0c32SMarcel Telka 			if (avl_is_empty(&c->authc_tree) == B_FALSE)
156671da0c32SMarcel Telka 				continue;
156771da0c32SMarcel Telka 
156871da0c32SMarcel Telka 			avl_remove(tree, c);
156971da0c32SMarcel Telka 
157071da0c32SMarcel Telka 			nfsauth_free_clnt_node(c);
15717c478bd9Sstevel@tonic-gate 		}
15727c478bd9Sstevel@tonic-gate 
15737c478bd9Sstevel@tonic-gate 		rw_exit(&exi->exi_cache_lock);
15747c478bd9Sstevel@tonic-gate 	}
157571da0c32SMarcel Telka }
1576