xref: /illumos-gate/usr/src/uts/common/fs/nfs/nfs_auth.c (revision f44e1126)
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 /*
2389621fe1SMarcel Telka  * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
248ee802ccSGeorge Wilson  * Copyright (c) 2015 by Delphix. All rights reserved.
25d1580181SBryan Cantrill  * Copyright (c) 2015 Joyent, Inc.  All rights reserved.
260dfe541eSEvan Layton  * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
277c478bd9Sstevel@tonic-gate  */
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate #include <sys/param.h>
307c478bd9Sstevel@tonic-gate #include <sys/errno.h>
317c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
327c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
337c478bd9Sstevel@tonic-gate #include <sys/cred.h>
347c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
357c478bd9Sstevel@tonic-gate #include <sys/systm.h>
367c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
377c478bd9Sstevel@tonic-gate #include <sys/pathname.h>
387c478bd9Sstevel@tonic-gate #include <sys/utsname.h>
397c478bd9Sstevel@tonic-gate #include <sys/debug.h>
401cc55349Srmesta #include <sys/door.h>
411cc55349Srmesta #include <sys/sdt.h>
423ccecb66SThomas Haynes #include <sys/thread.h>
4371da0c32SMarcel Telka #include <sys/avl.h>
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate #include <rpc/types.h>
467c478bd9Sstevel@tonic-gate #include <rpc/auth.h>
477c478bd9Sstevel@tonic-gate #include <rpc/clnt.h>
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate #include <nfs/nfs.h>
507c478bd9Sstevel@tonic-gate #include <nfs/export.h>
517c478bd9Sstevel@tonic-gate #include <nfs/nfs_clnt.h>
521cc55349Srmesta #include <nfs/auth.h>
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate static struct kmem_cache *exi_cache_handle;
557c478bd9Sstevel@tonic-gate static void exi_cache_reclaim(void *);
560dfe541eSEvan Layton static void exi_cache_reclaim_zone(nfs_globals_t *);
577c478bd9Sstevel@tonic-gate static void exi_cache_trim(struct exportinfo *exi);
587c478bd9Sstevel@tonic-gate 
593ccecb66SThomas Haynes extern pri_t minclsyspri;
603ccecb66SThomas Haynes 
610dfe541eSEvan Layton /* NFS auth cache statistics */
6271da0c32SMarcel Telka volatile uint_t nfsauth_cache_hit;
6371da0c32SMarcel Telka volatile uint_t nfsauth_cache_miss;
6471da0c32SMarcel Telka volatile uint_t nfsauth_cache_refresh;
6571da0c32SMarcel Telka volatile uint_t nfsauth_cache_reclaim;
668ee802ccSGeorge Wilson volatile uint_t exi_cache_auth_reclaim_failed;
678ee802ccSGeorge Wilson volatile uint_t exi_cache_clnt_reclaim_failed;
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate /*
703ccecb66SThomas Haynes  * The lifetime of an auth cache entry:
713ccecb66SThomas Haynes  * ------------------------------------
723ccecb66SThomas Haynes  *
733ccecb66SThomas Haynes  * An auth cache entry is created with both the auth_time
743ccecb66SThomas Haynes  * and auth_freshness times set to the current time.
753ccecb66SThomas Haynes  *
763ccecb66SThomas Haynes  * Upon every client access which results in a hit, the
773ccecb66SThomas Haynes  * auth_time will be updated.
783ccecb66SThomas Haynes  *
793ccecb66SThomas Haynes  * If a client access determines that the auth_freshness
803ccecb66SThomas Haynes  * indicates that the entry is STALE, then it will be
813ccecb66SThomas Haynes  * refreshed. Note that this will explicitly reset
823ccecb66SThomas Haynes  * auth_time.
833ccecb66SThomas Haynes  *
843ccecb66SThomas Haynes  * When the REFRESH successfully occurs, then the
853ccecb66SThomas Haynes  * auth_freshness is updated.
863ccecb66SThomas Haynes  *
873ccecb66SThomas Haynes  * There are two ways for an entry to leave the cache:
883ccecb66SThomas Haynes  *
893ccecb66SThomas Haynes  * 1) Purged by an action on the export (remove or changed)
903ccecb66SThomas Haynes  * 2) Memory backpressure from the kernel (check against NFSAUTH_CACHE_TRIM)
913ccecb66SThomas Haynes  *
923ccecb66SThomas Haynes  * For 2) we check the timeout value against auth_time.
937c478bd9Sstevel@tonic-gate  */
943ccecb66SThomas Haynes 
953ccecb66SThomas Haynes /*
963ccecb66SThomas Haynes  * Number of seconds until we mark for refresh an auth cache entry.
973ccecb66SThomas Haynes  */
983ccecb66SThomas Haynes #define	NFSAUTH_CACHE_REFRESH 600
993ccecb66SThomas Haynes 
1003ccecb66SThomas Haynes /*
1013ccecb66SThomas Haynes  * Number of idle seconds until we yield to backpressure
1023ccecb66SThomas Haynes  * to trim a cache entry.
1033ccecb66SThomas Haynes  */
1043ccecb66SThomas Haynes #define	NFSAUTH_CACHE_TRIM 3600
1053ccecb66SThomas Haynes 
1063ccecb66SThomas Haynes /*
1073ccecb66SThomas Haynes  * While we could encapuslate the exi_list inside the
1083ccecb66SThomas Haynes  * exi structure, we can't do that for the auth_list.
1093ccecb66SThomas Haynes  * So, to keep things looking clean, we keep them both
1103ccecb66SThomas Haynes  * in these external lists.
1113ccecb66SThomas Haynes  */
1123ccecb66SThomas Haynes typedef struct refreshq_exi_node {
1133ccecb66SThomas Haynes 	struct exportinfo	*ren_exi;
1143ccecb66SThomas Haynes 	list_t			ren_authlist;
1153ccecb66SThomas Haynes 	list_node_t		ren_node;
1163ccecb66SThomas Haynes } refreshq_exi_node_t;
1173ccecb66SThomas Haynes 
1183ccecb66SThomas Haynes typedef struct refreshq_auth_node {
1193ccecb66SThomas Haynes 	struct auth_cache	*ran_auth;
12071da0c32SMarcel Telka 	char			*ran_netid;
1213ccecb66SThomas Haynes 	list_node_t		ran_node;
1223ccecb66SThomas Haynes } refreshq_auth_node_t;
1233ccecb66SThomas Haynes 
1243ccecb66SThomas Haynes /*
1250dfe541eSEvan Layton  * If there is ever a problem with loading the module, then nfsauth_fini()
1260dfe541eSEvan Layton  * needs to be called to remove state.  In that event, since the refreshq
1270dfe541eSEvan Layton  * thread has been started, they need to work together to get rid of state.
1283ccecb66SThomas Haynes  */
1293ccecb66SThomas Haynes typedef enum nfsauth_refreshq_thread_state {
1303ccecb66SThomas Haynes 	REFRESHQ_THREAD_RUNNING,
1313ccecb66SThomas Haynes 	REFRESHQ_THREAD_FINI_REQ,
1320dfe541eSEvan Layton 	REFRESHQ_THREAD_HALTED,
1330dfe541eSEvan Layton 	REFRESHQ_THREAD_NEED_CREATE
1343ccecb66SThomas Haynes } nfsauth_refreshq_thread_state_t;
1353ccecb66SThomas Haynes 
1360dfe541eSEvan Layton typedef struct nfsauth_globals {
1370dfe541eSEvan Layton 	kmutex_t	mountd_lock;
1380dfe541eSEvan Layton 	door_handle_t   mountd_dh;
1390dfe541eSEvan Layton 
1400dfe541eSEvan Layton 	/*
1410dfe541eSEvan Layton 	 * Used to manipulate things on the refreshq_queue.  Note that the
1420dfe541eSEvan Layton 	 * refresh thread will effectively pop a node off of the queue,
1430dfe541eSEvan Layton 	 * at which point it will no longer need to hold the mutex.
1440dfe541eSEvan Layton 	 */
1450dfe541eSEvan Layton 	kmutex_t	refreshq_lock;
1460dfe541eSEvan Layton 	list_t		refreshq_queue;
1470dfe541eSEvan Layton 	kcondvar_t	refreshq_cv;
1480dfe541eSEvan Layton 
1490dfe541eSEvan Layton 	/*
1500dfe541eSEvan Layton 	 * A list_t would be overkill.  These are auth_cache entries which are
1510dfe541eSEvan Layton 	 * no longer linked to an exi.  It should be the case that all of their
1520dfe541eSEvan Layton 	 * states are NFS_AUTH_INVALID, i.e., the only way to be put on this
1530dfe541eSEvan Layton 	 * list is iff their state indicated that they had been placed on the
1540dfe541eSEvan Layton 	 * refreshq_queue.
1550dfe541eSEvan Layton 	 *
1560dfe541eSEvan Layton 	 * Note that while there is no link from the exi or back to the exi,
1570dfe541eSEvan Layton 	 * the exi can not go away until these entries are harvested.
1580dfe541eSEvan Layton 	 */
1590dfe541eSEvan Layton 	struct auth_cache		*refreshq_dead_entries;
1600dfe541eSEvan Layton 	nfsauth_refreshq_thread_state_t	refreshq_thread_state;
1610dfe541eSEvan Layton 
1620dfe541eSEvan Layton } nfsauth_globals_t;
1633ccecb66SThomas Haynes 
1643ccecb66SThomas Haynes static void nfsauth_free_node(struct auth_cache *);
1650dfe541eSEvan Layton static void nfsauth_refresh_thread(nfsauth_globals_t *);
1667c478bd9Sstevel@tonic-gate 
16771da0c32SMarcel Telka static int nfsauth_cache_compar(const void *, const void *);
16871da0c32SMarcel Telka 
1690dfe541eSEvan Layton static nfsauth_globals_t *
nfsauth_get_zg(void)1700dfe541eSEvan Layton nfsauth_get_zg(void)
1710dfe541eSEvan Layton {
1720dfe541eSEvan Layton 	nfs_globals_t *ng = nfs_srv_getzg();
1730dfe541eSEvan Layton 	nfsauth_globals_t *nag = ng->nfs_auth;
1740dfe541eSEvan Layton 	ASSERT(nag != NULL);
1750dfe541eSEvan Layton 	return (nag);
1760dfe541eSEvan Layton }
1771cc55349Srmesta 
1787c478bd9Sstevel@tonic-gate void
mountd_args(uint_t did)1791cc55349Srmesta mountd_args(uint_t did)
1807c478bd9Sstevel@tonic-gate {
1810dfe541eSEvan Layton 	nfsauth_globals_t *nag;
1820dfe541eSEvan Layton 
1830dfe541eSEvan Layton 	nag = nfsauth_get_zg();
1840dfe541eSEvan Layton 	mutex_enter(&nag->mountd_lock);
1850dfe541eSEvan Layton 	if (nag->mountd_dh != NULL)
1860dfe541eSEvan Layton 		door_ki_rele(nag->mountd_dh);
1870dfe541eSEvan Layton 	nag->mountd_dh = door_ki_lookup(did);
1880dfe541eSEvan Layton 	mutex_exit(&nag->mountd_lock);
1891cc55349Srmesta }
1907c478bd9Sstevel@tonic-gate 
1911cc55349Srmesta void
nfsauth_init(void)1921cc55349Srmesta nfsauth_init(void)
1931cc55349Srmesta {
1940dfe541eSEvan Layton 	exi_cache_handle = kmem_cache_create("exi_cache_handle",
1950dfe541eSEvan Layton 	    sizeof (struct auth_cache), 0, NULL, NULL,
1960dfe541eSEvan Layton 	    exi_cache_reclaim, NULL, NULL, 0);
1970dfe541eSEvan Layton }
1987c478bd9Sstevel@tonic-gate 
1990dfe541eSEvan Layton void
nfsauth_fini(void)2000dfe541eSEvan Layton nfsauth_fini(void)
2010dfe541eSEvan Layton {
2020dfe541eSEvan Layton 	kmem_cache_destroy(exi_cache_handle);
2030dfe541eSEvan Layton }
2043ccecb66SThomas Haynes 
2050dfe541eSEvan Layton void
nfsauth_zone_init(nfs_globals_t * ng)2060dfe541eSEvan Layton nfsauth_zone_init(nfs_globals_t *ng)
2070dfe541eSEvan Layton {
2080dfe541eSEvan Layton 	nfsauth_globals_t *nag;
2090dfe541eSEvan Layton 
2100dfe541eSEvan Layton 	nag = kmem_zalloc(sizeof (*nag), KM_SLEEP);
2113ccecb66SThomas Haynes 
2127c478bd9Sstevel@tonic-gate 	/*
213bbf21555SRichard Lowe 	 * mountd can be restarted by smf(7).  We need to make sure
2140dfe541eSEvan Layton 	 * the updated door handle will safely make it to mountd_dh.
2157c478bd9Sstevel@tonic-gate 	 */
2160dfe541eSEvan Layton 	mutex_init(&nag->mountd_lock, NULL, MUTEX_DEFAULT, NULL);
2170dfe541eSEvan Layton 	mutex_init(&nag->refreshq_lock, NULL, MUTEX_DEFAULT, NULL);
2180dfe541eSEvan Layton 	list_create(&nag->refreshq_queue, sizeof (refreshq_exi_node_t),
2190dfe541eSEvan Layton 	    offsetof(refreshq_exi_node_t, ren_node));
2200dfe541eSEvan Layton 	cv_init(&nag->refreshq_cv, NULL, CV_DEFAULT, NULL);
2210dfe541eSEvan Layton 	nag->refreshq_thread_state = REFRESHQ_THREAD_NEED_CREATE;
2223ccecb66SThomas Haynes 
2230dfe541eSEvan Layton 	ng->nfs_auth = nag;
2247c478bd9Sstevel@tonic-gate }
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate void
nfsauth_zone_shutdown(nfs_globals_t * ng)2270dfe541eSEvan Layton nfsauth_zone_shutdown(nfs_globals_t *ng)
2287c478bd9Sstevel@tonic-gate {
2293ccecb66SThomas Haynes 	refreshq_exi_node_t	*ren;
2300dfe541eSEvan Layton 	nfsauth_globals_t	*nag = ng->nfs_auth;
2313ccecb66SThomas Haynes 
2320dfe541eSEvan Layton 	/* Prevent the nfsauth_refresh_thread from getting new work */
2330dfe541eSEvan Layton 	mutex_enter(&nag->refreshq_lock);
2340dfe541eSEvan Layton 	if (nag->refreshq_thread_state == REFRESHQ_THREAD_RUNNING) {
2350dfe541eSEvan Layton 		nag->refreshq_thread_state = REFRESHQ_THREAD_FINI_REQ;
2360dfe541eSEvan Layton 		cv_broadcast(&nag->refreshq_cv);
2373ccecb66SThomas Haynes 
2380dfe541eSEvan Layton 		/* Wait for nfsauth_refresh_thread() to exit */
2390dfe541eSEvan Layton 		while (nag->refreshq_thread_state != REFRESHQ_THREAD_HALTED)
2400dfe541eSEvan Layton 			cv_wait(&nag->refreshq_cv, &nag->refreshq_lock);
2413ccecb66SThomas Haynes 	}
2420dfe541eSEvan Layton 	mutex_exit(&nag->refreshq_lock);
2433ccecb66SThomas Haynes 
2443ccecb66SThomas Haynes 	/*
24571da0c32SMarcel Telka 	 * Walk the exi_list and in turn, walk the auth_lists and free all
24671da0c32SMarcel Telka 	 * lists.  In addition, free INVALID auth_cache entries.
2473ccecb66SThomas Haynes 	 */
2480dfe541eSEvan Layton 	while ((ren = list_remove_head(&nag->refreshq_queue))) {
24971da0c32SMarcel Telka 		refreshq_auth_node_t *ran;
25071da0c32SMarcel Telka 
25171da0c32SMarcel Telka 		while ((ran = list_remove_head(&ren->ren_authlist)) != NULL) {
25271da0c32SMarcel Telka 			struct auth_cache *p = ran->ran_auth;
25371da0c32SMarcel Telka 			if (p->auth_state == NFS_AUTH_INVALID)
25471da0c32SMarcel Telka 				nfsauth_free_node(p);
25571da0c32SMarcel Telka 			strfree(ran->ran_netid);
2560dfe541eSEvan Layton 			kmem_free(ran, sizeof (*ran));
2573ccecb66SThomas Haynes 		}
2583ccecb66SThomas Haynes 
2593ccecb66SThomas Haynes 		list_destroy(&ren->ren_authlist);
2603ccecb66SThomas Haynes 		exi_rele(ren->ren_exi);
2610dfe541eSEvan Layton 		kmem_free(ren, sizeof (*ren));
2623ccecb66SThomas Haynes 	}
2630dfe541eSEvan Layton }
2643ccecb66SThomas Haynes 
2650dfe541eSEvan Layton void
nfsauth_zone_fini(nfs_globals_t * ng)2660dfe541eSEvan Layton nfsauth_zone_fini(nfs_globals_t *ng)
2670dfe541eSEvan Layton {
2680dfe541eSEvan Layton 	nfsauth_globals_t *nag = ng->nfs_auth;
2690dfe541eSEvan Layton 
2700dfe541eSEvan Layton 	ng->nfs_auth = NULL;
2710dfe541eSEvan Layton 
2720dfe541eSEvan Layton 	list_destroy(&nag->refreshq_queue);
2730dfe541eSEvan Layton 	cv_destroy(&nag->refreshq_cv);
2740dfe541eSEvan Layton 	mutex_destroy(&nag->refreshq_lock);
2750dfe541eSEvan Layton 	mutex_destroy(&nag->mountd_lock);
2760dfe541eSEvan Layton 	/* Extra cleanup. */
2770dfe541eSEvan Layton 	if (nag->mountd_dh != NULL)
2780dfe541eSEvan Layton 		door_ki_rele(nag->mountd_dh);
2790dfe541eSEvan Layton 	kmem_free(nag, sizeof (*nag));
2807c478bd9Sstevel@tonic-gate }
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate /*
2837c478bd9Sstevel@tonic-gate  * Convert the address in a netbuf to
2847c478bd9Sstevel@tonic-gate  * a hash index for the auth_cache table.
2857c478bd9Sstevel@tonic-gate  */
2867c478bd9Sstevel@tonic-gate static int
hash(struct netbuf * a)2877c478bd9Sstevel@tonic-gate hash(struct netbuf *a)
2887c478bd9Sstevel@tonic-gate {
2897c478bd9Sstevel@tonic-gate 	int i, h = 0;
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate 	for (i = 0; i < a->len; i++)
2927c478bd9Sstevel@tonic-gate 		h ^= a->buf[i];
2937c478bd9Sstevel@tonic-gate 
2947c478bd9Sstevel@tonic-gate 	return (h & (AUTH_TABLESIZE - 1));
2957c478bd9Sstevel@tonic-gate }
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate /*
2987c478bd9Sstevel@tonic-gate  * Mask out the components of an
2997c478bd9Sstevel@tonic-gate  * address that do not identify
3007c478bd9Sstevel@tonic-gate  * a host. For socket addresses the
3017c478bd9Sstevel@tonic-gate  * masking gets rid of the port number.
3027c478bd9Sstevel@tonic-gate  */
3037c478bd9Sstevel@tonic-gate static void
addrmask(struct netbuf * addr,struct netbuf * mask)3047c478bd9Sstevel@tonic-gate addrmask(struct netbuf *addr, struct netbuf *mask)
3057c478bd9Sstevel@tonic-gate {
3067c478bd9Sstevel@tonic-gate 	int i;
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate 	for (i = 0; i < addr->len; i++)
3097c478bd9Sstevel@tonic-gate 		addr->buf[i] &= mask->buf[i];
3107c478bd9Sstevel@tonic-gate }
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate /*
3137c478bd9Sstevel@tonic-gate  * nfsauth4_access is used for NFS V4 auth checking. Besides doing
3147c478bd9Sstevel@tonic-gate  * the common nfsauth_access(), it will check if the client can
3157c478bd9Sstevel@tonic-gate  * have a limited access to this vnode even if the security flavor
3167c478bd9Sstevel@tonic-gate  * used does not meet the policy.
3177c478bd9Sstevel@tonic-gate  */
3187c478bd9Sstevel@tonic-gate int
nfsauth4_access(struct exportinfo * exi,vnode_t * vp,struct svc_req * req,cred_t * cr,uid_t * uid,gid_t * gid,uint_t * ngids,gid_t ** gids)3195cb0d679SMarcel Telka nfsauth4_access(struct exportinfo *exi, vnode_t *vp, struct svc_req *req,
32089621fe1SMarcel Telka     cred_t *cr, uid_t *uid, gid_t *gid, uint_t *ngids, gid_t **gids)
3217c478bd9Sstevel@tonic-gate {
3227c478bd9Sstevel@tonic-gate 	int access;
3237c478bd9Sstevel@tonic-gate 
32489621fe1SMarcel Telka 	access = nfsauth_access(exi, req, cr, uid, gid, ngids, gids);
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate 	/*
3277c478bd9Sstevel@tonic-gate 	 * There are cases that the server needs to allow the client
3287c478bd9Sstevel@tonic-gate 	 * to have a limited view.
3297c478bd9Sstevel@tonic-gate 	 *
3307c478bd9Sstevel@tonic-gate 	 * e.g.
3317c478bd9Sstevel@tonic-gate 	 * /export is shared as "sec=sys,rw=dfs-test-4,sec=krb5,rw"
3327c478bd9Sstevel@tonic-gate 	 * /export/home is shared as "sec=sys,rw"
3337c478bd9Sstevel@tonic-gate 	 *
3347c478bd9Sstevel@tonic-gate 	 * When the client mounts /export with sec=sys, the client
3357c478bd9Sstevel@tonic-gate 	 * would get a limited view with RO access on /export to see
3367c478bd9Sstevel@tonic-gate 	 * "home" only because the client is allowed to access
3377c478bd9Sstevel@tonic-gate 	 * /export/home with auth_sys.
3387c478bd9Sstevel@tonic-gate 	 */
3397c478bd9Sstevel@tonic-gate 	if (access & NFSAUTH_DENIED || access & NFSAUTH_WRONGSEC) {
3407c478bd9Sstevel@tonic-gate 		/*
3417c478bd9Sstevel@tonic-gate 		 * Allow ro permission with LIMITED view if there is a
3427c478bd9Sstevel@tonic-gate 		 * sub-dir exported under vp.
3437c478bd9Sstevel@tonic-gate 		 */
344b89a8333Snatalie li - Sun Microsystems - Irvine United States 		if (has_visible(exi, vp))
3457c478bd9Sstevel@tonic-gate 			return (NFSAUTH_LIMITED);
3467c478bd9Sstevel@tonic-gate 	}
3477c478bd9Sstevel@tonic-gate 
3487c478bd9Sstevel@tonic-gate 	return (access);
3497c478bd9Sstevel@tonic-gate }
3507c478bd9Sstevel@tonic-gate 
3511cc55349Srmesta static void
sys_log(const char * msg)3521cc55349Srmesta sys_log(const char *msg)
3531cc55349Srmesta {
3541cc55349Srmesta 	static time_t	tstamp = 0;
3551cc55349Srmesta 	time_t		now;
3561cc55349Srmesta 
3571cc55349Srmesta 	/*
3581cc55349Srmesta 	 * msg is shown (at most) once per minute
3591cc55349Srmesta 	 */
3601cc55349Srmesta 	now = gethrestime_sec();
3611cc55349Srmesta 	if ((tstamp + 60) < now) {
3621cc55349Srmesta 		tstamp = now;
3631cc55349Srmesta 		cmn_err(CE_WARN, msg);
3641cc55349Srmesta 	}
3651cc55349Srmesta }
3661cc55349Srmesta 
3677c478bd9Sstevel@tonic-gate /*
3683ccecb66SThomas Haynes  * Callup to the mountd to get access information in the kernel.
3697c478bd9Sstevel@tonic-gate  */
3703ccecb66SThomas Haynes static bool_t
nfsauth_retrieve(nfsauth_globals_t * nag,struct exportinfo * exi,char * req_netid,int flavor,struct netbuf * addr,int * access,cred_t * clnt_cred,uid_t * srv_uid,gid_t * srv_gid,uint_t * srv_gids_cnt,gid_t ** srv_gids)3710dfe541eSEvan Layton nfsauth_retrieve(nfsauth_globals_t *nag, struct exportinfo *exi,
3720dfe541eSEvan Layton     char *req_netid, int flavor, struct netbuf *addr, int *access,
3730dfe541eSEvan Layton     cred_t *clnt_cred, uid_t *srv_uid, gid_t *srv_gid, uint_t *srv_gids_cnt,
3740dfe541eSEvan Layton     gid_t **srv_gids)
3757c478bd9Sstevel@tonic-gate {
3761cc55349Srmesta 	varg_t			  varg = {0};
3771cc55349Srmesta 	nfsauth_res_t		  res = {0};
37889621fe1SMarcel Telka 	XDR			  xdrs;
3791cc55349Srmesta 	size_t			  absz;
3801cc55349Srmesta 	caddr_t			  abuf;
3811cc55349Srmesta 	int			  last = 0;
3821cc55349Srmesta 	door_arg_t		  da;
3831cc55349Srmesta 	door_info_t		  di;
3841cc55349Srmesta 	door_handle_t		  dh;
3851cc55349Srmesta 	uint_t			  ntries = 0;
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 	/*
3887c478bd9Sstevel@tonic-gate 	 * No entry in the cache for this client/flavor
3897c478bd9Sstevel@tonic-gate 	 * so we need to call the nfsauth service in the
3907c478bd9Sstevel@tonic-gate 	 * mount daemon.
3917c478bd9Sstevel@tonic-gate 	 */
3923ccecb66SThomas Haynes 
3931cc55349Srmesta 	varg.vers = V_PROTO;
3941cc55349Srmesta 	varg.arg_u.arg.cmd = NFSAUTH_ACCESS;
3953ccecb66SThomas Haynes 	varg.arg_u.arg.areq.req_client.n_len = addr->len;
3963ccecb66SThomas Haynes 	varg.arg_u.arg.areq.req_client.n_bytes = addr->buf;
3973ccecb66SThomas Haynes 	varg.arg_u.arg.areq.req_netid = req_netid;
3981cc55349Srmesta 	varg.arg_u.arg.areq.req_path = exi->exi_export.ex_path;
3991cc55349Srmesta 	varg.arg_u.arg.areq.req_flavor = flavor;
40012fb3699SMarcel Telka 	varg.arg_u.arg.areq.req_clnt_uid = crgetuid(clnt_cred);
40112fb3699SMarcel Telka 	varg.arg_u.arg.areq.req_clnt_gid = crgetgid(clnt_cred);
40212fb3699SMarcel Telka 	varg.arg_u.arg.areq.req_clnt_gids.len = crgetngroups(clnt_cred);
40312fb3699SMarcel Telka 	varg.arg_u.arg.areq.req_clnt_gids.val = (gid_t *)crgetgroups(clnt_cred);
40489621fe1SMarcel Telka 
40589621fe1SMarcel Telka 	DTRACE_PROBE1(nfsserv__func__nfsauth__varg, varg_t *, &varg);
4067c478bd9Sstevel@tonic-gate 
4071cc55349Srmesta 	/*
4081cc55349Srmesta 	 * Setup the XDR stream for encoding the arguments. Notice that
4091cc55349Srmesta 	 * in addition to the args having variable fields (req_netid and
4101cc55349Srmesta 	 * req_path), the argument data structure is itself versioned,
4111cc55349Srmesta 	 * so we need to make sure we can size the arguments buffer
4121cc55349Srmesta 	 * appropriately to encode all the args. If we can't get sizing
4131cc55349Srmesta 	 * info _or_ properly encode the arguments, there's really no
4141cc55349Srmesta 	 * point in continuting, so we fail the request.
4151cc55349Srmesta 	 */
41689621fe1SMarcel Telka 	if ((absz = xdr_sizeof(xdr_varg, &varg)) == 0) {
4173ccecb66SThomas Haynes 		*access = NFSAUTH_DENIED;
4183ccecb66SThomas Haynes 		return (FALSE);
4191cc55349Srmesta 	}
4203ccecb66SThomas Haynes 
4211cc55349Srmesta 	abuf = (caddr_t)kmem_alloc(absz, KM_SLEEP);
42289621fe1SMarcel Telka 	xdrmem_create(&xdrs, abuf, absz, XDR_ENCODE);
42389621fe1SMarcel Telka 	if (!xdr_varg(&xdrs, &varg)) {
42489621fe1SMarcel Telka 		XDR_DESTROY(&xdrs);
4251cc55349Srmesta 		goto fail;
4261cc55349Srmesta 	}
42789621fe1SMarcel Telka 	XDR_DESTROY(&xdrs);
4281cc55349Srmesta 
4291cc55349Srmesta 	/*
43089621fe1SMarcel Telka 	 * Prepare the door arguments
43189621fe1SMarcel Telka 	 *
43289621fe1SMarcel Telka 	 * We don't know the size of the message the daemon
43389621fe1SMarcel Telka 	 * will pass back to us.  By setting rbuf to NULL,
43489621fe1SMarcel Telka 	 * we force the door code to allocate a buf of the
43589621fe1SMarcel Telka 	 * appropriate size.  We must set rsize > 0, however,
43689621fe1SMarcel Telka 	 * else the door code acts as if no response was
43789621fe1SMarcel Telka 	 * expected and doesn't pass the data to us.
4381cc55349Srmesta 	 */
4391cc55349Srmesta 	da.data_ptr = (char *)abuf;
4401cc55349Srmesta 	da.data_size = absz;
4411cc55349Srmesta 	da.desc_ptr = NULL;
4421cc55349Srmesta 	da.desc_num = 0;
44389621fe1SMarcel Telka 	da.rbuf = NULL;
44489621fe1SMarcel Telka 	da.rsize = 1;
4451cc55349Srmesta 
44689621fe1SMarcel Telka retry:
4470dfe541eSEvan Layton 	mutex_enter(&nag->mountd_lock);
4480dfe541eSEvan Layton 	dh = nag->mountd_dh;
44989621fe1SMarcel Telka 	if (dh != NULL)
45089621fe1SMarcel Telka 		door_ki_hold(dh);
4510dfe541eSEvan Layton 	mutex_exit(&nag->mountd_lock);
4521cc55349Srmesta 
45389621fe1SMarcel Telka 	if (dh == NULL) {
45489621fe1SMarcel Telka 		/*
45589621fe1SMarcel Telka 		 * The rendezvous point has not been established yet!
456bbf21555SRichard Lowe 		 * This could mean that either mountd(8) has not yet
45789621fe1SMarcel Telka 		 * been started or that _this_ routine nuked the door
45889621fe1SMarcel Telka 		 * handle after receiving an EINTR for a REVOKED door.
45989621fe1SMarcel Telka 		 *
46089621fe1SMarcel Telka 		 * Returning NFSAUTH_DROP will cause the NFS client
46189621fe1SMarcel Telka 		 * to retransmit the request, so let's try to be more
46289621fe1SMarcel Telka 		 * rescillient and attempt for ntries before we bail.
46389621fe1SMarcel Telka 		 */
46489621fe1SMarcel Telka 		if (++ntries % NFSAUTH_DR_TRYCNT) {
46589621fe1SMarcel Telka 			delay(hz);
46689621fe1SMarcel Telka 			goto retry;
46789621fe1SMarcel Telka 		}
4681cc55349Srmesta 
46989621fe1SMarcel Telka 		kmem_free(abuf, absz);
47089621fe1SMarcel Telka 
47189621fe1SMarcel Telka 		sys_log("nfsauth: mountd has not established door");
47289621fe1SMarcel Telka 		*access = NFSAUTH_DROP;
47389621fe1SMarcel Telka 		return (FALSE);
47489621fe1SMarcel Telka 	}
4751cc55349Srmesta 
47689621fe1SMarcel Telka 	ntries = 0;
47789621fe1SMarcel Telka 
47889621fe1SMarcel Telka 	/*
47989621fe1SMarcel Telka 	 * Now that we've got what we need, place the call.
48089621fe1SMarcel Telka 	 */
48189621fe1SMarcel Telka 	switch (door_ki_upcall_limited(dh, &da, NULL, SIZE_MAX, 0)) {
48289621fe1SMarcel Telka 	case 0:				/* Success */
48389621fe1SMarcel Telka 		door_ki_rele(dh);
48489621fe1SMarcel Telka 
48589621fe1SMarcel Telka 		if (da.data_ptr == NULL && da.data_size == 0) {
4861cc55349Srmesta 			/*
48789621fe1SMarcel Telka 			 * The door_return that contained the data
48889621fe1SMarcel Telka 			 * failed! We're here because of the 2nd
48989621fe1SMarcel Telka 			 * door_return (w/o data) such that we can
49089621fe1SMarcel Telka 			 * get control of the thread (and exit
49189621fe1SMarcel Telka 			 * gracefully).
4921cc55349Srmesta 			 */
49389621fe1SMarcel Telka 			DTRACE_PROBE1(nfsserv__func__nfsauth__door__nil,
49489621fe1SMarcel Telka 			    door_arg_t *, &da);
49589621fe1SMarcel Telka 			goto fail;
49689621fe1SMarcel Telka 		}
49789621fe1SMarcel Telka 
49889621fe1SMarcel Telka 		break;
49989621fe1SMarcel Telka 
50089621fe1SMarcel Telka 	case EAGAIN:
50189621fe1SMarcel Telka 		/*
50289621fe1SMarcel Telka 		 * Server out of resources; back off for a bit
50389621fe1SMarcel Telka 		 */
50489621fe1SMarcel Telka 		door_ki_rele(dh);
50589621fe1SMarcel Telka 		delay(hz);
50689621fe1SMarcel Telka 		goto retry;
50789621fe1SMarcel Telka 		/* NOTREACHED */
50889621fe1SMarcel Telka 
50989621fe1SMarcel Telka 	case EINTR:
51089621fe1SMarcel Telka 		if (!door_ki_info(dh, &di)) {
5111cc55349Srmesta 			door_ki_rele(dh);
5121cc55349Srmesta 
51389621fe1SMarcel Telka 			if (di.di_attributes & DOOR_REVOKED) {
5141cc55349Srmesta 				/*
51589621fe1SMarcel Telka 				 * The server barfed and revoked
51689621fe1SMarcel Telka 				 * the (existing) door on us; we
517bbf21555SRichard Lowe 				 * want to wait to give smf(7) a
518bbf21555SRichard Lowe 				 * chance to restart mountd(8)
51989621fe1SMarcel Telka 				 * and establish a new door handle.
5201cc55349Srmesta 				 */
5210dfe541eSEvan Layton 				mutex_enter(&nag->mountd_lock);
5220dfe541eSEvan Layton 				if (dh == nag->mountd_dh) {
5230dfe541eSEvan Layton 					door_ki_rele(nag->mountd_dh);
5240dfe541eSEvan Layton 					nag->mountd_dh = NULL;
52589621fe1SMarcel Telka 				}
5260dfe541eSEvan Layton 				mutex_exit(&nag->mountd_lock);
5271cc55349Srmesta 				delay(hz);
5281cc55349Srmesta 				goto retry;
5291cc55349Srmesta 			}
53089621fe1SMarcel Telka 			/*
53189621fe1SMarcel Telka 			 * If the door was _not_ revoked on us,
53289621fe1SMarcel Telka 			 * then more than likely we took an INTR,
53389621fe1SMarcel Telka 			 * so we need to fail the operation.
53489621fe1SMarcel Telka 			 */
5351cc55349Srmesta 			goto fail;
53689621fe1SMarcel Telka 		}
53789621fe1SMarcel Telka 		/*
53889621fe1SMarcel Telka 		 * The only failure that can occur from getting
53989621fe1SMarcel Telka 		 * the door info is EINVAL, so we let the code
54089621fe1SMarcel Telka 		 * below handle it.
54189621fe1SMarcel Telka 		 */
54289621fe1SMarcel Telka 		/* FALLTHROUGH */
54389621fe1SMarcel Telka 
54489621fe1SMarcel Telka 	case EBADF:
54589621fe1SMarcel Telka 	case EINVAL:
54689621fe1SMarcel Telka 	default:
54789621fe1SMarcel Telka 		/*
54889621fe1SMarcel Telka 		 * If we have a stale door handle, give smf a last
54989621fe1SMarcel Telka 		 * chance to start it by sleeping for a little bit.
55089621fe1SMarcel Telka 		 * If we're still hosed, we'll fail the call.
55189621fe1SMarcel Telka 		 *
55289621fe1SMarcel Telka 		 * Since we're going to reacquire the door handle
55389621fe1SMarcel Telka 		 * upon the retry, we opt to sleep for a bit and
55489621fe1SMarcel Telka 		 * _not_ to clear mountd_dh. If mountd restarted
55589621fe1SMarcel Telka 		 * and was able to set mountd_dh, we should see
55689621fe1SMarcel Telka 		 * the new instance; if not, we won't get caught
55789621fe1SMarcel Telka 		 * up in the retry/DELAY loop.
55889621fe1SMarcel Telka 		 */
55989621fe1SMarcel Telka 		door_ki_rele(dh);
56089621fe1SMarcel Telka 		if (!last) {
56189621fe1SMarcel Telka 			delay(hz);
56289621fe1SMarcel Telka 			last++;
56389621fe1SMarcel Telka 			goto retry;
56489621fe1SMarcel Telka 		}
56589621fe1SMarcel Telka 		sys_log("nfsauth: stale mountd door handle");
56689621fe1SMarcel Telka 		goto fail;
5671cc55349Srmesta 	}
5681cc55349Srmesta 
56989621fe1SMarcel Telka 	ASSERT(da.rbuf != NULL);
57089621fe1SMarcel Telka 
5711cc55349Srmesta 	/*
5721cc55349Srmesta 	 * No door errors encountered; setup the XDR stream for decoding
5731cc55349Srmesta 	 * the results. If we fail to decode the results, we've got no
5741cc55349Srmesta 	 * other recourse than to fail the request.
5751cc55349Srmesta 	 */
57689621fe1SMarcel Telka 	xdrmem_create(&xdrs, da.rbuf, da.rsize, XDR_DECODE);
57789621fe1SMarcel Telka 	if (!xdr_nfsauth_res(&xdrs, &res)) {
57889621fe1SMarcel Telka 		xdr_free(xdr_nfsauth_res, (char *)&res);
57989621fe1SMarcel Telka 		XDR_DESTROY(&xdrs);
58089621fe1SMarcel Telka 		kmem_free(da.rbuf, da.rsize);
5811cc55349Srmesta 		goto fail;
58289621fe1SMarcel Telka 	}
58389621fe1SMarcel Telka 	XDR_DESTROY(&xdrs);
58489621fe1SMarcel Telka 	kmem_free(da.rbuf, da.rsize);
5851cc55349Srmesta 
5861cc55349Srmesta 	DTRACE_PROBE1(nfsserv__func__nfsauth__results, nfsauth_res_t *, &res);
5871cc55349Srmesta 	switch (res.stat) {
5881cc55349Srmesta 		case NFSAUTH_DR_OKAY:
5893ccecb66SThomas Haynes 			*access = res.ares.auth_perm;
5905cb0d679SMarcel Telka 			*srv_uid = res.ares.auth_srv_uid;
5915cb0d679SMarcel Telka 			*srv_gid = res.ares.auth_srv_gid;
592d1580181SBryan Cantrill 
593d1580181SBryan Cantrill 			if ((*srv_gids_cnt = res.ares.auth_srv_gids.len) != 0) {
594d1580181SBryan Cantrill 				*srv_gids = kmem_alloc(*srv_gids_cnt *
595d1580181SBryan Cantrill 				    sizeof (gid_t), KM_SLEEP);
596d1580181SBryan Cantrill 				bcopy(res.ares.auth_srv_gids.val, *srv_gids,
597d1580181SBryan Cantrill 				    *srv_gids_cnt * sizeof (gid_t));
598d1580181SBryan Cantrill 			} else {
599d1580181SBryan Cantrill 				*srv_gids = NULL;
600d1580181SBryan Cantrill 			}
601d1580181SBryan Cantrill 
6021cc55349Srmesta 			break;
6031cc55349Srmesta 
6041cc55349Srmesta 		case NFSAUTH_DR_EFAIL:
6051cc55349Srmesta 		case NFSAUTH_DR_DECERR:
6061cc55349Srmesta 		case NFSAUTH_DR_BADCMD:
6071cc55349Srmesta 		default:
60889621fe1SMarcel Telka 			xdr_free(xdr_nfsauth_res, (char *)&res);
6091cc55349Srmesta fail:
6103ccecb66SThomas Haynes 			*access = NFSAUTH_DENIED;
6111cc55349Srmesta 			kmem_free(abuf, absz);
6123ccecb66SThomas Haynes 			return (FALSE);
6131cc55349Srmesta 			/* NOTREACHED */
6147c478bd9Sstevel@tonic-gate 	}
6157c478bd9Sstevel@tonic-gate 
61689621fe1SMarcel Telka 	xdr_free(xdr_nfsauth_res, (char *)&res);
61789621fe1SMarcel Telka 	kmem_free(abuf, absz);
61889621fe1SMarcel Telka 
6193ccecb66SThomas Haynes 	return (TRUE);
6203ccecb66SThomas Haynes }
6213ccecb66SThomas Haynes 
6223ccecb66SThomas Haynes static void
nfsauth_refresh_thread(nfsauth_globals_t * nag)6230dfe541eSEvan Layton nfsauth_refresh_thread(nfsauth_globals_t *nag)
6243ccecb66SThomas Haynes {
6253ccecb66SThomas Haynes 	refreshq_exi_node_t	*ren;
6263ccecb66SThomas Haynes 	refreshq_auth_node_t	*ran;
6273ccecb66SThomas Haynes 
6283ccecb66SThomas Haynes 	struct exportinfo	*exi;
6293ccecb66SThomas Haynes 
6303ccecb66SThomas Haynes 	int			access;
6313ccecb66SThomas Haynes 	bool_t			retrieval;
6323ccecb66SThomas Haynes 
6333ccecb66SThomas Haynes 	callb_cpr_t		cprinfo;
6343ccecb66SThomas Haynes 
6350dfe541eSEvan Layton 	CALLB_CPR_INIT(&cprinfo, &nag->refreshq_lock, callb_generic_cpr,
6363ccecb66SThomas Haynes 	    "nfsauth_refresh");
6373ccecb66SThomas Haynes 
6383ccecb66SThomas Haynes 	for (;;) {
6390dfe541eSEvan Layton 		mutex_enter(&nag->refreshq_lock);
6400dfe541eSEvan Layton 		if (nag->refreshq_thread_state != REFRESHQ_THREAD_RUNNING) {
6413ccecb66SThomas Haynes 			/* Keep the hold on the lock! */
6423ccecb66SThomas Haynes 			break;
6433ccecb66SThomas Haynes 		}
6443ccecb66SThomas Haynes 
6450dfe541eSEvan Layton 		ren = list_remove_head(&nag->refreshq_queue);
6463ccecb66SThomas Haynes 		if (ren == NULL) {
6473ccecb66SThomas Haynes 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
6480dfe541eSEvan Layton 			cv_wait(&nag->refreshq_cv, &nag->refreshq_lock);
6490dfe541eSEvan Layton 			CALLB_CPR_SAFE_END(&cprinfo, &nag->refreshq_lock);
6500dfe541eSEvan Layton 			mutex_exit(&nag->refreshq_lock);
6513ccecb66SThomas Haynes 			continue;
6523ccecb66SThomas Haynes 		}
6530dfe541eSEvan Layton 		mutex_exit(&nag->refreshq_lock);
6543ccecb66SThomas Haynes 
6553ccecb66SThomas Haynes 		exi = ren->ren_exi;
6563ccecb66SThomas Haynes 		ASSERT(exi != NULL);
6573ccecb66SThomas Haynes 
65835107df5SMarcel Telka 		/*
65935107df5SMarcel Telka 		 * Since the ren was removed from the refreshq_queue above,
66035107df5SMarcel Telka 		 * this is the only thread aware about the ren existence, so we
66135107df5SMarcel Telka 		 * have the exclusive ownership of it and we do not need to
66235107df5SMarcel Telka 		 * protect it by any lock.
66335107df5SMarcel Telka 		 */
6643ccecb66SThomas Haynes 		while ((ran = list_remove_head(&ren->ren_authlist))) {
66571da0c32SMarcel Telka 			uid_t uid;
66671da0c32SMarcel Telka 			gid_t gid;
66789621fe1SMarcel Telka 			uint_t ngids;
66889621fe1SMarcel Telka 			gid_t *gids;
66935107df5SMarcel Telka 			struct auth_cache *p = ran->ran_auth;
67071da0c32SMarcel Telka 			char *netid = ran->ran_netid;
67135107df5SMarcel Telka 
67235107df5SMarcel Telka 			ASSERT(p != NULL);
67371da0c32SMarcel Telka 			ASSERT(netid != NULL);
67471da0c32SMarcel Telka 
67535107df5SMarcel Telka 			kmem_free(ran, sizeof (refreshq_auth_node_t));
67635107df5SMarcel Telka 
67771da0c32SMarcel Telka 			mutex_enter(&p->auth_lock);
67871da0c32SMarcel Telka 
6793ccecb66SThomas Haynes 			/*
68071da0c32SMarcel Telka 			 * Once the entry goes INVALID, it can not change
68171da0c32SMarcel Telka 			 * state.
68271da0c32SMarcel Telka 			 *
68371da0c32SMarcel Telka 			 * No need to refresh entries also in a case we are
68471da0c32SMarcel Telka 			 * just shutting down.
6853ccecb66SThomas Haynes 			 *
68671da0c32SMarcel Telka 			 * In general, there is no need to hold the
68771da0c32SMarcel Telka 			 * refreshq_lock to test the refreshq_thread_state.  We
68871da0c32SMarcel Telka 			 * do hold it at other places because there is some
68971da0c32SMarcel Telka 			 * related thread synchronization (or some other tasks)
69071da0c32SMarcel Telka 			 * close to the refreshq_thread_state check.
69171da0c32SMarcel Telka 			 *
69271da0c32SMarcel Telka 			 * The check for the refreshq_thread_state value here
69371da0c32SMarcel Telka 			 * is purely advisory to allow the faster
69471da0c32SMarcel Telka 			 * nfsauth_refresh_thread() shutdown.  In a case we
69571da0c32SMarcel Telka 			 * will miss such advisory, nothing catastrophic
69671da0c32SMarcel Telka 			 * happens: we will just spin longer here before the
69771da0c32SMarcel Telka 			 * shutdown.
6983ccecb66SThomas Haynes 			 */
69971da0c32SMarcel Telka 			if (p->auth_state == NFS_AUTH_INVALID ||
7000dfe541eSEvan Layton 			    nag->refreshq_thread_state !=
7010dfe541eSEvan Layton 			    REFRESHQ_THREAD_RUNNING) {
70271da0c32SMarcel Telka 				mutex_exit(&p->auth_lock);
7033ccecb66SThomas Haynes 
70471da0c32SMarcel Telka 				if (p->auth_state == NFS_AUTH_INVALID)
70571da0c32SMarcel Telka 					nfsauth_free_node(p);
7063ccecb66SThomas Haynes 
70771da0c32SMarcel Telka 				strfree(netid);
7083ccecb66SThomas Haynes 
7093ccecb66SThomas Haynes 				continue;
7103ccecb66SThomas Haynes 			}
7113ccecb66SThomas Haynes 
71271da0c32SMarcel Telka 			/*
71371da0c32SMarcel Telka 			 * Make sure the state is valid.  Note that once we
71471da0c32SMarcel Telka 			 * change the state to NFS_AUTH_REFRESHING, no other
71571da0c32SMarcel Telka 			 * thread will be able to work on this entry.
71671da0c32SMarcel Telka 			 */
71771da0c32SMarcel Telka 			ASSERT(p->auth_state == NFS_AUTH_STALE);
71871da0c32SMarcel Telka 
7193ccecb66SThomas Haynes 			p->auth_state = NFS_AUTH_REFRESHING;
7203ccecb66SThomas Haynes 			mutex_exit(&p->auth_lock);
7213ccecb66SThomas Haynes 
7223ccecb66SThomas Haynes 			DTRACE_PROBE2(nfsauth__debug__cache__refresh,
7233ccecb66SThomas Haynes 			    struct exportinfo *, exi,
7243ccecb66SThomas Haynes 			    struct auth_cache *, p);
7253ccecb66SThomas Haynes 
7263ccecb66SThomas Haynes 			/*
7273ccecb66SThomas Haynes 			 * The first caching of the access rights
7283ccecb66SThomas Haynes 			 * is done with the netid pulled out of the
7293ccecb66SThomas Haynes 			 * request from the client. All subsequent
7303ccecb66SThomas Haynes 			 * users of the cache may or may not have
7313ccecb66SThomas Haynes 			 * the same netid. It doesn't matter. So
7323ccecb66SThomas Haynes 			 * when we refresh, we simply use the netid
7333ccecb66SThomas Haynes 			 * of the request which triggered the
7343ccecb66SThomas Haynes 			 * refresh attempt.
7353ccecb66SThomas Haynes 			 */
7360dfe541eSEvan Layton 			retrieval = nfsauth_retrieve(nag, exi, netid,
73771da0c32SMarcel Telka 			    p->auth_flavor, &p->auth_clnt->authc_addr, &access,
73812fb3699SMarcel Telka 			    p->auth_clnt_cred, &uid, &gid, &ngids, &gids);
7393ccecb66SThomas Haynes 
7403ccecb66SThomas Haynes 			/*
7413ccecb66SThomas Haynes 			 * This can only be set in one other place
7423ccecb66SThomas Haynes 			 * and the state has to be NFS_AUTH_FRESH.
7433ccecb66SThomas Haynes 			 */
74471da0c32SMarcel Telka 			strfree(netid);
7453ccecb66SThomas Haynes 
7463ccecb66SThomas Haynes 			mutex_enter(&p->auth_lock);
7473ccecb66SThomas Haynes 			if (p->auth_state == NFS_AUTH_INVALID) {
7483ccecb66SThomas Haynes 				mutex_exit(&p->auth_lock);
74971da0c32SMarcel Telka 				nfsauth_free_node(p);
75089621fe1SMarcel Telka 				if (retrieval == TRUE)
75189621fe1SMarcel Telka 					kmem_free(gids, ngids * sizeof (gid_t));
7523ccecb66SThomas Haynes 			} else {
75335107df5SMarcel Telka 				/*
75435107df5SMarcel Telka 				 * If we got an error, do not reset the
75535107df5SMarcel Telka 				 * time. This will cause the next access
75635107df5SMarcel Telka 				 * check for the client to reschedule this
75735107df5SMarcel Telka 				 * node.
75835107df5SMarcel Telka 				 */
75935107df5SMarcel Telka 				if (retrieval == TRUE) {
76035107df5SMarcel Telka 					p->auth_access = access;
76189621fe1SMarcel Telka 
76271da0c32SMarcel Telka 					p->auth_srv_uid = uid;
76371da0c32SMarcel Telka 					p->auth_srv_gid = gid;
76489621fe1SMarcel Telka 					kmem_free(p->auth_srv_gids,
76589621fe1SMarcel Telka 					    p->auth_srv_ngids * sizeof (gid_t));
76689621fe1SMarcel Telka 					p->auth_srv_ngids = ngids;
76789621fe1SMarcel Telka 					p->auth_srv_gids = gids;
76889621fe1SMarcel Telka 
76935107df5SMarcel Telka 					p->auth_freshness = gethrestime_sec();
77035107df5SMarcel Telka 				}
7713ccecb66SThomas Haynes 				p->auth_state = NFS_AUTH_FRESH;
77271da0c32SMarcel Telka 
77371da0c32SMarcel Telka 				cv_broadcast(&p->auth_cv);
7743ccecb66SThomas Haynes 				mutex_exit(&p->auth_lock);
7753ccecb66SThomas Haynes 			}
7763ccecb66SThomas Haynes 		}
7773ccecb66SThomas Haynes 
7783ccecb66SThomas Haynes 		list_destroy(&ren->ren_authlist);
7793ccecb66SThomas Haynes 		exi_rele(ren->ren_exi);
7803ccecb66SThomas Haynes 		kmem_free(ren, sizeof (refreshq_exi_node_t));
7813ccecb66SThomas Haynes 	}
7823ccecb66SThomas Haynes 
7830dfe541eSEvan Layton 	nag->refreshq_thread_state = REFRESHQ_THREAD_HALTED;
7840dfe541eSEvan Layton 	cv_broadcast(&nag->refreshq_cv);
7853ccecb66SThomas Haynes 	CALLB_CPR_EXIT(&cprinfo);
7860dfe541eSEvan Layton 	DTRACE_PROBE(nfsauth__nfsauth__refresh__thread__exit);
7873ccecb66SThomas Haynes 	zthread_exit();
7883ccecb66SThomas Haynes }
7893ccecb66SThomas Haynes 
79071da0c32SMarcel Telka int
nfsauth_cache_clnt_compar(const void * v1,const void * v2)79171da0c32SMarcel Telka nfsauth_cache_clnt_compar(const void *v1, const void *v2)
79271da0c32SMarcel Telka {
79371da0c32SMarcel Telka 	int c;
79471da0c32SMarcel Telka 
79571da0c32SMarcel Telka 	const struct auth_cache_clnt *a1 = (const struct auth_cache_clnt *)v1;
79671da0c32SMarcel Telka 	const struct auth_cache_clnt *a2 = (const struct auth_cache_clnt *)v2;
79771da0c32SMarcel Telka 
79871da0c32SMarcel Telka 	if (a1->authc_addr.len < a2->authc_addr.len)
79971da0c32SMarcel Telka 		return (-1);
80071da0c32SMarcel Telka 	if (a1->authc_addr.len > a2->authc_addr.len)
80171da0c32SMarcel Telka 		return (1);
80271da0c32SMarcel Telka 
80371da0c32SMarcel Telka 	c = memcmp(a1->authc_addr.buf, a2->authc_addr.buf, a1->authc_addr.len);
80471da0c32SMarcel Telka 	if (c < 0)
80571da0c32SMarcel Telka 		return (-1);
80671da0c32SMarcel Telka 	if (c > 0)
80771da0c32SMarcel Telka 		return (1);
80871da0c32SMarcel Telka 
80971da0c32SMarcel Telka 	return (0);
81071da0c32SMarcel Telka }
81171da0c32SMarcel Telka 
81271da0c32SMarcel Telka static int
nfsauth_cache_compar(const void * v1,const void * v2)81371da0c32SMarcel Telka nfsauth_cache_compar(const void *v1, const void *v2)
81471da0c32SMarcel Telka {
81512fb3699SMarcel Telka 	int c;
81612fb3699SMarcel Telka 
81771da0c32SMarcel Telka 	const struct auth_cache *a1 = (const struct auth_cache *)v1;
81871da0c32SMarcel Telka 	const struct auth_cache *a2 = (const struct auth_cache *)v2;
81971da0c32SMarcel Telka 
82071da0c32SMarcel Telka 	if (a1->auth_flavor < a2->auth_flavor)
82171da0c32SMarcel Telka 		return (-1);
82271da0c32SMarcel Telka 	if (a1->auth_flavor > a2->auth_flavor)
82371da0c32SMarcel Telka 		return (1);
82471da0c32SMarcel Telka 
82512fb3699SMarcel Telka 	if (crgetuid(a1->auth_clnt_cred) < crgetuid(a2->auth_clnt_cred))
82612fb3699SMarcel Telka 		return (-1);
82712fb3699SMarcel Telka 	if (crgetuid(a1->auth_clnt_cred) > crgetuid(a2->auth_clnt_cred))
82812fb3699SMarcel Telka 		return (1);
82912fb3699SMarcel Telka 
83012fb3699SMarcel Telka 	if (crgetgid(a1->auth_clnt_cred) < crgetgid(a2->auth_clnt_cred))
83171da0c32SMarcel Telka 		return (-1);
83212fb3699SMarcel Telka 	if (crgetgid(a1->auth_clnt_cred) > crgetgid(a2->auth_clnt_cred))
83371da0c32SMarcel Telka 		return (1);
83471da0c32SMarcel Telka 
83512fb3699SMarcel Telka 	if (crgetngroups(a1->auth_clnt_cred) < crgetngroups(a2->auth_clnt_cred))
83671da0c32SMarcel Telka 		return (-1);
83712fb3699SMarcel Telka 	if (crgetngroups(a1->auth_clnt_cred) > crgetngroups(a2->auth_clnt_cred))
83812fb3699SMarcel Telka 		return (1);
83912fb3699SMarcel Telka 
84012fb3699SMarcel Telka 	c = memcmp(crgetgroups(a1->auth_clnt_cred),
84112fb3699SMarcel Telka 	    crgetgroups(a2->auth_clnt_cred), crgetngroups(a1->auth_clnt_cred));
84212fb3699SMarcel Telka 	if (c < 0)
84312fb3699SMarcel Telka 		return (-1);
84412fb3699SMarcel Telka 	if (c > 0)
84571da0c32SMarcel Telka 		return (1);
84671da0c32SMarcel Telka 
84771da0c32SMarcel Telka 	return (0);
84871da0c32SMarcel Telka }
84971da0c32SMarcel Telka 
8503ccecb66SThomas Haynes /*
8513ccecb66SThomas Haynes  * Get the access information from the cache or callup to the mountd
8523ccecb66SThomas Haynes  * to get and cache the access information in the kernel.
8533ccecb66SThomas Haynes  */
8545cb0d679SMarcel Telka static int
nfsauth_cache_get(struct exportinfo * exi,struct svc_req * req,int flavor,cred_t * cr,uid_t * uid,gid_t * gid,uint_t * ngids,gid_t ** gids)8555cb0d679SMarcel Telka nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor,
85689621fe1SMarcel Telka     cred_t *cr, uid_t *uid, gid_t *gid, uint_t *ngids, gid_t **gids)
8573ccecb66SThomas Haynes {
8580dfe541eSEvan Layton 	nfsauth_globals_t	*nag;
8590a4b0810SKaren Rochford 	struct netbuf		*taddrmask;
86071da0c32SMarcel Telka 	struct netbuf		addr;	/* temporary copy of client's address */
86171da0c32SMarcel Telka 	const struct netbuf	*claddr;
86271da0c32SMarcel Telka 	avl_tree_t		*tree;
86371da0c32SMarcel Telka 	struct auth_cache	ac;	/* used as a template for avl_find() */
86471da0c32SMarcel Telka 	struct auth_cache_clnt	*c;
86571da0c32SMarcel Telka 	struct auth_cache_clnt	acc;	/* used as a template for avl_find() */
86671da0c32SMarcel Telka 	struct auth_cache	*p = NULL;
8673ccecb66SThomas Haynes 	int			access;
8683ccecb66SThomas Haynes 
8695cb0d679SMarcel Telka 	uid_t			tmpuid;
8705cb0d679SMarcel Telka 	gid_t			tmpgid;
87189621fe1SMarcel Telka 	uint_t			tmpngids;
87289621fe1SMarcel Telka 	gid_t			*tmpgids;
8735cb0d679SMarcel Telka 
87471da0c32SMarcel Telka 	avl_index_t		where;	/* used for avl_find()/avl_insert() */
87571da0c32SMarcel Telka 
8765cb0d679SMarcel Telka 	ASSERT(cr != NULL);
8775cb0d679SMarcel Telka 
8780dfe541eSEvan Layton 	ASSERT3P(curzone->zone_id, ==, exi->exi_zoneid);
8790dfe541eSEvan Layton 	nag = nfsauth_get_zg();
8800dfe541eSEvan Layton 
8813ccecb66SThomas Haynes 	/*
8823ccecb66SThomas Haynes 	 * Now check whether this client already
8833ccecb66SThomas Haynes 	 * has an entry for this flavor in the cache
8843ccecb66SThomas Haynes 	 * for this export.
8853ccecb66SThomas Haynes 	 * Get the caller's address, mask off the
8863ccecb66SThomas Haynes 	 * parts of the address that do not identify
8873ccecb66SThomas Haynes 	 * the host (port number, etc), and then hash
8883ccecb66SThomas Haynes 	 * it to find the chain of cache entries.
8893ccecb66SThomas Haynes 	 */
8903ccecb66SThomas Haynes 
8913ccecb66SThomas Haynes 	claddr = svc_getrpccaller(req->rq_xprt);
8923ccecb66SThomas Haynes 	addr = *claddr;
8930dfe541eSEvan Layton 	if (claddr->len != 0) {
8940dfe541eSEvan Layton 		addr.buf = kmem_alloc(addr.maxlen, KM_SLEEP);
8950dfe541eSEvan Layton 		bcopy(claddr->buf, addr.buf, claddr->len);
8960dfe541eSEvan Layton 	} else {
8970dfe541eSEvan Layton 		addr.buf = NULL;
8980dfe541eSEvan Layton 	}
89971da0c32SMarcel Telka 
9000a4b0810SKaren Rochford 	SVC_GETADDRMASK(req->rq_xprt, SVC_TATTR_ADDRMASK, (void **)&taddrmask);
9010a4b0810SKaren Rochford 	ASSERT(taddrmask != NULL);
90271da0c32SMarcel Telka 	addrmask(&addr, taddrmask);
90371da0c32SMarcel Telka 
90471da0c32SMarcel Telka 	acc.authc_addr = addr;
90571da0c32SMarcel Telka 
90671da0c32SMarcel Telka 	tree = exi->exi_cache[hash(&addr)];
9073ccecb66SThomas Haynes 
9083ccecb66SThomas Haynes 	rw_enter(&exi->exi_cache_lock, RW_READER);
90971da0c32SMarcel Telka 	c = (struct auth_cache_clnt *)avl_find(tree, &acc, NULL);
91071da0c32SMarcel Telka 
91171da0c32SMarcel Telka 	if (c == NULL) {
91271da0c32SMarcel Telka 		struct auth_cache_clnt *nc;
91371da0c32SMarcel Telka 
91471da0c32SMarcel Telka 		rw_exit(&exi->exi_cache_lock);
91571da0c32SMarcel Telka 
916ca783257SDan McDonald 		nc = kmem_alloc(sizeof (*nc), KM_NOSLEEP_LAZY);
91771da0c32SMarcel Telka 		if (nc == NULL)
91871da0c32SMarcel Telka 			goto retrieve;
91971da0c32SMarcel Telka 
92071da0c32SMarcel Telka 		/*
92171da0c32SMarcel Telka 		 * Initialize the new auth_cache_clnt
92271da0c32SMarcel Telka 		 */
92371da0c32SMarcel Telka 		nc->authc_addr = addr;
924ca783257SDan McDonald 		nc->authc_addr.buf = kmem_alloc(addr.maxlen, KM_NOSLEEP_LAZY);
9257bbfa3eeSMarcel Telka 		if (addr.maxlen != 0 && nc->authc_addr.buf == NULL) {
92671da0c32SMarcel Telka 			kmem_free(nc, sizeof (*nc));
92771da0c32SMarcel Telka 			goto retrieve;
92871da0c32SMarcel Telka 		}
92971da0c32SMarcel Telka 		bcopy(addr.buf, nc->authc_addr.buf, addr.len);
93071da0c32SMarcel Telka 		rw_init(&nc->authc_lock, NULL, RW_DEFAULT, NULL);
93171da0c32SMarcel Telka 		avl_create(&nc->authc_tree, nfsauth_cache_compar,
93271da0c32SMarcel Telka 		    sizeof (struct auth_cache),
93371da0c32SMarcel Telka 		    offsetof(struct auth_cache, auth_link));
93471da0c32SMarcel Telka 
93571da0c32SMarcel Telka 		rw_enter(&exi->exi_cache_lock, RW_WRITER);
93671da0c32SMarcel Telka 		c = (struct auth_cache_clnt *)avl_find(tree, &acc, &where);
93771da0c32SMarcel Telka 		if (c == NULL) {
93871da0c32SMarcel Telka 			avl_insert(tree, nc, where);
93971da0c32SMarcel Telka 			rw_downgrade(&exi->exi_cache_lock);
94071da0c32SMarcel Telka 			c = nc;
94171da0c32SMarcel Telka 		} else {
94271da0c32SMarcel Telka 			rw_downgrade(&exi->exi_cache_lock);
94371da0c32SMarcel Telka 
94471da0c32SMarcel Telka 			avl_destroy(&nc->authc_tree);
94571da0c32SMarcel Telka 			rw_destroy(&nc->authc_lock);
9467bbfa3eeSMarcel Telka 			kmem_free(nc->authc_addr.buf, nc->authc_addr.maxlen);
94771da0c32SMarcel Telka 			kmem_free(nc, sizeof (*nc));
94871da0c32SMarcel Telka 		}
9493ccecb66SThomas Haynes 	}
9503ccecb66SThomas Haynes 
95171da0c32SMarcel Telka 	ASSERT(c != NULL);
95271da0c32SMarcel Telka 
95371da0c32SMarcel Telka 	rw_enter(&c->authc_lock, RW_READER);
954a547d306SVitaliy Gusev 
955a547d306SVitaliy Gusev 	ac.auth_flavor = flavor;
956a547d306SVitaliy Gusev 	ac.auth_clnt_cred = cr;
957a547d306SVitaliy Gusev 
95871da0c32SMarcel Telka 	p = (struct auth_cache *)avl_find(&c->authc_tree, &ac, NULL);
95971da0c32SMarcel Telka 
96071da0c32SMarcel Telka 	if (p == NULL) {
96171da0c32SMarcel Telka 		struct auth_cache *np;
96271da0c32SMarcel Telka 
96371da0c32SMarcel Telka 		rw_exit(&c->authc_lock);
96471da0c32SMarcel Telka 
965ca783257SDan McDonald 		np = kmem_cache_alloc(exi_cache_handle, KM_NOSLEEP_LAZY);
96671da0c32SMarcel Telka 		if (np == NULL) {
96771da0c32SMarcel Telka 			rw_exit(&exi->exi_cache_lock);
96871da0c32SMarcel Telka 			goto retrieve;
96971da0c32SMarcel Telka 		}
97071da0c32SMarcel Telka 
97189621fe1SMarcel Telka 		/*
97271da0c32SMarcel Telka 		 * Initialize the new auth_cache
97389621fe1SMarcel Telka 		 */
97471da0c32SMarcel Telka 		np->auth_clnt = c;
97571da0c32SMarcel Telka 		np->auth_flavor = flavor;
976a547d306SVitaliy Gusev 		np->auth_clnt_cred = crdup(cr);
97771da0c32SMarcel Telka 		np->auth_srv_ngids = 0;
97871da0c32SMarcel Telka 		np->auth_srv_gids = NULL;
97971da0c32SMarcel Telka 		np->auth_time = np->auth_freshness = gethrestime_sec();
98071da0c32SMarcel Telka 		np->auth_state = NFS_AUTH_NEW;
98171da0c32SMarcel Telka 		mutex_init(&np->auth_lock, NULL, MUTEX_DEFAULT, NULL);
98271da0c32SMarcel Telka 		cv_init(&np->auth_cv, NULL, CV_DEFAULT, NULL);
98371da0c32SMarcel Telka 
98471da0c32SMarcel Telka 		rw_enter(&c->authc_lock, RW_WRITER);
98571da0c32SMarcel Telka 		rw_exit(&exi->exi_cache_lock);
98689621fe1SMarcel Telka 
98771da0c32SMarcel Telka 		p = (struct auth_cache *)avl_find(&c->authc_tree, &ac, &where);
98871da0c32SMarcel Telka 		if (p == NULL) {
98971da0c32SMarcel Telka 			avl_insert(&c->authc_tree, np, where);
99071da0c32SMarcel Telka 			rw_downgrade(&c->authc_lock);
99171da0c32SMarcel Telka 			p = np;
99271da0c32SMarcel Telka 		} else {
99371da0c32SMarcel Telka 			rw_downgrade(&c->authc_lock);
99471da0c32SMarcel Telka 
99571da0c32SMarcel Telka 			cv_destroy(&np->auth_cv);
99671da0c32SMarcel Telka 			mutex_destroy(&np->auth_lock);
997a547d306SVitaliy Gusev 			crfree(np->auth_clnt_cred);
99871da0c32SMarcel Telka 			kmem_cache_free(exi_cache_handle, np);
99971da0c32SMarcel Telka 		}
100071da0c32SMarcel Telka 	} else {
100171da0c32SMarcel Telka 		rw_exit(&exi->exi_cache_lock);
100271da0c32SMarcel Telka 	}
100371da0c32SMarcel Telka 
100471da0c32SMarcel Telka 	mutex_enter(&p->auth_lock);
100571da0c32SMarcel Telka 	rw_exit(&c->authc_lock);
100671da0c32SMarcel Telka 
100771da0c32SMarcel Telka 	/*
100871da0c32SMarcel Telka 	 * If the entry is in the WAITING state then some other thread is just
100971da0c32SMarcel Telka 	 * retrieving the required info.  The entry was either NEW, or the list
101071da0c32SMarcel Telka 	 * of client's supplemental groups is going to be changed (either by
101171da0c32SMarcel Telka 	 * this thread, or by some other thread).  We need to wait until the
101271da0c32SMarcel Telka 	 * nfsauth_retrieve() is done.
101371da0c32SMarcel Telka 	 */
101471da0c32SMarcel Telka 	while (p->auth_state == NFS_AUTH_WAITING)
101571da0c32SMarcel Telka 		cv_wait(&p->auth_cv, &p->auth_lock);
101671da0c32SMarcel Telka 
101771da0c32SMarcel Telka 	/*
101871da0c32SMarcel Telka 	 * Here the entry cannot be in WAITING or INVALID state.
101971da0c32SMarcel Telka 	 */
102071da0c32SMarcel Telka 	ASSERT(p->auth_state != NFS_AUTH_WAITING);
102171da0c32SMarcel Telka 	ASSERT(p->auth_state != NFS_AUTH_INVALID);
102271da0c32SMarcel Telka 
102371da0c32SMarcel Telka 	/*
102471da0c32SMarcel Telka 	 * If the cache entry is not valid yet, we need to retrieve the
102571da0c32SMarcel Telka 	 * info ourselves.
102671da0c32SMarcel Telka 	 */
102771da0c32SMarcel Telka 	if (p->auth_state == NFS_AUTH_NEW) {
102871da0c32SMarcel Telka 		bool_t res;
102971da0c32SMarcel Telka 		/*
103071da0c32SMarcel Telka 		 * NFS_AUTH_NEW is the default output auth_state value in a
103171da0c32SMarcel Telka 		 * case we failed somewhere below.
103271da0c32SMarcel Telka 		 */
103371da0c32SMarcel Telka 		auth_state_t state = NFS_AUTH_NEW;
103471da0c32SMarcel Telka 
103571da0c32SMarcel Telka 		p->auth_state = NFS_AUTH_WAITING;
103671da0c32SMarcel Telka 		mutex_exit(&p->auth_lock);
10377bbfa3eeSMarcel Telka 		kmem_free(addr.buf, addr.maxlen);
103871da0c32SMarcel Telka 		addr = p->auth_clnt->authc_addr;
103971da0c32SMarcel Telka 
1040*f44e1126SVitaliy Gusev 		nfsauth_cache_miss++;
104171da0c32SMarcel Telka 
10420dfe541eSEvan Layton 		res = nfsauth_retrieve(nag, exi, svc_getnetid(req->rq_xprt),
10430dfe541eSEvan Layton 		    flavor, &addr, &access, cr, &tmpuid, &tmpgid, &tmpngids,
10440dfe541eSEvan Layton 		    &tmpgids);
104571da0c32SMarcel Telka 
104671da0c32SMarcel Telka 		p->auth_access = access;
104771da0c32SMarcel Telka 		p->auth_time = p->auth_freshness = gethrestime_sec();
104871da0c32SMarcel Telka 
104971da0c32SMarcel Telka 		if (res == TRUE) {
105071da0c32SMarcel Telka 			if (uid != NULL)
105171da0c32SMarcel Telka 				*uid = tmpuid;
105271da0c32SMarcel Telka 			if (gid != NULL)
105371da0c32SMarcel Telka 				*gid = tmpgid;
105471da0c32SMarcel Telka 			if (ngids != NULL && gids != NULL) {
105571da0c32SMarcel Telka 				*ngids = tmpngids;
105671da0c32SMarcel Telka 				*gids = tmpgids;
105771da0c32SMarcel Telka 
105871da0c32SMarcel Telka 				/*
105971da0c32SMarcel Telka 				 * We need a copy of gids for the
106071da0c32SMarcel Telka 				 * auth_cache entry
106171da0c32SMarcel Telka 				 */
106271da0c32SMarcel Telka 				tmpgids = kmem_alloc(tmpngids * sizeof (gid_t),
1063ca783257SDan McDonald 				    KM_NOSLEEP_LAZY);
106471da0c32SMarcel Telka 				if (tmpgids != NULL)
106571da0c32SMarcel Telka 					bcopy(*gids, tmpgids,
106671da0c32SMarcel Telka 					    tmpngids * sizeof (gid_t));
106789621fe1SMarcel Telka 			}
106889621fe1SMarcel Telka 
106971da0c32SMarcel Telka 			if (tmpgids != NULL || tmpngids == 0) {
107071da0c32SMarcel Telka 				p->auth_srv_uid = tmpuid;
107171da0c32SMarcel Telka 				p->auth_srv_gid = tmpgid;
107271da0c32SMarcel Telka 				p->auth_srv_ngids = tmpngids;
107371da0c32SMarcel Telka 				p->auth_srv_gids = tmpgids;
10743d1d816fSMarcel Telka 
107571da0c32SMarcel Telka 				state = NFS_AUTH_FRESH;
107671da0c32SMarcel Telka 			}
107789621fe1SMarcel Telka 		}
10783d1d816fSMarcel Telka 
10793d1d816fSMarcel Telka 		/*
108071da0c32SMarcel Telka 		 * Set the auth_state and notify waiters.
10813d1d816fSMarcel Telka 		 */
108271da0c32SMarcel Telka 		mutex_enter(&p->auth_lock);
108371da0c32SMarcel Telka 		p->auth_state = state;
108471da0c32SMarcel Telka 		cv_broadcast(&p->auth_cv);
108571da0c32SMarcel Telka 		mutex_exit(&p->auth_lock);
108671da0c32SMarcel Telka 	} else {
108771da0c32SMarcel Telka 		uint_t nach;
108871da0c32SMarcel Telka 		time_t refresh;
10893ccecb66SThomas Haynes 
10903ccecb66SThomas Haynes 		refresh = gethrestime_sec() - p->auth_freshness;
10913ccecb66SThomas Haynes 
109271da0c32SMarcel Telka 		p->auth_time = gethrestime_sec();
109371da0c32SMarcel Telka 
109471da0c32SMarcel Telka 		if (uid != NULL)
109571da0c32SMarcel Telka 			*uid = p->auth_srv_uid;
109671da0c32SMarcel Telka 		if (gid != NULL)
109771da0c32SMarcel Telka 			*gid = p->auth_srv_gid;
109871da0c32SMarcel Telka 		if (ngids != NULL && gids != NULL) {
1099d1580181SBryan Cantrill 			if ((*ngids = p->auth_srv_ngids) != 0) {
1100d1580181SBryan Cantrill 				size_t sz = *ngids * sizeof (gid_t);
1101d1580181SBryan Cantrill 				*gids = kmem_alloc(sz, KM_SLEEP);
1102d1580181SBryan Cantrill 				bcopy(p->auth_srv_gids, *gids, sz);
1103d1580181SBryan Cantrill 			} else {
1104d1580181SBryan Cantrill 				*gids = NULL;
1105d1580181SBryan Cantrill 			}
110671da0c32SMarcel Telka 		}
110771da0c32SMarcel Telka 
110871da0c32SMarcel Telka 		access = p->auth_access;
110971da0c32SMarcel Telka 
11103ccecb66SThomas Haynes 		if ((refresh > NFSAUTH_CACHE_REFRESH) &&
11113ccecb66SThomas Haynes 		    p->auth_state == NFS_AUTH_FRESH) {
111271da0c32SMarcel Telka 			refreshq_auth_node_t *ran;
111371da0c32SMarcel Telka 			uint_t nacr;
111471da0c32SMarcel Telka 
11153ccecb66SThomas Haynes 			p->auth_state = NFS_AUTH_STALE;
11163ccecb66SThomas Haynes 			mutex_exit(&p->auth_lock);
11173ccecb66SThomas Haynes 
1118*f44e1126SVitaliy Gusev 			nacr = ++nfsauth_cache_refresh;
11193ccecb66SThomas Haynes 			DTRACE_PROBE3(nfsauth__debug__cache__stale,
11203ccecb66SThomas Haynes 			    struct exportinfo *, exi,
11213ccecb66SThomas Haynes 			    struct auth_cache *, p,
112271da0c32SMarcel Telka 			    uint_t, nacr);
11233ccecb66SThomas Haynes 
11243ccecb66SThomas Haynes 			ran = kmem_alloc(sizeof (refreshq_auth_node_t),
11253ccecb66SThomas Haynes 			    KM_SLEEP);
11263ccecb66SThomas Haynes 			ran->ran_auth = p;
112771da0c32SMarcel Telka 			ran->ran_netid = strdup(svc_getnetid(req->rq_xprt));
11283ccecb66SThomas Haynes 
11290dfe541eSEvan Layton 			mutex_enter(&nag->refreshq_lock);
11300dfe541eSEvan Layton 
11310dfe541eSEvan Layton 			if (nag->refreshq_thread_state ==
11320dfe541eSEvan Layton 			    REFRESHQ_THREAD_NEED_CREATE) {
11330dfe541eSEvan Layton 				/* Launch nfsauth refresh thread */
11340dfe541eSEvan Layton 				nag->refreshq_thread_state =
11350dfe541eSEvan Layton 				    REFRESHQ_THREAD_RUNNING;
11360dfe541eSEvan Layton 				(void) zthread_create(NULL, 0,
11370dfe541eSEvan Layton 				    nfsauth_refresh_thread, nag, 0,
11380dfe541eSEvan Layton 				    minclsyspri);
11390dfe541eSEvan Layton 			}
11400dfe541eSEvan Layton 
11413ccecb66SThomas Haynes 			/*
11420dfe541eSEvan Layton 			 * We should not add a work queue item if the thread
11430dfe541eSEvan Layton 			 * is not accepting them.
11443ccecb66SThomas Haynes 			 */
11450dfe541eSEvan Layton 			if (nag->refreshq_thread_state ==
11460dfe541eSEvan Layton 			    REFRESHQ_THREAD_RUNNING) {
114771da0c32SMarcel Telka 				refreshq_exi_node_t *ren;
114871da0c32SMarcel Telka 
11493ccecb66SThomas Haynes 				/*
11503ccecb66SThomas Haynes 				 * Is there an existing exi_list?
11513ccecb66SThomas Haynes 				 */
11520dfe541eSEvan Layton 				for (ren = list_head(&nag->refreshq_queue);
11533ccecb66SThomas Haynes 				    ren != NULL;
11540dfe541eSEvan Layton 				    ren = list_next(&nag->refreshq_queue,
11550dfe541eSEvan Layton 				    ren)) {
11563ccecb66SThomas Haynes 					if (ren->ren_exi == exi) {
11573ccecb66SThomas Haynes 						list_insert_tail(
11583ccecb66SThomas Haynes 						    &ren->ren_authlist, ran);
11593ccecb66SThomas Haynes 						break;
11603ccecb66SThomas Haynes 					}
11613ccecb66SThomas Haynes 				}
11623ccecb66SThomas Haynes 
11633ccecb66SThomas Haynes 				if (ren == NULL) {
11643ccecb66SThomas Haynes 					ren = kmem_alloc(
11653ccecb66SThomas Haynes 					    sizeof (refreshq_exi_node_t),
11663ccecb66SThomas Haynes 					    KM_SLEEP);
11673ccecb66SThomas Haynes 
11683ccecb66SThomas Haynes 					exi_hold(exi);
11693ccecb66SThomas Haynes 					ren->ren_exi = exi;
11703ccecb66SThomas Haynes 
11713ccecb66SThomas Haynes 					list_create(&ren->ren_authlist,
11723ccecb66SThomas Haynes 					    sizeof (refreshq_auth_node_t),
11733ccecb66SThomas Haynes 					    offsetof(refreshq_auth_node_t,
11743ccecb66SThomas Haynes 					    ran_node));
11753ccecb66SThomas Haynes 
11763ccecb66SThomas Haynes 					list_insert_tail(&ren->ren_authlist,
11773ccecb66SThomas Haynes 					    ran);
11780dfe541eSEvan Layton 					list_insert_tail(&nag->refreshq_queue,
11790dfe541eSEvan Layton 					    ren);
11803ccecb66SThomas Haynes 				}
11813ccecb66SThomas Haynes 
11820dfe541eSEvan Layton 				cv_broadcast(&nag->refreshq_cv);
11833ccecb66SThomas Haynes 			} else {
118471da0c32SMarcel Telka 				strfree(ran->ran_netid);
11853ccecb66SThomas Haynes 				kmem_free(ran, sizeof (refreshq_auth_node_t));
11863ccecb66SThomas Haynes 			}
11873ccecb66SThomas Haynes 
11880dfe541eSEvan Layton 			mutex_exit(&nag->refreshq_lock);
11893ccecb66SThomas Haynes 		} else {
11903ccecb66SThomas Haynes 			mutex_exit(&p->auth_lock);
11913ccecb66SThomas Haynes 		}
11923ccecb66SThomas Haynes 
1193*f44e1126SVitaliy Gusev 		nach = ++nfsauth_cache_hit;
119471da0c32SMarcel Telka 		DTRACE_PROBE2(nfsauth__debug__cache__hit,
119571da0c32SMarcel Telka 		    uint_t, nach,
119671da0c32SMarcel Telka 		    time_t, refresh);
11973ccecb66SThomas Haynes 
11987bbfa3eeSMarcel Telka 		kmem_free(addr.buf, addr.maxlen);
11993ccecb66SThomas Haynes 	}
12003ccecb66SThomas Haynes 
120171da0c32SMarcel Telka 	return (access);
120271da0c32SMarcel Telka 
120389621fe1SMarcel Telka retrieve:
120412fb3699SMarcel Telka 
120571da0c32SMarcel Telka 	/*
120671da0c32SMarcel Telka 	 * Retrieve the required data without caching.
120771da0c32SMarcel Telka 	 */
12083ccecb66SThomas Haynes 
120971da0c32SMarcel Telka 	ASSERT(p == NULL);
12103ccecb66SThomas Haynes 
1211*f44e1126SVitaliy Gusev 	nfsauth_cache_miss++;
121271da0c32SMarcel Telka 
12130dfe541eSEvan Layton 	if (nfsauth_retrieve(nag, exi, svc_getnetid(req->rq_xprt), flavor,
12140dfe541eSEvan Layton 	    &addr, &access, cr, &tmpuid, &tmpgid, &tmpngids, &tmpgids)) {
121571da0c32SMarcel Telka 		if (uid != NULL)
121671da0c32SMarcel Telka 			*uid = tmpuid;
121771da0c32SMarcel Telka 		if (gid != NULL)
121871da0c32SMarcel Telka 			*gid = tmpgid;
121989621fe1SMarcel Telka 		if (ngids != NULL && gids != NULL) {
122071da0c32SMarcel Telka 			*ngids = tmpngids;
122171da0c32SMarcel Telka 			*gids = tmpgids;
122271da0c32SMarcel Telka 		} else {
122389621fe1SMarcel Telka 			kmem_free(tmpgids, tmpngids * sizeof (gid_t));
122489621fe1SMarcel Telka 		}
12257c478bd9Sstevel@tonic-gate 	}
12267c478bd9Sstevel@tonic-gate 
12277bbfa3eeSMarcel Telka 	kmem_free(addr.buf, addr.maxlen);
122871da0c32SMarcel Telka 
12297c478bd9Sstevel@tonic-gate 	return (access);
12307c478bd9Sstevel@tonic-gate }
12317c478bd9Sstevel@tonic-gate 
12327c478bd9Sstevel@tonic-gate /*
12337c478bd9Sstevel@tonic-gate  * Check if the requesting client has access to the filesystem with
12347c478bd9Sstevel@tonic-gate  * a given nfs flavor number which is an explicitly shared flavor.
12357c478bd9Sstevel@tonic-gate  */
12367c478bd9Sstevel@tonic-gate int
nfsauth4_secinfo_access(struct exportinfo * exi,struct svc_req * req,int flavor,int perm,cred_t * cr)12377c478bd9Sstevel@tonic-gate nfsauth4_secinfo_access(struct exportinfo *exi, struct svc_req *req,
123812fb3699SMarcel Telka     int flavor, int perm, cred_t *cr)
12397c478bd9Sstevel@tonic-gate {
12407c478bd9Sstevel@tonic-gate 	int access;
12417c478bd9Sstevel@tonic-gate 
12427c478bd9Sstevel@tonic-gate 	if (! (perm & M_4SEC_EXPORTED)) {
12437c478bd9Sstevel@tonic-gate 		return (NFSAUTH_DENIED);
12447c478bd9Sstevel@tonic-gate 	}
12457c478bd9Sstevel@tonic-gate 
12467c478bd9Sstevel@tonic-gate 	/*
12477c478bd9Sstevel@tonic-gate 	 * Optimize if there are no lists
12487c478bd9Sstevel@tonic-gate 	 */
12495cb0d679SMarcel Telka 	if ((perm & (M_ROOT | M_NONE | M_MAP)) == 0) {
12507c478bd9Sstevel@tonic-gate 		perm &= ~M_4SEC_EXPORTED;
12517c478bd9Sstevel@tonic-gate 		if (perm == M_RO)
12527c478bd9Sstevel@tonic-gate 			return (NFSAUTH_RO);
12537c478bd9Sstevel@tonic-gate 		if (perm == M_RW)
12547c478bd9Sstevel@tonic-gate 			return (NFSAUTH_RW);
12557c478bd9Sstevel@tonic-gate 	}
12567c478bd9Sstevel@tonic-gate 
125789621fe1SMarcel Telka 	access = nfsauth_cache_get(exi, req, flavor, cr, NULL, NULL, NULL,
125889621fe1SMarcel Telka 	    NULL);
12597c478bd9Sstevel@tonic-gate 
12607c478bd9Sstevel@tonic-gate 	return (access);
12617c478bd9Sstevel@tonic-gate }
12627c478bd9Sstevel@tonic-gate 
12637c478bd9Sstevel@tonic-gate int
nfsauth_access(struct exportinfo * exi,struct svc_req * req,cred_t * cr,uid_t * uid,gid_t * gid,uint_t * ngids,gid_t ** gids)12645cb0d679SMarcel Telka nfsauth_access(struct exportinfo *exi, struct svc_req *req, cred_t *cr,
126589621fe1SMarcel Telka     uid_t *uid, gid_t *gid, uint_t *ngids, gid_t **gids)
12667c478bd9Sstevel@tonic-gate {
12677c478bd9Sstevel@tonic-gate 	int access, mapaccess;
12687c478bd9Sstevel@tonic-gate 	struct secinfo *sp;
12697c478bd9Sstevel@tonic-gate 	int i, flavor, perm;
12707c478bd9Sstevel@tonic-gate 	int authnone_entry = -1;
12717c478bd9Sstevel@tonic-gate 
12729e835c76SMarcel Telka 	/*
12739e835c76SMarcel Telka 	 * By default root is mapped to anonymous user.
12749e835c76SMarcel Telka 	 * This might get overriden later in nfsauth_cache_get().
12759e835c76SMarcel Telka 	 */
12769e835c76SMarcel Telka 	if (crgetuid(cr) == 0) {
12779e835c76SMarcel Telka 		if (uid != NULL)
12789e835c76SMarcel Telka 			*uid = exi->exi_export.ex_anon;
12799e835c76SMarcel Telka 		if (gid != NULL)
12809e835c76SMarcel Telka 			*gid = exi->exi_export.ex_anon;
12819e835c76SMarcel Telka 	} else {
12829e835c76SMarcel Telka 		if (uid != NULL)
12839e835c76SMarcel Telka 			*uid = crgetuid(cr);
12849e835c76SMarcel Telka 		if (gid != NULL)
12859e835c76SMarcel Telka 			*gid = crgetgid(cr);
12869e835c76SMarcel Telka 	}
12879e835c76SMarcel Telka 
12889e835c76SMarcel Telka 	if (ngids != NULL)
12899e835c76SMarcel Telka 		*ngids = 0;
12909e835c76SMarcel Telka 	if (gids != NULL)
12919e835c76SMarcel Telka 		*gids = NULL;
12929e835c76SMarcel Telka 
12937c478bd9Sstevel@tonic-gate 	/*
12947c478bd9Sstevel@tonic-gate 	 *  Get the nfs flavor number from xprt.
12957c478bd9Sstevel@tonic-gate 	 */
12967c478bd9Sstevel@tonic-gate 	flavor = (int)(uintptr_t)req->rq_xprt->xp_cookie;
12977c478bd9Sstevel@tonic-gate 
12987c478bd9Sstevel@tonic-gate 	/*
12997c478bd9Sstevel@tonic-gate 	 * First check the access restrictions on the filesystem.  If
13007c478bd9Sstevel@tonic-gate 	 * there are no lists associated with this flavor then there's no
13017c478bd9Sstevel@tonic-gate 	 * need to make an expensive call to the nfsauth service or to
13027c478bd9Sstevel@tonic-gate 	 * cache anything.
13037c478bd9Sstevel@tonic-gate 	 */
13047c478bd9Sstevel@tonic-gate 
13057c478bd9Sstevel@tonic-gate 	sp = exi->exi_export.ex_secinfo;
13067c478bd9Sstevel@tonic-gate 	for (i = 0; i < exi->exi_export.ex_seccnt; i++) {
13077c478bd9Sstevel@tonic-gate 		if (flavor != sp[i].s_secinfo.sc_nfsnum) {
13087c478bd9Sstevel@tonic-gate 			if (sp[i].s_secinfo.sc_nfsnum == AUTH_NONE)
13097c478bd9Sstevel@tonic-gate 				authnone_entry = i;
13107c478bd9Sstevel@tonic-gate 			continue;
13117c478bd9Sstevel@tonic-gate 		}
13127c478bd9Sstevel@tonic-gate 		break;
13137c478bd9Sstevel@tonic-gate 	}
13147c478bd9Sstevel@tonic-gate 
13157c478bd9Sstevel@tonic-gate 	mapaccess = 0;
13167c478bd9Sstevel@tonic-gate 
13177c478bd9Sstevel@tonic-gate 	if (i >= exi->exi_export.ex_seccnt) {
13187c478bd9Sstevel@tonic-gate 		/*
13197c478bd9Sstevel@tonic-gate 		 * Flavor not found, but use AUTH_NONE if it exists
13207c478bd9Sstevel@tonic-gate 		 */
13217c478bd9Sstevel@tonic-gate 		if (authnone_entry == -1)
13227c478bd9Sstevel@tonic-gate 			return (NFSAUTH_DENIED);
13237c478bd9Sstevel@tonic-gate 		flavor = AUTH_NONE;
13247c478bd9Sstevel@tonic-gate 		mapaccess = NFSAUTH_MAPNONE;
13257c478bd9Sstevel@tonic-gate 		i = authnone_entry;
13267c478bd9Sstevel@tonic-gate 	}
13277c478bd9Sstevel@tonic-gate 
13287c478bd9Sstevel@tonic-gate 	/*
13297c478bd9Sstevel@tonic-gate 	 * If the flavor is in the ex_secinfo list, but not an explicitly
13307c478bd9Sstevel@tonic-gate 	 * shared flavor by the user, it is a result of the nfsv4 server
13317c478bd9Sstevel@tonic-gate 	 * namespace setup. We will grant an RO permission similar for
13327c478bd9Sstevel@tonic-gate 	 * a pseudo node except that this node is a shared one.
13337c478bd9Sstevel@tonic-gate 	 *
13347c478bd9Sstevel@tonic-gate 	 * e.g. flavor in (flavor) indicates that it is not explictly
13357c478bd9Sstevel@tonic-gate 	 *	shared by the user:
13367c478bd9Sstevel@tonic-gate 	 *
13377c478bd9Sstevel@tonic-gate 	 *		/	(sys, krb5)
13387c478bd9Sstevel@tonic-gate 	 *		|
13397c478bd9Sstevel@tonic-gate 	 *		export  #share -o sec=sys (krb5)
13407c478bd9Sstevel@tonic-gate 	 *		|
13417c478bd9Sstevel@tonic-gate 	 *		secure  #share -o sec=krb5
13427c478bd9Sstevel@tonic-gate 	 *
13437c478bd9Sstevel@tonic-gate 	 *	In this case, when a krb5 request coming in to access
13447c478bd9Sstevel@tonic-gate 	 *	/export, RO permission is granted.
13457c478bd9Sstevel@tonic-gate 	 */
13467c478bd9Sstevel@tonic-gate 	if (!(sp[i].s_flags & M_4SEC_EXPORTED))
13477c478bd9Sstevel@tonic-gate 		return (mapaccess | NFSAUTH_RO);
13487c478bd9Sstevel@tonic-gate 
13497c478bd9Sstevel@tonic-gate 	/*
135089621fe1SMarcel Telka 	 * Optimize if there are no lists.
135189621fe1SMarcel Telka 	 * We cannot optimize for AUTH_SYS with NGRPS (16) supplemental groups.
13527c478bd9Sstevel@tonic-gate 	 */
13537c478bd9Sstevel@tonic-gate 	perm = sp[i].s_flags;
135489621fe1SMarcel Telka 	if ((perm & (M_ROOT | M_NONE | M_MAP)) == 0 && (ngroups_max <= NGRPS ||
135589621fe1SMarcel Telka 	    flavor != AUTH_SYS || crgetngroups(cr) < NGRPS)) {
13567c478bd9Sstevel@tonic-gate 		perm &= ~M_4SEC_EXPORTED;
13577c478bd9Sstevel@tonic-gate 		if (perm == M_RO)
13587c478bd9Sstevel@tonic-gate 			return (mapaccess | NFSAUTH_RO);
13597c478bd9Sstevel@tonic-gate 		if (perm == M_RW)
13607c478bd9Sstevel@tonic-gate 			return (mapaccess | NFSAUTH_RW);
13617c478bd9Sstevel@tonic-gate 	}
13627c478bd9Sstevel@tonic-gate 
136389621fe1SMarcel Telka 	access = nfsauth_cache_get(exi, req, flavor, cr, uid, gid, ngids, gids);
136489621fe1SMarcel Telka 
136589621fe1SMarcel Telka 	/*
136689621fe1SMarcel Telka 	 * For both NFSAUTH_DENIED and NFSAUTH_WRONGSEC we do not care about
136789621fe1SMarcel Telka 	 * the supplemental groups.
136889621fe1SMarcel Telka 	 */
136989621fe1SMarcel Telka 	if (access & NFSAUTH_DENIED || access & NFSAUTH_WRONGSEC) {
137089621fe1SMarcel Telka 		if (ngids != NULL && gids != NULL) {
137189621fe1SMarcel Telka 			kmem_free(*gids, *ngids * sizeof (gid_t));
137289621fe1SMarcel Telka 			*ngids = 0;
137389621fe1SMarcel Telka 			*gids = NULL;
137489621fe1SMarcel Telka 		}
137589621fe1SMarcel Telka 	}
1376f73f2d50SVallish Vaidyeshwara 
1377f73f2d50SVallish Vaidyeshwara 	/*
1378f73f2d50SVallish Vaidyeshwara 	 * Client's security flavor doesn't match with "ro" or
1379f73f2d50SVallish Vaidyeshwara 	 * "rw" list. Try again using AUTH_NONE if present.
1380f73f2d50SVallish Vaidyeshwara 	 */
1381f73f2d50SVallish Vaidyeshwara 	if ((access & NFSAUTH_WRONGSEC) && (flavor != AUTH_NONE)) {
1382f73f2d50SVallish Vaidyeshwara 		/*
1383f73f2d50SVallish Vaidyeshwara 		 * Have we already encountered AUTH_NONE ?
1384f73f2d50SVallish Vaidyeshwara 		 */
1385f73f2d50SVallish Vaidyeshwara 		if (authnone_entry != -1) {
1386f73f2d50SVallish Vaidyeshwara 			mapaccess = NFSAUTH_MAPNONE;
13875cb0d679SMarcel Telka 			access = nfsauth_cache_get(exi, req, AUTH_NONE, cr,
138889621fe1SMarcel Telka 			    NULL, NULL, NULL, NULL);
1389f73f2d50SVallish Vaidyeshwara 		} else {
1390f73f2d50SVallish Vaidyeshwara 			/*
1391f73f2d50SVallish Vaidyeshwara 			 * Check for AUTH_NONE presence.
1392f73f2d50SVallish Vaidyeshwara 			 */
1393f73f2d50SVallish Vaidyeshwara 			for (; i < exi->exi_export.ex_seccnt; i++) {
1394f73f2d50SVallish Vaidyeshwara 				if (sp[i].s_secinfo.sc_nfsnum == AUTH_NONE) {
1395f73f2d50SVallish Vaidyeshwara 					mapaccess = NFSAUTH_MAPNONE;
1396f73f2d50SVallish Vaidyeshwara 					access = nfsauth_cache_get(exi, req,
139789621fe1SMarcel Telka 					    AUTH_NONE, cr, NULL, NULL, NULL,
139889621fe1SMarcel Telka 					    NULL);
1399f73f2d50SVallish Vaidyeshwara 					break;
1400f73f2d50SVallish Vaidyeshwara 				}
1401f73f2d50SVallish Vaidyeshwara 			}
1402f73f2d50SVallish Vaidyeshwara 		}
1403f73f2d50SVallish Vaidyeshwara 	}
1404f73f2d50SVallish Vaidyeshwara 
1405b89a8333Snatalie li - Sun Microsystems - Irvine United States 	if (access & NFSAUTH_DENIED)
1406b89a8333Snatalie li - Sun Microsystems - Irvine United States 		access = NFSAUTH_DENIED;
14077c478bd9Sstevel@tonic-gate 
14087c478bd9Sstevel@tonic-gate 	return (access | mapaccess);
14097c478bd9Sstevel@tonic-gate }
14107c478bd9Sstevel@tonic-gate 
141171da0c32SMarcel Telka static void
nfsauth_free_clnt_node(struct auth_cache_clnt * p)141271da0c32SMarcel Telka nfsauth_free_clnt_node(struct auth_cache_clnt *p)
141371da0c32SMarcel Telka {
141471da0c32SMarcel Telka 	void *cookie = NULL;
141571da0c32SMarcel Telka 	struct auth_cache *node;
141671da0c32SMarcel Telka 
141771da0c32SMarcel Telka 	while ((node = avl_destroy_nodes(&p->authc_tree, &cookie)) != NULL)
141871da0c32SMarcel Telka 		nfsauth_free_node(node);
141971da0c32SMarcel Telka 	avl_destroy(&p->authc_tree);
142071da0c32SMarcel Telka 
14217bbfa3eeSMarcel Telka 	kmem_free(p->authc_addr.buf, p->authc_addr.maxlen);
142271da0c32SMarcel Telka 	rw_destroy(&p->authc_lock);
142371da0c32SMarcel Telka 
142471da0c32SMarcel Telka 	kmem_free(p, sizeof (*p));
142571da0c32SMarcel Telka }
142671da0c32SMarcel Telka 
14273ccecb66SThomas Haynes static void
nfsauth_free_node(struct auth_cache * p)14283ccecb66SThomas Haynes nfsauth_free_node(struct auth_cache *p)
14293ccecb66SThomas Haynes {
143012fb3699SMarcel Telka 	crfree(p->auth_clnt_cred);
143189621fe1SMarcel Telka 	kmem_free(p->auth_srv_gids, p->auth_srv_ngids * sizeof (gid_t));
14323ccecb66SThomas Haynes 	mutex_destroy(&p->auth_lock);
143371da0c32SMarcel Telka 	cv_destroy(&p->auth_cv);
143489621fe1SMarcel Telka 	kmem_cache_free(exi_cache_handle, p);
14353ccecb66SThomas Haynes }
14363ccecb66SThomas Haynes 
14377c478bd9Sstevel@tonic-gate /*
14387c478bd9Sstevel@tonic-gate  * Free the nfsauth cache for a given export
14397c478bd9Sstevel@tonic-gate  */
14407c478bd9Sstevel@tonic-gate void
nfsauth_cache_free(struct exportinfo * exi)14417c478bd9Sstevel@tonic-gate nfsauth_cache_free(struct exportinfo *exi)
14427c478bd9Sstevel@tonic-gate {
14437c478bd9Sstevel@tonic-gate 	int i;
144471da0c32SMarcel Telka 
144571da0c32SMarcel Telka 	/*
144671da0c32SMarcel Telka 	 * The only way we got here was with an exi_rele, which means that no
144771da0c32SMarcel Telka 	 * auth cache entry is being refreshed.
144871da0c32SMarcel Telka 	 */
14497c478bd9Sstevel@tonic-gate 
14507c478bd9Sstevel@tonic-gate 	for (i = 0; i < AUTH_TABLESIZE; i++) {
145171da0c32SMarcel Telka 		avl_tree_t *tree = exi->exi_cache[i];
145271da0c32SMarcel Telka 		void *cookie = NULL;
145371da0c32SMarcel Telka 		struct auth_cache_clnt *node;
14543ccecb66SThomas Haynes 
145571da0c32SMarcel Telka 		while ((node = avl_destroy_nodes(tree, &cookie)) != NULL)
145671da0c32SMarcel Telka 			nfsauth_free_clnt_node(node);
14577c478bd9Sstevel@tonic-gate 	}
14587c478bd9Sstevel@tonic-gate }
14597c478bd9Sstevel@tonic-gate 
14607c478bd9Sstevel@tonic-gate /*
14610dfe541eSEvan Layton  * Called by the kernel memory allocator when memory is low.
14620dfe541eSEvan Layton  * Free unused cache entries. If that's not enough, the VM system
14630dfe541eSEvan Layton  * will call again for some more.
14640dfe541eSEvan Layton  *
14650dfe541eSEvan Layton  * This needs to operate on all zones, so we take a reader lock
14660dfe541eSEvan Layton  * on the list of zones and walk the list.  This is OK here
14670dfe541eSEvan Layton  * becuase exi_cache_trim doesn't block or cause new objects
14680dfe541eSEvan Layton  * to be allocated (basically just frees lots of stuff).
14690dfe541eSEvan Layton  * Use care if nfssrv_globals_rwl is taken as reader in any
14700dfe541eSEvan Layton  * other cases because it will block nfs_server_zone_init
14710dfe541eSEvan Layton  * and nfs_server_zone_fini, which enter as writer.
14727c478bd9Sstevel@tonic-gate  */
14737c478bd9Sstevel@tonic-gate /*ARGSUSED*/
14747c478bd9Sstevel@tonic-gate void
exi_cache_reclaim(void * cdrarg)14757c478bd9Sstevel@tonic-gate exi_cache_reclaim(void *cdrarg)
14760dfe541eSEvan Layton {
14770dfe541eSEvan Layton 	nfs_globals_t *ng;
14780dfe541eSEvan Layton 
14790dfe541eSEvan Layton 	rw_enter(&nfssrv_globals_rwl, RW_READER);
14800dfe541eSEvan Layton 
14810dfe541eSEvan Layton 	ng = list_head(&nfssrv_globals_list);
14820dfe541eSEvan Layton 	while (ng != NULL) {
14830dfe541eSEvan Layton 		exi_cache_reclaim_zone(ng);
14840dfe541eSEvan Layton 		ng = list_next(&nfssrv_globals_list, ng);
14850dfe541eSEvan Layton 	}
14860dfe541eSEvan Layton 
14870dfe541eSEvan Layton 	rw_exit(&nfssrv_globals_rwl);
14880dfe541eSEvan Layton }
14890dfe541eSEvan Layton 
14900dfe541eSEvan Layton static void
exi_cache_reclaim_zone(nfs_globals_t * ng)14910dfe541eSEvan Layton exi_cache_reclaim_zone(nfs_globals_t *ng)
14927c478bd9Sstevel@tonic-gate {
14937c478bd9Sstevel@tonic-gate 	int i;
14947c478bd9Sstevel@tonic-gate 	struct exportinfo *exi;
14950dfe541eSEvan Layton 	nfs_export_t *ne = ng->nfs_export;
14967c478bd9Sstevel@tonic-gate 
14970dfe541eSEvan Layton 	rw_enter(&ne->exported_lock, RW_READER);
14987c478bd9Sstevel@tonic-gate 
14997c478bd9Sstevel@tonic-gate 	for (i = 0; i < EXPTABLESIZE; i++) {
15000dfe541eSEvan Layton 		for (exi = ne->exptable[i]; exi; exi = exi->fid_hash.next)
15017c478bd9Sstevel@tonic-gate 			exi_cache_trim(exi);
15027c478bd9Sstevel@tonic-gate 	}
15037c478bd9Sstevel@tonic-gate 
15040dfe541eSEvan Layton 	rw_exit(&ne->exported_lock);
150571da0c32SMarcel Telka 
1506*f44e1126SVitaliy Gusev 	nfsauth_cache_reclaim++;
15077c478bd9Sstevel@tonic-gate }
15087c478bd9Sstevel@tonic-gate 
15090dfe541eSEvan Layton static void
exi_cache_trim(struct exportinfo * exi)15107c478bd9Sstevel@tonic-gate exi_cache_trim(struct exportinfo *exi)
15117c478bd9Sstevel@tonic-gate {
151271da0c32SMarcel Telka 	struct auth_cache_clnt *c;
151371da0c32SMarcel Telka 	struct auth_cache_clnt *nextc;
15147c478bd9Sstevel@tonic-gate 	struct auth_cache *p;
151571da0c32SMarcel Telka 	struct auth_cache *next;
15167c478bd9Sstevel@tonic-gate 	int i;
15177c478bd9Sstevel@tonic-gate 	time_t stale_time;
151871da0c32SMarcel Telka 	avl_tree_t *tree;
15197c478bd9Sstevel@tonic-gate 
152071da0c32SMarcel Telka 	for (i = 0; i < AUTH_TABLESIZE; i++) {
152171da0c32SMarcel Telka 		tree = exi->exi_cache[i];
152271da0c32SMarcel Telka 		stale_time = gethrestime_sec() - NFSAUTH_CACHE_TRIM;
152371da0c32SMarcel Telka 		rw_enter(&exi->exi_cache_lock, RW_READER);
15247c478bd9Sstevel@tonic-gate 
15257c478bd9Sstevel@tonic-gate 		/*
15267c478bd9Sstevel@tonic-gate 		 * Free entries that have not been
15273ccecb66SThomas Haynes 		 * used for NFSAUTH_CACHE_TRIM seconds.
15287c478bd9Sstevel@tonic-gate 		 */
152971da0c32SMarcel Telka 		for (c = avl_first(tree); c != NULL; c = AVL_NEXT(tree, c)) {
15308ee802ccSGeorge Wilson 			/*
15318ee802ccSGeorge Wilson 			 * We are being called by the kmem subsystem to reclaim
15328ee802ccSGeorge Wilson 			 * memory so don't block if we can't get the lock.
15338ee802ccSGeorge Wilson 			 */
15348ee802ccSGeorge Wilson 			if (rw_tryenter(&c->authc_lock, RW_WRITER) == 0) {
15358ee802ccSGeorge Wilson 				exi_cache_auth_reclaim_failed++;
15368ee802ccSGeorge Wilson 				rw_exit(&exi->exi_cache_lock);
15378ee802ccSGeorge Wilson 				return;
15388ee802ccSGeorge Wilson 			}
15398ee802ccSGeorge Wilson 
154071da0c32SMarcel Telka 			for (p = avl_first(&c->authc_tree); p != NULL;
154171da0c32SMarcel Telka 			    p = next) {
154271da0c32SMarcel Telka 				next = AVL_NEXT(&c->authc_tree, p);
15437c478bd9Sstevel@tonic-gate 
154471da0c32SMarcel Telka 				ASSERT(p->auth_state != NFS_AUTH_INVALID);
15453ccecb66SThomas Haynes 
154671da0c32SMarcel Telka 				mutex_enter(&p->auth_lock);
15473ccecb66SThomas Haynes 
154871da0c32SMarcel Telka 				/*
154971da0c32SMarcel Telka 				 * We won't trim recently used and/or WAITING
155071da0c32SMarcel Telka 				 * entries.
155171da0c32SMarcel Telka 				 */
155271da0c32SMarcel Telka 				if (p->auth_time > stale_time ||
155371da0c32SMarcel Telka 				    p->auth_state == NFS_AUTH_WAITING) {
155471da0c32SMarcel Telka 					mutex_exit(&p->auth_lock);
155571da0c32SMarcel Telka 					continue;
155671da0c32SMarcel Telka 				}
155771da0c32SMarcel Telka 
155871da0c32SMarcel Telka 				DTRACE_PROBE1(nfsauth__debug__trim__state,
155971da0c32SMarcel Telka 				    auth_state_t, p->auth_state);
156071da0c32SMarcel Telka 
156171da0c32SMarcel Telka 				/*
156271da0c32SMarcel Telka 				 * STALE and REFRESHING entries needs to be
156371da0c32SMarcel Telka 				 * marked INVALID only because they are
156471da0c32SMarcel Telka 				 * referenced by some other structures or
156571da0c32SMarcel Telka 				 * threads.  They will be freed later.
156671da0c32SMarcel Telka 				 */
156771da0c32SMarcel Telka 				if (p->auth_state == NFS_AUTH_STALE ||
156871da0c32SMarcel Telka 				    p->auth_state == NFS_AUTH_REFRESHING) {
156971da0c32SMarcel Telka 					p->auth_state = NFS_AUTH_INVALID;
157071da0c32SMarcel Telka 					mutex_exit(&p->auth_lock);
157171da0c32SMarcel Telka 
157271da0c32SMarcel Telka 					avl_remove(&c->authc_tree, p);
157371da0c32SMarcel Telka 				} else {
157471da0c32SMarcel Telka 					mutex_exit(&p->auth_lock);
157571da0c32SMarcel Telka 
157671da0c32SMarcel Telka 					avl_remove(&c->authc_tree, p);
157771da0c32SMarcel Telka 					nfsauth_free_node(p);
157871da0c32SMarcel Telka 				}
15793ccecb66SThomas Haynes 			}
158071da0c32SMarcel Telka 			rw_exit(&c->authc_lock);
158171da0c32SMarcel Telka 		}
15823ccecb66SThomas Haynes 
158371da0c32SMarcel Telka 		if (rw_tryupgrade(&exi->exi_cache_lock) == 0) {
158471da0c32SMarcel Telka 			rw_exit(&exi->exi_cache_lock);
15858ee802ccSGeorge Wilson 			exi_cache_clnt_reclaim_failed++;
15868ee802ccSGeorge Wilson 			continue;
15877c478bd9Sstevel@tonic-gate 		}
15887c478bd9Sstevel@tonic-gate 
158971da0c32SMarcel Telka 		for (c = avl_first(tree); c != NULL; c = nextc) {
159071da0c32SMarcel Telka 			nextc = AVL_NEXT(tree, c);
159171da0c32SMarcel Telka 
159271da0c32SMarcel Telka 			if (avl_is_empty(&c->authc_tree) == B_FALSE)
159371da0c32SMarcel Telka 				continue;
159471da0c32SMarcel Telka 
159571da0c32SMarcel Telka 			avl_remove(tree, c);
159671da0c32SMarcel Telka 
159771da0c32SMarcel Telka 			nfsauth_free_clnt_node(c);
159871da0c32SMarcel Telka 		}
159971da0c32SMarcel Telka 
160071da0c32SMarcel Telka 		rw_exit(&exi->exi_cache_lock);
160171da0c32SMarcel Telka 	}
16027c478bd9Sstevel@tonic-gate }
1603