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                                                     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
 276found:
 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);
 292out_put:
 293        afs_put_permits(permits);
 294        return;
 295
 296someone_else_changed_it_unlock:
 297        spin_unlock(&vnode->lock);
 298someone_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
 306static 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 */
 345int 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 */
 401int 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
 474permission_denied:
 475        ret = -EACCES;
 476error:
 477        key_put(key);
 478        _leave(" = %d", ret);
 479        return ret;
 480}
 481
 482void __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}
 490