ext4: Don't mark filesystem error if fallocate fails
[safe/jmp/linux-2.6] / security / selinux / hooks.c
index 13384fe..75c2e99 100644 (file)
  *  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, 2007 Hewlett-Packard Development Company, L.P.
+ *                Paul Moore <paul.moore@hp.com>
+ *  Copyright (C) 2007 Hitachi Software Engineering Co., Ltd.
+ *                     Yuichi Nakamura <ynakam@hitachisoft.jp>
  *
  *     This program is free software; you can redistribute it and/or modify
  *     it under the terms of the GNU General Public License version 2,
  *      as published by the Free Software Foundation.
  */
 
-#include <linux/config.h>
-#include <linux/module.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/ptrace.h>
@@ -34,7 +36,6 @@
 #include <linux/slab.h>
 #include <linux/pagemap.h>
 #include <linux/swap.h>
-#include <linux/smp_lock.h>
 #include <linux/spinlock.h>
 #include <linux/syscalls.h>
 #include <linux/file.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/ip.h>            /* for local_port_range[] */
 #include <net/tcp.h>           /* struct or_callable used in sock_rcv_skb */
+#include <net/net_namespace.h>
+#include <net/netlabel.h>
 #include <asm/uaccess.h>
-#include <asm/semaphore.h>
 #include <asm/ioctls.h>
+#include <asm/atomic.h>
 #include <linux/bitops.h>
 #include <linux/interrupt.h>
 #include <linux/netdevice.h>   /* for network interface checks */
 #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/sysctl.h>
 #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 "netnode.h"
 #include "xfrm.h"
+#include "netlabel.h"
 
 #define XATTR_SELINUX_SUFFIX "selinux"
 #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
 
+#define NUM_SEL_MNT_OPTS 4
+
 extern unsigned int policydb_loaded_version;
 extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
 extern int selinux_compat_net;
+extern struct security_operations *security_ops;
+
+/* SECMARK reference count */
+atomic_t selinux_secmark_refcount = ATOMIC_INIT(0);
 
 #ifdef CONFIG_SECURITY_SELINUX_DEVELOP
 int selinux_enforcing = 0;
@@ -120,32 +134,21 @@ 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 */
-static int selinux_getsecurity(u32 sid, void *buffer, size_t size)
+/**
+ * selinux_secmark_enabled - Check to see if SECMARK is currently enabled
+ *
+ * Description:
+ * This function checks the SECMARK reference counter to see if any SECMARK
+ * targets are currently configured, if the reference counter is greater than
+ * zero SECMARK is considered to be enabled.  Returns true (1) if SECMARK is
+ * enabled, false (0) if SECMARK is disabled.
+ *
+ */
+static int selinux_secmark_enabled(void)
 {
-       char *context;
-       unsigned len;
-       int rc;
-
-       rc = security_sid_to_context(sid, &context, &len);
-       if (rc)
-               return rc;
-
-       if (!buffer || !size)
-               goto getsecurity_exit;
-
-       if (size < len) {
-               len = -ERANGE;
-               goto getsecurity_exit;
-       }
-       memcpy(buffer, context, len);
-
-getsecurity_exit:
-       kfree(context);
-       return len;
+       return (atomic_read(&selinux_secmark_refcount) > 0);
 }
 
 /* Allocate and free functions for each kind of security blob. */
@@ -177,12 +180,11 @@ 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_zalloc(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,13 +241,14 @@ 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);
        sbsec->sb = sb;
        sbsec->sid = SECINITSID_UNLABELED;
        sbsec->def_sid = SECINITSID_FILE;
+       sbsec->mntpoint_sid = SECINITSID_UNLABELED;
        sb->s_security = sbsec;
 
        return 0;
@@ -268,17 +271,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;
 }
 
@@ -286,9 +289,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);
 }
@@ -316,293 +316,616 @@ static inline int inode_doinit(struct inode *inode)
 }
 
 enum {
+       Opt_error = -1,
        Opt_context = 1,
        Opt_fscontext = 2,
-       Opt_defcontext = 4,
+       Opt_defcontext = 3,
+       Opt_rootcontext = 4,
 };
 
 static match_table_t tokens = {
        {Opt_context, "context=%s"},
        {Opt_fscontext, "fscontext=%s"},
        {Opt_defcontext, "defcontext=%s"},
+       {Opt_rootcontext, "rootcontext=%s"},
+       {Opt_error, NULL},
 };
 
 #define SEL_MOUNT_FAIL_MSG "SELinux:  duplicate or incompatible mount options\n"
 
-static int try_context_mount(struct super_block *sb, void *data)
+static int may_context_mount_sb_relabel(u32 sid,
+                       struct superblock_security_struct *sbsec,
+                       struct task_security_struct *tsec)
 {
-       char *context = NULL, *defcontext = NULL;
-       const char *name;
-       u32 sid;
-       int alloc = 0, rc = 0, seen = 0;
-       struct task_security_struct *tsec = current->security;
-       struct superblock_security_struct *sbsec = sb->s_security;
+       int rc;
 
-       if (!data)
-               goto out;
+       rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
+                         FILESYSTEM__RELABELFROM, NULL);
+       if (rc)
+               return rc;
 
-       name = sb->s_type->name;
+       rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM,
+                         FILESYSTEM__RELABELTO, NULL);
+       return rc;
+}
 
-       if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA) {
+static int may_context_mount_inode_relabel(u32 sid,
+                       struct superblock_security_struct *sbsec,
+                       struct task_security_struct *tsec)
+{
+       int rc;
+       rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
+                         FILESYSTEM__RELABELFROM, NULL);
+       if (rc)
+               return rc;
 
-               /* NFS we understand. */
-               if (!strcmp(name, "nfs")) {
-                       struct nfs_mount_data *d = data;
+       rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM,
+                         FILESYSTEM__ASSOCIATE, NULL);
+       return rc;
+}
 
-                       if (d->version <  NFS_MOUNT_VERSION)
-                               goto out;
+static int sb_finish_set_opts(struct super_block *sb)
+{
+       struct superblock_security_struct *sbsec = sb->s_security;
+       struct dentry *root = sb->s_root;
+       struct inode *root_inode = root->d_inode;
+       int rc = 0;
 
-                       if (d->context[0]) {
-                               context = d->context;
-                               seen |= Opt_context;
-                       }
-               } else
+       if (sbsec->behavior == SECURITY_FS_USE_XATTR) {
+               /* Make sure that the xattr handler exists and that no
+                  error other than -ENODATA is returned by getxattr on
+                  the root directory.  -ENODATA is ok, as this may be
+                  the first boot of the SELinux kernel before we have
+                  assigned xattr values to the filesystem. */
+               if (!root_inode->i_op->getxattr) {
+                       printk(KERN_WARNING "SELinux: (dev %s, type %s) has no "
+                              "xattr support\n", sb->s_id, sb->s_type->name);
+                       rc = -EOPNOTSUPP;
                        goto out;
+               }
+               rc = root_inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0);
+               if (rc < 0 && rc != -ENODATA) {
+                       if (rc == -EOPNOTSUPP)
+                               printk(KERN_WARNING "SELinux: (dev %s, type "
+                                      "%s) has no security xattr handler\n",
+                                      sb->s_id, sb->s_type->name);
+                       else
+                               printk(KERN_WARNING "SELinux: (dev %s, type "
+                                      "%s) getxattr errno %d\n", sb->s_id,
+                                      sb->s_type->name, -rc);
+                       goto out;
+               }
+       }
 
-       } else {
-               /* Standard string-based options. */
-               char *p, *options = data;
+       sbsec->initialized = 1;
 
-               while ((p = strsep(&options, ",")) != NULL) {
-                       int token;
-                       substring_t args[MAX_OPT_ARGS];
+       if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors))
+               printk(KERN_ERR "SELinux: initialized (dev %s, type %s), unknown behavior\n",
+                      sb->s_id, sb->s_type->name);
+       else
+               printk(KERN_DEBUG "SELinux: initialized (dev %s, type %s), %s\n",
+                      sb->s_id, sb->s_type->name,
+                      labeling_behaviors[sbsec->behavior-1]);
 
-                       if (!*p)
-                               continue;
+       /* Initialize the root inode. */
+       rc = inode_doinit_with_dentry(root_inode, root);
 
-                       token = match_token(p, tokens, args);
+       /* Initialize any other inodes associated with the superblock, e.g.
+          inodes created prior to initial policy load or inodes created
+          during get_sb by a pseudo filesystem that directly
+          populates itself. */
+       spin_lock(&sbsec->isec_lock);
+next_inode:
+       if (!list_empty(&sbsec->isec_head)) {
+               struct inode_security_struct *isec =
+                               list_entry(sbsec->isec_head.next,
+                                          struct inode_security_struct, list);
+               struct inode *inode = isec->inode;
+               spin_unlock(&sbsec->isec_lock);
+               inode = igrab(inode);
+               if (inode) {
+                       if (!IS_PRIVATE(inode))
+                               inode_doinit(inode);
+                       iput(inode);
+               }
+               spin_lock(&sbsec->isec_lock);
+               list_del_init(&isec->list);
+               goto next_inode;
+       }
+       spin_unlock(&sbsec->isec_lock);
+out:
+       return rc;
+}
 
-                       switch (token) {
-                       case Opt_context:
-                               if (seen) {
-                                       rc = -EINVAL;
-                                       printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
-                                       goto out_free;
-                               }
-                               context = match_strdup(&args[0]);
-                               if (!context) {
-                                       rc = -ENOMEM;
-                                       goto out_free;
-                               }
-                               if (!alloc)
-                                       alloc = 1;
-                               seen |= Opt_context;
-                               break;
+/*
+ * This function should allow an FS to ask what it's mount security
+ * options were so it can use those later for submounts, displaying
+ * mount options, or whatever.
+ */
+static int selinux_get_mnt_opts(const struct super_block *sb,
+                               char ***mount_options, int **mnt_opts_flags,
+                               int *num_opts)
+{
+       int rc = 0, i;
+       struct superblock_security_struct *sbsec = sb->s_security;
+       char *context = NULL;
+       u32 len;
+       char tmp;
 
-                       case Opt_fscontext:
-                               if (seen & (Opt_context|Opt_fscontext)) {
-                                       rc = -EINVAL;
-                                       printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
-                                       goto out_free;
-                               }
-                               context = match_strdup(&args[0]);
-                               if (!context) {
-                                       rc = -ENOMEM;
-                                       goto out_free;
-                               }
-                               if (!alloc)
-                                       alloc = 1;
-                               seen |= Opt_fscontext;
-                               break;
+       *num_opts = 0;
+       *mount_options = NULL;
+       *mnt_opts_flags = NULL;
 
-                       case Opt_defcontext:
-                               if (sbsec->behavior != SECURITY_FS_USE_XATTR) {
-                                       rc = -EINVAL;
-                                       printk(KERN_WARNING "SELinux:  "
-                                              "defcontext option is invalid "
-                                              "for this filesystem type\n");
-                                       goto out_free;
-                               }
-                               if (seen & (Opt_context|Opt_defcontext)) {
-                                       rc = -EINVAL;
-                                       printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
-                                       goto out_free;
-                               }
-                               defcontext = match_strdup(&args[0]);
-                               if (!defcontext) {
-                                       rc = -ENOMEM;
-                                       goto out_free;
-                               }
-                               if (!alloc)
-                                       alloc = 1;
-                               seen |= Opt_defcontext;
-                               break;
+       if (!sbsec->initialized)
+               return -EINVAL;
 
-                       default:
-                               rc = -EINVAL;
-                               printk(KERN_WARNING "SELinux:  unknown mount "
-                                      "option\n");
-                               goto out_free;
+       if (!ss_initialized)
+               return -EINVAL;
 
-                       }
-               }
+       /*
+        * if we ever use sbsec flags for anything other than tracking mount
+        * settings this is going to need a mask
+        */
+       tmp = sbsec->flags;
+       /* count the number of mount options for this sb */
+       for (i = 0; i < 8; i++) {
+               if (tmp & 0x01)
+                       (*num_opts)++;
+               tmp >>= 1;
        }
 
-       if (!seen)
-               goto out;
+       *mount_options = kcalloc(*num_opts, sizeof(char *), GFP_ATOMIC);
+       if (!*mount_options) {
+               rc = -ENOMEM;
+               goto out_free;
+       }
 
-       if (context) {
-               rc = security_context_to_sid(context, strlen(context), &sid);
-               if (rc) {
-                       printk(KERN_WARNING "SELinux: security_context_to_sid"
-                              "(%s) failed for (dev %s, type %s) errno=%d\n",
-                              context, sb->s_id, name, rc);
-                       goto out_free;
-               }
+       *mnt_opts_flags = kcalloc(*num_opts, sizeof(int), GFP_ATOMIC);
+       if (!*mnt_opts_flags) {
+               rc = -ENOMEM;
+               goto out_free;
+       }
 
-               rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
-                                 FILESYSTEM__RELABELFROM, NULL);
+       i = 0;
+       if (sbsec->flags & FSCONTEXT_MNT) {
+               rc = security_sid_to_context(sbsec->sid, &context, &len);
                if (rc)
                        goto out_free;
-
-               rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM,
-                                 FILESYSTEM__RELABELTO, NULL);
+               (*mount_options)[i] = context;
+               (*mnt_opts_flags)[i++] = FSCONTEXT_MNT;
+       }
+       if (sbsec->flags & CONTEXT_MNT) {
+               rc = security_sid_to_context(sbsec->mntpoint_sid, &context, &len);
                if (rc)
                        goto out_free;
-
-               sbsec->sid = sid;
-
-               if (seen & Opt_context)
-                       sbsec->behavior = SECURITY_FS_USE_MNTPOINT;
+               (*mount_options)[i] = context;
+               (*mnt_opts_flags)[i++] = CONTEXT_MNT;
        }
-
-       if (defcontext) {
-               rc = security_context_to_sid(defcontext, strlen(defcontext), &sid);
-               if (rc) {
-                       printk(KERN_WARNING "SELinux: security_context_to_sid"
-                              "(%s) failed for (dev %s, type %s) errno=%d\n",
-                              defcontext, sb->s_id, name, rc);
-                       goto out_free;
-               }
-
-               if (sid == sbsec->def_sid)
-                       goto out_free;
-
-               rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
-                                 FILESYSTEM__RELABELFROM, NULL);
+       if (sbsec->flags & DEFCONTEXT_MNT) {
+               rc = security_sid_to_context(sbsec->def_sid, &context, &len);
                if (rc)
                        goto out_free;
+               (*mount_options)[i] = context;
+               (*mnt_opts_flags)[i++] = DEFCONTEXT_MNT;
+       }
+       if (sbsec->flags & ROOTCONTEXT_MNT) {
+               struct inode *root = sbsec->sb->s_root->d_inode;
+               struct inode_security_struct *isec = root->i_security;
 
-               rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM,
-                                 FILESYSTEM__ASSOCIATE, NULL);
+               rc = security_sid_to_context(isec->sid, &context, &len);
                if (rc)
                        goto out_free;
-
-               sbsec->def_sid = sid;
+               (*mount_options)[i] = context;
+               (*mnt_opts_flags)[i++] = ROOTCONTEXT_MNT;
        }
 
+       BUG_ON(i != *num_opts);
+
+       return 0;
+
 out_free:
-       if (alloc) {
-               kfree(context);
-               kfree(defcontext);
-       }
-out:
+       /* don't leak context string if security_sid_to_context had an error */
+       if (*mount_options && i)
+               for (; i > 0; i--)
+                       kfree((*mount_options)[i-1]);
+       kfree(*mount_options);
+       *mount_options = NULL;
+       kfree(*mnt_opts_flags);
+       *mnt_opts_flags = NULL;
+       *num_opts = 0;
        return rc;
 }
 
-static int superblock_doinit(struct super_block *sb, void *data)
+static int bad_option(struct superblock_security_struct *sbsec, char flag,
+                     u32 old_sid, u32 new_sid)
+{
+       /* check if the old mount command had the same options */
+       if (sbsec->initialized)
+               if (!(sbsec->flags & flag) ||
+                   (old_sid != new_sid))
+                       return 1;
+
+       /* check if we were passed the same options twice,
+        * aka someone passed context=a,context=b
+        */
+       if (!sbsec->initialized)
+               if (sbsec->flags & flag)
+                       return 1;
+       return 0;
+}
+/*
+ * Allow filesystems with binary mount data to explicitly set mount point
+ * labeling information.
+ */
+static int selinux_set_mnt_opts(struct super_block *sb, char **mount_options,
+                               int *flags, int num_opts)
 {
+       int rc = 0, i;
+       struct task_security_struct *tsec = current->security;
        struct superblock_security_struct *sbsec = sb->s_security;
-       struct dentry *root = sb->s_root;
-       struct inode *inode = root->d_inode;
-       int rc = 0;
+       const char *name = sb->s_type->name;
+       struct inode *inode = sbsec->sb->s_root->d_inode;
+       struct inode_security_struct *root_isec = inode->i_security;
+       u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
+       u32 defcontext_sid = 0;
 
-       down(&sbsec->sem);
-       if (sbsec->initialized)
-               goto out;
+       mutex_lock(&sbsec->lock);
 
        if (!ss_initialized) {
-               /* Defer initialization until selinux_complete_init,
-                  after the initial policy is loaded and the security
-                  server is ready to handle calls. */
-               spin_lock(&sb_security_lock);
-               if (list_empty(&sbsec->list))
-                       list_add(&sbsec->list, &superblock_security_head);
-               spin_unlock(&sb_security_lock);
+               if (!num_opts) {
+                       /* Defer initialization until selinux_complete_init,
+                          after the initial policy is loaded and the security
+                          server is ready to handle calls. */
+                       spin_lock(&sb_security_lock);
+                       if (list_empty(&sbsec->list))
+                               list_add(&sbsec->list, &superblock_security_head);
+                       spin_unlock(&sb_security_lock);
+                       goto out;
+               }
+               rc = -EINVAL;
+               printk(KERN_WARNING "Unable to set superblock options before "
+                      "the security server is initialized\n");
                goto out;
        }
 
+       /*
+        * parse the mount options, check if they are valid sids.
+        * also check if someone is trying to mount the same sb more
+        * than once with different security options.
+        */
+       for (i = 0; i < num_opts; i++) {
+               u32 sid;
+               rc = security_context_to_sid(mount_options[i],
+                                            strlen(mount_options[i]), &sid);
+               if (rc) {
+                       printk(KERN_WARNING "SELinux: security_context_to_sid"
+                              "(%s) failed for (dev %s, type %s) errno=%d\n",
+                              mount_options[i], sb->s_id, name, rc);
+                       goto out;
+               }
+               switch (flags[i]) {
+               case FSCONTEXT_MNT:
+                       fscontext_sid = sid;
+
+                       if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid,
+                                       fscontext_sid))
+                               goto out_double_mount;
+
+                       sbsec->flags |= FSCONTEXT_MNT;
+                       break;
+               case CONTEXT_MNT:
+                       context_sid = sid;
+
+                       if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid,
+                                       context_sid))
+                               goto out_double_mount;
+
+                       sbsec->flags |= CONTEXT_MNT;
+                       break;
+               case ROOTCONTEXT_MNT:
+                       rootcontext_sid = sid;
+
+                       if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid,
+                                       rootcontext_sid))
+                               goto out_double_mount;
+
+                       sbsec->flags |= ROOTCONTEXT_MNT;
+
+                       break;
+               case DEFCONTEXT_MNT:
+                       defcontext_sid = sid;
+
+                       if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid,
+                                       defcontext_sid))
+                               goto out_double_mount;
+
+                       sbsec->flags |= DEFCONTEXT_MNT;
+
+                       break;
+               default:
+                       rc = -EINVAL;
+                       goto out;
+               }
+       }
+
+       if (sbsec->initialized) {
+               /* previously mounted with options, but not on this attempt? */
+               if (sbsec->flags && !num_opts)
+                       goto out_double_mount;
+               rc = 0;
+               goto out;
+       }
+
+       if (strcmp(sb->s_type->name, "proc") == 0)
+               sbsec->proc = 1;
+
        /* Determine the labeling behavior to use for this filesystem type. */
        rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid);
        if (rc) {
-               printk(KERN_WARNING "%s:  security_fs_use(%s) returned %d\n",
+               printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
                       __FUNCTION__, sb->s_type->name, rc);
                goto out;
        }
 
-       rc = try_context_mount(sb, data);
-       if (rc)
-               goto out;
+       /* sets the context of the superblock for the fs being mounted. */
+       if (fscontext_sid) {
 
-       if (sbsec->behavior == SECURITY_FS_USE_XATTR) {
-               /* Make sure that the xattr handler exists and that no
-                  error other than -ENODATA is returned by getxattr on
-                  the root directory.  -ENODATA is ok, as this may be
-                  the first boot of the SELinux kernel before we have
-                  assigned xattr values to the filesystem. */
-               if (!inode->i_op->getxattr) {
-                       printk(KERN_WARNING "SELinux: (dev %s, type %s) has no "
-                              "xattr support\n", sb->s_id, sb->s_type->name);
-                       rc = -EOPNOTSUPP;
+               rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, tsec);
+               if (rc)
                        goto out;
+
+               sbsec->sid = fscontext_sid;
+       }
+
+       /*
+        * Switch to using mount point labeling behavior.
+        * sets the label used on all file below the mountpoint, and will set
+        * the superblock context if not already set.
+        */
+       if (context_sid) {
+               if (!fscontext_sid) {
+                       rc = may_context_mount_sb_relabel(context_sid, sbsec, tsec);
+                       if (rc)
+                               goto out;
+                       sbsec->sid = context_sid;
+               } else {
+                       rc = may_context_mount_inode_relabel(context_sid, sbsec, tsec);
+                       if (rc)
+                               goto out;
                }
-               rc = inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0);
-               if (rc < 0 && rc != -ENODATA) {
-                       if (rc == -EOPNOTSUPP)
-                               printk(KERN_WARNING "SELinux: (dev %s, type "
-                                      "%s) has no security xattr handler\n",
-                                      sb->s_id, sb->s_type->name);
-                       else
-                               printk(KERN_WARNING "SELinux: (dev %s, type "
-                                      "%s) getxattr errno %d\n", sb->s_id,
-                                      sb->s_type->name, -rc);
+               if (!rootcontext_sid)
+                       rootcontext_sid = context_sid;
+
+               sbsec->mntpoint_sid = context_sid;
+               sbsec->behavior = SECURITY_FS_USE_MNTPOINT;
+       }
+
+       if (rootcontext_sid) {
+               rc = may_context_mount_inode_relabel(rootcontext_sid, sbsec, tsec);
+               if (rc)
                        goto out;
+
+               root_isec->sid = rootcontext_sid;
+               root_isec->initialized = 1;
+       }
+
+       if (defcontext_sid) {
+               if (sbsec->behavior != SECURITY_FS_USE_XATTR) {
+                       rc = -EINVAL;
+                       printk(KERN_WARNING "SELinux: defcontext option is "
+                              "invalid for this filesystem type\n");
+                       goto out;
+               }
+
+               if (defcontext_sid != sbsec->def_sid) {
+                       rc = may_context_mount_inode_relabel(defcontext_sid,
+                                                            sbsec, tsec);
+                       if (rc)
+                               goto out;
                }
+
+               sbsec->def_sid = defcontext_sid;
        }
 
-       if (strcmp(sb->s_type->name, "proc") == 0)
-               sbsec->proc = 1;
+       rc = sb_finish_set_opts(sb);
+out:
+       mutex_unlock(&sbsec->lock);
+       return rc;
+out_double_mount:
+       rc = -EINVAL;
+       printk(KERN_WARNING "SELinux: mount invalid.  Same superblock, different "
+              "security settings for (dev %s, type %s)\n", sb->s_id, name);
+       goto out;
+}
 
