tree-wide: fix assorted typos all over the place
[safe/jmp/linux-2.6] / security / selinux / ss / services.c
index 988079f..5914eeb 100644 (file)
@@ -2,7 +2,7 @@
  * Implementation of the security services.
  *
  * Authors : Stephen Smalley, <sds@epoch.ncsc.mil>
- *           James Morris <jmorris@redhat.com>
+ *          James Morris <jmorris@redhat.com>
  *
  * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
  *
  *
  * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
  *
- *     Added conditional policy language extensions
+ *     Added conditional policy language extensions
  *
  * Updated: Hewlett-Packard <paul.moore@hp.com>
  *
  *      Added support for NetLabel
+ *      Added support for the policy capability bitmap
  *
- * Copyright (C) 2006 Hewlett-Packard Development Company, L.P.
+ * Updated: Chad Sellers <csellers@tresys.com>
+ *
+ *  Added validation of kernel classes and permissions
+ *
+ * Updated: KaiGai Kohei <kaigai@ak.jp.nec.com>
+ *
+ *  Added support for bounds domain and audit messaged on masked permissions
+ *
+ * Copyright (C) 2008, 2009 NEC Corporation
+ * Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P.
  * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
- * Copyright (C) 2003 - 2004 Tresys Technology, LLC
+ * 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
+ *     it under the terms of the GNU General Public License as published by
  *     the Free Software Foundation, version 2.
  */
 #include <linux/kernel.h>
 #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 <linux/mutex.h>
-#include <net/sock.h>
+#include <linux/selinux.h>
 #include <net/netlabel.h>
 
 #include "flask.h"
 #include "conditional.h"
 #include "mls.h"
 #include "objsec.h"
-#include "selinux_netlabel.h"
+#include "netlabel.h"
+#include "xfrm.h"
+#include "ebitmap.h"
+#include "audit.h"
 
 extern void selnl_notify_policyload(u32 seqno);
 unsigned int policydb_loaded_version;
 
-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)
+int selinux_policycap_netpeer;
+int selinux_policycap_openperm;
+
+/*
+ * This is declared in avc.c
+ */
+extern const struct selinux_class_perm selinux_class_perm;
 
-static DEFINE_MUTEX(load_mutex);
-#define LOAD_LOCK mutex_lock(&load_mutex)
-#define LOAD_UNLOCK mutex_unlock(&load_mutex)
+static DEFINE_RWLOCK(policy_rwlock);
 
 static struct sidtab sidtab;
 struct policydb policydb;
-int ss_initialized = 0;
+int ss_initialized;
 
 /*
  * The largest sequence number that has been used when
@@ -73,12 +87,17 @@ int ss_initialized = 0;
  * The sequence number only changes when a policy change
  * occurs.
  */
-static u32 latest_granting = 0;
+static u32 latest_granting;
 
 /* Forward declaration. */
 static int context_struct_to_string(struct context *context, char **scontext,
                                    u32 *scontext_len);
 
+static int context_struct_compute_av(struct context *scontext,
+                                    struct context *tcontext,
+                                    u16 tclass,
+                                    u32 requested,
+                                    struct av_decision *avd);
 /*
  * Return the boolean value of a constraint expression
  * when it is applied to the specified source and target
@@ -146,10 +165,10 @@ static int constraint_expr_eval(struct context *scontext,
                                                                  val1 - 1);
                                        continue;
                                case CEXPR_INCOMP:
-                                       s[++sp] = ( !ebitmap_get_bit(&r1->dominates,
-                                                                    val2 - 1) &&
-                                                   !ebitmap_get_bit(&r2->dominates,
-                                                                    val1 - 1) );
+                                       s[++sp] = (!ebitmap_get_bit(&r1->dominates,
+                                                                   val2 - 1) &&
+                                                  !ebitmap_get_bit(&r2->dominates,
+                                                                   val1 - 1));
                                        continue;
                                default:
                                        break;
@@ -265,6 +284,173 @@ mls_ops:
 }
 
 /*
+ * security_dump_masked_av - dumps masked permissions during
+ * security_compute_av due to RBAC, MLS/Constraint and Type bounds.
+ */
+static int dump_masked_av_helper(void *k, void *d, void *args)
+{
+       struct perm_datum *pdatum = d;
+       char **permission_names = args;
+
+       BUG_ON(pdatum->value < 1 || pdatum->value > 32);
+
+       permission_names[pdatum->value - 1] = (char *)k;
+
+       return 0;
+}
+
+static void security_dump_masked_av(struct context *scontext,
+                                   struct context *tcontext,
+                                   u16 tclass,
+                                   u32 permissions,
+                                   const char *reason)
+{
+       struct common_datum *common_dat;
+       struct class_datum *tclass_dat;
+       struct audit_buffer *ab;
+       char *tclass_name;
+       char *scontext_name = NULL;
+       char *tcontext_name = NULL;
+       char *permission_names[32];
+       int index, length;
+       bool need_comma = false;
+
+       if (!permissions)
+               return;
+
+       tclass_name = policydb.p_class_val_to_name[tclass - 1];
+       tclass_dat = policydb.class_val_to_struct[tclass - 1];
+       common_dat = tclass_dat->comdatum;
+
+       /* init permission_names */
+       if (common_dat &&
+           hashtab_map(common_dat->permissions.table,
+                       dump_masked_av_helper, permission_names) < 0)
+               goto out;
+
+       if (hashtab_map(tclass_dat->permissions.table,
+                       dump_masked_av_helper, permission_names) < 0)
+               goto out;
+
+       /* get scontext/tcontext in text form */
+       if (context_struct_to_string(scontext,
+                                    &scontext_name, &length) < 0)
+               goto out;
+
+       if (context_struct_to_string(tcontext,
+                                    &tcontext_name, &length) < 0)
+               goto out;
+
+       /* audit a message */
+       ab = audit_log_start(current->audit_context,
+                            GFP_ATOMIC, AUDIT_SELINUX_ERR);
+       if (!ab)
+               goto out;
+
+       audit_log_format(ab, "op=security_compute_av reason=%s "
+                        "scontext=%s tcontext=%s tclass=%s perms=",
+                        reason, scontext_name, tcontext_name, tclass_name);
+
+       for (index = 0; index < 32; index++) {
+               u32 mask = (1 << index);
+
+               if ((mask & permissions) == 0)
+                       continue;
+
+               audit_log_format(ab, "%s%s",
+                                need_comma ? "," : "",
+                                permission_names[index]
+                                ? permission_names[index] : "????");
+               need_comma = true;
+       }
+       audit_log_end(ab);
+out:
+       /* release scontext/tcontext */
+       kfree(tcontext_name);
+       kfree(scontext_name);
+
+       return;
+}
+
+/*
+ * security_boundary_permission - drops violated permissions
+ * on boundary constraint.
+ */
+static void type_attribute_bounds_av(struct context *scontext,
+                                    struct context *tcontext,
+                                    u16 tclass,
+                                    u32 requested,
+                                    struct av_decision *avd)
+{
+       struct context lo_scontext;
+       struct context lo_tcontext;
+       struct av_decision lo_avd;
+       struct type_datum *source
+               = policydb.type_val_to_struct[scontext->type - 1];
+       struct type_datum *target
+               = policydb.type_val_to_struct[tcontext->type - 1];
+       u32 masked = 0;
+
+       if (source->bounds) {
+               memset(&lo_avd, 0, sizeof(lo_avd));
+
+               memcpy(&lo_scontext, scontext, sizeof(lo_scontext));
+               lo_scontext.type = source->bounds;
+
+               context_struct_compute_av(&lo_scontext,
+                                         tcontext,
+                                         tclass,
+                                         requested,
+                                         &lo_avd);
+               if ((lo_avd.allowed & avd->allowed) == avd->allowed)
+                       return;         /* no masked permission */
+               masked = ~lo_avd.allowed & avd->allowed;
+       }
+
+       if (target->bounds) {
+               memset(&lo_avd, 0, sizeof(lo_avd));
+
+               memcpy(&lo_tcontext, tcontext, sizeof(lo_tcontext));
+               lo_tcontext.type = target->bounds;
+
+               context_struct_compute_av(scontext,
+                                         &lo_tcontext,
+                                         tclass,
+                                         requested,
+                                         &lo_avd);
+               if ((lo_avd.allowed & avd->allowed) == avd->allowed)
+                       return;         /* no masked permission */
+               masked = ~lo_avd.allowed & avd->allowed;
+       }
+
+       if (source->bounds && target->bounds) {
+               memset(&lo_avd, 0, sizeof(lo_avd));
+               /*
+                * lo_scontext and lo_tcontext are already
+                * set up.
+                */
+
+               context_struct_compute_av(&lo_scontext,
+                                         &lo_tcontext,
+                                         tclass,
+                                         requested,
+                                         &lo_avd);
+               if ((lo_avd.allowed & avd->allowed) == avd->allowed)
+                       return;         /* no masked permission */
+               masked = ~lo_avd.allowed & avd->allowed;
+       }
+
+       if (masked) {
+               /* mask violated permissions */
+               avd->allowed &= ~masked;
+
+               /* audit masked permissions */
+               security_dump_masked_av(scontext, tcontext,
+                                       tclass, masked, "bounds");
+       }
+}
+
+/*
  * Compute access vectors based on a context structure pair for
  * the permissions in a particular class.
  */
