Commit d91ee87d authored by Eric W. Biederman's avatar Eric W. Biederman

vfs: Pass data, ns, and ns->userns to mount_ns

Today what is normally called data (the mount options) is not passed
to fill_super through mount_ns.

Pass the mount options and the namespace separately to mount_ns so
that filesystems such as proc that have mount options, can use
mount_ns.

Pass the user namespace to mount_ns so that the standard permission
check that verifies the mounter has permissions over the namespace can
be performed in mount_ns instead of in each filesystems .mount method.
Thus removing the duplication between mqueuefs and proc in terms of
permission checks.  The extra permission check does not currently
affect the rpc_pipefs filesystem and the nfsd filesystem as those
filesystems do not currently allow unprivileged mounts.  Without
unpvileged mounts it is guaranteed that the caller has already passed
capable(CAP_SYS_ADMIN) which guarantees extra permission check will
pass.

Update rpc_pipefs and the nfsd filesystem to ensure that the network
namespace reference is always taken in fill_super and always put in kill_sb
so that the logic is simpler and so that errors originating inside of
fill_super do not cause a network namespace leak.
Acked-by: default avatarSeth Forshee <seth.forshee@canonical.com>
Signed-off-by: default avatar"Eric W. Biederman" <ebiederm@xmission.com>
parent b236017a
...@@ -1154,20 +1154,15 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent) ...@@ -1154,20 +1154,15 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
#endif #endif
/* last one */ {""} /* last one */ {""}
}; };
struct net *net = data; get_net(sb->s_fs_info);
int ret; return simple_fill_super(sb, 0x6e667364, nfsd_files);
ret = simple_fill_super(sb, 0x6e667364, nfsd_files);
if (ret)
return ret;
sb->s_fs_info = get_net(net);
return 0;
} }
static struct dentry *nfsd_mount(struct file_system_type *fs_type, static struct dentry *nfsd_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data) int flags, const char *dev_name, void *data)
{ {
return mount_ns(fs_type, flags, current->nsproxy->net_ns, nfsd_fill_super); struct net *net = current->nsproxy->net_ns;
return mount_ns(fs_type, flags, data, net, net->user_ns, nfsd_fill_super);
} }
static void nfsd_umount(struct super_block *sb) static void nfsd_umount(struct super_block *sb)
......
...@@ -918,12 +918,19 @@ static int ns_set_super(struct super_block *sb, void *data) ...@@ -918,12 +918,19 @@ static int ns_set_super(struct super_block *sb, void *data)
return set_anon_super(sb, NULL); return set_anon_super(sb, NULL);
} }
struct dentry *mount_ns(struct file_system_type *fs_type, int flags, struct dentry *mount_ns(struct file_system_type *fs_type,
void *data, int (*fill_super)(struct super_block *, void *, int)) int flags, void *data, void *ns, struct user_namespace *user_ns,
int (*fill_super)(struct super_block *, void *, int))
{ {
struct super_block *sb; struct super_block *sb;
sb = sget(fs_type, ns_test_super, ns_set_super, flags, data); /* Don't allow mounting unless the caller has CAP_SYS_ADMIN
* over the namespace.
*/
if (!(flags & MS_KERNMOUNT) && !ns_capable(user_ns, CAP_SYS_ADMIN))
return ERR_PTR(-EPERM);
sb = sget(fs_type, ns_test_super, ns_set_super, flags, ns);
if (IS_ERR(sb)) if (IS_ERR(sb))
return ERR_CAST(sb); return ERR_CAST(sb);
......
...@@ -2034,8 +2034,9 @@ struct file_system_type { ...@@ -2034,8 +2034,9 @@ struct file_system_type {
#define MODULE_ALIAS_FS(NAME) MODULE_ALIAS("fs-" NAME) #define MODULE_ALIAS_FS(NAME) MODULE_ALIAS("fs-" NAME)
extern struct dentry *mount_ns(struct file_system_type *fs_type, int flags, extern struct dentry *mount_ns(struct file_system_type *fs_type,
void *data, int (*fill_super)(struct super_block *, void *, int)); int flags, void *data, void *ns, struct user_namespace *user_ns,
int (*fill_super)(struct super_block *, void *, int));
extern struct dentry *mount_bdev(struct file_system_type *fs_type, extern struct dentry *mount_bdev(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data, int flags, const char *dev_name, void *data,
int (*fill_super)(struct super_block *, void *, int)); int (*fill_super)(struct super_block *, void *, int));
......
...@@ -305,7 +305,7 @@ static struct inode *mqueue_get_inode(struct super_block *sb, ...@@ -305,7 +305,7 @@ static struct inode *mqueue_get_inode(struct super_block *sb,
static int mqueue_fill_super(struct super_block *sb, void *data, int silent) static int mqueue_fill_super(struct super_block *sb, void *data, int silent)
{ {
struct inode *inode; struct inode *inode;
struct ipc_namespace *ns = data; struct ipc_namespace *ns = sb->s_fs_info;
sb->s_blocksize = PAGE_SIZE; sb->s_blocksize = PAGE_SIZE;
sb->s_blocksize_bits = PAGE_SHIFT; sb->s_blocksize_bits = PAGE_SHIFT;
...@@ -326,17 +326,14 @@ static struct dentry *mqueue_mount(struct file_system_type *fs_type, ...@@ -326,17 +326,14 @@ static struct dentry *mqueue_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, int flags, const char *dev_name,
void *data) void *data)
{ {
if (!(flags & MS_KERNMOUNT)) { struct ipc_namespace *ns;
struct ipc_namespace *ns = current->nsproxy->ipc_ns; if (flags & MS_KERNMOUNT) {
/* Don't allow mounting unless the caller has CAP_SYS_ADMIN ns = data;
* over the ipc namespace. data = NULL;
*/ } else {
if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN)) ns = current->nsproxy->ipc_ns;
return ERR_PTR(-EPERM);
data = ns;
} }
return mount_ns(fs_type, flags, data, mqueue_fill_super); return mount_ns(fs_type, flags, data, ns, ns->user_ns, mqueue_fill_super);
} }
static void init_once(void *foo) static void init_once(void *foo)
......
...@@ -1386,7 +1386,7 @@ rpc_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1386,7 +1386,7 @@ rpc_fill_super(struct super_block *sb, void *data, int silent)
{ {
struct inode *inode; struct inode *inode;
struct dentry *root, *gssd_dentry; struct dentry *root, *gssd_dentry;
struct net *net = data; struct net *net = get_net(sb->s_fs_info);
struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
int err; int err;
...@@ -1419,7 +1419,6 @@ rpc_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1419,7 +1419,6 @@ rpc_fill_super(struct super_block *sb, void *data, int silent)
sb); sb);
if (err) if (err)
goto err_depopulate; goto err_depopulate;
sb->s_fs_info = get_net(net);
mutex_unlock(&sn->pipefs_sb_lock); mutex_unlock(&sn->pipefs_sb_lock);
return 0; return 0;
...@@ -1448,7 +1447,8 @@ static struct dentry * ...@@ -1448,7 +1447,8 @@ static struct dentry *
rpc_mount(struct file_system_type *fs_type, rpc_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data) int flags, const char *dev_name, void *data)
{ {
return mount_ns(fs_type, flags, current->nsproxy->net_ns, rpc_fill_super); struct net *net = current->nsproxy->net_ns;
return mount_ns(fs_type, flags, data, net, net->user_ns, rpc_fill_super);
} }
static void rpc_kill_sb(struct super_block *sb) static void rpc_kill_sb(struct super_block *sb)
...@@ -1468,9 +1468,9 @@ static void rpc_kill_sb(struct super_block *sb) ...@@ -1468,9 +1468,9 @@ static void rpc_kill_sb(struct super_block *sb)
RPC_PIPEFS_UMOUNT, RPC_PIPEFS_UMOUNT,
sb); sb);
mutex_unlock(&sn->pipefs_sb_lock); mutex_unlock(&sn->pipefs_sb_lock);
put_net(net);
out: out:
kill_litter_super(sb); kill_litter_super(sb);
put_net(net);
} }
static struct file_system_type rpc_pipe_fs_type = { static struct file_system_type rpc_pipe_fs_type = {
......
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