CRED: Wrap access to SELinux's task SID
[safe/jmp/linux-2.6] / security / selinux / hooks.c
index 1c864c0..3f3de56 100644 (file)
@@ -9,7 +9,8 @@
  *           James Morris <jmorris@redhat.com>
  *
  *  Copyright (C) 2001,2002 Networks Associates Technology, Inc.
- *  Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ *  Copyright (C) 2003-2008 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ *                                        Eric Paris <eparis@redhat.com>
  *  Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
  *                         <dgoeddel@trustedcs.com>
  *  Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P.
@@ -24,7 +25,7 @@
 
 #include <linux/init.h>
 #include <linux/kernel.h>
-#include <linux/ptrace.h>
+#include <linux/tracehook.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/security.h>
@@ -42,9 +43,7 @@
 #include <linux/fdtable.h>
 #include <linux/namei.h>
 #include <linux/mount.h>
-#include <linux/ext2_fs.h>
 #include <linux/proc_fs.h>
-#include <linux/kd.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/netfilter_ipv6.h>
 #include <linux/tty.h>
@@ -53,7 +52,7 @@
 #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 <linux/uaccess.h>
 #include <asm/ioctls.h>
 #include <asm/atomic.h>
 #include <linux/bitops.h>
@@ -76,6 +75,7 @@
 #include <linux/string.h>
 #include <linux/selinux.h>
 #include <linux/mutex.h>
+#include <linux/posix-timers.h>
 
 #include "avc.h"
 #include "objsec.h"
@@ -104,7 +104,9 @@ int selinux_enforcing;
 
 static int __init enforcing_setup(char *str)
 {
-       selinux_enforcing = simple_strtol(str, NULL, 0);
+       unsigned long enforcing;
+       if (!strict_strtoul(str, 0, &enforcing))
+               selinux_enforcing = enforcing ? 1 : 0;
        return 1;
 }
 __setup("enforcing=", enforcing_setup);
@@ -115,7 +117,9 @@ int selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE;
 
 static int __init selinux_enabled_setup(char *str)
 {
-       selinux_enabled = simple_strtol(str, NULL, 0);
+       unsigned long enabled;
+       if (!strict_strtoul(str, 0, &enabled))
+               selinux_enabled = enabled ? 1 : 0;
        return 1;
 }
 __setup("selinux=", selinux_enabled_setup);
@@ -123,13 +127,11 @@ __setup("selinux=", selinux_enabled_setup);
 int selinux_enabled = 1;
 #endif
 
-/* Original (dummy) security module. */
-static struct security_operations *original_ops;
 
-/* Minimal support for a secondary security module,
-   just to allow the use of the dummy or capability modules.
-   The owlsm module can alternatively be used as a secondary
  module as long as CONFIG_OWLSM_FD is not enabled. */
+/*
+ * Minimal support for a secondary security module,
+ * just to allow the use of the capability module.
+ */
 static struct security_operations *secondary_ops;
 
 /* Lists of inode and superblock security structures initialized
@@ -156,7 +158,7 @@ static int selinux_secmark_enabled(void)
 
 /* Allocate and free functions for each kind of security blob. */
 
-static int task_alloc_security(struct task_struct *task)
+static int cred_alloc_security(struct cred *cred)
 {
        struct task_security_struct *tsec;
 
@@ -165,22 +167,40 @@ static int task_alloc_security(struct task_struct *task)
                return -ENOMEM;
 
        tsec->osid = tsec->sid = SECINITSID_UNLABELED;
-       task->security = tsec;
+       cred->security = tsec;
 
        return 0;
 }
 
