fs: Update link security restrictions to match Linux 3.6:
- Drop kconfig options; restrictions can only be disabled by sysctl - Change the audit message type from AUDIT_AVC (1400) to AUDIT_ANON_LINK (1702) svn path=/dists/sid/linux/; revision=19310
This commit is contained in:
parent
b91d68d275
commit
4a4fd06c67
|
@ -57,6 +57,10 @@ linux (3.2.26-1) UNRELEASED; urgency=low
|
||||||
* net: Add byte queue limits (bql) for reduced buffer-bloat
|
* net: Add byte queue limits (bql) for reduced buffer-bloat
|
||||||
* bnx2,bnx2x,e1000e,forcedeth,igb,ixgbe,r8169,sfc,skge,sky2,tg3:
|
* bnx2,bnx2x,e1000e,forcedeth,igb,ixgbe,r8169,sfc,skge,sky2,tg3:
|
||||||
Add support for bql
|
Add support for bql
|
||||||
|
* fs: Update link security restrictions to match Linux 3.6:
|
||||||
|
- Drop kconfig options; restrictions can only be disabled by sysctl
|
||||||
|
- Change the audit message type from AUDIT_AVC (1400) to
|
||||||
|
AUDIT_ANON_LINK (1702)
|
||||||
|
|
||||||
-- Ben Hutchings <ben@decadent.org.uk> Tue, 24 Jul 2012 02:20:37 +0100
|
-- Ben Hutchings <ben@decadent.org.uk> Tue, 24 Jul 2012 02:20:37 +0100
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
From: Kees Cook <keescook@chromium.org>
|
||||||
|
Date: Wed, 25 Jul 2012 17:29:08 -0700
|
||||||
|
Subject: [2/2] fs: add link restriction audit reporting
|
||||||
|
|
||||||
|
commit a51d9eaa41866ab6b4b6ecad7b621f8b66ece0dc upstream.
|
||||||
|
|
||||||
|
Adds audit messages for unexpected link restriction violations so that
|
||||||
|
system owners will have some sort of potentially actionable information
|
||||||
|
about misbehaving processes.
|
||||||
|
|
||||||
|
Signed-off-by: Kees Cook <keescook@chromium.org>
|
||||||
|
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
|
||||||
|
---
|
||||||
|
fs/namei.c | 2 ++
|
||||||
|
include/linux/audit.h | 4 ++++
|
||||||
|
kernel/audit.c | 21 +++++++++++++++++++++
|
||||||
|
3 files changed, 27 insertions(+)
|
||||||
|
|
||||||
|
--- a/fs/namei.c
|
||||||
|
+++ b/fs/namei.c
|
||||||
|
@@ -666,6 +666,7 @@ static inline int may_follow_link(struct
|
||||||
|
|
||||||
|
path_put_conditional(link, nd);
|
||||||
|
path_put(&nd->path);
|
||||||
|
+ audit_log_link_denied("follow_link", link);
|
||||||
|
return -EACCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -734,6 +735,7 @@ static int may_linkat(struct path *link)
|
||||||
|
capable(CAP_FOWNER))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
+ audit_log_link_denied("linkat", link);
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
--- a/include/linux/audit.h
|
||||||
|
+++ b/include/linux/audit.h
|
||||||
|
@@ -129,6 +129,7 @@
|
||||||
|
#define AUDIT_LAST_KERN_ANOM_MSG 1799
|
||||||
|
#define AUDIT_ANOM_PROMISCUOUS 1700 /* Device changed promiscuous mode */
|
||||||
|
#define AUDIT_ANOM_ABEND 1701 /* Process ended abnormally */
|
||||||
|
+#define AUDIT_ANOM_LINK 1702 /* Suspicious use of file links */
|
||||||
|
#define AUDIT_INTEGRITY_DATA 1800 /* Data integrity verification */
|
||||||
|
#define AUDIT_INTEGRITY_METADATA 1801 /* Metadata integrity verification */
|
||||||
|
#define AUDIT_INTEGRITY_STATUS 1802 /* Integrity enable status */
|
||||||
|
@@ -611,6 +612,8 @@ extern void audit_log_d_path(struct
|
||||||
|
struct path *path);
|
||||||
|
extern void audit_log_key(struct audit_buffer *ab,
|
||||||
|
char *key);
|
||||||
|
+extern void audit_log_link_denied(const char *operation,
|
||||||
|
+ struct path *link);
|
||||||
|
extern void audit_log_lost(const char *message);
|
||||||
|
#ifdef CONFIG_SECURITY
|
||||||
|
extern void audit_log_secctx(struct audit_buffer *ab, u32 secid);
|
||||||
|
@@ -640,6 +643,7 @@ extern int audit_enabled;
|
||||||
|
#define audit_log_untrustedstring(a,s) do { ; } while (0)
|
||||||
|
#define audit_log_d_path(b, p, d) do { ; } while (0)
|
||||||
|
#define audit_log_key(b, k) do { ; } while (0)
|
||||||
|
+#define audit_log_link_denied(o, l) do { ; } while (0)
|
||||||
|
#define audit_log_secctx(b,s) do { ; } while (0)
|
||||||
|
#define audit_enabled 0
|
||||||
|
#endif
|
||||||
|
--- a/kernel/audit.c
|
||||||
|
+++ b/kernel/audit.c
|
||||||
|
@@ -1449,6 +1449,27 @@ void audit_log_key(struct audit_buffer *
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
+ * audit_log_link_denied - report a link restriction denial
|
||||||
|
+ * @operation: specific link opreation
|
||||||
|
+ * @link: the path that triggered the restriction
|
||||||
|
+ */
|
||||||
|
+void audit_log_link_denied(const char *operation, struct path *link)
|
||||||
|
+{
|
||||||
|
+ struct audit_buffer *ab;
|
||||||
|
+
|
||||||
|
+ ab = audit_log_start(current->audit_context, GFP_KERNEL,
|
||||||
|
+ AUDIT_ANOM_LINK);
|
||||||
|
+ audit_log_format(ab, "op=%s action=denied", operation);
|
||||||
|
+ audit_log_format(ab, " pid=%d comm=", current->pid);
|
||||||
|
+ audit_log_untrustedstring(ab, current->comm);
|
||||||
|
+ audit_log_d_path(ab, " path=", link);
|
||||||
|
+ audit_log_format(ab, " dev=");
|
||||||
|
+ audit_log_untrustedstring(ab, link->dentry->d_inode->i_sb->s_id);
|
||||||
|
+ audit_log_format(ab, " ino=%lu", link->dentry->d_inode->i_ino);
|
||||||
|
+ audit_log_end(ab);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
* audit_log_end - end one audit record
|
||||||
|
* @ab: the audit_buffer
|
||||||
|
*
|
|
@ -0,0 +1,356 @@
|
||||||
|
From: Kees Cook <keescook@chromium.org>
|
||||||
|
Date: Wed, 25 Jul 2012 17:29:07 -0700
|
||||||
|
Subject: [1/2] fs: add link restrictions
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
commit 800179c9b8a1e796e441674776d11cd4c05d61d7 upstream.
|
||||||
|
|
||||||
|
This adds symlink and hardlink restrictions to the Linux VFS.
|
||||||
|
|
||||||
|
Symlinks:
|
||||||
|
|
||||||
|
A long-standing class of security issues is the symlink-based
|
||||||
|
time-of-check-time-of-use race, most commonly seen in world-writable
|
||||||
|
directories like /tmp. The common method of exploitation of this flaw
|
||||||
|
is to cross privilege boundaries when following a given symlink (i.e. a
|
||||||
|
root process follows a symlink belonging to another user). For a likely
|
||||||
|
incomplete list of hundreds of examples across the years, please see:
|
||||||
|
http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=/tmp
|
||||||
|
|
||||||
|
The solution is to permit symlinks to only be followed when outside
|
||||||
|
a sticky world-writable directory, or when the uid of the symlink and
|
||||||
|
follower match, or when the directory owner matches the symlink's owner.
|
||||||
|
|
||||||
|
Some pointers to the history of earlier discussion that I could find:
|
||||||
|
|
||||||
|
1996 Aug, Zygo Blaxell
|
||||||
|
http://marc.info/?l=bugtraq&m=87602167419830&w=2
|
||||||
|
1996 Oct, Andrew Tridgell
|
||||||
|
http://lkml.indiana.edu/hypermail/linux/kernel/9610.2/0086.html
|
||||||
|
1997 Dec, Albert D Cahalan
|
||||||
|
http://lkml.org/lkml/1997/12/16/4
|
||||||
|
2005 Feb, Lorenzo Hernández García-Hierro
|
||||||
|
http://lkml.indiana.edu/hypermail/linux/kernel/0502.0/1896.html
|
||||||
|
2010 May, Kees Cook
|
||||||
|
https://lkml.org/lkml/2010/5/30/144
|
||||||
|
|
||||||
|
Past objections and rebuttals could be summarized as:
|
||||||
|
|
||||||
|
- Violates POSIX.
|
||||||
|
- POSIX didn't consider this situation and it's not useful to follow
|
||||||
|
a broken specification at the cost of security.
|
||||||
|
- Might break unknown applications that use this feature.
|
||||||
|
- Applications that break because of the change are easy to spot and
|
||||||
|
fix. Applications that are vulnerable to symlink ToCToU by not having
|
||||||
|
the change aren't. Additionally, no applications have yet been found
|
||||||
|
that rely on this behavior.
|
||||||
|
- Applications should just use mkstemp() or O_CREATE|O_EXCL.
|
||||||
|
- True, but applications are not perfect, and new software is written
|
||||||
|
all the time that makes these mistakes; blocking this flaw at the
|
||||||
|
kernel is a single solution to the entire class of vulnerability.
|
||||||
|
- This should live in the core VFS.
|
||||||
|
- This should live in an LSM. (https://lkml.org/lkml/2010/5/31/135)
|
||||||
|
- This should live in an LSM.
|
||||||
|
- This should live in the core VFS. (https://lkml.org/lkml/2010/8/2/188)
|
||||||
|
|
||||||
|
Hardlinks:
|
||||||
|
|
||||||
|
On systems that have user-writable directories on the same partition
|
||||||
|
as system files, a long-standing class of security issues is the
|
||||||
|
hardlink-based time-of-check-time-of-use race, most commonly seen in
|
||||||
|
world-writable directories like /tmp. The common method of exploitation
|
||||||
|
of this flaw is to cross privilege boundaries when following a given
|
||||||
|
hardlink (i.e. a root process follows a hardlink created by another
|
||||||
|
user). Additionally, an issue exists where users can "pin" a potentially
|
||||||
|
vulnerable setuid/setgid file so that an administrator will not actually
|
||||||
|
upgrade a system fully.
|
||||||
|
|
||||||
|
The solution is to permit hardlinks to only be created when the user is
|
||||||
|
already the existing file's owner, or if they already have read/write
|
||||||
|
access to the existing file.
|
||||||
|
|
||||||
|
Many Linux users are surprised when they learn they can link to files
|
||||||
|
they have no access to, so this change appears to follow the doctrine
|
||||||
|
of "least surprise". Additionally, this change does not violate POSIX,
|
||||||
|
which states "the implementation may require that the calling process
|
||||||
|
has permission to access the existing file"[1].
|
||||||
|
|
||||||
|
This change is known to break some implementations of the "at" daemon,
|
||||||
|
though the version used by Fedora and Ubuntu has been fixed[2] for
|
||||||
|
a while. Otherwise, the change has been undisruptive while in use in
|
||||||
|
Ubuntu for the last 1.5 years.
|
||||||
|
|
||||||
|
[1] http://pubs.opengroup.org/onlinepubs/9699919799/functions/linkat.html
|
||||||
|
[2] http://anonscm.debian.org/gitweb/?p=collab-maint/at.git;a=commitdiff;h=f4114656c3a6c6f6070e315ffdf940a49eda3279
|
||||||
|
|
||||||
|
This patch is based on the patches in Openwall and grsecurity, along with
|
||||||
|
suggestions from Al Viro. I have added a sysctl to enable the protected
|
||||||
|
behavior, and documentation.
|
||||||
|
|
||||||
|
Signed-off-by: Kees Cook <keescook@chromium.org>
|
||||||
|
Acked-by: Ingo Molnar <mingo@elte.hu>
|
||||||
|
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||||
|
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
|
||||||
|
[bwh: Backported to 3.2:
|
||||||
|
- Adjust context
|
||||||
|
- In path_openat(), convert error from may_follow_link() to filp as it
|
||||||
|
won't be converted outside the loop]
|
||||||
|
---
|
||||||
|
Documentation/sysctl/fs.txt | 42 +++++++++++++++
|
||||||
|
fs/namei.c | 122 +++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
include/linux/fs.h | 2 +
|
||||||
|
kernel/sysctl.c | 18 +++++++
|
||||||
|
4 files changed, 184 insertions(+)
|
||||||
|
|
||||||
|
--- a/Documentation/sysctl/fs.txt
|
||||||
|
+++ b/Documentation/sysctl/fs.txt
|
||||||
|
@@ -32,6 +32,8 @@ Currently, these files are in /proc/sys/
|
||||||
|
- nr_open
|
||||||
|
- overflowuid
|
||||||
|
- overflowgid
|
||||||
|
+- protected_hardlinks
|
||||||
|
+- protected_symlinks
|
||||||
|
- suid_dumpable
|
||||||
|
- super-max
|
||||||
|
- super-nr
|
||||||
|
@@ -157,6 +159,46 @@ The default is 65534.
|
||||||
|
|
||||||
|
==============================================================
|
||||||
|
|
||||||
|
+protected_hardlinks:
|
||||||
|
+
|
||||||
|
+A long-standing class of security issues is the hardlink-based
|
||||||
|
+time-of-check-time-of-use race, most commonly seen in world-writable
|
||||||
|
+directories like /tmp. The common method of exploitation of this flaw
|
||||||
|
+is to cross privilege boundaries when following a given hardlink (i.e. a
|
||||||
|
+root process follows a hardlink created by another user). Additionally,
|
||||||
|
+on systems without separated partitions, this stops unauthorized users
|
||||||
|
+from "pinning" vulnerable setuid/setgid files against being upgraded by
|
||||||
|
+the administrator, or linking to special files.
|
||||||
|
+
|
||||||
|
+When set to "0", hardlink creation behavior is unrestricted.
|
||||||
|
+
|
||||||
|
+When set to "1" hardlinks cannot be created by users if they do not
|
||||||
|
+already own the source file, or do not have read/write access to it.
|
||||||
|
+
|
||||||
|
+This protection is based on the restrictions in Openwall and grsecurity.
|
||||||
|
+
|
||||||
|
+==============================================================
|
||||||
|
+
|
||||||
|
+protected_symlinks:
|
||||||
|
+
|
||||||
|
+A long-standing class of security issues is the symlink-based
|
||||||
|
+time-of-check-time-of-use race, most commonly seen in world-writable
|
||||||
|
+directories like /tmp. The common method of exploitation of this flaw
|
||||||
|
+is to cross privilege boundaries when following a given symlink (i.e. a
|
||||||
|
+root process follows a symlink belonging to another user). For a likely
|
||||||
|
+incomplete list of hundreds of examples across the years, please see:
|
||||||
|
+http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=/tmp
|
||||||
|
+
|
||||||
|
+When set to "0", symlink following behavior is unrestricted.
|
||||||
|
+
|
||||||
|
+When set to "1" symlinks are permitted to be followed only when outside
|
||||||
|
+a sticky world-writable directory, or when the uid of the symlink and
|
||||||
|
+follower match, or when the directory owner matches the symlink's owner.
|
||||||
|
+
|
||||||
|
+This protection is based on the restrictions in Openwall and grsecurity.
|
||||||
|
+
|
||||||
|
+==============================================================
|
||||||
|
+
|
||||||
|
suid_dumpable:
|
||||||
|
|
||||||
|
This value can be used to query and set the core dump mode for setuid
|
||||||
|
--- a/fs/namei.c
|
||||||
|
+++ b/fs/namei.c
|
||||||
|
@@ -624,6 +624,119 @@ static inline void put_link(struct namei
|
||||||
|
path_put(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
+int sysctl_protected_symlinks __read_mostly = 1;
|
||||||
|
+int sysctl_protected_hardlinks __read_mostly = 1;
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * may_follow_link - Check symlink following for unsafe situations
|
||||||
|
+ * @link: The path of the symlink
|
||||||
|
+ *
|
||||||
|
+ * In the case of the sysctl_protected_symlinks sysctl being enabled,
|
||||||
|
+ * CAP_DAC_OVERRIDE needs to be specifically ignored if the symlink is
|
||||||
|
+ * in a sticky world-writable directory. This is to protect privileged
|
||||||
|
+ * processes from failing races against path names that may change out
|
||||||
|
+ * from under them by way of other users creating malicious symlinks.
|
||||||
|
+ * It will permit symlinks to be followed only when outside a sticky
|
||||||
|
+ * world-writable directory, or when the uid of the symlink and follower
|
||||||
|
+ * match, or when the directory owner matches the symlink's owner.
|
||||||
|
+ *
|
||||||
|
+ * Returns 0 if following the symlink is allowed, -ve on error.
|
||||||
|
+ */
|
||||||
|
+static inline int may_follow_link(struct path *link, struct nameidata *nd)
|
||||||
|
+{
|
||||||
|
+ const struct inode *inode;
|
||||||
|
+ const struct inode *parent;
|
||||||
|
+
|
||||||
|
+ if (!sysctl_protected_symlinks)
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+ /* Allowed if owner and follower match. */
|
||||||
|
+ inode = link->dentry->d_inode;
|
||||||
|
+ if (current_cred()->fsuid == inode->i_uid)
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+ /* Allowed if parent directory not sticky and world-writable. */
|
||||||
|
+ parent = nd->path.dentry->d_inode;
|
||||||
|
+ if ((parent->i_mode & (S_ISVTX|S_IWOTH)) != (S_ISVTX|S_IWOTH))
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+ /* Allowed if parent directory and link owner match. */
|
||||||
|
+ if (parent->i_uid == inode->i_uid)
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+ path_put_conditional(link, nd);
|
||||||
|
+ path_put(&nd->path);
|
||||||
|
+ return -EACCES;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * safe_hardlink_source - Check for safe hardlink conditions
|
||||||
|
+ * @inode: the source inode to hardlink from
|
||||||
|
+ *
|
||||||
|
+ * Return false if at least one of the following conditions:
|
||||||
|
+ * - inode is not a regular file
|
||||||
|
+ * - inode is setuid
|
||||||
|
+ * - inode is setgid and group-exec
|
||||||
|
+ * - access failure for read and write
|
||||||
|
+ *
|
||||||
|
+ * Otherwise returns true.
|
||||||
|
+ */
|
||||||
|
+static bool safe_hardlink_source(struct inode *inode)
|
||||||
|
+{
|
||||||
|
+ umode_t mode = inode->i_mode;
|
||||||
|
+
|
||||||
|
+ /* Special files should not get pinned to the filesystem. */
|
||||||
|
+ if (!S_ISREG(mode))
|
||||||
|
+ return false;
|
||||||
|
+
|
||||||
|
+ /* Setuid files should not get pinned to the filesystem. */
|
||||||
|
+ if (mode & S_ISUID)
|
||||||
|
+ return false;
|
||||||
|
+
|
||||||
|
+ /* Executable setgid files should not get pinned to the filesystem. */
|
||||||
|
+ if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
|
||||||
|
+ return false;
|
||||||
|
+
|
||||||
|
+ /* Hardlinking to unreadable or unwritable sources is dangerous. */
|
||||||
|
+ if (inode_permission(inode, MAY_READ | MAY_WRITE))
|
||||||
|
+ return false;
|
||||||
|
+
|
||||||
|
+ return true;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * may_linkat - Check permissions for creating a hardlink
|
||||||
|
+ * @link: the source to hardlink from
|
||||||
|
+ *
|
||||||
|
+ * Block hardlink when all of:
|
||||||
|
+ * - sysctl_protected_hardlinks enabled
|
||||||
|
+ * - fsuid does not match inode
|
||||||
|
+ * - hardlink source is unsafe (see safe_hardlink_source() above)
|
||||||
|
+ * - not CAP_FOWNER
|
||||||
|
+ *
|
||||||
|
+ * Returns 0 if successful, -ve on error.
|
||||||
|
+ */
|
||||||
|
+static int may_linkat(struct path *link)
|
||||||
|
+{
|
||||||
|
+ const struct cred *cred;
|
||||||
|
+ struct inode *inode;
|
||||||
|
+
|
||||||
|
+ if (!sysctl_protected_hardlinks)
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+ cred = current_cred();
|
||||||
|
+ inode = link->dentry->d_inode;
|
||||||
|
+
|
||||||
|
+ /* Source inode owner (or CAP_FOWNER) can hardlink all they like,
|
||||||
|
+ * otherwise, it must be a safe source.
|
||||||
|
+ */
|
||||||
|
+ if (cred->fsuid == inode->i_uid || safe_hardlink_source(inode) ||
|
||||||
|
+ capable(CAP_FOWNER))
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+ return -EPERM;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static __always_inline int
|
||||||
|
follow_link(struct path *link, struct nameidata *nd, void **p)
|
||||||
|
{
|
||||||
|
@@ -1613,6 +1726,9 @@ static int path_lookupat(int dfd, const
|
||||||
|
while (err > 0) {
|
||||||
|
void *cookie;
|
||||||
|
struct path link = path;
|
||||||
|
+ err = may_follow_link(&link, nd);
|
||||||
|
+ if (unlikely(err))
|
||||||
|
+ break;
|
||||||
|
nd->flags |= LOOKUP_PARENT;
|
||||||
|
err = follow_link(&link, nd, &cookie);
|
||||||
|
if (!err)
|
||||||
|
@@ -2325,6 +2441,11 @@ static struct file *path_openat(int dfd,
|
||||||
|
filp = ERR_PTR(-ELOOP);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
+ error = may_follow_link(&link, nd);
|
||||||
|
+ if (unlikely(error)) {
|
||||||
|
+ filp = ERR_PTR(error);
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
nd->flags |= LOOKUP_PARENT;
|
||||||
|
nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
|
||||||
|
error = follow_link(&link, nd, &cookie);
|
||||||
|
@@ -2972,6 +3093,9 @@ SYSCALL_DEFINE5(linkat, int, olddfd, con
|
||||||
|
error = -EXDEV;
|
||||||
|
if (old_path.mnt != new_path.mnt)
|
||||||
|
goto out_dput;
|
||||||
|
+ error = may_linkat(&old_path);
|
||||||
|
+ if (unlikely(error))
|
||||||
|
+ goto out_dput;
|
||||||
|
error = mnt_want_write(new_path.mnt);
|
||||||
|
if (error)
|
||||||
|
goto out_dput;
|
||||||
|
--- a/include/linux/fs.h
|
||||||
|
+++ b/include/linux/fs.h
|
||||||
|
@@ -420,6 +420,8 @@ extern unsigned long get_max_files(void)
|
||||||
|
extern int sysctl_nr_open;
|
||||||
|
extern struct inodes_stat_t inodes_stat;
|
||||||
|
extern int leases_enable, lease_break_time;
|
||||||
|
+extern int sysctl_protected_symlinks;
|
||||||
|
+extern int sysctl_protected_hardlinks;
|
||||||
|
|
||||||
|
struct buffer_head;
|
||||||
|
typedef int (get_block_t)(struct inode *inode, sector_t iblock,
|
||||||
|
--- a/kernel/sysctl.c
|
||||||
|
+++ b/kernel/sysctl.c
|
||||||
|
@@ -1495,6 +1495,24 @@ static struct ctl_table fs_table[] = {
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
+ .procname = "protected_symlinks",
|
||||||
|
+ .data = &sysctl_protected_symlinks,
|
||||||
|
+ .maxlen = sizeof(int),
|
||||||
|
+ .mode = 0600,
|
||||||
|
+ .proc_handler = proc_dointvec_minmax,
|
||||||
|
+ .extra1 = &zero,
|
||||||
|
+ .extra2 = &one,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .procname = "protected_hardlinks",
|
||||||
|
+ .data = &sysctl_protected_hardlinks,
|
||||||
|
+ .maxlen = sizeof(int),
|
||||||
|
+ .mode = 0600,
|
||||||
|
+ .proc_handler = proc_dointvec_minmax,
|
||||||
|
+ .extra1 = &zero,
|
||||||
|
+ .extra2 = &one,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
.procname = "suid_dumpable",
|
||||||
|
.data = &suid_dumpable,
|
||||||
|
.maxlen = sizeof(int),
|
|
@ -1,112 +0,0 @@
|
||||||
From 52db90d0fa770e2277645eb34956820cec26b2cb Mon Sep 17 00:00:00 2001
|
|
||||||
From: Kees Cook <keescook@chromium.org>
|
|
||||||
Date: Sat, 25 Feb 2012 12:28:44 +1100
|
|
||||||
Subject: [PATCH 5/5] fs: hardlink creation restriction cleanup
|
|
||||||
|
|
||||||
Clean-up of hardlink restriction logic, as suggested by Andrew Morton.
|
|
||||||
|
|
||||||
Signed-off-by: Kees Cook <keescook@chromium.org>
|
|
||||||
Cc: Ingo Molnar <mingo@elte.hu>
|
|
||||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
|
||||||
---
|
|
||||||
fs/namei.c | 62 ++++++++++++++++++++++++++++++++++++++++++-----------------
|
|
||||||
1 files changed, 44 insertions(+), 18 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/fs/namei.c b/fs/namei.c
|
|
||||||
index fe13533..1436fae 100644
|
|
||||||
--- a/fs/namei.c
|
|
||||||
+++ b/fs/namei.c
|
|
||||||
@@ -693,46 +693,72 @@ static inline int may_follow_link(struct path *link)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
+ * safe_hardlink_source - Check for safe hardlink conditions
|
|
||||||
+ * @inode: the source inode to hardlink from
|
|
||||||
+ *
|
|
||||||
+ * Return false if at least one of the following conditions:
|
|
||||||
+ * - inode is not a regular file
|
|
||||||
+ * - inode is setuid
|
|
||||||
+ * - inode is setgid and group-exec
|
|
||||||
+ * - access failure for read and write
|
|
||||||
+ *
|
|
||||||
+ * Otherwise returns true.
|
|
||||||
+ */
|
|
||||||
+static bool safe_hardlink_source(struct inode *inode)
|
|
||||||
+{
|
|
||||||
+ mode_t mode = inode->i_mode;
|
|
||||||
+
|
|
||||||
+ /* Special files should not get pinned to the filesystem. */
|
|
||||||
+ if (!S_ISREG(mode))
|
|
||||||
+ return false;
|
|
||||||
+
|
|
||||||
+ /* Setuid files should not get pinned to the filesystem. */
|
|
||||||
+ if (mode & S_ISUID)
|
|
||||||
+ return false;
|
|
||||||
+
|
|
||||||
+ /* Executable setgid files should not get pinned to the filesystem. */
|
|
||||||
+ if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
|
|
||||||
+ return false;
|
|
||||||
+
|
|
||||||
+ /* Hardlinking to unreadable or unwritable sources is dangerous. */
|
|
||||||
+ if (inode_permission(inode, MAY_READ | MAY_WRITE))
|
|
||||||
+ return false;
|
|
||||||
+
|
|
||||||
+ return true;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/**
|
|
||||||
* may_linkat - Check permissions for creating a hardlink
|
|
||||||
* @link: the source to hardlink from
|
|
||||||
*
|
|
||||||
* Block hardlink when all of:
|
|
||||||
* - sysctl_protected_hardlinks enabled
|
|
||||||
* - fsuid does not match inode
|
|
||||||
- * - at least one of:
|
|
||||||
- * - inode is not a regular file
|
|
||||||
- * - inode is setuid
|
|
||||||
- * - inode is setgid and group-exec
|
|
||||||
- * - access failure for read and write
|
|
||||||
+ * - hardlink source is unsafe (see safe_hardlink_source() above)
|
|
||||||
* - not CAP_FOWNER
|
|
||||||
*
|
|
||||||
* Returns 0 if successful, -ve on error.
|
|
||||||
*/
|
|
||||||
static int may_linkat(struct path *link)
|
|
||||||
{
|
|
||||||
- int error = 0;
|
|
||||||
const struct cred *cred;
|
|
||||||
struct inode *inode;
|
|
||||||
- int mode;
|
|
||||||
|
|
||||||
if (!sysctl_protected_hardlinks)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
cred = current_cred();
|
|
||||||
inode = link->dentry->d_inode;
|
|
||||||
- mode = inode->i_mode;
|
|
||||||
-
|
|
||||||
- if (cred->fsuid != inode->i_uid &&
|
|
||||||
- (!S_ISREG(mode) || (mode & S_ISUID) ||
|
|
||||||
- ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) ||
|
|
||||||
- (inode_permission(inode, MAY_READ | MAY_WRITE))) &&
|
|
||||||
- !capable(CAP_FOWNER))
|
|
||||||
- error = -EPERM;
|
|
||||||
|
|
||||||
- if (error)
|
|
||||||
- audit_log_link_denied("linkat", link);
|
|
||||||
+ /* Source inode owner (or CAP_FOWNER) can hardlink all they like,
|
|
||||||
+ * otherwise, it must be a safe source.
|
|
||||||
+ */
|
|
||||||
+ if (cred->fsuid == inode->i_uid || safe_hardlink_source(inode) ||
|
|
||||||
+ capable(CAP_FOWNER))
|
|
||||||
+ return 0;
|
|
||||||
|
|
||||||
- return error;
|
|
||||||
+ audit_log_link_denied("linkat", link);
|
|
||||||
+ return -EPERM;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
static inline int may_follow_link(struct path *link)
|
|
||||||
--
|
|
||||||
1.7.9.1
|
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
From 19f621ccbef745dedad641f44f535e3bcb00f30d Mon Sep 17 00:00:00 2001
|
|
||||||
From: Andrew Morton <akpm@linux-foundation.org>
|
|
||||||
Date: Sat, 25 Feb 2012 12:28:43 +1100
|
|
||||||
Subject: [PATCH 4/5] fs-hardlink-creation-restrictions-fix
|
|
||||||
|
|
||||||
uninline may_linkat() and audit_log_link_denied().
|
|
||||||
|
|
||||||
Cc: Kees Cook <keescook@chromium.org>
|
|
||||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
|
||||||
---
|
|
||||||
fs/namei.c | 5 ++---
|
|
||||||
1 files changed, 2 insertions(+), 3 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/fs/namei.c b/fs/namei.c
|
|
||||||
index 7be190c..fe13533 100644
|
|
||||||
--- a/fs/namei.c
|
|
||||||
+++ b/fs/namei.c
|
|
||||||
@@ -629,8 +629,7 @@ int sysctl_protected_symlinks __read_mostly =
|
|
||||||
int sysctl_protected_hardlinks __read_mostly =
|
|
||||||
CONFIG_PROTECTED_HARDLINKS_SYSCTL;
|
|
||||||
|
|
||||||
-static inline void
|
|
||||||
-audit_log_link_denied(const char *operation, struct path *link)
|
|
||||||
+static void audit_log_link_denied(const char *operation, struct path *link)
|
|
||||||
{
|
|
||||||
struct audit_buffer *ab;
|
|
||||||
|
|
||||||
@@ -709,7 +708,7 @@ static inline int may_follow_link(struct path *link)
|
|
||||||
*
|
|
||||||
* Returns 0 if successful, -ve on error.
|
|
||||||
*/
|
|
||||||
-static inline int may_linkat(struct path *link)
|
|
||||||
+static int may_linkat(struct path *link)
|
|
||||||
{
|
|
||||||
int error = 0;
|
|
||||||
const struct cred *cred;
|
|
||||||
--
|
|
||||||
1.7.9.1
|
|
||||||
|
|
|
@ -1,390 +0,0 @@
|
||||||
From fa3abdeee4e792ed794eef7ea71e7e0073cec32d Mon Sep 17 00:00:00 2001
|
|
||||||
From: Kees Cook <keescook@chromium.org>
|
|
||||||
Date: Sat, 25 Feb 2012 12:28:43 +1100
|
|
||||||
Subject: [PATCH 3/5] fs: hardlink creation restrictions
|
|
||||||
|
|
||||||
On systems that have user-writable directories on the same partition as
|
|
||||||
system files, a long-standing class of security issues is the
|
|
||||||
hardlink-based time-of-check-time-of-use race, most commonly seen in
|
|
||||||
world-writable directories like /tmp. The common method of exploitation
|
|
||||||
of this flaw is to cross privilege boundaries when following a given
|
|
||||||
hardlink (i.e. a root process follows a hardlink created by another
|
|
||||||
user). Additionally, an issue exists where users can "pin" a potentially
|
|
||||||
vulnerable setuid/setgid file so that an administrator will not actually
|
|
||||||
upgrade a system fully.
|
|
||||||
|
|
||||||
The solution is to permit hardlinks to only be created when the user is
|
|
||||||
already the existing file's owner, or if they already have read/write
|
|
||||||
access to the existing file.
|
|
||||||
|
|
||||||
Many Linux users are surprised when they learn they can link to files they
|
|
||||||
have no access to, so this change appears to follow the doctrine of "least
|
|
||||||
surprise". Additionally, this change does not violate POSIX, which states
|
|
||||||
"the implementation may require that the calling process has permission to
|
|
||||||
access the existing file"[1].
|
|
||||||
|
|
||||||
This change is known to break some implementations of the "at" daemon,
|
|
||||||
though the version used by Fedora and Ubuntu has been fixed[2] for a
|
|
||||||
while. Otherwise, the change has been undisruptive while in use in Ubuntu
|
|
||||||
for the last 1.5 years.
|
|
||||||
|
|
||||||
This patch is based on the patch in Openwall and grsecurity. I have added
|
|
||||||
a sysctl to enable the protected behavior, documentation, and an audit
|
|
||||||
notification.
|
|
||||||
|
|
||||||
[1] http://pubs.opengroup.org/onlinepubs/9699919799/functions/linkat.html
|
|
||||||
[2] http://anonscm.debian.org/gitweb/?p=collab-maint/at.git;a=commitdiff;h=f4114656c3a6c6f6070e315ffdf940a49eda3279
|
|
||||||
|
|
||||||
Signed-off-by: Kees Cook <keescook@chromium.org>
|
|
||||||
Acked-by: Ingo Molnar <mingo@elte.hu>
|
|
||||||
Cc: Matthew Wilcox <matthew@wil.cx>
|
|
||||||
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
|
|
||||||
Cc: Rik van Riel <riel@redhat.com>
|
|
||||||
Cc: Federica Teodori <federica.teodori@googlemail.com>
|
|
||||||
Cc: Lucian Adrian Grijincu <lucian.grijincu@gmail.com>
|
|
||||||
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
|
|
||||||
Cc: Eric Paris <eparis@redhat.com>
|
|
||||||
Cc: Randy Dunlap <rdunlap@xenotime.net>
|
|
||||||
Cc: Dan Rosenberg <drosenberg@vsecurity.com>
|
|
||||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
|
||||||
---
|
|
||||||
Documentation/sysctl/fs.txt | 21 ++++++++
|
|
||||||
fs/Kconfig | 54 ++++++++++++++++------
|
|
||||||
fs/namei.c | 109 ++++++++++++++++++++++++++++++++-----------
|
|
||||||
include/linux/fs.h | 1 +
|
|
||||||
kernel/sysctl.c | 11 ++++-
|
|
||||||
5 files changed, 153 insertions(+), 43 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/Documentation/sysctl/fs.txt b/Documentation/sysctl/fs.txt
|
|
||||||
index 01daa80..9d29414 100644
|
|
||||||
--- a/Documentation/sysctl/fs.txt
|
|
||||||
+++ b/Documentation/sysctl/fs.txt
|
|
||||||
@@ -32,6 +32,7 @@ Currently, these files are in /proc/sys/fs:
|
|
||||||
- nr_open
|
|
||||||
- overflowuid
|
|
||||||
- overflowgid
|
|
||||||
+- protected_hardlinks
|
|
||||||
- protected_symlinks
|
|
||||||
- suid_dumpable
|
|
||||||
- super-max
|
|
||||||
@@ -158,6 +159,26 @@ The default is 65534.
|
|
||||||
|
|
||||||
==============================================================
|
|
||||||
|
|
||||||
+protected_hardlinks:
|
|
||||||
+
|
|
||||||
+A long-standing class of security issues is the hardlink-based
|
|
||||||
+time-of-check-time-of-use race, most commonly seen in world-writable
|
|
||||||
+directories like /tmp. The common method of exploitation of this flaw
|
|
||||||
+is to cross privilege boundaries when following a given hardlink (i.e. a
|
|
||||||
+root process follows a hardlink created by another user). Additionally,
|
|
||||||
+on systems without separated partitions, this stops unauthorized users
|
|
||||||
+from "pinning" vulnerable setuid/setgid files against being upgraded by
|
|
||||||
+the administrator, or linking to special files.
|
|
||||||
+
|
|
||||||
+When set to "0", hardlink creation behavior is unrestricted.
|
|
||||||
+
|
|
||||||
+When set to "1" hardlinks cannot be created by users if they do not
|
|
||||||
+already own the source file, or do not have read/write access to it.
|
|
||||||
+
|
|
||||||
+This protection is based on the restrictions in Openwall and grsecurity.
|
|
||||||
+
|
|
||||||
+==============================================================
|
|
||||||
+
|
|
||||||
protected_symlinks:
|
|
||||||
|
|
||||||
A long-standing class of security issues is the symlink-based
|
|
||||||
diff --git a/fs/Kconfig b/fs/Kconfig
|
|
||||||
index f2c46f3..d2a422e 100644
|
|
||||||
--- a/fs/Kconfig
|
|
||||||
+++ b/fs/Kconfig
|
|
||||||
@@ -272,27 +272,29 @@ endif # NETWORK_FILESYSTEMS
|
|
||||||
source "fs/nls/Kconfig"
|
|
||||||
source "fs/dlm/Kconfig"
|
|
||||||
|
|
||||||
-config PROTECTED_SYMLINKS
|
|
||||||
- bool "Evaluate vulnerable symlink conditions"
|
|
||||||
+config PROTECTED_LINKS
|
|
||||||
+ bool "Evaluate vulnerable link conditions"
|
|
||||||
default y
|
|
||||||
help
|
|
||||||
- A long-standing class of security issues is the symlink-based
|
|
||||||
+ A long-standing class of security issues is the link-based
|
|
||||||
time-of-check-time-of-use race, most commonly seen in
|
|
||||||
world-writable directories like /tmp. The common method of
|
|
||||||
exploitation of this flaw is to cross privilege boundaries
|
|
||||||
- when following a given symlink (i.e. a root process follows
|
|
||||||
- a malicious symlink belonging to another user).
|
|
||||||
+ when following a given link (i.e. a root process follows
|
|
||||||
+ a malicious symlink belonging to another user, or a hardlink
|
|
||||||
+ created to a root-owned file).
|
|
||||||
|
|
||||||
- Enabling this adds the logic to examine these dangerous symlink
|
|
||||||
- conditions. Whether or not the dangerous symlink situations are
|
|
||||||
- allowed is controlled by PROTECTED_SYMLINKS_ENABLED.
|
|
||||||
+ Enabling this adds the logic to examine these dangerous link
|
|
||||||
+ conditions. Whether or not the dangerous link situations are
|
|
||||||
+ allowed is controlled by PROTECTED_HARDLINKS_ENABLED and
|
|
||||||
+ PROTECTED_SYMLINKS_ENABLED.
|
|
||||||
|
|
||||||
-config PROTECTED_SYMLINKS_ENABLED
|
|
||||||
- depends on PROTECTED_SYMLINKS
|
|
||||||
+config PROTECTED_SYMLINKS
|
|
||||||
+ depends on PROTECTED_LINKS
|
|
||||||
bool "Disallow symlink following in sticky world-writable dirs"
|
|
||||||
default y
|
|
||||||
help
|
|
||||||
- Solve ToCToU symlink race vulnerablities by permitting symlinks
|
|
||||||
+ Solve ToCToU symlink race vulnerabilities by permitting symlinks
|
|
||||||
to be followed only when outside a sticky world-writable directory,
|
|
||||||
or when the uid of the symlink and follower match, or when the
|
|
||||||
directory and symlink owners match.
|
|
||||||
@@ -300,10 +302,34 @@ config PROTECTED_SYMLINKS_ENABLED
|
|
||||||
When PROC_SYSCTL is enabled, this setting can also be controlled
|
|
||||||
via /proc/sys/kernel/protected_symlinks.
|
|
||||||
|
|
||||||
-config PROTECTED_SYMLINKS_ENABLED_SYSCTL
|
|
||||||
- depends on PROTECTED_SYMLINKS
|
|
||||||
+ See Documentation/sysctl/fs.txt for details.
|
|
||||||
+
|
|
||||||
+config PROTECTED_SYMLINKS_SYSCTL
|
|
||||||
+ depends on PROTECTED_LINKS
|
|
||||||
+ int
|
|
||||||
+ default "1" if PROTECTED_SYMLINKS
|
|
||||||
+ default "0"
|
|
||||||
+
|
|
||||||
+config PROTECTED_HARDLINKS
|
|
||||||
+ depends on PROTECTED_LINKS
|
|
||||||
+ bool "Disallow hardlink creation to non-accessible files"
|
|
||||||
+ default y
|
|
||||||
+ help
|
|
||||||
+ Solve ToCToU hardlink race vulnerabilities by permitting hardlinks
|
|
||||||
+ to be created only when to a regular file that is owned by the user,
|
|
||||||
+ or is readable and writable by the user. Also blocks users from
|
|
||||||
+ "pinning" vulnerable setuid/setgid programs from being upgraded by
|
|
||||||
+ the administrator.
|
|
||||||
+
|
|
||||||
+ When PROC_SYSCTL is enabled, this setting can also be controlled
|
|
||||||
+ via /proc/sys/kernel/protected_hardlinks.
|
|
||||||
+
|
|
||||||
+ See Documentation/sysctl/fs.txt for details.
|
|
||||||
+
|
|
||||||
+config PROTECTED_HARDLINKS_SYSCTL
|
|
||||||
+ depends on PROTECTED_LINKS
|
|
||||||
int
|
|
||||||
- default "1" if PROTECTED_SYMLINKS_ENABLED
|
|
||||||
+ default "1" if PROTECTED_HARDLINKS
|
|
||||||
default "0"
|
|
||||||
|
|
||||||
endmenu
|
|
||||||
diff --git a/fs/namei.c b/fs/namei.c
|
|
||||||
index 39edcf7..7be190c 100644
|
|
||||||
--- a/fs/namei.c
|
|
||||||
+++ b/fs/namei.c
|
|
||||||
@@ -623,16 +623,33 @@ static inline void put_link(struct nameidata *nd, struct path *link, void *cooki
|
|
||||||
path_put(link);
|
|
||||||
}
|
|
||||||
|
|
||||||
-#ifdef CONFIG_PROTECTED_SYMLINKS
|
|
||||||
+#ifdef CONFIG_PROTECTED_LINKS
|
|
||||||
int sysctl_protected_symlinks __read_mostly =
|
|
||||||
- CONFIG_PROTECTED_SYMLINKS_ENABLED_SYSCTL;
|
|
||||||
+ CONFIG_PROTECTED_SYMLINKS_SYSCTL;
|
|
||||||
+int sysctl_protected_hardlinks __read_mostly =
|
|
||||||
+ CONFIG_PROTECTED_HARDLINKS_SYSCTL;
|
|
||||||
+
|
|
||||||
+static inline void
|
|
||||||
+audit_log_link_denied(const char *operation, struct path *link)
|
|
||||||
+{
|
|
||||||
+ struct audit_buffer *ab;
|
|
||||||
+
|
|
||||||
+ ab = audit_log_start(current->audit_context, GFP_KERNEL, AUDIT_AVC);
|
|
||||||
+ audit_log_format(ab, "op=%s action=denied", operation);
|
|
||||||
+ audit_log_format(ab, " pid=%d comm=", current->pid);
|
|
||||||
+ audit_log_untrustedstring(ab, current->comm);
|
|
||||||
+ audit_log_d_path(ab, " path=", link);
|
|
||||||
+ audit_log_format(ab, " dev=");
|
|
||||||
+ audit_log_untrustedstring(ab, link->dentry->d_inode->i_sb->s_id);
|
|
||||||
+ audit_log_format(ab, " ino=%lu", link->dentry->d_inode->i_ino);
|
|
||||||
+ audit_log_end(ab);
|
|
||||||
+}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* may_follow_link - Check symlink following for unsafe situations
|
|
||||||
- * @dentry: The inode/dentry of the symlink
|
|
||||||
- * @nameidata: The path data of the symlink
|
|
||||||
+ * @link: The path of the symlink
|
|
||||||
*
|
|
||||||
- * In the case of the protected_symlinks sysctl being enabled,
|
|
||||||
+ * In the case of the sysctl_protected_symlinks sysctl being enabled,
|
|
||||||
* CAP_DAC_OVERRIDE needs to be specifically ignored if the symlink is
|
|
||||||
* in a sticky world-writable directory. This is to protect privileged
|
|
||||||
* processes from failing races against path names that may change out
|
|
||||||
@@ -643,19 +660,20 @@ int sysctl_protected_symlinks __read_mostly =
|
|
||||||
*
|
|
||||||
* Returns 0 if following the symlink is allowed, -ve on error.
|
|
||||||
*/
|
|
||||||
-static inline int
|
|
||||||
-may_follow_link(struct dentry *dentry, struct nameidata *nameidata)
|
|
||||||
+static inline int may_follow_link(struct path *link)
|
|
||||||
{
|
|
||||||
int error = 0;
|
|
||||||
const struct inode *parent;
|
|
||||||
const struct inode *inode;
|
|
||||||
const struct cred *cred;
|
|
||||||
+ struct dentry *dentry;
|
|
||||||
|
|
||||||
if (!sysctl_protected_symlinks)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Allowed if owner and follower match. */
|
|
||||||
cred = current_cred();
|
|
||||||
+ dentry = link->dentry;
|
|
||||||
inode = dentry->d_inode;
|
|
||||||
if (cred->fsuid == inode->i_uid)
|
|
||||||
return 0;
|
|
||||||
@@ -669,29 +687,61 @@ may_follow_link(struct dentry *dentry, struct nameidata *nameidata)
|
|
||||||
}
|
|
||||||
spin_unlock(&dentry->d_lock);
|
|
||||||
|
|
||||||
-#ifdef CONFIG_AUDIT
|
|
||||||
- if (error) {
|
|
||||||
- struct audit_buffer *ab;
|
|
||||||
-
|
|
||||||
- ab = audit_log_start(current->audit_context,
|
|
||||||
- GFP_KERNEL, AUDIT_AVC);
|
|
||||||
- audit_log_format(ab, "op=follow_link action=denied");
|
|
||||||
- audit_log_format(ab, " pid=%d comm=", current->pid);
|
|
||||||
- audit_log_untrustedstring(ab, current->comm);
|
|
||||||
- audit_log_d_path(ab, " path=", &nameidata->path);
|
|
||||||
- audit_log_format(ab, " name=");
|
|
||||||
- audit_log_untrustedstring(ab, dentry->d_name.name);
|
|
||||||
- audit_log_format(ab, " dev=");
|
|
||||||
- audit_log_untrustedstring(ab, inode->i_sb->s_id);
|
|
||||||
- audit_log_format(ab, " ino=%lu", inode->i_ino);
|
|
||||||
- audit_log_end(ab);
|
|
||||||
- }
|
|
||||||
-#endif
|
|
||||||
+ if (error)
|
|
||||||
+ audit_log_link_denied("follow_link", link);
|
|
||||||
+
|
|
||||||
+ return error;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/**
|
|
||||||
+ * may_linkat - Check permissions for creating a hardlink
|
|
||||||
+ * @link: the source to hardlink from
|
|
||||||
+ *
|
|
||||||
+ * Block hardlink when all of:
|
|
||||||
+ * - sysctl_protected_hardlinks enabled
|
|
||||||
+ * - fsuid does not match inode
|
|
||||||
+ * - at least one of:
|
|
||||||
+ * - inode is not a regular file
|
|
||||||
+ * - inode is setuid
|
|
||||||
+ * - inode is setgid and group-exec
|
|
||||||
+ * - access failure for read and write
|
|
||||||
+ * - not CAP_FOWNER
|
|
||||||
+ *
|
|
||||||
+ * Returns 0 if successful, -ve on error.
|
|
||||||
+ */
|
|
||||||
+static inline int may_linkat(struct path *link)
|
|
||||||
+{
|
|
||||||
+ int error = 0;
|
|
||||||
+ const struct cred *cred;
|
|
||||||
+ struct inode *inode;
|
|
||||||
+ int mode;
|
|
||||||
+
|
|
||||||
+ if (!sysctl_protected_hardlinks)
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+ cred = current_cred();
|
|
||||||
+ inode = link->dentry->d_inode;
|
|
||||||
+ mode = inode->i_mode;
|
|
||||||
+
|
|
||||||
+ if (cred->fsuid != inode->i_uid &&
|
|
||||||
+ (!S_ISREG(mode) || (mode & S_ISUID) ||
|
|
||||||
+ ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) ||
|
|
||||||
+ (inode_permission(inode, MAY_READ | MAY_WRITE))) &&
|
|
||||||
+ !capable(CAP_FOWNER))
|
|
||||||
+ error = -EPERM;
|
|
||||||
+
|
|
||||||
+ if (error)
|
|
||||||
+ audit_log_link_denied("linkat", link);
|
|
||||||
+
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
-static inline int
|
|
||||||
-may_follow_link(struct dentry *dentry, struct nameidata *nameidata)
|
|
||||||
+static inline int may_follow_link(struct path *link)
|
|
||||||
+{
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static inline int may_linkat(struct path *link)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -720,7 +770,7 @@ follow_link(struct path *link, struct nameidata *nd, void **p, bool sensitive)
|
|
||||||
nd_set_link(nd, NULL);
|
|
||||||
|
|
||||||
if (sensitive)
|
|
||||||
- error = may_follow_link(link->dentry, nd);
|
|
||||||
+ error = may_follow_link(link);
|
|
||||||
if (!error)
|
|
||||||
error = security_inode_follow_link(link->dentry, nd);
|
|
||||||
if (error) {
|
|
||||||
@@ -3058,6 +3108,9 @@ SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname,
|
|
||||||
error = -EXDEV;
|
|
||||||
if (old_path.mnt != new_path.mnt)
|
|
||||||
goto out_dput;
|
|
||||||
+ error = may_linkat(&old_path);
|
|
||||||
+ if (error)
|
|
||||||
+ goto out_dput;
|
|
||||||
error = mnt_want_write(new_path.mnt);
|
|
||||||
if (error)
|
|
||||||
goto out_dput;
|
|
||||||
diff --git a/include/linux/fs.h b/include/linux/fs.h
|
|
||||||
index 404cc89..f42a557 100644
|
|
||||||
--- a/include/linux/fs.h
|
|
||||||
+++ b/include/linux/fs.h
|
|
||||||
@@ -424,6 +424,7 @@ extern int sysctl_nr_open;
|
|
||||||
extern struct inodes_stat_t inodes_stat;
|
|
||||||
extern int leases_enable, lease_break_time;
|
|
||||||
extern int sysctl_protected_symlinks;
|
|
||||||
+extern int sysctl_protected_hardlinks;
|
|
||||||
|
|
||||||
struct buffer_head;
|
|
||||||
typedef int (get_block_t)(struct inode *inode, sector_t iblock,
|
|
||||||
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
|
|
||||||
index 0624e7c..0b29d58 100644
|
|
||||||
--- a/kernel/sysctl.c
|
|
||||||
+++ b/kernel/sysctl.c
|
|
||||||
@@ -1497,7 +1497,7 @@ static struct ctl_table fs_table[] = {
|
|
||||||
},
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
-#ifdef CONFIG_PROTECTED_SYMLINKS
|
|
||||||
+#ifdef CONFIG_PROTECTED_LINKS
|
|
||||||
{
|
|
||||||
.procname = "protected_symlinks",
|
|
||||||
.data = &sysctl_protected_symlinks,
|
|
||||||
@@ -1507,6 +1507,15 @@ static struct ctl_table fs_table[] = {
|
|
||||||
.extra1 = &zero,
|
|
||||||
.extra2 = &one,
|
|
||||||
},
|
|
||||||
+ {
|
|
||||||
+ .procname = "protected_hardlinks",
|
|
||||||
+ .data = &sysctl_protected_hardlinks,
|
|
||||||
+ .maxlen = sizeof(int),
|
|
||||||
+ .mode = 0600,
|
|
||||||
+ .proc_handler = proc_dointvec_minmax,
|
|
||||||
+ .extra1 = &zero,
|
|
||||||
+ .extra2 = &one,
|
|
||||||
+ },
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
.procname = "suid_dumpable",
|
|
||||||
--
|
|
||||||
1.7.9.1
|
|
||||||
|
|
|
@ -1,151 +0,0 @@
|
||||||
From d48f814bd83a3cbd95dedaf5e4dd91c05cffddc6 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Kees Cook <keescook@chromium.org>
|
|
||||||
Date: Sat, 25 Feb 2012 12:28:43 +1100
|
|
||||||
Subject: [PATCH 2/5] fs-symlink-restrictions-on-sticky-directories-fix-2
|
|
||||||
|
|
||||||
s/sticky_//
|
|
||||||
|
|
||||||
Cc: Kees Cook <keescook@chromium.org>
|
|
||||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
|
||||||
---
|
|
||||||
Documentation/sysctl/fs.txt | 4 ++--
|
|
||||||
fs/Kconfig | 16 ++++++++--------
|
|
||||||
fs/namei.c | 10 +++++-----
|
|
||||||
include/linux/fs.h | 2 +-
|
|
||||||
kernel/sysctl.c | 6 +++---
|
|
||||||
5 files changed, 19 insertions(+), 19 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/Documentation/sysctl/fs.txt b/Documentation/sysctl/fs.txt
|
|
||||||
index 4b47cd5..01daa80 100644
|
|
||||||
--- a/Documentation/sysctl/fs.txt
|
|
||||||
+++ b/Documentation/sysctl/fs.txt
|
|
||||||
@@ -32,7 +32,7 @@ Currently, these files are in /proc/sys/fs:
|
|
||||||
- nr_open
|
|
||||||
- overflowuid
|
|
||||||
- overflowgid
|
|
||||||
-- protected_sticky_symlinks
|
|
||||||
+- protected_symlinks
|
|
||||||
- suid_dumpable
|
|
||||||
- super-max
|
|
||||||
- super-nr
|
|
||||||
@@ -158,7 +158,7 @@ The default is 65534.
|
|
||||||
|
|
||||||
==============================================================
|
|
||||||
|
|
||||||
-protected_sticky_symlinks:
|
|
||||||
+protected_symlinks:
|
|
||||||
|
|
||||||
A long-standing class of security issues is the symlink-based
|
|
||||||
time-of-check-time-of-use race, most commonly seen in world-writable
|
|
||||||
diff --git a/fs/Kconfig b/fs/Kconfig
|
|
||||||
index d0fdbdd..f2c46f3 100644
|
|
||||||
--- a/fs/Kconfig
|
|
||||||
+++ b/fs/Kconfig
|
|
||||||
@@ -272,7 +272,7 @@ endif # NETWORK_FILESYSTEMS
|
|
||||||
source "fs/nls/Kconfig"
|
|
||||||
source "fs/dlm/Kconfig"
|
|
||||||
|
|
||||||
-config PROTECTED_STICKY_SYMLINKS
|
|
||||||
+config PROTECTED_SYMLINKS
|
|
||||||
bool "Evaluate vulnerable symlink conditions"
|
|
||||||
default y
|
|
||||||
help
|
|
||||||
@@ -285,10 +285,10 @@ config PROTECTED_STICKY_SYMLINKS
|
|
||||||
|
|
||||||
Enabling this adds the logic to examine these dangerous symlink
|
|
||||||
conditions. Whether or not the dangerous symlink situations are
|
|
||||||
- allowed is controlled by PROTECTED_STICKY_SYMLINKS_ENABLED.
|
|
||||||
+ allowed is controlled by PROTECTED_SYMLINKS_ENABLED.
|
|
||||||
|
|
||||||
-config PROTECTED_STICKY_SYMLINKS_ENABLED
|
|
||||||
- depends on PROTECTED_STICKY_SYMLINKS
|
|
||||||
+config PROTECTED_SYMLINKS_ENABLED
|
|
||||||
+ depends on PROTECTED_SYMLINKS
|
|
||||||
bool "Disallow symlink following in sticky world-writable dirs"
|
|
||||||
default y
|
|
||||||
help
|
|
||||||
@@ -298,12 +298,12 @@ config PROTECTED_STICKY_SYMLINKS_ENABLED
|
|
||||||
directory and symlink owners match.
|
|
||||||
|
|
||||||
When PROC_SYSCTL is enabled, this setting can also be controlled
|
|
||||||
- via /proc/sys/kernel/protected_sticky_symlinks.
|
|
||||||
+ via /proc/sys/kernel/protected_symlinks.
|
|
||||||
|
|
||||||
-config PROTECTED_STICKY_SYMLINKS_ENABLED_SYSCTL
|
|
||||||
- depends on PROTECTED_STICKY_SYMLINKS
|
|
||||||
+config PROTECTED_SYMLINKS_ENABLED_SYSCTL
|
|
||||||
+ depends on PROTECTED_SYMLINKS
|
|
||||||
int
|
|
||||||
- default "1" if PROTECTED_STICKY_SYMLINKS_ENABLED
|
|
||||||
+ default "1" if PROTECTED_SYMLINKS_ENABLED
|
|
||||||
default "0"
|
|
||||||
|
|
||||||
endmenu
|
|
||||||
diff --git a/fs/namei.c b/fs/namei.c
|
|
||||||
index 5b4c05b..39edcf7 100644
|
|
||||||
--- a/fs/namei.c
|
|
||||||
+++ b/fs/namei.c
|
|
||||||
@@ -623,16 +623,16 @@ static inline void put_link(struct nameidata *nd, struct path *link, void *cooki
|
|
||||||
path_put(link);
|
|
||||||
}
|
|
||||||
|
|
||||||
-#ifdef CONFIG_PROTECTED_STICKY_SYMLINKS
|
|
||||||
-int sysctl_protected_sticky_symlinks __read_mostly =
|
|
||||||
- CONFIG_PROTECTED_STICKY_SYMLINKS_ENABLED_SYSCTL;
|
|
||||||
+#ifdef CONFIG_PROTECTED_SYMLINKS
|
|
||||||
+int sysctl_protected_symlinks __read_mostly =
|
|
||||||
+ CONFIG_PROTECTED_SYMLINKS_ENABLED_SYSCTL;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* may_follow_link - Check symlink following for unsafe situations
|
|
||||||
* @dentry: The inode/dentry of the symlink
|
|
||||||
* @nameidata: The path data of the symlink
|
|
||||||
*
|
|
||||||
- * In the case of the protected_sticky_symlinks sysctl being enabled,
|
|
||||||
+ * In the case of the protected_symlinks sysctl being enabled,
|
|
||||||
* CAP_DAC_OVERRIDE needs to be specifically ignored if the symlink is
|
|
||||||
* in a sticky world-writable directory. This is to protect privileged
|
|
||||||
* processes from failing races against path names that may change out
|
|
||||||
@@ -651,7 +651,7 @@ may_follow_link(struct dentry *dentry, struct nameidata *nameidata)
|
|
||||||
const struct inode *inode;
|
|
||||||
const struct cred *cred;
|
|
||||||
|
|
||||||
- if (!sysctl_protected_sticky_symlinks)
|
|
||||||
+ if (!sysctl_protected_symlinks)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Allowed if owner and follower match. */
|
|
||||||
diff --git a/include/linux/fs.h b/include/linux/fs.h
|
|
||||||
index aba8db0..404cc89 100644
|
|
||||||
--- a/include/linux/fs.h
|
|
||||||
+++ b/include/linux/fs.h
|
|
||||||
@@ -423,7 +423,7 @@ extern unsigned long get_max_files(void);
|
|
||||||
extern int sysctl_nr_open;
|
|
||||||
extern struct inodes_stat_t inodes_stat;
|
|
||||||
extern int leases_enable, lease_break_time;
|
|
||||||
-extern int sysctl_protected_sticky_symlinks;
|
|
||||||
+extern int sysctl_protected_symlinks;
|
|
||||||
|
|
||||||
struct buffer_head;
|
|
||||||
typedef int (get_block_t)(struct inode *inode, sector_t iblock,
|
|
||||||
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
|
|
||||||
index c469b88..0624e7c 100644
|
|
||||||
--- a/kernel/sysctl.c
|
|
||||||
+++ b/kernel/sysctl.c
|
|
||||||
@@ -1497,10 +1497,10 @@ static struct ctl_table fs_table[] = {
|
|
||||||
},
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
-#ifdef CONFIG_PROTECTED_STICKY_SYMLINKS
|
|
||||||
+#ifdef CONFIG_PROTECTED_SYMLINKS
|
|
||||||
{
|
|
||||||
- .procname = "protected_sticky_symlinks",
|
|
||||||
- .data = &sysctl_protected_sticky_symlinks,
|
|
||||||
+ .procname = "protected_symlinks",
|
|
||||||
+ .data = &sysctl_protected_symlinks,
|
|
||||||
.maxlen = sizeof(int),
|
|
||||||
.mode = 0600,
|
|
||||||
.proc_handler = proc_dointvec_minmax,
|
|
||||||
--
|
|
||||||
1.7.9.1
|
|
||||||
|
|
|
@ -1,329 +0,0 @@
|
||||||
From af16d0017a7de1f00af3966b5013bebfce8a81b4 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Kees Cook <keescook@chromium.org>
|
|
||||||
Date: Sat, 25 Feb 2012 12:28:42 +1100
|
|
||||||
Subject: [PATCH 1/5] fs: symlink restrictions on sticky directories
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: text/plain; charset=UTF-8
|
|
||||||
Content-Transfer-Encoding: 8bit
|
|
||||||
|
|
||||||
A longstanding class of security issues is the symlink-based
|
|
||||||
time-of-check-time-of-use race, most commonly seen in world-writable
|
|
||||||
directories like /tmp. The common method of exploitation of this flaw is
|
|
||||||
to cross privilege boundaries when following a given symlink (i.e. a root
|
|
||||||
process follows a symlink belonging to another user). For a likely
|
|
||||||
incomplete list of hundreds of examples across the years, please see:
|
|
||||||
http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=/tmp
|
|
||||||
|
|
||||||
The solution is to permit symlinks to only be followed when outside a
|
|
||||||
sticky world-writable directory, or when the uid of the symlink and
|
|
||||||
follower match, or when the directory owner matches the symlink's owner.
|
|
||||||
|
|
||||||
Some pointers to the history of earlier discussion that I could find:
|
|
||||||
|
|
||||||
1996 Aug, Zygo Blaxell
|
|
||||||
http://marc.info/?l=bugtraq&m=87602167419830&w=2
|
|
||||||
1996 Oct, Andrew Tridgell
|
|
||||||
http://lkml.indiana.edu/hypermail/linux/kernel/9610.2/0086.html
|
|
||||||
1997 Dec, Albert D Cahalan
|
|
||||||
http://lkml.org/lkml/1997/12/16/4
|
|
||||||
2005 Feb, Lorenzo Hernández García-Hierro
|
|
||||||
http://lkml.indiana.edu/hypermail/linux/kernel/0502.0/1896.html
|
|
||||||
2010 May, Kees Cook
|
|
||||||
https://lkml.org/lkml/2010/5/30/144
|
|
||||||
|
|
||||||
Past objections and rebuttals could be summarized as:
|
|
||||||
|
|
||||||
- Violates POSIX.
|
|
||||||
- POSIX didn't consider this situation and it's not useful to follow
|
|
||||||
a broken specification at the cost of security.
|
|
||||||
- Might break unknown applications that use this feature.
|
|
||||||
- Applications that break because of the change are easy to spot and
|
|
||||||
fix. Applications that are vulnerable to symlink ToCToU by not having
|
|
||||||
the change aren't. Additionally, no applications have yet been found
|
|
||||||
that rely on this behavior.
|
|
||||||
- Applications should just use mkstemp() or O_CREATE|O_EXCL.
|
|
||||||
- True, but applications are not perfect, and new software is written
|
|
||||||
all the time that makes these mistakes; blocking this flaw at the
|
|
||||||
kernel is a single solution to the entire class of vulnerability.
|
|
||||||
- This should live in the core VFS.
|
|
||||||
- This should live in an LSM. (https://lkml.org/lkml/2010/5/31/135)
|
|
||||||
- This should live in an LSM.
|
|
||||||
- This should live in the core VFS. (https://lkml.org/lkml/2010/8/2/188)
|
|
||||||
|
|
||||||
This patch is based on the patch in Openwall and grsecurity, along with
|
|
||||||
suggestions from Al Viro. I have added a sysctl to enable the protected
|
|
||||||
behavior, documentation, and an audit notification.
|
|
||||||
|
|
||||||
[akpm@linux-foundation.org: move sysctl_protected_sticky_symlinks declaration into .h]
|
|
||||||
Signed-off-by: Kees Cook <keescook@chromium.org>
|
|
||||||
Reviewed-by: Ingo Molnar <mingo@elte.hu>
|
|
||||||
Cc: Matthew Wilcox <matthew@wil.cx>
|
|
||||||
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
|
|
||||||
Cc: Rik van Riel <riel@redhat.com>
|
|
||||||
Cc: Federica Teodori <federica.teodori@googlemail.com>
|
|
||||||
Cc: Lucian Adrian Grijincu <lucian.grijincu@gmail.com>
|
|
||||||
Cc: Ingo Molnar <mingo@elte.hu>
|
|
||||||
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
|
|
||||||
Cc: Eric Paris <eparis@redhat.com>
|
|
||||||
Cc: Randy Dunlap <rdunlap@xenotime.net>
|
|
||||||
Cc: Dan Rosenberg <drosenberg@vsecurity.com>
|
|
||||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
|
||||||
---
|
|
||||||
Documentation/sysctl/fs.txt | 21 ++++++++++
|
|
||||||
fs/Kconfig | 34 ++++++++++++++++
|
|
||||||
fs/namei.c | 91 ++++++++++++++++++++++++++++++++++++++++---
|
|
||||||
include/linux/fs.h | 1 +
|
|
||||||
kernel/sysctl.c | 11 +++++
|
|
||||||
5 files changed, 152 insertions(+), 6 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/Documentation/sysctl/fs.txt b/Documentation/sysctl/fs.txt
|
|
||||||
index 88fd7f5..4b47cd5 100644
|
|
||||||
--- a/Documentation/sysctl/fs.txt
|
|
||||||
+++ b/Documentation/sysctl/fs.txt
|
|
||||||
@@ -32,6 +32,7 @@ Currently, these files are in /proc/sys/fs:
|
|
||||||
- nr_open
|
|
||||||
- overflowuid
|
|
||||||
- overflowgid
|
|
||||||
+- protected_sticky_symlinks
|
|
||||||
- suid_dumpable
|
|
||||||
- super-max
|
|
||||||
- super-nr
|
|
||||||
@@ -157,6 +158,26 @@ The default is 65534.
|
|
||||||
|
|
||||||
==============================================================
|
|
||||||
|
|
||||||
+protected_sticky_symlinks:
|
|
||||||
+
|
|
||||||
+A long-standing class of security issues is the symlink-based
|
|
||||||
+time-of-check-time-of-use race, most commonly seen in world-writable
|
|
||||||
+directories like /tmp. The common method of exploitation of this flaw
|
|
||||||
+is to cross privilege boundaries when following a given symlink (i.e. a
|
|
||||||
+root process follows a symlink belonging to another user). For a likely
|
|
||||||
+incomplete list of hundreds of examples across the years, please see:
|
|
||||||
+http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=/tmp
|
|
||||||
+
|
|
||||||
+When set to "0", symlink following behavior is unrestricted.
|
|
||||||
+
|
|
||||||
+When set to "1" symlinks are permitted to be followed only when outside
|
|
||||||
+a sticky world-writable directory, or when the uid of the symlink and
|
|
||||||
+follower match, or when the directory owner matches the symlink's owner.
|
|
||||||
+
|
|
||||||
+This protection is based on the restrictions in Openwall and grsecurity.
|
|
||||||
+
|
|
||||||
+==============================================================
|
|
||||||
+
|
|
||||||
suid_dumpable:
|
|
||||||
|
|
||||||
This value can be used to query and set the core dump mode for setuid
|
|
||||||
diff --git a/fs/Kconfig b/fs/Kconfig
|
|
||||||
index 1497ddf..d0fdbdd 100644
|
|
||||||
--- a/fs/Kconfig
|
|
||||||
+++ b/fs/Kconfig
|
|
||||||
@@ -272,4 +272,38 @@ endif # NETWORK_FILESYSTEMS
|
|
||||||
source "fs/nls/Kconfig"
|
|
||||||
source "fs/dlm/Kconfig"
|
|
||||||
|
|
||||||
+config PROTECTED_STICKY_SYMLINKS
|
|
||||||
+ bool "Evaluate vulnerable symlink conditions"
|
|
||||||
+ default y
|
|
||||||
+ help
|
|
||||||
+ A long-standing class of security issues is the symlink-based
|
|
||||||
+ time-of-check-time-of-use race, most commonly seen in
|
|
||||||
+ world-writable directories like /tmp. The common method of
|
|
||||||
+ exploitation of this flaw is to cross privilege boundaries
|
|
||||||
+ when following a given symlink (i.e. a root process follows
|
|
||||||
+ a malicious symlink belonging to another user).
|
|
||||||
+
|
|
||||||
+ Enabling this adds the logic to examine these dangerous symlink
|
|
||||||
+ conditions. Whether or not the dangerous symlink situations are
|
|
||||||
+ allowed is controlled by PROTECTED_STICKY_SYMLINKS_ENABLED.
|
|
||||||
+
|
|
||||||
+config PROTECTED_STICKY_SYMLINKS_ENABLED
|
|
||||||
+ depends on PROTECTED_STICKY_SYMLINKS
|
|
||||||
+ bool "Disallow symlink following in sticky world-writable dirs"
|
|
||||||
+ default y
|
|
||||||
+ help
|
|
||||||
+ Solve ToCToU symlink race vulnerablities by permitting symlinks
|
|
||||||
+ to be followed only when outside a sticky world-writable directory,
|
|
||||||
+ or when the uid of the symlink and follower match, or when the
|
|
||||||
+ directory and symlink owners match.
|
|
||||||
+
|
|
||||||
+ When PROC_SYSCTL is enabled, this setting can also be controlled
|
|
||||||
+ via /proc/sys/kernel/protected_sticky_symlinks.
|
|
||||||
+
|
|
||||||
+config PROTECTED_STICKY_SYMLINKS_ENABLED_SYSCTL
|
|
||||||
+ depends on PROTECTED_STICKY_SYMLINKS
|
|
||||||
+ int
|
|
||||||
+ default "1" if PROTECTED_STICKY_SYMLINKS_ENABLED
|
|
||||||
+ default "0"
|
|
||||||
+
|
|
||||||
endmenu
|
|
||||||
diff --git a/fs/namei.c b/fs/namei.c
|
|
||||||
index 5d1fab5..5b4c05b 100644
|
|
||||||
--- a/fs/namei.c
|
|
||||||
+++ b/fs/namei.c
|
|
||||||
@@ -623,10 +623,84 @@ static inline void put_link(struct nameidata *nd, struct path *link, void *cooki
|
|
||||||
path_put(link);
|
|
||||||
}
|
|
||||||
|
|
||||||
+#ifdef CONFIG_PROTECTED_STICKY_SYMLINKS
|
|
||||||
+int sysctl_protected_sticky_symlinks __read_mostly =
|
|
||||||
+ CONFIG_PROTECTED_STICKY_SYMLINKS_ENABLED_SYSCTL;
|
|
||||||
+
|
|
||||||
+/**
|
|
||||||
+ * may_follow_link - Check symlink following for unsafe situations
|
|
||||||
+ * @dentry: The inode/dentry of the symlink
|
|
||||||
+ * @nameidata: The path data of the symlink
|
|
||||||
+ *
|
|
||||||
+ * In the case of the protected_sticky_symlinks sysctl being enabled,
|
|
||||||
+ * CAP_DAC_OVERRIDE needs to be specifically ignored if the symlink is
|
|
||||||
+ * in a sticky world-writable directory. This is to protect privileged
|
|
||||||
+ * processes from failing races against path names that may change out
|
|
||||||
+ * from under them by way of other users creating malicious symlinks.
|
|
||||||
+ * It will permit symlinks to be followed only when outside a sticky
|
|
||||||
+ * world-writable directory, or when the uid of the symlink and follower
|
|
||||||
+ * match, or when the directory owner matches the symlink's owner.
|
|
||||||
+ *
|
|
||||||
+ * Returns 0 if following the symlink is allowed, -ve on error.
|
|
||||||
+ */
|
|
||||||
+static inline int
|
|
||||||
+may_follow_link(struct dentry *dentry, struct nameidata *nameidata)
|
|
||||||
+{
|
|
||||||
+ int error = 0;
|
|
||||||
+ const struct inode *parent;
|
|
||||||
+ const struct inode *inode;
|
|
||||||
+ const struct cred *cred;
|
|
||||||
+
|
|
||||||
+ if (!sysctl_protected_sticky_symlinks)
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+ /* Allowed if owner and follower match. */
|
|
||||||
+ cred = current_cred();
|
|
||||||
+ inode = dentry->d_inode;
|
|
||||||
+ if (cred->fsuid == inode->i_uid)
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+ /* Check parent directory mode and owner. */
|
|
||||||
+ spin_lock(&dentry->d_lock);
|
|
||||||
+ parent = dentry->d_parent->d_inode;
|
|
||||||
+ if ((parent->i_mode & (S_ISVTX|S_IWOTH)) == (S_ISVTX|S_IWOTH) &&
|
|
||||||
+ parent->i_uid != inode->i_uid) {
|
|
||||||
+ error = -EACCES;
|
|
||||||
+ }
|
|
||||||
+ spin_unlock(&dentry->d_lock);
|
|
||||||
+
|
|
||||||
+#ifdef CONFIG_AUDIT
|
|
||||||
+ if (error) {
|
|
||||||
+ struct audit_buffer *ab;
|
|
||||||
+
|
|
||||||
+ ab = audit_log_start(current->audit_context,
|
|
||||||
+ GFP_KERNEL, AUDIT_AVC);
|
|
||||||
+ audit_log_format(ab, "op=follow_link action=denied");
|
|
||||||
+ audit_log_format(ab, " pid=%d comm=", current->pid);
|
|
||||||
+ audit_log_untrustedstring(ab, current->comm);
|
|
||||||
+ audit_log_d_path(ab, " path=", &nameidata->path);
|
|
||||||
+ audit_log_format(ab, " name=");
|
|
||||||
+ audit_log_untrustedstring(ab, dentry->d_name.name);
|
|
||||||
+ audit_log_format(ab, " dev=");
|
|
||||||
+ audit_log_untrustedstring(ab, inode->i_sb->s_id);
|
|
||||||
+ audit_log_format(ab, " ino=%lu", inode->i_ino);
|
|
||||||
+ audit_log_end(ab);
|
|
||||||
+ }
|
|
||||||
+#endif
|
|
||||||
+ return error;
|
|
||||||
+}
|
|
||||||
+#else
|
|
||||||
+static inline int
|
|
||||||
+may_follow_link(struct dentry *dentry, struct nameidata *nameidata)
|
|
||||||
+{
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
static __always_inline int
|
|
||||||
-follow_link(struct path *link, struct nameidata *nd, void **p)
|
|
||||||
+follow_link(struct path *link, struct nameidata *nd, void **p, bool sensitive)
|
|
||||||
{
|
|
||||||
- int error;
|
|
||||||
+ int error = 0;
|
|
||||||
struct dentry *dentry = link->dentry;
|
|
||||||
|
|
||||||
BUG_ON(nd->flags & LOOKUP_RCU);
|
|
||||||
@@ -645,7 +719,10 @@ follow_link(struct path *link, struct nameidata *nd, void **p)
|
|
||||||
touch_atime(link->mnt, dentry);
|
|
||||||
nd_set_link(nd, NULL);
|
|
||||||
|
|
||||||
- error = security_inode_follow_link(link->dentry, nd);
|
|
||||||
+ if (sensitive)
|
|
||||||
+ error = may_follow_link(link->dentry, nd);
|
|
||||||
+ if (!error)
|
|
||||||
+ error = security_inode_follow_link(link->dentry, nd);
|
|
||||||
if (error) {
|
|
||||||
*p = ERR_PTR(error); /* no ->put_link(), please */
|
|
||||||
path_put(&nd->path);
|
|
||||||
@@ -1342,7 +1419,7 @@ static inline int nested_symlink(struct path *path, struct nameidata *nd)
|
|
||||||
struct path link = *path;
|
|
||||||
void *cookie;
|
|
||||||
|
|
||||||
- res = follow_link(&link, nd, &cookie);
|
|
||||||
+ res = follow_link(&link, nd, &cookie, false);
|
|
||||||
if (!res)
|
|
||||||
res = walk_component(nd, path, &nd->last,
|
|
||||||
nd->last_type, LOOKUP_FOLLOW);
|
|
||||||
@@ -1615,7 +1692,8 @@ static int path_lookupat(int dfd, const char *name,
|
|
||||||
void *cookie;
|
|
||||||
struct path link = path;
|
|
||||||
nd->flags |= LOOKUP_PARENT;
|
|
||||||
- err = follow_link(&link, nd, &cookie);
|
|
||||||
+
|
|
||||||
+ err = follow_link(&link, nd, &cookie, true);
|
|
||||||
if (!err)
|
|
||||||
err = lookup_last(nd, &path);
|
|
||||||
put_link(nd, &link, cookie);
|
|
||||||
@@ -2327,7 +2405,8 @@ static struct file *path_openat(int dfd, const char *pathname,
|
|
||||||
}
|
|
||||||
nd->flags |= LOOKUP_PARENT;
|
|
||||||
nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
|
|
||||||
- error = follow_link(&link, nd, &cookie);
|
|
||||||
+
|
|
||||||
+ error = follow_link(&link, nd, &cookie, true);
|
|
||||||
if (unlikely(error))
|
|
||||||
filp = ERR_PTR(error);
|
|
||||||
else
|
|
||||||
diff --git a/include/linux/fs.h b/include/linux/fs.h
|
|
||||||
index 9808b21..aba8db0 100644
|
|
||||||
--- a/include/linux/fs.h
|
|
||||||
+++ b/include/linux/fs.h
|
|
||||||
@@ -423,6 +423,7 @@ extern unsigned long get_max_files(void);
|
|
||||||
extern int sysctl_nr_open;
|
|
||||||
extern struct inodes_stat_t inodes_stat;
|
|
||||||
extern int leases_enable, lease_break_time;
|
|
||||||
+extern int sysctl_protected_sticky_symlinks;
|
|
||||||
|
|
||||||
struct buffer_head;
|
|
||||||
typedef int (get_block_t)(struct inode *inode, sector_t iblock,
|
|
||||||
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
|
|
||||||
index 62538ee..c469b88 100644
|
|
||||||
--- a/kernel/sysctl.c
|
|
||||||
+++ b/kernel/sysctl.c
|
|
||||||
@@ -1497,6 +1497,17 @@ static struct ctl_table fs_table[] = {
|
|
||||||
},
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
+#ifdef CONFIG_PROTECTED_STICKY_SYMLINKS
|
|
||||||
+ {
|
|
||||||
+ .procname = "protected_sticky_symlinks",
|
|
||||||
+ .data = &sysctl_protected_sticky_symlinks,
|
|
||||||
+ .maxlen = sizeof(int),
|
|
||||||
+ .mode = 0600,
|
|
||||||
+ .proc_handler = proc_dointvec_minmax,
|
|
||||||
+ .extra1 = &zero,
|
|
||||||
+ .extra2 = &one,
|
|
||||||
+ },
|
|
||||||
+#endif
|
|
||||||
{
|
|
||||||
.procname = "suid_dumpable",
|
|
||||||
.data = &suid_dumpable,
|
|
||||||
--
|
|
||||||
1.7.9.1
|
|
||||||
|
|
|
@ -62,11 +62,9 @@ bugfix/x86/KVM-nVMX-Fix-warning-causing-idt-vectoring-info-beha.patch
|
||||||
|
|
||||||
features/all/hwmon-it87-Add-IT8728F-support.patch
|
features/all/hwmon-it87-Add-IT8728F-support.patch
|
||||||
|
|
||||||
features/all/fs-symlink-restrictions-on-sticky-directories.patch
|
# Add link security restrictions from 3.6
|
||||||
features/all/fs-symlink-restrictions-on-sticky-directories-fix-2.patch
|
features/all/fs-add-link-restrictions.patch
|
||||||
features/all/fs-hardlink-creation-restrictions.patch
|
features/all/fs-add-link-restriction-audit-reporting.patch
|
||||||
features/all/fs-hardlink-creation-restrictions-fix.patch
|
|
||||||
features/all/fs-hardlink-creation-restriction-cleanup.patch
|
|
||||||
|
|
||||||
# Update all Hyper-V drivers to 3.4-rc1 (no longer staging)
|
# Update all Hyper-V drivers to 3.4-rc1 (no longer staging)
|
||||||
features/x86/hyperv/0001-NLS-improve-UTF8-UTF16-string-conversion-routine.patch
|
features/x86/hyperv/0001-NLS-improve-UTF8-UTF16-string-conversion-routine.patch
|
||||||
|
|
Loading…
Reference in New Issue