* Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
*
* Support for enhanced MLS infrastructure.
+ * Support for context based audit filters.
*
* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
*
* Added conditional policy language extensions
*
- * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
- * Copyright (C) 2003 - 2004 Tresys Technology, LLC
+ * Updated: Hewlett-Packard <paul.moore@hp.com>
+ *
+ * Added support for NetLabel
+ *
+ * Updated: Chad Sellers <csellers@tresys.com>
+ *
+ * Added validation of kernel classes and permissions
+ *
+ * Copyright (C) 2006 Hewlett-Packard Development Company, L.P.
+ * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
+ * Copyright (C) 2003 - 2004, 2006 Tresys Technology, LLC
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/spinlock.h>
+#include <linux/rcupdate.h>
#include <linux/errno.h>
#include <linux/in.h>
#include <linux/sched.h>
#include <linux/audit.h>
-#include <asm/semaphore.h>
+#include <linux/mutex.h>
+#include <net/sock.h>
+#include <net/netlabel.h>
+
#include "flask.h"
#include "avc.h"
#include "avc_ss.h"
#include "services.h"
#include "conditional.h"
#include "mls.h"
+#include "objsec.h"
+#include "selinux_netlabel.h"
+#include "xfrm.h"
+#include "ebitmap.h"
extern void selnl_notify_policyload(u32 seqno);
unsigned int policydb_loaded_version;
+/*
+ * This is declared in avc.c
+ */
+extern const struct selinux_class_perm selinux_class_perm;
+
static DEFINE_RWLOCK(policy_rwlock);
#define POLICY_RDLOCK read_lock(&policy_rwlock)
#define POLICY_WRLOCK write_lock_irq(&policy_rwlock)
#define POLICY_RDUNLOCK read_unlock(&policy_rwlock)
#define POLICY_WRUNLOCK write_unlock_irq(&policy_rwlock)
-static DECLARE_MUTEX(load_sem);
-#define LOAD_LOCK down(&load_sem)
-#define LOAD_UNLOCK up(&load_sem)
+static DEFINE_MUTEX(load_mutex);
+#define LOAD_LOCK mutex_lock(&load_mutex)
+#define LOAD_UNLOCK mutex_unlock(&load_mutex)
static struct sidtab sidtab;
struct policydb policydb;
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);
+ if (!scontextp) {
+ rc = -ENOMEM;
+ goto out;
+ }
strcpy(scontextp, initial_sid_to_string[sid]);
*scontext = scontextp;
goto out;
goto out;
}
+ context_init(&newcontext);
+
POLICY_RDLOCK;
scontext = sidtab_search(&sidtab, ssid);
goto out_unlock;
}
- context_init(&newcontext);
-
/* Set the user identity. */
switch (specified) {
case AVTAB_TRANSITION:
}
/*
- * Verify that each permission that is defined under the
- * existing policy is still defined with the same value
- * in the new policy.
- */
-static int validate_perm(void *key, void *datum, void *p)
-{
- struct hashtab *h;
- struct perm_datum *perdatum, *perdatum2;
- int rc = 0;
-
-
- h = p;
- perdatum = datum;
-
- perdatum2 = hashtab_search(h, key);
- if (!perdatum2) {
- printk(KERN_ERR "security: permission %s disappeared",
- (char *)key);
- rc = -ENOENT;
- goto out;
- }
- if (perdatum->value != perdatum2->value) {
- printk(KERN_ERR "security: the value of permission %s changed",
- (char *)key);
- rc = -EINVAL;
- }
-out:
- return rc;
-}
-
-/*
- * Verify that each class that is defined under the
- * existing policy is still defined with the same
- * attributes in the new policy.
+ * Verify that each kernel class that is defined in the
+ * policy is correct
*/
-static int validate_class(void *key, void *datum, void *p)
+static int validate_classes(struct policydb *p)
{
- struct policydb *newp;
- struct class_datum *cladatum, *cladatum2;
- int rc;
-
- newp = p;
- cladatum = datum;
-
- cladatum2 = hashtab_search(newp->p_classes.table, key);
- if (!cladatum2) {
- printk(KERN_ERR "security: class %s disappeared\n",
- (char *)key);
- rc = -ENOENT;
- goto out;
- }
- if (cladatum->value != cladatum2->value) {
- printk(KERN_ERR "security: the value of class %s changed\n",
- (char *)key);
- rc = -EINVAL;
- goto out;
+ int i, j;
+ struct class_datum *cladatum;
+ struct perm_datum *perdatum;
+ u32 nprim, tmp, common_pts_len, perm_val, pol_val;
+ u16 class_val;
+ const struct selinux_class_perm *kdefs = &selinux_class_perm;
+ const char *def_class, *def_perm, *pol_class;
+ struct symtab *perms;
+
+ for (i = 1; i < kdefs->cts_len; i++) {
+ def_class = kdefs->class_to_string[i];
+ if (i > p->p_classes.nprim) {
+ printk(KERN_INFO
+ "security: class %s not defined in policy\n",
+ def_class);
+ continue;
+ }
+ pol_class = p->p_class_val_to_name[i-1];
+ if (strcmp(pol_class, def_class)) {
+ printk(KERN_ERR
+ "security: class %d is incorrect, found %s but should be %s\n",
+ i, pol_class, def_class);
+ return -EINVAL;
+ }
}
- if ((cladatum->comdatum && !cladatum2->comdatum) ||
- (!cladatum->comdatum && cladatum2->comdatum)) {
- printk(KERN_ERR "security: the inherits clause for the access "
- "vector definition for class %s changed\n", (char *)key);
- rc = -EINVAL;
- goto out;
+ for (i = 0; i < kdefs->av_pts_len; i++) {
+ class_val = kdefs->av_perm_to_string[i].tclass;
+ perm_val = kdefs->av_perm_to_string[i].value;
+ def_perm = kdefs->av_perm_to_string[i].name;
+ if (class_val > p->p_classes.nprim)
+ continue;
+ pol_class = p->p_class_val_to_name[class_val-1];
+ cladatum = hashtab_search(p->p_classes.table, pol_class);
+ BUG_ON(!cladatum);
+ perms = &cladatum->permissions;
+ nprim = 1 << (perms->nprim - 1);
+ if (perm_val > nprim) {
+ printk(KERN_INFO
+ "security: permission %s in class %s not defined in policy\n",
+ def_perm, pol_class);
+ continue;
+ }
+ perdatum = hashtab_search(perms->table, def_perm);
+ if (perdatum == NULL) {
+ printk(KERN_ERR
+ "security: permission %s in class %s not found in policy\n",
+ def_perm, pol_class);
+ return -EINVAL;
+ }
+ pol_val = 1 << (perdatum->value - 1);
+ if (pol_val != perm_val) {
+ printk(KERN_ERR
+ "security: permission %s in class %s has incorrect value\n",
+ def_perm, pol_class);
+ return -EINVAL;
+ }
}
- if (cladatum->comdatum) {
- rc = hashtab_map(cladatum->comdatum->permissions.table, validate_perm,
- cladatum2->comdatum->permissions.table);
- if (rc) {
- printk(" in the access vector definition for class "
- "%s\n", (char *)key);
- goto out;
+ for (i = 0; i < kdefs->av_inherit_len; i++) {
+ class_val = kdefs->av_inherit[i].tclass;
+ if (class_val > p->p_classes.nprim)
+ continue;
+ pol_class = p->p_class_val_to_name[class_val-1];
+ cladatum = hashtab_search(p->p_classes.table, pol_class);
+ BUG_ON(!cladatum);
+ if (!cladatum->comdatum) {
+ printk(KERN_ERR
+ "security: class %s should have an inherits clause but does not\n",
+ pol_class);
+ return -EINVAL;
+ }
+ tmp = kdefs->av_inherit[i].common_base;
+ common_pts_len = 0;
+ while (!(tmp & 0x01)) {
+ common_pts_len++;
+ tmp >>= 1;
+ }
+ perms = &cladatum->comdatum->permissions;
+ for (j = 0; j < common_pts_len; j++) {
+ def_perm = kdefs->av_inherit[i].common_pts[j];
+ if (j >= perms->nprim) {
+ printk(KERN_INFO
+ "security: permission %s in class %s not defined in policy\n",
+ def_perm, pol_class);
+ continue;
+ }
+ perdatum = hashtab_search(perms->table, def_perm);
+ if (perdatum == NULL) {
+ printk(KERN_ERR
+ "security: permission %s in class %s not found in policy\n",
+ def_perm, pol_class);
+ return -EINVAL;
+ }
+ if (perdatum->value != j + 1) {
+ printk(KERN_ERR
+ "security: permission %s in class %s has incorrect value\n",
+ def_perm, pol_class);
+ return -EINVAL;
+ }
}
}
- rc = hashtab_map(cladatum->permissions.table, validate_perm,
- cladatum2->permissions.table);
- if (rc)
- printk(" in access vector definition for class %s\n",
- (char *)key);
-out:
- return rc;
+ return 0;
}
/* Clone the SID into the new SID table. */
avtab_cache_destroy();
return -EINVAL;
}
+ /* Verify that the kernel defined classes are correct. */
+ if (validate_classes(&policydb)) {
+ printk(KERN_ERR
+ "security: the definition of a class is incorrect\n");
+ LOAD_UNLOCK;
+ sidtab_destroy(&sidtab);
+ policydb_destroy(&policydb);
+ avtab_cache_destroy();
+ return -EINVAL;
+ }
policydb_loaded_version = policydb.policyvers;
ss_initialized = 1;
seqno = ++latest_granting;
selinux_complete_init();
avc_ss_reset(seqno);
selnl_notify_policyload(seqno);
+ selinux_netlbl_cache_invalidate();
+ selinux_xfrm_notify_policyload();
return 0;
}
sidtab_init(&newsidtab);
- /* Verify that the existing classes did not change. */
- if (hashtab_map(policydb.p_classes.table, validate_class, &newpolicydb)) {
- printk(KERN_ERR "security: the definition of an existing "
- "class changed\n");
+ /* Verify that the kernel defined classes are correct. */
+ if (validate_classes(&newpolicydb)) {
+ printk(KERN_ERR
+ "security: the definition of a class is incorrect\n");
rc = -EINVAL;
goto err;
}
avc_ss_reset(seqno);
selnl_notify_policyload(seqno);
+ selinux_netlbl_cache_invalidate();
+ selinux_xfrm_notify_policyload();
return 0;
goto out;
}
- *names = (char**)kcalloc(*len, sizeof(char*), GFP_ATOMIC);
+ *names = kcalloc(*len, sizeof(char*), GFP_ATOMIC);
if (!*names)
goto err;
- *values = (int*)kcalloc(*len, sizeof(int), GFP_ATOMIC);
+ *values = kcalloc(*len, sizeof(int), GFP_ATOMIC);
if (!*values)
goto err;
size_t name_len;
(*values)[i] = policydb.bool_val_to_struct[i]->state;
name_len = strlen(policydb.p_bool_val_to_name[i]) + 1;
- (*names)[i] = (char*)kmalloc(sizeof(char) * name_len, GFP_ATOMIC);
+ (*names)[i] = kmalloc(sizeof(char) * name_len, GFP_ATOMIC);
if (!(*names)[i])
goto err;
strncpy((*names)[i], policydb.p_bool_val_to_name[i], name_len);
goto out;
}
- printk(KERN_INFO "security: committed booleans { ");
for (i = 0; i < len; i++) {
+ if (!!values[i] != policydb.bool_val_to_struct[i]->state) {
+ audit_log(current->audit_context, GFP_ATOMIC,
+ AUDIT_MAC_CONFIG_CHANGE,
+ "bool=%s val=%d old_val=%d auid=%u",
+ policydb.p_bool_val_to_name[i],
+ !!values[i],
+ policydb.bool_val_to_struct[i]->state,
+ audit_get_loginuid(current->audit_context));
+ }
if (values[i]) {
policydb.bool_val_to_struct[i]->state = 1;
} else {
policydb.bool_val_to_struct[i]->state = 0;
}
- if (i != 0)
- printk(", ");
- printk("%s:%d", policydb.p_bool_val_to_name[i],
- policydb.bool_val_to_struct[i]->state);
}
- printk(" }\n");
for (cur = policydb.cond_list; cur != NULL; cur = cur->next) {
rc = evaluate_cond_node(&policydb, cur);
if (!rc) {
avc_ss_reset(seqno);
selnl_notify_policyload(seqno);
+ selinux_xfrm_notify_policyload();
}
return rc;
}
POLICY_RDUNLOCK;
return rc;
}
+
+/*
+ * security_sid_mls_copy() - computes a new sid based on the given
+ * sid and the mls portion of mls_sid.
+ */
+int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid)
+{
+ struct context *context1;
+ struct context *context2;
+ struct context newcon;
+ char *s;
+ u32 len;
+ int rc = 0;
+
+ if (!ss_initialized || !selinux_mls_enabled) {
+ *new_sid = sid;
+ goto out;
+ }
+
+ context_init(&newcon);
+
+ POLICY_RDLOCK;
+ context1 = sidtab_search(&sidtab, sid);
+ if (!context1) {
+ printk(KERN_ERR "security_sid_mls_copy: unrecognized SID "
+ "%d\n", sid);
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+
+ context2 = sidtab_search(&sidtab, mls_sid);
+ if (!context2) {
+ printk(KERN_ERR "security_sid_mls_copy: unrecognized SID "
+ "%d\n", mls_sid);
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+
+ newcon.user = context1->user;
+ newcon.role = context1->role;
+ newcon.type = context1->type;
+ rc = mls_context_cpy(&newcon, context2);
+ if (rc)
+ goto out_unlock;
+
+ /* Check the validity of the new context. */
+ if (!policydb_context_isvalid(&policydb, &newcon)) {
+ rc = convert_context_handle_invalid_context(&newcon);
+ if (rc)
+ goto bad;
+ }
+
+ rc = sidtab_context_to_sid(&sidtab, &newcon, new_sid);
+ goto out_unlock;
+
+bad:
+ if (!context_struct_to_string(&newcon, &s, &len)) {
+ audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR,
+ "security_sid_mls_copy: invalid context %s", s);
+ kfree(s);
+ }
+
+out_unlock:
+ POLICY_RDUNLOCK;
+ context_destroy(&newcon);
+out:
+ return rc;
+}
+
+struct selinux_audit_rule {
+ u32 au_seqno;
+ struct context au_ctxt;
+};
+
+void selinux_audit_rule_free(struct selinux_audit_rule *rule)
+{
+ if (rule) {
+ context_destroy(&rule->au_ctxt);
+ kfree(rule);
+ }
+}
+
+int selinux_audit_rule_init(u32 field, u32 op, char *rulestr,
+ struct selinux_audit_rule **rule)
+{
+ struct selinux_audit_rule *tmprule;
+ struct role_datum *roledatum;
+ struct type_datum *typedatum;
+ struct user_datum *userdatum;
+ int rc = 0;
+
+ *rule = NULL;
+
+ if (!ss_initialized)
+ return -ENOTSUPP;
+
+ switch (field) {
+ case AUDIT_SUBJ_USER:
+ case AUDIT_SUBJ_ROLE:
+ case AUDIT_SUBJ_TYPE:
+ case AUDIT_OBJ_USER:
+ case AUDIT_OBJ_ROLE:
+ case AUDIT_OBJ_TYPE:
+ /* only 'equals' and 'not equals' fit user, role, and type */
+ if (op != AUDIT_EQUAL && op != AUDIT_NOT_EQUAL)
+ return -EINVAL;
+ break;
+ case AUDIT_SUBJ_SEN:
+ case AUDIT_SUBJ_CLR:
+ case AUDIT_OBJ_LEV_LOW:
+ case AUDIT_OBJ_LEV_HIGH:
+ /* we do not allow a range, indicated by the presense of '-' */
+ if (strchr(rulestr, '-'))
+ return -EINVAL;
+ break;
+ default:
+ /* only the above fields are valid */
+ return -EINVAL;
+ }
+
+ tmprule = kzalloc(sizeof(struct selinux_audit_rule), GFP_KERNEL);
+ if (!tmprule)
+ return -ENOMEM;
+
+ context_init(&tmprule->au_ctxt);
+
+ POLICY_RDLOCK;
+
+ tmprule->au_seqno = latest_granting;
+
+ switch (field) {
+ case AUDIT_SUBJ_USER:
+ case AUDIT_OBJ_USER:
+ userdatum = hashtab_search(policydb.p_users.table, rulestr);
+ if (!userdatum)
+ rc = -EINVAL;
+ else
+ tmprule->au_ctxt.user = userdatum->value;
+ break;
+ case AUDIT_SUBJ_ROLE:
+ case AUDIT_OBJ_ROLE:
+ roledatum = hashtab_search(policydb.p_roles.table, rulestr);
+ if (!roledatum)
+ rc = -EINVAL;
+ else
+ tmprule->au_ctxt.role = roledatum->value;
+ break;
+ case AUDIT_SUBJ_TYPE:
+ case AUDIT_OBJ_TYPE:
+ typedatum = hashtab_search(policydb.p_types.table, rulestr);
+ if (!typedatum)
+ rc = -EINVAL;
+ else
+ tmprule->au_ctxt.type = typedatum->value;
+ break;
+ case AUDIT_SUBJ_SEN:
+ case AUDIT_SUBJ_CLR:
+ case AUDIT_OBJ_LEV_LOW:
+ case AUDIT_OBJ_LEV_HIGH:
+ rc = mls_from_string(rulestr, &tmprule->au_ctxt, GFP_ATOMIC);
+ break;
+ }
+
+ POLICY_RDUNLOCK;
+
+ if (rc) {
+ selinux_audit_rule_free(tmprule);
+ tmprule = NULL;
+ }
+
+ *rule = tmprule;
+
+ return rc;
+}
+
+int selinux_audit_rule_match(u32 sid, u32 field, u32 op,
+ struct selinux_audit_rule *rule,
+ struct audit_context *actx)
+{
+ struct context *ctxt;
+ struct mls_level *level;
+ int match = 0;
+
+ if (!rule) {
+ audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR,
+ "selinux_audit_rule_match: missing rule\n");
+ return -ENOENT;
+ }
+
+ POLICY_RDLOCK;
+
+ if (rule->au_seqno < latest_granting) {
+ audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR,
+ "selinux_audit_rule_match: stale rule\n");
+ match = -ESTALE;
+ goto out;
+ }
+
+ ctxt = sidtab_search(&sidtab, sid);
+ if (!ctxt) {
+ audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR,
+ "selinux_audit_rule_match: unrecognized SID %d\n",
+ sid);
+ match = -ENOENT;
+ goto out;
+ }
+
+ /* a field/op pair that is not caught here will simply fall through
+ without a match */
+ switch (field) {
+ case AUDIT_SUBJ_USER:
+ case AUDIT_OBJ_USER:
+ switch (op) {
+ case AUDIT_EQUAL:
+ match = (ctxt->user == rule->au_ctxt.user);
+ break;
+ case AUDIT_NOT_EQUAL:
+ match = (ctxt->user != rule->au_ctxt.user);
+ break;
+ }
+ break;
+ case AUDIT_SUBJ_ROLE:
+ case AUDIT_OBJ_ROLE:
+ switch (op) {
+ case AUDIT_EQUAL:
+ match = (ctxt->role == rule->au_ctxt.role);
+ break;
+ case AUDIT_NOT_EQUAL:
+ match = (ctxt->role != rule->au_ctxt.role);
+ break;
+ }
+ break;
+ case AUDIT_SUBJ_TYPE:
+ case AUDIT_OBJ_TYPE:
+ switch (op) {
+ case AUDIT_EQUAL:
+ match = (ctxt->type == rule->au_ctxt.type);
+ break;
+ case AUDIT_NOT_EQUAL:
+ match = (ctxt->type != rule->au_ctxt.type);
+ break;
+ }
+ break;
+ case AUDIT_SUBJ_SEN:
+ case AUDIT_SUBJ_CLR:
+ case AUDIT_OBJ_LEV_LOW:
+ case AUDIT_OBJ_LEV_HIGH:
+ level = ((field == AUDIT_SUBJ_SEN ||
+ field == AUDIT_OBJ_LEV_LOW) ?
+ &ctxt->range.level[0] : &ctxt->range.level[1]);
+ switch (op) {
+ case AUDIT_EQUAL:
+ match = mls_level_eq(&rule->au_ctxt.range.level[0],
+ level);
+ break;
+ case AUDIT_NOT_EQUAL:
+ match = !mls_level_eq(&rule->au_ctxt.range.level[0],
+ level);
+ break;
+ case AUDIT_LESS_THAN:
+ match = (mls_level_dom(&rule->au_ctxt.range.level[0],
+ level) &&
+ !mls_level_eq(&rule->au_ctxt.range.level[0],
+ level));
+ break;
+ case AUDIT_LESS_THAN_OR_EQUAL:
+ match = mls_level_dom(&rule->au_ctxt.range.level[0],
+ level);
+ break;
+ case AUDIT_GREATER_THAN:
+ match = (mls_level_dom(level,
+ &rule->au_ctxt.range.level[0]) &&
+ !mls_level_eq(level,
+ &rule->au_ctxt.range.level[0]));
+ break;
+ case AUDIT_GREATER_THAN_OR_EQUAL:
+ match = mls_level_dom(level,
+ &rule->au_ctxt.range.level[0]);
+ break;
+ }
+ }
+
+out:
+ POLICY_RDUNLOCK;
+ return match;
+}
+
+static int (*aurule_callback)(void) = NULL;
+
+static int aurule_avc_callback(u32 event, u32 ssid, u32 tsid,
+ u16 class, u32 perms, u32 *retained)
+{
+ int err = 0;
+
+ if (event == AVC_CALLBACK_RESET && aurule_callback)
+ err = aurule_callback();
+ return err;
+}
+
+static int __init aurule_init(void)
+{
+ int err;
+
+ err = avc_add_callback(aurule_avc_callback, AVC_CALLBACK_RESET,
+ SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
+ if (err)
+ panic("avc_add_callback() failed, error %d\n", err);
+
+ return err;
+}
+__initcall(aurule_init);
+
+void selinux_audit_set_callback(int (*callback)(void))
+{
+ aurule_callback = callback;
+}
+
+/**
+ * security_skb_extlbl_sid - Determine the external label of a packet
+ * @skb: the packet
+ * @base_sid: the SELinux SID to use as a context for MLS only external labels
+ * @sid: the packet's SID
+ *
+ * Description:
+ * Check the various different forms of external packet labeling and determine
+ * the external SID for the packet.
+ *
+ */
+void security_skb_extlbl_sid(struct sk_buff *skb, u32 base_sid, u32 *sid)
+{
+ u32 xfrm_sid;
+ u32 nlbl_sid;
+
+ selinux_skb_xfrm_sid(skb, &xfrm_sid);
+ if (selinux_netlbl_skbuff_getsid(skb,
+ (xfrm_sid == SECSID_NULL ?
+ base_sid : xfrm_sid),
+ &nlbl_sid) != 0)
+ nlbl_sid = SECSID_NULL;
+
+ *sid = (nlbl_sid == SECSID_NULL ? xfrm_sid : nlbl_sid);
+}
+
+#ifdef CONFIG_NETLABEL
+/*
+ * NetLabel cache structure
+ */
+#define NETLBL_CACHE(x) ((struct selinux_netlbl_cache *)(x))
+#define NETLBL_CACHE_T_NONE 0
+#define NETLBL_CACHE_T_SID 1
+#define NETLBL_CACHE_T_MLS 2
+struct selinux_netlbl_cache {
+ u32 type;
+ union {
+ u32 sid;
+ struct mls_range mls_label;
+ } data;
+};
+
+/**
+ * security_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 security_netlbl_cache_free(const void *data)
+{
+ struct selinux_netlbl_cache *cache;
+
+ if (data == NULL)
+ return;
+
+ cache = NETLBL_CACHE(data);
+ switch (cache->type) {
+ case NETLBL_CACHE_T_MLS:
+ ebitmap_destroy(&cache->data.mls_label.level[0].cat);
+ break;
+ }
+ kfree(data);
+}
+
+/**
+ * security_netlbl_cache_add - Add an entry to the NetLabel cache
+ * @secattr: the NetLabel packet security attributes
+ * @ctx: the SELinux context
+ *
+ * Description:
+ * Attempt to cache the context in @ctx, which was derived from the packet in
+ * @skb, in the NetLabel subsystem cache. This function assumes @secattr has
+ * already been initialized.
+ *
+ */
+static void security_netlbl_cache_add(struct netlbl_lsm_secattr *secattr,
+ struct context *ctx)
+{
+ struct selinux_netlbl_cache *cache = NULL;
+
+ secattr->cache = netlbl_secattr_cache_alloc(GFP_ATOMIC);
+ if (secattr->cache == NULL)
+ return;
+
+ cache = kzalloc(sizeof(*cache), GFP_ATOMIC);
+ if (cache == NULL)
+ return;
+
+ cache->type = NETLBL_CACHE_T_MLS;
+ if (ebitmap_cpy(&cache->data.mls_label.level[0].cat,
+ &ctx->range.level[0].cat) != 0)
+ return;
+ cache->data.mls_label.level[1].cat.highbit =
+ cache->data.mls_label.level[0].cat.highbit;
+ cache->data.mls_label.level[1].cat.node =
+ cache->data.mls_label.level[0].cat.node;
+ cache->data.mls_label.level[0].sens = ctx->range.level[0].sens;
+ cache->data.mls_label.level[1].sens = ctx->range.level[0].sens;
+
+ secattr->cache->free = security_netlbl_cache_free;
+ secattr->cache->data = (void *)cache;
+ secattr->flags |= NETLBL_SECATTR_CACHE;
+}
+
+/**
+ * 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 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
+ * 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.
+ *
+ */
+int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
+ u32 base_sid,
+ u32 *sid)
+{
+ int rc = -EIDRM;
+ struct context *ctx;
+ struct context ctx_new;
+ struct selinux_netlbl_cache *cache;
+
+ if (!ss_initialized) {
+ *sid = SECSID_NULL;
+ return 0;
+ }
+
+ POLICY_RDLOCK;
+
+ if (secattr->flags & NETLBL_SECATTR_CACHE) {
+ cache = NETLBL_CACHE(secattr->cache->data);
+ switch (cache->type) {
+ case NETLBL_CACHE_T_SID:
+ *sid = cache->data.sid;
+ rc = 0;
+ break;
+ case NETLBL_CACHE_T_MLS:
+ ctx = sidtab_search(&sidtab, base_sid);
+ if (ctx == NULL)
+ goto netlbl_secattr_to_sid_return;
+
+ ctx_new.user = ctx->user;
+ ctx_new.role = ctx->role;
+ ctx_new.type = ctx->type;
+ ctx_new.range.level[0].sens =
+ cache->data.mls_label.level[0].sens;
+ ctx_new.range.level[0].cat.highbit =
+ cache->data.mls_label.level[0].cat.highbit;
+ ctx_new.range.level[0].cat.node =
+ cache->data.mls_label.level[0].cat.node;
+ ctx_new.range.level[1].sens =
+ cache->data.mls_label.level[1].sens;
+ ctx_new.range.level[1].cat.highbit =
+ cache->data.mls_label.level[1].cat.highbit;
+ ctx_new.range.level[1].cat.node =
+ cache->data.mls_label.level[1].cat.node;
+
+ rc = sidtab_context_to_sid(&sidtab, &ctx_new, sid);
+ break;
+ default:
+ goto netlbl_secattr_to_sid_return;
+ }
+ } else if (secattr->flags & NETLBL_SECATTR_MLS_LVL) {
+ ctx = sidtab_search(&sidtab, base_sid);
+ if (ctx == NULL)
+ goto netlbl_secattr_to_sid_return;
+
+ ctx_new.user = ctx->user;
+ ctx_new.role = ctx->role;
+ ctx_new.type = ctx->type;
+ mls_import_netlbl_lvl(&ctx_new, secattr);
+ if (secattr->flags & NETLBL_SECATTR_MLS_CAT) {
+ if (ebitmap_netlbl_import(&ctx_new.range.level[0].cat,
+ secattr->mls_cat) != 0)
+ goto netlbl_secattr_to_sid_return;
+ ctx_new.range.level[1].cat.highbit =
+ ctx_new.range.level[0].cat.highbit;
+ ctx_new.range.level[1].cat.node =
+ ctx_new.range.level[0].cat.node;
+ } else {
+ ebitmap_init(&ctx_new.range.level[0].cat);
+ ebitmap_init(&ctx_new.range.level[1].cat);
+ }
+ if (mls_context_isvalid(&policydb, &ctx_new) != 1)
+ goto netlbl_secattr_to_sid_return_cleanup;
+
+ rc = sidtab_context_to_sid(&sidtab, &ctx_new, sid);
+ if (rc != 0)
+ goto netlbl_secattr_to_sid_return_cleanup;
+
+ security_netlbl_cache_add(secattr, &ctx_new);
+
+ ebitmap_destroy(&ctx_new.range.level[0].cat);
+ } else {
+ *sid = SECSID_NULL;
+ rc = 0;
+ }
+
+netlbl_secattr_to_sid_return:
+ POLICY_RDUNLOCK;
+ return rc;
+netlbl_secattr_to_sid_return_cleanup:
+ ebitmap_destroy(&ctx_new.range.level[0].cat);
+ goto netlbl_secattr_to_sid_return;
+}
+
+/**
+ * security_netlbl_sid_to_secattr - Convert a SELinux SID to a NetLabel secattr
+ * @sid: the SELinux SID
+ * @secattr: the NetLabel packet security attributes
+ *
+ * Description:
+ * Convert the given SELinux SID in @sid into a NetLabel security attribute.
+ * Returns zero on success, negative values on failure.
+ *
+ */
+int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr)
+{
+ int rc = -ENOENT;
+ struct context *ctx;
+
+ netlbl_secattr_init(secattr);
+
+ if (!ss_initialized)
+ return 0;
+
+ POLICY_RDLOCK;
+ ctx = sidtab_search(&sidtab, sid);
+ if (ctx == NULL)
+ goto netlbl_sid_to_secattr_failure;
+ secattr->domain = kstrdup(policydb.p_type_val_to_name[ctx->type - 1],
+ GFP_ATOMIC);
+ secattr->flags |= NETLBL_SECATTR_DOMAIN;
+ mls_export_netlbl_lvl(ctx, secattr);
+ rc = mls_export_netlbl_cat(ctx, secattr);
+ if (rc != 0)
+ goto netlbl_sid_to_secattr_failure;
+ POLICY_RDUNLOCK;
+
+ return 0;
+
+netlbl_sid_to_secattr_failure:
+ POLICY_RDUNLOCK;
+ netlbl_secattr_destroy(secattr);
+ return rc;
+}
+#endif /* CONFIG_NETLABEL */