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