-       sbsec->initialized = 1;
+static void selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
+                                       struct super_block *newsb)
+{
+       const struct superblock_security_struct *oldsbsec = oldsb->s_security;
+       struct superblock_security_struct *newsbsec = newsb->s_security;
 
-       if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors)) {
-               printk(KERN_INFO "SELinux: initialized (dev %s, type %s), unknown behavior\n",
-                      sb->s_id, sb->s_type->name);
+       int set_fscontext =     (oldsbsec->flags & FSCONTEXT_MNT);
+       int set_context =       (oldsbsec->flags & CONTEXT_MNT);
+       int set_rootcontext =   (oldsbsec->flags & ROOTCONTEXT_MNT);
+
+       /* we can't error, we can't save the info, this shouldn't get called
+        * this early in the boot process. */
+       BUG_ON(!ss_initialized);
+
+       /* this might go away sometime down the line if there is a new user
+        * of clone, but for now, nfs better not get here... */
+       BUG_ON(newsbsec->initialized);
+
+       /* how can we clone if the old one wasn't set up?? */
+       BUG_ON(!oldsbsec->initialized);
+
+       mutex_lock(&newsbsec->lock);
+
+       newsbsec->flags = oldsbsec->flags;
+
+       newsbsec->sid = oldsbsec->sid;
+       newsbsec->def_sid = oldsbsec->def_sid;
+       newsbsec->behavior = oldsbsec->behavior;
+
+       if (set_context) {
+               u32 sid = oldsbsec->mntpoint_sid;
+
+               if (!set_fscontext)
+                       newsbsec->sid = sid;
+               if (!set_rootcontext) {
+                       struct inode *newinode = newsb->s_root->d_inode;
+                       struct inode_security_struct *newisec = newinode->i_security;
+                       newisec->sid = sid;
+               }
+               newsbsec->mntpoint_sid = sid;
        }
-       else {
-               printk(KERN_INFO "SELinux: initialized (dev %s, type %s), %s\n",
-                      sb->s_id, sb->s_type->name,
-                      labeling_behaviors[sbsec->behavior-1]);
+       if (set_rootcontext) {
+               const struct inode *oldinode = oldsb->s_root->d_inode;
+               const struct inode_security_struct *oldisec = oldinode->i_security;
+               struct inode *newinode = newsb->s_root->d_inode;
+               struct inode_security_struct *newisec = newinode->i_security;
+
+               newisec->sid = oldisec->sid;
        }
 
-       /* Initialize the root inode. */
-       rc = inode_doinit_with_dentry(sb->s_root->d_inode, sb->s_root);
+       sb_finish_set_opts(newsb);
+       mutex_unlock(&newsbsec->lock);
+}
+
+/*
+ * string mount options parsing and call set the sbsec
+ */
+static int superblock_doinit(struct super_block *sb, void *data)
+{
+       char *context = NULL, *defcontext = NULL;
+       char *fscontext = NULL, *rootcontext = NULL;
+       int rc = 0;
+       char *p, *options = data;
+       /* selinux only know about a fixed number of mount options */
+       char *mnt_opts[NUM_SEL_MNT_OPTS];
+       int mnt_opts_flags[NUM_SEL_MNT_OPTS], num_mnt_opts = 0;
+
+       if (!data)
+               goto out;
+
+       /* with the nfs patch this will become a goto out; */
+       if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA) {
+               const char *name = sb->s_type->name;
+               /* NFS we understand. */
+               if (!strcmp(name, "nfs")) {
+                       struct nfs_mount_data *d = data;
+
+                       if (d->version !=  NFS_MOUNT_VERSION)
+                               goto out;
+
+                       if (d->context[0]) {
+                               context = kstrdup(d->context, GFP_KERNEL);
+                               if (!context) {
+                                       rc = -ENOMEM;
+                                       goto out;
+                               }
+                       }
+                       goto build_flags;
+               } else
+                       goto out;
+       }
+
+       /* Standard string-based options. */
+       while ((p = strsep(&options, "|")) != NULL) {
+               int token;
+               substring_t args[MAX_OPT_ARGS];
+
+               if (!*p)
+                       continue;
+
+               token = match_token(p, tokens, args);
+
+               switch (token) {
+               case Opt_context:
+                       if (context || defcontext) {
+                               rc = -EINVAL;
+                               printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
+                               goto out_err;
+                       }
+                       context = match_strdup(&args[0]);
+                       if (!context) {
+                               rc = -ENOMEM;
+                               goto out_err;
+                       }
+                       break;
+
+               case Opt_fscontext:
+                       if (fscontext) {
+                               rc = -EINVAL;
+                               printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
+                               goto out_err;
+                       }
+                       fscontext = match_strdup(&args[0]);
+                       if (!fscontext) {
+                               rc = -ENOMEM;
+                               goto out_err;
+                       }
+                       break;
+
+               case Opt_rootcontext:
+                       if (rootcontext) {
+                               rc = -EINVAL;
+                               printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
+                               goto out_err;
+                       }
+                       rootcontext = match_strdup(&args[0]);
+                       if (!rootcontext) {
+                               rc = -ENOMEM;
+                               goto out_err;
+                       }
+                       break;
+
+               case Opt_defcontext:
+                       if (context || defcontext) {
+                               rc = -EINVAL;
+                               printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
+                               goto out_err;
+                       }
+                       defcontext = match_strdup(&args[0]);
+                       if (!defcontext) {
+                               rc = -ENOMEM;
+                               goto out_err;
+                       }
+                       break;
+
+               default:
+                       rc = -EINVAL;
+                       printk(KERN_WARNING "SELinux:  unknown mount option\n");
+                       goto out_err;
 
-       /* Initialize any other inodes associated with the superblock, e.g.
-          inodes created prior to initial policy load or inodes created
-          during get_sb by a pseudo filesystem that directly
-          populates itself. */
-       spin_lock(&sbsec->isec_lock);
-next_inode:
-       if (!list_empty(&sbsec->isec_head)) {
-               struct inode_security_struct *isec =
-                               list_entry(sbsec->isec_head.next,
-                                          struct inode_security_struct, list);
-               struct inode *inode = isec->inode;
-               spin_unlock(&sbsec->isec_lock);
-               inode = igrab(inode);
-               if (inode) {
-                       if (!IS_PRIVATE (inode))
-                               inode_doinit(inode);
-                       iput(inode);
                }
-               spin_lock(&sbsec->isec_lock);
-               list_del_init(&isec->list);
-               goto next_inode;
        }
-       spin_unlock(&sbsec->isec_lock);
+
+build_flags:
+       if (fscontext) {
+               mnt_opts[num_mnt_opts] = fscontext;
+               mnt_opts_flags[num_mnt_opts++] = FSCONTEXT_MNT;
+       }
+       if (context) {
+               mnt_opts[num_mnt_opts] = context;
+               mnt_opts_flags[num_mnt_opts++] = CONTEXT_MNT;
+       }
+       if (rootcontext) {
+               mnt_opts[num_mnt_opts] = rootcontext;
+               mnt_opts_flags[num_mnt_opts++] = ROOTCONTEXT_MNT;
+       }
+       if (defcontext) {
+               mnt_opts[num_mnt_opts] = defcontext;
+               mnt_opts_flags[num_mnt_opts++] = DEFCONTEXT_MNT;
+       }
+
 out:
-       up(&sbsec->sem);
+       rc = selinux_set_mnt_opts(sb, mnt_opts, mnt_opts_flags, num_mnt_opts);
+out_err:
+       kfree(context);
+       kfree(defcontext);
+       kfree(fscontext);
+       kfree(rootcontext);
        return rc;
 }
 
@@ -664,6 +987,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;
                }
@@ -756,15 +1081,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) {
@@ -775,7 +1098,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) {
@@ -798,7 +1121,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;
@@ -806,7 +1129,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);
@@ -816,7 +1139,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;
@@ -824,7 +1147,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,
@@ -837,7 +1160,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;
@@ -873,11 +1196,14 @@ 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:
+               isec->sid = sbsec->mntpoint_sid;
+               break;
        default:
-               /* Default to the fs SID. */
+               /* Default to the fs superblock SID. */
                isec->sid = sbsec->sid;
 
                if (sbsec->proc) {
@@ -888,7 +1214,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;
                        }
                }
@@ -897,12 +1223,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;
 }
 
@@ -947,12 +1272,18 @@ static int task_has_perm(struct task_struct *tsk1,
                            SECCLASS_PROCESS, perms, NULL);
 }
 
