Commit f2b00be4 authored by Miklos Szeredi's avatar Miklos Szeredi

cap: fix conversions on getxattr

If a capability is stored on disk in v2 format cap_inode_getsecurity() will
currently return in v2 format unconditionally.

This is wrong: v2 cap should be equivalent to a v3 cap with zero rootid,
and so the same conversions performed on it.

If the rootid cannot be mapped, v3 is returned unconverted.  Fix this so
that both v2 and v3 return -EOVERFLOW if the rootid (or the owner of the fs
user namespace in case of v2) cannot be mapped into the current user
namespace.
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
Acked-by: default avatar"Eric W. Biederman" <ebiederm@xmission.com>
parent 554677b9
...@@ -371,10 +371,11 @@ int cap_inode_getsecurity(struct inode *inode, const char *name, void **buffer, ...@@ -371,10 +371,11 @@ int cap_inode_getsecurity(struct inode *inode, const char *name, void **buffer,
{ {
int size, ret; int size, ret;
kuid_t kroot; kuid_t kroot;
u32 nsmagic, magic;
uid_t root, mappedroot; uid_t root, mappedroot;
char *tmpbuf = NULL; char *tmpbuf = NULL;
struct vfs_cap_data *cap; struct vfs_cap_data *cap;
struct vfs_ns_cap_data *nscap; struct vfs_ns_cap_data *nscap = NULL;
struct dentry *dentry; struct dentry *dentry;
struct user_namespace *fs_ns; struct user_namespace *fs_ns;
...@@ -396,46 +397,61 @@ int cap_inode_getsecurity(struct inode *inode, const char *name, void **buffer, ...@@ -396,46 +397,61 @@ int cap_inode_getsecurity(struct inode *inode, const char *name, void **buffer,
fs_ns = inode->i_sb->s_user_ns; fs_ns = inode->i_sb->s_user_ns;
cap = (struct vfs_cap_data *) tmpbuf; cap = (struct vfs_cap_data *) tmpbuf;
if (is_v2header((size_t) ret, cap)) { if (is_v2header((size_t) ret, cap)) {
/* If this is sizeof(vfs_cap_data) then we're ok with the root = 0;
* on-disk value, so return that. */ } else if (is_v3header((size_t) ret, cap)) {
if (alloc) nscap = (struct vfs_ns_cap_data *) tmpbuf;
*buffer = tmpbuf; root = le32_to_cpu(nscap->rootid);
else } else {
kfree(tmpbuf); size = -EINVAL;
return ret; goto out_free;
} else if (!is_v3header((size_t) ret, cap)) {
kfree(tmpbuf);
return -EINVAL;
} }
nscap = (struct vfs_ns_cap_data *) tmpbuf;
root = le32_to_cpu(nscap->rootid);
kroot = make_kuid(fs_ns, root); kroot = make_kuid(fs_ns, root);
/* If the root kuid maps to a valid uid in current ns, then return /* If the root kuid maps to a valid uid in current ns, then return
* this as a nscap. */ * this as a nscap. */
mappedroot = from_kuid(current_user_ns(), kroot); mappedroot = from_kuid(current_user_ns(), kroot);
if (mappedroot != (uid_t)-1 && mappedroot != (uid_t)0) { if (mappedroot != (uid_t)-1 && mappedroot != (uid_t)0) {
size = sizeof(struct vfs_ns_cap_data);
if (alloc) { if (alloc) {
*buffer = tmpbuf; if (!nscap) {
/* v2 -> v3 conversion */
nscap = kzalloc(size, GFP_ATOMIC);
if (!nscap) {
size = -ENOMEM;
goto out_free;
}
nsmagic = VFS_CAP_REVISION_3;
magic = le32_to_cpu(cap->magic_etc);
if (magic & VFS_CAP_FLAGS_EFFECTIVE)
nsmagic |= VFS_CAP_FLAGS_EFFECTIVE;
memcpy(&nscap->data, &cap->data, sizeof(__le32) * 2 * VFS_CAP_U32);
nscap->magic_etc = cpu_to_le32(nsmagic);
} else {
/* use allocated v3 buffer */
tmpbuf = NULL;
}
nscap->rootid = cpu_to_le32(mappedroot); nscap->rootid = cpu_to_le32(mappedroot);
} else *buffer = nscap;
kfree(tmpbuf); }
return size; goto out_free;
} }
if (!rootid_owns_currentns(kroot)) { if (!rootid_owns_currentns(kroot)) {
kfree(tmpbuf); size = -EOVERFLOW;
return -EOPNOTSUPP; goto out_free;
} }
/* This comes from a parent namespace. Return as a v2 capability */ /* This comes from a parent namespace. Return as a v2 capability */
size = sizeof(struct vfs_cap_data); size = sizeof(struct vfs_cap_data);
if (alloc) { if (alloc) {
*buffer = kmalloc(size, GFP_ATOMIC); if (nscap) {
if (*buffer) { /* v3 -> v2 conversion */
struct vfs_cap_data *cap = *buffer; cap = kzalloc(size, GFP_ATOMIC);
__le32 nsmagic, magic; if (!cap) {
size = -ENOMEM;
goto out_free;
}
magic = VFS_CAP_REVISION_2; magic = VFS_CAP_REVISION_2;
nsmagic = le32_to_cpu(nscap->magic_etc); nsmagic = le32_to_cpu(nscap->magic_etc);
if (nsmagic & VFS_CAP_FLAGS_EFFECTIVE) if (nsmagic & VFS_CAP_FLAGS_EFFECTIVE)
...@@ -443,9 +459,12 @@ int cap_inode_getsecurity(struct inode *inode, const char *name, void **buffer, ...@@ -443,9 +459,12 @@ int cap_inode_getsecurity(struct inode *inode, const char *name, void **buffer,
memcpy(&cap->data, &nscap->data, sizeof(__le32) * 2 * VFS_CAP_U32); memcpy(&cap->data, &nscap->data, sizeof(__le32) * 2 * VFS_CAP_U32);
cap->magic_etc = cpu_to_le32(magic); cap->magic_etc = cpu_to_le32(magic);
} else { } else {
size = -ENOMEM; /* use unconverted v2 */
tmpbuf = NULL;
} }
*buffer = cap;
} }
out_free:
kfree(tmpbuf); kfree(tmpbuf);
return size; return size;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment