Received: by 10.223.185.116 with SMTP id b49csp5961339wrg; Wed, 28 Feb 2018 01:24:55 -0800 (PST) X-Google-Smtp-Source: AH8x224UYJiLSGWLGX7dvkdYmTNjqFt5HV6fsr88YMBQ+YVxQGpf2xMLWwG64fdeZ/m4dR3FoCMp X-Received: by 2002:a17:902:33a5:: with SMTP id b34-v6mr17084669plc.263.1519809895303; Wed, 28 Feb 2018 01:24:55 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1519809895; cv=none; d=google.com; s=arc-20160816; b=WHVmDCaIuyvv9ZlAZB/X9fu79A748DaqoTqFqVpEf6VZkhJBvsacL2ixa6xu3XUvdO p3iIsdgL2B6W3ZGslso4KSqChfaWb+v7NDu0fPYkL9KjAtGKboVB26/U1rq3QQxrh+dv bAn28TbF9VxUiW+1PmP3WXDFSiqQgOMjY7/J6JJVbgMkz0m7XEYN0k6x9jz63kXhhO0z NQGlSYfRCbW6cr/9STZ50f3sYzzLlCz3ZnQfIIYWleJuSRqlKDj2NfO8P7365c3lfBDh fwF/IWACU6ZLicLWC+qchumM0eiOtflfFyh3fnWAWjiVCrbVKVOSLKuO6zZxIabod7QI uDHA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:cc:to:subject:message-id:date:from :references:in-reply-to:mime-version:dkim-signature :arc-authentication-results; bh=Xcvb9zJY8r6bBpA6DWD2YX1cSAodGsQm7jafOJDgjBA=; b=puwCYXVrucWpVlwZ1jB2De/CTdWd2SXi20QnULtYUmgwukkeZEGwE6IUH//BhuHds6 5XTUiuurZPMuLOsV222Ujp4wfgs0dIouumRp/esfigUz2Dww1praaijI1ZQ+RgURrucL 6jwlN2Qd36XmhWEwepGF/8zMwmCKPlB0rxsseg1XQsfTon5PiyolAICP1lHv0RTwvxwf Y2uQnRccxLCtjUY/2MDicGxuqQrvQ2BcP5vs4oUaL/C28IHcus7rojZHvTILiInhZtwX k9mfY3ND6ilW7/GxB/sEmkr7gGEfqhMKJSfHSHknoQqZIi1iYfFJh4LVhVljFwRSUoKT fHDw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=oZHBcXLH; 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 j8-v6si958633plk.634.2018.02.28.01.24.38; Wed, 28 Feb 2018 01:24:55 -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=oZHBcXLH; 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 S1752119AbeB1JWf (ORCPT + 99 others); Wed, 28 Feb 2018 04:22:35 -0500 Received: from mail-vk0-f67.google.com ([209.85.213.67]:33784 "EHLO mail-vk0-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751631AbeB1JWc (ORCPT ); Wed, 28 Feb 2018 04:22:32 -0500 Received: by mail-vk0-f67.google.com with SMTP id z130so1050392vkd.0; Wed, 28 Feb 2018 01:22:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :cc; bh=Xcvb9zJY8r6bBpA6DWD2YX1cSAodGsQm7jafOJDgjBA=; b=oZHBcXLHyQgRDh+JbJxTew9IfGOAMyVlLtb2jK2Ltv5ilYoV1vhu7LpiP7rIpyuxC2 aFe/Ios4PPxyyv1qMBBPaY7DtGs0lKIpM4benI2BGPFDmWgtgrqnGTLy1m/MnQEVQ1Yf YHlgFrpb4+epwTLE36hS8IAW3s3AdMD3zZciBueAVJRs5AdAtsn0/UcMNY2h+GgymJCM P12ylOqHbm7TRRaFrxGMNddpgNySVP0i1GXOEoSriUCLdoggtYj2qb7Q5dArxgskRoZn ACd7FbGRFV1sR27FM2LDHSSinYErD371pBW5bWRzv+p8P5fHLyprxWWfrxYOxkWnVVmM hZjQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to:cc; bh=Xcvb9zJY8r6bBpA6DWD2YX1cSAodGsQm7jafOJDgjBA=; b=Mjt+sVp0Gja0j2Du2nzo3A+d4ppAOyKbjaXmqfz+fT6svRNDAzJ0/sG2JgRHALTGqm NlTL/9Bs9ruVJXq9mdlDfEjC0U4LOAk8JJIA6t0JYIhDy5PLVvssAT+XB6Epg2g/ECsz /cI+ESDOWJwC2FzO1fC+PY0xUkyHRswPLi/vom5NxlJt22sZyLntf+EmMpyAvpUk0BXF FoBr5BzNb1G3ullVw76KvuIOWn8yvX930pz4ChtCnKuVET2nTbUc9J8CZQ75BNqhPxAH gp3sl3HerBAHIoan6qaydcFMXFqy5t0qR1cGbLg/1hUIWdxmvVawiR0DK8PBk3Uk+Uc0 xyDQ== X-Gm-Message-State: APf1xPCx3hoFwAIGSxdcHaBj1ZUapJyGJLGxM+oxlVIAsdoja22psAto B3aAU3btexpc4i6QfVIQr4M8pw1PFosY3Uuxl1M= X-Received: by 10.31.167.18 with SMTP id q18mr6060359vke.10.1519809750912; Wed, 28 Feb 2018 01:22:30 -0800 (PST) MIME-Version: 1.0 Received: by 10.103.170.7 with HTTP; Wed, 28 Feb 2018 01:22:10 -0800 (PST) In-Reply-To: References: <1519729200-16056-1-git-send-email-s.mesoraca16@gmail.com> From: Salvatore Mesoraca Date: Wed, 28 Feb 2018 10:22:10 +0100 Message-ID: Subject: Re: [PATCH v4] Protected FIFOs and regular files To: Kees Cook Cc: LKML , Kernel Hardening , "linux-fsdevel@vger.kernel.org" , Alan Cox , Alexander Viro , David Laight , Ian Campbell , Jann Horn , Matthew Wilcox , Pavel Vasilyev , Solar Designer , "Eric W. Biederman" , "Tobin C. Harding" Content-Type: text/plain; charset="UTF-8" Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 2018-02-27 21:22 GMT+01:00 Kees Cook : > On Tue, Feb 27, 2018 at 11:47 AM, Kees Cook wrote: >> On Tue, Feb 27, 2018 at 3:00 AM, Salvatore Mesoraca >> wrote: >>> 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); >> >> Instead of this pr_notice_ratelimited(), I think >> audit_log_link_denied() should be refactored and used instead. Drop >> this line from this patch, and I think this is great as-is. The >> logging can be separate (as it may get heavily bike-shed, as I >> experienced with hard/symlink restrictions). >> >> Otherwise, I think this looks great. >> >> Acked-by: Kees Cook > > I've also tested this now; so: > > Tested-by: Kees Cook > > # grep . /proc/sys/fs/protected_* > /proc/sys/fs/protected_fifos:0 > /proc/sys/fs/protected_hardlinks:1 > /proc/sys/fs/protected_regular:0 > /proc/sys/fs/protected_symlinks:1 > # cd /tmp > # mkfifo fifo > # touch regular > # chown nobody fifo regular > # chmod a+w fifo regular > # chmod a+w regular > # cat fifo > output & > # su - keescook > $ cd /tmp > $ python > ... >>>> import os >>>> fd = os.open("fifo", os.O_RDWR | os.O_CREAT) >>>> os.write(fd, "OHAI\n") > 5 >>>> fd = os.open("regular", os.O_RDWR | os.O_CREAT) >>>> os.write(fd, "OHAI\n") > 5 >>>> exit() > $ exit > # echo 1 > /proc/sys/fs/protected_fifos > # echo 1 > /proc/sys/fs/protected_regular > # su - keescook > $ cd /tmp > $ python > ... >>>> import os >>>> fd = os.open("fifo", os.O_RDWR | os.O_CREAT) > Traceback (most recent call last): > File "", line 1, in > OSError: [Errno 13] Permission denied: 'fifo' >>>> fd = os.open("regular", os.O_RDWR | os.O_CREAT) > Traceback (most recent call last): > File "", line 1, in > OSError: [Errno 13] Permission denied: 'regular' > >> I'll create a branch for this on git.kernel.org and see if anything >> surprising pops out. :) > > Here it is with my suggested refactoring of the audit message: > > https://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git/log/?h=kspp/userspace/protected-creat > > Which produces: > > [ 146.854080] audit: type=1703 audit(1519762816.978:95): op=fifo > ppid=3091 pid=3092 auid=0 uid=1000 gid=1000 euid=1000 suid=1000 > fsuid=1000 egid=1000 sgid=1000 fsgid=1000 tty=pts8 ses=1 comm="python" > exe="/usr/bin/python2.7" res=0 > [ 146.858691] audit: type=1302 audit(1519762816.978:95): item=0 > name="/tmp/fifo" inode=531 dev=fd:01 mode=010666 ouid=65534 ogid=0 > rdev=00:00 nametype=NORMAL cap_fp=0000000000000000 > cap_fi=0000000000000000 cap_fe=0 cap_fver=0 > ... > [ 152.993518] audit: type=1703 audit(1519762823.117:96): op=regular > ppid=3091 pid=3092 auid=0 uid=1000 gid=1000 euid=1000 suid=1000 > fsuid=1000 egid=1000 sgid=1000 fsgid=1000 tty=pts8 ses=1 comm="python" > exe="/usr/bin/python2.7" res=0 > [ 152.997963] audit: type=1302 audit(1519762823.117:96): item=0 > name="/tmp/regular" inode=700 dev=fd:01 mode=0100666 ouid=65534 ogid=0 > rdev=00:00 nametype=NORMAL cap_fp=0000000000000000 > cap_fi=0000000000000000 cap_fe=0 cap_fver=0 > > and other things (uid, etc) Awesome! Thank you very much for your help! Salvatore