+#if CAP_LAST_CAP > 63
+#error Fix SELinux to handle capabilities > 63.
+#endif
+
 /* Check whether a task is allowed to use a capability. */
 static int task_has_capability(struct task_struct *tsk,
                               int cap)
 {
        struct task_security_struct *tsec;
        struct avc_audit_data ad;
+       u16 sclass;
+       u32 av = CAP_TO_MASK(cap);
 
        tsec = tsk->security;
 
@@ -960,8 +1291,19 @@ static int task_has_capability(struct task_struct *tsk,
        ad.tsk = tsk;
        ad.u.cap = cap;
 
-       return avc_has_perm(tsec->sid, tsec->sid,
-                           SECCLASS_CAPABILITY, CAP_TO_MASK(cap), &ad);
+       switch (CAP_TO_INDEX(cap)) {
+       case 0:
+               sclass = SECCLASS_CAPABILITY;
+               break;
+       case 1:
+               sclass = SECCLASS_CAPABILITY2;
+               break;
+       default:
+               printk(KERN_ERR
+                      "SELinux:  out of range capability %d\n", cap);
+               BUG();
+       }
+       return avc_has_perm(tsec->sid, tsec->sid, sclass, av, &ad);
 }
 
 /* Check whether a task is allowed to use a system operation. */
@@ -988,6 +1330,9 @@ static int inode_has_perm(struct task_struct *tsk,
        struct inode_security_struct *isec;
        struct avc_audit_data ad;
 
+       if (unlikely (IS_PRIVATE (inode)))
+               return 0;
+
        tsec = tsk->security;
        isec = inode->i_security;
 
@@ -1011,8 +1356,8 @@ static inline int dentry_has_perm(struct task_struct *tsk,
        struct inode *inode = dentry->d_inode;
        struct avc_audit_data ad;
        AVC_AUDIT_DATA_INIT(&ad,FS);
-       ad.u.fs.mnt = mnt;
-       ad.u.fs.dentry = dentry;
+       ad.u.fs.path.mnt = mnt;
+       ad.u.fs.path.dentry = dentry;
        return inode_has_perm(tsk, inode, av, &ad);
 }
 
@@ -1030,15 +1375,12 @@ 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 inode *inode = dentry->d_inode;
+       struct inode *inode = file->f_path.dentry->d_inode;
        struct avc_audit_data ad;
        int rc;
 
        AVC_AUDIT_DATA_INIT(&ad, FS);
-       ad.u.fs.mnt = mnt;
-       ad.u.fs.dentry = dentry;
+       ad.u.fs.path = file->f_path;
 
        if (tsec->sid != fsec->sid) {
                rc = avc_has_perm(tsec->sid, fsec->sid,
@@ -1073,7 +1415,7 @@ static int may_create(struct inode *dir,
        sbsec = dir->i_sb->s_security;
 
        AVC_AUDIT_DATA_INIT(&ad, FS);
-       ad.u.fs.dentry = dentry;
+       ad.u.fs.path.dentry = dentry;
 
        rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR,
                          DIR__ADD_NAME | DIR__SEARCH,
@@ -1099,6 +1441,17 @@ static int may_create(struct inode *dir,
                            FILESYSTEM__ASSOCIATE, &ad);
 }
 
+/* Check whether a task can create a key. */
+static int may_create_key(u32 ksid,
+                         struct task_struct *ctx)
+{
+       struct task_security_struct *tsec;
+
+       tsec = ctx->security;
+
+       return avc_has_perm(tsec->sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL);
+}
+
 #define MAY_LINK   0
 #define MAY_UNLINK 1
 #define MAY_RMDIR  2
@@ -1120,7 +1473,7 @@ static int may_link(struct inode *dir,
        isec = dentry->d_inode->i_security;
 
        AVC_AUDIT_DATA_INIT(&ad, FS);
-       ad.u.fs.dentry = dentry;
+       ad.u.fs.path.dentry = dentry;
 
        av = DIR__SEARCH;
        av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME);
@@ -1167,7 +1520,7 @@ static inline int may_rename(struct inode *old_dir,
 
        AVC_AUDIT_DATA_INIT(&ad, FS);
 
-       ad.u.fs.dentry = old_dentry;
+       ad.u.fs.path.dentry = old_dentry;
        rc = avc_has_perm(tsec->sid, old_dsec->sid, SECCLASS_DIR,
                          DIR__REMOVE_NAME | DIR__SEARCH, &ad);
        if (rc)
@@ -1183,7 +1536,7 @@ static inline int may_rename(struct inode *old_dir,
                        return rc;
        }
 
-       ad.u.fs.dentry = new_dentry;
+       ad.u.fs.path.dentry = new_dentry;
        av = DIR__ADD_NAME | DIR__SEARCH;
        if (new_dentry->d_inode)
                av |= DIR__REMOVE_NAME;
@@ -1263,25 +1616,6 @@ 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)
@@ -1342,6 +1676,47 @@ static int selinux_capable(struct task_struct *tsk, int cap)
        return task_has_capability(tsk,cap);
 }
 
+static int selinux_sysctl_get_sid(ctl_table *table, u16 tclass, u32 *sid)
+{
+       int buflen, rc;
+       char *buffer, *path, *end;
+
+       rc = -ENOMEM;
+       buffer = (char*)__get_free_page(GFP_KERNEL);
+       if (!buffer)
+               goto out;
+
+       buflen = PAGE_SIZE;
+       end = buffer+buflen;
+       *--end = '\0';
+       buflen--;
+       path = end-1;
+       *path = '/';
+       while (table) {
+               const char *name = table->procname;
+               size_t namelen = strlen(name);
+               buflen -= namelen + 1;
+               if (buflen < 0)
+                       goto out_free;
+               end -= namelen;
+               memcpy(end, name, namelen);
+               *--end = '/';
+               path = end;
+               table = table->parent;
+       }
+       buflen -= 4;
+       if (buflen < 0)
+               goto out_free;
+       end -= 4;
+       memcpy(end, "/sys", 4);
+       path = end;
+       rc = security_genfs_sid("proc", path, tclass, sid);
+out_free:
+       free_page((unsigned long)buffer);
+out:
+       return rc;
+}
+
 static int selinux_sysctl(ctl_table *table, int op)
 {
        int error = 0;
@@ -1356,8 +1731,8 @@ static int selinux_sysctl(ctl_table *table, int op)
 
        tsec = current->security;
 
-       rc = selinux_proc_get_sid(table->de, (op == 001) ?
-                                 SECCLASS_DIR : SECCLASS_FILE, &tsid);
+       rc = selinux_sysctl_get_sid(table, (op == 0001) ?
+                                   SECCLASS_DIR : SECCLASS_FILE, &tsid);
        if (rc) {
                /* Default to the well-defined sysctl SID. */
                tsid = SECINITSID_SYSCTL;
@@ -1460,7 +1835,7 @@ static int selinux_syslog(int type)
  * Do not audit the selinux permission check, as this is applied to all
  * processes that allocate mappings.
  */
-static int selinux_vm_enough_memory(long pages)
+static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
 {
        int rc, cap_sys_admin = 0;
        struct task_security_struct *tsec = current->security;
@@ -1468,14 +1843,15 @@ static int selinux_vm_enough_memory(long pages)
        rc = secondary_ops->capable(current, CAP_SYS_ADMIN);
        if (rc == 0)
                rc = avc_has_perm_noaudit(tsec->sid, tsec->sid,
-                                       SECCLASS_CAPABILITY,
-                                       CAP_TO_MASK(CAP_SYS_ADMIN),
-                                       NULL);
+                                         SECCLASS_CAPABILITY,
+                                         CAP_TO_MASK(CAP_SYS_ADMIN),
+                                         0,
+                                         NULL);
 
        if (rc == 0)
                cap_sys_admin = 1;
 
-       return __vm_enough_memory(pages, cap_sys_admin);
+       return __vm_enough_memory(mm, pages, cap_sys_admin);
 }
 
 /* binprm security operations */
@@ -1499,7 +1875,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;
@@ -1521,8 +1897,10 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
        /* Default to the current task SID. */
        bsec->sid = tsec->sid;
 
-       /* Reset create SID on execve. */
+       /* Reset fs, key, and sock SIDs on execve. */
        tsec->create_sid = 0;
+       tsec->keycreate_sid = 0;
+       tsec->sockcreate_sid = 0;
 
        if (tsec->exec_sid) {
                newsid = tsec->exec_sid;
@@ -1537,10 +1915,9 @@ 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.path = bprm->file->f_path;
 
-       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) {
@@ -1608,10 +1985,13 @@ 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;
+       struct tty_struct *tty;
        struct fdtable *fdt;
        long j = -1;
+       int drop_tty = 0;
 
+       mutex_lock(&tty_mutex);
+       tty = get_current_tty();
        if (tty) {
                file_list_lock();
                file = list_entry(tty->tty_files.next, typeof(*file), f_u.fu_list);
@@ -1621,16 +2001,18 @@ 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)) {
-                               /* Reset controlling tty. */
-                               current->signal->tty = NULL;
-                               current->signal->tty_old_pgrp = 0;
+                               drop_tty = 1;
                        }
                }
                file_list_unlock();
        }
+       mutex_unlock(&tty_mutex);
+       /* Reset controlling tty. */
+       if (drop_tty)
+               no_tty();
 
        /* Revalidate access to inherited open files. */
 
@@ -1644,7 +2026,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)
@@ -1670,7 +2052,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;
@@ -1774,6 +2157,9 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
                spin_unlock_irq(&current->sighand->siglock);
        }
 
+       /* Always clear parent death signal on SID transitions. */
+       current->pdeath_signal = 0;
+
        /* Check whether the new SID can inherit resource limits
           from the old SID.  If not, reset all soft limits to
           the lower of the current task's hard limit and the init
@@ -1830,7 +2216,8 @@ static inline int selinux_option(char *option, int len)
 {
        return (match_prefix("context=", sizeof("context=")-1, option, len) ||
                match_prefix("fscontext=", sizeof("fscontext=")-1, option, len) ||
-               match_prefix("defcontext=", sizeof("defcontext=")-1, option, len));
+               match_prefix("defcontext=", sizeof("defcontext=")-1, option, len) ||
+               match_prefix("rootcontext=", sizeof("rootcontext=")-1, option, len));
 }
 
 static inline void take_option(char **to, char *from, int *first, int len)
@@ -1838,18 +2225,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;
@@ -1871,11 +2280,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);
 
@@ -1899,7 +2311,7 @@ static int selinux_sb_kern_mount(struct super_block *sb, void *data)
                return rc;
 
        AVC_AUDIT_DATA_INIT(&ad,FS);
-       ad.u.fs.dentry = sb->s_root;
+       ad.u.fs.path.dentry = sb->s_root;
        return superblock_has_perm(current, sb, FILESYSTEM__MOUNT, &ad);
 }
 
@@ -1908,7 +2320,7 @@ static int selinux_sb_statfs(struct dentry *dentry)
        struct avc_audit_data ad;
 
        AVC_AUDIT_DATA_INIT(&ad,FS);
-       ad.u.fs.dentry = dentry->d_sb->s_root;
+       ad.u.fs.path.dentry = dentry->d_sb->s_root;
        return superblock_has_perm(current, dentry->d_sb, FILESYSTEM__GETATTR, &ad);
 }
 
@@ -1925,10 +2337,10 @@ static int selinux_mount(char * dev_name,
                return rc;
 
        if (flags & MS_REMOUNT)
-               return superblock_has_perm(current, nd->mnt->mnt_sb,
+               return superblock_has_perm(current, nd->path.mnt->mnt_sb,
                                           FILESYSTEM__REMOUNT, NULL);
        else
-               return dentry_has_perm(current, nd->mnt, nd->dentry,
+               return dentry_has_perm(current, nd->path.mnt, nd->path.dentry,
                                       FILE__MOUNTON);
 }
 
@@ -1987,7 +2399,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;
@@ -2125,6 +2543,25 @@ static int selinux_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
        return dentry_has_perm(current, mnt, dentry, FILE__GETATTR);
 }
 
+static int selinux_inode_setotherxattr(struct dentry *dentry, char *name)
+{
+       if (!strncmp(name, XATTR_SECURITY_PREFIX,
+                    sizeof XATTR_SECURITY_PREFIX - 1)) {
+               if (!strcmp(name, XATTR_NAME_CAPS)) {
+                       if (!capable(CAP_SETFCAP))
+                               return -EPERM;
+               } else if (!capable(CAP_SYS_ADMIN)) {
+                       /* A different attribute in the security namespace.
+                          Restrict to administrator. */
+                       return -EPERM;
+               }
+       }
+
+       /* Not an attribute we recognize, so just check the
+          ordinary setattr permission. */
+       return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
+}
+
 static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value, size_t size, int flags)
 {
        struct task_security_struct *tsec = current->security;
@@ -2135,29 +2572,18 @@ static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value
        u32 newsid;
        int rc = 0;
 
-       if (strcmp(name, XATTR_NAME_SELINUX)) {
-               if (!strncmp(name, XATTR_SECURITY_PREFIX,
-                            sizeof XATTR_SECURITY_PREFIX - 1) &&
-                   !capable(CAP_SYS_ADMIN)) {
-                       /* A different attribute in the security namespace.
-                          Restrict to administrator. */
-                       return -EPERM;
-               }
-
-               /* Not an attribute we recognize, so just check the
-                  ordinary setattr permission. */
-               return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
-       }
+       if (strcmp(name, XATTR_NAME_SELINUX))
+               return selinux_inode_setotherxattr(dentry, name);
 
        sbsec = inode->i_sb->s_security;
        if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
                return -EOPNOTSUPP;
 
-       if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+       if (!is_owner_or_cap(inode))
                return -EPERM;
 
        AVC_AUDIT_DATA_INIT(&ad,FS);
-       ad.u.fs.dentry = dentry;
+       ad.u.fs.path.dentry = dentry;
 
        rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
                          FILE__RELABELFROM, &ad);
@@ -2221,31 +2647,14 @@ static int selinux_inode_listxattr (struct dentry *dentry)
 
 static int selinux_inode_removexattr (struct dentry *dentry, char *name)
 {
-       if (strcmp(name, XATTR_NAME_SELINUX)) {
-               if (!strncmp(name, XATTR_SECURITY_PREFIX,
-                            sizeof XATTR_SECURITY_PREFIX - 1) &&
-                   !capable(CAP_SYS_ADMIN)) {
-                       /* A different attribute in the security namespace.
-                          Restrict to administrator. */
-                       return -EPERM;
-               }
-
-               /* Not an attribute we recognize, so just check the
-                  ordinary setattr permission. Might want a separate
-                  permission for removexattr. */
-               return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
-       }
+       if (strcmp(name, XATTR_NAME_SELINUX))
+               return selinux_inode_setotherxattr(dentry, name);
 
        /* No one is allowed to remove a SELinux security label.
           You can change the label, but all data must be labeled. */
        return -EACCES;
 }
 
-static const char *selinux_inode_xattr_getsuffix(void)
-{
-      return XATTR_SELINUX_SUFFIX;
-}
-
 /*
  * Copy the in-core inode security context value to the user.  If the
  * getxattr() prior to this succeeded, check to see if we need to
@@ -2253,14 +2662,27 @@ static const char *selinux_inode_xattr_getsuffix(void)
  *
  * Permission check is handled by selinux_inode_getxattr hook.
  */
-static int selinux_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err)
+static int selinux_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc)
 {
+       u32 size;
+       int error;
+       char *context = NULL;
        struct inode_security_struct *isec = inode->i_security;
 
        if (strcmp(name, XATTR_SELINUX_SUFFIX))
                return -EOPNOTSUPP;
 
-       return selinux_getsecurity(isec->sid, buffer, size);
+       error = security_sid_to_context(isec->sid, &context, &size);
+       if (error)
+               return error;
+       error = size;
+       if (alloc) {
+               *buffer = context;
+               goto out_nofree;
+       }
+       kfree(context);
+out_nofree:
+       return error;
 }
 
 static int selinux_inode_setsecurity(struct inode *inode, const char *name,
@@ -2292,11 +2714,22 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t
        return len;
 }
 
+static int selinux_inode_need_killpriv(struct dentry *dentry)
+{
+       return secondary_ops->inode_need_killpriv(dentry);
+}
+
+static int selinux_inode_killpriv(struct dentry *dentry)
+{
+       return secondary_ops->inode_killpriv(dentry);
+}
+
 /* file security operations */
 