-static void task_free_security(struct task_struct *task)
+/*
+ * get the security ID of a task
+ */
+static inline u32 task_sid(const struct task_struct *task)
 {
-       struct task_security_struct *tsec = task->security;
-       task->security = NULL;
-       kfree(tsec);
+       const struct task_security_struct *tsec;
+       u32 sid;
+
+       rcu_read_lock();
+       tsec = __task_cred(task)->security;
+       sid = tsec->sid;
+       rcu_read_unlock();
+       return sid;
+}
+
+/*
+ * get the security ID of the current task
+ */
+static inline u32 current_sid(void)
+{
+       const struct task_security_struct *tsec = current_cred()->security;
+
+       return tsec->sid;
 }
 
 static int inode_alloc_security(struct inode *inode)
 {
-       struct task_security_struct *tsec = current->security;
        struct inode_security_struct *isec;
+       u32 sid = current_sid();
 
        isec = kmem_cache_zalloc(sel_inode_cache, GFP_NOFS);
        if (!isec)
@@ -191,7 +211,7 @@ static int inode_alloc_security(struct inode *inode)
        isec->inode = inode;
        isec->sid = SECINITSID_UNLABELED;
        isec->sclass = SECCLASS_FILE;
-       isec->task_sid = tsec->sid;
+       isec->task_sid = sid;
        inode->i_security = isec;
 
        return 0;
@@ -213,15 +233,15 @@ static void inode_free_security(struct inode *inode)
 
 static int file_alloc_security(struct file *file)
 {
-       struct task_security_struct *tsec = current->security;
        struct file_security_struct *fsec;
+       u32 sid = current_sid();
 
        fsec = kzalloc(sizeof(struct file_security_struct), GFP_KERNEL);
        if (!fsec)
                return -ENOMEM;
 
-       fsec->sid = tsec->sid;
-       fsec->fown_sid = tsec->sid;
+       fsec->sid = sid;
+       fsec->fown_sid = sid;
        file->f_security = fsec;
 
        return 0;
@@ -290,6 +310,7 @@ static void sk_free_security(struct sock *sk)
        struct sk_security_struct *ssec = sk->sk_security;
 
        sk->sk_security = NULL;
+       selinux_netlbl_sk_security_free(ssec);
        kfree(ssec);
 }
 
@@ -323,7 +344,7 @@ enum {
        Opt_rootcontext = 4,
 };
 
-static match_table_t tokens = {
+static const match_table_t tokens = {
        {Opt_context, CONTEXT_STR "%s"},
        {Opt_fscontext, FSCONTEXT_STR "%s"},
        {Opt_defcontext, DEFCONTEXT_STR "%s"},
@@ -335,8 +356,9 @@ static match_table_t tokens = {
 
 static int may_context_mount_sb_relabel(u32 sid,
                        struct superblock_security_struct *sbsec,
-                       struct task_security_struct *tsec)
+                       const struct cred *cred)
 {
+       const struct task_security_struct *tsec = cred->security;
        int rc;
 
        rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
@@ -351,8 +373,9 @@ static int may_context_mount_sb_relabel(u32 sid,
 
 static int may_context_mount_inode_relabel(u32 sid,
                        struct superblock_security_struct *sbsec,
-                       struct task_security_struct *tsec)
+                       const struct cred *cred)
 {
+       const struct task_security_struct *tsec = cred->security;
        int rc;
        rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
                          FILESYSTEM__RELABELFROM, NULL);
@@ -550,8 +573,8 @@ static int bad_option(struct superblock_security_struct *sbsec, char flag,
 static int selinux_set_mnt_opts(struct super_block *sb,
                                struct security_mnt_opts *opts)
 {
+       const struct cred *cred = current_cred();
        int rc = 0, i;
-       struct task_security_struct *tsec = current->security;
        struct superblock_security_struct *sbsec = sb->s_security;
        const char *name = sb->s_type->name;
        struct inode *inode = sbsec->sb->s_root->d_inode;
@@ -594,7 +617,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
         */
        if (sbsec->initialized && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
            && (num_opts == 0))
-               goto out;
+               goto out;
 
        /*
         * parse the mount options, check if they are valid sids.
@@ -677,8 +700,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
 
        /* sets the context of the superblock for the fs being mounted. */
        if (fscontext_sid) {
-
-               rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, tsec);
+               rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, cred);
                if (rc)
                        goto out;
 
@@ -692,12 +714,14 @@ static int selinux_set_mnt_opts(struct super_block *sb,
         */
        if (context_sid) {
                if (!fscontext_sid) {
-                       rc = may_context_mount_sb_relabel(context_sid, sbsec, tsec);
+                       rc = may_context_mount_sb_relabel(context_sid, sbsec,
+                                                         cred);
                        if (rc)
                                goto out;
                        sbsec->sid = context_sid;
                } else {
-                       rc = may_context_mount_inode_relabel(context_sid, sbsec, tsec);
+                       rc = may_context_mount_inode_relabel(context_sid, sbsec,
+                                                            cred);
                        if (rc)
                                goto out;
                }
@@ -709,7 +733,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
        }
 
        if (rootcontext_sid) {
-               rc = may_context_mount_inode_relabel(rootcontext_sid, sbsec, tsec);
+               rc = may_context_mount_inode_relabel(rootcontext_sid, sbsec,
+                                                    cred);
                if (rc)
                        goto out;
 
@@ -727,7 +752,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
 
                if (defcontext_sid != sbsec->def_sid) {
                        rc = may_context_mount_inode_relabel(defcontext_sid,
-                                                            sbsec, tsec);
+                                                            sbsec, cred);
                        if (rc)
                                goto out;
                }
@@ -956,6 +981,62 @@ out_err:
        return rc;
 }
 
+static void selinux_write_opts(struct seq_file *m,
+                              struct security_mnt_opts *opts)
+{
+       int i;
+       char *prefix;
+
+       for (i = 0; i < opts->num_mnt_opts; i++) {
+               char *has_comma = strchr(opts->mnt_opts[i], ',');
+
+               switch (opts->mnt_opts_flags[i]) {
+               case CONTEXT_MNT:
+                       prefix = CONTEXT_STR;
+                       break;
+               case FSCONTEXT_MNT:
+                       prefix = FSCONTEXT_STR;
+                       break;
+               case ROOTCONTEXT_MNT:
+                       prefix = ROOTCONTEXT_STR;
+                       break;
+               case DEFCONTEXT_MNT:
+                       prefix = DEFCONTEXT_STR;
+                       break;
+               default:
+                       BUG();
+               };
+               /* we need a comma before each option */
+               seq_putc(m, ',');
+               seq_puts(m, prefix);
+               if (has_comma)
+                       seq_putc(m, '\"');
+               seq_puts(m, opts->mnt_opts[i]);
+               if (has_comma)
+                       seq_putc(m, '\"');
+       }
+}
+
+static int selinux_sb_show_options(struct seq_file *m, struct super_block *sb)
+{
+       struct security_mnt_opts opts;
+       int rc;
+
+       rc = selinux_get_mnt_opts(sb, &opts);
+       if (rc) {
+               /* before policy load we may get EINVAL, don't show anything */
+               if (rc == -EINVAL)
+                       rc = 0;
+               return rc;
+       }
+
+       selinux_write_opts(m, &opts);
+
+       security_free_mnt_opts(&opts);
+
+       return rc;
+}
+
 static inline u16 inode_mode_to_security_class(umode_t mode)
 {
        switch (mode & S_IFMT) {
@@ -1234,7 +1315,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                /* Default to the fs superblock SID. */
                isec->sid = sbsec->sid;
 
-               if (sbsec->proc) {
+               if (sbsec->proc && !S_ISLNK(inode->i_mode)) {
                        struct proc_inode *proci = PROC_I(inode);
                        if (proci->pde) {
                                isec->sclass = inode_mode_to_security_class(inode->i_mode);
@@ -1286,18 +1367,23 @@ static inline u32 signal_to_av(int sig)
        return perm;
 }
 
-/* Check permission betweeen a pair of tasks, e.g. signal checks,
-   fork check, ptrace check, etc. */
-static int task_has_perm(struct task_struct *tsk1,
-                        struct task_struct *tsk2,
+/*
+ * Check permission betweeen a pair of tasks, e.g. signal checks,
+ * fork check, ptrace check, etc.
+ * tsk1 is the actor and tsk2 is the target
+ */
+static int task_has_perm(const struct task_struct *tsk1,
+                        const struct task_struct *tsk2,
                         u32 perms)
 {
-       struct task_security_struct *tsec1, *tsec2;
+       const struct task_security_struct *__tsec1, *__tsec2;
+       u32 sid1, sid2;
 
-       tsec1 = tsk1->security;
-       tsec2 = tsk2->security;
-       return avc_has_perm(tsec1->sid, tsec2->sid,
-                           SECCLASS_PROCESS, perms, NULL);
+       rcu_read_lock();
+       __tsec1 = __task_cred(tsk1)->security;  sid1 = __tsec1->sid;
+       __tsec2 = __task_cred(tsk2)->security;  sid2 = __tsec2->sid;
+       rcu_read_unlock();
+       return avc_has_perm(sid1, sid2, SECCLASS_PROCESS, perms, NULL);
 }
 
 #if CAP_LAST_CAP > 63
@@ -1306,14 +1392,14 @@ static int task_has_perm(struct task_struct *tsk1,
 
 /* Check whether a task is allowed to use a capability. */
 static int task_has_capability(struct task_struct *tsk,
-                              int cap)
+                              int cap, int audit)
 {
-       struct task_security_struct *tsec;
        struct avc_audit_data ad;
+       struct av_decision avd;
        u16 sclass;
+       u32 sid = task_sid(tsk);
        u32 av = CAP_TO_MASK(cap);
-
-       tsec = tsk->security;
+       int rc;
 
        AVC_AUDIT_DATA_INIT(&ad, CAP);
        ad.tsk = tsk;
@@ -1331,18 +1417,20 @@ static int task_has_capability(struct task_struct *tsk,
                       "SELinux:  out of range capability %d\n", cap);
                BUG();
        }
-       return avc_has_perm(tsec->sid, tsec->sid, sclass, av, &ad);
+
+       rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd);
+       if (audit == SECURITY_CAP_AUDIT)
+               avc_audit(sid, sid, sclass, av, &avd, rc, &ad);
+       return rc;
 }
 
 /* Check whether a task is allowed to use a system operation. */
 static int task_has_system(struct task_struct *tsk,
                           u32 perms)
 {
-       struct task_security_struct *tsec;
-
-       tsec = tsk->security;
+       u32 sid = task_sid(tsk);
 
-       return avc_has_perm(tsec->sid, SECINITSID_KERNEL,
+       return avc_has_perm(sid, SECINITSID_KERNEL,
                            SECCLASS_SYSTEM, perms, NULL);
 }
 
@@ -1354,14 +1442,14 @@ static int inode_has_perm(struct task_struct *tsk,
                          u32 perms,
                          struct avc_audit_data *adp)
 {
-       struct task_security_struct *tsec;
        struct inode_security_struct *isec;
        struct avc_audit_data ad;
+       u32 sid;
 
        if (unlikely(IS_PRIVATE(inode)))
                return 0;
 
-       tsec = tsk->security;
+       sid = task_sid(tsk);
        isec = inode->i_security;
 
        if (!adp) {
@@ -1370,7 +1458,7 @@ static int inode_has_perm(struct task_struct *tsk,
                ad.u.fs.inode = inode;
        }
 
-       return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, adp);
+       return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp);
 }
 
 /* Same as inode_has_perm, but pass explicit audit data containing
@@ -1401,17 +1489,17 @@ static int file_has_perm(struct task_struct *tsk,
                                struct file *file,
                                u32 av)
 {
-       struct task_security_struct *tsec = tsk->security;
        struct file_security_struct *fsec = file->f_security;
        struct inode *inode = file->f_path.dentry->d_inode;
        struct avc_audit_data ad;
+       u32 sid = task_sid(tsk);
        int rc;
 
        AVC_AUDIT_DATA_INIT(&ad, FS);
        ad.u.fs.path = file->f_path;
 
-       if (tsec->sid != fsec->sid) {
-               rc = avc_has_perm(tsec->sid, fsec->sid,
+       if (sid != fsec->sid) {
+               rc = avc_has_perm(sid, fsec->sid,
                                  SECCLASS_FD,
                                  FD__USE,
                                  &ad);
@@ -1431,36 +1519,36 @@ static int may_create(struct inode *dir,
                      struct dentry *dentry,
                      u16 tclass)
 {
-       struct task_security_struct *tsec;
+       const struct cred *cred = current_cred();
+       const struct task_security_struct *tsec = cred->security;
        struct inode_security_struct *dsec;
        struct superblock_security_struct *sbsec;
-       u32 newsid;
+       u32 sid, newsid;
        struct avc_audit_data ad;
        int rc;
 
-       tsec = current->security;
        dsec = dir->i_security;
        sbsec = dir->i_sb->s_security;
 
+       sid = tsec->sid;
+       newsid = tsec->create_sid;
+
        AVC_AUDIT_DATA_INIT(&ad, FS);
        ad.u.fs.path.dentry = dentry;
 
-       rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR,
+       rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR,
                          DIR__ADD_NAME | DIR__SEARCH,
                          &ad);
        if (rc)
                return rc;
 
-       if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
-               newsid = tsec->create_sid;
-       } else {
-               rc = security_transition_sid(tsec->sid, dsec->sid, tclass,
-                                            &newsid);
+       if (!newsid || sbsec->behavior == SECURITY_FS_USE_MNTPOINT) {
+               rc = security_transition_sid(sid, dsec->sid, tclass, &newsid);
                if (rc)
                        return rc;
        }
 
-       rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, &ad);
+       rc = avc_has_perm(sid, newsid, tclass, FILE__CREATE, &ad);
        if (rc)
                return rc;
 
@@ -1473,11 +1561,9 @@ static int may_create(struct inode *dir,
 static int may_create_key(u32 ksid,
                          struct task_struct *ctx)
 {
-       struct task_security_struct *tsec;
-
-       tsec = ctx->security;
+       u32 sid = task_sid(ctx);
 
-       return avc_has_perm(tsec->sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL);
+       return avc_has_perm(sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL);
 }
 
 #define MAY_LINK       0
@@ -1490,13 +1576,12 @@ static int may_link(struct inode *dir,
                    int kind)
 
 {
-       struct task_security_struct *tsec;
        struct inode_security_struct *dsec, *isec;
        struct avc_audit_data ad;
+       u32 sid = current_sid();
        u32 av;
        int rc;
 
-       tsec = current->security;
        dsec = dir->i_security;
        isec = dentry->d_inode->i_security;
 
@@ -1505,7 +1590,7 @@ static int may_link(struct inode *dir,
 
        av = DIR__SEARCH;
        av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME);
-       rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, av, &ad);
+       rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR, av, &ad);
        if (rc)
                return rc;
 
@@ -1525,7 +1610,7 @@ static int may_link(struct inode *dir,
                return 0;
        }
 
-       rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, av, &ad);
+       rc = avc_has_perm(sid, isec->sid, isec->sclass, av, &ad);
        return rc;
 }
 
@@ -1534,14 +1619,13 @@ static inline int may_rename(struct inode *old_dir,
                             struct inode *new_dir,
                             struct dentry *new_dentry)
 {
-       struct task_security_struct *tsec;
        struct inode_security_struct *old_dsec, *new_dsec, *old_isec, *new_isec;
        struct avc_audit_data ad;
+       u32 sid = current_sid();
        u32 av;
        int old_is_dir, new_is_dir;
        int rc;
 
-       tsec = current->security;
        old_dsec = old_dir->i_security;
        old_isec = old_dentry->d_inode->i_security;
        old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
@@ -1550,16 +1634,16 @@ static inline int may_rename(struct inode *old_dir,
        AVC_AUDIT_DATA_INIT(&ad, FS);
 
        ad.u.fs.path.dentry = old_dentry;
-       rc = avc_has_perm(tsec->sid, old_dsec->sid, SECCLASS_DIR,
+       rc = avc_has_perm(sid, old_dsec->sid, SECCLASS_DIR,
                          DIR__REMOVE_NAME | DIR__SEARCH, &ad);
        if (rc)
                return rc;
-       rc = avc_has_perm(tsec->sid, old_isec->sid,
+       rc = avc_has_perm(sid, old_isec->sid,
                          old_isec->sclass, FILE__RENAME, &ad);
        if (rc)
                return rc;
        if (old_is_dir && new_dir != old_dir) {
-               rc = avc_has_perm(tsec->sid, old_isec->sid,
+               rc = avc_has_perm(sid, old_isec->sid,
                                  old_isec->sclass, DIR__REPARENT, &ad);
                if (rc)
                        return rc;
@@ -1569,13 +1653,13 @@ static inline int may_rename(struct inode *old_dir,
        av = DIR__ADD_NAME | DIR__SEARCH;
        if (new_dentry->d_inode)
                av |= DIR__REMOVE_NAME;
-       rc = avc_has_perm(tsec->sid, new_dsec->sid, SECCLASS_DIR, av, &ad);
+       rc = avc_has_perm(sid, new_dsec->sid, SECCLASS_DIR, av, &ad);
        if (rc)
                return rc;
        if (new_dentry->d_inode) {
                new_isec = new_dentry->d_inode->i_security;
                new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode);
-               rc = avc_has_perm(tsec->sid, new_isec->sid,
+               rc = avc_has_perm(sid, new_isec->sid,
                                  new_isec->sclass,
                                  (new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad);
                if (rc)
@@ -1591,13 +1675,11 @@ static int superblock_has_perm(struct task_struct *tsk,
                               u32 perms,
                               struct avc_audit_data *ad)
 {
-       struct task_security_struct *tsec;
        struct superblock_security_struct *sbsec;
+       u32 sid = task_sid(tsk);
 
-       tsec = tsk->security;
        sbsec = sb->s_security;
-       return avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
-                           perms, ad);
+       return avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, perms, ad);
 }
 
 /* Convert a Linux mode and permission mask to an access vector. */
@@ -1628,15 +1710,39 @@ static inline u32 file_mask_to_av(int mode, int mask)
        return av;
 }
 
+/* Convert a Linux file to an access vector. */
+static inline u32 file_to_av(struct file *file)
+{
+       u32 av = 0;
+
+       if (file->f_mode & FMODE_READ)
+               av |= FILE__READ;
+       if (file->f_mode & FMODE_WRITE) {
+               if (file->f_flags & O_APPEND)
+                       av |= FILE__APPEND;
+               else
+                       av |= FILE__WRITE;
+       }
+       if (!av) {
+               /*
+                * Special file opened with flags 3 for ioctl-only use.
+                */
+               av = FILE__IOCTL;
+       }
+
+       return av;
+}
+
 /*
- * Convert a file mask to an access vector and include the correct open
+ * Convert a file to an access vector and include the correct open
  * open permission.
  */
-static inline u32 open_file_mask_to_av(int mode, int mask)
+static inline u32 open_file_to_av(struct file *file)
 {
-       u32 av = file_mask_to_av(mode, mask);
+       u32 av = file_to_av(file);
 
        if (selinux_policycap_openperm) {
+               mode_t mode = file->f_path.dentry->d_inode->i_mode;
                /*
                 * lnk files and socks do not really have an 'open'
                 */
@@ -1652,45 +1758,40 @@ static inline u32 open_file_mask_to_av(int mode, int mask)
                        av |= DIR__OPEN;
                else
                        printk(KERN_ERR "SELinux: WARNING: inside %s with "
-                               "unknown mode:%x\n", __func__, mode);
+                               "unknown mode:%o\n", __func__, mode);
        }
        return av;
 }
 
-/* Convert a Linux file to an access vector. */
-static inline u32 file_to_av(struct file *file)
+/* Hook functions begin here. */
+
+static int selinux_ptrace_may_access(struct task_struct *child,
+                                    unsigned int mode)
 {
-       u32 av = 0;
+       int rc;
 
-       if (file->f_mode & FMODE_READ)
-               av |= FILE__READ;
-       if (file->f_mode & FMODE_WRITE) {
-               if (file->f_flags & O_APPEND)
-                       av |= FILE__APPEND;
-               else
-                       av |= FILE__WRITE;
-       }
-       if (!av) {
-               /*
-                * Special file opened with flags 3 for ioctl-only use.
-                */
-               av = FILE__IOCTL;
+       rc = secondary_ops->ptrace_may_access(child, mode);
+       if (rc)
+               return rc;
+
+       if (mode == PTRACE_MODE_READ) {
+               u32 sid = current_sid();
+               u32 csid = task_sid(child);
+               return avc_has_perm(sid, csid, SECCLASS_FILE, FILE__READ, NULL);
        }
 
-       return av;
+       return task_has_perm(current, child, PROCESS__PTRACE);
 }
 
-/* Hook functions begin here. */
-
-static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
+static int selinux_ptrace_traceme(struct task_struct *parent)
 {
        int rc;
 
-       rc = secondary_ops->ptrace(parent, child);
+       rc = secondary_ops->ptrace_traceme(parent);
        if (rc)
                return rc;
 
-       return task_has_perm(parent, child, PROCESS__PTRACE);
+       return task_has_perm(parent, current, PROCESS__PTRACE);
 }
 
 static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
@@ -1705,33 +1806,35 @@ static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
        return secondary_ops->capget(target, effective, inheritable, permitted);
 }
 
-static int selinux_capset_check(struct task_struct *target, kernel_cap_t *effective,
-                               kernel_cap_t *inheritable, kernel_cap_t *permitted)
+static int selinux_capset_check(const kernel_cap_t *effective,
+                               const kernel_cap_t *inheritable,
+                               const kernel_cap_t *permitted)
 {
        int error;
 
-       error = secondary_ops->capset_check(target, effective, inheritable, permitted);
+       error = secondary_ops->capset_check(effective, inheritable, permitted);
        if (error)
                return error;
 
-       return task_has_perm(current, target, PROCESS__SETCAP);
+       return task_has_perm(current, current, PROCESS__SETCAP);
 }
 
-static void selinux_capset_set(struct task_struct *target, kernel_cap_t *effective,
-                              kernel_cap_t *inheritable, kernel_cap_t *permitted)
+static void selinux_capset_set(const kernel_cap_t *effective,
+                              const kernel_cap_t *inheritable,
+                              const kernel_cap_t *permitted)
 {
-       secondary_ops->capset_set(target, effective, inheritable, permitted);
+       secondary_ops->capset_set(effective, inheritable, permitted);
 }
 
-static int selinux_capable(struct task_struct *tsk, int cap)
+static int selinux_capable(struct task_struct *tsk, int cap, int audit)
 {
        int rc;
 
-       rc = secondary_ops->capable(tsk, cap);
+       rc = secondary_ops->capable(tsk, cap, audit);
        if (rc)
                return rc;
 
-       return task_has_capability(tsk, cap);
+       return task_has_capability(tsk, cap, audit);
 }
 
 static int selinux_sysctl_get_sid(ctl_table *table, u16 tclass, u32 *sid)
@@ -1779,15 +1882,14 @@ static int selinux_sysctl(ctl_table *table, int op)
 {
        int error = 0;
        u32 av;
-       struct task_security_struct *tsec;
-       u32 tsid;
+       u32 tsid, sid;
        int rc;
 
        rc = secondary_ops->sysctl(table, op);
        if (rc)
                return rc;
 
-       tsec = current->security;
+       sid = current_sid();
 
        rc = selinux_sysctl_get_sid(table, (op == 0001) ?
                                    SECCLASS_DIR : SECCLASS_FILE, &tsid);
@@ -1799,7 +1901,7 @@ static int selinux_sysctl(ctl_table *table, int op)
        /* The op values are "defined" in sysctl.c, thereby creating
         * a bad coupling between this module and sysctl.c */
        if (op == 001) {
-               error = avc_has_perm(tsec->sid, tsid,
+               error = avc_has_perm(sid, tsid,
                                     SECCLASS_DIR, DIR__SEARCH, NULL);
        } else {
                av = 0;
@@ -1808,7 +1910,7 @@ static int selinux_sysctl(ctl_table *table, int op)
                if (op & 002)
                        av |= FILE__WRITE;
                if (av)
-                       error = avc_has_perm(tsec->sid, tsid,
+                       error = avc_has_perm(sid, tsid,
                                             SECCLASS_FILE, av, NULL);
        }
 
@@ -1894,38 +1996,14 @@ static int selinux_syslog(int type)
 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;
-
-       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),
-                                         0,
-                                         NULL);
 
+       rc = selinux_capable(current, CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT);
        if (rc == 0)
                cap_sys_admin = 1;
 
        return __vm_enough_memory(mm, pages, cap_sys_admin);
 }
 
-/**
- * task_tracer_task - return the task that is tracing the given task
- * @task:              task to consider
- *
- * Returns NULL if noone is tracing @task, or the &struct task_struct
- * pointer to its tracer.
- *
- * Must be called under rcu_read_lock().
- */
-static struct task_struct *task_tracer_task(struct task_struct *task)
-{
-       if (task->ptrace & PT_PTRACED)
-               return rcu_dereference(task->parent);
-       return NULL;
-}
-
 /* binprm security operations */
 
 static int selinux_bprm_alloc_security(struct linux_binprm *bprm)
@@ -1962,7 +2040,7 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
        if (bsec->set)
                return 0;
 
-       tsec = current->security;
+       tsec = current_security();
        isec = inode->i_security;
 
        /* Default to the current task SID. */
@@ -2027,14 +2105,19 @@ static int selinux_bprm_check_security(struct linux_binprm *bprm)
 
 static int selinux_bprm_secureexec(struct linux_binprm *bprm)
 {
-       struct task_security_struct *tsec = current->security;
+       const struct cred *cred = current_cred();
+       const struct task_security_struct *tsec = cred->security;
+       u32 sid, osid;
        int atsecure = 0;
 
-       if (tsec->osid != tsec->sid) {
+       sid = tsec->sid;
+       osid = tsec->osid;
+
+       if (osid != sid) {
                /* Enable secure mode for SIDs transitions unless
                   the noatsecure permission is granted between
                   the two SIDs, i.e. ahp returns 0. */
-               atsecure = avc_has_perm(tsec->osid, tsec->sid,
+               atsecure = avc_has_perm(osid, sid,
                                         SECCLASS_PROCESS,
                                         PROCESS__NOATSECURE, NULL);
        }
@@ -2061,26 +2144,27 @@ static inline void flush_unauthorized_files(struct files_struct *files)
        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);
-               if (file) {
+               if (!list_empty(&tty->tty_files)) {
+                       struct inode *inode;
+
                        /* Revalidate access to controlling tty.
                           Use inode_has_perm on the tty inode directly rather
                           than using file_has_perm, as this particular open
                           file may belong to another process and we are only
                           interested in the inode-based check here. */
-                       struct inode *inode = file->f_path.dentry->d_inode;
+                       file = list_first_entry(&tty->tty_files, struct file, f_u.fu_list);
+                       inode = file->f_path.dentry->d_inode;
                        if (inode_has_perm(current, inode,
                                           FILE__READ | FILE__WRITE, NULL)) {
                                drop_tty = 1;
                        }
                }
                file_list_unlock();
+               tty_kref_put(tty);
        }
-       mutex_unlock(&tty_mutex);
        /* Reset controlling tty. */
        if (drop_tty)
                no_tty();
@@ -2150,7 +2234,7 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
 
        secondary_ops->bprm_apply_creds(bprm, unsafe);
 
-       tsec = current->security;
+       tsec = current_security();
 
        bsec = bprm->security;
        sid = bsec->sid;
@@ -2177,9 +2261,9 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
                        u32 ptsid = 0;
 
                        rcu_read_lock();
-                       tracer = task_tracer_task(current);
+                       tracer = tracehook_tracer_task(current);
                        if (likely(tracer != NULL)) {
-                               sec = tracer->security;
+                               sec = __task_cred(tracer)->security;
                                ptsid = sec->sid;
                        }
                        rcu_read_unlock();
@@ -2206,9 +2290,11 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
        struct rlimit *rlim, *initrlim;
        struct itimerval itimer;
        struct bprm_security_struct *bsec;
+       struct sighand_struct *psig;
        int rc, i;
+       unsigned long flags;
 
-       tsec = current->security;
+       tsec = current_security();
        bsec = bprm->security;
 
        if (bsec->unsafe) {
@@ -2262,18 +2348,17 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
                        initrlim = init_task.signal->rlim+i;
                        rlim->rlim_cur = min(rlim->rlim_max, initrlim->rlim_cur);
                }
-               if (current->signal->rlim[RLIMIT_CPU].rlim_cur != RLIM_INFINITY) {
-                       /*
-                        * This will cause RLIMIT_CPU calculations
-                        * to be refigured.
-                        */
-                       current->it_prof_expires = jiffies_to_cputime(1);
-               }
+               update_rlimit_cpu(rlim->rlim_cur);
        }
 
        /* Wake up the parent if it is waiting so that it can
           recheck wait permission to the new task SID. */
+       read_lock_irq(&tasklist_lock);
+       psig = current->parent->sighand;
+       spin_lock_irqsave(&psig->siglock, flags);
        wake_up_interruptible(&current->parent->signal->wait_chldexit);
+       spin_unlock_irqrestore(&psig->siglock, flags);
+       read_unlock_irq(&tasklist_lock);
 }
 
 /* superblock security operations */
@@ -2449,21 +2534,22 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
                                       char **name, void **value,
                                       size_t *len)
 {
-       struct task_security_struct *tsec;
+       const struct cred *cred = current_cred();
+       const struct task_security_struct *tsec = cred->security;
        struct inode_security_struct *dsec;
        struct superblock_security_struct *sbsec;
-       u32 newsid, clen;
+       u32 sid, newsid, clen;
        int rc;
        char *namep = NULL, *context;
 
-       tsec = current->security;
        dsec = dir->i_security;
        sbsec = dir->i_sb->s_security;
 
-       if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
-               newsid = tsec->create_sid;
-       } else {
-               rc = security_transition_sid(tsec->sid, dsec->sid,
+       sid = tsec->sid;
+       newsid = tsec->create_sid;
+
+       if (!newsid || sbsec->behavior == SECURITY_FS_USE_MNTPOINT) {
+               rc = security_transition_sid(sid, dsec->sid,
                                             inode_mode_to_security_class(inode->i_mode),
                                             &newsid);
                if (rc) {
@@ -2495,7 +2581,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
        }
 
        if (value && len) {
-               rc = security_sid_to_context(newsid, &context, &clen);
+               rc = security_sid_to_context_force(newsid, &context, &clen);
                if (rc) {
                        kfree(namep);
                        return rc;
@@ -2579,12 +2665,11 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *na
        return dentry_has_perm(current, NULL, dentry, FILE__READ);
 }
 
-static int selinux_inode_permission(struct inode *inode, int mask,
-                                   struct nameidata *nd)
+static int selinux_inode_permission(struct inode *inode, int mask)
 {
        int rc;
 
-       rc = secondary_ops->inode_permission(inode, mask, nd);
+       rc = secondary_ops->inode_permission(inode, mask);
        if (rc)
                return rc;
 
@@ -2594,7 +2679,7 @@ static int selinux_inode_permission(struct inode *inode, int mask,
        }
 
        return inode_has_perm(current, inode,
-                              open_file_mask_to_av(inode->i_mode, mask), NULL);
+                             file_mask_to_av(inode->i_mode, mask), NULL);
 }
 
 static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
@@ -2642,12 +2727,11 @@ static int selinux_inode_setotherxattr(struct dentry *dentry, const char *name)
 static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
                                  const void *value, size_t size, int flags)
 {
-       struct task_security_struct *tsec = current->security;
        struct inode *inode = dentry->d_inode;
        struct inode_security_struct *isec = inode->i_security;
        struct superblock_security_struct *sbsec;
        struct avc_audit_data ad;
-       u32 newsid;
+       u32 newsid, sid = current_sid();
        int rc = 0;
 
        if (strcmp(name, XATTR_NAME_SELINUX))
@@ -2663,21 +2747,26 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
        AVC_AUDIT_DATA_INIT(&ad, FS);
        ad.u.fs.path.dentry = dentry;
 
-       rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
+       rc = avc_has_perm(sid, isec->sid, isec->sclass,
                          FILE__RELABELFROM, &ad);
        if (rc)
                return rc;
 
        rc = security_context_to_sid(value, size, &newsid);
+       if (rc == -EINVAL) {
+               if (!capable(CAP_MAC_ADMIN))
+                       return rc;
+               rc = security_context_to_sid_force(value, size, &newsid);
+       }
        if (rc)
                return rc;
 
-       rc = avc_has_perm(tsec->sid, newsid, isec->sclass,
+       rc = avc_has_perm(sid, newsid, isec->sclass,
                          FILE__RELABELTO, &ad);
        if (rc)
                return rc;
 
-       rc = security_validate_transition(isec->sid, newsid, tsec->sid,
+       rc = security_validate_transition(isec->sid, newsid, sid,
                                          isec->sclass);
        if (rc)
                return rc;
@@ -2690,7 +2779,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
 }
 
 static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
-                                        const void *value, size_t size,
+                                       const void *value, size_t size,
                                        int flags)
 {
        struct inode *inode = dentry->d_inode;
@@ -2703,10 +2792,11 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
                return;
        }
 
-       rc = security_context_to_sid(value, size, &newsid);
+       rc = security_context_to_sid_force(value, size, &newsid);
        if (rc) {
-               printk(KERN_WARNING "%s:  unable to obtain SID for context "
-                      "%s, rc=%d\n", __func__, (char *)value, -rc);
+               printk(KERN_ERR "SELinux:  unable to map context to SID"
+                      "for (%s, %lu), rc=%d\n",
+                      inode->i_sb->s_id, inode->i_ino, -rc);
                return;
        }
 
@@ -2735,9 +2825,7 @@ static int selinux_inode_removexattr(struct dentry *dentry, const char *name)
 }
 
 /*
- * 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
- * canonicalize the value to be finally returned to the user.
+ * Copy the inode security context value to the user.
  *
  * Permission check is handled by selinux_inode_getxattr hook.
  */
@@ -2751,7 +2839,21 @@ static int selinux_inode_getsecurity(const struct inode *inode, const char *name
        if (strcmp(name, XATTR_SELINUX_SUFFIX))
                return -EOPNOTSUPP;
 
-       error = security_sid_to_context(isec->sid, &context, &size);
+       /*
+        * If the caller has CAP_MAC_ADMIN, then get the raw context
+        * value even if it is not defined by current policy; otherwise,
+        * use the in-core value under current policy.
+        * Use the non-auditing forms of the permission checks since
+        * getxattr may be called by unprivileged processes commonly
+        * and lack of permission just means that we fall back to the
+        * in-core context value, not a denial.
+        */
+       error = selinux_capable(current, CAP_MAC_ADMIN, SECURITY_CAP_NOAUDIT);
+       if (!error)
+               error = security_sid_to_context_force(isec->sid, &context,
+                                                     &size);
+       else
+               error = security_sid_to_context(isec->sid, &context, &size);
        if (error)
                return error;
        error = size;
@@ -2836,16 +2938,16 @@ static int selinux_revalidate_file_permission(struct file *file, int 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;
+       u32 sid = current_sid();
 
        if (!mask) {
                /* No permission to check.  Existence test. */
                return 0;
        }
 
-       if (tsec->sid == fsec->sid && fsec->isid == isec->sid
+       if (sid == fsec->sid && fsec->isid == isec->sid
            && fsec->pseqno == avc_policy_seqno())
                return selinux_netlbl_inode_permission(inode, mask);
 
@@ -2865,46 +2967,16 @@ static void selinux_file_free_security(struct file *file)
 static int selinux_file_ioctl(struct file *file, unsigned int cmd,
                              unsigned long arg)
 {
-       int error = 0;
-
-       switch (cmd) {
-       case FIONREAD:
-       /* fall through */
-       case FIBMAP:
-       /* fall through */
-       case FIGETBSZ:
-       /* fall through */
-       case EXT2_IOC_GETFLAGS:
-       /* fall through */
-       case EXT2_IOC_GETVERSION:
-               error = file_has_perm(current, file, FILE__GETATTR);
-               break;
-
-       case EXT2_IOC_SETFLAGS:
-       /* fall through */
-       case EXT2_IOC_SETVERSION:
-               error = file_has_perm(current, file, FILE__SETATTR);
-               break;
-
-       /* sys_ioctl() checks */
-       case FIONBIO:
-       /* fall through */
-       case FIOASYNC:
-               error = file_has_perm(current, file, 0);
-               break;
+       u32 av = 0;
 
-       case KDSKBENT:
-       case KDSKBSENT:
-               error = task_has_capability(current, CAP_SYS_TTY_CONFIG);
-               break;
+       if (_IOC_DIR(cmd) & _IOC_WRITE)
+               av |= FILE__WRITE;
+       if (_IOC_DIR(cmd) & _IOC_READ)
+               av |= FILE__READ;
+       if (!av)
+               av = FILE__IOCTL;
 
-       /* default case assumes that the command will go
-        * to the file's ioctl() function.
-        */
-       default:
-               error = file_has_perm(current, file, FILE__IOCTL);
-       }
-       return error;
+       return file_has_perm(current, file, av);
 }
 
 static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
@@ -2943,7 +3015,7 @@ static int selinux_file_mmap(struct file *file, unsigned long reqprot,
                             unsigned long addr, unsigned long addr_only)
 {
        int rc = 0;
-       u32 sid = ((struct task_security_struct *)(current->security))->sid;
+       u32 sid = current_sid();
 
        if (addr < mmap_min_addr)
                rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT,
@@ -3052,12 +3124,10 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
 
 static int selinux_file_set_fowner(struct file *file)
 {
-       struct task_security_struct *tsec;
        struct file_security_struct *fsec;
 
-       tsec = current->security;
        fsec = file->f_security;
-       fsec->fown_sid = tsec->sid;
+       fsec->fown_sid = current_sid();
 
        return 0;
 }
@@ -3066,14 +3136,13 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk,
                                       struct fown_struct *fown, int signum)
 {
        struct file *file;
+       u32 sid = current_sid();
        u32 perm;
-       struct task_security_struct *tsec;
        struct file_security_struct *fsec;
 
        /* struct fown_struct is never outside the context of a struct file */
        file = container_of(fown, struct file, f_owner);
 
-       tsec = tsk->security;
        fsec = file->f_security;
 
        if (!signum)
@@ -3081,7 +3150,7 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk,
        else
                perm = signal_to_av(signum);
 
-       return avc_has_perm(fsec->fown_sid, tsec->sid,
+       return avc_has_perm(fsec->fown_sid, sid,
                            SECCLASS_PROCESS, perm, NULL);
 }
 
@@ -3115,7 +3184,7 @@ static int selinux_dentry_open(struct file *file)
         * 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);
+       return inode_has_perm(current, inode, open_file_to_av(file), NULL);
 }
 
 /* task security operations */
@@ -3131,17 +3200,17 @@ static int selinux_task_create(unsigned long clone_flags)
        return task_has_perm(current, current, PROCESS__FORK);
 }
 
-static int selinux_task_alloc_security(struct task_struct *tsk)
+static int selinux_cred_alloc_security(struct cred *cred)
 {
        struct task_security_struct *tsec1, *tsec2;
        int rc;
 
-       tsec1 = current->security;
+       tsec1 = current_security();
 
-       rc = task_alloc_security(tsk);
+       rc = cred_alloc_security(cred);
        if (rc)
                return rc;
-       tsec2 = tsk->security;
+       tsec2 = cred->security;
 
        tsec2->osid = tsec1->osid;
        tsec2->sid = tsec1->sid;
@@ -3155,9 +3224,14 @@ static int selinux_task_alloc_security(struct task_struct *tsk)
        return 0;
 }
 
-static void selinux_task_free_security(struct task_struct *tsk)
+/*
+ * detach and free the LSM part of a set of credentials
+ */
+static void selinux_cred_free(struct cred *cred)
 {
-       task_free_security(tsk);
+       struct task_security_struct *tsec = cred->security;
+       cred->security = NULL;
+       kfree(tsec);
 }
 
 static int selinux_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
@@ -3199,8 +3273,7 @@ static int selinux_task_getsid(struct task_struct *p)
 
 static void selinux_task_getsecid(struct task_struct *p, u32 *secid)
 {
-       struct task_security_struct *tsec = p->security;
-       *secid = tsec->sid;
+       *secid = task_sid(p);
 }
 
 static int selinux_task_setgroups(struct group_info *group_info)
@@ -3281,7 +3354,6 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
 {
        u32 perm;
        int rc;
-       struct task_security_struct *tsec;
 
        rc = secondary_ops->task_kill(p, info, sig, secid);
        if (rc)
@@ -3291,9 +3363,9 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
                perm = PROCESS__SIGNULL; /* null signal; existence test */
        else
                perm = signal_to_av(sig);
-       tsec = p->security;
        if (secid)
-               rc = avc_has_perm(secid, tsec->sid, SECCLASS_PROCESS, perm, NULL);
+               rc = avc_has_perm(secid, task_sid(p),
+                                 SECCLASS_PROCESS, perm, NULL);
        else
                rc = task_has_perm(current, p, perm);
        return rc;
@@ -3323,7 +3395,7 @@ static void selinux_task_reparent_to_init(struct task_struct *p)
 
        secondary_ops->task_reparent_to_init(p);
 
-       tsec = p->security;
+       tsec = p->cred->security;
        tsec->osid = tsec->sid;
        tsec->sid = SECINITSID_KERNEL;
        return;
@@ -3332,12 +3404,11 @@ static void selinux_task_reparent_to_init(struct task_struct *p)
 static void selinux_task_to_inode(struct task_struct *p,
                                  struct inode *inode)
 {
-       struct task_security_struct *tsec = p->security;
        struct inode_security_struct *isec = inode->i_security;
+       u32 sid = task_sid(p);
 
-       isec->sid = tsec->sid;
+       isec->sid = sid;
        isec->initialized = 1;
-       return;
 }
 
 /* Returns error only if unable to parse addresses */
@@ -3495,38 +3566,44 @@ out:
 #endif /* IPV6 */
 
 static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
-                            char **addrp, int src, u8 *proto)
+                            char **_addrp, int src, u8 *proto)
 {
-       int ret = 0;
+       char *addrp;
+       int ret;
 
        switch (ad->u.net.family) {
        case PF_INET:
                ret = selinux_parse_skb_ipv4(skb, ad, proto);
-               if (ret || !addrp)
-                       break;
-               *addrp = (char *)(src ? &ad->u.net.v4info.saddr :
-                                       &ad->u.net.v4info.daddr);
-               break;
+               if (ret)
+                       goto parse_error;
+               addrp = (char *)(src ? &ad->u.net.v4info.saddr :
+                                      &ad->u.net.v4info.daddr);
+               goto okay;
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        case PF_INET6:
                ret = selinux_parse_skb_ipv6(skb, ad, proto);
-               if (ret || !addrp)
-                       break;
-               *addrp = (char *)(src ? &ad->u.net.v6info.saddr :
-                                       &ad->u.net.v6info.daddr);
-               break;
+               if (ret)
+                       goto parse_error;
+               addrp = (char *)(src ? &ad->u.net.v6info.saddr :
+                                      &ad->u.net.v6info.daddr);
+               goto okay;
 #endif /* IPV6 */
        default:
-               break;
+               addrp = NULL;
+               goto okay;
        }
 
-       if (unlikely(ret))
-               printk(KERN_WARNING
-                      "SELinux: failure in selinux_parse_skb(),"
-                      " unable to parse packet\n");
-
+parse_error:
+       printk(KERN_WARNING
+              "SELinux: failure in selinux_parse_skb(),"
+              " unable to parse packet\n");
        return ret;
+
+okay:
+       if (_addrp)
+               *_addrp = addrp;
+       return 0;
 }
 
 /**
@@ -3570,19 +3647,19 @@ static int socket_has_perm(struct task_struct *task, struct socket *sock,
                           u32 perms)
 {
        struct inode_security_struct *isec;
-       struct task_security_struct *tsec;
        struct avc_audit_data ad;
+       u32 sid;
        int err = 0;
 
-       tsec = task->security;
        isec = SOCK_INODE(sock)->i_security;
 
        if (isec->sid == SECINITSID_KERNEL)
                goto out;
+       sid = task_sid(task);
 
        AVC_AUDIT_DATA_INIT(&ad, NET);
        ad.u.net.sk = sock->sk;
-       err = avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
+       err = avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad);
 
 out:
        return err;
@@ -3591,18 +3668,20 @@ out:
 static int selinux_socket_create(int family, int type,
                                 int protocol, int kern)
 {
+       const struct cred *cred = current_cred();
+       const struct task_security_struct *tsec = cred->security;
+       u32 sid, newsid;
+       u16 secclass;
        int err = 0;
-       struct task_security_struct *tsec;
-       u32 newsid;
 
        if (kern)
                goto out;
 
-       tsec = current->security;
-       newsid = tsec->sockcreate_sid ? : tsec->sid;
-       err = avc_has_perm(tsec->sid, newsid,
-                          socket_type_to_security_class(family, type,
-                          protocol), SOCKET__CREATE, NULL);
+       sid = tsec->sid;
+       newsid = tsec->sockcreate_sid ?sid;
+
+       secclass = socket_type_to_security_class(family, type, protocol);
+       err = avc_has_perm(sid, newsid, secclass, SOCKET__CREATE, NULL);
 
 out:
        return err;
@@ -3611,18 +3690,26 @@ out:
 static int selinux_socket_post_create(struct socket *sock, int family,
                                      int type, int protocol, int kern)
 {
-       int err = 0;
+       const struct cred *cred = current_cred();
+       const struct task_security_struct *tsec = cred->security;
        struct inode_security_struct *isec;
-       struct task_security_struct *tsec;
        struct sk_security_struct *sksec;
-       u32 newsid;
+       u32 sid, newsid;
+       int err = 0;
+
+       sid = tsec->sid;
+       newsid = tsec->sockcreate_sid;
 
        isec = SOCK_INODE(sock)->i_security;
 
-       tsec = current->security;
-       newsid = tsec->sockcreate_sid ? : tsec->sid;
+       if (kern)
+               isec->sid = SECINITSID_KERNEL;
+       else if (newsid)
+               isec->sid = newsid;
+       else
+               isec->sid = sid;
+
        isec->sclass = socket_type_to_security_class(family, type, protocol);
-       isec->sid = kern ? SECINITSID_KERNEL : newsid;
        isec->initialized = 1;
 
        if (sock->sk) {
@@ -3657,26 +3744,22 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
        if (family == PF_INET || family == PF_INET6) {
                char *addrp;
                struct inode_security_struct *isec;
-               struct task_security_struct *tsec;
                struct avc_audit_data ad;
                struct sockaddr_in *addr4 = NULL;
                struct sockaddr_in6 *addr6 = NULL;
                unsigned short snum;
                struct sock *sk = sock->sk;
-               u32 sid, node_perm, addrlen;
+               u32 sid, node_perm;
 
-               tsec = current->security;
                isec = SOCK_INODE(sock)->i_security;
 
                if (family == PF_INET) {
                        addr4 = (struct sockaddr_in *)address;
                        snum = ntohs(addr4->sin_port);
-                       addrlen = sizeof(addr4->sin_addr.s_addr);
                        addrp = (char *)&addr4->sin_addr.s_addr;
                } else {
                        addr6 = (struct sockaddr_in6 *)address;
                        snum = ntohs(addr6->sin6_port);
-                       addrlen = sizeof(addr6->sin6_addr.s6_addr);
                        addrp = (char *)&addr6->sin6_addr.s6_addr;
                }
 
@@ -3743,6 +3826,7 @@ out:
 
 static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen)
 {
+       struct sock *sk = sock->sk;
        struct inode_security_struct *isec;
        int err;
 
@@ -3756,7 +3840,6 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
        isec = SOCK_INODE(sock)->i_security;
        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;
@@ -3790,6 +3873,8 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
                        goto out;
        }
 
+       err = selinux_netlbl_socket_connect(sk, address);
+
 out:
        return err;
 }
@@ -4019,20 +4104,28 @@ static int selinux_sock_rcv_skb_iptables_compat(struct sock *sk,
 }
 
 static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
-                                      struct avc_audit_data *ad,
-                                      u16 family, char *addrp)
+                                      u16 family)
 {
        int err;
        struct sk_security_struct *sksec = sk->sk_security;
        u32 peer_sid;
        u32 sk_sid = sksec->sid;
+       struct avc_audit_data ad;
+       char *addrp;
+
+       AVC_AUDIT_DATA_INIT(&ad, NET);
+       ad.u.net.netif = skb->iif;
+       ad.u.net.family = family;
+       err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL);
+       if (err)
+               return err;
 
        if (selinux_compat_net)
-               err = selinux_sock_rcv_skb_iptables_compat(sk, skb, ad,
+               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);
+                                  PACKET__RECV, &ad);
        if (err)
                return err;
 
@@ -4041,12 +4134,14 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
                if (err)
                        return err;
                err = avc_has_perm(sk_sid, peer_sid,
-                                  SECCLASS_PEER, PEER__RECV, ad);
+                                  SECCLASS_PEER, PEER__RECV, &ad);
+               if (err)
+                       selinux_netlbl_err(skb, err, 0);
        } else {
-               err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, ad);
+               err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, &ad);
                if (err)
                        return err;
-               err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, ad);
+               err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, &ad);
        }
 
        return err;
@@ -4060,6 +4155,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
        u32 sk_sid = sksec->sid;
        struct avc_audit_data ad;
        char *addrp;
+       u8 secmark_active;
+       u8 peerlbl_active;
 
        if (family != PF_INET && family != PF_INET6)
                return 0;
@@ -4068,6 +4165,18 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
        if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
                family = PF_INET;
 
+       /* 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, family);
+
+       secmark_active = selinux_secmark_enabled();
+       peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
+       if (!secmark_active && !peerlbl_active)
+               return 0;
+
        AVC_AUDIT_DATA_INIT(&ad, NET);
        ad.u.net.netif = skb->iif;
        ad.u.net.family = family;
@@ -4075,15 +4184,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
        if (err)
                return err;
 
-       /* 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()) {
+       if (peerlbl_active) {
                u32 peer_sid;
 
                err = selinux_skb_peerlbl_sid(skb, family, &peer_sid);
@@ -4091,13 +4192,17 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
                        return err;
                err = selinux_inet_sys_rcv_skb(skb->iif, addrp, family,
                                               peer_sid, &ad);
-               if (err)
+               if (err) {
+                       selinux_netlbl_err(skb, err, 0);
                        return err;
+               }
                err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER,
                                   PEER__RECV, &ad);
+               if (err)
+                       selinux_netlbl_err(skb, err, 0);
        }
 
-       if (selinux_secmark_enabled()) {
+       if (secmark_active) {
                err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET,
                                   PACKET__RECV, &ad);
                if (err)
@@ -4156,10 +4261,12 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *
        u32 peer_secid = SECSID_NULL;
        u16 family;
 
-       if (sock)
+       if (skb && skb->protocol == htons(ETH_P_IP))
+               family = PF_INET;
+       else if (skb && skb->protocol == htons(ETH_P_IPV6))
+               family = PF_INET6;
+       else if (sock)
                family = sock->sk->sk_family;
-       else if (skb && skb->sk)
-               family = skb->sk->sk_family;
        else
                goto out;
 
@@ -4217,8 +4324,6 @@ static void selinux_sock_graft(struct sock *sk, struct socket *parent)
            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,
@@ -4226,10 +4331,15 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
 {
        struct sk_security_struct *sksec = sk->sk_security;
        int err;
+       u16 family = sk->sk_family;
        u32 newsid;
        u32 peersid;
 
-       err = selinux_skb_peerlbl_sid(skb, sk->sk_family, &peersid);
+       /* handle mapped IPv4 packets arriving via IPv6 sockets */
+       if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
+               family = PF_INET;
+
+       err = selinux_skb_peerlbl_sid(skb, family, &peersid);
        if (err)
                return err;
        if (peersid == SECSID_NULL) {
@@ -4264,12 +4374,18 @@ static void selinux_inet_csk_clone(struct sock *newsk,
        selinux_netlbl_sk_security_reset(newsksec, req->rsk_ops->family);
 }
 
-static void selinux_inet_conn_established(struct sock *sk,
-                               struct sk_buff *skb)
+static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb)
 {
+       u16 family = sk->sk_family;
        struct sk_security_struct *sksec = sk->sk_security;
 
-       selinux_skb_peerlbl_sid(skb, sk->sk_family, &sksec->peer_sid);
+       /* handle mapped IPv4 packets arriving via IPv6 sockets */
+       if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
+               family = PF_INET;
+
+       selinux_skb_peerlbl_sid(skb, family, &sksec->peer_sid);
+
+       selinux_netlbl_inet_conn_established(sk, family);
 }
 
 static void selinux_req_classify_flow(const struct request_sock *req,
@@ -4299,7 +4415,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
                                  "SELinux:  unrecognized netlink message"
                                  " type=%hu for sclass=%hu\n",
                                  nlh->nlmsg_type, isec->sclass);
-                       if (!selinux_enforcing)
+                       if (!selinux_enforcing || security_get_allow_unknown())
                                err = 0;
                }
 
@@ -4319,39 +4435,54 @@ out:
 static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,
                                       u16 family)
 {
+       int err;
        char *addrp;
        u32 peer_sid;
        struct avc_audit_data ad;
        u8 secmark_active;
+       u8 netlbl_active;
        u8 peerlbl_active;
 
        if (!selinux_policycap_netpeer)
                return NF_ACCEPT;
 
        secmark_active = selinux_secmark_enabled();
-       peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
+       netlbl_active = netlbl_enabled();
+       peerlbl_active = netlbl_active || selinux_xfrm_enabled();
        if (!secmark_active && !peerlbl_active)
                return NF_ACCEPT;
 
+       if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0)
+               return NF_DROP;
+
        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)
+       if (peerlbl_active) {
+               err = selinux_inet_sys_rcv_skb(ifindex, addrp, family,
+                                              peer_sid, &ad);
+               if (err) {
+                       selinux_netlbl_err(skb, err, 1);
                        return NF_DROP;
+               }
+       }
 
        if (secmark_active)
                if (avc_has_perm(peer_sid, skb->secmark,
                                 SECCLASS_PACKET, PACKET__FORWARD_IN, &ad))
                        return NF_DROP;
 
+       if (netlbl_active)
+               /* we do this in the FORWARD path and not the POST_ROUTING
+                * path because we want to make sure we apply the necessary
+                * labeling before IPsec is applied so we can leverage AH
+                * protection */
+               if (selinux_netlbl_skbuff_setsid(skb, family, peer_sid) != 0)
+                       return NF_DROP;
+
        return NF_ACCEPT;
 }
 
@@ -4375,6 +4506,37 @@ static unsigned int selinux_ipv6_forward(unsigned int hooknum,
 }
 #endif /* IPV6 */
 
+static unsigned int selinux_ip_output(struct sk_buff *skb,
+                                     u16 family)
+{
+       u32 sid;
+
+       if (!netlbl_enabled())
+               return NF_ACCEPT;
+
+       /* we do this in the LOCAL_OUT path and not the POST_ROUTING path
+        * because we want to make sure we apply the necessary labeling
+        * before IPsec is applied so we can leverage AH protection */
+       if (skb->sk) {
+               struct sk_security_struct *sksec = skb->sk->sk_security;
+               sid = sksec->sid;
+       } else
+               sid = SECINITSID_KERNEL;
+       if (selinux_netlbl_skbuff_setsid(skb, family, sid) != 0)
+               return NF_DROP;
+
+       return NF_ACCEPT;
+}
+
+static unsigned int selinux_ipv4_output(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_output(skb, PF_INET);
+}
+
 static int selinux_ip_postroute_iptables_compat(struct sock *sk,
                                                int ifindex,
                                                struct avc_audit_data *ad,
@@ -4442,30 +4604,36 @@ static int selinux_ip_postroute_iptables_compat(struct sock *sk,
 
 static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
                                                int ifindex,
-                                               struct avc_audit_data *ad,
-                                               u16 family,
-                                               char *addrp,
-                                               u8 proto)
+                                               u16 family)
 {
        struct sock *sk = skb->sk;
        struct sk_security_struct *sksec;
+       struct avc_audit_data ad;
+       char *addrp;
+       u8 proto;
 
        if (sk == NULL)
                return NF_ACCEPT;
        sksec = sk->sk_security;
 
+       AVC_AUDIT_DATA_INIT(&ad, NET);
+       ad.u.net.netif = ifindex;
+       ad.u.net.family = family;
+       if (selinux_parse_skb(skb, &ad, &addrp, 0, &proto))
+               return NF_DROP;
+
        if (selinux_compat_net) {
                if (selinux_ip_postroute_iptables_compat(skb->sk, ifindex,
-                                                        ad, family, addrp))
+                                                        &ad, family, addrp))
                        return NF_DROP;
        } else {
                if (avc_has_perm(sksec->sid, skb->secmark,
-                                SECCLASS_PACKET, PACKET__SEND, ad))
+                                SECCLASS_PACKET, PACKET__SEND, &ad))
                        return NF_DROP;
        }
 
        if (selinux_policycap_netpeer)
