From de6102258b6fc4b111129c4659f703e5ec64bcd2 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 7 Jun 2012 06:59:12 +0000 Subject: [PATCH] proc: Backport hidepid mount option from Linux 3.4 (Closes: #669028) svn path=/dists/sid/linux/; revision=19095 --- debian/changelog | 1 + .../0001-procfs-parse-mount-options.patch | 167 +++++++++ ...fs-add-hidepid-and-gid-mount-options.patch | 332 ++++++++++++++++++ ...pointer-deref-in-proc_pid_permission.patch | 42 +++ .../0004-proc-fix-mount-t-proc-o-AAA.patch | 49 +++ debian/patches/series-all | 6 + 6 files changed, 597 insertions(+) create mode 100644 debian/patches/features/all/hidepid/0001-procfs-parse-mount-options.patch create mode 100644 debian/patches/features/all/hidepid/0002-procfs-add-hidepid-and-gid-mount-options.patch create mode 100644 debian/patches/features/all/hidepid/0003-proc-fix-null-pointer-deref-in-proc_pid_permission.patch create mode 100644 debian/patches/features/all/hidepid/0004-proc-fix-mount-t-proc-o-AAA.patch diff --git a/debian/changelog b/debian/changelog index 003832033..4fbdca55b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -21,6 +21,7 @@ linux (3.2.19-2) UNRELEASED; urgency=low modules as required even if they are built-in in some configurations * filter: Allow to create sk-unattached filters * net: Backport team driver from Linux 3.5-rc1 + * proc: Backport hidepid mount option from Linux 3.4 (Closes: #669028) -- Ben Hutchings Sat, 02 Jun 2012 20:31:53 +0100 diff --git a/debian/patches/features/all/hidepid/0001-procfs-parse-mount-options.patch b/debian/patches/features/all/hidepid/0001-procfs-parse-mount-options.patch new file mode 100644 index 000000000..8eabaebd7 --- /dev/null +++ b/debian/patches/features/all/hidepid/0001-procfs-parse-mount-options.patch @@ -0,0 +1,167 @@ +From: Vasiliy Kulikov +Date: Tue, 10 Jan 2012 15:11:27 -0800 +Subject: procfs: parse mount options + +commit 97412950b10e64f347aec4a9b759395c2465adf6 upstream. + +Add support for procfs mount options. Actual mount options are coming in +the next patches. + +Signed-off-by: Vasiliy Kulikov +Cc: Alexey Dobriyan +Cc: Al Viro +Cc: Randy Dunlap +Cc: "H. Peter Anvin" +Cc: Greg KH +Cc: Theodore Tso +Cc: Alan Cox +Cc: James Morris +Cc: Oleg Nesterov +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +[bwh: Backported to 3.2: super_operations::show_options takes a + struct vfsmount *, not struct dentry *] +--- + fs/proc/inode.c | 10 ++++++++++ + fs/proc/internal.h | 1 + + fs/proc/root.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++-- + 3 files changed, 64 insertions(+), 2 deletions(-) + +--- a/fs/proc/inode.c ++++ b/fs/proc/inode.c +@@ -7,6 +7,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -17,7 +18,9 @@ + #include + #include + #include ++#include + #include ++#include + + #include + #include +@@ -102,12 +105,19 @@ + init_once); + } + ++static int proc_show_options(struct seq_file *seq, struct vfsmount *vfs) ++{ ++ return 0; ++} ++ + static const struct super_operations proc_sops = { + .alloc_inode = proc_alloc_inode, + .destroy_inode = proc_destroy_inode, + .drop_inode = generic_delete_inode, + .evict_inode = proc_evict_inode, + .statfs = simple_statfs, ++ .remount_fs = proc_remount, ++ .show_options = proc_show_options, + }; + + static void __pde_users_dec(struct proc_dir_entry *pde) +--- a/fs/proc/internal.h ++++ b/fs/proc/internal.h +@@ -117,6 +117,7 @@ + + int proc_fill_super(struct super_block *); + struct inode *proc_get_inode(struct super_block *, struct proc_dir_entry *); ++int proc_remount(struct super_block *sb, int *flags, char *data); + + /* + * These are generic /proc routines that use the internal +--- a/fs/proc/root.c ++++ b/fs/proc/root.c +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + + #include "internal.h" + +@@ -36,6 +37,48 @@ + return err; + } + ++enum { ++ Opt_err, ++}; ++ ++static const match_table_t tokens = { ++ {Opt_err, NULL}, ++}; ++ ++static int proc_parse_options(char *options, struct pid_namespace *pid) ++{ ++ char *p; ++ substring_t args[MAX_OPT_ARGS]; ++ ++ pr_debug("proc: options = %s\n", options); ++ ++ if (!options) ++ return 1; ++ ++ while ((p = strsep(&options, ",")) != NULL) { ++ int token; ++ if (!*p) ++ continue; ++ ++ args[0].to = args[0].from = 0; ++ token = match_token(p, tokens, args); ++ switch (token) { ++ default: ++ pr_err("proc: unrecognized mount option \"%s\" " ++ "or missing value\n", p); ++ return 0; ++ } ++ } ++ ++ return 1; ++} ++ ++int proc_remount(struct super_block *sb, int *flags, char *data) ++{ ++ struct pid_namespace *pid = sb->s_fs_info; ++ return !proc_parse_options(data, pid); ++} ++ + static struct dentry *proc_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) + { +@@ -43,11 +86,15 @@ + struct super_block *sb; + struct pid_namespace *ns; + struct proc_inode *ei; ++ char *options; + +- if (flags & MS_KERNMOUNT) ++ if (flags & MS_KERNMOUNT) { + ns = (struct pid_namespace *)data; +- else ++ options = NULL; ++ } else { + ns = current->nsproxy->pid_ns; ++ options = data; ++ } + + sb = sget(fs_type, proc_test_super, proc_set_super, ns); + if (IS_ERR(sb)) +@@ -55,6 +102,10 @@ + + if (!sb->s_root) { + sb->s_flags = flags; ++ if (!proc_parse_options(options, ns)) { ++ deactivate_locked_super(sb); ++ return ERR_PTR(-EINVAL); ++ } + err = proc_fill_super(sb); + if (err) { + deactivate_locked_super(sb); diff --git a/debian/patches/features/all/hidepid/0002-procfs-add-hidepid-and-gid-mount-options.patch b/debian/patches/features/all/hidepid/0002-procfs-add-hidepid-and-gid-mount-options.patch new file mode 100644 index 000000000..f1adaf8dc --- /dev/null +++ b/debian/patches/features/all/hidepid/0002-procfs-add-hidepid-and-gid-mount-options.patch @@ -0,0 +1,332 @@ +From: Vasiliy Kulikov +Date: Tue, 10 Jan 2012 15:11:31 -0800 +Subject: procfs: add hidepid= and gid= mount options + +commit 0499680a42141d86417a8fbaa8c8db806bea1201 upstream. + +Add support for mount options to restrict access to /proc/PID/ +directories. The default backward-compatible "relaxed" behaviour is left +untouched. + +The first mount option is called "hidepid" and its value defines how much +info about processes we want to be available for non-owners: + +hidepid=0 (default) means the old behavior - anybody may read all +world-readable /proc/PID/* files. + +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. As permission checking done in proc_pid_permission() +and files' permissions are left untouched, programs expecting specific +files' modes are not confused. + +hidepid=2 means hidepid=1 plus all /proc/PID/ will be invisible to other +users. It doesn't mean that it hides whether a process exists (it can be +learned by other means, e.g. by kill -0 $PID), but it hides process' euid +and egid. It compicates intruder's task of gathering info about running +processes, whether some daemon runs with elevated privileges, whether +another user runs some sensitive program, whether other users run any +program at all, etc. + +gid=XXX defines a group that will be able to gather all processes' info +(as in hidepid=0 mode). This group should be used instead of putting +nonroot user in sudoers file or something. However, untrusted users (like +daemons, etc.) which are not supposed to monitor the tasks in the whole +system should not be added to the group. + +hidepid=1 or higher is designed to restrict access to procfs files, which +might reveal some sensitive private information like precise keystrokes +timings: + +http://www.openwall.com/lists/oss-security/2011/11/05/3 + +hidepid=1/2 doesn't break monitoring userspace tools. ps, top, pgrep, and +conky gracefully handle EPERM/ENOENT and behave as if the current user is +the only user running processes. pstree shows the process subtree which +contains "pstree" process. + +Note: the patch doesn't deal with setuid/setgid issues of keeping +preopened descriptors of procfs files (like +https://lkml.org/lkml/2011/2/7/368). We rely on that the leaked +information like the scheduling counters of setuid apps doesn't threaten +anybody's privacy - only the user started the setuid program may read the +counters. + +Signed-off-by: Vasiliy Kulikov +Cc: Alexey Dobriyan +Cc: Al Viro +Cc: Randy Dunlap +Cc: "H. Peter Anvin" +Cc: Greg KH +Cc: Theodore Tso +Cc: Alan Cox +Cc: James Morris +Cc: Oleg Nesterov +Cc: Hugh Dickins +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +[bwh: Backported to 3.2: super_operations::show_options takes a + struct vfsmount *, not struct dentry *] +--- + Documentation/filesystems/proc.txt | 39 ++++++++++++++++++++ + fs/proc/base.c | 69 +++++++++++++++++++++++++++++++++++- + fs/proc/inode.c | 8 +++++ + fs/proc/root.c | 21 +++++++++-- + include/linux/pid_namespace.h | 2 ++ + 5 files changed, 135 insertions(+), 4 deletions(-) + +--- a/Documentation/filesystems/proc.txt ++++ b/Documentation/filesystems/proc.txt +@@ -41,6 +41,8 @@ + 3.5 /proc//mountinfo - Information about mounts + 3.6 /proc//comm & /proc//task//comm + ++ 4 Configuring procfs ++ 4.1 Mount options + + ------------------------------------------------------------------------------ + Preface +@@ -1542,3 +1544,40 @@ + is limited in size compared to the cmdline value, so writing anything longer + then the kernel's TASK_COMM_LEN (currently 16 chars) will result in a truncated + comm value. ++ ++ ++------------------------------------------------------------------------------ ++Configuring procfs ++------------------------------------------------------------------------------ ++ ++4.1 Mount options ++--------------------- ++ ++The following mount options are supported: ++ ++ hidepid= Set /proc// access mode. ++ gid= Set the group authorized to learn processes information. ++ ++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. ++ ++gid= defines a group authorized to learn processes information otherwise ++prohibited by hidepid=. If you use some daemon like identd which needs to learn ++information about processes information, just add identd to this group. +--- a/fs/proc/base.c ++++ b/fs/proc/base.c +@@ -574,6 +574,50 @@ + return 0; + } + ++/* ++ * May current process learn task's sched/cmdline info (for hide_pid_min=1) ++ * or euid/egid (for hide_pid_min=2)? ++ */ ++static bool has_pid_permissions(struct pid_namespace *pid, ++ struct task_struct *task, ++ int hide_pid_min) ++{ ++ if (pid->hide_pid < hide_pid_min) ++ return true; ++ if (in_group_p(pid->pid_gid)) ++ return true; ++ return ptrace_may_access(task, PTRACE_MODE_READ); ++} ++ ++ ++static int proc_pid_permission(struct inode *inode, int mask) ++{ ++ struct pid_namespace *pid = inode->i_sb->s_fs_info; ++ struct task_struct *task; ++ bool has_perms; ++ ++ task = get_proc_task(inode); ++ has_perms = has_pid_permissions(pid, task, 1); ++ put_task_struct(task); ++ ++ if (!has_perms) { ++ if (pid->hide_pid == 2) { ++ /* ++ * Let's make getdents(), stat(), and open() ++ * consistent with each other. If a process ++ * may not stat() a file, it shouldn't be seen ++ * in procfs at all. ++ */ ++ return -ENOENT; ++ } ++ ++ return -EPERM; ++ } ++ return generic_permission(inode, mask); ++} ++ ++ ++ + static const struct inode_operations proc_def_inode_operations = { + .setattr = proc_setattr, + }; +@@ -1642,6 +1686,7 @@ + struct inode *inode = dentry->d_inode; + struct task_struct *task; + const struct cred *cred; ++ struct pid_namespace *pid = dentry->d_sb->s_fs_info; + + generic_fillattr(inode, stat); + +@@ -1650,6 +1695,14 @@ + stat->gid = 0; + task = pid_task(proc_pid(inode), PIDTYPE_PID); + if (task) { ++ if (!has_pid_permissions(pid, task, 2)) { ++ rcu_read_unlock(); ++ /* ++ * This doesn't prevent learning whether PID exists, ++ * it only makes getattr() consistent with readdir(). ++ */ ++ return -ENOENT; ++ } + if ((inode->i_mode == (S_IFDIR|S_IRUGO|S_IXUGO)) || + task_dumpable(task)) { + cred = __task_cred(task); +@@ -2794,6 +2847,7 @@ + .lookup = proc_tgid_base_lookup, + .getattr = pid_getattr, + .setattr = proc_setattr, ++ .permission = proc_pid_permission, + }; + + static void proc_flush_task_mnt(struct vfsmount *mnt, pid_t pid, pid_t tgid) +@@ -2997,6 +3051,12 @@ + proc_pid_instantiate, iter.task, NULL); + } + ++static int fake_filldir(void *buf, const char *name, int namelen, ++ loff_t offset, u64 ino, unsigned d_type) ++{ ++ return 0; ++} ++ + /* for the /proc/ directory itself, after non-process stuff has been done */ + int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) + { +@@ -3004,6 +3064,7 @@ + struct task_struct *reaper; + struct tgid_iter iter; + struct pid_namespace *ns; ++ filldir_t __filldir; + + if (filp->f_pos >= PID_MAX_LIMIT + TGID_OFFSET) + goto out_no_task; +@@ -3025,8 +3086,13 @@ + for (iter = next_tgid(ns, iter); + iter.task; + iter.tgid += 1, iter = next_tgid(ns, iter)) { ++ if (has_pid_permissions(ns, iter.task, 2)) ++ __filldir = filldir; ++ else ++ __filldir = fake_filldir; ++ + filp->f_pos = iter.tgid + TGID_OFFSET; +- if (proc_pid_fill_cache(filp, dirent, filldir, iter) < 0) { ++ if (proc_pid_fill_cache(filp, dirent, __filldir, iter) < 0) { + put_task_struct(iter.task); + goto out; + } +@@ -3361,6 +3427,7 @@ + .lookup = proc_task_lookup, + .getattr = proc_task_getattr, + .setattr = proc_setattr, ++ .permission = proc_pid_permission, + }; + + static const struct file_operations proc_task_operations = { +--- a/fs/proc/inode.c ++++ b/fs/proc/inode.c +@@ -107,6 +107,14 @@ + + static int proc_show_options(struct seq_file *seq, struct vfsmount *vfs) + { ++ struct super_block *sb = vfs->mnt_sb; ++ struct pid_namespace *pid = sb->s_fs_info; ++ ++ if (pid->pid_gid) ++ seq_printf(seq, ",gid=%lu", (unsigned long)pid->pid_gid); ++ if (pid->hide_pid != 0) ++ seq_printf(seq, ",hidepid=%u", pid->hide_pid); ++ + return 0; + } + +--- a/fs/proc/root.c ++++ b/fs/proc/root.c +@@ -38,10 +38,12 @@ + } + + enum { +- Opt_err, ++ Opt_gid, Opt_hidepid, Opt_err, + }; + + static const match_table_t tokens = { ++ {Opt_hidepid, "hidepid=%u"}, ++ {Opt_gid, "gid=%u"}, + {Opt_err, NULL}, + }; + +@@ -49,8 +51,7 @@ + { + char *p; + substring_t args[MAX_OPT_ARGS]; +- +- pr_debug("proc: options = %s\n", options); ++ int option; + + if (!options) + return 1; +@@ -63,6 +64,20 @@ + args[0].to = args[0].from = 0; + token = match_token(p, tokens, args); + switch (token) { ++ case Opt_gid: ++ if (match_int(&args[0], &option)) ++ return 0; ++ pid->pid_gid = option; ++ break; ++ case Opt_hidepid: ++ if (match_int(&args[0], &option)) ++ return 0; ++ if (option < 0 || option > 2) { ++ pr_err("proc: hidepid value must be between 0 and 2.\n"); ++ return 0; ++ } ++ pid->hide_pid = option; ++ break; + default: + pr_err("proc: unrecognized mount option \"%s\" " + "or missing value\n", p); +--- a/include/linux/pid_namespace.h ++++ b/include/linux/pid_namespace.h +@@ -30,6 +30,8 @@ + #ifdef CONFIG_BSD_PROCESS_ACCT + struct bsd_acct_struct *bacct; + #endif ++ gid_t pid_gid; ++ int hide_pid; + }; + + extern struct pid_namespace init_pid_ns; diff --git a/debian/patches/features/all/hidepid/0003-proc-fix-null-pointer-deref-in-proc_pid_permission.patch b/debian/patches/features/all/hidepid/0003-proc-fix-null-pointer-deref-in-proc_pid_permission.patch new file mode 100644 index 000000000..6e4ecaa53 --- /dev/null +++ b/debian/patches/features/all/hidepid/0003-proc-fix-null-pointer-deref-in-proc_pid_permission.patch @@ -0,0 +1,42 @@ +From: Xiaotian Feng +Date: Thu, 12 Jan 2012 17:17:08 -0800 +Subject: proc: fix null pointer deref in proc_pid_permission() + +commit a2ef990ab5a6705a356d146dd773a3b359787497 upstream. + +get_proc_task() can fail to search the task and return NULL, +put_task_struct() will then bomb the kernel with following oops: + + BUG: unable to handle kernel NULL pointer dereference at 0000000000000010 + IP: [] proc_pid_permission+0x64/0xe0 + PGD 112075067 PUD 112814067 PMD 0 + Oops: 0002 [#1] PREEMPT SMP + +This is a regression introduced by commit 0499680a ("procfs: add hidepid= +and gid= mount options"). The kernel should return -ESRCH if +get_proc_task() failed. + +Signed-off-by: Xiaotian Feng +Cc: Al Viro +Cc: Vasiliy Kulikov +Cc: Stephen Wilson +Acked-by: David Rientjes +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +--- + fs/proc/base.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/fs/proc/base.c b/fs/proc/base.c +index 8173dfd..5485a53 100644 +--- a/fs/proc/base.c ++++ b/fs/proc/base.c +@@ -654,6 +654,8 @@ static int proc_pid_permission(struct inode *inode, int mask) + bool has_perms; + + task = get_proc_task(inode); ++ if (!task) ++ return -ESRCH; + has_perms = has_pid_permissions(pid, task, 1); + put_task_struct(task); + diff --git a/debian/patches/features/all/hidepid/0004-proc-fix-mount-t-proc-o-AAA.patch b/debian/patches/features/all/hidepid/0004-proc-fix-mount-t-proc-o-AAA.patch new file mode 100644 index 000000000..da1a2f4b2 --- /dev/null +++ b/debian/patches/features/all/hidepid/0004-proc-fix-mount-t-proc-o-AAA.patch @@ -0,0 +1,49 @@ +From: Vasiliy Kulikov +Date: Thu, 5 Apr 2012 14:25:04 -0700 +Subject: proc: fix mount -t proc -o AAA + +commit 99663be772c827b8f5f594fe87eb4807be1994e5 upstream. + +The proc_parse_options() call from proc_mount() runs only once at boot +time. So on any later mount attempt, any mount options are ignored +because ->s_root is already initialized. + +As a consequence, "mount -o " will ignore the options. The +only way to change mount options is "mount -o remount,". + +To fix this, parse the mount options unconditionally. + +Signed-off-by: Vasiliy Kulikov +Reported-by: Arkadiusz Miskiewicz +Tested-by: Arkadiusz Miskiewicz +Cc: Alexey Dobriyan +Cc: Al Viro +Cc: Valdis Kletnieks +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +--- + fs/proc/root.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/fs/proc/root.c b/fs/proc/root.c +index 46a15d8..eed44bf 100644 +--- a/fs/proc/root.c ++++ b/fs/proc/root.c +@@ -115,12 +115,13 @@ static struct dentry *proc_mount(struct file_system_type *fs_type, + if (IS_ERR(sb)) + return ERR_CAST(sb); + ++ if (!proc_parse_options(options, ns)) { ++ deactivate_locked_super(sb); ++ return ERR_PTR(-EINVAL); ++ } ++ + if (!sb->s_root) { + sb->s_flags = flags; +- if (!proc_parse_options(options, ns)) { +- deactivate_locked_super(sb); +- return ERR_PTR(-EINVAL); +- } + err = proc_fill_super(sb); + if (err) { + deactivate_locked_super(sb); diff --git a/debian/patches/series-all b/debian/patches/series-all index 658496c17..1448a6f5b 100644 --- a/debian/patches/series-all +++ b/debian/patches/series-all @@ -337,3 +337,9 @@ features/all/team/0020-team-add-missed-statics.patch features/all/team/0021-team-lb-let-userspace-care-about-port-macs.patch features/all/team/0022-team-allow-to-enable-disable-ports.patch features/all/team/0023-team-add-per-port-option-for-enabling-disabling-port.patch + +# procfs hidepid from 3.4 +features/all/hidepid/0001-procfs-parse-mount-options.patch +features/all/hidepid/0002-procfs-add-hidepid-and-gid-mount-options.patch +features/all/hidepid/0003-proc-fix-null-pointer-deref-in-proc_pid_permission.patch +features/all/hidepid/0004-proc-fix-mount-t-proc-o-AAA.patch