fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / security / selinux / hooks.c
index 0b32f30..e9c61d1 100644 (file)
@@ -12,6 +12,8 @@
  *  Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
  *  Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
  *                          <dgoeddel@trustedcs.com>
+ *  Copyright (C) 2006 Hewlett-Packard Development Company, L.P.
+ *                     Paul Moore, <paul.moore@hp.com>
  *
  *     This program is free software; you can redistribute it and/or modify
  *     it under the terms of the GNU General Public License version 2,
@@ -21,7 +23,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
-#include <linux/ptrace.h>
+#include <linux/tracehook.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/security.h>
@@ -49,7 +51,6 @@
 #include <net/ip.h>            /* for sysctl_local_port_range[] */
 #include <net/tcp.h>           /* struct or_callable used in sock_rcv_skb */
 #include <asm/uaccess.h>
-#include <asm/semaphore.h>
 #include <asm/ioctls.h>
 #include <linux/bitops.h>
 #include <linux/interrupt.h>
@@ -57,6 +58,7 @@
 #include <linux/netlink.h>
 #include <linux/tcp.h>
 #include <linux/udp.h>
+#include <linux/dccp.h>
 #include <linux/quota.h>
 #include <linux/un.h>          /* for Unix socket types */
 #include <net/af_unix.h>       /* for Unix socket types */
 #include <linux/audit.h>
 #include <linux/string.h>
 #include <linux/selinux.h>
+#include <linux/mutex.h>
 
 #include "avc.h"
 #include "objsec.h"
 #include "netif.h"
 #include "xfrm.h"
+#include "selinux_netlabel.h"
 
 #define XATTR_SELINUX_SUFFIX "selinux"
 #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
@@ -120,7 +124,7 @@ static struct security_operations *secondary_ops = NULL;
 static LIST_HEAD(superblock_security_head);
 static DEFINE_SPINLOCK(sb_security_lock);
 
-static kmem_cache_t *sel_inode_cache;
+static struct kmem_cache *sel_inode_cache;
 
 /* Return security context for a given sid or just the context 
    length if the buffer is null or length is 0 */
@@ -159,7 +163,7 @@ static int task_alloc_security(struct task_struct *task)
                return -ENOMEM;
 
        tsec->task = task;
-       tsec->osid = tsec->sid = tsec->ptrace_sid = SECINITSID_UNLABELED;
+       tsec->osid = tsec->sid = SECINITSID_UNLABELED;
        task->security = tsec;
 
        return 0;
@@ -177,12 +181,12 @@ static int inode_alloc_security(struct inode *inode)
        struct task_security_struct *tsec = current->security;
        struct inode_security_struct *isec;
 
-       isec = kmem_cache_alloc(sel_inode_cache, SLAB_KERNEL);
+       isec = kmem_cache_alloc(sel_inode_cache, GFP_KERNEL);
        if (!isec)
                return -ENOMEM;
 
        memset(isec, 0, sizeof(*isec));
-       init_MUTEX(&isec->sem);
+       mutex_init(&isec->lock);
        INIT_LIST_HEAD(&isec->list);
        isec->inode = inode;
        isec->sid = SECINITSID_UNLABELED;
@@ -239,7 +243,7 @@ static int superblock_alloc_security(struct super_block *sb)
        if (!sbsec)
                return -ENOMEM;
 
-       init_MUTEX(&sbsec->sem);
+       mutex_init(&sbsec->lock);
        INIT_LIST_HEAD(&sbsec->list);
        INIT_LIST_HEAD(&sbsec->isec_head);
        spin_lock_init(&sbsec->isec_lock);
@@ -269,17 +273,17 @@ static int sk_alloc_security(struct sock *sk, int family, gfp_t priority)
 {
        struct sk_security_struct *ssec;
 
-       if (family != PF_UNIX)
-               return 0;
-
        ssec = kzalloc(sizeof(*ssec), priority);
        if (!ssec)
                return -ENOMEM;
 
        ssec->sk = sk;
        ssec->peer_sid = SECINITSID_UNLABELED;
+       ssec->sid = SECINITSID_UNLABELED;
        sk->sk_security = ssec;
 
+       selinux_netlbl_sk_security_init(ssec, family);
+
        return 0;
 }
 
@@ -287,9 +291,6 @@ static void sk_free_security(struct sock *sk)
 {
        struct sk_security_struct *ssec = sk->sk_security;
 
-       if (sk->sk_family != PF_UNIX)
-               return;
-
        sk->sk_security = NULL;
        kfree(ssec);
 }
@@ -398,7 +399,7 @@ static int try_context_mount(struct super_block *sb, void *data)
                /* Standard string-based options. */
                char *p, *options = data;
 
