summaryrefslogtreecommitdiff
path: root/security/selinux
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /security/selinux
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'security/selinux')
-rw-r--r--security/selinux/Kconfig97
-rw-r--r--security/selinux/Makefile12
-rw-r--r--security/selinux/avc.c949
-rw-r--r--security/selinux/hooks.c4565
-rw-r--r--security/selinux/include/av_inherit.h30
-rw-r--r--security/selinux/include/av_perm_to_string.h232
-rw-r--r--security/selinux/include/av_permissions.h902
-rw-r--r--security/selinux/include/avc.h137
-rw-r--r--security/selinux/include/avc_ss.h14
-rw-r--r--security/selinux/include/class_to_string.h58
-rw-r--r--security/selinux/include/common_perm_to_string.h58
-rw-r--r--security/selinux/include/conditional.h22
-rw-r--r--security/selinux/include/flask.h95
-rw-r--r--security/selinux/include/initial_sid_to_string.h33
-rw-r--r--security/selinux/include/netif.h21
-rw-r--r--security/selinux/include/objsec.h112
-rw-r--r--security/selinux/include/security.h97
-rw-r--r--security/selinux/netif.c270
-rw-r--r--security/selinux/netlink.c113
-rw-r--r--security/selinux/nlmsgtab.c156
-rw-r--r--security/selinux/selinuxfs.c1340
-rw-r--r--security/selinux/ss/Makefile9
-rw-r--r--security/selinux/ss/avtab.c399
-rw-r--r--security/selinux/ss/avtab.h85
-rw-r--r--security/selinux/ss/conditional.c489
-rw-r--r--security/selinux/ss/conditional.h77
-rw-r--r--security/selinux/ss/constraint.h61
-rw-r--r--security/selinux/ss/context.h107
-rw-r--r--security/selinux/ss/ebitmap.c293
-rw-r--r--security/selinux/ss/ebitmap.h48
-rw-r--r--security/selinux/ss/hashtab.c167
-rw-r--r--security/selinux/ss/hashtab.h87
-rw-r--r--security/selinux/ss/mls.c527
-rw-r--r--security/selinux/ss/mls.h42
-rw-r--r--security/selinux/ss/mls_types.h56
-rw-r--r--security/selinux/ss/policydb.c1843
-rw-r--r--security/selinux/ss/policydb.h275
-rw-r--r--security/selinux/ss/services.c1777
-rw-r--r--security/selinux/ss/services.h15
-rw-r--r--security/selinux/ss/sidtab.c305
-rw-r--r--security/selinux/ss/sidtab.h59
-rw-r--r--security/selinux/ss/symtab.c44
-rw-r--r--security/selinux/ss/symtab.h23
43 files changed, 16101 insertions, 0 deletions
diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig
new file mode 100644
index 00000000000..b59582b9228
--- /dev/null
+++ b/security/selinux/Kconfig
@@ -0,0 +1,97 @@
+config SECURITY_SELINUX
+ bool "NSA SELinux Support"
+ depends on SECURITY && NET && INET
+ default n
+ help
+ This selects NSA Security-Enhanced Linux (SELinux).
+ You will also need a policy configuration and a labeled filesystem.
+ You can obtain the policy compiler (checkpolicy), the utility for
+ labeling filesystems (setfiles), and an example policy configuration
+ from <http://www.nsa.gov/selinux/>.
+ If you are unsure how to answer this question, answer N.
+
+config SECURITY_SELINUX_BOOTPARAM
+ bool "NSA SELinux boot parameter"
+ depends on SECURITY_SELINUX
+ default n
+ help
+ This option adds a kernel parameter 'selinux', which allows SELinux
+ to be disabled at boot. If this option is selected, SELinux
+ functionality can be disabled with selinux=0 on the kernel
+ command line. The purpose of this option is to allow a single
+ kernel image to be distributed with SELinux built in, but not
+ necessarily enabled.
+
+ If you are unsure how to answer this question, answer N.
+
+config SECURITY_SELINUX_BOOTPARAM_VALUE
+ int "NSA SELinux boot parameter default value"
+ depends on SECURITY_SELINUX_BOOTPARAM
+ range 0 1
+ default 1
+ help
+ This option sets the default value for the kernel parameter
+ 'selinux', which allows SELinux to be disabled at boot. If this
+ option is set to 0 (zero), the SELinux kernel parameter will
+ default to 0, disabling SELinux at bootup. If this option is
+ set to 1 (one), the SELinux kernel parameter will default to 1,
+ enabling SELinux at bootup.
+
+ If you are unsure how to answer this question, answer 1.
+
+config SECURITY_SELINUX_DISABLE
+ bool "NSA SELinux runtime disable"
+ depends on SECURITY_SELINUX
+ default n
+ help
+ This option enables writing to a selinuxfs node 'disable', which
+ allows SELinux to be disabled at runtime prior to the policy load.
+ SELinux will then remain disabled until the next boot.
+ This option is similar to the selinux=0 boot parameter, but is to
+ support runtime disabling of SELinux, e.g. from /sbin/init, for
+ portability across platforms where boot parameters are difficult
+ to employ.
+
+ If you are unsure how to answer this question, answer N.
+
+config SECURITY_SELINUX_DEVELOP
+ bool "NSA SELinux Development Support"
+ depends on SECURITY_SELINUX
+ default y
+ help
+ This enables the development support option of NSA SELinux,
+ which is useful for experimenting with SELinux and developing
+ policies. If unsure, say Y. With this option enabled, the
+ kernel will start in permissive mode (log everything, deny nothing)
+ unless you specify enforcing=1 on the kernel command line. You
+ can interactively toggle the kernel between enforcing mode and
+ permissive mode (if permitted by the policy) via /selinux/enforce.
+
+config SECURITY_SELINUX_AVC_STATS
+ bool "NSA SELinux AVC Statistics"
+ depends on SECURITY_SELINUX
+ default y
+ help
+ This option collects access vector cache statistics to
+ /selinux/avc/cache_stats, which may be monitored via
+ tools such as avcstat.
+
+config SECURITY_SELINUX_CHECKREQPROT_VALUE
+ int "NSA SELinux checkreqprot default value"
+ depends on SECURITY_SELINUX
+ range 0 1
+ default 1
+ help
+ This option sets the default value for the 'checkreqprot' flag
+ that determines whether SELinux checks the protection requested
+ by the application or the protection that will be applied by the
+ kernel (including any implied execute for read-implies-exec) for
+ mmap and mprotect calls. If this option is set to 0 (zero),
+ SELinux will default to checking the protection that will be applied
+ by the kernel. If this option is set to 1 (one), SELinux will
+ default to checking the protection requested by the application.
+ The checkreqprot flag may be changed from the default via the
+ 'checkreqprot=' boot parameter. It may also be changed at runtime
+ via /selinux/checkreqprot if authorized by policy.
+
+ If you are unsure how to answer this question, answer 1.
diff --git a/security/selinux/Makefile b/security/selinux/Makefile
new file mode 100644
index 00000000000..b038cd0fae2
--- /dev/null
+++ b/security/selinux/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for building the SELinux module as part of the kernel tree.
+#
+
+obj-$(CONFIG_SECURITY_SELINUX) := selinux.o ss/
+
+selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o
+
+selinux-$(CONFIG_SECURITY_NETWORK) += netif.o
+
+EXTRA_CFLAGS += -Isecurity/selinux/include
+
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
new file mode 100644
index 00000000000..fe6285e5c68
--- /dev/null
+++ b/security/selinux/avc.c
@@ -0,0 +1,949 @@
+/*
+ * Implementation of the kernel access vector cache (AVC).
+ *
+ * Authors: Stephen Smalley, <sds@epoch.ncsc.mil>
+ * James Morris <jmorris@redhat.com>
+ *
+ * Update: KaiGai, Kohei <kaigai@ak.jp.nec.com>
+ * Replaced the avc_lock spinlock by RCU.
+ *
+ * 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 version 2,
+ * as published by the Free Software Foundation.
+ */
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/dcache.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/percpu.h>
+#include <net/sock.h>
+#include <linux/un.h>
+#include <net/af_unix.h>
+#include <linux/ip.h>
+#include <linux/audit.h>
+#include <linux/ipv6.h>
+#include <net/ipv6.h>
+#include "avc.h"
+#include "avc_ss.h"
+
+static const struct av_perm_to_string
+{
+ u16 tclass;
+ u32 value;
+ const char *name;
+} av_perm_to_string[] = {
+#define S_(c, v, s) { c, v, s },
+#include "av_perm_to_string.h"
+#undef S_
+};
+
+#ifdef CONFIG_AUDIT
+static const char *class_to_string[] = {
+#define S_(s) s,
+#include "class_to_string.h"
+#undef S_
+};
+#endif
+
+#define TB_(s) static const char * s [] = {
+#define TE_(s) };
+#define S_(s) s,
+#include "common_perm_to_string.h"
+#undef TB_
+#undef TE_
+#undef S_
+
+static const struct av_inherit
+{
+ u16 tclass;
+ const char **common_pts;
+ u32 common_base;
+} av_inherit[] = {
+#define S_(c, i, b) { c, common_##i##_perm_to_string, b },
+#include "av_inherit.h"
+#undef S_
+};
+
+#define AVC_CACHE_SLOTS 512
+#define AVC_DEF_CACHE_THRESHOLD 512
+#define AVC_CACHE_RECLAIM 16
+
+#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
+#define avc_cache_stats_incr(field) \
+do { \
+ per_cpu(avc_cache_stats, get_cpu()).field++; \
+ put_cpu(); \
+} while (0)
+#else
+#define avc_cache_stats_incr(field) do {} while (0)
+#endif
+
+struct avc_entry {
+ u32 ssid;
+ u32 tsid;
+ u16 tclass;
+ struct av_decision avd;
+ atomic_t used; /* used recently */
+};
+
+struct avc_node {
+ struct avc_entry ae;
+ struct list_head list;
+ struct rcu_head rhead;
+};
+
+struct avc_cache {
+ struct list_head slots[AVC_CACHE_SLOTS];
+ spinlock_t slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */
+ atomic_t lru_hint; /* LRU hint for reclaim scan */
+ atomic_t active_nodes;
+ u32 latest_notif; /* latest revocation notification */
+};
+
+struct avc_callback_node {
+ int (*callback) (u32 event, u32 ssid, u32 tsid,
+ u16 tclass, u32 perms,
+ u32 *out_retained);
+ u32 events;
+ u32 ssid;
+ u32 tsid;
+ u16 tclass;
+ u32 perms;
+ struct avc_callback_node *next;
+};
+
+/* Exported via selinufs */
+unsigned int avc_cache_threshold = AVC_DEF_CACHE_THRESHOLD;
+
+#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
+DEFINE_PER_CPU(struct avc_cache_stats, avc_cache_stats) = { 0 };
+#endif
+
+static struct avc_cache avc_cache;
+static struct avc_callback_node *avc_callbacks;
+static kmem_cache_t *avc_node_cachep;
+
+static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass)
+{
+ return (ssid ^ (tsid<<2) ^ (tclass<<4)) & (AVC_CACHE_SLOTS - 1);
+}
+
+/**
+ * avc_dump_av - Display an access vector in human-readable form.
+ * @tclass: target security class
+ * @av: access vector
+ */
+static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av)
+{
+ const char **common_pts = NULL;
+ u32 common_base = 0;
+ int i, i2, perm;
+
+ if (av == 0) {
+ audit_log_format(ab, " null");
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(av_inherit); i++) {
+ if (av_inherit[i].tclass == tclass) {
+ common_pts = av_inherit[i].common_pts;
+ common_base = av_inherit[i].common_base;
+ break;
+ }
+ }
+
+ audit_log_format(ab, " {");
+ i = 0;
+ perm = 1;
+ while (perm < common_base) {
+ if (perm & av) {
+ audit_log_format(ab, " %s", common_pts[i]);
+ av &= ~perm;
+ }
+ i++;
+ perm <<= 1;
+ }
+
+ while (i < sizeof(av) * 8) {
+ if (perm & av) {
+ for (i2 = 0; i2 < ARRAY_SIZE(av_perm_to_string); i2++) {
+ if ((av_perm_to_string[i2].tclass == tclass) &&
+ (av_perm_to_string[i2].value == perm))
+ break;
+ }
+ if (i2 < ARRAY_SIZE(av_perm_to_string)) {
+ audit_log_format(ab, " %s",
+ av_perm_to_string[i2].name);
+ av &= ~perm;
+ }
+ }
+ i++;
+ perm <<= 1;
+ }
+
+ if (av)
+ audit_log_format(ab, " 0x%x", av);
+
+ audit_log_format(ab, " }");
+}
+
+/**
+ * avc_dump_query - Display a SID pair and a class in human-readable form.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ */
+static void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tclass)
+{
+ int rc;
+ char *scontext;
+ u32 scontext_len;
+
+ rc = security_sid_to_context(ssid, &scontext, &scontext_len);
+ if (rc)
+ audit_log_format(ab, "ssid=%d", ssid);
+ else {
+ audit_log_format(ab, "scontext=%s", scontext);
+ kfree(scontext);
+ }
+
+ rc = security_sid_to_context(tsid, &scontext, &scontext_len);
+ if (rc)
+ audit_log_format(ab, " tsid=%d", tsid);
+ else {
+ audit_log_format(ab, " tcontext=%s", scontext);
+ kfree(scontext);
+ }
+ audit_log_format(ab, " tclass=%s", class_to_string[tclass]);
+}
+
+/**
+ * avc_init - Initialize the AVC.
+ *
+ * Initialize the access vector cache.
+ */
+void __init avc_init(void)
+{
+ int i;
+
+ for (i = 0; i < AVC_CACHE_SLOTS; i++) {
+ INIT_LIST_HEAD(&avc_cache.slots[i]);
+ spin_lock_init(&avc_cache.slots_lock[i]);
+ }
+ atomic_set(&avc_cache.active_nodes, 0);
+ atomic_set(&avc_cache.lru_hint, 0);
+
+ avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node),
+ 0, SLAB_PANIC, NULL, NULL);
+
+ audit_log(current->audit_context, "AVC INITIALIZED\n");
+}
+
+int avc_get_hash_stats(char *page)
+{
+ int i, chain_len, max_chain_len, slots_used;
+ struct avc_node *node;
+
+ rcu_read_lock();
+
+ slots_used = 0;
+ max_chain_len = 0;
+ for (i = 0; i < AVC_CACHE_SLOTS; i++) {
+ if (!list_empty(&avc_cache.slots[i])) {
+ slots_used++;
+ chain_len = 0;
+ list_for_each_entry_rcu(node, &avc_cache.slots[i], list)
+ chain_len++;
+ if (chain_len > max_chain_len)
+ max_chain_len = chain_len;
+ }
+ }
+
+ rcu_read_unlock();
+
+ return scnprintf(page, PAGE_SIZE, "entries: %d\nbuckets used: %d/%d\n"
+ "longest chain: %d\n",
+ atomic_read(&avc_cache.active_nodes),
+ slots_used, AVC_CACHE_SLOTS, max_chain_len);
+}
+
+static void avc_node_free(struct rcu_head *rhead)
+{
+ struct avc_node *node = container_of(rhead, struct avc_node, rhead);
+ kmem_cache_free(avc_node_cachep, node);
+ avc_cache_stats_incr(frees);
+}
+
+static void avc_node_delete(struct avc_node *node)
+{
+ list_del_rcu(&node->list);
+ call_rcu(&node->rhead, avc_node_free);
+ atomic_dec(&avc_cache.active_nodes);
+}
+
+static void avc_node_kill(struct avc_node *node)
+{
+ kmem_cache_free(avc_node_cachep, node);
+ avc_cache_stats_incr(frees);
+ atomic_dec(&avc_cache.active_nodes);
+}
+
+static void avc_node_replace(struct avc_node *new, struct avc_node *old)
+{
+ list_replace_rcu(&old->list, &new->list);
+ call_rcu(&old->rhead, avc_node_free);
+ atomic_dec(&avc_cache.active_nodes);
+}
+
+static inline int avc_reclaim_node(void)
+{
+ struct avc_node *node;
+ int hvalue, try, ecx;
+ unsigned long flags;
+
+ for (try = 0, ecx = 0; try < AVC_CACHE_SLOTS; try++ ) {
+ hvalue = atomic_inc_return(&avc_cache.lru_hint) & (AVC_CACHE_SLOTS - 1);
+
+ if (!spin_trylock_irqsave(&avc_cache.slots_lock[hvalue], flags))
+ continue;
+
+ list_for_each_entry(node, &avc_cache.slots[hvalue], list) {
+ if (atomic_dec_and_test(&node->ae.used)) {
+ /* Recently Unused */
+ avc_node_delete(node);
+ avc_cache_stats_incr(reclaims);
+ ecx++;
+ if (ecx >= AVC_CACHE_RECLAIM) {
+ spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flags);
+ goto out;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flags);
+ }
+out:
+ return ecx;
+}
+
+static struct avc_node *avc_alloc_node(void)
+{
+ struct avc_node *node;
+
+ node = kmem_cache_alloc(avc_node_cachep, SLAB_ATOMIC);
+ if (!node)
+ goto out;
+
+ memset(node, 0, sizeof(*node));
+ INIT_RCU_HEAD(&node->rhead);
+ INIT_LIST_HEAD(&node->list);
+ atomic_set(&node->ae.used, 1);
+ avc_cache_stats_incr(allocations);
+
+ if (atomic_inc_return(&avc_cache.active_nodes) > avc_cache_threshold)
+ avc_reclaim_node();
+
+out:
+ return node;
+}
+
+static void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, u16 tclass, struct avc_entry *ae)
+{
+ node->ae.ssid = ssid;
+ node->ae.tsid = tsid;
+ node->ae.tclass = tclass;
+ memcpy(&node->ae.avd, &ae->avd, sizeof(node->ae.avd));
+}
+
+static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass)
+{
+ struct avc_node *node, *ret = NULL;
+ int hvalue;
+
+ hvalue = avc_hash(ssid, tsid, tclass);
+ list_for_each_entry_rcu(node, &avc_cache.slots[hvalue], list) {
+ if (ssid == node->ae.ssid &&
+ tclass == node->ae.tclass &&
+ tsid == node->ae.tsid) {
+ ret = node;
+ break;
+ }
+ }
+
+ if (ret == NULL) {
+ /* cache miss */
+ goto out;
+ }
+
+ /* cache hit */
+ if (atomic_read(&ret->ae.used) != 1)
+ atomic_set(&ret->ae.used, 1);
+out:
+ return ret;
+}
+
+/**
+ * avc_lookup - Look up an AVC entry.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @requested: requested permissions, interpreted based on @tclass
+ *
+ * Look up an AVC entry that is valid for the
+ * @requested permissions between the SID pair
+ * (@ssid, @tsid), interpreting the permissions
+ * based on @tclass. If a valid AVC entry exists,
+ * then this function return the avc_node.
+ * Otherwise, this function returns NULL.
+ */
+static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass, u32 requested)
+{
+ struct avc_node *node;
+
+ avc_cache_stats_incr(lookups);
+ node = avc_search_node(ssid, tsid, tclass);
+
+ if (node && ((node->ae.avd.decided & requested) == requested)) {
+ avc_cache_stats_incr(hits);
+ goto out;
+ }
+
+ node = NULL;
+ avc_cache_stats_incr(misses);
+out:
+ return node;
+}
+
+static int avc_latest_notif_update(int seqno, int is_insert)
+{
+ int ret = 0;
+ static DEFINE_SPINLOCK(notif_lock);
+ unsigned long flag;
+
+ spin_lock_irqsave(&notif_lock, flag);
+ if (is_insert) {
+ if (seqno < avc_cache.latest_notif) {
+ printk(KERN_WARNING "avc: seqno %d < latest_notif %d\n",
+ seqno, avc_cache.latest_notif);
+ ret = -EAGAIN;
+ }
+ } else {
+ if (seqno > avc_cache.latest_notif)
+ avc_cache.latest_notif = seqno;
+ }
+ spin_unlock_irqrestore(&notif_lock, flag);
+
+ return ret;
+}
+
+/**
+ * avc_insert - Insert an AVC entry.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @ae: AVC entry
+ *
+ * Insert an AVC entry for the SID pair
+ * (@ssid, @tsid) and class @tclass.
+ * The access vectors and the sequence number are
+ * normally provided by the security server in
+ * response to a security_compute_av() call. If the
+ * sequence number @ae->avd.seqno is not less than the latest
+ * revocation notification, then the function copies
+ * the access vectors into a cache entry, returns
+ * avc_node inserted. Otherwise, this function returns NULL.
+ */
+static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct avc_entry *ae)
+{
+ struct avc_node *pos, *node = NULL;
+ int hvalue;
+ unsigned long flag;
+
+ if (avc_latest_notif_update(ae->avd.seqno, 1))
+ goto out;
+
+ node = avc_alloc_node();
+ if (node) {
+ hvalue = avc_hash(ssid, tsid, tclass);
+ avc_node_populate(node, ssid, tsid, tclass, ae);
+
+ spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag);
+ list_for_each_entry(pos, &avc_cache.slots[hvalue], list) {
+ if (pos->ae.ssid == ssid &&
+ pos->ae.tsid == tsid &&
+ pos->ae.tclass == tclass) {
+ avc_node_replace(node, pos);
+ goto found;
+ }
+ }
+ list_add_rcu(&node->list, &avc_cache.slots[hvalue]);
+found:
+ spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag);
+ }
+out:
+ return node;
+}
+
+static inline void avc_print_ipv6_addr(struct audit_buffer *ab,
+ struct in6_addr *addr, u16 port,
+ char *name1, char *name2)
+{
+ if (!ipv6_addr_any(addr))
+ audit_log_format(ab, " %s=%04x:%04x:%04x:%04x:%04x:"
+ "%04x:%04x:%04x", name1, NIP6(*addr));
+ if (port)
+ audit_log_format(ab, " %s=%d", name2, ntohs(port));
+}
+
+static inline void avc_print_ipv4_addr(struct audit_buffer *ab, u32 addr,
+ u16 port, char *name1, char *name2)
+{
+ if (addr)
+ audit_log_format(ab, " %s=%d.%d.%d.%d", name1, NIPQUAD(addr));
+ if (port)
+ audit_log_format(ab, " %s=%d", name2, ntohs(port));
+}
+
+/**
+ * avc_audit - Audit the granting or denial of permissions.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @requested: requested permissions
+ * @avd: access vector decisions
+ * @result: result from avc_has_perm_noaudit
+ * @a: auxiliary audit data
+ *
+ * Audit the granting or denial of permissions in accordance
+ * with the policy. This function is typically called by
+ * avc_has_perm() after a permission check, but can also be
+ * called directly by callers who use avc_has_perm_noaudit()
+ * in order to separate the permission check from the auditing.
+ * For example, this separation is useful when the permission check must
+ * be performed under a lock, to allow the lock to be released
+ * before calling the auditing code.
+ */
+void avc_audit(u32 ssid, u32 tsid,
+ u16 tclass, u32 requested,
+ struct av_decision *avd, int result, struct avc_audit_data *a)
+{
+ struct task_struct *tsk = current;
+ struct inode *inode = NULL;
+ u32 denied, audited;
+ struct audit_buffer *ab;
+
+ denied = requested & ~avd->allowed;
+ if (denied) {
+ audited = denied;
+ if (!(audited & avd->auditdeny))
+ return;
+ } else if (result) {
+ audited = denied = requested;
+ } else {
+ audited = requested;
+ if (!(audited & avd->auditallow))
+ return;
+ }
+
+ ab = audit_log_start(current->audit_context);
+ if (!ab)
+ return; /* audit_panic has been called */
+ audit_log_format(ab, "avc: %s ", denied ? "denied" : "granted");
+ avc_dump_av(ab, tclass,audited);
+ audit_log_format(ab, " for ");
+ if (a && a->tsk)
+ tsk = a->tsk;
+ if (tsk && tsk->pid) {
+ struct mm_struct *mm;
+ struct vm_area_struct *vma;
+ audit_log_format(ab, " pid=%d", tsk->pid);
+ if (tsk == current)
+ mm = current->mm;
+ else
+ mm = get_task_mm(tsk);
+ if (mm) {
+ if (down_read_trylock(&mm->mmap_sem)) {
+ vma = mm->mmap;
+ while (vma) {
+ if ((vma->vm_flags & VM_EXECUTABLE) &&
+ vma->vm_file) {
+ audit_log_d_path(ab, "exe=",
+ vma->vm_file->f_dentry,
+ vma->vm_file->f_vfsmnt);
+ break;
+ }
+ vma = vma->vm_next;
+ }
+ up_read(&mm->mmap_sem);
+ } else {
+ audit_log_format(ab, " comm=%s", tsk->comm);
+ }
+ if (tsk != current)
+ mmput(mm);
+ } else {
+ audit_log_format(ab, " comm=%s", tsk->comm);
+ }
+ }
+ if (a) {
+ switch (a->type) {
+ case AVC_AUDIT_DATA_IPC:
+ audit_log_format(ab, " key=%d", a->u.ipc_id);
+ break;
+ case AVC_AUDIT_DATA_CAP:
+ audit_log_format(ab, " capability=%d", a->u.cap);
+ break;
+ case AVC_AUDIT_DATA_FS:
+ if (a->u.fs.dentry) {
+ struct dentry *dentry = a->u.fs.dentry;
+ if (a->u.fs.mnt) {
+ audit_log_d_path(ab, "path=", dentry,
+ a->u.fs.mnt);
+ } else {
+ audit_log_format(ab, " name=%s",
+ dentry->d_name.name);
+ }
+ inode = dentry->d_inode;
+ } else if (a->u.fs.inode) {
+ struct dentry *dentry;
+ inode = a->u.fs.inode;
+ dentry = d_find_alias(inode);
+ if (dentry) {
+ audit_log_format(ab, " name=%s",
+ dentry->d_name.name);
+ dput(dentry);
+ }
+ }
+ if (inode)
+ audit_log_format(ab, " dev=%s ino=%ld",
+ inode->i_sb->s_id,
+ inode->i_ino);
+ break;
+ case AVC_AUDIT_DATA_NET:
+ if (a->u.net.sk) {
+ struct sock *sk = a->u.net.sk;
+ struct unix_sock *u;
+ int len = 0;
+ char *p = NULL;
+
+ switch (sk->sk_family) {
+ case AF_INET: {
+ struct inet_sock *inet = inet_sk(sk);
+
+ avc_print_ipv4_addr(ab, inet->rcv_saddr,
+ inet->sport,
+ "laddr", "lport");
+ avc_print_ipv4_addr(ab, inet->daddr,
+ inet->dport,
+ "faddr", "fport");
+ break;
+ }
+ case AF_INET6: {
+ struct inet_sock *inet = inet_sk(sk);
+ struct ipv6_pinfo *inet6 = inet6_sk(sk);
+
+ avc_print_ipv6_addr(ab, &inet6->rcv_saddr,
+ inet->sport,
+ "laddr", "lport");
+ avc_print_ipv6_addr(ab, &inet6->daddr,
+ inet->dport,
+ "faddr", "fport");
+ break;
+ }
+ case AF_UNIX:
+ u = unix_sk(sk);
+ if (u->dentry) {
+ audit_log_d_path(ab, "path=",
+ u->dentry, u->mnt);
+ break;
+ }
+ if (!u->addr)
+ break;
+ len = u->addr->len-sizeof(short);
+ p = &u->addr->name->sun_path[0];
+ if (*p)
+ audit_log_format(ab,
+ "path=%*.*s", len,
+ len, p);
+ else
+ audit_log_format(ab,
+ "path=@%*.*s", len-1,
+ len-1, p+1);
+ break;
+ }
+ }
+
+ switch (a->u.net.family) {
+ case AF_INET:
+ avc_print_ipv4_addr(ab, a->u.net.v4info.saddr,
+ a->u.net.sport,
+ "saddr", "src");
+ avc_print_ipv4_addr(ab, a->u.net.v4info.daddr,
+ a->u.net.dport,
+ "daddr", "dest");
+ break;
+ case AF_INET6:
+ avc_print_ipv6_addr(ab, &a->u.net.v6info.saddr,
+ a->u.net.sport,
+ "saddr", "src");
+ avc_print_ipv6_addr(ab, &a->u.net.v6info.daddr,
+ a->u.net.dport,
+ "daddr", "dest");
+ break;
+ }
+ if (a->u.net.netif)
+ audit_log_format(ab, " netif=%s",
+ a->u.net.netif);
+ break;
+ }
+ }
+ audit_log_format(ab, " ");
+ avc_dump_query(ab, ssid, tsid, tclass);
+ audit_log_end(ab);
+}
+
+/**
+ * avc_add_callback - Register a callback for security events.
+ * @callback: callback function
+ * @events: security events
+ * @ssid: source security identifier or %SECSID_WILD
+ * @tsid: target security identifier or %SECSID_WILD
+ * @tclass: target security class
+ * @perms: permissions
+ *
+ * Register a callback function for events in the set @events
+ * related to the SID pair (@ssid, @tsid) and
+ * and the permissions @perms, interpreting
+ * @perms based on @tclass. Returns %0 on success or
+ * -%ENOMEM if insufficient memory exists to add the callback.
+ */
+int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid,
+ u16 tclass, u32 perms,
+ u32 *out_retained),
+ u32 events, u32 ssid, u32 tsid,
+ u16 tclass, u32 perms)
+{
+ struct avc_callback_node *c;
+ int rc = 0;
+
+ c = kmalloc(sizeof(*c), GFP_ATOMIC);
+ if (!c) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ c->callback = callback;
+ c->events = events;
+ c->ssid = ssid;
+ c->tsid = tsid;
+ c->perms = perms;
+ c->next = avc_callbacks;
+ avc_callbacks = c;
+out:
+ return rc;
+}
+
+static inline int avc_sidcmp(u32 x, u32 y)
+{
+ return (x == y || x == SECSID_WILD || y == SECSID_WILD);
+}
+
+/**
+ * avc_update_node Update an AVC entry
+ * @event : Updating event
+ * @perms : Permission mask bits
+ * @ssid,@tsid,@tclass : identifier of an AVC entry
+ *
+ * if a valid AVC entry doesn't exist,this function returns -ENOENT.
+ * if kmalloc() called internal returns NULL, this function returns -ENOMEM.
+ * otherwise, this function update the AVC entry. The original AVC-entry object
+ * will release later by RCU.
+ */
+static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass)
+{
+ int hvalue, rc = 0;
+ unsigned long flag;
+ struct avc_node *pos, *node, *orig = NULL;
+
+ node = avc_alloc_node();
+ if (!node) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* Lock the target slot */
+ hvalue = avc_hash(ssid, tsid, tclass);
+ spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag);
+
+ list_for_each_entry(pos, &avc_cache.slots[hvalue], list){
+ if ( ssid==pos->ae.ssid &&
+ tsid==pos->ae.tsid &&
+ tclass==pos->ae.tclass ){
+ orig = pos;
+ break;
+ }
+ }
+
+ if (!orig) {
+ rc = -ENOENT;
+ avc_node_kill(node);
+ goto out_unlock;
+ }
+
+ /*
+ * Copy and replace original node.
+ */
+
+ avc_node_populate(node, ssid, tsid, tclass, &orig->ae);
+
+ switch (event) {
+ case AVC_CALLBACK_GRANT:
+ node->ae.avd.allowed |= perms;
+ break;
+ case AVC_CALLBACK_TRY_REVOKE:
+ case AVC_CALLBACK_REVOKE:
+ node->ae.avd.allowed &= ~perms;
+ break;
+ case AVC_CALLBACK_AUDITALLOW_ENABLE:
+ node->ae.avd.auditallow |= perms;
+ break;
+ case AVC_CALLBACK_AUDITALLOW_DISABLE:
+ node->ae.avd.auditallow &= ~perms;
+ break;
+ case AVC_CALLBACK_AUDITDENY_ENABLE:
+ node->ae.avd.auditdeny |= perms;
+ break;
+ case AVC_CALLBACK_AUDITDENY_DISABLE:
+ node->ae.avd.auditdeny &= ~perms;
+ break;
+ }
+ avc_node_replace(node, orig);
+out_unlock:
+ spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag);
+out:
+ return rc;
+}
+
+/**
+ * avc_ss_reset - Flush the cache and revalidate migrated permissions.
+ * @seqno: policy sequence number
+ */
+int avc_ss_reset(u32 seqno)
+{
+ struct avc_callback_node *c;
+ int i, rc = 0;
+ unsigned long flag;
+ struct avc_node *node;
+
+ for (i = 0; i < AVC_CACHE_SLOTS; i++) {
+ spin_lock_irqsave(&avc_cache.slots_lock[i], flag);
+ list_for_each_entry(node, &avc_cache.slots[i], list)
+ avc_node_delete(node);
+ spin_unlock_irqrestore(&avc_cache.slots_lock[i], flag);
+ }
+
+ for (c = avc_callbacks; c; c = c->next) {
+ if (c->events & AVC_CALLBACK_RESET) {
+ rc = c->callback(AVC_CALLBACK_RESET,
+ 0, 0, 0, 0, NULL);
+ if (rc)
+ goto out;
+ }
+ }
+
+ avc_latest_notif_update(seqno, 0);
+out:
+ return rc;
+}
+
+/**
+ * avc_has_perm_noaudit - Check permissions but perform no auditing.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @requested: requested permissions, interpreted based on @tclass
+ * @avd: access vector decisions
+ *
+ * Check the AVC to determine whether the @requested permissions are granted
+ * for the SID pair (@ssid, @tsid), interpreting the permissions
+ * based on @tclass, and call the security server on a cache miss to obtain
+ * a new decision and add it to the cache. Return a copy of the decisions
+ * in @avd. Return %0 if all @requested permissions are granted,
+ * -%EACCES if any permissions are denied, or another -errno upon
+ * other errors. This function is typically called by avc_has_perm(),
+ * but may also be called directly to separate permission checking from
+ * auditing, e.g. in cases where a lock must be held for the check but
+ * should be released for the auditing.
+ */
+int avc_has_perm_noaudit(u32 ssid, u32 tsid,
+ u16 tclass, u32 requested,
+ struct av_decision *avd)
+{
+ struct avc_node *node;
+ struct avc_entry entry, *p_ae;
+ int rc = 0;
+ u32 denied;
+
+ rcu_read_lock();
+
+ node = avc_lookup(ssid, tsid, tclass, requested);
+ if (!node) {
+ rcu_read_unlock();
+ rc = security_compute_av(ssid,tsid,tclass,requested,&entry.avd);
+ if (rc)
+ goto out;
+ rcu_read_lock();
+ node = avc_insert(ssid,tsid,tclass,&entry);
+ }
+
+ p_ae = node ? &node->ae : &entry;
+
+ if (avd)
+ memcpy(avd, &p_ae->avd, sizeof(*avd));
+
+ denied = requested & ~(p_ae->avd.allowed);
+
+ if (!requested || denied) {
+ if (selinux_enforcing)
+ rc = -EACCES;
+ else
+ if (node)
+ avc_update_node(AVC_CALLBACK_GRANT,requested,
+ ssid,tsid,tclass);
+ }
+
+ rcu_read_unlock();
+out:
+ return rc;
+}
+
+/**
+ * avc_has_perm - Check permissions and perform any appropriate auditing.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @requested: requested permissions, interpreted based on @tclass
+ * @auditdata: auxiliary audit data
+ *
+ * Check the AVC to determine whether the @requested permissions are granted
+ * for the SID pair (@ssid, @tsid), interpreting the permissions
+ * based on @tclass, and call the security server on a cache miss to obtain
+ * a new decision and add it to the cache. Audit the granting or denial of
+ * permissions in accordance with the policy. Return %0 if all @requested
+ * permissions are granted, -%EACCES if any permissions are denied, or
+ * another -errno upon other errors.
+ */
+int avc_has_perm(u32 ssid, u32 tsid, u16 tclass,
+ u32 requested, struct avc_audit_data *auditdata)
+{
+ struct av_decision avd;
+ int rc;
+
+ rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, &avd);
+ avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
+ return rc;
+}
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
new file mode 100644
index 00000000000..8a2cc75b394
--- /dev/null
+++ b/security/selinux/hooks.c
@@ -0,0 +1,4565 @@
+/*
+ * NSA Security-Enhanced Linux (SELinux) security module
+ *
+ * This file contains the SELinux hook function implementations.
+ *
+ * Authors: Stephen Smalley, <sds@epoch.ncsc.mil>
+ * Chris Vance, <cvance@nai.com>
+ * Wayne Salamon, <wsalamon@nai.com>
+ * James Morris <jmorris@redhat.com>
+ *
+ * Copyright (C) 2001,2002 Networks Associates Technology, Inc.
+ * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
+ * <dgoeddel@trustedcs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/security.h>
+#include <linux/xattr.h>
+#include <linux/capability.h>
+#include <linux/unistd.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/swap.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <linux/syscalls.h>
+#include <linux/file.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include <linux/ext2_fs.h>
+#include <linux/proc_fs.h>
+#include <linux/kd.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv6.h>
+#include <linux/tty.h>
+#include <net/icmp.h>
+#include <net/ip.h> /* for sysctl_local_port_range[] */
+#include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include <asm/ioctls.h>
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h> /* for network interface checks */
+#include <linux/netlink.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/quota.h>
+#include <linux/un.h> /* for Unix socket types */
+#include <net/af_unix.h> /* for Unix socket types */
+#include <linux/parser.h>
+#include <linux/nfs_mount.h>
+#include <net/ipv6.h>
+#include <linux/hugetlb.h>
+#include <linux/personality.h>
+#include <linux/sysctl.h>
+#include <linux/audit.h>
+
+#include "avc.h"
+#include "objsec.h"
+#include "netif.h"
+
+#define XATTR_SELINUX_SUFFIX "selinux"
+#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
+
+extern unsigned int policydb_loaded_version;
+extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
+
+#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
+int selinux_enforcing = 0;
+
+static int __init enforcing_setup(char *str)
+{
+ selinux_enforcing = simple_strtol(str,NULL,0);
+ return 1;
+}
+__setup("enforcing=", enforcing_setup);
+#endif
+
+#ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM
+int selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE;
+
+static int __init selinux_enabled_setup(char *str)
+{
+ selinux_enabled = simple_strtol(str, NULL, 0);
+ return 1;
+}
+__setup("selinux=", selinux_enabled_setup);
+#endif
+
+/* Original (dummy) security module. */
+static struct security_operations *original_ops = NULL;
+
+/* Minimal support for a secondary security module,
+ just to allow the use of the dummy or capability modules.
+ The owlsm module can alternatively be used as a secondary
+ module as long as CONFIG_OWLSM_FD is not enabled. */
+static struct security_operations *secondary_ops = NULL;
+
+/* Lists of inode and superblock security structures initialized
+ before the policy was loaded. */
+static LIST_HEAD(superblock_security_head);
+static DEFINE_SPINLOCK(sb_security_lock);
+
+/* Allocate and free functions for each kind of security blob. */
+
+static int task_alloc_security(struct task_struct *task)
+{
+ struct task_security_struct *tsec;
+
+ tsec = kmalloc(sizeof(struct task_security_struct), GFP_KERNEL);
+ if (!tsec)
+ return -ENOMEM;
+
+ memset(tsec, 0, sizeof(struct task_security_struct));
+ tsec->magic = SELINUX_MAGIC;
+ tsec->task = task;
+ tsec->osid = tsec->sid = tsec->ptrace_sid = SECINITSID_UNLABELED;
+ task->security = tsec;
+
+ return 0;
+}
+
+static void task_free_security(struct task_struct *task)
+{
+ struct task_security_struct *tsec = task->security;
+
+ if (!tsec || tsec->magic != SELINUX_MAGIC)
+ return;
+
+ task->security = NULL;
+ kfree(tsec);
+}
+
+static int inode_alloc_security(struct inode *inode)
+{
+ struct task_security_struct *tsec = current->security;
+ struct inode_security_struct *isec;
+
+ isec = kmalloc(sizeof(struct inode_security_struct), GFP_KERNEL);
+ if (!isec)
+ return -ENOMEM;
+
+ memset(isec, 0, sizeof(struct inode_security_struct));
+ init_MUTEX(&isec->sem);
+ INIT_LIST_HEAD(&isec->list);
+ isec->magic = SELINUX_MAGIC;
+ isec->inode = inode;
+ isec->sid = SECINITSID_UNLABELED;
+ isec->sclass = SECCLASS_FILE;
+ if (tsec && tsec->magic == SELINUX_MAGIC)
+ isec->task_sid = tsec->sid;
+ else
+ isec->task_sid = SECINITSID_UNLABELED;
+ inode->i_security = isec;
+
+ return 0;
+}
+
+static void inode_free_security(struct inode *inode)
+{
+ struct inode_security_struct *isec = inode->i_security;
+ struct superblock_security_struct *sbsec = inode->i_sb->s_security;
+
+ if (!isec || isec->magic != SELINUX_MAGIC)
+ return;
+
+ spin_lock(&sbsec->isec_lock);
+ if (!list_empty(&isec->list))
+ list_del_init(&isec->list);
+ spin_unlock(&sbsec->isec_lock);
+
+ inode->i_security = NULL;
+ kfree(isec);
+}
+
+static int file_alloc_security(struct file *file)
+{
+ struct task_security_struct *tsec = current->security;
+ struct file_security_struct *fsec;
+
+ fsec = kmalloc(sizeof(struct file_security_struct), GFP_ATOMIC);
+ if (!fsec)
+ return -ENOMEM;
+
+ memset(fsec, 0, sizeof(struct file_security_struct));
+ fsec->magic = SELINUX_MAGIC;
+ fsec->file = file;
+ if (tsec && tsec->magic == SELINUX_MAGIC) {
+ fsec->sid = tsec->sid;
+ fsec->fown_sid = tsec->sid;
+ } else {
+ fsec->sid = SECINITSID_UNLABELED;
+ fsec->fown_sid = SECINITSID_UNLABELED;
+ }
+ file->f_security = fsec;
+
+ return 0;
+}
+
+static void file_free_security(struct file *file)
+{
+ struct file_security_struct *fsec = file->f_security;
+
+ if (!fsec || fsec->magic != SELINUX_MAGIC)
+ return;
+
+ file->f_security = NULL;
+ kfree(fsec);
+}
+
+static int superblock_alloc_security(struct super_block *sb)
+{
+ struct superblock_security_struct *sbsec;
+
+ sbsec = kmalloc(sizeof(struct superblock_security_struct), GFP_KERNEL);
+ if (!sbsec)
+ return -ENOMEM;
+
+ memset(sbsec, 0, sizeof(struct superblock_security_struct));
+ init_MUTEX(&sbsec->sem);
+ INIT_LIST_HEAD(&sbsec->list);
+ INIT_LIST_HEAD(&sbsec->isec_head);
+ spin_lock_init(&sbsec->isec_lock);
+ sbsec->magic = SELINUX_MAGIC;
+ sbsec->sb = sb;
+ sbsec->sid = SECINITSID_UNLABELED;
+ sbsec->def_sid = SECINITSID_FILE;
+ sb->s_security = sbsec;
+
+ return 0;
+}
+
+static void superblock_free_security(struct super_block *sb)
+{
+ struct superblock_security_struct *sbsec = sb->s_security;
+
+ if (!sbsec || sbsec->magic != SELINUX_MAGIC)
+ return;
+
+ spin_lock(&sb_security_lock);
+ if (!list_empty(&sbsec->list))
+ list_del_init(&sbsec->list);
+ spin_unlock(&sb_security_lock);
+
+ sb->s_security = NULL;
+ kfree(sbsec);
+}
+
+#ifdef CONFIG_SECURITY_NETWORK
+static int sk_alloc_security(struct sock *sk, int family, int priority)
+{
+ struct sk_security_struct *ssec;
+
+ if (family != PF_UNIX)
+ return 0;
+
+ ssec = kmalloc(sizeof(*ssec), priority);
+ if (!ssec)
+ return -ENOMEM;
+
+ memset(ssec, 0, sizeof(*ssec));
+ ssec->magic = SELINUX_MAGIC;
+ ssec->sk = sk;
+ ssec->peer_sid = SECINITSID_UNLABELED;
+ sk->sk_security = ssec;
+
+ return 0;
+}
+
+static void sk_free_security(struct sock *sk)
+{
+ struct sk_security_struct *ssec = sk->sk_security;
+
+ if (sk->sk_family != PF_UNIX || ssec->magic != SELINUX_MAGIC)
+ return;
+
+ sk->sk_security = NULL;
+ kfree(ssec);
+}
+#endif /* CONFIG_SECURITY_NETWORK */
+
+/* The security server must be initialized before
+ any labeling or access decisions can be provided. */
+extern int ss_initialized;
+
+/* The file system's label must be initialized prior to use. */
+
+static char *labeling_behaviors[6] = {
+ "uses xattr",
+ "uses transition SIDs",
+ "uses task SIDs",
+ "uses genfs_contexts",
+ "not configured for labeling",
+ "uses mountpoint labeling",
+};
+
+static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry);
+
+static inline int inode_doinit(struct inode *inode)
+{
+ return inode_doinit_with_dentry(inode, NULL);
+}
+
+enum {
+ Opt_context = 1,
+ Opt_fscontext = 2,
+ Opt_defcontext = 4,
+};
+
+static match_table_t tokens = {
+ {Opt_context, "context=%s"},
+ {Opt_fscontext, "fscontext=%s"},
+ {Opt_defcontext, "defcontext=%s"},
+};
+
+#define SEL_MOUNT_FAIL_MSG "SELinux: duplicate or incompatible mount options\n"
+
+static int try_context_mount(struct super_block *sb, void *data)
+{
+ char *context = NULL, *defcontext = NULL;
+ const char *name;
+ u32 sid;
+ int alloc = 0, rc = 0, seen = 0;
+ struct task_security_struct *tsec = current->security;
+ struct superblock_security_struct *sbsec = sb->s_security;
+
+ if (!data)
+ goto out;
+
+ name = sb->s_type->name;
+
+ if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA) {
+
+ /* NFS we understand. */
+ if (!strcmp(name, "nfs")) {
+ struct nfs_mount_data *d = data;
+
+ if (d->version < NFS_MOUNT_VERSION)
+ goto out;
+
+ if (d->context[0]) {
+ context = d->context;
+ seen |= Opt_context;
+ }
+ } else
+ goto out;
+
+ } else {
+ /* Standard string-based options. */
+ char *p, *options = data;
+
+ while ((p = strsep(&options, ",")) != NULL) {
+ int token;
+ substring_t args[MAX_OPT_ARGS];
+
+ if (!*p)
+ continue;
+
+ token = match_token(p, tokens, args);
+
+ switch (token) {
+ case Opt_context:
+ if (seen) {
+ rc = -EINVAL;
+ printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
+ goto out_free;
+ }
+ context = match_strdup(&args[0]);
+ if (!context) {
+ rc = -ENOMEM;
+ goto out_free;
+ }
+ if (!alloc)
+ alloc = 1;
+ seen |= Opt_context;
+ break;
+
+ case Opt_fscontext:
+ if (seen & (Opt_context|Opt_fscontext)) {
+ rc = -EINVAL;
+ printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
+ goto out_free;
+ }
+ context = match_strdup(&args[0]);
+ if (!context) {
+ rc = -ENOMEM;
+ goto out_free;
+ }
+ if (!alloc)
+ alloc = 1;
+ seen |= Opt_fscontext;
+ break;
+
+ case Opt_defcontext:
+ if (sbsec->behavior != SECURITY_FS_USE_XATTR) {
+ rc = -EINVAL;
+ printk(KERN_WARNING "SELinux: "
+ "defcontext option is invalid "
+ "for this filesystem type\n");
+ goto out_free;
+ }
+ if (seen & (Opt_context|Opt_defcontext)) {
+ rc = -EINVAL;
+ printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
+ goto out_free;
+ }
+ defcontext = match_strdup(&args[0]);
+ if (!defcontext) {
+ rc = -ENOMEM;
+ goto out_free;
+ }
+ if (!alloc)
+ alloc = 1;
+ seen |= Opt_defcontext;
+ break;
+
+ default:
+ rc = -EINVAL;
+ printk(KERN_WARNING "SELinux: unknown mount "
+ "option\n");
+ goto out_free;
+
+ }
+ }
+ }
+
+ if (!seen)
+ goto out;
+
+ if (context) {
+ rc = security_context_to_sid(context, strlen(context), &sid);
+ if (rc) {
+ printk(KERN_WARNING "SELinux: security_context_to_sid"
+ "(%s) failed for (dev %s, type %s) errno=%d\n",
+ context, sb->s_id, name, rc);
+ goto out_free;
+ }
+
+ rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
+ FILESYSTEM__RELABELFROM, NULL);
+ if (rc)
+ goto out_free;
+
+ rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM,
+ FILESYSTEM__RELABELTO, NULL);
+ if (rc)
+ goto out_free;
+
+ sbsec->sid = sid;
+
+ if (seen & Opt_context)
+ sbsec->behavior = SECURITY_FS_USE_MNTPOINT;
+ }
+
+ if (defcontext) {
+ rc = security_context_to_sid(defcontext, strlen(defcontext), &sid);
+ if (rc) {
+ printk(KERN_WARNING "SELinux: security_context_to_sid"
+ "(%s) failed for (dev %s, type %s) errno=%d\n",
+ defcontext, sb->s_id, name, rc);
+ goto out_free;
+ }
+
+ if (sid == sbsec->def_sid)
+ goto out_free;
+
+ rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
+ FILESYSTEM__RELABELFROM, NULL);
+ if (rc)
+ goto out_free;
+
+ rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM,
+ FILESYSTEM__ASSOCIATE, NULL);
+ if (rc)
+ goto out_free;
+
+ sbsec->def_sid = sid;
+ }
+
+out_free:
+ if (alloc) {
+ kfree(context);
+ kfree(defcontext);
+ }
+out:
+ return rc;
+}
+
+static int superblock_doinit(struct super_block *sb, void *data)
+{
+ struct superblock_security_struct *sbsec = sb->s_security;
+ struct dentry *root = sb->s_root;
+ struct inode *inode = root->d_inode;
+ int rc = 0;
+
+ down(&sbsec->sem);
+ if (sbsec->initialized)
+ goto out;
+
+ if (!ss_initialized) {
+ /* Defer initialization until selinux_complete_init,
+ after the initial policy is loaded and the security
+ server is ready to handle calls. */
+ spin_lock(&sb_security_lock);
+ if (list_empty(&sbsec->list))
+ list_add(&sbsec->list, &superblock_security_head);
+ spin_unlock(&sb_security_lock);
+ goto out;
+ }
+
+ /* Determine the labeling behavior to use for this filesystem type. */
+ rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid);
+ if (rc) {
+ printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
+ __FUNCTION__, sb->s_type->name, rc);
+ goto out;
+ }
+
+ rc = try_context_mount(sb, data);
+ if (rc)
+ goto out;
+
+ if (sbsec->behavior == SECURITY_FS_USE_XATTR) {
+ /* Make sure that the xattr handler exists and that no
+ error other than -ENODATA is returned by getxattr on
+ the root directory. -ENODATA is ok, as this may be
+ the first boot of the SELinux kernel before we have
+ assigned xattr values to the filesystem. */
+ if (!inode->i_op->getxattr) {
+ printk(KERN_WARNING "SELinux: (dev %s, type %s) has no "
+ "xattr support\n", sb->s_id, sb->s_type->name);
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+ rc = inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0);
+ if (rc < 0 && rc != -ENODATA) {
+ if (rc == -EOPNOTSUPP)
+ printk(KERN_WARNING "SELinux: (dev %s, type "
+ "%s) has no security xattr handler\n",
+ sb->s_id, sb->s_type->name);
+ else
+ printk(KERN_WARNING "SELinux: (dev %s, type "
+ "%s) getxattr errno %d\n", sb->s_id,
+ sb->s_type->name, -rc);
+ goto out;
+ }
+ }
+
+ if (strcmp(sb->s_type->name, "proc") == 0)
+ sbsec->proc = 1;
+
+ sbsec->initialized = 1;
+
+ if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors)) {
+ printk(KERN_INFO "SELinux: initialized (dev %s, type %s), unknown behavior\n",
+ sb->s_id, sb->s_type->name);
+ }
+ else {
+ printk(KERN_INFO "SELinux: initialized (dev %s, type %s), %s\n",
+ sb->s_id, sb->s_type->name,
+ labeling_behaviors[sbsec->behavior-1]);
+ }
+
+ /* Initialize the root inode. */
+ rc = inode_doinit_with_dentry(sb->s_root->d_inode, sb->s_root);
+
+ /* Initialize any other inodes associated with the superblock, e.g.
+ inodes created prior to initial policy load or inodes created
+ during get_sb by a pseudo filesystem that directly
+ populates itself. */
+ spin_lock(&sbsec->isec_lock);
+next_inode:
+ if (!list_empty(&sbsec->isec_head)) {
+ struct inode_security_struct *isec =
+ list_entry(sbsec->isec_head.next,
+ struct inode_security_struct, list);
+ struct inode *inode = isec->inode;
+ spin_unlock(&sbsec->isec_lock);
+ inode = igrab(inode);
+ if (inode) {
+ if (!IS_PRIVATE (inode))
+ inode_doinit(inode);
+ iput(inode);
+ }
+ spin_lock(&sbsec->isec_lock);
+ list_del_init(&isec->list);
+ goto next_inode;
+ }
+ spin_unlock(&sbsec->isec_lock);
+out:
+ up(&sbsec->sem);
+ return rc;
+}
+
+static inline u16 inode_mode_to_security_class(umode_t mode)
+{
+ switch (mode & S_IFMT) {
+ case S_IFSOCK:
+ return SECCLASS_SOCK_FILE;
+ case S_IFLNK:
+ return SECCLASS_LNK_FILE;
+ case S_IFREG:
+ return SECCLASS_FILE;
+ case S_IFBLK:
+ return SECCLASS_BLK_FILE;
+ case S_IFDIR:
+ return SECCLASS_DIR;
+ case S_IFCHR:
+ return SECCLASS_CHR_FILE;
+ case S_IFIFO:
+ return SECCLASS_FIFO_FILE;
+
+ }
+
+ return SECCLASS_FILE;
+}
+
+static inline u16 socket_type_to_security_class(int family, int type, int protocol)
+{
+ switch (family) {
+ case PF_UNIX:
+ switch (type) {
+ case SOCK_STREAM:
+ case SOCK_SEQPACKET:
+ return SECCLASS_UNIX_STREAM_SOCKET;
+ case SOCK_DGRAM:
+ return SECCLASS_UNIX_DGRAM_SOCKET;
+ }
+ break;
+ case PF_INET:
+ case PF_INET6:
+ switch (type) {
+ case SOCK_STREAM:
+ return SECCLASS_TCP_SOCKET;
+ case SOCK_DGRAM:
+ return SECCLASS_UDP_SOCKET;
+ case SOCK_RAW:
+ return SECCLASS_RAWIP_SOCKET;
+ }
+ break;
+ case PF_NETLINK:
+ switch (protocol) {
+ case NETLINK_ROUTE:
+ return SECCLASS_NETLINK_ROUTE_SOCKET;
+ case NETLINK_FIREWALL:
+ return SECCLASS_NETLINK_FIREWALL_SOCKET;
+ case NETLINK_TCPDIAG:
+ return SECCLASS_NETLINK_TCPDIAG_SOCKET;
+ case NETLINK_NFLOG:
+ return SECCLASS_NETLINK_NFLOG_SOCKET;
+ case NETLINK_XFRM:
+ return SECCLASS_NETLINK_XFRM_SOCKET;
+ case NETLINK_SELINUX:
+ return SECCLASS_NETLINK_SELINUX_SOCKET;
+ case NETLINK_AUDIT:
+ return SECCLASS_NETLINK_AUDIT_SOCKET;
+ case NETLINK_IP6_FW:
+ return SECCLASS_NETLINK_IP6FW_SOCKET;
+ case NETLINK_DNRTMSG:
+ return SECCLASS_NETLINK_DNRT_SOCKET;
+ default:
+ return SECCLASS_NETLINK_SOCKET;
+ }
+ case PF_PACKET:
+ return SECCLASS_PACKET_SOCKET;
+ case PF_KEY:
+ return SECCLASS_KEY_SOCKET;
+ }
+
+ return SECCLASS_SOCKET;
+}
+
+#ifdef CONFIG_PROC_FS
+static int selinux_proc_get_sid(struct proc_dir_entry *de,
+ u16 tclass,
+ u32 *sid)
+{
+ int buflen, rc;
+ char *buffer, *path, *end;
+
+ buffer = (char*)__get_free_page(GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ buflen = PAGE_SIZE;
+ end = buffer+buflen;
+ *--end = '\0';
+ buflen--;
+ path = end-1;
+ *path = '/';
+ while (de && de != de->parent) {
+ buflen -= de->namelen + 1;
+ if (buflen < 0)
+ break;
+ end -= de->namelen;
+ memcpy(end, de->name, de->namelen);
+ *--end = '/';
+ path = end;
+ de = de->parent;
+ }
+ rc = security_genfs_sid("proc", path, tclass, sid);
+ free_page((unsigned long)buffer);
+ return rc;
+}
+#else
+static int selinux_proc_get_sid(struct proc_dir_entry *de,
+ u16 tclass,
+ u32 *sid)
+{
+ return -EINVAL;
+}
+#endif
+
+/* The inode's security attributes must be initialized before first use. */
+static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry)
+{
+ struct superblock_security_struct *sbsec = NULL;
+ struct inode_security_struct *isec = inode->i_security;
+ u32 sid;
+ struct dentry *dentry;
+#define INITCONTEXTLEN 255
+ char *context = NULL;
+ unsigned len = 0;
+ int rc = 0;
+ int hold_sem = 0;
+
+ if (isec->initialized)
+ goto out;
+
+ down(&isec->sem);
+ hold_sem = 1;
+ if (isec->initialized)
+ goto out;
+
+ sbsec = inode->i_sb->s_security;
+ if (!sbsec->initialized) {
+ /* Defer initialization until selinux_complete_init,
+ after the initial policy is loaded and the security
+ server is ready to handle calls. */
+ spin_lock(&sbsec->isec_lock);
+ if (list_empty(&isec->list))
+ list_add(&isec->list, &sbsec->isec_head);
+ spin_unlock(&sbsec->isec_lock);
+ goto out;
+ }
+
+ switch (sbsec->behavior) {
+ case SECURITY_FS_USE_XATTR:
+ if (!inode->i_op->getxattr) {
+ isec->sid = sbsec->def_sid;
+ break;
+ }
+
+ /* Need a dentry, since the xattr API requires one.
+ Life would be simpler if we could just pass the inode. */
+ if (opt_dentry) {
+ /* Called from d_instantiate or d_splice_alias. */
+ dentry = dget(opt_dentry);
+ } else {
+ /* Called from selinux_complete_init, try to find a dentry. */
+ dentry = d_find_alias(inode);
+ }
+ if (!dentry) {
+ printk(KERN_WARNING "%s: no dentry for dev=%s "
+ "ino=%ld\n", __FUNCTION__, inode->i_sb->s_id,
+ inode->i_ino);
+ goto out;
+ }
+
+ len = INITCONTEXTLEN;
+ context = kmalloc(len, GFP_KERNEL);
+ if (!context) {
+ rc = -ENOMEM;
+ dput(dentry);
+ goto out;
+ }
+ rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX,
+ context, len);
+ if (rc == -ERANGE) {
+ /* Need a larger buffer. Query for the right size. */
+ rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX,
+ NULL, 0);
+ if (rc < 0) {
+ dput(dentry);
+ goto out;
+ }
+ kfree(context);
+ len = rc;
+ context = kmalloc(len, GFP_KERNEL);
+ if (!context) {
+ rc = -ENOMEM;
+ dput(dentry);
+ goto out;
+ }
+ rc = inode->i_op->getxattr(dentry,
+ XATTR_NAME_SELINUX,
+ context, len);
+ }
+ dput(dentry);
+ if (rc < 0) {
+ if (rc != -ENODATA) {
+ printk(KERN_WARNING "%s: getxattr returned "
+ "%d for dev=%s ino=%ld\n", __FUNCTION__,
+ -rc, inode->i_sb->s_id, inode->i_ino);
+ kfree(context);
+ goto out;
+ }
+ /* Map ENODATA to the default file SID */
+ sid = sbsec->def_sid;
+ rc = 0;
+ } else {
+ rc = security_context_to_sid(context, rc, &sid);
+ if (rc) {
+ printk(KERN_WARNING "%s: context_to_sid(%s) "
+ "returned %d for dev=%s ino=%ld\n",
+ __FUNCTION__, context, -rc,
+ inode->i_sb->s_id, inode->i_ino);
+ kfree(context);
+ /* Leave with the unlabeled SID */
+ rc = 0;
+ break;
+ }
+ }
+ kfree(context);
+ isec->sid = sid;
+ break;
+ case SECURITY_FS_USE_TASK:
+ isec->sid = isec->task_sid;
+ break;
+ case SECURITY_FS_USE_TRANS:
+ /* Default to the fs SID. */
+ isec->sid = sbsec->sid;
+
+ /* Try to obtain a transition SID. */
+ isec->sclass = inode_mode_to_security_class(inode->i_mode);
+ rc = security_transition_sid(isec->task_sid,
+ sbsec->sid,
+ isec->sclass,
+ &sid);
+ if (rc)
+ goto out;
+ isec->sid = sid;
+ break;
+ default:
+ /* Default to the fs SID. */
+ isec->sid = sbsec->sid;
+
+ if (sbsec->proc) {
+ struct proc_inode *proci = PROC_I(inode);
+ if (proci->pde) {
+ isec->sclass = inode_mode_to_security_class(inode->i_mode);
+ rc = selinux_proc_get_sid(proci->pde,
+ isec->sclass,
+ &sid);
+ if (rc)
+ goto out;
+ isec->sid = sid;
+ }
+ }
+ break;
+ }
+
+ isec->initialized = 1;
+
+out:
+ if (isec->sclass == SECCLASS_FILE)
+ isec->sclass = inode_mode_to_security_class(inode->i_mode);
+
+ if (hold_sem)
+ up(&isec->sem);
+ return rc;
+}
+
+/* Convert a Linux signal to an access vector. */
+static inline u32 signal_to_av(int sig)
+{
+ u32 perm = 0;
+
+ switch (sig) {
+ case SIGCHLD:
+ /* Commonly granted from child to parent. */
+ perm = PROCESS__SIGCHLD;
+ break;
+ case SIGKILL:
+ /* Cannot be caught or ignored */
+ perm = PROCESS__SIGKILL;
+ break;
+ case SIGSTOP:
+ /* Cannot be caught or ignored */
+ perm = PROCESS__SIGSTOP;
+ break;
+ default:
+ /* All other signals. */
+ perm = PROCESS__SIGNAL;
+ break;
+ }
+
+ return perm;
+}
+
+/* Check permission betweeen a pair of tasks, e.g. signal checks,
+ fork check, ptrace check, etc. */
+static int task_has_perm(struct task_struct *tsk1,
+ struct task_struct *tsk2,
+ u32 perms)
+{
+ struct task_security_struct *tsec1, *tsec2;
+
+ tsec1 = tsk1->security;
+ tsec2 = tsk2->security;
+ return avc_has_perm(tsec1->sid, tsec2->sid,
+ SECCLASS_PROCESS, perms, NULL);
+}
+
+/* Check whether a task is allowed to use a capability. */
+static int task_has_capability(struct task_struct *tsk,
+ int cap)
+{
+ struct task_security_struct *tsec;
+ struct avc_audit_data ad;
+
+ tsec = tsk->security;
+
+ AVC_AUDIT_DATA_INIT(&ad,CAP);
+ ad.tsk = tsk;
+ ad.u.cap = cap;
+
+ return avc_has_perm(tsec->sid, tsec->sid,
+ SECCLASS_CAPABILITY, CAP_TO_MASK(cap), &ad);
+}
+
+/* Check whether a task is allowed to use a system operation. */
+static int task_has_system(struct task_struct *tsk,
+ u32 perms)
+{
+ struct task_security_struct *tsec;
+
+ tsec = tsk->security;
+
+ return avc_has_perm(tsec->sid, SECINITSID_KERNEL,
+ SECCLASS_SYSTEM, perms, NULL);
+}
+
+/* Check whether a task has a particular permission to an inode.
+ The 'adp' parameter is optional and allows other audit
+ data to be passed (e.g. the dentry). */
+static int inode_has_perm(struct task_struct *tsk,
+ struct inode *inode,
+ u32 perms,
+ struct avc_audit_data *adp)
+{
+ struct task_security_struct *tsec;
+ struct inode_security_struct *isec;
+ struct avc_audit_data ad;
+
+ tsec = tsk->security;
+ isec = inode->i_security;
+
+ if (!adp) {
+ adp = &ad;
+ AVC_AUDIT_DATA_INIT(&ad, FS);
+ ad.u.fs.inode = inode;
+ }
+
+ return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, adp);
+}
+
+/* Same as inode_has_perm, but pass explicit audit data containing
+ the dentry to help the auditing code to more easily generate the
+ pathname if needed. */
+static inline int dentry_has_perm(struct task_struct *tsk,
+ struct vfsmount *mnt,
+ struct dentry *dentry,
+ u32 av)
+{
+ struct inode *inode = dentry->d_inode;
+ struct avc_audit_data ad;
+ AVC_AUDIT_DATA_INIT(&ad,FS);
+ ad.u.fs.mnt = mnt;
+ ad.u.fs.dentry = dentry;
+ return inode_has_perm(tsk, inode, av, &ad);
+}
+
+/* Check whether a task can use an open file descriptor to
+ access an inode in a given way. Check access to the
+ descriptor itself, and then use dentry_has_perm to
+ check a particular permission to the file.
+ Access to the descriptor is implicitly granted if it
+ has the same SID as the process. If av is zero, then
+ access to the file is not checked, e.g. for cases
+ where only the descriptor is affected like seek. */
+static inline int file_has_perm(struct task_struct *tsk,
+ struct file *file,
+ u32 av)
+{
+ struct task_security_struct *tsec = tsk->security;
+ struct file_security_struct *fsec = file->f_security;
+ struct vfsmount *mnt = file->f_vfsmnt;
+ struct dentry *dentry = file->f_dentry;
+ struct inode *inode = dentry->d_inode;
+ struct avc_audit_data ad;
+ int rc;
+
+ AVC_AUDIT_DATA_INIT(&ad, FS);
+ ad.u.fs.mnt = mnt;
+ ad.u.fs.dentry = dentry;
+
+ if (tsec->sid != fsec->sid) {
+ rc = avc_has_perm(tsec->sid, fsec->sid,
+ SECCLASS_FD,
+ FD__USE,
+ &ad);
+ if (rc)
+ return rc;
+ }
+
+ /* av is zero if only checking access to the descriptor. */
+ if (av)
+ return inode_has_perm(tsk, inode, av, &ad);
+
+ return 0;
+}
+
+/* Check whether a task can create a file. */
+static int may_create(struct inode *dir,
+ struct dentry *dentry,
+ u16 tclass)
+{
+ struct task_security_struct *tsec;
+ struct inode_security_struct *dsec;
+ struct superblock_security_struct *sbsec;
+ u32 newsid;
+ struct avc_audit_data ad;
+ int rc;
+
+ tsec = current->security;
+ dsec = dir->i_security;
+ sbsec = dir->i_sb->s_security;
+
+ AVC_AUDIT_DATA_INIT(&ad, FS);
+ ad.u.fs.dentry = dentry;
+
+ rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR,
+ DIR__ADD_NAME | DIR__SEARCH,
+ &ad);
+ if (rc)
+ return rc;
+
+ if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
+ newsid = tsec->create_sid;
+ } else {
+ rc = security_transition_sid(tsec->sid, dsec->sid, tclass,
+ &newsid);
+ if (rc)
+ return rc;
+ }
+
+ rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, &ad);
+ if (rc)
+ return rc;
+
+ return avc_has_perm(newsid, sbsec->sid,
+ SECCLASS_FILESYSTEM,
+ FILESYSTEM__ASSOCIATE, &ad);
+}
+
+#define MAY_LINK 0
+#define MAY_UNLINK 1
+#define MAY_RMDIR 2
+
+/* Check whether a task can link, unlink, or rmdir a file/directory. */
+static int may_link(struct inode *dir,
+ struct dentry *dentry,
+ int kind)
+
+{
+ struct task_security_struct *tsec;
+ struct inode_security_struct *dsec, *isec;
+ struct avc_audit_data ad;
+ u32 av;
+ int rc;
+
+ tsec = current->security;
+ dsec = dir->i_security;
+ isec = dentry->d_inode->i_security;
+
+ AVC_AUDIT_DATA_INIT(&ad, FS);
+ ad.u.fs.dentry = dentry;
+
+ av = DIR__SEARCH;
+ av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME);
+ rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, av, &ad);
+ if (rc)
+ return rc;
+
+ switch (kind) {
+ case MAY_LINK:
+ av = FILE__LINK;
+ break;
+ case MAY_UNLINK:
+ av = FILE__UNLINK;
+ break;
+ case MAY_RMDIR:
+ av = DIR__RMDIR;
+ break;
+ default:
+ printk(KERN_WARNING "may_link: unrecognized kind %d\n", kind);
+ return 0;
+ }
+
+ rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, av, &ad);
+ return rc;
+}
+
+static inline int may_rename(struct inode *old_dir,
+ struct dentry *old_dentry,
+ struct inode *new_dir,
+ struct dentry *new_dentry)
+{
+ struct task_security_struct *tsec;
+ struct inode_security_struct *old_dsec, *new_dsec, *old_isec, *new_isec;
+ struct avc_audit_data ad;
+ u32 av;
+ int old_is_dir, new_is_dir;
+ int rc;
+
+ tsec = current->security;
+ old_dsec = old_dir->i_security;
+ old_isec = old_dentry->d_inode->i_security;
+ old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
+ new_dsec = new_dir->i_security;
+
+ AVC_AUDIT_DATA_INIT(&ad, FS);
+
+ ad.u.fs.dentry = old_dentry;
+ rc = avc_has_perm(tsec->sid, old_dsec->sid, SECCLASS_DIR,
+ DIR__REMOVE_NAME | DIR__SEARCH, &ad);
+ if (rc)
+ return rc;
+ rc = avc_has_perm(tsec->sid, old_isec->sid,
+ old_isec->sclass, FILE__RENAME, &ad);
+ if (rc)
+ return rc;
+ if (old_is_dir && new_dir != old_dir) {
+ rc = avc_has_perm(tsec->sid, old_isec->sid,
+ old_isec->sclass, DIR__REPARENT, &ad);
+ if (rc)
+ return rc;
+ }
+
+ ad.u.fs.dentry = new_dentry;
+ av = DIR__ADD_NAME | DIR__SEARCH;
+ if (new_dentry->d_inode)
+ av |= DIR__REMOVE_NAME;
+ rc = avc_has_perm(tsec->sid, new_dsec->sid, SECCLASS_DIR, av, &ad);
+ if (rc)
+ return rc;
+ if (new_dentry->d_inode) {
+ new_isec = new_dentry->d_inode->i_security;
+ new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode);
+ rc = avc_has_perm(tsec->sid, new_isec->sid,
+ new_isec->sclass,
+ (new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+/* Check whether a task can perform a filesystem operation. */
+static int superblock_has_perm(struct task_struct *tsk,
+ struct super_block *sb,
+ u32 perms,
+ struct avc_audit_data *ad)
+{
+ struct task_security_struct *tsec;
+ struct superblock_security_struct *sbsec;
+
+ tsec = tsk->security;
+ sbsec = sb->s_security;
+ return avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
+ perms, ad);
+}
+
+/* Convert a Linux mode and permission mask to an access vector. */
+static inline u32 file_mask_to_av(int mode, int mask)
+{
+ u32 av = 0;
+
+ if ((mode & S_IFMT) != S_IFDIR) {
+ if (mask & MAY_EXEC)
+ av |= FILE__EXECUTE;
+ if (mask & MAY_READ)
+ av |= FILE__READ;
+
+ if (mask & MAY_APPEND)
+ av |= FILE__APPEND;
+ else if (mask & MAY_WRITE)
+ av |= FILE__WRITE;
+
+ } else {
+ if (mask & MAY_EXEC)
+ av |= DIR__SEARCH;
+ if (mask & MAY_WRITE)
+ av |= DIR__WRITE;
+ if (mask & MAY_READ)
+ av |= DIR__READ;
+ }
+
+ return av;
+}
+
+/* Convert a Linux file to an access vector. */
+static inline u32 file_to_av(struct file *file)
+{
+ u32 av = 0;
+
+ if (file->f_mode & FMODE_READ)
+ av |= FILE__READ;
+ if (file->f_mode & FMODE_WRITE) {
+ if (file->f_flags & O_APPEND)
+ av |= FILE__APPEND;
+ else
+ av |= FILE__WRITE;
+ }
+
+ return av;
+}
+
+/* Set an inode's SID to a specified value. */
+static int inode_security_set_sid(struct inode *inode, u32 sid)
+{
+ struct inode_security_struct *isec = inode->i_security;
+ struct superblock_security_struct *sbsec = inode->i_sb->s_security;
+
+ if (!sbsec->initialized) {
+ /* Defer initialization to selinux_complete_init. */
+ return 0;
+ }
+
+ down(&isec->sem);
+ isec->sclass = inode_mode_to_security_class(inode->i_mode);
+ isec->sid = sid;
+ isec->initialized = 1;
+ up(&isec->sem);
+ return 0;
+}
+
+/* Set the security attributes on a newly created file. */
+static int post_create(struct inode *dir,
+ struct dentry *dentry)
+{
+
+ struct task_security_struct *tsec;
+ struct inode *inode;
+ struct inode_security_struct *dsec;
+ struct superblock_security_struct *sbsec;
+ u32 newsid;
+ char *context;
+ unsigned int len;
+ int rc;
+
+ tsec = current->security;
+ dsec = dir->i_security;
+ sbsec = dir->i_sb->s_security;
+
+ inode = dentry->d_inode;
+ if (!inode) {
+ /* Some file system types (e.g. NFS) may not instantiate
+ a dentry for all create operations (e.g. symlink),
+ so we have to check to see if the inode is non-NULL. */
+ printk(KERN_WARNING "post_create: no inode, dir (dev=%s, "
+ "ino=%ld)\n", dir->i_sb->s_id, dir->i_ino);
+ return 0;
+ }
+
+ if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
+ newsid = tsec->create_sid;
+ } else {
+ rc = security_transition_sid(tsec->sid, dsec->sid,
+ inode_mode_to_security_class(inode->i_mode),
+ &newsid);
+ if (rc) {
+ printk(KERN_WARNING "post_create: "
+ "security_transition_sid failed, rc=%d (dev=%s "
+ "ino=%ld)\n",
+ -rc, inode->i_sb->s_id, inode->i_ino);
+ return rc;
+ }
+ }
+
+ rc = inode_security_set_sid(inode, newsid);
+ if (rc) {
+ printk(KERN_WARNING "post_create: inode_security_set_sid "
+ "failed, rc=%d (dev=%s ino=%ld)\n",
+ -rc, inode->i_sb->s_id, inode->i_ino);
+ return rc;
+ }
+
+ if (sbsec->behavior == SECURITY_FS_USE_XATTR &&
+ inode->i_op->setxattr) {
+ /* Use extended attributes. */
+ rc = security_sid_to_context(newsid, &context, &len);
+ if (rc) {
+ printk(KERN_WARNING "post_create: sid_to_context "
+ "failed, rc=%d (dev=%s ino=%ld)\n",
+ -rc, inode->i_sb->s_id, inode->i_ino);
+ return rc;
+ }
+ down(&inode->i_sem);
+ rc = inode->i_op->setxattr(dentry,
+ XATTR_NAME_SELINUX,
+ context, len, 0);
+ up(&inode->i_sem);
+ kfree(context);
+ if (rc < 0) {
+ printk(KERN_WARNING "post_create: setxattr failed, "
+ "rc=%d (dev=%s ino=%ld)\n",
+ -rc, inode->i_sb->s_id, inode->i_ino);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+
+/* Hook functions begin here. */
+
+static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
+{
+ struct task_security_struct *psec = parent->security;
+ struct task_security_struct *csec = child->security;
+ int rc;
+
+ rc = secondary_ops->ptrace(parent,child);
+ if (rc)
+ return rc;
+
+ rc = task_has_perm(parent, child, PROCESS__PTRACE);
+ /* Save the SID of the tracing process for later use in apply_creds. */
+ if (!rc)
+ csec->ptrace_sid = psec->sid;
+ return rc;
+}
+
+static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
+ kernel_cap_t *inheritable, kernel_cap_t *permitted)
+{
+ int error;
+
+ error = task_has_perm(current, target, PROCESS__GETCAP);
+ if (error)
+ return error;
+
+ return secondary_ops->capget(target, effective, inheritable, permitted);
+}
+
+static int selinux_capset_check(struct task_struct *target, kernel_cap_t *effective,
+ kernel_cap_t *inheritable, kernel_cap_t *permitted)
+{
+ int error;
+
+ error = secondary_ops->capset_check(target, effective, inheritable, permitted);
+ if (error)
+ return error;
+
+ return task_has_perm(current, target, PROCESS__SETCAP);
+}
+
+static void selinux_capset_set(struct task_struct *target, kernel_cap_t *effective,
+ kernel_cap_t *inheritable, kernel_cap_t *permitted)
+{
+ secondary_ops->capset_set(target, effective, inheritable, permitted);
+}
+
+static int selinux_capable(struct task_struct *tsk, int cap)
+{
+ int rc;
+
+ rc = secondary_ops->capable(tsk, cap);
+ if (rc)
+ return rc;
+
+ return task_has_capability(tsk,cap);
+}
+
+static int selinux_sysctl(ctl_table *table, int op)
+{
+ int error = 0;
+ u32 av;
+ struct task_security_struct *tsec;
+ u32 tsid;
+ int rc;
+
+ rc = secondary_ops->sysctl(table, op);
+ if (rc)
+ return rc;
+
+ tsec = current->security;
+
+ rc = selinux_proc_get_sid(table->de, (op == 001) ?
+ SECCLASS_DIR : SECCLASS_FILE, &tsid);
+ if (rc) {
+ /* Default to the well-defined sysctl SID. */
+ tsid = SECINITSID_SYSCTL;
+ }
+
+ /* The op values are "defined" in sysctl.c, thereby creating
+ * a bad coupling between this module and sysctl.c */
+ if(op == 001) {
+ error = avc_has_perm(tsec->sid, tsid,
+ SECCLASS_DIR, DIR__SEARCH, NULL);
+ } else {
+ av = 0;
+ if (op & 004)
+ av |= FILE__READ;
+ if (op & 002)
+ av |= FILE__WRITE;
+ if (av)
+ error = avc_has_perm(tsec->sid, tsid,
+ SECCLASS_FILE, av, NULL);
+ }
+
+ return error;
+}
+
+static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
+{
+ int rc = 0;
+
+ if (!sb)
+ return 0;
+
+ switch (cmds) {
+ case Q_SYNC:
+ case Q_QUOTAON:
+ case Q_QUOTAOFF:
+ case Q_SETINFO:
+ case Q_SETQUOTA:
+ rc = superblock_has_perm(current,
+ sb,
+ FILESYSTEM__QUOTAMOD, NULL);
+ break;
+ case Q_GETFMT:
+ case Q_GETINFO:
+ case Q_GETQUOTA:
+ rc = superblock_has_perm(current,
+ sb,
+ FILESYSTEM__QUOTAGET, NULL);
+ break;
+ default:
+ rc = 0; /* let the kernel handle invalid cmds */
+ break;
+ }
+ return rc;
+}
+
+static int selinux_quota_on(struct dentry *dentry)
+{
+ return dentry_has_perm(current, NULL, dentry, FILE__QUOTAON);
+}
+
+static int selinux_syslog(int type)
+{
+ int rc;
+
+ rc = secondary_ops->syslog(type);
+ if (rc)
+ return rc;
+
+ switch (type) {
+ case 3: /* Read last kernel messages */
+ case 10: /* Return size of the log buffer */
+ rc = task_has_system(current, SYSTEM__SYSLOG_READ);
+ break;
+ case 6: /* Disable logging to console */
+ case 7: /* Enable logging to console */
+ case 8: /* Set level of messages printed to console */
+ rc = task_has_system(current, SYSTEM__SYSLOG_CONSOLE);
+ break;
+ case 0: /* Close log */
+ case 1: /* Open log */
+ case 2: /* Read from log */
+ case 4: /* Read/clear last kernel messages */
+ case 5: /* Clear ring buffer */
+ default:
+ rc = task_has_system(current, SYSTEM__SYSLOG_MOD);
+ break;
+ }
+ return rc;
+}
+
+/*
+ * Check that a process has enough memory to allocate a new virtual
+ * mapping. 0 means there is enough memory for the allocation to
+ * succeed and -ENOMEM implies there is not.
+ *
+ * Note that secondary_ops->capable and task_has_perm_noaudit return 0
+ * if the capability is granted, but __vm_enough_memory requires 1 if
+ * the capability is granted.
+ *
+ * Do not audit the selinux permission check, as this is applied to all
+ * processes that allocate mappings.
+ */
+static int selinux_vm_enough_memory(long pages)
+{
+ int rc, cap_sys_admin = 0;
+ struct task_security_struct *tsec = current->security;
+
+ rc = secondary_ops->capable(current, CAP_SYS_ADMIN);
+ if (rc == 0)
+ rc = avc_has_perm_noaudit(tsec->sid, tsec->sid,
+ SECCLASS_CAPABILITY,
+ CAP_TO_MASK(CAP_SYS_ADMIN),
+ NULL);
+
+ if (rc == 0)
+ cap_sys_admin = 1;
+
+ return __vm_enough_memory(pages, cap_sys_admin);
+}
+
+/* binprm security operations */
+
+static int selinux_bprm_alloc_security(struct linux_binprm *bprm)
+{
+ struct bprm_security_struct *bsec;
+
+ bsec = kmalloc(sizeof(struct bprm_security_struct), GFP_KERNEL);
+ if (!bsec)
+ return -ENOMEM;
+
+ memset(bsec, 0, sizeof *bsec);
+ bsec->magic = SELINUX_MAGIC;
+ bsec->bprm = bprm;
+ bsec->sid = SECINITSID_UNLABELED;
+ bsec->set = 0;
+
+ bprm->security = bsec;
+ return 0;
+}
+
+static int selinux_bprm_set_security(struct linux_binprm *bprm)
+{
+ struct task_security_struct *tsec;
+ struct inode *inode = bprm->file->f_dentry->d_inode;
+ struct inode_security_struct *isec;
+ struct bprm_security_struct *bsec;
+ u32 newsid;
+ struct avc_audit_data ad;
+ int rc;
+
+ rc = secondary_ops->bprm_set_security(bprm);
+ if (rc)
+ return rc;
+
+ bsec = bprm->security;
+
+ if (bsec->set)
+ return 0;
+
+ tsec = current->security;
+ isec = inode->i_security;
+
+ /* Default to the current task SID. */
+ bsec->sid = tsec->sid;
+
+ /* Reset create SID on execve. */
+ tsec->create_sid = 0;
+
+ if (tsec->exec_sid) {
+ newsid = tsec->exec_sid;
+ /* Reset exec SID on execve. */
+ tsec->exec_sid = 0;
+ } else {
+ /* Check for a default transition on this program. */
+ rc = security_transition_sid(tsec->sid, isec->sid,
+ SECCLASS_PROCESS, &newsid);
+ if (rc)
+ return rc;
+ }
+
+ AVC_AUDIT_DATA_INIT(&ad, FS);
+ ad.u.fs.mnt = bprm->file->f_vfsmnt;
+ ad.u.fs.dentry = bprm->file->f_dentry;
+
+ if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID)
+ newsid = tsec->sid;
+
+ if (tsec->sid == newsid) {
+ rc = avc_has_perm(tsec->sid, isec->sid,
+ SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);
+ if (rc)
+ return rc;
+ } else {
+ /* Check permissions for the transition. */
+ rc = avc_has_perm(tsec->sid, newsid,
+ SECCLASS_PROCESS, PROCESS__TRANSITION, &ad);
+ if (rc)
+ return rc;
+
+ rc = avc_has_perm(newsid, isec->sid,
+ SECCLASS_FILE, FILE__ENTRYPOINT, &ad);
+ if (rc)
+ return rc;
+
+ /* Clear any possibly unsafe personality bits on exec: */
+ current->personality &= ~PER_CLEAR_ON_SETID;
+
+ /* Set the security field to the new SID. */
+ bsec->sid = newsid;
+ }
+
+ bsec->set = 1;
+ return 0;
+}
+
+static int selinux_bprm_check_security (struct linux_binprm *bprm)
+{
+ return secondary_ops->bprm_check_security(bprm);
+}
+
+
+static int selinux_bprm_secureexec (struct linux_binprm *bprm)
+{
+ struct task_security_struct *tsec = current->security;
+ int atsecure = 0;
+
+ if (tsec->osid != tsec->sid) {
+ /* Enable secure mode for SIDs transitions unless
+ the noatsecure permission is granted between
+ the two SIDs, i.e. ahp returns 0. */
+ atsecure = avc_has_perm(tsec->osid, tsec->sid,
+ SECCLASS_PROCESS,
+ PROCESS__NOATSECURE, NULL);
+ }
+
+ return (atsecure || secondary_ops->bprm_secureexec(bprm));
+}
+
+static void selinux_bprm_free_security(struct linux_binprm *bprm)
+{
+ struct bprm_security_struct *bsec = bprm->security;
+ bprm->security = NULL;
+ kfree(bsec);
+}
+
+extern struct vfsmount *selinuxfs_mount;
+extern struct dentry *selinux_null;
+
+/* Derived from fs/exec.c:flush_old_files. */
+static inline void flush_unauthorized_files(struct files_struct * files)
+{
+ struct avc_audit_data ad;
+ struct file *file, *devnull = NULL;
+ struct tty_struct *tty = current->signal->tty;
+ long j = -1;
+
+ if (tty) {
+ file_list_lock();
+ file = list_entry(tty->tty_files.next, typeof(*file), f_list);
+ if (file) {
+ /* Revalidate access to controlling tty.
+ Use inode_has_perm on the tty inode directly rather
+ than using file_has_perm, as this particular open
+ file may belong to another process and we are only
+ interested in the inode-based check here. */
+ struct inode *inode = file->f_dentry->d_inode;
+ if (inode_has_perm(current, inode,
+ FILE__READ | FILE__WRITE, NULL)) {
+ /* Reset controlling tty. */
+ current->signal->tty = NULL;
+ current->signal->tty_old_pgrp = 0;
+ }
+ }
+ file_list_unlock();
+ }
+
+ /* Revalidate access to inherited open files. */
+
+ AVC_AUDIT_DATA_INIT(&ad,FS);
+
+ spin_lock(&files->file_lock);
+ for (;;) {
+ unsigned long set, i;
+ int fd;
+
+ j++;
+ i = j * __NFDBITS;
+ if (i >= files->max_fds || i >= files->max_fdset)
+ break;
+ set = files->open_fds->fds_bits[j];
+ if (!set)
+ continue;
+ spin_unlock(&files->file_lock);
+ for ( ; set ; i++,set >>= 1) {
+ if (set & 1) {
+ file = fget(i);
+ if (!file)
+ continue;
+ if (file_has_perm(current,
+ file,
+ file_to_av(file))) {
+ sys_close(i);
+ fd = get_unused_fd();
+ if (fd != i) {
+ if (fd >= 0)
+ put_unused_fd(fd);
+ fput(file);
+ continue;
+ }
+ if (devnull) {
+ atomic_inc(&devnull->f_count);
+ } else {
+ devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR);
+ if (!devnull) {
+ put_unused_fd(fd);
+ fput(file);
+ continue;
+ }
+ }
+ fd_install(fd, devnull);
+ }
+ fput(file);
+ }
+ }
+ spin_lock(&files->file_lock);
+
+ }
+ spin_unlock(&files->file_lock);
+}
+
+static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
+{
+ struct task_security_struct *tsec;
+ struct bprm_security_struct *bsec;
+ u32 sid;
+ int rc;
+
+ secondary_ops->bprm_apply_creds(bprm, unsafe);
+
+ tsec = current->security;
+
+ bsec = bprm->security;
+ sid = bsec->sid;
+
+ tsec->osid = tsec->sid;
+ bsec->unsafe = 0;
+ if (tsec->sid != sid) {
+ /* Check for shared state. If not ok, leave SID
+ unchanged and kill. */
+ if (unsafe & LSM_UNSAFE_SHARE) {
+ rc = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
+ PROCESS__SHARE, NULL);
+ if (rc) {
+ bsec->unsafe = 1;
+ return;
+ }
+ }
+
+ /* Check for ptracing, and update the task SID if ok.
+ Otherwise, leave SID unchanged and kill. */
+ if (unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
+ rc = avc_has_perm(tsec->ptrace_sid, sid,
+ SECCLASS_PROCESS, PROCESS__PTRACE,
+ NULL);
+ if (rc) {
+ bsec->unsafe = 1;
+ return;
+ }
+ }
+ tsec->sid = sid;
+ }
+}
+
+/*
+ * called after apply_creds without the task lock held
+ */
+static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
+{
+ struct task_security_struct *tsec;
+ struct rlimit *rlim, *initrlim;
+ struct itimerval itimer;
+ struct bprm_security_struct *bsec;
+ int rc, i;
+
+ tsec = current->security;
+ bsec = bprm->security;
+
+ if (bsec->unsafe) {
+ force_sig_specific(SIGKILL, current);
+ return;
+ }
+ if (tsec->osid == tsec->sid)
+ return;
+
+ /* Close files for which the new task SID is not authorized. */
+ flush_unauthorized_files(current->files);
+
+ /* Check whether the new SID can inherit signal state
+ from the old SID. If not, clear itimers to avoid
+ subsequent signal generation and flush and unblock
+ signals. This must occur _after_ the task SID has
+ been updated so that any kill done after the flush
+ will be checked against the new SID. */
+ rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
+ PROCESS__SIGINH, NULL);
+ if (rc) {
+ memset(&itimer, 0, sizeof itimer);
+ for (i = 0; i < 3; i++)
+ do_setitimer(i, &itimer, NULL);
+ flush_signals(current);
+ spin_lock_irq(&current->sighand->siglock);
+ flush_signal_handlers(current, 1);
+ sigemptyset(&current->blocked);
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+ }
+
+ /* Check whether the new SID can inherit resource limits
+ from the old SID. If not, reset all soft limits to
+ the lower of the current task's hard limit and the init
+ task's soft limit. Note that the setting of hard limits
+ (even to lower them) can be controlled by the setrlimit
+ check. The inclusion of the init task's soft limit into
+ the computation is to avoid resetting soft limits higher
+ than the default soft limit for cases where the default
+ is lower than the hard limit, e.g. RLIMIT_CORE or
+ RLIMIT_STACK.*/
+ rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
+ PROCESS__RLIMITINH, NULL);
+ if (rc) {
+ for (i = 0; i < RLIM_NLIMITS; i++) {
+ rlim = current->signal->rlim + i;
+ initrlim = init_task.signal->rlim+i;
+ rlim->rlim_cur = min(rlim->rlim_max,initrlim->rlim_cur);
+ }
+ if (current->signal->rlim[RLIMIT_CPU].rlim_cur != RLIM_INFINITY) {
+ /*
+ * This will cause RLIMIT_CPU calculations
+ * to be refigured.
+ */
+ current->it_prof_expires = jiffies_to_cputime(1);
+ }
+ }
+
+ /* Wake up the parent if it is waiting so that it can
+ recheck wait permission to the new task SID. */
+ wake_up_interruptible(&current->parent->signal->wait_chldexit);
+}
+
+/* superblock security operations */
+
+static int selinux_sb_alloc_security(struct super_block *sb)
+{
+ return superblock_alloc_security(sb);
+}
+
+static void selinux_sb_free_security(struct super_block *sb)
+{
+ superblock_free_security(sb);
+}
+
+static inline int match_prefix(char *prefix, int plen, char *option, int olen)
+{
+ if (plen > olen)
+ return 0;
+
+ return !memcmp(prefix, option, plen);
+}
+
+static inline int selinux_option(char *option, int len)
+{
+ return (match_prefix("context=", sizeof("context=")-1, option, len) ||
+ match_prefix("fscontext=", sizeof("fscontext=")-1, option, len) ||
+ match_prefix("defcontext=", sizeof("defcontext=")-1, option, len));
+}
+
+static inline void take_option(char **to, char *from, int *first, int len)
+{
+ if (!*first) {
+ **to = ',';
+ *to += 1;
+ }
+ else
+ *first = 0;
+ memcpy(*to, from, len);
+ *to += len;
+}
+
+static int selinux_sb_copy_data(struct file_system_type *type, void *orig, void *copy)
+{
+ int fnosec, fsec, rc = 0;
+ char *in_save, *in_curr, *in_end;
+ char *sec_curr, *nosec_save, *nosec;
+
+ in_curr = orig;
+ sec_curr = copy;
+
+ /* Binary mount data: just copy */
+ if (type->fs_flags & FS_BINARY_MOUNTDATA) {
+ copy_page(sec_curr, in_curr);
+ goto out;
+ }
+
+ nosec = (char *)get_zeroed_page(GFP_KERNEL);
+ if (!nosec) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ nosec_save = nosec;
+ fnosec = fsec = 1;
+ in_save = in_end = orig;
+
+ do {
+ if (*in_end == ',' || *in_end == '\0') {
+ int len = in_end - in_curr;
+
+ if (selinux_option(in_curr, len))
+ take_option(&sec_curr, in_curr, &fsec, len);
+ else
+ take_option(&nosec, in_curr, &fnosec, len);
+
+ in_curr = in_end + 1;
+ }
+ } while (*in_end++);
+
+ copy_page(in_save, nosec_save);
+out:
+ return rc;
+}
+
+static int selinux_sb_kern_mount(struct super_block *sb, void *data)
+{
+ struct avc_audit_data ad;
+ int rc;
+
+ rc = superblock_doinit(sb, data);
+ if (rc)
+ return rc;
+
+ AVC_AUDIT_DATA_INIT(&ad,FS);
+ ad.u.fs.dentry = sb->s_root;
+ return superblock_has_perm(current, sb, FILESYSTEM__MOUNT, &ad);
+}
+
+static int selinux_sb_statfs(struct super_block *sb)
+{
+ struct avc_audit_data ad;
+
+ AVC_AUDIT_DATA_INIT(&ad,FS);
+ ad.u.fs.dentry = sb->s_root;
+ return superblock_has_perm(current, sb, FILESYSTEM__GETATTR, &ad);
+}
+
+static int selinux_mount(char * dev_name,
+ struct nameidata *nd,
+ char * type,
+ unsigned long flags,
+ void * data)
+{
+ int rc;
+
+ rc = secondary_ops->sb_mount(dev_name, nd, type, flags, data);
+ if (rc)
+ return rc;
+
+ if (flags & MS_REMOUNT)
+ return superblock_has_perm(current, nd->mnt->mnt_sb,
+ FILESYSTEM__REMOUNT, NULL);
+ else
+ return dentry_has_perm(current, nd->mnt, nd->dentry,
+ FILE__MOUNTON);
+}
+
+static int selinux_umount(struct vfsmount *mnt, int flags)
+{
+ int rc;
+
+ rc = secondary_ops->sb_umount(mnt, flags);
+ if (rc)
+ return rc;
+
+ return superblock_has_perm(current,mnt->mnt_sb,
+ FILESYSTEM__UNMOUNT,NULL);
+}
+
+/* inode security operations */
+
+static int selinux_inode_alloc_security(struct inode *inode)
+{
+ return inode_alloc_security(inode);
+}
+
+static void selinux_inode_free_security(struct inode *inode)
+{
+ inode_free_security(inode);
+}
+
+static int selinux_inode_create(struct inode *dir, struct dentry *dentry, int mask)
+{
+ return may_create(dir, dentry, SECCLASS_FILE);
+}
+
+static void selinux_inode_post_create(struct inode *dir, struct dentry *dentry, int mask)
+{
+ post_create(dir, dentry);
+}
+
+static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
+{
+ int rc;
+
+ rc = secondary_ops->inode_link(old_dentry,dir,new_dentry);
+ if (rc)
+ return rc;
+ return may_link(dir, old_dentry, MAY_LINK);
+}
+
+static void selinux_inode_post_link(struct dentry *old_dentry, struct inode *inode, struct dentry *new_dentry)
+{
+ return;
+}
+
+static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry)
+{
+ int rc;
+
+ rc = secondary_ops->inode_unlink(dir, dentry);
+ if (rc)
+ return rc;
+ return may_link(dir, dentry, MAY_UNLINK);
+}
+
+static int selinux_inode_symlink(struct inode *dir, struct dentry *dentry, const char *name)
+{
+ return may_create(dir, dentry, SECCLASS_LNK_FILE);
+}
+
+static void selinux_inode_post_symlink(struct inode *dir, struct dentry *dentry, const char *name)
+{
+ post_create(dir, dentry);
+}
+
+static int selinux_inode_mkdir(struct inode *dir, struct dentry *dentry, int mask)
+{
+ return may_create(dir, dentry, SECCLASS_DIR);
+}
+
+static void selinux_inode_post_mkdir(struct inode *dir, struct dentry *dentry, int mask)
+{
+ post_create(dir, dentry);
+}
+
+static int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ return may_link(dir, dentry, MAY_RMDIR);
+}
+
+static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+{
+ int rc;
+
+ rc = secondary_ops->inode_mknod(dir, dentry, mode, dev);
+ if (rc)
+ return rc;
+
+ return may_create(dir, dentry, inode_mode_to_security_class(mode));
+}
+
+static void selinux_inode_post_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+{
+ post_create(dir, dentry);
+}
+
+static int selinux_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
+ struct inode *new_inode, struct dentry *new_dentry)
+{
+ return may_rename(old_inode, old_dentry, new_inode, new_dentry);
+}
+
+static void selinux_inode_post_rename(struct inode *old_inode, struct dentry *old_dentry,
+ struct inode *new_inode, struct dentry *new_dentry)
+{
+ return;
+}
+
+static int selinux_inode_readlink(struct dentry *dentry)
+{
+ return dentry_has_perm(current, NULL, dentry, FILE__READ);
+}
+
+static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *nameidata)
+{
+ int rc;
+
+ rc = secondary_ops->inode_follow_link(dentry,nameidata);
+ if (rc)
+ return rc;
+ return dentry_has_perm(current, NULL, dentry, FILE__READ);
+}
+
+static int selinux_inode_permission(struct inode *inode, int mask,
+ struct nameidata *nd)
+{
+ int rc;
+
+ rc = secondary_ops->inode_permission(inode, mask, nd);
+ if (rc)
+ return rc;
+
+ if (!mask) {
+ /* No permission to check. Existence test. */
+ return 0;
+ }
+
+ return inode_has_perm(current, inode,
+ file_mask_to_av(inode->i_mode, mask), NULL);
+}
+
+static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+ int rc;
+
+ rc = secondary_ops->inode_setattr(dentry, iattr);
+ if (rc)
+ return rc;
+
+ if (iattr->ia_valid & ATTR_FORCE)
+ return 0;
+
+ if (iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID |
+ ATTR_ATIME_SET | ATTR_MTIME_SET))
+ return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
+
+ return dentry_has_perm(current, NULL, dentry, FILE__WRITE);
+}
+
+static int selinux_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
+{
+ return dentry_has_perm(current, mnt, dentry, FILE__GETATTR);
+}
+
+static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value, size_t size, int flags)
+{
+ struct task_security_struct *tsec = current->security;
+ struct inode *inode = dentry->d_inode;
+ struct inode_security_struct *isec = inode->i_security;
+ struct superblock_security_struct *sbsec;
+ struct avc_audit_data ad;
+ u32 newsid;
+ int rc = 0;
+
+ if (strcmp(name, XATTR_NAME_SELINUX)) {
+ if (!strncmp(name, XATTR_SECURITY_PREFIX,
+ sizeof XATTR_SECURITY_PREFIX - 1) &&
+ !capable(CAP_SYS_ADMIN)) {
+ /* A different attribute in the security namespace.
+ Restrict to administrator. */
+ return -EPERM;
+ }
+
+ /* Not an attribute we recognize, so just check the
+ ordinary setattr permission. */
+ return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
+ }
+
+ sbsec = inode->i_sb->s_security;
+ if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
+ return -EOPNOTSUPP;
+
+ if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+ return -EPERM;
+
+ AVC_AUDIT_DATA_INIT(&ad,FS);
+ ad.u.fs.dentry = dentry;
+
+ rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
+ FILE__RELABELFROM, &ad);
+ if (rc)
+ return rc;
+
+ rc = security_context_to_sid(value, size, &newsid);
+ if (rc)
+ return rc;
+
+ rc = avc_has_perm(tsec->sid, newsid, isec->sclass,
+ FILE__RELABELTO, &ad);
+ if (rc)
+ return rc;
+
+ rc = security_validate_transition(isec->sid, newsid, tsec->sid,
+ isec->sclass);
+ if (rc)
+ return rc;
+
+ return avc_has_perm(newsid,
+ sbsec->sid,
+ SECCLASS_FILESYSTEM,
+ FILESYSTEM__ASSOCIATE,
+ &ad);
+}
+
+static void selinux_inode_post_setxattr(struct dentry *dentry, char *name,
+ void *value, size_t size, int flags)
+{
+ struct inode *inode = dentry->d_inode;
+ struct inode_security_struct *isec = inode->i_security;
+ u32 newsid;
+ int rc;
+
+ if (strcmp(name, XATTR_NAME_SELINUX)) {
+ /* Not an attribute we recognize, so nothing to do. */
+ return;
+ }
+
+ rc = security_context_to_sid(value, size, &newsid);
+ if (rc) {
+ printk(KERN_WARNING "%s: unable to obtain SID for context "
+ "%s, rc=%d\n", __FUNCTION__, (char*)value, -rc);
+ return;
+ }
+
+ isec->sid = newsid;
+ return;
+}
+
+static int selinux_inode_getxattr (struct dentry *dentry, char *name)
+{
+ struct inode *inode = dentry->d_inode;
+ struct superblock_security_struct *sbsec = inode->i_sb->s_security;
+
+ if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
+ return -EOPNOTSUPP;
+
+ return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
+}
+
+static int selinux_inode_listxattr (struct dentry *dentry)
+{
+ return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
+}
+
+static int selinux_inode_removexattr (struct dentry *dentry, char *name)
+{
+ if (strcmp(name, XATTR_NAME_SELINUX)) {
+ if (!strncmp(name, XATTR_SECURITY_PREFIX,
+ sizeof XATTR_SECURITY_PREFIX - 1) &&
+ !capable(CAP_SYS_ADMIN)) {
+ /* A different attribute in the security namespace.
+ Restrict to administrator. */
+ return -EPERM;
+ }
+
+ /* Not an attribute we recognize, so just check the
+ ordinary setattr permission. Might want a separate
+ permission for removexattr. */
+ return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
+ }
+
+ /* No one is allowed to remove a SELinux security label.
+ You can change the label, but all data must be labeled. */
+ return -EACCES;
+}
+
+static int selinux_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size)
+{
+ struct inode_security_struct *isec = inode->i_security;
+ char *context;
+ unsigned len;
+ int rc;
+
+ /* Permission check handled by selinux_inode_getxattr hook.*/
+
+ if (strcmp(name, XATTR_SELINUX_SUFFIX))
+ return -EOPNOTSUPP;
+
+ rc = security_sid_to_context(isec->sid, &context, &len);
+ if (rc)
+ return rc;
+
+ if (!buffer || !size) {
+ kfree(context);
+ return len;
+ }
+ if (size < len) {
+ kfree(context);
+ return -ERANGE;
+ }
+ memcpy(buffer, context, len);
+ kfree(context);
+ return len;
+}
+
+static int selinux_inode_setsecurity(struct inode *inode, const char *name,
+ const void *value, size_t size, int flags)
+{
+ struct inode_security_struct *isec = inode->i_security;
+ u32 newsid;
+ int rc;
+
+ if (strcmp(name, XATTR_SELINUX_SUFFIX))
+ return -EOPNOTSUPP;
+
+ if (!value || !size)
+ return -EACCES;
+
+ rc = security_context_to_sid((void*)value, size, &newsid);
+ if (rc)
+ return rc;
+
+ isec->sid = newsid;
+ return 0;
+}
+
+static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size)
+{
+ const int len = sizeof(XATTR_NAME_SELINUX);
+ if (buffer && len <= buffer_size)
+ memcpy(buffer, XATTR_NAME_SELINUX, len);
+ return len;
+}
+
+/* file security operations */
+
+static int selinux_file_permission(struct file *file, int mask)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+
+ if (!mask) {
+ /* No permission to check. Existence test. */
+ return 0;
+ }
+
+ /* file_mask_to_av won't add FILE__WRITE if MAY_APPEND is set */
+ if ((file->f_flags & O_APPEND) && (mask & MAY_WRITE))
+ mask |= MAY_APPEND;
+
+ return file_has_perm(current, file,
+ file_mask_to_av(inode->i_mode, mask));
+}
+
+static int selinux_file_alloc_security(struct file *file)
+{
+ return file_alloc_security(file);
+}
+
+static void selinux_file_free_security(struct file *file)
+{
+ file_free_security(file);
+}
+
+static int selinux_file_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int error = 0;
+
+ switch (cmd) {
+ case FIONREAD:
+ /* fall through */
+ case FIBMAP:
+ /* fall through */
+ case FIGETBSZ:
+ /* fall through */
+ case EXT2_IOC_GETFLAGS:
+ /* fall through */
+ case EXT2_IOC_GETVERSION:
+ error = file_has_perm(current, file, FILE__GETATTR);
+ break;
+
+ case EXT2_IOC_SETFLAGS:
+ /* fall through */
+ case EXT2_IOC_SETVERSION:
+ error = file_has_perm(current, file, FILE__SETATTR);
+ break;
+
+ /* sys_ioctl() checks */
+ case FIONBIO:
+ /* fall through */
+ case FIOASYNC:
+ error = file_has_perm(current, file, 0);
+ break;
+
+ case KDSKBENT:
+ case KDSKBSENT:
+ error = task_has_capability(current,CAP_SYS_TTY_CONFIG);
+ break;
+
+ /* default case assumes that the command will go
+ * to the file's ioctl() function.
+ */
+ default:
+ error = file_has_perm(current, file, FILE__IOCTL);
+
+ }
+ return error;
+}
+
+static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
+{
+#ifndef CONFIG_PPC32
+ if ((prot & PROT_EXEC) && (!file || (!shared && (prot & PROT_WRITE)))) {
+ /*
+ * We are making executable an anonymous mapping or a
+ * private file mapping that will also be writable.
+ * This has an additional check.
+ */
+ int rc = task_has_perm(current, current, PROCESS__EXECMEM);
+ if (rc)
+ return rc;
+ }
+#endif
+
+ if (file) {
+ /* read access is always possible with a mapping */
+ u32 av = FILE__READ;
+
+ /* write access only matters if the mapping is shared */
+ if (shared && (prot & PROT_WRITE))
+ av |= FILE__WRITE;
+
+ if (prot & PROT_EXEC)
+ av |= FILE__EXECUTE;
+
+ return file_has_perm(current, file, av);
+ }
+ return 0;
+}
+
+static int selinux_file_mmap(struct file *file, unsigned long reqprot,
+ unsigned long prot, unsigned long flags)
+{
+ int rc;
+
+ rc = secondary_ops->file_mmap(file, reqprot, prot, flags);
+ if (rc)
+ return rc;
+
+ if (selinux_checkreqprot)
+ prot = reqprot;
+
+ return file_map_prot_check(file, prot,
+ (flags & MAP_TYPE) == MAP_SHARED);
+}
+
+static int selinux_file_mprotect(struct vm_area_struct *vma,
+ unsigned long reqprot,
+ unsigned long prot)
+{
+ int rc;
+
+ rc = secondary_ops->file_mprotect(vma, reqprot, prot);
+ if (rc)
+ return rc;
+
+ if (selinux_checkreqprot)
+ prot = reqprot;
+
+#ifndef CONFIG_PPC32
+ if (vma->vm_file != NULL && vma->anon_vma != NULL && (prot & PROT_EXEC)) {
+ /*
+ * We are making executable a file mapping that has
+ * had some COW done. Since pages might have been written,
+ * check ability to execute the possibly modified content.
+ * This typically should only occur for text relocations.
+ */
+ int rc = file_has_perm(current, vma->vm_file, FILE__EXECMOD);
+ if (rc)
+ return rc;
+ }
+#endif
+
+ return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED);
+}
+
+static int selinux_file_lock(struct file *file, unsigned int cmd)
+{
+ return file_has_perm(current, file, FILE__LOCK);
+}
+
+static int selinux_file_fcntl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int err = 0;
+
+ switch (cmd) {
+ case F_SETFL:
+ if (!file->f_dentry || !file->f_dentry->d_inode) {
+ err = -EINVAL;
+ break;
+ }
+
+ if ((file->f_flags & O_APPEND) && !(arg & O_APPEND)) {
+ err = file_has_perm(current, file,FILE__WRITE);
+ break;
+ }
+ /* fall through */
+ case F_SETOWN:
+ case F_SETSIG:
+ case F_GETFL:
+ case F_GETOWN:
+ case F_GETSIG:
+ /* Just check FD__USE permission */
+ err = file_has_perm(current, file, 0);
+ break;
+ case F_GETLK:
+ case F_SETLK:
+ case F_SETLKW:
+#if BITS_PER_LONG == 32
+ case F_GETLK64:
+ case F_SETLK64:
+ case F_SETLKW64:
+#endif
+ if (!file->f_dentry || !file->f_dentry->d_inode) {
+ err = -EINVAL;
+ break;
+ }
+ err = file_has_perm(current, file, FILE__LOCK);
+ break;
+ }
+
+ return err;
+}
+
+static int selinux_file_set_fowner(struct file *file)
+{
+ struct task_security_struct *tsec;
+ struct file_security_struct *fsec;
+
+ tsec = current->security;
+ fsec = file->f_security;
+ fsec->fown_sid = tsec->sid;
+
+ return 0;
+}
+
+static int selinux_file_send_sigiotask(struct task_struct *tsk,
+ struct fown_struct *fown, int signum)
+{
+ struct file *file;
+ u32 perm;
+ struct task_security_struct *tsec;
+ struct file_security_struct *fsec;
+
+ /* struct fown_struct is never outside the context of a struct file */
+ file = (struct file *)((long)fown - offsetof(struct file,f_owner));
+
+ tsec = tsk->security;
+ fsec = file->f_security;
+
+ if (!signum)
+ perm = signal_to_av(SIGIO); /* as per send_sigio_to_task */
+ else
+ perm = signal_to_av(signum);
+
+ return avc_has_perm(fsec->fown_sid, tsec->sid,
+ SECCLASS_PROCESS, perm, NULL);
+}
+
+static int selinux_file_receive(struct file *file)
+{
+ return file_has_perm(current, file, file_to_av(file));
+}
+
+/* task security operations */
+
+static int selinux_task_create(unsigned long clone_flags)
+{
+ int rc;
+
+ rc = secondary_ops->task_create(clone_flags);
+ if (rc)
+ return rc;
+
+ return task_has_perm(current, current, PROCESS__FORK);
+}
+
+static int selinux_task_alloc_security(struct task_struct *tsk)
+{
+ struct task_security_struct *tsec1, *tsec2;
+ int rc;
+
+ tsec1 = current->security;
+
+ rc = task_alloc_security(tsk);
+ if (rc)
+ return rc;
+ tsec2 = tsk->security;
+
+ tsec2->osid = tsec1->osid;
+ tsec2->sid = tsec1->sid;
+
+ /* Retain the exec and create SIDs across fork */
+ tsec2->exec_sid = tsec1->exec_sid;
+ tsec2->create_sid = tsec1->create_sid;
+
+ /* Retain ptracer SID across fork, if any.
+ This will be reset by the ptrace hook upon any
+ subsequent ptrace_attach operations. */
+ tsec2->ptrace_sid = tsec1->ptrace_sid;
+
+ return 0;
+}
+
+static void selinux_task_free_security(struct task_struct *tsk)
+{
+ task_free_security(tsk);
+}
+
+static int selinux_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
+{
+ /* Since setuid only affects the current process, and
+ since the SELinux controls are not based on the Linux
+ identity attributes, SELinux does not need to control
+ this operation. However, SELinux does control the use
+ of the CAP_SETUID and CAP_SETGID capabilities using the
+ capable hook. */
+ return 0;
+}
+
+static int selinux_task_post_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
+{
+ return secondary_ops->task_post_setuid(id0,id1,id2,flags);
+}
+
+static int selinux_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
+{
+ /* See the comment for setuid above. */
+ return 0;
+}
+
+static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)
+{
+ return task_has_perm(current, p, PROCESS__SETPGID);
+}
+
+static int selinux_task_getpgid(struct task_struct *p)
+{
+ return task_has_perm(current, p, PROCESS__GETPGID);
+}
+
+static int selinux_task_getsid(struct task_struct *p)
+{
+ return task_has_perm(current, p, PROCESS__GETSESSION);
+}
+
+static int selinux_task_setgroups(struct group_info *group_info)
+{
+ /* See the comment for setuid above. */
+ return 0;
+}
+
+static int selinux_task_setnice(struct task_struct *p, int nice)
+{
+ int rc;
+
+ rc = secondary_ops->task_setnice(p, nice);
+ if (rc)
+ return rc;
+
+ return task_has_perm(current,p, PROCESS__SETSCHED);
+}
+
+static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim)
+{
+ struct rlimit *old_rlim = current->signal->rlim + resource;
+ int rc;
+
+ rc = secondary_ops->task_setrlimit(resource, new_rlim);
+ if (rc)
+ return rc;
+
+ /* Control the ability to change the hard limit (whether
+ lowering or raising it), so that the hard limit can
+ later be used as a safe reset point for the soft limit
+ upon context transitions. See selinux_bprm_apply_creds. */
+ if (old_rlim->rlim_max != new_rlim->rlim_max)
+ return task_has_perm(current, current, PROCESS__SETRLIMIT);
+
+ return 0;
+}
+
+static int selinux_task_setscheduler(struct task_struct *p, int policy, struct sched_param *lp)
+{
+ return task_has_perm(current, p, PROCESS__SETSCHED);
+}
+
+static int selinux_task_getscheduler(struct task_struct *p)
+{
+ return task_has_perm(current, p, PROCESS__GETSCHED);
+}
+
+static int selinux_task_kill(struct task_struct *p, struct siginfo *info, int sig)
+{
+ u32 perm;
+ int rc;
+
+ rc = secondary_ops->task_kill(p, info, sig);
+ if (rc)
+ return rc;
+
+ if (info && ((unsigned long)info == 1 ||
+ (unsigned long)info == 2 || SI_FROMKERNEL(info)))
+ return 0;
+
+ if (!sig)
+ perm = PROCESS__SIGNULL; /* null signal; existence test */
+ else
+ perm = signal_to_av(sig);
+
+ return task_has_perm(current, p, perm);
+}
+
+static int selinux_task_prctl(int option,
+ unsigned long arg2,
+ unsigned long arg3,
+ unsigned long arg4,
+ unsigned long arg5)
+{
+ /* The current prctl operations do not appear to require
+ any SELinux controls since they merely observe or modify
+ the state of the current process. */
+ return 0;
+}
+
+static int selinux_task_wait(struct task_struct *p)
+{
+ u32 perm;
+
+ perm = signal_to_av(p->exit_signal);
+
+ return task_has_perm(p, current, perm);
+}
+
+static void selinux_task_reparent_to_init(struct task_struct *p)
+{
+ struct task_security_struct *tsec;
+
+ secondary_ops->task_reparent_to_init(p);
+
+ tsec = p->security;
+ tsec->osid = tsec->sid;
+ tsec->sid = SECINITSID_KERNEL;
+ return;
+}
+
+static void selinux_task_to_inode(struct task_struct *p,
+ struct inode *inode)
+{
+ struct task_security_struct *tsec = p->security;
+ struct inode_security_struct *isec = inode->i_security;
+
+ isec->sid = tsec->sid;
+ isec->initialized = 1;
+ return;
+}
+
+#ifdef CONFIG_SECURITY_NETWORK
+
+/* Returns error only if unable to parse addresses */
+static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad)
+{
+ int offset, ihlen, ret = -EINVAL;
+ struct iphdr _iph, *ih;
+
+ offset = skb->nh.raw - skb->data;
+ ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph);
+ if (ih == NULL)
+ goto out;
+
+ ihlen = ih->ihl * 4;
+ if (ihlen < sizeof(_iph))
+ goto out;
+
+ ad->u.net.v4info.saddr = ih->saddr;
+ ad->u.net.v4info.daddr = ih->daddr;
+ ret = 0;
+
+ switch (ih->protocol) {
+ case IPPROTO_TCP: {
+ struct tcphdr _tcph, *th;
+
+ if (ntohs(ih->frag_off) & IP_OFFSET)
+ break;
+
+ offset += ihlen;
+ th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph);
+ if (th == NULL)
+ break;
+
+ ad->u.net.sport = th->source;
+ ad->u.net.dport = th->dest;
+ break;
+ }
+
+ case IPPROTO_UDP: {
+ struct udphdr _udph, *uh;
+
+ if (ntohs(ih->frag_off) & IP_OFFSET)
+ break;
+
+ offset += ihlen;
+ uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph);
+ if (uh == NULL)
+ break;
+
+ ad->u.net.sport = uh->source;
+ ad->u.net.dport = uh->dest;
+ break;
+ }
+
+ default:
+ break;
+ }
+out:
+ return ret;
+}
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+
+/* Returns error only if unable to parse addresses */
+static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad)
+{
+ u8 nexthdr;
+ int ret = -EINVAL, offset;
+ struct ipv6hdr _ipv6h, *ip6;
+
+ offset = skb->nh.raw - skb->data;
+ ip6 = skb_header_pointer(skb, offset, sizeof(_ipv6h), &_ipv6h);
+ if (ip6 == NULL)
+ goto out;
+
+ ipv6_addr_copy(&ad->u.net.v6info.saddr, &ip6->saddr);
+ ipv6_addr_copy(&ad->u.net.v6info.daddr, &ip6->daddr);
+ ret = 0;
+
+ nexthdr = ip6->nexthdr;
+ offset += sizeof(_ipv6h);
+ offset = ipv6_skip_exthdr(skb, offset, &nexthdr,
+ skb->tail - skb->head - offset);
+ if (offset < 0)
+ goto out;
+
+ switch (nexthdr) {
+ case IPPROTO_TCP: {
+ struct tcphdr _tcph, *th;
+
+ th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph);
+ if (th == NULL)
+ break;
+
+ ad->u.net.sport = th->source;
+ ad->u.net.dport = th->dest;
+ break;
+ }
+
+ case IPPROTO_UDP: {
+ struct udphdr _udph, *uh;
+
+ uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph);
+ if (uh == NULL)
+ break;
+
+ ad->u.net.sport = uh->source;
+ ad->u.net.dport = uh->dest;
+ break;
+ }
+
+ /* includes fragments */
+ default:
+ break;
+ }
+out:
+ return ret;
+}
+
+#endif /* IPV6 */
+
+static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
+ char **addrp, int *len, int src)
+{
+ int ret = 0;
+
+ switch (ad->u.net.family) {
+ case PF_INET:
+ ret = selinux_parse_skb_ipv4(skb, ad);
+ if (ret || !addrp)
+ break;
+ *len = 4;
+ *addrp = (char *)(src ? &ad->u.net.v4info.saddr :
+ &ad->u.net.v4info.daddr);
+ break;
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case PF_INET6:
+ ret = selinux_parse_skb_ipv6(skb, ad);
+ if (ret || !addrp)
+ break;
+ *len = 16;
+ *addrp = (char *)(src ? &ad->u.net.v6info.saddr :
+ &ad->u.net.v6info.daddr);
+ break;
+#endif /* IPV6 */
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/* socket security operations */
+static int socket_has_perm(struct task_struct *task, struct socket *sock,
+ u32 perms)
+{
+ struct inode_security_struct *isec;
+ struct task_security_struct *tsec;
+ struct avc_audit_data ad;
+ int err = 0;
+
+ tsec = task->security;
+ isec = SOCK_INODE(sock)->i_security;
+
+ if (isec->sid == SECINITSID_KERNEL)
+ goto out;
+
+ AVC_AUDIT_DATA_INIT(&ad,NET);
+ ad.u.net.sk = sock->sk;
+ err = avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
+
+out:
+ return err;
+}
+
+static int selinux_socket_create(int family, int type,
+ int protocol, int kern)
+{
+ int err = 0;
+ struct task_security_struct *tsec;
+
+ if (kern)
+ goto out;
+
+ tsec = current->security;
+ err = avc_has_perm(tsec->sid, tsec->sid,
+ socket_type_to_security_class(family, type,
+ protocol), SOCKET__CREATE, NULL);
+
+out:
+ return err;
+}
+
+static void selinux_socket_post_create(struct socket *sock, int family,
+ int type, int protocol, int kern)
+{
+ struct inode_security_struct *isec;
+ struct task_security_struct *tsec;
+
+ isec = SOCK_INODE(sock)->i_security;
+
+ tsec = current->security;
+ isec->sclass = socket_type_to_security_class(family, type, protocol);
+ isec->sid = kern ? SECINITSID_KERNEL : tsec->sid;
+ isec->initialized = 1;
+
+ return;
+}
+
+/* Range of port numbers used to automatically bind.
+ Need to determine whether we should perform a name_bind
+ permission check between the socket and the port number. */
+#define ip_local_port_range_0 sysctl_local_port_range[0]
+#define ip_local_port_range_1 sysctl_local_port_range[1]
+
+static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
+{
+ u16 family;
+ int err;
+
+ err = socket_has_perm(current, sock, SOCKET__BIND);
+ if (err)
+ goto out;
+
+ /*
+ * If PF_INET or PF_INET6, check name_bind permission for the port.
+ */
+ family = sock->sk->sk_family;
+ if (family == PF_INET || family == PF_INET6) {
+ char *addrp;
+ struct inode_security_struct *isec;
+ struct task_security_struct *tsec;
+ struct avc_audit_data ad;
+ struct sockaddr_in *addr4 = NULL;
+ struct sockaddr_in6 *addr6 = NULL;
+ unsigned short snum;
+ struct sock *sk = sock->sk;
+ u32 sid, node_perm, addrlen;
+
+ tsec = current->security;
+ isec = SOCK_INODE(sock)->i_security;
+
+ if (family == PF_INET) {
+ addr4 = (struct sockaddr_in *)address;
+ snum = ntohs(addr4->sin_port);
+ addrlen = sizeof(addr4->sin_addr.s_addr);
+ addrp = (char *)&addr4->sin_addr.s_addr;
+ } else {
+ addr6 = (struct sockaddr_in6 *)address;
+ snum = ntohs(addr6->sin6_port);
+ addrlen = sizeof(addr6->sin6_addr.s6_addr);
+ addrp = (char *)&addr6->sin6_addr.s6_addr;
+ }
+
+ if (snum&&(snum < max(PROT_SOCK,ip_local_port_range_0) ||
+ snum > ip_local_port_range_1)) {
+ err = security_port_sid(sk->sk_family, sk->sk_type,
+ sk->sk_protocol, snum, &sid);
+ if (err)
+ goto out;
+ AVC_AUDIT_DATA_INIT(&ad,NET);
+ ad.u.net.sport = htons(snum);
+ ad.u.net.family = family;
+ err = avc_has_perm(isec->sid, sid,
+ isec->sclass,
+ SOCKET__NAME_BIND, &ad);
+ if (err)
+ goto out;
+ }
+
+ switch(sk->sk_protocol) {
+ case IPPROTO_TCP:
+ node_perm = TCP_SOCKET__NODE_BIND;
+ break;
+
+ case IPPROTO_UDP:
+ node_perm = UDP_SOCKET__NODE_BIND;
+ break;
+
+ default:
+ node_perm = RAWIP_SOCKET__NODE_BIND;
+ break;
+ }
+
+ err = security_node_sid(family, addrp, addrlen, &sid);
+ if (err)
+ goto out;
+
+ AVC_AUDIT_DATA_INIT(&ad,NET);
+ ad.u.net.sport = htons(snum);
+ ad.u.net.family = family;
+
+ if (family == PF_INET)
+ ad.u.net.v4info.saddr = addr4->sin_addr.s_addr;
+ else
+ ipv6_addr_copy(&ad.u.net.v6info.saddr, &addr6->sin6_addr);
+
+ err = avc_has_perm(isec->sid, sid,
+ isec->sclass, node_perm, &ad);
+ if (err)
+ goto out;
+ }
+out:
+ return err;
+}
+
+static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen)
+{
+ struct inode_security_struct *isec;
+ int err;
+
+ err = socket_has_perm(current, sock, SOCKET__CONNECT);
+ if (err)
+ return err;
+
+ /*
+ * If a TCP socket, check name_connect permission for the port.
+ */
+ isec = SOCK_INODE(sock)->i_security;
+ if (isec->sclass == SECCLASS_TCP_SOCKET) {
+ struct sock *sk = sock->sk;
+ struct avc_audit_data ad;
+ struct sockaddr_in *addr4 = NULL;
+ struct sockaddr_in6 *addr6 = NULL;
+ unsigned short snum;
+ u32 sid;
+
+ if (sk->sk_family == PF_INET) {
+ addr4 = (struct sockaddr_in *)address;
+ if (addrlen != sizeof(struct sockaddr_in))
+ return -EINVAL;
+ snum = ntohs(addr4->sin_port);
+ } else {
+ addr6 = (struct sockaddr_in6 *)address;
+ if (addrlen != sizeof(struct sockaddr_in6))
+ return -EINVAL;
+ snum = ntohs(addr6->sin6_port);
+ }
+
+ err = security_port_sid(sk->sk_family, sk->sk_type,
+ sk->sk_protocol, snum, &sid);
+ if (err)
+ goto out;
+
+ AVC_AUDIT_DATA_INIT(&ad,NET);
+ ad.u.net.dport = htons(snum);
+ ad.u.net.family = sk->sk_family;
+ err = avc_has_perm(isec->sid, sid, isec->sclass,
+ TCP_SOCKET__NAME_CONNECT, &ad);
+ if (err)
+ goto out;
+ }
+
+out:
+ return err;
+}
+
+static int selinux_socket_listen(struct socket *sock, int backlog)
+{
+ return socket_has_perm(current, sock, SOCKET__LISTEN);
+}
+
+static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
+{
+ int err;
+ struct inode_security_struct *isec;
+ struct inode_security_struct *newisec;
+
+ err = socket_has_perm(current, sock, SOCKET__ACCEPT);
+ if (err)
+ return err;
+
+ newisec = SOCK_INODE(newsock)->i_security;
+
+ isec = SOCK_INODE(sock)->i_security;
+ newisec->sclass = isec->sclass;
+ newisec->sid = isec->sid;
+ newisec->initialized = 1;
+
+ return 0;
+}
+
+static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg,
+ int size)
+{
+ return socket_has_perm(current, sock, SOCKET__WRITE);
+}
+
+static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg,
+ int size, int flags)
+{
+ return socket_has_perm(current, sock, SOCKET__READ);
+}
+
+static int selinux_socket_getsockname(struct socket *sock)
+{
+ return socket_has_perm(current, sock, SOCKET__GETATTR);
+}
+
+static int selinux_socket_getpeername(struct socket *sock)
+{
+ return socket_has_perm(current, sock, SOCKET__GETATTR);
+}
+
+static int selinux_socket_setsockopt(struct socket *sock,int level,int optname)
+{
+ return socket_has_perm(current, sock, SOCKET__SETOPT);
+}
+
+static int selinux_socket_getsockopt(struct socket *sock, int level,
+ int optname)
+{
+ return socket_has_perm(current, sock, SOCKET__GETOPT);
+}
+
+static int selinux_socket_shutdown(struct socket *sock, int how)
+{
+ return socket_has_perm(current, sock, SOCKET__SHUTDOWN);
+}
+
+static int selinux_socket_unix_stream_connect(struct socket *sock,
+ struct socket *other,
+ struct sock *newsk)
+{
+ struct sk_security_struct *ssec;
+ struct inode_security_struct *isec;
+ struct inode_security_struct *other_isec;
+ struct avc_audit_data ad;
+ int err;
+
+ err = secondary_ops->unix_stream_connect(sock, other, newsk);
+ if (err)
+ return err;
+
+ isec = SOCK_INODE(sock)->i_security;
+ other_isec = SOCK_INODE(other)->i_security;
+
+ AVC_AUDIT_DATA_INIT(&ad,NET);
+ ad.u.net.sk = other->sk;
+
+ err = avc_has_perm(isec->sid, other_isec->sid,
+ isec->sclass,
+ UNIX_STREAM_SOCKET__CONNECTTO, &ad);
+ if (err)
+ return err;
+
+ /* connecting socket */
+ ssec = sock->sk->sk_security;
+ ssec->peer_sid = other_isec->sid;
+
+ /* server child socket */
+ ssec = newsk->sk_security;
+ ssec->peer_sid = isec->sid;
+
+ return 0;
+}
+
+static int selinux_socket_unix_may_send(struct socket *sock,
+ struct socket *other)
+{
+ struct inode_security_struct *isec;
+ struct inode_security_struct *other_isec;
+ struct avc_audit_data ad;
+ int err;
+
+ isec = SOCK_INODE(sock)->i_security;
+ other_isec = SOCK_INODE(other)->i_security;
+
+ AVC_AUDIT_DATA_INIT(&ad,NET);
+ ad.u.net.sk = other->sk;
+
+ err = avc_has_perm(isec->sid, other_isec->sid,
+ isec->sclass, SOCKET__SENDTO, &ad);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+ u16 family;
+ char *addrp;
+ int len, err = 0;
+ u32 netif_perm, node_perm, node_sid, if_sid, recv_perm = 0;
+ u32 sock_sid = 0;
+ u16 sock_class = 0;
+ struct socket *sock;
+ struct net_device *dev;
+ struct avc_audit_data ad;
+
+ family = sk->sk_family;
+ if (family != PF_INET && family != PF_INET6)
+ goto out;
+
+ /* Handle mapped IPv4 packets arriving via IPv6 sockets */
+ if (family == PF_INET6 && skb->protocol == ntohs(ETH_P_IP))
+ family = PF_INET;
+
+ read_lock_bh(&sk->sk_callback_lock);
+ sock = sk->sk_socket;
+ if (sock) {
+ struct inode *inode;
+ inode = SOCK_INODE(sock);
+ if (inode) {
+ struct inode_security_struct *isec;
+ isec = inode->i_security;
+ sock_sid = isec->sid;
+ sock_class = isec->sclass;
+ }
+ }
+ read_unlock_bh(&sk->sk_callback_lock);
+ if (!sock_sid)
+ goto out;
+
+ dev = skb->dev;
+ if (!dev)
+ goto out;
+
+ err = sel_netif_sids(dev, &if_sid, NULL);
+ if (err)
+ goto out;
+
+ switch (sock_class) {
+ case SECCLASS_UDP_SOCKET:
+ netif_perm = NETIF__UDP_RECV;
+ node_perm = NODE__UDP_RECV;
+ recv_perm = UDP_SOCKET__RECV_MSG;
+ break;
+
+ case SECCLASS_TCP_SOCKET:
+ netif_perm = NETIF__TCP_RECV;
+ node_perm = NODE__TCP_RECV;
+ recv_perm = TCP_SOCKET__RECV_MSG;
+ break;
+
+ default:
+ netif_perm = NETIF__RAWIP_RECV;
+ node_perm = NODE__RAWIP_RECV;
+ break;
+ }
+
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.netif = dev->name;
+ ad.u.net.family = family;
+
+ err = selinux_parse_skb(skb, &ad, &addrp, &len, 1);
+ if (err)
+ goto out;
+
+ err = avc_has_perm(sock_sid, if_sid, SECCLASS_NETIF, netif_perm, &ad);
+ if (err)
+ goto out;
+
+ /* Fixme: this lookup is inefficient */
+ err = security_node_sid(family, addrp, len, &node_sid);
+ if (err)
+ goto out;
+
+ err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, &ad);
+ if (err)
+ goto out;
+
+ if (recv_perm) {
+ u32 port_sid;
+
+ /* Fixme: make this more efficient */
+ err = security_port_sid(sk->sk_family, sk->sk_type,
+ sk->sk_protocol, ntohs(ad.u.net.sport),
+ &port_sid);
+ if (err)
+ goto out;
+
+ err = avc_has_perm(sock_sid, port_sid,
+ sock_class, recv_perm, &ad);
+ }
+out:
+ return err;
+}
+
+static int selinux_socket_getpeersec(struct socket *sock, char __user *optval,
+ int __user *optlen, unsigned len)
+{
+ int err = 0;
+ char *scontext;
+ u32 scontext_len;
+ struct sk_security_struct *ssec;
+ struct inode_security_struct *isec;
+
+ isec = SOCK_INODE(sock)->i_security;
+ if (isec->sclass != SECCLASS_UNIX_STREAM_SOCKET) {
+ err = -ENOPROTOOPT;
+ goto out;
+ }
+
+ ssec = sock->sk->sk_security;
+
+ err = security_sid_to_context(ssec->peer_sid, &scontext, &scontext_len);
+ if (err)
+ goto out;
+
+ if (scontext_len > len) {
+ err = -ERANGE;
+ goto out_len;
+ }
+
+ if (copy_to_user(optval, scontext, scontext_len))
+ err = -EFAULT;
+
+out_len:
+ if (put_user(scontext_len, optlen))
+ err = -EFAULT;
+
+ kfree(scontext);
+out:
+ return err;
+}
+
+static int selinux_sk_alloc_security(struct sock *sk, int family, int priority)
+{
+ return sk_alloc_security(sk, family, priority);
+}
+
+static void selinux_sk_free_security(struct sock *sk)
+{
+ sk_free_security(sk);
+}
+
+static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
+{
+ int err = 0;
+ u32 perm;
+ struct nlmsghdr *nlh;
+ struct socket *sock = sk->sk_socket;
+ struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
+
+ if (skb->len < NLMSG_SPACE(0)) {
+ err = -EINVAL;
+ goto out;
+ }
+ nlh = (struct nlmsghdr *)skb->data;
+
+ err = selinux_nlmsg_lookup(isec->sclass, nlh->nlmsg_type, &perm);
+ if (err) {
+ if (err == -EINVAL) {
+ audit_log(current->audit_context,
+ "SELinux: unrecognized netlink message"
+ " type=%hu for sclass=%hu\n",
+ nlh->nlmsg_type, isec->sclass);
+ if (!selinux_enforcing)
+ err = 0;
+ }
+
+ /* Ignore */
+ if (err == -ENOENT)
+ err = 0;
+ goto out;
+ }
+
+ err = socket_has_perm(current, sock, perm);
+out:
+ return err;
+}
+
+#ifdef CONFIG_NETFILTER
+
+static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
+ struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *),
+ u16 family)
+{
+ char *addrp;
+ int len, err = NF_ACCEPT;
+ u32 netif_perm, node_perm, node_sid, if_sid, send_perm = 0;
+ struct sock *sk;
+ struct socket *sock;
+ struct inode *inode;
+ struct sk_buff *skb = *pskb;
+ struct inode_security_struct *isec;
+ struct avc_audit_data ad;
+ struct net_device *dev = (struct net_device *)out;
+
+ sk = skb->sk;
+ if (!sk)
+ goto out;
+
+ sock = sk->sk_socket;
+ if (!sock)
+ goto out;
+
+ inode = SOCK_INODE(sock);
+ if (!inode)
+ goto out;
+
+ err = sel_netif_sids(dev, &if_sid, NULL);
+ if (err)
+ goto out;
+
+ isec = inode->i_security;
+
+ switch (isec->sclass) {
+ case SECCLASS_UDP_SOCKET:
+ netif_perm = NETIF__UDP_SEND;
+ node_perm = NODE__UDP_SEND;
+ send_perm = UDP_SOCKET__SEND_MSG;
+ break;
+
+ case SECCLASS_TCP_SOCKET:
+ netif_perm = NETIF__TCP_SEND;
+ node_perm = NODE__TCP_SEND;
+ send_perm = TCP_SOCKET__SEND_MSG;
+ break;
+
+ default:
+ netif_perm = NETIF__RAWIP_SEND;
+ node_perm = NODE__RAWIP_SEND;
+ break;
+ }
+
+
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.netif = dev->name;
+ ad.u.net.family = family;
+
+ err = selinux_parse_skb(skb, &ad, &addrp,
+ &len, 0) ? NF_DROP : NF_ACCEPT;
+ if (err != NF_ACCEPT)
+ goto out;
+
+ err = avc_has_perm(isec->sid, if_sid, SECCLASS_NETIF,
+ netif_perm, &ad) ? NF_DROP : NF_ACCEPT;
+ if (err != NF_ACCEPT)
+ goto out;
+
+ /* Fixme: this lookup is inefficient */
+ err = security_node_sid(family, addrp, len,
+ &node_sid) ? NF_DROP : NF_ACCEPT;
+ if (err != NF_ACCEPT)
+ goto out;
+
+ err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE,
+ node_perm, &ad) ? NF_DROP : NF_ACCEPT;
+ if (err != NF_ACCEPT)
+ goto out;
+
+ if (send_perm) {
+ u32 port_sid;
+
+ /* Fixme: make this more efficient */
+ err = security_port_sid(sk->sk_family,
+ sk->sk_type,
+ sk->sk_protocol,
+ ntohs(ad.u.net.dport),
+ &port_sid) ? NF_DROP : NF_ACCEPT;
+ if (err != NF_ACCEPT)
+ goto out;
+
+ err = avc_has_perm(isec->sid, port_sid, isec->sclass,
+ send_perm, &ad) ? NF_DROP : NF_ACCEPT;
+ }
+
+out:
+ return err;
+}
+
+static unsigned int selinux_ipv4_postroute_last(unsigned int hooknum,
+ struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ return selinux_ip_postroute_last(hooknum, pskb, in, out, okfn, PF_INET);
+}
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+
+static unsigned int selinux_ipv6_postroute_last(unsigned int hooknum,
+ struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ return selinux_ip_postroute_last(hooknum, pskb, in, out, okfn, PF_INET6);
+}
+
+#endif /* IPV6 */
+
+#endif /* CONFIG_NETFILTER */
+
+#else
+
+static inline int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
+{
+ return 0;
+}
+
+#endif /* CONFIG_SECURITY_NETWORK */
+
+static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
+{
+ struct task_security_struct *tsec;
+ struct av_decision avd;
+ int err;
+
+ err = secondary_ops->netlink_send(sk, skb);
+ if (err)
+ return err;
+
+ tsec = current->security;
+
+ avd.allowed = 0;
+ avc_has_perm_noaudit(tsec->sid, tsec->sid,
+ SECCLASS_CAPABILITY, ~0, &avd);
+ cap_mask(NETLINK_CB(skb).eff_cap, avd.allowed);
+
+ if (policydb_loaded_version >= POLICYDB_VERSION_NLCLASS)
+ err = selinux_nlmsg_perm(sk, skb);
+
+ return err;
+}
+
+static int selinux_netlink_recv(struct sk_buff *skb)
+{
+ if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN))
+ return -EPERM;
+ return 0;
+}
+
+static int ipc_alloc_security(struct task_struct *task,
+ struct kern_ipc_perm *perm,
+ u16 sclass)
+{
+ struct task_security_struct *tsec = task->security;
+ struct ipc_security_struct *isec;
+
+ isec = kmalloc(sizeof(struct ipc_security_struct), GFP_KERNEL);
+ if (!isec)
+ return -ENOMEM;
+
+ memset(isec, 0, sizeof(struct ipc_security_struct));
+ isec->magic = SELINUX_MAGIC;
+ isec->sclass = sclass;
+ isec->ipc_perm = perm;
+ if (tsec) {
+ isec->sid = tsec->sid;
+ } else {
+ isec->sid = SECINITSID_UNLABELED;
+ }
+ perm->security = isec;
+
+ return 0;
+}
+
+static void ipc_free_security(struct kern_ipc_perm *perm)
+{
+ struct ipc_security_struct *isec = perm->security;
+ if (!isec || isec->magic != SELINUX_MAGIC)
+ return;
+
+ perm->security = NULL;
+ kfree(isec);
+}
+
+static int msg_msg_alloc_security(struct msg_msg *msg)
+{
+ struct msg_security_struct *msec;
+
+ msec = kmalloc(sizeof(struct msg_security_struct), GFP_KERNEL);
+ if (!msec)
+ return -ENOMEM;
+
+ memset(msec, 0, sizeof(struct msg_security_struct));
+ msec->magic = SELINUX_MAGIC;
+ msec->msg = msg;
+ msec->sid = SECINITSID_UNLABELED;
+ msg->security = msec;
+
+ return 0;
+}
+
+static void msg_msg_free_security(struct msg_msg *msg)
+{
+ struct msg_security_struct *msec = msg->security;
+ if (!msec || msec->magic != SELINUX_MAGIC)
+ return;
+
+ msg->security = NULL;
+ kfree(msec);
+}
+
+static int ipc_has_perm(struct kern_ipc_perm *ipc_perms,
+ u16 sclass, u32 perms)
+{
+ struct task_security_struct *tsec;
+ struct ipc_security_struct *isec;
+ struct avc_audit_data ad;
+
+ tsec = current->security;
+ isec = ipc_perms->security;
+
+ AVC_AUDIT_DATA_INIT(&ad, IPC);
+ ad.u.ipc_id = ipc_perms->key;
+
+ return avc_has_perm(tsec->sid, isec->sid, sclass, perms, &ad);
+}
+
+static int selinux_msg_msg_alloc_security(struct msg_msg *msg)
+{
+ return msg_msg_alloc_security(msg);
+}
+
+static void selinux_msg_msg_free_security(struct msg_msg *msg)
+{
+ msg_msg_free_security(msg);
+}
+
+/* message queue security operations */
+static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
+{
+ struct task_security_struct *tsec;
+ struct ipc_security_struct *isec;
+ struct avc_audit_data ad;
+ int rc;
+
+ rc = ipc_alloc_security(current, &msq->q_perm, SECCLASS_MSGQ);
+ if (rc)
+ return rc;
+
+ tsec = current->security;
+ isec = msq->q_perm.security;
+
+ AVC_AUDIT_DATA_INIT(&ad, IPC);
+ ad.u.ipc_id = msq->q_perm.key;
+
+ rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
+ MSGQ__CREATE, &ad);
+ if (rc) {
+ ipc_free_security(&msq->q_perm);
+ return rc;
+ }
+ return 0;
+}
+
+static void selinux_msg_queue_free_security(struct msg_queue *msq)
+{
+ ipc_free_security(&msq->q_perm);
+}
+
+static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg)
+{
+ struct task_security_struct *tsec;
+ struct ipc_security_struct *isec;
+ struct avc_audit_data ad;
+
+ tsec = current->security;
+ isec = msq->q_perm.security;
+
+ AVC_AUDIT_DATA_INIT(&ad, IPC);
+ ad.u.ipc_id = msq->q_perm.key;
+
+ return avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
+ MSGQ__ASSOCIATE, &ad);
+}
+
+static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd)
+{
+ int err;
+ int perms;
+
+ switch(cmd) {
+ case IPC_INFO:
+ case MSG_INFO:
+ /* No specific object, just general system-wide information. */
+ return task_has_system(current, SYSTEM__IPC_INFO);
+ case IPC_STAT:
+ case MSG_STAT:
+ perms = MSGQ__GETATTR | MSGQ__ASSOCIATE;
+ break;
+ case IPC_SET:
+ perms = MSGQ__SETATTR;
+ break;
+ case IPC_RMID:
+ perms = MSGQ__DESTROY;
+ break;
+ default:
+ return 0;
+ }
+
+ err = ipc_has_perm(&msq->q_perm, SECCLASS_MSGQ, perms);
+ return err;
+}
+
+static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, int msqflg)
+{
+ struct task_security_struct *tsec;
+ struct ipc_security_struct *isec;
+ struct msg_security_struct *msec;
+ struct avc_audit_data ad;
+ int rc;
+
+ tsec = current->security;
+ isec = msq->q_perm.security;
+ msec = msg->security;
+
+ /*
+ * First time through, need to assign label to the message
+ */
+ if (msec->sid == SECINITSID_UNLABELED) {
+ /*
+ * Compute new sid based on current process and
+ * message queue this message will be stored in
+ */
+ rc = security_transition_sid(tsec->sid,
+ isec->sid,
+ SECCLASS_MSG,
+ &msec->sid);
+ if (rc)
+ return rc;
+ }
+
+ AVC_AUDIT_DATA_INIT(&ad, IPC);
+ ad.u.ipc_id = msq->q_perm.key;
+
+ /* Can this process write to the queue? */
+ rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
+ MSGQ__WRITE, &ad);
+ if (!rc)
+ /* Can this process send the message */
+ rc = avc_has_perm(tsec->sid, msec->sid,
+ SECCLASS_MSG, MSG__SEND, &ad);
+ if (!rc)
+ /* Can the message be put in the queue? */
+ rc = avc_has_perm(msec->sid, isec->sid,
+ SECCLASS_MSGQ, MSGQ__ENQUEUE, &ad);
+
+ return rc;
+}
+
+static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
+ struct task_struct *target,
+ long type, int mode)
+{
+ struct task_security_struct *tsec;
+ struct ipc_security_struct *isec;
+ struct msg_security_struct *msec;
+ struct avc_audit_data ad;
+ int rc;
+
+ tsec = target->security;
+ isec = msq->q_perm.security;
+ msec = msg->security;
+
+ AVC_AUDIT_DATA_INIT(&ad, IPC);
+ ad.u.ipc_id = msq->q_perm.key;
+
+ rc = avc_has_perm(tsec->sid, isec->sid,
+ SECCLASS_MSGQ, MSGQ__READ, &ad);
+ if (!rc)
+ rc = avc_has_perm(tsec->sid, msec->sid,
+ SECCLASS_MSG, MSG__RECEIVE, &ad);
+ return rc;
+}
+
+/* Shared Memory security operations */
+static int selinux_shm_alloc_security(struct shmid_kernel *shp)
+{
+ struct task_security_struct *tsec;
+ struct ipc_security_struct *isec;
+ struct avc_audit_data ad;
+ int rc;
+
+ rc = ipc_alloc_security(current, &shp->shm_perm, SECCLASS_SHM);
+ if (rc)
+ return rc;
+
+ tsec = current->security;
+ isec = shp->shm_perm.security;
+
+ AVC_AUDIT_DATA_INIT(&ad, IPC);
+ ad.u.ipc_id = shp->shm_perm.key;
+
+ rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
+ SHM__CREATE, &ad);
+ if (rc) {
+ ipc_free_security(&shp->shm_perm);
+ return rc;
+ }
+ return 0;
+}
+
+static void selinux_shm_free_security(struct shmid_kernel *shp)
+{
+ ipc_free_security(&shp->shm_perm);
+}
+
+static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg)
+{
+ struct task_security_struct *tsec;
+ struct ipc_security_struct *isec;
+ struct avc_audit_data ad;
+
+ tsec = current->security;
+ isec = shp->shm_perm.security;
+
+ AVC_AUDIT_DATA_INIT(&ad, IPC);
+ ad.u.ipc_id = shp->shm_perm.key;
+
+ return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
+ SHM__ASSOCIATE, &ad);
+}
+
+/* Note, at this point, shp is locked down */
+static int selinux_shm_shmctl(struct shmid_kernel *shp, int cmd)
+{
+ int perms;
+ int err;
+
+ switch(cmd) {
+ case IPC_INFO:
+ case SHM_INFO:
+ /* No specific object, just general system-wide information. */
+ return task_has_system(current, SYSTEM__IPC_INFO);
+ case IPC_STAT:
+ case SHM_STAT:
+ perms = SHM__GETATTR | SHM__ASSOCIATE;
+ break;
+ case IPC_SET:
+ perms = SHM__SETATTR;
+ break;
+ case SHM_LOCK:
+ case SHM_UNLOCK:
+ perms = SHM__LOCK;
+ break;
+ case IPC_RMID:
+ perms = SHM__DESTROY;
+ break;
+ default:
+ return 0;
+ }
+
+ err = ipc_has_perm(&shp->shm_perm, SECCLASS_SHM, perms);
+ return err;
+}
+
+static int selinux_shm_shmat(struct shmid_kernel *shp,
+ char __user *shmaddr, int shmflg)
+{
+ u32 perms;
+ int rc;
+
+ rc = secondary_ops->shm_shmat(shp, shmaddr, shmflg);
+ if (rc)
+ return rc;
+
+ if (shmflg & SHM_RDONLY)
+ perms = SHM__READ;
+ else
+ perms = SHM__READ | SHM__WRITE;
+
+ return ipc_has_perm(&shp->shm_perm, SECCLASS_SHM, perms);
+}
+
+/* Semaphore security operations */
+static int selinux_sem_alloc_security(struct sem_array *sma)
+{
+ struct task_security_struct *tsec;
+ struct ipc_security_struct *isec;
+ struct avc_audit_data ad;
+ int rc;
+
+ rc = ipc_alloc_security(current, &sma->sem_perm, SECCLASS_SEM);
+ if (rc)
+ return rc;
+
+ tsec = current->security;
+ isec = sma->sem_perm.security;
+
+ AVC_AUDIT_DATA_INIT(&ad, IPC);
+ ad.u.ipc_id = sma->sem_perm.key;
+
+ rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
+ SEM__CREATE, &ad);
+ if (rc) {
+ ipc_free_security(&sma->sem_perm);
+ return rc;
+ }
+ return 0;
+}
+
+static void selinux_sem_free_security(struct sem_array *sma)
+{
+ ipc_free_security(&sma->sem_perm);
+}
+
+static int selinux_sem_associate(struct sem_array *sma, int semflg)
+{
+ struct task_security_struct *tsec;
+ struct ipc_security_struct *isec;
+ struct avc_audit_data ad;
+
+ tsec = current->security;
+ isec = sma->sem_perm.security;
+
+ AVC_AUDIT_DATA_INIT(&ad, IPC);
+ ad.u.ipc_id = sma->sem_perm.key;
+
+ return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
+ SEM__ASSOCIATE, &ad);
+}
+
+/* Note, at this point, sma is locked down */
+static int selinux_sem_semctl(struct sem_array *sma, int cmd)
+{
+ int err;
+ u32 perms;
+
+ switch(cmd) {
+ case IPC_INFO:
+ case SEM_INFO:
+ /* No specific object, just general system-wide information. */
+ return task_has_system(current, SYSTEM__IPC_INFO);
+ case GETPID:
+ case GETNCNT:
+ case GETZCNT:
+ perms = SEM__GETATTR;
+ break;
+ case GETVAL:
+ case GETALL:
+ perms = SEM__READ;
+ break;
+ case SETVAL:
+ case SETALL:
+ perms = SEM__WRITE;
+ break;
+ case IPC_RMID:
+ perms = SEM__DESTROY;
+ break;
+ case IPC_SET:
+ perms = SEM__SETATTR;
+ break;
+ case IPC_STAT:
+ case SEM_STAT:
+ perms = SEM__GETATTR | SEM__ASSOCIATE;
+ break;
+ default:
+ return 0;
+ }
+
+ err = ipc_has_perm(&sma->sem_perm, SECCLASS_SEM, perms);
+ return err;
+}
+
+static int selinux_sem_semop(struct sem_array *sma,
+ struct sembuf *sops, unsigned nsops, int alter)
+{
+ u32 perms;
+
+ if (alter)
+ perms = SEM__READ | SEM__WRITE;
+ else
+ perms = SEM__READ;
+
+ return ipc_has_perm(&sma->sem_perm, SECCLASS_SEM, perms);
+}
+
+static int selinux_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
+{
+ struct ipc_security_struct *isec = ipcp->security;
+ u16 sclass = SECCLASS_IPC;
+ u32 av = 0;
+
+ if (isec && isec->magic == SELINUX_MAGIC)
+ sclass = isec->sclass;
+
+ av = 0;
+ if (flag & S_IRUGO)
+ av |= IPC__UNIX_READ;
+ if (flag & S_IWUGO)
+ av |= IPC__UNIX_WRITE;
+
+ if (av == 0)
+ return 0;
+
+ return ipc_has_perm(ipcp, sclass, av);
+}
+
+/* module stacking operations */
+static int selinux_register_security (const char *name, struct security_operations *ops)
+{
+ if (secondary_ops != original_ops) {
+ printk(KERN_INFO "%s: There is already a secondary security "
+ "module registered.\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ secondary_ops = ops;
+
+ printk(KERN_INFO "%s: Registering secondary module %s\n",
+ __FUNCTION__,
+ name);
+
+ return 0;
+}
+
+static int selinux_unregister_security (const char *name, struct security_operations *ops)
+{
+ if (ops != secondary_ops) {
+ printk (KERN_INFO "%s: trying to unregister a security module "
+ "that is not registered.\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ secondary_ops = original_ops;
+
+ return 0;
+}
+
+static void selinux_d_instantiate (struct dentry *dentry, struct inode *inode)
+{
+ if (inode)
+ inode_doinit_with_dentry(inode, dentry);
+}
+
+static int selinux_getprocattr(struct task_struct *p,
+ char *name, void *value, size_t size)
+{
+ struct task_security_struct *tsec;
+ u32 sid, len;
+ char *context;
+ int error;
+
+ if (current != p) {
+ error = task_has_perm(current, p, PROCESS__GETATTR);
+ if (error)
+ return error;
+ }
+
+ if (!size)
+ return -ERANGE;
+
+ tsec = p->security;
+
+ if (!strcmp(name, "current"))
+ sid = tsec->sid;
+ else if (!strcmp(name, "prev"))
+ sid = tsec->osid;
+ else if (!strcmp(name, "exec"))
+ sid = tsec->exec_sid;
+ else if (!strcmp(name, "fscreate"))
+ sid = tsec->create_sid;
+ else
+ return -EINVAL;
+
+ if (!sid)
+ return 0;
+
+ error = security_sid_to_context(sid, &context, &len);
+ if (error)
+ return error;
+ if (len > size) {
+ kfree(context);
+ return -ERANGE;
+ }
+ memcpy(value, context, len);
+ kfree(context);
+ return len;
+}
+
+static int selinux_setprocattr(struct task_struct *p,
+ char *name, void *value, size_t size)
+{
+ struct task_security_struct *tsec;
+ u32 sid = 0;
+ int error;
+ char *str = value;
+
+ if (current != p) {
+ /* SELinux only allows a process to change its own
+ security attributes. */
+ return -EACCES;
+ }
+
+ /*
+ * Basic control over ability to set these attributes at all.
+ * current == p, but we'll pass them separately in case the
+ * above restriction is ever removed.
+ */
+ if (!strcmp(name, "exec"))
+ error = task_has_perm(current, p, PROCESS__SETEXEC);
+ else if (!strcmp(name, "fscreate"))
+ error = task_has_perm(current, p, PROCESS__SETFSCREATE);
+ else if (!strcmp(name, "current"))
+ error = task_has_perm(current, p, PROCESS__SETCURRENT);
+ else
+ error = -EINVAL;
+ if (error)
+ return error;
+
+ /* Obtain a SID for the context, if one was specified. */
+ if (size && str[1] && str[1] != '\n') {
+ if (str[size-1] == '\n') {
+ str[size-1] = 0;
+ size--;
+ }
+ error = security_context_to_sid(value, size, &sid);
+ if (error)
+ return error;
+ }
+
+ /* Permission checking based on the specified context is
+ performed during the actual operation (execve,
+ open/mkdir/...), when we know the full context of the
+ operation. See selinux_bprm_set_security for the execve
+ checks and may_create for the file creation checks. The
+ operation will then fail if the context is not permitted. */
+ tsec = p->security;
+ if (!strcmp(name, "exec"))
+ tsec->exec_sid = sid;
+ else if (!strcmp(name, "fscreate"))
+ tsec->create_sid = sid;
+ else if (!strcmp(name, "current")) {
+ struct av_decision avd;
+
+ if (sid == 0)
+ return -EINVAL;
+
+ /* Only allow single threaded processes to change context */
+ if (atomic_read(&p->mm->mm_users) != 1) {
+ struct task_struct *g, *t;
+ struct mm_struct *mm = p->mm;
+ read_lock(&tasklist_lock);
+ do_each_thread(g, t)
+ if (t->mm == mm && t != p) {
+ read_unlock(&tasklist_lock);
+ return -EPERM;
+ }
+ while_each_thread(g, t);
+ read_unlock(&tasklist_lock);
+ }
+
+ /* Check permissions for the transition. */
+ error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
+ PROCESS__DYNTRANSITION, NULL);
+ if (error)
+ return error;
+
+ /* Check for ptracing, and update the task SID if ok.
+ Otherwise, leave SID unchanged and fail. */
+ task_lock(p);
+ if (p->ptrace & PT_PTRACED) {
+ error = avc_has_perm_noaudit(tsec->ptrace_sid, sid,
+ SECCLASS_PROCESS,
+ PROCESS__PTRACE, &avd);
+ if (!error)
+ tsec->sid = sid;
+ task_unlock(p);
+ avc_audit(tsec->ptrace_sid, sid, SECCLASS_PROCESS,
+ PROCESS__PTRACE, &avd, error, NULL);
+ if (error)
+ return error;
+ } else {
+ tsec->sid = sid;
+ task_unlock(p);
+ }
+ }
+ else
+ return -EINVAL;
+
+ return size;
+}
+
+static struct security_operations selinux_ops = {
+ .ptrace = selinux_ptrace,
+ .capget = selinux_capget,
+ .capset_check = selinux_capset_check,
+ .capset_set = selinux_capset_set,
+ .sysctl = selinux_sysctl,
+ .capable = selinux_capable,
+ .quotactl = selinux_quotactl,
+ .quota_on = selinux_quota_on,
+ .syslog = selinux_syslog,
+ .vm_enough_memory = selinux_vm_enough_memory,
+
+ .netlink_send = selinux_netlink_send,
+ .netlink_recv = selinux_netlink_recv,
+
+ .bprm_alloc_security = selinux_bprm_alloc_security,
+ .bprm_free_security = selinux_bprm_free_security,
+ .bprm_apply_creds = selinux_bprm_apply_creds,
+ .bprm_post_apply_creds = selinux_bprm_post_apply_creds,
+ .bprm_set_security = selinux_bprm_set_security,
+ .bprm_check_security = selinux_bprm_check_security,
+ .bprm_secureexec = selinux_bprm_secureexec,
+
+ .sb_alloc_security = selinux_sb_alloc_security,
+ .sb_free_security = selinux_sb_free_security,
+ .sb_copy_data = selinux_sb_copy_data,
+ .sb_kern_mount = selinux_sb_kern_mount,
+ .sb_statfs = selinux_sb_statfs,
+ .sb_mount = selinux_mount,
+ .sb_umount = selinux_umount,
+
+ .inode_alloc_security = selinux_inode_alloc_security,
+ .inode_free_security = selinux_inode_free_security,
+ .inode_create = selinux_inode_create,
+ .inode_post_create = selinux_inode_post_create,
+ .inode_link = selinux_inode_link,
+ .inode_post_link = selinux_inode_post_link,
+ .inode_unlink = selinux_inode_unlink,
+ .inode_symlink = selinux_inode_symlink,
+ .inode_post_symlink = selinux_inode_post_symlink,
+ .inode_mkdir = selinux_inode_mkdir,
+ .inode_post_mkdir = selinux_inode_post_mkdir,
+ .inode_rmdir = selinux_inode_rmdir,
+ .inode_mknod = selinux_inode_mknod,
+ .inode_post_mknod = selinux_inode_post_mknod,
+ .inode_rename = selinux_inode_rename,
+ .inode_post_rename = selinux_inode_post_rename,
+ .inode_readlink = selinux_inode_readlink,
+ .inode_follow_link = selinux_inode_follow_link,
+ .inode_permission = selinux_inode_permission,
+ .inode_setattr = selinux_inode_setattr,
+ .inode_getattr = selinux_inode_getattr,
+ .inode_setxattr = selinux_inode_setxattr,
+ .inode_post_setxattr = selinux_inode_post_setxattr,
+ .inode_getxattr = selinux_inode_getxattr,
+ .inode_listxattr = selinux_inode_listxattr,
+ .inode_removexattr = selinux_inode_removexattr,
+ .inode_getsecurity = selinux_inode_getsecurity,
+ .inode_setsecurity = selinux_inode_setsecurity,
+ .inode_listsecurity = selinux_inode_listsecurity,
+
+ .file_permission = selinux_file_permission,
+ .file_alloc_security = selinux_file_alloc_security,
+ .file_free_security = selinux_file_free_security,
+ .file_ioctl = selinux_file_ioctl,
+ .file_mmap = selinux_file_mmap,
+ .file_mprotect = selinux_file_mprotect,
+ .file_lock = selinux_file_lock,
+ .file_fcntl = selinux_file_fcntl,
+ .file_set_fowner = selinux_file_set_fowner,
+ .file_send_sigiotask = selinux_file_send_sigiotask,
+ .file_receive = selinux_file_receive,
+
+ .task_create = selinux_task_create,
+ .task_alloc_security = selinux_task_alloc_security,
+ .task_free_security = selinux_task_free_security,
+ .task_setuid = selinux_task_setuid,
+ .task_post_setuid = selinux_task_post_setuid,
+ .task_setgid = selinux_task_setgid,
+ .task_setpgid = selinux_task_setpgid,
+ .task_getpgid = selinux_task_getpgid,
+ .task_getsid = selinux_task_getsid,
+ .task_setgroups = selinux_task_setgroups,
+ .task_setnice = selinux_task_setnice,
+ .task_setrlimit = selinux_task_setrlimit,
+ .task_setscheduler = selinux_task_setscheduler,
+ .task_getscheduler = selinux_task_getscheduler,
+ .task_kill = selinux_task_kill,
+ .task_wait = selinux_task_wait,
+ .task_prctl = selinux_task_prctl,
+ .task_reparent_to_init = selinux_task_reparent_to_init,
+ .task_to_inode = selinux_task_to_inode,
+
+ .ipc_permission = selinux_ipc_permission,
+
+ .msg_msg_alloc_security = selinux_msg_msg_alloc_security,
+ .msg_msg_free_security = selinux_msg_msg_free_security,
+
+ .msg_queue_alloc_security = selinux_msg_queue_alloc_security,
+ .msg_queue_free_security = selinux_msg_queue_free_security,
+ .msg_queue_associate = selinux_msg_queue_associate,
+ .msg_queue_msgctl = selinux_msg_queue_msgctl,
+ .msg_queue_msgsnd = selinux_msg_queue_msgsnd,
+ .msg_queue_msgrcv = selinux_msg_queue_msgrcv,
+
+ .shm_alloc_security = selinux_shm_alloc_security,
+ .shm_free_security = selinux_shm_free_security,
+ .shm_associate = selinux_shm_associate,
+ .shm_shmctl = selinux_shm_shmctl,
+ .shm_shmat = selinux_shm_shmat,
+
+ .sem_alloc_security = selinux_sem_alloc_security,
+ .sem_free_security = selinux_sem_free_security,
+ .sem_associate = selinux_sem_associate,
+ .sem_semctl = selinux_sem_semctl,
+ .sem_semop = selinux_sem_semop,
+
+ .register_security = selinux_register_security,
+ .unregister_security = selinux_unregister_security,
+
+ .d_instantiate = selinux_d_instantiate,
+
+ .getprocattr = selinux_getprocattr,
+ .setprocattr = selinux_setprocattr,
+
+#ifdef CONFIG_SECURITY_NETWORK
+ .unix_stream_connect = selinux_socket_unix_stream_connect,
+ .unix_may_send = selinux_socket_unix_may_send,
+
+ .socket_create = selinux_socket_create,
+ .socket_post_create = selinux_socket_post_create,
+ .socket_bind = selinux_socket_bind,
+ .socket_connect = selinux_socket_connect,
+ .socket_listen = selinux_socket_listen,
+ .socket_accept = selinux_socket_accept,
+ .socket_sendmsg = selinux_socket_sendmsg,
+ .socket_recvmsg = selinux_socket_recvmsg,
+ .socket_getsockname = selinux_socket_getsockname,
+ .socket_getpeername = selinux_socket_getpeername,
+ .socket_getsockopt = selinux_socket_getsockopt,
+ .socket_setsockopt = selinux_socket_setsockopt,
+ .socket_shutdown = selinux_socket_shutdown,
+ .socket_sock_rcv_skb = selinux_socket_sock_rcv_skb,
+ .socket_getpeersec = selinux_socket_getpeersec,
+ .sk_alloc_security = selinux_sk_alloc_security,
+ .sk_free_security = selinux_sk_free_security,
+#endif
+};
+
+static __init int selinux_init(void)
+{
+ struct task_security_struct *tsec;
+
+ if (!selinux_enabled) {
+ printk(KERN_INFO "SELinux: Disabled at boot.\n");
+ return 0;
+ }
+
+ printk(KERN_INFO "SELinux: Initializing.\n");
+
+ /* Set the security state for the initial task. */
+ if (task_alloc_security(current))
+ panic("SELinux: Failed to initialize initial task.\n");
+ tsec = current->security;
+ tsec->osid = tsec->sid = SECINITSID_KERNEL;
+
+ avc_init();
+
+ original_ops = secondary_ops = security_ops;
+ if (!secondary_ops)
+ panic ("SELinux: No initial security operations\n");
+ if (register_security (&selinux_ops))
+ panic("SELinux: Unable to register with kernel.\n");
+
+ if (selinux_enforcing) {
+ printk(KERN_INFO "SELinux: Starting in enforcing mode\n");
+ } else {
+ printk(KERN_INFO "SELinux: Starting in permissive mode\n");
+ }
+ return 0;
+}
+
+void selinux_complete_init(void)
+{
+ printk(KERN_INFO "SELinux: Completing initialization.\n");
+
+ /* Set up any superblocks initialized prior to the policy load. */
+ printk(KERN_INFO "SELinux: Setting up existing superblocks.\n");
+ spin_lock(&sb_security_lock);
+next_sb:
+ if (!list_empty(&superblock_security_head)) {
+ struct superblock_security_struct *sbsec =
+ list_entry(superblock_security_head.next,
+ struct superblock_security_struct,
+ list);
+ struct super_block *sb = sbsec->sb;
+ spin_lock(&sb_lock);
+ sb->s_count++;
+ spin_unlock(&sb_lock);
+ spin_unlock(&sb_security_lock);
+ down_read(&sb->s_umount);
+ if (sb->s_root)
+ superblock_doinit(sb, NULL);
+ drop_super(sb);
+ spin_lock(&sb_security_lock);
+ list_del_init(&sbsec->list);
+ goto next_sb;
+ }
+ spin_unlock(&sb_security_lock);
+}
+
+/* SELinux requires early initialization in order to label
+ all processes and objects when they are created. */
+security_initcall(selinux_init);
+
+#if defined(CONFIG_SECURITY_NETWORK) && defined(CONFIG_NETFILTER)
+
+static struct nf_hook_ops selinux_ipv4_op = {
+ .hook = selinux_ipv4_postroute_last,
+ .owner = THIS_MODULE,
+ .pf = PF_INET,
+ .hooknum = NF_IP_POST_ROUTING,
+ .priority = NF_IP_PRI_SELINUX_LAST,
+};
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+
+static struct nf_hook_ops selinux_ipv6_op = {
+ .hook = selinux_ipv6_postroute_last,
+ .owner = THIS_MODULE,
+ .pf = PF_INET6,
+ .hooknum = NF_IP6_POST_ROUTING,
+ .priority = NF_IP6_PRI_SELINUX_LAST,
+};
+
+#endif /* IPV6 */
+
+static int __init selinux_nf_ip_init(void)
+{
+ int err = 0;
+
+ if (!selinux_enabled)
+ goto out;
+
+ printk(KERN_INFO "SELinux: Registering netfilter hooks\n");
+
+ err = nf_register_hook(&selinux_ipv4_op);
+ if (err)
+ panic("SELinux: nf_register_hook for IPv4: error %d\n", err);
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+
+ err = nf_register_hook(&selinux_ipv6_op);
+ if (err)
+ panic("SELinux: nf_register_hook for IPv6: error %d\n", err);
+
+#endif /* IPV6 */
+out:
+ return err;
+}
+
+__initcall(selinux_nf_ip_init);
+
+#ifdef CONFIG_SECURITY_SELINUX_DISABLE
+static void selinux_nf_ip_exit(void)
+{
+ printk(KERN_INFO "SELinux: Unregistering netfilter hooks\n");
+
+ nf_unregister_hook(&selinux_ipv4_op);
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ nf_unregister_hook(&selinux_ipv6_op);
+#endif /* IPV6 */
+}
+#endif
+
+#else /* CONFIG_SECURITY_NETWORK && CONFIG_NETFILTER */
+
+#ifdef CONFIG_SECURITY_SELINUX_DISABLE
+#define selinux_nf_ip_exit()
+#endif
+
+#endif /* CONFIG_SECURITY_NETWORK && CONFIG_NETFILTER */
+
+#ifdef CONFIG_SECURITY_SELINUX_DISABLE
+int selinux_disable(void)
+{
+ extern void exit_sel_fs(void);
+ static int selinux_disabled = 0;
+
+ if (ss_initialized) {
+ /* Not permitted after initial policy load. */
+ return -EINVAL;
+ }
+
+ if (selinux_disabled) {
+ /* Only do this once. */
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "SELinux: Disabled at runtime.\n");
+
+ selinux_disabled = 1;
+
+ /* Reset security_ops to the secondary module, dummy or capability. */
+ security_ops = secondary_ops;
+
+ /* Unregister netfilter hooks. */
+ selinux_nf_ip_exit();
+
+ /* Unregister selinuxfs. */
+ exit_sel_fs();
+
+ return 0;
+}
+#endif
+
+
diff --git a/security/selinux/include/av_inherit.h b/security/selinux/include/av_inherit.h
new file mode 100644
index 00000000000..9facb27822a
--- /dev/null
+++ b/security/selinux/include/av_inherit.h
@@ -0,0 +1,30 @@
+/* This file is automatically generated. Do not edit. */
+ S_(SECCLASS_DIR, file, 0x00020000UL)
+ S_(SECCLASS_FILE, file, 0x00020000UL)
+ S_(SECCLASS_LNK_FILE, file, 0x00020000UL)
+ S_(SECCLASS_CHR_FILE, file, 0x00020000UL)
+ S_(SECCLASS_BLK_FILE, file, 0x00020000UL)
+ S_(SECCLASS_SOCK_FILE, file, 0x00020000UL)
+ S_(SECCLASS_FIFO_FILE, file, 0x00020000UL)
+ S_(SECCLASS_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_TCP_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_UDP_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_RAWIP_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_NETLINK_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_PACKET_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_KEY_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_UNIX_STREAM_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_UNIX_DGRAM_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_IPC, ipc, 0x00000200UL)
+ S_(SECCLASS_SEM, ipc, 0x00000200UL)
+ S_(SECCLASS_MSGQ, ipc, 0x00000200UL)
+ S_(SECCLASS_SHM, ipc, 0x00000200UL)
+ S_(SECCLASS_NETLINK_ROUTE_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_NETLINK_FIREWALL_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_NETLINK_TCPDIAG_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_NETLINK_NFLOG_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_NETLINK_XFRM_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_NETLINK_SELINUX_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_NETLINK_AUDIT_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_NETLINK_IP6FW_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_NETLINK_DNRT_SOCKET, socket, 0x00400000UL)
diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h
new file mode 100644
index 00000000000..903e8b3cc2e
--- /dev/null
+++ b/security/selinux/include/av_perm_to_string.h
@@ -0,0 +1,232 @@
+/* This file is automatically generated. Do not edit. */
+ S_(SECCLASS_FILESYSTEM, FILESYSTEM__MOUNT, "mount")
+ S_(SECCLASS_FILESYSTEM, FILESYSTEM__REMOUNT, "remount")
+ S_(SECCLASS_FILESYSTEM, FILESYSTEM__UNMOUNT, "unmount")
+ S_(SECCLASS_FILESYSTEM, FILESYSTEM__GETATTR, "getattr")
+ S_(SECCLASS_FILESYSTEM, FILESYSTEM__RELABELFROM, "relabelfrom")
+ S_(SECCLASS_FILESYSTEM, FILESYSTEM__RELABELTO, "relabelto")
+ S_(SECCLASS_FILESYSTEM, FILESYSTEM__TRANSITION, "transition")
+ S_(SECCLASS_FILESYSTEM, FILESYSTEM__ASSOCIATE, "associate")
+ S_(SECCLASS_FILESYSTEM, FILESYSTEM__QUOTAMOD, "quotamod")
+ S_(SECCLASS_FILESYSTEM, FILESYSTEM__QUOTAGET, "quotaget")
+ S_(SECCLASS_DIR, DIR__ADD_NAME, "add_name")
+ S_(SECCLASS_DIR, DIR__REMOVE_NAME, "remove_name")
+ S_(SECCLASS_DIR, DIR__REPARENT, "reparent")
+ S_(SECCLASS_DIR, DIR__SEARCH, "search")
+ S_(SECCLASS_DIR, DIR__RMDIR, "rmdir")
+ S_(SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, "execute_no_trans")
+ S_(SECCLASS_FILE, FILE__ENTRYPOINT, "entrypoint")
+ S_(SECCLASS_FILE, FILE__EXECMOD, "execmod")
+ S_(SECCLASS_CHR_FILE, CHR_FILE__EXECUTE_NO_TRANS, "execute_no_trans")
+ S_(SECCLASS_CHR_FILE, CHR_FILE__ENTRYPOINT, "entrypoint")
+ S_(SECCLASS_CHR_FILE, CHR_FILE__EXECMOD, "execmod")
+ S_(SECCLASS_FD, FD__USE, "use")
+ S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__CONNECTTO, "connectto")
+ S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__NEWCONN, "newconn")
+ S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__ACCEPTFROM, "acceptfrom")
+ S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__NODE_BIND, "node_bind")
+ S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__NAME_CONNECT, "name_connect")
+ S_(SECCLASS_UDP_SOCKET, UDP_SOCKET__NODE_BIND, "node_bind")
+ S_(SECCLASS_RAWIP_SOCKET, RAWIP_SOCKET__NODE_BIND, "node_bind")
+ S_(SECCLASS_NODE, NODE__TCP_RECV, "tcp_recv")
+ S_(SECCLASS_NODE, NODE__TCP_SEND, "tcp_send")
+ S_(SECCLASS_NODE, NODE__UDP_RECV, "udp_recv")
+ S_(SECCLASS_NODE, NODE__UDP_SEND, "udp_send")
+ S_(SECCLASS_NODE, NODE__RAWIP_RECV, "rawip_recv")
+ S_(SECCLASS_NODE, NODE__RAWIP_SEND, "rawip_send")
+ S_(SECCLASS_NODE, NODE__ENFORCE_DEST, "enforce_dest")
+ S_(SECCLASS_NETIF, NETIF__TCP_RECV, "tcp_recv")
+ S_(SECCLASS_NETIF, NETIF__TCP_SEND, "tcp_send")
+ S_(SECCLASS_NETIF, NETIF__UDP_RECV, "udp_recv")
+ S_(SECCLASS_NETIF, NETIF__UDP_SEND, "udp_send")
+ S_(SECCLASS_NETIF, NETIF__RAWIP_RECV, "rawip_recv")
+ S_(SECCLASS_NETIF, NETIF__RAWIP_SEND, "rawip_send")
+ S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__CONNECTTO, "connectto")
+ S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__NEWCONN, "newconn")
+ S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__ACCEPTFROM, "acceptfrom")
+ S_(SECCLASS_PROCESS, PROCESS__FORK, "fork")
+ S_(SECCLASS_PROCESS, PROCESS__TRANSITION, "transition")
+ S_(SECCLASS_PROCESS, PROCESS__SIGCHLD, "sigchld")
+ S_(SECCLASS_PROCESS, PROCESS__SIGKILL, "sigkill")
+ S_(SECCLASS_PROCESS, PROCESS__SIGSTOP, "sigstop")
+ S_(SECCLASS_PROCESS, PROCESS__SIGNULL, "signull")
+ S_(SECCLASS_PROCESS, PROCESS__SIGNAL, "signal")
+ S_(SECCLASS_PROCESS, PROCESS__PTRACE, "ptrace")
+ S_(SECCLASS_PROCESS, PROCESS__GETSCHED, "getsched")
+ S_(SECCLASS_PROCESS, PROCESS__SETSCHED, "setsched")
+ S_(SECCLASS_PROCESS, PROCESS__GETSESSION, "getsession")
+ S_(SECCLASS_PROCESS, PROCESS__GETPGID, "getpgid")
+ S_(SECCLASS_PROCESS, PROCESS__SETPGID, "setpgid")
+ S_(SECCLASS_PROCESS, PROCESS__GETCAP, "getcap")
+ S_(SECCLASS_PROCESS, PROCESS__SETCAP, "setcap")
+ S_(SECCLASS_PROCESS, PROCESS__SHARE, "share")
+ S_(SECCLASS_PROCESS, PROCESS__GETATTR, "getattr")
+ S_(SECCLASS_PROCESS, PROCESS__SETEXEC, "setexec")
+ S_(SECCLASS_PROCESS, PROCESS__SETFSCREATE, "setfscreate")
+ S_(SECCLASS_PROCESS, PROCESS__NOATSECURE, "noatsecure")
+ S_(SECCLASS_PROCESS, PROCESS__SIGINH, "siginh")
+ S_(SECCLASS_PROCESS, PROCESS__SETRLIMIT, "setrlimit")
+ S_(SECCLASS_PROCESS, PROCESS__RLIMITINH, "rlimitinh")
+ S_(SECCLASS_PROCESS, PROCESS__DYNTRANSITION, "dyntransition")
+ S_(SECCLASS_PROCESS, PROCESS__SETCURRENT, "setcurrent")
+ S_(SECCLASS_PROCESS, PROCESS__EXECMEM, "execmem")
+ S_(SECCLASS_MSGQ, MSGQ__ENQUEUE, "enqueue")
+ S_(SECCLASS_MSG, MSG__SEND, "send")
+ S_(SECCLASS_MSG, MSG__RECEIVE, "receive")
+ S_(SECCLASS_SHM, SHM__LOCK, "lock")
+ S_(SECCLASS_SECURITY, SECURITY__COMPUTE_AV, "compute_av")
+ S_(SECCLASS_SECURITY, SECURITY__COMPUTE_CREATE, "compute_create")
+ S_(SECCLASS_SECURITY, SECURITY__COMPUTE_MEMBER, "compute_member")
+ S_(SECCLASS_SECURITY, SECURITY__CHECK_CONTEXT, "check_context")
+ S_(SECCLASS_SECURITY, SECURITY__LOAD_POLICY, "load_policy")
+ S_(SECCLASS_SECURITY, SECURITY__COMPUTE_RELABEL, "compute_relabel")
+ S_(SECCLASS_SECURITY, SECURITY__COMPUTE_USER, "compute_user")
+ S_(SECCLASS_SECURITY, SECURITY__SETENFORCE, "setenforce")
+ S_(SECCLASS_SECURITY, SECURITY__SETBOOL, "setbool")
+ S_(SECCLASS_SECURITY, SECURITY__SETSECPARAM, "setsecparam")
+ S_(SECCLASS_SECURITY, SECURITY__SETCHECKREQPROT, "setcheckreqprot")
+ S_(SECCLASS_SYSTEM, SYSTEM__IPC_INFO, "ipc_info")
+ S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, "syslog_read")
+ S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, "syslog_mod")
+ S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE, "syslog_console")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__CHOWN, "chown")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__DAC_OVERRIDE, "dac_override")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__DAC_READ_SEARCH, "dac_read_search")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__FOWNER, "fowner")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__FSETID, "fsetid")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__KILL, "kill")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SETGID, "setgid")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SETUID, "setuid")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SETPCAP, "setpcap")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__LINUX_IMMUTABLE, "linux_immutable")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__NET_BIND_SERVICE, "net_bind_service")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__NET_BROADCAST, "net_broadcast")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__NET_ADMIN, "net_admin")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__NET_RAW, "net_raw")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__IPC_LOCK, "ipc_lock")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__IPC_OWNER, "ipc_owner")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_MODULE, "sys_module")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_RAWIO, "sys_rawio")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_CHROOT, "sys_chroot")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_PTRACE, "sys_ptrace")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_PACCT, "sys_pacct")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_ADMIN, "sys_admin")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_BOOT, "sys_boot")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_NICE, "sys_nice")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_RESOURCE, "sys_resource")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_TIME, "sys_time")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_TTY_CONFIG, "sys_tty_config")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__MKNOD, "mknod")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__LEASE, "lease")
+ S_(SECCLASS_PASSWD, PASSWD__PASSWD, "passwd")
+ S_(SECCLASS_PASSWD, PASSWD__CHFN, "chfn")
+ S_(SECCLASS_PASSWD, PASSWD__CHSH, "chsh")
+ S_(SECCLASS_PASSWD, PASSWD__ROOTOK, "rootok")
+ S_(SECCLASS_PASSWD, PASSWD__CRONTAB, "crontab")
+ S_(SECCLASS_DRAWABLE, DRAWABLE__CREATE, "create")
+ S_(SECCLASS_DRAWABLE, DRAWABLE__DESTROY, "destroy")
+ S_(SECCLASS_DRAWABLE, DRAWABLE__DRAW, "draw")
+ S_(SECCLASS_DRAWABLE, DRAWABLE__COPY, "copy")
+ S_(SECCLASS_DRAWABLE, DRAWABLE__GETATTR, "getattr")
+ S_(SECCLASS_GC, GC__CREATE, "create")
+ S_(SECCLASS_GC, GC__FREE, "free")
+ S_(SECCLASS_GC, GC__GETATTR, "getattr")
+ S_(SECCLASS_GC, GC__SETATTR, "setattr")
+ S_(SECCLASS_WINDOW, WINDOW__ADDCHILD, "addchild")
+ S_(SECCLASS_WINDOW, WINDOW__CREATE, "create")
+ S_(SECCLASS_WINDOW, WINDOW__DESTROY, "destroy")
+ S_(SECCLASS_WINDOW, WINDOW__MAP, "map")
+ S_(SECCLASS_WINDOW, WINDOW__UNMAP, "unmap")
+ S_(SECCLASS_WINDOW, WINDOW__CHSTACK, "chstack")
+ S_(SECCLASS_WINDOW, WINDOW__CHPROPLIST, "chproplist")
+ S_(SECCLASS_WINDOW, WINDOW__CHPROP, "chprop")
+ S_(SECCLASS_WINDOW, WINDOW__LISTPROP, "listprop")
+ S_(SECCLASS_WINDOW, WINDOW__GETATTR, "getattr")
+ S_(SECCLASS_WINDOW, WINDOW__SETATTR, "setattr")
+ S_(SECCLASS_WINDOW, WINDOW__SETFOCUS, "setfocus")
+ S_(SECCLASS_WINDOW, WINDOW__MOVE, "move")
+ S_(SECCLASS_WINDOW, WINDOW__CHSELECTION, "chselection")
+ S_(SECCLASS_WINDOW, WINDOW__CHPARENT, "chparent")
+ S_(SECCLASS_WINDOW, WINDOW__CTRLLIFE, "ctrllife")
+ S_(SECCLASS_WINDOW, WINDOW__ENUMERATE, "enumerate")
+ S_(SECCLASS_WINDOW, WINDOW__TRANSPARENT, "transparent")
+ S_(SECCLASS_WINDOW, WINDOW__MOUSEMOTION, "mousemotion")
+ S_(SECCLASS_WINDOW, WINDOW__CLIENTCOMEVENT, "clientcomevent")
+ S_(SECCLASS_WINDOW, WINDOW__INPUTEVENT, "inputevent")
+ S_(SECCLASS_WINDOW, WINDOW__DRAWEVENT, "drawevent")
+ S_(SECCLASS_WINDOW, WINDOW__WINDOWCHANGEEVENT, "windowchangeevent")
+ S_(SECCLASS_WINDOW, WINDOW__WINDOWCHANGEREQUEST, "windowchangerequest")
+ S_(SECCLASS_WINDOW, WINDOW__SERVERCHANGEEVENT, "serverchangeevent")
+ S_(SECCLASS_WINDOW, WINDOW__EXTENSIONEVENT, "extensionevent")
+ S_(SECCLASS_FONT, FONT__LOAD, "load")
+ S_(SECCLASS_FONT, FONT__FREE, "free")
+ S_(SECCLASS_FONT, FONT__GETATTR, "getattr")
+ S_(SECCLASS_FONT, FONT__USE, "use")
+ S_(SECCLASS_COLORMAP, COLORMAP__CREATE, "create")
+ S_(SECCLASS_COLORMAP, COLORMAP__FREE, "free")
+ S_(SECCLASS_COLORMAP, COLORMAP__INSTALL, "install")
+ S_(SECCLASS_COLORMAP, COLORMAP__UNINSTALL, "uninstall")
+ S_(SECCLASS_COLORMAP, COLORMAP__LIST, "list")
+ S_(SECCLASS_COLORMAP, COLORMAP__READ, "read")
+ S_(SECCLASS_COLORMAP, COLORMAP__STORE, "store")
+ S_(SECCLASS_COLORMAP, COLORMAP__GETATTR, "getattr")
+ S_(SECCLASS_COLORMAP, COLORMAP__SETATTR, "setattr")
+ S_(SECCLASS_PROPERTY, PROPERTY__CREATE, "create")
+ S_(SECCLASS_PROPERTY, PROPERTY__FREE, "free")
+ S_(SECCLASS_PROPERTY, PROPERTY__READ, "read")
+ S_(SECCLASS_PROPERTY, PROPERTY__WRITE, "write")
+ S_(SECCLASS_CURSOR, CURSOR__CREATE, "create")
+ S_(SECCLASS_CURSOR, CURSOR__CREATEGLYPH, "createglyph")
+ S_(SECCLASS_CURSOR, CURSOR__FREE, "free")
+ S_(SECCLASS_CURSOR, CURSOR__ASSIGN, "assign")
+ S_(SECCLASS_CURSOR, CURSOR__SETATTR, "setattr")
+ S_(SECCLASS_XCLIENT, XCLIENT__KILL, "kill")
+ S_(SECCLASS_XINPUT, XINPUT__LOOKUP, "lookup")
+ S_(SECCLASS_XINPUT, XINPUT__GETATTR, "getattr")
+ S_(SECCLASS_XINPUT, XINPUT__SETATTR, "setattr")
+ S_(SECCLASS_XINPUT, XINPUT__SETFOCUS, "setfocus")
+ S_(SECCLASS_XINPUT, XINPUT__WARPPOINTER, "warppointer")
+ S_(SECCLASS_XINPUT, XINPUT__ACTIVEGRAB, "activegrab")
+ S_(SECCLASS_XINPUT, XINPUT__PASSIVEGRAB, "passivegrab")
+ S_(SECCLASS_XINPUT, XINPUT__UNGRAB, "ungrab")
+ S_(SECCLASS_XINPUT, XINPUT__BELL, "bell")
+ S_(SECCLASS_XINPUT, XINPUT__MOUSEMOTION, "mousemotion")
+ S_(SECCLASS_XINPUT, XINPUT__RELABELINPUT, "relabelinput")
+ S_(SECCLASS_XSERVER, XSERVER__SCREENSAVER, "screensaver")
+ S_(SECCLASS_XSERVER, XSERVER__GETHOSTLIST, "gethostlist")
+ S_(SECCLASS_XSERVER, XSERVER__SETHOSTLIST, "sethostlist")
+ S_(SECCLASS_XSERVER, XSERVER__GETFONTPATH, "getfontpath")
+ S_(SECCLASS_XSERVER, XSERVER__SETFONTPATH, "setfontpath")
+ S_(SECCLASS_XSERVER, XSERVER__GETATTR, "getattr")
+ S_(SECCLASS_XSERVER, XSERVER__GRAB, "grab")
+ S_(SECCLASS_XSERVER, XSERVER__UNGRAB, "ungrab")
+ S_(SECCLASS_XEXTENSION, XEXTENSION__QUERY, "query")
+ S_(SECCLASS_XEXTENSION, XEXTENSION__USE, "use")
+ S_(SECCLASS_PAX, PAX__PAGEEXEC, "pageexec")
+ S_(SECCLASS_PAX, PAX__EMUTRAMP, "emutramp")
+ S_(SECCLASS_PAX, PAX__MPROTECT, "mprotect")
+ S_(SECCLASS_PAX, PAX__RANDMMAP, "randmmap")
+ S_(SECCLASS_PAX, PAX__RANDEXEC, "randexec")
+ S_(SECCLASS_PAX, PAX__SEGMEXEC, "segmexec")
+ S_(SECCLASS_NETLINK_ROUTE_SOCKET, NETLINK_ROUTE_SOCKET__NLMSG_READ, "nlmsg_read")
+ S_(SECCLASS_NETLINK_ROUTE_SOCKET, NETLINK_ROUTE_SOCKET__NLMSG_WRITE, "nlmsg_write")
+ S_(SECCLASS_NETLINK_FIREWALL_SOCKET, NETLINK_FIREWALL_SOCKET__NLMSG_READ, "nlmsg_read")
+ S_(SECCLASS_NETLINK_FIREWALL_SOCKET, NETLINK_FIREWALL_SOCKET__NLMSG_WRITE, "nlmsg_write")
+ S_(SECCLASS_NETLINK_TCPDIAG_SOCKET, NETLINK_TCPDIAG_SOCKET__NLMSG_READ, "nlmsg_read")
+ S_(SECCLASS_NETLINK_TCPDIAG_SOCKET, NETLINK_TCPDIAG_SOCKET__NLMSG_WRITE, "nlmsg_write")
+ S_(SECCLASS_NETLINK_XFRM_SOCKET, NETLINK_XFRM_SOCKET__NLMSG_READ, "nlmsg_read")
+ S_(SECCLASS_NETLINK_XFRM_SOCKET, NETLINK_XFRM_SOCKET__NLMSG_WRITE, "nlmsg_write")
+ S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_READ, "nlmsg_read")
+ S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE, "nlmsg_write")
+ S_(SECCLASS_NETLINK_IP6FW_SOCKET, NETLINK_IP6FW_SOCKET__NLMSG_READ, "nlmsg_read")
+ S_(SECCLASS_NETLINK_IP6FW_SOCKET, NETLINK_IP6FW_SOCKET__NLMSG_WRITE, "nlmsg_write")
+ S_(SECCLASS_DBUS, DBUS__ACQUIRE_SVC, "acquire_svc")
+ S_(SECCLASS_DBUS, DBUS__SEND_MSG, "send_msg")
+ S_(SECCLASS_NSCD, NSCD__GETPWD, "getpwd")
+ S_(SECCLASS_NSCD, NSCD__GETGRP, "getgrp")
+ S_(SECCLASS_NSCD, NSCD__GETHOST, "gethost")
+ S_(SECCLASS_NSCD, NSCD__GETSTAT, "getstat")
+ S_(SECCLASS_NSCD, NSCD__ADMIN, "admin")
+ S_(SECCLASS_NSCD, NSCD__SHMEMPWD, "shmempwd")
+ S_(SECCLASS_NSCD, NSCD__SHMEMGRP, "shmemgrp")
+ S_(SECCLASS_NSCD, NSCD__SHMEMHOST, "shmemhost")
diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h
new file mode 100644
index 00000000000..b0a12ac8f7e
--- /dev/null
+++ b/security/selinux/include/av_permissions.h
@@ -0,0 +1,902 @@
+/* This file is automatically generated. Do not edit. */
+#define COMMON_FILE__IOCTL 0x00000001UL
+#define COMMON_FILE__READ 0x00000002UL
+#define COMMON_FILE__WRITE 0x00000004UL
+#define COMMON_FILE__CREATE 0x00000008UL
+#define COMMON_FILE__GETATTR 0x00000010UL
+#define COMMON_FILE__SETATTR 0x00000020UL
+#define COMMON_FILE__LOCK 0x00000040UL
+#define COMMON_FILE__RELABELFROM 0x00000080UL
+#define COMMON_FILE__RELABELTO 0x00000100UL
+#define COMMON_FILE__APPEND 0x00000200UL
+#define COMMON_FILE__UNLINK 0x00000400UL
+#define COMMON_FILE__LINK 0x00000800UL
+#define COMMON_FILE__RENAME 0x00001000UL
+#define COMMON_FILE__EXECUTE 0x00002000UL
+#define COMMON_FILE__SWAPON 0x00004000UL
+#define COMMON_FILE__QUOTAON 0x00008000UL
+#define COMMON_FILE__MOUNTON 0x00010000UL
+
+#define COMMON_SOCKET__IOCTL 0x00000001UL
+#define COMMON_SOCKET__READ 0x00000002UL
+#define COMMON_SOCKET__WRITE 0x00000004UL
+#define COMMON_SOCKET__CREATE 0x00000008UL
+#define COMMON_SOCKET__GETATTR 0x00000010UL
+#define COMMON_SOCKET__SETATTR 0x00000020UL
+#define COMMON_SOCKET__LOCK 0x00000040UL
+#define COMMON_SOCKET__RELABELFROM 0x00000080UL
+#define COMMON_SOCKET__RELABELTO 0x00000100UL
+#define COMMON_SOCKET__APPEND 0x00000200UL
+#define COMMON_SOCKET__BIND 0x00000400UL
+#define COMMON_SOCKET__CONNECT 0x00000800UL
+#define COMMON_SOCKET__LISTEN 0x00001000UL
+#define COMMON_SOCKET__ACCEPT 0x00002000UL
+#define COMMON_SOCKET__GETOPT 0x00004000UL
+#define COMMON_SOCKET__SETOPT 0x00008000UL
+#define COMMON_SOCKET__SHUTDOWN 0x00010000UL
+#define COMMON_SOCKET__RECVFROM 0x00020000UL
+#define COMMON_SOCKET__SENDTO 0x00040000UL
+#define COMMON_SOCKET__RECV_MSG 0x00080000UL
+#define COMMON_SOCKET__SEND_MSG 0x00100000UL
+#define COMMON_SOCKET__NAME_BIND 0x00200000UL
+
+#define COMMON_IPC__CREATE 0x00000001UL
+#define COMMON_IPC__DESTROY 0x00000002UL
+#define COMMON_IPC__GETATTR 0x00000004UL
+#define COMMON_IPC__SETATTR 0x00000008UL
+#define COMMON_IPC__READ 0x00000010UL
+#define COMMON_IPC__WRITE 0x00000020UL
+#define COMMON_IPC__ASSOCIATE 0x00000040UL
+#define COMMON_IPC__UNIX_READ 0x00000080UL
+#define COMMON_IPC__UNIX_WRITE 0x00000100UL
+
+#define FILESYSTEM__MOUNT 0x00000001UL
+#define FILESYSTEM__REMOUNT 0x00000002UL
+#define FILESYSTEM__UNMOUNT 0x00000004UL
+#define FILESYSTEM__GETATTR 0x00000008UL
+#define FILESYSTEM__RELABELFROM 0x00000010UL
+#define FILESYSTEM__RELABELTO 0x00000020UL
+#define FILESYSTEM__TRANSITION 0x00000040UL
+#define FILESYSTEM__ASSOCIATE 0x00000080UL
+#define FILESYSTEM__QUOTAMOD 0x00000100UL
+#define FILESYSTEM__QUOTAGET 0x00000200UL
+
+#define DIR__IOCTL 0x00000001UL
+#define DIR__READ 0x00000002UL
+#define DIR__WRITE 0x00000004UL
+#define DIR__CREATE 0x00000008UL
+#define DIR__GETATTR 0x00000010UL
+#define DIR__SETATTR 0x00000020UL
+#define DIR__LOCK 0x00000040UL
+#define DIR__RELABELFROM 0x00000080UL
+#define DIR__RELABELTO 0x00000100UL
+#define DIR__APPEND 0x00000200UL
+#define DIR__UNLINK 0x00000400UL
+#define DIR__LINK 0x00000800UL
+#define DIR__RENAME 0x00001000UL
+#define DIR__EXECUTE 0x00002000UL
+#define DIR__SWAPON 0x00004000UL
+#define DIR__QUOTAON 0x00008000UL
+#define DIR__MOUNTON 0x00010000UL
+
+#define DIR__ADD_NAME 0x00020000UL
+#define DIR__REMOVE_NAME 0x00040000UL
+#define DIR__REPARENT 0x00080000UL
+#define DIR__SEARCH 0x00100000UL
+#define DIR__RMDIR 0x00200000UL
+
+#define FILE__IOCTL 0x00000001UL
+#define FILE__READ 0x00000002UL
+#define FILE__WRITE 0x00000004UL
+#define FILE__CREATE 0x00000008UL
+#define FILE__GETATTR 0x00000010UL
+#define FILE__SETATTR 0x00000020UL
+#define FILE__LOCK 0x00000040UL
+#define FILE__RELABELFROM 0x00000080UL
+#define FILE__RELABELTO 0x00000100UL
+#define FILE__APPEND 0x00000200UL
+#define FILE__UNLINK 0x00000400UL
+#define FILE__LINK 0x00000800UL
+#define FILE__RENAME 0x00001000UL
+#define FILE__EXECUTE 0x00002000UL
+#define FILE__SWAPON 0x00004000UL
+#define FILE__QUOTAON 0x00008000UL
+#define FILE__MOUNTON 0x00010000UL
+
+#define FILE__EXECUTE_NO_TRANS 0x00020000UL
+#define FILE__ENTRYPOINT 0x00040000UL
+#define FILE__EXECMOD 0x00080000UL
+
+#define LNK_FILE__IOCTL 0x00000001UL
+#define LNK_FILE__READ 0x00000002UL
+#define LNK_FILE__WRITE 0x00000004UL
+#define LNK_FILE__CREATE 0x00000008UL
+#define LNK_FILE__GETATTR 0x00000010UL
+#define LNK_FILE__SETATTR 0x00000020UL
+#define LNK_FILE__LOCK 0x00000040UL
+#define LNK_FILE__RELABELFROM 0x00000080UL
+#define LNK_FILE__RELABELTO 0x00000100UL
+#define LNK_FILE__APPEND 0x00000200UL
+#define LNK_FILE__UNLINK 0x00000400UL
+#define LNK_FILE__LINK 0x00000800UL
+#define LNK_FILE__RENAME 0x00001000UL
+#define LNK_FILE__EXECUTE 0x00002000UL
+#define LNK_FILE__SWAPON 0x00004000UL
+#define LNK_FILE__QUOTAON 0x00008000UL
+#define LNK_FILE__MOUNTON 0x00010000UL
+
+#define CHR_FILE__IOCTL 0x00000001UL
+#define CHR_FILE__READ 0x00000002UL
+#define CHR_FILE__WRITE 0x00000004UL
+#define CHR_FILE__CREATE 0x00000008UL
+#define CHR_FILE__GETATTR 0x00000010UL
+#define CHR_FILE__SETATTR 0x00000020UL
+#define CHR_FILE__LOCK 0x00000040UL
+#define CHR_FILE__RELABELFROM 0x00000080UL
+#define CHR_FILE__RELABELTO 0x00000100UL
+#define CHR_FILE__APPEND 0x00000200UL
+#define CHR_FILE__UNLINK 0x00000400UL
+#define CHR_FILE__LINK 0x00000800UL
+#define CHR_FILE__RENAME 0x00001000UL
+#define CHR_FILE__EXECUTE 0x00002000UL
+#define CHR_FILE__SWAPON 0x00004000UL
+#define CHR_FILE__QUOTAON 0x00008000UL
+#define CHR_FILE__MOUNTON 0x00010000UL
+
+#define CHR_FILE__EXECUTE_NO_TRANS 0x00020000UL
+#define CHR_FILE__ENTRYPOINT 0x00040000UL
+#define CHR_FILE__EXECMOD 0x00080000UL
+
+#define BLK_FILE__IOCTL 0x00000001UL
+#define BLK_FILE__READ 0x00000002UL
+#define BLK_FILE__WRITE 0x00000004UL
+#define BLK_FILE__CREATE 0x00000008UL
+#define BLK_FILE__GETATTR 0x00000010UL
+#define BLK_FILE__SETATTR 0x00000020UL
+#define BLK_FILE__LOCK 0x00000040UL
+#define BLK_FILE__RELABELFROM 0x00000080UL
+#define BLK_FILE__RELABELTO 0x00000100UL
+#define BLK_FILE__APPEND 0x00000200UL
+#define BLK_FILE__UNLINK 0x00000400UL
+#define BLK_FILE__LINK 0x00000800UL
+#define BLK_FILE__RENAME 0x00001000UL
+#define BLK_FILE__EXECUTE 0x00002000UL
+#define BLK_FILE__SWAPON 0x00004000UL
+#define BLK_FILE__QUOTAON 0x00008000UL
+#define BLK_FILE__MOUNTON 0x00010000UL
+
+#define SOCK_FILE__IOCTL 0x00000001UL
+#define SOCK_FILE__READ 0x00000002UL
+#define SOCK_FILE__WRITE 0x00000004UL
+#define SOCK_FILE__CREATE 0x00000008UL
+#define SOCK_FILE__GETATTR 0x00000010UL
+#define SOCK_FILE__SETATTR 0x00000020UL
+#define SOCK_FILE__LOCK 0x00000040UL
+#define SOCK_FILE__RELABELFROM 0x00000080UL
+#define SOCK_FILE__RELABELTO 0x00000100UL
+#define SOCK_FILE__APPEND 0x00000200UL
+#define SOCK_FILE__UNLINK 0x00000400UL
+#define SOCK_FILE__LINK 0x00000800UL
+#define SOCK_FILE__RENAME 0x00001000UL
+#define SOCK_FILE__EXECUTE 0x00002000UL
+#define SOCK_FILE__SWAPON 0x00004000UL
+#define SOCK_FILE__QUOTAON 0x00008000UL
+#define SOCK_FILE__MOUNTON 0x00010000UL
+
+#define FIFO_FILE__IOCTL 0x00000001UL
+#define FIFO_FILE__READ 0x00000002UL
+#define FIFO_FILE__WRITE 0x00000004UL
+#define FIFO_FILE__CREATE 0x00000008UL
+#define FIFO_FILE__GETATTR 0x00000010UL
+#define FIFO_FILE__SETATTR 0x00000020UL
+#define FIFO_FILE__LOCK 0x00000040UL
+#define FIFO_FILE__RELABELFROM 0x00000080UL
+#define FIFO_FILE__RELABELTO 0x00000100UL
+#define FIFO_FILE__APPEND 0x00000200UL
+#define FIFO_FILE__UNLINK 0x00000400UL
+#define FIFO_FILE__LINK 0x00000800UL
+#define FIFO_FILE__RENAME 0x00001000UL
+#define FIFO_FILE__EXECUTE 0x00002000UL
+#define FIFO_FILE__SWAPON 0x00004000UL
+#define FIFO_FILE__QUOTAON 0x00008000UL
+#define FIFO_FILE__MOUNTON 0x00010000UL
+
+#define FD__USE 0x00000001UL
+
+#define SOCKET__IOCTL 0x00000001UL
+#define SOCKET__READ 0x00000002UL
+#define SOCKET__WRITE 0x00000004UL
+#define SOCKET__CREATE 0x00000008UL
+#define SOCKET__GETATTR 0x00000010UL
+#define SOCKET__SETATTR 0x00000020UL
+#define SOCKET__LOCK 0x00000040UL
+#define SOCKET__RELABELFROM 0x00000080UL
+#define SOCKET__RELABELTO 0x00000100UL
+#define SOCKET__APPEND 0x00000200UL
+#define SOCKET__BIND 0x00000400UL
+#define SOCKET__CONNECT 0x00000800UL
+#define SOCKET__LISTEN 0x00001000UL
+#define SOCKET__ACCEPT 0x00002000UL
+#define SOCKET__GETOPT 0x00004000UL
+#define SOCKET__SETOPT 0x00008000UL
+#define SOCKET__SHUTDOWN 0x00010000UL
+#define SOCKET__RECVFROM 0x00020000UL
+#define SOCKET__SENDTO 0x00040000UL
+#define SOCKET__RECV_MSG 0x00080000UL
+#define SOCKET__SEND_MSG 0x00100000UL
+#define SOCKET__NAME_BIND 0x00200000UL
+
+#define TCP_SOCKET__IOCTL 0x00000001UL
+#define TCP_SOCKET__READ 0x00000002UL
+#define TCP_SOCKET__WRITE 0x00000004UL
+#define TCP_SOCKET__CREATE 0x00000008UL
+#define TCP_SOCKET__GETATTR 0x00000010UL
+#define TCP_SOCKET__SETATTR 0x00000020UL
+#define TCP_SOCKET__LOCK 0x00000040UL
+#define TCP_SOCKET__RELABELFROM 0x00000080UL
+#define TCP_SOCKET__RELABELTO 0x00000100UL
+#define TCP_SOCKET__APPEND 0x00000200UL
+#define TCP_SOCKET__BIND 0x00000400UL
+#define TCP_SOCKET__CONNECT 0x00000800UL
+#define TCP_SOCKET__LISTEN 0x00001000UL
+#define TCP_SOCKET__ACCEPT 0x00002000UL
+#define TCP_SOCKET__GETOPT 0x00004000UL
+#define TCP_SOCKET__SETOPT 0x00008000UL
+#define TCP_SOCKET__SHUTDOWN 0x00010000UL
+#define TCP_SOCKET__RECVFROM 0x00020000UL
+#define TCP_SOCKET__SENDTO 0x00040000UL
+#define TCP_SOCKET__RECV_MSG 0x00080000UL
+#define TCP_SOCKET__SEND_MSG 0x00100000UL
+#define TCP_SOCKET__NAME_BIND 0x00200000UL
+
+#define TCP_SOCKET__CONNECTTO 0x00400000UL
+#define TCP_SOCKET__NEWCONN 0x00800000UL
+#define TCP_SOCKET__ACCEPTFROM 0x01000000UL
+#define TCP_SOCKET__NODE_BIND 0x02000000UL
+#define TCP_SOCKET__NAME_CONNECT 0x04000000UL
+
+#define UDP_SOCKET__IOCTL 0x00000001UL
+#define UDP_SOCKET__READ 0x00000002UL
+#define UDP_SOCKET__WRITE 0x00000004UL
+#define UDP_SOCKET__CREATE 0x00000008UL
+#define UDP_SOCKET__GETATTR 0x00000010UL
+#define UDP_SOCKET__SETATTR 0x00000020UL
+#define UDP_SOCKET__LOCK 0x00000040UL
+#define UDP_SOCKET__RELABELFROM 0x00000080UL
+#define UDP_SOCKET__RELABELTO 0x00000100UL
+#define UDP_SOCKET__APPEND 0x00000200UL
+#define UDP_SOCKET__BIND 0x00000400UL
+#define UDP_SOCKET__CONNECT 0x00000800UL
+#define UDP_SOCKET__LISTEN 0x00001000UL
+#define UDP_SOCKET__ACCEPT 0x00002000UL
+#define UDP_SOCKET__GETOPT 0x00004000UL
+#define UDP_SOCKET__SETOPT 0x00008000UL
+#define UDP_SOCKET__SHUTDOWN 0x00010000UL
+#define UDP_SOCKET__RECVFROM 0x00020000UL
+#define UDP_SOCKET__SENDTO 0x00040000UL
+#define UDP_SOCKET__RECV_MSG 0x00080000UL
+#define UDP_SOCKET__SEND_MSG 0x00100000UL
+#define UDP_SOCKET__NAME_BIND 0x00200000UL
+
+#define UDP_SOCKET__NODE_BIND 0x00400000UL
+
+#define RAWIP_SOCKET__IOCTL 0x00000001UL
+#define RAWIP_SOCKET__READ 0x00000002UL
+#define RAWIP_SOCKET__WRITE 0x00000004UL
+#define RAWIP_SOCKET__CREATE 0x00000008UL
+#define RAWIP_SOCKET__GETATTR 0x00000010UL
+#define RAWIP_SOCKET__SETATTR 0x00000020UL
+#define RAWIP_SOCKET__LOCK 0x00000040UL
+#define RAWIP_SOCKET__RELABELFROM 0x00000080UL
+#define RAWIP_SOCKET__RELABELTO 0x00000100UL
+#define RAWIP_SOCKET__APPEND 0x00000200UL
+#define RAWIP_SOCKET__BIND 0x00000400UL
+#define RAWIP_SOCKET__CONNECT 0x00000800UL
+#define RAWIP_SOCKET__LISTEN 0x00001000UL
+#define RAWIP_SOCKET__ACCEPT 0x00002000UL
+#define RAWIP_SOCKET__GETOPT 0x00004000UL
+#define RAWIP_SOCKET__SETOPT 0x00008000UL
+#define RAWIP_SOCKET__SHUTDOWN 0x00010000UL
+#define RAWIP_SOCKET__RECVFROM 0x00020000UL
+#define RAWIP_SOCKET__SENDTO 0x00040000UL
+#define RAWIP_SOCKET__RECV_MSG 0x00080000UL
+#define RAWIP_SOCKET__SEND_MSG 0x00100000UL
+#define RAWIP_SOCKET__NAME_BIND 0x00200000UL
+
+#define RAWIP_SOCKET__NODE_BIND 0x00400000UL
+
+#define NODE__TCP_RECV 0x00000001UL
+#define NODE__TCP_SEND 0x00000002UL
+#define NODE__UDP_RECV 0x00000004UL
+#define NODE__UDP_SEND 0x00000008UL
+#define NODE__RAWIP_RECV 0x00000010UL
+#define NODE__RAWIP_SEND 0x00000020UL
+#define NODE__ENFORCE_DEST 0x00000040UL
+
+#define NETIF__TCP_RECV 0x00000001UL
+#define NETIF__TCP_SEND 0x00000002UL
+#define NETIF__UDP_RECV 0x00000004UL
+#define NETIF__UDP_SEND 0x00000008UL
+#define NETIF__RAWIP_RECV 0x00000010UL
+#define NETIF__RAWIP_SEND 0x00000020UL
+
+#define NETLINK_SOCKET__IOCTL 0x00000001UL
+#define NETLINK_SOCKET__READ 0x00000002UL
+#define NETLINK_SOCKET__WRITE 0x00000004UL
+#define NETLINK_SOCKET__CREATE 0x00000008UL
+#define NETLINK_SOCKET__GETATTR 0x00000010UL
+#define NETLINK_SOCKET__SETATTR 0x00000020UL
+#define NETLINK_SOCKET__LOCK 0x00000040UL
+#define NETLINK_SOCKET__RELABELFROM 0x00000080UL
+#define NETLINK_SOCKET__RELABELTO 0x00000100UL
+#define NETLINK_SOCKET__APPEND 0x00000200UL
+#define NETLINK_SOCKET__BIND 0x00000400UL
+#define NETLINK_SOCKET__CONNECT 0x00000800UL
+#define NETLINK_SOCKET__LISTEN 0x00001000UL
+#define NETLINK_SOCKET__ACCEPT 0x00002000UL
+#define NETLINK_SOCKET__GETOPT 0x00004000UL
+#define NETLINK_SOCKET__SETOPT 0x00008000UL
+#define NETLINK_SOCKET__SHUTDOWN 0x00010000UL
+#define NETLINK_SOCKET__RECVFROM 0x00020000UL
+#define NETLINK_SOCKET__SENDTO 0x00040000UL
+#define NETLINK_SOCKET__RECV_MSG 0x00080000UL
+#define NETLINK_SOCKET__SEND_MSG 0x00100000UL
+#define NETLINK_SOCKET__NAME_BIND 0x00200000UL
+
+#define PACKET_SOCKET__IOCTL 0x00000001UL
+#define PACKET_SOCKET__READ 0x00000002UL
+#define PACKET_SOCKET__WRITE 0x00000004UL
+#define PACKET_SOCKET__CREATE 0x00000008UL
+#define PACKET_SOCKET__GETATTR 0x00000010UL
+#define PACKET_SOCKET__SETATTR 0x00000020UL
+#define PACKET_SOCKET__LOCK 0x00000040UL
+#define PACKET_SOCKET__RELABELFROM 0x00000080UL
+#define PACKET_SOCKET__RELABELTO 0x00000100UL
+#define PACKET_SOCKET__APPEND 0x00000200UL
+#define PACKET_SOCKET__BIND 0x00000400UL
+#define PACKET_SOCKET__CONNECT 0x00000800UL
+#define PACKET_SOCKET__LISTEN 0x00001000UL
+#define PACKET_SOCKET__ACCEPT 0x00002000UL
+#define PACKET_SOCKET__GETOPT 0x00004000UL
+#define PACKET_SOCKET__SETOPT 0x00008000UL
+#define PACKET_SOCKET__SHUTDOWN 0x00010000UL
+#define PACKET_SOCKET__RECVFROM 0x00020000UL
+#define PACKET_SOCKET__SENDTO 0x00040000UL
+#define PACKET_SOCKET__RECV_MSG 0x00080000UL
+#define PACKET_SOCKET__SEND_MSG 0x00100000UL
+#define PACKET_SOCKET__NAME_BIND 0x00200000UL
+
+#define KEY_SOCKET__IOCTL 0x00000001UL
+#define KEY_SOCKET__READ 0x00000002UL
+#define KEY_SOCKET__WRITE 0x00000004UL
+#define KEY_SOCKET__CREATE 0x00000008UL
+#define KEY_SOCKET__GETATTR 0x00000010UL
+#define KEY_SOCKET__SETATTR 0x00000020UL
+#define KEY_SOCKET__LOCK 0x00000040UL
+#define KEY_SOCKET__RELABELFROM 0x00000080UL
+#define KEY_SOCKET__RELABELTO 0x00000100UL
+#define KEY_SOCKET__APPEND 0x00000200UL
+#define KEY_SOCKET__BIND 0x00000400UL
+#define KEY_SOCKET__CONNECT 0x00000800UL
+#define KEY_SOCKET__LISTEN 0x00001000UL
+#define KEY_SOCKET__ACCEPT 0x00002000UL
+#define KEY_SOCKET__GETOPT 0x00004000UL
+#define KEY_SOCKET__SETOPT 0x00008000UL
+#define KEY_SOCKET__SHUTDOWN 0x00010000UL
+#define KEY_SOCKET__RECVFROM 0x00020000UL
+#define KEY_SOCKET__SENDTO 0x00040000UL
+#define KEY_SOCKET__RECV_MSG 0x00080000UL
+#define KEY_SOCKET__SEND_MSG 0x00100000UL
+#define KEY_SOCKET__NAME_BIND 0x00200000UL
+
+#define UNIX_STREAM_SOCKET__IOCTL 0x00000001UL
+#define UNIX_STREAM_SOCKET__READ 0x00000002UL
+#define UNIX_STREAM_SOCKET__WRITE 0x00000004UL
+#define UNIX_STREAM_SOCKET__CREATE 0x00000008UL
+#define UNIX_STREAM_SOCKET__GETATTR 0x00000010UL
+#define UNIX_STREAM_SOCKET__SETATTR 0x00000020UL
+#define UNIX_STREAM_SOCKET__LOCK 0x00000040UL
+#define UNIX_STREAM_SOCKET__RELABELFROM 0x00000080UL
+#define UNIX_STREAM_SOCKET__RELABELTO 0x00000100UL
+#define UNIX_STREAM_SOCKET__APPEND 0x00000200UL
+#define UNIX_STREAM_SOCKET__BIND 0x00000400UL
+#define UNIX_STREAM_SOCKET__CONNECT 0x00000800UL
+#define UNIX_STREAM_SOCKET__LISTEN 0x00001000UL
+#define UNIX_STREAM_SOCKET__ACCEPT 0x00002000UL
+#define UNIX_STREAM_SOCKET__GETOPT 0x00004000UL
+#define UNIX_STREAM_SOCKET__SETOPT 0x00008000UL
+#define UNIX_STREAM_SOCKET__SHUTDOWN 0x00010000UL
+#define UNIX_STREAM_SOCKET__RECVFROM 0x00020000UL
+#define UNIX_STREAM_SOCKET__SENDTO 0x00040000UL
+#define UNIX_STREAM_SOCKET__RECV_MSG 0x00080000UL
+#define UNIX_STREAM_SOCKET__SEND_MSG 0x00100000UL
+#define UNIX_STREAM_SOCKET__NAME_BIND 0x00200000UL
+
+#define UNIX_STREAM_SOCKET__CONNECTTO 0x00400000UL
+#define UNIX_STREAM_SOCKET__NEWCONN 0x00800000UL
+#define UNIX_STREAM_SOCKET__ACCEPTFROM 0x01000000UL
+
+#define UNIX_DGRAM_SOCKET__IOCTL 0x00000001UL
+#define UNIX_DGRAM_SOCKET__READ 0x00000002UL
+#define UNIX_DGRAM_SOCKET__WRITE 0x00000004UL
+#define UNIX_DGRAM_SOCKET__CREATE 0x00000008UL
+#define UNIX_DGRAM_SOCKET__GETATTR 0x00000010UL
+#define UNIX_DGRAM_SOCKET__SETATTR 0x00000020UL
+#define UNIX_DGRAM_SOCKET__LOCK 0x00000040UL
+#define UNIX_DGRAM_SOCKET__RELABELFROM 0x00000080UL
+#define UNIX_DGRAM_SOCKET__RELABELTO 0x00000100UL
+#define UNIX_DGRAM_SOCKET__APPEND 0x00000200UL
+#define UNIX_DGRAM_SOCKET__BIND 0x00000400UL
+#define UNIX_DGRAM_SOCKET__CONNECT 0x00000800UL
+#define UNIX_DGRAM_SOCKET__LISTEN 0x00001000UL
+#define UNIX_DGRAM_SOCKET__ACCEPT 0x00002000UL
+#define UNIX_DGRAM_SOCKET__GETOPT 0x00004000UL
+#define UNIX_DGRAM_SOCKET__SETOPT 0x00008000UL
+#define UNIX_DGRAM_SOCKET__SHUTDOWN 0x00010000UL
+#define UNIX_DGRAM_SOCKET__RECVFROM 0x00020000UL
+#define UNIX_DGRAM_SOCKET__SENDTO 0x00040000UL
+#define UNIX_DGRAM_SOCKET__RECV_MSG 0x00080000UL
+#define UNIX_DGRAM_SOCKET__SEND_MSG 0x00100000UL
+#define UNIX_DGRAM_SOCKET__NAME_BIND 0x00200000UL
+
+#define PROCESS__FORK 0x00000001UL
+#define PROCESS__TRANSITION 0x00000002UL
+#define PROCESS__SIGCHLD 0x00000004UL
+#define PROCESS__SIGKILL 0x00000008UL
+#define PROCESS__SIGSTOP 0x00000010UL
+#define PROCESS__SIGNULL 0x00000020UL
+#define PROCESS__SIGNAL 0x00000040UL
+#define PROCESS__PTRACE 0x00000080UL
+#define PROCESS__GETSCHED 0x00000100UL
+#define PROCESS__SETSCHED 0x00000200UL
+#define PROCESS__GETSESSION 0x00000400UL
+#define PROCESS__GETPGID 0x00000800UL
+#define PROCESS__SETPGID 0x00001000UL
+#define PROCESS__GETCAP 0x00002000UL
+#define PROCESS__SETCAP 0x00004000UL
+#define PROCESS__SHARE 0x00008000UL
+#define PROCESS__GETATTR 0x00010000UL
+#define PROCESS__SETEXEC 0x00020000UL
+#define PROCESS__SETFSCREATE 0x00040000UL
+#define PROCESS__NOATSECURE 0x00080000UL
+#define PROCESS__SIGINH 0x00100000UL
+#define PROCESS__SETRLIMIT 0x00200000UL
+#define PROCESS__RLIMITINH 0x00400000UL
+#define PROCESS__DYNTRANSITION 0x00800000UL
+#define PROCESS__SETCURRENT 0x01000000UL
+#define PROCESS__EXECMEM 0x02000000UL
+
+#define IPC__CREATE 0x00000001UL
+#define IPC__DESTROY 0x00000002UL
+#define IPC__GETATTR 0x00000004UL
+#define IPC__SETATTR 0x00000008UL
+#define IPC__READ 0x00000010UL
+#define IPC__WRITE 0x00000020UL
+#define IPC__ASSOCIATE 0x00000040UL
+#define IPC__UNIX_READ 0x00000080UL
+#define IPC__UNIX_WRITE 0x00000100UL
+
+#define SEM__CREATE 0x00000001UL
+#define SEM__DESTROY 0x00000002UL
+#define SEM__GETATTR 0x00000004UL
+#define SEM__SETATTR 0x00000008UL
+#define SEM__READ 0x00000010UL
+#define SEM__WRITE 0x00000020UL
+#define SEM__ASSOCIATE 0x00000040UL
+#define SEM__UNIX_READ 0x00000080UL
+#define SEM__UNIX_WRITE 0x00000100UL
+
+#define MSGQ__CREATE 0x00000001UL
+#define MSGQ__DESTROY 0x00000002UL
+#define MSGQ__GETATTR 0x00000004UL
+#define MSGQ__SETATTR 0x00000008UL
+#define MSGQ__READ 0x00000010UL
+#define MSGQ__WRITE 0x00000020UL
+#define MSGQ__ASSOCIATE 0x00000040UL
+#define MSGQ__UNIX_READ 0x00000080UL
+#define MSGQ__UNIX_WRITE 0x00000100UL
+
+#define MSGQ__ENQUEUE 0x00000200UL
+
+#define MSG__SEND 0x00000001UL
+#define MSG__RECEIVE 0x00000002UL
+
+#define SHM__CREATE 0x00000001UL
+#define SHM__DESTROY 0x00000002UL
+#define SHM__GETATTR 0x00000004UL
+#define SHM__SETATTR 0x00000008UL
+#define SHM__READ 0x00000010UL
+#define SHM__WRITE 0x00000020UL
+#define SHM__ASSOCIATE 0x00000040UL
+#define SHM__UNIX_READ 0x00000080UL
+#define SHM__UNIX_WRITE 0x00000100UL
+
+#define SHM__LOCK 0x00000200UL
+
+#define SECURITY__COMPUTE_AV 0x00000001UL
+#define SECURITY__COMPUTE_CREATE 0x00000002UL
+#define SECURITY__COMPUTE_MEMBER 0x00000004UL
+#define SECURITY__CHECK_CONTEXT 0x00000008UL
+#define SECURITY__LOAD_POLICY 0x00000010UL
+#define SECURITY__COMPUTE_RELABEL 0x00000020UL
+#define SECURITY__COMPUTE_USER 0x00000040UL
+#define SECURITY__SETENFORCE 0x00000080UL
+#define SECURITY__SETBOOL 0x00000100UL
+#define SECURITY__SETSECPARAM 0x00000200UL
+#define SECURITY__SETCHECKREQPROT 0x00000400UL
+
+#define SYSTEM__IPC_INFO 0x00000001UL
+#define SYSTEM__SYSLOG_READ 0x00000002UL
+#define SYSTEM__SYSLOG_MOD 0x00000004UL
+#define SYSTEM__SYSLOG_CONSOLE 0x00000008UL
+
+#define CAPABILITY__CHOWN 0x00000001UL
+#define CAPABILITY__DAC_OVERRIDE 0x00000002UL
+#define CAPABILITY__DAC_READ_SEARCH 0x00000004UL
+#define CAPABILITY__FOWNER 0x00000008UL
+#define CAPABILITY__FSETID 0x00000010UL
+#define CAPABILITY__KILL 0x00000020UL
+#define CAPABILITY__SETGID 0x00000040UL
+#define CAPABILITY__SETUID 0x00000080UL
+#define CAPABILITY__SETPCAP 0x00000100UL
+#define CAPABILITY__LINUX_IMMUTABLE 0x00000200UL
+#define CAPABILITY__NET_BIND_SERVICE 0x00000400UL
+#define CAPABILITY__NET_BROADCAST 0x00000800UL
+#define CAPABILITY__NET_ADMIN 0x00001000UL
+#define CAPABILITY__NET_RAW 0x00002000UL
+#define CAPABILITY__IPC_LOCK 0x00004000UL
+#define CAPABILITY__IPC_OWNER 0x00008000UL
+#define CAPABILITY__SYS_MODULE 0x00010000UL
+#define CAPABILITY__SYS_RAWIO 0x00020000UL
+#define CAPABILITY__SYS_CHROOT 0x00040000UL
+#define CAPABILITY__SYS_PTRACE 0x00080000UL
+#define CAPABILITY__SYS_PACCT 0x00100000UL
+#define CAPABILITY__SYS_ADMIN 0x00200000UL
+#define CAPABILITY__SYS_BOOT 0x00400000UL
+#define CAPABILITY__SYS_NICE 0x00800000UL
+#define CAPABILITY__SYS_RESOURCE 0x01000000UL
+#define CAPABILITY__SYS_TIME 0x02000000UL
+#define CAPABILITY__SYS_TTY_CONFIG 0x04000000UL
+#define CAPABILITY__MKNOD 0x08000000UL
+#define CAPABILITY__LEASE 0x10000000UL
+
+#define PASSWD__PASSWD 0x00000001UL
+#define PASSWD__CHFN 0x00000002UL
+#define PASSWD__CHSH 0x00000004UL
+#define PASSWD__ROOTOK 0x00000008UL
+#define PASSWD__CRONTAB 0x00000010UL
+
+#define DRAWABLE__CREATE 0x00000001UL
+#define DRAWABLE__DESTROY 0x00000002UL
+#define DRAWABLE__DRAW 0x00000004UL
+#define DRAWABLE__COPY 0x00000008UL
+#define DRAWABLE__GETATTR 0x00000010UL
+
+#define GC__CREATE 0x00000001UL
+#define GC__FREE 0x00000002UL
+#define GC__GETATTR 0x00000004UL
+#define GC__SETATTR 0x00000008UL
+
+#define WINDOW__ADDCHILD 0x00000001UL
+#define WINDOW__CREATE 0x00000002UL
+#define WINDOW__DESTROY 0x00000004UL
+#define WINDOW__MAP 0x00000008UL
+#define WINDOW__UNMAP 0x00000010UL
+#define WINDOW__CHSTACK 0x00000020UL
+#define WINDOW__CHPROPLIST 0x00000040UL
+#define WINDOW__CHPROP 0x00000080UL
+#define WINDOW__LISTPROP 0x00000100UL
+#define WINDOW__GETATTR 0x00000200UL
+#define WINDOW__SETATTR 0x00000400UL
+#define WINDOW__SETFOCUS 0x00000800UL
+#define WINDOW__MOVE 0x00001000UL
+#define WINDOW__CHSELECTION 0x00002000UL
+#define WINDOW__CHPARENT 0x00004000UL
+#define WINDOW__CTRLLIFE 0x00008000UL
+#define WINDOW__ENUMERATE 0x00010000UL
+#define WINDOW__TRANSPARENT 0x00020000UL
+#define WINDOW__MOUSEMOTION 0x00040000UL
+#define WINDOW__CLIENTCOMEVENT 0x00080000UL
+#define WINDOW__INPUTEVENT 0x00100000UL
+#define WINDOW__DRAWEVENT 0x00200000UL
+#define WINDOW__WINDOWCHANGEEVENT 0x00400000UL
+#define WINDOW__WINDOWCHANGEREQUEST 0x00800000UL
+#define WINDOW__SERVERCHANGEEVENT 0x01000000UL
+#define WINDOW__EXTENSIONEVENT 0x02000000UL
+
+#define FONT__LOAD 0x00000001UL
+#define FONT__FREE 0x00000002UL
+#define FONT__GETATTR 0x00000004UL
+#define FONT__USE 0x00000008UL
+
+#define COLORMAP__CREATE 0x00000001UL
+#define COLORMAP__FREE 0x00000002UL
+#define COLORMAP__INSTALL 0x00000004UL
+#define COLORMAP__UNINSTALL 0x00000008UL
+#define COLORMAP__LIST 0x00000010UL
+#define COLORMAP__READ 0x00000020UL
+#define COLORMAP__STORE 0x00000040UL
+#define COLORMAP__GETATTR 0x00000080UL
+#define COLORMAP__SETATTR 0x00000100UL
+
+#define PROPERTY__CREATE 0x00000001UL
+#define PROPERTY__FREE 0x00000002UL
+#define PROPERTY__READ 0x00000004UL
+#define PROPERTY__WRITE 0x00000008UL
+
+#define CURSOR__CREATE 0x00000001UL
+#define CURSOR__CREATEGLYPH 0x00000002UL
+#define CURSOR__FREE 0x00000004UL
+#define CURSOR__ASSIGN 0x00000008UL
+#define CURSOR__SETATTR 0x00000010UL
+
+#define XCLIENT__KILL 0x00000001UL
+
+#define XINPUT__LOOKUP 0x00000001UL
+#define XINPUT__GETATTR 0x00000002UL
+#define XINPUT__SETATTR 0x00000004UL
+#define XINPUT__SETFOCUS 0x00000008UL
+#define XINPUT__WARPPOINTER 0x00000010UL
+#define XINPUT__ACTIVEGRAB 0x00000020UL
+#define XINPUT__PASSIVEGRAB 0x00000040UL
+#define XINPUT__UNGRAB 0x00000080UL
+#define XINPUT__BELL 0x00000100UL
+#define XINPUT__MOUSEMOTION 0x00000200UL
+#define XINPUT__RELABELINPUT 0x00000400UL
+
+#define XSERVER__SCREENSAVER 0x00000001UL
+#define XSERVER__GETHOSTLIST 0x00000002UL
+#define XSERVER__SETHOSTLIST 0x00000004UL
+#define XSERVER__GETFONTPATH 0x00000008UL
+#define XSERVER__SETFONTPATH 0x00000010UL
+#define XSERVER__GETATTR 0x00000020UL
+#define XSERVER__GRAB 0x00000040UL
+#define XSERVER__UNGRAB 0x00000080UL
+
+#define XEXTENSION__QUERY 0x00000001UL
+#define XEXTENSION__USE 0x00000002UL
+
+#define PAX__PAGEEXEC 0x00000001UL
+#define PAX__EMUTRAMP 0x00000002UL
+#define PAX__MPROTECT 0x00000004UL
+#define PAX__RANDMMAP 0x00000008UL
+#define PAX__RANDEXEC 0x00000010UL
+#define PAX__SEGMEXEC 0x00000020UL
+
+#define NETLINK_ROUTE_SOCKET__IOCTL 0x00000001UL
+#define NETLINK_ROUTE_SOCKET__READ 0x00000002UL
+#define NETLINK_ROUTE_SOCKET__WRITE 0x00000004UL
+#define NETLINK_ROUTE_SOCKET__CREATE 0x00000008UL
+#define NETLINK_ROUTE_SOCKET__GETATTR 0x00000010UL
+#define NETLINK_ROUTE_SOCKET__SETATTR 0x00000020UL
+#define NETLINK_ROUTE_SOCKET__LOCK 0x00000040UL
+#define NETLINK_ROUTE_SOCKET__RELABELFROM 0x00000080UL
+#define NETLINK_ROUTE_SOCKET__RELABELTO 0x00000100UL
+#define NETLINK_ROUTE_SOCKET__APPEND 0x00000200UL
+#define NETLINK_ROUTE_SOCKET__BIND 0x00000400UL
+#define NETLINK_ROUTE_SOCKET__CONNECT 0x00000800UL
+#define NETLINK_ROUTE_SOCKET__LISTEN 0x00001000UL
+#define NETLINK_ROUTE_SOCKET__ACCEPT 0x00002000UL
+#define NETLINK_ROUTE_SOCKET__GETOPT 0x00004000UL
+#define NETLINK_ROUTE_SOCKET__SETOPT 0x00008000UL
+#define NETLINK_ROUTE_SOCKET__SHUTDOWN 0x00010000UL
+#define NETLINK_ROUTE_SOCKET__RECVFROM 0x00020000UL
+#define NETLINK_ROUTE_SOCKET__SENDTO 0x00040000UL
+#define NETLINK_ROUTE_SOCKET__RECV_MSG 0x00080000UL
+#define NETLINK_ROUTE_SOCKET__SEND_MSG 0x00100000UL
+#define NETLINK_ROUTE_SOCKET__NAME_BIND 0x00200000UL
+
+#define NETLINK_ROUTE_SOCKET__NLMSG_READ 0x00400000UL
+#define NETLINK_ROUTE_SOCKET__NLMSG_WRITE 0x00800000UL
+
+#define NETLINK_FIREWALL_SOCKET__IOCTL 0x00000001UL
+#define NETLINK_FIREWALL_SOCKET__READ 0x00000002UL
+#define NETLINK_FIREWALL_SOCKET__WRITE 0x00000004UL
+#define NETLINK_FIREWALL_SOCKET__CREATE 0x00000008UL
+#define NETLINK_FIREWALL_SOCKET__GETATTR 0x00000010UL
+#define NETLINK_FIREWALL_SOCKET__SETATTR 0x00000020UL
+#define NETLINK_FIREWALL_SOCKET__LOCK 0x00000040UL
+#define NETLINK_FIREWALL_SOCKET__RELABELFROM 0x00000080UL
+#define NETLINK_FIREWALL_SOCKET__RELABELTO 0x00000100UL
+#define NETLINK_FIREWALL_SOCKET__APPEND 0x00000200UL
+#define NETLINK_FIREWALL_SOCKET__BIND 0x00000400UL
+#define NETLINK_FIREWALL_SOCKET__CONNECT 0x00000800UL
+#define NETLINK_FIREWALL_SOCKET__LISTEN 0x00001000UL
+#define NETLINK_FIREWALL_SOCKET__ACCEPT 0x00002000UL
+#define NETLINK_FIREWALL_SOCKET__GETOPT 0x00004000UL
+#define NETLINK_FIREWALL_SOCKET__SETOPT 0x00008000UL
+#define NETLINK_FIREWALL_SOCKET__SHUTDOWN 0x00010000UL
+#define NETLINK_FIREWALL_SOCKET__RECVFROM 0x00020000UL
+#define NETLINK_FIREWALL_SOCKET__SENDTO 0x00040000UL
+#define NETLINK_FIREWALL_SOCKET__RECV_MSG 0x00080000UL
+#define NETLINK_FIREWALL_SOCKET__SEND_MSG 0x00100000UL
+#define NETLINK_FIREWALL_SOCKET__NAME_BIND 0x00200000UL
+
+#define NETLINK_FIREWALL_SOCKET__NLMSG_READ 0x00400000UL
+#define NETLINK_FIREWALL_SOCKET__NLMSG_WRITE 0x00800000UL
+
+#define NETLINK_TCPDIAG_SOCKET__IOCTL 0x00000001UL
+#define NETLINK_TCPDIAG_SOCKET__READ 0x00000002UL
+#define NETLINK_TCPDIAG_SOCKET__WRITE 0x00000004UL
+#define NETLINK_TCPDIAG_SOCKET__CREATE 0x00000008UL
+#define NETLINK_TCPDIAG_SOCKET__GETATTR 0x00000010UL
+#define NETLINK_TCPDIAG_SOCKET__SETATTR 0x00000020UL
+#define NETLINK_TCPDIAG_SOCKET__LOCK 0x00000040UL
+#define NETLINK_TCPDIAG_SOCKET__RELABELFROM 0x00000080UL
+#define NETLINK_TCPDIAG_SOCKET__RELABELTO 0x00000100UL
+#define NETLINK_TCPDIAG_SOCKET__APPEND 0x00000200UL
+#define NETLINK_TCPDIAG_SOCKET__BIND 0x00000400UL
+#define NETLINK_TCPDIAG_SOCKET__CONNECT 0x00000800UL
+#define NETLINK_TCPDIAG_SOCKET__LISTEN 0x00001000UL
+#define NETLINK_TCPDIAG_SOCKET__ACCEPT 0x00002000UL
+#define NETLINK_TCPDIAG_SOCKET__GETOPT 0x00004000UL
+#define NETLINK_TCPDIAG_SOCKET__SETOPT 0x00008000UL
+#define NETLINK_TCPDIAG_SOCKET__SHUTDOWN 0x00010000UL
+#define NETLINK_TCPDIAG_SOCKET__RECVFROM 0x00020000UL
+#define NETLINK_TCPDIAG_SOCKET__SENDTO 0x00040000UL
+#define NETLINK_TCPDIAG_SOCKET__RECV_MSG 0x00080000UL
+#define NETLINK_TCPDIAG_SOCKET__SEND_MSG 0x00100000UL
+#define NETLINK_TCPDIAG_SOCKET__NAME_BIND 0x00200000UL
+
+#define NETLINK_TCPDIAG_SOCKET__NLMSG_READ 0x00400000UL
+#define NETLINK_TCPDIAG_SOCKET__NLMSG_WRITE 0x00800000UL
+
+#define NETLINK_NFLOG_SOCKET__IOCTL 0x00000001UL
+#define NETLINK_NFLOG_SOCKET__READ 0x00000002UL
+#define NETLINK_NFLOG_SOCKET__WRITE 0x00000004UL
+#define NETLINK_NFLOG_SOCKET__CREATE 0x00000008UL
+#define NETLINK_NFLOG_SOCKET__GETATTR 0x00000010UL
+#define NETLINK_NFLOG_SOCKET__SETATTR 0x00000020UL
+#define NETLINK_NFLOG_SOCKET__LOCK 0x00000040UL
+#define NETLINK_NFLOG_SOCKET__RELABELFROM 0x00000080UL
+#define NETLINK_NFLOG_SOCKET__RELABELTO 0x00000100UL
+#define NETLINK_NFLOG_SOCKET__APPEND 0x00000200UL
+#define NETLINK_NFLOG_SOCKET__BIND 0x00000400UL
+#define NETLINK_NFLOG_SOCKET__CONNECT 0x00000800UL
+#define NETLINK_NFLOG_SOCKET__LISTEN 0x00001000UL
+#define NETLINK_NFLOG_SOCKET__ACCEPT 0x00002000UL
+#define NETLINK_NFLOG_SOCKET__GETOPT 0x00004000UL
+#define NETLINK_NFLOG_SOCKET__SETOPT 0x00008000UL
+#define NETLINK_NFLOG_SOCKET__SHUTDOWN 0x00010000UL
+#define NETLINK_NFLOG_SOCKET__RECVFROM 0x00020000UL
+#define NETLINK_NFLOG_SOCKET__SENDTO 0x00040000UL
+#define NETLINK_NFLOG_SOCKET__RECV_MSG 0x00080000UL
+#define NETLINK_NFLOG_SOCKET__SEND_MSG 0x00100000UL
+#define NETLINK_NFLOG_SOCKET__NAME_BIND 0x00200000UL
+
+#define NETLINK_XFRM_SOCKET__IOCTL 0x00000001UL
+#define NETLINK_XFRM_SOCKET__READ 0x00000002UL
+#define NETLINK_XFRM_SOCKET__WRITE 0x00000004UL
+#define NETLINK_XFRM_SOCKET__CREATE 0x00000008UL
+#define NETLINK_XFRM_SOCKET__GETATTR 0x00000010UL
+#define NETLINK_XFRM_SOCKET__SETATTR 0x00000020UL
+#define NETLINK_XFRM_SOCKET__LOCK 0x00000040UL
+#define NETLINK_XFRM_SOCKET__RELABELFROM 0x00000080UL
+#define NETLINK_XFRM_SOCKET__RELABELTO 0x00000100UL
+#define NETLINK_XFRM_SOCKET__APPEND 0x00000200UL
+#define NETLINK_XFRM_SOCKET__BIND 0x00000400UL
+#define NETLINK_XFRM_SOCKET__CONNECT 0x00000800UL
+#define NETLINK_XFRM_SOCKET__LISTEN 0x00001000UL
+#define NETLINK_XFRM_SOCKET__ACCEPT 0x00002000UL
+#define NETLINK_XFRM_SOCKET__GETOPT 0x00004000UL
+#define NETLINK_XFRM_SOCKET__SETOPT 0x00008000UL
+#define NETLINK_XFRM_SOCKET__SHUTDOWN 0x00010000UL
+#define NETLINK_XFRM_SOCKET__RECVFROM 0x00020000UL
+#define NETLINK_XFRM_SOCKET__SENDTO 0x00040000UL
+#define NETLINK_XFRM_SOCKET__RECV_MSG 0x00080000UL
+#define NETLINK_XFRM_SOCKET__SEND_MSG 0x00100000UL
+#define NETLINK_XFRM_SOCKET__NAME_BIND 0x00200000UL
+
+#define NETLINK_XFRM_SOCKET__NLMSG_READ 0x00400000UL
+#define NETLINK_XFRM_SOCKET__NLMSG_WRITE 0x00800000UL
+
+#define NETLINK_SELINUX_SOCKET__IOCTL 0x00000001UL
+#define NETLINK_SELINUX_SOCKET__READ 0x00000002UL
+#define NETLINK_SELINUX_SOCKET__WRITE 0x00000004UL
+#define NETLINK_SELINUX_SOCKET__CREATE 0x00000008UL
+#define NETLINK_SELINUX_SOCKET__GETATTR 0x00000010UL
+#define NETLINK_SELINUX_SOCKET__SETATTR 0x00000020UL
+#define NETLINK_SELINUX_SOCKET__LOCK 0x00000040UL
+#define NETLINK_SELINUX_SOCKET__RELABELFROM 0x00000080UL
+#define NETLINK_SELINUX_SOCKET__RELABELTO 0x00000100UL
+#define NETLINK_SELINUX_SOCKET__APPEND 0x00000200UL
+#define NETLINK_SELINUX_SOCKET__BIND 0x00000400UL
+#define NETLINK_SELINUX_SOCKET__CONNECT 0x00000800UL
+#define NETLINK_SELINUX_SOCKET__LISTEN 0x00001000UL
+#define NETLINK_SELINUX_SOCKET__ACCEPT 0x00002000UL
+#define NETLINK_SELINUX_SOCKET__GETOPT 0x00004000UL
+#define NETLINK_SELINUX_SOCKET__SETOPT 0x00008000UL
+#define NETLINK_SELINUX_SOCKET__SHUTDOWN 0x00010000UL
+#define NETLINK_SELINUX_SOCKET__RECVFROM 0x00020000UL
+#define NETLINK_SELINUX_SOCKET__SENDTO 0x00040000UL
+#define NETLINK_SELINUX_SOCKET__RECV_MSG 0x00080000UL
+#define NETLINK_SELINUX_SOCKET__SEND_MSG 0x00100000UL
+#define NETLINK_SELINUX_SOCKET__NAME_BIND 0x00200000UL
+
+#define NETLINK_AUDIT_SOCKET__IOCTL 0x00000001UL
+#define NETLINK_AUDIT_SOCKET__READ 0x00000002UL
+#define NETLINK_AUDIT_SOCKET__WRITE 0x00000004UL
+#define NETLINK_AUDIT_SOCKET__CREATE 0x00000008UL
+#define NETLINK_AUDIT_SOCKET__GETATTR 0x00000010UL
+#define NETLINK_AUDIT_SOCKET__SETATTR 0x00000020UL
+#define NETLINK_AUDIT_SOCKET__LOCK 0x00000040UL
+#define NETLINK_AUDIT_SOCKET__RELABELFROM 0x00000080UL
+#define NETLINK_AUDIT_SOCKET__RELABELTO 0x00000100UL
+#define NETLINK_AUDIT_SOCKET__APPEND 0x00000200UL
+#define NETLINK_AUDIT_SOCKET__BIND 0x00000400UL
+#define NETLINK_AUDIT_SOCKET__CONNECT 0x00000800UL
+#define NETLINK_AUDIT_SOCKET__LISTEN 0x00001000UL
+#define NETLINK_AUDIT_SOCKET__ACCEPT 0x00002000UL
+#define NETLINK_AUDIT_SOCKET__GETOPT 0x00004000UL
+#define NETLINK_AUDIT_SOCKET__SETOPT 0x00008000UL
+#define NETLINK_AUDIT_SOCKET__SHUTDOWN 0x00010000UL
+#define NETLINK_AUDIT_SOCKET__RECVFROM 0x00020000UL
+#define NETLINK_AUDIT_SOCKET__SENDTO 0x00040000UL
+#define NETLINK_AUDIT_SOCKET__RECV_MSG 0x00080000UL
+#define NETLINK_AUDIT_SOCKET__SEND_MSG 0x00100000UL
+#define NETLINK_AUDIT_SOCKET__NAME_BIND 0x00200000UL
+
+#define NETLINK_AUDIT_SOCKET__NLMSG_READ 0x00400000UL
+#define NETLINK_AUDIT_SOCKET__NLMSG_WRITE 0x00800000UL
+
+#define NETLINK_IP6FW_SOCKET__IOCTL 0x00000001UL
+#define NETLINK_IP6FW_SOCKET__READ 0x00000002UL
+#define NETLINK_IP6FW_SOCKET__WRITE 0x00000004UL
+#define NETLINK_IP6FW_SOCKET__CREATE 0x00000008UL
+#define NETLINK_IP6FW_SOCKET__GETATTR 0x00000010UL
+#define NETLINK_IP6FW_SOCKET__SETATTR 0x00000020UL
+#define NETLINK_IP6FW_SOCKET__LOCK 0x00000040UL
+#define NETLINK_IP6FW_SOCKET__RELABELFROM 0x00000080UL
+#define NETLINK_IP6FW_SOCKET__RELABELTO 0x00000100UL
+#define NETLINK_IP6FW_SOCKET__APPEND 0x00000200UL
+#define NETLINK_IP6FW_SOCKET__BIND 0x00000400UL
+#define NETLINK_IP6FW_SOCKET__CONNECT 0x00000800UL
+#define NETLINK_IP6FW_SOCKET__LISTEN 0x00001000UL
+#define NETLINK_IP6FW_SOCKET__ACCEPT 0x00002000UL
+#define NETLINK_IP6FW_SOCKET__GETOPT 0x00004000UL
+#define NETLINK_IP6FW_SOCKET__SETOPT 0x00008000UL
+#define NETLINK_IP6FW_SOCKET__SHUTDOWN 0x00010000UL
+#define NETLINK_IP6FW_SOCKET__RECVFROM 0x00020000UL
+#define NETLINK_IP6FW_SOCKET__SENDTO 0x00040000UL
+#define NETLINK_IP6FW_SOCKET__RECV_MSG 0x00080000UL
+#define NETLINK_IP6FW_SOCKET__SEND_MSG 0x00100000UL
+#define NETLINK_IP6FW_SOCKET__NAME_BIND 0x00200000UL
+
+#define NETLINK_IP6FW_SOCKET__NLMSG_READ 0x00400000UL
+#define NETLINK_IP6FW_SOCKET__NLMSG_WRITE 0x00800000UL
+
+#define NETLINK_DNRT_SOCKET__IOCTL 0x00000001UL
+#define NETLINK_DNRT_SOCKET__READ 0x00000002UL
+#define NETLINK_DNRT_SOCKET__WRITE 0x00000004UL
+#define NETLINK_DNRT_SOCKET__CREATE 0x00000008UL
+#define NETLINK_DNRT_SOCKET__GETATTR 0x00000010UL
+#define NETLINK_DNRT_SOCKET__SETATTR 0x00000020UL
+#define NETLINK_DNRT_SOCKET__LOCK 0x00000040UL
+#define NETLINK_DNRT_SOCKET__RELABELFROM 0x00000080UL
+#define NETLINK_DNRT_SOCKET__RELABELTO 0x00000100UL
+#define NETLINK_DNRT_SOCKET__APPEND 0x00000200UL
+#define NETLINK_DNRT_SOCKET__BIND 0x00000400UL
+#define NETLINK_DNRT_SOCKET__CONNECT 0x00000800UL
+#define NETLINK_DNRT_SOCKET__LISTEN 0x00001000UL
+#define NETLINK_DNRT_SOCKET__ACCEPT 0x00002000UL
+#define NETLINK_DNRT_SOCKET__GETOPT 0x00004000UL
+#define NETLINK_DNRT_SOCKET__SETOPT 0x00008000UL
+#define NETLINK_DNRT_SOCKET__SHUTDOWN 0x00010000UL
+#define NETLINK_DNRT_SOCKET__RECVFROM 0x00020000UL
+#define NETLINK_DNRT_SOCKET__SENDTO 0x00040000UL
+#define NETLINK_DNRT_SOCKET__RECV_MSG 0x00080000UL
+#define NETLINK_DNRT_SOCKET__SEND_MSG 0x00100000UL
+#define NETLINK_DNRT_SOCKET__NAME_BIND 0x00200000UL
+
+#define DBUS__ACQUIRE_SVC 0x00000001UL
+#define DBUS__SEND_MSG 0x00000002UL
+
+#define NSCD__GETPWD 0x00000001UL
+#define NSCD__GETGRP 0x00000002UL
+#define NSCD__GETHOST 0x00000004UL
+#define NSCD__GETSTAT 0x00000008UL
+#define NSCD__ADMIN 0x00000010UL
+#define NSCD__SHMEMPWD 0x00000020UL
+#define NSCD__SHMEMGRP 0x00000040UL
+#define NSCD__SHMEMHOST 0x00000080UL
+
diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h
new file mode 100644
index 00000000000..960ef18ddc4
--- /dev/null
+++ b/security/selinux/include/avc.h
@@ -0,0 +1,137 @@
+/*
+ * Access vector cache interface for object managers.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#ifndef _SELINUX_AVC_H_
+#define _SELINUX_AVC_H_
+
+#include <linux/stddef.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/kdev_t.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/in6.h>
+#include <asm/system.h>
+#include "flask.h"
+#include "av_permissions.h"
+#include "security.h"
+
+#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
+extern int selinux_enforcing;
+#else
+#define selinux_enforcing 1
+#endif
+
+/*
+ * An entry in the AVC.
+ */
+struct avc_entry;
+
+struct task_struct;
+struct vfsmount;
+struct dentry;
+struct inode;
+struct sock;
+struct sk_buff;
+
+/* Auxiliary data to use in generating the audit record. */
+struct avc_audit_data {
+ char type;
+#define AVC_AUDIT_DATA_FS 1
+#define AVC_AUDIT_DATA_NET 2
+#define AVC_AUDIT_DATA_CAP 3
+#define AVC_AUDIT_DATA_IPC 4
+ struct task_struct *tsk;
+ union {
+ struct {
+ struct vfsmount *mnt;
+ struct dentry *dentry;
+ struct inode *inode;
+ } fs;
+ struct {
+ char *netif;
+ struct sock *sk;
+ u16 family;
+ u16 dport;
+ u16 sport;
+ union {
+ struct {
+ u32 daddr;
+ u32 saddr;
+ } v4;
+ struct {
+ struct in6_addr daddr;
+ struct in6_addr saddr;
+ } v6;
+ } fam;
+ } net;
+ int cap;
+ int ipc_id;
+ } u;
+};
+
+#define v4info fam.v4
+#define v6info fam.v6
+
+/* Initialize an AVC audit data structure. */
+#define AVC_AUDIT_DATA_INIT(_d,_t) \
+ { memset((_d), 0, sizeof(struct avc_audit_data)); (_d)->type = AVC_AUDIT_DATA_##_t; }
+
+/*
+ * AVC statistics
+ */
+struct avc_cache_stats
+{
+ unsigned int lookups;
+ unsigned int hits;
+ unsigned int misses;
+ unsigned int allocations;
+ unsigned int reclaims;
+ unsigned int frees;
+};
+
+/*
+ * AVC operations
+ */
+
+void __init avc_init(void);
+
+void avc_audit(u32 ssid, u32 tsid,
+ u16 tclass, u32 requested,
+ struct av_decision *avd, int result, struct avc_audit_data *auditdata);
+
+int avc_has_perm_noaudit(u32 ssid, u32 tsid,
+ u16 tclass, u32 requested,
+ struct av_decision *avd);
+
+int avc_has_perm(u32 ssid, u32 tsid,
+ u16 tclass, u32 requested,
+ struct avc_audit_data *auditdata);
+
+#define AVC_CALLBACK_GRANT 1
+#define AVC_CALLBACK_TRY_REVOKE 2
+#define AVC_CALLBACK_REVOKE 4
+#define AVC_CALLBACK_RESET 8
+#define AVC_CALLBACK_AUDITALLOW_ENABLE 16
+#define AVC_CALLBACK_AUDITALLOW_DISABLE 32
+#define AVC_CALLBACK_AUDITDENY_ENABLE 64
+#define AVC_CALLBACK_AUDITDENY_DISABLE 128
+
+int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid,
+ u16 tclass, u32 perms,
+ u32 *out_retained),
+ u32 events, u32 ssid, u32 tsid,
+ u16 tclass, u32 perms);
+
+/* Exported to selinuxfs */
+int avc_get_hash_stats(char *page);
+extern unsigned int avc_cache_threshold;
+
+#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
+DECLARE_PER_CPU(struct avc_cache_stats, avc_cache_stats);
+#endif
+
+#endif /* _SELINUX_AVC_H_ */
+
diff --git a/security/selinux/include/avc_ss.h b/security/selinux/include/avc_ss.h
new file mode 100644
index 00000000000..450a2831e2e
--- /dev/null
+++ b/security/selinux/include/avc_ss.h
@@ -0,0 +1,14 @@
+/*
+ * Access vector cache interface for the security server.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#ifndef _SELINUX_AVC_SS_H_
+#define _SELINUX_AVC_SS_H_
+
+#include "flask.h"
+
+int avc_ss_reset(u32 seqno);
+
+#endif /* _SELINUX_AVC_SS_H_ */
+
diff --git a/security/selinux/include/class_to_string.h b/security/selinux/include/class_to_string.h
new file mode 100644
index 00000000000..519a77d7394
--- /dev/null
+++ b/security/selinux/include/class_to_string.h
@@ -0,0 +1,58 @@
+/* This file is automatically generated. Do not edit. */
+/*
+ * Security object class definitions
+ */
+ S_("null")
+ S_("security")
+ S_("process")
+ S_("system")
+ S_("capability")
+ S_("filesystem")
+ S_("file")
+ S_("dir")
+ S_("fd")
+ S_("lnk_file")
+ S_("chr_file")
+ S_("blk_file")
+ S_("sock_file")
+ S_("fifo_file")
+ S_("socket")
+ S_("tcp_socket")
+ S_("udp_socket")
+ S_("rawip_socket")
+ S_("node")
+ S_("netif")
+ S_("netlink_socket")
+ S_("packet_socket")
+ S_("key_socket")
+ S_("unix_stream_socket")
+ S_("unix_dgram_socket")
+ S_("sem")
+ S_("msg")
+ S_("msgq")
+ S_("shm")
+ S_("ipc")
+ S_("passwd")
+ S_("drawable")
+ S_("window")
+ S_("gc")
+ S_("font")
+ S_("colormap")
+ S_("property")
+ S_("cursor")
+ S_("xclient")
+ S_("xinput")
+ S_("xserver")
+ S_("xextension")
+ S_("pax")
+ S_("netlink_route_socket")
+ S_("netlink_firewall_socket")
+ S_("netlink_tcpdiag_socket")
+ S_("netlink_nflog_socket")
+ S_("netlink_xfrm_socket")
+ S_("netlink_selinux_socket")
+ S_("netlink_audit_socket")
+ S_("netlink_ip6fw_socket")
+ S_("netlink_dnrt_socket")
+ S_("dbus")
+ S_("nscd")
diff --git a/security/selinux/include/common_perm_to_string.h b/security/selinux/include/common_perm_to_string.h
new file mode 100644
index 00000000000..ce5b6e2fe9d
--- /dev/null
+++ b/security/selinux/include/common_perm_to_string.h
@@ -0,0 +1,58 @@
+/* This file is automatically generated. Do not edit. */
+TB_(common_file_perm_to_string)
+ S_("ioctl")
+ S_("read")
+ S_("write")
+ S_("create")
+ S_("getattr")
+ S_("setattr")
+ S_("lock")
+ S_("relabelfrom")
+ S_("relabelto")
+ S_("append")
+ S_("unlink")
+ S_("link")
+ S_("rename")
+ S_("execute")
+ S_("swapon")
+ S_("quotaon")
+ S_("mounton")
+TE_(common_file_perm_to_string)
+
+TB_(common_socket_perm_to_string)
+ S_("ioctl")
+ S_("read")
+ S_("write")
+ S_("create")
+ S_("getattr")
+ S_("setattr")
+ S_("lock")
+ S_("relabelfrom")
+ S_("relabelto")
+ S_("append")
+ S_("bind")
+ S_("connect")
+ S_("listen")
+ S_("accept")
+ S_("getopt")
+ S_("setopt")
+ S_("shutdown")
+ S_("recvfrom")
+ S_("sendto")
+ S_("recv_msg")
+ S_("send_msg")
+ S_("name_bind")
+TE_(common_socket_perm_to_string)
+
+TB_(common_ipc_perm_to_string)
+ S_("create")
+ S_("destroy")
+ S_("getattr")
+ S_("setattr")
+ S_("read")
+ S_("write")
+ S_("associate")
+ S_("unix_read")
+ S_("unix_write")
+TE_(common_ipc_perm_to_string)
+
diff --git a/security/selinux/include/conditional.h b/security/selinux/include/conditional.h
new file mode 100644
index 00000000000..67ce7a8d830
--- /dev/null
+++ b/security/selinux/include/conditional.h
@@ -0,0 +1,22 @@
+/*
+ * Interface to booleans in the security server. This is exported
+ * for the selinuxfs.
+ *
+ * Author: Karl MacMillan <kmacmillan@tresys.com>
+ *
+ * Copyright (C) 2003 - 2004 Tresys Technology, LLC
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ */
+
+#ifndef _SELINUX_CONDITIONAL_H_
+#define _SELINUX_CONDITIONAL_H_
+
+int security_get_bools(int *len, char ***names, int **values);
+
+int security_set_bools(int len, int *values);
+
+int security_get_bool_value(int bool);
+
+#endif
diff --git a/security/selinux/include/flask.h b/security/selinux/include/flask.h
new file mode 100644
index 00000000000..4eef1b654e9
--- /dev/null
+++ b/security/selinux/include/flask.h
@@ -0,0 +1,95 @@
+/* This file is automatically generated. Do not edit. */
+#ifndef _SELINUX_FLASK_H_
+#define _SELINUX_FLASK_H_
+
+/*
+ * Security object class definitions
+ */
+#define SECCLASS_SECURITY 1
+#define SECCLASS_PROCESS 2
+#define SECCLASS_SYSTEM 3
+#define SECCLASS_CAPABILITY 4
+#define SECCLASS_FILESYSTEM 5
+#define SECCLASS_FILE 6
+#define SECCLASS_DIR 7
+#define SECCLASS_FD 8
+#define SECCLASS_LNK_FILE 9
+#define SECCLASS_CHR_FILE 10
+#define SECCLASS_BLK_FILE 11
+#define SECCLASS_SOCK_FILE 12
+#define SECCLASS_FIFO_FILE 13
+#define SECCLASS_SOCKET 14
+#define SECCLASS_TCP_SOCKET 15
+#define SECCLASS_UDP_SOCKET 16
+#define SECCLASS_RAWIP_SOCKET 17
+#define SECCLASS_NODE 18
+#define SECCLASS_NETIF 19
+#define SECCLASS_NETLINK_SOCKET 20
+#define SECCLASS_PACKET_SOCKET 21
+#define SECCLASS_KEY_SOCKET 22
+#define SECCLASS_UNIX_STREAM_SOCKET 23
+#define SECCLASS_UNIX_DGRAM_SOCKET 24
+#define SECCLASS_SEM 25
+#define SECCLASS_MSG 26
+#define SECCLASS_MSGQ 27
+#define SECCLASS_SHM 28
+#define SECCLASS_IPC 29
+#define SECCLASS_PASSWD 30
+#define SECCLASS_DRAWABLE 31
+#define SECCLASS_WINDOW 32
+#define SECCLASS_GC 33
+#define SECCLASS_FONT 34
+#define SECCLASS_COLORMAP 35
+#define SECCLASS_PROPERTY 36
+#define SECCLASS_CURSOR 37
+#define SECCLASS_XCLIENT 38
+#define SECCLASS_XINPUT 39
+#define SECCLASS_XSERVER 40
+#define SECCLASS_XEXTENSION 41
+#define SECCLASS_PAX 42
+#define SECCLASS_NETLINK_ROUTE_SOCKET 43
+#define SECCLASS_NETLINK_FIREWALL_SOCKET 44
+#define SECCLASS_NETLINK_TCPDIAG_SOCKET 45
+#define SECCLASS_NETLINK_NFLOG_SOCKET 46
+#define SECCLASS_NETLINK_XFRM_SOCKET 47
+#define SECCLASS_NETLINK_SELINUX_SOCKET 48
+#define SECCLASS_NETLINK_AUDIT_SOCKET 49
+#define SECCLASS_NETLINK_IP6FW_SOCKET 50
+#define SECCLASS_NETLINK_DNRT_SOCKET 51
+#define SECCLASS_DBUS 52
+#define SECCLASS_NSCD 53
+
+/*
+ * Security identifier indices for initial entities
+ */
+#define SECINITSID_KERNEL 1
+#define SECINITSID_SECURITY 2
+#define SECINITSID_UNLABELED 3
+#define SECINITSID_FS 4
+#define SECINITSID_FILE 5
+#define SECINITSID_FILE_LABELS 6
+#define SECINITSID_INIT 7
+#define SECINITSID_ANY_SOCKET 8
+#define SECINITSID_PORT 9
+#define SECINITSID_NETIF 10
+#define SECINITSID_NETMSG 11
+#define SECINITSID_NODE 12
+#define SECINITSID_IGMP_PACKET 13
+#define SECINITSID_ICMP_SOCKET 14
+#define SECINITSID_TCP_SOCKET 15
+#define SECINITSID_SYSCTL_MODPROBE 16
+#define SECINITSID_SYSCTL 17
+#define SECINITSID_SYSCTL_FS 18
+#define SECINITSID_SYSCTL_KERNEL 19
+#define SECINITSID_SYSCTL_NET 20
+#define SECINITSID_SYSCTL_NET_UNIX 21
+#define SECINITSID_SYSCTL_VM 22
+#define SECINITSID_SYSCTL_DEV 23
+#define SECINITSID_KMOD 24
+#define SECINITSID_POLICY 25
+#define SECINITSID_SCMP_PACKET 26
+#define SECINITSID_DEVNULL 27
+
+#define SECINITSID_NUM 27
+
+#endif
diff --git a/security/selinux/include/initial_sid_to_string.h b/security/selinux/include/initial_sid_to_string.h
new file mode 100644
index 00000000000..d4fac82793a
--- /dev/null
+++ b/security/selinux/include/initial_sid_to_string.h
@@ -0,0 +1,33 @@
+/* This file is automatically generated. Do not edit. */
+static char *initial_sid_to_string[] =
+{
+ "null",
+ "kernel",
+ "security",
+ "unlabeled",
+ "fs",
+ "file",
+ "file_labels",
+ "init",
+ "any_socket",
+ "port",
+ "netif",
+ "netmsg",
+ "node",
+ "igmp_packet",
+ "icmp_socket",
+ "tcp_socket",
+ "sysctl_modprobe",
+ "sysctl",
+ "sysctl_fs",
+ "sysctl_kernel",
+ "sysctl_net",
+ "sysctl_net_unix",
+ "sysctl_vm",
+ "sysctl_dev",
+ "kmod",
+ "policy",
+ "scmp_packet",
+ "devnull",
+};
+
diff --git a/security/selinux/include/netif.h b/security/selinux/include/netif.h
new file mode 100644
index 00000000000..8bd6f9992d2
--- /dev/null
+++ b/security/selinux/include/netif.h
@@ -0,0 +1,21 @@
+/*
+ * Network interface table.
+ *
+ * Network interfaces (devices) do not have a security field, so we
+ * maintain a table associating each interface with a SID.
+ *
+ * Author: James Morris <jmorris@redhat.com>
+ *
+ * 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 version 2,
+ * as published by the Free Software Foundation.
+ */
+#ifndef _SELINUX_NETIF_H_
+#define _SELINUX_NETIF_H_
+
+int sel_netif_sids(struct net_device *dev, u32 *if_sid, u32 *msg_sid);
+
+#endif /* _SELINUX_NETIF_H_ */
+
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
new file mode 100644
index 00000000000..887937c8134
--- /dev/null
+++ b/security/selinux/include/objsec.h
@@ -0,0 +1,112 @@
+/*
+ * NSA Security-Enhanced Linux (SELinux) security module
+ *
+ * This file contains the SELinux security data structures for kernel objects.
+ *
+ * Author(s): Stephen Smalley, <sds@epoch.ncsc.mil>
+ * Chris Vance, <cvance@nai.com>
+ * Wayne Salamon, <wsalamon@nai.com>
+ * James Morris <jmorris@redhat.com>
+ *
+ * Copyright (C) 2001,2002 Networks Associates Technology, Inc.
+ * 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 version 2,
+ * as published by the Free Software Foundation.
+ */
+#ifndef _SELINUX_OBJSEC_H_
+#define _SELINUX_OBJSEC_H_
+
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/binfmts.h>
+#include <linux/in.h>
+#include "flask.h"
+#include "avc.h"
+
+struct task_security_struct {
+ unsigned long magic; /* magic number for this module */
+ struct task_struct *task; /* back pointer to task object */
+ u32 osid; /* SID prior to last execve */
+ u32 sid; /* current SID */
+ u32 exec_sid; /* exec SID */
+ u32 create_sid; /* fscreate SID */
+ u32 ptrace_sid; /* SID of ptrace parent */
+};
+
+struct inode_security_struct {
+ unsigned long magic; /* magic number for this module */
+ struct inode *inode; /* back pointer to inode object */
+ struct list_head list; /* list of inode_security_struct */
+ u32 task_sid; /* SID of creating task */
+ u32 sid; /* SID of this object */
+ u16 sclass; /* security class of this object */
+ unsigned char initialized; /* initialization flag */
+ struct semaphore sem;
+ unsigned char inherit; /* inherit SID from parent entry */
+};
+
+struct file_security_struct {
+ unsigned long magic; /* magic number for this module */
+ struct file *file; /* back pointer to file object */
+ u32 sid; /* SID of open file description */
+ u32 fown_sid; /* SID of file owner (for SIGIO) */
+};
+
+struct superblock_security_struct {
+ unsigned long magic; /* magic number for this module */
+ struct super_block *sb; /* back pointer to sb object */
+ struct list_head list; /* list of superblock_security_struct */
+ u32 sid; /* SID of file system */
+ u32 def_sid; /* default SID for labeling */
+ unsigned int behavior; /* labeling behavior */
+ unsigned char initialized; /* initialization flag */
+ unsigned char proc; /* proc fs */
+ struct semaphore sem;
+ struct list_head isec_head;
+ spinlock_t isec_lock;
+};
+
+struct msg_security_struct {
+ unsigned long magic; /* magic number for this module */
+ struct msg_msg *msg; /* back pointer */
+ u32 sid; /* SID of message */
+};
+
+struct ipc_security_struct {
+ unsigned long magic; /* magic number for this module */
+ struct kern_ipc_perm *ipc_perm; /* back pointer */
+ u16 sclass; /* security class of this object */
+ u32 sid; /* SID of IPC resource */
+};
+
+struct bprm_security_struct {
+ unsigned long magic; /* magic number for this module */
+ struct linux_binprm *bprm; /* back pointer to bprm object */
+ u32 sid; /* SID for transformed process */
+ unsigned char set;
+
+ /*
+ * unsafe is used to share failure information from bprm_apply_creds()
+ * to bprm_post_apply_creds().
+ */
+ char unsafe;
+};
+
+struct netif_security_struct {
+ struct net_device *dev; /* back pointer */
+ u32 if_sid; /* SID for this interface */
+ u32 msg_sid; /* default SID for messages received on this interface */
+};
+
+struct sk_security_struct {
+ unsigned long magic; /* magic number for this module */
+ struct sock *sk; /* back pointer to sk object */
+ u32 peer_sid; /* SID of peer */
+};
+
+extern unsigned int selinux_checkreqprot;
+
+#endif /* _SELINUX_OBJSEC_H_ */
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
new file mode 100644
index 00000000000..fa187c9a351
--- /dev/null
+++ b/security/selinux/include/security.h
@@ -0,0 +1,97 @@
+/*
+ * Security server interface.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ *
+ */
+
+#ifndef _SELINUX_SECURITY_H_
+#define _SELINUX_SECURITY_H_
+
+#include "flask.h"
+
+#define SECSID_NULL 0x00000000 /* unspecified SID */
+#define SECSID_WILD 0xffffffff /* wildcard SID */
+#define SECCLASS_NULL 0x0000 /* no class */
+
+#define SELINUX_MAGIC 0xf97cff8c
+
+/* Identify specific policy version changes */
+#define POLICYDB_VERSION_BASE 15
+#define POLICYDB_VERSION_BOOL 16
+#define POLICYDB_VERSION_IPV6 17
+#define POLICYDB_VERSION_NLCLASS 18
+#define POLICYDB_VERSION_VALIDATETRANS 19
+#define POLICYDB_VERSION_MLS 19
+
+/* Range of policy versions we understand*/
+#define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE
+#define POLICYDB_VERSION_MAX POLICYDB_VERSION_MLS
+
+#ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM
+extern int selinux_enabled;
+#else
+#define selinux_enabled 1
+#endif
+
+extern int selinux_mls_enabled;
+
+int security_load_policy(void * data, size_t len);
+
+struct av_decision {
+ u32 allowed;
+ u32 decided;
+ u32 auditallow;
+ u32 auditdeny;
+ u32 seqno;
+};
+
+int security_compute_av(u32 ssid, u32 tsid,
+ u16 tclass, u32 requested,
+ struct av_decision *avd);
+
+int security_transition_sid(u32 ssid, u32 tsid,
+ u16 tclass, u32 *out_sid);
+
+int security_member_sid(u32 ssid, u32 tsid,
+ u16 tclass, u32 *out_sid);
+
+int security_change_sid(u32 ssid, u32 tsid,
+ u16 tclass, u32 *out_sid);
+
+int security_sid_to_context(u32 sid, char **scontext,
+ u32 *scontext_len);
+
+int security_context_to_sid(char *scontext, u32 scontext_len,
+ u32 *out_sid);
+
+int security_get_user_sids(u32 callsid, char *username,
+ u32 **sids, u32 *nel);
+
+int security_port_sid(u16 domain, u16 type, u8 protocol, u16 port,
+ u32 *out_sid);
+
+int security_netif_sid(char *name, u32 *if_sid,
+ u32 *msg_sid);
+
+int security_node_sid(u16 domain, void *addr, u32 addrlen,
+ u32 *out_sid);
+
+int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
+ u16 tclass);
+
+#define SECURITY_FS_USE_XATTR 1 /* use xattr */
+#define SECURITY_FS_USE_TRANS 2 /* use transition SIDs, e.g. devpts/tmpfs */
+#define SECURITY_FS_USE_TASK 3 /* use task SIDs, e.g. pipefs/sockfs */
+#define SECURITY_FS_USE_GENFS 4 /* use the genfs support */
+#define SECURITY_FS_USE_NONE 5 /* no labeling support */
+#define SECURITY_FS_USE_MNTPOINT 6 /* use mountpoint labeling */
+
+int security_fs_use(const char *fstype, unsigned int *behavior,
+ u32 *sid);
+
+int security_genfs_sid(const char *fstype, char *name, u16 sclass,
+ u32 *sid);
+
+#endif /* _SELINUX_SECURITY_H_ */
+
diff --git a/security/selinux/netif.c b/security/selinux/netif.c
new file mode 100644
index 00000000000..718d7be9f4d
--- /dev/null
+++ b/security/selinux/netif.c
@@ -0,0 +1,270 @@
+/*
+ * Network interface table.
+ *
+ * Network interfaces (devices) do not have a security field, so we
+ * maintain a table associating each interface with a SID.
+ *
+ * Author: James Morris <jmorris@redhat.com>
+ *
+ * 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 version 2,
+ * as published by the Free Software Foundation.
+ */
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/notifier.h>
+#include <linux/netdevice.h>
+#include <linux/rcupdate.h>
+
+#include "security.h"
+#include "objsec.h"
+#include "netif.h"
+
+#define SEL_NETIF_HASH_SIZE 64
+#define SEL_NETIF_HASH_MAX 1024
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+struct sel_netif
+{
+ struct list_head list;
+ struct netif_security_struct nsec;
+ struct rcu_head rcu_head;
+};
+
+static u32 sel_netif_total;
+static LIST_HEAD(sel_netif_list);
+static DEFINE_SPINLOCK(sel_netif_lock);
+static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE];
+
+static inline u32 sel_netif_hasfn(struct net_device *dev)
+{
+ return (dev->ifindex & (SEL_NETIF_HASH_SIZE - 1));
+}
+
+/*
+ * All of the devices should normally fit in the hash, so we optimize
+ * for that case.
+ */
+static inline struct sel_netif *sel_netif_find(struct net_device *dev)
+{
+ struct list_head *pos;
+ int idx = sel_netif_hasfn(dev);
+
+ __list_for_each_rcu(pos, &sel_netif_hash[idx]) {
+ struct sel_netif *netif = list_entry(pos,
+ struct sel_netif, list);
+ if (likely(netif->nsec.dev == dev))
+ return netif;
+ }
+ return NULL;
+}
+
+static int sel_netif_insert(struct sel_netif *netif)
+{
+ int idx, ret = 0;
+
+ if (sel_netif_total >= SEL_NETIF_HASH_MAX) {
+ ret = -ENOSPC;
+ goto out;
+ }
+
+ idx = sel_netif_hasfn(netif->nsec.dev);
+ list_add_rcu(&netif->list, &sel_netif_hash[idx]);
+ sel_netif_total++;
+out:
+ return ret;
+}
+
+static void sel_netif_free(struct rcu_head *p)
+{
+ struct sel_netif *netif = container_of(p, struct sel_netif, rcu_head);
+
+ DEBUGP("%s: %s\n", __FUNCTION__, netif->nsec.dev->name);
+ kfree(netif);
+}
+
+static void sel_netif_destroy(struct sel_netif *netif)
+{
+ DEBUGP("%s: %s\n", __FUNCTION__, netif->nsec.dev->name);
+
+ list_del_rcu(&netif->list);
+ sel_netif_total--;
+ call_rcu(&netif->rcu_head, sel_netif_free);
+}
+
+static struct sel_netif *sel_netif_lookup(struct net_device *dev)
+{
+ int ret;
+ struct sel_netif *netif, *new;
+ struct netif_security_struct *nsec;
+
+ netif = sel_netif_find(dev);
+ if (likely(netif != NULL))
+ goto out;
+
+ new = kmalloc(sizeof(*new), GFP_ATOMIC);
+ if (!new) {
+ netif = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+
+ memset(new, 0, sizeof(*new));
+ nsec = &new->nsec;
+
+ ret = security_netif_sid(dev->name, &nsec->if_sid, &nsec->msg_sid);
+ if (ret < 0) {
+ kfree(new);
+ netif = ERR_PTR(ret);
+ goto out;
+ }
+
+ nsec->dev = dev;
+
+ spin_lock_bh(&sel_netif_lock);
+
+ netif = sel_netif_find(dev);
+ if (netif) {
+ spin_unlock_bh(&sel_netif_lock);
+ kfree(new);
+ goto out;
+ }
+
+ ret = sel_netif_insert(new);
+ spin_unlock_bh(&sel_netif_lock);
+
+ if (ret) {
+ kfree(new);
+ netif = ERR_PTR(ret);
+ goto out;
+ }
+
+ netif = new;
+
+ DEBUGP("new: ifindex=%u name=%s if_sid=%u msg_sid=%u\n", dev->ifindex, dev->name,
+ nsec->if_sid, nsec->msg_sid);
+out:
+ return netif;
+}
+
+static void sel_netif_assign_sids(u32 if_sid_in, u32 msg_sid_in, u32 *if_sid_out, u32 *msg_sid_out)
+{
+ if (if_sid_out)
+ *if_sid_out = if_sid_in;
+ if (msg_sid_out)
+ *msg_sid_out = msg_sid_in;
+}
+
+static int sel_netif_sids_slow(struct net_device *dev, u32 *if_sid, u32 *msg_sid)
+{
+ int ret = 0;
+ u32 tmp_if_sid, tmp_msg_sid;
+
+ ret = security_netif_sid(dev->name, &tmp_if_sid, &tmp_msg_sid);
+ if (!ret)
+ sel_netif_assign_sids(tmp_if_sid, tmp_msg_sid, if_sid, msg_sid);
+ return ret;
+}
+
+int sel_netif_sids(struct net_device *dev, u32 *if_sid, u32 *msg_sid)
+{
+ int ret = 0;
+ struct sel_netif *netif;
+
+ rcu_read_lock();
+ netif = sel_netif_lookup(dev);
+ if (IS_ERR(netif)) {
+ rcu_read_unlock();
+ ret = sel_netif_sids_slow(dev, if_sid, msg_sid);
+ goto out;
+ }
+ sel_netif_assign_sids(netif->nsec.if_sid, netif->nsec.msg_sid, if_sid, msg_sid);
+ rcu_read_unlock();
+out:
+ return ret;
+}
+
+static void sel_netif_kill(struct net_device *dev)
+{
+ struct sel_netif *netif;
+
+ spin_lock_bh(&sel_netif_lock);
+ netif = sel_netif_find(dev);
+ if (netif)
+ sel_netif_destroy(netif);
+ spin_unlock_bh(&sel_netif_lock);
+}
+
+static void sel_netif_flush(void)
+{
+ int idx;
+
+ spin_lock_bh(&sel_netif_lock);
+ for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++) {
+ struct sel_netif *netif;
+
+ list_for_each_entry(netif, &sel_netif_hash[idx], list)
+ sel_netif_destroy(netif);
+ }
+ spin_unlock_bh(&sel_netif_lock);
+}
+
+static int sel_netif_avc_callback(u32 event, u32 ssid, u32 tsid,
+ u16 class, u32 perms, u32 *retained)
+{
+ if (event == AVC_CALLBACK_RESET) {
+ sel_netif_flush();
+ synchronize_net();
+ }
+ return 0;
+}
+
+static int sel_netif_netdev_notifier_handler(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = ptr;
+
+ if (event == NETDEV_DOWN)
+ sel_netif_kill(dev);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block sel_netif_netdev_notifier = {
+ .notifier_call = sel_netif_netdev_notifier_handler,
+};
+
+static __init int sel_netif_init(void)
+{
+ int i, err = 0;
+
+ if (!selinux_enabled)
+ goto out;
+
+ for (i = 0; i < SEL_NETIF_HASH_SIZE; i++)
+ INIT_LIST_HEAD(&sel_netif_hash[i]);
+
+ register_netdevice_notifier(&sel_netif_netdev_notifier);
+
+ err = avc_add_callback(sel_netif_avc_callback, AVC_CALLBACK_RESET,
+ SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
+ if (err)
+ panic("avc_add_callback() failed, error %d\n", err);
+
+out:
+ return err;
+}
+
+__initcall(sel_netif_init);
+
diff --git a/security/selinux/netlink.c b/security/selinux/netlink.c
new file mode 100644
index 00000000000..18d08acafa7
--- /dev/null
+++ b/security/selinux/netlink.c
@@ -0,0 +1,113 @@
+/*
+ * Netlink event notifications for SELinux.
+ *
+ * Author: James Morris <jmorris@redhat.com>
+ *
+ * Copyright (C) 2004 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 version 2,
+ * as published by the Free Software Foundation.
+ */
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/selinux_netlink.h>
+
+static struct sock *selnl;
+
+static int selnl_msglen(int msgtype)
+{
+ int ret = 0;
+
+ switch (msgtype) {
+ case SELNL_MSG_SETENFORCE:
+ ret = sizeof(struct selnl_msg_setenforce);
+ break;
+
+ case SELNL_MSG_POLICYLOAD:
+ ret = sizeof(struct selnl_msg_policyload);
+ break;
+
+ default:
+ BUG();
+ }
+ return ret;
+}
+
+static void selnl_add_payload(struct nlmsghdr *nlh, int len, int msgtype, void *data)
+{
+ switch (msgtype) {
+ case SELNL_MSG_SETENFORCE: {
+ struct selnl_msg_setenforce *msg = NLMSG_DATA(nlh);
+
+ memset(msg, 0, len);
+ msg->val = *((int *)data);
+ break;
+ }
+
+ case SELNL_MSG_POLICYLOAD: {
+ struct selnl_msg_policyload *msg = NLMSG_DATA(nlh);
+
+ memset(msg, 0, len);
+ msg->seqno = *((u32 *)data);
+ break;
+ }
+
+ default:
+ BUG();
+ }
+}
+
+static void selnl_notify(int msgtype, void *data)
+{
+ int len;
+ unsigned char *tmp;
+ struct sk_buff *skb;
+ struct nlmsghdr *nlh;
+
+ len = selnl_msglen(msgtype);
+
+ skb = alloc_skb(NLMSG_SPACE(len), GFP_USER);
+ if (!skb)
+ goto oom;
+
+ tmp = skb->tail;
+ nlh = NLMSG_PUT(skb, 0, 0, msgtype, len);
+ selnl_add_payload(nlh, len, msgtype, data);
+ nlh->nlmsg_len = skb->tail - tmp;
+ netlink_broadcast(selnl, skb, 0, SELNL_GRP_AVC, GFP_USER);
+out:
+ return;
+
+nlmsg_failure:
+ kfree_skb(skb);
+oom:
+ printk(KERN_ERR "SELinux: OOM in %s\n", __FUNCTION__);
+ goto out;
+}
+
+void selnl_notify_setenforce(int val)
+{
+ selnl_notify(SELNL_MSG_SETENFORCE, &val);
+}
+
+void selnl_notify_policyload(u32 seqno)
+{
+ selnl_notify(SELNL_MSG_POLICYLOAD, &seqno);
+}
+
+static int __init selnl_init(void)
+{
+ selnl = netlink_kernel_create(NETLINK_SELINUX, NULL);
+ if (selnl == NULL)
+ panic("SELinux: Cannot create netlink socket.");
+ netlink_set_nonroot(NETLINK_SELINUX, NL_NONROOT_RECV);
+ return 0;
+}
+
+__initcall(selnl_init);
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
new file mode 100644
index 00000000000..fa7fa030e6e
--- /dev/null
+++ b/security/selinux/nlmsgtab.c
@@ -0,0 +1,156 @@
+/*
+ * Netlink message type permission tables, for user generated messages.
+ *
+ * Author: James Morris <jmorris@redhat.com>
+ *
+ * Copyright (C) 2004 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 version 2,
+ * as published by the Free Software Foundation.
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/if.h>
+#include <linux/netfilter_ipv4/ip_queue.h>
+#include <linux/tcp_diag.h>
+#include <linux/xfrm.h>
+#include <linux/audit.h>
+
+#include "flask.h"
+#include "av_permissions.h"
+
+struct nlmsg_perm
+{
+ u16 nlmsg_type;
+ u32 perm;
+};
+
+static struct nlmsg_perm nlmsg_route_perms[] =
+{
+ { RTM_NEWLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETLINK, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_SETLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_NEWADDR, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELADDR, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETADDR, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWROUTE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELROUTE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETROUTE, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWRULE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELRULE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETRULE, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWQDISC, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELQDISC, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETQDISC, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWACTION, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELACTION, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETACTION, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWPREFIX, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETPREFIX, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_GETMULTICAST, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_GETANYCAST, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+};
+
+static struct nlmsg_perm nlmsg_firewall_perms[] =
+{
+ { IPQM_MODE, NETLINK_FIREWALL_SOCKET__NLMSG_WRITE },
+ { IPQM_VERDICT, NETLINK_FIREWALL_SOCKET__NLMSG_WRITE },
+};
+
+static struct nlmsg_perm nlmsg_tcpdiag_perms[] =
+{
+ { TCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ },
+};
+
+static struct nlmsg_perm nlmsg_xfrm_perms[] =
+{
+ { XFRM_MSG_NEWSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_DELSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_GETSA, NETLINK_XFRM_SOCKET__NLMSG_READ },
+ { XFRM_MSG_NEWPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_DELPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_GETPOLICY, NETLINK_XFRM_SOCKET__NLMSG_READ },
+ { XFRM_MSG_ALLOCSPI, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_UPDPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_UPDSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+};
+
+static struct nlmsg_perm nlmsg_audit_perms[] =
+{
+ { AUDIT_GET, NETLINK_AUDIT_SOCKET__NLMSG_READ },
+ { AUDIT_SET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_LIST, NETLINK_AUDIT_SOCKET__NLMSG_READ },
+ { AUDIT_ADD, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_DEL, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_LOGIN, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+};
+
+
+static int nlmsg_perm(u16 nlmsg_type, u32 *perm, struct nlmsg_perm *tab, size_t tabsize)
+{
+ int i, err = -EINVAL;
+
+ for (i = 0; i < tabsize/sizeof(struct nlmsg_perm); i++)
+ if (nlmsg_type == tab[i].nlmsg_type) {
+ *perm = tab[i].perm;
+ err = 0;
+ break;
+ }
+
+ return err;
+}
+
+int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
+{
+ int err = 0;
+
+ switch (sclass) {
+ case SECCLASS_NETLINK_ROUTE_SOCKET:
+ err = nlmsg_perm(nlmsg_type, perm, nlmsg_route_perms,
+ sizeof(nlmsg_route_perms));
+ break;
+
+ case SECCLASS_NETLINK_FIREWALL_SOCKET:
+ case NETLINK_IP6_FW:
+ err = nlmsg_perm(nlmsg_type, perm, nlmsg_firewall_perms,
+ sizeof(nlmsg_firewall_perms));
+ break;
+
+ case SECCLASS_NETLINK_TCPDIAG_SOCKET:
+ err = nlmsg_perm(nlmsg_type, perm, nlmsg_tcpdiag_perms,
+ sizeof(nlmsg_tcpdiag_perms));
+ break;
+
+ case SECCLASS_NETLINK_XFRM_SOCKET:
+ err = nlmsg_perm(nlmsg_type, perm, nlmsg_xfrm_perms,
+ sizeof(nlmsg_xfrm_perms));
+ break;
+
+ case SECCLASS_NETLINK_AUDIT_SOCKET:
+ err = nlmsg_perm(nlmsg_type, perm, nlmsg_audit_perms,
+ sizeof(nlmsg_audit_perms));
+ break;
+
+ /* No messaging from userspace, or class unknown/unhandled */
+ default:
+ err = -ENOENT;
+ break;
+ }
+
+ return err;
+}
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
new file mode 100644
index 00000000000..07221568b50
--- /dev/null
+++ b/security/selinux/selinuxfs.c
@@ -0,0 +1,1340 @@
+/* Updated: Karl MacMillan <kmacmillan@tresys.com>
+ *
+ * Added conditional policy language extensions
+ *
+ * Copyright (C) 2003 - 2004 Tresys Technology, LLC
+ * Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/pagemap.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/security.h>
+#include <linux/major.h>
+#include <linux/seq_file.h>
+#include <linux/percpu.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+
+/* selinuxfs pseudo filesystem for exporting the security policy API.
+ Based on the proc code and the fs/nfsd/nfsctl.c code. */
+
+#include "flask.h"
+#include "avc.h"
+#include "avc_ss.h"
+#include "security.h"
+#include "objsec.h"
+#include "conditional.h"
+
+unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE;
+
+static int __init checkreqprot_setup(char *str)
+{
+ selinux_checkreqprot = simple_strtoul(str,NULL,0) ? 1 : 0;
+ return 1;
+}
+__setup("checkreqprot=", checkreqprot_setup);
+
+
+static DECLARE_MUTEX(sel_sem);
+
+/* global data for booleans */
+static struct dentry *bool_dir = NULL;
+static int bool_num = 0;
+static int *bool_pending_values = NULL;
+
+extern void selnl_notify_setenforce(int val);
+
+/* Check whether a task is allowed to use a security operation. */
+static int task_has_security(struct task_struct *tsk,
+ u32 perms)
+{
+ struct task_security_struct *tsec;
+
+ tsec = tsk->security;
+ if (!tsec)
+ return -EACCES;
+
+ return avc_has_perm(tsec->sid, SECINITSID_SECURITY,
+ SECCLASS_SECURITY, perms, NULL);
+}
+
+enum sel_inos {
+ SEL_ROOT_INO = 2,
+ SEL_LOAD, /* load policy */
+ SEL_ENFORCE, /* get or set enforcing status */
+ SEL_CONTEXT, /* validate context */
+ SEL_ACCESS, /* compute access decision */
+ SEL_CREATE, /* compute create labeling decision */
+ SEL_RELABEL, /* compute relabeling decision */
+ SEL_USER, /* compute reachable user contexts */
+ SEL_POLICYVERS, /* return policy version for this kernel */
+ SEL_COMMIT_BOOLS, /* commit new boolean values */
+ SEL_MLS, /* return if MLS policy is enabled */
+ SEL_DISABLE, /* disable SELinux until next reboot */
+ SEL_AVC, /* AVC management directory */
+ SEL_MEMBER, /* compute polyinstantiation membership decision */
+ SEL_CHECKREQPROT, /* check requested protection, not kernel-applied one */
+};
+
+#define TMPBUFLEN 12
+static ssize_t sel_read_enforce(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char tmpbuf[TMPBUFLEN];
+ ssize_t length;
+
+ length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_enforcing);
+ return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
+}
+
+#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
+static ssize_t sel_write_enforce(struct file * file, const char __user * buf,
+ size_t count, loff_t *ppos)
+
+{
+ char *page;
+ ssize_t length;
+ int new_value;
+
+ if (count < 0 || count >= PAGE_SIZE)
+ return -ENOMEM;
+ if (*ppos != 0) {
+ /* No partial writes. */
+ return -EINVAL;
+ }
+ page = (char*)get_zeroed_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+ length = -EFAULT;
+ if (copy_from_user(page, buf, count))
+ goto out;
+
+ length = -EINVAL;
+ if (sscanf(page, "%d", &new_value) != 1)
+ goto out;
+
+ if (new_value != selinux_enforcing) {
+ length = task_has_security(current, SECURITY__SETENFORCE);
+ if (length)
+ goto out;
+ selinux_enforcing = new_value;
+ if (selinux_enforcing)
+ avc_ss_reset(0);
+ selnl_notify_setenforce(selinux_enforcing);
+ }
+ length = count;
+out:
+ free_page((unsigned long) page);
+ return length;
+}
+#else
+#define sel_write_enforce NULL
+#endif
+
+static struct file_operations sel_enforce_ops = {
+ .read = sel_read_enforce,
+ .write = sel_write_enforce,
+};
+
+#ifdef CONFIG_SECURITY_SELINUX_DISABLE
+static ssize_t sel_write_disable(struct file * file, const char __user * buf,
+ size_t count, loff_t *ppos)
+
+{
+ char *page;
+ ssize_t length;
+ int new_value;
+ extern int selinux_disable(void);
+
+ if (count < 0 || count >= PAGE_SIZE)
+ return -ENOMEM;
+ if (*ppos != 0) {
+ /* No partial writes. */
+ return -EINVAL;
+ }
+ page = (char*)get_zeroed_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+ length = -EFAULT;
+ if (copy_from_user(page, buf, count))
+ goto out;
+
+ length = -EINVAL;
+ if (sscanf(page, "%d", &new_value) != 1)
+ goto out;
+
+ if (new_value) {
+ length = selinux_disable();
+ if (length < 0)
+ goto out;
+ }
+
+ length = count;
+out:
+ free_page((unsigned long) page);
+ return length;
+}
+#else
+#define sel_write_disable NULL
+#endif
+
+static struct file_operations sel_disable_ops = {
+ .write = sel_write_disable,
+};
+
+static ssize_t sel_read_policyvers(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char tmpbuf[TMPBUFLEN];
+ ssize_t length;
+
+ length = scnprintf(tmpbuf, TMPBUFLEN, "%u", POLICYDB_VERSION_MAX);
+ return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
+}
+
+static struct file_operations sel_policyvers_ops = {
+ .read = sel_read_policyvers,
+};
+
+/* declaration for sel_write_load */
+static int sel_make_bools(void);
+
+static ssize_t sel_read_mls(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char tmpbuf[TMPBUFLEN];
+ ssize_t length;
+
+ length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_mls_enabled);
+ return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
+}
+
+static struct file_operations sel_mls_ops = {
+ .read = sel_read_mls,
+};
+
+static ssize_t sel_write_load(struct file * file, const char __user * buf,
+ size_t count, loff_t *ppos)
+
+{
+ int ret;
+ ssize_t length;
+ void *data = NULL;
+
+ down(&sel_sem);
+
+ length = task_has_security(current, SECURITY__LOAD_POLICY);
+ if (length)
+ goto out;
+
+ if (*ppos != 0) {
+ /* No partial writes. */
+ length = -EINVAL;
+ goto out;
+ }
+
+ if ((count < 0) || (count > 64 * 1024 * 1024)
+ || (data = vmalloc(count)) == NULL) {
+ length = -ENOMEM;
+ goto out;
+ }
+
+ length = -EFAULT;
+ if (copy_from_user(data, buf, count) != 0)
+ goto out;
+
+ length = security_load_policy(data, count);
+ if (length)
+ goto out;
+
+ ret = sel_make_bools();
+ if (ret)
+ length = ret;
+ else
+ length = count;
+out:
+ up(&sel_sem);
+ vfree(data);
+ return length;
+}
+
+static struct file_operations sel_load_ops = {
+ .write = sel_write_load,
+};
+
+
+static ssize_t sel_write_context(struct file * file, const char __user * buf,
+ size_t count, loff_t *ppos)
+
+{
+ char *page;
+ u32 sid;
+ ssize_t length;
+
+ length = task_has_security(current, SECURITY__CHECK_CONTEXT);
+ if (length)
+ return length;
+
+ if (count < 0 || count >= PAGE_SIZE)
+ return -ENOMEM;
+ if (*ppos != 0) {
+ /* No partial writes. */
+ return -EINVAL;
+ }
+ page = (char*)get_zeroed_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+ length = -EFAULT;
+ if (copy_from_user(page, buf, count))
+ goto out;
+
+ length = security_context_to_sid(page, count, &sid);
+ if (length < 0)
+ goto out;
+
+ length = count;
+out:
+ free_page((unsigned long) page);
+ return length;
+}
+
+static struct file_operations sel_context_ops = {
+ .write = sel_write_context,
+};
+
+static ssize_t sel_read_checkreqprot(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char tmpbuf[TMPBUFLEN];
+ ssize_t length;
+
+ length = scnprintf(tmpbuf, TMPBUFLEN, "%u", selinux_checkreqprot);
+ return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
+}
+
+static ssize_t sel_write_checkreqprot(struct file * file, const char __user * buf,
+ size_t count, loff_t *ppos)
+{
+ char *page;
+ ssize_t length;
+ unsigned int new_value;
+
+ length = task_has_security(current, SECURITY__SETCHECKREQPROT);
+ if (length)
+ return length;
+
+ if (count < 0 || count >= PAGE_SIZE)
+ return -ENOMEM;
+ if (*ppos != 0) {
+ /* No partial writes. */
+ return -EINVAL;
+ }
+ page = (char*)get_zeroed_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+ length = -EFAULT;
+ if (copy_from_user(page, buf, count))
+ goto out;
+
+ length = -EINVAL;
+ if (sscanf(page, "%u", &new_value) != 1)
+ goto out;
+
+ selinux_checkreqprot = new_value ? 1 : 0;
+ length = count;
+out:
+ free_page((unsigned long) page);
+ return length;
+}
+static struct file_operations sel_checkreqprot_ops = {
+ .read = sel_read_checkreqprot,
+ .write = sel_write_checkreqprot,
+};
+
+/*
+ * Remaining nodes use transaction based IO methods like nfsd/nfsctl.c
+ */
+static ssize_t sel_write_access(struct file * file, char *buf, size_t size);
+static ssize_t sel_write_create(struct file * file, char *buf, size_t size);
+static ssize_t sel_write_relabel(struct file * file, char *buf, size_t size);
+static ssize_t sel_write_user(struct file * file, char *buf, size_t size);
+static ssize_t sel_write_member(struct file * file, char *buf, size_t size);
+
+static ssize_t (*write_op[])(struct file *, char *, size_t) = {
+ [SEL_ACCESS] = sel_write_access,
+ [SEL_CREATE] = sel_write_create,
+ [SEL_RELABEL] = sel_write_relabel,
+ [SEL_USER] = sel_write_user,
+ [SEL_MEMBER] = sel_write_member,
+};
+
+static ssize_t selinux_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos)
+{
+ ino_t ino = file->f_dentry->d_inode->i_ino;
+ char *data;
+ ssize_t rv;
+
+ if (ino >= sizeof(write_op)/sizeof(write_op[0]) || !write_op[ino])
+ return -EINVAL;
+
+ data = simple_transaction_get(file, buf, size);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ rv = write_op[ino](file, data, size);
+ if (rv>0) {
+ simple_transaction_set(file, rv);
+ rv = size;
+ }
+ return rv;
+}
+
+static struct file_operations transaction_ops = {
+ .write = selinux_transaction_write,
+ .read = simple_transaction_read,
+ .release = simple_transaction_release,
+};
+
+/*
+ * payload - write methods
+ * If the method has a response, the response should be put in buf,
+ * and the length returned. Otherwise return 0 or and -error.
+ */
+
+static ssize_t sel_write_access(struct file * file, char *buf, size_t size)
+{
+ char *scon, *tcon;
+ u32 ssid, tsid;
+ u16 tclass;
+ u32 req;
+ struct av_decision avd;
+ ssize_t length;
+
+ length = task_has_security(current, SECURITY__COMPUTE_AV);
+ if (length)
+ return length;
+
+ length = -ENOMEM;
+ scon = kmalloc(size+1, GFP_KERNEL);
+ if (!scon)
+ return length;
+ memset(scon, 0, size+1);
+
+ tcon = kmalloc(size+1, GFP_KERNEL);
+ if (!tcon)
+ goto out;
+ memset(tcon, 0, size+1);
+
+ length = -EINVAL;
+ if (sscanf(buf, "%s %s %hu %x", scon, tcon, &tclass, &req) != 4)
+ goto out2;
+
+ length = security_context_to_sid(scon, strlen(scon)+1, &ssid);
+ if (length < 0)
+ goto out2;
+ length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid);
+ if (length < 0)
+ goto out2;
+
+ length = security_compute_av(ssid, tsid, tclass, req, &avd);
+ if (length < 0)
+ goto out2;
+
+ length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT,
+ "%x %x %x %x %u",
+ avd.allowed, avd.decided,
+ avd.auditallow, avd.auditdeny,
+ avd.seqno);
+out2:
+ kfree(tcon);
+out:
+ kfree(scon);
+ return length;
+}
+
+static ssize_t sel_write_create(struct file * file, char *buf, size_t size)
+{
+ char *scon, *tcon;
+ u32 ssid, tsid, newsid;
+ u16 tclass;
+ ssize_t length;
+ char *newcon;
+ u32 len;
+
+ length = task_has_security(current, SECURITY__COMPUTE_CREATE);
+ if (length)
+ return length;
+
+ length = -ENOMEM;
+ scon = kmalloc(size+1, GFP_KERNEL);
+ if (!scon)
+ return length;
+ memset(scon, 0, size+1);
+
+ tcon = kmalloc(size+1, GFP_KERNEL);
+ if (!tcon)
+ goto out;
+ memset(tcon, 0, size+1);
+
+ length = -EINVAL;
+ if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
+ goto out2;
+
+ length = security_context_to_sid(scon, strlen(scon)+1, &ssid);
+ if (length < 0)
+ goto out2;
+ length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid);
+ if (length < 0)
+ goto out2;
+
+ length = security_transition_sid(ssid, tsid, tclass, &newsid);
+ if (length < 0)
+ goto out2;
+
+ length = security_sid_to_context(newsid, &newcon, &len);
+ if (length < 0)
+ goto out2;
+
+ if (len > SIMPLE_TRANSACTION_LIMIT) {
+ printk(KERN_ERR "%s: context size (%u) exceeds payload "
+ "max\n", __FUNCTION__, len);
+ length = -ERANGE;
+ goto out3;
+ }
+
+ memcpy(buf, newcon, len);
+ length = len;
+out3:
+ kfree(newcon);
+out2:
+ kfree(tcon);
+out:
+ kfree(scon);
+ return length;
+}
+
+static ssize_t sel_write_relabel(struct file * file, char *buf, size_t size)
+{
+ char *scon, *tcon;
+ u32 ssid, tsid, newsid;
+ u16 tclass;
+ ssize_t length;
+ char *newcon;
+ u32 len;
+
+ length = task_has_security(current, SECURITY__COMPUTE_RELABEL);
+ if (length)
+ return length;
+
+ length = -ENOMEM;
+ scon = kmalloc(size+1, GFP_KERNEL);
+ if (!scon)
+ return length;
+ memset(scon, 0, size+1);
+
+ tcon = kmalloc(size+1, GFP_KERNEL);
+ if (!tcon)
+ goto out;
+ memset(tcon, 0, size+1);
+
+ length = -EINVAL;
+ if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
+ goto out2;
+
+ length = security_context_to_sid(scon, strlen(scon)+1, &ssid);
+ if (length < 0)
+ goto out2;
+ length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid);
+ if (length < 0)
+ goto out2;
+
+ length = security_change_sid(ssid, tsid, tclass, &newsid);
+ if (length < 0)
+ goto out2;
+
+ length = security_sid_to_context(newsid, &newcon, &len);
+ if (length < 0)
+ goto out2;
+
+ if (len > SIMPLE_TRANSACTION_LIMIT) {
+ length = -ERANGE;
+ goto out3;
+ }
+
+ memcpy(buf, newcon, len);
+ length = len;
+out3:
+ kfree(newcon);
+out2:
+ kfree(tcon);
+out:
+ kfree(scon);
+ return length;
+}
+
+static ssize_t sel_write_user(struct file * file, char *buf, size_t size)
+{
+ char *con, *user, *ptr;
+ u32 sid, *sids;
+ ssize_t length;
+ char *newcon;
+ int i, rc;
+ u32 len, nsids;
+
+ length = task_has_security(current, SECURITY__COMPUTE_USER);
+ if (length)
+ return length;
+
+ length = -ENOMEM;
+ con = kmalloc(size+1, GFP_KERNEL);
+ if (!con)
+ return length;
+ memset(con, 0, size+1);
+
+ user = kmalloc(size+1, GFP_KERNEL);
+ if (!user)
+ goto out;
+ memset(user, 0, size+1);
+
+ length = -EINVAL;
+ if (sscanf(buf, "%s %s", con, user) != 2)
+ goto out2;
+
+ length = security_context_to_sid(con, strlen(con)+1, &sid);
+ if (length < 0)
+ goto out2;
+
+ length = security_get_user_sids(sid, user, &sids, &nsids);
+ if (length < 0)
+ goto out2;
+
+ length = sprintf(buf, "%u", nsids) + 1;
+ ptr = buf + length;
+ for (i = 0; i < nsids; i++) {
+ rc = security_sid_to_context(sids[i], &newcon, &len);
+ if (rc) {
+ length = rc;
+ goto out3;
+ }
+ if ((length + len) >= SIMPLE_TRANSACTION_LIMIT) {
+ kfree(newcon);
+ length = -ERANGE;
+ goto out3;
+ }
+ memcpy(ptr, newcon, len);
+ kfree(newcon);
+ ptr += len;
+ length += len;
+ }
+out3:
+ kfree(sids);
+out2:
+ kfree(user);
+out:
+ kfree(con);
+ return length;
+}
+
+static ssize_t sel_write_member(struct file * file, char *buf, size_t size)
+{
+ char *scon, *tcon;
+ u32 ssid, tsid, newsid;
+ u16 tclass;
+ ssize_t length;
+ char *newcon;
+ u32 len;
+
+ length = task_has_security(current, SECURITY__COMPUTE_MEMBER);
+ if (length)
+ return length;
+
+ length = -ENOMEM;
+ scon = kmalloc(size+1, GFP_KERNEL);
+ if (!scon)
+ return length;
+ memset(scon, 0, size+1);
+
+ tcon = kmalloc(size+1, GFP_KERNEL);
+ if (!tcon)
+ goto out;
+ memset(tcon, 0, size+1);
+
+ length = -EINVAL;
+ if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
+ goto out2;
+
+ length = security_context_to_sid(scon, strlen(scon)+1, &ssid);
+ if (length < 0)
+ goto out2;
+ length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid);
+ if (length < 0)
+ goto out2;
+
+ length = security_member_sid(ssid, tsid, tclass, &newsid);
+ if (length < 0)
+ goto out2;
+
+ length = security_sid_to_context(newsid, &newcon, &len);
+ if (length < 0)
+ goto out2;
+
+ if (len > SIMPLE_TRANSACTION_LIMIT) {
+ printk(KERN_ERR "%s: context size (%u) exceeds payload "
+ "max\n", __FUNCTION__, len);
+ length = -ERANGE;
+ goto out3;
+ }
+
+ memcpy(buf, newcon, len);
+ length = len;
+out3:
+ kfree(newcon);
+out2:
+ kfree(tcon);
+out:
+ kfree(scon);
+ return length;
+}
+
+static struct inode *sel_make_inode(struct super_block *sb, int mode)
+{
+ struct inode *ret = new_inode(sb);
+
+ if (ret) {
+ ret->i_mode = mode;
+ ret->i_uid = ret->i_gid = 0;
+ ret->i_blksize = PAGE_CACHE_SIZE;
+ ret->i_blocks = 0;
+ ret->i_atime = ret->i_mtime = ret->i_ctime = CURRENT_TIME;
+ }
+ return ret;
+}
+
+#define BOOL_INO_OFFSET 30
+
+static ssize_t sel_read_bool(struct file *filep, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *page = NULL;
+ ssize_t length;
+ ssize_t end;
+ ssize_t ret;
+ int cur_enforcing;
+ struct inode *inode;
+
+ down(&sel_sem);
+
+ ret = -EFAULT;
+
+ /* check to see if this file has been deleted */
+ if (!filep->f_op)
+ goto out;
+
+ if (count < 0 || count > PAGE_SIZE) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (!(page = (char*)get_zeroed_page(GFP_KERNEL))) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ inode = filep->f_dentry->d_inode;
+ cur_enforcing = security_get_bool_value(inode->i_ino - BOOL_INO_OFFSET);
+ if (cur_enforcing < 0) {
+ ret = cur_enforcing;
+ goto out;
+ }
+
+ length = scnprintf(page, PAGE_SIZE, "%d %d", cur_enforcing,
+ bool_pending_values[inode->i_ino - BOOL_INO_OFFSET]);
+ if (length < 0) {
+ ret = length;
+ goto out;
+ }
+
+ if (*ppos >= length) {
+ ret = 0;
+ goto out;
+ }
+ if (count + *ppos > length)
+ count = length - *ppos;
+ end = count + *ppos;
+ if (copy_to_user(buf, (char *) page + *ppos, count)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ *ppos = end;
+ ret = count;
+out:
+ up(&sel_sem);
+ if (page)
+ free_page((unsigned long)page);
+ return ret;
+}
+
+static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *page = NULL;
+ ssize_t length = -EFAULT;
+ int new_value;
+ struct inode *inode;
+
+ down(&sel_sem);
+
+ length = task_has_security(current, SECURITY__SETBOOL);
+ if (length)
+ goto out;
+
+ /* check to see if this file has been deleted */
+ if (!filep->f_op)
+ goto out;
+
+ if (count < 0 || count >= PAGE_SIZE) {
+ length = -ENOMEM;
+ goto out;
+ }
+ if (*ppos != 0) {
+ /* No partial writes. */
+ goto out;
+ }
+ page = (char*)get_zeroed_page(GFP_KERNEL);
+ if (!page) {
+ length = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user(page, buf, count))
+ goto out;
+
+ length = -EINVAL;
+ if (sscanf(page, "%d", &new_value) != 1)
+ goto out;
+
+ if (new_value)
+ new_value = 1;
+
+ inode = filep->f_dentry->d_inode;
+ bool_pending_values[inode->i_ino - BOOL_INO_OFFSET] = new_value;
+ length = count;
+
+out:
+ up(&sel_sem);
+ if (page)
+ free_page((unsigned long) page);
+ return length;
+}
+
+static struct file_operations sel_bool_ops = {
+ .read = sel_read_bool,
+ .write = sel_write_bool,
+};
+
+static ssize_t sel_commit_bools_write(struct file *filep,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *page = NULL;
+ ssize_t length = -EFAULT;
+ int new_value;
+
+ down(&sel_sem);
+
+ length = task_has_security(current, SECURITY__SETBOOL);
+ if (length)
+ goto out;
+
+ /* check to see if this file has been deleted */
+ if (!filep->f_op)
+ goto out;
+
+ if (count < 0 || count >= PAGE_SIZE) {
+ length = -ENOMEM;
+ goto out;
+ }
+ if (*ppos != 0) {
+ /* No partial writes. */
+ goto out;
+ }
+ page = (char*)get_zeroed_page(GFP_KERNEL);
+ if (!page) {
+ length = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user(page, buf, count))
+ goto out;
+
+ length = -EINVAL;
+ if (sscanf(page, "%d", &new_value) != 1)
+ goto out;
+
+ if (new_value) {
+ security_set_bools(bool_num, bool_pending_values);
+ }
+
+ length = count;
+
+out:
+ up(&sel_sem);
+ if (page)
+ free_page((unsigned long) page);
+ return length;
+}
+
+static struct file_operations sel_commit_bools_ops = {
+ .write = sel_commit_bools_write,
+};
+
+/* delete booleans - partial revoke() from
+ * fs/proc/generic.c proc_kill_inodes */
+static void sel_remove_bools(struct dentry *de)
+{
+ struct list_head *p, *node;
+ struct super_block *sb = de->d_sb;
+
+ spin_lock(&dcache_lock);
+ node = de->d_subdirs.next;
+ while (node != &de->d_subdirs) {
+ struct dentry *d = list_entry(node, struct dentry, d_child);
+ list_del_init(node);
+
+ if (d->d_inode) {
+ d = dget_locked(d);
+ spin_unlock(&dcache_lock);
+ d_delete(d);
+ simple_unlink(de->d_inode, d);
+ dput(d);
+ spin_lock(&dcache_lock);
+ }
+ node = de->d_subdirs.next;
+ }
+
+ spin_unlock(&dcache_lock);
+
+ file_list_lock();
+ list_for_each(p, &sb->s_files) {
+ struct file * filp = list_entry(p, struct file, f_list);
+ struct dentry * dentry = filp->f_dentry;
+
+ if (dentry->d_parent != de) {
+ continue;
+ }
+ filp->f_op = NULL;
+ }
+ file_list_unlock();
+}
+
+#define BOOL_DIR_NAME "booleans"
+
+static int sel_make_bools(void)
+{
+ int i, ret = 0;
+ ssize_t len;
+ struct dentry *dentry = NULL;
+ struct dentry *dir = bool_dir;
+ struct inode *inode = NULL;
+ struct inode_security_struct *isec;
+ char **names = NULL, *page;
+ int num;
+ int *values = NULL;
+ u32 sid;
+
+ /* remove any existing files */
+ if (bool_pending_values)
+ kfree(bool_pending_values);
+
+ sel_remove_bools(dir);
+
+ if (!(page = (char*)get_zeroed_page(GFP_KERNEL)))
+ return -ENOMEM;
+
+ ret = security_get_bools(&num, &names, &values);
+ if (ret != 0)
+ goto out;
+
+ for (i = 0; i < num; i++) {
+ dentry = d_alloc_name(dir, names[i]);
+ if (!dentry) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ inode = sel_make_inode(dir->d_sb, S_IFREG | S_IRUGO | S_IWUSR);
+ if (!inode) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ len = snprintf(page, PAGE_SIZE, "/%s/%s", BOOL_DIR_NAME, names[i]);
+ if (len < 0) {
+ ret = -EINVAL;
+ goto err;
+ } else if (len >= PAGE_SIZE) {
+ ret = -ENAMETOOLONG;
+ goto err;
+ }
+ isec = (struct inode_security_struct*)inode->i_security;
+ if ((ret = security_genfs_sid("selinuxfs", page, SECCLASS_FILE, &sid)))
+ goto err;
+ isec->sid = sid;
+ isec->initialized = 1;
+ inode->i_fop = &sel_bool_ops;
+ inode->i_ino = i + BOOL_INO_OFFSET;
+ d_add(dentry, inode);
+ }
+ bool_num = num;
+ bool_pending_values = values;
+out:
+ free_page((unsigned long)page);
+ if (names) {
+ for (i = 0; i < num; i++) {
+ if (names[i])
+ kfree(names[i]);
+ }
+ kfree(names);
+ }
+ return ret;
+err:
+ d_genocide(dir);
+ ret = -ENOMEM;
+ goto out;
+}
+
+#define NULL_FILE_NAME "null"
+
+struct dentry *selinux_null = NULL;
+
+static ssize_t sel_read_avc_cache_threshold(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char tmpbuf[TMPBUFLEN];
+ ssize_t length;
+
+ length = scnprintf(tmpbuf, TMPBUFLEN, "%u", avc_cache_threshold);
+ return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
+}
+
+static ssize_t sel_write_avc_cache_threshold(struct file * file,
+ const char __user * buf,
+ size_t count, loff_t *ppos)
+
+{
+ char *page;
+ ssize_t ret;
+ int new_value;
+
+ if (count < 0 || count >= PAGE_SIZE) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (*ppos != 0) {
+ /* No partial writes. */
+ ret = -EINVAL;
+ goto out;
+ }
+
+ page = (char*)get_zeroed_page(GFP_KERNEL);
+ if (!page) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user(page, buf, count)) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+
+ if (sscanf(page, "%u", &new_value) != 1) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (new_value != avc_cache_threshold) {
+ ret = task_has_security(current, SECURITY__SETSECPARAM);
+ if (ret)
+ goto out_free;
+ avc_cache_threshold = new_value;
+ }
+ ret = count;
+out_free:
+ free_page((unsigned long)page);
+out:
+ return ret;
+}
+
+static ssize_t sel_read_avc_hash_stats(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *page;
+ ssize_t ret = 0;
+
+ page = (char *)__get_free_page(GFP_KERNEL);
+ if (!page) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ ret = avc_get_hash_stats(page);
+ if (ret >= 0)
+ ret = simple_read_from_buffer(buf, count, ppos, page, ret);
+ free_page((unsigned long)page);
+out:
+ return ret;
+}
+
+static struct file_operations sel_avc_cache_threshold_ops = {
+ .read = sel_read_avc_cache_threshold,
+ .write = sel_write_avc_cache_threshold,
+};
+
+static struct file_operations sel_avc_hash_stats_ops = {
+ .read = sel_read_avc_hash_stats,
+};
+
+#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
+static struct avc_cache_stats *sel_avc_get_stat_idx(loff_t *idx)
+{
+ int cpu;
+
+ for (cpu = *idx; cpu < NR_CPUS; ++cpu) {
+ if (!cpu_possible(cpu))
+ continue;
+ *idx = cpu + 1;
+ return &per_cpu(avc_cache_stats, cpu);
+ }
+ return NULL;
+}
+
+static void *sel_avc_stats_seq_start(struct seq_file *seq, loff_t *pos)
+{
+ loff_t n = *pos - 1;
+
+ if (*pos == 0)
+ return SEQ_START_TOKEN;
+
+ return sel_avc_get_stat_idx(&n);
+}
+
+static void *sel_avc_stats_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+ return sel_avc_get_stat_idx(pos);
+}
+
+static int sel_avc_stats_seq_show(struct seq_file *seq, void *v)
+{
+ struct avc_cache_stats *st = v;
+
+ if (v == SEQ_START_TOKEN)
+ seq_printf(seq, "lookups hits misses allocations reclaims "
+ "frees\n");
+ else
+ seq_printf(seq, "%u %u %u %u %u %u\n", st->lookups,
+ st->hits, st->misses, st->allocations,
+ st->reclaims, st->frees);
+ return 0;
+}
+
+static void sel_avc_stats_seq_stop(struct seq_file *seq, void *v)
+{ }
+
+static struct seq_operations sel_avc_cache_stats_seq_ops = {
+ .start = sel_avc_stats_seq_start,
+ .next = sel_avc_stats_seq_next,
+ .show = sel_avc_stats_seq_show,
+ .stop = sel_avc_stats_seq_stop,
+};
+
+static int sel_open_avc_cache_stats(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &sel_avc_cache_stats_seq_ops);
+}
+
+static struct file_operations sel_avc_cache_stats_ops = {
+ .open = sel_open_avc_cache_stats,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+#endif
+
+static int sel_make_avc_files(struct dentry *dir)
+{
+ int i, ret = 0;
+ static struct tree_descr files[] = {
+ { "cache_threshold",
+ &sel_avc_cache_threshold_ops, S_IRUGO|S_IWUSR },
+ { "hash_stats", &sel_avc_hash_stats_ops, S_IRUGO },
+#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
+ { "cache_stats", &sel_avc_cache_stats_ops, S_IRUGO },
+#endif
+ };
+
+ for (i = 0; i < sizeof (files) / sizeof (files[0]); i++) {
+ struct inode *inode;
+ struct dentry *dentry;
+
+ dentry = d_alloc_name(dir, files[i].name);
+ if (!dentry) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode);
+ if (!inode) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ inode->i_fop = files[i].ops;
+ d_add(dentry, inode);
+ }
+out:
+ return ret;
+err:
+ d_genocide(dir);
+ goto out;
+}
+
+static int sel_make_dir(struct super_block *sb, struct dentry *dentry)
+{
+ int ret = 0;
+ struct inode *inode;
+
+ inode = sel_make_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO);
+ if (!inode) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ inode->i_op = &simple_dir_inode_operations;
+ inode->i_fop = &simple_dir_operations;
+ d_add(dentry, inode);
+out:
+ return ret;
+}
+
+static int sel_fill_super(struct super_block * sb, void * data, int silent)
+{
+ int ret;
+ struct dentry *dentry;
+ struct inode *inode;
+ struct inode_security_struct *isec;
+
+ static struct tree_descr selinux_files[] = {
+ [SEL_LOAD] = {"load", &sel_load_ops, S_IRUSR|S_IWUSR},
+ [SEL_ENFORCE] = {"enforce", &sel_enforce_ops, S_IRUGO|S_IWUSR},
+ [SEL_CONTEXT] = {"context", &sel_context_ops, S_IRUGO|S_IWUGO},
+ [SEL_ACCESS] = {"access", &transaction_ops, S_IRUGO|S_IWUGO},
+ [SEL_CREATE] = {"create", &transaction_ops, S_IRUGO|S_IWUGO},
+ [SEL_RELABEL] = {"relabel", &transaction_ops, S_IRUGO|S_IWUGO},
+ [SEL_USER] = {"user", &transaction_ops, S_IRUGO|S_IWUGO},
+ [SEL_POLICYVERS] = {"policyvers", &sel_policyvers_ops, S_IRUGO},
+ [SEL_COMMIT_BOOLS] = {"commit_pending_bools", &sel_commit_bools_ops, S_IWUSR},
+ [SEL_MLS] = {"mls", &sel_mls_ops, S_IRUGO},
+ [SEL_DISABLE] = {"disable", &sel_disable_ops, S_IWUSR},
+ [SEL_MEMBER] = {"member", &transaction_ops, S_IRUGO|S_IWUGO},
+ [SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR},
+ /* last one */ {""}
+ };
+ ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files);
+ if (ret)
+ return ret;
+
+ dentry = d_alloc_name(sb->s_root, BOOL_DIR_NAME);
+ if (!dentry)
+ return -ENOMEM;
+
+ inode = sel_make_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO);
+ if (!inode)
+ goto out;
+ inode->i_op = &simple_dir_inode_operations;
+ inode->i_fop = &simple_dir_operations;
+ d_add(dentry, inode);
+ bool_dir = dentry;
+ ret = sel_make_bools();
+ if (ret)
+ goto out;
+
+ dentry = d_alloc_name(sb->s_root, NULL_FILE_NAME);
+ if (!dentry)
+ return -ENOMEM;
+
+ inode = sel_make_inode(sb, S_IFCHR | S_IRUGO | S_IWUGO);
+ if (!inode)
+ goto out;
+ isec = (struct inode_security_struct*)inode->i_security;
+ isec->sid = SECINITSID_DEVNULL;
+ isec->sclass = SECCLASS_CHR_FILE;
+ isec->initialized = 1;
+
+ init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, MKDEV(MEM_MAJOR, 3));
+ d_add(dentry, inode);
+ selinux_null = dentry;
+
+ dentry = d_alloc_name(sb->s_root, "avc");
+ if (!dentry)
+ return -ENOMEM;
+
+ ret = sel_make_dir(sb, dentry);
+ if (ret)
+ goto out;
+
+ ret = sel_make_avc_files(dentry);
+ if (ret)
+ goto out;
+
+ return 0;
+out:
+ dput(dentry);
+ printk(KERN_ERR "%s: failed while creating inodes\n", __FUNCTION__);
+ return -ENOMEM;
+}
+
+static struct super_block *sel_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data)
+{
+ return get_sb_single(fs_type, flags, data, sel_fill_super);
+}
+
+static struct file_system_type sel_fs_type = {
+ .name = "selinuxfs",
+ .get_sb = sel_get_sb,
+ .kill_sb = kill_litter_super,
+};
+
+struct vfsmount *selinuxfs_mount;
+
+static int __init init_sel_fs(void)
+{
+ int err;
+
+ if (!selinux_enabled)
+ return 0;
+ err = register_filesystem(&sel_fs_type);
+ if (!err) {
+ selinuxfs_mount = kern_mount(&sel_fs_type);
+ if (IS_ERR(selinuxfs_mount)) {
+ printk(KERN_ERR "selinuxfs: could not mount!\n");
+ err = PTR_ERR(selinuxfs_mount);
+ selinuxfs_mount = NULL;
+ }
+ }
+ return err;
+}
+
+__initcall(init_sel_fs);
+
+#ifdef CONFIG_SECURITY_SELINUX_DISABLE
+void exit_sel_fs(void)
+{
+ unregister_filesystem(&sel_fs_type);
+}
+#endif
diff --git a/security/selinux/ss/Makefile b/security/selinux/ss/Makefile
new file mode 100644
index 00000000000..bad78779b9b
--- /dev/null
+++ b/security/selinux/ss/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for building the SELinux security server as part of the kernel tree.
+#
+
+EXTRA_CFLAGS += -Isecurity/selinux/include
+obj-y := ss.o
+
+ss-y := ebitmap.o hashtab.o symtab.o sidtab.o avtab.o policydb.o services.o conditional.o mls.o
+
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
new file mode 100644
index 00000000000..f238c034c44
--- /dev/null
+++ b/security/selinux/ss/avtab.c
@@ -0,0 +1,399 @@
+/*
+ * Implementation of the access vector table type.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+
+/* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
+ *
+ * Added conditional policy language extensions
+ *
+ * Copyright (C) 2003 Tresys Technology, LLC
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/errno.h>
+
+#include "avtab.h"
+#include "policydb.h"
+
+#define AVTAB_HASH(keyp) \
+((keyp->target_class + \
+ (keyp->target_type << 2) + \
+ (keyp->source_type << 9)) & \
+ AVTAB_HASH_MASK)
+
+static kmem_cache_t *avtab_node_cachep;
+
+static struct avtab_node*
+avtab_insert_node(struct avtab *h, int hvalue,
+ struct avtab_node * prev, struct avtab_node * cur,
+ struct avtab_key *key, struct avtab_datum *datum)
+{
+ struct avtab_node * newnode;
+ newnode = kmem_cache_alloc(avtab_node_cachep, SLAB_KERNEL);
+ if (newnode == NULL)
+ return NULL;
+ memset(newnode, 0, sizeof(struct avtab_node));
+ newnode->key = *key;
+ newnode->datum = *datum;
+ if (prev) {
+ newnode->next = prev->next;
+ prev->next = newnode;
+ } else {
+ newnode->next = h->htable[hvalue];
+ h->htable[hvalue] = newnode;
+ }
+
+ h->nel++;
+ return newnode;
+}
+
+static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum)
+{
+ int hvalue;
+ struct avtab_node *prev, *cur, *newnode;
+
+ if (!h)
+ return -EINVAL;
+
+ hvalue = AVTAB_HASH(key);
+ for (prev = NULL, cur = h->htable[hvalue];
+ cur;
+ prev = cur, cur = cur->next) {
+ if (key->source_type == cur->key.source_type &&
+ key->target_type == cur->key.target_type &&
+ key->target_class == cur->key.target_class &&
+ (datum->specified & cur->datum.specified))
+ return -EEXIST;
+ if (key->source_type < cur->key.source_type)
+ break;
+ if (key->source_type == cur->key.source_type &&
+ key->target_type < cur->key.target_type)
+ break;
+ if (key->source_type == cur->key.source_type &&
+ key->target_type == cur->key.target_type &&
+ key->target_class < cur->key.target_class)
+ break;
+ }
+
+ newnode = avtab_insert_node(h, hvalue, prev, cur, key, datum);
+ if(!newnode)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/* Unlike avtab_insert(), this function allow multiple insertions of the same
+ * key/specified mask into the table, as needed by the conditional avtab.
+ * It also returns a pointer to the node inserted.
+ */
+struct avtab_node *
+avtab_insert_nonunique(struct avtab * h, struct avtab_key * key, struct avtab_datum * datum)
+{
+ int hvalue;
+ struct avtab_node *prev, *cur, *newnode;
+
+ if (!h)
+ return NULL;
+ hvalue = AVTAB_HASH(key);
+ for (prev = NULL, cur = h->htable[hvalue];
+ cur;
+ prev = cur, cur = cur->next) {
+ if (key->source_type == cur->key.source_type &&
+ key->target_type == cur->key.target_type &&
+ key->target_class == cur->key.target_class &&
+ (datum->specified & cur->datum.specified))
+ break;
+ if (key->source_type < cur->key.source_type)
+ break;
+ if (key->source_type == cur->key.source_type &&
+ key->target_type < cur->key.target_type)
+ break;
+ if (key->source_type == cur->key.source_type &&
+ key->target_type == cur->key.target_type &&
+ key->target_class < cur->key.target_class)
+ break;
+ }
+ newnode = avtab_insert_node(h, hvalue, prev, cur, key, datum);
+
+ return newnode;
+}
+
+struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key, int specified)
+{
+ int hvalue;
+ struct avtab_node *cur;
+
+ if (!h)
+ return NULL;
+
+ hvalue = AVTAB_HASH(key);
+ for (cur = h->htable[hvalue]; cur; cur = cur->next) {
+ if (key->source_type == cur->key.source_type &&
+ key->target_type == cur->key.target_type &&
+ key->target_class == cur->key.target_class &&
+ (specified & cur->datum.specified))
+ return &cur->datum;
+
+ if (key->source_type < cur->key.source_type)
+ break;
+ if (key->source_type == cur->key.source_type &&
+ key->target_type < cur->key.target_type)
+ break;
+ if (key->source_type == cur->key.source_type &&
+ key->target_type == cur->key.target_type &&
+ key->target_class < cur->key.target_class)
+ break;
+ }
+
+ return NULL;
+}
+
+/* This search function returns a node pointer, and can be used in
+ * conjunction with avtab_search_next_node()
+ */
+struct avtab_node*
+avtab_search_node(struct avtab *h, struct avtab_key *key, int specified)
+{
+ int hvalue;
+ struct avtab_node *cur;
+
+ if (!h)
+ return NULL;
+
+ hvalue = AVTAB_HASH(key);
+ for (cur = h->htable[hvalue]; cur; cur = cur->next) {
+ if (key->source_type == cur->key.source_type &&
+ key->target_type == cur->key.target_type &&
+ key->target_class == cur->key.target_class &&
+ (specified & cur->datum.specified))
+ return cur;
+
+ if (key->source_type < cur->key.source_type)
+ break;
+ if (key->source_type == cur->key.source_type &&
+ key->target_type < cur->key.target_type)
+ break;
+ if (key->source_type == cur->key.source_type &&
+ key->target_type == cur->key.target_type &&
+ key->target_class < cur->key.target_class)
+ break;
+ }
+ return NULL;
+}
+
+struct avtab_node*
+avtab_search_node_next(struct avtab_node *node, int specified)
+{
+ struct avtab_node *cur;
+
+ if (!node)
+ return NULL;
+
+ for (cur = node->next; cur; cur = cur->next) {
+ if (node->key.source_type == cur->key.source_type &&
+ node->key.target_type == cur->key.target_type &&
+ node->key.target_class == cur->key.target_class &&
+ (specified & cur->datum.specified))
+ return cur;
+
+ if (node->key.source_type < cur->key.source_type)
+ break;
+ if (node->key.source_type == cur->key.source_type &&
+ node->key.target_type < cur->key.target_type)
+ break;
+ if (node->key.source_type == cur->key.source_type &&
+ node->key.target_type == cur->key.target_type &&
+ node->key.target_class < cur->key.target_class)
+ break;
+ }
+ return NULL;
+}
+
+void avtab_destroy(struct avtab *h)
+{
+ int i;
+ struct avtab_node *cur, *temp;
+
+ if (!h || !h->htable)
+ return;
+
+ for (i = 0; i < AVTAB_SIZE; i++) {
+ cur = h->htable[i];
+ while (cur != NULL) {
+ temp = cur;
+ cur = cur->next;
+ kmem_cache_free(avtab_node_cachep, temp);
+ }
+ h->htable[i] = NULL;
+ }
+ vfree(h->htable);
+ h->htable = NULL;
+}
+
+
+int avtab_init(struct avtab *h)
+{
+ int i;
+
+ h->htable = vmalloc(sizeof(*(h->htable)) * AVTAB_SIZE);
+ if (!h->htable)
+ return -ENOMEM;
+ for (i = 0; i < AVTAB_SIZE; i++)
+ h->htable[i] = NULL;
+ h->nel = 0;
+ return 0;
+}
+
+void avtab_hash_eval(struct avtab *h, char *tag)
+{
+ int i, chain_len, slots_used, max_chain_len;
+ struct avtab_node *cur;
+
+ slots_used = 0;
+ max_chain_len = 0;
+ for (i = 0; i < AVTAB_SIZE; i++) {
+ cur = h->htable[i];
+ if (cur) {
+ slots_used++;
+ chain_len = 0;
+ while (cur) {
+ chain_len++;
+ cur = cur->next;
+ }
+
+ if (chain_len > max_chain_len)
+ max_chain_len = chain_len;
+ }
+ }
+
+ printk(KERN_INFO "%s: %d entries and %d/%d buckets used, longest "
+ "chain length %d\n", tag, h->nel, slots_used, AVTAB_SIZE,
+ max_chain_len);
+}
+
+int avtab_read_item(void *fp, struct avtab_datum *avdatum, struct avtab_key *avkey)
+{
+ u32 buf[7];
+ u32 items, items2;
+ int rc;
+
+ memset(avkey, 0, sizeof(struct avtab_key));
+ memset(avdatum, 0, sizeof(struct avtab_datum));
+
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0) {
+ printk(KERN_ERR "security: avtab: truncated entry\n");
+ goto bad;
+ }
+ items2 = le32_to_cpu(buf[0]);
+ if (items2 > ARRAY_SIZE(buf)) {
+ printk(KERN_ERR "security: avtab: entry overflow\n");
+ goto bad;
+ }
+ rc = next_entry(buf, fp, sizeof(u32)*items2);
+ if (rc < 0) {
+ printk(KERN_ERR "security: avtab: truncated entry\n");
+ goto bad;
+ }
+ items = 0;
+ avkey->source_type = le32_to_cpu(buf[items++]);
+ avkey->target_type = le32_to_cpu(buf[items++]);
+ avkey->target_class = le32_to_cpu(buf[items++]);
+ avdatum->specified = le32_to_cpu(buf[items++]);
+ if (!(avdatum->specified & (AVTAB_AV | AVTAB_TYPE))) {
+ printk(KERN_ERR "security: avtab: null entry\n");
+ goto bad;
+ }
+ if ((avdatum->specified & AVTAB_AV) &&
+ (avdatum->specified & AVTAB_TYPE)) {
+ printk(KERN_ERR "security: avtab: entry has both access vectors and types\n");
+ goto bad;
+ }
+ if (avdatum->specified & AVTAB_AV) {
+ if (avdatum->specified & AVTAB_ALLOWED)
+ avtab_allowed(avdatum) = le32_to_cpu(buf[items++]);
+ if (avdatum->specified & AVTAB_AUDITDENY)
+ avtab_auditdeny(avdatum) = le32_to_cpu(buf[items++]);
+ if (avdatum->specified & AVTAB_AUDITALLOW)
+ avtab_auditallow(avdatum) = le32_to_cpu(buf[items++]);
+ } else {
+ if (avdatum->specified & AVTAB_TRANSITION)
+ avtab_transition(avdatum) = le32_to_cpu(buf[items++]);
+ if (avdatum->specified & AVTAB_CHANGE)
+ avtab_change(avdatum) = le32_to_cpu(buf[items++]);
+ if (avdatum->specified & AVTAB_MEMBER)
+ avtab_member(avdatum) = le32_to_cpu(buf[items++]);
+ }
+ if (items != items2) {
+ printk(KERN_ERR "security: avtab: entry only had %d items, expected %d\n",
+ items2, items);
+ goto bad;
+ }
+
+ return 0;
+bad:
+ return -1;
+}
+
+int avtab_read(struct avtab *a, void *fp, u32 config)
+{
+ int rc;
+ struct avtab_key avkey;
+ struct avtab_datum avdatum;
+ u32 buf[1];
+ u32 nel, i;
+
+
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0) {
+ printk(KERN_ERR "security: avtab: truncated table\n");
+ goto bad;
+ }
+ nel = le32_to_cpu(buf[0]);
+ if (!nel) {
+ printk(KERN_ERR "security: avtab: table is empty\n");
+ rc = -EINVAL;
+ goto bad;
+ }
+ for (i = 0; i < nel; i++) {
+ if (avtab_read_item(fp, &avdatum, &avkey)) {
+ rc = -EINVAL;
+ goto bad;
+ }
+ rc = avtab_insert(a, &avkey, &avdatum);
+ if (rc) {
+ if (rc == -ENOMEM)
+ printk(KERN_ERR "security: avtab: out of memory\n");
+ if (rc == -EEXIST)
+ printk(KERN_ERR "security: avtab: duplicate entry\n");
+ goto bad;
+ }
+ }
+
+ rc = 0;
+out:
+ return rc;
+
+bad:
+ avtab_destroy(a);
+ goto out;
+}
+
+void avtab_cache_init(void)
+{
+ avtab_node_cachep = kmem_cache_create("avtab_node",
+ sizeof(struct avtab_node),
+ 0, SLAB_PANIC, NULL, NULL);
+}
+
+void avtab_cache_destroy(void)
+{
+ kmem_cache_destroy (avtab_node_cachep);
+}
diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h
new file mode 100644
index 00000000000..519d4f6dc65
--- /dev/null
+++ b/security/selinux/ss/avtab.h
@@ -0,0 +1,85 @@
+/*
+ * An access vector table (avtab) is a hash table
+ * of access vectors and transition types indexed
+ * by a type pair and a class. An access vector
+ * table is used to represent the type enforcement
+ * tables.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+
+/* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
+ *
+ * Added conditional policy language extensions
+ *
+ * Copyright (C) 2003 Tresys Technology, LLC
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ */
+#ifndef _SS_AVTAB_H_
+#define _SS_AVTAB_H_
+
+struct avtab_key {
+ u32 source_type; /* source type */
+ u32 target_type; /* target type */
+ u32 target_class; /* target object class */
+};
+
+struct avtab_datum {
+#define AVTAB_ALLOWED 1
+#define AVTAB_AUDITALLOW 2
+#define AVTAB_AUDITDENY 4
+#define AVTAB_AV (AVTAB_ALLOWED | AVTAB_AUDITALLOW | AVTAB_AUDITDENY)
+#define AVTAB_TRANSITION 16
+#define AVTAB_MEMBER 32
+#define AVTAB_CHANGE 64
+#define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE)
+#define AVTAB_ENABLED 0x80000000 /* reserved for used in cond_avtab */
+ u32 specified; /* what fields are specified */
+ u32 data[3]; /* access vectors or types */
+#define avtab_allowed(x) (x)->data[0]
+#define avtab_auditdeny(x) (x)->data[1]
+#define avtab_auditallow(x) (x)->data[2]
+#define avtab_transition(x) (x)->data[0]
+#define avtab_change(x) (x)->data[1]
+#define avtab_member(x) (x)->data[2]
+};
+
+struct avtab_node {
+ struct avtab_key key;
+ struct avtab_datum datum;
+ struct avtab_node *next;
+};
+
+struct avtab {
+ struct avtab_node **htable;
+ u32 nel; /* number of elements */
+};
+
+int avtab_init(struct avtab *);
+struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *k, int specified);
+void avtab_destroy(struct avtab *h);
+void avtab_hash_eval(struct avtab *h, char *tag);
+
+int avtab_read_item(void *fp, struct avtab_datum *avdatum, struct avtab_key *avkey);
+int avtab_read(struct avtab *a, void *fp, u32 config);
+
+struct avtab_node *avtab_insert_nonunique(struct avtab *h, struct avtab_key *key,
+ struct avtab_datum *datum);
+
+struct avtab_node *avtab_search_node(struct avtab *h, struct avtab_key *key, int specified);
+
+struct avtab_node *avtab_search_node_next(struct avtab_node *node, int specified);
+
+void avtab_cache_init(void);
+void avtab_cache_destroy(void);
+
+#define AVTAB_HASH_BITS 15
+#define AVTAB_HASH_BUCKETS (1 << AVTAB_HASH_BITS)
+#define AVTAB_HASH_MASK (AVTAB_HASH_BUCKETS-1)
+
+#define AVTAB_SIZE AVTAB_HASH_BUCKETS
+
+#endif /* _SS_AVTAB_H_ */
+
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c
new file mode 100644
index 00000000000..b53441184ac
--- /dev/null
+++ b/security/selinux/ss/conditional.c
@@ -0,0 +1,489 @@
+/* Authors: Karl MacMillan <kmacmillan@tresys.com>
+ * Frank Mayer <mayerf@tresys.com>
+ *
+ * Copyright (C) 2003 - 2004 Tresys Technology, LLC
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/slab.h>
+
+#include "security.h"
+#include "conditional.h"
+
+/*
+ * cond_evaluate_expr evaluates a conditional expr
+ * in reverse polish notation. It returns true (1), false (0),
+ * or undefined (-1). Undefined occurs when the expression
+ * exceeds the stack depth of COND_EXPR_MAXDEPTH.
+ */
+static int cond_evaluate_expr(struct policydb *p, struct cond_expr *expr)
+{
+
+ struct cond_expr *cur;
+ int s[COND_EXPR_MAXDEPTH];
+ int sp = -1;
+
+ for (cur = expr; cur != NULL; cur = cur->next) {
+ switch (cur->expr_type) {
+ case COND_BOOL:
+ if (sp == (COND_EXPR_MAXDEPTH - 1))
+ return -1;
+ sp++;
+ s[sp] = p->bool_val_to_struct[cur->bool - 1]->state;
+ break;
+ case COND_NOT:
+ if (sp < 0)
+ return -1;
+ s[sp] = !s[sp];
+ break;
+ case COND_OR:
+ if (sp < 1)
+ return -1;
+ sp--;
+ s[sp] |= s[sp + 1];
+ break;
+ case COND_AND:
+ if (sp < 1)
+ return -1;
+ sp--;
+ s[sp] &= s[sp + 1];
+ break;
+ case COND_XOR:
+ if (sp < 1)
+ return -1;
+ sp--;
+ s[sp] ^= s[sp + 1];
+ break;
+ case COND_EQ:
+ if (sp < 1)
+ return -1;
+ sp--;
+ s[sp] = (s[sp] == s[sp + 1]);
+ break;
+ case COND_NEQ:
+ if (sp < 1)
+ return -1;
+ sp--;
+ s[sp] = (s[sp] != s[sp + 1]);
+ break;
+ default:
+ return -1;
+ }
+ }
+ return s[0];
+}
+
+/*
+ * evaluate_cond_node evaluates the conditional stored in
+ * a struct cond_node and if the result is different than the
+ * current state of the node it sets the rules in the true/false
+ * list appropriately. If the result of the expression is undefined
+ * all of the rules are disabled for safety.
+ */
+int evaluate_cond_node(struct policydb *p, struct cond_node *node)
+{
+ int new_state;
+ struct cond_av_list* cur;
+
+ new_state = cond_evaluate_expr(p, node->expr);
+ if (new_state != node->cur_state) {
+ node->cur_state = new_state;
+ if (new_state == -1)
+ printk(KERN_ERR "security: expression result was undefined - disabling all rules.\n");
+ /* turn the rules on or off */
+ for (cur = node->true_list; cur != NULL; cur = cur->next) {
+ if (new_state <= 0) {
+ cur->node->datum.specified &= ~AVTAB_ENABLED;
+ } else {
+ cur->node->datum.specified |= AVTAB_ENABLED;
+ }
+ }
+
+ for (cur = node->false_list; cur != NULL; cur = cur->next) {
+ /* -1 or 1 */
+ if (new_state) {
+ cur->node->datum.specified &= ~AVTAB_ENABLED;
+ } else {
+ cur->node->datum.specified |= AVTAB_ENABLED;
+ }
+ }
+ }
+ return 0;
+}
+
+int cond_policydb_init(struct policydb *p)
+{
+ p->bool_val_to_struct = NULL;
+ p->cond_list = NULL;
+ if (avtab_init(&p->te_cond_avtab))
+ return -1;
+
+ return 0;
+}
+
+static void cond_av_list_destroy(struct cond_av_list *list)
+{
+ struct cond_av_list *cur, *next;
+ for (cur = list; cur != NULL; cur = next) {
+ next = cur->next;
+ /* the avtab_ptr_t node is destroy by the avtab */
+ kfree(cur);
+ }
+}
+
+static void cond_node_destroy(struct cond_node *node)
+{
+ struct cond_expr *cur_expr, *next_expr;
+
+ for (cur_expr = node->expr; cur_expr != NULL; cur_expr = next_expr) {
+ next_expr = cur_expr->next;
+ kfree(cur_expr);
+ }
+ cond_av_list_destroy(node->true_list);
+ cond_av_list_destroy(node->false_list);
+ kfree(node);
+}
+
+static void cond_list_destroy(struct cond_node *list)
+{
+ struct cond_node *next, *cur;
+
+ if (list == NULL)
+ return;
+
+ for (cur = list; cur != NULL; cur = next) {
+ next = cur->next;
+ cond_node_destroy(cur);
+ }
+}
+
+void cond_policydb_destroy(struct policydb *p)
+{
+ if (p->bool_val_to_struct != NULL)
+ kfree(p->bool_val_to_struct);
+ avtab_destroy(&p->te_cond_avtab);
+ cond_list_destroy(p->cond_list);
+}
+
+int cond_init_bool_indexes(struct policydb *p)
+{
+ if (p->bool_val_to_struct)
+ kfree(p->bool_val_to_struct);
+ p->bool_val_to_struct = (struct cond_bool_datum**)
+ kmalloc(p->p_bools.nprim * sizeof(struct cond_bool_datum*), GFP_KERNEL);
+ if (!p->bool_val_to_struct)
+ return -1;
+ return 0;
+}
+
+int cond_destroy_bool(void *key, void *datum, void *p)
+{
+ if (key)
+ kfree(key);
+ kfree(datum);
+ return 0;
+}
+
+int cond_index_bool(void *key, void *datum, void *datap)
+{
+ struct policydb *p;
+ struct cond_bool_datum *booldatum;
+
+ booldatum = datum;
+ p = datap;
+
+ if (!booldatum->value || booldatum->value > p->p_bools.nprim)
+ return -EINVAL;
+
+ p->p_bool_val_to_name[booldatum->value - 1] = key;
+ p->bool_val_to_struct[booldatum->value -1] = booldatum;
+
+ return 0;
+}
+
+static int bool_isvalid(struct cond_bool_datum *b)
+{
+ if (!(b->state == 0 || b->state == 1))
+ return 0;
+ return 1;
+}
+
+int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp)
+{
+ char *key = NULL;
+ struct cond_bool_datum *booldatum;
+ u32 buf[3], len;
+ int rc;
+
+ booldatum = kmalloc(sizeof(struct cond_bool_datum), GFP_KERNEL);
+ if (!booldatum)
+ return -1;
+ memset(booldatum, 0, sizeof(struct cond_bool_datum));
+
+ rc = next_entry(buf, fp, sizeof buf);
+ if (rc < 0)
+ goto err;
+
+ booldatum->value = le32_to_cpu(buf[0]);
+ booldatum->state = le32_to_cpu(buf[1]);
+
+ if (!bool_isvalid(booldatum))
+ goto err;
+
+ len = le32_to_cpu(buf[2]);
+
+ key = kmalloc(len + 1, GFP_KERNEL);
+ if (!key)
+ goto err;
+ rc = next_entry(key, fp, len);
+ if (rc < 0)
+ goto err;
+ key[len] = 0;
+ if (hashtab_insert(h, key, booldatum))
+ goto err;
+
+ return 0;
+err:
+ cond_destroy_bool(key, booldatum, NULL);
+ return -1;
+}
+
+static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list **ret_list,
+ struct cond_av_list *other)
+{
+ struct cond_av_list *list, *last = NULL, *cur;
+ struct avtab_key key;
+ struct avtab_datum datum;
+ struct avtab_node *node_ptr;
+ int rc;
+ u32 buf[1], i, len;
+ u8 found;
+
+ *ret_list = NULL;
+
+ len = 0;
+ rc = next_entry(buf, fp, sizeof buf);
+ if (rc < 0)
+ return -1;
+
+ len = le32_to_cpu(buf[0]);
+ if (len == 0) {
+ return 0;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (avtab_read_item(fp, &datum, &key))
+ goto err;
+
+ /*
+ * For type rules we have to make certain there aren't any
+ * conflicting rules by searching the te_avtab and the
+ * cond_te_avtab.
+ */
+ if (datum.specified & AVTAB_TYPE) {
+ if (avtab_search(&p->te_avtab, &key, AVTAB_TYPE)) {
+ printk("security: type rule already exists outside of a conditional.");
+ goto err;
+ }
+ /*
+ * If we are reading the false list other will be a pointer to
+ * the true list. We can have duplicate entries if there is only
+ * 1 other entry and it is in our true list.
+ *
+ * If we are reading the true list (other == NULL) there shouldn't
+ * be any other entries.
+ */
+ if (other) {
+ node_ptr = avtab_search_node(&p->te_cond_avtab, &key, AVTAB_TYPE);
+ if (node_ptr) {
+ if (avtab_search_node_next(node_ptr, AVTAB_TYPE)) {
+ printk("security: too many conflicting type rules.");
+ goto err;
+ }
+ found = 0;
+ for (cur = other; cur != NULL; cur = cur->next) {
+ if (cur->node == node_ptr) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ printk("security: conflicting type rules.");
+ goto err;
+ }
+ }
+ } else {
+ if (avtab_search(&p->te_cond_avtab, &key, AVTAB_TYPE)) {
+ printk("security: conflicting type rules when adding type rule for true.");
+ goto err;
+ }
+ }
+ }
+ node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, &key, &datum);
+ if (!node_ptr) {
+ printk("security: could not insert rule.");
+ goto err;
+ }
+
+ list = kmalloc(sizeof(struct cond_av_list), GFP_KERNEL);
+ if (!list)
+ goto err;
+ memset(list, 0, sizeof(struct cond_av_list));
+
+ list->node = node_ptr;
+ if (i == 0)
+ *ret_list = list;
+ else
+ last->next = list;
+ last = list;
+
+ }
+
+ return 0;
+err:
+ cond_av_list_destroy(*ret_list);
+ *ret_list = NULL;
+ return -1;
+}
+
+static int expr_isvalid(struct policydb *p, struct cond_expr *expr)
+{
+ if (expr->expr_type <= 0 || expr->expr_type > COND_LAST) {
+ printk("security: conditional expressions uses unknown operator.\n");
+ return 0;
+ }
+
+ if (expr->bool > p->p_bools.nprim) {
+ printk("security: conditional expressions uses unknown bool.\n");
+ return 0;
+ }
+ return 1;
+}
+
+static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp)
+{
+ u32 buf[2], len, i;
+ int rc;
+ struct cond_expr *expr = NULL, *last = NULL;
+
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ return -1;
+
+ node->cur_state = le32_to_cpu(buf[0]);
+
+ len = 0;
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ return -1;
+
+ /* expr */
+ len = le32_to_cpu(buf[0]);
+
+ for (i = 0; i < len; i++ ) {
+ rc = next_entry(buf, fp, sizeof(u32) * 2);
+ if (rc < 0)
+ goto err;
+
+ expr = kmalloc(sizeof(struct cond_expr), GFP_KERNEL);
+ if (!expr) {
+ goto err;
+ }
+ memset(expr, 0, sizeof(struct cond_expr));
+
+ expr->expr_type = le32_to_cpu(buf[0]);
+ expr->bool = le32_to_cpu(buf[1]);
+
+ if (!expr_isvalid(p, expr)) {
+ kfree(expr);
+ goto err;
+ }
+
+ if (i == 0) {
+ node->expr = expr;
+ } else {
+ last->next = expr;
+ }
+ last = expr;
+ }
+
+ if (cond_read_av_list(p, fp, &node->true_list, NULL) != 0)
+ goto err;
+ if (cond_read_av_list(p, fp, &node->false_list, node->true_list) != 0)
+ goto err;
+ return 0;
+err:
+ cond_node_destroy(node);
+ return -1;
+}
+
+int cond_read_list(struct policydb *p, void *fp)
+{
+ struct cond_node *node, *last = NULL;
+ u32 buf[1], i, len;
+ int rc;
+
+ rc = next_entry(buf, fp, sizeof buf);
+ if (rc < 0)
+ return -1;
+
+ len = le32_to_cpu(buf[0]);
+
+ for (i = 0; i < len; i++) {
+ node = kmalloc(sizeof(struct cond_node), GFP_KERNEL);
+ if (!node)
+ goto err;
+ memset(node, 0, sizeof(struct cond_node));
+
+ if (cond_read_node(p, node, fp) != 0)
+ goto err;
+
+ if (i == 0) {
+ p->cond_list = node;
+ } else {
+ last->next = node;
+ }
+ last = node;
+ }
+ return 0;
+err:
+ cond_list_destroy(p->cond_list);
+ return -1;
+}
+
+/* Determine whether additional permissions are granted by the conditional
+ * av table, and if so, add them to the result
+ */
+void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd)
+{
+ struct avtab_node *node;
+
+ if(!ctab || !key || !avd)
+ return;
+
+ for(node = avtab_search_node(ctab, key, AVTAB_AV); node != NULL;
+ node = avtab_search_node_next(node, AVTAB_AV)) {
+ if ( (__u32) (AVTAB_ALLOWED|AVTAB_ENABLED) ==
+ (node->datum.specified & (AVTAB_ALLOWED|AVTAB_ENABLED)))
+ avd->allowed |= avtab_allowed(&node->datum);
+ if ( (__u32) (AVTAB_AUDITDENY|AVTAB_ENABLED) ==
+ (node->datum.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED)))
+ /* Since a '0' in an auditdeny mask represents a
+ * permission we do NOT want to audit (dontaudit), we use
+ * the '&' operand to ensure that all '0's in the mask
+ * are retained (much unlike the allow and auditallow cases).
+ */
+ avd->auditdeny &= avtab_auditdeny(&node->datum);
+ if ( (__u32) (AVTAB_AUDITALLOW|AVTAB_ENABLED) ==
+ (node->datum.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED)))
+ avd->auditallow |= avtab_auditallow(&node->datum);
+ }
+ return;
+}
diff --git a/security/selinux/ss/conditional.h b/security/selinux/ss/conditional.h
new file mode 100644
index 00000000000..f3a1fc6e5d6
--- /dev/null
+++ b/security/selinux/ss/conditional.h
@@ -0,0 +1,77 @@
+/* Authors: Karl MacMillan <kmacmillan@tresys.com>
+ * Frank Mayer <mayerf@tresys.com>
+ *
+ * Copyright (C) 2003 - 2004 Tresys Technology, LLC
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ */
+
+#ifndef _CONDITIONAL_H_
+#define _CONDITIONAL_H_
+
+#include "avtab.h"
+#include "symtab.h"
+#include "policydb.h"
+
+#define COND_EXPR_MAXDEPTH 10
+
+/*
+ * A conditional expression is a list of operators and operands
+ * in reverse polish notation.
+ */
+struct cond_expr {
+#define COND_BOOL 1 /* plain bool */
+#define COND_NOT 2 /* !bool */
+#define COND_OR 3 /* bool || bool */
+#define COND_AND 4 /* bool && bool */
+#define COND_XOR 5 /* bool ^ bool */
+#define COND_EQ 6 /* bool == bool */
+#define COND_NEQ 7 /* bool != bool */
+#define COND_LAST 8
+ __u32 expr_type;
+ __u32 bool;
+ struct cond_expr *next;
+};
+
+/*
+ * Each cond_node contains a list of rules to be enabled/disabled
+ * depending on the current value of the conditional expression. This
+ * struct is for that list.
+ */
+struct cond_av_list {
+ struct avtab_node *node;
+ struct cond_av_list *next;
+};
+
+/*
+ * A cond node represents a conditional block in a policy. It
+ * contains a conditional expression, the current state of the expression,
+ * two lists of rules to enable/disable depending on the value of the
+ * expression (the true list corresponds to if and the false list corresponds
+ * to else)..
+ */
+struct cond_node {
+ int cur_state;
+ struct cond_expr *expr;
+ struct cond_av_list *true_list;
+ struct cond_av_list *false_list;
+ struct cond_node *next;
+};
+
+int cond_policydb_init(struct policydb* p);
+void cond_policydb_destroy(struct policydb* p);
+
+int cond_init_bool_indexes(struct policydb* p);
+int cond_destroy_bool(void *key, void *datum, void *p);
+
+int cond_index_bool(void *key, void *datum, void *datap);
+
+int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp);
+int cond_read_list(struct policydb *p, void *fp);
+
+void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd);
+
+int evaluate_cond_node(struct policydb *p, struct cond_node *node);
+
+#endif /* _CONDITIONAL_H_ */
diff --git a/security/selinux/ss/constraint.h b/security/selinux/ss/constraint.h
new file mode 100644
index 00000000000..149dda731fd
--- /dev/null
+++ b/security/selinux/ss/constraint.h
@@ -0,0 +1,61 @@
+/*
+ * A constraint is a condition that must be satisfied in
+ * order for one or more permissions to be granted.
+ * Constraints are used to impose additional restrictions
+ * beyond the type-based rules in `te' or the role-based
+ * transition rules in `rbac'. Constraints are typically
+ * used to prevent a process from transitioning to a new user
+ * identity or role unless it is in a privileged type.
+ * Constraints are likewise typically used to prevent a
+ * process from labeling an object with a different user
+ * identity.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#ifndef _SS_CONSTRAINT_H_
+#define _SS_CONSTRAINT_H_
+
+#include "ebitmap.h"
+
+#define CEXPR_MAXDEPTH 5
+
+struct constraint_expr {
+#define CEXPR_NOT 1 /* not expr */
+#define CEXPR_AND 2 /* expr and expr */
+#define CEXPR_OR 3 /* expr or expr */
+#define CEXPR_ATTR 4 /* attr op attr */
+#define CEXPR_NAMES 5 /* attr op names */
+ u32 expr_type; /* expression type */
+
+#define CEXPR_USER 1 /* user */
+#define CEXPR_ROLE 2 /* role */
+#define CEXPR_TYPE 4 /* type */
+#define CEXPR_TARGET 8 /* target if set, source otherwise */
+#define CEXPR_XTARGET 16 /* special 3rd target for validatetrans rule */
+#define CEXPR_L1L2 32 /* low level 1 vs. low level 2 */
+#define CEXPR_L1H2 64 /* low level 1 vs. high level 2 */
+#define CEXPR_H1L2 128 /* high level 1 vs. low level 2 */
+#define CEXPR_H1H2 256 /* high level 1 vs. high level 2 */
+#define CEXPR_L1H1 512 /* low level 1 vs. high level 1 */
+#define CEXPR_L2H2 1024 /* low level 2 vs. high level 2 */
+ u32 attr; /* attribute */
+
+#define CEXPR_EQ 1 /* == or eq */
+#define CEXPR_NEQ 2 /* != */
+#define CEXPR_DOM 3 /* dom */
+#define CEXPR_DOMBY 4 /* domby */
+#define CEXPR_INCOMP 5 /* incomp */
+ u32 op; /* operator */
+
+ struct ebitmap names; /* names */
+
+ struct constraint_expr *next; /* next expression */
+};
+
+struct constraint_node {
+ u32 permissions; /* constrained permissions */
+ struct constraint_expr *expr; /* constraint on permissions */
+ struct constraint_node *next; /* next constraint */
+};
+
+#endif /* _SS_CONSTRAINT_H_ */
diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h
new file mode 100644
index 00000000000..0562bacb7b9
--- /dev/null
+++ b/security/selinux/ss/context.h
@@ -0,0 +1,107 @@
+/*
+ * A security context is a set of security attributes
+ * associated with each subject and object controlled
+ * by the security policy. Security contexts are
+ * externally represented as variable-length strings
+ * that can be interpreted by a user or application
+ * with an understanding of the security policy.
+ * Internally, the security server uses a simple
+ * structure. This structure is private to the
+ * security server and can be changed without affecting
+ * clients of the security server.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#ifndef _SS_CONTEXT_H_
+#define _SS_CONTEXT_H_
+
+#include "ebitmap.h"
+#include "mls_types.h"
+#include "security.h"
+
+/*
+ * A security context consists of an authenticated user
+ * identity, a role, a type and a MLS range.
+ */
+struct context {
+ u32 user;
+ u32 role;
+ u32 type;
+ struct mls_range range;
+};
+
+static inline void mls_context_init(struct context *c)
+{
+ memset(&c->range, 0, sizeof(c->range));
+}
+
+static inline int mls_context_cpy(struct context *dst, struct context *src)
+{
+ int rc;
+
+ if (!selinux_mls_enabled)
+ return 0;
+
+ dst->range.level[0].sens = src->range.level[0].sens;
+ rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[0].cat);
+ if (rc)
+ goto out;
+
+ dst->range.level[1].sens = src->range.level[1].sens;
+ rc = ebitmap_cpy(&dst->range.level[1].cat, &src->range.level[1].cat);
+ if (rc)
+ ebitmap_destroy(&dst->range.level[0].cat);
+out:
+ return rc;
+}
+
+static inline int mls_context_cmp(struct context *c1, struct context *c2)
+{
+ if (!selinux_mls_enabled)
+ return 1;
+
+ return ((c1->range.level[0].sens == c2->range.level[0].sens) &&
+ ebitmap_cmp(&c1->range.level[0].cat,&c2->range.level[0].cat) &&
+ (c1->range.level[1].sens == c2->range.level[1].sens) &&
+ ebitmap_cmp(&c1->range.level[1].cat,&c2->range.level[1].cat));
+}
+
+static inline void mls_context_destroy(struct context *c)
+{
+ if (!selinux_mls_enabled)
+ return;
+
+ ebitmap_destroy(&c->range.level[0].cat);
+ ebitmap_destroy(&c->range.level[1].cat);
+ mls_context_init(c);
+}
+
+static inline void context_init(struct context *c)
+{
+ memset(c, 0, sizeof(*c));
+}
+
+static inline int context_cpy(struct context *dst, struct context *src)
+{
+ dst->user = src->user;
+ dst->role = src->role;
+ dst->type = src->type;
+ return mls_context_cpy(dst, src);
+}
+
+static inline void context_destroy(struct context *c)
+{
+ c->user = c->role = c->type = 0;
+ mls_context_destroy(c);
+}
+
+static inline int context_cmp(struct context *c1, struct context *c2)
+{
+ return ((c1->user == c2->user) &&
+ (c1->role == c2->role) &&
+ (c1->type == c2->type) &&
+ mls_context_cmp(c1, c2));
+}
+
+#endif /* _SS_CONTEXT_H_ */
+
diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c
new file mode 100644
index 00000000000..d8ce9cc0b9f
--- /dev/null
+++ b/security/selinux/ss/ebitmap.c
@@ -0,0 +1,293 @@
+/*
+ * Implementation of the extensible bitmap type.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include "ebitmap.h"
+#include "policydb.h"
+
+int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2)
+{
+ struct ebitmap_node *n1, *n2;
+
+ if (e1->highbit != e2->highbit)
+ return 0;
+
+ n1 = e1->node;
+ n2 = e2->node;
+ while (n1 && n2 &&
+ (n1->startbit == n2->startbit) &&
+ (n1->map == n2->map)) {
+ n1 = n1->next;
+ n2 = n2->next;
+ }
+
+ if (n1 || n2)
+ return 0;
+
+ return 1;
+}
+
+int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src)
+{
+ struct ebitmap_node *n, *new, *prev;
+
+ ebitmap_init(dst);
+ n = src->node;
+ prev = NULL;
+ while (n) {
+ new = kmalloc(sizeof(*new), GFP_ATOMIC);
+ if (!new) {
+ ebitmap_destroy(dst);
+ return -ENOMEM;
+ }
+ memset(new, 0, sizeof(*new));
+ new->startbit = n->startbit;
+ new->map = n->map;
+ new->next = NULL;
+ if (prev)
+ prev->next = new;
+ else
+ dst->node = new;
+ prev = new;
+ n = n->next;
+ }
+
+ dst->highbit = src->highbit;
+ return 0;
+}
+
+int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2)
+{
+ struct ebitmap_node *n1, *n2;
+
+ if (e1->highbit < e2->highbit)
+ return 0;
+
+ n1 = e1->node;
+ n2 = e2->node;
+ while (n1 && n2 && (n1->startbit <= n2->startbit)) {
+ if (n1->startbit < n2->startbit) {
+ n1 = n1->next;
+ continue;
+ }
+ if ((n1->map & n2->map) != n2->map)
+ return 0;
+
+ n1 = n1->next;
+ n2 = n2->next;
+ }
+
+ if (n2)
+ return 0;
+
+ return 1;
+}
+
+int ebitmap_get_bit(struct ebitmap *e, unsigned long bit)
+{
+ struct ebitmap_node *n;
+
+ if (e->highbit < bit)
+ return 0;
+
+ n = e->node;
+ while (n && (n->startbit <= bit)) {
+ if ((n->startbit + MAPSIZE) > bit) {
+ if (n->map & (MAPBIT << (bit - n->startbit)))
+ return 1;
+ else
+ return 0;
+ }
+ n = n->next;
+ }
+
+ return 0;
+}
+
+int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value)
+{
+ struct ebitmap_node *n, *prev, *new;
+
+ prev = NULL;
+ n = e->node;
+ while (n && n->startbit <= bit) {
+ if ((n->startbit + MAPSIZE) > bit) {
+ if (value) {
+ n->map |= (MAPBIT << (bit - n->startbit));
+ } else {
+ n->map &= ~(MAPBIT << (bit - n->startbit));
+ if (!n->map) {
+ /* drop this node from the bitmap */
+
+ if (!n->next) {
+ /*
+ * this was the highest map
+ * within the bitmap
+ */
+ if (prev)
+ e->highbit = prev->startbit + MAPSIZE;
+ else
+ e->highbit = 0;
+ }
+ if (prev)
+ prev->next = n->next;
+ else
+ e->node = n->next;
+
+ kfree(n);
+ }
+ }
+ return 0;
+ }
+ prev = n;
+ n = n->next;
+ }
+
+ if (!value)
+ return 0;
+
+ new = kmalloc(sizeof(*new), GFP_ATOMIC);
+ if (!new)
+ return -ENOMEM;
+ memset(new, 0, sizeof(*new));
+
+ new->startbit = bit & ~(MAPSIZE - 1);
+ new->map = (MAPBIT << (bit - new->startbit));
+
+ if (!n)
+ /* this node will be the highest map within the bitmap */
+ e->highbit = new->startbit + MAPSIZE;
+
+ if (prev) {
+ new->next = prev->next;
+ prev->next = new;
+ } else {
+ new->next = e->node;
+ e->node = new;
+ }
+
+ return 0;
+}
+
+void ebitmap_destroy(struct ebitmap *e)
+{
+ struct ebitmap_node *n, *temp;
+
+ if (!e)
+ return;
+
+ n = e->node;
+ while (n) {
+ temp = n;
+ n = n->next;
+ kfree(temp);
+ }
+
+ e->highbit = 0;
+ e->node = NULL;
+ return;
+}
+
+int ebitmap_read(struct ebitmap *e, void *fp)
+{
+ int rc;
+ struct ebitmap_node *n, *l;
+ u32 buf[3], mapsize, count, i;
+ u64 map;
+
+ ebitmap_init(e);
+
+ rc = next_entry(buf, fp, sizeof buf);
+ if (rc < 0)
+ goto out;
+
+ mapsize = le32_to_cpu(buf[0]);
+ e->highbit = le32_to_cpu(buf[1]);
+ count = le32_to_cpu(buf[2]);
+
+ if (mapsize != MAPSIZE) {
+ printk(KERN_ERR "security: ebitmap: map size %u does not "
+ "match my size %Zd (high bit was %d)\n", mapsize,
+ MAPSIZE, e->highbit);
+ goto bad;
+ }
+ if (!e->highbit) {
+ e->node = NULL;
+ goto ok;
+ }
+ if (e->highbit & (MAPSIZE - 1)) {
+ printk(KERN_ERR "security: ebitmap: high bit (%d) is not a "
+ "multiple of the map size (%Zd)\n", e->highbit, MAPSIZE);
+ goto bad;
+ }
+ l = NULL;
+ for (i = 0; i < count; i++) {
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0) {
+ printk(KERN_ERR "security: ebitmap: truncated map\n");
+ goto bad;
+ }
+ n = kmalloc(sizeof(*n), GFP_KERNEL);
+ if (!n) {
+ printk(KERN_ERR "security: ebitmap: out of memory\n");
+ rc = -ENOMEM;
+ goto bad;
+ }
+ memset(n, 0, sizeof(*n));
+
+ n->startbit = le32_to_cpu(buf[0]);
+
+ if (n->startbit & (MAPSIZE - 1)) {
+ printk(KERN_ERR "security: ebitmap start bit (%d) is "
+ "not a multiple of the map size (%Zd)\n",
+ n->startbit, MAPSIZE);
+ goto bad_free;
+ }
+ if (n->startbit > (e->highbit - MAPSIZE)) {
+ printk(KERN_ERR "security: ebitmap start bit (%d) is "
+ "beyond the end of the bitmap (%Zd)\n",
+ n->startbit, (e->highbit - MAPSIZE));
+ goto bad_free;
+ }
+ rc = next_entry(&map, fp, sizeof(u64));
+ if (rc < 0) {
+ printk(KERN_ERR "security: ebitmap: truncated map\n");
+ goto bad_free;
+ }
+ n->map = le64_to_cpu(map);
+
+ if (!n->map) {
+ printk(KERN_ERR "security: ebitmap: null map in "
+ "ebitmap (startbit %d)\n", n->startbit);
+ goto bad_free;
+ }
+ if (l) {
+ if (n->startbit <= l->startbit) {
+ printk(KERN_ERR "security: ebitmap: start "
+ "bit %d comes after start bit %d\n",
+ n->startbit, l->startbit);
+ goto bad_free;
+ }
+ l->next = n;
+ } else
+ e->node = n;
+
+ l = n;
+ }
+
+ok:
+ rc = 0;
+out:
+ return rc;
+bad_free:
+ kfree(n);
+bad:
+ if (!rc)
+ rc = -EINVAL;
+ ebitmap_destroy(e);
+ goto out;
+}
diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h
new file mode 100644
index 00000000000..471370233fd
--- /dev/null
+++ b/security/selinux/ss/ebitmap.h
@@ -0,0 +1,48 @@
+/*
+ * An extensible bitmap is a bitmap that supports an
+ * arbitrary number of bits. Extensible bitmaps are
+ * used to represent sets of values, such as types,
+ * roles, categories, and classes.
+ *
+ * Each extensible bitmap is implemented as a linked
+ * list of bitmap nodes, where each bitmap node has
+ * an explicitly specified starting bit position within
+ * the total bitmap.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#ifndef _SS_EBITMAP_H_
+#define _SS_EBITMAP_H_
+
+#define MAPTYPE u64 /* portion of bitmap in each node */
+#define MAPSIZE (sizeof(MAPTYPE) * 8) /* number of bits in node bitmap */
+#define MAPBIT 1ULL /* a bit in the node bitmap */
+
+struct ebitmap_node {
+ u32 startbit; /* starting position in the total bitmap */
+ MAPTYPE map; /* this node's portion of the bitmap */
+ struct ebitmap_node *next;
+};
+
+struct ebitmap {
+ struct ebitmap_node *node; /* first node in the bitmap */
+ u32 highbit; /* highest position in the total bitmap */
+};
+
+#define ebitmap_length(e) ((e)->highbit)
+#define ebitmap_startbit(e) ((e)->node ? (e)->node->startbit : 0)
+
+static inline void ebitmap_init(struct ebitmap *e)
+{
+ memset(e, 0, sizeof(*e));
+}
+
+int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2);
+int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src);
+int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2);
+int ebitmap_get_bit(struct ebitmap *e, unsigned long bit);
+int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value);
+void ebitmap_destroy(struct ebitmap *e);
+int ebitmap_read(struct ebitmap *e, void *fp);
+
+#endif /* _SS_EBITMAP_H_ */
diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c
new file mode 100644
index 00000000000..26661fcc00c
--- /dev/null
+++ b/security/selinux/ss/hashtab.c
@@ -0,0 +1,167 @@
+/*
+ * Implementation of the hash table type.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include "hashtab.h"
+
+struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, void *key),
+ int (*keycmp)(struct hashtab *h, void *key1, void *key2),
+ u32 size)
+{
+ struct hashtab *p;
+ u32 i;
+
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
+ if (p == NULL)
+ return p;
+
+ memset(p, 0, sizeof(*p));
+ p->size = size;
+ p->nel = 0;
+ p->hash_value = hash_value;
+ p->keycmp = keycmp;
+ p->htable = kmalloc(sizeof(*(p->htable)) * size, GFP_KERNEL);
+ if (p->htable == NULL) {
+ kfree(p);
+ return NULL;
+ }
+
+ for (i = 0; i < size; i++)
+ p->htable[i] = NULL;
+
+ return p;
+}
+
+int hashtab_insert(struct hashtab *h, void *key, void *datum)
+{
+ u32 hvalue;
+ struct hashtab_node *prev, *cur, *newnode;
+
+ if (!h || h->nel == HASHTAB_MAX_NODES)
+ return -EINVAL;
+
+ hvalue = h->hash_value(h, key);
+ prev = NULL;
+ cur = h->htable[hvalue];
+ while (cur && h->keycmp(h, key, cur->key) > 0) {
+ prev = cur;
+ cur = cur->next;
+ }
+
+ if (cur && (h->keycmp(h, key, cur->key) == 0))
+ return -EEXIST;
+
+ newnode = kmalloc(sizeof(*newnode), GFP_KERNEL);
+ if (newnode == NULL)
+ return -ENOMEM;
+ memset(newnode, 0, sizeof(*newnode));
+ newnode->key = key;
+ newnode->datum = datum;
+ if (prev) {
+ newnode->next = prev->next;
+ prev->next = newnode;
+ } else {
+ newnode->next = h->htable[hvalue];
+ h->htable[hvalue] = newnode;
+ }
+
+ h->nel++;
+ return 0;
+}
+
+void *hashtab_search(struct hashtab *h, void *key)
+{
+ u32 hvalue;
+ struct hashtab_node *cur;
+
+ if (!h)
+ return NULL;
+
+ hvalue = h->hash_value(h, key);
+ cur = h->htable[hvalue];
+ while (cur != NULL && h->keycmp(h, key, cur->key) > 0)
+ cur = cur->next;
+
+ if (cur == NULL || (h->keycmp(h, key, cur->key) != 0))
+ return NULL;
+
+ return cur->datum;
+}
+
+void hashtab_destroy(struct hashtab *h)
+{
+ u32 i;
+ struct hashtab_node *cur, *temp;
+
+ if (!h)
+ return;
+
+ for (i = 0; i < h->size; i++) {
+ cur = h->htable[i];
+ while (cur != NULL) {
+ temp = cur;
+ cur = cur->next;
+ kfree(temp);
+ }
+ h->htable[i] = NULL;
+ }
+
+ kfree(h->htable);
+ h->htable = NULL;
+
+ kfree(h);
+}
+
+int hashtab_map(struct hashtab *h,
+ int (*apply)(void *k, void *d, void *args),
+ void *args)
+{
+ u32 i;
+ int ret;
+ struct hashtab_node *cur;
+
+ if (!h)
+ return 0;
+
+ for (i = 0; i < h->size; i++) {
+ cur = h->htable[i];
+ while (cur != NULL) {
+ ret = apply(cur->key, cur->datum, args);
+ if (ret)
+ return ret;
+ cur = cur->next;
+ }
+ }
+ return 0;
+}
+
+
+void hashtab_stat(struct hashtab *h, struct hashtab_info *info)
+{
+ u32 i, chain_len, slots_used, max_chain_len;
+ struct hashtab_node *cur;
+
+ slots_used = 0;
+ max_chain_len = 0;
+ for (slots_used = max_chain_len = i = 0; i < h->size; i++) {
+ cur = h->htable[i];
+ if (cur) {
+ slots_used++;
+ chain_len = 0;
+ while (cur) {
+ chain_len++;
+ cur = cur->next;
+ }
+
+ if (chain_len > max_chain_len)
+ max_chain_len = chain_len;
+ }
+ }
+
+ info->slots_used = slots_used;
+ info->max_chain_len = max_chain_len;
+}
diff --git a/security/selinux/ss/hashtab.h b/security/selinux/ss/hashtab.h
new file mode 100644
index 00000000000..4cc85816a71
--- /dev/null
+++ b/security/selinux/ss/hashtab.h
@@ -0,0 +1,87 @@
+/*
+ * A hash table (hashtab) maintains associations between
+ * key values and datum values. The type of the key values
+ * and the type of the datum values is arbitrary. The
+ * functions for hash computation and key comparison are
+ * provided by the creator of the table.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#ifndef _SS_HASHTAB_H_
+#define _SS_HASHTAB_H_
+
+#define HASHTAB_MAX_NODES 0xffffffff
+
+struct hashtab_node {
+ void *key;
+ void *datum;
+ struct hashtab_node *next;
+};
+
+struct hashtab {
+ struct hashtab_node **htable; /* hash table */
+ u32 size; /* number of slots in hash table */
+ u32 nel; /* number of elements in hash table */
+ u32 (*hash_value)(struct hashtab *h, void *key);
+ /* hash function */
+ int (*keycmp)(struct hashtab *h, void *key1, void *key2);
+ /* key comparison function */
+};
+
+struct hashtab_info {
+ u32 slots_used;
+ u32 max_chain_len;
+};
+
+/*
+ * Creates a new hash table with the specified characteristics.
+ *
+ * Returns NULL if insufficent space is available or
+ * the new hash table otherwise.
+ */
+struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, void *key),
+ int (*keycmp)(struct hashtab *h, void *key1, void *key2),
+ u32 size);
+
+/*
+ * Inserts the specified (key, datum) pair into the specified hash table.
+ *
+ * Returns -ENOMEM on memory allocation error,
+ * -EEXIST if there is already an entry with the same key,
+ * -EINVAL for general errors or
+ * 0 otherwise.
+ */
+int hashtab_insert(struct hashtab *h, void *k, void *d);
+
+/*
+ * Searches for the entry with the specified key in the hash table.
+ *
+ * Returns NULL if no entry has the specified key or
+ * the datum of the entry otherwise.
+ */
+void *hashtab_search(struct hashtab *h, void *k);
+
+/*
+ * Destroys the specified hash table.
+ */
+void hashtab_destroy(struct hashtab *h);
+
+/*
+ * Applies the specified apply function to (key,datum,args)
+ * for each entry in the specified hash table.
+ *
+ * The order in which the function is applied to the entries
+ * is dependent upon the internal structure of the hash table.
+ *
+ * If apply returns a non-zero status, then hashtab_map will cease
+ * iterating through the hash table and will propagate the error
+ * return to its caller.
+ */
+int hashtab_map(struct hashtab *h,
+ int (*apply)(void *k, void *d, void *args),
+ void *args);
+
+/* Fill info with some hash table statistics */
+void hashtab_stat(struct hashtab *h, struct hashtab_info *info);
+
+#endif /* _SS_HASHTAB_H */
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
new file mode 100644
index 00000000000..756036bcc24
--- /dev/null
+++ b/security/selinux/ss/mls.c
@@ -0,0 +1,527 @@
+/*
+ * Implementation of the multi-level security (MLS) policy.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+/*
+ * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
+ *
+ * Support for enhanced MLS infrastructure.
+ *
+ * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include "mls.h"
+#include "policydb.h"
+#include "services.h"
+
+/*
+ * Return the length in bytes for the MLS fields of the
+ * security context string representation of `context'.
+ */
+int mls_compute_context_len(struct context * context)
+{
+ int i, l, len, range;
+
+ if (!selinux_mls_enabled)
+ return 0;
+
+ len = 1; /* for the beginning ":" */
+ for (l = 0; l < 2; l++) {
+ range = 0;
+ len += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
+
+ for (i = 1; i <= ebitmap_length(&context->range.level[l].cat); i++) {
+ if (ebitmap_get_bit(&context->range.level[l].cat, i - 1)) {
+ if (range) {
+ range++;
+ continue;
+ }
+
+ len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1;
+ range++;
+ } else {
+ if (range > 1)
+ len += strlen(policydb.p_cat_val_to_name[i - 2]) + 1;
+ range = 0;
+ }
+ }
+ /* Handle case where last category is the end of range */
+ if (range > 1)
+ len += strlen(policydb.p_cat_val_to_name[i - 2]) + 1;
+
+ if (l == 0) {
+ if (mls_level_eq(&context->range.level[0],
+ &context->range.level[1]))
+ break;
+ else
+ len++;
+ }
+ }
+
+ return len;
+}
+
+/*
+ * Write the security context string representation of
+ * the MLS fields of `context' into the string `*scontext'.
+ * Update `*scontext' to point to the end of the MLS fields.
+ */
+void mls_sid_to_context(struct context *context,
+ char **scontext)
+{
+ char *scontextp;
+ int i, l, range, wrote_sep;
+
+ if (!selinux_mls_enabled)
+ return;
+
+ scontextp = *scontext;
+
+ *scontextp = ':';
+ scontextp++;
+
+ for (l = 0; l < 2; l++) {
+ range = 0;
+ wrote_sep = 0;
+ strcpy(scontextp,
+ policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
+ scontextp += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
+
+ /* categories */
+ for (i = 1; i <= ebitmap_length(&context->range.level[l].cat); i++) {
+ if (ebitmap_get_bit(&context->range.level[l].cat, i - 1)) {
+ if (range) {
+ range++;
+ continue;
+ }
+
+ if (!wrote_sep) {
+ *scontextp++ = ':';
+ wrote_sep = 1;
+ } else
+ *scontextp++ = ',';
+ strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]);
+ scontextp += strlen(policydb.p_cat_val_to_name[i - 1]);
+ range++;
+ } else {
+ if (range > 1) {
+ if (range > 2)
+ *scontextp++ = '.';
+ else
+ *scontextp++ = ',';
+
+ strcpy(scontextp, policydb.p_cat_val_to_name[i - 2]);
+ scontextp += strlen(policydb.p_cat_val_to_name[i - 2]);
+ }
+ range = 0;
+ }
+ }
+
+ /* Handle case where last category is the end of range */
+ if (range > 1) {
+ if (range > 2)
+ *scontextp++ = '.';
+ else
+ *scontextp++ = ',';
+
+ strcpy(scontextp, policydb.p_cat_val_to_name[i - 2]);
+ scontextp += strlen(policydb.p_cat_val_to_name[i - 2]);
+ }
+
+ if (l == 0) {
+ if (mls_level_eq(&context->range.level[0],
+ &context->range.level[1]))
+ break;
+ else {
+ *scontextp = '-';
+ scontextp++;
+ }
+ }
+ }
+
+ *scontext = scontextp;
+ return;
+}
+
+/*
+ * Return 1 if the MLS fields in the security context
+ * structure `c' are valid. Return 0 otherwise.
+ */
+int mls_context_isvalid(struct policydb *p, struct context *c)
+{
+ struct level_datum *levdatum;
+ struct user_datum *usrdatum;
+ int i, l;
+
+ if (!selinux_mls_enabled)
+ return 1;
+
+ /*
+ * MLS range validity checks: high must dominate low, low level must
+ * be valid (category set <-> sensitivity check), and high level must
+ * be valid (category set <-> sensitivity check)
+ */
+ if (!mls_level_dom(&c->range.level[1], &c->range.level[0]))
+ /* High does not dominate low. */
+ return 0;
+
+ for (l = 0; l < 2; l++) {
+ if (!c->range.level[l].sens || c->range.level[l].sens > p->p_levels.nprim)
+ return 0;
+ levdatum = hashtab_search(p->p_levels.table,
+ p->p_sens_val_to_name[c->range.level[l].sens - 1]);
+ if (!levdatum)
+ return 0;
+
+ for (i = 1; i <= ebitmap_length(&c->range.level[l].cat); i++) {
+ if (ebitmap_get_bit(&c->range.level[l].cat, i - 1)) {
+ if (i > p->p_cats.nprim)
+ return 0;
+ if (!ebitmap_get_bit(&levdatum->level->cat, i - 1))
+ /*
+ * Category may not be associated with
+ * sensitivity in low level.
+ */
+ return 0;
+ }
+ }
+ }
+
+ if (c->role == OBJECT_R_VAL)
+ return 1;
+
+ /*
+ * User must be authorized for the MLS range.
+ */
+ if (!c->user || c->user > p->p_users.nprim)
+ return 0;
+ usrdatum = p->user_val_to_struct[c->user - 1];
+ if (!mls_range_contains(usrdatum->range, c->range))
+ return 0; /* user may not be associated with range */
+
+ return 1;
+}
+
+/*
+ * Set the MLS fields in the security context structure
+ * `context' based on the string representation in
+ * the string `*scontext'. Update `*scontext' to
+ * point to the end of the string representation of
+ * the MLS fields.
+ *
+ * This function modifies the string in place, inserting
+ * NULL characters to terminate the MLS fields.
+ */
+int mls_context_to_sid(char oldc,
+ char **scontext,
+ struct context *context)
+{
+
+ char delim;
+ char *scontextp, *p, *rngptr;
+ struct level_datum *levdatum;
+ struct cat_datum *catdatum, *rngdatum;
+ int l, rc = -EINVAL;
+
+ if (!selinux_mls_enabled)
+ return 0;
+
+ /* No MLS component to the security context. */
+ if (!oldc)
+ goto out;
+
+ /* Extract low sensitivity. */
+ scontextp = p = *scontext;
+ while (*p && *p != ':' && *p != '-')
+ p++;
+
+ delim = *p;
+ if (delim != 0)
+ *p++ = 0;
+
+ for (l = 0; l < 2; l++) {
+ levdatum = hashtab_search(policydb.p_levels.table, scontextp);
+ if (!levdatum) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ context->range.level[l].sens = levdatum->level->sens;
+
+ if (delim == ':') {
+ /* Extract category set. */
+ while (1) {
+ scontextp = p;
+ while (*p && *p != ',' && *p != '-')
+ p++;
+ delim = *p;
+ if (delim != 0)
+ *p++ = 0;
+
+ /* Separate into range if exists */
+ if ((rngptr = strchr(scontextp, '.')) != NULL) {
+ /* Remove '.' */
+ *rngptr++ = 0;
+ }
+
+ catdatum = hashtab_search(policydb.p_cats.table,
+ scontextp);
+ if (!catdatum) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ rc = ebitmap_set_bit(&context->range.level[l].cat,
+ catdatum->value - 1, 1);
+ if (rc)
+ goto out;
+
+ /* If range, set all categories in range */
+ if (rngptr) {
+ int i;
+
+ rngdatum = hashtab_search(policydb.p_cats.table, rngptr);
+ if (!rngdatum) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (catdatum->value >= rngdatum->value) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ for (i = catdatum->value; i < rngdatum->value; i++) {
+ rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1);
+ if (rc)
+ goto out;
+ }
+ }
+
+ if (delim != ',')
+ break;
+ }
+ }
+ if (delim == '-') {
+ /* Extract high sensitivity. */
+ scontextp = p;
+ while (*p && *p != ':')
+ p++;
+
+ delim = *p;
+ if (delim != 0)
+ *p++ = 0;
+ } else
+ break;
+ }
+
+ if (l == 0) {
+ context->range.level[1].sens = context->range.level[0].sens;
+ rc = ebitmap_cpy(&context->range.level[1].cat,
+ &context->range.level[0].cat);
+ if (rc)
+ goto out;
+ }
+ *scontext = ++p;
+ rc = 0;
+out:
+ return rc;
+}
+
+/*
+ * Copies the MLS range from `src' into `dst'.
+ */
+static inline int mls_copy_context(struct context *dst,
+ struct context *src)
+{
+ int l, rc = 0;
+
+ /* Copy the MLS range from the source context */
+ for (l = 0; l < 2; l++) {
+ dst->range.level[l].sens = src->range.level[l].sens;
+ rc = ebitmap_cpy(&dst->range.level[l].cat,
+ &src->range.level[l].cat);
+ if (rc)
+ break;
+ }
+
+ return rc;
+}
+
+/*
+ * Copies the effective MLS range from `src' into `dst'.
+ */
+static inline int mls_scopy_context(struct context *dst,
+ struct context *src)
+{
+ int l, rc = 0;
+
+ /* Copy the MLS range from the source context */
+ for (l = 0; l < 2; l++) {
+ dst->range.level[l].sens = src->range.level[0].sens;
+ rc = ebitmap_cpy(&dst->range.level[l].cat,
+ &src->range.level[0].cat);
+ if (rc)
+ break;
+ }
+
+ return rc;
+}
+
+/*
+ * Copies the MLS range `range' into `context'.
+ */
+static inline int mls_range_set(struct context *context,
+ struct mls_range *range)
+{
+ int l, rc = 0;
+
+ /* Copy the MLS range into the context */
+ for (l = 0; l < 2; l++) {
+ context->range.level[l].sens = range->level[l].sens;
+ rc = ebitmap_cpy(&context->range.level[l].cat,
+ &range->level[l].cat);
+ if (rc)
+ break;
+ }
+
+ return rc;
+}
+
+int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
+ struct context *usercon)
+{
+ if (selinux_mls_enabled) {
+ struct mls_level *fromcon_sen = &(fromcon->range.level[0]);
+ struct mls_level *fromcon_clr = &(fromcon->range.level[1]);
+ struct mls_level *user_low = &(user->range.level[0]);
+ struct mls_level *user_clr = &(user->range.level[1]);
+ struct mls_level *user_def = &(user->dfltlevel);
+ struct mls_level *usercon_sen = &(usercon->range.level[0]);
+ struct mls_level *usercon_clr = &(usercon->range.level[1]);
+
+ /* Honor the user's default level if we can */
+ if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) {
+ *usercon_sen = *user_def;
+ } else if (mls_level_between(fromcon_sen, user_def, user_clr)) {
+ *usercon_sen = *fromcon_sen;
+ } else if (mls_level_between(fromcon_clr, user_low, user_def)) {
+ *usercon_sen = *user_low;
+ } else
+ return -EINVAL;
+
+ /* Lower the clearance of available contexts
+ if the clearance of "fromcon" is lower than
+ that of the user's default clearance (but
+ only if the "fromcon" clearance dominates
+ the user's computed sensitivity level) */
+ if (mls_level_dom(user_clr, fromcon_clr)) {
+ *usercon_clr = *fromcon_clr;
+ } else if (mls_level_dom(fromcon_clr, user_clr)) {
+ *usercon_clr = *user_clr;
+ } else
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Convert the MLS fields in the security context
+ * structure `c' from the values specified in the
+ * policy `oldp' to the values specified in the policy `newp'.
+ */
+int mls_convert_context(struct policydb *oldp,
+ struct policydb *newp,
+ struct context *c)
+{
+ struct level_datum *levdatum;
+ struct cat_datum *catdatum;
+ struct ebitmap bitmap;
+ int l, i;
+
+ if (!selinux_mls_enabled)
+ return 0;
+
+ for (l = 0; l < 2; l++) {
+ levdatum = hashtab_search(newp->p_levels.table,
+ oldp->p_sens_val_to_name[c->range.level[l].sens - 1]);
+
+ if (!levdatum)
+ return -EINVAL;
+ c->range.level[l].sens = levdatum->level->sens;
+
+ ebitmap_init(&bitmap);
+ for (i = 1; i <= ebitmap_length(&c->range.level[l].cat); i++) {
+ if (ebitmap_get_bit(&c->range.level[l].cat, i - 1)) {
+ int rc;
+
+ catdatum = hashtab_search(newp->p_cats.table,
+ oldp->p_cat_val_to_name[i - 1]);
+ if (!catdatum)
+ return -EINVAL;
+ rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1);
+ if (rc)
+ return rc;
+ }
+ }
+ ebitmap_destroy(&c->range.level[l].cat);
+ c->range.level[l].cat = bitmap;
+ }
+
+ return 0;
+}
+
+int mls_compute_sid(struct context *scontext,
+ struct context *tcontext,
+ u16 tclass,
+ u32 specified,
+ struct context *newcontext)
+{
+ if (!selinux_mls_enabled)
+ return 0;
+
+ switch (specified) {
+ case AVTAB_TRANSITION:
+ if (tclass == SECCLASS_PROCESS) {
+ struct range_trans *rangetr;
+ /* Look for a range transition rule. */
+ for (rangetr = policydb.range_tr; rangetr;
+ rangetr = rangetr->next) {
+ if (rangetr->dom == scontext->type &&
+ rangetr->type == tcontext->type) {
+ /* Set the range from the rule */
+ return mls_range_set(newcontext,
+ &rangetr->range);
+ }
+ }
+ }
+ /* Fallthrough */
+ case AVTAB_CHANGE:
+ if (tclass == SECCLASS_PROCESS)
+ /* Use the process MLS attributes. */
+ return mls_copy_context(newcontext, scontext);
+ else
+ /* Use the process effective MLS attributes. */
+ return mls_scopy_context(newcontext, scontext);
+ case AVTAB_MEMBER:
+ /* Only polyinstantiate the MLS attributes if
+ the type is being polyinstantiated */
+ if (newcontext->type != tcontext->type) {
+ /* Use the process effective MLS attributes. */
+ return mls_scopy_context(newcontext, scontext);
+ } else {
+ /* Use the related object MLS attributes. */
+ return mls_copy_context(newcontext, tcontext);
+ }
+ default:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h
new file mode 100644
index 00000000000..0d37beaa85e
--- /dev/null
+++ b/security/selinux/ss/mls.h
@@ -0,0 +1,42 @@
+/*
+ * Multi-level security (MLS) policy operations.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+/*
+ * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
+ *
+ * Support for enhanced MLS infrastructure.
+ *
+ * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
+ */
+
+#ifndef _SS_MLS_H_
+#define _SS_MLS_H_
+
+#include "context.h"
+#include "policydb.h"
+
+int mls_compute_context_len(struct context *context);
+void mls_sid_to_context(struct context *context, char **scontext);
+int mls_context_isvalid(struct policydb *p, struct context *c);
+
+int mls_context_to_sid(char oldc,
+ char **scontext,
+ struct context *context);
+
+int mls_convert_context(struct policydb *oldp,
+ struct policydb *newp,
+ struct context *context);
+
+int mls_compute_sid(struct context *scontext,
+ struct context *tcontext,
+ u16 tclass,
+ u32 specified,
+ struct context *newcontext);
+
+int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
+ struct context *usercon);
+
+#endif /* _SS_MLS_H */
+
diff --git a/security/selinux/ss/mls_types.h b/security/selinux/ss/mls_types.h
new file mode 100644
index 00000000000..0c692d58d48
--- /dev/null
+++ b/security/selinux/ss/mls_types.h
@@ -0,0 +1,56 @@
+/*
+ * Type definitions for the multi-level security (MLS) policy.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+/*
+ * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
+ *
+ * Support for enhanced MLS infrastructure.
+ *
+ * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
+ */
+
+#ifndef _SS_MLS_TYPES_H_
+#define _SS_MLS_TYPES_H_
+
+#include "security.h"
+
+struct mls_level {
+ u32 sens; /* sensitivity */
+ struct ebitmap cat; /* category set */
+};
+
+struct mls_range {
+ struct mls_level level[2]; /* low == level[0], high == level[1] */
+};
+
+static inline int mls_level_eq(struct mls_level *l1, struct mls_level *l2)
+{
+ if (!selinux_mls_enabled)
+ return 1;
+
+ return ((l1->sens == l2->sens) &&
+ ebitmap_cmp(&l1->cat, &l2->cat));
+}
+
+static inline int mls_level_dom(struct mls_level *l1, struct mls_level *l2)
+{
+ if (!selinux_mls_enabled)
+ return 1;
+
+ return ((l1->sens >= l2->sens) &&
+ ebitmap_contains(&l1->cat, &l2->cat));
+}
+
+#define mls_level_incomp(l1, l2) \
+(!mls_level_dom((l1), (l2)) && !mls_level_dom((l2), (l1)))
+
+#define mls_level_between(l1, l2, l3) \
+(mls_level_dom((l1), (l2)) && mls_level_dom((l3), (l1)))
+
+#define mls_range_contains(r1, r2) \
+(mls_level_dom(&(r2).level[0], &(r1).level[0]) && \
+ mls_level_dom(&(r1).level[1], &(r2).level[1]))
+
+#endif /* _SS_MLS_TYPES_H_ */
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
new file mode 100644
index 00000000000..14190efbf33
--- /dev/null
+++ b/security/selinux/ss/policydb.c
@@ -0,0 +1,1843 @@
+/*
+ * Implementation of the policy database.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+
+/*
+ * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
+ *
+ * Support for enhanced MLS infrastructure.
+ *
+ * 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
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include "security.h"
+
+#include "policydb.h"
+#include "conditional.h"
+#include "mls.h"
+
+#define _DEBUG_HASHES
+
+#ifdef DEBUG_HASHES
+static char *symtab_name[SYM_NUM] = {
+ "common prefixes",
+ "classes",
+ "roles",
+ "types",
+ "users",
+ "bools",
+ "levels",
+ "categories",
+};
+#endif
+
+int selinux_mls_enabled = 0;
+
+static unsigned int symtab_sizes[SYM_NUM] = {
+ 2,
+ 32,
+ 16,
+ 512,
+ 128,
+ 16,
+ 16,
+ 16,
+};
+
+struct policydb_compat_info {
+ int version;
+ int sym_num;
+ int ocon_num;
+};
+
+/* These need to be updated if SYM_NUM or OCON_NUM changes */
+static struct policydb_compat_info policydb_compat[] = {
+ {
+ .version = POLICYDB_VERSION_BASE,
+ .sym_num = SYM_NUM - 3,
+ .ocon_num = OCON_NUM - 1,
+ },
+ {
+ .version = POLICYDB_VERSION_BOOL,
+ .sym_num = SYM_NUM - 2,
+ .ocon_num = OCON_NUM - 1,
+ },
+ {
+ .version = POLICYDB_VERSION_IPV6,
+ .sym_num = SYM_NUM - 2,
+ .ocon_num = OCON_NUM,
+ },
+ {
+ .version = POLICYDB_VERSION_NLCLASS,
+ .sym_num = SYM_NUM - 2,
+ .ocon_num = OCON_NUM,
+ },
+ {
+ .version = POLICYDB_VERSION_MLS,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
+ },
+};
+
+static struct policydb_compat_info *policydb_lookup_compat(int version)
+{
+ int i;
+ struct policydb_compat_info *info = NULL;
+
+ for (i = 0; i < sizeof(policydb_compat)/sizeof(*info); i++) {
+ if (policydb_compat[i].version == version) {
+ info = &policydb_compat[i];
+ break;
+ }
+ }
+ return info;
+}
+
+/*
+ * Initialize the role table.
+ */
+static int roles_init(struct policydb *p)
+{
+ char *key = NULL;
+ int rc;
+ struct role_datum *role;
+
+ role = kmalloc(sizeof(*role), GFP_KERNEL);
+ if (!role) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ memset(role, 0, sizeof(*role));
+ role->value = ++p->p_roles.nprim;
+ if (role->value != OBJECT_R_VAL) {
+ rc = -EINVAL;
+ goto out_free_role;
+ }
+ key = kmalloc(strlen(OBJECT_R)+1,GFP_KERNEL);
+ if (!key) {
+ rc = -ENOMEM;
+ goto out_free_role;
+ }
+ strcpy(key, OBJECT_R);
+ rc = hashtab_insert(p->p_roles.table, key, role);
+ if (rc)
+ goto out_free_key;
+out:
+ return rc;
+
+out_free_key:
+ kfree(key);
+out_free_role:
+ kfree(role);
+ goto out;
+}
+
+/*
+ * Initialize a policy database structure.
+ */
+static int policydb_init(struct policydb *p)
+{
+ int i, rc;
+
+ memset(p, 0, sizeof(*p));
+
+ for (i = 0; i < SYM_NUM; i++) {
+ rc = symtab_init(&p->symtab[i], symtab_sizes[i]);
+ if (rc)
+ goto out_free_symtab;
+ }
+
+ rc = avtab_init(&p->te_avtab);
+ if (rc)
+ goto out_free_symtab;
+
+ rc = roles_init(p);
+ if (rc)
+ goto out_free_avtab;
+
+ rc = cond_policydb_init(p);
+ if (rc)
+ goto out_free_avtab;
+
+out:
+ return rc;
+
+out_free_avtab:
+ avtab_destroy(&p->te_avtab);
+
+out_free_symtab:
+ for (i = 0; i < SYM_NUM; i++)
+ hashtab_destroy(p->symtab[i].table);
+ goto out;
+}
+
+/*
+ * The following *_index functions are used to
+ * define the val_to_name and val_to_struct arrays
+ * in a policy database structure. The val_to_name
+ * arrays are used when converting security context
+ * structures into string representations. The
+ * val_to_struct arrays are used when the attributes
+ * of a class, role, or user are needed.
+ */
+
+static int common_index(void *key, void *datum, void *datap)
+{
+ struct policydb *p;
+ struct common_datum *comdatum;
+
+ comdatum = datum;
+ p = datap;
+ if (!comdatum->value || comdatum->value > p->p_commons.nprim)
+ return -EINVAL;
+ p->p_common_val_to_name[comdatum->value - 1] = key;
+ return 0;
+}
+
+static int class_index(void *key, void *datum, void *datap)
+{
+ struct policydb *p;
+ struct class_datum *cladatum;
+
+ cladatum = datum;
+ p = datap;
+ if (!cladatum->value || cladatum->value > p->p_classes.nprim)
+ return -EINVAL;
+ p->p_class_val_to_name[cladatum->value - 1] = key;
+ p->class_val_to_struct[cladatum->value - 1] = cladatum;
+ return 0;
+}
+
+static int role_index(void *key, void *datum, void *datap)
+{
+ struct policydb *p;
+ struct role_datum *role;
+
+ role = datum;
+ p = datap;
+ if (!role->value || role->value > p->p_roles.nprim)
+ return -EINVAL;
+ p->p_role_val_to_name[role->value - 1] = key;
+ p->role_val_to_struct[role->value - 1] = role;
+ return 0;
+}
+
+static int type_index(void *key, void *datum, void *datap)
+{
+ struct policydb *p;
+ struct type_datum *typdatum;
+
+ typdatum = datum;
+ p = datap;
+
+ if (typdatum->primary) {
+ if (!typdatum->value || typdatum->value > p->p_types.nprim)
+ return -EINVAL;
+ p->p_type_val_to_name[typdatum->value - 1] = key;
+ }
+
+ return 0;
+}
+
+static int user_index(void *key, void *datum, void *datap)
+{
+ struct policydb *p;
+ struct user_datum *usrdatum;
+
+ usrdatum = datum;
+ p = datap;
+ if (!usrdatum->value || usrdatum->value > p->p_users.nprim)
+ return -EINVAL;
+ p->p_user_val_to_name[usrdatum->value - 1] = key;
+ p->user_val_to_struct[usrdatum->value - 1] = usrdatum;
+ return 0;
+}
+
+static int sens_index(void *key, void *datum, void *datap)
+{
+ struct policydb *p;
+ struct level_datum *levdatum;
+
+ levdatum = datum;
+ p = datap;
+
+ if (!levdatum->isalias) {
+ if (!levdatum->level->sens ||
+ levdatum->level->sens > p->p_levels.nprim)
+ return -EINVAL;
+ p->p_sens_val_to_name[levdatum->level->sens - 1] = key;
+ }
+
+ return 0;
+}
+
+static int cat_index(void *key, void *datum, void *datap)
+{
+ struct policydb *p;
+ struct cat_datum *catdatum;
+
+ catdatum = datum;
+ p = datap;
+
+ if (!catdatum->isalias) {
+ if (!catdatum->value || catdatum->value > p->p_cats.nprim)
+ return -EINVAL;
+ p->p_cat_val_to_name[catdatum->value - 1] = key;
+ }
+
+ return 0;
+}
+
+static int (*index_f[SYM_NUM]) (void *key, void *datum, void *datap) =
+{
+ common_index,
+ class_index,
+ role_index,
+ type_index,
+ user_index,
+ cond_index_bool,
+ sens_index,
+ cat_index,
+};
+
+/*
+ * Define the common val_to_name array and the class
+ * val_to_name and val_to_struct arrays in a policy
+ * database structure.
+ *
+ * Caller must clean up upon failure.
+ */
+static int policydb_index_classes(struct policydb *p)
+{
+ int rc;
+
+ p->p_common_val_to_name =
+ kmalloc(p->p_commons.nprim * sizeof(char *), GFP_KERNEL);
+ if (!p->p_common_val_to_name) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = hashtab_map(p->p_commons.table, common_index, p);
+ if (rc)
+ goto out;
+
+ p->class_val_to_struct =
+ kmalloc(p->p_classes.nprim * sizeof(*(p->class_val_to_struct)), GFP_KERNEL);
+ if (!p->class_val_to_struct) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ p->p_class_val_to_name =
+ kmalloc(p->p_classes.nprim * sizeof(char *), GFP_KERNEL);
+ if (!p->p_class_val_to_name) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = hashtab_map(p->p_classes.table, class_index, p);
+out:
+ return rc;
+}
+
+#ifdef DEBUG_HASHES
+static void symtab_hash_eval(struct symtab *s)
+{
+ int i;
+
+ for (i = 0; i < SYM_NUM; i++) {
+ struct hashtab *h = s[i].table;
+ struct hashtab_info info;
+
+ hashtab_stat(h, &info);
+ printk(KERN_INFO "%s: %d entries and %d/%d buckets used, "
+ "longest chain length %d\n", symtab_name[i], h->nel,
+ info.slots_used, h->size, info.max_chain_len);
+ }
+}
+#endif
+
+/*
+ * Define the other val_to_name and val_to_struct arrays
+ * in a policy database structure.
+ *
+ * Caller must clean up on failure.
+ */
+static int policydb_index_others(struct policydb *p)
+{
+ int i, rc = 0;
+
+ printk(KERN_INFO "security: %d users, %d roles, %d types, %d bools",
+ p->p_users.nprim, p->p_roles.nprim, p->p_types.nprim, p->p_bools.nprim);
+ if (selinux_mls_enabled)
+ printk(", %d sens, %d cats", p->p_levels.nprim,
+ p->p_cats.nprim);
+ printk("\n");
+
+ printk(KERN_INFO "security: %d classes, %d rules\n",
+ p->p_classes.nprim, p->te_avtab.nel);
+
+#ifdef DEBUG_HASHES
+ avtab_hash_eval(&p->te_avtab, "rules");
+ symtab_hash_eval(p->symtab);
+#endif
+
+ p->role_val_to_struct =
+ kmalloc(p->p_roles.nprim * sizeof(*(p->role_val_to_struct)),
+ GFP_KERNEL);
+ if (!p->role_val_to_struct) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ p->user_val_to_struct =
+ kmalloc(p->p_users.nprim * sizeof(*(p->user_val_to_struct)),
+ GFP_KERNEL);
+ if (!p->user_val_to_struct) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (cond_init_bool_indexes(p)) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ for (i = SYM_ROLES; i < SYM_NUM; i++) {
+ p->sym_val_to_name[i] =
+ kmalloc(p->symtab[i].nprim * sizeof(char *), GFP_KERNEL);
+ if (!p->sym_val_to_name[i]) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ rc = hashtab_map(p->symtab[i].table, index_f[i], p);
+ if (rc)
+ goto out;
+ }
+
+out:
+ return rc;
+}
+
+/*
+ * The following *_destroy functions are used to
+ * free any memory allocated for each kind of
+ * symbol data in the policy database.
+ */
+
+static int perm_destroy(void *key, void *datum, void *p)
+{
+ kfree(key);
+ kfree(datum);
+ return 0;
+}
+
+static int common_destroy(void *key, void *datum, void *p)
+{
+ struct common_datum *comdatum;
+
+ kfree(key);
+ comdatum = datum;
+ hashtab_map(comdatum->permissions.table, perm_destroy, NULL);
+ hashtab_destroy(comdatum->permissions.table);
+ kfree(datum);
+ return 0;
+}
+
+static int class_destroy(void *key, void *datum, void *p)
+{
+ struct class_datum *cladatum;
+ struct constraint_node *constraint, *ctemp;
+ struct constraint_expr *e, *etmp;
+
+ kfree(key);
+ cladatum = datum;
+ hashtab_map(cladatum->permissions.table, perm_destroy, NULL);
+ hashtab_destroy(cladatum->permissions.table);
+ constraint = cladatum->constraints;
+ while (constraint) {
+ e = constraint->expr;
+ while (e) {
+ ebitmap_destroy(&e->names);
+ etmp = e;
+ e = e->next;
+ kfree(etmp);
+ }
+ ctemp = constraint;
+ constraint = constraint->next;
+ kfree(ctemp);
+ }
+
+ constraint = cladatum->validatetrans;
+ while (constraint) {
+ e = constraint->expr;
+ while (e) {
+ ebitmap_destroy(&e->names);
+ etmp = e;
+ e = e->next;
+ kfree(etmp);
+ }
+ ctemp = constraint;
+ constraint = constraint->next;
+ kfree(ctemp);
+ }
+
+ kfree(cladatum->comkey);
+ kfree(datum);
+ return 0;
+}
+
+static int role_destroy(void *key, void *datum, void *p)
+{
+ struct role_datum *role;
+
+ kfree(key);
+ role = datum;
+ ebitmap_destroy(&role->dominates);
+ ebitmap_destroy(&role->types);
+ kfree(datum);
+ return 0;
+}
+
+static int type_destroy(void *key, void *datum, void *p)
+{
+ kfree(key);
+ kfree(datum);
+ return 0;
+}
+
+static int user_destroy(void *key, void *datum, void *p)
+{
+ struct user_datum *usrdatum;
+
+ kfree(key);
+ usrdatum = datum;
+ ebitmap_destroy(&usrdatum->roles);
+ ebitmap_destroy(&usrdatum->range.level[0].cat);
+ ebitmap_destroy(&usrdatum->range.level[1].cat);
+ ebitmap_destroy(&usrdatum->dfltlevel.cat);
+ kfree(datum);
+ return 0;
+}
+
+static int sens_destroy(void *key, void *datum, void *p)
+{
+ struct level_datum *levdatum;
+
+ kfree(key);
+ levdatum = datum;
+ ebitmap_destroy(&levdatum->level->cat);
+ kfree(levdatum->level);
+ kfree(datum);
+ return 0;
+}
+
+static int cat_destroy(void *key, void *datum, void *p)
+{
+ kfree(key);
+ kfree(datum);
+ return 0;
+}
+
+static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) =
+{
+ common_destroy,
+ class_destroy,
+ role_destroy,
+ type_destroy,
+ user_destroy,
+ cond_destroy_bool,
+ sens_destroy,
+ cat_destroy,
+};
+
+static void ocontext_destroy(struct ocontext *c, int i)
+{
+ context_destroy(&c->context[0]);
+ context_destroy(&c->context[1]);
+ if (i == OCON_ISID || i == OCON_FS ||
+ i == OCON_NETIF || i == OCON_FSUSE)
+ kfree(c->u.name);
+ kfree(c);
+}
+
+/*
+ * Free any memory allocated by a policy database structure.
+ */
+void policydb_destroy(struct policydb *p)
+{
+ struct ocontext *c, *ctmp;
+ struct genfs *g, *gtmp;
+ int i;
+
+ for (i = 0; i < SYM_NUM; i++) {
+ hashtab_map(p->symtab[i].table, destroy_f[i], NULL);
+ hashtab_destroy(p->symtab[i].table);
+ }
+
+ for (i = 0; i < SYM_NUM; i++) {
+ if (p->sym_val_to_name[i])
+ kfree(p->sym_val_to_name[i]);
+ }
+
+ if (p->class_val_to_struct)
+ kfree(p->class_val_to_struct);
+ if (p->role_val_to_struct)
+ kfree(p->role_val_to_struct);
+ if (p->user_val_to_struct)
+ kfree(p->user_val_to_struct);
+
+ avtab_destroy(&p->te_avtab);
+
+ for (i = 0; i < OCON_NUM; i++) {
+ c = p->ocontexts[i];
+ while (c) {
+ ctmp = c;
+ c = c->next;
+ ocontext_destroy(ctmp,i);
+ }
+ }
+
+ g = p->genfs;
+ while (g) {
+ kfree(g->fstype);
+ c = g->head;
+ while (c) {
+ ctmp = c;
+ c = c->next;
+ ocontext_destroy(ctmp,OCON_FSUSE);
+ }
+ gtmp = g;
+ g = g->next;
+ kfree(gtmp);
+ }
+
+ cond_policydb_destroy(p);
+
+ return;
+}
+
+/*
+ * Load the initial SIDs specified in a policy database
+ * structure into a SID table.
+ */
+int policydb_load_isids(struct policydb *p, struct sidtab *s)
+{
+ struct ocontext *head, *c;
+ int rc;
+
+ rc = sidtab_init(s);
+ if (rc) {
+ printk(KERN_ERR "security: out of memory on SID table init\n");
+ goto out;
+ }
+
+ head = p->ocontexts[OCON_ISID];
+ for (c = head; c; c = c->next) {
+ if (!c->context[0].user) {
+ printk(KERN_ERR "security: SID %s was never "
+ "defined.\n", c->u.name);
+ rc = -EINVAL;
+ goto out;
+ }
+ if (sidtab_insert(s, c->sid[0], &c->context[0])) {
+ printk(KERN_ERR "security: unable to load initial "
+ "SID %s.\n", c->u.name);
+ rc = -EINVAL;
+ goto out;
+ }
+ }
+out:
+ return rc;
+}
+
+/*
+ * Return 1 if the fields in the security context
+ * structure `c' are valid. Return 0 otherwise.
+ */
+int policydb_context_isvalid(struct policydb *p, struct context *c)
+{
+ struct role_datum *role;
+ struct user_datum *usrdatum;
+
+ if (!c->role || c->role > p->p_roles.nprim)
+ return 0;
+
+ if (!c->user || c->user > p->p_users.nprim)
+ return 0;
+
+ if (!c->type || c->type > p->p_types.nprim)
+ return 0;
+
+ if (c->role != OBJECT_R_VAL) {
+ /*
+ * Role must be authorized for the type.
+ */
+ role = p->role_val_to_struct[c->role - 1];
+ if (!ebitmap_get_bit(&role->types,
+ c->type - 1))
+ /* role may not be associated with type */
+ return 0;
+
+ /*
+ * User must be authorized for the role.
+ */
+ usrdatum = p->user_val_to_struct[c->user - 1];
+ if (!usrdatum)
+ return 0;
+
+ if (!ebitmap_get_bit(&usrdatum->roles,
+ c->role - 1))
+ /* user may not be associated with role */
+ return 0;
+ }
+
+ if (!mls_context_isvalid(p, c))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Read a MLS range structure from a policydb binary
+ * representation file.
+ */
+static int mls_read_range_helper(struct mls_range *r, void *fp)
+{
+ u32 buf[2], items;
+ int rc;
+
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto out;
+
+ items = le32_to_cpu(buf[0]);
+ if (items > ARRAY_SIZE(buf)) {
+ printk(KERN_ERR "security: mls: range overflow\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ rc = next_entry(buf, fp, sizeof(u32) * items);
+ if (rc < 0) {
+ printk(KERN_ERR "security: mls: truncated range\n");
+ goto out;
+ }
+ r->level[0].sens = le32_to_cpu(buf[0]);
+ if (items > 1)
+ r->level[1].sens = le32_to_cpu(buf[1]);
+ else
+ r->level[1].sens = r->level[0].sens;
+
+ rc = ebitmap_read(&r->level[0].cat, fp);
+ if (rc) {
+ printk(KERN_ERR "security: mls: error reading low "
+ "categories\n");
+ goto out;
+ }
+ if (items > 1) {
+ rc = ebitmap_read(&r->level[1].cat, fp);
+ if (rc) {
+ printk(KERN_ERR "security: mls: error reading high "
+ "categories\n");
+ goto bad_high;
+ }
+ } else {
+ rc = ebitmap_cpy(&r->level[1].cat, &r->level[0].cat);
+ if (rc) {
+ printk(KERN_ERR "security: mls: out of memory\n");
+ goto bad_high;
+ }
+ }
+
+ rc = 0;
+out:
+ return rc;
+bad_high:
+ ebitmap_destroy(&r->level[0].cat);
+ goto out;
+}
+
+/*
+ * Read and validate a security context structure
+ * from a policydb binary representation file.
+ */
+static int context_read_and_validate(struct context *c,
+ struct policydb *p,
+ void *fp)
+{
+ u32 buf[3];
+ int rc;
+
+ rc = next_entry(buf, fp, sizeof buf);
+ if (rc < 0) {
+ printk(KERN_ERR "security: context truncated\n");
+ goto out;
+ }
+ c->user = le32_to_cpu(buf[0]);
+ c->role = le32_to_cpu(buf[1]);
+ c->type = le32_to_cpu(buf[2]);
+ if (p->policyvers >= POLICYDB_VERSION_MLS) {
+ if (mls_read_range_helper(&c->range, fp)) {
+ printk(KERN_ERR "security: error reading MLS range of "
+ "context\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (!policydb_context_isvalid(p, c)) {
+ printk(KERN_ERR "security: invalid security context\n");
+ context_destroy(c);
+ rc = -EINVAL;
+ }
+out:
+ return rc;
+}
+
+/*
+ * The following *_read functions are used to
+ * read the symbol data from a policy database
+ * binary representation file.
+ */
+
+static int perm_read(struct policydb *p, struct hashtab *h, void *fp)
+{
+ char *key = NULL;
+ struct perm_datum *perdatum;
+ int rc;
+ u32 buf[2], len;
+
+ perdatum = kmalloc(sizeof(*perdatum), GFP_KERNEL);
+ if (!perdatum) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ memset(perdatum, 0, sizeof(*perdatum));
+
+ rc = next_entry(buf, fp, sizeof buf);
+ if (rc < 0)
+ goto bad;
+
+ len = le32_to_cpu(buf[0]);
+ perdatum->value = le32_to_cpu(buf[1]);
+
+ key = kmalloc(len + 1,GFP_KERNEL);
+ if (!key) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(key, fp, len);
+ if (rc < 0)
+ goto bad;
+ key[len] = 0;
+
+ rc = hashtab_insert(h, key, perdatum);
+ if (rc)
+ goto bad;
+out:
+ return rc;
+bad:
+ perm_destroy(key, perdatum, NULL);
+ goto out;
+}
+
+static int common_read(struct policydb *p, struct hashtab *h, void *fp)
+{
+ char *key = NULL;
+ struct common_datum *comdatum;
+ u32 buf[4], len, nel;
+ int i, rc;
+
+ comdatum = kmalloc(sizeof(*comdatum), GFP_KERNEL);
+ if (!comdatum) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ memset(comdatum, 0, sizeof(*comdatum));
+
+ rc = next_entry(buf, fp, sizeof buf);
+ if (rc < 0)
+ goto bad;
+
+ len = le32_to_cpu(buf[0]);
+ comdatum->value = le32_to_cpu(buf[1]);
+
+ rc = symtab_init(&comdatum->permissions, PERM_SYMTAB_SIZE);
+ if (rc)
+ goto bad;
+ comdatum->permissions.nprim = le32_to_cpu(buf[2]);
+ nel = le32_to_cpu(buf[3]);
+
+ key = kmalloc(len + 1,GFP_KERNEL);
+ if (!key) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(key, fp, len);
+ if (rc < 0)
+ goto bad;
+ key[len] = 0;
+
+ for (i = 0; i < nel; i++) {
+ rc = perm_read(p, comdatum->permissions.table, fp);
+ if (rc)
+ goto bad;
+ }
+
+ rc = hashtab_insert(h, key, comdatum);
+ if (rc)
+ goto bad;
+out:
+ return rc;
+bad:
+ common_destroy(key, comdatum, NULL);
+ goto out;
+}
+
+static int read_cons_helper(struct constraint_node **nodep, int ncons,
+ int allowxtarget, void *fp)
+{
+ struct constraint_node *c, *lc;
+ struct constraint_expr *e, *le;
+ u32 buf[3], nexpr;
+ int rc, i, j, depth;
+
+ lc = NULL;
+ for (i = 0; i < ncons; i++) {
+ c = kmalloc(sizeof(*c), GFP_KERNEL);
+ if (!c)
+ return -ENOMEM;
+ memset(c, 0, sizeof(*c));
+
+ if (lc) {
+ lc->next = c;
+ } else {
+ *nodep = c;
+ }
+
+ rc = next_entry(buf, fp, (sizeof(u32) * 2));
+ if (rc < 0)
+ return rc;
+ c->permissions = le32_to_cpu(buf[0]);
+ nexpr = le32_to_cpu(buf[1]);
+ le = NULL;
+ depth = -1;
+ for (j = 0; j < nexpr; j++) {
+ e = kmalloc(sizeof(*e), GFP_KERNEL);
+ if (!e)
+ return -ENOMEM;
+ memset(e, 0, sizeof(*e));
+
+ if (le) {
+ le->next = e;
+ } else {
+ c->expr = e;
+ }
+
+ rc = next_entry(buf, fp, (sizeof(u32) * 3));
+ if (rc < 0)
+ return rc;
+ e->expr_type = le32_to_cpu(buf[0]);
+ e->attr = le32_to_cpu(buf[1]);
+ e->op = le32_to_cpu(buf[2]);
+
+ switch (e->expr_type) {
+ case CEXPR_NOT:
+ if (depth < 0)
+ return -EINVAL;
+ break;
+ case CEXPR_AND:
+ case CEXPR_OR:
+ if (depth < 1)
+ return -EINVAL;
+ depth--;
+ break;
+ case CEXPR_ATTR:
+ if (depth == (CEXPR_MAXDEPTH - 1))
+ return -EINVAL;
+ depth++;
+ break;
+ case CEXPR_NAMES:
+ if (!allowxtarget && (e->attr & CEXPR_XTARGET))
+ return -EINVAL;
+ if (depth == (CEXPR_MAXDEPTH - 1))
+ return -EINVAL;
+ depth++;
+ if (ebitmap_read(&e->names, fp))
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ le = e;
+ }
+ if (depth != 0)
+ return -EINVAL;
+ lc = c;
+ }
+
+ return 0;
+}
+
+static int class_read(struct policydb *p, struct hashtab *h, void *fp)
+{
+ char *key = NULL;
+ struct class_datum *cladatum;
+ u32 buf[6], len, len2, ncons, nel;
+ int i, rc;
+
+ cladatum = kmalloc(sizeof(*cladatum), GFP_KERNEL);
+ if (!cladatum) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ memset(cladatum, 0, sizeof(*cladatum));
+
+ rc = next_entry(buf, fp, sizeof(u32)*6);
+ if (rc < 0)
+ goto bad;
+
+ len = le32_to_cpu(buf[0]);
+ len2 = le32_to_cpu(buf[1]);
+ cladatum->value = le32_to_cpu(buf[2]);
+
+ rc = symtab_init(&cladatum->permissions, PERM_SYMTAB_SIZE);
+ if (rc)
+ goto bad;
+ cladatum->permissions.nprim = le32_to_cpu(buf[3]);
+ nel = le32_to_cpu(buf[4]);
+
+ ncons = le32_to_cpu(buf[5]);
+
+ key = kmalloc(len + 1,GFP_KERNEL);
+ if (!key) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(key, fp, len);
+ if (rc < 0)
+ goto bad;
+ key[len] = 0;
+
+ if (len2) {
+ cladatum->comkey = kmalloc(len2 + 1,GFP_KERNEL);
+ if (!cladatum->comkey) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(cladatum->comkey, fp, len2);
+ if (rc < 0)
+ goto bad;
+ cladatum->comkey[len2] = 0;
+
+ cladatum->comdatum = hashtab_search(p->p_commons.table,
+ cladatum->comkey);
+ if (!cladatum->comdatum) {
+ printk(KERN_ERR "security: unknown common %s\n",
+ cladatum->comkey);
+ rc = -EINVAL;
+ goto bad;
+ }
+ }
+ for (i = 0; i < nel; i++) {
+ rc = perm_read(p, cladatum->permissions.table, fp);
+ if (rc)
+ goto bad;
+ }
+
+ rc = read_cons_helper(&cladatum->constraints, ncons, 0, fp);
+ if (rc)
+ goto bad;
+
+ if (p->policyvers >= POLICYDB_VERSION_VALIDATETRANS) {
+ /* grab the validatetrans rules */
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad;
+ ncons = le32_to_cpu(buf[0]);
+ rc = read_cons_helper(&cladatum->validatetrans, ncons, 1, fp);
+ if (rc)
+ goto bad;
+ }
+
+ rc = hashtab_insert(h, key, cladatum);
+ if (rc)
+ goto bad;
+
+ rc = 0;
+out:
+ return rc;
+bad:
+ class_destroy(key, cladatum, NULL);
+ goto out;
+}
+
+static int role_read(struct policydb *p, struct hashtab *h, void *fp)
+{
+ char *key = NULL;
+ struct role_datum *role;
+ int rc;
+ u32 buf[2], len;
+
+ role = kmalloc(sizeof(*role), GFP_KERNEL);
+ if (!role) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ memset(role, 0, sizeof(*role));
+
+ rc = next_entry(buf, fp, sizeof buf);
+ if (rc < 0)
+ goto bad;
+
+ len = le32_to_cpu(buf[0]);
+ role->value = le32_to_cpu(buf[1]);
+
+ key = kmalloc(len + 1,GFP_KERNEL);
+ if (!key) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(key, fp, len);
+ if (rc < 0)
+ goto bad;
+ key[len] = 0;
+
+ rc = ebitmap_read(&role->dominates, fp);
+ if (rc)
+ goto bad;
+
+ rc = ebitmap_read(&role->types, fp);
+ if (rc)
+ goto bad;
+
+ if (strcmp(key, OBJECT_R) == 0) {
+ if (role->value != OBJECT_R_VAL) {
+ printk(KERN_ERR "Role %s has wrong value %d\n",
+ OBJECT_R, role->value);
+ rc = -EINVAL;
+ goto bad;
+ }
+ rc = 0;
+ goto bad;
+ }
+
+ rc = hashtab_insert(h, key, role);
+ if (rc)
+ goto bad;
+out:
+ return rc;
+bad:
+ role_destroy(key, role, NULL);
+ goto out;
+}
+
+static int type_read(struct policydb *p, struct hashtab *h, void *fp)
+{
+ char *key = NULL;
+ struct type_datum *typdatum;
+ int rc;
+ u32 buf[3], len;
+
+ typdatum = kmalloc(sizeof(*typdatum),GFP_KERNEL);
+ if (!typdatum) {
+ rc = -ENOMEM;
+ return rc;
+ }
+ memset(typdatum, 0, sizeof(*typdatum));
+
+ rc = next_entry(buf, fp, sizeof buf);
+ if (rc < 0)
+ goto bad;
+
+ len = le32_to_cpu(buf[0]);
+ typdatum->value = le32_to_cpu(buf[1]);
+ typdatum->primary = le32_to_cpu(buf[2]);
+
+ key = kmalloc(len + 1,GFP_KERNEL);
+ if (!key) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(key, fp, len);
+ if (rc < 0)
+ goto bad;
+ key[len] = 0;
+
+ rc = hashtab_insert(h, key, typdatum);
+ if (rc)
+ goto bad;
+out:
+ return rc;
+bad:
+ type_destroy(key, typdatum, NULL);
+ goto out;
+}
+
+
+/*
+ * Read a MLS level structure from a policydb binary
+ * representation file.
+ */
+static int mls_read_level(struct mls_level *lp, void *fp)
+{
+ u32 buf[1];
+ int rc;
+
+ memset(lp, 0, sizeof(*lp));
+
+ rc = next_entry(buf, fp, sizeof buf);
+ if (rc < 0) {
+ printk(KERN_ERR "security: mls: truncated level\n");
+ goto bad;
+ }
+ lp->sens = le32_to_cpu(buf[0]);
+
+ if (ebitmap_read(&lp->cat, fp)) {
+ printk(KERN_ERR "security: mls: error reading level "
+ "categories\n");
+ goto bad;
+ }
+ return 0;
+
+bad:
+ return -EINVAL;
+}
+
+static int user_read(struct policydb *p, struct hashtab *h, void *fp)
+{
+ char *key = NULL;
+ struct user_datum *usrdatum;
+ int rc;
+ u32 buf[2], len;
+
+ usrdatum = kmalloc(sizeof(*usrdatum), GFP_KERNEL);
+ if (!usrdatum) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ memset(usrdatum, 0, sizeof(*usrdatum));
+
+ rc = next_entry(buf, fp, sizeof buf);
+ if (rc < 0)
+ goto bad;
+
+ len = le32_to_cpu(buf[0]);
+ usrdatum->value = le32_to_cpu(buf[1]);
+
+ key = kmalloc(len + 1,GFP_KERNEL);
+ if (!key) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(key, fp, len);
+ if (rc < 0)
+ goto bad;
+ key[len] = 0;
+
+ rc = ebitmap_read(&usrdatum->roles, fp);
+ if (rc)
+ goto bad;
+
+ if (p->policyvers >= POLICYDB_VERSION_MLS) {
+ rc = mls_read_range_helper(&usrdatum->range, fp);
+ if (rc)
+ goto bad;
+ rc = mls_read_level(&usrdatum->dfltlevel, fp);
+ if (rc)
+ goto bad;
+ }
+
+ rc = hashtab_insert(h, key, usrdatum);
+ if (rc)
+ goto bad;
+out:
+ return rc;
+bad:
+ user_destroy(key, usrdatum, NULL);
+ goto out;
+}
+
+static int sens_read(struct policydb *p, struct hashtab *h, void *fp)
+{
+ char *key = NULL;
+ struct level_datum *levdatum;
+ int rc;
+ u32 buf[2], len;
+
+ levdatum = kmalloc(sizeof(*levdatum), GFP_ATOMIC);
+ if (!levdatum) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ memset(levdatum, 0, sizeof(*levdatum));
+
+ rc = next_entry(buf, fp, sizeof buf);
+ if (rc < 0)
+ goto bad;
+
+ len = le32_to_cpu(buf[0]);
+ levdatum->isalias = le32_to_cpu(buf[1]);
+
+ key = kmalloc(len + 1,GFP_ATOMIC);
+ if (!key) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(key, fp, len);
+ if (rc < 0)
+ goto bad;
+ key[len] = 0;
+
+ levdatum->level = kmalloc(sizeof(struct mls_level), GFP_ATOMIC);
+ if (!levdatum->level) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ if (mls_read_level(levdatum->level, fp)) {
+ rc = -EINVAL;
+ goto bad;
+ }
+
+ rc = hashtab_insert(h, key, levdatum);
+ if (rc)
+ goto bad;
+out:
+ return rc;
+bad:
+ sens_destroy(key, levdatum, NULL);
+ goto out;
+}
+
+static int cat_read(struct policydb *p, struct hashtab *h, void *fp)
+{
+ char *key = NULL;
+ struct cat_datum *catdatum;
+ int rc;
+ u32 buf[3], len;
+
+ catdatum = kmalloc(sizeof(*catdatum), GFP_ATOMIC);
+ if (!catdatum) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ memset(catdatum, 0, sizeof(*catdatum));
+
+ rc = next_entry(buf, fp, sizeof buf);
+ if (rc < 0)
+ goto bad;
+
+ len = le32_to_cpu(buf[0]);
+ catdatum->value = le32_to_cpu(buf[1]);
+ catdatum->isalias = le32_to_cpu(buf[2]);
+
+ key = kmalloc(len + 1,GFP_ATOMIC);
+ if (!key) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(key, fp, len);
+ if (rc < 0)
+ goto bad;
+ key[len] = 0;
+
+ rc = hashtab_insert(h, key, catdatum);
+ if (rc)
+ goto bad;
+out:
+ return rc;
+
+bad:
+ cat_destroy(key, catdatum, NULL);
+ goto out;
+}
+
+static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) =
+{
+ common_read,
+ class_read,
+ role_read,
+ type_read,
+ user_read,
+ cond_read_bool,
+ sens_read,
+ cat_read,
+};
+
+extern int ss_initialized;
+
+/*
+ * Read the configuration data from a policy database binary
+ * representation file into a policy database structure.
+ */
+int policydb_read(struct policydb *p, void *fp)
+{
+ struct role_allow *ra, *lra;
+ struct role_trans *tr, *ltr;
+ struct ocontext *l, *c, *newc;
+ struct genfs *genfs_p, *genfs, *newgenfs;
+ int i, j, rc;
+ u32 buf[8], len, len2, config, nprim, nel, nel2;
+ char *policydb_str;
+ struct policydb_compat_info *info;
+ struct range_trans *rt, *lrt;
+
+ config = 0;
+
+ rc = policydb_init(p);
+ if (rc)
+ goto out;
+
+ /* Read the magic number and string length. */
+ rc = next_entry(buf, fp, sizeof(u32)* 2);
+ if (rc < 0)
+ goto bad;
+
+ for (i = 0; i < 2; i++)
+ buf[i] = le32_to_cpu(buf[i]);
+
+ if (buf[0] != POLICYDB_MAGIC) {
+ printk(KERN_ERR "security: policydb magic number 0x%x does "
+ "not match expected magic number 0x%x\n",
+ buf[0], POLICYDB_MAGIC);
+ goto bad;
+ }
+
+ len = buf[1];
+ if (len != strlen(POLICYDB_STRING)) {
+ printk(KERN_ERR "security: policydb string length %d does not "
+ "match expected length %Zu\n",
+ len, strlen(POLICYDB_STRING));
+ goto bad;
+ }
+ policydb_str = kmalloc(len + 1,GFP_KERNEL);
+ if (!policydb_str) {
+ printk(KERN_ERR "security: unable to allocate memory for policydb "
+ "string of length %d\n", len);
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(policydb_str, fp, len);
+ if (rc < 0) {
+ printk(KERN_ERR "security: truncated policydb string identifier\n");
+ kfree(policydb_str);
+ goto bad;
+ }
+ policydb_str[len] = 0;
+ if (strcmp(policydb_str, POLICYDB_STRING)) {
+ printk(KERN_ERR "security: policydb string %s does not match "
+ "my string %s\n", policydb_str, POLICYDB_STRING);
+ kfree(policydb_str);
+ goto bad;
+ }
+ /* Done with policydb_str. */
+ kfree(policydb_str);
+ policydb_str = NULL;
+
+ /* Read the version, config, and table sizes. */
+ rc = next_entry(buf, fp, sizeof(u32)*4);
+ if (rc < 0)
+ goto bad;
+ for (i = 0; i < 4; i++)
+ buf[i] = le32_to_cpu(buf[i]);
+
+ p->policyvers = buf[0];
+ if (p->policyvers < POLICYDB_VERSION_MIN ||
+ p->policyvers > POLICYDB_VERSION_MAX) {
+ printk(KERN_ERR "security: policydb version %d does not match "
+ "my version range %d-%d\n",
+ buf[0], POLICYDB_VERSION_MIN, POLICYDB_VERSION_MAX);
+ goto bad;
+ }
+
+ if ((buf[1] & POLICYDB_CONFIG_MLS)) {
+ if (ss_initialized && !selinux_mls_enabled) {
+ printk(KERN_ERR "Cannot switch between non-MLS and MLS "
+ "policies\n");
+ goto bad;
+ }
+ selinux_mls_enabled = 1;
+ config |= POLICYDB_CONFIG_MLS;
+
+ if (p->policyvers < POLICYDB_VERSION_MLS) {
+ printk(KERN_ERR "security policydb version %d (MLS) "
+ "not backwards compatible\n", p->policyvers);
+ goto bad;
+ }
+ } else {
+ if (ss_initialized && selinux_mls_enabled) {
+ printk(KERN_ERR "Cannot switch between MLS and non-MLS "
+ "policies\n");
+ goto bad;
+ }
+ }
+
+ info = policydb_lookup_compat(p->policyvers);
+ if (!info) {
+ printk(KERN_ERR "security: unable to find policy compat info "
+ "for version %d\n", p->policyvers);
+ goto bad;
+ }
+
+ if (buf[2] != info->sym_num || buf[3] != info->ocon_num) {
+ printk(KERN_ERR "security: policydb table sizes (%d,%d) do "
+ "not match mine (%d,%d)\n", buf[2], buf[3],
+ info->sym_num, info->ocon_num);
+ goto bad;
+ }
+
+ for (i = 0; i < info->sym_num; i++) {
+ rc = next_entry(buf, fp, sizeof(u32)*2);
+ if (rc < 0)
+ goto bad;
+ nprim = le32_to_cpu(buf[0]);
+ nel = le32_to_cpu(buf[1]);
+ for (j = 0; j < nel; j++) {
+ rc = read_f[i](p, p->symtab[i].table, fp);
+ if (rc)
+ goto bad;
+ }
+
+ p->symtab[i].nprim = nprim;
+ }
+
+ rc = avtab_read(&p->te_avtab, fp, config);
+ if (rc)
+ goto bad;
+
+ if (p->policyvers >= POLICYDB_VERSION_BOOL) {
+ rc = cond_read_list(p, fp);
+ if (rc)
+ goto bad;
+ }
+
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad;
+ nel = le32_to_cpu(buf[0]);
+ ltr = NULL;
+ for (i = 0; i < nel; i++) {
+ tr = kmalloc(sizeof(*tr), GFP_KERNEL);
+ if (!tr) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ memset(tr, 0, sizeof(*tr));
+ if (ltr) {
+ ltr->next = tr;
+ } else {
+ p->role_tr = tr;
+ }
+ rc = next_entry(buf, fp, sizeof(u32)*3);
+ if (rc < 0)
+ goto bad;
+ tr->role = le32_to_cpu(buf[0]);
+ tr->type = le32_to_cpu(buf[1]);
+ tr->new_role = le32_to_cpu(buf[2]);
+ ltr = tr;
+ }
+
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad;
+ nel = le32_to_cpu(buf[0]);
+ lra = NULL;
+ for (i = 0; i < nel; i++) {
+ ra = kmalloc(sizeof(*ra), GFP_KERNEL);
+ if (!ra) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ memset(ra, 0, sizeof(*ra));
+ if (lra) {
+ lra->next = ra;
+ } else {
+ p->role_allow = ra;
+ }
+ rc = next_entry(buf, fp, sizeof(u32)*2);
+ if (rc < 0)
+ goto bad;
+ ra->role = le32_to_cpu(buf[0]);
+ ra->new_role = le32_to_cpu(buf[1]);
+ lra = ra;
+ }
+
+ rc = policydb_index_classes(p);
+ if (rc)
+ goto bad;
+
+ rc = policydb_index_others(p);
+ if (rc)
+ goto bad;
+
+ for (i = 0; i < info->ocon_num; i++) {
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad;
+ nel = le32_to_cpu(buf[0]);
+ l = NULL;
+ for (j = 0; j < nel; j++) {
+ c = kmalloc(sizeof(*c), GFP_KERNEL);
+ if (!c) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ memset(c, 0, sizeof(*c));
+ if (l) {
+ l->next = c;
+ } else {
+ p->ocontexts[i] = c;
+ }
+ l = c;
+ rc = -EINVAL;
+ switch (i) {
+ case OCON_ISID:
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad;
+ c->sid[0] = le32_to_cpu(buf[0]);
+ rc = context_read_and_validate(&c->context[0], p, fp);
+ if (rc)
+ goto bad;
+ break;
+ case OCON_FS:
+ case OCON_NETIF:
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad;
+ len = le32_to_cpu(buf[0]);
+ c->u.name = kmalloc(len + 1,GFP_KERNEL);
+ if (!c->u.name) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(c->u.name, fp, len);
+ if (rc < 0)
+ goto bad;
+ c->u.name[len] = 0;
+ rc = context_read_and_validate(&c->context[0], p, fp);
+ if (rc)
+ goto bad;
+ rc = context_read_and_validate(&c->context[1], p, fp);
+ if (rc)
+ goto bad;
+ break;
+ case OCON_PORT:
+ rc = next_entry(buf, fp, sizeof(u32)*3);
+ if (rc < 0)
+ goto bad;
+ c->u.port.protocol = le32_to_cpu(buf[0]);
+ c->u.port.low_port = le32_to_cpu(buf[1]);
+ c->u.port.high_port = le32_to_cpu(buf[2]);
+ rc = context_read_and_validate(&c->context[0], p, fp);
+ if (rc)
+ goto bad;
+ break;
+ case OCON_NODE:
+ rc = next_entry(buf, fp, sizeof(u32)* 2);
+ if (rc < 0)
+ goto bad;
+ c->u.node.addr = le32_to_cpu(buf[0]);
+ c->u.node.mask = le32_to_cpu(buf[1]);
+ rc = context_read_and_validate(&c->context[0], p, fp);
+ if (rc)
+ goto bad;
+ break;
+ case OCON_FSUSE:
+ rc = next_entry(buf, fp, sizeof(u32)*2);
+ if (rc < 0)
+ goto bad;
+ c->v.behavior = le32_to_cpu(buf[0]);
+ if (c->v.behavior > SECURITY_FS_USE_NONE)
+ goto bad;
+ len = le32_to_cpu(buf[1]);
+ c->u.name = kmalloc(len + 1,GFP_KERNEL);
+ if (!c->u.name) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(c->u.name, fp, len);
+ if (rc < 0)
+ goto bad;
+ c->u.name[len] = 0;
+ rc = context_read_and_validate(&c->context[0], p, fp);
+ if (rc)
+ goto bad;
+ break;
+ case OCON_NODE6: {
+ int k;
+
+ rc = next_entry(buf, fp, sizeof(u32) * 8);
+ if (rc < 0)
+ goto bad;
+ for (k = 0; k < 4; k++)
+ c->u.node6.addr[k] = le32_to_cpu(buf[k]);
+ for (k = 0; k < 4; k++)
+ c->u.node6.mask[k] = le32_to_cpu(buf[k+4]);
+ if (context_read_and_validate(&c->context[0], p, fp))
+ goto bad;
+ break;
+ }
+ }
+ }
+ }
+
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad;
+ nel = le32_to_cpu(buf[0]);
+ genfs_p = NULL;
+ rc = -EINVAL;
+ for (i = 0; i < nel; i++) {
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad;
+ len = le32_to_cpu(buf[0]);
+ newgenfs = kmalloc(sizeof(*newgenfs), GFP_KERNEL);
+ if (!newgenfs) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ memset(newgenfs, 0, sizeof(*newgenfs));
+
+ newgenfs->fstype = kmalloc(len + 1,GFP_KERNEL);
+ if (!newgenfs->fstype) {
+ rc = -ENOMEM;
+ kfree(newgenfs);
+ goto bad;
+ }
+ rc = next_entry(newgenfs->fstype, fp, len);
+ if (rc < 0) {
+ kfree(newgenfs->fstype);
+ kfree(newgenfs);
+ goto bad;
+ }
+ newgenfs->fstype[len] = 0;
+ for (genfs_p = NULL, genfs = p->genfs; genfs;
+ genfs_p = genfs, genfs = genfs->next) {
+ if (strcmp(newgenfs->fstype, genfs->fstype) == 0) {
+ printk(KERN_ERR "security: dup genfs "
+ "fstype %s\n", newgenfs->fstype);
+ kfree(newgenfs->fstype);
+ kfree(newgenfs);
+ goto bad;
+ }
+ if (strcmp(newgenfs->fstype, genfs->fstype) < 0)
+ break;
+ }
+ newgenfs->next = genfs;
+ if (genfs_p)
+ genfs_p->next = newgenfs;
+ else
+ p->genfs = newgenfs;
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad;
+ nel2 = le32_to_cpu(buf[0]);
+ for (j = 0; j < nel2; j++) {
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad;
+ len = le32_to_cpu(buf[0]);
+
+ newc = kmalloc(sizeof(*newc), GFP_KERNEL);
+ if (!newc) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ memset(newc, 0, sizeof(*newc));
+
+ newc->u.name = kmalloc(len + 1,GFP_KERNEL);
+ if (!newc->u.name) {
+ rc = -ENOMEM;
+ goto bad_newc;
+ }
+ rc = next_entry(newc->u.name, fp, len);
+ if (rc < 0)
+ goto bad_newc;
+ newc->u.name[len] = 0;
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad_newc;
+ newc->v.sclass = le32_to_cpu(buf[0]);
+ if (context_read_and_validate(&newc->context[0], p, fp))
+ goto bad_newc;
+ for (l = NULL, c = newgenfs->head; c;
+ l = c, c = c->next) {
+ if (!strcmp(newc->u.name, c->u.name) &&
+ (!c->v.sclass || !newc->v.sclass ||
+ newc->v.sclass == c->v.sclass)) {
+ printk(KERN_ERR "security: dup genfs "
+ "entry (%s,%s)\n",
+ newgenfs->fstype, c->u.name);
+ goto bad_newc;
+ }
+ len = strlen(newc->u.name);
+ len2 = strlen(c->u.name);
+ if (len > len2)
+ break;
+ }
+
+ newc->next = c;
+ if (l)
+ l->next = newc;
+ else
+ newgenfs->head = newc;
+ }
+ }
+
+ if (p->policyvers >= POLICYDB_VERSION_MLS) {
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad;
+ nel = le32_to_cpu(buf[0]);
+ lrt = NULL;
+ for (i = 0; i < nel; i++) {
+ rt = kmalloc(sizeof(*rt), GFP_KERNEL);
+ if (!rt) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ memset(rt, 0, sizeof(*rt));
+ if (lrt)
+ lrt->next = rt;
+ else
+ p->range_tr = rt;
+ rc = next_entry(buf, fp, (sizeof(u32) * 2));
+ if (rc < 0)
+ goto bad;
+ rt->dom = le32_to_cpu(buf[0]);
+ rt->type = le32_to_cpu(buf[1]);
+ rc = mls_read_range_helper(&rt->range, fp);
+ if (rc)
+ goto bad;
+ lrt = rt;
+ }
+ }
+
+ rc = 0;
+out:
+ return rc;
+bad_newc:
+ ocontext_destroy(newc,OCON_FSUSE);
+bad:
+ if (!rc)
+ rc = -EINVAL;
+ policydb_destroy(p);
+ goto out;
+}
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
new file mode 100644
index 00000000000..2470e2a1a1c
--- /dev/null
+++ b/security/selinux/ss/policydb.h
@@ -0,0 +1,275 @@
+/*
+ * A policy database (policydb) specifies the
+ * configuration data for the security policy.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+
+/*
+ * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
+ *
+ * Support for enhanced MLS infrastructure.
+ *
+ * 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
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ */
+
+#ifndef _SS_POLICYDB_H_
+#define _SS_POLICYDB_H_
+
+#include "symtab.h"
+#include "avtab.h"
+#include "sidtab.h"
+#include "context.h"
+#include "constraint.h"
+
+/*
+ * A datum type is defined for each kind of symbol
+ * in the configuration data: individual permissions,
+ * common prefixes for access vectors, classes,
+ * users, roles, types, sensitivities, categories, etc.
+ */
+
+/* Permission attributes */
+struct perm_datum {
+ u32 value; /* permission bit + 1 */
+};
+
+/* Attributes of a common prefix for access vectors */
+struct common_datum {
+ u32 value; /* internal common value */
+ struct symtab permissions; /* common permissions */
+};
+
+/* Class attributes */
+struct class_datum {
+ u32 value; /* class value */
+ char *comkey; /* common name */
+ struct common_datum *comdatum; /* common datum */
+ struct symtab permissions; /* class-specific permission symbol table */
+ struct constraint_node *constraints; /* constraints on class permissions */
+ struct constraint_node *validatetrans; /* special transition rules */
+};
+
+/* Role attributes */
+struct role_datum {
+ u32 value; /* internal role value */
+ struct ebitmap dominates; /* set of roles dominated by this role */
+ struct ebitmap types; /* set of authorized types for role */
+};
+
+struct role_trans {
+ u32 role; /* current role */
+ u32 type; /* program executable type */
+ u32 new_role; /* new role */
+ struct role_trans *next;
+};
+
+struct role_allow {
+ u32 role; /* current role */
+ u32 new_role; /* new role */
+ struct role_allow *next;
+};
+
+/* Type attributes */
+struct type_datum {
+ u32 value; /* internal type value */
+ unsigned char primary; /* primary name? */
+};
+
+/* User attributes */
+struct user_datum {
+ u32 value; /* internal user value */
+ struct ebitmap roles; /* set of authorized roles for user */
+ struct mls_range range; /* MLS range (min - max) for user */
+ struct mls_level dfltlevel; /* default login MLS level for user */
+};
+
+
+/* Sensitivity attributes */
+struct level_datum {
+ struct mls_level *level; /* sensitivity and associated categories */
+ unsigned char isalias; /* is this sensitivity an alias for another? */
+};
+
+/* Category attributes */
+struct cat_datum {
+ u32 value; /* internal category bit + 1 */
+ unsigned char isalias; /* is this category an alias for another? */
+};
+
+struct range_trans {
+ u32 dom; /* current process domain */
+ u32 type; /* program executable type */
+ struct mls_range range; /* new range */
+ struct range_trans *next;
+};
+
+/* Boolean data type */
+struct cond_bool_datum {
+ __u32 value; /* internal type value */
+ int state;
+};
+
+struct cond_node;
+
+/*
+ * The configuration data includes security contexts for
+ * initial SIDs, unlabeled file systems, TCP and UDP port numbers,
+ * network interfaces, and nodes. This structure stores the
+ * relevant data for one such entry. Entries of the same kind
+ * (e.g. all initial SIDs) are linked together into a list.
+ */
+struct ocontext {
+ union {
+ char *name; /* name of initial SID, fs, netif, fstype, path */
+ struct {
+ u8 protocol;
+ u16 low_port;
+ u16 high_port;
+ } port; /* TCP or UDP port information */
+ struct {
+ u32 addr;
+ u32 mask;
+ } node; /* node information */
+ struct {
+ u32 addr[4];
+ u32 mask[4];
+ } node6; /* IPv6 node information */
+ } u;
+ union {
+ u32 sclass; /* security class for genfs */
+ u32 behavior; /* labeling behavior for fs_use */
+ } v;
+ struct context context[2]; /* security context(s) */
+ u32 sid[2]; /* SID(s) */
+ struct ocontext *next;
+};
+
+struct genfs {
+ char *fstype;
+ struct ocontext *head;
+ struct genfs *next;
+};
+
+/* symbol table array indices */
+#define SYM_COMMONS 0
+#define SYM_CLASSES 1
+#define SYM_ROLES 2
+#define SYM_TYPES 3
+#define SYM_USERS 4
+#define SYM_BOOLS 5
+#define SYM_LEVELS 6
+#define SYM_CATS 7
+#define SYM_NUM 8
+
+/* object context array indices */
+#define OCON_ISID 0 /* initial SIDs */
+#define OCON_FS 1 /* unlabeled file systems */
+#define OCON_PORT 2 /* TCP and UDP port numbers */
+#define OCON_NETIF 3 /* network interfaces */
+#define OCON_NODE 4 /* nodes */
+#define OCON_FSUSE 5 /* fs_use */
+#define OCON_NODE6 6 /* IPv6 nodes */
+#define OCON_NUM 7
+
+/* The policy database */
+struct policydb {
+ /* symbol tables */
+ struct symtab symtab[SYM_NUM];
+#define p_commons symtab[SYM_COMMONS]
+#define p_classes symtab[SYM_CLASSES]
+#define p_roles symtab[SYM_ROLES]
+#define p_types symtab[SYM_TYPES]
+#define p_users symtab[SYM_USERS]
+#define p_bools symtab[SYM_BOOLS]
+#define p_levels symtab[SYM_LEVELS]
+#define p_cats symtab[SYM_CATS]
+
+ /* symbol names indexed by (value - 1) */
+ char **sym_val_to_name[SYM_NUM];
+#define p_common_val_to_name sym_val_to_name[SYM_COMMONS]
+#define p_class_val_to_name sym_val_to_name[SYM_CLASSES]
+#define p_role_val_to_name sym_val_to_name[SYM_ROLES]
+#define p_type_val_to_name sym_val_to_name[SYM_TYPES]
+#define p_user_val_to_name sym_val_to_name[SYM_USERS]
+#define p_bool_val_to_name sym_val_to_name[SYM_BOOLS]
+#define p_sens_val_to_name sym_val_to_name[SYM_LEVELS]
+#define p_cat_val_to_name sym_val_to_name[SYM_CATS]
+
+ /* class, role, and user attributes indexed by (value - 1) */
+ struct class_datum **class_val_to_struct;
+ struct role_datum **role_val_to_struct;
+ struct user_datum **user_val_to_struct;
+
+ /* type enforcement access vectors and transitions */
+ struct avtab te_avtab;
+
+ /* role transitions */
+ struct role_trans *role_tr;
+
+ /* bools indexed by (value - 1) */
+ struct cond_bool_datum **bool_val_to_struct;
+ /* type enforcement conditional access vectors and transitions */
+ struct avtab te_cond_avtab;
+ /* linked list indexing te_cond_avtab by conditional */
+ struct cond_node* cond_list;
+
+ /* role allows */
+ struct role_allow *role_allow;
+
+ /* security contexts of initial SIDs, unlabeled file systems,
+ TCP or UDP port numbers, network interfaces and nodes */
+ struct ocontext *ocontexts[OCON_NUM];
+
+ /* security contexts for files in filesystems that cannot support
+ a persistent label mapping or use another
+ fixed labeling behavior. */
+ struct genfs *genfs;
+
+ /* range transitions */
+ struct range_trans *range_tr;
+
+ unsigned int policyvers;
+};
+
+extern void policydb_destroy(struct policydb *p);
+extern int policydb_load_isids(struct policydb *p, struct sidtab *s);
+extern int policydb_context_isvalid(struct policydb *p, struct context *c);
+extern int policydb_read(struct policydb *p, void *fp);
+
+#define PERM_SYMTAB_SIZE 32
+
+#define POLICYDB_CONFIG_MLS 1
+
+#define OBJECT_R "object_r"
+#define OBJECT_R_VAL 1
+
+#define POLICYDB_MAGIC SELINUX_MAGIC
+#define POLICYDB_STRING "SE Linux"
+
+struct policy_file {
+ char *data;
+ size_t len;
+};
+
+static inline int next_entry(void *buf, struct policy_file *fp, size_t bytes)
+{
+ if (bytes > fp->len)
+ return -EINVAL;
+
+ memcpy(buf, fp->data, bytes);
+ fp->data += bytes;
+ fp->len -= bytes;
+ return 0;
+}
+
+#endif /* _SS_POLICYDB_H_ */
+
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
new file mode 100644
index 00000000000..5a820cf88c9
--- /dev/null
+++ b/security/selinux/ss/services.c
@@ -0,0 +1,1777 @@
+/*
+ * Implementation of the security services.
+ *
+ * Authors : Stephen Smalley, <sds@epoch.ncsc.mil>
+ * James Morris <jmorris@redhat.com>
+ *
+ * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
+ *
+ * Support for enhanced MLS infrastructure.
+ *
+ * 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
+ * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/sched.h>
+#include <linux/audit.h>
+#include <asm/semaphore.h>
+#include "flask.h"
+#include "avc.h"
+#include "avc_ss.h"
+#include "security.h"
+#include "context.h"
+#include "policydb.h"
+#include "sidtab.h"
+#include "services.h"
+#include "conditional.h"
+#include "mls.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)
+
+static DECLARE_MUTEX(load_sem);
+#define LOAD_LOCK down(&load_sem)
+#define LOAD_UNLOCK up(&load_sem)
+
+static struct sidtab sidtab;
+struct policydb policydb;
+int ss_initialized = 0;
+
+/*
+ * The largest sequence number that has been used when
+ * providing an access decision to the access vector cache.
+ * The sequence number only changes when a policy change
+ * occurs.
+ */
+static u32 latest_granting = 0;
+
+/* Forward declaration. */
+static int context_struct_to_string(struct context *context, char **scontext,
+ u32 *scontext_len);
+
+/*
+ * Return the boolean value of a constraint expression
+ * when it is applied to the specified source and target
+ * security contexts.
+ *
+ * xcontext is a special beast... It is used by the validatetrans rules
+ * only. For these rules, scontext is the context before the transition,
+ * tcontext is the context after the transition, and xcontext is the context
+ * of the process performing the transition. All other callers of
+ * constraint_expr_eval should pass in NULL for xcontext.
+ */
+static int constraint_expr_eval(struct context *scontext,
+ struct context *tcontext,
+ struct context *xcontext,
+ struct constraint_expr *cexpr)
+{
+ u32 val1, val2;
+ struct context *c;
+ struct role_datum *r1, *r2;
+ struct mls_level *l1, *l2;
+ struct constraint_expr *e;
+ int s[CEXPR_MAXDEPTH];
+ int sp = -1;
+
+ for (e = cexpr; e; e = e->next) {
+ switch (e->expr_type) {
+ case CEXPR_NOT:
+ BUG_ON(sp < 0);
+ s[sp] = !s[sp];
+ break;
+ case CEXPR_AND:
+ BUG_ON(sp < 1);
+ sp--;
+ s[sp] &= s[sp+1];
+ break;
+ case CEXPR_OR:
+ BUG_ON(sp < 1);
+ sp--;
+ s[sp] |= s[sp+1];
+ break;
+ case CEXPR_ATTR:
+ if (sp == (CEXPR_MAXDEPTH-1))
+ return 0;
+ switch (e->attr) {
+ case CEXPR_USER:
+ val1 = scontext->user;
+ val2 = tcontext->user;
+ break;
+ case CEXPR_TYPE:
+ val1 = scontext->type;
+ val2 = tcontext->type;
+ break;
+ case CEXPR_ROLE:
+ val1 = scontext->role;
+ val2 = tcontext->role;
+ r1 = policydb.role_val_to_struct[val1 - 1];
+ r2 = policydb.role_val_to_struct[val2 - 1];
+ switch (e->op) {
+ case CEXPR_DOM:
+ s[++sp] = ebitmap_get_bit(&r1->dominates,
+ val2 - 1);
+ continue;
+ case CEXPR_DOMBY:
+ s[++sp] = ebitmap_get_bit(&r2->dominates,
+ val1 - 1);
+ continue;
+ case CEXPR_INCOMP:
+ s[++sp] = ( !ebitmap_get_bit(&r1->dominates,
+ val2 - 1) &&
+ !ebitmap_get_bit(&r2->dominates,
+ val1 - 1) );
+ continue;
+ default:
+ break;
+ }
+ break;
+ case CEXPR_L1L2:
+ l1 = &(scontext->range.level[0]);
+ l2 = &(tcontext->range.level[0]);
+ goto mls_ops;
+ case CEXPR_L1H2:
+ l1 = &(scontext->range.level[0]);
+ l2 = &(tcontext->range.level[1]);
+ goto mls_ops;
+ case CEXPR_H1L2:
+ l1 = &(scontext->range.level[1]);
+ l2 = &(tcontext->range.level[0]);
+ goto mls_ops;
+ case CEXPR_H1H2:
+ l1 = &(scontext->range.level[1]);
+ l2 = &(tcontext->range.level[1]);
+ goto mls_ops;
+ case CEXPR_L1H1:
+ l1 = &(scontext->range.level[0]);
+ l2 = &(scontext->range.level[1]);
+ goto mls_ops;
+ case CEXPR_L2H2:
+ l1 = &(tcontext->range.level[0]);
+ l2 = &(tcontext->range.level[1]);
+ goto mls_ops;
+mls_ops:
+ switch (e->op) {
+ case CEXPR_EQ:
+ s[++sp] = mls_level_eq(l1, l2);
+ continue;
+ case CEXPR_NEQ:
+ s[++sp] = !mls_level_eq(l1, l2);
+ continue;
+ case CEXPR_DOM:
+ s[++sp] = mls_level_dom(l1, l2);
+ continue;
+ case CEXPR_DOMBY:
+ s[++sp] = mls_level_dom(l2, l1);
+ continue;
+ case CEXPR_INCOMP:
+ s[++sp] = mls_level_incomp(l2, l1);
+ continue;
+ default:
+ BUG();
+ return 0;
+ }
+ break;
+ default:
+ BUG();
+ return 0;
+ }
+
+ switch (e->op) {
+ case CEXPR_EQ:
+ s[++sp] = (val1 == val2);
+ break;
+ case CEXPR_NEQ:
+ s[++sp] = (val1 != val2);
+ break;
+ default:
+ BUG();
+ return 0;
+ }
+ break;
+ case CEXPR_NAMES:
+ if (sp == (CEXPR_MAXDEPTH-1))
+ return 0;
+ c = scontext;
+ if (e->attr & CEXPR_TARGET)
+ c = tcontext;
+ else if (e->attr & CEXPR_XTARGET) {
+ c = xcontext;
+ if (!c) {
+ BUG();
+ return 0;
+ }
+ }
+ if (e->attr & CEXPR_USER)
+ val1 = c->user;
+ else if (e->attr & CEXPR_ROLE)
+ val1 = c->role;
+ else if (e->attr & CEXPR_TYPE)
+ val1 = c->type;
+ else {
+ BUG();
+ return 0;
+ }
+
+ switch (e->op) {
+ case CEXPR_EQ:
+ s[++sp] = ebitmap_get_bit(&e->names, val1 - 1);
+ break;
+ case CEXPR_NEQ:
+ s[++sp] = !ebitmap_get_bit(&e->names, val1 - 1);
+ break;
+ default:
+ BUG();
+ return 0;
+ }
+ break;
+ default:
+ BUG();
+ return 0;
+ }
+ }
+
+ BUG_ON(sp != 0);
+ return s[0];
+}
+
+/*
+ * Compute access vectors based on a context structure pair for
+ * the permissions in a particular class.
+ */
+static int context_struct_compute_av(struct context *scontext,
+ struct context *tcontext,
+ u16 tclass,
+ u32 requested,
+ struct av_decision *avd)
+{
+ struct constraint_node *constraint;
+ struct role_allow *ra;
+ struct avtab_key avkey;
+ struct avtab_datum *avdatum;
+ struct class_datum *tclass_datum;
+
+ /*
+ * Remap extended Netlink classes for old policy versions.
+ * Do this here rather than socket_type_to_security_class()
+ * in case a newer policy version is loaded, allowing sockets
+ * to remain in the correct class.
+ */
+ if (policydb_loaded_version < POLICYDB_VERSION_NLCLASS)
+ if (tclass >= SECCLASS_NETLINK_ROUTE_SOCKET &&
+ tclass <= SECCLASS_NETLINK_DNRT_SOCKET)
+ tclass = SECCLASS_NETLINK_SOCKET;
+
+ if (!tclass || tclass > policydb.p_classes.nprim) {
+ printk(KERN_ERR "security_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;
+
+ /*
+ * If a specific type enforcement rule was defined for
+ * this permission check, then use it.
+ */
+ avkey.source_type = scontext->type;
+ avkey.target_type = tcontext->type;
+ avkey.target_class = tclass;
+ avdatum = avtab_search(&policydb.te_avtab, &avkey, AVTAB_AV);
+ if (avdatum) {
+ if (avdatum->specified & AVTAB_ALLOWED)
+ avd->allowed = avtab_allowed(avdatum);
+ if (avdatum->specified & AVTAB_AUDITDENY)
+ avd->auditdeny = avtab_auditdeny(avdatum);
+ if (avdatum->specified & AVTAB_AUDITALLOW)
+ avd->auditallow = avtab_auditallow(avdatum);
+ }
+
+ /* Check conditional av table for additional permissions */
+ cond_compute_av(&policydb.te_cond_avtab, &avkey, avd);
+
+ /*
+ * Remove any permissions prohibited by a constraint (this includes
+ * the MLS policy).
+ */
+ constraint = tclass_datum->constraints;
+ while (constraint) {
+ if ((constraint->permissions & (avd->allowed)) &&
+ !constraint_expr_eval(scontext, tcontext, NULL,
+ constraint->expr)) {
+ avd->allowed = (avd->allowed) & ~(constraint->permissions);
+ }
+ constraint = constraint->next;
+ }
+
+ /*
+ * If checking process transition permission and the
+ * role is changing, then check the (current_role, new_role)
+ * pair.
+ */
+ if (tclass == SECCLASS_PROCESS &&
+ (avd->allowed & (PROCESS__TRANSITION | PROCESS__DYNTRANSITION)) &&
+ scontext->role != tcontext->role) {
+ for (ra = policydb.role_allow; ra; ra = ra->next) {
+ if (scontext->role == ra->role &&
+ tcontext->role == ra->new_role)
+ break;
+ }
+ if (!ra)
+ avd->allowed = (avd->allowed) & ~(PROCESS__TRANSITION |
+ PROCESS__DYNTRANSITION);
+ }
+
+ return 0;
+}
+
+static int security_validtrans_handle_fail(struct context *ocontext,
+ struct context *ncontext,
+ struct context *tcontext,
+ u16 tclass)
+{
+ char *o = NULL, *n = NULL, *t = NULL;
+ u32 olen, nlen, tlen;
+
+ if (context_struct_to_string(ocontext, &o, &olen) < 0)
+ goto out;
+ if (context_struct_to_string(ncontext, &n, &nlen) < 0)
+ goto out;
+ if (context_struct_to_string(tcontext, &t, &tlen) < 0)
+ goto out;
+ audit_log(current->audit_context,
+ "security_validate_transition: denied for"
+ " oldcontext=%s newcontext=%s taskcontext=%s tclass=%s",
+ o, n, t, policydb.p_class_val_to_name[tclass-1]);
+out:
+ kfree(o);
+ kfree(n);
+ kfree(t);
+
+ if (!selinux_enforcing)
+ return 0;
+ return -EPERM;
+}
+
+int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
+ u16 tclass)
+{
+ struct context *ocontext;
+ struct context *ncontext;
+ struct context *tcontext;
+ struct class_datum *tclass_datum;
+ struct constraint_node *constraint;
+ int rc = 0;
+
+ if (!ss_initialized)
+ return 0;
+
+ POLICY_RDLOCK;
+
+ /*
+ * Remap extended Netlink classes for old policy versions.
+ * Do this here rather than socket_type_to_security_class()
+ * in case a newer policy version is loaded, allowing sockets
+ * to remain in the correct class.
+ */
+ if (policydb_loaded_version < POLICYDB_VERSION_NLCLASS)
+ if (tclass >= SECCLASS_NETLINK_ROUTE_SOCKET &&
+ tclass <= SECCLASS_NETLINK_DNRT_SOCKET)
+ tclass = SECCLASS_NETLINK_SOCKET;
+
+ if (!tclass || tclass > policydb.p_classes.nprim) {
+ printk(KERN_ERR "security_validate_transition: "
+ "unrecognized class %d\n", tclass);
+ rc = -EINVAL;
+ goto out;
+ }
+ tclass_datum = policydb.class_val_to_struct[tclass - 1];
+
+ ocontext = sidtab_search(&sidtab, oldsid);
+ if (!ocontext) {
+ printk(KERN_ERR "security_validate_transition: "
+ " unrecognized SID %d\n", oldsid);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ ncontext = sidtab_search(&sidtab, newsid);
+ if (!ncontext) {
+ printk(KERN_ERR "security_validate_transition: "
+ " unrecognized SID %d\n", newsid);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ tcontext = sidtab_search(&sidtab, tasksid);
+ if (!tcontext) {
+ printk(KERN_ERR "security_validate_transition: "
+ " unrecognized SID %d\n", tasksid);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ constraint = tclass_datum->validatetrans;
+ while (constraint) {
+ if (!constraint_expr_eval(ocontext, ncontext, tcontext,
+ constraint->expr)) {
+ rc = security_validtrans_handle_fail(ocontext, ncontext,
+ tcontext, tclass);
+ goto out;
+ }
+ constraint = constraint->next;
+ }
+
+out:
+ POLICY_RDUNLOCK;
+ return rc;
+}
+
+/**
+ * security_compute_av - Compute access vector decisions.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @requested: requested permissions
+ * @avd: access vector decisions
+ *
+ * Compute a set of access vector decisions based on the
+ * SID pair (@ssid, @tsid) for the permissions in @tclass.
+ * Return -%EINVAL if any of the parameters are invalid or %0
+ * if the access vector decisions were computed successfully.
+ */
+int security_compute_av(u32 ssid,
+ u32 tsid,
+ u16 tclass,
+ u32 requested,
+ struct av_decision *avd)
+{
+ struct context *scontext = NULL, *tcontext = NULL;
+ int rc = 0;
+
+ if (!ss_initialized) {
+ avd->allowed = requested;
+ avd->decided = requested;
+ avd->auditallow = 0;
+ avd->auditdeny = 0xffffffff;
+ avd->seqno = latest_granting;
+ return 0;
+ }
+
+ POLICY_RDLOCK;
+
+ scontext = sidtab_search(&sidtab, ssid);
+ if (!scontext) {
+ printk(KERN_ERR "security_compute_av: unrecognized SID %d\n",
+ ssid);
+ rc = -EINVAL;
+ goto out;
+ }
+ tcontext = sidtab_search(&sidtab, tsid);
+ if (!tcontext) {
+ printk(KERN_ERR "security_compute_av: unrecognized SID %d\n",
+ tsid);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ rc = context_struct_compute_av(scontext, tcontext, tclass,
+ requested, avd);
+out:
+ POLICY_RDUNLOCK;
+ return rc;
+}
+
+/*
+ * Write the security context string representation of
+ * the context structure `context' 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.
+ */
+static int context_struct_to_string(struct context *context, char **scontext, u32 *scontext_len)
+{
+ char *scontextp;
+
+ *scontext = NULL;
+ *scontext_len = 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;
+ *scontext_len += strlen(policydb.p_type_val_to_name[context->type - 1]) + 1;
+ *scontext_len += mls_compute_context_len(context);
+
+ /* Allocate space for the context; caller must free this space. */
+ scontextp = kmalloc(*scontext_len, GFP_ATOMIC);
+ if (!scontextp) {
+ return -ENOMEM;
+ }
+ *scontext = scontextp;
+
+ /*
+ * Copy the user name, role name and type name into the context.
+ */
+ sprintf(scontextp, "%s:%s:%s",
+ policydb.p_user_val_to_name[context->user - 1],
+ policydb.p_role_val_to_name[context->role - 1],
+ policydb.p_type_val_to_name[context->type - 1]);
+ scontextp += strlen(policydb.p_user_val_to_name[context->user - 1]) +
+ 1 + strlen(policydb.p_role_val_to_name[context->role - 1]) +
+ 1 + strlen(policydb.p_type_val_to_name[context->type - 1]);
+
+ mls_sid_to_context(context, &scontextp);
+
+ *scontextp = 0;
+
+ return 0;
+}
+
+#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)
+{
+ struct context *context;
+ int rc = 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);
+ strcpy(scontextp, initial_sid_to_string[sid]);
+ *scontext = scontextp;
+ goto out;
+ }
+ printk(KERN_ERR "security_sid_to_context: called before initial "
+ "load_policy on unknown SID %d\n", sid);
+ rc = -EINVAL;
+ goto out;
+ }
+ POLICY_RDLOCK;
+ context = sidtab_search(&sidtab, sid);
+ if (!context) {
+ printk(KERN_ERR "security_sid_to_context: unrecognized SID "
+ "%d\n", sid);
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+ rc = context_struct_to_string(context, scontext, scontext_len);
+out_unlock:
+ POLICY_RDUNLOCK;
+out:
+ return rc;
+
+}
+
+/**
+ * security_context_to_sid - Obtain a SID for a given security context.
+ * @scontext: security context
+ * @scontext_len: length in bytes
+ * @sid: security identifier, SID
+ *
+ * Obtains a SID associated with the security context that
+ * has the string representation specified by @scontext.
+ * Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient
+ * memory is available, or 0 on success.
+ */
+int security_context_to_sid(char *scontext, u32 scontext_len, u32 *sid)
+{
+ 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;
+
+ /* Parse the security context. */
+
+ rc = -EINVAL;
+ scontextp = (char *) scontext2;
+
+ /* Extract the user. */
+ p = scontextp;
+ while (*p && *p != ':')
+ p++;
+
+ if (*p == 0)
+ goto out_unlock;
+
+ *p++ = 0;
+
+ usrdatum = hashtab_search(policydb.p_users.table, scontextp);
+ if (!usrdatum)
+ goto out_unlock;
+
+ context.user = usrdatum->value;
+
+ /* Extract role. */
+ scontextp = p;
+ while (*p && *p != ':')
+ p++;
+
+ if (*p == 0)
+ goto out_unlock;
+
+ *p++ = 0;
+
+ role = hashtab_search(policydb.p_roles.table, scontextp);
+ if (!role)
+ goto out_unlock;
+ context.role = role->value;
+
+ /* Extract type. */
+ scontextp = p;
+ while (*p && *p != ':')
+ p++;
+ oldc = *p;
+ *p++ = 0;
+
+ typdatum = hashtab_search(policydb.p_types.table, scontextp);
+ if (!typdatum)
+ goto out_unlock;
+
+ context.type = typdatum->value;
+
+ rc = mls_context_to_sid(oldc, &p, &context);
+ if (rc)
+ goto out_unlock;
+
+ if ((p - scontext2) < scontext_len) {
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+
+ /* Check the validity of the new context. */
+ if (!policydb_context_isvalid(&policydb, &context)) {
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+ /* Obtain the new sid. */
+ rc = sidtab_context_to_sid(&sidtab, &context, sid);
+out_unlock:
+ POLICY_RDUNLOCK;
+ context_destroy(&context);
+ kfree(scontext2);
+out:
+ return rc;
+}
+
+static int compute_sid_handle_invalid_context(
+ struct context *scontext,
+ struct context *tcontext,
+ u16 tclass,
+ struct context *newcontext)
+{
+ char *s = NULL, *t = NULL, *n = NULL;
+ u32 slen, tlen, nlen;
+
+ if (context_struct_to_string(scontext, &s, &slen) < 0)
+ goto out;
+ if (context_struct_to_string(tcontext, &t, &tlen) < 0)
+ goto out;
+ if (context_struct_to_string(newcontext, &n, &nlen) < 0)
+ goto out;
+ audit_log(current->audit_context,
+ "security_compute_sid: invalid context %s"
+ " for scontext=%s"
+ " tcontext=%s"
+ " tclass=%s",
+ n, s, t, policydb.p_class_val_to_name[tclass-1]);
+out:
+ kfree(s);
+ kfree(t);
+ kfree(n);
+ if (!selinux_enforcing)
+ return 0;
+ return -EACCES;
+}
+
+static int security_compute_sid(u32 ssid,
+ u32 tsid,
+ u16 tclass,
+ u32 specified,
+ u32 *out_sid)
+{
+ struct context *scontext = NULL, *tcontext = NULL, newcontext;
+ struct role_trans *roletr = NULL;
+ struct avtab_key avkey;
+ struct avtab_datum *avdatum;
+ struct avtab_node *node;
+ unsigned int type_change = 0;
+ int rc = 0;
+
+ if (!ss_initialized) {
+ switch (tclass) {
+ case SECCLASS_PROCESS:
+ *out_sid = ssid;
+ break;
+ default:
+ *out_sid = tsid;
+ break;
+ }
+ goto out;
+ }
+
+ POLICY_RDLOCK;
+
+ scontext = sidtab_search(&sidtab, ssid);
+ if (!scontext) {
+ printk(KERN_ERR "security_compute_sid: unrecognized SID %d\n",
+ ssid);
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+ tcontext = sidtab_search(&sidtab, tsid);
+ if (!tcontext) {
+ printk(KERN_ERR "security_compute_sid: unrecognized SID %d\n",
+ tsid);
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+
+ context_init(&newcontext);
+
+ /* Set the user identity. */
+ switch (specified) {
+ case AVTAB_TRANSITION:
+ case AVTAB_CHANGE:
+ /* Use the process user identity. */
+ newcontext.user = scontext->user;
+ break;
+ case AVTAB_MEMBER:
+ /* Use the related object owner. */
+ newcontext.user = tcontext->user;
+ break;
+ }
+
+ /* Set the role and type to default values. */
+ switch (tclass) {
+ case SECCLASS_PROCESS:
+ /* Use the current role and type of process. */
+ newcontext.role = scontext->role;
+ newcontext.type = scontext->type;
+ break;
+ default:
+ /* Use the well-defined object role. */
+ newcontext.role = OBJECT_R_VAL;
+ /* Use the type of the related object. */
+ newcontext.type = tcontext->type;
+ }
+
+ /* Look for a type transition/member/change rule. */
+ avkey.source_type = scontext->type;
+ avkey.target_type = tcontext->type;
+ avkey.target_class = tclass;
+ avdatum = avtab_search(&policydb.te_avtab, &avkey, AVTAB_TYPE);
+
+ /* If no permanent rule, also check for enabled conditional rules */
+ if(!avdatum) {
+ node = avtab_search_node(&policydb.te_cond_avtab, &avkey, specified);
+ for (; node != NULL; node = avtab_search_node_next(node, specified)) {
+ if (node->datum.specified & AVTAB_ENABLED) {
+ avdatum = &node->datum;
+ break;
+ }
+ }
+ }
+
+ type_change = (avdatum && (avdatum->specified & specified));
+ if (type_change) {
+ /* Use the type from the type transition/member/change rule. */
+ switch (specified) {
+ case AVTAB_TRANSITION:
+ newcontext.type = avtab_transition(avdatum);
+ break;
+ case AVTAB_MEMBER:
+ newcontext.type = avtab_member(avdatum);
+ break;
+ case AVTAB_CHANGE:
+ newcontext.type = avtab_change(avdatum);
+ break;
+ }
+ }
+
+ /* Check for class-specific changes. */
+ switch (tclass) {
+ case SECCLASS_PROCESS:
+ if (specified & AVTAB_TRANSITION) {
+ /* Look for a role transition rule. */
+ for (roletr = policydb.role_tr; roletr;
+ roletr = roletr->next) {
+ if (roletr->role == scontext->role &&
+ roletr->type == tcontext->type) {
+ /* Use the role transition rule. */
+ newcontext.role = roletr->new_role;
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* Set the MLS attributes.
+ This is done last because it may allocate memory. */
+ rc = mls_compute_sid(scontext, tcontext, tclass, specified, &newcontext);
+ if (rc)
+ goto out_unlock;
+
+ /* Check the validity of the context. */
+ if (!policydb_context_isvalid(&policydb, &newcontext)) {
+ rc = compute_sid_handle_invalid_context(scontext,
+ tcontext,
+ tclass,
+ &newcontext);
+ if (rc)
+ goto out_unlock;
+ }
+ /* Obtain the sid for the context. */
+ rc = sidtab_context_to_sid(&sidtab, &newcontext, out_sid);
+out_unlock:
+ POLICY_RDUNLOCK;
+ context_destroy(&newcontext);
+out:
+ return rc;
+}
+
+/**
+ * security_transition_sid - Compute the SID for a new subject/object.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @out_sid: security identifier for new subject/object
+ *
+ * Compute a SID to use for labeling a new subject or object in the
+ * class @tclass based on a SID pair (@ssid, @tsid).
+ * Return -%EINVAL if any of the parameters are invalid, -%ENOMEM
+ * if insufficient memory is available, or %0 if the new SID was
+ * computed successfully.
+ */
+int security_transition_sid(u32 ssid,
+ u32 tsid,
+ u16 tclass,
+ u32 *out_sid)
+{
+ return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, out_sid);
+}
+
+/**
+ * security_member_sid - Compute the SID for member selection.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @out_sid: security identifier for selected member
+ *
+ * Compute a SID to use when selecting a member of a polyinstantiated
+ * object of class @tclass based on a SID pair (@ssid, @tsid).
+ * Return -%EINVAL if any of the parameters are invalid, -%ENOMEM
+ * if insufficient memory is available, or %0 if the SID was
+ * computed successfully.
+ */
+int security_member_sid(u32 ssid,
+ u32 tsid,
+ u16 tclass,
+ u32 *out_sid)
+{
+ return security_compute_sid(ssid, tsid, tclass, AVTAB_MEMBER, out_sid);
+}
+
+/**
+ * security_change_sid - Compute the SID for object relabeling.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @out_sid: security identifier for selected member
+ *
+ * Compute a SID to use for relabeling an object of class @tclass
+ * based on a SID pair (@ssid, @tsid).
+ * Return -%EINVAL if any of the parameters are invalid, -%ENOMEM
+ * if insufficient memory is available, or %0 if the SID was
+ * computed successfully.
+ */
+int security_change_sid(u32 ssid,
+ u32 tsid,
+ u16 tclass,
+ u32 *out_sid)
+{
+ return security_compute_sid(ssid, tsid, tclass, AVTAB_CHANGE, out_sid);
+}
+
+/*
+ * 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.
+ */
+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;
+ }
+ 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;
+ }
+ }
+ 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;
+}
+
+/* Clone the SID into the new SID table. */
+static int clone_sid(u32 sid,
+ struct context *context,
+ void *arg)
+{
+ struct sidtab *s = arg;
+
+ return sidtab_insert(s, sid, context);
+}
+
+static inline int convert_context_handle_invalid_context(struct context *context)
+{
+ int rc = 0;
+
+ if (selinux_enforcing) {
+ rc = -EINVAL;
+ } else {
+ char *s;
+ u32 len;
+
+ context_struct_to_string(context, &s, &len);
+ printk(KERN_ERR "security: context %s is invalid\n", s);
+ kfree(s);
+ }
+ return rc;
+}
+
+struct convert_context_args {
+ struct policydb *oldp;
+ struct policydb *newp;
+};
+
+/*
+ * Convert the values in the security context
+ * structure `c' from the values specified
+ * in the policy `p->oldp' to the values specified
+ * in the policy `p->newp'. Verify that the
+ * context is valid under the new policy.
+ */
+static int convert_context(u32 key,
+ struct context *c,
+ void *p)
+{
+ struct convert_context_args *args;
+ struct context oldc;
+ struct role_datum *role;
+ struct type_datum *typdatum;
+ struct user_datum *usrdatum;
+ char *s;
+ u32 len;
+ int rc;
+
+ args = p;
+
+ rc = context_cpy(&oldc, c);
+ if (rc)
+ goto out;
+
+ rc = -EINVAL;
+
+ /* Convert the user. */
+ usrdatum = hashtab_search(args->newp->p_users.table,
+ 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) {
+ 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) {
+ goto bad;
+ }
+ c->type = typdatum->value;
+
+ rc = mls_convert_context(args->oldp, args->newp, c);
+ if (rc)
+ goto bad;
+
+ /* Check the validity of the new context. */
+ if (!policydb_context_isvalid(args->newp, c)) {
+ rc = convert_context_handle_invalid_context(&oldc);
+ if (rc)
+ goto bad;
+ }
+
+ context_destroy(&oldc);
+out:
+ return rc;
+bad:
+ context_struct_to_string(&oldc, &s, &len);
+ context_destroy(&oldc);
+ printk(KERN_ERR "security: invalidating context %s\n", s);
+ kfree(s);
+ goto out;
+}
+
+extern void selinux_complete_init(void);
+
+/**
+ * security_load_policy - Load a security policy configuration.
+ * @data: binary policy data
+ * @len: length of data in bytes
+ *
+ * Load a new set of security policy configuration data,
+ * validate it and convert the SID table as necessary.
+ * This function will flush the access vector cache after
+ * loading the new policy.
+ */
+int security_load_policy(void *data, size_t len)
+{
+ struct policydb oldpolicydb, newpolicydb;
+ struct sidtab oldsidtab, newsidtab;
+ struct convert_context_args args;
+ u32 seqno;
+ 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;
+ }
+ policydb_loaded_version = policydb.policyvers;
+ ss_initialized = 1;
+
+ LOAD_UNLOCK;
+ selinux_complete_init();
+ return 0;
+ }
+
+#if 0
+ sidtab_hash_eval(&sidtab, "sids");
+#endif
+
+ if (policydb_read(&newpolicydb, fp)) {
+ LOAD_UNLOCK;
+ return -EINVAL;
+ }
+
+ 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");
+ rc = -EINVAL;
+ goto err;
+ }
+
+ /* Clone the SID table. */
+ sidtab_shutdown(&sidtab);
+ if (sidtab_map(&sidtab, clone_sid, &newsidtab)) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ /* Convert the internal representations of contexts
+ in the new SID table and remove invalid SIDs. */
+ args.oldp = &policydb;
+ args.newp = &newpolicydb;
+ sidtab_map_remove_on_error(&newsidtab, convert_context, &args);
+
+ /* 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;
+ memcpy(&policydb, &newpolicydb, sizeof policydb);
+ sidtab_set(&sidtab, &newsidtab);
+ seqno = ++latest_granting;
+ policydb_loaded_version = policydb.policyvers;
+ POLICY_WRUNLOCK;
+ LOAD_UNLOCK;
+
+ /* Free the old policydb and SID table. */
+ policydb_destroy(&oldpolicydb);
+ sidtab_destroy(&oldsidtab);
+
+ avc_ss_reset(seqno);
+ selnl_notify_policyload(seqno);
+
+ return 0;
+
+err:
+ LOAD_UNLOCK;
+ sidtab_destroy(&newsidtab);
+ policydb_destroy(&newpolicydb);
+ return rc;
+
+}
+
+/**
+ * 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)
+{
+ struct ocontext *c;
+ int rc = 0;
+
+ POLICY_RDLOCK;
+
+ c = policydb.ocontexts[OCON_PORT];
+ while (c) {
+ if (c->u.port.protocol == protocol &&
+ c->u.port.low_port <= port &&
+ c->u.port.high_port >= port)
+ break;
+ c = c->next;
+ }
+
+ if (c) {
+ if (!c->sid[0]) {
+ rc = sidtab_context_to_sid(&sidtab,
+ &c->context[0],
+ &c->sid[0]);
+ if (rc)
+ goto out;
+ }
+ *out_sid = c->sid[0];
+ } else {
+ *out_sid = SECINITSID_PORT;
+ }
+
+out:
+ POLICY_RDUNLOCK;
+ return rc;
+}
+
+/**
+ * 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 rc = 0;
+ struct ocontext *c;
+
+ POLICY_RDLOCK;
+
+ c = policydb.ocontexts[OCON_NETIF];
+ while (c) {
+ if (strcmp(name, c->u.name) == 0)
+ break;
+ c = c->next;
+ }
+
+ if (c) {
+ if (!c->sid[0] || !c->sid[1]) {
+ rc = sidtab_context_to_sid(&sidtab,
+ &c->context[0],
+ &c->sid[0]);
+ if (rc)
+ goto out;
+ rc = sidtab_context_to_sid(&sidtab,
+ &c->context[1],
+ &c->sid[1]);
+ if (rc)
+ goto out;
+ }
+ *if_sid = c->sid[0];
+ *msg_sid = c->sid[1];
+ } else {
+ *if_sid = SECINITSID_NETIF;
+ *msg_sid = SECINITSID_NETMSG;
+ }
+
+out:
+ POLICY_RDUNLOCK;
+ return rc;
+}
+
+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])) {
+ fail = 1;
+ break;
+ }
+
+ return !fail;
+}
+
+/**
+ * security_node_sid - Obtain the SID for a node (host).
+ * @domain: communication domain aka address family
+ * @addrp: address
+ * @addrlen: address length in bytes
+ * @out_sid: security identifier
+ */
+int security_node_sid(u16 domain,
+ void *addrp,
+ u32 addrlen,
+ u32 *out_sid)
+{
+ int rc = 0;
+ struct ocontext *c;
+
+ POLICY_RDLOCK;
+
+ switch (domain) {
+ case AF_INET: {
+ u32 addr;
+
+ if (addrlen != sizeof(u32)) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ addr = *((u32 *)addrp);
+
+ c = policydb.ocontexts[OCON_NODE];
+ while (c) {
+ if (c->u.node.addr == (addr & c->u.node.mask))
+ break;
+ c = c->next;
+ }
+ break;
+ }
+
+ case AF_INET6:
+ if (addrlen != sizeof(u64) * 2) {
+ rc = -EINVAL;
+ goto out;
+ }
+ c = policydb.ocontexts[OCON_NODE6];
+ while (c) {
+ if (match_ipv6_addrmask(addrp, c->u.node6.addr,
+ c->u.node6.mask))
+ break;
+ c = c->next;
+ }
+ break;
+
+ default:
+ *out_sid = SECINITSID_NODE;
+ goto out;
+ }
+
+ if (c) {
+ if (!c->sid[0]) {
+ rc = sidtab_context_to_sid(&sidtab,
+ &c->context[0],
+ &c->sid[0]);
+ if (rc)
+ goto out;
+ }
+ *out_sid = c->sid[0];
+ } else {
+ *out_sid = SECINITSID_NODE;
+ }
+
+out:
+ POLICY_RDUNLOCK;
+ return rc;
+}
+
+#define SIDS_NEL 25
+
+/**
+ * security_get_user_sids - Obtain reachable SIDs for a user.
+ * @fromsid: starting SID
+ * @username: username
+ * @sids: array of reachable SIDs for user
+ * @nel: number of elements in @sids
+ *
+ * Generate the set of SIDs for legal security contexts
+ * for a given user that can be reached by @fromsid.
+ * Set *@sids to point to a dynamically allocated
+ * array containing the set of SIDs. Set *@nel to the
+ * number of elements in the array.
+ */
+
+int security_get_user_sids(u32 fromsid,
+ char *username,
+ u32 **sids,
+ u32 *nel)
+{
+ struct context *fromcon, usercon;
+ u32 *mysids, *mysids2, sid;
+ u32 mynel = 0, maxnel = SIDS_NEL;
+ struct user_datum *user;
+ struct role_datum *role;
+ struct av_decision avd;
+ int rc = 0, i, j;
+
+ if (!ss_initialized) {
+ *sids = NULL;
+ *nel = 0;
+ goto out;
+ }
+
+ POLICY_RDLOCK;
+
+ fromcon = sidtab_search(&sidtab, fromsid);
+ if (!fromcon) {
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+
+ user = hashtab_search(policydb.p_users.table, username);
+ if (!user) {
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+ usercon.user = user->value;
+
+ mysids = kmalloc(maxnel*sizeof(*mysids), GFP_ATOMIC);
+ if (!mysids) {
+ rc = -ENOMEM;
+ goto out_unlock;
+ }
+ memset(mysids, 0, maxnel*sizeof(*mysids));
+
+ for (i = ebitmap_startbit(&user->roles); i < ebitmap_length(&user->roles); i++) {
+ if (!ebitmap_get_bit(&user->roles, i))
+ continue;
+ role = policydb.role_val_to_struct[i];
+ usercon.role = i+1;
+ for (j = ebitmap_startbit(&role->types); j < ebitmap_length(&role->types); j++) {
+ if (!ebitmap_get_bit(&role->types, j))
+ continue;
+ 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);
+ goto out_unlock;
+ }
+ if (mynel < maxnel) {
+ mysids[mynel++] = sid;
+ } else {
+ maxnel += SIDS_NEL;
+ mysids2 = kmalloc(maxnel*sizeof(*mysids2), GFP_ATOMIC);
+ if (!mysids2) {
+ rc = -ENOMEM;
+ kfree(mysids);
+ goto out_unlock;
+ }
+ memset(mysids2, 0, maxnel*sizeof(*mysids2));
+ memcpy(mysids2, mysids, mynel * sizeof(*mysids2));
+ kfree(mysids);
+ mysids = mysids2;
+ mysids[mynel++] = sid;
+ }
+ }
+ }
+
+ *sids = mysids;
+ *nel = mynel;
+
+out_unlock:
+ POLICY_RDUNLOCK;
+out:
+ return rc;
+}
+
+/**
+ * security_genfs_sid - Obtain a SID for a file in a filesystem
+ * @fstype: filesystem type
+ * @path: path from root of mount
+ * @sclass: file security class
+ * @sid: SID for path
+ *
+ * Obtain a SID to use for a file in a filesystem that
+ * cannot support xattr or use a fixed labeling behavior like
+ * transition SIDs or task SIDs.
+ */
+int security_genfs_sid(const char *fstype,
+ char *path,
+ u16 sclass,
+ u32 *sid)
+{
+ int len;
+ struct genfs *genfs;
+ struct ocontext *c;
+ int rc = 0, cmp = 0;
+
+ POLICY_RDLOCK;
+
+ for (genfs = policydb.genfs; genfs; genfs = genfs->next) {
+ cmp = strcmp(fstype, genfs->fstype);
+ if (cmp <= 0)
+ break;
+ }
+
+ if (!genfs || cmp) {
+ *sid = SECINITSID_UNLABELED;
+ rc = -ENOENT;
+ goto out;
+ }
+
+ for (c = genfs->head; c; c = c->next) {
+ len = strlen(c->u.name);
+ if ((!c->v.sclass || sclass == c->v.sclass) &&
+ (strncmp(c->u.name, path, len) == 0))
+ break;
+ }
+
+ if (!c) {
+ *sid = SECINITSID_UNLABELED;
+ rc = -ENOENT;
+ goto out;
+ }
+
+ if (!c->sid[0]) {
+ rc = sidtab_context_to_sid(&sidtab,
+ &c->context[0],
+ &c->sid[0]);
+ if (rc)
+ goto out;
+ }
+
+ *sid = c->sid[0];
+out:
+ POLICY_RDUNLOCK;
+ return rc;
+}
+
+/**
+ * security_fs_use - Determine how to handle labeling for a filesystem.
+ * @fstype: filesystem type
+ * @behavior: labeling behavior
+ * @sid: SID for filesystem (superblock)
+ */
+int security_fs_use(
+ const char *fstype,
+ unsigned int *behavior,
+ u32 *sid)
+{
+ int rc = 0;
+ struct ocontext *c;
+
+ POLICY_RDLOCK;
+
+ c = policydb.ocontexts[OCON_FSUSE];
+ while (c) {
+ if (strcmp(fstype, c->u.name) == 0)
+ break;
+ c = c->next;
+ }
+
+ if (c) {
+ *behavior = c->v.behavior;
+ if (!c->sid[0]) {
+ rc = sidtab_context_to_sid(&sidtab,
+ &c->context[0],
+ &c->sid[0]);
+ if (rc)
+ goto out;
+ }
+ *sid = c->sid[0];
+ } else {
+ rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, sid);
+ if (rc) {
+ *behavior = SECURITY_FS_USE_NONE;
+ rc = 0;
+ } else {
+ *behavior = SECURITY_FS_USE_GENFS;
+ }
+ }
+
+out:
+ POLICY_RDUNLOCK;
+ return rc;
+}
+
+int security_get_bools(int *len, char ***names, int **values)
+{
+ int i, rc = -ENOMEM;
+
+ POLICY_RDLOCK;
+ *names = NULL;
+ *values = NULL;
+
+ *len = policydb.p_bools.nprim;
+ if (!*len) {
+ rc = 0;
+ goto out;
+ }
+
+ *names = (char**)kmalloc(sizeof(char*) * *len, GFP_ATOMIC);
+ if (!*names)
+ goto err;
+ memset(*names, 0, sizeof(char*) * *len);
+
+ *values = (int*)kmalloc(sizeof(int) * *len, GFP_ATOMIC);
+ if (!*values)
+ goto err;
+
+ for (i = 0; i < *len; i++) {
+ 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);
+ if (!(*names)[i])
+ goto err;
+ strncpy((*names)[i], policydb.p_bool_val_to_name[i], name_len);
+ (*names)[i][name_len - 1] = 0;
+ }
+ rc = 0;
+out:
+ POLICY_RDUNLOCK;
+ return rc;
+err:
+ if (*names) {
+ for (i = 0; i < *len; i++)
+ if ((*names)[i])
+ kfree((*names)[i]);
+ }
+ if (*values)
+ kfree(*values);
+ goto out;
+}
+
+
+int security_set_bools(int len, int *values)
+{
+ int i, rc = 0;
+ int lenp, seqno = 0;
+ struct cond_node *cur;
+
+ POLICY_WRLOCK;
+
+ lenp = policydb.p_bools.nprim;
+ if (len != lenp) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ printk(KERN_INFO "security: committed booleans { ");
+ for (i = 0; i < len; i++) {
+ 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)
+ goto out;
+ }
+
+ seqno = ++latest_granting;
+
+out:
+ POLICY_WRUNLOCK;
+ if (!rc) {
+ avc_ss_reset(seqno);
+ selnl_notify_policyload(seqno);
+ }
+ return rc;
+}
+
+int security_get_bool_value(int bool)
+{
+ int rc = 0;
+ int len;
+
+ POLICY_RDLOCK;
+
+ len = policydb.p_bools.nprim;
+ if (bool >= len) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ rc = policydb.bool_val_to_struct[bool]->state;
+out:
+ POLICY_RDUNLOCK;
+ return rc;
+}
diff --git a/security/selinux/ss/services.h b/security/selinux/ss/services.h
new file mode 100644
index 00000000000..e8d907e903c
--- /dev/null
+++ b/security/selinux/ss/services.h
@@ -0,0 +1,15 @@
+/*
+ * Implementation of the security services.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#ifndef _SS_SERVICES_H_
+#define _SS_SERVICES_H_
+
+#include "policydb.h"
+#include "sidtab.h"
+
+extern struct policydb policydb;
+
+#endif /* _SS_SERVICES_H_ */
+
diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c
new file mode 100644
index 00000000000..871c33bd074
--- /dev/null
+++ b/security/selinux/ss/sidtab.c
@@ -0,0 +1,305 @@
+/*
+ * Implementation of the SID table type.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include "flask.h"
+#include "security.h"
+#include "sidtab.h"
+
+#define SIDTAB_HASH(sid) \
+(sid & SIDTAB_HASH_MASK)
+
+#define INIT_SIDTAB_LOCK(s) spin_lock_init(&s->lock)
+#define SIDTAB_LOCK(s, x) spin_lock_irqsave(&s->lock, x)
+#define SIDTAB_UNLOCK(s, x) spin_unlock_irqrestore(&s->lock, x)
+
+int sidtab_init(struct sidtab *s)
+{
+ int i;
+
+ s->htable = kmalloc(sizeof(*(s->htable)) * SIDTAB_SIZE, GFP_ATOMIC);
+ if (!s->htable)
+ return -ENOMEM;
+ for (i = 0; i < SIDTAB_SIZE; i++)
+ s->htable[i] = NULL;
+ s->nel = 0;
+ s->next_sid = 1;
+ s->shutdown = 0;
+ INIT_SIDTAB_LOCK(s);
+ return 0;
+}
+
+int sidtab_insert(struct sidtab *s, u32 sid, struct context *context)
+{
+ int hvalue, rc = 0;
+ struct sidtab_node *prev, *cur, *newnode;
+
+ if (!s) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ hvalue = SIDTAB_HASH(sid);
+ prev = NULL;
+ cur = s->htable[hvalue];
+ while (cur != NULL && sid > cur->sid) {
+ prev = cur;
+ cur = cur->next;
+ }
+
+ if (cur && sid == cur->sid) {
+ rc = -EEXIST;
+ goto out;
+ }
+
+ newnode = kmalloc(sizeof(*newnode), GFP_ATOMIC);
+ if (newnode == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ newnode->sid = sid;
+ if (context_cpy(&newnode->context, context)) {
+ kfree(newnode);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (prev) {
+ newnode->next = prev->next;
+ wmb();
+ prev->next = newnode;
+ } else {
+ newnode->next = s->htable[hvalue];
+ wmb();
+ s->htable[hvalue] = newnode;
+ }
+
+ s->nel++;
+ if (sid >= s->next_sid)
+ s->next_sid = sid + 1;
+out:
+ return rc;
+}
+
+struct context *sidtab_search(struct sidtab *s, u32 sid)
+{
+ int hvalue;
+ struct sidtab_node *cur;
+
+ if (!s)
+ return NULL;
+
+ hvalue = SIDTAB_HASH(sid);
+ cur = s->htable[hvalue];
+ while (cur != NULL && sid > cur->sid)
+ cur = cur->next;
+
+ if (cur == NULL || sid != cur->sid) {
+ /* Remap invalid SIDs to the unlabeled SID. */
+ sid = SECINITSID_UNLABELED;
+ hvalue = SIDTAB_HASH(sid);
+ cur = s->htable[hvalue];
+ while (cur != NULL && sid > cur->sid)
+ cur = cur->next;
+ if (!cur || sid != cur->sid)
+ return NULL;
+ }
+
+ return &cur->context;
+}
+
+int sidtab_map(struct sidtab *s,
+ int (*apply) (u32 sid,
+ struct context *context,
+ void *args),
+ void *args)
+{
+ int i, rc = 0;
+ struct sidtab_node *cur;
+
+ if (!s)
+ goto out;
+
+ for (i = 0; i < SIDTAB_SIZE; i++) {
+ cur = s->htable[i];
+ while (cur != NULL) {
+ rc = apply(cur->sid, &cur->context, args);
+ if (rc)
+ goto out;
+ cur = cur->next;
+ }
+ }
+out:
+ return rc;
+}
+
+void sidtab_map_remove_on_error(struct sidtab *s,
+ int (*apply) (u32 sid,
+ struct context *context,
+ void *args),
+ void *args)
+{
+ int i, ret;
+ struct sidtab_node *last, *cur, *temp;
+
+ if (!s)
+ return;
+
+ for (i = 0; i < SIDTAB_SIZE; i++) {
+ last = NULL;
+ cur = s->htable[i];
+ while (cur != NULL) {
+ ret = apply(cur->sid, &cur->context, args);
+ if (ret) {
+ if (last) {
+ last->next = cur->next;
+ } else {
+ s->htable[i] = cur->next;
+ }
+
+ temp = cur;
+ cur = cur->next;
+ context_destroy(&temp->context);
+ kfree(temp);
+ s->nel--;
+ } else {
+ last = cur;
+ cur = cur->next;
+ }
+ }
+ }
+
+ return;
+}
+
+static inline u32 sidtab_search_context(struct sidtab *s,
+ struct context *context)
+{
+ int i;
+ struct sidtab_node *cur;
+
+ for (i = 0; i < SIDTAB_SIZE; i++) {
+ cur = s->htable[i];
+ while (cur != NULL) {
+ if (context_cmp(&cur->context, context))
+ return cur->sid;
+ cur = cur->next;
+ }
+ }
+ return 0;
+}
+
+int sidtab_context_to_sid(struct sidtab *s,
+ struct context *context,
+ u32 *out_sid)
+{
+ u32 sid;
+ int ret = 0;
+ unsigned long flags;
+
+ *out_sid = SECSID_NULL;
+
+ sid = sidtab_search_context(s, context);
+ if (!sid) {
+ SIDTAB_LOCK(s, flags);
+ /* Rescan now that we hold the lock. */
+ sid = sidtab_search_context(s, context);
+ if (sid)
+ goto unlock_out;
+ /* No SID exists for the context. Allocate a new one. */
+ if (s->next_sid == UINT_MAX || s->shutdown) {
+ ret = -ENOMEM;
+ goto unlock_out;
+ }
+ sid = s->next_sid++;
+ ret = sidtab_insert(s, sid, context);
+ if (ret)
+ s->next_sid--;
+unlock_out:
+ SIDTAB_UNLOCK(s, flags);
+ }
+
+ if (ret)
+ return ret;
+
+ *out_sid = sid;
+ return 0;
+}
+
+void sidtab_hash_eval(struct sidtab *h, char *tag)
+{
+ int i, chain_len, slots_used, max_chain_len;
+ struct sidtab_node *cur;
+
+ slots_used = 0;
+ max_chain_len = 0;
+ for (i = 0; i < SIDTAB_SIZE; i++) {
+ cur = h->htable[i];
+ if (cur) {
+ slots_used++;
+ chain_len = 0;
+ while (cur) {
+ chain_len++;
+ cur = cur->next;
+ }
+
+ if (chain_len > max_chain_len)
+ max_chain_len = chain_len;
+ }
+ }
+
+ printk(KERN_INFO "%s: %d entries and %d/%d buckets used, longest "
+ "chain length %d\n", tag, h->nel, slots_used, SIDTAB_SIZE,
+ max_chain_len);
+}
+
+void sidtab_destroy(struct sidtab *s)
+{
+ int i;
+ struct sidtab_node *cur, *temp;
+
+ if (!s)
+ return;
+
+ for (i = 0; i < SIDTAB_SIZE; i++) {
+ cur = s->htable[i];
+ while (cur != NULL) {
+ temp = cur;
+ cur = cur->next;
+ context_destroy(&temp->context);
+ kfree(temp);
+ }
+ s->htable[i] = NULL;
+ }
+ kfree(s->htable);
+ s->htable = NULL;
+ s->nel = 0;
+ s->next_sid = 1;
+}
+
+void sidtab_set(struct sidtab *dst, struct sidtab *src)
+{
+ unsigned long flags;
+
+ SIDTAB_LOCK(src, flags);
+ dst->htable = src->htable;
+ dst->nel = src->nel;
+ dst->next_sid = src->next_sid;
+ dst->shutdown = 0;
+ SIDTAB_UNLOCK(src, flags);
+}
+
+void sidtab_shutdown(struct sidtab *s)
+{
+ unsigned long flags;
+
+ SIDTAB_LOCK(s, flags);
+ s->shutdown = 1;
+ SIDTAB_UNLOCK(s, flags);
+}
diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h
new file mode 100644
index 00000000000..2fe9dfa3eb3
--- /dev/null
+++ b/security/selinux/ss/sidtab.h
@@ -0,0 +1,59 @@
+/*
+ * A security identifier table (sidtab) is a hash table
+ * of security context structures indexed by SID value.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#ifndef _SS_SIDTAB_H_
+#define _SS_SIDTAB_H_
+
+#include "context.h"
+
+struct sidtab_node {
+ u32 sid; /* security identifier */
+ struct context context; /* security context structure */
+ struct sidtab_node *next;
+};
+
+#define SIDTAB_HASH_BITS 7
+#define SIDTAB_HASH_BUCKETS (1 << SIDTAB_HASH_BITS)
+#define SIDTAB_HASH_MASK (SIDTAB_HASH_BUCKETS-1)
+
+#define SIDTAB_SIZE SIDTAB_HASH_BUCKETS
+
+struct sidtab {
+ struct sidtab_node **htable;
+ unsigned int nel; /* number of elements */
+ unsigned int next_sid; /* next SID to allocate */
+ unsigned char shutdown;
+ spinlock_t lock;
+};
+
+int sidtab_init(struct sidtab *s);
+int sidtab_insert(struct sidtab *s, u32 sid, struct context *context);
+struct context *sidtab_search(struct sidtab *s, u32 sid);
+
+int sidtab_map(struct sidtab *s,
+ int (*apply) (u32 sid,
+ struct context *context,
+ void *args),
+ void *args);
+
+void sidtab_map_remove_on_error(struct sidtab *s,
+ int (*apply) (u32 sid,
+ struct context *context,
+ void *args),
+ void *args);
+
+int sidtab_context_to_sid(struct sidtab *s,
+ struct context *context,
+ u32 *sid);
+
+void sidtab_hash_eval(struct sidtab *h, char *tag);
+void sidtab_destroy(struct sidtab *s);
+void sidtab_set(struct sidtab *dst, struct sidtab *src);
+void sidtab_shutdown(struct sidtab *s);
+
+#endif /* _SS_SIDTAB_H_ */
+
+
diff --git a/security/selinux/ss/symtab.c b/security/selinux/ss/symtab.c
new file mode 100644
index 00000000000..24a10d36d3b
--- /dev/null
+++ b/security/selinux/ss/symtab.c
@@ -0,0 +1,44 @@
+/*
+ * Implementation of the symbol table type.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include "symtab.h"
+
+static unsigned int symhash(struct hashtab *h, void *key)
+{
+ char *p, *keyp;
+ unsigned int size;
+ unsigned int val;
+
+ val = 0;
+ keyp = key;
+ size = strlen(keyp);
+ for (p = keyp; (p - keyp) < size; p++)
+ val = (val << 4 | (val >> (8*sizeof(unsigned int)-4))) ^ (*p);
+ return val & (h->size - 1);
+}
+
+static int symcmp(struct hashtab *h, void *key1, void *key2)
+{
+ char *keyp1, *keyp2;
+
+ keyp1 = key1;
+ keyp2 = key2;
+ return strcmp(keyp1, keyp2);
+}
+
+
+int symtab_init(struct symtab *s, unsigned int size)
+{
+ s->table = hashtab_create(symhash, symcmp, size);
+ if (!s->table)
+ return -1;
+ s->nprim = 0;
+ return 0;
+}
+
diff --git a/security/selinux/ss/symtab.h b/security/selinux/ss/symtab.h
new file mode 100644
index 00000000000..ca422b42fbc
--- /dev/null
+++ b/security/selinux/ss/symtab.h
@@ -0,0 +1,23 @@
+/*
+ * A symbol table (symtab) maintains associations between symbol
+ * strings and datum values. The type of the datum values
+ * is arbitrary. The symbol table type is implemented
+ * using the hash table type (hashtab).
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#ifndef _SS_SYMTAB_H_
+#define _SS_SYMTAB_H_
+
+#include "hashtab.h"
+
+struct symtab {
+ struct hashtab *table; /* hash table (keyed on a string) */
+ u32 nprim; /* number of primary names in table */
+};
+
+int symtab_init(struct symtab *s, unsigned int size);
+
+#endif /* _SS_SYMTAB_H_ */
+
+