aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2006-06-26 03:25:58 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-26 12:58:26 -0400
commit778c1144771f0064b6f51bee865cceb0d996f2f9 (patch)
tree1204e6d84cc5e7e75374544102839e85cfb9eba0
parent5b0c1dd38b66e2dd0cf655aa845e341b50b93ddd (diff)
[PATCH] proc: Use sane permission checks on the /proc/<pid>/fd/ symlinks
Since 2.2 we have been doing a chroot check to see if it is appropriate to return a read or follow one of these magic symlinks. The chroot check was asking a question about the visibility of files to the calling process and it was actually checking the destination process, and not the files themselves. That test was clearly bogus. In my first pass through I simply fixed the test to check the visibility of the files themselves. That naive approach to fixing the permissions was too strict and resulted in cases where a task could not even see all of it's file descriptors. What has disturbed me about relaxing this check is that file descriptors are per-process private things, and they are occasionaly used a user space capability tokens. Looking a little farther into the symlink path on /proc I did find userid checks and a check for capability (CAP_DAC_OVERRIDE) so there were permissions checking this. But I was still concerned about privacy. Besides /proc there is only one other way to find out this kind of information, and that is ptrace. ptrace has been around for a long time and it has a well established security model. So after thinking about it I finally realized that the permission checks that make sense are the permission checks applied to ptrace_attach. The checks are simple per process, and won't cause nasty surprises for people coming from less capable unices. Unfortunately there is one case that the current ptrace_attach test does not cover: Zombies and kernel threads. Single stepping those kinds of processes is impossible. Being able to see which file descriptors are open on these tasks is important to lsof, fuser and friends. So for these special processes I made the rule you can't find out unless you have CAP_SYS_PTRACE. These proc permission checks should now conform to the principle of least surprise. As well as using much less code to implement :) Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--fs/proc/base.c124
1 files changed, 29 insertions, 95 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c
index f0db7f616ac3..f38da6bda269 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -532,42 +532,34 @@ static int proc_oom_score(struct task_struct *task, char *buffer)
532/************************************************************************/ 532/************************************************************************/
533 533
534/* permission checks */ 534/* permission checks */
535 535static int proc_fd_access_allowed(struct inode *inode)
536/* If the process being read is separated by chroot from the reading process,
537 * don't let the reader access the threads.
538 */
539static int proc_check_chroot(struct dentry *de, struct vfsmount *mnt)
540{ 536{
541 struct dentry *base; 537 struct task_struct *task;
542 struct vfsmount *our_vfsmnt; 538 int allowed = 0;
543 int res = 0; 539 /* Allow access to a task's file descriptors if either we may
544 540 * use ptrace attach to the process and find out that
545 read_lock(&current->fs->lock); 541 * information, or if the task cannot possibly be ptraced
546 our_vfsmnt = mntget(current->fs->rootmnt); 542 * allow access if we have the proper capability.
547 base = dget(current->fs->root); 543 */
548 read_unlock(&current->fs->lock); 544 task = get_proc_task(inode);
549 545 if (task == current)
550 spin_lock(&vfsmount_lock); 546 allowed = 1;
547 if (task && !allowed) {
548 int alive;
551 549
552 while (mnt != our_vfsmnt) { 550 task_lock(task);
553 if (mnt == mnt->mnt_parent) 551 alive = !!task->mm;
554 goto out; 552 task_unlock(task);
555 de = mnt->mnt_mountpoint; 553 if (alive)
556 mnt = mnt->mnt_parent; 554 /* For a living task obey ptrace_may_attach */
555 allowed = ptrace_may_attach(task);
556 else
557 /* For a special task simply check the capability */
558 allowed = capable(CAP_SYS_PTRACE);
557 } 559 }
558 560 if (task)
559 if (!is_subdir(de, base)) 561 put_task_struct(task);
560 goto out; 562 return allowed;
561 spin_unlock(&vfsmount_lock);
562
563exit:
564 dput(base);
565 mntput(our_vfsmnt);
566 return res;
567out:
568 spin_unlock(&vfsmount_lock);
569 res = -EACCES;
570 goto exit;
571} 563}
572 564
573extern struct seq_operations mounts_op; 565extern struct seq_operations mounts_op;
@@ -1062,52 +1054,6 @@ static struct file_operations proc_seccomp_operations = {
1062}; 1054};
1063#endif /* CONFIG_SECCOMP */ 1055#endif /* CONFIG_SECCOMP */
1064 1056
1065static int proc_check_dentry_visible(struct inode *inode,
1066 struct dentry *dentry, struct vfsmount *mnt)
1067{
1068 /* Verify that the current process can already see the
1069 * file pointed at by the file descriptor.
1070 * This prevents /proc from being an accidental information leak.
1071 *
1072 * This prevents access to files that are not visible do to
1073 * being on the otherside of a chroot, in a different
1074 * namespace, or are simply process local (like pipes).
1075 */
1076 struct task_struct *task;
1077 int error = -EACCES;
1078
1079 /* See if the the two tasks share a commone set of
1080 * file descriptors. If so everything is visible.
1081 */
1082 rcu_read_lock();
1083 task = tref_task(proc_tref(inode));
1084 if (task) {
1085 struct files_struct *task_files, *files;
1086 /* This test answeres the question:
1087 * Is there a point in time since we looked up the
1088 * file descriptor where the two tasks share the
1089 * same files struct?
1090 */
1091 rmb();
1092 files = current->files;
1093 task_files = task->files;
1094 if (files && (files == task_files))
1095 error = 0;
1096 }
1097 rcu_read_unlock();
1098 if (!error)
1099 goto out;
1100
1101 /* If the two tasks don't share a common set of file
1102 * descriptors see if the destination dentry is already
1103 * visible in the current tasks filesystem namespace.
1104 */
1105 error = proc_check_chroot(dentry, mnt);
1106out:
1107 return error;
1108
1109}
1110
1111static void *proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd) 1057static void *proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd)
1112{ 1058{
1113 struct inode *inode = dentry->d_inode; 1059 struct inode *inode = dentry->d_inode;
@@ -1116,18 +1062,12 @@ static void *proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd)
1116 /* We don't need a base pointer in the /proc filesystem */ 1062 /* We don't need a base pointer in the /proc filesystem */
1117 path_release(nd); 1063 path_release(nd);
1118 1064
1119 if (current->fsuid != inode->i_uid && !capable(CAP_DAC_OVERRIDE)) 1065 /* Are we allowed to snoop on the tasks file descriptors? */
1066 if (!proc_fd_access_allowed(inode))
1120 goto out; 1067 goto out;
1121 1068
1122 error = PROC_I(inode)->op.proc_get_link(inode, &nd->dentry, &nd->mnt); 1069 error = PROC_I(inode)->op.proc_get_link(inode, &nd->dentry, &nd->mnt);
1123 nd->last_type = LAST_BIND; 1070 nd->last_type = LAST_BIND;
1124 if (error)
1125 goto out;
1126
1127 /* Only return files this task can already see */
1128 error = proc_check_dentry_visible(inode, nd->dentry, nd->mnt);
1129 if (error)
1130 path_release(nd);
1131out: 1071out:
1132 return ERR_PTR(error); 1072 return ERR_PTR(error);
1133} 1073}
@@ -1165,21 +1105,15 @@ static int proc_pid_readlink(struct dentry * dentry, char __user * buffer, int b
1165 struct dentry *de; 1105 struct dentry *de;
1166 struct vfsmount *mnt = NULL; 1106 struct vfsmount *mnt = NULL;
1167 1107
1168 1108 /* Are we allowed to snoop on the tasks file descriptors? */
1169 if (current->fsuid != inode->i_uid && !capable(CAP_DAC_OVERRIDE)) 1109 if (!proc_fd_access_allowed(inode))
1170 goto out; 1110 goto out;
1171 1111
1172 error = PROC_I(inode)->op.proc_get_link(inode, &de, &mnt); 1112 error = PROC_I(inode)->op.proc_get_link(inode, &de, &mnt);
1173 if (error) 1113 if (error)
1174 goto out; 1114 goto out;
1175 1115
1176 /* Only return files this task can already see */
1177 error = proc_check_dentry_visible(inode, de, mnt);
1178 if (error)
1179 goto out_put;
1180
1181 error = do_proc_readlink(de, mnt, buffer, buflen); 1116 error = do_proc_readlink(de, mnt, buffer, buflen);
1182out_put:
1183 dput(de); 1117 dput(de);
1184 mntput(mnt); 1118 mntput(mnt);
1185out: 1119out: