Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGoldwyn Rodrigues <rgoldwyn@suse.com>2018-07-06 07:22:46 -0500
committerGoldwyn Rodrigues <rgoldwyn@suse.com>2018-07-06 07:22:46 -0500
commitdae28bb29d1a6fb325c6e5f3188b494f78c4b393 (patch)
tree31827bd056fe3a16e788740afc6579ab2e59c291
parentf6ee1e75a5bf973c7f94cd7bc7e78c8bb55e3116 (diff)
ovl: override creds with the ones from the superblock mounter
(bsc#1099993).
-rw-r--r--patches.fixes/ovl-override-creds-with-the-ones-from-the-superblock.patch346
-rw-r--r--series.conf1
2 files changed, 347 insertions, 0 deletions
diff --git a/patches.fixes/ovl-override-creds-with-the-ones-from-the-superblock.patch b/patches.fixes/ovl-override-creds-with-the-ones-from-the-superblock.patch
new file mode 100644
index 0000000000..3b7be1bf5f
--- /dev/null
+++ b/patches.fixes/ovl-override-creds-with-the-ones-from-the-superblock.patch
@@ -0,0 +1,346 @@
+From 3fe6e52f062643676eb4518d68cee3bc1272091b Mon Sep 17 00:00:00 2001
+From: Antonio Murdaca <amurdaca@redhat.com>
+Date: Thu, 7 Apr 2016 15:48:25 +0200
+Subject: [PATCH] ovl: override creds with the ones from the superblock mounter
+References: bsc#1099993
+Git-commit: 3fe6e52f062643676eb4518d68cee3bc1272091b
+Patch-mainline: v4.7-rc1
+
+In user namespace the whiteout creation fails with -EPERM because the
+current process isn't capable(CAP_SYS_ADMIN) when setting xattr.
+
+A simple reproducer:
+
+$ mkdir upper lower work merged lower/dir
+$ sudo mount -t overlay overlay -olowerdir=lower,upperdir=upper,workdir=work merged
+$ unshare -m -p -f -U -r bash
+
+Now as root in the user namespace:
+
+\# touch merged/dir/{1,2,3} # this will force a copy up of lower/dir
+\# rm -fR merged/*
+
+This ends up failing with -EPERM after the files in dir has been
+correctly deleted:
+
+unlinkat(4, "2", 0) = 0
+unlinkat(4, "1", 0) = 0
+unlinkat(4, "3", 0) = 0
+close(4) = 0
+unlinkat(AT_FDCWD, "merged/dir", AT_REMOVEDIR) = -1 EPERM (Operation not
+permitted)
+
+Interestingly, if you don't place files in merged/dir you can remove it,
+meaning if upper/dir does not exist, creating the char device file works
+properly in that same location.
+
+This patch uses ovl_sb_creator_cred() to get the cred struct from the
+superblock mounter and override the old cred with these new ones so that
+the whiteout creation is possible because overlay is wrong in assuming that
+the creds it will get with prepare_creds will be in the initial user
+namespace. The old cap_raise game is removed in favor of just overriding
+the old cred struct.
+
+This patch also drops from ovl_copy_up_one() the following two lines:
+
+override_cred->fsuid = stat->uid;
+override_cred->fsgid = stat->gid;
+
+This is because the correct uid and gid are taken directly with the stat
+struct and correctly set with ovl_set_attr().
+
+Signed-off-by: Antonio Murdaca <runcom@redhat.com>
+Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
+Acked-by: Goldwyn Rodrigues <rgoldwyn@suse.com>
+
+---
+ fs/overlayfs/copy_up.c | 26 +------------------
+ fs/overlayfs/dir.c | 67 ++++--------------------------------------------
+ fs/overlayfs/overlayfs.h | 1 +
+ fs/overlayfs/readdir.c | 13 ++--------
+ fs/overlayfs/super.c | 18 ++++++++++++-
+ 5 files changed, 26 insertions(+), 99 deletions(-)
+
+diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
+index 762f2eb246d9..65fe6d2deb00 100644
+--- a/fs/overlayfs/copy_up.c
++++ b/fs/overlayfs/copy_up.c
+@@ -318,7 +318,6 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
+ struct dentry *upperdir;
+ struct dentry *upperdentry;
+ const struct cred *old_cred;
+- struct cred *override_cred;
+ char *link = NULL;
+ struct dentry *d, *ancestor = NULL;
+
+@@ -341,28 +340,7 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
+ return PTR_ERR(link);
+ }
+
+- err = -ENOMEM;
+- override_cred = prepare_creds();
+- 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);
++ old_cred = ovl_override_creds(dentry->d_sb);
+
+ err = -EIO;
+
+@@ -387,9 +365,7 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
+ out_unlock:
+ unlock_rename(workdir, upperdir);
+ revert_creds(old_cred);
+- put_cred(override_cred);
+
+-out_free_link:
+ if (link)
+ free_page((unsigned long) link);
+
+diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
+index 0c2451b135da..1ead1cbd1c19 100644
+--- a/fs/overlayfs/dir.c
++++ b/fs/overlayfs/dir.c
+@@ -422,28 +422,13 @@ static int ovl_create_or_link(struct dentry *dentry, int mode, dev_t rdev,
+ err = ovl_create_upper(dentry, inode, &stat, link, hardlink);
+ } else {
+ const struct cred *old_cred;
+- struct cred *override_cred;
+
+- err = -ENOMEM;
+- override_cred = prepare_creds();
+- 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);
++ old_cred = ovl_override_creds(dentry->d_sb);
+
+ err = ovl_create_over_whiteout(dentry, inode, &stat, link,
+ hardlink);
+
+ revert_creds(old_cred);
+- put_cred(override_cred);
+ }
+
+ if (!err)
+@@ -673,32 +658,11 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
+ if (OVL_TYPE_PURE_UPPER(type)) {
+ err = ovl_remove_upper(dentry, is_dir);
+ } else {
+- const struct cred *old_cred;
+- struct cred *override_cred;
+-
+- err = -ENOMEM;
+- override_cred = prepare_creds();
+- 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);
++ const struct cred *old_cred = ovl_override_creds(dentry->d_sb);
+
+ err = ovl_remove_and_whiteout(dentry, is_dir);
+
+ revert_creds(old_cred);
+- put_cred(override_cred);
+ }
+ out_drop_write:
+ ovl_drop_write(dentry);
+@@ -737,7 +701,6 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
+ bool new_is_dir = false;
+ struct dentry *opaquedir = NULL;
+ const struct cred *old_cred = NULL;
+- struct cred *override_cred = NULL;
+
+ err = -EINVAL;
+ if (flags & ~(RENAME_EXCHANGE | RENAME_NOREPLACE))
+@@ -806,26 +769,8 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
+ old_opaque = !OVL_TYPE_PURE_UPPER(old_type);
+ new_opaque = !OVL_TYPE_PURE_UPPER(new_type);
+
+- if (old_opaque || new_opaque) {
+- err = -ENOMEM;
+- override_cred = prepare_creds();
+- 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);
+- }
++ if (old_opaque || new_opaque)
++ old_cred = ovl_override_creds(old->d_sb);
+
+ if (overwrite && OVL_TYPE_MERGE_OR_LOWER(new_type) && new_is_dir) {
+ opaquedir = ovl_check_empty_and_clear(new);
+@@ -956,10 +901,8 @@ out_dput_old:
+ out_unlock:
+ unlock_rename(new_upperdir, old_upperdir);
+ out_revert_creds:
+- if (old_opaque || new_opaque) {
++ if (old_opaque || new_opaque)
+ revert_creds(old_cred);
+- put_cred(override_cred);
+- }
+ out_drop_write:
+ ovl_drop_write(old);
+ out:
+diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
+index 5cd41b095f3e..255ae187d437 100644
+--- a/fs/overlayfs/overlayfs.h
++++ b/fs/overlayfs/overlayfs.h
+@@ -153,6 +153,7 @@ void ovl_drop_write(struct dentry *dentry);
+ bool ovl_dentry_is_opaque(struct dentry *dentry);
+ void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque);
+ bool ovl_is_whiteout(struct dentry *dentry, struct dentry *ovl_dir);
++const struct cred *ovl_override_creds(struct super_block *sb);
+ void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry);
+ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
+ unsigned int flags);
+diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
+index f20a90bd4096..3bdafb303ef5 100644
+--- a/fs/overlayfs/readdir.c
++++ b/fs/overlayfs/readdir.c
+@@ -208,17 +208,8 @@ static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd)
+ struct ovl_cache_entry *p;
+ struct dentry *dentry;
+ const struct cred *old_cred;
+- struct cred *override_cred;
+
+- override_cred = prepare_creds();
+- 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);
++ old_cred = ovl_override_creds(rdd->dentry->d_sb);
+
+ err = mutex_lock_killable(&dir->d_inode->i_mutex);
+ if (!err) {
+@@ -235,7 +226,6 @@ static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd)
+ mutex_unlock(&dir->d_inode->i_mutex);
+ }
+ revert_creds(old_cred);
+- put_cred(override_cred);
+
+ return err;
+ }
+@@ -291,6 +281,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 00bf03a1f081..f85e73f49543 100644
+--- a/fs/overlayfs/super.c
++++ b/fs/overlayfs/super.c
+@@ -44,6 +44,8 @@ struct ovl_fs {
+ long lower_namelen;
+ /* pathnames of lower and upper dirs, for show_options */
+ struct ovl_config config;
++ /* creds of process who forced instantiation of super block */
++ const struct cred *creator_cred;
+ bool compat;
+ };
+
+@@ -279,6 +281,13 @@ bool ovl_is_whiteout(struct dentry *dentry, struct dentry *ovl_dir)
+ return inode && IS_WHITEOUT(inode);
+ }
+
++const struct cred *ovl_override_creds(struct super_block *sb)
++{
++ struct ovl_fs *ofs = sb->s_fs_info;
++
++ return override_creds(ofs->creator_cred);
++}
++
+ static bool ovl_is_opaquedir(struct dentry *dentry)
+ {
+ int res;
+@@ -620,6 +629,7 @@ static void ovl_put_super(struct super_block *sb)
+ kfree(ufs->config.lowerdir);
+ kfree(ufs->config.upperdir);
+ kfree(ufs->config.workdir);
++ put_cred(ufs->creator_cred);
+ kfree(ufs);
+ }
+
+@@ -1172,10 +1182,14 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
+ else
+ sb->s_d_op = &ovl_dentry_operations;
+
++ ufs->creator_cred = prepare_creds();
++ if (!ufs->creator_cred)
++ goto out_put_lower_mnt;
++
+ err = -ENOMEM;
+ oe = ovl_alloc_entry(numlower);
+ if (!oe)
+- goto out_put_lower_mnt;
++ goto out_put_cred;
+
+ root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, oe));
+ if (!root_dentry)
+@@ -1211,6 +1225,8 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
+
+ out_free_oe:
+ kfree(oe);
++out_put_cred:
++ put_cred(ufs->creator_cred);
+ out_put_lower_mnt:
+ for (i = 0; i < ufs->numlower; i++)
+ mntput(ufs->lower_mnt[i]);
+--
+2.16.4
+
diff --git a/series.conf b/series.conf
index 565c7a393d..2e868739e7 100644
--- a/series.conf
+++ b/series.conf
@@ -8018,6 +8018,7 @@
patches.suse/overlayfs-compat-fix-incorrect-dentry-use-in-ovl_rename2.patch
patches.fixes/ovl-fix-dentry-leak-for-default_permissions.patch
+ patches.fixes/ovl-override-creds-with-the-ones-from-the-superblock.patch
########################################################
#