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 17 static DEFINE_HASHTABLE(afs_permits_cache, 10); 18 static DEFINE_SPINLOCK(afs_permits_lock); 19 20 /* 21 * get a key 22 */ 23 struct 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(&key_type_rxrpc, cell->anonymous_key->description, 31 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 * Dispose of a list of permits. 50 */ 51 static void afs_permits_rcu(struct rcu_head *rcu) 52 { 53 struct afs_permits *permits = 54 container_of(rcu, struct afs_permits, rcu); 55 int i; 56 57 for (i = 0; i < permits->nr_permits; i++) 58 key_put(permits->permits[i].key); 59 kfree(permits); 60 } 61 62 /* 63 * Discard a permission cache. 64 */ 65 void afs_put_permits(struct afs_permits *permits) 66 { 67 if (permits && refcount_dec_and_test(&permits->usage)) { 68 spin_lock(&afs_permits_lock); 69 hash_del_rcu(&permits->hash_node); 70 spin_unlock(&afs_permits_lock); 71 call_rcu(&permits->rcu, afs_permits_rcu); 72 } 73 } 74 75 /* 76 * Clear a permit cache on callback break. 77 */ 78 void afs_clear_permits(struct afs_vnode *vnode) 79 { 80 struct afs_permits *permits; 81 82 spin_lock(&vnode->lock); 83 permits = rcu_dereference_protected(vnode->permit_cache, 84 lockdep_is_held(&vnode->lock)); 85 RCU_INIT_POINTER(vnode->permit_cache, NULL); 86 spin_unlock(&vnode->lock); 87 88 afs_put_permits(permits); 89 } 90 91 /* 92 * Hash a list of permits. Use simple addition to make it easy to add an extra 93 * one at an as-yet indeterminate position in the list. 94 */ 95 static void afs_hash_permits(struct afs_permits *permits) 96 { 97 unsigned long h = permits->nr_permits; 98 int i; 99 100 for (i = 0; i < permits->nr_permits; i++) { 101 h += (unsigned long)permits->permits[i].key / sizeof(void *); 102 h += permits->permits[i].access; 103 } 104 105 permits->h = h; 106 } 107 108 /* 109 * Cache the CallerAccess result obtained from doing a fileserver operation 110 * that returned a vnode status for a particular key. If a callback break 111 * occurs whilst the operation was in progress then we have to ditch the cache 112 * as the ACL *may* have changed. 113 */ 114 void afs_cache_permit(struct afs_vnode *vnode, struct key *key, 115 unsigned int cb_break, struct afs_status_cb *scb) 116 { 117 struct afs_permits *permits, *xpermits, *replacement, *zap, *new = NULL; 118 afs_access_t caller_access = scb->status.caller_access; 119 size_t size = 0; 120 bool changed = false; 121 int i, j; 122 123 _enter("{%llx:%llu},%x,%x", 124 vnode->fid.vid, vnode->fid.vnode, key_serial(key), caller_access); 125 126 rcu_read_lock(); 127 128 /* Check for the common case first: We got back the same access as last 129 * time we tried and already have it recorded. 130 */ 131 permits = rcu_dereference(vnode->permit_cache); 132 if (permits) { 133 if (!permits->invalidated) { 134 for (i = 0; i < permits->nr_permits; i++) { 135 if (permits->permits[i].key < key) 136 continue; 137 if (permits->permits[i].key > key) 138 break; 139 if (permits->permits[i].access != caller_access) { 140 changed = true; 141 break; 142 } 143 144 if (afs_cb_is_broken(cb_break, vnode, 145 rcu_dereference(vnode->cb_interest))) { 146 changed = true; 147 break; 148 } 149 150 /* The cache is still good. */ 151 rcu_read_unlock(); 152 return; 153 } 154 } 155 156 changed |= permits->invalidated; 157 size = permits->nr_permits; 158 159 /* If this set of permits is now wrong, clear the permits 160 * pointer so that no one tries to use the stale information. 161 */ 162 if (changed) { 163 spin_lock(&vnode->lock); 164 if (permits != rcu_access_pointer(vnode->permit_cache)) 165 goto someone_else_changed_it_unlock; 166 RCU_INIT_POINTER(vnode->permit_cache, NULL); 167 spin_unlock(&vnode->lock); 168 169 afs_put_permits(permits); 170 permits = NULL; 171 size = 0; 172 } 173 } 174 175 if (afs_cb_is_broken(cb_break, vnode, rcu_dereference(vnode->cb_interest))) 176 goto someone_else_changed_it; 177 178 /* We need a ref on any permits list we want to copy as we'll have to 179 * drop the lock to do memory allocation. 180 */ 181 if (permits && !refcount_inc_not_zero(&permits->usage)) 182 goto someone_else_changed_it; 183 184 rcu_read_unlock(); 185 186 /* Speculatively create a new list with the revised permission set. We 187 * discard this if we find an extant match already in the hash, but 188 * it's easier to compare with memcmp this way. 189 * 190 * We fill in the key pointers at this time, but we don't get the refs 191 * yet. 192 */ 193 size++; 194 new = kzalloc(sizeof(struct afs_permits) + 195 sizeof(struct afs_permit) * size, GFP_NOFS); 196 if (!new) 197 goto out_put; 198 199 refcount_set(&new->usage, 1); 200 new->nr_permits = size; 201 i = j = 0; 202 if (permits) { 203 for (i = 0; i < permits->nr_permits; i++) { 204 if (j == i && permits->permits[i].key > key) { 205 new->permits[j].key = key; 206 new->permits[j].access = caller_access; 207 j++; 208 } 209 new->permits[j].key = permits->permits[i].key; 210 new->permits[j].access = permits->permits[i].access; 211 j++; 212 } 213 } 214 215 if (j == i) { 216 new->permits[j].key = key; 217 new->permits[j].access = caller_access; 218 } 219 220 afs_hash_permits(new); 221 222 /* Now see if the permit list we want is actually already available */ 223 spin_lock(&afs_permits_lock); 224 225 hash_for_each_possible(afs_permits_cache, xpermits, hash_node, new->h) { 226 if (xpermits->h != new->h || 227 xpermits->invalidated || 228 xpermits->nr_permits != new->nr_permits || 229 memcmp(xpermits->permits, new->permits, 230 new->nr_permits * sizeof(struct afs_permit)) != 0) 231 continue; 232 233 if (refcount_inc_not_zero(&xpermits->usage)) { 234 replacement = xpermits; 235 goto found; 236 } 237 238 break; 239 } 240 241 for (i = 0; i < new->nr_permits; i++) 242 key_get(new->permits[i].key); 243 hash_add_rcu(afs_permits_cache, &new->hash_node, new->h); 244 replacement = new; 245 new = NULL; 246 247 found: 248 spin_unlock(&afs_permits_lock); 249 250 kfree(new); 251 252 rcu_read_lock(); 253 spin_lock(&vnode->lock); 254 zap = rcu_access_pointer(vnode->permit_cache); 255 if (!afs_cb_is_broken(cb_break, vnode, rcu_dereference(vnode->cb_interest)) && 256 zap == permits) 257 rcu_assign_pointer(vnode->permit_cache, replacement); 258 else 259 zap = replacement; 260 spin_unlock(&vnode->lock); 261 rcu_read_unlock(); 262 afs_put_permits(zap); 263 out_put: 264 afs_put_permits(permits); 265 return; 266 267 someone_else_changed_it_unlock: 268 spin_unlock(&vnode->lock); 269 someone_else_changed_it: 270 /* Someone else changed the cache under us - don't recheck at this 271 * time. 272 */ 273 rcu_read_unlock(); 274 return; 275 } 276 277 /* 278 * check with the fileserver to see if the directory or parent directory is 279 * permitted to be accessed with this authorisation, and if so, what access it 280 * is granted 281 */ 282 int afs_check_permit(struct afs_vnode *vnode, struct key *key, 283 afs_access_t *_access) 284 { 285 struct afs_permits *permits; 286 bool valid = false; 287 int i, ret; 288 289 _enter("{%llx:%llu},%x", 290 vnode->fid.vid, vnode->fid.vnode, key_serial(key)); 291 292 /* check the permits to see if we've got one yet */ 293 if (key == vnode->volume->cell->anonymous_key) { 294 _debug("anon"); 295 *_access = vnode->status.anon_access; 296 valid = true; 297 } else { 298 rcu_read_lock(); 299 permits = rcu_dereference(vnode->permit_cache); 300 if (permits) { 301 for (i = 0; i < permits->nr_permits; i++) { 302 if (permits->permits[i].key < key) 303 continue; 304 if (permits->permits[i].key > key) 305 break; 306 307 *_access = permits->permits[i].access; 308 valid = !permits->invalidated; 309 break; 310 } 311 } 312 rcu_read_unlock(); 313 } 314 315 if (!valid) { 316 /* Check the status on the file we're actually interested in 317 * (the post-processing will cache the result). 318 */ 319 _debug("no valid permit"); 320 321 ret = afs_fetch_status(vnode, key, false, _access); 322 if (ret < 0) { 323 *_access = 0; 324 _leave(" = %d", ret); 325 return ret; 326 } 327 } 328 329 _leave(" = 0 [access %x]", *_access); 330 return 0; 331 } 332 333 /* 334 * check the permissions on an AFS file 335 * - AFS ACLs are attached to directories only, and a file is controlled by its 336 * parent directory's ACL 337 */ 338 int afs_permission(struct inode *inode, int mask) 339 { 340 struct afs_vnode *vnode = AFS_FS_I(inode); 341 afs_access_t uninitialized_var(access); 342 struct key *key; 343 int ret; 344 345 if (mask & MAY_NOT_BLOCK) 346 return -ECHILD; 347 348 _enter("{{%llx:%llu},%lx},%x,", 349 vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask); 350 351 key = afs_request_key(vnode->volume->cell); 352 if (IS_ERR(key)) { 353 _leave(" = %ld [key]", PTR_ERR(key)); 354 return PTR_ERR(key); 355 } 356 357 ret = afs_validate(vnode, key); 358 if (ret < 0) 359 goto error; 360 361 /* check the permits to see if we've got one yet */ 362 ret = afs_check_permit(vnode, key, &access); 363 if (ret < 0) 364 goto error; 365 366 /* interpret the access mask */ 367 _debug("REQ %x ACC %x on %s", 368 mask, access, S_ISDIR(inode->i_mode) ? "dir" : "file"); 369 370 if (S_ISDIR(inode->i_mode)) { 371 if (mask & (MAY_EXEC | MAY_READ | MAY_CHDIR)) { 372 if (!(access & AFS_ACE_LOOKUP)) 373 goto permission_denied; 374 } 375 if (mask & MAY_WRITE) { 376 if (!(access & (AFS_ACE_DELETE | /* rmdir, unlink, rename from */ 377 AFS_ACE_INSERT))) /* create, mkdir, symlink, rename to */ 378 goto permission_denied; 379 } 380 } else { 381 if (!(access & AFS_ACE_LOOKUP)) 382 goto permission_denied; 383 if ((mask & MAY_EXEC) && !(inode->i_mode & S_IXUSR)) 384 goto permission_denied; 385 if (mask & (MAY_EXEC | MAY_READ)) { 386 if (!(access & AFS_ACE_READ)) 387 goto permission_denied; 388 if (!(inode->i_mode & S_IRUSR)) 389 goto permission_denied; 390 } else if (mask & MAY_WRITE) { 391 if (!(access & AFS_ACE_WRITE)) 392 goto permission_denied; 393 if (!(inode->i_mode & S_IWUSR)) 394 goto permission_denied; 395 } 396 } 397 398 key_put(key); 399 _leave(" = %d", ret); 400 return ret; 401 402 permission_denied: 403 ret = -EACCES; 404 error: 405 key_put(key); 406 _leave(" = %d", ret); 407 return ret; 408 } 409 410 void __exit afs_clean_up_permit_cache(void) 411 { 412 int i; 413 414 for (i = 0; i < HASH_SIZE(afs_permits_cache); i++) 415 WARN_ON_ONCE(!hlist_empty(&afs_permits_cache[i])); 416 417 } 418