From patchwork Mon Apr 16 17:59:18 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kees Cook X-Patchwork-Id: 10343629 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 B8DAD601D7 for ; Mon, 16 Apr 2018 17:59:38 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A71CF28861 for ; Mon, 16 Apr 2018 17:59:38 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 9AF4828864; Mon, 16 Apr 2018 17:59:38 +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=-5.3 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from mother.openwall.net (mother.openwall.net [195.42.179.200]) by mail.wl.linuxfoundation.org (Postfix) with SMTP id 43C7928861 for ; Mon, 16 Apr 2018 17:59:36 +0000 (UTC) Received: (qmail 32169 invoked by uid 550); 16 Apr 2018 17:59:34 -0000 Mailing-List: contact kernel-hardening-help@lists.openwall.com; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-ID: Delivered-To: mailing list kernel-hardening@lists.openwall.com Received: (qmail 32103 invoked from network); 16 Apr 2018 17:59:32 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=date:from:to:cc:subject:message-id:mime-version:content-disposition; bh=CNC88z6sleaW8STVafgNNOROOdBGL0slPbtQOICfr5w=; b=Wc7RLS21GaUJViHFz9Ze+CkPIVI3NcBybudf8MU7dXXH68bAOtkk0A4idl4AIRv6b3 2K7t61zHGC9Jmm9c0gxIiNb0P6LxzIQvTb7tSb2lT2jyBjJgdmJPBpVfQPkp3TDkGva+ mqHeJpgibybIc2gxGG5NSicxdnzzZ+0ff5bdY= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:mime-version :content-disposition; bh=CNC88z6sleaW8STVafgNNOROOdBGL0slPbtQOICfr5w=; b=ofS86tRe7FAIvSOaph74qB3rvkz6lq2i/0JWVcXk33g+1OCAfwEsDz6/nb3rzoWrmV 5YhBD/7CMkAEhD7fSpoXK36QLtyOlkBkRuo2FAMVpffnDKPVxoMsU32ZQLf8veEO7fTH Nb4/G3iHVmHWKrvFUwSOKq9NZ3/ymBKc+VLKzFUCfTKUBRa6MKBKqtPq6AElXJlzGPcI GCAu++nU1fn3wkf0inEBGvNzfScSBfrcBpUa4S7+wkij5wqUfUvWEkrs1f6q36Wgq7Yz yTdHf4t/apHS/rqh9oHuvoCvs+eCtnqomT7AbdCaVouqPTO0j6L58uU6NQsHYl5t4+xZ v0EQ== X-Gm-Message-State: ALQs6tD7RZoyLNBGgoLUcazy5REOi2yQFyU4Y9jeLqaWseuDpUiF6FiH h0AoPhVcNnBUvkDynI47jZvBsg== X-Google-Smtp-Source: AIpwx496tG19ewzdDUpyNP2AB66gXplowCxOifiODW7PDAb1tW0S5ULRkVVZMLRaP5jhvPMN0GkJBQ== X-Received: by 2002:a17:902:8a82:: with SMTP id p2-v6mr16296742plo.91.1523901560696; Mon, 16 Apr 2018 10:59:20 -0700 (PDT) Date: Mon, 16 Apr 2018 10:59:18 -0700 From: Kees Cook To: Andrew Morton Cc: Solar Designer , kernel-hardening@lists.openwall.com, linux-kernel@vger.kernel.org Subject: [PATCH v5] namei: Allow restricted O_CREAT of FIFOs and regular files Message-ID: <20180416175918.GA13494@beast> MIME-Version: 1.0 Content-Disposition: inline X-Virus-Scanned: ClamAV using ClamSMTP From: Salvatore Mesoraca 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 [kees: drop pr_warn_ratelimited() in favor of audit changes in the future] [kees: adjust commit subjet] Signed-off-by: Kees Cook --- Documentation/sysctl/fs.txt | 36 +++++++++++++++++++++++++++++++ fs/namei.c | 52 ++++++++++++++++++++++++++++++++++++++++++--- include/linux/fs.h | 2 ++ kernel/sysctl.c | 18 ++++++++++++++++ 4 files changed, 105 insertions(+), 3 deletions(-) diff --git a/Documentation/sysctl/fs.txt b/Documentation/sysctl/fs.txt index 6c00c1e2743f..819caf8ca05f 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 186bd2464fd5..3157c984414b 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -887,6 +887,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 @@ -1001,6 +1003,45 @@ 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 + * @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, + 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))))) { + return -EACCES; + } + return 0; +} + static __always_inline const char *get_link(struct nameidata *nd) { @@ -3342,9 +3383,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, 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 760d8da1b6c7..dbd4ae431910 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -73,6 +73,8 @@ extern struct inodes_stat_t inodes_stat; 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 6a78cf70761d..f45ed9e696eb 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1805,6 +1805,24 @@ static struct ctl_table fs_table[] = { .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),