vserver 1.9.3
[linux-2.6.git] / security / selinux / hooks.c
index 8880f37..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,6 +63,7 @@
 #include <linux/nfs_mount.h>
 #include <net/ipv6.h>
 #include <linux/hugetlb.h>
+#include <linux/personality.h>
 
 #include "avc.h"
 #include "objsec.h"
@@ -70,6 +72,9 @@
 #define XATTR_SELINUX_SUFFIX "selinux"
 #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
 
+extern int policydb_loaded_version;
+extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
+
 #ifdef CONFIG_SECURITY_SELINUX_DEVELOP
 int selinux_enforcing = 0;
 
@@ -82,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)
 {
@@ -626,7 +631,7 @@ static inline u16 inode_mode_to_security_class(umode_t mode)
        return SECCLASS_FILE;
 }
 
-static inline u16 socket_type_to_security_class(int family, int type)
+static inline u16 socket_type_to_security_class(int family, int type, int protocol)
 {
        switch (family) {
        case PF_UNIX:
@@ -647,7 +652,28 @@ static inline u16 socket_type_to_security_class(int family, int type)
                        return SECCLASS_RAWIP_SOCKET;
                }
        case PF_NETLINK:
-               return SECCLASS_NETLINK_SOCKET;
+               switch (protocol) {
+               case NETLINK_ROUTE:
+                       return SECCLASS_NETLINK_ROUTE_SOCKET;
+               case NETLINK_FIREWALL:
+                       return SECCLASS_NETLINK_FIREWALL_SOCKET;
+               case NETLINK_TCPDIAG:
+                       return SECCLASS_NETLINK_TCPDIAG_SOCKET;
+               case NETLINK_NFLOG:
+                       return SECCLASS_NETLINK_NFLOG_SOCKET;
+               case NETLINK_XFRM:
+                       return SECCLASS_NETLINK_XFRM_SOCKET;
+               case NETLINK_SELINUX:
+                       return SECCLASS_NETLINK_SELINUX_SOCKET;
+               case NETLINK_AUDIT:
+                       return SECCLASS_NETLINK_AUDIT_SOCKET;
+               case NETLINK_IP6_FW:
+                       return SECCLASS_NETLINK_IP6FW_SOCKET;
+               case NETLINK_DNRTMSG:
+                       return SECCLASS_NETLINK_DNRT_SOCKET;
+               default:
+                       return SECCLASS_NETLINK_SOCKET;
+               }
        case PF_PACKET:
                return SECCLASS_PACKET_SOCKET;
        case PF_KEY:
@@ -852,7 +878,8 @@ out:
                struct socket *sock = SOCKET_I(inode);
                if (sock->sk) {
                        isec->sclass = socket_type_to_security_class(sock->sk->sk_family,
-                                                                    sock->sk->sk_type);
+                                                                    sock->sk->sk_type,
+                                                                    sock->sk->sk_protocol);
                } else {
                        isec->sclass = SECCLASS_SOCKET;
                }
@@ -1239,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);
@@ -1363,11 +1396,11 @@ static int selinux_capset_check(struct task_struct *target, kernel_cap_t *effect
 {
        int error;
 
-       error = task_has_perm(current, target, PROCESS__SETCAP);
+       error = secondary_ops->capset_check(target, effective, inheritable, permitted);
        if (error)
                return error;
 
-       return secondary_ops->capset_check(target, effective, inheritable, permitted);
+       return task_has_perm(current, target, PROCESS__SETCAP);
 }
 
 static void selinux_capset_set(struct task_struct *target, kernel_cap_t *effective,
@@ -1379,7 +1412,7 @@ static void selinux_capset_set(struct task_struct *target, kernel_cap_t *effecti
        if (error)
                return;
 
-       return secondary_ops->capset_set(target, effective, inheritable, permitted);
+       secondary_ops->capset_set(target, effective, inheritable, permitted);
 }
 
 static int selinux_capable(struct task_struct *tsk, int cap)
@@ -1401,6 +1434,10 @@ static int selinux_sysctl(ctl_table *table, int op)
        u32 tsid;
        int rc;
 
+       rc = secondary_ops->sysctl(table, op);
+       if (rc)
+               return rc;
+
        tsec = current->security;
 
        rc = selinux_proc_get_sid(table->de, (op == 001) ?
@@ -1475,6 +1512,7 @@ static int selinux_syslog(int type)
 
        switch (type) {
                case 3:         /* Read last kernel messages */
+               case 10:        /* Return size of the log buffer */
                        rc = task_has_system(current, SYSTEM__SYSLOG_READ);
                        break;
                case 6:         /* Disable logging to console */
@@ -1516,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;
@@ -1565,22 +1603,6 @@ static int selinux_vm_enough_memory(long pages)
        return -ENOMEM;
 }
 
-static int selinux_netlink_send(struct sk_buff *skb)
-{
-       if (capable(CAP_NET_ADMIN))
-               cap_raise (NETLINK_CB (skb).eff_cap, CAP_NET_ADMIN);
-       else
-               NETLINK_CB(skb).eff_cap = 0;
-       return 0;
-}
-
-static int selinux_netlink_recv(struct sk_buff *skb)
-{
-       if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN))
-               return -EPERM;
-       return 0;
-}
-
 /* binprm security operations */
 
 static int selinux_bprm_alloc_security(struct linux_binprm *bprm)
@@ -1669,6 +1691,9 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
                if (rc)
                        return rc;
 
+               /* Clear any possibly unsafe personality bits on exec: */
+               current->personality &= ~PER_CLEAR_ON_SETID;
+
                /* Set the security field to the new SID. */
                bsec->sid = newsid;
        }
