Commit 2f6f0654ab61961fd0f7701fe3be89ea111f0cda

Authored by Eric W. Biederman
1 parent d20b92ab66

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>

Showing 5 changed files with 126 additions and 21 deletions Side-by-side Diff

... ... @@ -78,7 +78,8 @@
78 78 {
79 79 const struct posix_acl_entry *pa, *pe;
80 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 83 int needs_mask = 0;
83 84  
84 85 FOREACH_ACL_ENTRY(pa, acl, pe) {
... ... @@ -87,7 +88,6 @@
87 88 switch (pa->e_tag) {
88 89 case ACL_USER_OBJ:
89 90 if (state == ACL_USER_OBJ) {
90   - id = 0;
91 91 state = ACL_USER;
92 92 break;
93 93 }
94 94  
95 95  
... ... @@ -96,16 +96,17 @@
96 96 case ACL_USER:
97 97 if (state != ACL_USER)
98 98 return -EINVAL;
99   - if (pa->e_id == ACL_UNDEFINED_ID ||
100   - pa->e_id < id)
  99 + if (!uid_valid(pa->e_uid))
101 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 105 needs_mask = 1;
104 106 break;
105 107  
106 108 case ACL_GROUP_OBJ:
107 109 if (state == ACL_USER) {
108   - id = 0;
109 110 state = ACL_GROUP;
110 111 break;
111 112 }
112 113  
... ... @@ -114,10 +115,12 @@
114 115 case ACL_GROUP:
115 116 if (state != ACL_GROUP)
116 117 return -EINVAL;
117   - if (pa->e_id == ACL_UNDEFINED_ID ||
118   - pa->e_id < id)
  118 + if (!gid_valid(pa->e_gid))
119 119 return -EINVAL;
120   - id = pa->e_id + 1;
  120 + if (gid_valid(prev_gid) &&
  121 + gid_lte(pa->e_gid, prev_gid))
  122 + return -EINVAL;
  123 + prev_gid = pa->e_gid;
121 124 needs_mask = 1;
122 125 break;
123 126  
124 127  
125 128  
... ... @@ -195,15 +198,12 @@
195 198 return ERR_PTR(-ENOMEM);
196 199  
197 200 acl->a_entries[0].e_tag = ACL_USER_OBJ;
198   - acl->a_entries[0].e_id = ACL_UNDEFINED_ID;
199 201 acl->a_entries[0].e_perm = (mode & S_IRWXU) >> 6;
200 202  
201 203 acl->a_entries[1].e_tag = ACL_GROUP_OBJ;
202   - acl->a_entries[1].e_id = ACL_UNDEFINED_ID;
203 204 acl->a_entries[1].e_perm = (mode & S_IRWXG) >> 3;
204 205  
205 206 acl->a_entries[2].e_tag = ACL_OTHER;
206   - acl->a_entries[2].e_id = ACL_UNDEFINED_ID;
207 207 acl->a_entries[2].e_perm = (mode & S_IRWXO);
208 208 return acl;
209 209 }
210 210  
... ... @@ -224,11 +224,11 @@
224 224 switch(pa->e_tag) {
225 225 case ACL_USER_OBJ:
226 226 /* (May have been checked already) */
227   - if (inode->i_uid == current_fsuid())
  227 + if (uid_eq(inode->i_uid, current_fsuid()))
228 228 goto check_perm;
229 229 break;
230 230 case ACL_USER:
231   - if (pa->e_id == current_fsuid())
  231 + if (uid_eq(pa->e_uid, current_fsuid()))
232 232 goto mask;
233 233 break;
234 234 case ACL_GROUP_OBJ:
... ... @@ -239,7 +239,7 @@
239 239 }
240 240 break;
241 241 case ACL_GROUP:
242   - if (in_group_p(pa->e_id)) {
  242 + if (in_group_p(pa->e_gid)) {
243 243 found = 1;
244 244 if ((pa->e_perm & want) == want)
245 245 goto mask;
... ... @@ -20,6 +20,7 @@
20 20 #include <linux/fsnotify.h>
21 21 #include <linux/audit.h>
22 22 #include <linux/vmalloc.h>
  23 +#include <linux/posix_acl_xattr.h>
23 24  
24 25 #include <asm/uaccess.h>
25 26  
... ... @@ -347,6 +348,9 @@
347 348 error = -EFAULT;
348 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 356 error = vfs_setxattr(d, kname, kvalue, size, flags);
... ... @@ -450,6 +454,9 @@
450 454  
451 455 error = vfs_getxattr(d, kname, kvalue, size);
452 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 460 if (size && copy_to_user(value, kvalue, error))
454 461 error = -EFAULT;
455 462 } else if (error == -ERANGE && size >= XATTR_SIZE_MAX) {
... ... @@ -9,8 +9,66 @@
9 9 #include <linux/fs.h>
10 10 #include <linux/posix_acl_xattr.h>
11 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 + */
  17 +static 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;
13 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 +
  56 +void 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 +
  64 +void 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 +}
  71 +
14 72 /*
15 73 * Convert from extended attribute to in-memory representation.
16 74 */
17 75  
18 76  
... ... @@ -50,12 +108,21 @@
50 108 case ACL_GROUP_OBJ:
51 109 case ACL_MASK:
52 110 case ACL_OTHER:
53   - acl_e->e_id = ACL_UNDEFINED_ID;
54 111 break;
55 112  
56 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 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 126 break;
60 127  
61 128 default:
... ... @@ -89,9 +156,22 @@
89 156 ext_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
90 157  
91 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);
93   - ext_entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm);
94   - ext_entry->e_id = cpu_to_le32(acl->a_entries[n].e_id);
  159 + const struct posix_acl_entry *acl_e = &acl->a_entries[n];
  160 + ext_entry->e_tag = cpu_to_le16(acl_e->e_tag);
  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 176 return real_size;
97 177 }
include/linux/posix_acl.h
... ... @@ -36,7 +36,13 @@
36 36 struct posix_acl_entry {
37 37 short e_tag;
38 38 unsigned short e_perm;
39   - unsigned int e_id;
  39 + union {
  40 + kuid_t e_uid;
  41 + kgid_t e_gid;
  42 +#ifndef CONFIG_UIDGID_STRICT_TYPE_CHECKS
  43 + unsigned int e_id;
  44 +#endif
  45 + };
40 46 };
41 47  
42 48 struct posix_acl {
include/linux/posix_acl_xattr.h
... ... @@ -52,6 +52,18 @@
52 52 return size / sizeof(posix_acl_xattr_entry);
53 53 }
54 54  
  55 +#ifdef CONFIG_FS_POSIX_ACL
  56 +void posix_acl_fix_xattr_from_user(void *value, size_t size);
  57 +void posix_acl_fix_xattr_to_user(void *value, size_t size);
  58 +#else
  59 +static inline void posix_acl_fix_xattr_from_user(void *value, size_t size)
  60 +{
  61 +}
  62 +static inline void posix_acl_fix_xattr_to_user(void *value, size_t size)
  63 +{
  64 +}
  65 +#endif
  66 +
55 67 struct posix_acl *posix_acl_from_xattr(const void *value, size_t size);
56 68 int posix_acl_to_xattr(const struct posix_acl *acl, void *buffer, size_t size);
57 69