From 4a4fd06c6709bc363c32c737312915dc692173ca Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sun, 5 Aug 2012 21:02:07 +0000 Subject: [PATCH] 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 --- debian/changelog | 4 + ...add-link-restriction-audit-reporting.patch | 93 +++++ .../all/fs-add-link-restrictions.patch | 356 ++++++++++++++++ ...ardlink-creation-restriction-cleanup.patch | 112 ----- ...s-hardlink-creation-restrictions-fix.patch | 39 -- .../fs-hardlink-creation-restrictions.patch | 390 ------------------ ...rictions-on-sticky-directories-fix-2.patch | 151 ------- ...k-restrictions-on-sticky-directories.patch | 329 --------------- debian/patches/series | 8 +- 9 files changed, 456 insertions(+), 1026 deletions(-) create mode 100644 debian/patches/features/all/fs-add-link-restriction-audit-reporting.patch create mode 100644 debian/patches/features/all/fs-add-link-restrictions.patch delete mode 100644 debian/patches/features/all/fs-hardlink-creation-restriction-cleanup.patch delete mode 100644 debian/patches/features/all/fs-hardlink-creation-restrictions-fix.patch delete mode 100644 debian/patches/features/all/fs-hardlink-creation-restrictions.patch delete mode 100644 debian/patches/features/all/fs-symlink-restrictions-on-sticky-directories-fix-2.patch delete mode 100644 debian/patches/features/all/fs-symlink-restrictions-on-sticky-directories.patch diff --git a/debian/changelog b/debian/changelog index 690d8735a..50332f766 100644 --- a/debian/changelog +++ b/debian/changelog @@ -57,6 +57,10 @@ linux (3.2.26-1) UNRELEASED; urgency=low * net: Add byte queue limits (bql) for reduced buffer-bloat * bnx2,bnx2x,e1000e,forcedeth,igb,ixgbe,r8169,sfc,skge,sky2,tg3: 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 Tue, 24 Jul 2012 02:20:37 +0100 diff --git a/debian/patches/features/all/fs-add-link-restriction-audit-reporting.patch b/debian/patches/features/all/fs-add-link-restriction-audit-reporting.patch new file mode 100644 index 000000000..68ed59ed4 --- /dev/null +++ b/debian/patches/features/all/fs-add-link-restriction-audit-reporting.patch @@ -0,0 +1,93 @@ +From: Kees Cook +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 +Signed-off-by: Al Viro +--- + 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 + * diff --git a/debian/patches/features/all/fs-add-link-restrictions.patch b/debian/patches/features/all/fs-add-link-restrictions.patch new file mode 100644 index 000000000..4e221b32c --- /dev/null +++ b/debian/patches/features/all/fs-add-link-restrictions.patch @@ -0,0 +1,356 @@ +From: Kees Cook +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 +Acked-by: Ingo Molnar +Signed-off-by: Andrew Morton +Signed-off-by: Al Viro +[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), diff --git a/debian/patches/features/all/fs-hardlink-creation-restriction-cleanup.patch b/debian/patches/features/all/fs-hardlink-creation-restriction-cleanup.patch deleted file mode 100644 index 4f9d334f6..000000000 --- a/debian/patches/features/all/fs-hardlink-creation-restriction-cleanup.patch +++ /dev/null @@ -1,112 +0,0 @@ -From 52db90d0fa770e2277645eb34956820cec26b2cb Mon Sep 17 00:00:00 2001 -From: Kees Cook -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 -Cc: Ingo Molnar -Signed-off-by: Andrew Morton ---- - 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 - diff --git a/debian/patches/features/all/fs-hardlink-creation-restrictions-fix.patch b/debian/patches/features/all/fs-hardlink-creation-restrictions-fix.patch deleted file mode 100644 index 07175635a..000000000 --- a/debian/patches/features/all/fs-hardlink-creation-restrictions-fix.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 19f621ccbef745dedad641f44f535e3bcb00f30d Mon Sep 17 00:00:00 2001 -From: Andrew Morton -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 -Signed-off-by: Andrew Morton ---- - 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 - diff --git a/debian/patches/features/all/fs-hardlink-creation-restrictions.patch b/debian/patches/features/all/fs-hardlink-creation-restrictions.patch deleted file mode 100644 index db36fbeaf..000000000 --- a/debian/patches/features/all/fs-hardlink-creation-restrictions.patch +++ /dev/null @@ -1,390 +0,0 @@ -From fa3abdeee4e792ed794eef7ea71e7e0073cec32d Mon Sep 17 00:00:00 2001 -From: Kees Cook -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 -Acked-by: Ingo Molnar -Cc: Matthew Wilcox -Cc: Alexander Viro -Cc: Rik van Riel -Cc: Federica Teodori -Cc: Lucian Adrian Grijincu -Cc: Peter Zijlstra -Cc: Eric Paris -Cc: Randy Dunlap -Cc: Dan Rosenberg -Signed-off-by: Andrew Morton ---- - 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 - diff --git a/debian/patches/features/all/fs-symlink-restrictions-on-sticky-directories-fix-2.patch b/debian/patches/features/all/fs-symlink-restrictions-on-sticky-directories-fix-2.patch deleted file mode 100644 index aef3732ee..000000000 --- a/debian/patches/features/all/fs-symlink-restrictions-on-sticky-directories-fix-2.patch +++ /dev/null @@ -1,151 +0,0 @@ -From d48f814bd83a3cbd95dedaf5e4dd91c05cffddc6 Mon Sep 17 00:00:00 2001 -From: Kees Cook -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 -Signed-off-by: Andrew Morton ---- - 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 - diff --git a/debian/patches/features/all/fs-symlink-restrictions-on-sticky-directories.patch b/debian/patches/features/all/fs-symlink-restrictions-on-sticky-directories.patch deleted file mode 100644 index 774e1bd59..000000000 --- a/debian/patches/features/all/fs-symlink-restrictions-on-sticky-directories.patch +++ /dev/null @@ -1,329 +0,0 @@ -From af16d0017a7de1f00af3966b5013bebfce8a81b4 Mon Sep 17 00:00:00 2001 -From: Kees Cook -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 -Reviewed-by: Ingo Molnar -Cc: Matthew Wilcox -Cc: Alexander Viro -Cc: Rik van Riel -Cc: Federica Teodori -Cc: Lucian Adrian Grijincu -Cc: Ingo Molnar -Cc: Peter Zijlstra -Cc: Eric Paris -Cc: Randy Dunlap -Cc: Dan Rosenberg -Signed-off-by: Andrew Morton ---- - 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 - diff --git a/debian/patches/series b/debian/patches/series index 9680edde9..79814738f 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -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/fs-symlink-restrictions-on-sticky-directories.patch -features/all/fs-symlink-restrictions-on-sticky-directories-fix-2.patch -features/all/fs-hardlink-creation-restrictions.patch -features/all/fs-hardlink-creation-restrictions-fix.patch -features/all/fs-hardlink-creation-restriction-cleanup.patch +# Add link security restrictions from 3.6 +features/all/fs-add-link-restrictions.patch +features/all/fs-add-link-restriction-audit-reporting.patch # Update all Hyper-V drivers to 3.4-rc1 (no longer staging) features/x86/hyperv/0001-NLS-improve-UTF8-UTF16-string-conversion-routine.patch