File Index
Files and directories
Sign in
FILE

security.c

File preview with syntax highlighting.

linux-6.19.14/fs/afs/security.c C
  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);
 19static DEFINE_MUTEX(afs_key_lock);
 20
 21/*
 22 * Allocate a key to use as a placeholder for anonymous user security.
 23 */
 24static int afs_alloc_anon_key(struct afs_cell *cell)
 25{
 26	struct key *key;
 27
 28	mutex_lock(&afs_key_lock);
 29	key = cell->anonymous_key;
 30	if (!key) {
 31		key = rxrpc_get_null_key(cell->key_desc);
 32		if (!IS_ERR(key))
 33			cell->anonymous_key = key;
 34	}
 35	mutex_unlock(&afs_key_lock);
 36
 37	if (IS_ERR(key))
 38		return PTR_ERR(key);
 39
 40	_debug("anon key %p{%x}",
 41	       cell->anonymous_key, key_serial(cell->anonymous_key));
 42	return 0;
 43}
 44
 45/*
 46 * get a key
 47 */
 48struct key *afs_request_key(struct afs_cell *cell)
 49{
 50	struct key *key;
 51	int ret;
 52
 53	_enter("{%s}", cell->key_desc);
 54
 55	_debug("key %s", cell->key_desc);
 56	key = request_key_net(&key_type_rxrpc, cell->key_desc,
 57			      cell->net->net, NULL);
 58	if (IS_ERR(key)) {
 59		if (PTR_ERR(key) != -ENOKEY) {
 60			_leave(" = %ld", PTR_ERR(key));
 61			return key;
 62		}
 63
 64		if (!cell->anonymous_key) {
 65			ret = afs_alloc_anon_key(cell);
 66			if (ret < 0)
 67				return ERR_PTR(ret);
 68		}
 69
 70		/* act as anonymous user */
 71		_leave(" = {%x} [anon]", key_serial(cell->anonymous_key));
 72		return key_get(cell->anonymous_key);
 73	} else {
 74		/* act as authorised user */
 75		_leave(" = {%x} [auth]", key_serial(key));
 76		return key;
 77	}
 78}
 79
 80/*
 81 * Get a key when pathwalk is in rcuwalk mode.
 82 */
 83struct key *afs_request_key_rcu(struct afs_cell *cell)
 84{
 85	struct key *key;
 86
 87	_enter("{%s}", cell->key_desc);
 88
 89	_debug("key %s", cell->key_desc);
 90	key = request_key_net_rcu(&key_type_rxrpc, cell->key_desc,
 91				  cell->net->net);
 92	if (IS_ERR(key)) {
 93		if (PTR_ERR(key) != -ENOKEY) {
 94			_leave(" = %ld", PTR_ERR(key));
 95			return key;
 96		}
 97
 98		/* act as anonymous user */
 99		if (!cell->anonymous_key)
100			return NULL; /* Need to allocate */
101		_leave(" = {%x} [anon]", key_serial(cell->anonymous_key));
102		return key_get(cell->anonymous_key);
103	} else {
104		/* act as authorised user */
105		_leave(" = {%x} [auth]", key_serial(key));
106		return key;
107	}
108}
109
110/*
111 * Dispose of a list of permits.
112 */
113static void afs_permits_rcu(struct rcu_head *rcu)
114{
115	struct afs_permits *permits =
116		container_of(rcu, struct afs_permits, rcu);
117	int i;
118
119	for (i = 0; i < permits->nr_permits; i++)
120		key_put(permits->permits[i].key);
121	kfree(permits);
122}
123
124/*
125 * Discard a permission cache.
126 */
127void afs_put_permits(struct afs_permits *permits)
128{
129	if (permits && refcount_dec_and_test(&permits->usage)) {
130		spin_lock(&afs_permits_lock);
131		hash_del_rcu(&permits->hash_node);
132		spin_unlock(&afs_permits_lock);
133		call_rcu(&permits->rcu, afs_permits_rcu);
134	}
135}
136
137/*
138 * Clear a permit cache on callback break.
139 */
140void afs_clear_permits(struct afs_vnode *vnode)
141{
142	struct afs_permits *permits;
143
144	spin_lock(&vnode->lock);
145	permits = rcu_dereference_protected(vnode->permit_cache,
146					    lockdep_is_held(&vnode->lock));
147	RCU_INIT_POINTER(vnode->permit_cache, NULL);
148	spin_unlock(&vnode->lock);
149
150	afs_put_permits(permits);
151}
152
153/*
154 * Hash a list of permits.  Use simple addition to make it easy to add an extra
155 * one at an as-yet indeterminate position in the list.
156 */
157static void afs_hash_permits(struct afs_permits *permits)
158{
159	unsigned long h = permits->nr_permits;
160	int i;
161
162	for (i = 0; i < permits->nr_permits; i++) {
163		h += (unsigned long)permits->permits[i].key / sizeof(void *);
164		h += permits->permits[i].access;
165	}
166
167	permits->h = h;
168}
169
170/*
171 * Cache the CallerAccess result obtained from doing a fileserver operation
172 * that returned a vnode status for a particular key.  If a callback break
173 * occurs whilst the operation was in progress then we have to ditch the cache
174 * as the ACL *may* have changed.
175 */
176void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
177		      unsigned int cb_break, struct afs_status_cb *scb)
178{
179	struct afs_permits *permits, *xpermits, *replacement, *zap, *new = NULL;
180	afs_access_t caller_access = scb->status.caller_access;
181	size_t size = 0;
182	bool changed = false;
183	int i, j;
184
185	_enter("{%llx:%llu},%x,%x",
186	       vnode->fid.vid, vnode->fid.vnode, key_serial(key), caller_access);
187
188	rcu_read_lock();
189
190	/* Check for the common case first: We got back the same access as last
191	 * time we tried and already have it recorded.
192	 */
193	permits = rcu_dereference(vnode->permit_cache);
194	if (permits) {
195		if (!permits->invalidated) {
196			for (i = 0; i < permits->nr_permits; i++) {
197				if (permits->permits[i].key < key)
198					continue;
199				if (permits->permits[i].key > key)
200					break;
201				if (permits->permits[i].access != caller_access) {
202					changed = true;
203					break;
204				}
205
206				if (afs_cb_is_broken(cb_break, vnode)) {
207					changed = true;
208					break;
209				}
210
211				/* The cache is still good. */
212				rcu_read_unlock();
213				return;
214			}
215		}
216
217		changed |= permits->invalidated;
218		size = permits->nr_permits;
219
220		/* If this set of permits is now wrong, clear the permits
221		 * pointer so that no one tries to use the stale information.
222		 */
223		if (changed) {
224			spin_lock(&vnode->lock);
225			if (permits != rcu_access_pointer(vnode->permit_cache))
226				goto someone_else_changed_it_unlock;
227			RCU_INIT_POINTER(vnode->permit_cache, NULL);
228			spin_unlock(&vnode->lock);
229
230			afs_put_permits(permits);
231			permits = NULL;
232			size = 0;
233		}
234	}
235
236	if (afs_cb_is_broken(cb_break, vnode))
237		goto someone_else_changed_it;
238
239	/* We need a ref on any permits list we want to copy as we'll have to
240	 * drop the lock to do memory allocation.
241	 */
242	if (permits && !refcount_inc_not_zero(&permits->usage))
243		goto someone_else_changed_it;
244
245	rcu_read_unlock();
246
247	/* Speculatively create a new list with the revised permission set.  We
248	 * discard this if we find an extant match already in the hash, but
249	 * it's easier to compare with memcmp this way.
250	 *
251	 * We fill in the key pointers at this time, but we don't get the refs
252	 * yet.
253	 */
254	size++;
255	new = kzalloc(struct_size(new, permits, size), GFP_NOFS);
256	if (!new)
257		goto out_put;
258
259	refcount_set(&new->usage, 1);
260	new->nr_permits = size;
261	i = j = 0;
262	if (permits) {
263		for (i = 0; i < permits->nr_permits; i++) {
264			if (j == i && permits->permits[i].key > key) {
265				new->permits[j].key = key;
266				new->permits[j].access = caller_access;
267				j++;
268			}
269			new->permits[j].key = permits->permits[i].key;
270			new->permits[j].access = permits->permits[i].access;
271			j++;
272		}
273	}
274
275	if (j == i) {
276		new->permits[j].key = key;
277		new->permits[j].access = caller_access;
278	}
279
280	afs_hash_permits(new);
281
282	/* Now see if the permit list we want is actually already available */
283	spin_lock(&afs_permits_lock);
284
285	hash_for_each_possible(afs_permits_cache, xpermits, hash_node, new->h) {
286		if (xpermits->h != new->h ||
287		    xpermits->invalidated ||
288		    xpermits->nr_permits != new->nr_permits ||
289		    memcmp(xpermits->permits, new->permits,
290			   new->nr_permits * sizeof(struct afs_permit)) != 0)
291			continue;
292
293		if (refcount_inc_not_zero(&xpermits->usage)) {
294			replacement = xpermits;
295			goto found;
296		}
297
298		break;
299	}
300
301	for (i = 0; i < new->nr_permits; i++)
302		key_get(new->permits[i].key);
303	hash_add_rcu(afs_permits_cache, &new->hash_node, new->h);
304	replacement = new;
305	new = NULL;
306
307found:
308	spin_unlock(&afs_permits_lock);
309
310	kfree(new);
311
312	rcu_read_lock();
313	spin_lock(&vnode->lock);
314	zap = rcu_access_pointer(vnode->permit_cache);
315	if (!afs_cb_is_broken(cb_break, vnode) && zap == permits)
316		rcu_assign_pointer(vnode->permit_cache, replacement);
317	else
318		zap = replacement;
319	spin_unlock(&vnode->lock);
320	rcu_read_unlock();
321	afs_put_permits(zap);
322out_put:
323	afs_put_permits(permits);
324	return;
325
326someone_else_changed_it_unlock:
327	spin_unlock(&vnode->lock);
328someone_else_changed_it:
329	/* Someone else changed the cache under us - don't recheck at this
330	 * time.
331	 */
332	rcu_read_unlock();
333	return;
334}
335
336static bool afs_check_permit_rcu(struct afs_vnode *vnode, struct key *key,
337				 afs_access_t *_access)
338{
339	const struct afs_permits *permits;
340	int i;
341
342	_enter("{%llx:%llu},%x",
343	       vnode->fid.vid, vnode->fid.vnode, key_serial(key));
344
345	/* check the permits to see if we've got one yet */
346	if (key == vnode->volume->cell->anonymous_key) {
347		*_access = vnode->status.anon_access;
348		_leave(" = t [anon %x]", *_access);
349		return true;
350	}
351
352	permits = rcu_dereference(vnode->permit_cache);
353	if (permits) {
354		for (i = 0; i < permits->nr_permits; i++) {
355			if (permits->permits[i].key < key)
356				continue;
357			if (permits->permits[i].key > key)
358				break;
359
360			*_access = permits->permits[i].access;
361			_leave(" = %u [perm %x]", !permits->invalidated, *_access);
362			return !permits->invalidated;
363		}
364	}
365
366	_leave(" = f");
367	return false;
368}
369
370/*
371 * check with the fileserver to see if the directory or parent directory is
372 * permitted to be accessed with this authorisation, and if so, what access it
373 * is granted
374 */
375int afs_check_permit(struct afs_vnode *vnode, struct key *key,
376		     afs_access_t *_access)
377{
378	struct afs_permits *permits;
379	bool valid = false;
380	int i, ret;
381
382	_enter("{%llx:%llu},%x",
383	       vnode->fid.vid, vnode->fid.vnode, key_serial(key));
384
385	/* check the permits to see if we've got one yet */
386	if (key == vnode->volume->cell->anonymous_key) {
387		_debug("anon");
388		*_access = vnode->status.anon_access;
389		valid = true;
390	} else {
391		rcu_read_lock();
392		permits = rcu_dereference(vnode->permit_cache);
393		if (permits) {
394			for (i = 0; i < permits->nr_permits; i++) {
395				if (permits->permits[i].key < key)
396					continue;
397				if (permits->permits[i].key > key)
398					break;
399
400				*_access = permits->permits[i].access;
401				valid = !permits->invalidated;
402				break;
403			}
404		}
405		rcu_read_unlock();
406	}
407
408	if (!valid) {
409		/* Check the status on the file we're actually interested in
410		 * (the post-processing will cache the result).
411		 */
412		_debug("no valid permit");
413
414		ret = afs_fetch_status(vnode, key, false, _access);
415		if (ret < 0) {
416			*_access = 0;
417			_leave(" = %d", ret);
418			return ret;
419		}
420	}
421
422	_leave(" = 0 [access %x]", *_access);
423	return 0;
424}
425
426/*
427 * check the permissions on an AFS file
428 * - AFS ACLs are attached to directories only, and a file is controlled by its
429 *   parent directory's ACL
430 */
431int afs_permission(struct mnt_idmap *idmap, struct inode *inode,
432		   int mask)
433{
434	struct afs_vnode *vnode = AFS_FS_I(inode);
435	afs_access_t access;
436	struct key *key;
437	int ret = 0;
438
439	_enter("{{%llx:%llu},%lx},%x,",
440	       vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask);
441
442	if (mask & MAY_NOT_BLOCK) {
443		key = afs_request_key_rcu(vnode->volume->cell);
444		if (IS_ERR_OR_NULL(key))
445			return -ECHILD;
446
447		ret = -ECHILD;
448		if (!afs_check_validity(vnode) ||
449		    !afs_check_permit_rcu(vnode, key, &access))
450			goto error;
451	} else {
452		key = afs_request_key(vnode->volume->cell);
453		if (IS_ERR(key)) {
454			_leave(" = %ld [key]", PTR_ERR(key));
455			return PTR_ERR(key);
456		}
457
458		ret = afs_validate(vnode, key);
459		if (ret < 0)
460			goto error;
461
462		/* check the permits to see if we've got one yet */
463		ret = afs_check_permit(vnode, key, &access);
464		if (ret < 0)
465			goto error;
466	}
467
468	/* interpret the access mask */
469	_debug("REQ %x ACC %x on %s",
470	       mask, access, S_ISDIR(inode->i_mode) ? "dir" : "file");
471
472	ret = 0;
473	if (S_ISDIR(inode->i_mode)) {
474		if (mask & (MAY_EXEC | MAY_READ | MAY_CHDIR)) {
475			if (!(access & AFS_ACE_LOOKUP))
476				goto permission_denied;
477		}
478		if (mask & MAY_WRITE) {
479			if (!(access & (AFS_ACE_DELETE | /* rmdir, unlink, rename from */
480					AFS_ACE_INSERT))) /* create, mkdir, symlink, rename to */
481				goto permission_denied;
482		}
483	} else {
484		if (!(access & AFS_ACE_LOOKUP))
485			goto permission_denied;
486		if ((mask & MAY_EXEC) && !(inode->i_mode & S_IXUSR))
487			goto permission_denied;
488		if (mask & (MAY_EXEC | MAY_READ)) {
489			if (!(access & AFS_ACE_READ))
490				goto permission_denied;
491			if (!(inode->i_mode & S_IRUSR))
492				goto permission_denied;
493		} else if (mask & MAY_WRITE) {
494			if (!(access & AFS_ACE_WRITE))
495				goto permission_denied;
496			if (!(inode->i_mode & S_IWUSR))
497				goto permission_denied;
498		}
499	}
500
501	key_put(key);
502	_leave(" = %d", ret);
503	return ret;
504
505permission_denied:
506	ret = -EACCES;
507error:
508	key_put(key);
509	_leave(" = %d", ret);
510	return ret;
511}
512
513void __exit afs_clean_up_permit_cache(void)
514{
515	int i;
516
517	for (i = 0; i < HASH_SIZE(afs_permits_cache); i++)
518		WARN_ON_ONCE(!hlist_empty(&afs_permits_cache[i]));
519
520}
~/ Read-only file preview.