From patchwork Tue Sep 26 14:14:32 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Salvatore Mesoraca X-Patchwork-Id: 9972133 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 4179F60365 for ; Tue, 26 Sep 2017 14:15:58 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 30476284C7 for ; Tue, 26 Sep 2017 14:15:58 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 251902885A; Tue, 26 Sep 2017 14:15:58 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RCVD_IN_SORBS_SPAM autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9081328957 for ; Tue, 26 Sep 2017 14:15:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S969388AbdIZOPo (ORCPT ); Tue, 26 Sep 2017 10:15:44 -0400 Received: from mail-wr0-f196.google.com ([209.85.128.196]:37013 "EHLO mail-wr0-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S968264AbdIZOPl (ORCPT ); Tue, 26 Sep 2017 10:15:41 -0400 Received: by mail-wr0-f196.google.com with SMTP id u48so2603620wrf.4; Tue, 26 Sep 2017 07:15:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=pPAN1bGdsGqFGLw5Ij0E2O2AaVTdbz2yFkkSSKvetFw=; b=mtX99IHnlBj15j651Ih6ys4ES++MkCMoSKzkCS9/SQxF4VkEBrExbKnFaLsbYzB5e/ 25mvMAzODJiTsmjuuFxX4DYXDeEJfkIqgl57knathnLsprbcU5+ijspdr3lie8PhnQVb rcyT5d/PWuCSFmM+MbWVjxG7AnbntYj8Lzw7cMXVURLhxt30H27WmdwX7pTnN/i4GHBL M5MXKkkvXY+hwk9AOQmNSKEOD0bf1xqzo994uJWz+mfs40Rr+rlTx25u4Kj/3zeXbDNi rjGXYpPHdwQ+L9dDOsgXUF+3O578uxMfPPWRsSjDOTSwH2svtIsTaCN01WviL9MWO7eB NJ9A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=pPAN1bGdsGqFGLw5Ij0E2O2AaVTdbz2yFkkSSKvetFw=; b=bWa0+jzvCJjRpRJfPezZ0dteOOs7XDB8XODoPiX7uuvhOxAmzFHvvAzV3Lja54kg9F iG/3Se05onB7aIPyfa1Pv7/Z/uVYHmND4vNF8BluJ+6c65s++zkzYwERp487oZv9aMCB ijq5f2i1LO4Qxz0sK4VtxJpaKDm76tnJrDg3K2X0X0bBm6hmyeEd0lNlKtLn2rQeu9nE 3F8PsmuZzARGveBJ3R2JUppup8GICRcfzVQ8bHLF7GsMKyBQN0WKPSHQLL/PX779WARp XuuVS81Nl9rcS98bUOZec4XjfWcK7jbtStmWrBVOk1RJsS8nD++CZ02Yh640FKI+x/KY McLA== X-Gm-Message-State: AHPjjUhn1NsuKRyS8UqLdeIwXxoVC98JSHFIoAd8L0wLPIXXp7Re3AxQ rJTPGvttZdEbnUuxqDuteQk1XGJoJ7RZdQ== X-Google-Smtp-Source: AOwi7QBjqsTZre99KIVsDBaCedmw3425VUDt7ac0fr3mQd/nR0zm2ArjU4gFN7ahDA2mc7FHE/GdxA== X-Received: by 10.223.139.211 with SMTP id w19mr8615660wra.199.1506435339251; Tue, 26 Sep 2017 07:15:39 -0700 (PDT) Received: from localhost ([37.180.125.209]) by smtp.gmail.com with ESMTPSA id 204sm3076238wml.10.2017.09.26.07.15.36 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 26 Sep 2017 07:15:38 -0700 (PDT) From: Salvatore Mesoraca To: linux-kernel@vger.kernel.org Cc: Kernel Hardening , Kees Cook , Solar Designer , Alexander Viro , "Eric W. Biederman" , linux-fsdevel@vger.kernel.org, Salvatore Mesoraca Subject: [RFC v2 1/2] Protected FIFOs and regular files Date: Tue, 26 Sep 2017 16:14:32 +0200 Message-Id: <1506435273-8428-2-git-send-email-s.mesoraca16@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1506435273-8428-1-git-send-email-s.mesoraca16@gmail.com> References: <1506435273-8428-1-git-send-email-s.mesoraca16@gmail.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Disallows open of FIFOs or regular files not owned by the user in world writable sticky directories, unless the owner is the same as that of the directory or the file is opened without the O_CREAT flag. The purpose is to make data spoofing attacks harder. This protection can be turned on and off separately for FIFOs and regular files via sysctl, just like the symlinks/hardlinks protection. This patch is based on Openwall's "HARDEN_FIFO" feature by Solar Designer. This is a brief list of old vulnerabilities that could have been prevented by this feature, some of them even allow for privilege escalation: CVE-2000-1134 CVE-2007-3852 CVE-2008-0525 CVE-2009-0416 CVE-2011-4834 CVE-2015-1838 CVE-2015-7442 CVE-2016-7489 This list is not meant to be complete. It's difficult to track down all vulnerabilities of this kind because they were often reported without any mention of this particular attack vector. In fact, before symlinks restrictions, fifos/regular files were not the favorite vehicle to exploit them. Suggested-by: Solar Designer Suggested-by: Kees Cook Signed-off-by: Salvatore Mesoraca --- Documentation/sysctl/fs.txt | 36 ++++++++++++++++++++++++++ fs/namei.c | 61 ++++++++++++++++++++++++++++++++++++++++++--- include/linux/fs.h | 2 ++ kernel/sysctl.c | 18 +++++++++++++ 4 files changed, 114 insertions(+), 3 deletions(-) diff --git a/Documentation/sysctl/fs.txt b/Documentation/sysctl/fs.txt index 35e17f7..655e261 100644 --- a/Documentation/sysctl/fs.txt +++ b/Documentation/sysctl/fs.txt @@ -34,7 +34,9 @@ Currently, these files are in /proc/sys/fs: - overflowgid - pipe-user-pages-hard - pipe-user-pages-soft +- protected_fifos - protected_hardlinks +- protected_regular - protected_symlinks - suid_dumpable - super-max @@ -182,6 +184,24 @@ applied. ============================================================== +protected_fifos: + +The intent of this protection is to avoid unintentional writes to +an attacker-controlled FIFO, where a program expected to create a regular +file. + +When set to "0", FIFOs writing is unrestricted. + +When set to "1" don't allow O_CREAT open on FIFOs that we don't own +in world writable sticky directories, unless they are owned by the +owner of the directory. + +When set to "2" it also apply to group writable sticky directories. + +This protection is based on the restrictions in Openwall. + +============================================================== + protected_hardlinks: A long-standing class of security issues is the hardlink-based @@ -202,6 +222,22 @@ This protection is based on the restrictions in Openwall and grsecurity. ============================================================== +protected_regular: + +This protection is similar to protected_fifos, but it +avoids writes to an attacker-controlled regular file, where a program +expected to create one. + +When set to "0", regular files writing is unrestricted. + +When set to "1" don't allow O_CREAT open on regular files that we +don't own in world writable sticky directories, unless they are +owned by the owner of the directory. + +When set to "2" it also apply to group writable sticky directories. + +============================================================== + protected_symlinks: A long-standing class of security issues is the symlink-based diff --git a/fs/namei.c b/fs/namei.c index c75ea03..d2b287d 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -901,6 +901,8 @@ static inline void put_link(struct nameidata *nd) int sysctl_protected_symlinks __read_mostly = 0; int sysctl_protected_hardlinks __read_mostly = 0; +int sysctl_protected_fifos __read_mostly; +int sysctl_protected_regular __read_mostly; /** * may_follow_link - Check symlink following for unsafe situations @@ -1014,6 +1016,54 @@ static int may_linkat(struct path *link) return -EPERM; } +/** + * may_create_in_sticky - Check whether an O_CREAT open in a sticky directory + * should be allowed or not, when the file already + * existed. + * @dir: the stick parent directory + * @name: the file name + * @inode: the inode of the file to open + * + * Block an O_CREAT open of a FIFO (or a regular file) when: + * - sysctl_protected_fifos (or sysctl_protected_regular) is enabled + * - the file already exists + * - we are in a sticky directory + * - we don't own the file + * - the owner of the directory doesn't own the file + * - the directory is world writable + * If the sysctl_protected_fifos (or sysctl_protected_regular) is set to 2 + * the directory doesn't have to be world writable: being group writable will + * be enough. + * + * Returns 0 if the open is allowed, -ve on error. + */ +static int may_create_in_sticky(struct dentry * const dir, + const unsigned char * const name, + struct inode * const inode) +{ + if ((!sysctl_protected_fifos && S_ISFIFO(inode->i_mode)) || + (!sysctl_protected_regular && S_ISREG(inode->i_mode)) || + likely(!(dir->d_inode->i_mode & S_ISVTX)) || + uid_eq(inode->i_uid, dir->d_inode->i_uid) || + uid_eq(current_fsuid(), inode->i_uid)) + return 0; + + if (likely(dir->d_inode->i_mode & 0002) || + (dir->d_inode->i_mode & 0020 && + ((sysctl_protected_fifos >= 2 && S_ISFIFO(inode->i_mode)) || + (sysctl_protected_regular >= 2 && S_ISREG(inode->i_mode))))) { + pr_notice_ratelimited("denied writing in '%s' of %d.%d in a sticky directory by UID %d, EUID %d, process %s:%d.\n", + name, + from_kuid(&init_user_ns, inode->i_uid), + from_kgid(&init_user_ns, inode->i_gid), + from_kuid(&init_user_ns, current_uid()), + from_kuid(&init_user_ns, current_euid()), + current->comm, current->pid); + return -EACCES; + } + return 0; +} + static __always_inline const char *get_link(struct nameidata *nd) { @@ -3364,9 +3414,14 @@ static int do_last(struct nameidata *nd, if (error) return error; audit_inode(nd->name, nd->path.dentry, 0); - error = -EISDIR; - if ((open_flag & O_CREAT) && d_is_dir(nd->path.dentry)) - goto out; + if (open_flag & O_CREAT) { + error = -EISDIR; + if (d_is_dir(nd->path.dentry)) + goto out; + error = may_create_in_sticky(dir, nd->last.name, inode); + if (unlikely(error)) + goto out; + } error = -ENOTDIR; if ((nd->flags & LOOKUP_DIRECTORY) && !d_can_lookup(nd->path.dentry)) goto out; diff --git a/include/linux/fs.h b/include/linux/fs.h index 339e737..14bb497 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -71,6 +71,8 @@ extern int leases_enable, lease_break_time; extern int sysctl_protected_symlinks; extern int sysctl_protected_hardlinks; +extern int sysctl_protected_fifos; +extern int sysctl_protected_regular; typedef __kernel_rwf_t rwf_t; diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 6648fbb..6b127e2 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1807,6 +1807,24 @@ static int sysrq_sysctl_handler(struct ctl_table *table, int write, .extra2 = &one, }, { + .procname = "protected_fifos", + .data = &sysctl_protected_fifos, + .maxlen = sizeof(int), + .mode = 0600, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &two, + }, + { + .procname = "protected_regular", + .data = &sysctl_protected_regular, + .maxlen = sizeof(int), + .mode = 0600, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &two, + }, + { .procname = "suid_dumpable", .data = &suid_dumpable, .maxlen = sizeof(int),