1
From af16d0017a7de1f00af3966b5013bebfce8a81b4 Mon Sep 17 00:00:00 2001
2
From: Kees Cook <keescook@chromium.org>
3
Date: Sat, 25 Feb 2012 12:28:42 +1100
4
Subject: [PATCH 1/5] fs: symlink restrictions on sticky directories
6
Content-Type: text/plain; charset=UTF-8
7
Content-Transfer-Encoding: 8bit
9
A longstanding class of security issues is the symlink-based
10
time-of-check-time-of-use race, most commonly seen in world-writable
11
directories like /tmp. The common method of exploitation of this flaw is
12
to cross privilege boundaries when following a given symlink (i.e. a root
13
process follows a symlink belonging to another user). For a likely
14
incomplete list of hundreds of examples across the years, please see:
15
http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=/tmp
17
The solution is to permit symlinks to only be followed when outside a
18
sticky world-writable directory, or when the uid of the symlink and
19
follower match, or when the directory owner matches the symlink's owner.
21
Some pointers to the history of earlier discussion that I could find:
23
1996 Aug, Zygo Blaxell
24
http://marc.info/?l=bugtraq&m=87602167419830&w=2
25
1996 Oct, Andrew Tridgell
26
http://lkml.indiana.edu/hypermail/linux/kernel/9610.2/0086.html
27
1997 Dec, Albert D Cahalan
28
http://lkml.org/lkml/1997/12/16/4
29
2005 Feb, Lorenzo Hernández García-Hierro
30
http://lkml.indiana.edu/hypermail/linux/kernel/0502.0/1896.html
32
https://lkml.org/lkml/2010/5/30/144
34
Past objections and rebuttals could be summarized as:
37
- POSIX didn't consider this situation and it's not useful to follow
38
a broken specification at the cost of security.
39
- Might break unknown applications that use this feature.
40
- Applications that break because of the change are easy to spot and
41
fix. Applications that are vulnerable to symlink ToCToU by not having
42
the change aren't. Additionally, no applications have yet been found
43
that rely on this behavior.
44
- Applications should just use mkstemp() or O_CREATE|O_EXCL.
45
- True, but applications are not perfect, and new software is written
46
all the time that makes these mistakes; blocking this flaw at the
47
kernel is a single solution to the entire class of vulnerability.
48
- This should live in the core VFS.
49
- This should live in an LSM. (https://lkml.org/lkml/2010/5/31/135)
50
- This should live in an LSM.
51
- This should live in the core VFS. (https://lkml.org/lkml/2010/8/2/188)
53
This patch is based on the patch in Openwall and grsecurity, along with
54
suggestions from Al Viro. I have added a sysctl to enable the protected
55
behavior, documentation, and an audit notification.
57
[akpm@linux-foundation.org: move sysctl_protected_sticky_symlinks declaration into .h]
58
Signed-off-by: Kees Cook <keescook@chromium.org>
59
Reviewed-by: Ingo Molnar <mingo@elte.hu>
60
Cc: Matthew Wilcox <matthew@wil.cx>
61
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
62
Cc: Rik van Riel <riel@redhat.com>
63
Cc: Federica Teodori <federica.teodori@googlemail.com>
64
Cc: Lucian Adrian Grijincu <lucian.grijincu@gmail.com>
65
Cc: Ingo Molnar <mingo@elte.hu>
66
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
67
Cc: Eric Paris <eparis@redhat.com>
68
Cc: Randy Dunlap <rdunlap@xenotime.net>
69
Cc: Dan Rosenberg <drosenberg@vsecurity.com>
70
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
72
Documentation/sysctl/fs.txt | 21 ++++++++++
73
fs/Kconfig | 34 ++++++++++++++++
74
fs/namei.c | 91 ++++++++++++++++++++++++++++++++++++++++---
75
include/linux/fs.h | 1 +
76
kernel/sysctl.c | 11 +++++
77
5 files changed, 152 insertions(+), 6 deletions(-)
79
diff --git a/Documentation/sysctl/fs.txt b/Documentation/sysctl/fs.txt
80
index 88fd7f5..4b47cd5 100644
81
--- a/Documentation/sysctl/fs.txt
82
+++ b/Documentation/sysctl/fs.txt
83
@@ -32,6 +32,7 @@ Currently, these files are in /proc/sys/fs:
87
+- protected_sticky_symlinks
91
@@ -157,6 +158,26 @@ The default is 65534.
93
==============================================================
95
+protected_sticky_symlinks:
97
+A long-standing class of security issues is the symlink-based
98
+time-of-check-time-of-use race, most commonly seen in world-writable
99
+directories like /tmp. The common method of exploitation of this flaw
100
+is to cross privilege boundaries when following a given symlink (i.e. a
101
+root process follows a symlink belonging to another user). For a likely
102
+incomplete list of hundreds of examples across the years, please see:
103
+http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=/tmp
105
+When set to "0", symlink following behavior is unrestricted.
107
+When set to "1" symlinks are permitted to be followed only when outside
108
+a sticky world-writable directory, or when the uid of the symlink and
109
+follower match, or when the directory owner matches the symlink's owner.
111
+This protection is based on the restrictions in Openwall and grsecurity.
113
+==============================================================
117
This value can be used to query and set the core dump mode for setuid
118
diff --git a/fs/Kconfig b/fs/Kconfig
119
index 1497ddf..d0fdbdd 100644
122
@@ -272,4 +272,38 @@ endif # NETWORK_FILESYSTEMS
123
source "fs/nls/Kconfig"
124
source "fs/dlm/Kconfig"
126
+config PROTECTED_STICKY_SYMLINKS
127
+ bool "Evaluate vulnerable symlink conditions"
130
+ A long-standing class of security issues is the symlink-based
131
+ time-of-check-time-of-use race, most commonly seen in
132
+ world-writable directories like /tmp. The common method of
133
+ exploitation of this flaw is to cross privilege boundaries
134
+ when following a given symlink (i.e. a root process follows
135
+ a malicious symlink belonging to another user).
137
+ Enabling this adds the logic to examine these dangerous symlink
138
+ conditions. Whether or not the dangerous symlink situations are
139
+ allowed is controlled by PROTECTED_STICKY_SYMLINKS_ENABLED.
141
+config PROTECTED_STICKY_SYMLINKS_ENABLED
142
+ depends on PROTECTED_STICKY_SYMLINKS
143
+ bool "Disallow symlink following in sticky world-writable dirs"
146
+ Solve ToCToU symlink race vulnerablities by permitting symlinks
147
+ to be followed only when outside a sticky world-writable directory,
148
+ or when the uid of the symlink and follower match, or when the
149
+ directory and symlink owners match.
151
+ When PROC_SYSCTL is enabled, this setting can also be controlled
152
+ via /proc/sys/kernel/protected_sticky_symlinks.
154
+config PROTECTED_STICKY_SYMLINKS_ENABLED_SYSCTL
155
+ depends on PROTECTED_STICKY_SYMLINKS
157
+ default "1" if PROTECTED_STICKY_SYMLINKS_ENABLED
161
diff --git a/fs/namei.c b/fs/namei.c
162
index 5d1fab5..5b4c05b 100644
165
@@ -623,10 +623,84 @@ static inline void put_link(struct nameidata *nd, struct path *link, void *cooki
169
+#ifdef CONFIG_PROTECTED_STICKY_SYMLINKS
170
+int sysctl_protected_sticky_symlinks __read_mostly =
171
+ CONFIG_PROTECTED_STICKY_SYMLINKS_ENABLED_SYSCTL;
174
+ * may_follow_link - Check symlink following for unsafe situations
175
+ * @dentry: The inode/dentry of the symlink
176
+ * @nameidata: The path data of the symlink
178
+ * In the case of the protected_sticky_symlinks sysctl being enabled,
179
+ * CAP_DAC_OVERRIDE needs to be specifically ignored if the symlink is
180
+ * in a sticky world-writable directory. This is to protect privileged
181
+ * processes from failing races against path names that may change out
182
+ * from under them by way of other users creating malicious symlinks.
183
+ * It will permit symlinks to be followed only when outside a sticky
184
+ * world-writable directory, or when the uid of the symlink and follower
185
+ * match, or when the directory owner matches the symlink's owner.
187
+ * Returns 0 if following the symlink is allowed, -ve on error.
190
+may_follow_link(struct dentry *dentry, struct nameidata *nameidata)
193
+ const struct inode *parent;
194
+ const struct inode *inode;
195
+ const struct cred *cred;
197
+ if (!sysctl_protected_sticky_symlinks)
200
+ /* Allowed if owner and follower match. */
201
+ cred = current_cred();
202
+ inode = dentry->d_inode;
203
+ if (cred->fsuid == inode->i_uid)
206
+ /* Check parent directory mode and owner. */
207
+ spin_lock(&dentry->d_lock);
208
+ parent = dentry->d_parent->d_inode;
209
+ if ((parent->i_mode & (S_ISVTX|S_IWOTH)) == (S_ISVTX|S_IWOTH) &&
210
+ parent->i_uid != inode->i_uid) {
213
+ spin_unlock(&dentry->d_lock);
217
+ struct audit_buffer *ab;
219
+ ab = audit_log_start(current->audit_context,
220
+ GFP_KERNEL, AUDIT_AVC);
221
+ audit_log_format(ab, "op=follow_link action=denied");
222
+ audit_log_format(ab, " pid=%d comm=", current->pid);
223
+ audit_log_untrustedstring(ab, current->comm);
224
+ audit_log_d_path(ab, " path=", &nameidata->path);
225
+ audit_log_format(ab, " name=");
226
+ audit_log_untrustedstring(ab, dentry->d_name.name);
227
+ audit_log_format(ab, " dev=");
228
+ audit_log_untrustedstring(ab, inode->i_sb->s_id);
229
+ audit_log_format(ab, " ino=%lu", inode->i_ino);
237
+may_follow_link(struct dentry *dentry, struct nameidata *nameidata)
243
static __always_inline int
244
-follow_link(struct path *link, struct nameidata *nd, void **p)
245
+follow_link(struct path *link, struct nameidata *nd, void **p, bool sensitive)
249
struct dentry *dentry = link->dentry;
251
BUG_ON(nd->flags & LOOKUP_RCU);
252
@@ -645,7 +719,10 @@ follow_link(struct path *link, struct nameidata *nd, void **p)
253
touch_atime(link->mnt, dentry);
254
nd_set_link(nd, NULL);
256
- error = security_inode_follow_link(link->dentry, nd);
258
+ error = may_follow_link(link->dentry, nd);
260
+ error = security_inode_follow_link(link->dentry, nd);
262
*p = ERR_PTR(error); /* no ->put_link(), please */
264
@@ -1342,7 +1419,7 @@ static inline int nested_symlink(struct path *path, struct nameidata *nd)
265
struct path link = *path;
268
- res = follow_link(&link, nd, &cookie);
269
+ res = follow_link(&link, nd, &cookie, false);
271
res = walk_component(nd, path, &nd->last,
272
nd->last_type, LOOKUP_FOLLOW);
273
@@ -1615,7 +1692,8 @@ static int path_lookupat(int dfd, const char *name,
275
struct path link = path;
276
nd->flags |= LOOKUP_PARENT;
277
- err = follow_link(&link, nd, &cookie);
279
+ err = follow_link(&link, nd, &cookie, true);
281
err = lookup_last(nd, &path);
282
put_link(nd, &link, cookie);
283
@@ -2327,7 +2405,8 @@ static struct file *path_openat(int dfd, const char *pathname,
285
nd->flags |= LOOKUP_PARENT;
286
nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
287
- error = follow_link(&link, nd, &cookie);
289
+ error = follow_link(&link, nd, &cookie, true);
291
filp = ERR_PTR(error);
293
diff --git a/include/linux/fs.h b/include/linux/fs.h
294
index 9808b21..aba8db0 100644
295
--- a/include/linux/fs.h
296
+++ b/include/linux/fs.h
297
@@ -423,6 +423,7 @@ extern unsigned long get_max_files(void);
298
extern int sysctl_nr_open;
299
extern struct inodes_stat_t inodes_stat;
300
extern int leases_enable, lease_break_time;
301
+extern int sysctl_protected_sticky_symlinks;
304
typedef int (get_block_t)(struct inode *inode, sector_t iblock,
305
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
306
index 62538ee..c469b88 100644
307
--- a/kernel/sysctl.c
308
+++ b/kernel/sysctl.c
309
@@ -1497,6 +1497,17 @@ static struct ctl_table fs_table[] = {
313
+#ifdef CONFIG_PROTECTED_STICKY_SYMLINKS
315
+ .procname = "protected_sticky_symlinks",
316
+ .data = &sysctl_protected_sticky_symlinks,
317
+ .maxlen = sizeof(int),
319
+ .proc_handler = proc_dointvec_minmax,
325
.procname = "suid_dumpable",
326
.data = &suid_dumpable,