fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / security / selinux / ss / services.c
index 8e4423b..ca9154d 100644 (file)
@@ -4,17 +4,27 @@
  * Authors : Stephen Smalley, <sds@epoch.ncsc.mil>
  *           James Morris <jmorris@redhat.com>
  *
- *  Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
  *
- *     This program is free software; you can redistribute it and/or modify
- *     it under the terms of the GNU General Public License version 2,
- *      as published by the Free Software Foundation.
+ *     Support for enhanced MLS infrastructure.
+ *     Support for context based audit filters.
  *
  * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
  *
  *     Added conditional policy language extensions
  *
- * Copyright (C) 2003 - 2004 Tresys Technology, LLC
+ * Updated: Hewlett-Packard <paul.moore@hp.com>
+ *
+ *      Added support for NetLabel
+ *
+ * Updated: Chad Sellers <csellers@tresys.com>
+ *
+ *  Added validation of kernel classes and permissions
+ *
+ * Copyright (C) 2006 Hewlett-Packard Development Company, L.P.
+ * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
+ * Copyright (C) 2003 - 2004, 2006 Tresys Technology, LLC
+ * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
  *     This program is free software; you can redistribute it and/or modify
  *     it under the terms of the GNU General Public License as published by
  *     the Free Software Foundation, version 2.
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/spinlock.h>
+#include <linux/rcupdate.h>
 #include <linux/errno.h>
 #include <linux/in.h>
 #include <linux/sched.h>
 #include <linux/audit.h>
-#include <asm/semaphore.h>
+#include <linux/mutex.h>
+#include <net/sock.h>
+#include <net/netlabel.h>
+
 #include "flask.h"
 #include "avc.h"
 #include "avc_ss.h"
 #include "services.h"
 #include "conditional.h"
 #include "mls.h"
+#include "objsec.h"
+#include "selinux_netlabel.h"
+#include "xfrm.h"
+#include "ebitmap.h"
 
 extern void selnl_notify_policyload(u32 seqno);
-extern int policydb_loaded_version;
+unsigned int policydb_loaded_version;
+
+/*
+ * This is declared in avc.c
+ */
+extern const struct selinux_class_perm selinux_class_perm;
 
-static rwlock_t policy_rwlock = RW_LOCK_UNLOCKED;
+static DEFINE_RWLOCK(policy_rwlock);
 #define POLICY_RDLOCK read_lock(&policy_rwlock)
 #define POLICY_WRLOCK write_lock_irq(&policy_rwlock)
 #define POLICY_RDUNLOCK read_unlock(&policy_rwlock)
 #define POLICY_WRUNLOCK write_unlock_irq(&policy_rwlock)
 
-static DECLARE_MUTEX(load_sem);
-#define LOAD_LOCK down(&load_sem)
-#define LOAD_UNLOCK up(&load_sem)
+static DEFINE_MUTEX(load_mutex);
+#define LOAD_LOCK mutex_lock(&load_mutex)
+#define LOAD_UNLOCK mutex_unlock(&load_mutex)
 
-struct sidtab sidtab;
+static struct sidtab sidtab;
 struct policydb policydb;
 int ss_initialized = 0;
 
@@ -64,18 +87,30 @@ int ss_initialized = 0;
  */
 static u32 latest_granting = 0;
 