-               if (selinux_xfrm_postroute_last(sksec->sid, skb, ad, proto))
+               if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
                        return NF_DROP;
 
        return NF_ACCEPT;
@@ -4479,23 +4647,15 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
        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 = 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);
+               return selinux_ip_postroute_compat(skb, ifindex, family);
 
        /* 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
@@ -4511,21 +4671,45 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
        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 */
+       /* if the packet is being forwarded then get the peer label from the
+        * packet itself; otherwise check to see if it is from a local
+        * application or the kernel, if from an application get the peer label
+        * from the sending socket, otherwise use the kernel's sid */
        sk = skb->sk;
-       if (sk) {
+       if (sk == NULL) {
+               switch (family) {
+               case PF_INET:
+                       if (IPCB(skb)->flags & IPSKB_FORWARDED)
+                               secmark_perm = PACKET__FORWARD_OUT;
+                       else
+                               secmark_perm = PACKET__SEND;
+                       break;
+               case PF_INET6:
+                       if (IP6CB(skb)->flags & IP6SKB_FORWARDED)
+                               secmark_perm = PACKET__FORWARD_OUT;
+                       else
+                               secmark_perm = PACKET__SEND;
+                       break;
+               default:
+                       return NF_DROP;
+               }
+               if (secmark_perm == PACKET__FORWARD_OUT) {
+                       if (selinux_skb_peerlbl_sid(skb, family, &peer_sid))
+                               return NF_DROP;
+               } else
+                       peer_sid = SECINITSID_KERNEL;
+       } else {
                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;
        }
 
+       AVC_AUDIT_DATA_INIT(&ad, NET);
+       ad.u.net.netif = ifindex;
+       ad.u.net.family = family;
+       if (selinux_parse_skb(skb, &ad, &addrp, 0, NULL))
+               return NF_DROP;
+
        if (secmark_active)
                if (avc_has_perm(peer_sid, skb->secmark,
                                 SECCLASS_PACKET, secmark_perm, &ad))
@@ -4607,15 +4791,16 @@ static int ipc_alloc_security(struct task_struct *task,
                              struct kern_ipc_perm *perm,
                              u16 sclass)
 {
-       struct task_security_struct *tsec = task->security;
        struct ipc_security_struct *isec;
+       u32 sid;
 
        isec = kzalloc(sizeof(struct ipc_security_struct), GFP_KERNEL);
        if (!isec)
                return -ENOMEM;
 
+       sid = task_sid(task);
        isec->sclass = sclass;
-       isec->sid = tsec->sid;
+       isec->sid = sid;
        perm->security = isec;
 
        return 0;
@@ -4653,17 +4838,16 @@ static void msg_msg_free_security(struct msg_msg *msg)
 static int ipc_has_perm(struct kern_ipc_perm *ipc_perms,
                        u32 perms)
 {
-       struct task_security_struct *tsec;
        struct ipc_security_struct *isec;
        struct avc_audit_data ad;
+       u32 sid = current_sid();
 
-       tsec = current->security;
        isec = ipc_perms->security;
 
        AVC_AUDIT_DATA_INIT(&ad, IPC);
        ad.u.ipc_id = ipc_perms->key;
 
-       return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
+       return avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad);
 }
 
 static int selinux_msg_msg_alloc_security(struct msg_msg *msg)
@@ -4679,22 +4863,21 @@ static void selinux_msg_msg_free_security(struct msg_msg *msg)
 /* message queue security operations */
 static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
 {
-       struct task_security_struct *tsec;
        struct ipc_security_struct *isec;
        struct avc_audit_data ad;
+       u32 sid = current_sid();
        int rc;
 
        rc = ipc_alloc_security(current, &msq->q_perm, SECCLASS_MSGQ);
        if (rc)
                return rc;
 
-       tsec = current->security;
        isec = msq->q_perm.security;
 
        AVC_AUDIT_DATA_INIT(&ad, IPC);
        ad.u.ipc_id = msq->q_perm.key;
 
-       rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
+       rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,
                          MSGQ__CREATE, &ad);
        if (rc) {
                ipc_free_security(&msq->q_perm);
@@ -4710,17 +4893,16 @@ static void selinux_msg_queue_free_security(struct msg_queue *msq)
 
 static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg)
 {
-       struct task_security_struct *tsec;
        struct ipc_security_struct *isec;
        struct avc_audit_data ad;
+       u32 sid = current_sid();
 
-       tsec = current->security;
        isec = msq->q_perm.security;
 
        AVC_AUDIT_DATA_INIT(&ad, IPC);
        ad.u.ipc_id = msq->q_perm.key;
 
-       return avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
+       return avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,
                            MSGQ__ASSOCIATE, &ad);
 }
 
