|
@@ -74,6 +74,16 @@
|
|
|
#include <linux/poll.h>
|
|
|
#include "internal.h"
|
|
|
|
|
|
+/* NOTE:
|
|
|
+ * Implementing inode permission operations in /proc is almost
|
|
|
+ * certainly an error. Permission checks need to happen during
|
|
|
+ * each system call not at open time. The reason is that most of
|
|
|
+ * what we wish to check for permissions in /proc varies at runtime.
|
|
|
+ *
|
|
|
+ * The classic example of a problem is opening file descriptors
|
|
|
+ * in /proc for a task before it execs a suid executable.
|
|
|
+ */
|
|
|
+
|
|
|
/*
|
|
|
* For hysterical raisins we keep the same inumbers as in the old procfs.
|
|
|
* Feel free to change the macro below - just keep the range distinct from
|
|
@@ -494,13 +504,11 @@ static int proc_oom_score(struct task_struct *task, char *buffer)
|
|
|
|
|
|
/* If the process being read is separated by chroot from the reading process,
|
|
|
* don't let the reader access the threads.
|
|
|
- *
|
|
|
- * note: this does dput(root) and mntput(vfsmnt) on exit.
|
|
|
*/
|
|
|
-static int proc_check_chroot(struct dentry *root, struct vfsmount *vfsmnt)
|
|
|
+static int proc_check_chroot(struct dentry *de, struct vfsmount *mnt)
|
|
|
{
|
|
|
- struct dentry *de, *base;
|
|
|
- struct vfsmount *our_vfsmnt, *mnt;
|
|
|
+ struct dentry *base;
|
|
|
+ struct vfsmount *our_vfsmnt;
|
|
|
int res = 0;
|
|
|
|
|
|
read_lock(¤t->fs->lock);
|
|
@@ -509,8 +517,6 @@ static int proc_check_chroot(struct dentry *root, struct vfsmount *vfsmnt)
|
|
|
read_unlock(¤t->fs->lock);
|
|
|
|
|
|
spin_lock(&vfsmount_lock);
|
|
|
- de = root;
|
|
|
- mnt = vfsmnt;
|
|
|
|
|
|
while (mnt != our_vfsmnt) {
|
|
|
if (mnt == mnt->mnt_parent)
|
|
@@ -526,8 +532,6 @@ static int proc_check_chroot(struct dentry *root, struct vfsmount *vfsmnt)
|
|
|
exit:
|
|
|
dput(base);
|
|
|
mntput(our_vfsmnt);
|
|
|
- dput(root);
|
|
|
- mntput(vfsmnt);
|
|
|
return res;
|
|
|
out:
|
|
|
spin_unlock(&vfsmount_lock);
|
|
@@ -535,23 +539,6 @@ out:
|
|
|
goto exit;
|
|
|
}
|
|
|
|
|
|
-static int proc_check_root(struct inode *inode)
|
|
|
-{
|
|
|
- struct dentry *root;
|
|
|
- struct vfsmount *vfsmnt;
|
|
|
-
|
|
|
- if (proc_root_link(inode, &root, &vfsmnt)) /* Ewww... */
|
|
|
- return -ENOENT;
|
|
|
- return proc_check_chroot(root, vfsmnt);
|
|
|
-}
|
|
|
-
|
|
|
-static int proc_permission(struct inode *inode, int mask, struct nameidata *nd)
|
|
|
-{
|
|
|
- if (generic_permission(inode, mask, NULL) != 0)
|
|
|
- return -EACCES;
|
|
|
- return proc_check_root(inode);
|
|
|
-}
|
|
|
-
|
|
|
extern struct seq_operations proc_pid_maps_op;
|
|
|
static int maps_open(struct inode *inode, struct file *file)
|
|
|
{
|
|
@@ -1048,6 +1035,48 @@ static struct file_operations proc_seccomp_operations = {
|
|
|
};
|
|
|
#endif /* CONFIG_SECCOMP */
|
|
|
|
|
|
+static int proc_check_dentry_visible(struct inode *inode,
|
|
|
+ struct dentry *dentry, struct vfsmount *mnt)
|
|
|
+{
|
|
|
+ /* Verify that the current process can already see the
|
|
|
+ * file pointed at by the file descriptor.
|
|
|
+ * This prevents /proc from being an accidental information leak.
|
|
|
+ *
|
|
|
+ * This prevents access to files that are not visible do to
|
|
|
+ * being on the otherside of a chroot, in a different
|
|
|
+ * namespace, or are simply process local (like pipes).
|
|
|
+ */
|
|
|
+ struct task_struct *task;
|
|
|
+ struct files_struct *task_files, *files;
|
|
|
+ int error = -EACCES;
|
|
|
+
|
|
|
+ /* See if the the two tasks share a commone set of
|
|
|
+ * file descriptors. If so everything is visible.
|
|
|
+ */
|
|
|
+ task = proc_task(inode);
|
|
|
+ if (!task)
|
|
|
+ goto out;
|
|
|
+ files = get_files_struct(current);
|
|
|
+ task_files = get_files_struct(task);
|
|
|
+ if (files && task_files && (files == task_files))
|
|
|
+ error = 0;
|
|
|
+ if (task_files)
|
|
|
+ put_files_struct(task_files);
|
|
|
+ if (files)
|
|
|
+ put_files_struct(files);
|
|
|
+ if (!error)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ /* If the two tasks don't share a common set of file
|
|
|
+ * descriptors see if the destination dentry is already
|
|
|
+ * visible in the current tasks filesystem namespace.
|
|
|
+ */
|
|
|
+ error = proc_check_chroot(dentry, mnt);
|
|
|
+out:
|
|
|
+ return error;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
static void *proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd)
|
|
|
{
|
|
|
struct inode *inode = dentry->d_inode;
|
|
@@ -1058,12 +1087,16 @@ static void *proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd)
|
|
|
|
|
|
if (current->fsuid != inode->i_uid && !capable(CAP_DAC_OVERRIDE))
|
|
|
goto out;
|
|
|
- error = proc_check_root(inode);
|
|
|
- if (error)
|
|
|
- goto out;
|
|
|
|
|
|
error = PROC_I(inode)->op.proc_get_link(inode, &nd->dentry, &nd->mnt);
|
|
|
nd->last_type = LAST_BIND;
|
|
|
+ if (error)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ /* Only return files this task can already see */
|
|
|
+ error = proc_check_dentry_visible(inode, nd->dentry, nd->mnt);
|
|
|
+ if (error)
|
|
|
+ path_release(nd);
|
|
|
out:
|
|
|
return ERR_PTR(error);
|
|
|
}
|
|
@@ -1104,15 +1137,18 @@ static int proc_pid_readlink(struct dentry * dentry, char __user * buffer, int b
|
|
|
|
|
|
if (current->fsuid != inode->i_uid && !capable(CAP_DAC_OVERRIDE))
|
|
|
goto out;
|
|
|
- error = proc_check_root(inode);
|
|
|
- if (error)
|
|
|
- goto out;
|
|
|
|
|
|
error = PROC_I(inode)->op.proc_get_link(inode, &de, &mnt);
|
|
|
if (error)
|
|
|
goto out;
|
|
|
|
|
|
+ /* Only return files this task can already see */
|
|
|
+ error = proc_check_dentry_visible(inode, de, mnt);
|
|
|
+ if (error)
|
|
|
+ goto out_put;
|
|
|
+
|
|
|
error = do_proc_readlink(de, mnt, buffer, buflen);
|
|
|
+out_put:
|
|
|
dput(de);
|
|
|
mntput(mnt);
|
|
|
out:
|
|
@@ -1512,7 +1548,6 @@ static struct file_operations proc_task_operations = {
|
|
|
*/
|
|
|
static struct inode_operations proc_fd_inode_operations = {
|
|
|
.lookup = proc_lookupfd,
|
|
|
- .permission = proc_permission,
|
|
|
};
|
|
|
|
|
|
static struct inode_operations proc_task_inode_operations = {
|