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