@@ -281,6 +467,7 @@ static int context_struct_compute_av(struct context *scontext,
        struct class_datum *tclass_datum;
        struct ebitmap *sattr, *tattr;
        struct ebitmap_node *snode, *tnode;
+       const struct selinux_class_perm *kdefs = &selinux_class_perm;
        unsigned int i, j;
 
        /*
@@ -294,21 +481,44 @@ static int context_struct_compute_av(struct context *scontext,
                    tclass <= SECCLASS_NETLINK_DNRT_SOCKET)
                        tclass = SECCLASS_NETLINK_SOCKET;
 
-       if (!tclass || tclass > policydb.p_classes.nprim) {
-               printk(KERN_ERR "security_compute_av:  unrecognized class %d\n",
-                      tclass);
-               return -EINVAL;
-       }
-       tclass_datum = policydb.class_val_to_struct[tclass - 1];
-
        /*
         * Initialize the access vectors to the default values.
         */
        avd->allowed = 0;
-       avd->decided = 0xffffffff;
        avd->auditallow = 0;
        avd->auditdeny = 0xffffffff;
        avd->seqno = latest_granting;
+       avd->flags = 0;
+
+       /*
+        * Check for all the invalid cases.
+        * - tclass 0
+        * - tclass > policy and > kernel
+        * - tclass > policy but is a userspace class
+        * - tclass > policy but we do not allow unknowns
+        */
+       if (unlikely(!tclass))
+               goto inval_class;
+       if (unlikely(tclass > policydb.p_classes.nprim))
+               if (tclass > kdefs->cts_len ||
+                   !kdefs->class_to_string[tclass] ||
+                   !policydb.allow_unknown)
+                       goto inval_class;
+
+       /*
+        * Kernel class and we allow unknown so pad the allow decision
+        * the pad will be all 1 for unknown classes.
+        */
+       if (tclass <= kdefs->cts_len && policydb.allow_unknown)
+               avd->allowed = policydb.undefined_perms[tclass - 1];
+
+       /*
+        * Not in policy. Since decision is completed (all 1 or all 0) return.
+        */
+       if (unlikely(tclass > policydb.p_classes.nprim))
+               return 0;
+
+       tclass_datum = policydb.class_val_to_struct[tclass - 1];
 
        /*
         * If a specific type enforcement rule was defined for
@@ -318,16 +528,12 @@ static int context_struct_compute_av(struct context *scontext,
        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;
+       ebitmap_for_each_positive_bit(sattr, snode, i) {
+               ebitmap_for_each_positive_bit(tattr, tnode, j) {
                        avkey.source_type = i + 1;
                        avkey.target_type = j + 1;
                        for (node = avtab_search_node(&policydb.te_avtab, &avkey);
-                            node != NULL;
+                            node;
                             node = avtab_search_node_next(node, avkey.specified)) {
                                if (node->key.specified == AVTAB_ALLOWED)
                                        avd->allowed |= node->datum.data;
@@ -352,7 +558,7 @@ static int context_struct_compute_av(struct context *scontext,
                if ((constraint->permissions & (avd->allowed)) &&
                    !constraint_expr_eval(scontext, tcontext, NULL,
                                          constraint->expr)) {
-                       avd->allowed = (avd->allowed) & ~(constraint->permissions);
+                       avd->allowed &= ~(constraint->permissions);
                }
                constraint = constraint->next;
        }
@@ -371,17 +577,40 @@ static int context_struct_compute_av(struct context *scontext,
                                break;
                }
                if (!ra)
-                       avd->allowed = (avd->allowed) & ~(PROCESS__TRANSITION |
-                                                       PROCESS__DYNTRANSITION);
+                       avd->allowed &= ~(PROCESS__TRANSITION |
+                                         PROCESS__DYNTRANSITION);
+       }
+
+       /*
+        * If the given source and target types have boundary
+        * constraint, lazy checks have to mask any violated
+        * permission and notice it to userspace via audit.
+        */
+       type_attribute_bounds_av(scontext, tcontext,
+                                tclass, requested, avd);
+
+       return 0;
+
+inval_class:
+       if (!tclass || tclass > kdefs->cts_len ||
+           !kdefs->class_to_string[tclass]) {
+               if (printk_ratelimit())
+                       printk(KERN_ERR "SELinux: %s:  unrecognized class %d\n",
+                              __func__, tclass);
+               return -EINVAL;
        }
 
+       /*
+        * Known to the kernel, but not to the policy.
+        * Handle as a denial (allowed is 0).
+        */
        return 0;
 }
 
 static int security_validtrans_handle_fail(struct context *ocontext,
-                                           struct context *ncontext,
-                                           struct context *tcontext,
-                                           u16 tclass)
+                                          struct context *ncontext,
+                                          struct context *tcontext,
+                                          u16 tclass)
 {
        char *o = NULL, *n = NULL, *t = NULL;
        u32 olen, nlen, tlen;
@@ -393,9 +622,9 @@ static int security_validtrans_handle_fail(struct context *ocontext,
        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]);
+                 "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);
@@ -407,7 +636,7 @@ out:
 }
 
 int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
-                                 u16 tclass)
+                                u16 tclass)
 {
        struct context *ocontext;
        struct context *ncontext;
@@ -419,7 +648,7 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
        if (!ss_initialized)
                return 0;
 
-       POLICY_RDLOCK;
+       read_lock(&policy_rwlock);
 
        /*
         * Remap extended Netlink classes for old policy versions.
@@ -433,8 +662,8 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
                        tclass = SECCLASS_NETLINK_SOCKET;
 
        if (!tclass || tclass > policydb.p_classes.nprim) {
-               printk(KERN_ERR "security_validate_transition:  "
-                      "unrecognized class %d\n", tclass);
+               printk(KERN_ERR "SELinux: %s:  unrecognized class %d\n",
+                       __func__, tclass);
                rc = -EINVAL;
                goto out;
        }
@@ -442,24 +671,24 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
 
        ocontext = sidtab_search(&sidtab, oldsid);
        if (!ocontext) {
-               printk(KERN_ERR "security_validate_transition: "
-                      " unrecognized SID %d\n", oldsid);
+               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
+                       __func__, oldsid);
                rc = -EINVAL;
                goto out;
        }
 
        ncontext = sidtab_search(&sidtab, newsid);
        if (!ncontext) {
-               printk(KERN_ERR "security_validate_transition: "
-                      " unrecognized SID %d\n", newsid);
+               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
+                       __func__, newsid);
                rc = -EINVAL;
                goto out;
        }
 
        tcontext = sidtab_search(&sidtab, tasksid);
        if (!tcontext) {
-               printk(KERN_ERR "security_validate_transition: "
-                      " unrecognized SID %d\n", tasksid);
+               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
+                       __func__, tasksid);
                rc = -EINVAL;
                goto out;
        }
@@ -467,19 +696,102 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
        constraint = tclass_datum->validatetrans;
        while (constraint) {
                if (!constraint_expr_eval(ocontext, ncontext, tcontext,
-                                         constraint->expr)) {
+                                         constraint->expr)) {
                        rc = security_validtrans_handle_fail(ocontext, ncontext,
-                                                            tcontext, tclass);
+                                                            tcontext, tclass);
                        goto out;
                }
                constraint = constraint->next;
        }
 
 out:
-       POLICY_RDUNLOCK;
+       read_unlock(&policy_rwlock);
+       return rc;
+}
+
+/*
+ * security_bounded_transition - check whether the given
+ * transition is directed to bounded, or not.
+ * It returns 0, if @newsid is bounded by @oldsid.
+ * Otherwise, it returns error code.
+ *
+ * @oldsid : current security identifier
+ * @newsid : destinated security identifier
+ */
+int security_bounded_transition(u32 old_sid, u32 new_sid)
+{
+       struct context *old_context, *new_context;
+       struct type_datum *type;
+       int index;
+       int rc = -EINVAL;
+
+       read_lock(&policy_rwlock);
+
+       old_context = sidtab_search(&sidtab, old_sid);
+       if (!old_context) {
+               printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n",
+                      __func__, old_sid);
+               goto out;
+       }
+
+       new_context = sidtab_search(&sidtab, new_sid);
+       if (!new_context) {
+               printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n",
+                      __func__, new_sid);
+               goto out;
+       }
+
+       /* type/domain unchanged */
+       if (old_context->type == new_context->type) {
+               rc = 0;
+               goto out;
+       }
+
+       index = new_context->type;
+       while (true) {
+               type = policydb.type_val_to_struct[index - 1];
+               BUG_ON(!type);
+
+               /* not bounded anymore */
+               if (!type->bounds) {
+                       rc = -EPERM;
+                       break;
+               }
+
+               /* @newsid is bounded by @oldsid */
+               if (type->bounds == old_context->type) {
+                       rc = 0;
+                       break;
+               }
+               index = type->bounds;
+       }
+
+       if (rc) {
+               char *old_name = NULL;
+               char *new_name = NULL;
+               int length;
+
+               if (!context_struct_to_string(old_context,
+                                             &old_name, &length) &&
+                   !context_struct_to_string(new_context,
+                                             &new_name, &length)) {
+                       audit_log(current->audit_context,
+                                 GFP_ATOMIC, AUDIT_SELINUX_ERR,
+                                 "op=security_bounded_transition "
+                                 "result=denied "
+                                 "oldcontext=%s newcontext=%s",
+                                 old_name, new_name);
+               }
+               kfree(new_name);
+               kfree(old_name);
+       }
+out:
+       read_unlock(&policy_rwlock);
+
        return rc;
 }
 
+
 /**
  * security_compute_av - Compute access vector decisions.
  * @ssid: source security identifier
@@ -504,34 +816,37 @@ int security_compute_av(u32 ssid,
 
        if (!ss_initialized) {
                avd->allowed = 0xffffffff;
-               avd->decided = 0xffffffff;
                avd->auditallow = 0;
                avd->auditdeny = 0xffffffff;
                avd->seqno = latest_granting;
                return 0;
        }
 
-       POLICY_RDLOCK;
+       read_lock(&policy_rwlock);
 
        scontext = sidtab_search(&sidtab, ssid);
        if (!scontext) {
-               printk(KERN_ERR "security_compute_av:  unrecognized SID %d\n",
-                      ssid);
+               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
+                      __func__, ssid);
                rc = -EINVAL;
                goto out;
        }
        tcontext = sidtab_search(&sidtab, tsid);
        if (!tcontext) {
-               printk(KERN_ERR "security_compute_av:  unrecognized SID %d\n",
-                      tsid);
+               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
+                      __func__, tsid);
                rc = -EINVAL;
                goto out;
        }
 
        rc = context_struct_compute_av(scontext, tcontext, tclass,
                                       requested, avd);
+
+       /* permissive domain? */
+       if (ebitmap_get_bit(&policydb.permissive_map, scontext->type))
+           avd->flags |= AVD_FLAGS_PERMISSIVE;
 out:
-       POLICY_RDUNLOCK;
+       read_unlock(&policy_rwlock);
        return rc;
 }
 
