• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* AFS security handling
2  *
3  * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
4  * 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  */
11 
12 #include <linux/init.h>
13 #include <linux/slab.h>
14 #include <linux/fs.h>
15 #include <linux/ctype.h>
16 #include <linux/sched.h>
17 #include <keys/rxrpc-type.h>
18 #include "internal.h"
19 
20 /*
21  * get a key
22  */
afs_request_key(struct afs_cell * cell)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 permits list
50  */
afs_zap_permits(struct rcu_head * rcu)51 void afs_zap_permits(struct rcu_head *rcu)
52 {
53 	struct afs_permits *permits =
54 		container_of(rcu, struct afs_permits, rcu);
55 	int loop;
56 
57 	_enter("{%d}", permits->count);
58 
59 	for (loop = permits->count - 1; loop >= 0; loop--)
60 		key_put(permits->permits[loop].key);
61 	kfree(permits);
62 }
63 
64 /*
65  * dispose of a permits list in which all the key pointers have been copied
66  */
afs_dispose_of_permits(struct rcu_head * rcu)67 static void afs_dispose_of_permits(struct rcu_head *rcu)
68 {
69 	struct afs_permits *permits =
70 		container_of(rcu, struct afs_permits, rcu);
71 
72 	_enter("{%d}", permits->count);
73 
74 	kfree(permits);
75 }
76 
77 /*
78  * get the authorising vnode - this is the specified inode itself if it's a
79  * directory or it's the parent directory if the specified inode is a file or
80  * symlink
81  * - the caller must release the ref on the inode
82  */
afs_get_auth_inode(struct afs_vnode * vnode,struct key * key)83 static struct afs_vnode *afs_get_auth_inode(struct afs_vnode *vnode,
84 					    struct key *key)
85 {
86 	struct afs_vnode *auth_vnode;
87 	struct inode *auth_inode;
88 
89 	_enter("");
90 
91 	if (S_ISDIR(vnode->vfs_inode.i_mode)) {
92 		auth_inode = igrab(&vnode->vfs_inode);
93 		ASSERT(auth_inode != NULL);
94 	} else {
95 		auth_inode = afs_iget(vnode->vfs_inode.i_sb, key,
96 				      &vnode->status.parent, NULL, NULL);
97 		if (IS_ERR(auth_inode))
98 			return ERR_CAST(auth_inode);
99 	}
100 
101 	auth_vnode = AFS_FS_I(auth_inode);
102 	_leave(" = {%x}", auth_vnode->fid.vnode);
103 	return auth_vnode;
104 }
105 
106 /*
107  * clear the permit cache on a directory vnode
108  */
afs_clear_permits(struct afs_vnode * vnode)109 void afs_clear_permits(struct afs_vnode *vnode)
110 {
111 	struct afs_permits *permits;
112 
113 	_enter("{%x:%u}", vnode->fid.vid, vnode->fid.vnode);
114 
115 	mutex_lock(&vnode->permits_lock);
116 	permits = vnode->permits;
117 	rcu_assign_pointer(vnode->permits, NULL);
118 	mutex_unlock(&vnode->permits_lock);
119 
120 	if (permits)
121 		call_rcu(&permits->rcu, afs_zap_permits);
122 	_leave("");
123 }
124 
125 /*
126  * add the result obtained for a vnode to its or its parent directory's cache
127  * for the key used to access it
128  */
afs_cache_permit(struct afs_vnode * vnode,struct key * key,long acl_order)129 void afs_cache_permit(struct afs_vnode *vnode, struct key *key, long acl_order)
130 {
131 	struct afs_permits *permits, *xpermits;
132 	struct afs_permit *permit;
133 	struct afs_vnode *auth_vnode;
134 	int count, loop;
135 
136 	_enter("{%x:%u},%x,%lx",
137 	       vnode->fid.vid, vnode->fid.vnode, key_serial(key), acl_order);
138 
139 	auth_vnode = afs_get_auth_inode(vnode, key);
140 	if (IS_ERR(auth_vnode)) {
141 		_leave(" [get error %ld]", PTR_ERR(auth_vnode));
142 		return;
143 	}
144 
145 	mutex_lock(&auth_vnode->permits_lock);
146 
147 	/* guard against a rename being detected whilst we waited for the
148 	 * lock */
149 	if (memcmp(&auth_vnode->fid, &vnode->status.parent,
150 		   sizeof(struct afs_fid)) != 0) {
151 		_debug("renamed");
152 		goto out_unlock;
153 	}
154 
155 	/* have to be careful as the directory's callback may be broken between
156 	 * us receiving the status we're trying to cache and us getting the
157 	 * lock to update the cache for the status */
158 	if (auth_vnode->acl_order - acl_order > 0) {
159 		_debug("ACL changed?");
160 		goto out_unlock;
161 	}
162 
163 	/* always update the anonymous mask */
164 	_debug("anon access %x", vnode->status.anon_access);
165 	auth_vnode->status.anon_access = vnode->status.anon_access;
166 	if (key == vnode->volume->cell->anonymous_key)
167 		goto out_unlock;
168 
169 	xpermits = auth_vnode->permits;
170 	count = 0;
171 	if (xpermits) {
172 		/* see if the permit is already in the list
173 		 * - if it is then we just amend the list
174 		 */
175 		count = xpermits->count;
176 		permit = xpermits->permits;
177 		for (loop = count; loop > 0; loop--) {
178 			if (permit->key == key) {
179 				permit->access_mask =
180 					vnode->status.caller_access;
181 				goto out_unlock;
182 			}
183 			permit++;
184 		}
185 	}
186 
187 	permits = kmalloc(sizeof(*permits) + sizeof(*permit) * (count + 1),
188 			  GFP_NOFS);
189 	if (!permits)
190 		goto out_unlock;
191 
192 	if (xpermits)
193 		memcpy(permits->permits, xpermits->permits,
194 			count * sizeof(struct afs_permit));
195 
196 	_debug("key %x access %x",
197 	       key_serial(key), vnode->status.caller_access);
198 	permits->permits[count].access_mask = vnode->status.caller_access;
199 	permits->permits[count].key = key_get(key);
200 	permits->count = count + 1;
201 
202 	rcu_assign_pointer(auth_vnode->permits, permits);
203 	if (xpermits)
204 		call_rcu(&xpermits->rcu, afs_dispose_of_permits);
205 
206 out_unlock:
207 	mutex_unlock(&auth_vnode->permits_lock);
208 	iput(&auth_vnode->vfs_inode);
209 	_leave("");
210 }
211 
212 /*
213  * check with the fileserver to see if the directory or parent directory is
214  * permitted to be accessed with this authorisation, and if so, what access it
215  * is granted
216  */
afs_check_permit(struct afs_vnode * vnode,struct key * key,afs_access_t * _access)217 static int afs_check_permit(struct afs_vnode *vnode, struct key *key,
218 			    afs_access_t *_access)
219 {
220 	struct afs_permits *permits;
221 	struct afs_permit *permit;
222 	struct afs_vnode *auth_vnode;
223 	bool valid;
224 	int loop, ret;
225 
226 	_enter("{%x:%u},%x",
227 	       vnode->fid.vid, vnode->fid.vnode, key_serial(key));
228 
229 	auth_vnode = afs_get_auth_inode(vnode, key);
230 	if (IS_ERR(auth_vnode)) {
231 		*_access = 0;
232 		_leave(" = %ld", PTR_ERR(auth_vnode));
233 		return PTR_ERR(auth_vnode);
234 	}
235 
236 	ASSERT(S_ISDIR(auth_vnode->vfs_inode.i_mode));
237 
238 	/* check the permits to see if we've got one yet */
239 	if (key == auth_vnode->volume->cell->anonymous_key) {
240 		_debug("anon");
241 		*_access = auth_vnode->status.anon_access;
242 		valid = true;
243 	} else {
244 		valid = false;
245 		rcu_read_lock();
246 		permits = rcu_dereference(auth_vnode->permits);
247 		if (permits) {
248 			permit = permits->permits;
249 			for (loop = permits->count; loop > 0; loop--) {
250 				if (permit->key == key) {
251 					_debug("found in cache");
252 					*_access = permit->access_mask;
253 					valid = true;
254 					break;
255 				}
256 				permit++;
257 			}
258 		}
259 		rcu_read_unlock();
260 	}
261 
262 	if (!valid) {
263 		/* check the status on the file we're actually interested in
264 		 * (the post-processing will cache the result on auth_vnode) */
265 		_debug("no valid permit");
266 
267 		set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
268 		ret = afs_vnode_fetch_status(vnode, auth_vnode, key);
269 		if (ret < 0) {
270 			iput(&auth_vnode->vfs_inode);
271 			*_access = 0;
272 			_leave(" = %d", ret);
273 			return ret;
274 		}
275 		*_access = vnode->status.caller_access;
276 	}
277 
278 	iput(&auth_vnode->vfs_inode);
279 	_leave(" = 0 [access %x]", *_access);
280 	return 0;
281 }
282 
283 /*
284  * check the permissions on an AFS file
285  * - AFS ACLs are attached to directories only, and a file is controlled by its
286  *   parent directory's ACL
287  */
afs_permission(struct inode * inode,int mask)288 int afs_permission(struct inode *inode, int mask)
289 {
290 	struct afs_vnode *vnode = AFS_FS_I(inode);
291 	afs_access_t uninitialized_var(access);
292 	struct key *key;
293 	int ret;
294 
295 	if (mask & MAY_NOT_BLOCK)
296 		return -ECHILD;
297 
298 	_enter("{{%x:%u},%lx},%x,",
299 	       vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask);
300 
301 	key = afs_request_key(vnode->volume->cell);
302 	if (IS_ERR(key)) {
303 		_leave(" = %ld [key]", PTR_ERR(key));
304 		return PTR_ERR(key);
305 	}
306 
307 	/* if the promise has expired, we need to check the server again */
308 	if (!vnode->cb_promised) {
309 		_debug("not promised");
310 		ret = afs_vnode_fetch_status(vnode, NULL, key);
311 		if (ret < 0)
312 			goto error;
313 		_debug("new promise [fl=%lx]", vnode->flags);
314 	}
315 
316 	/* check the permits to see if we've got one yet */
317 	ret = afs_check_permit(vnode, key, &access);
318 	if (ret < 0)
319 		goto error;
320 
321 	/* interpret the access mask */
322 	_debug("REQ %x ACC %x on %s",
323 	       mask, access, S_ISDIR(inode->i_mode) ? "dir" : "file");
324 
325 	if (S_ISDIR(inode->i_mode)) {
326 		if (mask & MAY_EXEC) {
327 			if (!(access & AFS_ACE_LOOKUP))
328 				goto permission_denied;
329 		} else if (mask & MAY_READ) {
330 			if (!(access & AFS_ACE_READ))
331 				goto permission_denied;
332 		} else if (mask & MAY_WRITE) {
333 			if (!(access & (AFS_ACE_DELETE | /* rmdir, unlink, rename from */
334 					AFS_ACE_INSERT | /* create, mkdir, symlink, rename to */
335 					AFS_ACE_WRITE))) /* chmod */
336 				goto permission_denied;
337 		} else {
338 			BUG();
339 		}
340 	} else {
341 		if (!(access & AFS_ACE_LOOKUP))
342 			goto permission_denied;
343 		if (mask & (MAY_EXEC | MAY_READ)) {
344 			if (!(access & AFS_ACE_READ))
345 				goto permission_denied;
346 		} else if (mask & MAY_WRITE) {
347 			if (!(access & AFS_ACE_WRITE))
348 				goto permission_denied;
349 		}
350 	}
351 
352 	key_put(key);
353 	ret = generic_permission(inode, mask);
354 	_leave(" = %d", ret);
355 	return ret;
356 
357 permission_denied:
358 	ret = -EACCES;
359 error:
360 	key_put(key);
361 	_leave(" = %d", ret);
362 	return ret;
363 }
364