-               while ((p = strsep(&options, ",")) != NULL) {
+               while ((p = strsep(&options, "|")) != NULL) {
                        int token;
                        substring_t args[MAX_OPT_ARGS];
 
@@ -594,7 +595,7 @@ static int superblock_doinit(struct super_block *sb, void *data)
        struct inode *inode = root->d_inode;
        int rc = 0;
 
-       down(&sbsec->sem);
+       mutex_lock(&sbsec->lock);
        if (sbsec->initialized)
                goto out;
 
@@ -689,7 +690,7 @@ next_inode:
        }
        spin_unlock(&sbsec->isec_lock);
 out:
-       up(&sbsec->sem);
+       mutex_unlock(&sbsec->lock);
        return rc;
 }
 
@@ -751,6 +752,8 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc
                                return SECCLASS_UDP_SOCKET;
                        else
                                return SECCLASS_RAWIP_SOCKET;
+               case SOCK_DCCP:
+                       return SECCLASS_DCCP_SOCKET;
                default:
                        return SECCLASS_RAWIP_SOCKET;
                }
@@ -843,15 +846,13 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
        char *context = NULL;
        unsigned len = 0;
        int rc = 0;
-       int hold_sem = 0;
 
        if (isec->initialized)
                goto out;
 
-       down(&isec->sem);
-       hold_sem = 1;
+       mutex_lock(&isec->lock);
        if (isec->initialized)
-               goto out;
+               goto out_unlock;
 
        sbsec = inode->i_sb->s_security;
        if (!sbsec->initialized) {
@@ -862,7 +863,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                if (list_empty(&isec->list))
                        list_add(&isec->list, &sbsec->isec_head);
                spin_unlock(&sbsec->isec_lock);
-               goto out;
+               goto out_unlock;
        }
 
        switch (sbsec->behavior) {
@@ -885,7 +886,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                        printk(KERN_WARNING "%s:  no dentry for dev=%s "
                               "ino=%ld\n", __FUNCTION__, inode->i_sb->s_id,
                               inode->i_ino);
-                       goto out;
+                       goto out_unlock;
                }
 
                len = INITCONTEXTLEN;
@@ -893,7 +894,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                if (!context) {
                        rc = -ENOMEM;
                        dput(dentry);
-                       goto out;
+                       goto out_unlock;
                }
                rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX,
                                           context, len);
@@ -903,7 +904,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                                                   NULL, 0);
                        if (rc < 0) {
                                dput(dentry);
-                               goto out;
+                               goto out_unlock;
                        }
                        kfree(context);
                        len = rc;
@@ -911,7 +912,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                        if (!context) {
                                rc = -ENOMEM;
                                dput(dentry);
-                               goto out;
+                               goto out_unlock;
                        }
                        rc = inode->i_op->getxattr(dentry,
                                                   XATTR_NAME_SELINUX,
@@ -924,7 +925,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                                       "%d for dev=%s ino=%ld\n", __FUNCTION__,
                                       -rc, inode->i_sb->s_id, inode->i_ino);
                                kfree(context);
-                               goto out;
+                               goto out_unlock;
                        }
                        /* Map ENODATA to the default file SID */
                        sid = sbsec->def_sid;
@@ -960,7 +961,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                                             isec->sclass,
                                             &sid);
                if (rc)
-                       goto out;
+                       goto out_unlock;
                isec->sid = sid;
                break;
        case SECURITY_FS_USE_MNTPOINT:
@@ -978,7 +979,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                                                          isec->sclass,
                                                          &sid);
                                if (rc)
-                                       goto out;
+                                       goto out_unlock;
                                isec->sid = sid;
                        }
                }
@@ -987,12 +988,11 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
 
        isec->initialized = 1;
 
+out_unlock:
+       mutex_unlock(&isec->lock);
 out:
        if (isec->sclass == SECCLASS_FILE)
                isec->sclass = inode_mode_to_security_class(inode->i_mode);
-
-       if (hold_sem)
-               up(&isec->sem);
        return rc;
 }
 
@@ -1120,8 +1120,8 @@ static int file_has_perm(struct task_struct *tsk,
 {
        struct task_security_struct *tsec = tsk->security;
        struct file_security_struct *fsec = file->f_security;
-       struct vfsmount *mnt = file->f_vfsmnt;
-       struct dentry *dentry = file->f_dentry;
+       struct vfsmount *mnt = file->f_path.mnt;
+       struct dentry *dentry = file->f_path.dentry;
        struct inode *inode = dentry->d_inode;
        struct avc_audit_data ad;
        int rc;
@@ -1364,42 +1364,17 @@ static inline u32 file_to_av(struct file *file)
        return av;
 }
 
-/* Set an inode's SID to a specified value. */
-static 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);
-       isec->sid = sid;
-       isec->initialized = 1;
-       up(&isec->sem);
-       return 0;
-}
-
 /* Hook functions begin here. */
 
 static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
 {
-       struct task_security_struct *psec = parent->security;
-       struct task_security_struct *csec = child->security;
        int rc;
 
        rc = secondary_ops->ptrace(parent,child);
        if (rc)
                return rc;
 
-       rc = task_has_perm(parent, child, PROCESS__PTRACE);
-       /* Save the SID of the tracing process for later use in apply_creds. */
-       if (!(child->ptrace & PT_PTRACED) && !rc)
-               csec->ptrace_sid = psec->sid;
-       return rc;
+       return task_has_perm(parent, child, PROCESS__PTRACE);
 }
 
 static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
