Skip to contentSkip to conversation

fs: allow protected cross-uid sticky symlinks

PATCH Superseded Kees Cook 2 participants 5 messages Jun 1, 2010

Subsystems (3)

v3 of 6 Superseded Latest is v6 →

lore
Share & tools
lore

Kees Cook

author
#0 lore raw

PATCH v3 1/1 fs: allow protected cross-uid 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

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

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.
- 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 patch is based on the patch in Openwall and grsecurity. I have added a sysctl to enable the protected behavior, documentation, and a ratelimited warning.

v2:
- dropped redundant S_ISLNK check.
- moved sysctl extern into security.h.
- asked to include CC to linux-fsdevel.

v3:
- move into VFS core.
- add CONFIG entry for build-time default.
- rename sysctl, invert logic.
- use get_task_comm for task name.
- lock dentry when checking parent.

---
 Documentation/sysctl/fs.txt |   15 +++++++++
 fs/Kconfig                  |   15 +++++++++
 fs/namei.c                  |   69 +++++++++++++++++++++++++++++++++++++++----
 kernel/sysctl.c             |   10 ++++++
 4 files changed, 103 insertions(+), 6 deletions(-)
Documentation/sysctl/fs.txt +15 -0
index 6268250..07f3b5b 100644
@@ -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
@@ -158,6 +159,20 @@ The default is 65534.
 
 ==============================================================
 
+protected-sticky-symlinks:
+
+Following symlinks in sticky world-writable directories (like /tmp) can
+be dangerous due to time-of-check-time-of-use races that frequently result
+in security vulnerabilities.
+
+The default value is "0", leaving the behavior of symlink following
+unchanged from POSIX.  A value of "1" will enable the protection, causing
+symlinks to be followable only if outside a sticky world-writable directory,
+or if the symlink and the follower's uid match, or if the symlink and its
+directory are owned by the same uid.
+
+==============================================================
+
 suid_dumpable:
 
 This value can be used to query and set the core dump mode for setuid
fs/Kconfig +15 -0
index 5f85b59..59497c7 100644
@@ -256,3 +256,18 @@ source "fs/nls/Kconfig"
 source "fs/dlm/Kconfig"
 
 endmenu
+
+config PROTECTED_STICKY_SYMLINKS
+	bool "Protect symlink following in sticky world-writable directories"
+	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 solves the problem by permitting 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 and symlink owners match.
fs/namei.c +63 -6

99 lines not on this page · raw message

kernel/sysctl.c +10 -0
index 997080f..56affd6 100644
@@ -87,6 +87,7 @@ extern int sysctl_oom_kill_allocating_task;
 extern int sysctl_oom_dump_tasks;
 extern int max_threads;
 extern int core_uses_pid;
+extern int protected_sticky_symlinks;
 extern int suid_dumpable;
 extern char core_pattern[];
 extern unsigned int core_pipe_limit;
@@ -1455,6 +1456,15 @@ static struct ctl_table fs_table[] = {
 #endif
 #endif
 	{
+		.procname	= "protected-sticky-symlinks",
+		.data		= &protected_sticky_symlinks,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec_minmax,
+		.extra1		= &zero,
+		.extra2		= &one,
+	},
+	{
 		.procname	= "suid_dumpable",
 		.data		= &suid_dumpable,
 		.maxlen		= sizeof(int),
Signature · 5 lines
1.7.0.4

-- 
Kees Cook
Ubuntu Security Team
maintainer
#1 lore raw
Quoted 8 lines · Kees Cook

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

Quoted 5 lines

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:

I don't buy it. If we are concerned about the symlinks in the middle of pathname, your checks are useless (mkdir /tmp/a, ln -s whatever /tmp/a/b, have victim open /tmp/a/b/something). If we are not, then your checks are in the wrong place.

"The more we prohibit, the safer we are" is best left to the likes of TSA; if we are really interested in security and not in security theatre or BDSM fetishism, let's make sure that heuristics we use make sense.

author
#2 lore raw
Quoted 13 lines · Al Viro
Quoted 12 lines · Kees Cook

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.

Quoted 6 lines
Quoted 1 line

Some pointers to the history of earlier discussion that I could find:

I don't buy it. If we are concerned about the symlinks in the middle of pathname, your checks are useless (mkdir /tmp/a, ln -s whatever /tmp/a/b, have victim open /tmp/a/b/something). If we are not, then your checks are in the wrong place.

Well, that's not traditionally where the problems happen, but I have no problem strengthening the protection to include a full examination of the entire path looking for sticky/world-writable directories.

If not, what is the right place for the checks?

Quoted 3 lines

"The more we prohibit, the safer we are" is best left to the likes of TSA; if we are really interested in security and not in security theatre or BDSM fetishism, let's make sure that heuristics we use make sense.

I'm not suggesting we remove symlinks. :) I don't feel that there is any theatre here, since it only eliminates a strictly bad situation. If there are even more strictly bad situations, then we should eliminate those too.

-Kees

Signature · 2 lines
Kees Cook
Ubuntu Security Team
maintainer
#3 lore raw
Quoted 10 lines · Kees Cook
Quoted 4 lines

I don't buy it. If we are concerned about the symlinks in the middle of pathname, your checks are useless (mkdir /tmp/a, ln -s whatever /tmp/a/b, have victim open /tmp/a/b/something). If we are not, then your checks are in the wrong place.

Well, that's not traditionally where the problems happen, but I have no problem strengthening the protection to include a full examination of the entire path looking for sticky/world-writable directories.

If not, what is the right place for the checks?

Handling of trailing symlink on open(). At most. And I wouldn't be surprised if the real answer turns out to include "... if we have O_CREAT in flags", but that needs to be determined.

author
#4 lore raw
Quoted 6 lines · Al Viro
Quoted 5 lines · Kees Cook
Quoted 4 lines

I don't buy it. If we are concerned about the symlinks in the middle of pathname, your checks are useless (mkdir /tmp/a, ln -s whatever /tmp/a/b, have victim open /tmp/a/b/something). If we are not, then your checks are in the wrong place.

Quoted 7 lines
Quoted 5 lines

Well, that's not traditionally where the problems happen, but I have no problem strengthening the protection to include a full examination of the entire path looking for sticky/world-writable directories.

If not, what is the right place for the checks?

Handling of trailing symlink on open(). At most.

What would this look like? Moving the checks into may_open()?

Quoted 3 lines

And I wouldn't be
surprised if the real answer turns out to include "... if we have O_CREAT in flags", but that needs to be determined.

I think even without O_CREAT the protection is needed (some of the /tmp-races are things like reading a file pointed to by a symlink and spewing the contents to stderr, etc).

Thanks,

-Kees

Signature · 2 lines
Kees Cook
Ubuntu Security Team