• 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 	memcpy(permits->permits, xpermits->permits,
193 	       count * sizeof(struct afs_permit));
194 
195 	_debug("key %x access %x",
196 	       key_serial(key), vnode->status.caller_access);
197 	permits->permits[count].access_mask = vnode->status.caller_access;
198 	permits->permits[count].key = key_get(key);
199 	permits->count = count + 1;
200 
201 	rcu_assign_pointer(auth_vnode->permits, permits);
202 	if (xpermits)
203 		call_rcu(&xpermits->rcu, afs_dispose_of_permits);
204 
205 out_unlock:
206 	mutex_unlock(&auth_vnode->permits_lock);
207 	iput(&auth_vnode->vfs_inode);
208 	_leave("");
209 }
210 
211 /*
212  * check with the fileserver to see if the directory or parent directory is
213  * permitted to be accessed with this authorisation, and if so, what access it
214  * is granted
215  */
afs_check_permit(struct afs_vnode * vnode,struct key * key,afs_access_t * _access)216 static int afs_check_permit(struct afs_vnode *vnode, struct key *key,
217 			    afs_access_t *_access)
218 {
219 	struct afs_permits *permits;
220 	struct afs_permit *permit;
221 	struct afs_vnode *auth_vnode;
222 	bool valid;
223 	int loop, ret;
224 
225 	_enter("{%x:%u},%x",
226 	       vnode->fid.vid, vnode->fid.vnode, key_serial(key));
227 
228 	auth_vnode = afs_get_auth_inode(vnode, key);
229 	if (IS_ERR(auth_vnode)) {
230 		*_access = 0;
231 		_leave(" = %ld", PTR_ERR(auth_vnode));
232 		return PTR_ERR(auth_vnode);
233 	}
234 
235 	ASSERT(S_ISDIR(auth_vnode->vfs_inode.i_mode));
236 
237 	/* check the permits to see if we've got one yet */
238 	if (key == auth_vnode->volume->cell->anonymous_key) {
239 		_debug("anon");
240 		*_access = auth_vnode->status.anon_access;
241 		valid = true;
242 	} else {
243 		valid = false;
244 		rcu_read_lock();
245 		permits = rcu_dereference(auth_vnode->permits);
246 		if (permits) {
247 			permit = permits->permits;
248 			for (loop = permits->count; loop > 0; loop--) {
249 				if (permit->key == key) {
250 					_debug("found in cache");
251 					*_access = permit->access_mask;
252 					valid = true;
253 					break;
254 				}
255 				permit++;
256 			}
257 		}
258 		rcu_read_unlock();
259 	}
260 
261 	if (!valid) {
262 		/* check the status on the file we're actually interested in
263 		 * (the post-processing will cache the result on auth_vnode) */
264 		_debug("no valid permit");
265 
266 		set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
267 		ret = afs_vnode_fetch_status(vnode, auth_vnode, key);
268 		if (ret < 0) {
269 			iput(&auth_vnode->vfs_inode);
270 			*_access = 0;
271 			_leave(" = %d", ret);
272 			return ret;
273 		}
274 		*_access = vnode->status.caller_access;
275 	}
276 
277 	iput(&auth_vnode->vfs_inode);
278 	_leave(" = 0 [access %x]", *_access);
279 	return 0;
280 }
281 
282 /*
283  * check the permissions on an AFS file
284  * - AFS ACLs are attached to directories only, and a file is controlled by its
285  *   parent directory's ACL
286  */
afs_permission(struct inode * inode,int mask)287 int afs_permission(struct inode *inode, int mask)
288 {
289 	struct afs_vnode *vnode = AFS_FS_I(inode);
290 	afs_access_t uninitialized_var(access);
291 	struct key *key;
292 	int ret;
293 
294 	_enter("{{%x:%u},%lx},%x,",
295 	       vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask);
296 
297 	key = afs_request_key(vnode->volume->cell);
298 	if (IS_ERR(key)) {
299 		_leave(" = %ld [key]", PTR_ERR(key));
300 		return PTR_ERR(key);
301 	}
302 
303 	/* if the promise has expired, we need to check the server again */
304 	if (!vnode->cb_promised) {
305 		_debug("not promised");
306 		ret = afs_vnode_fetch_status(vnode, NULL, key);
307 		if (ret < 0)
308 			goto error;
309 		_debug("new promise [fl=%lx]", vnode->flags);
310 	}
311 
312 	/* check the permits to see if we've got one yet */
313 	ret = afs_check_permit(vnode, key, &access);
314 	if (ret < 0)
315 		goto error;
316 
317 	/* interpret the access mask */
318 	_debug("REQ %x ACC %x on %s",
319 	       mask, access, S_ISDIR(inode->i_mode) ? "dir" : "file");
320 
321 	if (S_ISDIR(inode->i_mode)) {
322 		if (mask & MAY_EXEC) {
323 			if (!(access & AFS_ACE_LOOKUP))
324 				goto permission_denied;
325 		} else if (mask & MAY_READ) {
326 			if (!(access & AFS_ACE_READ))
327 				goto permission_denied;
328 		} else if (mask & MAY_WRITE) {
329 			if (!(access & (AFS_ACE_DELETE | /* rmdir, unlink, rename from */
330 					AFS_ACE_INSERT | /* create, mkdir, symlink, rename to */
331 					AFS_ACE_WRITE))) /* chmod */
332 				goto permission_denied;
333 		} else {
334 			BUG();
335 		}
336 	} else {
337 		if (!(access & AFS_ACE_LOOKUP))
338 			goto permission_denied;
339 		if (mask & (MAY_EXEC | MAY_READ)) {
340 			if (!(access & AFS_ACE_READ))
341 				goto permission_denied;
342 		} else if (mask & MAY_WRITE) {
343 			if (!(access & AFS_ACE_WRITE))
344 				goto permission_denied;
345 		}
346 	}
347 
348 	key_put(key);
349 	ret = generic_permission(inode, mask, NULL);
350 	_leave(" = %d", ret);
351 	return ret;
352 
353 permission_denied:
354 	ret = -EACCES;
355 error:
356 	key_put(key);
357 	_leave(" = %d", ret);
358 	return ret;
359 }
360