18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* AFS security handling 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2007, 2017 Red Hat, Inc. All Rights Reserved. 58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include <linux/fs.h> 118c2ecf20Sopenharmony_ci#include <linux/ctype.h> 128c2ecf20Sopenharmony_ci#include <linux/sched.h> 138c2ecf20Sopenharmony_ci#include <linux/hashtable.h> 148c2ecf20Sopenharmony_ci#include <keys/rxrpc-type.h> 158c2ecf20Sopenharmony_ci#include "internal.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic DEFINE_HASHTABLE(afs_permits_cache, 10); 188c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(afs_permits_lock); 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* 218c2ecf20Sopenharmony_ci * get a key 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_cistruct key *afs_request_key(struct afs_cell *cell) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci struct key *key; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci _enter("{%x}", key_serial(cell->anonymous_key)); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci _debug("key %s", cell->anonymous_key->description); 308c2ecf20Sopenharmony_ci key = request_key_net(&key_type_rxrpc, cell->anonymous_key->description, 318c2ecf20Sopenharmony_ci cell->net->net, NULL); 328c2ecf20Sopenharmony_ci if (IS_ERR(key)) { 338c2ecf20Sopenharmony_ci if (PTR_ERR(key) != -ENOKEY) { 348c2ecf20Sopenharmony_ci _leave(" = %ld", PTR_ERR(key)); 358c2ecf20Sopenharmony_ci return key; 368c2ecf20Sopenharmony_ci } 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci /* act as anonymous user */ 398c2ecf20Sopenharmony_ci _leave(" = {%x} [anon]", key_serial(cell->anonymous_key)); 408c2ecf20Sopenharmony_ci return key_get(cell->anonymous_key); 418c2ecf20Sopenharmony_ci } else { 428c2ecf20Sopenharmony_ci /* act as authorised user */ 438c2ecf20Sopenharmony_ci _leave(" = {%x} [auth]", key_serial(key)); 448c2ecf20Sopenharmony_ci return key; 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* 498c2ecf20Sopenharmony_ci * Get a key when pathwalk is in rcuwalk mode. 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_cistruct key *afs_request_key_rcu(struct afs_cell *cell) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct key *key; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci _enter("{%x}", key_serial(cell->anonymous_key)); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci _debug("key %s", cell->anonymous_key->description); 588c2ecf20Sopenharmony_ci key = request_key_net_rcu(&key_type_rxrpc, 598c2ecf20Sopenharmony_ci cell->anonymous_key->description, 608c2ecf20Sopenharmony_ci cell->net->net); 618c2ecf20Sopenharmony_ci if (IS_ERR(key)) { 628c2ecf20Sopenharmony_ci if (PTR_ERR(key) != -ENOKEY) { 638c2ecf20Sopenharmony_ci _leave(" = %ld", PTR_ERR(key)); 648c2ecf20Sopenharmony_ci return key; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* act as anonymous user */ 688c2ecf20Sopenharmony_ci _leave(" = {%x} [anon]", key_serial(cell->anonymous_key)); 698c2ecf20Sopenharmony_ci return key_get(cell->anonymous_key); 708c2ecf20Sopenharmony_ci } else { 718c2ecf20Sopenharmony_ci /* act as authorised user */ 728c2ecf20Sopenharmony_ci _leave(" = {%x} [auth]", key_serial(key)); 738c2ecf20Sopenharmony_ci return key; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* 788c2ecf20Sopenharmony_ci * Dispose of a list of permits. 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_cistatic void afs_permits_rcu(struct rcu_head *rcu) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct afs_permits *permits = 838c2ecf20Sopenharmony_ci container_of(rcu, struct afs_permits, rcu); 848c2ecf20Sopenharmony_ci int i; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci for (i = 0; i < permits->nr_permits; i++) 878c2ecf20Sopenharmony_ci key_put(permits->permits[i].key); 888c2ecf20Sopenharmony_ci kfree(permits); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/* 928c2ecf20Sopenharmony_ci * Discard a permission cache. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_civoid afs_put_permits(struct afs_permits *permits) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci if (permits && refcount_dec_and_test(&permits->usage)) { 978c2ecf20Sopenharmony_ci spin_lock(&afs_permits_lock); 988c2ecf20Sopenharmony_ci hash_del_rcu(&permits->hash_node); 998c2ecf20Sopenharmony_ci spin_unlock(&afs_permits_lock); 1008c2ecf20Sopenharmony_ci call_rcu(&permits->rcu, afs_permits_rcu); 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* 1058c2ecf20Sopenharmony_ci * Clear a permit cache on callback break. 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_civoid afs_clear_permits(struct afs_vnode *vnode) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct afs_permits *permits; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci spin_lock(&vnode->lock); 1128c2ecf20Sopenharmony_ci permits = rcu_dereference_protected(vnode->permit_cache, 1138c2ecf20Sopenharmony_ci lockdep_is_held(&vnode->lock)); 1148c2ecf20Sopenharmony_ci RCU_INIT_POINTER(vnode->permit_cache, NULL); 1158c2ecf20Sopenharmony_ci spin_unlock(&vnode->lock); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci afs_put_permits(permits); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/* 1218c2ecf20Sopenharmony_ci * Hash a list of permits. Use simple addition to make it easy to add an extra 1228c2ecf20Sopenharmony_ci * one at an as-yet indeterminate position in the list. 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_cistatic void afs_hash_permits(struct afs_permits *permits) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci unsigned long h = permits->nr_permits; 1278c2ecf20Sopenharmony_ci int i; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci for (i = 0; i < permits->nr_permits; i++) { 1308c2ecf20Sopenharmony_ci h += (unsigned long)permits->permits[i].key / sizeof(void *); 1318c2ecf20Sopenharmony_ci h += permits->permits[i].access; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci permits->h = h; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci/* 1388c2ecf20Sopenharmony_ci * Cache the CallerAccess result obtained from doing a fileserver operation 1398c2ecf20Sopenharmony_ci * that returned a vnode status for a particular key. If a callback break 1408c2ecf20Sopenharmony_ci * occurs whilst the operation was in progress then we have to ditch the cache 1418c2ecf20Sopenharmony_ci * as the ACL *may* have changed. 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_civoid afs_cache_permit(struct afs_vnode *vnode, struct key *key, 1448c2ecf20Sopenharmony_ci unsigned int cb_break, struct afs_status_cb *scb) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct afs_permits *permits, *xpermits, *replacement, *zap, *new = NULL; 1478c2ecf20Sopenharmony_ci afs_access_t caller_access = scb->status.caller_access; 1488c2ecf20Sopenharmony_ci size_t size = 0; 1498c2ecf20Sopenharmony_ci bool changed = false; 1508c2ecf20Sopenharmony_ci int i, j; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci _enter("{%llx:%llu},%x,%x", 1538c2ecf20Sopenharmony_ci vnode->fid.vid, vnode->fid.vnode, key_serial(key), caller_access); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci rcu_read_lock(); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* Check for the common case first: We got back the same access as last 1588c2ecf20Sopenharmony_ci * time we tried and already have it recorded. 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_ci permits = rcu_dereference(vnode->permit_cache); 1618c2ecf20Sopenharmony_ci if (permits) { 1628c2ecf20Sopenharmony_ci if (!permits->invalidated) { 1638c2ecf20Sopenharmony_ci for (i = 0; i < permits->nr_permits; i++) { 1648c2ecf20Sopenharmony_ci if (permits->permits[i].key < key) 1658c2ecf20Sopenharmony_ci continue; 1668c2ecf20Sopenharmony_ci if (permits->permits[i].key > key) 1678c2ecf20Sopenharmony_ci break; 1688c2ecf20Sopenharmony_ci if (permits->permits[i].access != caller_access) { 1698c2ecf20Sopenharmony_ci changed = true; 1708c2ecf20Sopenharmony_ci break; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (afs_cb_is_broken(cb_break, vnode)) { 1748c2ecf20Sopenharmony_ci changed = true; 1758c2ecf20Sopenharmony_ci break; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* The cache is still good. */ 1798c2ecf20Sopenharmony_ci rcu_read_unlock(); 1808c2ecf20Sopenharmony_ci return; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci changed |= permits->invalidated; 1858c2ecf20Sopenharmony_ci size = permits->nr_permits; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* If this set of permits is now wrong, clear the permits 1888c2ecf20Sopenharmony_ci * pointer so that no one tries to use the stale information. 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_ci if (changed) { 1918c2ecf20Sopenharmony_ci spin_lock(&vnode->lock); 1928c2ecf20Sopenharmony_ci if (permits != rcu_access_pointer(vnode->permit_cache)) 1938c2ecf20Sopenharmony_ci goto someone_else_changed_it_unlock; 1948c2ecf20Sopenharmony_ci RCU_INIT_POINTER(vnode->permit_cache, NULL); 1958c2ecf20Sopenharmony_ci spin_unlock(&vnode->lock); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci afs_put_permits(permits); 1988c2ecf20Sopenharmony_ci permits = NULL; 1998c2ecf20Sopenharmony_ci size = 0; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (afs_cb_is_broken(cb_break, vnode)) 2048c2ecf20Sopenharmony_ci goto someone_else_changed_it; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* We need a ref on any permits list we want to copy as we'll have to 2078c2ecf20Sopenharmony_ci * drop the lock to do memory allocation. 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_ci if (permits && !refcount_inc_not_zero(&permits->usage)) 2108c2ecf20Sopenharmony_ci goto someone_else_changed_it; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci rcu_read_unlock(); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* Speculatively create a new list with the revised permission set. We 2158c2ecf20Sopenharmony_ci * discard this if we find an extant match already in the hash, but 2168c2ecf20Sopenharmony_ci * it's easier to compare with memcmp this way. 2178c2ecf20Sopenharmony_ci * 2188c2ecf20Sopenharmony_ci * We fill in the key pointers at this time, but we don't get the refs 2198c2ecf20Sopenharmony_ci * yet. 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_ci size++; 2228c2ecf20Sopenharmony_ci new = kzalloc(sizeof(struct afs_permits) + 2238c2ecf20Sopenharmony_ci sizeof(struct afs_permit) * size, GFP_NOFS); 2248c2ecf20Sopenharmony_ci if (!new) 2258c2ecf20Sopenharmony_ci goto out_put; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci refcount_set(&new->usage, 1); 2288c2ecf20Sopenharmony_ci new->nr_permits = size; 2298c2ecf20Sopenharmony_ci i = j = 0; 2308c2ecf20Sopenharmony_ci if (permits) { 2318c2ecf20Sopenharmony_ci for (i = 0; i < permits->nr_permits; i++) { 2328c2ecf20Sopenharmony_ci if (j == i && permits->permits[i].key > key) { 2338c2ecf20Sopenharmony_ci new->permits[j].key = key; 2348c2ecf20Sopenharmony_ci new->permits[j].access = caller_access; 2358c2ecf20Sopenharmony_ci j++; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci new->permits[j].key = permits->permits[i].key; 2388c2ecf20Sopenharmony_ci new->permits[j].access = permits->permits[i].access; 2398c2ecf20Sopenharmony_ci j++; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (j == i) { 2448c2ecf20Sopenharmony_ci new->permits[j].key = key; 2458c2ecf20Sopenharmony_ci new->permits[j].access = caller_access; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci afs_hash_permits(new); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* Now see if the permit list we want is actually already available */ 2518c2ecf20Sopenharmony_ci spin_lock(&afs_permits_lock); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci hash_for_each_possible(afs_permits_cache, xpermits, hash_node, new->h) { 2548c2ecf20Sopenharmony_ci if (xpermits->h != new->h || 2558c2ecf20Sopenharmony_ci xpermits->invalidated || 2568c2ecf20Sopenharmony_ci xpermits->nr_permits != new->nr_permits || 2578c2ecf20Sopenharmony_ci memcmp(xpermits->permits, new->permits, 2588c2ecf20Sopenharmony_ci new->nr_permits * sizeof(struct afs_permit)) != 0) 2598c2ecf20Sopenharmony_ci continue; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (refcount_inc_not_zero(&xpermits->usage)) { 2628c2ecf20Sopenharmony_ci replacement = xpermits; 2638c2ecf20Sopenharmony_ci goto found; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci break; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci for (i = 0; i < new->nr_permits; i++) 2708c2ecf20Sopenharmony_ci key_get(new->permits[i].key); 2718c2ecf20Sopenharmony_ci hash_add_rcu(afs_permits_cache, &new->hash_node, new->h); 2728c2ecf20Sopenharmony_ci replacement = new; 2738c2ecf20Sopenharmony_ci new = NULL; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cifound: 2768c2ecf20Sopenharmony_ci spin_unlock(&afs_permits_lock); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci kfree(new); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci rcu_read_lock(); 2818c2ecf20Sopenharmony_ci spin_lock(&vnode->lock); 2828c2ecf20Sopenharmony_ci zap = rcu_access_pointer(vnode->permit_cache); 2838c2ecf20Sopenharmony_ci if (!afs_cb_is_broken(cb_break, vnode) && zap == permits) 2848c2ecf20Sopenharmony_ci rcu_assign_pointer(vnode->permit_cache, replacement); 2858c2ecf20Sopenharmony_ci else 2868c2ecf20Sopenharmony_ci zap = replacement; 2878c2ecf20Sopenharmony_ci spin_unlock(&vnode->lock); 2888c2ecf20Sopenharmony_ci rcu_read_unlock(); 2898c2ecf20Sopenharmony_ci afs_put_permits(zap); 2908c2ecf20Sopenharmony_ciout_put: 2918c2ecf20Sopenharmony_ci afs_put_permits(permits); 2928c2ecf20Sopenharmony_ci return; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cisomeone_else_changed_it_unlock: 2958c2ecf20Sopenharmony_ci spin_unlock(&vnode->lock); 2968c2ecf20Sopenharmony_cisomeone_else_changed_it: 2978c2ecf20Sopenharmony_ci /* Someone else changed the cache under us - don't recheck at this 2988c2ecf20Sopenharmony_ci * time. 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_ci rcu_read_unlock(); 3018c2ecf20Sopenharmony_ci return; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic bool afs_check_permit_rcu(struct afs_vnode *vnode, struct key *key, 3058c2ecf20Sopenharmony_ci afs_access_t *_access) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci const struct afs_permits *permits; 3088c2ecf20Sopenharmony_ci int i; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci _enter("{%llx:%llu},%x", 3118c2ecf20Sopenharmony_ci vnode->fid.vid, vnode->fid.vnode, key_serial(key)); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* check the permits to see if we've got one yet */ 3148c2ecf20Sopenharmony_ci if (key == vnode->volume->cell->anonymous_key) { 3158c2ecf20Sopenharmony_ci *_access = vnode->status.anon_access; 3168c2ecf20Sopenharmony_ci _leave(" = t [anon %x]", *_access); 3178c2ecf20Sopenharmony_ci return true; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci permits = rcu_dereference(vnode->permit_cache); 3218c2ecf20Sopenharmony_ci if (permits) { 3228c2ecf20Sopenharmony_ci for (i = 0; i < permits->nr_permits; i++) { 3238c2ecf20Sopenharmony_ci if (permits->permits[i].key < key) 3248c2ecf20Sopenharmony_ci continue; 3258c2ecf20Sopenharmony_ci if (permits->permits[i].key > key) 3268c2ecf20Sopenharmony_ci break; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci *_access = permits->permits[i].access; 3298c2ecf20Sopenharmony_ci _leave(" = %u [perm %x]", !permits->invalidated, *_access); 3308c2ecf20Sopenharmony_ci return !permits->invalidated; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci _leave(" = f"); 3358c2ecf20Sopenharmony_ci return false; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci/* 3398c2ecf20Sopenharmony_ci * check with the fileserver to see if the directory or parent directory is 3408c2ecf20Sopenharmony_ci * permitted to be accessed with this authorisation, and if so, what access it 3418c2ecf20Sopenharmony_ci * is granted 3428c2ecf20Sopenharmony_ci */ 3438c2ecf20Sopenharmony_ciint afs_check_permit(struct afs_vnode *vnode, struct key *key, 3448c2ecf20Sopenharmony_ci afs_access_t *_access) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct afs_permits *permits; 3478c2ecf20Sopenharmony_ci bool valid = false; 3488c2ecf20Sopenharmony_ci int i, ret; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci _enter("{%llx:%llu},%x", 3518c2ecf20Sopenharmony_ci vnode->fid.vid, vnode->fid.vnode, key_serial(key)); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* check the permits to see if we've got one yet */ 3548c2ecf20Sopenharmony_ci if (key == vnode->volume->cell->anonymous_key) { 3558c2ecf20Sopenharmony_ci _debug("anon"); 3568c2ecf20Sopenharmony_ci *_access = vnode->status.anon_access; 3578c2ecf20Sopenharmony_ci valid = true; 3588c2ecf20Sopenharmony_ci } else { 3598c2ecf20Sopenharmony_ci rcu_read_lock(); 3608c2ecf20Sopenharmony_ci permits = rcu_dereference(vnode->permit_cache); 3618c2ecf20Sopenharmony_ci if (permits) { 3628c2ecf20Sopenharmony_ci for (i = 0; i < permits->nr_permits; i++) { 3638c2ecf20Sopenharmony_ci if (permits->permits[i].key < key) 3648c2ecf20Sopenharmony_ci continue; 3658c2ecf20Sopenharmony_ci if (permits->permits[i].key > key) 3668c2ecf20Sopenharmony_ci break; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci *_access = permits->permits[i].access; 3698c2ecf20Sopenharmony_ci valid = !permits->invalidated; 3708c2ecf20Sopenharmony_ci break; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci rcu_read_unlock(); 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (!valid) { 3778c2ecf20Sopenharmony_ci /* Check the status on the file we're actually interested in 3788c2ecf20Sopenharmony_ci * (the post-processing will cache the result). 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_ci _debug("no valid permit"); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci ret = afs_fetch_status(vnode, key, false, _access); 3838c2ecf20Sopenharmony_ci if (ret < 0) { 3848c2ecf20Sopenharmony_ci *_access = 0; 3858c2ecf20Sopenharmony_ci _leave(" = %d", ret); 3868c2ecf20Sopenharmony_ci return ret; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci _leave(" = 0 [access %x]", *_access); 3918c2ecf20Sopenharmony_ci return 0; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci/* 3958c2ecf20Sopenharmony_ci * check the permissions on an AFS file 3968c2ecf20Sopenharmony_ci * - AFS ACLs are attached to directories only, and a file is controlled by its 3978c2ecf20Sopenharmony_ci * parent directory's ACL 3988c2ecf20Sopenharmony_ci */ 3998c2ecf20Sopenharmony_ciint afs_permission(struct inode *inode, int mask) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci struct afs_vnode *vnode = AFS_FS_I(inode); 4028c2ecf20Sopenharmony_ci afs_access_t access; 4038c2ecf20Sopenharmony_ci struct key *key; 4048c2ecf20Sopenharmony_ci int ret = 0; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci _enter("{{%llx:%llu},%lx},%x,", 4078c2ecf20Sopenharmony_ci vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (mask & MAY_NOT_BLOCK) { 4108c2ecf20Sopenharmony_ci key = afs_request_key_rcu(vnode->volume->cell); 4118c2ecf20Sopenharmony_ci if (IS_ERR(key)) 4128c2ecf20Sopenharmony_ci return -ECHILD; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci ret = -ECHILD; 4158c2ecf20Sopenharmony_ci if (!afs_check_validity(vnode) || 4168c2ecf20Sopenharmony_ci !afs_check_permit_rcu(vnode, key, &access)) 4178c2ecf20Sopenharmony_ci goto error; 4188c2ecf20Sopenharmony_ci } else { 4198c2ecf20Sopenharmony_ci key = afs_request_key(vnode->volume->cell); 4208c2ecf20Sopenharmony_ci if (IS_ERR(key)) { 4218c2ecf20Sopenharmony_ci _leave(" = %ld [key]", PTR_ERR(key)); 4228c2ecf20Sopenharmony_ci return PTR_ERR(key); 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci ret = afs_validate(vnode, key); 4268c2ecf20Sopenharmony_ci if (ret < 0) 4278c2ecf20Sopenharmony_ci goto error; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* check the permits to see if we've got one yet */ 4308c2ecf20Sopenharmony_ci ret = afs_check_permit(vnode, key, &access); 4318c2ecf20Sopenharmony_ci if (ret < 0) 4328c2ecf20Sopenharmony_ci goto error; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci /* interpret the access mask */ 4368c2ecf20Sopenharmony_ci _debug("REQ %x ACC %x on %s", 4378c2ecf20Sopenharmony_ci mask, access, S_ISDIR(inode->i_mode) ? "dir" : "file"); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci ret = 0; 4408c2ecf20Sopenharmony_ci if (S_ISDIR(inode->i_mode)) { 4418c2ecf20Sopenharmony_ci if (mask & (MAY_EXEC | MAY_READ | MAY_CHDIR)) { 4428c2ecf20Sopenharmony_ci if (!(access & AFS_ACE_LOOKUP)) 4438c2ecf20Sopenharmony_ci goto permission_denied; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci if (mask & MAY_WRITE) { 4468c2ecf20Sopenharmony_ci if (!(access & (AFS_ACE_DELETE | /* rmdir, unlink, rename from */ 4478c2ecf20Sopenharmony_ci AFS_ACE_INSERT))) /* create, mkdir, symlink, rename to */ 4488c2ecf20Sopenharmony_ci goto permission_denied; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci } else { 4518c2ecf20Sopenharmony_ci if (!(access & AFS_ACE_LOOKUP)) 4528c2ecf20Sopenharmony_ci goto permission_denied; 4538c2ecf20Sopenharmony_ci if ((mask & MAY_EXEC) && !(inode->i_mode & S_IXUSR)) 4548c2ecf20Sopenharmony_ci goto permission_denied; 4558c2ecf20Sopenharmony_ci if (mask & (MAY_EXEC | MAY_READ)) { 4568c2ecf20Sopenharmony_ci if (!(access & AFS_ACE_READ)) 4578c2ecf20Sopenharmony_ci goto permission_denied; 4588c2ecf20Sopenharmony_ci if (!(inode->i_mode & S_IRUSR)) 4598c2ecf20Sopenharmony_ci goto permission_denied; 4608c2ecf20Sopenharmony_ci } else if (mask & MAY_WRITE) { 4618c2ecf20Sopenharmony_ci if (!(access & AFS_ACE_WRITE)) 4628c2ecf20Sopenharmony_ci goto permission_denied; 4638c2ecf20Sopenharmony_ci if (!(inode->i_mode & S_IWUSR)) 4648c2ecf20Sopenharmony_ci goto permission_denied; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci key_put(key); 4698c2ecf20Sopenharmony_ci _leave(" = %d", ret); 4708c2ecf20Sopenharmony_ci return ret; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cipermission_denied: 4738c2ecf20Sopenharmony_ci ret = -EACCES; 4748c2ecf20Sopenharmony_cierror: 4758c2ecf20Sopenharmony_ci key_put(key); 4768c2ecf20Sopenharmony_ci _leave(" = %d", ret); 4778c2ecf20Sopenharmony_ci return ret; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_civoid __exit afs_clean_up_permit_cache(void) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci int i; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci for (i = 0; i < HASH_SIZE(afs_permits_cache); i++) 4858c2ecf20Sopenharmony_ci WARN_ON_ONCE(!hlist_empty(&afs_permits_cache[i])); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci} 488