Received: by 10.223.185.116 with SMTP id b49csp4807911wrg; Tue, 27 Feb 2018 03:01:53 -0800 (PST) X-Google-Smtp-Source: AH8x226nsChTfksL3wZnAr87GZjimktTnUfsnukCrWNe5r4q1p5xeAtOV4J9ASM5RJuX0FWQqDnZ X-Received: by 10.99.115.73 with SMTP id d9mr11342756pgn.354.1519729313822; Tue, 27 Feb 2018 03:01:53 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1519729313; cv=none; d=google.com; s=arc-20160816; b=nBdRgcDZaJgwnzS78qXNMKi49i0cySpmCmo7Yr2nbiu5EaaEqud6JpX14sPSPYgYrP AHWRG85wJbm3x3b4PESr/zPaSkCxqPuREF220GNoJ6/0K1h1qglIyA/Nd0GYDkOo7XMY PQv66BEc1Wf7oZmMjGCDnQn/BRAhPLFMBvn1+QUKP2kX4gAcasrHnvQvVGEDdzmobtuG ly9+896sqYycoFKr93SEHBAIP/NZlAfm5Ucezi/kPvlpuABHhI9PesymwJjaCsGP4n4l oci6UPJWiDeAFmMF7BTfcrsQ22kic6afE7yx7HO788q1uOL5aZpHZA2/APmu8+LMs8Fg fw3g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:message-id:date:subject:cc:to:from :dkim-signature:arc-authentication-results; bh=u6GWceNK7tZYE1+dNpwq6sGf/SwAjDQpQh59O+KT/dE=; b=PzoCB0IZp9Q0Btqk0N5KcaTtSKlYHYr7/Q4gmelpv7YmPIP8JcrWHCeJcLzvPlmC7i exUw//6ZacvsftJ2r5LLuZ0TvVS7WiE4ccbDBMj4JY2iZv0u6vMJKlzUyOzBlsOOVfnF iNhv2sddfWSqcqT3rWbI9VhEKhd/0cI7wY5SFZDUJfxQfG8rfShnuxwoRXIz96kzNaoU s6BxzEbRdc+xT2BpzKPijIciDk3IziDqzclRLNKP6Rz3Ui1q0/VqRXsTkFT3ZSPJutQO d5WqMrkWRhBNW7/b4t272bXznkTVuQoQRn9hxG/ScDWBXLq4I2IVffkrgQ18vgjfkO+o P7sw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=nnaLC78g; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id q3si5914707pgt.517.2018.02.27.03.01.38; Tue, 27 Feb 2018 03:01:53 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=nnaLC78g; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752730AbeB0LAQ (ORCPT + 99 others); Tue, 27 Feb 2018 06:00:16 -0500 Received: from mail-wm0-f67.google.com ([74.125.82.67]:38470 "EHLO mail-wm0-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752101AbeB0LAM (ORCPT ); Tue, 27 Feb 2018 06:00:12 -0500 Received: by mail-wm0-f67.google.com with SMTP id z9so22816644wmb.3; Tue, 27 Feb 2018 03:00:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=u6GWceNK7tZYE1+dNpwq6sGf/SwAjDQpQh59O+KT/dE=; b=nnaLC78g3JsQfZ6XW5RbJtwzNx94+bclaavXe3PddJ0DAA/lXsobzestiIbld53mqi Y2mitEYuUkhgfQtK0+HnkqnsbBi109hU5IHEwZjtRmkryCst75c/ZwShIdyod1yE6m/g SFyiMbSns0ZrKekAlpLyJlvm836vv2ci56Q5RiCwVh2KCwlrWlGXaDUyqZLBS34vQ70C NeZwXk3N9JSJf2SkVWruhV/xgcEnU6q0IhT7Wh71Y9D3mjJYaNwKq6yCKVZcHNoA9mSH r2TyB6zivLZQh9JGLFBtJe428N4LTq620YT46sQoenSw3e+JBI5tQUmSl58GArdxQKQf 3t4w== 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; bh=u6GWceNK7tZYE1+dNpwq6sGf/SwAjDQpQh59O+KT/dE=; b=m3DpgqTtq7AWlWOLuE8+s+45Pv77Mk4mPM/dKQMGe2ClqwHkEOdI/6+cU8Q/OdfKKJ Y2JkdpDTZr9pEN1ypXtrLjW0bG/jGEQXpdNRkJPl5PeUkhAfG4Z0ZMA1PDcMFYlK0uDG 1VNJWkYA4XoAy4SSoLR9Q9UOGRlZqh99QjESENN4CuBh/Nb0tUJhLtEf1ABBI4dpgrsc sWKYT+ZYUHoR+NfC0bySDFtZqjODFUdbB4S7OT77XQp9zLK+2sPAlYY+14jPy2P93Vlh JE3Fmp/CJZZ+9UafsOLPhjtJontVyuqEg2qJL0wpua2GbN0GVRv3Yt66za1YJwVkG+g+ 2fig== X-Gm-Message-State: APf1xPAI0AHHmgyCdwpK6pZD/WCzaOFKkeqjHKkAqK2UBZ5AlftNib2Y UhsSnNMQoTRMjpm/1+R14doZa77Fmn4= X-Received: by 10.28.135.142 with SMTP id j136mr11672848wmd.33.1519729210222; Tue, 27 Feb 2018 03:00:10 -0800 (PST) Received: from localhost ([109.112.47.14]) by smtp.gmail.com with ESMTPSA id 10sm5804459wrv.95.2018.02.27.03.00.08 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 27 Feb 2018 03:00:09 -0800 (PST) From: Salvatore Mesoraca To: linux-kernel@vger.kernel.org Cc: Kernel Hardening , linux-fsdevel@vger.kernel.org, Salvatore Mesoraca , Alan Cox , Alexander Viro , David Laight , Ian Campbell , Jann Horn , Kees Cook , Matthew Wilcox , Pavel Vasilyev , Solar Designer , "Eric W. Biederman" , "Tobin C. Harding" Subject: [PATCH v4] Protected FIFOs and regular files Date: Tue, 27 Feb 2018 12:00:00 +0100 Message-Id: <1519729200-16056-1-git-send-email-s.mesoraca16@gmail.com> X-Mailer: git-send-email 1.9.1 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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 hardlinks/symlinks restrictions, fifos/regular files weren't the favorite vehicle to exploit them. Suggested-by: Solar Designer Suggested-by: Kees Cook Signed-off-by: Salvatore Mesoraca --- Notes: Changes in v3: - Fixed format string for uid_t that is unsigned (suggested by Jann Horn). Changes in v4: - Some English fixes (suggested by Tobin C. Harding). - The original patchset has been split to help this part land upstream (suggested by Solar Designer). 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 6c00c1e..819caf8 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", writing to FIFOs 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 applies 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", writing to regular files 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 applies 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 921ae32..eaab668 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -883,6 +883,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 @@ -996,6 +998,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, on files that already + * exist. + * @dir: the sticky 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 %u.%u in a sticky directory by UID %u, EUID %u, 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) { @@ -3355,9 +3405,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 2a81556..9bf4e5c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -72,6 +72,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 f98f28c..295f528 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1794,6 +1794,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), -- 1.9.1