-static int selinux_file_permission(struct file *file, int mask)
+static int selinux_revalidate_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. */
@@ -2307,8 +2740,31 @@ 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_permission(struct file *file, int mask)
+{
+       struct inode *inode = file->f_path.dentry->d_inode;
+       struct task_security_struct *tsec = current->security;
+       struct file_security_struct *fsec = file->f_security;
+       struct inode_security_struct *isec = inode->i_security;
+
+       if (!mask) {
+               /* No permission to check.  Existence test. */
+               return 0;
+       }
+
+       if (tsec->sid == fsec->sid && fsec->isid == isec->sid
+           && fsec->pseqno == avc_policy_seqno())
+               return selinux_netlbl_inode_permission(inode, mask);
+
+       return selinux_revalidate_file_permission(file, mask);
 }
 
 static int selinux_file_alloc_security(struct file *file)
@@ -2399,12 +2855,16 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared
 }
 
 static int selinux_file_mmap(struct file *file, unsigned long reqprot,
-                            unsigned long prot, unsigned long flags)
+                            unsigned long prot, unsigned long flags,
+                            unsigned long addr, unsigned long addr_only)
 {
-       int rc;
+       int rc = 0;
+       u32 sid = ((struct task_security_struct*)(current->security))->sid;
 
-       rc = secondary_ops->file_mmap(file, reqprot, prot, flags);
-       if (rc)
+       if (addr < mmap_min_addr)
+               rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT,
+                                 MEMPROTECT__MMAP_ZERO, NULL);
+       if (rc || addr_only)
                return rc;
 
        if (selinux_checkreqprot)
@@ -2469,7 +2929,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;
                        }
@@ -2495,7 +2955,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;
                        }
@@ -2527,7 +2987,7 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk,
        struct file_security_struct *fsec;
 
        /* struct fown_struct is never outside the context of a struct file */
-        file = (struct file *)((long)fown - offsetof(struct file,f_owner));
+        file = container_of(fown, struct file, f_owner);
 
        tsec = tsk->security;
        fsec = file->f_security;
@@ -2546,6 +3006,34 @@ static int selinux_file_receive(struct file *file)
        return file_has_perm(current, file, file_to_av(file));
 }
 
+static int selinux_dentry_open(struct file *file)
+{
+       struct file_security_struct *fsec;
+       struct inode *inode;
+       struct inode_security_struct *isec;
+       inode = file->f_path.dentry->d_inode;
+       fsec = file->f_security;
+       isec = inode->i_security;
+       /*
+        * Save inode label and policy sequence number
+        * at open-time so that selinux_file_permission
+        * can determine whether revalidation is necessary.
+        * Task label is already saved in the file security
+        * struct as its SID.
+        */
+       fsec->isid = isec->sid;
+       fsec->pseqno = avc_policy_seqno();
+       /*
+        * Since the inode label or policy seqno may have changed
+        * between the selinux_inode_permission check and the saving
+        * of state above, recheck that access is still permitted.
+        * Otherwise, access might never be revalidated against the
+        * new inode label or new policy.
+        * This check is not redundant - do not remove.
+        */
+       return inode_has_perm(current, inode, file_to_av(file), NULL);
+}
+
 /* task security operations */
 
 static int selinux_task_create(unsigned long clone_flags)
@@ -2574,9 +3062,11 @@ static int selinux_task_alloc_security(struct task_struct *tsk)
        tsec2->osid = tsec1->osid;
        tsec2->sid = tsec1->sid;
 
-       /* Retain the exec and create SIDs across fork */
+       /* Retain the exec, fs, key, and sock SIDs across fork */
        tsec2->exec_sid = tsec1->exec_sid;
        tsec2->create_sid = tsec1->create_sid;
+       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
@@ -2628,6 +3118,11 @@ static int selinux_task_getsid(struct task_struct *p)
        return task_has_perm(current, p, PROCESS__GETSESSION);
 }
 
+static void selinux_task_getsecid(struct task_struct *p, u32 *secid)
+{
+       selinux_get_task_sid(p, secid);
+}
+
 static int selinux_task_setgroups(struct group_info *group_info)
 {
        /* See the comment for setuid above. */
@@ -2647,9 +3142,20 @@ static int selinux_task_setnice(struct task_struct *p, int nice)
 
 static int selinux_task_setioprio(struct task_struct *p, int ioprio)
 {
+       int rc;
+
+       rc = secondary_ops->task_setioprio(p, ioprio);
+       if (rc)
+               return rc;
+
        return task_has_perm(current, p, PROCESS__SETSCHED);
 }
 
+static int selinux_task_getioprio(struct task_struct *p)
+{
+       return task_has_perm(current, p, PROCESS__GETSCHED);
+}
+
 static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim)
 {
        struct rlimit *old_rlim = current->signal->rlim + resource;
@@ -2671,6 +3177,12 @@ static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim
 
 static int selinux_task_setscheduler(struct task_struct *p, int policy, struct sched_param *lp)
 {
+       int rc;
+
+       rc = secondary_ops->task_setscheduler(p, policy, lp);
+       if (rc)
+               return rc;
+
        return task_has_perm(current, p, PROCESS__SETSCHED);
 }
 
@@ -2684,12 +3196,14 @@ static int selinux_task_movememory(struct task_struct *p)
        return task_has_perm(current, p, PROCESS__SETSCHED);
 }
 
-static int selinux_task_kill(struct task_struct *p, struct siginfo *info, int sig)
+static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
+                               int sig, u32 secid)
 {
        u32 perm;
        int rc;
+       struct task_security_struct *tsec;
 
-       rc = secondary_ops->task_kill(p, info, sig);
+       rc = secondary_ops->task_kill(p, info, sig, secid);
        if (rc)
                return rc;
 
@@ -2700,8 +3214,12 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info, int si
                perm = PROCESS__SIGNULL; /* null signal; existence test */
        else
                perm = signal_to_av(sig);
-
-       return task_has_perm(current, p, perm);
+       tsec = p->security;
+       if (secid)
+               rc = avc_has_perm(secid, tsec->sid, SECCLASS_PROCESS, perm, NULL);
+       else
+               rc = task_has_perm(current, p, perm);
+       return rc;
 }
 
 static int selinux_task_prctl(int option,
@@ -2718,11 +3236,7 @@ static int selinux_task_prctl(int option,
 
 static int selinux_task_wait(struct task_struct *p)
 {
-       u32 perm;
-
-       perm = signal_to_av(p->exit_signal);
-
-       return task_has_perm(p, current, perm);
+       return task_has_perm(p, current, PROCESS__SIGCHLD);
 }
 
 static void selinux_task_reparent_to_init(struct task_struct *p)
@@ -2749,12 +3263,13 @@ 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;
 
-       offset = skb->nh.raw - skb->data;
+       offset = skb_network_offset(skb);
        ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph);
        if (ih == NULL)
                goto out;
@@ -2767,6 +3282,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;
@@ -2800,6 +3318,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;
         }
@@ -2810,13 +3344,14 @@ 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;
        struct ipv6hdr _ipv6h, *ip6;
 
-       offset = skb->nh.raw - skb->data;
+       offset = skb_network_offset(skb);
        ip6 = skb_header_pointer(skb, offset, sizeof(_ipv6h), &_ipv6h);
        if (ip6 == NULL)
                goto out;
@@ -2831,6 +3366,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;
@@ -2856,6 +3394,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;
@@ -2867,26 +3417,24 @@ 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 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;
                *addrp = (char *)(src ? &ad->u.net.v4info.saddr :
                                        &ad->u.net.v4info.daddr);
                break;
 
 #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;
                *addrp = (char *)(src ? &ad->u.net.v6info.saddr :
                                        &ad->u.net.v6info.daddr);
                break;
@@ -2895,9 +3443,50 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
                break;
        }
 
+       if (unlikely(ret))
+               printk(KERN_WARNING
+                      "SELinux: failure in selinux_parse_skb(),"
+                      " unable to parse packet\n");
+
        return ret;
 }
 
+/**
+ * selinux_skb_peerlbl_sid - Determine the peer label of a packet
+ * @skb: the packet
+ * @family: protocol family
+ * @sid: the packet's peer label SID
+ *
+ * Description:
+ * Check the various different forms of network peer labeling and determine
+ * the peer label/SID for the packet; most of the magic actually occurs in
+ * the security server function security_net_peersid_cmp().  The function
+ * returns zero if the value in @sid is valid (although it may be SECSID_NULL)
+ * or -EACCES if @sid is invalid due to inconsistencies with the different
+ * peer labels.
+ *
+ */
+static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid)
+{
+       int err;
+       u32 xfrm_sid;
+       u32 nlbl_sid;
+       u32 nlbl_type;
+
+       selinux_skb_xfrm_sid(skb, &xfrm_sid);
+       selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid);
+
+       err = security_net_peersid_resolve(nlbl_sid, nlbl_type, xfrm_sid, sid);
+       if (unlikely(err)) {
+               printk(KERN_WARNING
+                      "SELinux: failure in selinux_skb_peerlbl_sid(),"
+                      " unable to determine packet's peer label\n");
+               return -EACCES;
+       }
+
+       return 0;
+}
+
 /* socket security operations */
 static int socket_has_perm(struct task_struct *task, struct socket *sock,
                           u32 perms)
@@ -2926,12 +3515,14 @@ static int selinux_socket_create(int family, int type,
 {
        int err = 0;
        struct task_security_struct *tsec;
+       u32 newsid;
 
        if (kern)
                goto out;
 
        tsec = current->security;
-       err = avc_has_perm(tsec->sid, tsec->sid,
+       newsid = tsec->sockcreate_sid ? : tsec->sid;
+       err = avc_has_perm(tsec->sid, newsid,
                           socket_type_to_security_class(family, type,
                           protocol), SOCKET__CREATE, NULL);
 
@@ -2939,27 +3530,36 @@ 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;
 
        tsec = current->security;
+       newsid = tsec->sockcreate_sid ? : tsec->sid;
        isec->sclass = socket_type_to_security_class(family, type, protocol);
-       isec->sid = kern ? SECINITSID_KERNEL : tsec->sid;
+       isec->sid = kern ? SECINITSID_KERNEL : newsid;
        isec->initialized = 1;
 
-       return;
+       if (sock->sk) {
+               sksec = sock->sk->sk_security;
+               sksec->sid = isec->sid;
+               sksec->sclass = isec->sclass;
+               err = selinux_netlbl_socket_post_create(sock);
+       }
+
+       return err;
 }
 
 /* Range of port numbers used to automatically bind.
    Need to determine whether we should perform a name_bind
    permission check between the socket and the port number. */
-#define ip_local_port_range_0 sysctl_local_port_range[0]
-#define ip_local_port_range_1 sysctl_local_port_range[1]
 
 static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
 {
@@ -3002,20 +3602,27 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
                        addrp = (char *)&addr6->sin6_addr.s6_addr;
                }
 
-               if (snum&&(snum < max(PROT_SOCK,ip_local_port_range_0) ||
-                          snum > ip_local_port_range_1)) {
-                       err = security_port_sid(sk->sk_family, sk->sk_type,
-                                               sk->sk_protocol, snum, &sid);
-                       if (err)
-                               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, &ad);
-                       if (err)
-                               goto out;
+               if (snum) {
+                       int low, high;
+
+                       inet_get_local_port_range(&low, &high);
+
+                       if (snum < max(PROT_SOCK, low) || snum > high) {
+                               err = security_port_sid(sk->sk_family,
+                                                       sk->sk_type,
+                                                       sk->sk_protocol, snum,
+                                                       &sid);
+                               if (err)
+                                       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, &ad);
+                               if (err)
+                                       goto out;
+                       }
                }
                
                switch(isec->sclass) {
@@ -3026,13 +3633,17 @@ 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;
                }
                
-               err = security_node_sid(family, addrp, addrlen, &sid);
+               err = sel_netnode_sid(addrp, family, &sid);
                if (err)
                        goto out;
                
@@ -3064,16 +3675,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;
@@ -3092,11 +3704,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;
        }
