linux/fs/afs/security.c
<<
>>
Prefs
   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
  17static DEFINE_HASHTABLE(afs_permits_cache, 10);
  18static DEFINE_SPINLOCK(afs_permits_lock);
  19
  20/*
  21 * get a key
  22 */
  23struct 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 */
  51struct 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 */
  80static 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 */
  94void 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 */
 107void 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 */
 124static 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 */
 143void 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                                        changed = true;
 175                                        break;
 176                                }
 177
 178                                /* The cache is still good. */
 179                                rcu_read_unlock();
 180                                return;
 181                        }
 182                }
 183
 184                changed |= permits->invalidated;
 185                size = permits->nr_permits;
 186
 187                /* If this set of permits is now wrong, clear the permits
 188                 * pointer so that no one tries to use the stale information.
 189                 */
 190                if (changed) {
 191                        spin_lock(&vnode->lock);
 192                        if (permits != rcu_access_pointer(vnode->permit_cache))
 193                                goto someone_else_changed_it_unlock;
 194                        RCU_INIT_POINTER(vnode->permit_cache, NULL);
 195                        spin_unlock(&vnode->lock);
 196
 197                        afs_put_permits(permits);
 198                        permits = NULL;
 199                        size = 0;
 200                }
 201        }
 202
 203        if (afs_cb_is_broken(cb_break, vnode))
 204                goto someone_else_changed_it;
 205
 206        /* We need a ref on any permits list we want to copy as we'll have to
 207         * drop the lock to do memory allocation.
 208         */
 209        if (permits && !refcount_inc_not_zero(&permits->usage))
 210                goto someone_else_changed_it;
 211
 212        rcu_read_unlock();
 213
 214        /* Speculatively create a new list with the revised permission set.  We
 215         * discard this if we find an extant match already in the hash, but
 216         * it's easier to compare with memcmp this way.
 217         *
 218         * We fill in the key pointers at this time, but we don't get the refs
 219         * yet.
 220         */
 221        size++;
 222        new = kzalloc(sizeof(struct afs_permits) +
 223                      sizeof(struct afs_permit) * size, GFP_NOFS);
 224        if (!new)
 225                goto out_put;
 226
 227        refcount_set(&new->usage, 1);
 228        new->nr_permits = size;
 229        i = j = 0;
 230        if (permits) {
 231                for (i = 0; i < permits->nr_permits; i++) {
 232                        if (j == i && permits->permits[i].key > key) {
 233                                new->permits[j].key = key;
 234                                new->permits[j].access = caller_access;
 235                                j++;
 236                        }
 237                        new->permits[j].key = permits->permits[i].key;
 238                        new->permits[j].access = permits->permits[i].access;
 239                        j++;
 240                }
 241        }
 242
 243        if (j == i) {
 244                new->permits[j].key = key;
 245                new->permits[j].access = caller_access;
 246        }
 247
 248        afs_hash_permits(new);
 249
 250        /* Now see if the permit list we want is actually already available */
 251        spin_lock(&afs_permits_lock);
 252
 253        hash_for_each_possible(afs_permits_cache, xpermits, hash_node, new->h) {
 254                if (xpermits->h != new->h ||
 255                    xpermits->invalidated ||
 256                    xpermits->nr_permits != new->nr_permits ||
 257                    memcmp(xpermits->permits, new->permits,
 258                           new->nr_permits * sizeof(struct afs_permit)) != 0)
 259                        continue;
 260
 261                if (refcount_inc_not_zero(&xpermits->usage)) {
 262                        replacement = xpermits;
 263                        goto found;
 264                }
 265
 266                break;
 267        }
 268
 269        for (i = 0; i < new->nr_permits; i++)
 270                key_get(new->permits[i].key);
 271        hash_add_rcu(afs_permits_cache, &new->hash_node, new->h);
 272        replacement = new;
 273        new = NULL;
 274
 275found:
 276        spin_unlock(&afs_permits_lock);
 277
 278        kfree(new);
 279
 280        rcu_read_lock();
 281        spin_lock(&vnode->lock);
 282        zap = rcu_access_pointer(vnode->permit_cache);
 283        if (!afs_cb_is_broken(cb_break, vnode) && zap == permits)
 284                rcu_assign_pointer(vnode->permit_cache, replacement);
 285        else
 286                zap = replacement;
 287        spin_unlock(&vnode->lock);
 288        rcu_read_unlock();
 289        afs_put_permits(zap);
 290out_put:
 291        afs_put_permits(permits);
 292        return;
 293
 294someone_else_changed_it_unlock:
 295        spin_unlock(&vnode->lock);
 296someone_else_changed_it:
 297        /* Someone else changed the cache under us - don't recheck at this
 298         * time.
 299         */
 300        rcu_read_unlock();
 301        return;
 302}
 303
 304static bool afs_check_permit_rcu(struct afs_vnode *vnode, struct key *key,
 305                                 afs_access_t *_access)
 306{
 307        const struct afs_permits *permits;
 308        int i;
 309
 310        _enter("{%llx:%llu},%x",
 311               vnode->fid.vid, vnode->fid.vnode, key_serial(key));
 312
 313        /* check the permits to see if we've got one yet */
 314        if (key == vnode->volume->cell->anonymous_key) {
 315                *_access = vnode->status.anon_access;
 316                _leave(" = t [anon %x]", *_access);
 317                return true;
 318        }
 319
 320        permits = rcu_dereference(vnode->permit_cache);
 321        if (permits) {
 322                for (i = 0; i < permits->nr_permits; i++) {
 323                        if (permits->permits[i].key < key)
 324                                continue;
 325                        if (permits->permits[i].key > key)
 326                                break;
 327
 328                        *_access = permits->permits[i].access;
 329                        _leave(" = %u [perm %x]", !permits->invalidated, *_access);
 330                        return !permits->invalidated;
 331                }
 332        }
 333
 334        _leave(" = f");
 335        return false;
 336}
 337
 338/*
 339 * check with the fileserver to see if the directory or parent directory is
 340 * permitted to be accessed with this authorisation, and if so, what access it
 341 * is granted
 342 */
 343int afs_check_permit(struct afs_vnode *vnode, struct key *key,
 344                     afs_access_t *_access)
 345{
 346        struct afs_permits *permits;
 347        bool valid = false;
 348        int i, ret;
 349
 350        _enter("{%llx:%llu},%x",
 351               vnode->fid.vid, vnode->fid.vnode, key_serial(key));
 352
 353        /* check the permits to see if we've got one yet */
 354        if (key == vnode->volume->cell->anonymous_key) {
 355                _debug("anon");
 356                *_access = vnode->status.anon_access;
 357                valid = true;
 358        } else {
 359                rcu_read_lock();
 360                permits = rcu_dereference(vnode->permit_cache);
 361                if (permits) {
 362                        for (i = 0; i < permits->nr_permits; i++) {
 363                                if (permits->permits[i].key < key)
 364                                        continue;
 365                                if (permits->permits[i].key > key)
 366                                        break;
 367
 368                                *_access = permits->permits[i].access;
 369                                valid = !permits->invalidated;
 370                                break;
 371                        }
 372                }
 373                rcu_read_unlock();
 374        }
 375
 376        if (!valid) {
 377                /* Check the status on the file we're actually interested in
 378                 * (the post-processing will cache the result).
 379                 */
 380                _debug("no valid permit");
 381
 382                ret = afs_fetch_status(vnode, key, false, _access);
 383                if (ret < 0) {
 384                        *_access = 0;
 385                        _leave(" = %d", ret);
 386                        return ret;
 387                }
 388        }
 389
 390        _leave(" = 0 [access %x]", *_access);
 391        return 0;
 392}
 393
 394/*
 395 * check the permissions on an AFS file
 396 * - AFS ACLs are attached to directories only, and a file is controlled by its
 397 *   parent directory's ACL
 398 */
 399int afs_permission(struct user_namespace *mnt_userns, struct inode *inode,
 400                   int mask)
 401{
 402        struct afs_vnode *vnode = AFS_FS_I(inode);
 403        afs_access_t access;
 404        struct key *key;
 405        int ret = 0;
 406
 407        _enter("{{%llx:%llu},%lx},%x,",
 408               vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask);
 409
 410        if (mask & MAY_NOT_BLOCK) {
 411                key = afs_request_key_rcu(vnode->volume->cell);
 412                if (IS_ERR(key))
 413                        return -ECHILD;
 414
 415                ret = -ECHILD;
 416                if (!afs_check_validity(vnode) ||
 417                    !afs_check_permit_rcu(vnode, key, &access))
 418                        goto error;
 419        } else {
 420                key = afs_request_key(vnode->volume->cell);
 421                if (IS_ERR(key)) {
 422                        _leave(" = %ld [key]", PTR_ERR(key));
 423                        return PTR_ERR(key);
 424                }
 425
 426                ret = afs_validate(vnode, key);
 427                if (ret < 0)
 428                        goto error;
 429
 430                /* check the permits to see if we've got one yet */
 431                ret = afs_check_permit(vnode, key, &access);
 432                if (ret < 0)
 433                        goto error;
 434        }
 435
 436        /* interpret the access mask */
 437        _debug("REQ %x ACC %x on %s",
 438               mask, access, S_ISDIR(inode->i_mode) ? "dir" : "file");
 439
 440        ret = 0;
 441        if (S_ISDIR(inode->i_mode)) {
 442                if (mask & (MAY_EXEC | MAY_READ | MAY_CHDIR)) {
 443                        if (!(access & AFS_ACE_LOOKUP))
 444                                goto permission_denied;
 445                }
 446                if (mask & MAY_WRITE) {
 447                        if (!(access & (AFS_ACE_DELETE | /* rmdir, unlink, rename from */
 448                                        AFS_ACE_INSERT))) /* create, mkdir, symlink, rename to */
 449                                goto permission_denied;
 450                }
 451        } else {
 452                if (!(access & AFS_ACE_LOOKUP))
 453                        goto permission_denied;
 454                if ((mask & MAY_EXEC) && !(inode->i_mode & S_IXUSR))
 455                        goto permission_denied;
 456                if (mask & (MAY_EXEC | MAY_READ)) {
 457                        if (!(access & AFS_ACE_READ))
 458                                goto permission_denied;
 459                        if (!(inode->i_mode & S_IRUSR))
 460                                goto permission_denied;
 461                } else if (mask & MAY_WRITE) {
 462                        if (!(access & AFS_ACE_WRITE))
 463                                goto permission_denied;
 464                        if (!(inode->i_mode & S_IWUSR))
 465                                goto permission_denied;
 466                }
 467        }
 468
 469        key_put(key);
 470        _leave(" = %d", ret);
 471        return ret;
 472
 473permission_denied:
 474        ret = -EACCES;
 475error:
 476        key_put(key);
 477        _leave(" = %d", ret);
 478        return ret;
 479}
 480
 481void __exit afs_clean_up_permit_cache(void)
 482{
 483        int i;
 484
 485        for (i = 0; i < HASH_SIZE(afs_permits_cache); i++)
 486                WARN_ON_ONCE(!hlist_empty(&afs_permits_cache[i]));
 487
 488}
 489