Pull mem-attribute into release branch
[linux-2.6] / 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, NULL, NULL);
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},%lx},%x,",
292                vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask);
293
294         key = afs_request_key(vnode->volume->cell);
295         if (IS_ERR(key)) {
296                 _leave(" = %ld [key]", PTR_ERR(key));
297                 return PTR_ERR(key);
298         }
299
300         /* if the promise has expired, we need to check the server again */
301         if (!vnode->cb_promised) {
302                 _debug("not promised");
303                 ret = afs_vnode_fetch_status(vnode, NULL, key);
304                 if (ret < 0)
305                         goto error;
306                 _debug("new promise [fl=%lx]", vnode->flags);
307         }
308
309         /* check the permits to see if we've got one yet */
310         ret = afs_check_permit(vnode, key, &access);
311         if (ret < 0)
312                 goto error;
313
314         /* interpret the access mask */
315         _debug("REQ %x ACC %x on %s",
316                mask, access, S_ISDIR(inode->i_mode) ? "dir" : "file");
317
318         if (S_ISDIR(inode->i_mode)) {
319                 if (mask & MAY_EXEC) {
320                         if (!(access & AFS_ACE_LOOKUP))
321                                 goto permission_denied;
322                 } else if (mask & MAY_READ) {
323                         if (!(access & AFS_ACE_READ))
324                                 goto permission_denied;
325                 } else if (mask & MAY_WRITE) {
326                         if (!(access & (AFS_ACE_DELETE | /* rmdir, unlink, rename from */
327                                         AFS_ACE_INSERT | /* create, mkdir, symlink, rename to */
328                                         AFS_ACE_WRITE))) /* chmod */
329                                 goto permission_denied;
330                 } else {
331                         BUG();
332                 }
333         } else {
334                 if (!(access & AFS_ACE_LOOKUP))
335                         goto permission_denied;
336                 if (mask & (MAY_EXEC | MAY_READ)) {
337                         if (!(access & AFS_ACE_READ))
338                                 goto permission_denied;
339                 } else if (mask & MAY_WRITE) {
340                         if (!(access & AFS_ACE_WRITE))
341                                 goto permission_denied;
342                 }
343         }
344
345         key_put(key);
346         ret = generic_permission(inode, mask, NULL);
347         _leave(" = %d", ret);
348         return ret;
349
350 permission_denied:
351         ret = -EACCES;
352 error:
353         key_put(key);
354         _leave(" = %d", ret);
355         return ret;
356 }