Commit e5e96b1e authored by Seth Forshee's avatar Seth Forshee Committed by Tim Gardner

UBUNTU: SAUCE: 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.

On the other hand, if the filesystem is mounted from a user
namespace and then accessed in init_user_ns the credentials taken
will exceed those of the mounter. This could result in performing
operations that the user could not do otherwise, such as creating
files which are sxid for another user or group. Both of these
issues can be prevented by using the mounter's credentials when
performing privileged overlayfs-internal operations.

Add a new internal interface, ovl_prepare_creds(), which returns
a new set of credentials for performing privileged internal
operations that is identical to the mounter's creds. Use this
internal interface instead of using prepare_creds() and
selectively raising the needed capabilities.

BugLink: http://bugs.launchpad.net/bugs/1531747
BugLink: http://bugs.launchpad.net/bugs/1534961
BugLink: http://bugs.launchpad.net/bugs/1535150Signed-off-by: default avatarSeth Forshee <seth.forshee@canonical.com>
Signed-off-by: default avatarAndy Whitcroft <apw@canonical.com>
parent 30c0ff60
......@@ -331,26 +331,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;
......
......@@ -12,6 +12,7 @@
#include <linux/xattr.h>
#include <linux/security.h>
#include <linux/cred.h>
#include <linux/sched.h>
#include "overlayfs.h"
void ovl_cleanup(struct inode *wdir, struct dentry *wdentry)
......@@ -349,6 +350,11 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
struct inode *wdir = workdir->d_inode;
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
struct inode *udir = upperdir->d_inode;
struct iattr attr = {
.ia_valid = ATTR_UID | ATTR_GID,
.ia_uid = stat->uid,
.ia_gid = stat->gid,
};
struct dentry *upper;
struct dentry *newdentry;
int err;
......@@ -374,6 +380,11 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
err = ovl_create_real(wdir, newdentry, stat, link, hardlink, true);
if (err)
goto out_dput2;
mutex_lock(&newdentry->d_inode->i_mutex);
err = notify_change(newdentry, &attr, NULL);
mutex_unlock(&newdentry->d_inode->i_mutex);
if (err)
goto out_cleanup;
if (S_ISDIR(stat->mode)) {
err = ovl_set_opaque(newdentry);
......@@ -418,6 +429,8 @@ static int ovl_create_or_link(struct dentry *dentry, int mode, dev_t rdev,
struct kstat stat = {
.mode = mode,
.rdev = rdev,
.uid = current->cred->fsuid,
.gid = current->cred->fsgid,
};
err = -ENOMEM;
......@@ -436,18 +449,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 +692,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 +855,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);
}
......
......@@ -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);
......
......@@ -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,
......
......@@ -65,6 +65,27 @@ 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;
return new_cred;
}
static struct dentry *__ovl_dentry_lower(struct ovl_entry *oe)
{
return oe->numlower ? oe->lowerstack[0].dentry : NULL;
......
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