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