@@ -3133,7 +3747,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,
@@ -3154,7 +3774,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,
@@ -3201,8 +3827,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,
@@ -3227,122 +3854,182 @@ static int selinux_socket_unix_may_send(struct socket *sock,
        return 0;
 }
 
-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)
+static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family,
+                                   u32 peer_sid,
+                                   struct avc_audit_data *ad)
 {
-       int err = 0;
-       u32 netif_perm, node_perm, node_sid, if_sid, recv_perm = 0;
+       int err;
+       u32 if_sid;
+       u32 node_sid;
 
-       if (!skb->dev)
-               goto out;
+       err = sel_netif_sid(ifindex, &if_sid);
+       if (err)
+               return err;
+       err = avc_has_perm(peer_sid, if_sid,
+                          SECCLASS_NETIF, NETIF__INGRESS, ad);
+       if (err)
+               return err;
 
-       err = sel_netif_sids(skb->dev, &if_sid, NULL);
+       err = sel_netnode_sid(addrp, family, &node_sid);
        if (err)
-               goto out;
+               return err;
+       return avc_has_perm(peer_sid, node_sid,
+                           SECCLASS_NODE, NODE__RECVFROM, ad);
+}
+
+static int selinux_sock_rcv_skb_iptables_compat(struct sock *sk,
+                                               struct sk_buff *skb,
+                                               struct avc_audit_data *ad,
+                                               u16 family,
+                                               char *addrp)
+{
+       int err;
+       struct sk_security_struct *sksec = sk->sk_security;
+       u16 sk_class;
+       u32 netif_perm, node_perm, recv_perm;
+       u32 port_sid, node_sid, if_sid, sk_sid;
+
+       sk_sid = sksec->sid;
+       sk_class = sksec->sclass;
 
-       switch (sock_class) {
+       switch (sk_class) {
        case SECCLASS_UDP_SOCKET:
                netif_perm = NETIF__UDP_RECV;
                node_perm = NODE__UDP_RECV;
                recv_perm = UDP_SOCKET__RECV_MSG;
                break;
-       
        case SECCLASS_TCP_SOCKET:
                netif_perm = NETIF__TCP_RECV;
                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;
+               recv_perm = 0;
                break;
        }
 
-       err = avc_has_perm(sock_sid, if_sid, SECCLASS_NETIF, netif_perm, ad);
+       err = sel_netif_sid(skb->iif, &if_sid);
        if (err)
-               goto out;
-       
-       err = security_node_sid(family, addrp, len, &node_sid);
+               return err;
+       err = avc_has_perm(sk_sid, if_sid, SECCLASS_NETIF, netif_perm, ad);
        if (err)
-               goto out;
+               return err;
        
-       err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, ad);
+       err = sel_netnode_sid(addrp, family, &node_sid);
        if (err)
-               goto out;
+               return err;
+       err = avc_has_perm(sk_sid, node_sid, SECCLASS_NODE, node_perm, ad);
+       if (err)
+               return err;
+
+       if (!recv_perm)
+               return 0;
+       err = security_port_sid(sk->sk_family, sk->sk_type,
+                               sk->sk_protocol, ntohs(ad->u.net.sport),
+                               &port_sid);
+       if (unlikely(err)) {
+               printk(KERN_WARNING
+                      "SELinux: failure in"
+                      " selinux_sock_rcv_skb_iptables_compat(),"
+                      " network port label not found\n");
+               return err;
+       }
+       return avc_has_perm(sk_sid, port_sid, sk_class, recv_perm, ad);
+}
 
-       if (recv_perm) {
-               u32 port_sid;
+static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
+                                      struct avc_audit_data *ad,
+                                      u16 family, char *addrp)
+{
+       int err;
+       struct sk_security_struct *sksec = sk->sk_security;
+       u32 peer_sid;
+       u32 sk_sid = sksec->sid;
 
-               err = security_port_sid(sk->sk_family, sk->sk_type,
-                                       sk->sk_protocol, ntohs(ad->u.net.sport),
-                                       &port_sid);
-               if (err)
-                       goto out;
+       if (selinux_compat_net)
+               err = selinux_sock_rcv_skb_iptables_compat(sk, skb, ad,
+                                                          family, addrp);
+       else
+               err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET,
+                                  PACKET__RECV, ad);
+       if (err)
+               return err;
 
-               err = avc_has_perm(sock_sid, port_sid,
-                                  sock_class, recv_perm, ad);
+       if (selinux_policycap_netpeer) {
+               err = selinux_skb_peerlbl_sid(skb, family, &peer_sid);
+               if (err)
+                       return err;
+               err = avc_has_perm(sk_sid, peer_sid,
+                                  SECCLASS_PEER, PEER__RECV, ad);
+       } else {
+               err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, ad);
+               if (err)
+                       return err;
+               err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, ad);
        }
 
-out:
        return err;
 }
 
 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;
+       int err;
+       struct sk_security_struct *sksec = sk->sk_security;
+       u16 family = sk->sk_family;
+       u32 sk_sid = sksec->sid;
        struct avc_audit_data ad;
+       char *addrp;
 
-       family = sk->sk_family;
        if (family != PF_INET && family != PF_INET6)
-               goto out;
+               return 0;
 
        /* 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.netif = skb->iif;
        ad.u.net.family = family;
-
-       err = selinux_parse_skb(skb, &ad, &addrp, &len, 1);
+       err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL);
        if (err)
-               goto out;
+               return err;
 
-       if (selinux_compat_net)
-               err = selinux_sock_rcv_skb_compat(sk, skb, &ad, sock_sid,
-                                                 sock_class, family,
-                                                 addrp, len);
-       else
-               err = avc_has_perm(sock_sid, skb->secmark, SECCLASS_PACKET,
+       /* If any sort of compatibility mode is enabled then handoff processing
+        * to the selinux_sock_rcv_skb_compat() function to deal with the
+        * special handling.  We do this in an attempt to keep this function
+        * as fast and as clean as possible. */
+       if (selinux_compat_net || !selinux_policycap_netpeer)
+               return selinux_sock_rcv_skb_compat(sk, skb, &ad,
+                                                  family, addrp);
+
+       if (netlbl_enabled() || selinux_xfrm_enabled()) {
+               u32 peer_sid;
+
+               err = selinux_skb_peerlbl_sid(skb, family, &peer_sid);
+               if (err)
+                       return err;
+               err = selinux_inet_sys_rcv_skb(skb->iif, addrp, family,
+                                              peer_sid, &ad);
+               if (err)
+                       return err;
+               err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER,
+                                  PEER__RECV, &ad);
+       }
+
+       if (selinux_secmark_enabled()) {
+               err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET,
                                   PACKET__RECV, &ad);
-       if (err)
-               goto out;
+               if (err)
+                       return err;
+       }
 
-       err = selinux_xfrm_sock_rcv_skb(sock_sid, skb);
-out:   
        return err;
 }
 
@@ -3354,24 +4041,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;
        }
@@ -3398,23 +4077,30 @@ out:
        return err;
 }
 
-static int selinux_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata, u32 *seclen)
+static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid)
 {
-       int err = 0;
-       u32 peer_sid = selinux_socket_getpeer_dgram(skb);
+       u32 peer_secid = SECSID_NULL;
+       u16 family;
 
-       if (peer_sid == SECSID_NULL)
-               return -EINVAL;
+       if (sock)
+               family = sock->sk->sk_family;
+       else if (skb && skb->sk)
+               family = skb->sk->sk_family;
+       else
+               goto out;
 
-       err = security_sid_to_context(peer_sid, secdata, seclen);
-       if (err)
-               return err;
+       if (sock && family == PF_UNIX)
+               selinux_get_inode_sid(SOCK_INODE(sock), &peer_secid);
+       else if (skb)
+               selinux_skb_peerlbl_sid(skb, family, &peer_secid);
 
+out:
+       *secid = peer_secid;
+       if (peer_secid == SECSID_NULL)
+               return -EINVAL;
        return 0;
 }
 
-
-
 static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority)
 {
        return sk_alloc_security(sk, family, priority);
@@ -3425,22 +4111,97 @@ 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;
+       newssec->sclass = ssec->sclass;
+
+       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;
+       sksec->sclass = isec->sclass;
+
+       selinux_netlbl_sock_graft(sk, parent);
+}
+
+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;
+
+       err = selinux_skb_peerlbl_sid(skb, sk->sk_family, &peersid);
+       if (err)
+               return err;
+       if (peersid == SECSID_NULL) {
+               req->secid = sksec->sid;
+               req->peer_secid = SECSID_NULL;
+               return 0;
+       }
+
+       err = security_sid_mls_copy(sksec->sid, peersid, &newsid);
+       if (err)
+               return err;
+
+       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. */
 
