123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- 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
- 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>
- ---
- fs/overlayfs/copy_up.c | 26 +------------------
- fs/overlayfs/dir.c | 67 ++++--------------------------------------------
- fs/overlayfs/overlayfs.h | 1 +
- fs/overlayfs/readdir.c | 14 +++-------
- fs/overlayfs/super.c | 18 ++++++++++++-
- 5 files changed, 27 insertions(+), 99 deletions(-)
- --- a/fs/overlayfs/copy_up.c
- +++ b/fs/overlayfs/copy_up.c
- @@ -317,7 +317,6 @@ int ovl_copy_up_one(struct dentry *paren
- struct dentry *upperdir;
- struct dentry *upperdentry;
- const struct cred *old_cred;
- - struct cred *override_cred;
- char *link = NULL;
-
- if (WARN_ON(!workdir))
- @@ -336,28 +335,7 @@ int ovl_copy_up_one(struct dentry *paren
- 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;
- if (lock_rename(workdir, upperdir) != NULL) {
- @@ -380,9 +358,7 @@ int ovl_copy_up_one(struct dentry *paren
- out_unlock:
- unlock_rename(workdir, upperdir);
- revert_creds(old_cred);
- - put_cred(override_cred);
-
- -out_free_link:
- if (link)
- free_page((unsigned long) link);
-
- --- a/fs/overlayfs/dir.c
- +++ b/fs/overlayfs/dir.c
- @@ -408,28 +408,13 @@ static int ovl_create_or_link(struct den
- 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)
- @@ -659,32 +644,11 @@ static int ovl_do_remove(struct dentry *
- 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);
- @@ -723,7 +687,6 @@ static int ovl_rename2(struct inode *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))
- @@ -792,26 +755,8 @@ static int ovl_rename2(struct inode *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);
- @@ -942,10 +887,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:
- --- a/fs/overlayfs/overlayfs.h
- +++ b/fs/overlayfs/overlayfs.h
- @@ -150,6 +150,7 @@ void ovl_drop_write(struct dentry *dentr
- 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);
- +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);
- --- a/fs/overlayfs/readdir.c
- +++ b/fs/overlayfs/readdir.c
- @@ -36,6 +36,7 @@ struct ovl_dir_cache {
-
- struct ovl_readdir_data {
- struct dir_context ctx;
- + struct dentry *dentry;
- bool is_lowest;
- struct rb_root root;
- struct list_head *list;
- @@ -205,17 +206,8 @@ static int ovl_check_whiteouts(struct de
- 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) {
- @@ -231,7 +223,6 @@ static int ovl_check_whiteouts(struct de
- mutex_unlock(&dir->d_inode->i_mutex);
- }
- revert_creds(old_cred);
- - put_cred(override_cred);
-
- return err;
- }
- @@ -287,6 +278,7 @@ static int ovl_dir_read_merged(struct de
- struct path realpath;
- struct ovl_readdir_data rdd = {
- .ctx.actor = ovl_fill_merge,
- + .dentry = dentry,
- .list = list,
- .root = RB_ROOT,
- .is_lowest = false,
- --- a/fs/overlayfs/super.c
- +++ b/fs/overlayfs/super.c
- @@ -42,6 +42,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;
- };
-
- struct ovl_dir_cache;
- @@ -246,6 +248,13 @@ bool ovl_is_whiteout(struct dentry *dent
- 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;
- @@ -587,6 +596,7 @@ static void ovl_put_super(struct super_b
- kfree(ufs->config.lowerdir);
- kfree(ufs->config.upperdir);
- kfree(ufs->config.workdir);
- + put_cred(ufs->creator_cred);
- kfree(ufs);
- }
-
- @@ -1087,10 +1097,14 @@ static int ovl_fill_super(struct super_b
- 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)
- @@ -1123,6 +1137,8 @@ static int ovl_fill_super(struct super_b
-
- 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]);
|