root/fs/afs/security.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. afs_request_key
  2. afs_request_key_rcu
  3. afs_permits_rcu
  4. afs_put_permits
  5. afs_clear_permits
  6. afs_hash_permits
  7. afs_cache_permit
  8. afs_check_permit_rcu
  9. afs_check_permit
  10. afs_permission
  11. afs_clean_up_permit_cache

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

/* [<][>][^][v][top][bottom][index][help] */