-       read_lock_bh(&sk->sk_callback_lock);
-       isec = get_sock_isec(sk);
+       /* 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;
 
-       if (isec)
-               sock_sid = isec->sid;
+       selinux_skb_peerlbl_sid(skb, sk->sk_family, &sksec->peer_sid);
+}
 
-       read_unlock_bh(&sk->sk_callback_lock);
-       return sock_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)
@@ -3455,7 +4216,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
                err = -EINVAL;
                goto out;
        }
-       nlh = (struct nlmsghdr *)skb->data;
+       nlh = nlmsg_hdr(skb);
        
        err = selinux_nlmsg_lookup(isec->sclass, nlh->nlmsg_type, &perm);
        if (err) {
@@ -3481,173 +4242,292 @@ 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)
+static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,
+                                      u16 family)
+{
+       char *addrp;
+       u32 peer_sid;
+       struct avc_audit_data ad;
+       u8 secmark_active;
+       u8 peerlbl_active;
+
+       if (!selinux_policycap_netpeer)
+               return NF_ACCEPT;
+
+       secmark_active = selinux_secmark_enabled();
+       peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
+       if (!secmark_active && !peerlbl_active)
+               return NF_ACCEPT;
+
+       AVC_AUDIT_DATA_INIT(&ad, NET);
+       ad.u.net.netif = ifindex;
+       ad.u.net.family = family;
+       if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0)
+               return NF_DROP;
+
+       if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0)
+               return NF_DROP;
+
+       if (peerlbl_active)
+               if (selinux_inet_sys_rcv_skb(ifindex, addrp, family,
+                                            peer_sid, &ad) != 0)
+                       return NF_DROP;
+
+       if (secmark_active)
+               if (avc_has_perm(peer_sid, skb->secmark,
+                                SECCLASS_PACKET, PACKET__FORWARD_IN, &ad))
+                       return NF_DROP;
+
+       return NF_ACCEPT;
+}
+
+static unsigned int selinux_ipv4_forward(unsigned int hooknum,
+                                        struct sk_buff *skb,
+                                        const struct net_device *in,
+                                        const struct net_device *out,
+                                        int (*okfn)(struct sk_buff *))
+{
+       return selinux_ip_forward(skb, in->ifindex, PF_INET);
+}
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static unsigned int selinux_ipv6_forward(unsigned int hooknum,
+                                        struct sk_buff *skb,
+                                        const struct net_device *in,
+                                        const struct net_device *out,
+                                        int (*okfn)(struct sk_buff *))
+{
+       return selinux_ip_forward(skb, in->ifindex, PF_INET6);
+}
+#endif /* IPV6 */
+
+static int selinux_ip_postroute_iptables_compat(struct sock *sk,
+                                               int ifindex,
+                                               struct avc_audit_data *ad,
+                                               u16 family, char *addrp)
 {
        int err;
-       u32 netif_perm, node_perm, node_sid, if_sid, send_perm = 0;
-       
-       err = sel_netif_sids(dev, &if_sid, NULL);
-       if (err)
-               goto out;
+       struct sk_security_struct *sksec = sk->sk_security;
+       u16 sk_class;
+       u32 netif_perm, node_perm, send_perm;
+       u32 port_sid, node_sid, if_sid, sk_sid;
 
-       switch (isec->sclass) {
+       sk_sid = sksec->sid;
+       sk_class = sksec->sclass;
+
+       switch (sk_class) {
        case SECCLASS_UDP_SOCKET:
                netif_perm = NETIF__UDP_SEND;
                node_perm = NODE__UDP_SEND;
                send_perm = UDP_SOCKET__SEND_MSG;
                break;
-       
        case SECCLASS_TCP_SOCKET:
                netif_perm = NETIF__TCP_SEND;
                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;
+               send_perm = 0;
                break;
        }
 
-       err = avc_has_perm(isec->sid, if_sid, SECCLASS_NETIF, netif_perm, ad);
+       err = sel_netif_sid(ifindex, &if_sid);
        if (err)
-               goto out;
+               return err;
+       err = avc_has_perm(sk_sid, if_sid, SECCLASS_NETIF, netif_perm, ad);
+               return err;
                
-       err = security_node_sid(family, addrp, len, &node_sid);
+       err = sel_netnode_sid(addrp, family, &node_sid);
        if (err)
-               goto out;
-       
-       err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, node_perm, ad);
+               return err;
+       err = avc_has_perm(sk_sid, node_sid, SECCLASS_NODE, node_perm, ad);
        if (err)
-               goto out;
+               return err;
 
-       if (send_perm) {
-               u32 port_sid;
-               
-               err = security_port_sid(sk->sk_family,
-                                       sk->sk_type,
-                                       sk->sk_protocol,
-                                       ntohs(ad->u.net.dport),
-                                       &port_sid);
-               if (err)
-                       goto out;
+       if (send_perm != 0)
+               return 0;
 
-               err = avc_has_perm(isec->sid, port_sid, isec->sclass,
-                                  send_perm, ad);
+       err = security_port_sid(sk->sk_family, sk->sk_type,
+                               sk->sk_protocol, ntohs(ad->u.net.dport),
+                               &port_sid);
+       if (unlikely(err)) {
+               printk(KERN_WARNING
+                      "SELinux: failure in"
+                      " selinux_ip_postroute_iptables_compat(),"
+                      " network port label not found\n");
+               return err;
        }
-out:
-       return err;
+       return avc_has_perm(sk_sid, port_sid, sk_class, send_perm, ad);
 }
 
-static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
-                                              struct sk_buff **pskb,
-                                              const struct net_device *in,
-                                              const struct net_device *out,
-                                              int (*okfn)(struct sk_buff *),
-                                              u16 family)
+static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
+                                               int ifindex,
+                                               struct avc_audit_data *ad,
+                                               u16 family,
+                                               char *addrp,
+                                               u8 proto)
 {
-       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 sock *sk = skb->sk;
+       struct sk_security_struct *sksec;
 
-       sk = skb->sk;
-       if (!sk)
-               goto out;
+       if (sk == NULL)
+               return NF_ACCEPT;
+       sksec = sk->sk_security;
 
-       sock = sk->sk_socket;
-       if (!sock)
-               goto out;
+       if (selinux_compat_net) {
+               if (selinux_ip_postroute_iptables_compat(skb->sk, ifindex,
+                                                        ad, family, addrp))
+                       return NF_DROP;
+       } else {
+               if (avc_has_perm(sksec->sid, skb->secmark,
+                                SECCLASS_PACKET, PACKET__SEND, ad))
+                       return NF_DROP;
+       }
 
-       inode = SOCK_INODE(sock);
-       if (!inode)
-               goto out;
+       if (selinux_policycap_netpeer)
+               if (selinux_xfrm_postroute_last(sksec->sid, skb, ad, proto))
+                       return NF_DROP;
 
-       isec = inode->i_security;
+       return NF_ACCEPT;
+}
+
+static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
+                                        u16 family)
+{
+       u32 secmark_perm;
+       u32 peer_sid;
+       struct sock *sk;
+       struct avc_audit_data ad;
+       char *addrp;
+       u8 proto;
+       u8 secmark_active;
+       u8 peerlbl_active;
 
        AVC_AUDIT_DATA_INIT(&ad, NET);
-       ad.u.net.netif = dev->name;
+       ad.u.net.netif = ifindex;
        ad.u.net.family = family;
+       if (selinux_parse_skb(skb, &ad, &addrp, 0, &proto))
+               return NF_DROP;
+
+       /* If any sort of compatibility mode is enabled then handoff processing
+        * to the selinux_ip_postroute_compat() function to deal with the
+        * special handling.  We do this in an attempt to keep this function
+        * as fast and as clean as possible. */
+       if (selinux_compat_net || !selinux_policycap_netpeer)
+               return selinux_ip_postroute_compat(skb, ifindex, &ad,
+                                                  family, addrp, proto);
+
+       /* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec
+        * packet transformation so allow the packet to pass without any checks
+        * since we'll have another chance to perform access control checks
+        * when the packet is on it's final way out.
+        * NOTE: there appear to be some IPv6 multicast cases where skb->dst
+        *       is NULL, in this case go ahead and apply access control. */
+       if (skb->dst != NULL && skb->dst->xfrm != NULL)
+               return NF_ACCEPT;
+
+       secmark_active = selinux_secmark_enabled();
+       peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
+       if (!secmark_active && !peerlbl_active)
+               return NF_ACCEPT;
+
+       /* if the packet is locally generated (skb->sk != NULL) then use the
+        * socket's label as the peer label, otherwise the packet is being
+        * forwarded through this system and we need to fetch the peer label
+        * directly from the packet */
+       sk = skb->sk;
+       if (sk) {
+               struct sk_security_struct *sksec = sk->sk_security;
+               peer_sid = sksec->sid;
+               secmark_perm = PACKET__SEND;
+       } else {
+               if (selinux_skb_peerlbl_sid(skb, family, &peer_sid))
+                               return NF_DROP;
+               secmark_perm = PACKET__FORWARD_OUT;
+       }
 
-       err = selinux_parse_skb(skb, &ad, &addrp, &len, 0);
-       if (err)
-               goto out;
-
-       if (selinux_compat_net)
-               err = selinux_ip_postroute_last_compat(sk, dev, isec, &ad,
-                                                      family, addrp, len);
-       else
-               err = avc_has_perm(isec->sid, skb->secmark, SECCLASS_PACKET,
-                                  PACKET__SEND, &ad);
-
-       if (err)
-               goto out;
+       if (secmark_active)
+               if (avc_has_perm(peer_sid, skb->secmark,
+                                SECCLASS_PACKET, secmark_perm, &ad))
+                       return NF_DROP;
+
+       if (peerlbl_active) {
+               u32 if_sid;
+               u32 node_sid;
+
+               if (sel_netif_sid(ifindex, &if_sid))
+                       return NF_DROP;
+               if (avc_has_perm(peer_sid, if_sid,
+                                SECCLASS_NETIF, NETIF__EGRESS, &ad))
+                       return NF_DROP;
+
+               if (sel_netnode_sid(addrp, family, &node_sid))
+                       return NF_DROP;
+               if (avc_has_perm(peer_sid, node_sid,
+                                SECCLASS_NODE, NODE__SENDTO, &ad))
+                       return NF_DROP;
+       }
 
-       err = selinux_xfrm_postroute_last(isec->sid, skb);
-out:
-       return err ? NF_DROP : NF_ACCEPT;
+       return NF_ACCEPT;
 }
 
-static unsigned int selinux_ipv4_postroute_last(unsigned int hooknum,
-                                               struct sk_buff **pskb,
-                                               const struct net_device *in,
-                                               const struct net_device *out,
-                                               int (*okfn)(struct sk_buff *))
+static unsigned int selinux_ipv4_postroute(unsigned int hooknum,
+                                          struct sk_buff *skb,
+                                          const struct net_device *in,
+                                          const struct net_device *out,
+                                          int (*okfn)(struct sk_buff *))
 {
-       return selinux_ip_postroute_last(hooknum, pskb, in, out, okfn, PF_INET);
+       return selinux_ip_postroute(skb, out->ifindex, PF_INET);
 }
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-
-static unsigned int selinux_ipv6_postroute_last(unsigned int hooknum,
-                                               struct sk_buff **pskb,
-                                               const struct net_device *in,
-                                               const struct net_device *out,
-                                               int (*okfn)(struct sk_buff *))
+static unsigned int selinux_ipv6_postroute(unsigned int hooknum,
+                                          struct sk_buff *skb,
+                                          const struct net_device *in,
+                                          const struct net_device *out,
+                                          int (*okfn)(struct sk_buff *))
 {
-       return selinux_ip_postroute_last(hooknum, pskb, in, out, okfn, PF_INET6);
+       return selinux_ip_postroute(skb, out->ifindex, PF_INET6);
 }
-
 #endif /* IPV6 */
 
 #endif /* CONFIG_NETFILTER */
 
 static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
 {
-       struct task_security_struct *tsec;
-       struct av_decision avd;
        int err;
 
        err = secondary_ops->netlink_send(sk, skb);
        if (err)
                return err;
 
-       tsec = current->security;
-
-       avd.allowed = 0;
-       avc_has_perm_noaudit(tsec->sid, tsec->sid,
-                               SECCLASS_CAPABILITY, ~0, &avd);
-       cap_mask(NETLINK_CB(skb).eff_cap, avd.allowed);
-
        if (policydb_loaded_version >= POLICYDB_VERSION_NLCLASS)
                err = selinux_nlmsg_perm(sk, skb);
 
        return err;
 }
 
