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