@@ -1679,7 +1704,7 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
 
 static int selinux_bprm_check_security (struct linux_binprm *bprm)
 {
-       return 0;
+       return secondary_ops->bprm_check_security(bprm);
 }
 
 
@@ -1697,12 +1722,7 @@ static int selinux_bprm_secureexec (struct linux_binprm *bprm)
                                         PROCESS__NOATSECURE, NULL, NULL);
        }
 
-       /* Note that we must include the legacy uid/gid test below
-          to retain it, as the new userland will simply use the
-          value passed by AT_SECURE to decide whether to enable
-          secure mode. */
-       return ( atsecure || current->euid != current->uid ||
-               current->egid != current->gid);
+       return (atsecure || secondary_ops->bprm_secureexec(bprm));
 }
 
 static void selinux_bprm_free_security(struct linux_binprm *bprm)
@@ -1712,18 +1732,46 @@ static void selinux_bprm_free_security(struct linux_binprm *bprm)
        kfree(bsec);
 }
 
+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;
+       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);
        for (;;) {
                unsigned long set, i;
+               int fd;
 
                j++;
                i = j * __NFDBITS;
@@ -1740,8 +1788,27 @@ static inline void flush_unauthorized_files(struct files_struct * files)
                                        continue;
                                if (file_has_perm(current,
                                                  file,
-                                                 file_to_av(file)))
+                                                 file_to_av(file))) {
                                        sys_close(i);
+                                       fd = get_unused_fd();
+                                       if (fd != i) {
+                                               if (fd >= 0)
+                                                       put_unused_fd(fd);
+                                               fput(file);
+                                               continue;
+                                       }
+                                       if (devnull) {
+                                               atomic_inc(&devnull->f_count);
+                                       } else {
+                                               devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR);
+                                               if (!devnull) {
+                                                       put_unused_fd(fd);
+                                                       fput(file);
+                                                       continue;
+                                               }
+                                       }
+                                       fd_install(fd, devnull);
+                               }
                                fput(file);
                        }
                }