-static int selinux_netlink_recv(struct sk_buff *skb)
+static int selinux_netlink_recv(struct sk_buff *skb, int capability)
 {
-       if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN))
-               return -EPERM;
-       return 0;
+       int err;
+       struct avc_audit_data ad;
+
+       err = secondary_ops->netlink_recv(skb, capability);
+       if (err)
+               return err;
+
+       AVC_AUDIT_DATA_INIT(&ad, CAP);
+       ad.u.cap = capability;
+
+       return avc_has_perm(NETLINK_CB(skb).sid, NETLINK_CB(skb).sid,
+                           SECCLASS_CAPABILITY, CAP_TO_MASK(capability), &ad);
 }
 
 static int ipc_alloc_security(struct task_struct *task,
@@ -4094,7 +4974,7 @@ static int selinux_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
 static int selinux_register_security (const char *name, struct security_operations *ops)
 {
        if (secondary_ops != original_ops) {
-               printk(KERN_INFO "%s:  There is already a secondary security "
+               printk(KERN_ERR "%s:  There is already a secondary security "
                       "module registered.\n", __FUNCTION__);
                return -EINVAL;
        }
@@ -4108,19 +4988,6 @@ static int selinux_register_security (const char *name, struct security_operatio
        return 0;
 }
 
-static int selinux_unregister_security (const char *name, struct security_operations *ops)
-{
-       if (ops != secondary_ops) {
-               printk (KERN_INFO "%s:  trying to unregister a security module "
-                       "that is not registered.\n", __FUNCTION__);
-               return -EINVAL;
-       }
-
-       secondary_ops = original_ops;
-
-       return 0;
-}
-
 static void selinux_d_instantiate (struct dentry *dentry, struct inode *inode)
 {
        if (inode)
@@ -4128,11 +4995,12 @@ static void selinux_d_instantiate (struct dentry *dentry, struct inode *inode)
 }
 
 static int selinux_getprocattr(struct task_struct *p,
-                              char *name, void *value, size_t size)
+                              char *name, char **value)
 {
        struct task_security_struct *tsec;
        u32 sid;
        int error;
+       unsigned len;
 
        if (current != p) {
                error = task_has_perm(current, p, PROCESS__GETATTR);
@@ -4150,13 +5018,20 @@ static int selinux_getprocattr(struct task_struct *p,
                sid = tsec->exec_sid;
        else if (!strcmp(name, "fscreate"))
                sid = tsec->create_sid;
+       else if (!strcmp(name, "keycreate"))
+               sid = tsec->keycreate_sid;
+       else if (!strcmp(name, "sockcreate"))
+               sid = tsec->sockcreate_sid;
        else
                return -EINVAL;
 
        if (!sid)
                return 0;
 
-       return selinux_getsecurity(sid, value, size);
+       error = security_sid_to_context(sid, value, &len);
+       if (error)
+               return error;
+       return len;
 }
 
 static int selinux_setprocattr(struct task_struct *p,
@@ -4182,6 +5057,10 @@ static int selinux_setprocattr(struct task_struct *p,
                error = task_has_perm(current, p, PROCESS__SETEXEC);
        else if (!strcmp(name, "fscreate"))
                error = task_has_perm(current, p, PROCESS__SETFSCREATE);
+       else if (!strcmp(name, "keycreate"))
+               error = task_has_perm(current, p, PROCESS__SETKEYCREATE);
+       else if (!strcmp(name, "sockcreate"))
+               error = task_has_perm(current, p, PROCESS__SETSOCKCREATE);
        else if (!strcmp(name, "current"))
                error = task_has_perm(current, p, PROCESS__SETCURRENT);
        else
@@ -4211,6 +5090,13 @@ static int selinux_setprocattr(struct task_struct *p,
                tsec->exec_sid = sid;
        else if (!strcmp(name, "fscreate"))
                tsec->create_sid = sid;
+       else if (!strcmp(name, "keycreate")) {
+               error = may_create_key(sid, p);
+               if (error)
+                       return error;
+               tsec->keycreate_sid = sid;
+       } else if (!strcmp(name, "sockcreate"))
+               tsec->sockcreate_sid = sid;
        else if (!strcmp(name, "current")) {
                struct av_decision avd;
 
@@ -4243,7 +5129,7 @@ static int selinux_setprocattr(struct task_struct *p,
                if (p->ptrace & PT_PTRACED) {
                        error = avc_has_perm_noaudit(tsec->ptrace_sid, sid,
                                                     SECCLASS_PROCESS,
-                                                    PROCESS__PTRACE, &avd);
+                                                    PROCESS__PTRACE, 0, &avd);
                        if (!error)
                                tsec->sid = sid;
                        task_unlock(p);
@@ -4262,6 +5148,21 @@ static int selinux_setprocattr(struct task_struct *p,
        return size;
 }
 
+static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
+{
+       return security_sid_to_context(secid, secdata, seclen);
+}
+
+static int selinux_secctx_to_secid(char *secdata, u32 seclen, u32 *secid)
+{
+       return security_context_to_sid(secdata, seclen, secid);
+}
+
+static void selinux_release_secctx(char *secdata, u32 seclen)
+{
+       kfree(secdata);
+}
+
 #ifdef CONFIG_KEYS
 
 static int selinux_key_alloc(struct key *k, struct task_struct *tsk,
@@ -4275,7 +5176,10 @@ static int selinux_key_alloc(struct key *k, struct task_struct *tsk,
                return -ENOMEM;
 
        ksec->obj = k;
-       ksec->sid = tsec->sid;
+       if (tsec->keycreate_sid)
+               ksec->sid = tsec->keycreate_sid;
+       else
+               ksec->sid = tsec->sid;
        k->security = ksec;
 
        return 0;
@@ -4344,6 +5248,9 @@ static struct security_operations selinux_ops = {
        .sb_statfs =                    selinux_sb_statfs,
        .sb_mount =                     selinux_mount,
        .sb_umount =                    selinux_umount,
+       .sb_get_mnt_opts =              selinux_get_mnt_opts,
+       .sb_set_mnt_opts =              selinux_set_mnt_opts,
+       .sb_clone_mnt_opts =            selinux_sb_clone_mnt_opts,
 
        .inode_alloc_security =         selinux_inode_alloc_security,
        .inode_free_security =          selinux_inode_free_security,
@@ -4366,10 +5273,11 @@ static struct security_operations selinux_ops = {
        .inode_getxattr =               selinux_inode_getxattr,
        .inode_listxattr =              selinux_inode_listxattr,
        .inode_removexattr =            selinux_inode_removexattr,
-       .inode_xattr_getsuffix =        selinux_inode_xattr_getsuffix,
        .inode_getsecurity =            selinux_inode_getsecurity,
        .inode_setsecurity =            selinux_inode_setsecurity,
        .inode_listsecurity =           selinux_inode_listsecurity,
+       .inode_need_killpriv =          selinux_inode_need_killpriv,
+       .inode_killpriv =               selinux_inode_killpriv,
 
        .file_permission =              selinux_file_permission,
        .file_alloc_security =          selinux_file_alloc_security,
@@ -4383,6 +5291,8 @@ static struct security_operations selinux_ops = {
        .file_send_sigiotask =          selinux_file_send_sigiotask,
        .file_receive =                 selinux_file_receive,
 
+       .dentry_open =                  selinux_dentry_open,
+
        .task_create =                  selinux_task_create,
        .task_alloc_security =          selinux_task_alloc_security,
        .task_free_security =           selinux_task_free_security,
@@ -4392,9 +5302,11 @@ static struct security_operations selinux_ops = {
        .task_setpgid =                 selinux_task_setpgid,
        .task_getpgid =                 selinux_task_getpgid,
        .task_getsid =                  selinux_task_getsid,
+       .task_getsecid =                selinux_task_getsecid,
        .task_setgroups =               selinux_task_setgroups,
        .task_setnice =                 selinux_task_setnice,
        .task_setioprio =               selinux_task_setioprio,
+       .task_getioprio =               selinux_task_getioprio,
        .task_setrlimit =               selinux_task_setrlimit,
        .task_setscheduler =            selinux_task_setscheduler,
        .task_getscheduler =            selinux_task_getscheduler,
@@ -4430,13 +5342,16 @@ static struct security_operations selinux_ops = {
        .sem_semop =                    selinux_sem_semop,
 
        .register_security =            selinux_register_security,
-       .unregister_security =          selinux_unregister_security,
 
        .d_instantiate =                selinux_d_instantiate,
 
        .getprocattr =                  selinux_getprocattr,
        .setprocattr =                  selinux_setprocattr,
 
+       .secid_to_secctx =              selinux_secid_to_secctx,
+       .secctx_to_secid =              selinux_secctx_to_secid,
+       .release_secctx =               selinux_release_secctx,
+
         .unix_stream_connect =         selinux_socket_unix_stream_connect,
        .unix_may_send =                selinux_socket_unix_may_send,
 
@@ -4458,7 +5373,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,
@@ -4469,6 +5390,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
@@ -4497,7 +5420,7 @@ static __init int selinux_init(void)
 
        sel_inode_cache = kmem_cache_create("selinux_inode_security",
                                            sizeof(struct inode_security_struct),
-                                           0, SLAB_PANIC, NULL, NULL);
+                                           0, SLAB_PANIC, NULL);
        avc_init();
 
        original_ops = secondary_ops = security_ops;
@@ -4507,17 +5430,17 @@ static __init int selinux_init(void)
                panic("SELinux: Unable to register with kernel.\n");
 
        if (selinux_enforcing) {
-               printk(KERN_INFO "SELinux:  Starting in enforcing mode\n");
+               printk(KERN_DEBUG "SELinux:  Starting in enforcing mode\n");
        } else {
-               printk(KERN_INFO "SELinux:  Starting in permissive mode\n");
+               printk(KERN_DEBUG "SELinux:  Starting in permissive mode\n");
        }
 
 #ifdef CONFIG_KEYS
        /* Add security information to initial keyrings */
-       security_key_alloc(&root_user_keyring, current,
-                          KEY_ALLOC_NOT_IN_QUOTA);
-       security_key_alloc(&root_session_keyring, current,
-                          KEY_ALLOC_NOT_IN_QUOTA);
+       selinux_key_alloc(&root_user_keyring, current,
+                         KEY_ALLOC_NOT_IN_QUOTA);
+       selinux_key_alloc(&root_session_keyring, current,
+                         KEY_ALLOC_NOT_IN_QUOTA);
 #endif
 
        return 0;
@@ -4525,10 +5448,10 @@ static __init int selinux_init(void)
 
 void selinux_complete_init(void)
 {
-       printk(KERN_INFO "SELinux:  Completing initialization.\n");
+       printk(KERN_DEBUG "SELinux:  Completing initialization.\n");
 
        /* Set up any superblocks initialized prior to the policy load. */
-       printk(KERN_INFO "SELinux:  Setting up existing superblocks.\n");
+       printk(KERN_DEBUG "SELinux:  Setting up existing superblocks.\n");
        spin_lock(&sb_lock);
        spin_lock(&sb_security_lock);
 next_sb:
@@ -4560,22 +5483,40 @@ security_initcall(selinux_init);
 
 #if defined(CONFIG_NETFILTER)
 
-static struct nf_hook_ops selinux_ipv4_op = {
-       .hook =         selinux_ipv4_postroute_last,
-       .owner =        THIS_MODULE,
-       .pf =           PF_INET,
-       .hooknum =      NF_IP_POST_ROUTING,
-       .priority =     NF_IP_PRI_SELINUX_LAST,
+static struct nf_hook_ops selinux_ipv4_ops[] = {
+       {
+               .hook =         selinux_ipv4_postroute,
+               .owner =        THIS_MODULE,
+               .pf =           PF_INET,
+               .hooknum =      NF_INET_POST_ROUTING,
+               .priority =     NF_IP_PRI_SELINUX_LAST,
+       },
+       {
+               .hook =         selinux_ipv4_forward,
+               .owner =        THIS_MODULE,
+               .pf =           PF_INET,
+               .hooknum =      NF_INET_FORWARD,
+               .priority =     NF_IP_PRI_SELINUX_FIRST,
+       }
 };
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 
-static struct nf_hook_ops selinux_ipv6_op = {
-       .hook =         selinux_ipv6_postroute_last,
-       .owner =        THIS_MODULE,
-       .pf =           PF_INET6,
-       .hooknum =      NF_IP6_POST_ROUTING,
-       .priority =     NF_IP6_PRI_SELINUX_LAST,
+static struct nf_hook_ops selinux_ipv6_ops[] = {
+       {
+               .hook =         selinux_ipv6_postroute,
+               .owner =        THIS_MODULE,
+               .pf =           PF_INET6,
+               .hooknum =      NF_INET_POST_ROUTING,
+               .priority =     NF_IP6_PRI_SELINUX_LAST,
+       },
+       {
+               .hook =         selinux_ipv6_forward,
+               .owner =        THIS_MODULE,
+               .pf =           PF_INET6,
+               .hooknum =      NF_INET_FORWARD,
+               .priority =     NF_IP6_PRI_SELINUX_FIRST,
+       }
 };
 
 #endif /* IPV6 */
@@ -4583,22 +5524,27 @@ static struct nf_hook_ops selinux_ipv6_op = {
 static int __init selinux_nf_ip_init(void)
 {
        int err = 0;
+       u32 iter;
 
        if (!selinux_enabled)
                goto out;
-               
-       printk(KERN_INFO "SELinux:  Registering netfilter hooks\n");
-       
-       err = nf_register_hook(&selinux_ipv4_op);
-       if (err)
-               panic("SELinux: nf_register_hook for IPv4: error %d\n", err);
 
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       printk(KERN_DEBUG "SELinux:  Registering netfilter hooks\n");
 
-       err = nf_register_hook(&selinux_ipv6_op);
-       if (err)
-               panic("SELinux: nf_register_hook for IPv6: error %d\n", err);
+       for (iter = 0; iter < ARRAY_SIZE(selinux_ipv4_ops); iter++) {
+               err = nf_register_hook(&selinux_ipv4_ops[iter]);
+               if (err)
+                       panic("SELinux: nf_register_hook for IPv4: error %d\n",
+                             err);
+       }
 
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       for (iter = 0; iter < ARRAY_SIZE(selinux_ipv6_ops); iter++) {
+               err = nf_register_hook(&selinux_ipv6_ops[iter]);
+               if (err)
+                       panic("SELinux: nf_register_hook for IPv6: error %d\n",
+                             err);
+       }
 #endif /* IPV6 */
 
 out:
@@ -4610,11 +5556,15 @@ __initcall(selinux_nf_ip_init);
 #ifdef CONFIG_SECURITY_SELINUX_DISABLE
 static void selinux_nf_ip_exit(void)
 {
-       printk(KERN_INFO "SELinux:  Unregistering netfilter hooks\n");
+       u32 iter;
+
+       printk(KERN_DEBUG "SELinux:  Unregistering netfilter hooks\n");
 
-       nf_unregister_hook(&selinux_ipv4_op);
+       for (iter = 0; iter < ARRAY_SIZE(selinux_ipv4_ops); iter++)
+               nf_unregister_hook(&selinux_ipv4_ops[iter]);
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       nf_unregister_hook(&selinux_ipv6_op);
+       for (iter = 0; iter < ARRAY_SIZE(selinux_ipv6_ops); iter++)
+               nf_unregister_hook(&selinux_ipv6_ops[iter]);
 #endif /* IPV6 */
 }
 #endif