aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorVasiliy Kulikov <segooon@gmail.com>2012-01-10 18:11:31 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2012-01-10 19:30:54 -0500
commit0499680a42141d86417a8fbaa8c8db806bea1201 (patch)
treeeb2aeb559bf5418476319aa81fa4f6ed3659bbc0 /fs
parent97412950b10e64f347aec4a9b759395c2465adf6 (diff)
procfs: add hidepid= and gid= mount options
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/<pid>/ 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 <segoon@openwall.com> Cc: Alexey Dobriyan <adobriyan@gmail.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: Randy Dunlap <rdunlap@xenotime.net> Cc: "H. Peter Anvin" <hpa@zytor.com> Cc: Greg KH <greg@kroah.com> Cc: Theodore Tso <tytso@MIT.EDU> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Cc: James Morris <jmorris@namei.org> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Hugh Dickins <hughd@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/proc/base.c69
-rw-r--r--fs/proc/inode.c8
-rw-r--r--fs/proc/root.c21
3 files changed, 94 insertions, 4 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 4d755fed3ecb..8173dfd89cb2 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -631,6 +631,50 @@ int proc_setattr(struct dentry *dentry, struct iattr *attr)
631 return 0; 631 return 0;
632} 632}
633 633
634/*
635 * May current process learn task's sched/cmdline info (for hide_pid_min=1)
636 * or euid/egid (for hide_pid_min=2)?
637 */
638static bool has_pid_permissions(struct pid_namespace *pid,
639 struct task_struct *task,
640 int hide_pid_min)
641{
642 if (pid->hide_pid < hide_pid_min)
643 return true;
644 if (in_group_p(pid->pid_gid))
645 return true;
646 return ptrace_may_access(task, PTRACE_MODE_READ);
647}
648
649
650static int proc_pid_permission(struct inode *inode, int mask)
651{
652 struct pid_namespace *pid = inode->i_sb->s_fs_info;
653 struct task_struct *task;
654 bool has_perms;
655
656 task = get_proc_task(inode);
657 has_perms = has_pid_permissions(pid, task, 1);
658 put_task_struct(task);
659
660 if (!has_perms) {
661 if (pid->hide_pid == 2) {
662 /*
663 * Let's make getdents(), stat(), and open()
664 * consistent with each other. If a process
665 * may not stat() a file, it shouldn't be seen
666 * in procfs at all.
667 */
668 return -ENOENT;
669 }
670
671 return -EPERM;
672 }
673 return generic_permission(inode, mask);
674}
675
676
677
634static const struct inode_operations proc_def_inode_operations = { 678static const struct inode_operations proc_def_inode_operations = {
635 .setattr = proc_setattr, 679 .setattr = proc_setattr,
636}; 680};
@@ -1615,6 +1659,7 @@ int pid_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
1615 struct inode *inode = dentry->d_inode; 1659 struct inode *inode = dentry->d_inode;
1616 struct task_struct *task; 1660 struct task_struct *task;
1617 const struct cred *cred; 1661 const struct cred *cred;
1662 struct pid_namespace *pid = dentry->d_sb->s_fs_info;
1618 1663
1619 generic_fillattr(inode, stat); 1664 generic_fillattr(inode, stat);
1620 1665
@@ -1623,6 +1668,14 @@ int pid_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
1623 stat->gid = 0; 1668 stat->gid = 0;
1624 task = pid_task(proc_pid(inode), PIDTYPE_PID); 1669 task = pid_task(proc_pid(inode), PIDTYPE_PID);
1625 if (task) { 1670 if (task) {
1671 if (!has_pid_permissions(pid, task, 2)) {
1672 rcu_read_unlock();
1673 /*
1674 * This doesn't prevent learning whether PID exists,
1675 * it only makes getattr() consistent with readdir().
1676 */
1677 return -ENOENT;
1678 }
1626 if ((inode->i_mode == (S_IFDIR|S_IRUGO|S_IXUGO)) || 1679 if ((inode->i_mode == (S_IFDIR|S_IRUGO|S_IXUGO)) ||
1627 task_dumpable(task)) { 1680 task_dumpable(task)) {
1628 cred = __task_cred(task); 1681 cred = __task_cred(task);
@@ -3119,6 +3172,7 @@ static const struct inode_operations proc_tgid_base_inode_operations = {
3119 .lookup = proc_tgid_base_lookup, 3172 .lookup = proc_tgid_base_lookup,
3120 .getattr = pid_getattr, 3173 .getattr = pid_getattr,
3121 .setattr = proc_setattr, 3174 .setattr = proc_setattr,
3175 .permission = proc_pid_permission,
3122}; 3176};
3123 3177
3124static void proc_flush_task_mnt(struct vfsmount *mnt, pid_t pid, pid_t tgid) 3178static void proc_flush_task_mnt(struct vfsmount *mnt, pid_t pid, pid_t tgid)
@@ -3322,6 +3376,12 @@ static int proc_pid_fill_cache(struct file *filp, void *dirent, filldir_t filldi
3322 proc_pid_instantiate, iter.task, NULL); 3376 proc_pid_instantiate, iter.task, NULL);
3323} 3377}
3324 3378
3379static int fake_filldir(void *buf, const char *name, int namelen,
3380 loff_t offset, u64 ino, unsigned d_type)
3381{
3382 return 0;
3383}
3384
3325/* for the /proc/ directory itself, after non-process stuff has been done */ 3385/* for the /proc/ directory itself, after non-process stuff has been done */
3326int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) 3386int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir)
3327{ 3387{
@@ -3329,6 +3389,7 @@ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir)
3329 struct task_struct *reaper; 3389 struct task_struct *reaper;
3330 struct tgid_iter iter; 3390 struct tgid_iter iter;
3331 struct pid_namespace *ns; 3391 struct pid_namespace *ns;
3392 filldir_t __filldir;
3332 3393
3333 if (filp->f_pos >= PID_MAX_LIMIT + TGID_OFFSET) 3394 if (filp->f_pos >= PID_MAX_LIMIT + TGID_OFFSET)
3334 goto out_no_task; 3395 goto out_no_task;
@@ -3350,8 +3411,13 @@ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir)
3350 for (iter = next_tgid(ns, iter); 3411 for (iter = next_tgid(ns, iter);
3351 iter.task; 3412 iter.task;
3352 iter.tgid += 1, iter = next_tgid(ns, iter)) { 3413 iter.tgid += 1, iter = next_tgid(ns, iter)) {
3414 if (has_pid_permissions(ns, iter.task, 2))
3415 __filldir = filldir;
3416 else
3417 __filldir = fake_filldir;
3418
3353 filp->f_pos = iter.tgid + TGID_OFFSET; 3419 filp->f_pos = iter.tgid + TGID_OFFSET;
3354 if (proc_pid_fill_cache(filp, dirent, filldir, iter) < 0) { 3420 if (proc_pid_fill_cache(filp, dirent, __filldir, iter) < 0) {
3355 put_task_struct(iter.task); 3421 put_task_struct(iter.task);
3356 goto out; 3422 goto out;
3357 } 3423 }
@@ -3686,6 +3752,7 @@ static const struct inode_operations proc_task_inode_operations = {
3686 .lookup = proc_task_lookup, 3752 .lookup = proc_task_lookup,
3687 .getattr = proc_task_getattr, 3753 .getattr = proc_task_getattr,
3688 .setattr = proc_setattr, 3754 .setattr = proc_setattr,
3755 .permission = proc_pid_permission,
3689}; 3756};
3690 3757
3691static const struct file_operations proc_task_operations = { 3758static const struct file_operations proc_task_operations = {
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 27c762f34870..84fd3235a590 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -106,6 +106,14 @@ void __init proc_init_inodecache(void)
106 106
107static int proc_show_options(struct seq_file *seq, struct dentry *root) 107static int proc_show_options(struct seq_file *seq, struct dentry *root)
108{ 108{
109 struct super_block *sb = root->d_sb;
110 struct pid_namespace *pid = sb->s_fs_info;
111
112 if (pid->pid_gid)
113 seq_printf(seq, ",gid=%lu", (unsigned long)pid->pid_gid);
114 if (pid->hide_pid != 0)
115 seq_printf(seq, ",hidepid=%u", pid->hide_pid);
116
109 return 0; 117 return 0;
110} 118}
111 119
diff --git a/fs/proc/root.c b/fs/proc/root.c
index 6a8ac1d361a9..46a15d8a29ca 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -38,10 +38,12 @@ static int proc_set_super(struct super_block *sb, void *data)
38} 38}
39 39
40enum { 40enum {
41 Opt_err, 41 Opt_gid, Opt_hidepid, Opt_err,
42}; 42};
43 43
44static const match_table_t tokens = { 44static const match_table_t tokens = {
45 {Opt_hidepid, "hidepid=%u"},
46 {Opt_gid, "gid=%u"},
45 {Opt_err, NULL}, 47 {Opt_err, NULL},
46}; 48};
47 49
@@ -49,8 +51,7 @@ static int proc_parse_options(char *options, struct pid_namespace *pid)
49{ 51{
50 char *p; 52 char *p;
51 substring_t args[MAX_OPT_ARGS]; 53 substring_t args[MAX_OPT_ARGS];
52 54 int option;
53 pr_debug("proc: options = %s\n", options);
54 55
55 if (!options) 56 if (!options)
56 return 1; 57 return 1;
@@ -63,6 +64,20 @@ static int proc_parse_options(char *options, struct pid_namespace *pid)
63 args[0].to = args[0].from = 0; 64 args[0].to = args[0].from = 0;
64 token = match_token(p, tokens, args); 65 token = match_token(p, tokens, args);
65 switch (token) { 66 switch (token) {
67 case Opt_gid:
68 if (match_int(&args[0], &option))
69 return 0;
70 pid->pid_gid = option;
71 break;
72 case Opt_hidepid:
73 if (match_int(&args[0], &option))
74 return 0;
75 if (option < 0 || option > 2) {
76 pr_err("proc: hidepid value must be between 0 and 2.\n");
77 return 0;
78 }
79 pid->hide_pid = option;
80 break;
66 default: 81 default:
67 pr_err("proc: unrecognized mount option \"%s\" " 82 pr_err("proc: unrecognized mount option \"%s\" "
68 "or missing value\n", p); 83 "or missing value\n", p);