@@ -549,6 +864,14 @@ static int context_struct_to_string(struct context *context, char **scontext, u3
        *scontext = NULL;
        *scontext_len = 0;
 
+       if (context->len) {
+               *scontext_len = context->len;
+               *scontext = kstrdup(context->str, GFP_ATOMIC);
+               if (!(*scontext))
+                       return -ENOMEM;
+               return 0;
+       }
+
        /* Compute the size of the context. */
        *scontext_len += strlen(policydb.p_user_val_to_name[context->user - 1]) + 1;
        *scontext_len += strlen(policydb.p_role_val_to_name[context->role - 1]) + 1;
@@ -557,9 +880,8 @@ static int context_struct_to_string(struct context *context, char **scontext, u3
 
        /* Allocate space for the context; caller must free this space. */
        scontextp = kmalloc(*scontext_len, GFP_ATOMIC);
-       if (!scontextp) {
+       if (!scontextp)
                return -ENOMEM;
-       }
        *scontext = scontextp;
 
        /*
@@ -570,8 +892,8 @@ static int context_struct_to_string(struct context *context, char **scontext, u3
                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 + strlen(policydb.p_role_val_to_name[context->role - 1]) +
+                    1 + strlen(policydb.p_type_val_to_name[context->type - 1]);
 
        mls_sid_to_context(context, &scontextp);
 
@@ -582,27 +904,28 @@ static int context_struct_to_string(struct context *context, char **scontext, u3
 
 #include "initial_sid_to_string.h"
 
-/**
- * security_sid_to_context - Obtain a context for a given SID.
- * @sid: security identifier, SID
- * @scontext: security context
- * @scontext_len: length in bytes
- *
- * Write the string representation of the context associated with @sid
- * into a dynamically allocated string of the correct size.  Set @scontext
- * to point to this string and set @scontext_len to the length of the string.
- */
-int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len)
+const char *security_get_initial_sid_context(u32 sid)
+{
+       if (unlikely(sid > SECINITSID_NUM))
+               return NULL;
+       return initial_sid_to_string[sid];
+}
+
+static int security_sid_to_context_core(u32 sid, char **scontext,
+                                       u32 *scontext_len, int force)
 {
        struct context *context;
        int rc = 0;
 
+       *scontext = NULL;
+       *scontext_len  = 0;
+
        if (!ss_initialized) {
                if (sid <= SECINITSID_NUM) {
                        char *scontextp;
 
                        *scontext_len = strlen(initial_sid_to_string[sid]) + 1;
-                       scontextp = kmalloc(*scontext_len,GFP_ATOMIC);
+                       scontextp = kmalloc(*scontext_len, GFP_ATOMIC);
                        if (!scontextp) {
                                rc = -ENOMEM;
                                goto out;
@@ -611,73 +934,72 @@ int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len)
                        *scontext = scontextp;
                        goto out;
                }
-               printk(KERN_ERR "security_sid_to_context:  called before initial "
-                      "load_policy on unknown SID %d\n", sid);
+               printk(KERN_ERR "SELinux: %s:  called before initial "
+                      "load_policy on unknown SID %d\n", __func__, sid);
                rc = -EINVAL;
                goto out;
        }
-       POLICY_RDLOCK;
-       context = sidtab_search(&sidtab, sid);
+       read_lock(&policy_rwlock);
+       if (force)
+               context = sidtab_search_force(&sidtab, sid);
+       else
+               context = sidtab_search(&sidtab, sid);
        if (!context) {
-               printk(KERN_ERR "security_sid_to_context:  unrecognized SID "
-                      "%d\n", sid);
+               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
+                       __func__, sid);
                rc = -EINVAL;
                goto out_unlock;
        }
        rc = context_struct_to_string(context, scontext, scontext_len);
 out_unlock:
-       POLICY_RDUNLOCK;
+       read_unlock(&policy_rwlock);
 out:
        return rc;
 
 }
 