+/* Forward declaration. */
+static int context_struct_to_string(struct context *context, char **scontext,
+                                   u32 *scontext_len);
+
 /*
  * Return the boolean value of a constraint expression
  * when it is applied to the specified source and target
  * security contexts.
+ *
+ * xcontext is a special beast...  It is used by the validatetrans rules
+ * only.  For these rules, scontext is the context before the transition,
+ * tcontext is the context after the transition, and xcontext is the context
+ * of the process performing the transition.  All other callers of
+ * constraint_expr_eval should pass in NULL for xcontext.
  */
 static int constraint_expr_eval(struct context *scontext,
                                struct context *tcontext,
+                               struct context *xcontext,
                                struct constraint_expr *cexpr)
 {
        u32 val1, val2;
        struct context *c;
        struct role_datum *r1, *r2;
+       struct mls_level *l1, *l2;
        struct constraint_expr *e;
        int s[CEXPR_MAXDEPTH];
        int sp = -1;
@@ -132,6 +167,52 @@ static int constraint_expr_eval(struct context *scontext,
                                        break;
                                }
                                break;
+                       case CEXPR_L1L2:
+                               l1 = &(scontext->range.level[0]);
+                               l2 = &(tcontext->range.level[0]);
+                               goto mls_ops;
+                       case CEXPR_L1H2:
+                               l1 = &(scontext->range.level[0]);
+                               l2 = &(tcontext->range.level[1]);
+                               goto mls_ops;
+                       case CEXPR_H1L2:
+                               l1 = &(scontext->range.level[1]);
+                               l2 = &(tcontext->range.level[0]);
+                               goto mls_ops;
+                       case CEXPR_H1H2:
+                               l1 = &(scontext->range.level[1]);
+                               l2 = &(tcontext->range.level[1]);
+                               goto mls_ops;
+                       case CEXPR_L1H1:
+                               l1 = &(scontext->range.level[0]);
+                               l2 = &(scontext->range.level[1]);
+                               goto mls_ops;
+                       case CEXPR_L2H2:
+                               l1 = &(tcontext->range.level[0]);
+                               l2 = &(tcontext->range.level[1]);
+                               goto mls_ops;
+mls_ops:
+                       switch (e->op) {
+                       case CEXPR_EQ:
+                               s[++sp] = mls_level_eq(l1, l2);
+                               continue;
+                       case CEXPR_NEQ:
+                               s[++sp] = !mls_level_eq(l1, l2);
+                               continue;
+                       case CEXPR_DOM:
+                               s[++sp] = mls_level_dom(l1, l2);
+                               continue;
+                       case CEXPR_DOMBY:
+                               s[++sp] = mls_level_dom(l2, l1);
+                               continue;
+                       case CEXPR_INCOMP:
+                               s[++sp] = mls_level_incomp(l2, l1);
+                               continue;
+                       default:
+                               BUG();
+                               return 0;
+                       }
+                       break;
                        default:
                                BUG();
                                return 0;
@@ -155,6 +236,13 @@ static int constraint_expr_eval(struct context *scontext,
                        c = scontext;
                        if (e->attr & CEXPR_TARGET)
                                c = tcontext;
+                       else if (e->attr & CEXPR_XTARGET) {
+                               c = xcontext;
+                               if (!c) {
+                                       BUG();
+                                       return 0;
+                               }
+                       }
                        if (e->attr & CEXPR_USER)
                                val1 = c->user;
                        else if (e->attr & CEXPR_ROLE)
@@ -201,8 +289,11 @@ static int context_struct_compute_av(struct context *scontext,
        struct constraint_node *constraint;
        struct role_allow *ra;
        struct avtab_key avkey;
-       struct avtab_datum *avdatum;
+       struct avtab_node *node;
        struct class_datum *tclass_datum;
+       struct ebitmap *sattr, *tattr;
+       struct ebitmap_node *snode, *tnode;
+       unsigned int i, j;
 
        /*
         * Remap extended Netlink classes for old policy versions.
@@ -235,34 +326,43 @@ static int context_struct_compute_av(struct context *scontext,
         * If a specific type enforcement rule was defined for
         * this permission check, then use it.
         */
-       avkey.source_type = scontext->type;
-       avkey.target_type = tcontext->type;
        avkey.target_class = tclass;
-       avdatum = avtab_search(&policydb.te_avtab, &avkey, AVTAB_AV);
-       if (avdatum) {
-               if (avdatum->specified & AVTAB_ALLOWED)
-                       avd->allowed = avtab_allowed(avdatum);
-               if (avdatum->specified & AVTAB_AUDITDENY)
-                       avd->auditdeny = avtab_auditdeny(avdatum);
-               if (avdatum->specified & AVTAB_AUDITALLOW)
-                       avd->auditallow = avtab_auditallow(avdatum);
-       }
+       avkey.specified = AVTAB_AV;
+       sattr = &policydb.type_attr_map[scontext->type - 1];
+       tattr = &policydb.type_attr_map[tcontext->type - 1];
+       ebitmap_for_each_bit(sattr, snode, i) {
+               if (!ebitmap_node_get_bit(snode, i))
+                       continue;
+               ebitmap_for_each_bit(tattr, tnode, j) {
+                       if (!ebitmap_node_get_bit(tnode, j))
+                               continue;
+                       avkey.source_type = i + 1;
+                       avkey.target_type = j + 1;
+                       for (node = avtab_search_node(&policydb.te_avtab, &avkey);
+                            node != NULL;
+                            node = avtab_search_node_next(node, avkey.specified)) {
+                               if (node->key.specified == AVTAB_ALLOWED)
+                                       avd->allowed |= node->datum.data;
+                               else if (node->key.specified == AVTAB_AUDITALLOW)
+                                       avd->auditallow |= node->datum.data;
+                               else if (node->key.specified == AVTAB_AUDITDENY)
+                                       avd->auditdeny &= node->datum.data;
+                       }
 
-       /* Check conditional av table for additional permissions */
-       cond_compute_av(&policydb.te_cond_avtab, &avkey, avd);
+                       /* Check conditional av table for additional permissions */
+                       cond_compute_av(&policydb.te_cond_avtab, &avkey, avd);
 
-       /*
-        * Remove any permissions prohibited by the MLS policy.
-        */
-       mls_compute_av(scontext, tcontext, tclass_datum, &avd->allowed);
+               }
+       }
 
        /*
-        * Remove any permissions prohibited by a constraint.
+        * Remove any permissions prohibited by a constraint (this includes
+        * the MLS policy).
         */
        constraint = tclass_datum->constraints;
        while (constraint) {
                if ((constraint->permissions & (avd->allowed)) &&
-                   !constraint_expr_eval(scontext, tcontext,
+                   !constraint_expr_eval(scontext, tcontext, NULL,
                                          constraint->expr)) {
                        avd->allowed = (avd->allowed) & ~(constraint->permissions);
                }
@@ -275,7 +375,7 @@ static int context_struct_compute_av(struct context *scontext,
         * pair.
         */
        if (tclass == SECCLASS_PROCESS &&
-           (avd->allowed & PROCESS__TRANSITION) &&
+           (avd->allowed & (PROCESS__TRANSITION | PROCESS__DYNTRANSITION)) &&
            scontext->role != tcontext->role) {
                for (ra = policydb.role_allow; ra; ra = ra->next) {
                        if (scontext->role == ra->role &&
@@ -283,12 +383,115 @@ static int context_struct_compute_av(struct context *scontext,
                                break;
                }
                if (!ra)
-                       avd->allowed = (avd->allowed) & ~(PROCESS__TRANSITION);
+                       avd->allowed = (avd->allowed) & ~(PROCESS__TRANSITION |
+                                                       PROCESS__DYNTRANSITION);
        }
 
        return 0;
 }
 
+static int security_validtrans_handle_fail(struct context *ocontext,
+                                           struct context *ncontext,
+                                           struct context *tcontext,
+                                           u16 tclass)
+{
+       char *o = NULL, *n = NULL, *t = NULL;
+       u32 olen, nlen, tlen;
+
+       if (context_struct_to_string(ocontext, &o, &olen) < 0)
+               goto out;
+       if (context_struct_to_string(ncontext, &n, &nlen) < 0)
+               goto out;
+       if (context_struct_to_string(tcontext, &t, &tlen) < 0)
+               goto out;
+       audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR,
+                 "security_validate_transition:  denied for"
+                 " oldcontext=%s newcontext=%s taskcontext=%s tclass=%s",
+                 o, n, t, policydb.p_class_val_to_name[tclass-1]);
+out:
+       kfree(o);
+       kfree(n);
+       kfree(t);
+
+       if (!selinux_enforcing)
+               return 0;
+       return -EPERM;
+}
+
+int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
+                                 u16 tclass)
+{
+       struct context *ocontext;
+       struct context *ncontext;
+       struct context *tcontext;
+       struct class_datum *tclass_datum;
+       struct constraint_node *constraint;
+       int rc = 0;
+
+       if (!ss_initialized)
+               return 0;
+
+       POLICY_RDLOCK;
+
+       /*
+        * Remap extended Netlink classes for old policy versions.
+        * Do this here rather than socket_type_to_security_class()
+        * in case a newer policy version is loaded, allowing sockets
+        * to remain in the correct class.
+        */
+       if (policydb_loaded_version < POLICYDB_VERSION_NLCLASS)
+               if (tclass >= SECCLASS_NETLINK_ROUTE_SOCKET &&
+                   tclass <= SECCLASS_NETLINK_DNRT_SOCKET)
+                       tclass = SECCLASS_NETLINK_SOCKET;
+
+       if (!tclass || tclass > policydb.p_classes.nprim) {
+               printk(KERN_ERR "security_validate_transition:  "
+                      "unrecognized class %d\n", tclass);
+               rc = -EINVAL;
+               goto out;
+       }
+       tclass_datum = policydb.class_val_to_struct[tclass - 1];
+
+       ocontext = sidtab_search(&sidtab, oldsid);
+       if (!ocontext) {
+               printk(KERN_ERR "security_validate_transition: "
+                      " unrecognized SID %d\n", oldsid);
+               rc = -EINVAL;
+               goto out;
+       }
+
+       ncontext = sidtab_search(&sidtab, newsid);
+       if (!ncontext) {
+               printk(KERN_ERR "security_validate_transition: "
+                      " unrecognized SID %d\n", newsid);
+               rc = -EINVAL;
+               goto out;
+       }
+
+       tcontext = sidtab_search(&sidtab, tasksid);
+       if (!tcontext) {
+               printk(KERN_ERR "security_validate_transition: "
+                      " unrecognized SID %d\n", tasksid);
+               rc = -EINVAL;
+               goto out;
+       }
+
+       constraint = tclass_datum->validatetrans;
+       while (constraint) {
+               if (!constraint_expr_eval(ocontext, ncontext, tcontext,
+                                         constraint->expr)) {
+                       rc = security_validtrans_handle_fail(ocontext, ncontext,
+                                                            tcontext, tclass);
+                       goto out;
+               }
+               constraint = constraint->next;
+       }
+
+out:
+       POLICY_RDUNLOCK;
+       return rc;
+}
+
 /**
  * security_compute_av - Compute access vector decisions.
  * @ssid: source security identifier
@@ -312,8 +515,8 @@ int security_compute_av(u32 ssid,
        int rc = 0;
 
        if (!ss_initialized) {
-               avd->allowed = requested;
-               avd->decided = requested;
+               avd->allowed = 0xffffffff;
+               avd->decided = 0xffffffff;
                avd->auditallow = 0;
                avd->auditdeny = 0xffffffff;
                avd->seqno = latest_granting;
@@ -351,7 +554,7 @@ out:
  * to point to this string and set `*scontext_len' to
  * the length of the string.
  */
-int context_struct_to_string(struct context *context, char **scontext, u32 *scontext_len)
+static int context_struct_to_string(struct context *context, char **scontext, u32 *scontext_len)
 {
        char *scontextp;
 
@@ -365,7 +568,7 @@ int context_struct_to_string(struct context *context, char **scontext, u32 *scon
        *scontext_len += mls_compute_context_len(context);
 
        /* Allocate space for the context; caller must free this space. */
-       scontextp = kmalloc(*scontext_len+1,GFP_ATOMIC);
+       scontextp = kmalloc(*scontext_lenGFP_ATOMIC);
        if (!scontextp) {
                return -ENOMEM;
        }
@@ -374,17 +577,16 @@ int context_struct_to_string(struct context *context, char **scontext, u32 *scon
        /*
         * Copy the user name, role name and type name into the context.
         */
-       sprintf(scontextp, "%s:%s:%s:",
+       sprintf(scontextp, "%s:%s:%s",
                policydb.p_user_val_to_name[context->user - 1],
                policydb.p_role_val_to_name[context->role - 1],
                policydb.p_type_val_to_name[context->type - 1]);
        scontextp += strlen(policydb.p_user_val_to_name[context->user - 1]) +
                     1 + strlen(policydb.p_role_val_to_name[context->role - 1]) +
-                    1 + strlen(policydb.p_type_val_to_name[context->type - 1]) + 1;
+                    1 + strlen(policydb.p_type_val_to_name[context->type - 1]);
 
        mls_sid_to_context(context, &scontextp);
 
-       scontextp--;
        *scontextp = 0;
 
        return 0;
@@ -413,6 +615,10 @@ int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len)
 
                        *scontext_len = strlen(initial_sid_to_string[sid]) + 1;
                        scontextp = kmalloc(*scontext_len,GFP_ATOMIC);
+                       if (!scontextp) {
+                               rc = -ENOMEM;
+                               goto out;
+                       }
                        strcpy(scontextp, initial_sid_to_string[sid]);
                        *scontext = scontextp;
                        goto out;
@@ -438,18 +644,7 @@ out:
 
 }
 
-/**
- * security_context_to_sid - Obtain a SID for a given security context.
- * @scontext: security context
- * @scontext_len: length in bytes
- * @sid: security identifier, SID
- *
- * Obtains a SID associated with the security context that
- * has the string representation specified by @scontext.
- * Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient
- * memory is available, or 0 on success.
- */
-int security_context_to_sid(char *scontext, u32 scontext_len, u32 *sid)
+static int security_context_to_sid_core(char *scontext, u32 scontext_len, u32 *sid, u32 def_sid)
 {
        char *scontext2;
        struct context context;
@@ -540,7 +735,7 @@ int security_context_to_sid(char *scontext, u32 scontext_len, u32 *sid)
 
        context.type = typdatum->value;
 
-       rc = mls_context_to_sid(oldc, &p, &context);
+       rc = mls_context_to_sid(oldc, &p, &context, &sidtab, def_sid);
        if (rc)
                goto out_unlock;
 
@@ -564,6 +759,46 @@ out:
        return rc;
 }
 
+/**
+ * security_context_to_sid - Obtain a SID for a given security context.
+ * @scontext: security context
+ * @scontext_len: length in bytes
+ * @sid: security identifier, SID
+ *
+ * Obtains a SID associated with the security context that
+ * has the string representation specified by @scontext.
+ * Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient
+ * memory is available, or 0 on success.
+ */
+int security_context_to_sid(char *scontext, u32 scontext_len, u32 *sid)
+{
+       return security_context_to_sid_core(scontext, scontext_len,
+                                           sid, SECSID_NULL);
+}
+
+/**
+ * security_context_to_sid_default - Obtain a SID for a given security context,
+ * falling back to specified default if needed.
+ *
+ * @scontext: security context
+ * @scontext_len: length in bytes
+ * @sid: security identifier, SID
+ * @def_sid: default SID to assign on errror
+ *
+ * Obtains a SID associated with the security context that
+ * has the string representation specified by @scontext.
+ * The default SID is passed to the MLS layer to be used to allow
+ * kernel labeling of the MLS field if the MLS field is not present
+ * (for upgrading to MLS without full relabel).
+ * Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient
+ * memory is available, or 0 on success.
+ */
+int security_context_to_sid_default(char *scontext, u32 scontext_len, u32 *sid, u32 def_sid)
+{
+       return security_context_to_sid_core(scontext, scontext_len,
+                                           sid, def_sid);
+}
+
 static int compute_sid_handle_invalid_context(
        struct context *scontext,
        struct context *tcontext,
@@ -579,7 +814,7 @@ static int compute_sid_handle_invalid_context(
                goto out;
        if (context_struct_to_string(newcontext, &n, &nlen) < 0)
                goto out;
-       audit_log(current->audit_context,
+       audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR,
                  "security_compute_sid:  invalid context %s"
                  " for scontext=%s"
                  " tcontext=%s"
@@ -605,7 +840,6 @@ static int security_compute_sid(u32 ssid,
        struct avtab_key avkey;
        struct avtab_datum *avdatum;
        struct avtab_node *node;
-       unsigned int type_change = 0;
        int rc = 0;
 
        if (!ss_initialized) {
@@ -620,6 +854,8 @@ static int security_compute_sid(u32 ssid,
                goto out;
        }
 
+       context_init(&newcontext);
+
        POLICY_RDLOCK;
 
        scontext = sidtab_search(&sidtab, ssid);
@@ -637,8 +873,6 @@ static int security_compute_sid(u32 ssid,
                goto out_unlock;
        }
 
-       context_init(&newcontext);
-
        /* Set the user identity. */
        switch (specified) {
        case AVTAB_TRANSITION:
@@ -670,33 +904,23 @@ static int security_compute_sid(u32 ssid,
        avkey.source_type = scontext->type;
        avkey.target_type = tcontext->type;
        avkey.target_class = tclass;
-       avdatum = avtab_search(&policydb.te_avtab, &avkey, AVTAB_TYPE);
+       avkey.specified = specified;
+       avdatum = avtab_search(&policydb.te_avtab, &avkey);
 
        /* If no permanent rule, also check for enabled conditional rules */
        if(!avdatum) {
-               node = avtab_search_node(&policydb.te_cond_avtab, &avkey, specified);
+               node = avtab_search_node(&policydb.te_cond_avtab, &avkey);
                for (; node != NULL; node = avtab_search_node_next(node, specified)) {
-                       if (node->datum.specified & AVTAB_ENABLED) {
+                       if (node->key.specified & AVTAB_ENABLED) {
                                avdatum = &node->datum;
                                break;
                        }
                }
        }
 
-       type_change = (avdatum && (avdatum->specified & specified));
-       if (type_change) {
+       if (avdatum) {
                /* Use the type from the type transition/member/change rule. */
-               switch (specified) {
-               case AVTAB_TRANSITION:
-                       newcontext.type = avtab_transition(avdatum);
-                       break;
-               case AVTAB_MEMBER:
-                       newcontext.type = avtab_member(avdatum);
-                       break;
-               case AVTAB_CHANGE:
-                       newcontext.type = avtab_change(avdatum);
-                       break;
-               }
+               newcontext.type = avdatum->data;
        }
 
        /* Check for class-specific changes. */
@@ -714,23 +938,8 @@ static int security_compute_sid(u32 ssid,
                                }
                        }
                }
-
-               if (!type_change && !roletr) {
-                       /* No change in process role or type. */
-                       *out_sid = ssid;
-                       goto out_unlock;
-
-               }
                break;
        default:
-               if (!type_change &&
-                   (newcontext.user == tcontext->user) &&
-                   mls_context_cmp(scontext, tcontext)) {
-                        /* No change in object type, owner,
-                          or MLS attributes. */
-                       *out_sid = tsid;
-                       goto out_unlock;
-               }
                break;
        }
 
@@ -822,86 +1031,112 @@ int security_change_sid(u32 ssid,
 }
 
 /*
- * Verify that each permission that is defined under the
- * existing policy is still defined with the same value
- * in the new policy.
- */
-static int validate_perm(void *key, void *datum, void *p)
-{
-       struct hashtab *h;
-       struct perm_datum *perdatum, *perdatum2;
-       int rc = 0;
-
-
-       h = p;
-       perdatum = datum;
-
-       perdatum2 = hashtab_search(h, key);
-       if (!perdatum2) {
-               printk(KERN_ERR "security:  permission %s disappeared",
-                      (char *)key);
-               rc = -ENOENT;
-               goto out;
-       }
-       if (perdatum->value != perdatum2->value) {
-               printk(KERN_ERR "security:  the value of permission %s changed",
-                      (char *)key);
-               rc = -EINVAL;
-       }
-out:
-       return rc;
-}
-
-/*
- * Verify that each class that is defined under the
- * existing policy is still defined with the same
- * attributes in the new policy.
+ * Verify that each kernel class that is defined in the
+ * policy is correct
  */
-static int validate_class(void *key, void *datum, void *p)
+static int validate_classes(struct policydb *p)
 {
-       struct policydb *newp;
-       struct class_datum *cladatum, *cladatum2;
-       int rc;
-
-       newp = p;
-       cladatum = datum;
-
-       cladatum2 = hashtab_search(newp->p_classes.table, key);
-       if (!cladatum2) {
-               printk(KERN_ERR "security:  class %s disappeared\n",
-                      (char *)key);
-               rc = -ENOENT;
-               goto out;
-       }
-       if (cladatum->value != cladatum2->value) {
-               printk(KERN_ERR "security:  the value of class %s changed\n",
-                      (char *)key);
-               rc = -EINVAL;
-               goto out;
+       int i, j;
+       struct class_datum *cladatum;
+       struct perm_datum *perdatum;
+       u32 nprim, tmp, common_pts_len, perm_val, pol_val;
+       u16 class_val;
+       const struct selinux_class_perm *kdefs = &selinux_class_perm;
+       const char *def_class, *def_perm, *pol_class;
+       struct symtab *perms;
+
+       for (i = 1; i < kdefs->cts_len; i++) {
+               def_class = kdefs->class_to_string[i];
+               if (i > p->p_classes.nprim) {
+                       printk(KERN_INFO
+                              "security:  class %s not defined in policy\n",
+                              def_class);
+                       continue;
+               }
+               pol_class = p->p_class_val_to_name[i-1];
+               if (strcmp(pol_class, def_class)) {
+                       printk(KERN_ERR
+                              "security:  class %d is incorrect, found %s but should be %s\n",
+                              i, pol_class, def_class);
+                       return -EINVAL;
+               }
        }
-       if ((cladatum->comdatum && !cladatum2->comdatum) ||
-           (!cladatum->comdatum && cladatum2->comdatum)) {
-               printk(KERN_ERR "security:  the inherits clause for the access "
-                      "vector definition for class %s changed\n", (char *)key);
-               rc = -EINVAL;
-               goto out;
+       for (i = 0; i < kdefs->av_pts_len; i++) {
+               class_val = kdefs->av_perm_to_string[i].tclass;
+               perm_val = kdefs->av_perm_to_string[i].value;
+               def_perm = kdefs->av_perm_to_string[i].name;
+               if (class_val > p->p_classes.nprim)
+                       continue;
+               pol_class = p->p_class_val_to_name[class_val-1];
+               cladatum = hashtab_search(p->p_classes.table, pol_class);
+               BUG_ON(!cladatum);
+               perms = &cladatum->permissions;
+               nprim = 1 << (perms->nprim - 1);
+               if (perm_val > nprim) {
+                       printk(KERN_INFO
+                              "security:  permission %s in class %s not defined in policy\n",
+                              def_perm, pol_class);
+                       continue;
+               }
+               perdatum = hashtab_search(perms->table, def_perm);
+               if (perdatum == NULL) {
+                       printk(KERN_ERR
+                              "security:  permission %s in class %s not found in policy\n",
+                              def_perm, pol_class);
+                       return -EINVAL;
+               }
+               pol_val = 1 << (perdatum->value - 1);
+               if (pol_val != perm_val) {
+                       printk(KERN_ERR
+                              "security:  permission %s in class %s has incorrect value\n",
+                              def_perm, pol_class);
+                       return -EINVAL;
+               }
        }
-       if (cladatum->comdatum) {
-               rc = hashtab_map(cladatum->comdatum->permissions.table, validate_perm,
-                                cladatum2->comdatum->permissions.table);
-               if (rc) {
-                       printk(" in the access vector definition for class "
-                              "%s\n", (char *)key);
-                       goto out;
+       for (i = 0; i < kdefs->av_inherit_len; i++) {
+               class_val = kdefs->av_inherit[i].tclass;
+               if (class_val > p->p_classes.nprim)
+                       continue;
+               pol_class = p->p_class_val_to_name[class_val-1];
+               cladatum = hashtab_search(p->p_classes.table, pol_class);
+               BUG_ON(!cladatum);
+               if (!cladatum->comdatum) {
+                       printk(KERN_ERR
+                              "security:  class %s should have an inherits clause but does not\n",
+                              pol_class);
+                       return -EINVAL;
+               }
+               tmp = kdefs->av_inherit[i].common_base;
+               common_pts_len = 0;
+               while (!(tmp & 0x01)) {
+                       common_pts_len++;
+                       tmp >>= 1;
+               }
+               perms = &cladatum->comdatum->permissions;
+               for (j = 0; j < common_pts_len; j++) {
+                       def_perm = kdefs->av_inherit[i].common_pts[j];
+                       if (j >= perms->nprim) {
+                               printk(KERN_INFO
+                                      "security:  permission %s in class %s not defined in policy\n",
+                                      def_perm, pol_class);
+                               continue;
+                       }
+                       perdatum = hashtab_search(perms->table, def_perm);
+                       if (perdatum == NULL) {
+                               printk(KERN_ERR
+                                      "security:  permission %s in class %s not found in policy\n",
+                                      def_perm, pol_class);
+                               return -EINVAL;
+                       }
+                       if (perdatum->value != j + 1) {
+                               printk(KERN_ERR
+                                      "security:  permission %s in class %s has incorrect value\n",
+                                      def_perm, pol_class);
+                               return -EINVAL;
+                       }
                }
        }
-       rc = hashtab_map(cladatum->permissions.table, validate_perm,
-                        cladatum2->permissions.table);
-       if (rc)
-               printk(" in access vector definition for class %s\n",
-                      (char *)key);
-out:
-       return rc;
+       return 0;
 }
 
 /* Clone the SID into the new SID table. */
@@ -1034,19 +1269,37 @@ int security_load_policy(void *data, size_t len)
        LOAD_LOCK;
 
        if (!ss_initialized) {
+               avtab_cache_init();
                if (policydb_read(&policydb, fp)) {
                        LOAD_UNLOCK;
+                       avtab_cache_destroy();
                        return -EINVAL;
                }
                if (policydb_load_isids(&policydb, &sidtab)) {
                        LOAD_UNLOCK;
                        policydb_destroy(&policydb);
+                       avtab_cache_destroy();
+                       return -EINVAL;
+               }
+               /* Verify that the kernel defined classes are correct. */
+               if (validate_classes(&policydb)) {
+                       printk(KERN_ERR
+                              "security:  the definition of a class is incorrect\n");
+                       LOAD_UNLOCK;
+                       sidtab_destroy(&sidtab);
+                       policydb_destroy(&policydb);
+                       avtab_cache_destroy();
                        return -EINVAL;
                }
+               policydb_loaded_version = policydb.policyvers;
                ss_initialized = 1;
-
+               seqno = ++latest_granting;
                LOAD_UNLOCK;
                selinux_complete_init();
+               avc_ss_reset(seqno);
+               selnl_notify_policyload(seqno);
+               selinux_netlbl_cache_invalidate();
+               selinux_xfrm_notify_policyload();
                return 0;
        }
 
@@ -1061,10 +1314,10 @@ int security_load_policy(void *data, size_t len)
 
        sidtab_init(&newsidtab);
 
-       /* Verify that the existing classes did not change. */
-       if (hashtab_map(policydb.p_classes.table, validate_class, &newpolicydb)) {
-               printk(KERN_ERR "security:  the definition of an existing "
-                      "class changed\n");
+       /* Verify that the kernel defined classes are correct. */
+       if (validate_classes(&newpolicydb)) {
+               printk(KERN_ERR
+                      "security:  the definition of a class is incorrect\n");
                rc = -EINVAL;
                goto err;
        }
@@ -1091,7 +1344,7 @@ int security_load_policy(void *data, size_t len)
        memcpy(&policydb, &newpolicydb, sizeof policydb);
        sidtab_set(&sidtab, &newsidtab);
        seqno = ++latest_granting;
-
+       policydb_loaded_version = policydb.policyvers;
        POLICY_WRUNLOCK;
        LOAD_UNLOCK;
 
@@ -1101,6 +1354,8 @@ int security_load_policy(void *data, size_t len)
 
        avc_ss_reset(seqno);
        selnl_notify_policyload(seqno);
+       selinux_netlbl_cache_invalidate();
+       selinux_xfrm_notify_policyload();
 
        return 0;
 
@@ -1319,6 +1574,7 @@ int security_get_user_sids(u32 fromsid,
        struct user_datum *user;
        struct role_datum *role;
        struct av_decision avd;
+       struct ebitmap_node *rnode, *tnode;
        int rc = 0, i, j;
 
        if (!ss_initialized) {
@@ -1342,52 +1598,51 @@ int security_get_user_sids(u32 fromsid,
        }
        usercon.user = user->value;
 
-       mysids = kmalloc(maxnel*sizeof(*mysids), GFP_ATOMIC);
+       mysids = kcalloc(maxnel, sizeof(*mysids), GFP_ATOMIC);
        if (!mysids) {
                rc = -ENOMEM;
                goto out_unlock;
        }
-       memset(mysids, 0, maxnel*sizeof(*mysids));
 
-       for (i = ebitmap_startbit(&user->roles); i < ebitmap_length(&user->roles); i++) {
-               if (!ebitmap_get_bit(&user->roles, i))
+       ebitmap_for_each_bit(&user->roles, rnode, i) {
+               if (!ebitmap_node_get_bit(rnode, i))
                        continue;
                role = policydb.role_val_to_struct[i];
                usercon.role = i+1;
-               for (j = ebitmap_startbit(&role->types); j < ebitmap_length(&role->types); j++) {
-                       if (!ebitmap_get_bit(&role->types, j))
+               ebitmap_for_each_bit(&role->types, tnode, j) {
+                       if (!ebitmap_node_get_bit(tnode, j))
                                continue;
                        usercon.type = j+1;
-                       mls_for_user_ranges(user,usercon) {
-                               rc = context_struct_compute_av(fromcon, &usercon,
-                                                              SECCLASS_PROCESS,
-                                                              PROCESS__TRANSITION,
-                                                              &avd);
-                               if (rc ||  !(avd.allowed & PROCESS__TRANSITION))
-                                       continue;
-                               rc = sidtab_context_to_sid(&sidtab, &usercon, &sid);
-                               if (rc) {
+
+                       if (mls_setup_user_range(fromcon, user, &usercon))
+                               continue;
+
+                       rc = context_struct_compute_av(fromcon, &usercon,
+                                                      SECCLASS_PROCESS,
+                                                      PROCESS__TRANSITION,
+                                                      &avd);
+                       if (rc ||  !(avd.allowed & PROCESS__TRANSITION))
+                               continue;
+                       rc = sidtab_context_to_sid(&sidtab, &usercon, &sid);
+                       if (rc) {
+                               kfree(mysids);
+                               goto out_unlock;
+                       }
+                       if (mynel < maxnel) {
+                               mysids[mynel++] = sid;
+                       } else {
+                               maxnel += SIDS_NEL;
+                               mysids2 = kcalloc(maxnel, sizeof(*mysids2), GFP_ATOMIC);
+                               if (!mysids2) {
+                                       rc = -ENOMEM;
                                        kfree(mysids);
                                        goto out_unlock;
                                }
-                               if (mynel < maxnel) {
-                                       mysids[mynel++] = sid;
-                               } else {
-                                       maxnel += SIDS_NEL;
-                                       mysids2 = kmalloc(maxnel*sizeof(*mysids2), GFP_ATOMIC);
-                                       if (!mysids2) {
-                                               rc = -ENOMEM;
-                                               kfree(mysids);
-                                               goto out_unlock;
-                                       }
-                                       memset(mysids2, 0, maxnel*sizeof(*mysids2));
-                                       memcpy(mysids2, mysids, mynel * sizeof(*mysids2));
-                                       kfree(mysids);
-                                       mysids = mysids2;
-                                       mysids[mynel++] = sid;
-                               }
+                               memcpy(mysids2, mysids, mynel * sizeof(*mysids2));
+                               kfree(mysids);
+                               mysids = mysids2;
+                               mysids[mynel++] = sid;
                        }
-                       mls_end_user_ranges;
                }
        }
 
@@ -1524,12 +1779,11 @@ int security_get_bools(int *len, char ***names, int **values)
                goto out;
        }
 
-       *names = (char**)kmalloc(sizeof(char*) * *len, GFP_ATOMIC);
+       *names = kcalloc(*len, sizeof(char*), GFP_ATOMIC);
        if (!*names)
                goto err;
-       memset(*names, 0, sizeof(char*) * *len);
 
-       *values = (int*)kmalloc(sizeof(int) * *len, GFP_ATOMIC);
+       *values = kcalloc(*len, sizeof(int), GFP_ATOMIC);
        if (!*values)
                goto err;
 
@@ -1537,7 +1791,7 @@ int security_get_bools(int *len, char ***names, int **values)
                size_t name_len;
                (*values)[i] = policydb.bool_val_to_struct[i]->state;
                name_len = strlen(policydb.p_bool_val_to_name[i]) + 1;
-               (*names)[i] = (char*)kmalloc(sizeof(char) * name_len, GFP_ATOMIC);
+               (*names)[i] = kmalloc(sizeof(char) * name_len, GFP_ATOMIC);
                if (!(*names)[i])
                        goto err;
                strncpy((*names)[i], policydb.p_bool_val_to_name[i], name_len);
@@ -1550,11 +1804,9 @@ out:
 err:
        if (*names) {
                for (i = 0; i < *len; i++)
-                       if ((*names)[i])
-                               kfree((*names)[i]);
+                       kfree((*names)[i]);
        }
-       if (*values)
-               kfree(*values);
+       kfree(*values);
        goto out;
 }
 
@@ -1573,19 +1825,22 @@ int security_set_bools(int len, int *values)
                goto out;
        }
 
-       printk(KERN_INFO "security: committed booleans { ");
        for (i = 0; i < len; i++) {
+               if (!!values[i] != policydb.bool_val_to_struct[i]->state) {
+                       audit_log(current->audit_context, GFP_ATOMIC,
+                               AUDIT_MAC_CONFIG_CHANGE,
+                               "bool=%s val=%d old_val=%d auid=%u",
+                               policydb.p_bool_val_to_name[i],
+                               !!values[i],
+                               policydb.bool_val_to_struct[i]->state,
+                               audit_get_loginuid(current->audit_context));
+               }
                if (values[i]) {
                        policydb.bool_val_to_struct[i]->state = 1;
                } else {
                        policydb.bool_val_to_struct[i]->state = 0;
                }
-               if (i != 0)
-                       printk(", ");
-               printk("%s:%d", policydb.p_bool_val_to_name[i],
-                      policydb.bool_val_to_struct[i]->state);
        }
-       printk(" }\n");
 
        for (cur = policydb.cond_list; cur != NULL; cur = cur->next) {
                rc = evaluate_cond_node(&policydb, cur);
@@ -1600,6 +1855,7 @@ out:
        if (!rc) {
                avc_ss_reset(seqno);
                selnl_notify_policyload(seqno);
+               selinux_xfrm_notify_policyload();
        }
        return rc;
 }
@@ -1622,3 +1878,884 @@ out:
        POLICY_RDUNLOCK;
        return rc;
 }
+
+/*
+ * security_sid_mls_copy() - computes a new sid based on the given
+ * sid and the mls portion of mls_sid.
+ */
+int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid)
+{
+       struct context *context1;
+       struct context *context2;
+       struct context newcon;
+       char *s;
+       u32 len;
+       int rc = 0;
+
+       if (!ss_initialized || !selinux_mls_enabled) {
+               *new_sid = sid;
+               goto out;
+       }
+
+       context_init(&newcon);
+
+       POLICY_RDLOCK;
+       context1 = sidtab_search(&sidtab, sid);
+       if (!context1) {
+               printk(KERN_ERR "security_sid_mls_copy:  unrecognized SID "
+                      "%d\n", sid);
+               rc = -EINVAL;
+               goto out_unlock;
+       }
+
+       context2 = sidtab_search(&sidtab, mls_sid);
+       if (!context2) {
+               printk(KERN_ERR "security_sid_mls_copy:  unrecognized SID "
+                      "%d\n", mls_sid);
+               rc = -EINVAL;
+               goto out_unlock;
+       }
+
+       newcon.user = context1->user;
+       newcon.role = context1->role;
+       newcon.type = context1->type;
+       rc = mls_context_cpy(&newcon, context2);
+       if (rc)
+               goto out_unlock;
+
+       /* Check the validity of the new context. */
+       if (!policydb_context_isvalid(&policydb, &newcon)) {
+               rc = convert_context_handle_invalid_context(&newcon);
+               if (rc)
+                       goto bad;
+       }
+
+       rc = sidtab_context_to_sid(&sidtab, &newcon, new_sid);
+       goto out_unlock;
+
+bad:
+       if (!context_struct_to_string(&newcon, &s, &len)) {
+               audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR,
+                         "security_sid_mls_copy: invalid context %s", s);
+               kfree(s);
+       }
+
+out_unlock:
+       POLICY_RDUNLOCK;
+       context_destroy(&newcon);
+out:
+       return rc;
+}
+
+struct selinux_audit_rule {
+       u32 au_seqno;
+       struct context au_ctxt;
+};
+
+void selinux_audit_rule_free(struct selinux_audit_rule *rule)
+{
+       if (rule) {
+               context_destroy(&rule->au_ctxt);
+               kfree(rule);
+       }
+}
+
+int selinux_audit_rule_init(u32 field, u32 op, char *rulestr,
+                            struct selinux_audit_rule **rule)
+{
+       struct selinux_audit_rule *tmprule;
+       struct role_datum *roledatum;
+       struct type_datum *typedatum;
+       struct user_datum *userdatum;
+       int rc = 0;
+
+       *rule = NULL;
+
+       if (!ss_initialized)
+               return -ENOTSUPP;
+
+       switch (field) {
+       case AUDIT_SUBJ_USER:
+       case AUDIT_SUBJ_ROLE:
+       case AUDIT_SUBJ_TYPE:
+       case AUDIT_OBJ_USER:
+       case AUDIT_OBJ_ROLE:
+       case AUDIT_OBJ_TYPE:
+               /* only 'equals' and 'not equals' fit user, role, and type */
+               if (op != AUDIT_EQUAL && op != AUDIT_NOT_EQUAL)
+                       return -EINVAL;
+               break;
+       case AUDIT_SUBJ_SEN:
+       case AUDIT_SUBJ_CLR:
+       case AUDIT_OBJ_LEV_LOW:
+       case AUDIT_OBJ_LEV_HIGH:
+               /* we do not allow a range, indicated by the presense of '-' */
+               if (strchr(rulestr, '-'))
+                       return -EINVAL;
+               break;
+       default:
+               /* only the above fields are valid */
+               return -EINVAL;
+       }
+
+       tmprule = kzalloc(sizeof(struct selinux_audit_rule), GFP_KERNEL);
+       if (!tmprule)
+               return -ENOMEM;
+
+       context_init(&tmprule->au_ctxt);
+
+       POLICY_RDLOCK;
+
+       tmprule->au_seqno = latest_granting;
+
+       switch (field) {
+       case AUDIT_SUBJ_USER:
+       case AUDIT_OBJ_USER:
+               userdatum = hashtab_search(policydb.p_users.table, rulestr);
+               if (!userdatum)
+                       rc = -EINVAL;
+               else
+                       tmprule->au_ctxt.user = userdatum->value;
+               break;
+       case AUDIT_SUBJ_ROLE:
+       case AUDIT_OBJ_ROLE:
+               roledatum = hashtab_search(policydb.p_roles.table, rulestr);
+               if (!roledatum)
+                       rc = -EINVAL;
+               else
+                       tmprule->au_ctxt.role = roledatum->value;
+               break;
+       case AUDIT_SUBJ_TYPE:
+       case AUDIT_OBJ_TYPE:
+               typedatum = hashtab_search(policydb.p_types.table, rulestr);
+               if (!typedatum)
+                       rc = -EINVAL;
+               else
+                       tmprule->au_ctxt.type = typedatum->value;
+               break;
+       case AUDIT_SUBJ_SEN:
+       case AUDIT_SUBJ_CLR:
+       case AUDIT_OBJ_LEV_LOW:
+       case AUDIT_OBJ_LEV_HIGH:
+               rc = mls_from_string(rulestr, &tmprule->au_ctxt, GFP_ATOMIC);
+               break;
+       }
+
+       POLICY_RDUNLOCK;
+
+       if (rc) {
+               selinux_audit_rule_free(tmprule);
+               tmprule = NULL;
+       }
+
+       *rule = tmprule;
+
+       return rc;
+}
+
+int selinux_audit_rule_match(u32 sid, u32 field, u32 op,
+                             struct selinux_audit_rule *rule,
+                             struct audit_context *actx)
+{
+       struct context *ctxt;
+       struct mls_level *level;
+       int match = 0;
+
+       if (!rule) {
+               audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR,
+                         "selinux_audit_rule_match: missing rule\n");
+               return -ENOENT;
+       }
+
+       POLICY_RDLOCK;
+
+       if (rule->au_seqno < latest_granting) {
+               audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR,
+                         "selinux_audit_rule_match: stale rule\n");
+               match = -ESTALE;
+               goto out;
+       }
+
+       ctxt = sidtab_search(&sidtab, sid);
+       if (!ctxt) {
+               audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR,
+                         "selinux_audit_rule_match: unrecognized SID %d\n",
+                         sid);
+               match = -ENOENT;
+               goto out;
+       }
+
+       /* a field/op pair that is not caught here will simply fall through
+          without a match */
+       switch (field) {
+       case AUDIT_SUBJ_USER:
+       case AUDIT_OBJ_USER:
+               switch (op) {
+               case AUDIT_EQUAL:
+                       match = (ctxt->user == rule->au_ctxt.user);
+                       break;
+               case AUDIT_NOT_EQUAL:
+                       match = (ctxt->user != rule->au_ctxt.user);
+                       break;
+               }
+               break;
+       case AUDIT_SUBJ_ROLE:
+       case AUDIT_OBJ_ROLE:
+               switch (op) {
+               case AUDIT_EQUAL:
+                       match = (ctxt->role == rule->au_ctxt.role);
+                       break;
+               case AUDIT_NOT_EQUAL:
+                       match = (ctxt->role != rule->au_ctxt.role);
+                       break;
+               }
+               break;
+       case AUDIT_SUBJ_TYPE:
+       case AUDIT_OBJ_TYPE:
+               switch (op) {
+               case AUDIT_EQUAL:
+                       match = (ctxt->type == rule->au_ctxt.type);
+                       break;
+               case AUDIT_NOT_EQUAL:
+                       match = (ctxt->type != rule->au_ctxt.type);
+                       break;
+               }
+               break;
+       case AUDIT_SUBJ_SEN:
+       case AUDIT_SUBJ_CLR:
+       case AUDIT_OBJ_LEV_LOW:
+       case AUDIT_OBJ_LEV_HIGH:
+               level = ((field == AUDIT_SUBJ_SEN ||
+                         field == AUDIT_OBJ_LEV_LOW) ?
+                        &ctxt->range.level[0] : &ctxt->range.level[1]);
+               switch (op) {
+               case AUDIT_EQUAL:
+                       match = mls_level_eq(&rule->au_ctxt.range.level[0],
+                                            level);
+                       break;
+               case AUDIT_NOT_EQUAL:
+                       match = !mls_level_eq(&rule->au_ctxt.range.level[0],
+                                             level);
+                       break;
+               case AUDIT_LESS_THAN:
+                       match = (mls_level_dom(&rule->au_ctxt.range.level[0],
+                                              level) &&
+                                !mls_level_eq(&rule->au_ctxt.range.level[0],
+                                              level));
+                       break;
+               case AUDIT_LESS_THAN_OR_EQUAL:
+                       match = mls_level_dom(&rule->au_ctxt.range.level[0],
+                                             level);
+                       break;
+               case AUDIT_GREATER_THAN:
+                       match = (mls_level_dom(level,
+                                             &rule->au_ctxt.range.level[0]) &&
+                                !mls_level_eq(level,
+                                              &rule->au_ctxt.range.level[0]));
+                       break;
+               case AUDIT_GREATER_THAN_OR_EQUAL:
+                       match = mls_level_dom(level,
+                                             &rule->au_ctxt.range.level[0]);
+                       break;
+               }
+       }
+
+out:
+       POLICY_RDUNLOCK;
+       return match;
+}
+
+static int (*aurule_callback)(void) = NULL;
+
+static int aurule_avc_callback(u32 event, u32 ssid, u32 tsid,
+                               u16 class, u32 perms, u32 *retained)
+{
+       int err = 0;
+
+       if (event == AVC_CALLBACK_RESET && aurule_callback)
+               err = aurule_callback();
+       return err;
+}
+
+static int __init aurule_init(void)
+{
+       int err;
+
+       err = avc_add_callback(aurule_avc_callback, AVC_CALLBACK_RESET,
+                              SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
+       if (err)
+               panic("avc_add_callback() failed, error %d\n", err);
+
+       return err;
+}
+__initcall(aurule_init);
+
+void selinux_audit_set_callback(int (*callback)(void))
+{
+       aurule_callback = callback;
+}
+
+/**
+ * security_skb_extlbl_sid - Determine the external label of a packet
+ * @skb: the packet
+ * @base_sid: the SELinux SID to use as a context for MLS only external labels
+ * @sid: the packet's SID
+ *
+ * Description:
+ * Check the various different forms of external packet labeling and determine
+ * the external SID for the packet.
+ *
+ */
+void security_skb_extlbl_sid(struct sk_buff *skb, u32 base_sid, u32 *sid)
+{
+       u32 xfrm_sid;
+       u32 nlbl_sid;
+
+       selinux_skb_xfrm_sid(skb, &xfrm_sid);
+       if (selinux_netlbl_skbuff_getsid(skb,
+                                        (xfrm_sid == SECSID_NULL ?
+                                         base_sid : xfrm_sid),
+                                        &nlbl_sid) != 0)
+               nlbl_sid = SECSID_NULL;
+
+       *sid = (nlbl_sid == SECSID_NULL ? xfrm_sid : nlbl_sid);
+}
+
+#ifdef CONFIG_NETLABEL
+/*
+ * This is the structure we store inside the NetLabel cache block.
+ */
+#define NETLBL_CACHE(x)           ((struct netlbl_cache *)(x))
+#define NETLBL_CACHE_T_NONE       0
+#define NETLBL_CACHE_T_SID        1
+#define NETLBL_CACHE_T_MLS        2
+struct netlbl_cache {
+       u32 type;
+       union {
+               u32 sid;
+               struct mls_range mls_label;
+       } data;
+};
+
+/**
+ * selinux_netlbl_cache_free - Free the NetLabel cached data
+ * @data: the data to free
+ *
+ * Description:
+ * This function is intended to be used as the free() callback inside the
+ * netlbl_lsm_cache structure.
+ *
+ */
+static void selinux_netlbl_cache_free(const void *data)
+{
+       struct netlbl_cache *cache;
+
+       if (data == NULL)
+               return;
+
+       cache = NETLBL_CACHE(data);
+       switch (cache->type) {
+       case NETLBL_CACHE_T_MLS:
+               ebitmap_destroy(&cache->data.mls_label.level[0].cat);
+               break;
+       }
+       kfree(data);
+}
+
+/**
+ * selinux_netlbl_cache_add - Add an entry to the NetLabel cache
+ * @skb: the packet
+ * @ctx: the SELinux context
+ *
+ * Description:
+ * Attempt to cache the context in @ctx, which was derived from the packet in
+ * @skb, in the NetLabel subsystem cache.
+ *
+ */
+static void selinux_netlbl_cache_add(struct sk_buff *skb, struct context *ctx)
+{
+       struct netlbl_cache *cache = NULL;
+       struct netlbl_lsm_secattr secattr;
+
+       netlbl_secattr_init(&secattr);
+       secattr.cache = netlbl_secattr_cache_alloc(GFP_ATOMIC);
+       if (secattr.cache == NULL)
+               goto netlbl_cache_add_return;
+
+       cache = kzalloc(sizeof(*cache), GFP_ATOMIC);
+       if (cache == NULL)
+               goto netlbl_cache_add_return;
+
+       cache->type = NETLBL_CACHE_T_MLS;
+       if (ebitmap_cpy(&cache->data.mls_label.level[0].cat,
+                       &ctx->range.level[0].cat) != 0)
+               goto netlbl_cache_add_return;
+       cache->data.mls_label.level[1].cat.highbit =
+               cache->data.mls_label.level[0].cat.highbit;
+       cache->data.mls_label.level[1].cat.node =
+               cache->data.mls_label.level[0].cat.node;
+       cache->data.mls_label.level[0].sens = ctx->range.level[0].sens;
+       cache->data.mls_label.level[1].sens = ctx->range.level[0].sens;
+
+       secattr.cache->free = selinux_netlbl_cache_free;
+       secattr.cache->data = (void *)cache;
+       secattr.flags = NETLBL_SECATTR_CACHE;
+
+       netlbl_cache_add(skb, &secattr);
+
+netlbl_cache_add_return:
+       netlbl_secattr_destroy(&secattr);
+}
+
+/**
+ * selinux_netlbl_cache_invalidate - Invalidate the NetLabel cache
+ *
+ * Description:
+ * Invalidate the NetLabel security attribute mapping cache.
+ *
+ */
+void selinux_netlbl_cache_invalidate(void)
+{
+       netlbl_cache_invalidate();
+}
+
+/**
+ * selinux_netlbl_secattr_to_sid - Convert a NetLabel secattr to a SELinux SID
+ * @skb: the network packet
+ * @secattr: the NetLabel packet security attributes
+ * @base_sid: the SELinux SID to use as a context for MLS only attributes
+ * @sid: the SELinux SID
+ *
+ * Description:
+ * Convert the given NetLabel packet security attributes in @secattr into a
+ * SELinux SID.  If the @secattr field does not contain a full SELinux
+ * SID/context then use the context in @base_sid as the foundation.  If @skb
+ * is not NULL attempt to cache as much data as possibile.  Returns zero on
+ * success, negative values on failure.
+ *
+ */
+static int selinux_netlbl_secattr_to_sid(struct sk_buff *skb,
+                                        struct netlbl_lsm_secattr *secattr,
+                                        u32 base_sid,
+                                        u32 *sid)
+{
+       int rc = -EIDRM;
+       struct context *ctx;
+       struct context ctx_new;
+       struct netlbl_cache *cache;
+
+       POLICY_RDLOCK;
+
+       if (secattr->flags & NETLBL_SECATTR_CACHE) {
+               cache = NETLBL_CACHE(secattr->cache->data);
+               switch (cache->type) {
+               case NETLBL_CACHE_T_SID:
+                       *sid = cache->data.sid;
+                       rc = 0;
+                       break;
+               case NETLBL_CACHE_T_MLS:
+                       ctx = sidtab_search(&sidtab, base_sid);
+                       if (ctx == NULL)
+                               goto netlbl_secattr_to_sid_return;
+
+                       ctx_new.user = ctx->user;
+                       ctx_new.role = ctx->role;
+                       ctx_new.type = ctx->type;
+                       ctx_new.range.level[0].sens =
+                               cache->data.mls_label.level[0].sens;
+                       ctx_new.range.level[0].cat.highbit =
+                               cache->data.mls_label.level[0].cat.highbit;
+                       ctx_new.range.level[0].cat.node =
+                               cache->data.mls_label.level[0].cat.node;
+                       ctx_new.range.level[1].sens =
+                               cache->data.mls_label.level[1].sens;
+                       ctx_new.range.level[1].cat.highbit =
+                               cache->data.mls_label.level[1].cat.highbit;
+                       ctx_new.range.level[1].cat.node =
+                               cache->data.mls_label.level[1].cat.node;
+
+                       rc = sidtab_context_to_sid(&sidtab, &ctx_new, sid);
+                       break;
+               default:
+                       goto netlbl_secattr_to_sid_return;
+               }
+       } else if (secattr->flags & NETLBL_SECATTR_MLS_LVL) {
+               ctx = sidtab_search(&sidtab, base_sid);
+               if (ctx == NULL)
+                       goto netlbl_secattr_to_sid_return;
+
+               ctx_new.user = ctx->user;
+               ctx_new.role = ctx->role;
+               ctx_new.type = ctx->type;
+               mls_import_netlbl_lvl(&ctx_new, secattr);
+               if (secattr->flags & NETLBL_SECATTR_MLS_CAT) {
+                       if (ebitmap_netlbl_import(&ctx_new.range.level[0].cat,
+                                                 secattr->mls_cat) != 0)
+                               goto netlbl_secattr_to_sid_return;
+                       ctx_new.range.level[1].cat.highbit =
+                               ctx_new.range.level[0].cat.highbit;
+                       ctx_new.range.level[1].cat.node =
+                               ctx_new.range.level[0].cat.node;
+               } else {
+                       ebitmap_init(&ctx_new.range.level[0].cat);
+                       ebitmap_init(&ctx_new.range.level[1].cat);
+               }
+               if (mls_context_isvalid(&policydb, &ctx_new) != 1)
+                       goto netlbl_secattr_to_sid_return_cleanup;
+
+               rc = sidtab_context_to_sid(&sidtab, &ctx_new, sid);
+               if (rc != 0)
+                       goto netlbl_secattr_to_sid_return_cleanup;
+
+               if (skb != NULL)
+                       selinux_netlbl_cache_add(skb, &ctx_new);
+               ebitmap_destroy(&ctx_new.range.level[0].cat);
+       } else {
+               *sid = SECSID_NULL;
+               rc = 0;
+       }
+
+netlbl_secattr_to_sid_return:
+       POLICY_RDUNLOCK;
+       return rc;
+netlbl_secattr_to_sid_return_cleanup:
+       ebitmap_destroy(&ctx_new.range.level[0].cat);
+       goto netlbl_secattr_to_sid_return;
+}
+
+/**
+ * selinux_netlbl_skbuff_getsid - Get the sid of a packet using NetLabel
+ * @skb: the packet
+ * @base_sid: the SELinux SID to use as a context for MLS only attributes
+ * @sid: the SID
+ *
+ * Description:
+ * Call the NetLabel mechanism to get the security attributes of the given
+ * packet and use those attributes to determine the correct context/SID to
+ * assign to the packet.  Returns zero on success, negative values on failure.
+ *
+ */
+int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid)
+{
+       int rc;
+       struct netlbl_lsm_secattr secattr;
+
+       netlbl_secattr_init(&secattr);
+       rc = netlbl_skbuff_getattr(skb, &secattr);
+       if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
+               rc = selinux_netlbl_secattr_to_sid(skb,
+                                                  &secattr,
+                                                  base_sid,
+                                                  sid);
+       else
+               *sid = SECSID_NULL;
+       netlbl_secattr_destroy(&secattr);
+
+       return rc;
+}
+
+/**
+ * selinux_netlbl_socket_setsid - Label a socket using the NetLabel mechanism
+ * @sock: the socket to label
+ * @sid: the SID to use
+ *
+ * Description:
+ * Attempt to label a socket using the NetLabel mechanism using the given
+ * SID.  Returns zero values on success, negative values on failure.  The
+ * caller is responsibile for calling rcu_read_lock() before calling this
+ * this function and rcu_read_unlock() after this function returns.
+ *
+ */
+static int selinux_netlbl_socket_setsid(struct socket *sock, u32 sid)
+{
+       int rc = -ENOENT;
+       struct sk_security_struct *sksec = sock->sk->sk_security;
+       struct netlbl_lsm_secattr secattr;
+       struct context *ctx;
+
+       if (!ss_initialized)
+               return 0;
+
+       netlbl_secattr_init(&secattr);
+
+       POLICY_RDLOCK;
+
+       ctx = sidtab_search(&sidtab, sid);
+       if (ctx == NULL)
+               goto netlbl_socket_setsid_return;
+
+       secattr.domain = kstrdup(policydb.p_type_val_to_name[ctx->type - 1],
+                                GFP_ATOMIC);
+       secattr.flags |= NETLBL_SECATTR_DOMAIN;
+       mls_export_netlbl_lvl(ctx, &secattr);
+       rc = mls_export_netlbl_cat(ctx, &secattr);
+       if (rc != 0)
+               goto netlbl_socket_setsid_return;
+
+       rc = netlbl_socket_setattr(sock, &secattr);
+       if (rc == 0) {
+               spin_lock_bh(&sksec->nlbl_lock);
+               sksec->nlbl_state = NLBL_LABELED;
+               spin_unlock_bh(&sksec->nlbl_lock);
+       }
+
+netlbl_socket_setsid_return:
+       POLICY_RDUNLOCK;
+       netlbl_secattr_destroy(&secattr);
+       return rc;
+}
+
+/**
+ * selinux_netlbl_sk_security_reset - Reset the NetLabel fields
+ * @ssec: the sk_security_struct
+ * @family: the socket family
+ *
+ * Description:
+ * Called when the NetLabel state of a sk_security_struct needs to be reset.
+ * The caller is responsibile for all the NetLabel sk_security_struct locking.
+ *
+ */
+void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec,
+                                     int family)
+{
+        if (family == PF_INET)
+               ssec->nlbl_state = NLBL_REQUIRE;
+       else
+               ssec->nlbl_state = NLBL_UNSET;
+}
+
+/**
+ * selinux_netlbl_sk_security_init - Setup the NetLabel fields
+ * @ssec: the sk_security_struct
+ * @family: the socket family
+ *
+ * Description:
+ * Called when a new sk_security_struct is allocated to initialize the NetLabel
+ * fields.
+ *
+ */
+void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec,
+                                    int family)
+{
+       /* No locking needed, we are the only one who has access to ssec */
+       selinux_netlbl_sk_security_reset(ssec, family);
+       spin_lock_init(&ssec->nlbl_lock);
+}
+
+/**
+ * selinux_netlbl_sk_security_clone - Copy the NetLabel fields
+ * @ssec: the original sk_security_struct
+ * @newssec: the cloned sk_security_struct
+ *
+ * Description:
+ * Clone the NetLabel specific sk_security_struct fields from @ssec to
+ * @newssec.
+ *
+ */
+void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec,
+                                     struct sk_security_struct *newssec)
+{
+       /* We don't need to take newssec->nlbl_lock because we are the only
+        * thread with access to newssec, but we do need to take the RCU read
+        * lock as other threads could have access to ssec */
+       rcu_read_lock();
+       selinux_netlbl_sk_security_reset(newssec, ssec->sk->sk_family);
+       newssec->sclass = ssec->sclass;
+       rcu_read_unlock();
+}
+
+/**
+ * selinux_netlbl_socket_post_create - Label a socket using NetLabel
+ * @sock: the socket to label
+ *
+ * Description:
+ * Attempt to label a socket using the NetLabel mechanism using the given
+ * SID.  Returns zero values on success, negative values on failure.
+ *
+ */
+int selinux_netlbl_socket_post_create(struct socket *sock)
+{
+       int rc = 0;
+       struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
+       struct sk_security_struct *sksec = sock->sk->sk_security;
+
+       sksec->sclass = isec->sclass;
+
+       rcu_read_lock();
+       if (sksec->nlbl_state == NLBL_REQUIRE)
+               rc = selinux_netlbl_socket_setsid(sock, sksec->sid);
+       rcu_read_unlock();
+
+       return rc;
+}
+
+/**
+ * selinux_netlbl_sock_graft - Netlabel the new socket
+ * @sk: the new connection
+ * @sock: the new socket
+ *
+ * Description:
+ * The connection represented by @sk is being grafted onto @sock so set the
+ * socket's NetLabel to match the SID of @sk.
+ *
+ */
+void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock)
+{
+       struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
+       struct sk_security_struct *sksec = sk->sk_security;
+       struct netlbl_lsm_secattr secattr;
+       u32 nlbl_peer_sid;
+
+       sksec->sclass = isec->sclass;
+
+       rcu_read_lock();
+
+       if (sksec->nlbl_state != NLBL_REQUIRE) {
+               rcu_read_unlock();
+               return;
+       }
+
+       netlbl_secattr_init(&secattr);
+       if (netlbl_sock_getattr(sk, &secattr) == 0 &&
+           secattr.flags != NETLBL_SECATTR_NONE &&
+           selinux_netlbl_secattr_to_sid(NULL,
+                                         &secattr,
+                                         SECINITSID_UNLABELED,
+                                         &nlbl_peer_sid) == 0)
+               sksec->peer_sid = nlbl_peer_sid;
+       netlbl_secattr_destroy(&secattr);
+
+       /* Try to set the NetLabel on the socket to save time later, if we fail
+        * here we will pick up the pieces in later calls to
+        * selinux_netlbl_inode_permission(). */
+       selinux_netlbl_socket_setsid(sock, sksec->sid);
+
+       rcu_read_unlock();
+}
+
+/**
+ * selinux_netlbl_inode_permission - Verify the socket is NetLabel labeled
+ * @inode: the file descriptor's inode
+ * @mask: the permission mask
+ *
+ * Description:
+ * Looks at a file's inode and if it is marked as a socket protected by
+ * NetLabel then verify that the socket has been labeled, if not try to label
+ * the socket now with the inode's SID.  Returns zero on success, negative
+ * values on failure.
+ *
+ */
+int selinux_netlbl_inode_permission(struct inode *inode, int mask)
+{
+       int rc;
+       struct sk_security_struct *sksec;
+       struct socket *sock;
+
+       if (!S_ISSOCK(inode->i_mode) ||
+           ((mask & (MAY_WRITE | MAY_APPEND)) == 0))
+               return 0;
+       sock = SOCKET_I(inode);
+       sksec = sock->sk->sk_security;
+
+       rcu_read_lock();
+       if (sksec->nlbl_state != NLBL_REQUIRE) {
+               rcu_read_unlock();
+               return 0;
+       }
+       local_bh_disable();
+       bh_lock_sock_nested(sock->sk);
+       rc = selinux_netlbl_socket_setsid(sock, sksec->sid);
+       bh_unlock_sock(sock->sk);
+       local_bh_enable();
+       rcu_read_unlock();
+
+       return rc;
+}
+
+/**
+ * selinux_netlbl_sock_rcv_skb - Do an inbound access check using NetLabel
+ * @sksec: the sock's sk_security_struct
+ * @skb: the packet
+ * @ad: the audit data
+ *
+ * Description:
+ * Fetch the NetLabel security attributes from @skb and perform an access check
+ * against the receiving socket.  Returns zero on success, negative values on
+ * error.
+ *
+ */
+int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
+                               struct sk_buff *skb,
+                               struct avc_audit_data *ad)
+{
+       int rc;
+       u32 netlbl_sid;
+       u32 recv_perm;
+
+       rc = selinux_netlbl_skbuff_getsid(skb,
+                                         SECINITSID_UNLABELED,
+                                         &netlbl_sid);
+       if (rc != 0)
+               return rc;
+
+       if (netlbl_sid == SECSID_NULL)
+               return 0;
+
+       switch (sksec->sclass) {
+       case SECCLASS_UDP_SOCKET:
+               recv_perm = UDP_SOCKET__RECVFROM;
+               break;
+       case SECCLASS_TCP_SOCKET:
+               recv_perm = TCP_SOCKET__RECVFROM;
+               break;
+       default:
+               recv_perm = RAWIP_SOCKET__RECVFROM;
+       }
+
+       rc = avc_has_perm(sksec->sid,
+                         netlbl_sid,
+                         sksec->sclass,
+                         recv_perm,
+                         ad);
+       if (rc == 0)
+               return 0;
+
+       netlbl_skbuff_err(skb, rc);
+       return rc;
+}
+
+/**
+ * selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel
+ * @sock: the socket
+ * @level: the socket level or protocol
+ * @optname: the socket option name
+ *
+ * Description:
+ * Check the setsockopt() call and if the user is trying to replace the IP
+ * options on a socket and a NetLabel is in place for the socket deny the
+ * access; otherwise allow the access.  Returns zero when the access is
+ * allowed, -EACCES when denied, and other negative values on error.
+ *
+ */
+int selinux_netlbl_socket_setsockopt(struct socket *sock,
+                                    int level,
+                                    int optname)
+{
+       int rc = 0;
+       struct sk_security_struct *sksec = sock->sk->sk_security;
+       struct netlbl_lsm_secattr secattr;
+
+       rcu_read_lock();
+       if (level == IPPROTO_IP && optname == IP_OPTIONS &&
+           sksec->nlbl_state == NLBL_LABELED) {
+               netlbl_secattr_init(&secattr);
+               rc = netlbl_socket_getattr(sock, &secattr);
+               if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
+                       rc = -EACCES;
+               netlbl_secattr_destroy(&secattr);
+       }
+       rcu_read_unlock();
+
+       return rc;
+}
+#endif /* CONFIG_NETLABEL */