From 66258e6274428d61bfd05be84aee41347de06867 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Tue, 19 Jan 2016 10:25:24 -0600 Subject: [PATCH 2/6] overlayfs: Use mounter's credentials instead of selectively raising caps When overlayfs needs to perform internal operations which require privileges the current user may not have, it does so by selectively raising the required capabilities in the current set of credentials. If the current process is in a user namespace this doesn't always work, as operations such as setting privileged xattrs often requires privileges in init_user_ns. These operations really ought to be permitted, based on a couple of facts: 1. The vfs has already verified that the current process is allowed to perform the requested operation on the overlayfs super block, and overlayfs has verified that the operation is permitted in upperdir. 2. The original mounter of the overlayfs super block was privileged enough to perform the internal overlayfs operations required to satisfy the user's request in upperdir. A more direct method of encoding (2) is to use the mounter's credentials when performing privileged overlayfs-internal operations, and it has the benefit of fixing the issues of using an overlayfs mount from a less privileged context. overlayfs even has a copy of these credentials already. Add a new internal interface, ovl_prepare_creds(), which returns a new set of credentials for performing privileged internal operations. This is identical to the mounter's creds except that fsuid and fsgid are changed to match those of the current process. Use this internal interface instead of using prepare_creds() and selectively raising the needed capabilities. Signed-off-by: Seth Forshee --- fs/overlayfs/copy_up.c | 19 +------------------ fs/overlayfs/dir.c | 41 +++-------------------------------------- fs/overlayfs/overlayfs.h | 1 + fs/overlayfs/readdir.c | 9 +++------ fs/overlayfs/super.c | 23 +++++++++++++++++++++++ 5 files changed, 31 insertions(+), 62 deletions(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 6194082..fc3afd3 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -324,26 +324,9 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, } err = -ENOMEM; - override_cred = prepare_creds(); + override_cred = ovl_prepare_creds(dentry->d_sb); if (!override_cred) goto out_free_link; - - override_cred->fsuid = stat->uid; - override_cred->fsgid = stat->gid; - /* - * CAP_SYS_ADMIN for copying up extended attributes - * CAP_DAC_OVERRIDE for create - * CAP_FOWNER for chmod, timestamp update - * CAP_FSETID for chmod - * CAP_CHOWN for chown - * CAP_MKNOD for mknod - */ - cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); - cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); - cap_raise(override_cred->cap_effective, CAP_FOWNER); - cap_raise(override_cred->cap_effective, CAP_FSETID); - cap_raise(override_cred->cap_effective, CAP_CHOWN); - cap_raise(override_cred->cap_effective, CAP_MKNOD); old_cred = override_creds(override_cred); err = -EIO; diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index b16b7aa..d269174 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -436,18 +436,9 @@ static int ovl_create_or_link(struct dentry *dentry, int mode, dev_t rdev, struct cred *override_cred; err = -ENOMEM; - override_cred = prepare_creds(); + override_cred = ovl_prepare_creds(dentry->d_sb); if (!override_cred) goto out_iput; - - /* - * CAP_SYS_ADMIN for setting opaque xattr - * CAP_DAC_OVERRIDE for create in workdir, rename - * CAP_FOWNER for removing whiteout from sticky dir - */ - cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); - cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); - cap_raise(override_cred->cap_effective, CAP_FOWNER); old_cred = override_creds(override_cred); err = ovl_create_over_whiteout(dentry, inode, &stat, link, @@ -688,22 +679,9 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir) struct cred *override_cred; err = -ENOMEM; - override_cred = prepare_creds(); + override_cred = ovl_prepare_creds(dentry->d_sb); if (!override_cred) goto out_drop_write; - - /* - * CAP_SYS_ADMIN for setting xattr on whiteout, opaque dir - * CAP_DAC_OVERRIDE for create in workdir, rename - * CAP_FOWNER for removing whiteout from sticky dir - * CAP_FSETID for chmod of opaque dir - * CAP_CHOWN for chown of opaque dir - */ - cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); - cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); - cap_raise(override_cred->cap_effective, CAP_FOWNER); - cap_raise(override_cred->cap_effective, CAP_FSETID); - cap_raise(override_cred->cap_effective, CAP_CHOWN); old_cred = override_creds(override_cred); err = ovl_remove_and_whiteout(dentry, is_dir); @@ -864,22 +842,9 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old, if (old_opaque || new_opaque) { err = -ENOMEM; - override_cred = prepare_creds(); + override_cred = ovl_prepare_creds(old->d_sb); if (!override_cred) goto out_drop_write; - - /* - * CAP_SYS_ADMIN for setting xattr on whiteout, opaque dir - * CAP_DAC_OVERRIDE for create in workdir - * CAP_FOWNER for removing whiteout from sticky dir - * CAP_FSETID for chmod of opaque dir - * CAP_CHOWN for chown of opaque dir - */ - cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); - cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); - cap_raise(override_cred->cap_effective, CAP_FOWNER); - cap_raise(override_cred->cap_effective, CAP_FSETID); - cap_raise(override_cred->cap_effective, CAP_CHOWN); old_cred = override_creds(override_cred); } diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 46a58f1..ce600d2 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -149,6 +149,7 @@ static inline int ovl_do_whiteout(struct inode *dir, struct dentry *dentry, return ovl_do_whiteout_v2(dir, dentry); } +struct cred *ovl_prepare_creds(struct super_block *sb); enum ovl_path_type ovl_path_type(struct dentry *dentry); u64 ovl_dentry_version_get(struct dentry *dentry); void ovl_dentry_version_inc(struct dentry *dentry); diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index 16e0943..7d7d84c 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -37,6 +37,7 @@ struct ovl_dir_cache { struct ovl_readdir_data { struct dir_context ctx; + struct dentry *dentry; bool is_merge; struct rb_root root; struct list_head *list; @@ -209,14 +210,9 @@ static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd) const struct cred *old_cred; struct cred *override_cred; - override_cred = prepare_creds(); + override_cred = ovl_prepare_creds(rdd->dentry->d_sb); if (!override_cred) return -ENOMEM; - - /* - * CAP_DAC_OVERRIDE for lookup - */ - cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); old_cred = override_creds(override_cred); err = mutex_lock_killable(&dir->d_inode->i_mutex); @@ -289,6 +285,7 @@ static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list) struct path realpath; struct ovl_readdir_data rdd = { .ctx.actor = ovl_fill_merge, + .dentry = dentry, .list = list, .root = RB_ROOT, .is_merge = false, diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 03fe2e7..b19d889 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -64,6 +64,29 @@ struct ovl_entry { #define OVL_MAX_STACK 500 +/* + * Returns a set of credentials suitable for overlayfs internal + * operations which require elevated capabilities, equivalent to those + * of the user which mounted the superblock. Caller must put the + * returned credentials. + */ +struct cred *ovl_prepare_creds(struct super_block *sb) +{ + struct ovl_fs *ofs = sb->s_fs_info; + struct cred *new_cred; + + if (sb->s_magic != OVERLAYFS_SUPER_MAGIC) + return NULL; + + new_cred = clone_cred(ofs->mounter_creds); + if (!new_cred) + return NULL; + + new_cred->fsuid = current->cred->fsuid; + new_cred->fsgid = current->cred->fsgid; + return new_cred; +} + static struct dentry *__ovl_dentry_lower(struct ovl_entry *oe) { return oe->numlower ? oe->lowerstack[0].dentry : NULL; -- 2.7.0.rc3