-static int security_context_to_sid_core(char *scontext, u32 scontext_len, u32 *sid, u32 def_sid)
+/**
+ * security_sid_to_context - Obtain a context for a given SID.
+ * @sid: security identifier, SID
+ * @scontext: security context
+ * @scontext_len: length in bytes
+ *
+ * Write the string representation of the context associated with @sid
+ * into a dynamically allocated string of the correct size.  Set @scontext
+ * to point to this string and set @scontext_len to the length of the string.
+ */
+int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len)
+{
+       return security_sid_to_context_core(sid, scontext, scontext_len, 0);
+}
+
+int security_sid_to_context_force(u32 sid, char **scontext, u32 *scontext_len)
+{
+       return security_sid_to_context_core(sid, scontext, scontext_len, 1);
+}
+
+/*
+ * Caveat:  Mutates scontext.
+ */
+static int string_to_context_struct(struct policydb *pol,
+                                   struct sidtab *sidtabp,
+                                   char *scontext,
+                                   u32 scontext_len,
+                                   struct context *ctx,
+                                   u32 def_sid)
 {
-       char *scontext2;
-       struct context context;
        struct role_datum *role;
        struct type_datum *typdatum;
        struct user_datum *usrdatum;
        char *scontextp, *p, oldc;
        int rc = 0;
 
-       if (!ss_initialized) {
-               int i;
-
-               for (i = 1; i < SECINITSID_NUM; i++) {
-                       if (!strcmp(initial_sid_to_string[i], scontext)) {
-                               *sid = i;
-                               goto out;
-                       }
-               }
-               *sid = SECINITSID_KERNEL;
-               goto out;
-       }
-       *sid = SECSID_NULL;
-
-       /* Copy the string so that we can modify the copy as we parse it.
-          The string should already by null terminated, but we append a
-          null suffix to the copy to avoid problems with the existing
-          attr package, which doesn't view the null terminator as part
-          of the attribute value. */
-       scontext2 = kmalloc(scontext_len+1,GFP_KERNEL);
-       if (!scontext2) {
-               rc = -ENOMEM;
-               goto out;
-       }
-       memcpy(scontext2, scontext, scontext_len);
-       scontext2[scontext_len] = 0;
-
-       context_init(&context);
-       *sid = SECSID_NULL;
-
-       POLICY_RDLOCK;
+       context_init(ctx);
 
        /* Parse the security context. */
 
        rc = -EINVAL;
-       scontextp = (char *) scontext2;
+       scontextp = (char *) scontext;
 
        /* Extract the user. */
        p = scontextp;
@@ -685,15 +1007,15 @@ static int security_context_to_sid_core(char *scontext, u32 scontext_len, u32 *s
                p++;
 
        if (*p == 0)
-               goto out_unlock;
+               goto out;
 
        *p++ = 0;
 
-       usrdatum = hashtab_search(policydb.p_users.table, scontextp);
+       usrdatum = hashtab_search(pol->p_users.table, scontextp);
        if (!usrdatum)
-               goto out_unlock;
+               goto out;
 
-       context.user = usrdatum->value;
+       ctx->user = usrdatum->value;
 
        /* Extract role. */
        scontextp = p;
@@ -701,14 +1023,14 @@ static int security_context_to_sid_core(char *scontext, u32 scontext_len, u32 *s
                p++;
 
        if (*p == 0)
-               goto out_unlock;
+               goto out;
 
        *p++ = 0;
 
-       role = hashtab_search(policydb.p_roles.table, scontextp);
+       role = hashtab_search(pol->p_roles.table, scontextp);
        if (!role)
-               goto out_unlock;
-       context.role = role->value;
+               goto out;
+       ctx->role = role->value;
 
        /* Extract type. */
        scontextp = p;
@@ -717,33 +1039,87 @@ static int security_context_to_sid_core(char *scontext, u32 scontext_len, u32 *s
        oldc = *p;
        *p++ = 0;
 
-       typdatum = hashtab_search(policydb.p_types.table, scontextp);
-       if (!typdatum)
-               goto out_unlock;
+       typdatum = hashtab_search(pol->p_types.table, scontextp);
+       if (!typdatum || typdatum->attribute)
+               goto out;
 
-       context.type = typdatum->value;
+       ctx->type = typdatum->value;
 
-       rc = mls_context_to_sid(oldc, &p, &context, &sidtab, def_sid);
+       rc = mls_context_to_sid(pol, oldc, &p, ctx, sidtabp, def_sid);
        if (rc)
-               goto out_unlock;
+               goto out;
 
-       if ((p - scontext2) < scontext_len) {
+       if ((p - scontext) < scontext_len) {
                rc = -EINVAL;
-               goto out_unlock;
+               goto out;
        }
 
        /* Check the validity of the new context. */
-       if (!policydb_context_isvalid(&policydb, &context)) {
+       if (!policydb_context_isvalid(pol, ctx)) {
                rc = -EINVAL;
-               goto out_unlock;
+               goto out;
+       }
+       rc = 0;
+out:
+       if (rc)
+               context_destroy(ctx);
+       return rc;
+}
+
+static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
+                                       u32 *sid, u32 def_sid, gfp_t gfp_flags,
+                                       int force)
+{
+       char *scontext2, *str = NULL;
+       struct context context;
+       int rc = 0;
+
+       if (!ss_initialized) {
+               int i;
+
+               for (i = 1; i < SECINITSID_NUM; i++) {
+                       if (!strcmp(initial_sid_to_string[i], scontext)) {
+                               *sid = i;
+                               return 0;
+                       }
+               }
+               *sid = SECINITSID_KERNEL;
+               return 0;
+       }
+       *sid = SECSID_NULL;
+
+       /* Copy the string so that we can modify the copy as we parse it. */
+       scontext2 = kmalloc(scontext_len+1, gfp_flags);
+       if (!scontext2)
+               return -ENOMEM;
+       memcpy(scontext2, scontext, scontext_len);
+       scontext2[scontext_len] = 0;
+
+       if (force) {
+               /* Save another copy for storing in uninterpreted form */
+               str = kstrdup(scontext2, gfp_flags);
+               if (!str) {
+                       kfree(scontext2);
+                       return -ENOMEM;
+               }
        }
-       /* Obtain the new sid. */
+
+       read_lock(&policy_rwlock);
+       rc = string_to_context_struct(&policydb, &sidtab,
+                                     scontext2, scontext_len,
+                                     &context, def_sid);
+       if (rc == -EINVAL && force) {
+               context.str = str;
+               context.len = scontext_len;
+               str = NULL;
+       } else if (rc)
+               goto out;
        rc = sidtab_context_to_sid(&sidtab, &context, sid);
-out_unlock:
-       POLICY_RDUNLOCK;
        context_destroy(&context);
-       kfree(scontext2);
 out:
+       read_unlock(&policy_rwlock);
+       kfree(scontext2);
+       kfree(str);
        return rc;
 }
 
@@ -758,10 +1134,10 @@ out:
  * 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)
+int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *sid)
 {
        return security_context_to_sid_core(scontext, scontext_len,
-                                           sid, SECSID_NULL);
+                                           sid, SECSID_NULL, GFP_KERNEL, 0);
 }
 
 /**
@@ -771,20 +1147,29 @@ int security_context_to_sid(char *scontext, u32 scontext_len, u32 *sid)
  * @scontext: security context
  * @scontext_len: length in bytes
  * @sid: security identifier, SID
- * @def_sid: default SID to assign on errror
+ * @def_sid: default SID to assign on error
  *
  * 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).
+ * Implicitly forces adding of the context even if it cannot be mapped yet.
  * 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)
+int security_context_to_sid_default(const char *scontext, u32 scontext_len,
+                                   u32 *sid, u32 def_sid, gfp_t gfp_flags)
+{
+       return security_context_to_sid_core(scontext, scontext_len,
+                                           sid, def_sid, gfp_flags, 1);
+}
+
+int security_context_to_sid_force(const char *scontext, u32 scontext_len,
+                                 u32 *sid)
 {
        return security_context_to_sid_core(scontext, scontext_len,
-                                           sid, def_sid);
+                                           sid, SECSID_NULL, GFP_KERNEL, 1);
 }
 
 static int compute_sid_handle_invalid_context(
@@ -844,19 +1229,19 @@ static int security_compute_sid(u32 ssid,
 
        context_init(&newcontext);
 
-       POLICY_RDLOCK;
+       read_lock(&policy_rwlock);
 
        scontext = sidtab_search(&sidtab, ssid);
        if (!scontext) {
-               printk(KERN_ERR "security_compute_sid:  unrecognized SID %d\n",
-                      ssid);
+               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
+                      __func__, ssid);
                rc = -EINVAL;
                goto out_unlock;
        }
        tcontext = sidtab_search(&sidtab, tsid);
        if (!tcontext) {
-               printk(KERN_ERR "security_compute_sid:  unrecognized SID %d\n",
-                      tsid);
+               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
+                      __func__, tsid);
                rc = -EINVAL;
                goto out_unlock;
        }
@@ -896,9 +1281,9 @@ static int security_compute_sid(u32 ssid,
        avdatum = avtab_search(&policydb.te_avtab, &avkey);
 
        /* If no permanent rule, also check for enabled conditional rules */
-       if(!avdatum) {
+       if (!avdatum) {
                node = avtab_search_node(&policydb.te_cond_avtab, &avkey);
-               for (; node != NULL; node = avtab_search_node_next(node, specified)) {
+               for (; node; node = avtab_search_node_next(node, specified)) {
                        if (node->key.specified & AVTAB_ENABLED) {
                                avdatum = &node->datum;
                                break;
@@ -949,7 +1334,7 @@ static int security_compute_sid(u32 ssid,
        /* Obtain the sid for the context. */
        rc = sidtab_context_to_sid(&sidtab, &newcontext, out_sid);
 out_unlock:
-       POLICY_RDUNLOCK;
+       read_unlock(&policy_rwlock);
        context_destroy(&newcontext);
 out:
        return rc;
@@ -1019,86 +1404,140 @@ 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.
+ * Verify that each kernel class that is defined in the
+ * policy is correct
  */
-static int validate_perm(void *key, void *datum, void *p)
+static int validate_classes(struct policydb *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;
+       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;
+       bool print_unknown_handle = 0;
+
+       if (p->allow_unknown) {
+               u32 num_classes = kdefs->cts_len;
+               p->undefined_perms = kcalloc(num_classes, sizeof(u32), GFP_KERNEL);
+               if (!p->undefined_perms)
+                       return -ENOMEM;
+       }
+
+       for (i = 1; i < kdefs->cts_len; i++) {
+               def_class = kdefs->class_to_string[i];
+               if (!def_class)
+                       continue;
+               if (i > p->p_classes.nprim) {
+                       printk(KERN_INFO
+                              "SELinux:  class %s not defined in policy\n",
+                              def_class);
+                       if (p->reject_unknown)
+                               return -EINVAL;
+                       if (p->allow_unknown)
+                               p->undefined_perms[i-1] = ~0U;
+                       print_unknown_handle = 1;
+                       continue;
+               }
+               pol_class = p->p_class_val_to_name[i-1];
+               if (strcmp(pol_class, def_class)) {
+                       printk(KERN_ERR
+                              "SELinux:  class %d is incorrect, found %s but should be %s\n",
+                              i, pol_class, def_class);
+                       return -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.
- */
-static int validate_class(void *key, void *datum, void *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;
-       }
-       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
+                              "SELinux:  permission %s in class %s not defined in policy\n",
+                              def_perm, pol_class);
+                       if (p->reject_unknown)
+                               return -EINVAL;
+                       if (p->allow_unknown)
+                               p->undefined_perms[class_val-1] |= perm_val;
+                       print_unknown_handle = 1;
+                       continue;
+               }
+               perdatum = hashtab_search(perms->table, def_perm);
+               if (perdatum == NULL) {
+                       printk(KERN_ERR
+                              "SELinux:  permission %s in class %s not found in policy, bad policy\n",
+                              def_perm, pol_class);
+                       return -EINVAL;
+               }
+               pol_val = 1 << (perdatum->value - 1);
+               if (pol_val != perm_val) {
+                       printk(KERN_ERR
+                              "SELinux:  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
+                              "SELinux:  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
+                                      "SELinux:  permission %s in class %s not defined in policy\n",
+                                      def_perm, pol_class);
+                               if (p->reject_unknown)
+                                       return -EINVAL;
+                               if (p->allow_unknown)
+                                       p->undefined_perms[class_val-1] |= (1 << j);
+                               print_unknown_handle = 1;
+                               continue;
+                       }
+                       perdatum = hashtab_search(perms->table, def_perm);
+                       if (perdatum == NULL) {
+                               printk(KERN_ERR
+                                      "SELinux:  permission %s in class %s not found in policy, bad policy\n",
+                                      def_perm, pol_class);
+                               return -EINVAL;
+                       }
+                       if (perdatum->value != j + 1) {
+                               printk(KERN_ERR
+                                      "SELinux:  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;
+       if (print_unknown_handle)
+               printk(KERN_INFO "SELinux: the above unknown classes and permissions will be %s\n",
+                       (security_get_allow_unknown() ? "allowed" : "denied"));
+       return 0;
 }
 
 /* Clone the SID into the new SID table. */
@@ -1121,9 +1560,12 @@ static inline int convert_context_handle_invalid_context(struct context *context
                char *s;
                u32 len;
 
-               context_struct_to_string(context, &s, &len);
-               printk(KERN_ERR "security:  context %s is invalid\n", s);
-               kfree(s);
+               if (!context_struct_to_string(context, &s, &len)) {
+                       printk(KERN_WARNING
+                      "SELinux:  Context %s would be invalid if enforcing\n",
+                              s);
+                       kfree(s);
+               }
        }
        return rc;
 }
@@ -1155,6 +1597,37 @@ static int convert_context(u32 key,
 
        args = p;
 
+       if (c->str) {
+               struct context ctx;
+               s = kstrdup(c->str, GFP_KERNEL);
+               if (!s) {
+                       rc = -ENOMEM;
+                       goto out;
+               }
+               rc = string_to_context_struct(args->newp, NULL, s,
+                                             c->len, &ctx, SECSID_NULL);
+               kfree(s);
+               if (!rc) {
+                       printk(KERN_INFO
+                      "SELinux:  Context %s became valid (mapped).\n",
+                              c->str);
+                       /* Replace string with mapped representation. */
+                       kfree(c->str);
+                       memcpy(c, &ctx, sizeof(*c));
+                       goto out;
+               } else if (rc == -EINVAL) {
+                       /* Retain string representation for later mapping. */
+                       rc = 0;
+                       goto out;
+               } else {
+                       /* Other error condition, e.g. ENOMEM. */
+                       printk(KERN_ERR
+                      "SELinux:   Unable to map context %s, rc = %d.\n",
+                              c->str, -rc);
+                       goto out;
+               }
+       }
+
        rc = context_cpy(&oldc, c);
        if (rc)
                goto out;
@@ -1163,26 +1636,23 @@ static int convert_context(u32 key,
 
        /* Convert the user. */
        usrdatum = hashtab_search(args->newp->p_users.table,
-                                 args->oldp->p_user_val_to_name[c->user - 1]);
-       if (!usrdatum) {
+                                 args->oldp->p_user_val_to_name[c->user - 1]);
+       if (!usrdatum)
                goto bad;
-       }
        c->user = usrdatum->value;
 
        /* Convert the role. */
        role = hashtab_search(args->newp->p_roles.table,
-                             args->oldp->p_role_val_to_name[c->role - 1]);
-       if (!role) {
+                             args->oldp->p_role_val_to_name[c->role - 1]);
+       if (!role)
                goto bad;
-       }
        c->role = role->value;
 
        /* Convert the type. */
        typdatum = hashtab_search(args->newp->p_types.table,
-                                 args->oldp->p_type_val_to_name[c->type - 1]);
-       if (!typdatum) {
+                                 args->oldp->p_type_val_to_name[c->type - 1]);
+       if (!typdatum)
                goto bad;
-       }
        c->type = typdatum->value;
 
        rc = mls_convert_context(args->oldp, args->newp, c);
@@ -1197,17 +1667,34 @@ static int convert_context(u32 key,
        }
 
        context_destroy(&oldc);
+       rc = 0;
 out:
        return rc;
 bad:
-       context_struct_to_string(&oldc, &s, &len);
+       /* Map old representation to string and save it. */
+       if (context_struct_to_string(&oldc, &s, &len))
+               return -ENOMEM;
        context_destroy(&oldc);
-       printk(KERN_ERR "security:  invalidating context %s\n", s);
-       kfree(s);
+       context_destroy(c);
+       c->str = s;
+       c->len = len;
+       printk(KERN_INFO
+              "SELinux:  Context %s became invalid (unmapped).\n",
+              c->str);
+       rc = 0;
        goto out;
 }
 
+static void security_load_policycaps(void)
+{
+       selinux_policycap_netpeer = ebitmap_get_bit(&policydb.policycaps,
+                                                 POLICYDB_CAPABILITY_NETPEER);
+       selinux_policycap_openperm = ebitmap_get_bit(&policydb.policycaps,
+                                                 POLICYDB_CAPABILITY_OPENPERM);
+}
+
 extern void selinux_complete_init(void);
+static int security_preserve_bools(struct policydb *p);
 
 /**
  * security_load_policy - Load a security policy configuration.
@@ -1228,29 +1715,35 @@ int security_load_policy(void *data, size_t len)
        int rc = 0;
        struct policy_file file = { data, len }, *fp = &file;
 
-       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
+                              "SELinux:  the definition of a class is incorrect\n");
+                       sidtab_destroy(&sidtab);
+                       policydb_destroy(&policydb);
+                       avtab_cache_destroy();
+                       return -EINVAL;
+               }
+               security_load_policycaps();
                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;
        }
 
@@ -1258,21 +1751,28 @@ int security_load_policy(void *data, size_t len)
        sidtab_hash_eval(&sidtab, "sids");
 #endif
 
-       if (policydb_read(&newpolicydb, fp)) {
-               LOAD_UNLOCK;
+       if (policydb_read(&newpolicydb, fp))
                return -EINVAL;
-       }
 
-       sidtab_init(&newsidtab);
+       if (sidtab_init(&newsidtab)) {
+               policydb_destroy(&newpolicydb);
+               return -ENOMEM;
+       }
 
-       /* 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
+                      "SELinux:  the definition of a class is incorrect\n");
                rc = -EINVAL;
                goto err;
        }
 
+       rc = security_preserve_bools(&newpolicydb);
+       if (rc) {
+               printk(KERN_ERR "SELinux:  unable to preserve booleans\n");
+               goto err;
+       }
+
        /* Clone the SID table. */
        sidtab_shutdown(&sidtab);
        if (sidtab_map(&sidtab, clone_sid, &newsidtab)) {
@@ -1280,24 +1780,28 @@ int security_load_policy(void *data, size_t len)
                goto err;
        }
 
-       /* Convert the internal representations of contexts
-          in the new SID table and remove invalid SIDs. */
+       /*
+        * Convert the internal representations of contexts
+        * in the new SID table.
+        */
        args.oldp = &policydb;
        args.newp = &newpolicydb;
-       sidtab_map_remove_on_error(&newsidtab, convert_context, &args);
+       rc = sidtab_map(&newsidtab, convert_context, &args);
+       if (rc)
+               goto err;
 
        /* Save the old policydb and SID table to free later. */
        memcpy(&oldpolicydb, &policydb, sizeof policydb);
        sidtab_set(&oldsidtab, &sidtab);
 
        /* Install the new policydb and SID table. */
-       POLICY_WRLOCK;
+       write_lock_irq(&policy_rwlock);
        memcpy(&policydb, &newpolicydb, sizeof policydb);
        sidtab_set(&sidtab, &newsidtab);
+       security_load_policycaps();
        seqno = ++latest_granting;
        policydb_loaded_version = policydb.policyvers;
-       POLICY_WRUNLOCK;
-       LOAD_UNLOCK;
+       write_unlock_irq(&policy_rwlock);
 
        /* Free the old policydb and SID table. */
        policydb_destroy(&oldpolicydb);
@@ -1306,11 +1810,11 @@ 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;
 
 err:
-       LOAD_UNLOCK;
        sidtab_destroy(&newsidtab);
        policydb_destroy(&newpolicydb);
        return rc;
@@ -1319,22 +1823,16 @@ err:
 
 /**
  * security_port_sid - Obtain the SID for a port.
- * @domain: communication domain aka address family
- * @type: socket type
  * @protocol: protocol number
  * @port: port number
  * @out_sid: security identifier
  */
-int security_port_sid(u16 domain,
-                     u16 type,
-                     u8 protocol,
-                     u16 port,
-                     u32 *out_sid)
+int security_port_sid(u8 protocol, u16 port, u32 *out_sid)
 {
        struct ocontext *c;
        int rc = 0;
 
-       POLICY_RDLOCK;
+       read_lock(&policy_rwlock);
 
        c = policydb.ocontexts[OCON_PORT];
        while (c) {
@@ -1359,7 +1857,7 @@ int security_port_sid(u16 domain,
        }
 
 out:
-       POLICY_RDUNLOCK;
+       read_unlock(&policy_rwlock);
        return rc;
 }
 
@@ -1367,16 +1865,13 @@ out:
  * security_netif_sid - Obtain the SID for a network interface.
  * @name: interface name
  * @if_sid: interface SID
- * @msg_sid: default SID for received packets
  */
-int security_netif_sid(char *name,
-                      u32 *if_sid,
-                      u32 *msg_sid)
+int security_netif_sid(char *name, u32 *if_sid)
 {
        int rc = 0;
        struct ocontext *c;
 
-       POLICY_RDLOCK;
+       read_lock(&policy_rwlock);
 
        c = policydb.ocontexts[OCON_NETIF];
        while (c) {
@@ -1399,14 +1894,11 @@ int security_netif_sid(char *name,
                                goto out;
                }
                *if_sid = c->sid[0];
-               *msg_sid = c->sid[1];
-       } else {
+       } else
                *if_sid = SECINITSID_NETIF;
-               *msg_sid = SECINITSID_NETMSG;
-       }
 
 out:
-       POLICY_RDUNLOCK;
+       read_unlock(&policy_rwlock);
        return rc;
 }
 
@@ -1414,8 +1906,8 @@ static int match_ipv6_addrmask(u32 *input, u32 *addr, u32 *mask)
 {
        int i, fail = 0;
 
-       for(i = 0; i < 4; i++)
-               if(addr[i] != (input[i] & mask[i])) {
+       for (i = 0; i < 4; i++)
+               if (addr[i] != (input[i] & mask[i])) {
                        fail = 1;
                        break;
                }
@@ -1438,7 +1930,7 @@ int security_node_sid(u16 domain,
        int rc = 0;
        struct ocontext *c;
 
-       POLICY_RDLOCK;
+       read_lock(&policy_rwlock);
 
        switch (domain) {
        case AF_INET: {
@@ -1493,7 +1985,7 @@ int security_node_sid(u16 domain,
        }
 
 out:
-       POLICY_RDUNLOCK;
+       read_unlock(&policy_rwlock);
        return rc;
 }
 
@@ -1514,26 +2006,27 @@ out:
  */
 
 int security_get_user_sids(u32 fromsid,
-                          char *username,
+                          char *username,
                           u32 **sids,
                           u32 *nel)
 {
        struct context *fromcon, usercon;
-       u32 *mysids, *mysids2, sid;
+       u32 *mysids = NULL, *mysids2, sid;
        u32 mynel = 0, maxnel = SIDS_NEL;
        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) {
-               *sids = NULL;
-               *nel = 0;
+       *sids = NULL;
+       *nel = 0;
+
+       if (!ss_initialized)
                goto out;
-       }
 
-       POLICY_RDLOCK;
+       read_lock(&policy_rwlock);
+
+       context_init(&usercon);
 
        fromcon = sidtab_search(&sidtab, fromsid);
        if (!fromcon) {
@@ -1554,30 +2047,18 @@ int security_get_user_sids(u32 fromsid,
                goto out_unlock;
        }
 
-       ebitmap_for_each_bit(&user->roles, rnode, i) {
-               if (!ebitmap_node_get_bit(rnode, i))
-                       continue;
+       ebitmap_for_each_positive_bit(&user->roles, rnode, i) {
                role = policydb.role_val_to_struct[i];
                usercon.role = i+1;
-               ebitmap_for_each_bit(&role->types, tnode, j) {
-                       if (!ebitmap_node_get_bit(tnode, j))
-                               continue;
+               ebitmap_for_each_positive_bit(&role->types, tnode, j) {
                        usercon.type = j+1;
 
                        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);
+                       if (rc)
                                goto out_unlock;
-                       }
                        if (mynel < maxnel) {
                                mysids[mynel++] = sid;
                        } else {
@@ -1585,7 +2066,6 @@ int security_get_user_sids(u32 fromsid,
                                mysids2 = kcalloc(maxnel, sizeof(*mysids2), GFP_ATOMIC);
                                if (!mysids2) {
                                        rc = -ENOMEM;
-                                       kfree(mysids);
                                        goto out_unlock;
                                }
                                memcpy(mysids2, mysids, mynel * sizeof(*mysids2));
@@ -1596,11 +2076,32 @@ int security_get_user_sids(u32 fromsid,
                }
        }
 
-       *sids = mysids;
-       *nel = mynel;
-
 out_unlock:
-       POLICY_RDUNLOCK;
+       read_unlock(&policy_rwlock);
+       if (rc || !mynel) {
+               kfree(mysids);
+               goto out;
+       }
+
+       mysids2 = kcalloc(mynel, sizeof(*mysids2), GFP_KERNEL);
+       if (!mysids2) {
+               rc = -ENOMEM;
+               kfree(mysids);
+               goto out;
+       }
+       for (i = 0, j = 0; i < mynel; i++) {
+               rc = avc_has_perm_noaudit(fromsid, mysids[i],
+                                         SECCLASS_PROCESS,
+                                         PROCESS__TRANSITION, AVC_STRICT,
+                                         NULL);
+               if (!rc)
+                       mysids2[j++] = mysids[i];
+               cond_resched();
+       }
+       rc = 0;
+       kfree(mysids);
+       *sids = mysids2;
+       *nel = j;
 out:
        return rc;
 }
@@ -1617,7 +2118,7 @@ out:
  * transition SIDs or task SIDs.
  */
 int security_genfs_sid(const char *fstype,
-                      char *path,
+                      char *path,
                       u16 sclass,
                       u32 *sid)
 {
@@ -1626,7 +2127,10 @@ int security_genfs_sid(const char *fstype,
        struct ocontext *c;
        int rc = 0, cmp = 0;
 
-       POLICY_RDLOCK;
+       while (path[0] == '/' && path[1] == '/')
+               path++;
+
+       read_lock(&policy_rwlock);
 
        for (genfs = policydb.genfs; genfs; genfs = genfs->next) {
                cmp = strcmp(fstype, genfs->fstype);
@@ -1663,7 +2167,7 @@ int security_genfs_sid(const char *fstype,
 
        *sid = c->sid[0];
 out:
-       POLICY_RDUNLOCK;
+       read_unlock(&policy_rwlock);
        return rc;
 }
 
@@ -1681,7 +2185,7 @@ int security_fs_use(
        int rc = 0;
        struct ocontext *c;
 
-       POLICY_RDLOCK;
+       read_lock(&policy_rwlock);
 
        c = policydb.ocontexts[OCON_FSUSE];
        while (c) {
@@ -1711,7 +2215,7 @@ int security_fs_use(
        }
 
 out:
-       POLICY_RDUNLOCK;
+       read_unlock(&policy_rwlock);
        return rc;
 }
 
@@ -1719,7 +2223,7 @@ int security_get_bools(int *len, char ***names, int **values)
 {
        int i, rc = -ENOMEM;
 
-       POLICY_RDLOCK;
+       read_lock(&policy_rwlock);
        *names = NULL;
        *values = NULL;
 
@@ -1729,7 +2233,7 @@ int security_get_bools(int *len, char ***names, int **values)
                goto out;
        }
 
-       *names = kcalloc(*len, sizeof(char*), GFP_ATOMIC);
+       *names = kcalloc(*len, sizeof(char *), GFP_ATOMIC);
        if (!*names)
                goto err;
 
@@ -1741,7 +2245,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] = 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);
@@ -1749,7 +2253,7 @@ int security_get_bools(int *len, char ***names, int **values)
        }
        rc = 0;
 out:
-       POLICY_RDUNLOCK;
+       read_unlock(&policy_rwlock);
        return rc;
 err:
        if (*names) {
@@ -1767,7 +2271,7 @@ int security_set_bools(int len, int *values)
        int lenp, seqno = 0;
        struct cond_node *cur;
 
-       POLICY_WRLOCK;
+       write_lock_irq(&policy_rwlock);
 
        lenp = policydb.p_bools.nprim;
        if (len != lenp) {
@@ -1779,20 +2283,20 @@ int security_set_bools(int len, int *values)
                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",
+                               "bool=%s val=%d old_val=%d auid=%u ses=%u",
                                policydb.p_bool_val_to_name[i],
                                !!values[i],
                                policydb.bool_val_to_struct[i]->state,
-                               audit_get_loginuid(current->audit_context));
+                               audit_get_loginuid(current),
+                               audit_get_sessionid(current));
                }
-               if (values[i]) {
+               if (values[i])
                        policydb.bool_val_to_struct[i]->state = 1;
-               } else {
+               else
                        policydb.bool_val_to_struct[i]->state = 0;
-               }
        }
 
-       for (cur = policydb.cond_list; cur != NULL; cur = cur->next) {
+       for (cur = policydb.cond_list; cur; cur = cur->next) {
                rc = evaluate_cond_node(&policydb, cur);
                if (rc)
                        goto out;
@@ -1801,10 +2305,11 @@ int security_set_bools(int len, int *values)
        seqno = ++latest_granting;
 
 out:
-       POLICY_WRUNLOCK;
+       write_unlock_irq(&policy_rwlock);
        if (!rc) {
                avc_ss_reset(seqno);
                selnl_notify_policyload(seqno);
+               selinux_xfrm_notify_policyload();
        }
        return rc;
 }
@@ -1814,7 +2319,7 @@ int security_get_bool_value(int bool)
        int rc = 0;
        int len;
 
-       POLICY_RDLOCK;
+       read_lock(&policy_rwlock);
 
        len = policydb.p_bools.nprim;
        if (bool >= len) {
@@ -1824,7 +2329,38 @@ int security_get_bool_value(int bool)
 
        rc = policydb.bool_val_to_struct[bool]->state;
 out:
-       POLICY_RDUNLOCK;
+       read_unlock(&policy_rwlock);
+       return rc;
+}
+
+static int security_preserve_bools(struct policydb *p)
+{
+       int rc, nbools = 0, *bvalues = NULL, i;
+       char **bnames = NULL;
+       struct cond_bool_datum *booldatum;
+       struct cond_node *cur;
+
+       rc = security_get_bools(&nbools, &bnames, &bvalues);
+       if (rc)
+               goto out;
+       for (i = 0; i < nbools; i++) {
+               booldatum = hashtab_search(p->p_bools.table, bnames[i]);
+               if (booldatum)
+                       booldatum->state = bvalues[i];
+       }
+       for (cur = p->cond_list; cur; cur = cur->next) {
+               rc = evaluate_cond_node(p, cur);
+               if (rc)
+                       goto out;
+       }
+
+out:
+       if (bnames) {
+               for (i = 0; i < nbools; i++)
+                       kfree(bnames[i]);
+       }
+       kfree(bnames);
+       kfree(bvalues);
        return rc;
 }
 
@@ -1848,19 +2384,19 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid)
 
        context_init(&newcon);
 
-       POLICY_RDLOCK;
+       read_lock(&policy_rwlock);
        context1 = sidtab_search(&sidtab, sid);
        if (!context1) {
-               printk(KERN_ERR "security_sid_mls_copy:  unrecognized SID "
-                      "%d\n", sid);
+               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
+                       __func__, 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);
+               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
+                       __func__, mls_sid);
                rc = -EINVAL;
                goto out_unlock;
        }
@@ -1868,11 +2404,10 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid)
        newcon.user = context1->user;
        newcon.role = context1->role;
        newcon.type = context1->type;
-       rc = mls_copy_context(&newcon, context2);
+       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);
@@ -1891,38 +2426,249 @@ bad:
        }
 
 out_unlock:
-       POLICY_RDUNLOCK;
+       read_unlock(&policy_rwlock);
        context_destroy(&newcon);
 out:
        return rc;
 }
 
+/**
+ * security_net_peersid_resolve - Compare and resolve two network peer SIDs
+ * @nlbl_sid: NetLabel SID
+ * @nlbl_type: NetLabel labeling protocol type
+ * @xfrm_sid: XFRM SID
+ *
+ * Description:
+ * Compare the @nlbl_sid and @xfrm_sid values and if the two SIDs can be
+ * resolved into a single SID it is returned via @peer_sid and the function
+ * returns zero.  Otherwise @peer_sid is set to SECSID_NULL and the function
+ * returns a negative value.  A table summarizing the behavior is below:
+ *
+ *                                 | function return |      @sid
+ *   ------------------------------+-----------------+-----------------
+ *   no peer labels                |        0        |    SECSID_NULL
+ *   single peer label             |        0        |    <peer_label>
+ *   multiple, consistent labels   |        0        |    <peer_label>
+ *   multiple, inconsistent labels |    -<errno>     |    SECSID_NULL
+ *
+ */
+int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
+                                u32 xfrm_sid,
+                                u32 *peer_sid)
+{
+       int rc;
+       struct context *nlbl_ctx;
+       struct context *xfrm_ctx;
+
+       /* handle the common (which also happens to be the set of easy) cases
+        * right away, these two if statements catch everything involving a
+        * single or absent peer SID/label */
+       if (xfrm_sid == SECSID_NULL) {
+               *peer_sid = nlbl_sid;
+               return 0;
+       }
+       /* NOTE: an nlbl_type == NETLBL_NLTYPE_UNLABELED is a "fallback" label
+        * and is treated as if nlbl_sid == SECSID_NULL when a XFRM SID/label
+        * is present */
+       if (nlbl_sid == SECSID_NULL || nlbl_type == NETLBL_NLTYPE_UNLABELED) {
+               *peer_sid = xfrm_sid;
+               return 0;
+       }
+
+       /* we don't need to check ss_initialized here since the only way both
+        * nlbl_sid and xfrm_sid are not equal to SECSID_NULL would be if the
+        * security server was initialized and ss_initialized was true */
+       if (!selinux_mls_enabled) {
+               *peer_sid = SECSID_NULL;
+               return 0;
+       }
+
+       read_lock(&policy_rwlock);
+
+       nlbl_ctx = sidtab_search(&sidtab, nlbl_sid);
+       if (!nlbl_ctx) {
+               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
+                      __func__, nlbl_sid);
+               rc = -EINVAL;
+               goto out_slowpath;
+       }
+       xfrm_ctx = sidtab_search(&sidtab, xfrm_sid);
+       if (!xfrm_ctx) {
+               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
+                      __func__, xfrm_sid);
+               rc = -EINVAL;
+               goto out_slowpath;
+       }
+       rc = (mls_context_cmp(nlbl_ctx, xfrm_ctx) ? 0 : -EACCES);
+
+out_slowpath:
+       read_unlock(&policy_rwlock);
+       if (rc == 0)
+               /* at present NetLabel SIDs/labels really only carry MLS
+                * information so if the MLS portion of the NetLabel SID
+                * matches the MLS portion of the labeled XFRM SID/label
+                * then pass along the XFRM SID as it is the most
+                * expressive */
+               *peer_sid = xfrm_sid;
+       else
+               *peer_sid = SECSID_NULL;
+       return rc;
+}
+
+static int get_classes_callback(void *k, void *d, void *args)
+{
+       struct class_datum *datum = d;
+       char *name = k, **classes = args;
+       int value = datum->value - 1;
+
+       classes[value] = kstrdup(name, GFP_ATOMIC);
+       if (!classes[value])
+               return -ENOMEM;
+
+       return 0;
+}
+
+int security_get_classes(char ***classes, int *nclasses)
+{
+       int rc = -ENOMEM;
+
+       read_lock(&policy_rwlock);
+
+       *nclasses = policydb.p_classes.nprim;
+       *classes = kcalloc(*nclasses, sizeof(*classes), GFP_ATOMIC);
+       if (!*classes)
+               goto out;
+
+       rc = hashtab_map(policydb.p_classes.table, get_classes_callback,
+                       *classes);
+       if (rc < 0) {
+               int i;
+               for (i = 0; i < *nclasses; i++)
+                       kfree((*classes)[i]);
+               kfree(*classes);
+       }
+
+out:
+       read_unlock(&policy_rwlock);
+       return rc;
+}
+
+static int get_permissions_callback(void *k, void *d, void *args)
+{
+       struct perm_datum *datum = d;
+       char *name = k, **perms = args;
+       int value = datum->value - 1;
+
+       perms[value] = kstrdup(name, GFP_ATOMIC);
+       if (!perms[value])
+               return -ENOMEM;
+
+       return 0;
+}
+
+int security_get_permissions(char *class, char ***perms, int *nperms)
+{
+       int rc = -ENOMEM, i;
+       struct class_datum *match;
+
+       read_lock(&policy_rwlock);
+
+       match = hashtab_search(policydb.p_classes.table, class);
+       if (!match) {
+               printk(KERN_ERR "SELinux: %s:  unrecognized class %s\n",
+                       __func__, class);
+               rc = -EINVAL;
+               goto out;
+       }
+
+       *nperms = match->permissions.nprim;
+       *perms = kcalloc(*nperms, sizeof(*perms), GFP_ATOMIC);
+       if (!*perms)
+               goto out;
+
+       if (match->comdatum) {
+               rc = hashtab_map(match->comdatum->permissions.table,
+                               get_permissions_callback, *perms);
+               if (rc < 0)
+                       goto err;
+       }
+
+       rc = hashtab_map(match->permissions.table, get_permissions_callback,
+                       *perms);
+       if (rc < 0)
+               goto err;
+
+out:
+       read_unlock(&policy_rwlock);
+       return rc;
+
+err:
+       read_unlock(&policy_rwlock);
+       for (i = 0; i < *nperms; i++)
+               kfree((*perms)[i]);
+       kfree(*perms);
+       return rc;
+}
+
+int security_get_reject_unknown(void)
+{
+       return policydb.reject_unknown;
+}
+
+int security_get_allow_unknown(void)
+{
+       return policydb.allow_unknown;
+}
+
+/**
+ * security_policycap_supported - Check for a specific policy capability
+ * @req_cap: capability
+ *
+ * Description:
+ * This function queries the currently loaded policy to see if it supports the
+ * capability specified by @req_cap.  Returns true (1) if the capability is
+ * supported, false (0) if it isn't supported.
+ *
+ */
+int security_policycap_supported(unsigned int req_cap)
+{
+       int rc;
+
+       read_lock(&policy_rwlock);
+       rc = ebitmap_get_bit(&policydb.policycaps, req_cap);
+       read_unlock(&policy_rwlock);
+
+       return rc;
+}
+
 struct selinux_audit_rule {
        u32 au_seqno;
        struct context au_ctxt;
 };
 
-void selinux_audit_rule_free(struct selinux_audit_rule *rule)
+void selinux_audit_rule_free(void *vrule)
 {
+       struct selinux_audit_rule *rule = vrule;
+
        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)
+int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
 {
        struct selinux_audit_rule *tmprule;
        struct role_datum *roledatum;
        struct type_datum *typedatum;
        struct user_datum *userdatum;
+       struct selinux_audit_rule **rule = (struct selinux_audit_rule **)vrule;
        int rc = 0;
 
        *rule = NULL;
 
        if (!ss_initialized)
-               return -ENOTSUPP;
+               return -EOPNOTSUPP;
 
        switch (field) {
        case AUDIT_SUBJ_USER:
@@ -1932,7 +2678,7 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr,
        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)
+               if (op != Audit_equal && op != Audit_not_equal)
                        return -EINVAL;
                break;
        case AUDIT_SUBJ_SEN:
@@ -1954,7 +2700,7 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr,
 
        context_init(&tmprule->au_ctxt);
 
-       POLICY_RDLOCK;
+       read_lock(&policy_rwlock);
 
        tmprule->au_seqno = latest_granting;
 
@@ -1991,7 +2737,7 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr,
                break;
        }
 
-       POLICY_RDUNLOCK;
+       read_unlock(&policy_rwlock);
 
        if (rc) {
                selinux_audit_rule_free(tmprule);
@@ -2003,25 +2749,50 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr,
        return rc;
 }
 
-int selinux_audit_rule_match(u32 sid, u32 field, u32 op,
-                             struct selinux_audit_rule *rule,
-                             struct audit_context *actx)
+/* Check to see if the rule contains any selinux fields */
+int selinux_audit_rule_known(struct audit_krule *rule)
+{
+       int i;
+
+       for (i = 0; i < rule->field_count; i++) {
+               struct audit_field *f = &rule->fields[i];
+               switch (f->type) {
+               case AUDIT_SUBJ_USER:
+               case AUDIT_SUBJ_ROLE:
+               case AUDIT_SUBJ_TYPE:
+               case AUDIT_SUBJ_SEN:
+               case AUDIT_SUBJ_CLR:
+               case AUDIT_OBJ_USER:
+               case AUDIT_OBJ_ROLE:
+               case AUDIT_OBJ_TYPE:
+               case AUDIT_OBJ_LEV_LOW:
+               case AUDIT_OBJ_LEV_HIGH:
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule,
+                            struct audit_context *actx)
 {
        struct context *ctxt;
        struct mls_level *level;
+       struct selinux_audit_rule *rule = vrule;
        int match = 0;
 
        if (!rule) {
                audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR,
-                         "selinux_audit_rule_match: missing rule\n");
+                         "selinux_audit_rule_match: missing rule\n");
                return -ENOENT;
        }
 
-       POLICY_RDLOCK;
+       read_lock(&policy_rwlock);
 
        if (rule->au_seqno < latest_granting) {
                audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR,
-                         "selinux_audit_rule_match: stale rule\n");
+                         "selinux_audit_rule_match: stale rule\n");
                match = -ESTALE;
                goto out;
        }
@@ -2029,8 +2800,8 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op,
        ctxt = sidtab_search(&sidtab, sid);
        if (!ctxt) {
                audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR,
-                         "selinux_audit_rule_match: unrecognized SID %d\n",
-                         sid);
+                         "selinux_audit_rule_match: unrecognized SID %d\n",
+                         sid);
                match = -ENOENT;
                goto out;
        }
