.. | .. |
---|
| 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)) |
---|