lkml.org 
[lkml]   [1998]   [Mar]   [30]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    SubjectSecurity patch for /proc
    Hi all,

    Here's a patch which prevents chrooted processes from escaping from
    their chrooted area via /proc.

    At present, if you set up a chroot domain, you can't mount /proc in it,
    because processes can easily escape by chdiring through another
    non-chrooted process's root or cwd. This patch disallows access to a
    process in /proc unless it has the same or more restrictive root than
    your own.

    This still doesn't allow you to run root processes in a chrooted area
    with complete safety, but it does mean you can have processes with the
    same uid in different chrooted domains.

    There's still a couple of warts on this patch:
    - kswapd and bdflush don't have proper roots in their fs structures, so
    I always disallow access to them. I'm not sure of the implications of
    starting them after root is mounted.
    - I disallow access to all proc entries for a process, which is
    probably a little draconian. The rationale is to prevent a process from
    manipulating the memory or state of another process with the same UID,
    thereby acting beyond its chrooted domain.

    Missing features:
    - signal and ptrace should do similar checks, otherwise chrooted
    processes can still cause system-wide havoc
    - root processes should (optionally) lose priviledge which chrooted - a
    capability mask is probably the right way of doing this.

    J==== //depot/linux/2.1/fs/proc/base.c#11 - //depot/linux/local/fs/proc/base.c#6 ====
    @@ -47,7 +47,7 @@
    NULL, /* writepage */
    NULL, /* bmap */
    NULL, /* truncate */
    - NULL /* permission */
    + proc_permission /* permission */
    };

    /*
    ==== //depot/linux/2.1/fs/proc/inode.c#12 - //depot/linux/local/fs/proc/inode.c#6 ====
    @@ -129,6 +129,109 @@
    return 1;
    }

    +/*
    + * The standard rules, copied from fs/namei.c:permission().
    + */
    +static int standard_permission(struct inode *inode, int mask)
    +{
    + int mode = inode->i_mode;
    +
    + if ((mask & S_IWOTH) && IS_RDONLY(inode) &&
    + (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
    + return -EROFS; /* Nobody gets write access to a read-only fs */
    + else if ((mask & S_IWOTH) && IS_IMMUTABLE(inode))
    + return -EACCES; /* Nobody gets write access to an immutable file */
    + else if (current->fsuid == inode->i_uid)
    + mode >>= 6;
    + else if (in_group_p(inode->i_gid))
    + mode >>= 3;
    + if (((mode & mask & 0007) == mask) || fsuser())
    + return 0;
    + return -EACCES;
    +}
    +
    +/*
    + * Set up permission rules for processes looking at other processes.
    + * You're not allowed to see a process unless it has the same or more
    + * restricted root than your own. This prevents a chrooted processes
    + * from escaping through the /proc entries of less restricted
    + * processes, and thus allows /proc to be safely mounted in a chrooted
    + * area.
    + *
    + * Note that root (uid 0) doesn't get permission for this either,
    + * since chroot is stronger than root.
    + *
    + * XXX TODO: use the dentry mechanism to make off-limits procs simply
    + * invisible rather than denied? Does each namespace root get its own
    + * dentry tree?
    + *
    + * This also applies the default permissions checks, as it only adds
    + * restrictions.
    + *
    + * Jeremy Fitzhardinge <jeremy@zip.com.au>
    + */
    +int proc_permission(struct inode *inode, int mask)
    +{
    + struct task_struct *p;
    + unsigned long ino = inode->i_ino;
    + unsigned long pid;
    + struct dentry *de, *base;
    +
    + if (standard_permission(inode, mask) != 0)
    + return -EACCES;
    +
    + /*
    + * Find the root of the processes being examined (if any).
    + * XXX Surely there's a better way of doing this?
    + */
    + if (ino >= PROC_OPENPROM_FIRST &&
    + ino < PROC_OPENPROM_FIRST + PROC_NOPENPROM)
    + return 0; /* already allowed */
    +
    + pid = ino >> 16;
    + if (pid == 0)
    + return 0; /* already allowed */
    +
    + de = NULL;
    + base = current->fs->root;
    +
    + read_lock(&tasklist_lock);
    + p = find_task_by_pid(pid);
    +
    + if (p != NULL)
    + de = p->fs->root;
    + read_unlock(tasklist_lock);
    +
    + if (p == NULL)
    + return -EACCES; /* ENOENT? */
    +
    + if (de == NULL)
    + {
    + /* kswapd and bdflush don't have proper root or cwd... */
    + printk("pid %d has null root\n", pid);
    + return -EACCES;
    + }
    +
    + /* XXX locking? */
    + for(;;)
    + {
    + struct dentry *parent;
    +
    + if (de == base)
    + return 0; /* already allowed */
    +
    + de = de->d_covers;
    + parent = de->d_parent;
    +
    + if (de == parent)
    + break;
    +
    + de = parent;
    + }
    +
    + return -EACCES; /* incompatible roots */
    +}
    +
    struct inode * proc_get_inode(struct super_block * sb, int ino,
    struct proc_dir_entry * de)
    {
    ==== //depot/linux/2.1/fs/proc/link.c#13 - //depot/linux/local/fs/proc/link.c#9 ====
    @@ -58,7 +58,7 @@
    NULL, /* writepage */
    NULL, /* bmap */
    NULL, /* truncate */
    - NULL /* permission */
    + proc_permission /* permission */
    };

    static struct dentry * proc_follow_link(struct dentry *dentry,
    ==== //depot/linux/2.1/fs/proc/root.c#30 - //depot/linux/local/fs/proc/root.c#15 ====
    @@ -73,7 +73,7 @@
    NULL, /* writepage */
    NULL, /* bmap */
    NULL, /* truncate */
    - NULL /* permission */
    + NULL, /* permission */
    };

    /*
    ==== //depot/linux/2.1/include/linux/proc_fs.h#33 - //depot/linux/local/include/linux/proc_fs.h#14 ====
    @@ -327,6 +327,7 @@
    extern int proc_statfs(struct super_block *, struct statfs *, int);
    extern void proc_read_inode(struct inode *);
    extern void proc_write_inode(struct inode *);
    +extern int proc_permission(struct inode *, int);

    extern int proc_match(int, const char *,struct proc_dir_entry *);
    \
     
     \ /
      Last update: 2005-03-22 13:42    [W:0.027 / U:50.908 seconds]
    ©2003-2017 Jasper Spaans. hosted at Digital OceanAdvertise on this site