@@ -2041,10 +2812,10 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op,
        case AUDIT_SUBJ_USER:
        case AUDIT_OBJ_USER:
                switch (op) {
-               case AUDIT_EQUAL:
+               case Audit_equal:
                        match = (ctxt->user == rule->au_ctxt.user);
                        break;
-               case AUDIT_NOT_EQUAL:
+               case Audit_not_equal:
                        match = (ctxt->user != rule->au_ctxt.user);
                        break;
                }
@@ -2052,10 +2823,10 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op,
        case AUDIT_SUBJ_ROLE:
        case AUDIT_OBJ_ROLE:
                switch (op) {
-               case AUDIT_EQUAL:
+               case Audit_equal:
                        match = (ctxt->role == rule->au_ctxt.role);
                        break;
-               case AUDIT_NOT_EQUAL:
+               case Audit_not_equal:
                        match = (ctxt->role != rule->au_ctxt.role);
                        break;
                }
@@ -2063,10 +2834,10 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op,
        case AUDIT_SUBJ_TYPE:
        case AUDIT_OBJ_TYPE:
                switch (op) {
-               case AUDIT_EQUAL:
+               case Audit_equal:
                        match = (ctxt->type == rule->au_ctxt.type);
                        break;
-               case AUDIT_NOT_EQUAL:
+               case Audit_not_equal:
                        match = (ctxt->type != rule->au_ctxt.type);
                        break;
                }
