1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /* AFS security handling
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Copyright (C) 2007, 2017 Red Hat, Inc. All Rights Reserved.
5*4882a593Smuzhiyun * Written by David Howells (dhowells@redhat.com)
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include <linux/init.h>
9*4882a593Smuzhiyun #include <linux/slab.h>
10*4882a593Smuzhiyun #include <linux/fs.h>
11*4882a593Smuzhiyun #include <linux/ctype.h>
12*4882a593Smuzhiyun #include <linux/sched.h>
13*4882a593Smuzhiyun #include <linux/hashtable.h>
14*4882a593Smuzhiyun #include <keys/rxrpc-type.h>
15*4882a593Smuzhiyun #include "internal.h"
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun static DEFINE_HASHTABLE(afs_permits_cache, 10);
18*4882a593Smuzhiyun static DEFINE_SPINLOCK(afs_permits_lock);
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun /*
21*4882a593Smuzhiyun * get a key
22*4882a593Smuzhiyun */
afs_request_key(struct afs_cell * cell)23*4882a593Smuzhiyun struct key *afs_request_key(struct afs_cell *cell)
24*4882a593Smuzhiyun {
25*4882a593Smuzhiyun struct key *key;
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun _enter("{%x}", key_serial(cell->anonymous_key));
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun _debug("key %s", cell->anonymous_key->description);
30*4882a593Smuzhiyun key = request_key_net(&key_type_rxrpc, cell->anonymous_key->description,
31*4882a593Smuzhiyun cell->net->net, NULL);
32*4882a593Smuzhiyun if (IS_ERR(key)) {
33*4882a593Smuzhiyun if (PTR_ERR(key) != -ENOKEY) {
34*4882a593Smuzhiyun _leave(" = %ld", PTR_ERR(key));
35*4882a593Smuzhiyun return key;
36*4882a593Smuzhiyun }
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun /* act as anonymous user */
39*4882a593Smuzhiyun _leave(" = {%x} [anon]", key_serial(cell->anonymous_key));
40*4882a593Smuzhiyun return key_get(cell->anonymous_key);
41*4882a593Smuzhiyun } else {
42*4882a593Smuzhiyun /* act as authorised user */
43*4882a593Smuzhiyun _leave(" = {%x} [auth]", key_serial(key));
44*4882a593Smuzhiyun return key;
45*4882a593Smuzhiyun }
46*4882a593Smuzhiyun }
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun /*
49*4882a593Smuzhiyun * Get a key when pathwalk is in rcuwalk mode.
50*4882a593Smuzhiyun */
afs_request_key_rcu(struct afs_cell * cell)51*4882a593Smuzhiyun struct key *afs_request_key_rcu(struct afs_cell *cell)
52*4882a593Smuzhiyun {
53*4882a593Smuzhiyun struct key *key;
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun _enter("{%x}", key_serial(cell->anonymous_key));
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun _debug("key %s", cell->anonymous_key->description);
58*4882a593Smuzhiyun key = request_key_net_rcu(&key_type_rxrpc,
59*4882a593Smuzhiyun cell->anonymous_key->description,
60*4882a593Smuzhiyun cell->net->net);
61*4882a593Smuzhiyun if (IS_ERR(key)) {
62*4882a593Smuzhiyun if (PTR_ERR(key) != -ENOKEY) {
63*4882a593Smuzhiyun _leave(" = %ld", PTR_ERR(key));
64*4882a593Smuzhiyun return key;
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun /* act as anonymous user */
68*4882a593Smuzhiyun _leave(" = {%x} [anon]", key_serial(cell->anonymous_key));
69*4882a593Smuzhiyun return key_get(cell->anonymous_key);
70*4882a593Smuzhiyun } else {
71*4882a593Smuzhiyun /* act as authorised user */
72*4882a593Smuzhiyun _leave(" = {%x} [auth]", key_serial(key));
73*4882a593Smuzhiyun return key;
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun }
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun /*
78*4882a593Smuzhiyun * Dispose of a list of permits.
79*4882a593Smuzhiyun */
afs_permits_rcu(struct rcu_head * rcu)80*4882a593Smuzhiyun static void afs_permits_rcu(struct rcu_head *rcu)
81*4882a593Smuzhiyun {
82*4882a593Smuzhiyun struct afs_permits *permits =
83*4882a593Smuzhiyun container_of(rcu, struct afs_permits, rcu);
84*4882a593Smuzhiyun int i;
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun for (i = 0; i < permits->nr_permits; i++)
87*4882a593Smuzhiyun key_put(permits->permits[i].key);
88*4882a593Smuzhiyun kfree(permits);
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun /*
92*4882a593Smuzhiyun * Discard a permission cache.
93*4882a593Smuzhiyun */
afs_put_permits(struct afs_permits * permits)94*4882a593Smuzhiyun void afs_put_permits(struct afs_permits *permits)
95*4882a593Smuzhiyun {
96*4882a593Smuzhiyun if (permits && refcount_dec_and_test(&permits->usage)) {
97*4882a593Smuzhiyun spin_lock(&afs_permits_lock);
98*4882a593Smuzhiyun hash_del_rcu(&permits->hash_node);
99*4882a593Smuzhiyun spin_unlock(&afs_permits_lock);
100*4882a593Smuzhiyun call_rcu(&permits->rcu, afs_permits_rcu);
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun /*
105*4882a593Smuzhiyun * Clear a permit cache on callback break.
106*4882a593Smuzhiyun */
afs_clear_permits(struct afs_vnode * vnode)107*4882a593Smuzhiyun void afs_clear_permits(struct afs_vnode *vnode)
108*4882a593Smuzhiyun {
109*4882a593Smuzhiyun struct afs_permits *permits;
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun spin_lock(&vnode->lock);
112*4882a593Smuzhiyun permits = rcu_dereference_protected(vnode->permit_cache,
113*4882a593Smuzhiyun lockdep_is_held(&vnode->lock));
114*4882a593Smuzhiyun RCU_INIT_POINTER(vnode->permit_cache, NULL);
115*4882a593Smuzhiyun spin_unlock(&vnode->lock);
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun afs_put_permits(permits);
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun /*
121*4882a593Smuzhiyun * Hash a list of permits. Use simple addition to make it easy to add an extra
122*4882a593Smuzhiyun * one at an as-yet indeterminate position in the list.
123*4882a593Smuzhiyun */
afs_hash_permits(struct afs_permits * permits)124*4882a593Smuzhiyun static void afs_hash_permits(struct afs_permits *permits)
125*4882a593Smuzhiyun {
126*4882a593Smuzhiyun unsigned long h = permits->nr_permits;
127*4882a593Smuzhiyun int i;
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun for (i = 0; i < permits->nr_permits; i++) {
130*4882a593Smuzhiyun h += (unsigned long)permits->permits[i].key / sizeof(void *);
131*4882a593Smuzhiyun h += permits->permits[i].access;
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun permits->h = h;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun /*
138*4882a593Smuzhiyun * Cache the CallerAccess result obtained from doing a fileserver operation
139*4882a593Smuzhiyun * that returned a vnode status for a particular key. If a callback break
140*4882a593Smuzhiyun * occurs whilst the operation was in progress then we have to ditch the cache
141*4882a593Smuzhiyun * as the ACL *may* have changed.
142*4882a593Smuzhiyun */
afs_cache_permit(struct afs_vnode * vnode,struct key * key,unsigned int cb_break,struct afs_status_cb * scb)143*4882a593Smuzhiyun void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
144*4882a593Smuzhiyun unsigned int cb_break, struct afs_status_cb *scb)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun struct afs_permits *permits, *xpermits, *replacement, *zap, *new = NULL;
147*4882a593Smuzhiyun afs_access_t caller_access = scb->status.caller_access;
148*4882a593Smuzhiyun size_t size = 0;
149*4882a593Smuzhiyun bool changed = false;
150*4882a593Smuzhiyun int i, j;
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun _enter("{%llx:%llu},%x,%x",
153*4882a593Smuzhiyun vnode->fid.vid, vnode->fid.vnode, key_serial(key), caller_access);
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun rcu_read_lock();
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun /* Check for the common case first: We got back the same access as last
158*4882a593Smuzhiyun * time we tried and already have it recorded.
159*4882a593Smuzhiyun */
160*4882a593Smuzhiyun permits = rcu_dereference(vnode->permit_cache);
161*4882a593Smuzhiyun if (permits) {
162*4882a593Smuzhiyun if (!permits->invalidated) {
163*4882a593Smuzhiyun for (i = 0; i < permits->nr_permits; i++) {
164*4882a593Smuzhiyun if (permits->permits[i].key < key)
165*4882a593Smuzhiyun continue;
166*4882a593Smuzhiyun if (permits->permits[i].key > key)
167*4882a593Smuzhiyun break;
168*4882a593Smuzhiyun if (permits->permits[i].access != caller_access) {
169*4882a593Smuzhiyun changed = true;
170*4882a593Smuzhiyun break;
171*4882a593Smuzhiyun }
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun if (afs_cb_is_broken(cb_break, vnode)) {
174*4882a593Smuzhiyun changed = true;
175*4882a593Smuzhiyun break;
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun /* The cache is still good. */
179*4882a593Smuzhiyun rcu_read_unlock();
180*4882a593Smuzhiyun return;
181*4882a593Smuzhiyun }
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun changed |= permits->invalidated;
185*4882a593Smuzhiyun size = permits->nr_permits;
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun /* If this set of permits is now wrong, clear the permits
188*4882a593Smuzhiyun * pointer so that no one tries to use the stale information.
189*4882a593Smuzhiyun */
190*4882a593Smuzhiyun if (changed) {
191*4882a593Smuzhiyun spin_lock(&vnode->lock);
192*4882a593Smuzhiyun if (permits != rcu_access_pointer(vnode->permit_cache))
193*4882a593Smuzhiyun goto someone_else_changed_it_unlock;
194*4882a593Smuzhiyun RCU_INIT_POINTER(vnode->permit_cache, NULL);
195*4882a593Smuzhiyun spin_unlock(&vnode->lock);
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun afs_put_permits(permits);
198*4882a593Smuzhiyun permits = NULL;
199*4882a593Smuzhiyun size = 0;
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun if (afs_cb_is_broken(cb_break, vnode))
204*4882a593Smuzhiyun goto someone_else_changed_it;
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun /* We need a ref on any permits list we want to copy as we'll have to
207*4882a593Smuzhiyun * drop the lock to do memory allocation.
208*4882a593Smuzhiyun */
209*4882a593Smuzhiyun if (permits && !refcount_inc_not_zero(&permits->usage))
210*4882a593Smuzhiyun goto someone_else_changed_it;
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun rcu_read_unlock();
213*4882a593Smuzhiyun
214*4882a593Smuzhiyun /* Speculatively create a new list with the revised permission set. We
215*4882a593Smuzhiyun * discard this if we find an extant match already in the hash, but
216*4882a593Smuzhiyun * it's easier to compare with memcmp this way.
217*4882a593Smuzhiyun *
218*4882a593Smuzhiyun * We fill in the key pointers at this time, but we don't get the refs
219*4882a593Smuzhiyun * yet.
220*4882a593Smuzhiyun */
221*4882a593Smuzhiyun size++;
222*4882a593Smuzhiyun new = kzalloc(sizeof(struct afs_permits) +
223*4882a593Smuzhiyun sizeof(struct afs_permit) * size, GFP_NOFS);
224*4882a593Smuzhiyun if (!new)
225*4882a593Smuzhiyun goto out_put;
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun refcount_set(&new->usage, 1);
228*4882a593Smuzhiyun new->nr_permits = size;
229*4882a593Smuzhiyun i = j = 0;
230*4882a593Smuzhiyun if (permits) {
231*4882a593Smuzhiyun for (i = 0; i < permits->nr_permits; i++) {
232*4882a593Smuzhiyun if (j == i && permits->permits[i].key > key) {
233*4882a593Smuzhiyun new->permits[j].key = key;
234*4882a593Smuzhiyun new->permits[j].access = caller_access;
235*4882a593Smuzhiyun j++;
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun new->permits[j].key = permits->permits[i].key;
238*4882a593Smuzhiyun new->permits[j].access = permits->permits[i].access;
239*4882a593Smuzhiyun j++;
240*4882a593Smuzhiyun }
241*4882a593Smuzhiyun }
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun if (j == i) {
244*4882a593Smuzhiyun new->permits[j].key = key;
245*4882a593Smuzhiyun new->permits[j].access = caller_access;
246*4882a593Smuzhiyun }
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun afs_hash_permits(new);
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun /* Now see if the permit list we want is actually already available */
251*4882a593Smuzhiyun spin_lock(&afs_permits_lock);
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun hash_for_each_possible(afs_permits_cache, xpermits, hash_node, new->h) {
254*4882a593Smuzhiyun if (xpermits->h != new->h ||
255*4882a593Smuzhiyun xpermits->invalidated ||
256*4882a593Smuzhiyun xpermits->nr_permits != new->nr_permits ||
257*4882a593Smuzhiyun memcmp(xpermits->permits, new->permits,
258*4882a593Smuzhiyun new->nr_permits * sizeof(struct afs_permit)) != 0)
259*4882a593Smuzhiyun continue;
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun if (refcount_inc_not_zero(&xpermits->usage)) {
262*4882a593Smuzhiyun replacement = xpermits;
263*4882a593Smuzhiyun goto found;
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun break;
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun
269*4882a593Smuzhiyun for (i = 0; i < new->nr_permits; i++)
270*4882a593Smuzhiyun key_get(new->permits[i].key);
271*4882a593Smuzhiyun hash_add_rcu(afs_permits_cache, &new->hash_node, new->h);
272*4882a593Smuzhiyun replacement = new;
273*4882a593Smuzhiyun new = NULL;
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun found:
276*4882a593Smuzhiyun spin_unlock(&afs_permits_lock);
277*4882a593Smuzhiyun
278*4882a593Smuzhiyun kfree(new);
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun rcu_read_lock();
281*4882a593Smuzhiyun spin_lock(&vnode->lock);
282*4882a593Smuzhiyun zap = rcu_access_pointer(vnode->permit_cache);
283*4882a593Smuzhiyun if (!afs_cb_is_broken(cb_break, vnode) && zap == permits)
284*4882a593Smuzhiyun rcu_assign_pointer(vnode->permit_cache, replacement);
285*4882a593Smuzhiyun else
286*4882a593Smuzhiyun zap = replacement;
287*4882a593Smuzhiyun spin_unlock(&vnode->lock);
288*4882a593Smuzhiyun rcu_read_unlock();
289*4882a593Smuzhiyun afs_put_permits(zap);
290*4882a593Smuzhiyun out_put:
291*4882a593Smuzhiyun afs_put_permits(permits);
292*4882a593Smuzhiyun return;
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun someone_else_changed_it_unlock:
295*4882a593Smuzhiyun spin_unlock(&vnode->lock);
296*4882a593Smuzhiyun someone_else_changed_it:
297*4882a593Smuzhiyun /* Someone else changed the cache under us - don't recheck at this
298*4882a593Smuzhiyun * time.
299*4882a593Smuzhiyun */
300*4882a593Smuzhiyun rcu_read_unlock();
301*4882a593Smuzhiyun return;
302*4882a593Smuzhiyun }
303*4882a593Smuzhiyun
afs_check_permit_rcu(struct afs_vnode * vnode,struct key * key,afs_access_t * _access)304*4882a593Smuzhiyun static bool afs_check_permit_rcu(struct afs_vnode *vnode, struct key *key,
305*4882a593Smuzhiyun afs_access_t *_access)
306*4882a593Smuzhiyun {
307*4882a593Smuzhiyun const struct afs_permits *permits;
308*4882a593Smuzhiyun int i;
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun _enter("{%llx:%llu},%x",
311*4882a593Smuzhiyun vnode->fid.vid, vnode->fid.vnode, key_serial(key));
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun /* check the permits to see if we've got one yet */
314*4882a593Smuzhiyun if (key == vnode->volume->cell->anonymous_key) {
315*4882a593Smuzhiyun *_access = vnode->status.anon_access;
316*4882a593Smuzhiyun _leave(" = t [anon %x]", *_access);
317*4882a593Smuzhiyun return true;
318*4882a593Smuzhiyun }
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun permits = rcu_dereference(vnode->permit_cache);
321*4882a593Smuzhiyun if (permits) {
322*4882a593Smuzhiyun for (i = 0; i < permits->nr_permits; i++) {
323*4882a593Smuzhiyun if (permits->permits[i].key < key)
324*4882a593Smuzhiyun continue;
325*4882a593Smuzhiyun if (permits->permits[i].key > key)
326*4882a593Smuzhiyun break;
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun *_access = permits->permits[i].access;
329*4882a593Smuzhiyun _leave(" = %u [perm %x]", !permits->invalidated, *_access);
330*4882a593Smuzhiyun return !permits->invalidated;
331*4882a593Smuzhiyun }
332*4882a593Smuzhiyun }
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun _leave(" = f");
335*4882a593Smuzhiyun return false;
336*4882a593Smuzhiyun }
337*4882a593Smuzhiyun
338*4882a593Smuzhiyun /*
339*4882a593Smuzhiyun * check with the fileserver to see if the directory or parent directory is
340*4882a593Smuzhiyun * permitted to be accessed with this authorisation, and if so, what access it
341*4882a593Smuzhiyun * is granted
342*4882a593Smuzhiyun */
afs_check_permit(struct afs_vnode * vnode,struct key * key,afs_access_t * _access)343*4882a593Smuzhiyun int afs_check_permit(struct afs_vnode *vnode, struct key *key,
344*4882a593Smuzhiyun afs_access_t *_access)
345*4882a593Smuzhiyun {
346*4882a593Smuzhiyun struct afs_permits *permits;
347*4882a593Smuzhiyun bool valid = false;
348*4882a593Smuzhiyun int i, ret;
349*4882a593Smuzhiyun
350*4882a593Smuzhiyun _enter("{%llx:%llu},%x",
351*4882a593Smuzhiyun vnode->fid.vid, vnode->fid.vnode, key_serial(key));
352*4882a593Smuzhiyun
353*4882a593Smuzhiyun /* check the permits to see if we've got one yet */
354*4882a593Smuzhiyun if (key == vnode->volume->cell->anonymous_key) {
355*4882a593Smuzhiyun _debug("anon");
356*4882a593Smuzhiyun *_access = vnode->status.anon_access;
357*4882a593Smuzhiyun valid = true;
358*4882a593Smuzhiyun } else {
359*4882a593Smuzhiyun rcu_read_lock();
360*4882a593Smuzhiyun permits = rcu_dereference(vnode->permit_cache);
361*4882a593Smuzhiyun if (permits) {
362*4882a593Smuzhiyun for (i = 0; i < permits->nr_permits; i++) {
363*4882a593Smuzhiyun if (permits->permits[i].key < key)
364*4882a593Smuzhiyun continue;
365*4882a593Smuzhiyun if (permits->permits[i].key > key)
366*4882a593Smuzhiyun break;
367*4882a593Smuzhiyun
368*4882a593Smuzhiyun *_access = permits->permits[i].access;
369*4882a593Smuzhiyun valid = !permits->invalidated;
370*4882a593Smuzhiyun break;
371*4882a593Smuzhiyun }
372*4882a593Smuzhiyun }
373*4882a593Smuzhiyun rcu_read_unlock();
374*4882a593Smuzhiyun }
375*4882a593Smuzhiyun
376*4882a593Smuzhiyun if (!valid) {
377*4882a593Smuzhiyun /* Check the status on the file we're actually interested in
378*4882a593Smuzhiyun * (the post-processing will cache the result).
379*4882a593Smuzhiyun */
380*4882a593Smuzhiyun _debug("no valid permit");
381*4882a593Smuzhiyun
382*4882a593Smuzhiyun ret = afs_fetch_status(vnode, key, false, _access);
383*4882a593Smuzhiyun if (ret < 0) {
384*4882a593Smuzhiyun *_access = 0;
385*4882a593Smuzhiyun _leave(" = %d", ret);
386*4882a593Smuzhiyun return ret;
387*4882a593Smuzhiyun }
388*4882a593Smuzhiyun }
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun _leave(" = 0 [access %x]", *_access);
391*4882a593Smuzhiyun return 0;
392*4882a593Smuzhiyun }
393*4882a593Smuzhiyun
394*4882a593Smuzhiyun /*
395*4882a593Smuzhiyun * check the permissions on an AFS file
396*4882a593Smuzhiyun * - AFS ACLs are attached to directories only, and a file is controlled by its
397*4882a593Smuzhiyun * parent directory's ACL
398*4882a593Smuzhiyun */
afs_permission(struct inode * inode,int mask)399*4882a593Smuzhiyun int afs_permission(struct inode *inode, int mask)
400*4882a593Smuzhiyun {
401*4882a593Smuzhiyun struct afs_vnode *vnode = AFS_FS_I(inode);
402*4882a593Smuzhiyun afs_access_t access;
403*4882a593Smuzhiyun struct key *key;
404*4882a593Smuzhiyun int ret = 0;
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun _enter("{{%llx:%llu},%lx},%x,",
407*4882a593Smuzhiyun vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask);
408*4882a593Smuzhiyun
409*4882a593Smuzhiyun if (mask & MAY_NOT_BLOCK) {
410*4882a593Smuzhiyun key = afs_request_key_rcu(vnode->volume->cell);
411*4882a593Smuzhiyun if (IS_ERR(key))
412*4882a593Smuzhiyun return -ECHILD;
413*4882a593Smuzhiyun
414*4882a593Smuzhiyun ret = -ECHILD;
415*4882a593Smuzhiyun if (!afs_check_validity(vnode) ||
416*4882a593Smuzhiyun !afs_check_permit_rcu(vnode, key, &access))
417*4882a593Smuzhiyun goto error;
418*4882a593Smuzhiyun } else {
419*4882a593Smuzhiyun key = afs_request_key(vnode->volume->cell);
420*4882a593Smuzhiyun if (IS_ERR(key)) {
421*4882a593Smuzhiyun _leave(" = %ld [key]", PTR_ERR(key));
422*4882a593Smuzhiyun return PTR_ERR(key);
423*4882a593Smuzhiyun }
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun ret = afs_validate(vnode, key);
426*4882a593Smuzhiyun if (ret < 0)
427*4882a593Smuzhiyun goto error;
428*4882a593Smuzhiyun
429*4882a593Smuzhiyun /* check the permits to see if we've got one yet */
430*4882a593Smuzhiyun ret = afs_check_permit(vnode, key, &access);
431*4882a593Smuzhiyun if (ret < 0)
432*4882a593Smuzhiyun goto error;
433*4882a593Smuzhiyun }
434*4882a593Smuzhiyun
435*4882a593Smuzhiyun /* interpret the access mask */
436*4882a593Smuzhiyun _debug("REQ %x ACC %x on %s",
437*4882a593Smuzhiyun mask, access, S_ISDIR(inode->i_mode) ? "dir" : "file");
438*4882a593Smuzhiyun
439*4882a593Smuzhiyun ret = 0;
440*4882a593Smuzhiyun if (S_ISDIR(inode->i_mode)) {
441*4882a593Smuzhiyun if (mask & (MAY_EXEC | MAY_READ | MAY_CHDIR)) {
442*4882a593Smuzhiyun if (!(access & AFS_ACE_LOOKUP))
443*4882a593Smuzhiyun goto permission_denied;
444*4882a593Smuzhiyun }
445*4882a593Smuzhiyun if (mask & MAY_WRITE) {
446*4882a593Smuzhiyun if (!(access & (AFS_ACE_DELETE | /* rmdir, unlink, rename from */
447*4882a593Smuzhiyun AFS_ACE_INSERT))) /* create, mkdir, symlink, rename to */
448*4882a593Smuzhiyun goto permission_denied;
449*4882a593Smuzhiyun }
450*4882a593Smuzhiyun } else {
451*4882a593Smuzhiyun if (!(access & AFS_ACE_LOOKUP))
452*4882a593Smuzhiyun goto permission_denied;
453*4882a593Smuzhiyun if ((mask & MAY_EXEC) && !(inode->i_mode & S_IXUSR))
454*4882a593Smuzhiyun goto permission_denied;
455*4882a593Smuzhiyun if (mask & (MAY_EXEC | MAY_READ)) {
456*4882a593Smuzhiyun if (!(access & AFS_ACE_READ))
457*4882a593Smuzhiyun goto permission_denied;
458*4882a593Smuzhiyun if (!(inode->i_mode & S_IRUSR))
459*4882a593Smuzhiyun goto permission_denied;
460*4882a593Smuzhiyun } else if (mask & MAY_WRITE) {
461*4882a593Smuzhiyun if (!(access & AFS_ACE_WRITE))
462*4882a593Smuzhiyun goto permission_denied;
463*4882a593Smuzhiyun if (!(inode->i_mode & S_IWUSR))
464*4882a593Smuzhiyun goto permission_denied;
465*4882a593Smuzhiyun }
466*4882a593Smuzhiyun }
467*4882a593Smuzhiyun
468*4882a593Smuzhiyun key_put(key);
469*4882a593Smuzhiyun _leave(" = %d", ret);
470*4882a593Smuzhiyun return ret;
471*4882a593Smuzhiyun
472*4882a593Smuzhiyun permission_denied:
473*4882a593Smuzhiyun ret = -EACCES;
474*4882a593Smuzhiyun error:
475*4882a593Smuzhiyun key_put(key);
476*4882a593Smuzhiyun _leave(" = %d", ret);
477*4882a593Smuzhiyun return ret;
478*4882a593Smuzhiyun }
479*4882a593Smuzhiyun
afs_clean_up_permit_cache(void)480*4882a593Smuzhiyun void __exit afs_clean_up_permit_cache(void)
481*4882a593Smuzhiyun {
482*4882a593Smuzhiyun int i;
483*4882a593Smuzhiyun
484*4882a593Smuzhiyun for (i = 0; i < HASH_SIZE(afs_permits_cache); i++)
485*4882a593Smuzhiyun WARN_ON_ONCE(!hlist_empty(&afs_permits_cache[i]));
486*4882a593Smuzhiyun
487*4882a593Smuzhiyun }
488