{
const struct posix_acl_entry *pa, *pe;
int state = ACL_USER_OBJ;
- unsigned int id = 0; /* keep gcc happy */
+ kuid_t prev_uid = INVALID_UID;
+ kgid_t prev_gid = INVALID_GID;
int needs_mask = 0;
FOREACH_ACL_ENTRY(pa, acl, pe) {
switch (pa->e_tag) {
case ACL_USER_OBJ:
if (state == ACL_USER_OBJ) {
- id = 0;
state = ACL_USER;
break;
}
case ACL_USER:
if (state != ACL_USER)
return -EINVAL;
- if (pa->e_id == ACL_UNDEFINED_ID ||
- pa->e_id < id)
+ if (!uid_valid(pa->e_uid))
return -EINVAL;
- id = pa->e_id + 1;
+ if (uid_valid(prev_uid) &&
+ uid_lte(pa->e_uid, prev_uid))
+ return -EINVAL;
+ prev_uid = pa->e_uid;
needs_mask = 1;
break;
case ACL_GROUP_OBJ:
if (state == ACL_USER) {
- id = 0;
state = ACL_GROUP;
break;
}
case ACL_GROUP:
if (state != ACL_GROUP)
return -EINVAL;
- if (pa->e_id == ACL_UNDEFINED_ID ||
- pa->e_id < id)
+ if (!gid_valid(pa->e_gid))
+ return -EINVAL;
+ if (gid_valid(prev_gid) &&
+ gid_lte(pa->e_gid, prev_gid))
return -EINVAL;
- id = pa->e_id + 1;
+ prev_gid = pa->e_gid;
needs_mask = 1;
break;
return ERR_PTR(-ENOMEM);
acl->a_entries[0].e_tag = ACL_USER_OBJ;
- acl->a_entries[0].e_id = ACL_UNDEFINED_ID;
acl->a_entries[0].e_perm = (mode & S_IRWXU) >> 6;
acl->a_entries[1].e_tag = ACL_GROUP_OBJ;
- acl->a_entries[1].e_id = ACL_UNDEFINED_ID;
acl->a_entries[1].e_perm = (mode & S_IRWXG) >> 3;
acl->a_entries[2].e_tag = ACL_OTHER;
- acl->a_entries[2].e_id = ACL_UNDEFINED_ID;
acl->a_entries[2].e_perm = (mode & S_IRWXO);
return acl;
}
switch(pa->e_tag) {
case ACL_USER_OBJ:
/* (May have been checked already) */
- if (inode->i_uid == current_fsuid())
+ if (uid_eq(inode->i_uid, current_fsuid()))
goto check_perm;
break;
case ACL_USER:
- if (pa->e_id == current_fsuid())
+ if (uid_eq(pa->e_uid, current_fsuid()))
goto mask;
break;
case ACL_GROUP_OBJ:
}
break;
case ACL_GROUP:
- if (in_group_p(pa->e_id)) {
+ if (in_group_p(pa->e_gid)) {
found = 1;
if ((pa->e_perm & want) == want)
goto mask;
#include <linux/fsnotify.h>
#include <linux/audit.h>
#include <linux/vmalloc.h>
+#include <linux/posix_acl_xattr.h>
#include <asm/uaccess.h>
error = -EFAULT;
goto out;
}
+ if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
+ (strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0))
+ posix_acl_fix_xattr_from_user(kvalue, size);
}
error = vfs_setxattr(d, kname, kvalue, size, flags);
error = vfs_getxattr(d, kname, kvalue, size);
if (error > 0) {
+ if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
+ (strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0))
+ posix_acl_fix_xattr_to_user(kvalue, size);
if (size && copy_to_user(value, kvalue, error))
error = -EFAULT;
} else if (error == -ERANGE && size >= XATTR_SIZE_MAX) {
#include <linux/fs.h>
#include <linux/posix_acl_xattr.h>
#include <linux/gfp.h>
+#include <linux/user_namespace.h>
+/*
+ * Fix up the uids and gids in posix acl extended attributes in place.
+ */
+static void posix_acl_fix_xattr_userns(
+ struct user_namespace *to, struct user_namespace *from,
+ void *value, size_t size)
+{
+ posix_acl_xattr_header *header = (posix_acl_xattr_header *)value;
+ posix_acl_xattr_entry *entry = (posix_acl_xattr_entry *)(header+1), *end;
+ int count;
+ kuid_t uid;
+ kgid_t gid;
+
+ if (!value)
+ return;
+ if (size < sizeof(posix_acl_xattr_header))
+ return;
+ if (header->a_version != cpu_to_le32(POSIX_ACL_XATTR_VERSION))
+ return;
+
+ count = posix_acl_xattr_count(size);
+ if (count < 0)
+ return;
+ if (count == 0)
+ return;
+
+ for (end = entry + count; entry != end; entry++) {
+ switch(le16_to_cpu(entry->e_tag)) {
+ case ACL_USER:
+ uid = make_kuid(from, le32_to_cpu(entry->e_id));
+ entry->e_id = cpu_to_le32(from_kuid(to, uid));
+ break;
+ case ACL_GROUP:
+ gid = make_kgid(from, le32_to_cpu(entry->e_id));
+ entry->e_id = cpu_to_le32(from_kuid(to, uid));
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void posix_acl_fix_xattr_from_user(void *value, size_t size)
+{
+ struct user_namespace *user_ns = current_user_ns();
+ if (user_ns == &init_user_ns)
+ return;
+ posix_acl_fix_xattr_userns(&init_user_ns, user_ns, value, size);
+}
+
+void posix_acl_fix_xattr_to_user(void *value, size_t size)
+{
+ struct user_namespace *user_ns = current_user_ns();
+ if (user_ns == &init_user_ns)
+ return;
+ posix_acl_fix_xattr_userns(user_ns, &init_user_ns, value, size);
+}
/*
* Convert from extended attribute to in-memory representation.
case ACL_GROUP_OBJ:
case ACL_MASK:
case ACL_OTHER:
- acl_e->e_id = ACL_UNDEFINED_ID;
break;
case ACL_USER:
+ acl_e->e_uid =
+ make_kuid(&init_user_ns,
+ le32_to_cpu(entry->e_id));
+ if (!uid_valid(acl_e->e_uid))
+ goto fail;
+ break;
case ACL_GROUP:
- acl_e->e_id = le32_to_cpu(entry->e_id);
+ acl_e->e_gid =
+ make_kgid(&init_user_ns,
+ le32_to_cpu(entry->e_id));
+ if (!gid_valid(acl_e->e_gid))
+ goto fail;
break;
default:
ext_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
for (n=0; n < acl->a_count; n++, ext_entry++) {
- ext_entry->e_tag = cpu_to_le16(acl->a_entries[n].e_tag);
- ext_entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm);
- ext_entry->e_id = cpu_to_le32(acl->a_entries[n].e_id);
+ const struct posix_acl_entry *acl_e = &acl->a_entries[n];
+ ext_entry->e_tag = cpu_to_le16(acl_e->e_tag);
+ ext_entry->e_perm = cpu_to_le16(acl_e->e_perm);
+ switch(acl_e->e_tag) {
+ case ACL_USER:
+ ext_entry->e_id =
+ cpu_to_le32(from_kuid(&init_user_ns, acl_e->e_uid));
+ break;
+ case ACL_GROUP:
+ ext_entry->e_id =
+ cpu_to_le32(from_kgid(&init_user_ns, acl_e->e_gid));
+ break;
+ default:
+ ext_entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID);
+ break;
+ }
}
return real_size;
}
struct posix_acl_entry {
short e_tag;
unsigned short e_perm;
- unsigned int e_id;
+ union {
+ kuid_t e_uid;
+ kgid_t e_gid;
+#ifndef CONFIG_UIDGID_STRICT_TYPE_CHECKS
+ unsigned int e_id;
+#endif
+ };
};
struct posix_acl {
return size / sizeof(posix_acl_xattr_entry);
}
+#ifdef CONFIG_FS_POSIX_ACL
+void posix_acl_fix_xattr_from_user(void *value, size_t size);
+void posix_acl_fix_xattr_to_user(void *value, size_t size);
+#else
+static inline void posix_acl_fix_xattr_from_user(void *value, size_t size)
+{
+}
+static inline void posix_acl_fix_xattr_to_user(void *value, size_t size)
+{
+}
+#endif
+
struct posix_acl *posix_acl_from_xattr(const void *value, size_t size);
int posix_acl_to_xattr(const struct posix_acl *acl, void *buffer, size_t size);