@@ -2076,49 +2847,49 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op,
        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]);
+                         field == AUDIT_OBJ_LEV_LOW) ?
+                        &ctxt->range.level[0] : &ctxt->range.level[1]);
                switch (op) {
-               case AUDIT_EQUAL:
+               case Audit_equal:
                        match = mls_level_eq(&rule->au_ctxt.range.level[0],
-                                            level);
+                                            level);
                        break;
-               case AUDIT_NOT_EQUAL:
+               case Audit_not_equal:
                        match = !mls_level_eq(&rule->au_ctxt.range.level[0],
-                                             level);
+                                             level);
                        break;
-               case AUDIT_LESS_THAN:
+               case Audit_lt:
                        match = (mls_level_dom(&rule->au_ctxt.range.level[0],
-                                              level) &&
-                                !mls_level_eq(&rule->au_ctxt.range.level[0],
-                                              level));
+                                              level) &&
+                                !mls_level_eq(&rule->au_ctxt.range.level[0],
+                                              level));
                        break;
-               case AUDIT_LESS_THAN_OR_EQUAL:
+               case Audit_le:
                        match = mls_level_dom(&rule->au_ctxt.range.level[0],
-                                             level);
+                                             level);
                        break;
-               case AUDIT_GREATER_THAN:
+               case Audit_gt:
                        match = (mls_level_dom(level,
-                                             &rule->au_ctxt.range.level[0]) &&
-                                !mls_level_eq(level,
-                                              &rule->au_ctxt.range.level[0]));
+                                             &rule->au_ctxt.range.level[0]) &&
+                                !mls_level_eq(level,
+                                              &rule->au_ctxt.range.level[0]));
                        break;
