diff --git a/debian/changelog b/debian/changelog index fae1471fc..bf288b6c9 100644 --- a/debian/changelog +++ b/debian/changelog @@ -20,6 +20,13 @@ linux-2.6 (3.2.9-1) UNRELEASED; urgency=low - Update Dutch (Willem Kuyn) (Closes: #658736) - Add Polish (Michał Kułach) (Closes: #658912) * Bump ABI to 2 + * fs: Introduce and enable security restrictions on links: + - Do not follow symlinks in /tmp that are owned by other users + (sysctl: fs.protected_symlinks) + - Do not allow unprivileged users to create hard links to sensitive files + (sysctl: fs.protected_hardlinks) (Closes: #609455) + The precise restrictions are specified in Documentation/sysctl/fs.txt in + the linux-doc-3.2 and linux-source-3.2 packages. -- Bastian Blank Thu, 01 Mar 2012 11:47:17 +0100 diff --git a/debian/config/config b/debian/config/config index 0a249d8c6..dce1bb67d 100644 --- a/debian/config/config +++ b/debian/config/config @@ -3254,6 +3254,9 @@ CONFIG_TMPFS_POSIX_ACL=y CONFIG_HUGETLBFS=y CONFIG_MISC_FILESYSTEMS=y CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_PROTECTED_LINKS=y +CONFIG_PROTECTED_SYMLINKS=y +CONFIG_PROTECTED_HARDLINKS=y ## ## file: fs/9p/Kconfig diff --git a/debian/patches/features/all/fs-hardlink-creation-restriction-cleanup.patch b/debian/patches/features/all/fs-hardlink-creation-restriction-cleanup.patch new file mode 100644 index 000000000..4f9d334f6 --- /dev/null +++ b/debian/patches/features/all/fs-hardlink-creation-restriction-cleanup.patch @@ -0,0 +1,112 @@ +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 new file mode 100644 index 000000000..07175635a --- /dev/null +++ b/debian/patches/features/all/fs-hardlink-creation-restrictions-fix.patch @@ -0,0 +1,39 @@ +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 new file mode 100644 index 000000000..db36fbeaf --- /dev/null +++ b/debian/patches/features/all/fs-hardlink-creation-restrictions.patch @@ -0,0 +1,390 @@ +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 new file mode 100644 index 000000000..aef3732ee --- /dev/null +++ b/debian/patches/features/all/fs-symlink-restrictions-on-sticky-directories-fix-2.patch @@ -0,0 +1,151 @@ +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 new file mode 100644 index 000000000..774e1bd59 --- /dev/null +++ b/debian/patches/features/all/fs-symlink-restrictions-on-sticky-directories.patch @@ -0,0 +1,329 @@ +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/base b/debian/patches/series/base index bc08410cd..db819ab92 100644 --- a/debian/patches/series/base +++ b/debian/patches/series/base @@ -81,3 +81,8 @@ + bugfix/x86/drm-i915-fix-operator-precedence-when-enabling-RC6p.patch + bugfix/all/ipsec-be-careful-of-non-existing-mac-headers.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