| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* AFS security handling |
|---|
| 2 | 3 | * |
|---|
| 3 | 4 | * Copyright (C) 2007, 2017 Red Hat, Inc. All Rights Reserved. |
|---|
| 4 | 5 | * Written by David Howells (dhowells@redhat.com) |
|---|
| 5 | | - * |
|---|
| 6 | | - * This program is free software; you can redistribute it and/or |
|---|
| 7 | | - * modify it under the terms of the GNU General Public License |
|---|
| 8 | | - * as published by the Free Software Foundation; either version |
|---|
| 9 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 10 | 6 | */ |
|---|
| 11 | 7 | |
|---|
| 12 | 8 | #include <linux/init.h> |
|---|
| .. | .. |
|---|
| 31 | 27 | _enter("{%x}", key_serial(cell->anonymous_key)); |
|---|
| 32 | 28 | |
|---|
| 33 | 29 | _debug("key %s", cell->anonymous_key->description); |
|---|
| 34 | | - key = request_key(&key_type_rxrpc, cell->anonymous_key->description, |
|---|
| 35 | | - NULL); |
|---|
| 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); |
|---|
| 36 | 61 | if (IS_ERR(key)) { |
|---|
| 37 | 62 | if (PTR_ERR(key) != -ENOKEY) { |
|---|
| 38 | 63 | _leave(" = %ld", PTR_ERR(key)); |
|---|
| .. | .. |
|---|
| 116 | 141 | * as the ACL *may* have changed. |
|---|
| 117 | 142 | */ |
|---|
| 118 | 143 | void afs_cache_permit(struct afs_vnode *vnode, struct key *key, |
|---|
| 119 | | - unsigned int cb_break) |
|---|
| 144 | + unsigned int cb_break, struct afs_status_cb *scb) |
|---|
| 120 | 145 | { |
|---|
| 121 | 146 | struct afs_permits *permits, *xpermits, *replacement, *zap, *new = NULL; |
|---|
| 122 | | - afs_access_t caller_access = READ_ONCE(vnode->status.caller_access); |
|---|
| 147 | + afs_access_t caller_access = scb->status.caller_access; |
|---|
| 123 | 148 | size_t size = 0; |
|---|
| 124 | 149 | bool changed = false; |
|---|
| 125 | 150 | int i, j; |
|---|
| 126 | 151 | |
|---|
| 127 | | - _enter("{%x:%u},%x,%x", |
|---|
| 152 | + _enter("{%llx:%llu},%x,%x", |
|---|
| 128 | 153 | vnode->fid.vid, vnode->fid.vnode, key_serial(key), caller_access); |
|---|
| 129 | 154 | |
|---|
| 130 | 155 | rcu_read_lock(); |
|---|
| .. | .. |
|---|
| 145 | 170 | break; |
|---|
| 146 | 171 | } |
|---|
| 147 | 172 | |
|---|
| 148 | | - if (cb_break != afs_cb_break_sum(vnode, vnode->cb_interest)) { |
|---|
| 173 | + if (afs_cb_is_broken(cb_break, vnode)) { |
|---|
| 149 | 174 | changed = true; |
|---|
| 150 | 175 | break; |
|---|
| 151 | 176 | } |
|---|
| .. | .. |
|---|
| 175 | 200 | } |
|---|
| 176 | 201 | } |
|---|
| 177 | 202 | |
|---|
| 178 | | - if (cb_break != afs_cb_break_sum(vnode, vnode->cb_interest)) |
|---|
| 203 | + if (afs_cb_is_broken(cb_break, vnode)) |
|---|
| 179 | 204 | goto someone_else_changed_it; |
|---|
| 180 | 205 | |
|---|
| 181 | 206 | /* We need a ref on any permits list we want to copy as we'll have to |
|---|
| .. | .. |
|---|
| 252 | 277 | |
|---|
| 253 | 278 | kfree(new); |
|---|
| 254 | 279 | |
|---|
| 280 | + rcu_read_lock(); |
|---|
| 255 | 281 | spin_lock(&vnode->lock); |
|---|
| 256 | 282 | zap = rcu_access_pointer(vnode->permit_cache); |
|---|
| 257 | | - if (cb_break == afs_cb_break_sum(vnode, vnode->cb_interest) && |
|---|
| 258 | | - zap == permits) |
|---|
| 283 | + if (!afs_cb_is_broken(cb_break, vnode) && zap == permits) |
|---|
| 259 | 284 | rcu_assign_pointer(vnode->permit_cache, replacement); |
|---|
| 260 | 285 | else |
|---|
| 261 | 286 | zap = replacement; |
|---|
| 262 | 287 | spin_unlock(&vnode->lock); |
|---|
| 288 | + rcu_read_unlock(); |
|---|
| 263 | 289 | afs_put_permits(zap); |
|---|
| 264 | 290 | out_put: |
|---|
| 265 | 291 | afs_put_permits(permits); |
|---|
| .. | .. |
|---|
| 275 | 301 | return; |
|---|
| 276 | 302 | } |
|---|
| 277 | 303 | |
|---|
| 304 | +static 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 | + |
|---|
| 278 | 338 | /* |
|---|
| 279 | 339 | * check with the fileserver to see if the directory or parent directory is |
|---|
| 280 | 340 | * permitted to be accessed with this authorisation, and if so, what access it |
|---|
| .. | .. |
|---|
| 287 | 347 | bool valid = false; |
|---|
| 288 | 348 | int i, ret; |
|---|
| 289 | 349 | |
|---|
| 290 | | - _enter("{%x:%u},%x", |
|---|
| 350 | + _enter("{%llx:%llu},%x", |
|---|
| 291 | 351 | vnode->fid.vid, vnode->fid.vnode, key_serial(key)); |
|---|
| 292 | 352 | |
|---|
| 293 | 353 | /* check the permits to see if we've got one yet */ |
|---|
| .. | .. |
|---|
| 319 | 379 | */ |
|---|
| 320 | 380 | _debug("no valid permit"); |
|---|
| 321 | 381 | |
|---|
| 322 | | - ret = afs_fetch_status(vnode, key, false); |
|---|
| 382 | + ret = afs_fetch_status(vnode, key, false, _access); |
|---|
| 323 | 383 | if (ret < 0) { |
|---|
| 324 | 384 | *_access = 0; |
|---|
| 325 | 385 | _leave(" = %d", ret); |
|---|
| 326 | 386 | return ret; |
|---|
| 327 | 387 | } |
|---|
| 328 | | - *_access = vnode->status.caller_access; |
|---|
| 329 | 388 | } |
|---|
| 330 | 389 | |
|---|
| 331 | 390 | _leave(" = 0 [access %x]", *_access); |
|---|
| .. | .. |
|---|
| 340 | 399 | int afs_permission(struct inode *inode, int mask) |
|---|
| 341 | 400 | { |
|---|
| 342 | 401 | struct afs_vnode *vnode = AFS_FS_I(inode); |
|---|
| 343 | | - afs_access_t uninitialized_var(access); |
|---|
| 402 | + afs_access_t access; |
|---|
| 344 | 403 | struct key *key; |
|---|
| 345 | | - int ret; |
|---|
| 404 | + int ret = 0; |
|---|
| 346 | 405 | |
|---|
| 347 | | - if (mask & MAY_NOT_BLOCK) |
|---|
| 348 | | - return -ECHILD; |
|---|
| 349 | | - |
|---|
| 350 | | - _enter("{{%x:%u},%lx},%x,", |
|---|
| 406 | + _enter("{{%llx:%llu},%lx},%x,", |
|---|
| 351 | 407 | vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask); |
|---|
| 352 | 408 | |
|---|
| 353 | | - key = afs_request_key(vnode->volume->cell); |
|---|
| 354 | | - if (IS_ERR(key)) { |
|---|
| 355 | | - _leave(" = %ld [key]", PTR_ERR(key)); |
|---|
| 356 | | - return PTR_ERR(key); |
|---|
| 409 | + if (mask & MAY_NOT_BLOCK) { |
|---|
| 410 | + key = afs_request_key_rcu(vnode->volume->cell); |
|---|
| 411 | + if (IS_ERR(key)) |
|---|
| 412 | + return -ECHILD; |
|---|
| 413 | + |
|---|
| 414 | + ret = -ECHILD; |
|---|
| 415 | + if (!afs_check_validity(vnode) || |
|---|
| 416 | + !afs_check_permit_rcu(vnode, key, &access)) |
|---|
| 417 | + goto error; |
|---|
| 418 | + } else { |
|---|
| 419 | + key = afs_request_key(vnode->volume->cell); |
|---|
| 420 | + if (IS_ERR(key)) { |
|---|
| 421 | + _leave(" = %ld [key]", PTR_ERR(key)); |
|---|
| 422 | + return PTR_ERR(key); |
|---|
| 423 | + } |
|---|
| 424 | + |
|---|
| 425 | + ret = afs_validate(vnode, key); |
|---|
| 426 | + if (ret < 0) |
|---|
| 427 | + goto error; |
|---|
| 428 | + |
|---|
| 429 | + /* check the permits to see if we've got one yet */ |
|---|
| 430 | + ret = afs_check_permit(vnode, key, &access); |
|---|
| 431 | + if (ret < 0) |
|---|
| 432 | + goto error; |
|---|
| 357 | 433 | } |
|---|
| 358 | | - |
|---|
| 359 | | - ret = afs_validate(vnode, key); |
|---|
| 360 | | - if (ret < 0) |
|---|
| 361 | | - goto error; |
|---|
| 362 | | - |
|---|
| 363 | | - /* check the permits to see if we've got one yet */ |
|---|
| 364 | | - ret = afs_check_permit(vnode, key, &access); |
|---|
| 365 | | - if (ret < 0) |
|---|
| 366 | | - goto error; |
|---|
| 367 | 434 | |
|---|
| 368 | 435 | /* interpret the access mask */ |
|---|
| 369 | 436 | _debug("REQ %x ACC %x on %s", |
|---|
| 370 | 437 | mask, access, S_ISDIR(inode->i_mode) ? "dir" : "file"); |
|---|
| 371 | 438 | |
|---|
| 439 | + ret = 0; |
|---|
| 372 | 440 | if (S_ISDIR(inode->i_mode)) { |
|---|
| 373 | 441 | if (mask & (MAY_EXEC | MAY_READ | MAY_CHDIR)) { |
|---|
| 374 | 442 | if (!(access & AFS_ACE_LOOKUP)) |
|---|