vserver 1.9.3
[linux-2.6.git] / security / selinux / hooks.c
index 52fa3cf..f844e40 100644 (file)
@@ -43,6 +43,7 @@
 #include <linux/kd.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/netfilter_ipv6.h>
+#include <linux/tty.h>
 #include <net/icmp.h>
 #include <net/ip.h>            /* for sysctl_local_port_range[] */
 #include <net/tcp.h>           /* struct or_callable used in sock_rcv_skb */
@@ -62,7 +63,6 @@
 #include <linux/nfs_mount.h>
 #include <net/ipv6.h>
 #include <linux/hugetlb.h>
-#include <linux/major.h>
 #include <linux/personality.h>
 
 #include "avc.h"
@@ -87,7 +87,7 @@ __setup("enforcing=", enforcing_setup);
 #endif
 
 #ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM
-int selinux_enabled = 1;
+int selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE;
 
 static int __init selinux_enabled_setup(char *str)
 {
@@ -1266,6 +1266,12 @@ static inline u32 file_to_av(struct file *file)
 int inode_security_set_sid(struct inode *inode, u32 sid)
 {
        struct inode_security_struct *isec = inode->i_security;
+       struct superblock_security_struct *sbsec = inode->i_sb->s_security;
+
+       if (!sbsec->initialized) {
+               /* Defer initialization to selinux_complete_init. */
+               return 0;
+       }
 
        down(&isec->sem);
        isec->sclass = inode_mode_to_security_class(inode->i_mode);
@@ -1548,10 +1554,10 @@ static int selinux_vm_enough_memory(long pages)
         /*
         * Sometimes we want to use more memory than we have
         */
-       if (sysctl_overcommit_memory == 1)
+       if (sysctl_overcommit_memory == OVERCOMMIT_ALWAYS)
                return 0;
 
-       if (sysctl_overcommit_memory == 0) {
+       if (sysctl_overcommit_memory == OVERCOMMIT_GUESS) {
                free = get_page_cache_size();
                free += nr_free_pages();
                free += nr_swap_pages;
@@ -1726,72 +1732,40 @@ static void selinux_bprm_free_security(struct linux_binprm *bprm)
        kfree(bsec);
 }
 
-/* Create an open file that refers to the null device.
-   Derived from the OpenWall LSM. */
-struct file *open_devnull(void)
-{
-       struct inode *inode;
-       struct dentry *dentry;
-       struct file *file = NULL;
-       struct inode_security_struct *isec;
-       dev_t dev;
-
-       inode = new_inode(current->fs->rootmnt->mnt_sb);
-       if (!inode)
-               goto out;
-
-       dentry = dget(d_alloc_root(inode));
-       if (!dentry)
-               goto out_iput;
-
-       file = get_empty_filp();
-       if (!file)
-               goto out_dput;
-
-       dev = MKDEV(MEM_MAJOR, 3); /* null device */
-
-       inode->i_uid = current->fsuid;
-       inode->i_gid = current->fsgid;
-       inode->i_blksize = PAGE_SIZE;
-       inode->i_blocks = 0;
-       inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
-       inode->i_state = I_DIRTY; /* so that mark_inode_dirty won't touch us */
-
-       isec = inode->i_security;
-       isec->sid = SECINITSID_DEVNULL;
-       isec->sclass = SECCLASS_CHR_FILE;
-       isec->initialized = 1;
-
-       file->f_flags = O_RDWR;
-       file->f_mode = FMODE_READ | FMODE_WRITE;
-       file->f_dentry = dentry;
-       file->f_vfsmnt = mntget(current->fs->rootmnt);
-       file->f_pos = 0;
-
-       init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, dev);
-       if (inode->i_fop->open(inode, file))
-               goto out_fput;
-
-out:
-       return file;
-out_fput:
-       mntput(file->f_vfsmnt);
-       put_filp(file);
-out_dput:
-       dput(dentry);
-out_iput:
-       iput(inode);
-       file = NULL;
-       goto out;
-}
+extern struct vfsmount *selinuxfs_mount;
+extern struct dentry *selinux_null;
 
 /* Derived from fs/exec.c:flush_old_files. */
 static inline void flush_unauthorized_files(struct files_struct * files)
 {
        struct avc_audit_data ad;
        struct file *file, *devnull = NULL;
+       struct tty_struct *tty = current->signal->tty;
        long j = -1;
 
+       if (tty) {
+               file_list_lock();
+               file = list_entry(tty->tty_files.next, typeof(*file), f_list);
+               if (file) {
+                       /* Revalidate access to controlling tty.
+                          Use inode_has_perm on the tty inode directly rather
+                          than using file_has_perm, as this particular open
+                          file may belong to another process and we are only
+                          interested in the inode-based check here. */
+                       struct inode *inode = file->f_dentry->d_inode;
+                       if (inode_has_perm(current, inode,
+                                          FILE__READ | FILE__WRITE,
+                                          NULL, NULL)) {
+                               /* Reset controlling tty. */
+                               current->signal->tty = NULL;
+                               current->signal->tty_old_pgrp = 0;
+                       }
+               }
+               file_list_unlock();
+       }
+
+       /* Revalidate access to inherited open files. */
+
        AVC_AUDIT_DATA_INIT(&ad,FS);
 
        spin_lock(&files->file_lock);
@@ -1826,7 +1800,7 @@ static inline void flush_unauthorized_files(struct files_struct * files)
                                        if (devnull) {
                                                atomic_inc(&devnull->f_count);
                                        } else {
-                                               devnull = open_devnull();
+                                               devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR);
                                                if (!devnull) {
                                                        put_unused_fd(fd);
                                                        fput(file);
@@ -2492,21 +2466,14 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
        return error;
 }
 
-static int selinux_file_mmap(struct file *file, unsigned long prot, unsigned long flags)
+static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
 {
-       u32 av;
-       int rc;
-
-       rc = secondary_ops->file_mmap(file, prot, flags);
-       if (rc)
-               return rc;
-
        if (file) {
                /* read access is always possible with a mapping */
-               av = FILE__READ;
+               u32 av = FILE__READ;
 
                /* write access only matters if the mapping is shared */
-               if ((flags & MAP_TYPE) == MAP_SHARED && (prot & PROT_WRITE))
+               if (shared && (prot & PROT_WRITE))
                        av |= FILE__WRITE;
 
                if (prot & PROT_EXEC)
@@ -2517,6 +2484,18 @@ static int selinux_file_mmap(struct file *file, unsigned long prot, unsigned lon
        return 0;
 }
 
+static int selinux_file_mmap(struct file *file, unsigned long prot, unsigned long flags)
+{
+       int rc;
+
+       rc = secondary_ops->file_mmap(file, prot, flags);
+       if (rc)
+               return rc;
+
+       return file_map_prot_check(file, prot,
+                                  (flags & MAP_TYPE) == MAP_SHARED);
+}
+
 static int selinux_file_mprotect(struct vm_area_struct *vma,
                                 unsigned long prot)
 {
@@ -2526,7 +2505,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
        if (rc)
                return rc;
 
-       return selinux_file_mmap(vma->vm_file, prot, vma->vm_flags);
+       return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED);
 }
 
 static int selinux_file_lock(struct file *file, unsigned int cmd)
@@ -2651,6 +2630,11 @@ static int selinux_task_alloc_security(struct task_struct *tsk)
        tsec2->exec_sid = tsec1->exec_sid;
        tsec2->create_sid = tsec1->create_sid;
 
+       /* Retain ptracer SID across fork, if any.
+          This will be reset by the ptrace hook upon any
+          subsequent ptrace_attach operations. */
+       tsec2->ptrace_sid = tsec1->ptrace_sid;
+
        return 0;
 }
 
@@ -2822,49 +2806,52 @@ static void selinux_task_to_inode(struct task_struct *p,
 /* Returns error only if unable to parse addresses */
 static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad)
 {
-       int offset, ihlen, ret;
-       struct iphdr iph;
+       int offset, ihlen, ret = -EINVAL;
+       struct iphdr _iph, *ih;
 
        offset = skb->nh.raw - skb->data;
-       ret = skb_copy_bits(skb, offset, &iph, sizeof(iph));
-       if (ret)
+       ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph);
+       if (ih == NULL)
                goto out;
 
-       ihlen = iph.ihl * 4;
-       if (ihlen < sizeof(iph))
+       ihlen = ih->ihl * 4;
+       if (ihlen < sizeof(_iph))
                goto out;
 
-       ad->u.net.v4info.saddr = iph.saddr;
-       ad->u.net.v4info.daddr = iph.daddr;
+       ad->u.net.v4info.saddr = ih->saddr;
+       ad->u.net.v4info.daddr = ih->daddr;
+       ret = 0;
 
-       switch (iph.protocol) {
+       switch (ih->protocol) {
         case IPPROTO_TCP: {
-               struct tcphdr tcph;
+               struct tcphdr _tcph, *th;
 
-               if (ntohs(iph.frag_off) & IP_OFFSET)
+               if (ntohs(ih->frag_off) & IP_OFFSET)
                        break;
 
                offset += ihlen;
-               if (skb_copy_bits(skb, offset, &tcph, sizeof(tcph)) < 0)
+               th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph);
+               if (th == NULL)
                        break;
 
-               ad->u.net.sport = tcph.source;
-               ad->u.net.dport = tcph.dest;
+               ad->u.net.sport = th->source;
+               ad->u.net.dport = th->dest;
                break;
         }
         
         case IPPROTO_UDP: {
-               struct udphdr udph;
+               struct udphdr _udph, *uh;
                
-               if (ntohs(iph.frag_off) & IP_OFFSET)
+               if (ntohs(ih->frag_off) & IP_OFFSET)
                        break;
                        
                offset += ihlen;
-               if (skb_copy_bits(skb, offset, &udph, sizeof(udph)) < 0)
-                       break;  
+               uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph);
+               if (uh == NULL)
+                       break;  
 
-               ad->u.net.sport = udph.source;
-               ad->u.net.dport = udph.dest;
+               ad->u.net.sport = uh->source;
+               ad->u.net.dport = uh->dest;
                break;
         }
 
@@ -2881,19 +2868,20 @@ out:
 static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad)
 {
        u8 nexthdr;
-       int ret, offset;
-       struct ipv6hdr ipv6h;
+       int ret = -EINVAL, offset;
+       struct ipv6hdr _ipv6h, *ip6;
 
        offset = skb->nh.raw - skb->data;
-       ret = skb_copy_bits(skb, offset, &ipv6h, sizeof(ipv6h));
-       if (ret)
+       ip6 = skb_header_pointer(skb, offset, sizeof(_ipv6h), &_ipv6h);
+       if (ip6 == NULL)
                goto out;
 
-       ipv6_addr_copy(&ad->u.net.v6info.saddr, &ipv6h.saddr);
-       ipv6_addr_copy(&ad->u.net.v6info.daddr, &ipv6h.daddr);
+       ipv6_addr_copy(&ad->u.net.v6info.saddr, &ip6->saddr);
+       ipv6_addr_copy(&ad->u.net.v6info.daddr, &ip6->daddr);
+       ret = 0;
 
-       nexthdr = ipv6h.nexthdr;
-       offset += sizeof(ipv6h);
+       nexthdr = ip6->nexthdr;
+       offset += sizeof(_ipv6h);
        offset = ipv6_skip_exthdr(skb, offset, &nexthdr,
                                  skb->tail - skb->head - offset);
        if (offset < 0)
@@ -2901,24 +2889,26 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad
 
        switch (nexthdr) {
        case IPPROTO_TCP: {
-               struct tcphdr tcph;
+               struct tcphdr _tcph, *th;
 
-               if (skb_copy_bits(skb, offset, &tcph, sizeof(tcph)) < 0)
+               th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph);
+               if (th == NULL)
                        break;
 
-               ad->u.net.sport = tcph.source;
-               ad->u.net.dport = tcph.dest;
+               ad->u.net.sport = th->source;
+               ad->u.net.dport = th->dest;
                break;
        }
 
        case IPPROTO_UDP: {
-               struct udphdr udph;
+               struct udphdr _udph, *uh;
 
-               if (skb_copy_bits(skb, offset, &udph, sizeof(udph)) < 0)
+               uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph);
+               if (uh == NULL)
                        break;
 
-               ad->u.net.sport = udph.source;
-               ad->u.net.dport = udph.dest;
+               ad->u.net.sport = uh->source;
+               ad->u.net.dport = uh->dest;
                break;
        }
 
@@ -3078,6 +3068,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
                                goto out;
                        AVC_AUDIT_DATA_INIT(&ad,NET);
                        ad.u.net.sport = htons(snum);
+                       ad.u.net.family = family;
                        err = avc_has_perm(isec->sid, sid,
                                           isec->sclass,
                                           SOCKET__NAME_BIND, NULL, &ad);