@@ -4754,13 +4936,12 @@ static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd)
 
 static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, int msqflg)
 {
-       struct task_security_struct *tsec;
        struct ipc_security_struct *isec;
        struct msg_security_struct *msec;
        struct avc_audit_data ad;
+       u32 sid = current_sid();
        int rc;
 
-       tsec = current->security;
        isec = msq->q_perm.security;
        msec = msg->security;
 
@@ -4772,9 +4953,7 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
                 * Compute new sid based on current process and
                 * message queue this message will be stored in
                 */
-               rc = security_transition_sid(tsec->sid,
-                                            isec->sid,
-                                            SECCLASS_MSG,
+               rc = security_transition_sid(sid, isec->sid, SECCLASS_MSG,
                                             &msec->sid);
                if (rc)
                        return rc;
@@ -4784,16 +4963,16 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
        ad.u.ipc_id = msq->q_perm.key;
 
        /* Can this process write to the queue? */
-       rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
+       rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,
                          MSGQ__WRITE, &ad);
        if (!rc)
                /* Can this process send the message */
-               rc = avc_has_perm(tsec->sid, msec->sid,
-                                 SECCLASS_MSG, MSG__SEND, &ad);
+               rc = avc_has_perm(sid, msec->sid, SECCLASS_MSG,
+                                 MSG__SEND, &ad);
        if (!rc)
                /* Can the message be put in the queue? */
-               rc = avc_has_perm(msec->sid, isec->sid,
-                                 SECCLASS_MSGQ, MSGQ__ENQUEUE, &ad);
+               rc = avc_has_perm(msec->sid, isec->sid, SECCLASS_MSGQ,
+                                 MSGQ__ENQUEUE, &ad);
 
        return rc;
 }