@@ -1969,6 +2036,12 @@ static int selinux_mount(char * dev_name,
                          unsigned long flags,
                          void * data)
 {
+       int rc;
+
+       rc = secondary_ops->sb_mount(dev_name, nd, type, flags, data);
+       if (rc)
+               return rc;
+
        if (flags & MS_REMOUNT)
                return superblock_has_perm(current, nd->mnt->mnt_sb,
                                           FILESYSTEM__REMOUNT, NULL);
@@ -1979,6 +2052,12 @@ static int selinux_mount(char * dev_name,
 
 static int selinux_umount(struct vfsmount *mnt, int flags)
 {
+       int rc;
+
+       rc = secondary_ops->sb_umount(mnt, flags);
+       if (rc)
+               return rc;
+
        return superblock_has_perm(current,mnt->mnt_sb,
                                   FILESYSTEM__UNMOUNT,NULL);
 }
@@ -2022,6 +2101,11 @@ static void selinux_inode_post_link(struct dentry *old_dentry, struct inode *ino
 
 static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry)
 {
+       int rc;
+
+       rc = secondary_ops->inode_unlink(dir, dentry);
+       if (rc)
+               return rc;
        return may_link(dir, dentry, MAY_UNLINK);
 }
 
@@ -2052,6 +2136,12 @@ static int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry)
 
 static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
 {
+       int rc;
+
+       rc = secondary_ops->inode_mknod(dir, dentry, mode, dev);
+       if (rc)
+               return rc;
+
        return may_create(dir, dentry, inode_mode_to_security_class(mode));
 }
 
@@ -2090,6 +2180,12 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *na
 static int selinux_inode_permission(struct inode *inode, int mask,
                                    struct nameidata *nd)
 {
+       int rc;
+
+       rc = secondary_ops->inode_permission(inode, mask, nd);
+       if (rc)
+               return rc;
+
        if (!mask) {
                /* No permission to check.  Existence test. */
                return 0;
@@ -2101,6 +2197,12 @@ static int selinux_inode_permission(struct inode *inode, int mask,
 
 static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
 {
+       int rc;
+
+       rc = secondary_ops->inode_setattr(dentry, iattr);
+       if (rc)
+               return rc;
+
        if (iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID |
                               ATTR_ATIME_SET | ATTR_MTIME_SET))
                return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
@@ -2364,16 +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;
-
        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)
@@ -2384,10 +2484,28 @@ 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)
 {
-       return selinux_file_mmap(vma->vm_file, prot, vma->vm_flags);
+       int rc;
+
+       rc = secondary_ops->file_mprotect(vma, prot);
+       if (rc)
+               return rc;
+
+       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)
@@ -2484,6 +2602,12 @@ static int selinux_file_receive(struct file *file)
 
 static int selinux_task_create(unsigned long clone_flags)
 {
+       int rc;
+
+       rc = secondary_ops->task_create(clone_flags);
+       if (rc)
+               return rc;
+
        return task_has_perm(current, current, PROCESS__FORK);
 }
 
@@ -2506,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;
 }
 
@@ -2559,12 +2688,23 @@ static int selinux_task_setgroups(struct group_info *group_info)
 
 static int selinux_task_setnice(struct task_struct *p, int nice)
 {
+       int rc;
+
+       rc = secondary_ops->task_setnice(p, nice);
+       if (rc)
+               return rc;
+
        return task_has_perm(current,p, PROCESS__SETSCHED);
 }
 
 static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim)
 {
        struct rlimit *old_rlim = current->rlim + resource;
+       int rc;
+
+       rc = secondary_ops->task_setrlimit(resource, new_rlim);
+       if (rc)
+               return rc;
 
        /* Control the ability to change the hard limit (whether
           lowering or raising it), so that the hard limit can
@@ -2599,6 +2739,11 @@ static int selinux_task_getscheduler(struct task_struct *p)
 static int selinux_task_kill(struct task_struct *p, struct siginfo *info, int sig)
 {
        u32 perm;
+       int rc;
+
+       rc = secondary_ops->task_kill(p, info, sig);
+       if (rc)
+               return rc;
 
        if (info && ((unsigned long)info == 1 ||
                     (unsigned long)info == 2 || SI_FROMKERNEL(info)))
@@ -2661,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;
         }
 
@@ -2720,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)
@@ -2740,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;
        }
 
@@ -2838,8 +2989,8 @@ static int selinux_socket_create(int family, int type,
 
        tsec = current->security;
        err = avc_has_perm(tsec->sid, tsec->sid,
-                          socket_type_to_security_class(family, type),
-                          SOCKET__CREATE, NULL, NULL);
+                          socket_type_to_security_class(family, type,
+                          protocol), SOCKET__CREATE, NULL, NULL);
 
 out:
        return err;
@@ -2858,7 +3009,7 @@ static void selinux_socket_post_create(struct socket *sock, int family,
        isec = SOCK_INODE(sock)->i_security;
 
        tsec = current->security;
-       isec->sclass = socket_type_to_security_class(family, type);
+       isec->sclass = socket_type_to_security_class(family, type, protocol);
        isec->sid = kern ? SECINITSID_KERNEL : tsec->sid;
 
        return;
@@ -2917,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);
@@ -3040,6 +3192,10 @@ static int selinux_socket_unix_stream_connect(struct socket *sock,
        struct avc_audit_data ad;
        int err;
 
+       err = secondary_ops->unix_stream_connect(sock, other, newsk);
+       if (err)
+               return err;
+
        isec = SOCK_INODE(sock)->i_security;
        other_isec = SOCK_INODE(other)->i_security;
 
@@ -3094,12 +3250,12 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
        char *addrp;
        int len, err = 0;
        u32 netif_perm, node_perm, node_sid, recv_perm = 0;
+       u32 sock_sid = 0;
+       u16 sock_class = 0;
        struct socket *sock;
-       struct inode *inode;
        struct net_device *dev;
        struct sel_netif *netif;
        struct netif_security_struct *nsec;
-       struct inode_security_struct *isec;
        struct avc_audit_data ad;
 
        family = sk->sk_family;
@@ -3110,15 +3266,21 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
        if (family == PF_INET6 && skb->protocol == ntohs(ETH_P_IP))
                family = PF_INET;
 
-       sock = sk->sk_socket;
-       
-       /* TCP control messages don't always have a socket. */
-       if (!sock)
-               goto out;
-
-       inode = SOCK_INODE(sock);
-       if (!inode)
-               goto out;
+       read_lock_bh(&sk->sk_callback_lock);
+       sock = sk->sk_socket;
+       if (sock) {
+               struct inode *inode;
+               inode = SOCK_INODE(sock);
+               if (inode) {
+                       struct inode_security_struct *isec;
+                       isec = inode->i_security;
+                       sock_sid = isec->sid;
+                       sock_class = isec->sclass;
+               }
+       }
+       read_unlock_bh(&sk->sk_callback_lock);
+       if (!sock_sid)
+               goto out;
 
        dev = skb->dev;
        if (!dev)
@@ -3131,9 +3293,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
        }
        
        nsec = &netif->nsec;
