aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2012-02-07 21:52:57 -0500
committerEric W. Biederman <ebiederm@xmission.com>2012-09-18 04:01:35 -0400
commit2f6f0654ab61961fd0f7701fe3be89ea111f0cda (patch)
treea96703c892bd374b5ef7b102c52a5ec596970027 /fs
parentd20b92ab668cc44fc84bba0001839c5a8013a5cd (diff)
userns: Convert vfs posix_acl support to use kuids and kgids
- In setxattr if we are setting a posix acl convert uids and gids from the current user namespace into the initial user namespace, before the xattrs are passed to the underlying filesystem. Untranslatable uids and gids are represented as -1 which posix_acl_from_xattr will represent as INVALID_UID or INVALID_GID. posix_acl_valid will fail if an acl from userspace has any INVALID_UID or INVALID_GID values. In net this guarantees that untranslatable posix acls will not be stored by filesystems. - In getxattr if we are reading a posix acl convert uids and gids from the initial user namespace into the current user namespace. Uids and gids that can not be tranlsated into the current user namespace will be represented as -1. - Replace e_id in struct posix_acl_entry with an anymouns union of e_uid and e_gid. For the short term retain the e_id field until all of the users are converted. - Don't set struct posix_acl.e_id in the cases where the acl type does not use e_id. Greatly reducing the use of ACL_UNDEFINED_ID. - Rework the ordering checks in posix_acl_valid so that I use kuid_t and kgid_t types throughout the code, and so that I don't need arithmetic on uid and gid types. Cc: Theodore Tso <tytso@mit.edu> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Andreas Dilger <adilger.kernel@dilger.ca> Cc: Jan Kara <jack@suse.cz> Cc: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/posix_acl.c30
-rw-r--r--fs/xattr.c7
-rw-r--r--fs/xattr_acl.c90
3 files changed, 107 insertions, 20 deletions
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 5e325a42e33d..8bd2135b7f82 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -78,7 +78,8 @@ posix_acl_valid(const struct posix_acl *acl)
78{ 78{
79 const struct posix_acl_entry *pa, *pe; 79 const struct posix_acl_entry *pa, *pe;
80 int state = ACL_USER_OBJ; 80 int state = ACL_USER_OBJ;
81 unsigned int id = 0; /* keep gcc happy */ 81 kuid_t prev_uid = INVALID_UID;
82 kgid_t prev_gid = INVALID_GID;
82 int needs_mask = 0; 83 int needs_mask = 0;
83 84
84 FOREACH_ACL_ENTRY(pa, acl, pe) { 85 FOREACH_ACL_ENTRY(pa, acl, pe) {
@@ -87,7 +88,6 @@ posix_acl_valid(const struct posix_acl *acl)
87 switch (pa->e_tag) { 88 switch (pa->e_tag) {
88 case ACL_USER_OBJ: 89 case ACL_USER_OBJ:
89 if (state == ACL_USER_OBJ) { 90 if (state == ACL_USER_OBJ) {
90 id = 0;
91 state = ACL_USER; 91 state = ACL_USER;
92 break; 92 break;
93 } 93 }
@@ -96,16 +96,17 @@ posix_acl_valid(const struct posix_acl *acl)
96 case ACL_USER: 96 case ACL_USER:
97 if (state != ACL_USER) 97 if (state != ACL_USER)
98 return -EINVAL; 98 return -EINVAL;
99 if (pa->e_id == ACL_UNDEFINED_ID || 99 if (!uid_valid(pa->e_uid))
100 pa->e_id < id)
101 return -EINVAL; 100 return -EINVAL;
102 id = pa->e_id + 1; 101 if (uid_valid(prev_uid) &&
102 uid_lte(pa->e_uid, prev_uid))
103 return -EINVAL;
104 prev_uid = pa->e_uid;
103 needs_mask = 1; 105 needs_mask = 1;
104 break; 106 break;
105 107
106 case ACL_GROUP_OBJ: 108 case ACL_GROUP_OBJ:
107 if (state == ACL_USER) { 109 if (state == ACL_USER) {
108 id = 0;
109 state = ACL_GROUP; 110 state = ACL_GROUP;
110 break; 111 break;
111 } 112 }
@@ -114,10 +115,12 @@ posix_acl_valid(const struct posix_acl *acl)
114 case ACL_GROUP: 115 case ACL_GROUP:
115 if (state != ACL_GROUP) 116 if (state != ACL_GROUP)
116 return -EINVAL; 117 return -EINVAL;
117 if (pa->e_id == ACL_UNDEFINED_ID || 118 if (!gid_valid(pa->e_gid))
118 pa->e_id < id) 119 return -EINVAL;
120 if (gid_valid(prev_gid) &&
121 gid_lte(pa->e_gid, prev_gid))
119 return -EINVAL; 122 return -EINVAL;
120 id = pa->e_id + 1; 123 prev_gid = pa->e_gid;
121 needs_mask = 1; 124 needs_mask = 1;
122 break; 125 break;
123 126
@@ -195,15 +198,12 @@ posix_acl_from_mode(umode_t mode, gfp_t flags)
195 return ERR_PTR(-ENOMEM); 198 return ERR_PTR(-ENOMEM);
196 199
197 acl->a_entries[0].e_tag = ACL_USER_OBJ; 200 acl->a_entries[0].e_tag = ACL_USER_OBJ;
198 acl->a_entries[0].e_id = ACL_UNDEFINED_ID;
199 acl->a_entries[0].e_perm = (mode & S_IRWXU) >> 6; 201 acl->a_entries[0].e_perm = (mode & S_IRWXU) >> 6;
200 202
201 acl->a_entries[1].e_tag = ACL_GROUP_OBJ; 203 acl->a_entries[1].e_tag = ACL_GROUP_OBJ;
202 acl->a_entries[1].e_id = ACL_UNDEFINED_ID;
203 acl->a_entries[1].e_perm = (mode & S_IRWXG) >> 3; 204 acl->a_entries[1].e_perm = (mode & S_IRWXG) >> 3;
204 205
205 acl->a_entries[2].e_tag = ACL_OTHER; 206 acl->a_entries[2].e_tag = ACL_OTHER;
206 acl->a_entries[2].e_id = ACL_UNDEFINED_ID;
207 acl->a_entries[2].e_perm = (mode & S_IRWXO); 207 acl->a_entries[2].e_perm = (mode & S_IRWXO);
208 return acl; 208 return acl;
209} 209}
@@ -224,11 +224,11 @@ posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want)
224 switch(pa->e_tag) { 224 switch(pa->e_tag) {
225 case ACL_USER_OBJ: 225 case ACL_USER_OBJ:
226 /* (May have been checked already) */ 226 /* (May have been checked already) */
227 if (inode->i_uid == current_fsuid()) 227 if (uid_eq(inode->i_uid, current_fsuid()))
228 goto check_perm; 228 goto check_perm;
229 break; 229 break;
230 case ACL_USER: 230 case ACL_USER:
231 if (pa->e_id == current_fsuid()) 231 if (uid_eq(pa->e_uid, current_fsuid()))
232 goto mask; 232 goto mask;
233 break; 233 break;
234 case ACL_GROUP_OBJ: 234 case ACL_GROUP_OBJ:
@@ -239,7 +239,7 @@ posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want)
239 } 239 }
240 break; 240 break;
241 case ACL_GROUP: 241 case ACL_GROUP:
242 if (in_group_p(pa->e_id)) { 242 if (in_group_p(pa->e_gid)) {
243 found = 1; 243 found = 1;
244 if ((pa->e_perm & want) == want) 244 if ((pa->e_perm & want) == want)
245 goto mask; 245 goto mask;
diff --git a/fs/xattr.c b/fs/xattr.c
index 4d45b7189e7e..c111745c2da9 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -20,6 +20,7 @@
20#include <linux/fsnotify.h> 20#include <linux/fsnotify.h>
21#include <linux/audit.h> 21#include <linux/audit.h>
22#include <linux/vmalloc.h> 22#include <linux/vmalloc.h>
23#include <linux/posix_acl_xattr.h>
23 24
24#include <asm/uaccess.h> 25#include <asm/uaccess.h>
25 26
@@ -347,6 +348,9 @@ setxattr(struct dentry *d, const char __user *name, const void __user *value,
347 error = -EFAULT; 348 error = -EFAULT;
348 goto out; 349 goto out;
349 } 350 }
351 if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
352 (strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0))
353 posix_acl_fix_xattr_from_user(kvalue, size);
350 } 354 }
351 355
352 error = vfs_setxattr(d, kname, kvalue, size, flags); 356 error = vfs_setxattr(d, kname, kvalue, size, flags);
@@ -450,6 +454,9 @@ getxattr(struct dentry *d, const char __user *name, void __user *value,
450 454
451 error = vfs_getxattr(d, kname, kvalue, size); 455 error = vfs_getxattr(d, kname, kvalue, size);
452 if (error > 0) { 456 if (error > 0) {
457 if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
458 (strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0))
459 posix_acl_fix_xattr_to_user(kvalue, size);
453 if (size && copy_to_user(value, kvalue, error)) 460 if (size && copy_to_user(value, kvalue, error))
454 error = -EFAULT; 461 error = -EFAULT;
455 } else if (error == -ERANGE && size >= XATTR_SIZE_MAX) { 462 } else if (error == -ERANGE && size >= XATTR_SIZE_MAX) {
diff --git a/fs/xattr_acl.c b/fs/xattr_acl.c
index 69d06b07b169..bf472ca1b348 100644
--- a/fs/xattr_acl.c
+++ b/fs/xattr_acl.c
@@ -9,7 +9,65 @@
9#include <linux/fs.h> 9#include <linux/fs.h>
10#include <linux/posix_acl_xattr.h> 10#include <linux/posix_acl_xattr.h>
11#include <linux/gfp.h> 11#include <linux/gfp.h>
12#include <linux/user_namespace.h>
12 13
14/*
15 * Fix up the uids and gids in posix acl extended attributes in place.
16 */
17static void posix_acl_fix_xattr_userns(
18 struct user_namespace *to, struct user_namespace *from,
19 void *value, size_t size)
20{
21 posix_acl_xattr_header *header = (posix_acl_xattr_header *)value;
22 posix_acl_xattr_entry *entry = (posix_acl_xattr_entry *)(header+1), *end;
23 int count;
24 kuid_t uid;
25 kgid_t gid;
26
27 if (!value)
28 return;
29 if (size < sizeof(posix_acl_xattr_header))
30 return;
31 if (header->a_version != cpu_to_le32(POSIX_ACL_XATTR_VERSION))
32 return;
33
34 count = posix_acl_xattr_count(size);
35 if (count < 0)
36 return;
37 if (count == 0)
38 return;
39
40 for (end = entry + count; entry != end; entry++) {
41 switch(le16_to_cpu(entry->e_tag)) {
42 case ACL_USER:
43 uid = make_kuid(from, le32_to_cpu(entry->e_id));
44 entry->e_id = cpu_to_le32(from_kuid(to, uid));
45 break;
46 case ACL_GROUP:
47 gid = make_kgid(from, le32_to_cpu(entry->e_id));
48 entry->e_id = cpu_to_le32(from_kuid(to, uid));
49 break;
50 default:
51 break;
52 }
53 }
54}
55
56void posix_acl_fix_xattr_from_user(void *value, size_t size)
57{
58 struct user_namespace *user_ns = current_user_ns();
59 if (user_ns == &init_user_ns)
60 return;
61 posix_acl_fix_xattr_userns(&init_user_ns, user_ns, value, size);
62}
63
64void posix_acl_fix_xattr_to_user(void *value, size_t size)
65{
66 struct user_namespace *user_ns = current_user_ns();
67 if (user_ns == &init_user_ns)
68 return;
69 posix_acl_fix_xattr_userns(user_ns, &init_user_ns, value, size);
70}
13 71
14/* 72/*
15 * Convert from extended attribute to in-memory representation. 73 * Convert from extended attribute to in-memory representation.
@@ -50,12 +108,21 @@ posix_acl_from_xattr(const void *value, size_t size)
50 case ACL_GROUP_OBJ: 108 case ACL_GROUP_OBJ:
51 case ACL_MASK: 109 case ACL_MASK:
52 case ACL_OTHER: 110 case ACL_OTHER:
53 acl_e->e_id = ACL_UNDEFINED_ID;
54 break; 111 break;
55 112
56 case ACL_USER: 113 case ACL_USER:
114 acl_e->e_uid =
115 make_kuid(&init_user_ns,
116 le32_to_cpu(entry->e_id));
117 if (!uid_valid(acl_e->e_uid))
118 goto fail;
119 break;
57 case ACL_GROUP: 120 case ACL_GROUP:
58 acl_e->e_id = le32_to_cpu(entry->e_id); 121 acl_e->e_gid =
122 make_kgid(&init_user_ns,
123 le32_to_cpu(entry->e_id));
124 if (!gid_valid(acl_e->e_gid))
125 goto fail;
59 break; 126 break;
60 127
61 default: 128 default:
@@ -89,9 +156,22 @@ posix_acl_to_xattr(const struct posix_acl *acl, void *buffer, size_t size)
89 ext_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION); 156 ext_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
90 157
91 for (n=0; n < acl->a_count; n++, ext_entry++) { 158 for (n=0; n < acl->a_count; n++, ext_entry++) {
92 ext_entry->e_tag = cpu_to_le16(acl->a_entries[n].e_tag); 159 const struct posix_acl_entry *acl_e = &acl->a_entries[n];
93 ext_entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm); 160 ext_entry->e_tag = cpu_to_le16(acl_e->e_tag);
94 ext_entry->e_id = cpu_to_le32(acl->a_entries[n].e_id); 161 ext_entry->e_perm = cpu_to_le16(acl_e->e_perm);
162 switch(acl_e->e_tag) {
163 case ACL_USER:
164 ext_entry->e_id =
165 cpu_to_le32(from_kuid(&init_user_ns, acl_e->e_uid));
166 break;
167 case ACL_GROUP:
168 ext_entry->e_id =
169 cpu_to_le32(from_kgid(&init_user_ns, acl_e->e_gid));
170 break;
171 default:
172 ext_entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID);
173 break;
174 }
95 } 175 }
96 return real_size; 176 return real_size;
97} 177}