@@ -4802,23 +4981,22 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
                                    struct task_struct *target,
                                    long type, int mode)
 {
-       struct task_security_struct *tsec;
        struct ipc_security_struct *isec;
        struct msg_security_struct *msec;
        struct avc_audit_data ad;
+       u32 sid = task_sid(target);
        int rc;
 
-       tsec = target->security;
        isec = msq->q_perm.security;
        msec = msg->security;
 
        AVC_AUDIT_DATA_INIT(&ad, IPC);
        ad.u.ipc_id = msq->q_perm.key;
 
-       rc = avc_has_perm(tsec->sid, isec->sid,
+       rc = avc_has_perm(sid, isec->sid,
                          SECCLASS_MSGQ, MSGQ__READ, &ad);
        if (!rc)
-               rc = avc_has_perm(tsec->sid, msec->sid,
+               rc = avc_has_perm(sid, msec->sid,
                                  SECCLASS_MSG, MSG__RECEIVE, &ad);
        return rc;
 }
@@ -4826,22 +5004,21 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
 /* Shared Memory security operations */
 static int selinux_shm_alloc_security(struct shmid_kernel *shp)
 {
-       struct task_security_struct *tsec;
        struct ipc_security_struct *isec;
        struct avc_audit_data ad;
+       u32 sid = current_sid();
        int rc;
 
        rc = ipc_alloc_security(current, &shp->shm_perm, SECCLASS_SHM);
        if (rc)
                return rc;
 
-       tsec = current->security;
        isec = shp->shm_perm.security;
 
        AVC_AUDIT_DATA_INIT(&ad, IPC);
        ad.u.ipc_id = shp->shm_perm.key;
 
-       rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
+       rc = avc_has_perm(sid, isec->sid, SECCLASS_SHM,
                          SHM__CREATE, &ad);
        if (rc) {
                ipc_free_security(&shp->shm_perm);
@@ -4857,17 +5034,16 @@ static void selinux_shm_free_security(struct shmid_kernel *shp)
 
 static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg)
 {
-       struct task_security_struct *tsec;
        struct ipc_security_struct *isec;
        struct avc_audit_data ad;
+       u32 sid = current_sid();
 
-       tsec = current->security;
        isec = shp->shm_perm.security;
 
        AVC_AUDIT_DATA_INIT(&ad, IPC);
        ad.u.ipc_id = shp->shm_perm.key;
 
-       return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
+       return avc_has_perm(sid, isec->sid, SECCLASS_SHM,
                            SHM__ASSOCIATE, &ad);
 }
 
@@ -4925,22 +5101,21 @@ static int selinux_shm_shmat(struct shmid_kernel *shp,
 /* Semaphore security operations */
 static int selinux_sem_alloc_security(struct sem_array *sma)
 {
-       struct task_security_struct *tsec;
        struct ipc_security_struct *isec;
        struct avc_audit_data ad;
+       u32 sid = current_sid();
        int rc;
 
        rc = ipc_alloc_security(current, &sma->sem_perm, SECCLASS_SEM);
        if (rc)
                return rc;
 
-       tsec = current->security;
        isec = sma->sem_perm.security;
 
        AVC_AUDIT_DATA_INIT(&ad, IPC);
        ad.u.ipc_id = sma->sem_perm.key;
 
-       rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
+       rc = avc_has_perm(sid, isec->sid, SECCLASS_SEM,
                          SEM__CREATE, &ad);
        if (rc) {
                ipc_free_security(&sma->sem_perm);
@@ -4956,17 +5131,16 @@ static void selinux_sem_free_security(struct sem_array *sma)
 
 static int selinux_sem_associate(struct sem_array *sma, int semflg)
 {
-       struct task_security_struct *tsec;
        struct ipc_security_struct *isec;
        struct avc_audit_data ad;
+       u32 sid = current_sid();
 
-       tsec = current->security;
        isec = sma->sem_perm.security;
 
        AVC_AUDIT_DATA_INIT(&ad, IPC);
        ad.u.ipc_id = sma->sem_perm.key;
 
-       return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
+       return avc_has_perm(sid, isec->sid, SECCLASS_SEM,
                            SEM__ASSOCIATE, &ad);
 }
 
@@ -5047,24 +5221,6 @@ static void selinux_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
        *secid = isec->sid;
 }
 
-/* module stacking operations */
-static int selinux_register_security(const char *name, struct security_operations *ops)
-{
-       if (secondary_ops != original_ops) {
-               printk(KERN_ERR "%s:  There is already a secondary security "
-                      "module registered.\n", __func__);
-               return -EINVAL;
-       }
-
-       secondary_ops = ops;
-
-       printk(KERN_INFO "%s:  Registering secondary module %s\n",
-              __func__,
-              name);
-
-       return 0;
-}
-
 static void selinux_d_instantiate(struct dentry *dentry, struct inode *inode)
 {
        if (inode)
@@ -5074,7 +5230,7 @@ static void selinux_d_instantiate(struct dentry *dentry, struct inode *inode)
 static int selinux_getprocattr(struct task_struct *p,
                               char *name, char **value)
 {
-       struct task_security_struct *tsec;
+       const struct task_security_struct *__tsec;
        u32 sid;
        int error;
        unsigned len;
@@ -5085,22 +5241,24 @@ static int selinux_getprocattr(struct task_struct *p,
                        return error;
        }
 
-       tsec = p->security;
+       rcu_read_lock();
+       __tsec = __task_cred(p)->security;
 
        if (!strcmp(name, "current"))
-               sid = tsec->sid;
+               sid = __tsec->sid;
        else if (!strcmp(name, "prev"))
-               sid = tsec->osid;
+               sid = __tsec->osid;
        else if (!strcmp(name, "exec"))
-               sid = tsec->exec_sid;
+               sid = __tsec->exec_sid;
        else if (!strcmp(name, "fscreate"))
-               sid = tsec->create_sid;
+               sid = __tsec->create_sid;
        else if (!strcmp(name, "keycreate"))
-               sid = tsec->keycreate_sid;
+               sid = __tsec->keycreate_sid;
        else if (!strcmp(name, "sockcreate"))
-               sid = tsec->sockcreate_sid;
+               sid = __tsec->sockcreate_sid;
        else
-               return -EINVAL;
+               goto invalid;
+       rcu_read_unlock();
 
        if (!sid)
                return 0;
@@ -5109,6 +5267,10 @@ static int selinux_getprocattr(struct task_struct *p,
        if (error)
                return error;
        return len;
+
+invalid:
+       rcu_read_unlock();
+       return -EINVAL;
 }
 
 static int selinux_setprocattr(struct task_struct *p,
@@ -5153,6 +5315,12 @@ static int selinux_setprocattr(struct task_struct *p,
                        size--;
                }
                error = security_context_to_sid(value, size, &sid);
+               if (error == -EINVAL && !strcmp(name, "fscreate")) {
+                       if (!capable(CAP_MAC_ADMIN))
+                               return error;
+                       error = security_context_to_sid_force(value, size,
+                                                             &sid);
+               }
                if (error)
                        return error;
        }
@@ -5163,7 +5331,7 @@ static int selinux_setprocattr(struct task_struct *p,
           operation.  See selinux_bprm_set_security for the execve
           checks and may_create for the file creation checks. The
           operation will then fail if the context is not permitted. */
-       tsec = p->security;
+       tsec = p->cred->security;
        if (!strcmp(name, "exec"))
                tsec->exec_sid = sid;
        else if (!strcmp(name, "fscreate"))
@@ -5180,20 +5348,29 @@ static int selinux_setprocattr(struct task_struct *p,
 
                if (sid == 0)
                        return -EINVAL;
-
-               /* Only allow single threaded processes to change context */
+               /*
+                * SELinux allows to change context in the following case only.
+                *  - Single threaded processes.
+                *  - Multi threaded processes intend to change its context into
+                *    more restricted domain (defined by TYPEBOUNDS statement).
+                */
                if (atomic_read(&p->mm->mm_users) != 1) {
                        struct task_struct *g, *t;
                        struct mm_struct *mm = p->mm;
                        read_lock(&tasklist_lock);
-                       do_each_thread(g, t)
+                       do_each_thread(g, t) {
                                if (t->mm == mm && t != p) {
                                        read_unlock(&tasklist_lock);
-                                       return -EPERM;
+                                       error = security_bounded_transition(tsec->sid, sid);
+                                       if (!error)
+                                               goto boundary_ok;
+
+                                       return error;
                                }
-                       while_each_thread(g, t);
+                       while_each_thread(g, t);
                        read_unlock(&tasklist_lock);
                }
+boundary_ok:
 
                /* Check permissions for the transition. */
                error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
@@ -5205,10 +5382,9 @@ static int selinux_setprocattr(struct task_struct *p,
                   Otherwise, leave SID unchanged and fail. */
                task_lock(p);
                rcu_read_lock();
-               tracer = task_tracer_task(p);
+               tracer = tracehook_tracer_task(p);
                if (tracer != NULL) {
-                       struct task_security_struct *ptsec = tracer->security;
-                       u32 ptsid = ptsec->sid;
+                       u32 ptsid = task_sid(tracer);
                        rcu_read_unlock();
                        error = avc_has_perm_noaudit(ptsid, sid,
                                                     SECCLASS_PROCESS,
@@ -5251,19 +5427,22 @@ static void selinux_release_secctx(char *secdata, u32 seclen)
 static int selinux_key_alloc(struct key *k, struct task_struct *tsk,
                             unsigned long flags)
 {
-       struct task_security_struct *tsec = tsk->security;
+       const struct task_security_struct *__tsec;
        struct key_security_struct *ksec;
 
        ksec = kzalloc(sizeof(struct key_security_struct), GFP_KERNEL);
        if (!ksec)
                return -ENOMEM;
 
-       if (tsec->keycreate_sid)
-               ksec->sid = tsec->keycreate_sid;
+       rcu_read_lock();
+       __tsec = __task_cred(tsk)->security;
+       if (__tsec->keycreate_sid)
+               ksec->sid = __tsec->keycreate_sid;
        else
-               ksec->sid = tsec->sid;
-       k->security = ksec;
+               ksec->sid = __tsec->sid;
+       rcu_read_unlock();
 
+       k->security = ksec;
        return 0;
 }
 
@@ -5280,13 +5459,8 @@ static int selinux_key_permission(key_ref_t key_ref,
                            key_perm_t perm)
 {
        struct key *key;
-       struct task_security_struct *tsec;
        struct key_security_struct *ksec;
-
-       key = key_ref_to_ptr(key_ref);
-
-       tsec = ctx->security;
-       ksec = key->security;
+       u32 sid;
 
        /* if no specific permissions are requested, we skip the
           permission check. No serious, additional covert channels
@@ -5294,8 +5468,12 @@ static int selinux_key_permission(key_ref_t key_ref,
        if (perm == 0)
                return 0;
 
-       return avc_has_perm(tsec->sid, ksec->sid,
-                           SECCLASS_KEY, perm, NULL);
+       sid = task_sid(ctx);
+
+       key = key_ref_to_ptr(key_ref);
+       ksec = key->security;
+
+       return avc_has_perm(sid, ksec->sid, SECCLASS_KEY, perm, NULL);
 }
 
 static int selinux_key_getsecurity(struct key *key, char **_buffer)
@@ -5317,7 +5495,8 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer)
 static struct security_operations selinux_ops = {
        .name =                         "selinux",
 
-       .ptrace =                       selinux_ptrace,
+       .ptrace_may_access =            selinux_ptrace_may_access,
+       .ptrace_traceme =               selinux_ptrace_traceme,
        .capget =                       selinux_capget,
        .capset_check =                 selinux_capset_check,
        .capset_set =                   selinux_capset_set,
@@ -5343,10 +5522,10 @@ static struct security_operations selinux_ops = {
        .sb_free_security =             selinux_sb_free_security,
        .sb_copy_data =                 selinux_sb_copy_data,
        .sb_kern_mount =                selinux_sb_kern_mount,
+       .sb_show_options =              selinux_sb_show_options,
        .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,
        .sb_parse_opts_str =            selinux_parse_opts_str,
@@ -5378,7 +5557,7 @@ static struct security_operations selinux_ops = {
        .inode_listsecurity =           selinux_inode_listsecurity,
        .inode_need_killpriv =          selinux_inode_need_killpriv,
        .inode_killpriv =               selinux_inode_killpriv,
-       .inode_getsecid =               selinux_inode_getsecid,
+       .inode_getsecid =               selinux_inode_getsecid,
 
        .file_permission =              selinux_file_permission,
        .file_alloc_security =          selinux_file_alloc_security,
@@ -5395,8 +5574,8 @@ static struct security_operations selinux_ops = {
        .dentry_open =                  selinux_dentry_open,
 
        .task_create =                  selinux_task_create,
-       .task_alloc_security =          selinux_task_alloc_security,
-       .task_free_security =           selinux_task_free_security,
+       .cred_alloc_security =          selinux_cred_alloc_security,
+       .cred_free =                    selinux_cred_free,
        .task_setuid =                  selinux_task_setuid,
        .task_post_setuid =             selinux_task_post_setuid,
        .task_setgid =                  selinux_task_setgid,
@@ -5419,7 +5598,7 @@ static struct security_operations selinux_ops = {
        .task_to_inode =                selinux_task_to_inode,
 
        .ipc_permission =               selinux_ipc_permission,
-       .ipc_getsecid =                 selinux_ipc_getsecid,
+       .ipc_getsecid =                 selinux_ipc_getsecid,
 
        .msg_msg_alloc_security =       selinux_msg_msg_alloc_security,
        .msg_msg_free_security =        selinux_msg_msg_free_security,
@@ -5443,8 +5622,6 @@ static struct security_operations selinux_ops = {
        .sem_semctl =                   selinux_sem_semctl,
        .sem_semop =                    selinux_sem_semop,
 
-       .register_security =            selinux_register_security,
-
        .d_instantiate =                selinux_d_instantiate,
 
        .getprocattr =                  selinux_getprocattr,
@@ -5528,9 +5705,9 @@ static __init int selinux_init(void)
        printk(KERN_INFO "SELinux:  Initializing.\n");
 
        /* Set the security state for the initial task. */
-       if (task_alloc_security(current))
+       if (cred_alloc_security(current->cred))
                panic("SELinux:  Failed to initialize initial task.\n");
-       tsec = current->security;
+       tsec = current->cred->security;
        tsec->osid = tsec->sid = SECINITSID_KERNEL;
 
        sel_inode_cache = kmem_cache_create("selinux_inode_security",
@@ -5538,7 +5715,7 @@ static __init int selinux_init(void)
                                            0, SLAB_PANIC, NULL);
        avc_init();
 
-       original_ops = secondary_ops = security_ops;
+       secondary_ops = security_ops;
        if (!secondary_ops)
                panic("SELinux: No initial security operations\n");
        if (register_security(&selinux_ops))
@@ -5603,6 +5780,13 @@ static struct nf_hook_ops selinux_ipv4_ops[] = {
                .pf =           PF_INET,
                .hooknum =      NF_INET_FORWARD,
                .priority =     NF_IP_PRI_SELINUX_FIRST,
+       },
+       {
+               .hook =         selinux_ipv4_output,
+               .owner =        THIS_MODULE,
+               .pf =           PF_INET,
+               .hooknum =      NF_INET_LOCAL_OUT,
+               .priority =     NF_IP_PRI_SELINUX_FIRST,
        }
 };
 
@@ -5630,27 +5814,20 @@ static struct nf_hook_ops selinux_ipv6_ops[] = {
 static int __init selinux_nf_ip_init(void)
 {
        int err = 0;
-       u32 iter;
 
        if (!selinux_enabled)
                goto out;
 
        printk(KERN_DEBUG "SELinux:  Registering netfilter hooks\n");
 
-       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);
-       }
+       err = nf_register_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops));
+       if (err)
+               panic("SELinux: nf_register_hooks 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);
-       }
+       err = nf_register_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops));
+       if (err)
+               panic("SELinux: nf_register_hooks for IPv6: error %d\n", err);
 #endif /* IPV6 */
 
 out:
@@ -5662,15 +5839,11 @@ __initcall(selinux_nf_ip_init);
 #ifdef CONFIG_SECURITY_SELINUX_DISABLE
 static void selinux_nf_ip_exit(void)
 {
-       u32 iter;
-
        printk(KERN_DEBUG "SELinux:  Unregistering netfilter hooks\n");
 
-       for (iter = 0; iter < ARRAY_SIZE(selinux_ipv4_ops); iter++)
-               nf_unregister_hook(&selinux_ipv4_ops[iter]);
+       nf_unregister_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops));
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       for (iter = 0; iter < ARRAY_SIZE(selinux_ipv6_ops); iter++)
-               nf_unregister_hook(&selinux_ipv6_ops[iter]);
+       nf_unregister_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops));
 #endif /* IPV6 */
 }
 #endif