-               case AUDIT_GREATER_THAN_OR_EQUAL:
+               case Audit_ge:
                        match = mls_level_dom(level,
-                                             &rule->au_ctxt.range.level[0]);
+                                             &rule->au_ctxt.range.level[0]);
                        break;
                }
        }
 
 out:
-       POLICY_RDUNLOCK;
+       read_unlock(&policy_rwlock);
        return match;
 }
 
-static int (*aurule_callback)(void) = NULL;
+static int (*aurule_callback)(void) = audit_update_lsm_rules;
 
 static int aurule_avc_callback(u32 event, u32 ssid, u32 tsid,
-                               u16 class, u32 perms, u32 *retained)
+                              u16 class, u32 perms, u32 *retained)
 {
        int err = 0;
 
@@ -2132,7 +2903,7 @@ 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);
+                              SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
        if (err)
                panic("avc_add_callback() failed, error %d\n", err);
 
@@ -2140,185 +2911,90 @@ static int __init aurule_init(void)
 }
 __initcall(aurule_init);
 
-void selinux_audit_set_callback(int (*callback)(void))
-{
-       aurule_callback = callback;
-}
-
 #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 = 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
+ * security_netlbl_cache_add - Add an entry to the NetLabel cache
+ * @secattr: the NetLabel packet security attributes
+ * @sid: the SELinux SID
  *
  * Description:
  * Attempt to cache the context in @ctx, which was derived from the packet in
- * @skb, in the NetLabel subsystem cache.
+ * @skb, in the NetLabel subsystem cache.  This function assumes @secattr has
+ * already been initialized.
  *
  */
-static void selinux_netlbl_cache_add(struct sk_buff *skb, struct context *ctx)
+static void security_netlbl_cache_add(struct netlbl_lsm_secattr *secattr,
+                                     u32 sid)
 {
-       struct netlbl_cache *cache = NULL;
-       struct netlbl_lsm_secattr secattr;
-
-       netlbl_secattr_init(&secattr);
-
-       cache = kzalloc(sizeof(*cache), GFP_ATOMIC);
-       if (cache == NULL)
-               goto netlbl_cache_add_failure;
-       secattr.cache.free = selinux_netlbl_cache_free;
-       secattr.cache.data = (void *)cache;
-
-       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_failure;
-       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;
-
-       if (netlbl_cache_add(skb, &secattr) != 0)
-               goto netlbl_cache_add_failure;
-
-       return;
+       u32 *sid_cache;
 
-netlbl_cache_add_failure:
-       netlbl_secattr_destroy(&secattr, 1);
-}
+       sid_cache = kmalloc(sizeof(*sid_cache), GFP_ATOMIC);
+       if (sid_cache == NULL)
+               return;
+       secattr->cache = netlbl_secattr_cache_alloc(GFP_ATOMIC);
+       if (secattr->cache == NULL) {
+               kfree(sid_cache);
+               return;
+       }
 
