summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2017-01-31 12:06:16 -0500
committerEric W. Biederman <ebiederm@xmission.com>2017-02-01 10:36:12 -0500
commit93faccbbfa958a9668d3ab4e30f38dd205cee8d8 (patch)
treef4a102b92d86d19a52a88a57bdde19ad25250ca1
parentc6c70f4455d1eda91065e93cc4f7eddf4499b105 (diff)
fs: Better permission checking for submounts
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. Cc: stable@vger.kernel.org Fixes: 069d5ac9ae0d ("autofs: Fix automounts by using current_real_cred()->uid") Fixes: aeaa4a79ff6a ("fs: Call d_automount with the filesystems creds") Reviewed-by: Trond Myklebust <trond.myklebust@primarydata.com> Reviewed-by: Seth Forshee <seth.forshee@canonical.com> Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
-rw-r--r--fs/afs/mntpt.c2
-rw-r--r--fs/autofs4/waitq.c4
-rw-r--r--fs/cifs/cifs_dfs_ref.c7
-rw-r--r--fs/debugfs/inode.c8
-rw-r--r--fs/namei.c3
-rw-r--r--fs/namespace.c17
-rw-r--r--fs/nfs/namespace.c2
-rw-r--r--fs/nfs/nfs4namespace.c2
-rw-r--r--fs/super.c13
-rw-r--r--include/linux/debugfs.h3
-rw-r--r--include/linux/mount.h3
-rw-r--r--include/uapi/linux/fs.h1
-rw-r--r--kernel/trace/trace.c4
13 files changed, 47 insertions, 22 deletions
diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c
index 81dd075356b9..d4fb0afc0097 100644
--- a/fs/afs/mntpt.c
+++ b/fs/afs/mntpt.c
@@ -202,7 +202,7 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt)
202 202
203 /* try and do the mount */ 203 /* try and do the mount */
204 _debug("--- attempting mount %s -o %s ---", devname, options); 204 _debug("--- attempting mount %s -o %s ---", devname, options);
205 mnt = vfs_kern_mount(&afs_fs_type, 0, devname, options); 205 mnt = vfs_submount(mntpt, &afs_fs_type, devname, options);
206 _debug("--- mount result %p ---", mnt); 206 _debug("--- mount result %p ---", mnt);
207 207
208 free_page((unsigned long) devname); 208 free_page((unsigned long) devname);
diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c
index 1278335ce366..79fbd85db4ba 100644
--- a/fs/autofs4/waitq.c
+++ b/fs/autofs4/waitq.c
@@ -436,8 +436,8 @@ int autofs4_wait(struct autofs_sb_info *sbi,
436 memcpy(&wq->name, &qstr, sizeof(struct qstr)); 436 memcpy(&wq->name, &qstr, sizeof(struct qstr));
437 wq->dev = autofs4_get_dev(sbi); 437 wq->dev = autofs4_get_dev(sbi);
438 wq->ino = autofs4_get_ino(sbi); 438 wq->ino = autofs4_get_ino(sbi);
439 wq->uid = current_real_cred()->uid; 439 wq->uid = current_cred()->uid;
440 wq->gid = current_real_cred()->gid; 440 wq->gid = current_cred()->gid;
441 wq->pid = pid; 441 wq->pid = pid;
442 wq->tgid = tgid; 442 wq->tgid = tgid;
443 wq->status = -EINTR; /* Status return if interrupted */ 443 wq->status = -EINTR; /* Status return if interrupted */
diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
index ec9dbbcca3b9..9156be545b0f 100644
--- a/fs/cifs/cifs_dfs_ref.c
+++ b/fs/cifs/cifs_dfs_ref.c
@@ -245,7 +245,8 @@ compose_mount_options_err:
245 * @fullpath: full path in UNC format 245 * @fullpath: full path in UNC format
246 * @ref: server's referral 246 * @ref: server's referral
247 */ 247 */
248static struct vfsmount *cifs_dfs_do_refmount(struct cifs_sb_info *cifs_sb, 248static struct vfsmount *cifs_dfs_do_refmount(struct dentry *mntpt,
249 struct cifs_sb_info *cifs_sb,
249 const char *fullpath, const struct dfs_info3_param *ref) 250 const char *fullpath, const struct dfs_info3_param *ref)
250{ 251{
251 struct vfsmount *mnt; 252 struct vfsmount *mnt;
@@ -259,7 +260,7 @@ static struct vfsmount *cifs_dfs_do_refmount(struct cifs_sb_info *cifs_sb,
259 if (IS_ERR(mountdata)) 260 if (IS_ERR(mountdata))
260 return (struct vfsmount *)mountdata; 261 return (struct vfsmount *)mountdata;
261 262
262 mnt = vfs_kern_mount(&cifs_fs_type, 0, devname, mountdata); 263 mnt = vfs_submount(mntpt, &cifs_fs_type, devname, mountdata);
263 kfree(mountdata); 264 kfree(mountdata);
264 kfree(devname); 265 kfree(devname);
265 return mnt; 266 return mnt;
@@ -334,7 +335,7 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
334 mnt = ERR_PTR(-EINVAL); 335 mnt = ERR_PTR(-EINVAL);
335 break; 336 break;
336 } 337 }
337 mnt = cifs_dfs_do_refmount(cifs_sb, 338 mnt = cifs_dfs_do_refmount(mntpt, cifs_sb,
338 full_path, referrals + i); 339 full_path, referrals + i);
339 cifs_dbg(FYI, "%s: cifs_dfs_do_refmount:%s , mnt:%p\n", 340 cifs_dbg(FYI, "%s: cifs_dfs_do_refmount:%s , mnt:%p\n",
340 __func__, referrals[i].node_name, mnt); 341 __func__, referrals[i].node_name, mnt);
diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c
index f17fcf89e18e..1e30f74a9527 100644
--- a/fs/debugfs/inode.c
+++ b/fs/debugfs/inode.c
@@ -187,9 +187,9 @@ static const struct super_operations debugfs_super_operations = {
187 187
188static struct vfsmount *debugfs_automount(struct path *path) 188static struct vfsmount *debugfs_automount(struct path *path)
189{ 189{
190 struct vfsmount *(*f)(void *); 190 debugfs_automount_t f;
191 f = (struct vfsmount *(*)(void *))path->dentry->d_fsdata; 191 f = (debugfs_automount_t)path->dentry->d_fsdata;
192 return f(d_inode(path->dentry)->i_private); 192 return f(path->dentry, d_inode(path->dentry)->i_private);
193} 193}
194 194
195static const struct dentry_operations debugfs_dops = { 195static const struct dentry_operations debugfs_dops = {
@@ -504,7 +504,7 @@ EXPORT_SYMBOL_GPL(debugfs_create_dir);
504 */ 504 */
505struct dentry *debugfs_create_automount(const char *name, 505struct dentry *debugfs_create_automount(const char *name,
506 struct dentry *parent, 506 struct dentry *parent,
507 struct vfsmount *(*f)(void *), 507 debugfs_automount_t f,
508 void *data) 508 void *data)
509{ 509{
510 struct dentry *dentry = start_creating(name, parent); 510 struct dentry *dentry = start_creating(name, parent);
diff --git a/fs/namei.c b/fs/namei.c
index 6fa3e9138fe4..da689c9c005e 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1100,7 +1100,6 @@ static int follow_automount(struct path *path, struct nameidata *nd,
1100 bool *need_mntput) 1100 bool *need_mntput)
1101{ 1101{
1102 struct vfsmount *mnt; 1102 struct vfsmount *mnt;
1103 const struct cred *old_cred;
1104 int err; 1103 int err;
1105 1104
1106 if (!path->dentry->d_op || !path->dentry->d_op->d_automount) 1105 if (!path->dentry->d_op || !path->dentry->d_op->d_automount)
@@ -1129,9 +1128,7 @@ static int follow_automount(struct path *path, struct nameidata *nd,
1129 if (nd->total_link_count >= 40) 1128 if (nd->total_link_count >= 40)
1130 return -ELOOP; 1129 return -ELOOP;
1131 1130
1132 old_cred = override_creds(&init_cred);
1133 mnt = path->dentry->d_op->d_automount(path); 1131 mnt = path->dentry->d_op->d_automount(path);
1134 revert_creds(old_cred);
1135 if (IS_ERR(mnt)) { 1132 if (IS_ERR(mnt)) {
1136 /* 1133 /*
1137 * The filesystem is allowed to return -EISDIR here to indicate 1134 * The filesystem is allowed to return -EISDIR here to indicate
diff --git a/fs/namespace.c b/fs/namespace.c
index 487ba30bb5c6..089a6b23135a 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -989,6 +989,21 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
989} 989}
990EXPORT_SYMBOL_GPL(vfs_kern_mount); 990EXPORT_SYMBOL_GPL(vfs_kern_mount);
991 991
992struct vfsmount *
993vfs_submount(const struct dentry *mountpoint, struct file_system_type *type,
994 const char *name, void *data)
995{
996 /* Until it is worked out how to pass the user namespace
997 * through from the parent mount to the submount don't support
998 * unprivileged mounts with submounts.
999 */
1000 if (mountpoint->d_sb->s_user_ns != &init_user_ns)
1001 return ERR_PTR(-EPERM);
1002
1003 return vfs_kern_mount(type, MS_SUBMOUNT, name, data);
1004}
1005EXPORT_SYMBOL_GPL(vfs_submount);
1006
992static struct mount *clone_mnt(struct mount *old, struct dentry *root, 1007static struct mount *clone_mnt(struct mount *old, struct dentry *root,
993 int flag) 1008 int flag)
994{ 1009{
@@ -2794,7 +2809,7 @@ long do_mount(const char *dev_name, const char __user *dir_name,
2794 2809
2795 flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | MS_BORN | 2810 flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | MS_BORN |
2796 MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT | 2811 MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT |
2797 MS_STRICTATIME | MS_NOREMOTELOCK); 2812 MS_STRICTATIME | MS_NOREMOTELOCK | MS_SUBMOUNT);
2798 2813
2799 if (flags & MS_REMOUNT) 2814 if (flags & MS_REMOUNT)
2800 retval = do_remount(&path, flags & ~MS_REMOUNT, mnt_flags, 2815 retval = do_remount(&path, flags & ~MS_REMOUNT, mnt_flags,
diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c
index 5551e8ef67fd..e49d831c4e85 100644
--- a/fs/nfs/namespace.c
+++ b/fs/nfs/namespace.c
@@ -226,7 +226,7 @@ static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server,
226 const char *devname, 226 const char *devname,
227 struct nfs_clone_mount *mountdata) 227 struct nfs_clone_mount *mountdata)
228{ 228{
229 return vfs_kern_mount(&nfs_xdev_fs_type, 0, devname, mountdata); 229 return vfs_submount(mountdata->dentry, &nfs_xdev_fs_type, devname, mountdata);
230} 230}
231 231
232/** 232/**
diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c
index d21104912676..d8b040bd9814 100644
--- a/fs/nfs/nfs4namespace.c
+++ b/fs/nfs/nfs4namespace.c
@@ -279,7 +279,7 @@ static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
279 mountdata->hostname, 279 mountdata->hostname,
280 mountdata->mnt_path); 280 mountdata->mnt_path);
281 281
282 mnt = vfs_kern_mount(&nfs4_referral_fs_type, 0, page, mountdata); 282 mnt = vfs_submount(mountdata->dentry, &nfs4_referral_fs_type, page, mountdata);
283 if (!IS_ERR(mnt)) 283 if (!IS_ERR(mnt))
284 break; 284 break;
285 } 285 }
diff --git a/fs/super.c b/fs/super.c
index 1709ed029a2c..4185844f7a12 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -469,7 +469,7 @@ struct super_block *sget_userns(struct file_system_type *type,
469 struct super_block *old; 469 struct super_block *old;
470 int err; 470 int err;
471 471
472 if (!(flags & MS_KERNMOUNT) && 472 if (!(flags & (MS_KERNMOUNT|MS_SUBMOUNT)) &&
473 !(type->fs_flags & FS_USERNS_MOUNT) && 473 !(type->fs_flags & FS_USERNS_MOUNT) &&
474 !capable(CAP_SYS_ADMIN)) 474 !capable(CAP_SYS_ADMIN))
475 return ERR_PTR(-EPERM); 475 return ERR_PTR(-EPERM);
@@ -499,7 +499,7 @@ retry:
499 } 499 }
500 if (!s) { 500 if (!s) {
501 spin_unlock(&sb_lock); 501 spin_unlock(&sb_lock);
502 s = alloc_super(type, flags, user_ns); 502 s = alloc_super(type, (flags & ~MS_SUBMOUNT), user_ns);
503 if (!s) 503 if (!s)
504 return ERR_PTR(-ENOMEM); 504 return ERR_PTR(-ENOMEM);
505 goto retry; 505 goto retry;
@@ -540,8 +540,15 @@ struct super_block *sget(struct file_system_type *type,
540{ 540{
541 struct user_namespace *user_ns = current_user_ns(); 541 struct user_namespace *user_ns = current_user_ns();
542 542
543 /* We don't yet pass the user namespace of the parent
544 * mount through to here so always use &init_user_ns
545 * until that changes.
546 */
547 if (flags & MS_SUBMOUNT)
548 user_ns = &init_user_ns;
549
543 /* Ensure the requestor has permissions over the target filesystem */ 550 /* Ensure the requestor has permissions over the target filesystem */
544 if (!(flags & MS_KERNMOUNT) && !ns_capable(user_ns, CAP_SYS_ADMIN)) 551 if (!(flags & (MS_KERNMOUNT|MS_SUBMOUNT)) && !ns_capable(user_ns, CAP_SYS_ADMIN))
545 return ERR_PTR(-EPERM); 552 return ERR_PTR(-EPERM);
546 553
547 return sget_userns(type, test, set, flags, user_ns, data); 554 return sget_userns(type, test, set, flags, user_ns, data);
diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h
index 014cc564d1c4..233006be30aa 100644
--- a/include/linux/debugfs.h
+++ b/include/linux/debugfs.h
@@ -97,9 +97,10 @@ struct dentry *debugfs_create_dir(const char *name, struct dentry *parent);
97struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent, 97struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent,
98 const char *dest); 98 const char *dest);
99 99
100typedef struct vfsmount *(*debugfs_automount_t)(struct dentry *, void *);
100struct dentry *debugfs_create_automount(const char *name, 101struct dentry *debugfs_create_automount(const char *name,
101 struct dentry *parent, 102 struct dentry *parent,
102 struct vfsmount *(*f)(void *), 103 debugfs_automount_t f,
103 void *data); 104 void *data);
104 105
105void debugfs_remove(struct dentry *dentry); 106void debugfs_remove(struct dentry *dentry);
diff --git a/include/linux/mount.h b/include/linux/mount.h
index c6f55158d5e5..8e0352af06b7 100644
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -90,6 +90,9 @@ struct file_system_type;
90extern struct vfsmount *vfs_kern_mount(struct file_system_type *type, 90extern struct vfsmount *vfs_kern_mount(struct file_system_type *type,
91 int flags, const char *name, 91 int flags, const char *name,
92 void *data); 92 void *data);
93extern struct vfsmount *vfs_submount(const struct dentry *mountpoint,
94 struct file_system_type *type,
95 const char *name, void *data);
93 96
94extern void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list); 97extern void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list);
95extern void mark_mounts_for_expiry(struct list_head *mounts); 98extern void mark_mounts_for_expiry(struct list_head *mounts);
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index 36da93fbf188..048a85e9f017 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -132,6 +132,7 @@ struct inodes_stat_t {
132#define MS_LAZYTIME (1<<25) /* Update the on-disk [acm]times lazily */ 132#define MS_LAZYTIME (1<<25) /* Update the on-disk [acm]times lazily */
133 133
134/* These sb flags are internal to the kernel */ 134/* These sb flags are internal to the kernel */
135#define MS_SUBMOUNT (1<<26)
135#define MS_NOREMOTELOCK (1<<27) 136#define MS_NOREMOTELOCK (1<<27)
136#define MS_NOSEC (1<<28) 137#define MS_NOSEC (1<<28)
137#define MS_BORN (1<<29) 138#define MS_BORN (1<<29)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index d7449783987a..310f0ea0d1a2 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -7503,7 +7503,7 @@ init_tracer_tracefs(struct trace_array *tr, struct dentry *d_tracer)
7503 ftrace_init_tracefs(tr, d_tracer); 7503 ftrace_init_tracefs(tr, d_tracer);
7504} 7504}
7505 7505
7506static struct vfsmount *trace_automount(void *ingore) 7506static struct vfsmount *trace_automount(struct dentry *mntpt, void *ingore)
7507{ 7507{
7508 struct vfsmount *mnt; 7508 struct vfsmount *mnt;
7509 struct file_system_type *type; 7509 struct file_system_type *type;
@@ -7516,7 +7516,7 @@ static struct vfsmount *trace_automount(void *ingore)
7516 type = get_fs_type("tracefs"); 7516 type = get_fs_type("tracefs");
7517 if (!type) 7517 if (!type)
7518 return NULL; 7518 return NULL;
7519 mnt = vfs_kern_mount(type, 0, "tracefs", NULL); 7519 mnt = vfs_submount(mntpt, type, "tracefs", NULL);
7520 put_filesystem(type); 7520 put_filesystem(type);
7521 if (IS_ERR(mnt)) 7521 if (IS_ERR(mnt))
7522 return NULL; 7522 return NULL;