-       isec = inode->i_security;
 
-       switch (isec->sclass) {
+       switch (sock_class) {
        case SECCLASS_UDP_SOCKET:
                netif_perm = NETIF__UDP_RECV;
                node_perm = NODE__UDP_RECV;
@@ -3162,7 +3323,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
                goto out;
        }
 
-       err = avc_has_perm(isec->sid, nsec->if_sid, SECCLASS_NETIF,
+       err = avc_has_perm(sock_sid, nsec->if_sid, SECCLASS_NETIF,
                           netif_perm, &nsec->avcr, &ad);
        sel_netif_put(netif);
        if (err)
@@ -3173,7 +3334,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
        if (err)
                goto out;
        
-       err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, node_perm, NULL, &ad);
+       err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, NULL, &ad);
        if (err)
                goto out;
 
@@ -3187,7 +3348,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
                if (err)
                        goto out;
 
-               err = avc_has_perm(isec->sid, port_sid, isec->sclass,
+               err = avc_has_perm(sock_sid, port_sid, sock_class,
                                   recv_perm, NULL, &ad);
        }
 out:   
@@ -3242,6 +3403,33 @@ static void selinux_sk_free_security(struct sock *sk)
        sk_free_security(sk);
 }
 
+static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
+{
+       int err = 0;
+       u32 perm;
+       struct nlmsghdr *nlh;
+       struct socket *sock = sk->sk_socket;
+       struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
+       
+       if (skb->len < NLMSG_SPACE(0)) {
+               err = -EINVAL;
+               goto out;
+       }
+       nlh = (struct nlmsghdr *)skb->data;
+       
+       err = selinux_nlmsg_lookup(isec->sclass, nlh->nlmsg_type, &perm);
+       if (err) {
+               /* Ignore */
+               if (err == -ENOENT)
+                       err = 0;
+               goto out;
+       }
+
+       err = socket_has_perm(current, sock, perm);
+out:
+       return err;
+}
+
 #ifdef CONFIG_NETFILTER
 
 static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
@@ -3377,8 +3565,37 @@ static unsigned int selinux_ipv6_postroute_last(unsigned int hooknum,
 
 #endif /* CONFIG_NETFILTER */
 
+#else
+
+static inline int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
+{
+       return 0;
+}
+
 #endif /* CONFIG_SECURITY_NETWORK */
 
+static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
+{
+       int err = 0;
+
+       if (capable(CAP_NET_ADMIN))
+               cap_raise (NETLINK_CB (skb).eff_cap, CAP_NET_ADMIN);
+       else
+               NETLINK_CB(skb).eff_cap = 0;
+
+       if (policydb_loaded_version >= POLICYDB_VERSION_NLCLASS)
+               err = selinux_nlmsg_perm(sk, skb);
+
+       return err;
+}
+
+static int selinux_netlink_recv(struct sk_buff *skb)
+{
+       if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN))
+               return -EPERM;
+       return 0;
+}
+
 static int ipc_alloc_security(struct task_struct *task,
                              struct kern_ipc_perm *perm,
                              u16 sclass)
@@ -3465,7 +3682,7 @@ static int selinux_msg_msg_alloc_security(struct msg_msg *msg)
 
 static void selinux_msg_msg_free_security(struct msg_msg *msg)
 {
-       return msg_msg_free_security(msg);
+       msg_msg_free_security(msg);
 }
 
 /* message queue security operations */
@@ -3701,9 +3918,14 @@ static int selinux_shm_shmctl(struct shmid_kernel *shp, int cmd)
 }
 
 static int selinux_shm_shmat(struct shmid_kernel *shp,
-                            char *shmaddr, int shmflg)
+                            char __user *shmaddr, int shmflg)
 {
        u32 perms;
+       int rc;
+
+       rc = secondary_ops->shm_shmat(shp, shmaddr, shmflg);
+       if (rc)
+               return rc;
 
        if (shmflg & SHM_RDONLY)
                perms = SHM__READ;