CVE-2015-2925 Don't allow bind mount escape (rhbz 1209367 1209373)
This commit is contained in:
parent
7f6344cc34
commit
3c15d10e1c
|
@ -0,0 +1,65 @@
|
|||
From 0e9ff3b71d0b2866f8f4ce408043f3f06792aad3 Mon Sep 17 00:00:00 2001
|
||||
From: "Eric W. Biederman" <ebiederm@xmission.com>
|
||||
Date: Sat, 15 Aug 2015 13:36:12 -0500
|
||||
Subject: [PATCH 1/2] dcache: Handle escaped paths in prepend_path
|
||||
|
||||
commit cde93be45a8a90d8c264c776fab63487b5038a65 upstream.
|
||||
|
||||
A rename can result in a dentry that by walking up d_parent
|
||||
will never reach it's mnt_root. For lack of a better term
|
||||
I call this an escaped path.
|
||||
|
||||
prepend_path is called by four different functions __d_path,
|
||||
d_absolute_path, d_path, and getcwd.
|
||||
|
||||
__d_path only wants to see paths are connected to the root it passes
|
||||
in. So __d_path needs prepend_path to return an error.
|
||||
|
||||
d_absolute_path similarly wants to see paths that are connected to
|
||||
some root. Escaped paths are not connected to any mnt_root so
|
||||
d_absolute_path needs prepend_path to return an error greater
|
||||
than 1. So escaped paths will be treated like paths on lazily
|
||||
unmounted mounts.
|
||||
|
||||
getcwd needs to prepend "(unreachable)" so getcwd also needs
|
||||
prepend_path to return an error.
|
||||
|
||||
d_path is the interesting hold out. d_path just wants to print
|
||||
something, and does not care about the weird cases. Which raises
|
||||
the question what should be printed?
|
||||
|
||||
Given that <escaped_path>/<anything> should result in -ENOENT I
|
||||
believe it is desirable for escaped paths to be printed as empty
|
||||
paths. As there are not really any meaninful path components when
|
||||
considered from the perspective of a mount tree.
|
||||
|
||||
So tweak prepend_path to return an empty path with an new error
|
||||
code of 3 when it encounters an escaped path.
|
||||
|
||||
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
|
||||
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
|
||||
---
|
||||
fs/dcache.c | 7 +++++++
|
||||
1 file changed, 7 insertions(+)
|
||||
|
||||
diff --git a/fs/dcache.c b/fs/dcache.c
|
||||
index 5d03eb0ec0ac..2e8ddc1d09e9 100644
|
||||
--- a/fs/dcache.c
|
||||
+++ b/fs/dcache.c
|
||||
@@ -2923,6 +2923,13 @@ restart:
|
||||
|
||||
if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
|
||||
struct mount *parent = ACCESS_ONCE(mnt->mnt_parent);
|
||||
+ /* Escaped? */
|
||||
+ if (dentry != vfsmnt->mnt_root) {
|
||||
+ bptr = *buffer;
|
||||
+ blen = *buflen;
|
||||
+ error = 3;
|
||||
+ break;
|
||||
+ }
|
||||
/* Global root? */
|
||||
if (mnt != parent) {
|
||||
dentry = ACCESS_ONCE(mnt->mnt_mountpoint);
|
||||
--
|
||||
2.4.3
|
||||
|
11
kernel.spec
11
kernel.spec
|
@ -659,6 +659,10 @@ Patch526: 0001-x86-cpu-cacheinfo-Fix-teardown-path.patch
|
|||
#CVE-2015-5257 rhbz 1265607 1265612
|
||||
Patch527: USB-whiteheat-fix-potential-null-deref-at-probe.patch
|
||||
|
||||
#CVE-2015-2925 rhbz 1209367 1209373
|
||||
Patch528: dcache-Handle-escaped-paths-in-prepend_path.patch
|
||||
Patch529: vfs-Test-for-and-handle-paths-that-are-unreachable-f.patch
|
||||
|
||||
# END OF PATCH DEFINITIONS
|
||||
|
||||
%endif
|
||||
|
@ -1424,6 +1428,10 @@ ApplyPatch 0001-x86-cpu-cacheinfo-Fix-teardown-path.patch
|
|||
#CVE-2015-5257 rhbz 1265607 1265612
|
||||
ApplyPatch USB-whiteheat-fix-potential-null-deref-at-probe.patch
|
||||
|
||||
#CVE-2015-2925 rhbz 1209367 1209373
|
||||
ApplyPatch dcache-Handle-escaped-paths-in-prepend_path.patch
|
||||
ApplyPatch vfs-Test-for-and-handle-paths-that-are-unreachable-f.patch
|
||||
|
||||
# END OF PATCH APPLICATIONS
|
||||
|
||||
%endif
|
||||
|
@ -2283,6 +2291,9 @@ fi
|
|||
# ||----w |
|
||||
# || ||
|
||||
%changelog
|
||||
* Thu Oct 01 2015 Josh Boyer <jwboyer@fedoraproject.org>
|
||||
- CVE-2015-2925 Don't allow bind mount escape (rhbz 1209367 1209373)
|
||||
|
||||
* Tue Sep 29 2015 Josh Boyer <jwboyer@fedoraproject.org> - 4.1.9-100
|
||||
- Linux v4.1.9
|
||||
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
From 74038ebc44da6e3f9c918f2525f9111e10c8efc2 Mon Sep 17 00:00:00 2001
|
||||
From: "Eric W. Biederman" <ebiederm@xmission.com>
|
||||
Date: Sat, 15 Aug 2015 20:27:13 -0500
|
||||
Subject: [PATCH 2/2] vfs: Test for and handle paths that are unreachable from
|
||||
their mnt_root
|
||||
|
||||
commit 397d425dc26da728396e66d392d5dcb8dac30c37 upstream.
|
||||
|
||||
In rare cases a directory can be renamed out from under a bind mount.
|
||||
In those cases without special handling it becomes possible to walk up
|
||||
the directory tree to the root dentry of the filesystem and down
|
||||
from the root dentry to every other file or directory on the filesystem.
|
||||
|
||||
Like division by zero .. from an unconnected path can not be given
|
||||
a useful semantic as there is no predicting at which path component
|
||||
the code will realize it is unconnected. We certainly can not match
|
||||
the current behavior as the current behavior is a security hole.
|
||||
|
||||
Therefore when encounting .. when following an unconnected path
|
||||
return -ENOENT.
|
||||
|
||||
- Add a function path_connected to verify path->dentry is reachable
|
||||
from path->mnt.mnt_root. AKA to validate that rename did not do
|
||||
something nasty to the bind mount.
|
||||
|
||||
To avoid races path_connected must be called after following a path
|
||||
component to it's next path component.
|
||||
|
||||
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
|
||||
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
|
||||
---
|
||||
fs/namei.c | 31 ++++++++++++++++++++++++++++---
|
||||
1 file changed, 28 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/fs/namei.c b/fs/namei.c
|
||||
index fe30d3be43a8..acdab610521b 100644
|
||||
--- a/fs/namei.c
|
||||
+++ b/fs/namei.c
|
||||
@@ -505,6 +505,24 @@ struct nameidata {
|
||||
char *saved_names[MAX_NESTED_LINKS + 1];
|
||||
};
|
||||
|
||||
+/**
|
||||
+ * path_connected - Verify that a path->dentry is below path->mnt.mnt_root
|
||||
+ * @path: nameidate to verify
|
||||
+ *
|
||||
+ * Rename can sometimes move a file or directory outside of a bind
|
||||
+ * mount, path_connected allows those cases to be detected.
|
||||
+ */
|
||||
+static bool path_connected(const struct path *path)
|
||||
+{
|
||||
+ struct vfsmount *mnt = path->mnt;
|
||||
+
|
||||
+ /* Only bind mounts can have disconnected paths */
|
||||
+ if (mnt->mnt_root == mnt->mnt_sb->s_root)
|
||||
+ return true;
|
||||
+
|
||||
+ return is_subdir(path->dentry, mnt->mnt_root);
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Path walking has 2 modes, rcu-walk and ref-walk (see
|
||||
* Documentation/filesystems/path-lookup.txt). In situations when we can't
|
||||
@@ -1194,6 +1212,8 @@ static int follow_dotdot_rcu(struct nameidata *nd)
|
||||
goto failed;
|
||||
nd->path.dentry = parent;
|
||||
nd->seq = seq;
|
||||
+ if (unlikely(!path_connected(&nd->path)))
|
||||
+ goto failed;
|
||||
break;
|
||||
}
|
||||
if (!follow_up_rcu(&nd->path))
|
||||
@@ -1290,7 +1310,7 @@ static void follow_mount(struct path *path)
|
||||
}
|
||||
}
|
||||
|
||||
-static void follow_dotdot(struct nameidata *nd)
|
||||
+static int follow_dotdot(struct nameidata *nd)
|
||||
{
|
||||
if (!nd->root.mnt)
|
||||
set_root(nd);
|
||||
@@ -1306,6 +1326,10 @@ static void follow_dotdot(struct nameidata *nd)
|
||||
/* rare case of legitimate dget_parent()... */
|
||||
nd->path.dentry = dget_parent(nd->path.dentry);
|
||||
dput(old);
|
||||
+ if (unlikely(!path_connected(&nd->path))) {
|
||||
+ path_put(&nd->path);
|
||||
+ return -ENOENT;
|
||||
+ }
|
||||
break;
|
||||
}
|
||||
if (!follow_up(&nd->path))
|
||||
@@ -1313,6 +1337,7 @@ static void follow_dotdot(struct nameidata *nd)
|
||||
}
|
||||
follow_mount(&nd->path);
|
||||
nd->inode = nd->path.dentry->d_inode;
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1541,7 +1566,7 @@ static inline int handle_dots(struct nameidata *nd, int type)
|
||||
if (follow_dotdot_rcu(nd))
|
||||
return -ECHILD;
|
||||
} else
|
||||
- follow_dotdot(nd);
|
||||
+ return follow_dotdot(nd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -2290,7 +2315,7 @@ mountpoint_last(struct nameidata *nd, struct path *path)
|
||||
if (unlikely(nd->last_type != LAST_NORM)) {
|
||||
error = handle_dots(nd, nd->last_type);
|
||||
if (error)
|
||||
- goto out;
|
||||
+ return error;
|
||||
dentry = dget(nd->path.dentry);
|
||||
goto done;
|
||||
}
|
||||
--
|
||||
2.4.3
|
||||
|
Loading…
Reference in New Issue