@@ -1600,7 +1575,7 @@ static int selinux_bprm_alloc_security(struct linux_binprm *bprm)
 static int selinux_bprm_set_security(struct linux_binprm *bprm)
 {
        struct task_security_struct *tsec;
-       struct inode *inode = bprm->file->f_dentry->d_inode;
+       struct inode *inode = bprm->file->f_path.dentry->d_inode;
        struct inode_security_struct *isec;
        struct bprm_security_struct *bsec;
        u32 newsid;
@@ -1640,10 +1615,10 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
        }
 
        AVC_AUDIT_DATA_INIT(&ad, FS);
-       ad.u.fs.mnt = bprm->file->f_vfsmnt;
-       ad.u.fs.dentry = bprm->file->f_dentry;
+       ad.u.fs.mnt = bprm->file->f_path.mnt;
+       ad.u.fs.dentry = bprm->file->f_path.dentry;
 
-       if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID)
+       if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
                newsid = tsec->sid;
 
         if (tsec->sid == newsid) {
@@ -1727,7 +1702,7 @@ static inline void flush_unauthorized_files(struct files_struct * files)
                           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;
+                       struct inode *inode = file->f_path.dentry->d_inode;
                        if (inode_has_perm(current, inode,
                                           FILE__READ | FILE__WRITE, NULL)) {
                                drop_tty = 1;
@@ -1753,7 +1728,7 @@ static inline void flush_unauthorized_files(struct files_struct * files)
                j++;
                i = j * __NFDBITS;
                fdt = files_fdtable(files);
-               if (i >= fdt->max_fds || i >= fdt->max_fdset)
+               if (i >= fdt->max_fds)
                        break;
                set = fdt->open_fds->fds_bits[j];
                if (!set)
@@ -1779,7 +1754,8 @@ static inline void flush_unauthorized_files(struct files_struct * files)
                                                get_file(devnull);
                                        } else {
                                                devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR);
-                                               if (!devnull) {
+                                               if (IS_ERR(devnull)) {
+                                                       devnull = NULL;
                                                        put_unused_fd(fd);
                                                        fput(file);
                                                        continue;
@@ -1827,12 +1803,24 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
                /* Check for ptracing, and update the task SID if ok.
                   Otherwise, leave SID unchanged and kill. */
                if (unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
-                       rc = avc_has_perm(tsec->ptrace_sid, sid,
-                                         SECCLASS_PROCESS, PROCESS__PTRACE,
-                                         NULL);
-                       if (rc) {
-                               bsec->unsafe = 1;
-                               return;
+                       struct task_struct *t;
+
+                       rcu_read_lock();
+                       t = tracehook_tracer_task(current);
+                       if (unlikely(t == NULL))
+                               rcu_read_unlock();
+                       else {
+                               struct task_security_struct *sec = t->security;
+                               u32 ptsid = sec->sid;
+                               rcu_read_unlock();
+
+                               rc = avc_has_perm(ptsid, sid,
+                                                 SECCLASS_PROCESS,
+                                                 PROCESS__PTRACE, NULL);
+                               if (rc) {
+                                       bsec->unsafe = 1;
+                                       return;
+                               }
                        }
                }
                tsec->sid = sid;
@@ -1948,18 +1936,40 @@ static inline void take_option(char **to, char *from, int *first, int len)
        if (!*first) {
                **to = ',';
                *to += 1;
-       }
-       else
+       } else
                *first = 0;
        memcpy(*to, from, len);
        *to += len;
 }
 
+static inline void take_selinux_option(char **to, char *from, int *first, 
+                                      int len)
+{
+       int current_size = 0;
+
+       if (!*first) {
+               **to = '|';
+               *to += 1;
+       }
+       else
+               *first = 0;
+
+       while (current_size < len) {
+               if (*from != '"') {
+                       **to = *from;
+                       *to += 1;
+               }
+               from += 1;
+               current_size += 1;
+       }
+}
+
 static int selinux_sb_copy_data(struct file_system_type *type, void *orig, void *copy)
 {
        int fnosec, fsec, rc = 0;
        char *in_save, *in_curr, *in_end;
        char *sec_curr, *nosec_save, *nosec;
+       int open_quote = 0;
 
        in_curr = orig;
        sec_curr = copy;
@@ -1981,11 +1991,14 @@ static int selinux_sb_copy_data(struct file_system_type *type, void *orig, void
        in_save = in_end = orig;
 
        do {
-               if (*in_end == ',' || *in_end == '\0') {
+               if (*in_end == '"')
+                       open_quote = !open_quote;
+               if ((*in_end == ',' && open_quote == 0) ||
+                               *in_end == '\0') {
                        int len = in_end - in_curr;
 
                        if (selinux_option(in_curr, len))
-                               take_option(&sec_curr, in_curr, &fsec, len);
+                               take_selinux_option(&sec_curr, in_curr, &fsec, len);
                        else
                                take_option(&nosec, in_curr, &fnosec, len);
 
@@ -2097,7 +2110,13 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
                }
        }
 
-       inode_security_set_sid(inode, newsid);
+       /* Possibly defer initialization to selinux_complete_init. */
+       if (sbsec->initialized) {
+               struct inode_security_struct *isec = inode->i_security;
+               isec->sclass = inode_mode_to_security_class(inode->i_mode);
+               isec->sid = newsid;
+               isec->initialized = 1;
+       }
 
        if (!ss_initialized || sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
                return -EOPNOTSUPP;
@@ -2406,7 +2425,8 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t
 
 static int selinux_file_permission(struct file *file, int mask)
 {
-       struct inode *inode = file->f_dentry->d_inode;
+       int rc;
+       struct inode *inode = file->f_path.dentry->d_inode;
 
        if (!mask) {
                /* No permission to check.  Existence test. */
@@ -2417,8 +2437,12 @@ static int selinux_file_permission(struct file *file, int mask)
        if ((file->f_flags & O_APPEND) && (mask & MAY_WRITE))
                mask |= MAY_APPEND;
 
-       return file_has_perm(current, file,
-                            file_mask_to_av(inode->i_mode, mask));
+       rc = file_has_perm(current, file,
+                          file_mask_to_av(inode->i_mode, mask));
+       if (rc)
+               return rc;
+
+       return selinux_netlbl_inode_permission(inode, mask);
 }
 
 static int selinux_file_alloc_security(struct file *file)
@@ -2575,7 +2599,7 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
 
        switch (cmd) {
                case F_SETFL:
-                       if (!file->f_dentry || !file->f_dentry->d_inode) {
+                       if (!file->f_path.dentry || !file->f_path.dentry->d_inode) {
                                err = -EINVAL;
                                break;
                        }
@@ -2601,7 +2625,7 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
                case F_SETLK64:
                case F_SETLKW64:
 #endif
-                       if (!file->f_dentry || !file->f_dentry->d_inode) {
+                       if (!file->f_path.dentry || !file->f_path.dentry->d_inode) {
                                err = -EINVAL;
                                break;
                        }
@@ -2686,11 +2710,6 @@ static int selinux_task_alloc_security(struct task_struct *tsk)
        tsec2->keycreate_sid = tsec1->keycreate_sid;
        tsec2->sockcreate_sid = tsec1->sockcreate_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;
 }
 
@@ -2873,7 +2892,8 @@ 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)
+static int selinux_parse_skb_ipv4(struct sk_buff *skb,
+                       struct avc_audit_data *ad, u8 *proto)
 {
        int offset, ihlen, ret = -EINVAL;
        struct iphdr _iph, *ih;
@@ -2891,6 +2911,9 @@ static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad
        ad->u.net.v4info.daddr = ih->daddr;
        ret = 0;
 
+       if (proto)
+               *proto = ih->protocol;
+
        switch (ih->protocol) {
         case IPPROTO_TCP: {
                struct tcphdr _tcph, *th;
@@ -2924,6 +2947,22 @@ static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad
                break;
         }
 
+       case IPPROTO_DCCP: {
+               struct dccp_hdr _dccph, *dh;
+
+               if (ntohs(ih->frag_off) & IP_OFFSET)
+                       break;
+
+               offset += ihlen;
+               dh = skb_header_pointer(skb, offset, sizeof(_dccph), &_dccph);
+               if (dh == NULL)
+                       break;
+
+               ad->u.net.sport = dh->dccph_sport;
+               ad->u.net.dport = dh->dccph_dport;
+               break;
+        }
+
         default:
                break;
         }
@@ -2934,7 +2973,8 @@ out:
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 
 /* Returns error only if unable to parse addresses */
-static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad)
+static int selinux_parse_skb_ipv6(struct sk_buff *skb,
+                       struct avc_audit_data *ad, u8 *proto)
 {
        u8 nexthdr;
        int ret = -EINVAL, offset;
@@ -2955,6 +2995,9 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad
        if (offset < 0)
                goto out;
 
+       if (proto)
+               *proto = nexthdr;
+
        switch (nexthdr) {
        case IPPROTO_TCP: {
                struct tcphdr _tcph, *th;
@@ -2980,6 +3023,18 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad
                break;
        }
 
+       case IPPROTO_DCCP: {
+               struct dccp_hdr _dccph, *dh;
+
+               dh = skb_header_pointer(skb, offset, sizeof(_dccph), &_dccph);
+               if (dh == NULL)
+                       break;
+
+               ad->u.net.sport = dh->dccph_sport;
+               ad->u.net.dport = dh->dccph_dport;
+               break;
+        }
+
        /* includes fragments */
        default:
                break;
@@ -2991,13 +3046,13 @@ out:
 #endif /* IPV6 */
 
 static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
-                            char **addrp, int *len, int src)
+                            char **addrp, int *len, int src, u8 *proto)
 {
        int ret = 0;
 
        switch (ad->u.net.family) {
        case PF_INET:
-               ret = selinux_parse_skb_ipv4(skb, ad);
+               ret = selinux_parse_skb_ipv4(skb, ad, proto);
                if (ret || !addrp)
                        break;
                *len = 4;
@@ -3007,7 +3062,7 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        case PF_INET6:
-               ret = selinux_parse_skb_ipv6(skb, ad);
+               ret = selinux_parse_skb_ipv6(skb, ad, proto);
                if (ret || !addrp)
                        break;
                *len = 16;
@@ -3065,11 +3120,13 @@ out:
        return err;
 }
 
-static void selinux_socket_post_create(struct socket *sock, int family,
-                                      int type, int protocol, int kern)
+static int selinux_socket_post_create(struct socket *sock, int family,
+                                     int type, int protocol, int kern)
 {
+       int err = 0;
        struct inode_security_struct *isec;
        struct task_security_struct *tsec;
+       struct sk_security_struct *sksec;
        u32 newsid;
 
        isec = SOCK_INODE(sock)->i_security;
@@ -3080,7 +3137,13 @@ static void selinux_socket_post_create(struct socket *sock, int family,
        isec->sid = kern ? SECINITSID_KERNEL : newsid;
        isec->initialized = 1;
 
-       return;
+       if (sock->sk) {
+               sksec = sock->sk->sk_security;
+               sksec->sid = isec->sid;
+               err = selinux_netlbl_socket_post_create(sock);
+       }
+
+       return err;
 }
 
 /* Range of port numbers used to automatically bind.
@@ -3154,7 +3217,11 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
                case SECCLASS_UDP_SOCKET:
                        node_perm = UDP_SOCKET__NODE_BIND;
                        break;
-                       
+
+               case SECCLASS_DCCP_SOCKET:
+                       node_perm = DCCP_SOCKET__NODE_BIND;
+                       break;
+
                default:
                        node_perm = RAWIP_SOCKET__NODE_BIND;
                        break;
@@ -3192,16 +3259,17 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
                return err;
 
        /*
-        * If a TCP socket, check name_connect permission for the port.
+        * If a TCP or DCCP socket, check name_connect permission for the port.
         */
        isec = SOCK_INODE(sock)->i_security;
-       if (isec->sclass == SECCLASS_TCP_SOCKET) {
+       if (isec->sclass == SECCLASS_TCP_SOCKET ||
+           isec->sclass == SECCLASS_DCCP_SOCKET) {
                struct sock *sk = sock->sk;
                struct avc_audit_data ad;
                struct sockaddr_in *addr4 = NULL;
                struct sockaddr_in6 *addr6 = NULL;
                unsigned short snum;
-               u32 sid;
+               u32 sid, perm;
 
                if (sk->sk_family == PF_INET) {
                        addr4 = (struct sockaddr_in *)address;
@@ -3220,11 +3288,13 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
                if (err)
                        goto out;
 
+               perm = (isec->sclass == SECCLASS_TCP_SOCKET) ?
+                      TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT;
+
                AVC_AUDIT_DATA_INIT(&ad,NET);
                ad.u.net.dport = htons(snum);
                ad.u.net.family = sk->sk_family;
-               err = avc_has_perm(isec->sid, sid, isec->sclass,
-                                  TCP_SOCKET__NAME_CONNECT, &ad);
+               err = avc_has_perm(isec->sid, sid, isec->sclass, perm, &ad);
                if (err)
                        goto out;
        }
@@ -3261,7 +3331,13 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
 static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg,
                                  int size)
 {
-       return socket_has_perm(current, sock, SOCKET__WRITE);
+       int rc;
+
+       rc = socket_has_perm(current, sock, SOCKET__WRITE);
+       if (rc)
+               return rc;
+
+       return selinux_netlbl_inode_permission(SOCK_INODE(sock), MAY_WRITE);
 }
 
 static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg,
@@ -3282,7 +3358,13 @@ static int selinux_socket_getpeername(struct socket *sock)
 
 static int selinux_socket_setsockopt(struct socket *sock,int level,int optname)
 {
-       return socket_has_perm(current, sock, SOCKET__SETOPT);
+       int err;
+
+       err = socket_has_perm(current, sock, SOCKET__SETOPT);
+       if (err)
+               return err;
+
+       return selinux_netlbl_socket_setsockopt(sock, level, optname);
 }
 
 static int selinux_socket_getsockopt(struct socket *sock, int level,
@@ -3329,8 +3411,9 @@ static int selinux_socket_unix_stream_connect(struct socket *sock,
        /* server child socket */
        ssec = newsk->sk_security;
        ssec->peer_sid = isec->sid;
-       
-       return 0;
+       err = security_sid_mls_copy(other_isec->sid, ssec->peer_sid, &ssec->sid);
+
+       return err;
 }
 
 static int selinux_socket_unix_may_send(struct socket *sock,
@@ -3356,11 +3439,29 @@ static int selinux_socket_unix_may_send(struct socket *sock,
 }
 
 static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
-               struct avc_audit_data *ad, u32 sock_sid, u16 sock_class,
-               u16 family, char *addrp, int len)
+               struct avc_audit_data *ad, u16 family, char *addrp, int len)
 {
        int err = 0;
        u32 netif_perm, node_perm, node_sid, if_sid, recv_perm = 0;
+       struct socket *sock;
+       u16 sock_class = 0;
+       u32 sock_sid = 0;
+
+       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;
 
        if (!skb->dev)
                goto out;
@@ -3381,7 +3482,13 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
                node_perm = NODE__TCP_RECV;
                recv_perm = TCP_SOCKET__RECV_MSG;
                break;
-       
+
+       case SECCLASS_DCCP_SOCKET:
+               netif_perm = NETIF__DCCP_RECV;
+               node_perm = NODE__DCCP_RECV;
+               recv_perm = DCCP_SOCKET__RECV_MSG;
+               break;
+
        default:
                netif_perm = NETIF__RAWIP_RECV;
                node_perm = NODE__RAWIP_RECV;
@@ -3420,56 +3527,41 @@ out:
 static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
        u16 family;
-       u16 sock_class = 0;
        char *addrp;
        int len, err = 0;
-       u32 sock_sid = 0;
-       struct socket *sock;
        struct avc_audit_data ad;
+       struct sk_security_struct *sksec = sk->sk_security;
 
        family = sk->sk_family;
        if (family != PF_INET && family != PF_INET6)
                goto out;
 
        /* Handle mapped IPv4 packets arriving via IPv6 sockets */
-       if (family == PF_INET6 && skb->protocol == ntohs(ETH_P_IP))
+       if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
                family = PF_INET;
 
-       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;
-
        AVC_AUDIT_DATA_INIT(&ad, NET);
        ad.u.net.netif = skb->dev ? skb->dev->name : "[unknown]";
        ad.u.net.family = family;
 
-       err = selinux_parse_skb(skb, &ad, &addrp, &len, 1);
+       err = selinux_parse_skb(skb, &ad, &addrp, &len, 1, NULL);
        if (err)
                goto out;
 
        if (selinux_compat_net)
-               err = selinux_sock_rcv_skb_compat(sk, skb, &ad, sock_sid,
-                                                 sock_class, family,
+               err = selinux_sock_rcv_skb_compat(sk, skb, &ad, family,
                                                  addrp, len);
        else
-               err = avc_has_perm(sock_sid, skb->secmark, SECCLASS_PACKET,
+               err = avc_has_perm(sksec->sid, skb->secmark, SECCLASS_PACKET,
                                   PACKET__RECV, &ad);
        if (err)
                goto out;
 
-       err = selinux_xfrm_sock_rcv_skb(sock_sid, skb);
+       err = selinux_netlbl_sock_rcv_skb(sksec, skb, &ad);
+       if (err)
+               goto out;
+
+       err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, &ad);
 out:   
        return err;
 }
@@ -3482,24 +3574,16 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op
        u32 scontext_len;
        struct sk_security_struct *ssec;
        struct inode_security_struct *isec;
-       u32 peer_sid = 0;
+       u32 peer_sid = SECSID_NULL;
 
        isec = SOCK_INODE(sock)->i_security;
 
-       /* if UNIX_STREAM check peer_sid, if TCP check dst for labelled sa */
-       if (isec->sclass == SECCLASS_UNIX_STREAM_SOCKET) {
+       if (isec->sclass == SECCLASS_UNIX_STREAM_SOCKET ||
+           isec->sclass == SECCLASS_TCP_SOCKET) {
                ssec = sock->sk->sk_security;
                peer_sid = ssec->peer_sid;
        }
-       else if (isec->sclass == SECCLASS_TCP_SOCKET) {
-               peer_sid = selinux_socket_getpeer_stream(sock->sk);
-
-               if (peer_sid == SECSID_NULL) {
-                       err = -ENOPROTOOPT;
-                       goto out;
-               }
-       }
-       else {
+       if (peer_sid == SECSID_NULL) {
                err = -ENOPROTOOPT;
                goto out;
        }
@@ -3531,10 +3615,12 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *
        u32 peer_secid = SECSID_NULL;
        int err = 0;
 
-       if (sock && (sock->sk->sk_family == PF_UNIX))
+       if (sock && sock->sk->sk_family == PF_UNIX)
                selinux_get_inode_sid(SOCK_INODE(sock), &peer_secid);
        else if (skb)
-               peer_secid = selinux_socket_getpeer_dgram(skb);
+               security_skb_extlbl_sid(skb,
+                                       SECINITSID_UNLABELED,
+                                       &peer_secid);
 
        if (peer_secid == SECSID_NULL)
                err = -EINVAL;
@@ -3553,22 +3639,93 @@ static void selinux_sk_free_security(struct sock *sk)
        sk_free_security(sk);
 }
 
-static unsigned int selinux_sk_getsid_security(struct sock *sk, struct flowi *fl, u8 dir)
+static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk)
 {
-       struct inode_security_struct *isec;
-       u32 sock_sid = SECINITSID_ANY_SOCKET;
+       struct sk_security_struct *ssec = sk->sk_security;
+       struct sk_security_struct *newssec = newsk->sk_security;
+
+       newssec->sid = ssec->sid;
+       newssec->peer_sid = ssec->peer_sid;
+
+       selinux_netlbl_sk_security_clone(ssec, newssec);
+}
 
+static void selinux_sk_getsecid(struct sock *sk, u32 *secid)
+{
        if (!sk)
-               return selinux_no_sk_sid(fl);
+               *secid = SECINITSID_ANY_SOCKET;
+       else {
+               struct sk_security_struct *sksec = sk->sk_security;
+
+               *secid = sksec->sid;
+       }
+}
+
+static void selinux_sock_graft(struct sock* sk, struct socket *parent)
+{
+       struct inode_security_struct *isec = SOCK_INODE(parent)->i_security;
+       struct sk_security_struct *sksec = sk->sk_security;
+
+       if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 ||
+           sk->sk_family == PF_UNIX)
+               isec->sid = sksec->sid;
+
+       selinux_netlbl_sock_graft(sk, parent);
+}
 
-       read_lock_bh(&sk->sk_callback_lock);
-       isec = get_sock_isec(sk);
+static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
+                                    struct request_sock *req)
+{
+       struct sk_security_struct *sksec = sk->sk_security;
+       int err;
+       u32 newsid;
+       u32 peersid;
+
+       security_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &peersid);
+       if (peersid == SECSID_NULL) {
+               req->secid = sksec->sid;
+               req->peer_secid = SECSID_NULL;
+               return 0;
+       }
 
-       if (isec)
-               sock_sid = isec->sid;
+       err = security_sid_mls_copy(sksec->sid, peersid, &newsid);
+       if (err)
+               return err;
 
-       read_unlock_bh(&sk->sk_callback_lock);
-       return sock_sid;
+       req->secid = newsid;
+       req->peer_secid = peersid;
+       return 0;
+}
+
+static void selinux_inet_csk_clone(struct sock *newsk,
+                                  const struct request_sock *req)
+{
+       struct sk_security_struct *newsksec = newsk->sk_security;
+
+       newsksec->sid = req->secid;
+       newsksec->peer_sid = req->peer_secid;
+       /* NOTE: Ideally, we should also get the isec->sid for the
+          new socket in sync, but we don't have the isec available yet.
+          So we will wait until sock_graft to do it, by which
+          time it will have been created and available. */
+
+       /* We don't need to take any sort of lock here as we are the only
+        * thread with access to newsksec */
+       selinux_netlbl_sk_security_reset(newsksec, req->rsk_ops->family);
+}
+
+static void selinux_inet_conn_established(struct sock *sk,
+                               struct sk_buff *skb)
+{
+       struct sk_security_struct *sksec = sk->sk_security;
+
+       security_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &sksec->peer_sid);
+}
+
+static void selinux_req_classify_flow(const struct request_sock *req,
+                                     struct flowi *fl)
+{
+       fl->secid = req->secid;
 }
 
 static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
@@ -3610,12 +3767,24 @@ out:
 #ifdef CONFIG_NETFILTER
 
 static int selinux_ip_postroute_last_compat(struct sock *sk, struct net_device *dev,
-                                           struct inode_security_struct *isec,
                                            struct avc_audit_data *ad,
                                            u16 family, char *addrp, int len)
 {
-       int err;
+       int err = 0;
        u32 netif_perm, node_perm, node_sid, if_sid, send_perm = 0;
+       struct socket *sock;
+       struct inode *inode;
+       struct inode_security_struct *isec;
+
+       sock = sk->sk_socket;
+       if (!sock)
+               goto out;
+
+       inode = SOCK_INODE(sock);
+       if (!inode)
+               goto out;
+
+       isec = inode->i_security;
        
        err = sel_netif_sids(dev, &if_sid, NULL);
        if (err)
@@ -3633,7 +3802,13 @@ static int selinux_ip_postroute_last_compat(struct sock *sk, struct net_device *
                node_perm = NODE__TCP_SEND;
                send_perm = TCP_SOCKET__SEND_MSG;
                break;
-       
+
+       case SECCLASS_DCCP_SOCKET:
+               netif_perm = NETIF__DCCP_SEND;
+               node_perm = NODE__DCCP_SEND;
+               send_perm = DCCP_SOCKET__SEND_MSG;
+               break;
+
        default:
                netif_perm = NETIF__RAWIP_SEND;
                node_perm = NODE__RAWIP_SEND;
@@ -3680,46 +3855,37 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
        char *addrp;
        int len, err = 0;
        struct sock *sk;
-       struct socket *sock;
-       struct inode *inode;
        struct sk_buff *skb = *pskb;
-       struct inode_security_struct *isec;
        struct avc_audit_data ad;
        struct net_device *dev = (struct net_device *)out;
+       struct sk_security_struct *sksec;
+       u8 proto;
 
        sk = skb->sk;
        if (!sk)
                goto out;
 
-       sock = sk->sk_socket;
-       if (!sock)
-               goto out;
-
-       inode = SOCK_INODE(sock);
-       if (!inode)
-               goto out;
-
-       isec = inode->i_security;
+       sksec = sk->sk_security;
 
        AVC_AUDIT_DATA_INIT(&ad, NET);
        ad.u.net.netif = dev->name;
        ad.u.net.family = family;
 
-       err = selinux_parse_skb(skb, &ad, &addrp, &len, 0);
+       err = selinux_parse_skb(skb, &ad, &addrp, &len, 0, &proto);
        if (err)
                goto out;
 
        if (selinux_compat_net)
-               err = selinux_ip_postroute_last_compat(sk, dev, isec, &ad,
+               err = selinux_ip_postroute_last_compat(sk, dev, &ad,
                                                       family, addrp, len);
        else
-               err = avc_has_perm(isec->sid, skb->secmark, SECCLASS_PACKET,
+               err = avc_has_perm(sksec->sid, skb->secmark, SECCLASS_PACKET,
                                   PACKET__SEND, &ad);
 
        if (err)
                goto out;
 
-       err = selinux_xfrm_postroute_last(isec->sid, skb);
+       err = selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto);
 out:
        return err ? NF_DROP : NF_ACCEPT;
 }
@@ -4295,6 +4461,7 @@ static int selinux_setprocattr(struct task_struct *p,
                               char *name, void *value, size_t size)
 {
        struct task_security_struct *tsec;
+       struct task_struct *tracer;
        u32 sid = 0;
        int error;
        char *str = value;
@@ -4383,18 +4550,24 @@ static int selinux_setprocattr(struct task_struct *p,
                /* Check for ptracing, and update the task SID if ok.
                   Otherwise, leave SID unchanged and fail. */
                task_lock(p);
-               if (p->ptrace & PT_PTRACED) {
-                       error = avc_has_perm_noaudit(tsec->ptrace_sid, sid,
+               rcu_read_lock();
+               tracer = tracehook_tracer_task(p);
+               if (tracer != NULL) {
+                       struct task_security_struct *ptsec = tracer->security;
+                       u32 ptsid = ptsec->sid;
+                       rcu_read_unlock();
+                       error = avc_has_perm_noaudit(ptsid, sid,
                                                     SECCLASS_PROCESS,
                                                     PROCESS__PTRACE, &avd);
                        if (!error)
                                tsec->sid = sid;
                        task_unlock(p);
-                       avc_audit(tsec->ptrace_sid, sid, SECCLASS_PROCESS,
+                       avc_audit(ptsid, sid, SECCLASS_PROCESS,
                                  PROCESS__PTRACE, &avd, error, NULL);
                        if (error)
                                return error;
                } else {
+                       rcu_read_unlock();
                        tsec->sid = sid;
                        task_unlock(p);
                }
@@ -4620,7 +4793,13 @@ static struct security_operations selinux_ops = {
        .socket_getpeersec_dgram =      selinux_socket_getpeersec_dgram,
        .sk_alloc_security =            selinux_sk_alloc_security,
        .sk_free_security =             selinux_sk_free_security,
-       .sk_getsid =                    selinux_sk_getsid_security,
+       .sk_clone_security =            selinux_sk_clone_security,
+       .sk_getsecid =                  selinux_sk_getsecid,
+       .sock_graft =                   selinux_sock_graft,
+       .inet_conn_request =            selinux_inet_conn_request,
+       .inet_csk_clone =               selinux_inet_csk_clone,
+       .inet_conn_established =        selinux_inet_conn_established,
+       .req_classify_flow =            selinux_req_classify_flow,
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
        .xfrm_policy_alloc_security =   selinux_xfrm_policy_alloc,
@@ -4631,6 +4810,8 @@ static struct security_operations selinux_ops = {
        .xfrm_state_free_security =     selinux_xfrm_state_free,
        .xfrm_state_delete_security =   selinux_xfrm_state_delete,
        .xfrm_policy_lookup =           selinux_xfrm_policy_lookup,
+       .xfrm_state_pol_flow_match =    selinux_xfrm_state_pol_flow_match,
+       .xfrm_decode_session =          selinux_xfrm_decode_session,
 #endif
 
 #ifdef CONFIG_KEYS