-/**
- * 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();
+       *sid_cache = sid;
+       secattr->cache->free = kfree;
+       secattr->cache->data = sid_cache;
+       secattr->flags |= NETLBL_SECATTR_CACHE;
 }
 
 /**
- * selinux_netlbl_secattr_to_sid - Convert a NetLabel secattr to a SELinux SID
- * @skb: the network packet
+ * security_netlbl_secattr_to_sid - Convert a NetLabel secattr to a SELinux SID
  * @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
+ * Convert the given NetLabel 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.
+ * SID/context then use SECINITSID_NETMSG as the foundation.  If possibile the
+ * 'cache' field of @secattr is set and the CACHE flag is set; this is to
+ * allow the @secattr to be used by NetLabel to cache the secattr to SID
+ * conversion for future lookups.  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 security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
+                                  u32 *sid)
 {
        int rc = -EIDRM;
        struct context *ctx;
        struct context ctx_new;
-       struct netlbl_cache *cache;
 
-       POLICY_RDLOCK;
+       if (!ss_initialized) {
+               *sid = SECSID_NULL;
+               return 0;
+       }
 
-       if (secattr->cache.data) {
-               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;
+       read_lock(&policy_rwlock);
 
-                       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->mls_lvl_vld) {
-               ctx = sidtab_search(&sidtab, base_sid);
+       if (secattr->flags & NETLBL_SECATTR_CACHE) {
+               *sid = *(u32 *)secattr->cache->data;
+               rc = 0;
+       } else if (secattr->flags & NETLBL_SECATTR_SECID) {
+               *sid = secattr->attr.secid;
+               rc = 0;
+       } else if (secattr->flags & NETLBL_SECATTR_MLS_LVL) {
+               ctx = sidtab_search(&sidtab, SECINITSID_NETMSG);
                if (ctx == NULL)
                        goto netlbl_secattr_to_sid_return;
 
+               context_init(&ctx_new);
                ctx_new.user = ctx->user;
                ctx_new.role = ctx->role;
                ctx_new.type = ctx->type;
-               mls_import_lvl(&ctx_new, secattr->mls_lvl, secattr->mls_lvl);
-               if (secattr->mls_cat) {
-                       if (mls_import_cat(&ctx_new,
-                                          secattr->mls_cat,
-                                          secattr->mls_cat_len,
-                                          NULL,
-                                          0) != 0)
+               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->attr.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);
+                       memcpy(&ctx_new.range.level[1].cat,
+                              &ctx_new.range.level[0].cat,
+                              sizeof(ctx_new.range.level[0].cat));
                }
                if (mls_context_isvalid(&policydb, &ctx_new) != 1)
                        goto netlbl_secattr_to_sid_return_cleanup;
@@ -2327,16 +3003,16 @@ static int selinux_netlbl_secattr_to_sid(struct sk_buff *skb,
                if (rc != 0)
                        goto netlbl_secattr_to_sid_return_cleanup;
 
-               if (skb != NULL)
-                       selinux_netlbl_cache_add(skb, &ctx_new);
+               security_netlbl_cache_add(secattr, *sid);
+
                ebitmap_destroy(&ctx_new.range.level[0].cat);
        } else {
-               *sid = SECINITSID_UNLABELED;
+               *sid = SECSID_NULL;
                rc = 0;
        }
 
 netlbl_secattr_to_sid_return:
-       POLICY_RDUNLOCK;
+       read_unlock(&policy_rwlock);
        return rc;
 netlbl_secattr_to_sid_return_cleanup:
        ebitmap_destroy(&ctx_new.range.level[0].cat);
@@ -2344,346 +3020,47 @@ netlbl_secattr_to_sid_return_cleanup:
 }
 
 /**
- * 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
+ * security_netlbl_sid_to_secattr - Convert a SELinux SID to a NetLabel secattr
+ * @sid: the SELinux SID
+ * @secattr: the NetLabel packet security attributes
  *
  * 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.
+ * Convert the given SELinux SID in @sid into a NetLabel security attribute.
+ * Returns zero on success, negative values on failure.
  *
  */
-static int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
-                                       u32 base_sid,
-                                       u32 *sid)
+int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr)
 {
        int rc;
-       struct netlbl_lsm_secattr secattr;
-
-       netlbl_secattr_init(&secattr);
-       rc = netlbl_skbuff_getattr(skb, &secattr);
-       if (rc == 0)
-               rc = selinux_netlbl_secattr_to_sid(skb,
-                                                  &secattr,
-                                                  base_sid,
-                                                  sid);
-       netlbl_secattr_destroy(&secattr, 0);
-
-       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.
- *
- */
-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;
 
-       POLICY_RDLOCK;
-
+       read_lock(&policy_rwlock);
        ctx = sidtab_search(&sidtab, sid);
-       if (ctx == NULL)
-               goto netlbl_socket_setsid_return;
-
-       netlbl_secattr_init(&secattr);
-       secattr.domain = kstrdup(policydb.p_type_val_to_name[ctx->type - 1],
-                                GFP_ATOMIC);
-       mls_export_lvl(ctx, &secattr.mls_lvl, NULL);
-       secattr.mls_lvl_vld = 1;
-       mls_export_cat(ctx,
-                      &secattr.mls_cat,
-                      &secattr.mls_cat_len,
-                      NULL,
-                      NULL);
-
-       rc = netlbl_socket_setattr(sock, &secattr);
-       if (rc == 0)
-               sksec->nlbl_state = NLBL_LABELED;
-
-       netlbl_secattr_destroy(&secattr, 0);
-
-netlbl_socket_setsid_return:
-       POLICY_RDUNLOCK;
-       return rc;
-}
-
-/**
- * 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)
-{
-        if (family == PF_INET)
-               ssec->nlbl_state = NLBL_REQUIRE;
-       else
-               ssec->nlbl_state = NLBL_UNSET;
-}
-
-/**
- * selinux_netlbl_sk_clone_security - 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_clone_security(struct sk_security_struct *ssec,
-                                     struct sk_security_struct *newssec)
-{
-       newssec->sclass = ssec->sclass;
-       if (ssec->nlbl_state != NLBL_UNSET)
-               newssec->nlbl_state = NLBL_REQUIRE;
-       else
-               newssec->nlbl_state = NLBL_UNSET;
-}
-
-/**
- * selinux_netlbl_socket_post_create - Label a socket using NetLabel
- * @sock: the socket to label
- * @sock_family: the socket family
- * @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.
- *
- */
-int selinux_netlbl_socket_post_create(struct socket *sock,
-                                     int sock_family,
-                                     u32 sid)
-{
-       struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
-       struct sk_security_struct *sksec = sock->sk->sk_security;
-
-       sksec->sclass = isec->sclass;
-
-       if (sock_family != PF_INET)
-               return 0;
-
-       sksec->nlbl_state = NLBL_REQUIRE;
-       return selinux_netlbl_socket_setsid(sock, sid);
-}
-
-/**
- * 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;
-
-       if (sk->sk_family != PF_INET)
-               return;
-
-       netlbl_secattr_init(&secattr);
-       if (netlbl_sock_getattr(sk, &secattr) == 0 &&
-           selinux_netlbl_secattr_to_sid(NULL,
-                                         &secattr,
-                                         sksec->sid,
-                                         &nlbl_peer_sid) == 0)
-               sksec->peer_sid = nlbl_peer_sid;
-       netlbl_secattr_destroy(&secattr, 0);
-
-       sksec->nlbl_state = NLBL_REQUIRE;
-
-       /* 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);
-}
-
-/**
- * selinux_netlbl_inet_conn_request - Handle a new connection request
- * @skb: the packet
- * @sock_sid: the SID of the parent socket
- *
- * Description:
- * If present, use the security attributes of the packet in @skb and the
- * parent sock's SID to arrive at a SID for the new child sock.  Returns the
- * SID of the connection or SECSID_NULL on failure.
- *
- */
-u32 selinux_netlbl_inet_conn_request(struct sk_buff *skb, u32 sock_sid)
-{
-       int rc;
-       u32 peer_sid;
-
-       rc = selinux_netlbl_skbuff_getsid(skb, sock_sid, &peer_sid);
-       if (rc != 0)
-               return SECSID_NULL;
-
-       if (peer_sid == SECINITSID_UNLABELED)
-               return SECSID_NULL;
-
-       return peer_sid;
-}
-
-/**
- * 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 inode_security_struct *isec;
-       struct sk_security_struct *sksec;
-       struct socket *sock;
-
-       if (!S_ISSOCK(inode->i_mode))
-               return 0;
-
-       sock = SOCKET_I(inode);
-       isec = inode->i_security;
-       sksec = sock->sk->sk_security;
-       down(&isec->sem);
-       if (unlikely(sksec->nlbl_state == NLBL_REQUIRE &&
-                    (mask & (MAY_WRITE | MAY_APPEND)))) {
-               lock_sock(sock->sk);
-               rc = selinux_netlbl_socket_setsid(sock, sksec->sid);
-               release_sock(sock->sk);
-       } else
-               rc = 0;
-       up(&isec->sem);
-
-       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_NETMSG, &netlbl_sid);
-       if (rc != 0)
-               return rc;
-
-       if (netlbl_sid == SECINITSID_UNLABELED)
-               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;
+       if (ctx == NULL) {
+               rc = -ENOENT;
+               goto netlbl_sid_to_secattr_failure;
+       }
+       secattr->domain = kstrdup(policydb.p_type_val_to_name[ctx->type - 1],
+                                 GFP_ATOMIC);
+       if (secattr->domain == NULL) {
+               rc = -ENOMEM;
+               goto netlbl_sid_to_secattr_failure;
        }
+       secattr->attr.secid = sid;
+       secattr->flags |= NETLBL_SECATTR_DOMAIN_CPY | NETLBL_SECATTR_SECID;
+       mls_export_netlbl_lvl(ctx, secattr);
+       rc = mls_export_netlbl_cat(ctx, secattr);
+       if (rc != 0)
+               goto netlbl_sid_to_secattr_failure;
+       read_unlock(&policy_rwlock);
 
-       rc = avc_has_perm(sksec->sid,
-                         netlbl_sid,
-                         sksec->sclass,
-                         recv_perm,
-                         ad);
-       if (rc == 0)
-               return 0;
+       return 0;
 
-       netlbl_skbuff_err(skb, rc);
+netlbl_sid_to_secattr_failure:
+       read_unlock(&policy_rwlock);
        return rc;
 }
-
-/**
- * selinux_netlbl_socket_getpeersec_stream - Return the connected peer's SID
- * @sock: the socket
- *
- * Description:
- * Examine @sock to find the connected peer's SID.  Returns the SID on success
- * or SECSID_NULL on error.
- *
- */
-u32 selinux_netlbl_socket_getpeersec_stream(struct socket *sock)
-{
-       struct sk_security_struct *sksec = sock->sk->sk_security;
-
-       if (sksec->peer_sid == SECINITSID_UNLABELED)
-               return SECSID_NULL;
-
-       return sksec->peer_sid;
-}
-
-/**
- * selinux_netlbl_socket_getpeersec_dgram - Return the SID of a NetLabel packet
- * @skb: the packet
- *
- * Description:
- * Examine @skb to find the SID assigned to it by NetLabel.  Returns the SID on
- * success, SECSID_NULL on error.
- *
- */
-u32 selinux_netlbl_socket_getpeersec_dgram(struct sk_buff *skb)
-{
-       int peer_sid;
-       struct sock *sk = skb->sk;
-       struct inode_security_struct *isec;
-
-       if (sk == NULL || sk->sk_socket == NULL)
-               return SECSID_NULL;
-
-       isec = SOCK_INODE(sk->sk_socket)->i_security;
-       if (selinux_netlbl_skbuff_getsid(skb, isec->sid, &peer_sid) != 0)
-               return SECSID_NULL;
-       if (peer_sid == SECINITSID_UNLABELED)
-               return SECSID_NULL;
-
-       return peer_sid;
-}
 #endif /* CONFIG_NETLABEL */