Commit 197c4309 authored by Antonio Murdaca's avatar Antonio Murdaca Committed by Kleber Sacilotto de Souza

ovl: override creds with the ones from the superblock mounter

BugLink: https://bugs.launchpad.net/bugs/1797563

commit 3fe6e52f upstream.

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: default avatarAntonio Murdaca <runcom@redhat.com>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
Signed-off-by: default avatarSZ Lin (林上智) <sz.lin@moxa.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarStefan Bader <stefan.bader@canonical.com>
Signed-off-by: default avatarKleber Sacilotto de Souza <kleber.souza@canonical.com>
parent efa61fd3
...@@ -475,7 +475,10 @@ static int ovl_create_or_link(struct dentry *dentry, int mode, dev_t rdev, ...@@ -475,7 +475,10 @@ static int ovl_create_or_link(struct dentry *dentry, int mode, dev_t rdev,
if (!ovl_dentry_is_opaque(dentry)) { if (!ovl_dentry_is_opaque(dentry)) {
err = ovl_create_upper(dentry, inode, &stat, link, hardlink); err = ovl_create_upper(dentry, inode, &stat, link, hardlink);
} else { } else {
const struct cred *old_cred = ovl_override_creds(dentry->d_sb); const struct cred *old_cred;
old_cred = ovl_override_creds(dentry->d_sb);
err = ovl_create_over_whiteout(dentry, inode, &stat, link, err = ovl_create_over_whiteout(dentry, inode, &stat, link,
hardlink); hardlink);
revert_creds(old_cred); revert_creds(old_cred);
...@@ -709,7 +712,9 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir) ...@@ -709,7 +712,9 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
err = ovl_remove_upper(dentry, is_dir); err = ovl_remove_upper(dentry, is_dir);
} else { } else {
const struct cred *old_cred = ovl_override_creds(dentry->d_sb); const struct cred *old_cred = ovl_override_creds(dentry->d_sb);
err = ovl_remove_and_whiteout(dentry, is_dir); err = ovl_remove_and_whiteout(dentry, is_dir);
revert_creds(old_cred); revert_creds(old_cred);
} }
out_drop_write: out_drop_write:
......
...@@ -163,7 +163,6 @@ static inline int ovl_do_whiteout(struct inode *dir, struct dentry *dentry, ...@@ -163,7 +163,6 @@ static inline int ovl_do_whiteout(struct inode *dir, struct dentry *dentry,
return ovl_do_whiteout_v2(dir, dentry); return ovl_do_whiteout_v2(dir, dentry);
} }
const struct cred *ovl_override_creds(struct super_block *sb);
struct super_block *ovl_same_sb(struct super_block *sb); struct super_block *ovl_same_sb(struct super_block *sb);
enum ovl_path_type ovl_path_type(struct dentry *dentry); enum ovl_path_type ovl_path_type(struct dentry *dentry);
u64 ovl_dentry_version_get(struct dentry *dentry); u64 ovl_dentry_version_get(struct dentry *dentry);
...@@ -184,6 +183,7 @@ void ovl_drop_write(struct dentry *dentry); ...@@ -184,6 +183,7 @@ void ovl_drop_write(struct dentry *dentry);
bool ovl_dentry_is_opaque(struct dentry *dentry); bool ovl_dentry_is_opaque(struct dentry *dentry);
void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque); void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque);
bool ovl_is_whiteout(struct dentry *dentry, int is_legacy); bool ovl_is_whiteout(struct dentry *dentry, int is_legacy);
const struct cred *ovl_override_creds(struct super_block *sb);
void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry); void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry);
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
unsigned int flags); unsigned int flags);
......
...@@ -43,7 +43,8 @@ struct ovl_fs { ...@@ -43,7 +43,8 @@ struct ovl_fs {
int legacy; int legacy;
/* pathnames of lower and upper dirs, for show_options */ /* pathnames of lower and upper dirs, for show_options */
struct ovl_config config; struct ovl_config config;
struct cred *mounter_creds; /* creds of process who forced instantiation of super block */
struct cred *creator_cred;
/* sb common to all layers */ /* sb common to all layers */
struct super_block *same_sb; struct super_block *same_sb;
}; };
...@@ -67,13 +68,6 @@ struct ovl_entry { ...@@ -67,13 +68,6 @@ struct ovl_entry {
#define OVL_MAX_STACK 500 #define OVL_MAX_STACK 500
const struct cred *ovl_override_creds(struct super_block *sb)
{
struct ovl_fs *ofs = sb->s_fs_info;
return override_creds(ofs->mounter_creds);
}
static struct dentry *__ovl_dentry_lower(struct ovl_entry *oe) static struct dentry *__ovl_dentry_lower(struct ovl_entry *oe)
{ {
return oe->numlower ? oe->lowerstack[0].dentry : NULL; return oe->numlower ? oe->lowerstack[0].dentry : NULL;
...@@ -256,7 +250,7 @@ int ovl_dentry_root_may(struct dentry *dentry, struct path *realpath, int mode) ...@@ -256,7 +250,7 @@ int ovl_dentry_root_may(struct dentry *dentry, struct path *realpath, int mode)
int err = 0; int err = 0;
struct ovl_fs *ofs = dentry->d_sb->s_fs_info; struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
old_cred = override_creds(ofs->mounter_creds); old_cred = override_creds(ofs->creator_cred);
if (inode_permission(realpath->dentry->d_inode, mode)) if (inode_permission(realpath->dentry->d_inode, mode))
err = -EACCES; err = -EACCES;
...@@ -316,6 +310,13 @@ bool ovl_is_whiteout(struct dentry *dentry, int is_legacy) ...@@ -316,6 +310,13 @@ bool ovl_is_whiteout(struct dentry *dentry, int is_legacy)
return ovl_is_whiteout_v2(dentry); return ovl_is_whiteout_v2(dentry);
} }
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) static bool ovl_is_opaquedir(struct dentry *dentry)
{ {
int res; int res;
...@@ -661,7 +662,6 @@ static void ovl_put_super(struct super_block *sb) ...@@ -661,7 +662,6 @@ static void ovl_put_super(struct super_block *sb)
struct ovl_fs *ufs = sb->s_fs_info; struct ovl_fs *ufs = sb->s_fs_info;
unsigned i; unsigned i;
put_cred(ufs->mounter_creds);
dput(ufs->workdir); dput(ufs->workdir);
mntput(ufs->upper_mnt); mntput(ufs->upper_mnt);
for (i = 0; i < ufs->numlower; i++) for (i = 0; i < ufs->numlower; i++)
...@@ -671,6 +671,7 @@ static void ovl_put_super(struct super_block *sb) ...@@ -671,6 +671,7 @@ static void ovl_put_super(struct super_block *sb)
kfree(ufs->config.lowerdir); kfree(ufs->config.lowerdir);
kfree(ufs->config.upperdir); kfree(ufs->config.upperdir);
kfree(ufs->config.workdir); kfree(ufs->config.workdir);
put_cred(ufs->creator_cred);
kfree(ufs); kfree(ufs);
} }
...@@ -1205,20 +1206,19 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1205,20 +1206,19 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
else else
sb->s_d_op = &ovl_dentry_operations; sb->s_d_op = &ovl_dentry_operations;
ufs->creator_cred = prepare_creds();
if (!ufs->creator_cred)
goto out_put_lower_mnt;
err = -ENOMEM; err = -ENOMEM;
oe = ovl_alloc_entry(numlower); oe = ovl_alloc_entry(numlower);
if (!oe) if (!oe)
goto out_put_lower_mnt; goto out_put_cred;
root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, oe)); root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, oe));
if (!root_dentry) if (!root_dentry)
goto out_free_oe; goto out_free_oe;
/* Record the mounter. */
ufs->mounter_creds = prepare_creds();
if (!ufs->mounter_creds)
goto out_put_root;
mntput(upperpath.mnt); mntput(upperpath.mnt);
for (i = 0; i < numlower; i++) for (i = 0; i < numlower; i++)
mntput(stack[i].mnt); mntput(stack[i].mnt);
...@@ -1244,10 +1244,10 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1244,10 +1244,10 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
return 0; return 0;
out_put_root:
dput(root_dentry);
out_free_oe: out_free_oe:
kfree(oe); kfree(oe);
out_put_cred:
put_cred(ufs->creator_cred);
out_put_lower_mnt: out_put_lower_mnt:
for (i = 0; i < ufs->numlower; i++) for (i = 0; i < ufs->numlower; i++)
mntput(ufs->lower_mnt[i]); mntput(ufs->lower_mnt[i]);
......
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