// SPDX-License-Identifier: GPL-2.0-or-later /* AFS security handling * * Copyright (C) 2007, 2017 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com)
*/
/* act as anonymous user */
_leave(" = {%x} [anon]", key_serial(cell->anonymous_key)); return key_get(cell->anonymous_key);
} else { /* act as authorised user */
_leave(" = {%x} [auth]", key_serial(key)); return key;
}
}
/* * Dispose of a list of permits.
*/ staticvoid afs_permits_rcu(struct rcu_head *rcu)
{ struct afs_permits *permits =
container_of(rcu, struct afs_permits, rcu); int i;
for (i = 0; i < permits->nr_permits; i++)
key_put(permits->permits[i].key);
kfree(permits);
}
/* * Hash a list of permits. Use simple addition to make it easy to add an extra * one at an as-yet indeterminate position in the list.
*/ staticvoid afs_hash_permits(struct afs_permits *permits)
{ unsignedlong h = permits->nr_permits; int i;
for (i = 0; i < permits->nr_permits; i++) {
h += (unsignedlong)permits->permits[i].key / sizeof(void *);
h += permits->permits[i].access;
}
permits->h = h;
}
/* * Cache the CallerAccess result obtained from doing a fileserver operation * that returned a vnode status for a particular key. If a callback break * occurs whilst the operation was in progress then we have to ditch the cache * as the ACL *may* have changed.
*/ void afs_cache_permit(struct afs_vnode *vnode, struct key *key, unsignedint cb_break, struct afs_status_cb *scb)
{ struct afs_permits *permits, *xpermits, *replacement, *zap, *new = NULL;
afs_access_t caller_access = scb->status.caller_access;
size_t size = 0; bool changed = false; int i, j;
/* Check for the common case first: We got back the same access as last * time we tried and already have it recorded.
*/
permits = rcu_dereference(vnode->permit_cache); if (permits) { if (!permits->invalidated) { for (i = 0; i < permits->nr_permits; i++) { if (permits->permits[i].key < key) continue; if (permits->permits[i].key > key) break; if (permits->permits[i].access != caller_access) {
changed = true; break;
}
if (afs_cb_is_broken(cb_break, vnode)) {
changed = true; break;
}
/* The cache is still good. */
rcu_read_unlock(); return;
}
}
/* If this set of permits is now wrong, clear the permits * pointer so that no one tries to use the stale information.
*/ if (changed) {
spin_lock(&vnode->lock); if (permits != rcu_access_pointer(vnode->permit_cache)) goto someone_else_changed_it_unlock;
RCU_INIT_POINTER(vnode->permit_cache, NULL);
spin_unlock(&vnode->lock);
if (afs_cb_is_broken(cb_break, vnode)) goto someone_else_changed_it;
/* We need a ref on any permits list we want to copy as we'll have to * drop the lock to do memory allocation.
*/ if (permits && !refcount_inc_not_zero(&permits->usage)) goto someone_else_changed_it;
rcu_read_unlock();
/* Speculatively create a new list with the revised permission set. We * discard this if we find an extant match already in the hash, but * it's easier to compare with memcmp this way. * * We fill in the key pointers at this time, but we don't get the refs * yet.
*/
size++; new = kzalloc(struct_size(new, permits, size), GFP_NOFS); if (!new) goto out_put;
refcount_set(&new->usage, 1);
new->nr_permits = size;
i = j = 0; if (permits) { for (i = 0; i < permits->nr_permits; i++) { if (j == i && permits->permits[i].key > key) {
new->permits[j].key = key;
new->permits[j].access = caller_access;
j++;
}
new->permits[j].key = permits->permits[i].key;
new->permits[j].access = permits->permits[i].access;
j++;
}
}
if (refcount_inc_not_zero(&xpermits->usage)) {
replacement = xpermits; goto found;
}
break;
}
for (i = 0; i < new->nr_permits; i++)
key_get(new->permits[i].key);
hash_add_rcu(afs_permits_cache, &new->hash_node, new->h);
replacement = new; new = NULL;
someone_else_changed_it_unlock:
spin_unlock(&vnode->lock);
someone_else_changed_it: /* Someone else changed the cache under us - don't recheck at this * time.
*/
rcu_read_unlock(); return;
}
/* check the permits to see if we've got one yet */ if (key == vnode->volume->cell->anonymous_key) {
*_access = vnode->status.anon_access;
_leave(" = t [anon %x]", *_access); returntrue;
}
permits = rcu_dereference(vnode->permit_cache); if (permits) { for (i = 0; i < permits->nr_permits; i++) { if (permits->permits[i].key < key) continue; if (permits->permits[i].key > key) break;
/* * check with the fileserver to see if the directory or parent directory is * permitted to be accessed with this authorisation, and if so, what access it * is granted
*/ int afs_check_permit(struct afs_vnode *vnode, struct key *key,
afs_access_t *_access)
{ struct afs_permits *permits; bool valid = false; int i, ret;
/* check the permits to see if we've got one yet */ if (key == vnode->volume->cell->anonymous_key) {
_debug("anon");
*_access = vnode->status.anon_access;
valid = true;
} else {
rcu_read_lock();
permits = rcu_dereference(vnode->permit_cache); if (permits) { for (i = 0; i < permits->nr_permits; i++) { if (permits->permits[i].key < key) continue; if (permits->permits[i].key > key) break;
if (!valid) { /* Check the status on the file we're actually interested in * (the post-processing will cache the result).
*/
_debug("no valid permit");
/* * check the permissions on an AFS file * - AFS ACLs are attached to directories only, and a file is controlled by its * parent directory's ACL
*/ int afs_permission(struct mnt_idmap *idmap, struct inode *inode, int mask)
{ struct afs_vnode *vnode = AFS_FS_I(inode);
afs_access_t access; struct key *key; int ret = 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.