Received: by 2002:a25:1985:0:0:0:0:0 with SMTP id 127csp2682457ybz; Sun, 19 Apr 2020 07:13:20 -0700 (PDT) X-Google-Smtp-Source: APiQypLZ0BRztTY46ySJJyNyiT/9ZxfCSQe6GHToPeobl9KzQb92ynzy/eJ1grFyGXyoFGI/aW8D X-Received: by 2002:aa7:d64e:: with SMTP id v14mr10370430edr.19.1587305600560; Sun, 19 Apr 2020 07:13:20 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1587305600; cv=none; d=google.com; s=arc-20160816; b=M5Vy0qAtzBKVQIZerKZPf8oTgSESSbnJoqA4//VtRvrWKOu1rJ927C0VNsjTEVjCQ2 9AXM0K7Gq6aITszAi4Hto0i4OOvZy+hHXQMBSaHy8WY4kC2MSkr3ULllrEPDhvD71sIY Vep0l6lnL/JCVvewwGVt788JS9WRNsxGWjeTUdqVPGqHs13qgAPsxdRxk0ODbQf+25tf 3H/FYgsDJhENhT/AXnSA+f/FfRi/frRKA2aUGTWBrn1FNROJFu2Wv8kFDrDyQ4ey6uq+ 7GiR9+nomI/L2X0ttiyE7inaPCVBSRviqUtAV7mpp/6yUE9aIcrUFtAZ30h/esmrJkRX 8+Qw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=NCkcWK6HprxSGrXysVv2xEqv9zLZLJDpMqVjDUWFfO8=; b=rfdPxahS3G9vmjDrNJDPa9kUXIc6oTA7hcyWguOq681fa80PrwZXim1hB7uIx4vNYt FY6+OnSQR50p68KAsnxX3O0G/A7wzYY7h62RLB/9VoWmKCLHYbhNw5AVceIXh7A4ubVf mWZbxEK7uR4Tc87BCc/tSYVlfIEPukhb11JrCze0aqJNbWAn/iE1sJ7YfDueM0KE9565 ho2qmrycJLCYd/6m7IjCGr2SSArvgaQWVSH/VrvOVt6k9lMiv75JPFW2jekmytjE54Re oxpzSqLs0K7GnH/3A36K97AzcL79OzJvCdfCEJLTk2fKZsuPthNALMlc+VQypvObVBue sNzw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id v20si2587619edb.416.2020.04.19.07.12.56; Sun, 19 Apr 2020 07:13:20 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726419AbgDSOLx (ORCPT + 99 others); Sun, 19 Apr 2020 10:11:53 -0400 Received: from raptor.unsafe.ru ([5.9.43.93]:45592 "EHLO raptor.unsafe.ru" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726160AbgDSOLs (ORCPT ); Sun, 19 Apr 2020 10:11:48 -0400 Received: from comp-core-i7-2640m-0182e6.redhat.com (ip-89-102-33-211.net.upcbroadband.cz [89.102.33.211]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by raptor.unsafe.ru (Postfix) with ESMTPSA id BA05E20A00; Sun, 19 Apr 2020 14:11:43 +0000 (UTC) From: Alexey Gladkov To: LKML Cc: Kernel Hardening , Linux API , Linux FS Devel , Linux Security Module , Akinobu Mita , Alexander Viro , Alexey Dobriyan , Alexey Gladkov , Andrew Morton , Andy Lutomirski , Daniel Micay , Djalal Harouni , "Dmitry V . Levin" , "Eric W . Biederman" , Greg Kroah-Hartman , Ingo Molnar , "J . Bruce Fields" , Jeff Layton , Jonathan Corbet , Kees Cook , Linus Torvalds , Oleg Nesterov , David Howells Subject: [PATCH v12 6/7] proc: use human-readable values for hidepid Date: Sun, 19 Apr 2020 16:10:56 +0200 Message-Id: <20200419141057.621356-7-gladkov.alexey@gmail.com> X-Mailer: git-send-email 2.25.3 In-Reply-To: <20200419141057.621356-1-gladkov.alexey@gmail.com> References: <20200419141057.621356-1-gladkov.alexey@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Greylist: Sender succeeded SMTP AUTH, not delayed by milter-greylist-4.6.1 (raptor.unsafe.ru [5.9.43.93]); Sun, 19 Apr 2020 14:11:44 +0000 (UTC) Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The hidepid parameter values are becoming more and more and it becomes difficult to remember what each new magic number means. Backward compatibility is preserved since it is possible to specify numerical value for the hidepid parameter. This does not break the fsconfig since it is not possible to specify a numerical value through it. All numeric values are converted to a string. The type FSCONFIG_SET_BINARY cannot be used to indicate a numerical value. Selftest has been added to verify this behavior. Suggested-by: Andy Lutomirski Signed-off-by: Alexey Gladkov Reviewed-by: Alexey Dobriyan Reviewed-by: Kees Cook --- Documentation/filesystems/proc.rst | 52 +++++++++---------- fs/proc/inode.c | 15 +++++- fs/proc/root.c | 38 ++++++++++++-- tools/testing/selftests/proc/.gitignore | 1 + tools/testing/selftests/proc/Makefile | 1 + .../selftests/proc/proc-fsconfig-hidepid.c | 50 ++++++++++++++++++ 6 files changed, 126 insertions(+), 31 deletions(-) create mode 100644 tools/testing/selftests/proc/proc-fsconfig-hidepid.c diff --git a/Documentation/filesystems/proc.rst b/Documentation/filesystems/proc.rst index 360486c7a992..e2ecf248feb5 100644 --- a/Documentation/filesystems/proc.rst +++ b/Documentation/filesystems/proc.rst @@ -2147,28 +2147,28 @@ The following mount options are supported: subset= Show only the specified subset of procfs. ========= ======================================================== -hidepid=0 means classic mode - everybody may access all /proc// directories -(default). - -hidepid=1 means users may not access any /proc// directories but their -own. Sensitive files like cmdline, sched*, status are now protected against -other users. This makes it impossible to learn whether any user runs -specific program (given the program doesn't reveal itself by its behaviour). -As an additional bonus, as /proc//cmdline is unaccessible for other users, -poorly written programs passing sensitive information via program arguments are -now protected against local eavesdroppers. - -hidepid=2 means hidepid=1 plus all /proc// will be fully invisible to other -users. It doesn't mean that it hides a fact whether a process with a specific -pid value exists (it can be learned by other means, e.g. by "kill -0 $PID"), -but it hides process' uid and gid, which may be learned by stat()'ing -/proc// otherwise. It greatly complicates an intruder's task of gathering -information about running processes, whether some daemon runs with elevated -privileges, whether other user runs some sensitive program, whether other users -run any program at all, etc. - -hidepid=4 means that procfs should only contain /proc// directories -that the caller can ptrace. +hidepid=off or hidepid=0 means classic mode - everybody may access all +/proc// directories (default). + +hidepid=noaccess or hidepid=1 means users may not access any /proc// +directories but their own. Sensitive files like cmdline, sched*, status are now +protected against other users. This makes it impossible to learn whether any +user runs specific program (given the program doesn't reveal itself by its +behaviour). As an additional bonus, as /proc//cmdline is unaccessible for +other users, poorly written programs passing sensitive information via program +arguments are now protected against local eavesdroppers. + +hidepid=invisible or hidepid=2 means hidepid=1 plus all /proc// will be +fully invisible to other users. It doesn't mean that it hides a fact whether a +process with a specific pid value exists (it can be learned by other means, e.g. +by "kill -0 $PID"), but it hides process' uid and gid, which may be learned by +stat()'ing /proc// otherwise. It greatly complicates an intruder's task of +gathering information about running processes, whether some daemon runs with +elevated privileges, whether other user runs some sensitive program, whether +other users run any program at all, etc. + +hidepid=ptraceable or hidepid=4 means that procfs should only contain +/proc// directories that the caller can ptrace. gid= defines a group authorized to learn processes information otherwise prohibited by hidepid=. If you use some daemon like identd which needs to learn @@ -2216,8 +2216,8 @@ creates a new procfs instance. Mount options affect own procfs instance. It means that it became possible to have several procfs instances displaying tasks with different filtering options in one pid namespace. -# mount -o hidepid=2 -t proc proc /proc -# mount -o hidepid=1 -t proc proc /tmp/proc +# mount -o hidepid=invisible -t proc proc /proc +# mount -o hidepid=noaccess -t proc proc /tmp/proc # grep ^proc /proc/mounts -proc /proc proc rw,relatime,hidepid=2 0 0 -proc /tmp/proc proc rw,relatime,hidepid=1 0 0 +proc /proc proc rw,relatime,hidepid=invisible 0 0 +proc /tmp/proc proc rw,relatime,hidepid=noaccess 0 0 diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 0d5e68fa842f..cbacac2e892b 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -165,6 +166,18 @@ void proc_invalidate_siblings_dcache(struct hlist_head *inodes, spinlock_t *lock deactivate_super(old_sb); } +static inline const char *hidepid2str(int v) +{ + switch (v) { + case HIDEPID_OFF: return "off"; + case HIDEPID_NO_ACCESS: return "noaccess"; + case HIDEPID_INVISIBLE: return "invisible"; + case HIDEPID_NOT_PTRACEABLE: return "ptraceable"; + } + WARN_ONCE(1, "bad hide_pid value: %d\n", v); + return "unknown"; +} + static int proc_show_options(struct seq_file *seq, struct dentry *root) { struct proc_fs_info *fs_info = proc_sb_info(root->d_sb); @@ -172,7 +185,7 @@ static int proc_show_options(struct seq_file *seq, struct dentry *root) if (!gid_eq(fs_info->pid_gid, GLOBAL_ROOT_GID)) seq_printf(seq, ",gid=%u", from_kgid_munged(&init_user_ns, fs_info->pid_gid)); if (fs_info->hide_pid != HIDEPID_OFF) - seq_printf(seq, ",hidepid=%u", fs_info->hide_pid); + seq_printf(seq, ",hidepid=%s", hidepid2str(fs_info->hide_pid)); if (fs_info->pidonly != PROC_PIDONLY_OFF) seq_printf(seq, ",subset=pid"); diff --git a/fs/proc/root.c b/fs/proc/root.c index baff006a918f..288093261b7f 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -45,7 +45,7 @@ enum proc_param { static const struct fs_parameter_spec proc_fs_parameters[] = { fsparam_u32("gid", Opt_gid), - fsparam_u32("hidepid", Opt_hidepid), + fsparam_string("hidepid", Opt_hidepid), fsparam_string("subset", Opt_subset), {} }; @@ -58,6 +58,37 @@ static inline int valid_hidepid(unsigned int value) value == HIDEPID_NOT_PTRACEABLE); } +static int proc_parse_hidepid_param(struct fs_context *fc, struct fs_parameter *param) +{ + struct proc_fs_context *ctx = fc->fs_private; + struct fs_parameter_spec hidepid_u32_spec = fsparam_u32("hidepid", Opt_hidepid); + struct fs_parse_result result; + int base = (unsigned long)hidepid_u32_spec.data; + + if (param->type != fs_value_is_string) + return invalf(fc, "proc: unexpected type of hidepid value\n"); + + if (!kstrtouint(param->string, base, &result.uint_32)) { + if (!valid_hidepid(result.uint_32)) + return invalf(fc, "proc: unknown value of hidepid - %s\n", param->string); + ctx->hidepid = result.uint_32; + return 0; + } + + if (!strcmp(param->string, "off")) + ctx->hidepid = HIDEPID_OFF; + else if (!strcmp(param->string, "noaccess")) + ctx->hidepid = HIDEPID_NO_ACCESS; + else if (!strcmp(param->string, "invisible")) + ctx->hidepid = HIDEPID_INVISIBLE; + else if (!strcmp(param->string, "ptraceable")) + ctx->hidepid = HIDEPID_NOT_PTRACEABLE; + else + return invalf(fc, "proc: unknown value of hidepid - %s\n", param->string); + + return 0; +} + static int proc_parse_subset_param(struct fs_context *fc, char *value) { struct proc_fs_context *ctx = fc->fs_private; @@ -97,9 +128,8 @@ static int proc_parse_param(struct fs_context *fc, struct fs_parameter *param) break; case Opt_hidepid: - if (!valid_hidepid(result.uint_32)) - return invalf(fc, "proc: unknown value of hidepid.\n"); - ctx->hidepid = result.uint_32; + if (proc_parse_hidepid_param(fc, param)) + return -EINVAL; break; case Opt_subset: diff --git a/tools/testing/selftests/proc/.gitignore b/tools/testing/selftests/proc/.gitignore index 126901c5d6f4..bed4b5318a86 100644 --- a/tools/testing/selftests/proc/.gitignore +++ b/tools/testing/selftests/proc/.gitignore @@ -2,6 +2,7 @@ /fd-001-lookup /fd-002-posix-eq /fd-003-kthread +/proc-fsconfig-hidepid /proc-loadavg-001 /proc-multiple-procfs /proc-pid-vm diff --git a/tools/testing/selftests/proc/Makefile b/tools/testing/selftests/proc/Makefile index bf22457256be..8be8a03d2973 100644 --- a/tools/testing/selftests/proc/Makefile +++ b/tools/testing/selftests/proc/Makefile @@ -20,5 +20,6 @@ TEST_GEN_PROGS += setns-dcache TEST_GEN_PROGS += setns-sysvipc TEST_GEN_PROGS += thread-self TEST_GEN_PROGS += proc-multiple-procfs +TEST_GEN_PROGS += proc-fsconfig-hidepid include ../lib.mk diff --git a/tools/testing/selftests/proc/proc-fsconfig-hidepid.c b/tools/testing/selftests/proc/proc-fsconfig-hidepid.c new file mode 100644 index 000000000000..b9af8f537185 --- /dev/null +++ b/tools/testing/selftests/proc/proc-fsconfig-hidepid.c @@ -0,0 +1,50 @@ +/* + * Copyright © 2020 Alexey Gladkov + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include +#include +#include +#include +#include + +static inline int fsopen(const char *fsname, unsigned int flags) +{ + return syscall(__NR_fsopen, fsname, flags); +} + +static inline int fsconfig(int fd, unsigned int cmd, const char *key, const void *val, int aux) +{ + return syscall(__NR_fsconfig, fd, cmd, key, val, aux); +} + +int main(void) +{ + int fsfd, ret; + int hidepid = 2; + + assert((fsfd = fsopen("proc", 0)) != -1); + + ret = fsconfig(fsfd, FSCONFIG_SET_BINARY, "hidepid", &hidepid, 0); + assert(ret == -1); + assert(errno == EINVAL); + + assert(!fsconfig(fsfd, FSCONFIG_SET_STRING, "hidepid", "2", 0)); + assert(!fsconfig(fsfd, FSCONFIG_SET_STRING, "hidepid", "invisible", 0)); + + assert(!close(fsfd)); + + return 0; +} -- 2.25.3