Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
security.c
Go to the documentation of this file.
1 /* AFS security handling
2  *
3  * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
4  * Written by David Howells ([email protected])
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  */
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  */
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  */
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  */
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  */
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;
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  */
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  */
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 
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  */
288 int afs_permission(struct inode *inode, int mask)
289 {
290  struct afs_vnode *vnode = AFS_FS_I(inode);
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 }