Patch "fs: Better permission checking for submounts" has been added to the 4.9-stable tree
Subsystems (6)
This is a note to let you know that I've just added the patch titled
fs: Better permission checking for submounts
to the 4.9-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
The filename of the patch is:
fs-better-permission-checking-for-submounts.patch
and it can be found in the queue-4.9 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let <[email protected]> know about it.
Quoted 1 line
From
93faccbbfa958a9668d3ab4e30f38dd205cee8d8Mon Sep 17 00:00:00 2001
From: "Eric W. Biederman" <[email protected]>
Date: Wed, 1 Feb 2017 06:06:16 +1300
Subject: fs: Better permission checking for submounts
From: Eric W. Biederman <[email protected]>
commit 93faccbbfa958a9668d3ab4e30f38dd205cee8d8 upstream.
To support unprivileged users mounting filesystems two permission checks have to be performed: a test to see if the user allowed to create a mount in the mount namespace, and a test to see if the user is allowed to access the specified filesystem.
The automount case is special in that mounting the original filesystem grants permission to mount the sub-filesystems, to any user who happens to stumble across the their mountpoint and satisfies the ordinary filesystem permission checks.
Attempting to handle the automount case by using override_creds almost works. It preserves the idea that permission to mount the original filesystem is permission to mount the sub-filesystem. Unfortunately using override_creds messes up the filesystems ordinary permission checks.
Solve this by being explicit that a mount is a submount by introducing vfs_submount, and using it where appropriate.
vfs_submount uses a new mount internal mount flags MS_SUBMOUNT, to let sget and friends know that a mount is a submount so they can take appropriate action.
sget and sget_userns are modified to not perform any permission checks on submounts.
follow_automount is modified to stop using override_creds as that has proven problemantic.
do_mount is modified to always remove the new MS_SUBMOUNT flag so that we know userspace will never by able to specify it.
autofs4 is modified to stop using current_real_cred that was put in there to handle the previous version of submount permission checking.
cifs is modified to pass the mountpoint all of the way down to vfs_submount.
debugfs is modified to pass the mountpoint all of the way down to trace_automount by adding a new parameter. To make this change easier a new typedef debugfs_automount_t is introduced to capture the type of the debugfs automount function.
--- fs/afs/mntpt.c | 2 +- fs/autofs4/waitq.c | 4 ++-- fs/cifs/cifs_dfs_ref.c | 7 ++++--- fs/debugfs/inode.c | 8 ++++---- fs/namei.c | 3 --- fs/namespace.c | 17 ++++++++++++++++- fs/nfs/namespace.c | 2 +- fs/nfs/nfs4namespace.c | 2 +- fs/super.c | 13 ++++++++++--- include/linux/debugfs.h | 3 ++- include/linux/mount.h | 3 +++ include/uapi/linux/fs.h | 1 + kernel/trace/trace.c | 4 ++-- 13 files changed, 47 insertions(+), 22 deletions(-)
fs/afs/mntpt.c +1 -1
@@ -202,7 +202,7 @@ static struct vfsmount *afs_mntpt_do_aut/* try and do the mount */ _debug ("--- attempting mount %s -o %s ---" , devname, options); - mnt =vfs_kern_mount (&afs_fs_type,0 , devname, options); + mnt =vfs_submount (mntpt, &afs_fs_type, devname, options);_debug ("--- mount result %p ---" , mnt);free_page ((unsigned long ) devname);
fs/autofs4/waitq.c +2 -2
@@ -431,8 +431,8 @@ int autofs4_wait(struct autofs_sb_info *memcpy (&wq->name, &qstr,sizeof (struct qstr )); wq->dev =autofs4_get_dev (sbi); wq->ino =autofs4_get_ino (sbi); - wq->uid =current_real_cred ()->uid; - wq->gid =current_real_cred ()->gid; + wq->uid =current_cred ()->uid; + wq->gid =current_cred ()->gid; wq->pid = pid; wq->tgid = tgid; wq->status = -EINTR ;/* Status return if interrupted */
fs/cifs/cifs_dfs_ref.c +4 -3
@@ -245,7 +245,8 @@ compose_mount_options_err:* @fullpath: full path in UNC format * @ref: server's referral */ -static struct vfsmount *cifs_dfs_do_refmount (struct cifs_sb_info *cifs_sb, +static struct vfsmount *cifs_dfs_do_refmount (struct dentry *mntpt, +struct cifs_sb_info *cifs_sb,const char *fullpath,const struct dfs_info3_param *ref) {struct vfsmount *mnt;
@@ -259,7 +260,7 @@ static struct vfsmount *cifs_dfs_do_refmif (IS_ERR (mountdata))return (struct vfsmount *)mountdata; - mnt =vfs_kern_mount (&cifs_fs_type,0 , devname, mountdata); + mnt =vfs_submount (mntpt, &cifs_fs_type, devname, mountdata);kfree (mountdata);kfree (devname);return mnt;
@@ -334,7 +335,7 @@ static struct vfsmount *cifs_dfs_do_auto mnt =ERR_PTR (-EINVAL );break ; } - mnt =cifs_dfs_do_refmount (cifs_sb, + mnt =cifs_dfs_do_refmount (mntpt, cifs_sb, full_path, referrals + i);cifs_dbg (FYI ,"%s: cifs_dfs_do_refmount:%s , mnt:%p\n" , __func__, referrals[i].node_name, mnt);
fs/debugfs/inode.c +4 -4
@@ -187,9 +187,9 @@ static const struct super_operations debstatic struct vfsmount *debugfs_automount (struct path *path) { -struct vfsmount *(*f)(void *); - f = (struct vfsmount *(*)(void *))path->dentry->d_fsdata; -return f (d_inode (path->dentry)->i_private); +debugfs_automount_t f; + f = (debugfs_automount_t )path->dentry->d_fsdata; +return f (path->dentry,d_inode (path->dentry)->i_private); }static const struct dentry_operations debugfs_dops = {
@@ -504,7 +504,7 @@ EXPORT_SYMBOL_GPL(debugfs_create_dir);*/ struct dentry *debugfs_create_automount (const char *name,struct dentry *parent, -struct vfsmount *(*f)(void *), +debugfs_automount_t f,void *data) {struct dentry *dentry =start_creating (name, parent);
fs/namei.c +0 -3
@@ -1100,7 +1100,6 @@ static int follow_automount(struct path bool *need_mntput) {struct vfsmount *mnt; -const struct cred *old_cred;int err;if (!path->dentry->d_op || !path->dentry->d_op->d_automount)
@@ -1129,9 +1128,7 @@ static int follow_automount(struct pathif (nd->total_link_count >=40 )return -ELOOP ; - old_cred =override_creds (&init_cred); mnt = path->dentry->d_op->d_automount (path); -revert_creds (old_cred);if (IS_ERR (mnt)) {/* * The filesystem is allowed to return -EISDIR here to indicate
fs/namespace.c +16 -1
@@ -995,6 +995,21 @@ vfs_kern_mount(struct file_system_type * }EXPORT_SYMBOL_GPL (vfs_kern_mount); +struct vfsmount * +vfs_submount (const struct dentry *mountpoint,struct file_system_type *type, +const char *name,void *data) +{ +/* Until it is worked out how to pass the user namespace +* through from the parent mount to the submount don't support +* unprivileged mounts with submounts. +*/ +if (mountpoint->d_sb->s_user_ns != &init_user_ns) +return ERR_PTR (-EPERM ); + +return vfs_kern_mount (type,MS_SUBMOUNT , name, data); +} +EXPORT_SYMBOL_GPL (vfs_submount); +static struct mount *clone_mnt (struct mount *old,struct dentry *root,int flag) {
@@ -2779,7 +2794,7 @@ long do_mount(const char *dev_name, cons flags &= ~(MS_NOSUID |MS_NOEXEC |MS_NODEV |MS_ACTIVE |MS_BORN |MS_NOATIME |MS_NODIRATIME |MS_RELATIME |MS_KERNMOUNT | -MS_STRICTATIME |MS_NOREMOTELOCK ); +MS_STRICTATIME |MS_NOREMOTELOCK |MS_SUBMOUNT );if (flags &MS_REMOUNT ) retval = do_remount(&path, flags & ~MS_REMOUNT, mnt_flags,
fs/nfs/namespace.c +1 -1
@@ -226,7 +226,7 @@ static struct vfsmount *nfs_do_clone_mouconst char *devname, struct nfs_clone_mount *mountdata) { - return vfs_kern_mount(&nfs_xdev_fs_type, 0, devname, mountdata); + return vfs_submount(mountdata->dentry, &nfs_xdev_fs_type, devname, mountdata); }/**
fs/nfs/nfs4namespace.c +1 -1
@@ -279,7 +279,7 @@ static struct vfsmount *try_location(str mountdata->hostname, mountdata->mnt_path); - mnt =vfs_kern_mount (&nfs4_referral_fs_type,0 , page, mountdata); + mnt =vfs_submount (mountdata->dentry, &nfs4_referral_fs_type, page, mountdata);if (!IS_ERR (mnt))break ; }
fs/super.c +10 -3
@@ -470,7 +470,7 @@ struct super_block *sget_userns(struct fstruct super_block *old;int err; -if (!(flags &MS_KERNMOUNT ) && +if (!(flags & (MS_KERNMOUNT |MS_SUBMOUNT )) && !(type->fs_flags &FS_USERNS_MOUNT ) && !capable (CAP_SYS_ADMIN ))return ERR_PTR (-EPERM );
@@ -500,7 +500,7 @@ retry: }if (!s) {spin_unlock (&sb_lock); - s =alloc_super (type, flags, user_ns); + s =alloc_super (type, (flags & ~MS_SUBMOUNT ), user_ns);if (!s)return ERR_PTR (-ENOMEM ); goto retry;
@@ -541,8 +541,15 @@ struct super_block *sget(struct file_sys {struct user_namespace *user_ns =current_user_ns (); +/* We don't yet pass the user namespace of the parent +* mount through to here so always use &init_user_ns +* until that changes. +*/ +if (flags &MS_SUBMOUNT ) + user_ns = &init_user_ns; +/* Ensure the requestor has permissions over the target filesystem */ -if (!(flags &MS_KERNMOUNT ) && !ns_capable (user_ns,CAP_SYS_ADMIN )) +if (!(flags & (MS_KERNMOUNT |MS_SUBMOUNT )) && !ns_capable (user_ns,CAP_SYS_ADMIN ))return ERR_PTR (-EPERM );return sget_userns (type, test, set, flags, user_ns, data);
include/linux/debugfs.h +2 -1
@@ -96,9 +96,10 @@ struct dentry *debugfs_create_dir(conststruct dentry *debugfs_create_symlink (const char *name,struct dentry *parent,const char *dest); +typedef struct vfsmount *(*debugfs_automount_t )(struct dentry *,void *);struct dentry *debugfs_create_automount (const char *name,struct dentry *parent, -struct vfsmount *(*f)(void *), +debugfs_automount_t f,void *data);void debugfs_remove (struct dentry *dentry);
include/linux/mount.h +3 -0
@@ -90,6 +90,9 @@ struct file_system_type;extern struct vfsmount *vfs_kern_mount (struct file_system_type *type,int flags,const char *name,void *data); +extern struct vfsmount *vfs_submount (const struct dentry *mountpoint, +struct file_system_type *type, +const char *name,void *data);extern void mnt_set_expiry (struct vfsmount *mnt,struct list_head *expiry_list);extern void mark_mounts_for_expiry (struct list_head *mounts);
include/uapi/linux/fs.h +1 -0
@@ -132,6 +132,7 @@ struct inodes_stat_t {#define MS_LAZYTIME (1<<25)/* Update the on-disk [acm]times lazily */ /* These sb flags are internal to the kernel */ +#define MS_SUBMOUNT (1<<26)#define MS_NOREMOTELOCK (1<<27)#define MS_NOSEC (1<<28)#define MS_BORN (1<<29)
kernel/trace/trace.c +2 -2
@@ -7241,7 +7241,7 @@ init_tracer_tracefs(struct trace_array *ftrace_init_tracefs (tr, d_tracer); } -static struct vfsmount *trace_automount (void *ingore) +static struct vfsmount *trace_automount (struct dentry *mntpt,void *ingore) {struct vfsmount *mnt;struct file_system_type *type;
@@ -7254,7 +7254,7 @@ static struct vfsmount *trace_automount( type =get_fs_type ("tracefs" );if (!type)return NULL ; - mnt =vfs_kern_mount (type,0 ,"tracefs" ,NULL ); + mnt =vfs_submount (mntpt, type,"tracefs" ,NULL );put_filesystem (type);if (IS_ERR (mnt))return NULL ;
Patches currently in stable-queue which might be from [email protected] are
queue-4.9/mnt-tuck-mounts-under-others-instead-of-creating-shadow-side-mounts.patch queue-4.9/fs-better-permission-checking-for-submounts.patch