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!
diff --git a/security/Kconfig b/security/Kconfig
new file mode 100644
index 0000000..dcf04a0
--- /dev/null
+++ b/security/Kconfig
@@ -0,0 +1,91 @@
+#
+# Security configuration
+#
+
+menu "Security options"
+
+config KEYS
+	bool "Enable access key retention support"
+	help
+	  This option provides support for retaining authentication tokens and
+	  access keys in the kernel.
+
+	  It also includes provision of methods by which such keys might be
+	  associated with a process so that network filesystems, encryption
+	  support and the like can find them.
+
+	  Furthermore, a special type of key is available that acts as keyring:
+	  a searchable sequence of keys. Each process is equipped with access
+	  to five standard keyrings: UID-specific, GID-specific, session,
+	  process and thread.
+
+	  If you are unsure as to whether this is required, answer N.
+
+config KEYS_DEBUG_PROC_KEYS
+	bool "Enable the /proc/keys file by which all keys may be viewed"
+	depends on KEYS
+	help
+	  This option turns on support for the /proc/keys file through which
+	  all the keys on the system can be listed.
+
+	  This option is a slight security risk in that it makes it possible
+	  for anyone to see all the keys on the system. Normally the manager
+	  pretends keys that are inaccessible to a process don't exist as far
+	  as that process is concerned.
+
+config SECURITY
+	bool "Enable different security models"
+	help
+	  This allows you to choose different security modules to be
+	  configured into your kernel.
+
+	  If this option is not selected, the default Linux security
+	  model will be used.
+
+	  If you are unsure how to answer this question, answer N.
+
+config SECURITY_NETWORK
+	bool "Socket and Networking Security Hooks"
+	depends on SECURITY
+	help
+	  This enables the socket and networking security hooks.
+	  If enabled, a security module can use these hooks to
+	  implement socket and networking access controls.
+	  If you are unsure how to answer this question, answer N.
+
+config SECURITY_CAPABILITIES
+	tristate "Default Linux Capabilities"
+	depends on SECURITY
+	help
+	  This enables the "default" Linux capabilities functionality.
+	  If you are unsure how to answer this question, answer Y.
+
+config SECURITY_ROOTPLUG
+	tristate "Root Plug Support"
+	depends on USB && SECURITY
+	help
+	  This is a sample LSM module that should only be used as such.
+	  It prevents any programs running with egid == 0 if a specific
+	  USB device is not present in the system.
+
+	  See <http://www.linuxjournal.com/article.php?sid=6279> for
+	  more information about this module.
+	  
+	  If you are unsure how to answer this question, answer N.
+
+config SECURITY_SECLVL
+	tristate "BSD Secure Levels"
+	depends on SECURITY
+	select CRYPTO
+	select CRYPTO_SHA1
+	help
+	  Implements BSD Secure Levels as an LSM.  See
+	  <file:Documentation/seclvl.txt> for instructions on how to use this
+	  module.
+
+	  If you are unsure how to answer this question, answer N.
+
+source security/selinux/Kconfig
+
+endmenu
+
diff --git a/security/Makefile b/security/Makefile
new file mode 100644
index 0000000..197cc2f
--- /dev/null
+++ b/security/Makefile
@@ -0,0 +1,19 @@
+#
+# Makefile for the kernel security code
+#
+
+obj-$(CONFIG_KEYS)			+= keys/
+subdir-$(CONFIG_SECURITY_SELINUX)	+= selinux
+
+# if we don't select a security model, use the default capabilities
+ifneq ($(CONFIG_SECURITY),y)
+obj-y		+= commoncap.o
+endif
+
+# Object file lists
+obj-$(CONFIG_SECURITY)			+= security.o dummy.o
+# Must precede capability.o in order to stack properly.
+obj-$(CONFIG_SECURITY_SELINUX)		+= selinux/built-in.o
+obj-$(CONFIG_SECURITY_CAPABILITIES)	+= commoncap.o capability.o
+obj-$(CONFIG_SECURITY_ROOTPLUG)		+= commoncap.o root_plug.o
+obj-$(CONFIG_SECURITY_SECLVL)		+= seclvl.o
diff --git a/security/capability.c b/security/capability.c
new file mode 100644
index 0000000..ec18d60
--- /dev/null
+++ b/security/capability.c
@@ -0,0 +1,104 @@
+/*
+ *  Capabilities Linux Security Module
+ *
+ *	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; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/security.h>
+#include <linux/file.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/pagemap.h>
+#include <linux/swap.h>
+#include <linux/smp_lock.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/ptrace.h>
+#include <linux/moduleparam.h>
+
+static struct security_operations capability_ops = {
+	.ptrace =			cap_ptrace,
+	.capget =			cap_capget,
+	.capset_check =			cap_capset_check,
+	.capset_set =			cap_capset_set,
+	.capable =			cap_capable,
+	.settime =			cap_settime,
+	.netlink_send =			cap_netlink_send,
+	.netlink_recv =			cap_netlink_recv,
+
+	.bprm_apply_creds =		cap_bprm_apply_creds,
+	.bprm_set_security =		cap_bprm_set_security,
+	.bprm_secureexec =		cap_bprm_secureexec,
+
+	.inode_setxattr =		cap_inode_setxattr,
+	.inode_removexattr =		cap_inode_removexattr,
+
+	.task_post_setuid =		cap_task_post_setuid,
+	.task_reparent_to_init =	cap_task_reparent_to_init,
+
+	.syslog =                       cap_syslog,
+
+	.vm_enough_memory =             cap_vm_enough_memory,
+};
+
+#define MY_NAME __stringify(KBUILD_MODNAME)
+
+/* flag to keep track of how we were registered */
+static int secondary;
+
+static int capability_disable;
+module_param_named(disable, capability_disable, int, 0);
+MODULE_PARM_DESC(disable, "To disable capabilities module set disable = 1");
+
+static int __init capability_init (void)
+{
+	if (capability_disable) {
+		printk(KERN_INFO "Capabilities disabled at initialization\n");
+		return 0;
+	}
+	/* register ourselves with the security framework */
+	if (register_security (&capability_ops)) {
+		/* try registering with primary module */
+		if (mod_reg_security (MY_NAME, &capability_ops)) {
+			printk (KERN_INFO "Failure registering capabilities "
+				"with primary security module.\n");
+			return -EINVAL;
+		}
+		secondary = 1;
+	}
+	printk (KERN_INFO "Capability LSM initialized%s\n",
+		secondary ? " as secondary" : "");
+	return 0;
+}
+
+static void __exit capability_exit (void)
+{
+	if (capability_disable)
+		return;
+	/* remove ourselves from the security framework */
+	if (secondary) {
+		if (mod_unreg_security (MY_NAME, &capability_ops))
+			printk (KERN_INFO "Failure unregistering capabilities "
+				"with primary module.\n");
+		return;
+	}
+
+	if (unregister_security (&capability_ops)) {
+		printk (KERN_INFO
+			"Failure unregistering capabilities with the kernel\n");
+	}
+}
+
+security_initcall (capability_init);
+module_exit (capability_exit);
+
+MODULE_DESCRIPTION("Standard Linux Capabilities Security Module");
+MODULE_LICENSE("GPL");
diff --git a/security/commoncap.c b/security/commoncap.c
new file mode 100644
index 0000000..849b8c3
--- /dev/null
+++ b/security/commoncap.c
@@ -0,0 +1,345 @@
+/* Common capabilities, needed by capability.o and root_plug.o 
+ *
+ *	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; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/security.h>
+#include <linux/file.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/pagemap.h>
+#include <linux/swap.h>
+#include <linux/smp_lock.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/ptrace.h>
+#include <linux/xattr.h>
+#include <linux/hugetlb.h>
+
+int cap_netlink_send(struct sock *sk, struct sk_buff *skb)
+{
+	NETLINK_CB(skb).eff_cap = current->cap_effective;
+	return 0;
+}
+
+EXPORT_SYMBOL(cap_netlink_send);
+
+int cap_netlink_recv(struct sk_buff *skb)
+{
+	if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN))
+		return -EPERM;
+	return 0;
+}
+
+EXPORT_SYMBOL(cap_netlink_recv);
+
+int cap_capable (struct task_struct *tsk, int cap)
+{
+	/* Derived from include/linux/sched.h:capable. */
+	if (cap_raised(tsk->cap_effective, cap))
+		return 0;
+	return -EPERM;
+}
+
+int cap_settime(struct timespec *ts, struct timezone *tz)
+{
+	if (!capable(CAP_SYS_TIME))
+		return -EPERM;
+	return 0;
+}
+
+int cap_ptrace (struct task_struct *parent, struct task_struct *child)
+{
+	/* Derived from arch/i386/kernel/ptrace.c:sys_ptrace. */
+	if (!cap_issubset (child->cap_permitted, current->cap_permitted) &&
+	    !capable(CAP_SYS_PTRACE))
+		return -EPERM;
+	return 0;
+}
+
+int cap_capget (struct task_struct *target, kernel_cap_t *effective,
+		kernel_cap_t *inheritable, kernel_cap_t *permitted)
+{
+	/* Derived from kernel/capability.c:sys_capget. */
+	*effective = cap_t (target->cap_effective);
+	*inheritable = cap_t (target->cap_inheritable);
+	*permitted = cap_t (target->cap_permitted);
+	return 0;
+}
+
+int cap_capset_check (struct task_struct *target, kernel_cap_t *effective,
+		      kernel_cap_t *inheritable, kernel_cap_t *permitted)
+{
+	/* Derived from kernel/capability.c:sys_capset. */
+	/* verify restrictions on target's new Inheritable set */
+	if (!cap_issubset (*inheritable,
+			   cap_combine (target->cap_inheritable,
+					current->cap_permitted))) {
+		return -EPERM;
+	}
+
+	/* verify restrictions on target's new Permitted set */
+	if (!cap_issubset (*permitted,
+			   cap_combine (target->cap_permitted,
+					current->cap_permitted))) {
+		return -EPERM;
+	}
+
+	/* verify the _new_Effective_ is a subset of the _new_Permitted_ */
+	if (!cap_issubset (*effective, *permitted)) {
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+void cap_capset_set (struct task_struct *target, kernel_cap_t *effective,
+		     kernel_cap_t *inheritable, kernel_cap_t *permitted)
+{
+	target->cap_effective = *effective;
+	target->cap_inheritable = *inheritable;
+	target->cap_permitted = *permitted;
+}
+
+int cap_bprm_set_security (struct linux_binprm *bprm)
+{
+	/* Copied from fs/exec.c:prepare_binprm. */
+
+	/* We don't have VFS support for capabilities yet */
+	cap_clear (bprm->cap_inheritable);
+	cap_clear (bprm->cap_permitted);
+	cap_clear (bprm->cap_effective);
+
+	/*  To support inheritance of root-permissions and suid-root
+	 *  executables under compatibility mode, we raise all three
+	 *  capability sets for the file.
+	 *
+	 *  If only the real uid is 0, we only raise the inheritable
+	 *  and permitted sets of the executable file.
+	 */
+
+	if (!issecure (SECURE_NOROOT)) {
+		if (bprm->e_uid == 0 || current->uid == 0) {
+			cap_set_full (bprm->cap_inheritable);
+			cap_set_full (bprm->cap_permitted);
+		}
+		if (bprm->e_uid == 0)
+			cap_set_full (bprm->cap_effective);
+	}
+	return 0;
+}
+
+void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
+{
+	/* Derived from fs/exec.c:compute_creds. */
+	kernel_cap_t new_permitted, working;
+
+	new_permitted = cap_intersect (bprm->cap_permitted, cap_bset);
+	working = cap_intersect (bprm->cap_inheritable,
+				 current->cap_inheritable);
+	new_permitted = cap_combine (new_permitted, working);
+
+	if (bprm->e_uid != current->uid || bprm->e_gid != current->gid ||
+	    !cap_issubset (new_permitted, current->cap_permitted)) {
+		current->mm->dumpable = 0;
+
+		if (unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
+			if (!capable(CAP_SETUID)) {
+				bprm->e_uid = current->uid;
+				bprm->e_gid = current->gid;
+			}
+			if (!capable (CAP_SETPCAP)) {
+				new_permitted = cap_intersect (new_permitted,
+							current->cap_permitted);
+			}
+		}
+	}
+
+	current->suid = current->euid = current->fsuid = bprm->e_uid;
+	current->sgid = current->egid = current->fsgid = bprm->e_gid;
+
+	/* For init, we want to retain the capabilities set
+	 * in the init_task struct. Thus we skip the usual
+	 * capability rules */
+	if (current->pid != 1) {
+		current->cap_permitted = new_permitted;
+		current->cap_effective =
+		    cap_intersect (new_permitted, bprm->cap_effective);
+	}
+
+	/* AUD: Audit candidate if current->cap_effective is set */
+
+	current->keep_capabilities = 0;
+}
+
+int cap_bprm_secureexec (struct linux_binprm *bprm)
+{
+	/* If/when this module is enhanced to incorporate capability
+	   bits on files, the test below should be extended to also perform a 
+	   test between the old and new capability sets.  For now,
+	   it simply preserves the legacy decision algorithm used by
+	   the old userland. */
+	return (current->euid != current->uid ||
+		current->egid != current->gid);
+}
+
+int cap_inode_setxattr(struct dentry *dentry, char *name, void *value,
+		       size_t size, int flags)
+{
+	if (!strncmp(name, XATTR_SECURITY_PREFIX,
+		     sizeof(XATTR_SECURITY_PREFIX) - 1)  &&
+	    !capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	return 0;
+}
+
+int cap_inode_removexattr(struct dentry *dentry, char *name)
+{
+	if (!strncmp(name, XATTR_SECURITY_PREFIX,
+		     sizeof(XATTR_SECURITY_PREFIX) - 1)  &&
+	    !capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	return 0;
+}
+
+/* moved from kernel/sys.c. */
+/* 
+ * cap_emulate_setxuid() fixes the effective / permitted capabilities of
+ * a process after a call to setuid, setreuid, or setresuid.
+ *
+ *  1) When set*uiding _from_ one of {r,e,s}uid == 0 _to_ all of
+ *  {r,e,s}uid != 0, the permitted and effective capabilities are
+ *  cleared.
+ *
+ *  2) When set*uiding _from_ euid == 0 _to_ euid != 0, the effective
+ *  capabilities of the process are cleared.
+ *
+ *  3) When set*uiding _from_ euid != 0 _to_ euid == 0, the effective
+ *  capabilities are set to the permitted capabilities.
+ *
+ *  fsuid is handled elsewhere. fsuid == 0 and {r,e,s}uid!= 0 should 
+ *  never happen.
+ *
+ *  -astor 
+ *
+ * cevans - New behaviour, Oct '99
+ * A process may, via prctl(), elect to keep its capabilities when it
+ * calls setuid() and switches away from uid==0. Both permitted and
+ * effective sets will be retained.
+ * Without this change, it was impossible for a daemon to drop only some
+ * of its privilege. The call to setuid(!=0) would drop all privileges!
+ * Keeping uid 0 is not an option because uid 0 owns too many vital
+ * files..
+ * Thanks to Olaf Kirch and Peter Benie for spotting this.
+ */
+static inline void cap_emulate_setxuid (int old_ruid, int old_euid,
+					int old_suid)
+{
+	if ((old_ruid == 0 || old_euid == 0 || old_suid == 0) &&
+	    (current->uid != 0 && current->euid != 0 && current->suid != 0) &&
+	    !current->keep_capabilities) {
+		cap_clear (current->cap_permitted);
+		cap_clear (current->cap_effective);
+	}
+	if (old_euid == 0 && current->euid != 0) {
+		cap_clear (current->cap_effective);
+	}
+	if (old_euid != 0 && current->euid == 0) {
+		current->cap_effective = current->cap_permitted;
+	}
+}
+
+int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid,
+			  int flags)
+{
+	switch (flags) {
+	case LSM_SETID_RE:
+	case LSM_SETID_ID:
+	case LSM_SETID_RES:
+		/* Copied from kernel/sys.c:setreuid/setuid/setresuid. */
+		if (!issecure (SECURE_NO_SETUID_FIXUP)) {
+			cap_emulate_setxuid (old_ruid, old_euid, old_suid);
+		}
+		break;
+	case LSM_SETID_FS:
+		{
+			uid_t old_fsuid = old_ruid;
+
+			/* Copied from kernel/sys.c:setfsuid. */
+
+			/*
+			 * FIXME - is fsuser used for all CAP_FS_MASK capabilities?
+			 *          if not, we might be a bit too harsh here.
+			 */
+
+			if (!issecure (SECURE_NO_SETUID_FIXUP)) {
+				if (old_fsuid == 0 && current->fsuid != 0) {
+					cap_t (current->cap_effective) &=
+					    ~CAP_FS_MASK;
+				}
+				if (old_fsuid != 0 && current->fsuid == 0) {
+					cap_t (current->cap_effective) |=
+					    (cap_t (current->cap_permitted) &
+					     CAP_FS_MASK);
+				}
+			}
+			break;
+		}
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+void cap_task_reparent_to_init (struct task_struct *p)
+{
+	p->cap_effective = CAP_INIT_EFF_SET;
+	p->cap_inheritable = CAP_INIT_INH_SET;
+	p->cap_permitted = CAP_FULL_SET;
+	p->keep_capabilities = 0;
+	return;
+}
+
+int cap_syslog (int type)
+{
+	if ((type != 3 && type != 10) && !capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	return 0;
+}
+
+int cap_vm_enough_memory(long pages)
+{
+	int cap_sys_admin = 0;
+
+	if (cap_capable(current, CAP_SYS_ADMIN) == 0)
+		cap_sys_admin = 1;
+	return __vm_enough_memory(pages, cap_sys_admin);
+}
+
+EXPORT_SYMBOL(cap_capable);
+EXPORT_SYMBOL(cap_settime);
+EXPORT_SYMBOL(cap_ptrace);
+EXPORT_SYMBOL(cap_capget);
+EXPORT_SYMBOL(cap_capset_check);
+EXPORT_SYMBOL(cap_capset_set);
+EXPORT_SYMBOL(cap_bprm_set_security);
+EXPORT_SYMBOL(cap_bprm_apply_creds);
+EXPORT_SYMBOL(cap_bprm_secureexec);
+EXPORT_SYMBOL(cap_inode_setxattr);
+EXPORT_SYMBOL(cap_inode_removexattr);
+EXPORT_SYMBOL(cap_task_post_setuid);
+EXPORT_SYMBOL(cap_task_reparent_to_init);
+EXPORT_SYMBOL(cap_syslog);
+EXPORT_SYMBOL(cap_vm_enough_memory);
+
+MODULE_DESCRIPTION("Standard Linux Common Capabilities Security Module");
+MODULE_LICENSE("GPL");
diff --git a/security/dummy.c b/security/dummy.c
new file mode 100644
index 0000000..b32eff1
--- /dev/null
+++ b/security/dummy.c
@@ -0,0 +1,996 @@
+/*
+ * Stub functions for the default security function pointers in case no
+ * security model is loaded.
+ *
+ * Copyright (C) 2001 WireX Communications, Inc <chris@wirex.com>
+ * Copyright (C) 2001-2002  Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2001 Networks Associates Technology, Inc <ssmalley@nai.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; either version 2 of the License, or
+ *	(at your option) any later version.
+ */
+
+#undef DEBUG
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mman.h>
+#include <linux/pagemap.h>
+#include <linux/swap.h>
+#include <linux/security.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <net/sock.h>
+#include <linux/xattr.h>
+#include <linux/hugetlb.h>
+#include <linux/ptrace.h>
+#include <linux/file.h>
+
+static int dummy_ptrace (struct task_struct *parent, struct task_struct *child)
+{
+	return 0;
+}
+
+static int dummy_capget (struct task_struct *target, kernel_cap_t * effective,
+			 kernel_cap_t * inheritable, kernel_cap_t * permitted)
+{
+	*effective = *inheritable = *permitted = 0;
+	if (!issecure(SECURE_NOROOT)) {
+		if (target->euid == 0) {
+			*permitted |= (~0 & ~CAP_FS_MASK);
+			*effective |= (~0 & ~CAP_TO_MASK(CAP_SETPCAP) & ~CAP_FS_MASK);
+		}
+		if (target->fsuid == 0) {
+			*permitted |= CAP_FS_MASK;
+			*effective |= CAP_FS_MASK;
+		}
+	}
+	return 0;
+}
+
+static int dummy_capset_check (struct task_struct *target,
+			       kernel_cap_t * effective,
+			       kernel_cap_t * inheritable,
+			       kernel_cap_t * permitted)
+{
+	return -EPERM;
+}
+
+static void dummy_capset_set (struct task_struct *target,
+			      kernel_cap_t * effective,
+			      kernel_cap_t * inheritable,
+			      kernel_cap_t * permitted)
+{
+	return;
+}
+
+static int dummy_acct (struct file *file)
+{
+	return 0;
+}
+
+static int dummy_capable (struct task_struct *tsk, int cap)
+{
+	if (cap_raised (tsk->cap_effective, cap))
+		return 0;
+	return -EPERM;
+}
+
+static int dummy_sysctl (ctl_table * table, int op)
+{
+	return 0;
+}
+
+static int dummy_quotactl (int cmds, int type, int id, struct super_block *sb)
+{
+	return 0;
+}
+
+static int dummy_quota_on (struct dentry *dentry)
+{
+	return 0;
+}
+
+static int dummy_syslog (int type)
+{
+	if ((type != 3 && type != 10) && current->euid)
+		return -EPERM;
+	return 0;
+}
+
+static int dummy_settime(struct timespec *ts, struct timezone *tz)
+{
+	if (!capable(CAP_SYS_TIME))
+		return -EPERM;
+	return 0;
+}
+
+static int dummy_vm_enough_memory(long pages)
+{
+	int cap_sys_admin = 0;
+
+	if (dummy_capable(current, CAP_SYS_ADMIN) == 0)
+		cap_sys_admin = 1;
+	return __vm_enough_memory(pages, cap_sys_admin);
+}
+
+static int dummy_bprm_alloc_security (struct linux_binprm *bprm)
+{
+	return 0;
+}
+
+static void dummy_bprm_free_security (struct linux_binprm *bprm)
+{
+	return;
+}
+
+static void dummy_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
+{
+	if (bprm->e_uid != current->uid || bprm->e_gid != current->gid) {
+		current->mm->dumpable = 0;
+
+		if ((unsafe & ~LSM_UNSAFE_PTRACE_CAP) && !capable(CAP_SETUID)) {
+			bprm->e_uid = current->uid;
+			bprm->e_gid = current->gid;
+		}
+	}
+
+	current->suid = current->euid = current->fsuid = bprm->e_uid;
+	current->sgid = current->egid = current->fsgid = bprm->e_gid;
+
+	dummy_capget(current, &current->cap_effective, &current->cap_inheritable, &current->cap_permitted);
+}
+
+static void dummy_bprm_post_apply_creds (struct linux_binprm *bprm)
+{
+	return;
+}
+
+static int dummy_bprm_set_security (struct linux_binprm *bprm)
+{
+	return 0;
+}
+
+static int dummy_bprm_check_security (struct linux_binprm *bprm)
+{
+	return 0;
+}
+
+static int dummy_bprm_secureexec (struct linux_binprm *bprm)
+{
+	/* The new userland will simply use the value provided
+	   in the AT_SECURE field to decide whether secure mode
+	   is required.  Hence, this logic is required to preserve
+	   the legacy decision algorithm used by the old userland. */
+	return (current->euid != current->uid ||
+		current->egid != current->gid);
+}
+
+static int dummy_sb_alloc_security (struct super_block *sb)
+{
+	return 0;
+}
+
+static void dummy_sb_free_security (struct super_block *sb)
+{
+	return;
+}
+
+static int dummy_sb_copy_data (struct file_system_type *type,
+			       void *orig, void *copy)
+{
+	return 0;
+}
+
+static int dummy_sb_kern_mount (struct super_block *sb, void *data)
+{
+	return 0;
+}
+
+static int dummy_sb_statfs (struct super_block *sb)
+{
+	return 0;
+}
+
+static int dummy_sb_mount (char *dev_name, struct nameidata *nd, char *type,
+			   unsigned long flags, void *data)
+{
+	return 0;
+}
+
+static int dummy_sb_check_sb (struct vfsmount *mnt, struct nameidata *nd)
+{
+	return 0;
+}
+
+static int dummy_sb_umount (struct vfsmount *mnt, int flags)
+{
+	return 0;
+}
+
+static void dummy_sb_umount_close (struct vfsmount *mnt)
+{
+	return;
+}
+
+static void dummy_sb_umount_busy (struct vfsmount *mnt)
+{
+	return;
+}
+
+static void dummy_sb_post_remount (struct vfsmount *mnt, unsigned long flags,
+				   void *data)
+{
+	return;
+}
+
+
+static void dummy_sb_post_mountroot (void)
+{
+	return;
+}
+
+static void dummy_sb_post_addmount (struct vfsmount *mnt, struct nameidata *nd)
+{
+	return;
+}
+
+static int dummy_sb_pivotroot (struct nameidata *old_nd, struct nameidata *new_nd)
+{
+	return 0;
+}
+
+static void dummy_sb_post_pivotroot (struct nameidata *old_nd, struct nameidata *new_nd)
+{
+	return;
+}
+
+static int dummy_inode_alloc_security (struct inode *inode)
+{
+	return 0;
+}
+
+static void dummy_inode_free_security (struct inode *inode)
+{
+	return;
+}
+
+static int dummy_inode_create (struct inode *inode, struct dentry *dentry,
+			       int mask)
+{
+	return 0;
+}
+
+static void dummy_inode_post_create (struct inode *inode, struct dentry *dentry,
+				     int mask)
+{
+	return;
+}
+
+static int dummy_inode_link (struct dentry *old_dentry, struct inode *inode,
+			     struct dentry *new_dentry)
+{
+	return 0;
+}
+
+static void dummy_inode_post_link (struct dentry *old_dentry,
+				   struct inode *inode,
+				   struct dentry *new_dentry)
+{
+	return;
+}
+
+static int dummy_inode_unlink (struct inode *inode, struct dentry *dentry)
+{
+	return 0;
+}
+
+static int dummy_inode_symlink (struct inode *inode, struct dentry *dentry,
+				const char *name)
+{
+	return 0;
+}
+
+static void dummy_inode_post_symlink (struct inode *inode,
+				      struct dentry *dentry, const char *name)
+{
+	return;
+}
+
+static int dummy_inode_mkdir (struct inode *inode, struct dentry *dentry,
+			      int mask)
+{
+	return 0;
+}
+
+static void dummy_inode_post_mkdir (struct inode *inode, struct dentry *dentry,
+				    int mask)
+{
+	return;
+}
+
+static int dummy_inode_rmdir (struct inode *inode, struct dentry *dentry)
+{
+	return 0;
+}
+
+static int dummy_inode_mknod (struct inode *inode, struct dentry *dentry,
+			      int mode, dev_t dev)
+{
+	return 0;
+}
+
+static void dummy_inode_post_mknod (struct inode *inode, struct dentry *dentry,
+				    int mode, dev_t dev)
+{
+	return;
+}
+
+static int dummy_inode_rename (struct inode *old_inode,
+			       struct dentry *old_dentry,
+			       struct inode *new_inode,
+			       struct dentry *new_dentry)
+{
+	return 0;
+}
+
+static void dummy_inode_post_rename (struct inode *old_inode,
+				     struct dentry *old_dentry,
+				     struct inode *new_inode,
+				     struct dentry *new_dentry)
+{
+	return;
+}
+
+static int dummy_inode_readlink (struct dentry *dentry)
+{
+	return 0;
+}
+
+static int dummy_inode_follow_link (struct dentry *dentry,
+				    struct nameidata *nameidata)
+{
+	return 0;
+}
+
+static int dummy_inode_permission (struct inode *inode, int mask, struct nameidata *nd)
+{
+	return 0;
+}
+
+static int dummy_inode_setattr (struct dentry *dentry, struct iattr *iattr)
+{
+	return 0;
+}
+
+static int dummy_inode_getattr (struct vfsmount *mnt, struct dentry *dentry)
+{
+	return 0;
+}
+
+static void dummy_inode_delete (struct inode *ino)
+{
+	return;
+}
+
+static int dummy_inode_setxattr (struct dentry *dentry, char *name, void *value,
+				size_t size, int flags)
+{
+	if (!strncmp(name, XATTR_SECURITY_PREFIX,
+		     sizeof(XATTR_SECURITY_PREFIX) - 1) &&
+	    !capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	return 0;
+}
+
+static void dummy_inode_post_setxattr (struct dentry *dentry, char *name, void *value,
+				       size_t size, int flags)
+{
+}
+
+static int dummy_inode_getxattr (struct dentry *dentry, char *name)
+{
+	return 0;
+}
+
+static int dummy_inode_listxattr (struct dentry *dentry)
+{
+	return 0;
+}
+
+static int dummy_inode_removexattr (struct dentry *dentry, char *name)
+{
+	if (!strncmp(name, XATTR_SECURITY_PREFIX,
+		     sizeof(XATTR_SECURITY_PREFIX) - 1) &&
+	    !capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	return 0;
+}
+
+static int dummy_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size)
+{
+	return -EOPNOTSUPP;
+}
+
+static int dummy_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags)
+{
+	return -EOPNOTSUPP;
+}
+
+static int dummy_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size)
+{
+	return 0;
+}
+
+static int dummy_file_permission (struct file *file, int mask)
+{
+	return 0;
+}
+
+static int dummy_file_alloc_security (struct file *file)
+{
+	return 0;
+}
+
+static void dummy_file_free_security (struct file *file)
+{
+	return;
+}
+
+static int dummy_file_ioctl (struct file *file, unsigned int command,
+			     unsigned long arg)
+{
+	return 0;
+}
+
+static int dummy_file_mmap (struct file *file, unsigned long reqprot,
+			    unsigned long prot,
+			    unsigned long flags)
+{
+	return 0;
+}
+
+static int dummy_file_mprotect (struct vm_area_struct *vma,
+				unsigned long reqprot,
+				unsigned long prot)
+{
+	return 0;
+}
+
+static int dummy_file_lock (struct file *file, unsigned int cmd)
+{
+	return 0;
+}
+
+static int dummy_file_fcntl (struct file *file, unsigned int cmd,
+			     unsigned long arg)
+{
+	return 0;
+}
+
+static int dummy_file_set_fowner (struct file *file)
+{
+	return 0;
+}
+
+static int dummy_file_send_sigiotask (struct task_struct *tsk,
+				      struct fown_struct *fown, int sig)
+{
+	return 0;
+}
+
+static int dummy_file_receive (struct file *file)
+{
+	return 0;
+}
+
+static int dummy_task_create (unsigned long clone_flags)
+{
+	return 0;
+}
+
+static int dummy_task_alloc_security (struct task_struct *p)
+{
+	return 0;
+}
+
+static void dummy_task_free_security (struct task_struct *p)
+{
+	return;
+}
+
+static int dummy_task_setuid (uid_t id0, uid_t id1, uid_t id2, int flags)
+{
+	return 0;
+}
+
+static int dummy_task_post_setuid (uid_t id0, uid_t id1, uid_t id2, int flags)
+{
+	dummy_capget(current, &current->cap_effective, &current->cap_inheritable, &current->cap_permitted);
+	return 0;
+}
+
+static int dummy_task_setgid (gid_t id0, gid_t id1, gid_t id2, int flags)
+{
+	return 0;
+}
+
+static int dummy_task_setpgid (struct task_struct *p, pid_t pgid)
+{
+	return 0;
+}
+
+static int dummy_task_getpgid (struct task_struct *p)
+{
+	return 0;
+}
+
+static int dummy_task_getsid (struct task_struct *p)
+{
+	return 0;
+}
+
+static int dummy_task_setgroups (struct group_info *group_info)
+{
+	return 0;
+}
+
+static int dummy_task_setnice (struct task_struct *p, int nice)
+{
+	return 0;
+}
+
+static int dummy_task_setrlimit (unsigned int resource, struct rlimit *new_rlim)
+{
+	return 0;
+}
+
+static int dummy_task_setscheduler (struct task_struct *p, int policy,
+				    struct sched_param *lp)
+{
+	return 0;
+}
+
+static int dummy_task_getscheduler (struct task_struct *p)
+{
+	return 0;
+}
+
+static int dummy_task_wait (struct task_struct *p)
+{
+	return 0;
+}
+
+static int dummy_task_kill (struct task_struct *p, struct siginfo *info,
+			    int sig)
+{
+	return 0;
+}
+
+static int dummy_task_prctl (int option, unsigned long arg2, unsigned long arg3,
+			     unsigned long arg4, unsigned long arg5)
+{
+	return 0;
+}
+
+static void dummy_task_reparent_to_init (struct task_struct *p)
+{
+	p->euid = p->fsuid = 0;
+	return;
+}
+
+static void dummy_task_to_inode(struct task_struct *p, struct inode *inode)
+{ }
+
+static int dummy_ipc_permission (struct kern_ipc_perm *ipcp, short flag)
+{
+	return 0;
+}
+
+static int dummy_msg_msg_alloc_security (struct msg_msg *msg)
+{
+	return 0;
+}
+
+static void dummy_msg_msg_free_security (struct msg_msg *msg)
+{
+	return;
+}
+
+static int dummy_msg_queue_alloc_security (struct msg_queue *msq)
+{
+	return 0;
+}
+
+static void dummy_msg_queue_free_security (struct msg_queue *msq)
+{
+	return;
+}
+
+static int dummy_msg_queue_associate (struct msg_queue *msq, 
+				      int msqflg)
+{
+	return 0;
+}
+
+static int dummy_msg_queue_msgctl (struct msg_queue *msq, int cmd)
+{
+	return 0;
+}
+
+static int dummy_msg_queue_msgsnd (struct msg_queue *msq, struct msg_msg *msg,
+				   int msgflg)
+{
+	return 0;
+}
+
+static int dummy_msg_queue_msgrcv (struct msg_queue *msq, struct msg_msg *msg,
+				   struct task_struct *target, long type,
+				   int mode)
+{
+	return 0;
+}
+
+static int dummy_shm_alloc_security (struct shmid_kernel *shp)
+{
+	return 0;
+}
+
+static void dummy_shm_free_security (struct shmid_kernel *shp)
+{
+	return;
+}
+
+static int dummy_shm_associate (struct shmid_kernel *shp, int shmflg)
+{
+	return 0;
+}
+
+static int dummy_shm_shmctl (struct shmid_kernel *shp, int cmd)
+{
+	return 0;
+}
+
+static int dummy_shm_shmat (struct shmid_kernel *shp, char __user *shmaddr,
+			    int shmflg)
+{
+	return 0;
+}
+
+static int dummy_sem_alloc_security (struct sem_array *sma)
+{
+	return 0;
+}
+
+static void dummy_sem_free_security (struct sem_array *sma)
+{
+	return;
+}
+
+static int dummy_sem_associate (struct sem_array *sma, int semflg)
+{
+	return 0;
+}
+
+static int dummy_sem_semctl (struct sem_array *sma, int cmd)
+{
+	return 0;
+}
+
+static int dummy_sem_semop (struct sem_array *sma, 
+			    struct sembuf *sops, unsigned nsops, int alter)
+{
+	return 0;
+}
+
+static int dummy_netlink_send (struct sock *sk, struct sk_buff *skb)
+{
+	NETLINK_CB(skb).eff_cap = current->cap_effective;
+	return 0;
+}
+
+static int dummy_netlink_recv (struct sk_buff *skb)
+{
+	if (!cap_raised (NETLINK_CB (skb).eff_cap, CAP_NET_ADMIN))
+		return -EPERM;
+	return 0;
+}
+
+#ifdef CONFIG_SECURITY_NETWORK
+static int dummy_unix_stream_connect (struct socket *sock,
+				      struct socket *other,
+				      struct sock *newsk)
+{
+	return 0;
+}
+
+static int dummy_unix_may_send (struct socket *sock,
+				struct socket *other)
+{
+	return 0;
+}
+
+static int dummy_socket_create (int family, int type,
+				int protocol, int kern)
+{
+	return 0;
+}
+
+static void dummy_socket_post_create (struct socket *sock, int family, int type,
+				      int protocol, int kern)
+{
+	return;
+}
+
+static int dummy_socket_bind (struct socket *sock, struct sockaddr *address,
+			      int addrlen)
+{
+	return 0;
+}
+
+static int dummy_socket_connect (struct socket *sock, struct sockaddr *address,
+				 int addrlen)
+{
+	return 0;
+}
+
+static int dummy_socket_listen (struct socket *sock, int backlog)
+{
+	return 0;
+}
+
+static int dummy_socket_accept (struct socket *sock, struct socket *newsock)
+{
+	return 0;
+}
+
+static void dummy_socket_post_accept (struct socket *sock, 
+				      struct socket *newsock)
+{
+	return;
+}
+
+static int dummy_socket_sendmsg (struct socket *sock, struct msghdr *msg,
+				 int size)
+{
+	return 0;
+}
+
+static int dummy_socket_recvmsg (struct socket *sock, struct msghdr *msg,
+				 int size, int flags)
+{
+	return 0;
+}
+
+static int dummy_socket_getsockname (struct socket *sock)
+{
+	return 0;
+}
+
+static int dummy_socket_getpeername (struct socket *sock)
+{
+	return 0;
+}
+
+static int dummy_socket_setsockopt (struct socket *sock, int level, int optname)
+{
+	return 0;
+}
+
+static int dummy_socket_getsockopt (struct socket *sock, int level, int optname)
+{
+	return 0;
+}
+
+static int dummy_socket_shutdown (struct socket *sock, int how)
+{
+	return 0;
+}
+
+static int dummy_socket_sock_rcv_skb (struct sock *sk, struct sk_buff *skb)
+{
+	return 0;
+}
+
+static int dummy_socket_getpeersec(struct socket *sock, char __user *optval,
+				   int __user *optlen, unsigned len)
+{
+	return -ENOPROTOOPT;
+}
+
+static inline int dummy_sk_alloc_security (struct sock *sk, int family, int priority)
+{
+	return 0;
+}
+
+static inline void dummy_sk_free_security (struct sock *sk)
+{
+}
+#endif	/* CONFIG_SECURITY_NETWORK */
+
+static int dummy_register_security (const char *name, struct security_operations *ops)
+{
+	return -EINVAL;
+}
+
+static int dummy_unregister_security (const char *name, struct security_operations *ops)
+{
+	return -EINVAL;
+}
+
+static void dummy_d_instantiate (struct dentry *dentry, struct inode *inode)
+{
+	return;
+}
+
+static int dummy_getprocattr(struct task_struct *p, char *name, void *value, size_t size)
+{
+	return -EINVAL;
+}
+
+static int dummy_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
+{
+	return -EINVAL;
+}
+
+
+struct security_operations dummy_security_ops;
+
+#define set_to_dummy_if_null(ops, function)				\
+	do {								\
+		if (!ops->function) {					\
+			ops->function = dummy_##function;		\
+			pr_debug("Had to override the " #function	\
+				 " security operation with the dummy one.\n");\
+			}						\
+	} while (0)
+
+void security_fixup_ops (struct security_operations *ops)
+{
+	set_to_dummy_if_null(ops, ptrace);
+	set_to_dummy_if_null(ops, capget);
+	set_to_dummy_if_null(ops, capset_check);
+	set_to_dummy_if_null(ops, capset_set);
+	set_to_dummy_if_null(ops, acct);
+	set_to_dummy_if_null(ops, capable);
+	set_to_dummy_if_null(ops, quotactl);
+	set_to_dummy_if_null(ops, quota_on);
+	set_to_dummy_if_null(ops, sysctl);
+	set_to_dummy_if_null(ops, syslog);
+	set_to_dummy_if_null(ops, settime);
+	set_to_dummy_if_null(ops, vm_enough_memory);
+	set_to_dummy_if_null(ops, bprm_alloc_security);
+	set_to_dummy_if_null(ops, bprm_free_security);
+	set_to_dummy_if_null(ops, bprm_apply_creds);
+	set_to_dummy_if_null(ops, bprm_post_apply_creds);
+	set_to_dummy_if_null(ops, bprm_set_security);
+	set_to_dummy_if_null(ops, bprm_check_security);
+	set_to_dummy_if_null(ops, bprm_secureexec);
+	set_to_dummy_if_null(ops, sb_alloc_security);
+	set_to_dummy_if_null(ops, sb_free_security);
+	set_to_dummy_if_null(ops, sb_copy_data);
+	set_to_dummy_if_null(ops, sb_kern_mount);
+	set_to_dummy_if_null(ops, sb_statfs);
+	set_to_dummy_if_null(ops, sb_mount);
+	set_to_dummy_if_null(ops, sb_check_sb);
+	set_to_dummy_if_null(ops, sb_umount);
+	set_to_dummy_if_null(ops, sb_umount_close);
+	set_to_dummy_if_null(ops, sb_umount_busy);
+	set_to_dummy_if_null(ops, sb_post_remount);
+	set_to_dummy_if_null(ops, sb_post_mountroot);
+	set_to_dummy_if_null(ops, sb_post_addmount);
+	set_to_dummy_if_null(ops, sb_pivotroot);
+	set_to_dummy_if_null(ops, sb_post_pivotroot);
+	set_to_dummy_if_null(ops, inode_alloc_security);
+	set_to_dummy_if_null(ops, inode_free_security);
+	set_to_dummy_if_null(ops, inode_create);
+	set_to_dummy_if_null(ops, inode_post_create);
+	set_to_dummy_if_null(ops, inode_link);
+	set_to_dummy_if_null(ops, inode_post_link);
+	set_to_dummy_if_null(ops, inode_unlink);
+	set_to_dummy_if_null(ops, inode_symlink);
+	set_to_dummy_if_null(ops, inode_post_symlink);
+	set_to_dummy_if_null(ops, inode_mkdir);
+	set_to_dummy_if_null(ops, inode_post_mkdir);
+	set_to_dummy_if_null(ops, inode_rmdir);
+	set_to_dummy_if_null(ops, inode_mknod);
+	set_to_dummy_if_null(ops, inode_post_mknod);
+	set_to_dummy_if_null(ops, inode_rename);
+	set_to_dummy_if_null(ops, inode_post_rename);
+	set_to_dummy_if_null(ops, inode_readlink);
+	set_to_dummy_if_null(ops, inode_follow_link);
+	set_to_dummy_if_null(ops, inode_permission);
+	set_to_dummy_if_null(ops, inode_setattr);
+	set_to_dummy_if_null(ops, inode_getattr);
+	set_to_dummy_if_null(ops, inode_delete);
+	set_to_dummy_if_null(ops, inode_setxattr);
+	set_to_dummy_if_null(ops, inode_post_setxattr);
+	set_to_dummy_if_null(ops, inode_getxattr);
+	set_to_dummy_if_null(ops, inode_listxattr);
+	set_to_dummy_if_null(ops, inode_removexattr);
+	set_to_dummy_if_null(ops, inode_getsecurity);
+	set_to_dummy_if_null(ops, inode_setsecurity);
+	set_to_dummy_if_null(ops, inode_listsecurity);
+	set_to_dummy_if_null(ops, file_permission);
+	set_to_dummy_if_null(ops, file_alloc_security);
+	set_to_dummy_if_null(ops, file_free_security);
+	set_to_dummy_if_null(ops, file_ioctl);
+	set_to_dummy_if_null(ops, file_mmap);
+	set_to_dummy_if_null(ops, file_mprotect);
+	set_to_dummy_if_null(ops, file_lock);
+	set_to_dummy_if_null(ops, file_fcntl);
+	set_to_dummy_if_null(ops, file_set_fowner);
+	set_to_dummy_if_null(ops, file_send_sigiotask);
+	set_to_dummy_if_null(ops, file_receive);
+	set_to_dummy_if_null(ops, task_create);
+	set_to_dummy_if_null(ops, task_alloc_security);
+	set_to_dummy_if_null(ops, task_free_security);
+	set_to_dummy_if_null(ops, task_setuid);
+	set_to_dummy_if_null(ops, task_post_setuid);
+	set_to_dummy_if_null(ops, task_setgid);
+	set_to_dummy_if_null(ops, task_setpgid);
+	set_to_dummy_if_null(ops, task_getpgid);
+	set_to_dummy_if_null(ops, task_getsid);
+	set_to_dummy_if_null(ops, task_setgroups);
+	set_to_dummy_if_null(ops, task_setnice);
+	set_to_dummy_if_null(ops, task_setrlimit);
+	set_to_dummy_if_null(ops, task_setscheduler);
+	set_to_dummy_if_null(ops, task_getscheduler);
+	set_to_dummy_if_null(ops, task_wait);
+	set_to_dummy_if_null(ops, task_kill);
+	set_to_dummy_if_null(ops, task_prctl);
+	set_to_dummy_if_null(ops, task_reparent_to_init);
+ 	set_to_dummy_if_null(ops, task_to_inode);
+	set_to_dummy_if_null(ops, ipc_permission);
+	set_to_dummy_if_null(ops, msg_msg_alloc_security);
+	set_to_dummy_if_null(ops, msg_msg_free_security);
+	set_to_dummy_if_null(ops, msg_queue_alloc_security);
+	set_to_dummy_if_null(ops, msg_queue_free_security);
+	set_to_dummy_if_null(ops, msg_queue_associate);
+	set_to_dummy_if_null(ops, msg_queue_msgctl);
+	set_to_dummy_if_null(ops, msg_queue_msgsnd);
+	set_to_dummy_if_null(ops, msg_queue_msgrcv);
+	set_to_dummy_if_null(ops, shm_alloc_security);
+	set_to_dummy_if_null(ops, shm_free_security);
+	set_to_dummy_if_null(ops, shm_associate);
+	set_to_dummy_if_null(ops, shm_shmctl);
+	set_to_dummy_if_null(ops, shm_shmat);
+	set_to_dummy_if_null(ops, sem_alloc_security);
+	set_to_dummy_if_null(ops, sem_free_security);
+	set_to_dummy_if_null(ops, sem_associate);
+	set_to_dummy_if_null(ops, sem_semctl);
+	set_to_dummy_if_null(ops, sem_semop);
+	set_to_dummy_if_null(ops, netlink_send);
+	set_to_dummy_if_null(ops, netlink_recv);
+	set_to_dummy_if_null(ops, register_security);
+	set_to_dummy_if_null(ops, unregister_security);
+	set_to_dummy_if_null(ops, d_instantiate);
+ 	set_to_dummy_if_null(ops, getprocattr);
+ 	set_to_dummy_if_null(ops, setprocattr);
+#ifdef CONFIG_SECURITY_NETWORK
+	set_to_dummy_if_null(ops, unix_stream_connect);
+	set_to_dummy_if_null(ops, unix_may_send);
+	set_to_dummy_if_null(ops, socket_create);
+	set_to_dummy_if_null(ops, socket_post_create);
+	set_to_dummy_if_null(ops, socket_bind);
+	set_to_dummy_if_null(ops, socket_connect);
+	set_to_dummy_if_null(ops, socket_listen);
+	set_to_dummy_if_null(ops, socket_accept);
+	set_to_dummy_if_null(ops, socket_post_accept);
+	set_to_dummy_if_null(ops, socket_sendmsg);
+	set_to_dummy_if_null(ops, socket_recvmsg);
+	set_to_dummy_if_null(ops, socket_getsockname);
+	set_to_dummy_if_null(ops, socket_getpeername);
+	set_to_dummy_if_null(ops, socket_setsockopt);
+	set_to_dummy_if_null(ops, socket_getsockopt);
+	set_to_dummy_if_null(ops, socket_shutdown);
+	set_to_dummy_if_null(ops, socket_sock_rcv_skb);
+	set_to_dummy_if_null(ops, socket_getpeersec);
+	set_to_dummy_if_null(ops, sk_alloc_security);
+	set_to_dummy_if_null(ops, sk_free_security);
+#endif	/* CONFIG_SECURITY_NETWORK */
+}
+
diff --git a/security/keys/Makefile b/security/keys/Makefile
new file mode 100644
index 0000000..ddb495d6
--- /dev/null
+++ b/security/keys/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for key management
+#
+
+obj-y := \
+	key.o \
+	keyring.o \
+	keyctl.o \
+	process_keys.o \
+	user_defined.o \
+	request_key.o
+
+obj-$(CONFIG_KEYS_COMPAT) += compat.o
+obj-$(CONFIG_PROC_FS) += proc.o
diff --git a/security/keys/compat.c b/security/keys/compat.c
new file mode 100644
index 0000000..aff8b22
--- /dev/null
+++ b/security/keys/compat.c
@@ -0,0 +1,78 @@
+/* compat.c: 32-bit compatibility syscall for 64-bit systems
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@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; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/sched.h>
+#include <linux/syscalls.h>
+#include <linux/keyctl.h>
+#include <linux/compat.h>
+#include "internal.h"
+
+/*****************************************************************************/
+/*
+ * the key control system call, 32-bit compatibility version for 64-bit archs
+ * - this should only be called if the 64-bit arch uses weird pointers in
+ *   32-bit mode or doesn't guarantee that the top 32-bits of the argument
+ *   registers on taking a 32-bit syscall are zero
+ * - if you can, you should call sys_keyctl directly
+ */
+asmlinkage long compat_sys_keyctl(u32 option,
+			      u32 arg2, u32 arg3, u32 arg4, u32 arg5)
+{
+	switch (option) {
+	case KEYCTL_GET_KEYRING_ID:
+		return keyctl_get_keyring_ID(arg2, arg3);
+
+	case KEYCTL_JOIN_SESSION_KEYRING:
+		return keyctl_join_session_keyring(compat_ptr(arg2));
+
+	case KEYCTL_UPDATE:
+		return keyctl_update_key(arg2, compat_ptr(arg3), arg4);
+
+	case KEYCTL_REVOKE:
+		return keyctl_revoke_key(arg2);
+
+	case KEYCTL_DESCRIBE:
+		return keyctl_describe_key(arg2, compat_ptr(arg3), arg4);
+
+	case KEYCTL_CLEAR:
+		return keyctl_keyring_clear(arg2);
+
+	case KEYCTL_LINK:
+		return keyctl_keyring_link(arg2, arg3);
+
+	case KEYCTL_UNLINK:
+		return keyctl_keyring_unlink(arg2, arg3);
+
+	case KEYCTL_SEARCH:
+		return keyctl_keyring_search(arg2, compat_ptr(arg3),
+					     compat_ptr(arg4), arg5);
+
+	case KEYCTL_READ:
+		return keyctl_read_key(arg2, compat_ptr(arg3), arg4);
+
+	case KEYCTL_CHOWN:
+		return keyctl_chown_key(arg2, arg3, arg4);
+
+	case KEYCTL_SETPERM:
+		return keyctl_setperm_key(arg2, arg3);
+
+	case KEYCTL_INSTANTIATE:
+		return keyctl_instantiate_key(arg2, compat_ptr(arg3), arg4,
+					      arg5);
+
+	case KEYCTL_NEGATE:
+		return keyctl_negate_key(arg2, arg3, arg4);
+
+	default:
+		return -EOPNOTSUPP;
+	}
+
+} /* end compat_sys_keyctl() */
diff --git a/security/keys/internal.h b/security/keys/internal.h
new file mode 100644
index 0000000..67b2b93
--- /dev/null
+++ b/security/keys/internal.h
@@ -0,0 +1,123 @@
+/* internal.h: authentication token and access key management internal defs
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@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; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _INTERNAL_H
+#define _INTERNAL_H
+
+#include <linux/key.h>
+#include <linux/key-ui.h>
+
+extern struct key_type key_type_dead;
+extern struct key_type key_type_user;
+
+/*****************************************************************************/
+/*
+ * keep track of keys for a user
+ * - this needs to be separate to user_struct to avoid a refcount-loop
+ *   (user_struct pins some keyrings which pin this struct)
+ * - this also keeps track of keys under request from userspace for this UID
+ */
+struct key_user {
+	struct rb_node		node;
+	struct list_head	consq;		/* construction queue */
+	spinlock_t		lock;
+	atomic_t		usage;		/* for accessing qnkeys & qnbytes */
+	atomic_t		nkeys;		/* number of keys */
+	atomic_t		nikeys;		/* number of instantiated keys */
+	uid_t			uid;
+	int			qnkeys;		/* number of keys allocated to this user */
+	int			qnbytes;	/* number of bytes allocated to this user */
+};
+
+#define KEYQUOTA_MAX_KEYS	100
+#define KEYQUOTA_MAX_BYTES	10000
+#define KEYQUOTA_LINK_BYTES	4		/* a link in a keyring is worth 4 bytes */
+
+extern struct rb_root	key_user_tree;
+extern spinlock_t	key_user_lock;
+extern struct key_user	root_key_user;
+
+extern struct key_user *key_user_lookup(uid_t uid);
+extern void key_user_put(struct key_user *user);
+
+
+
+extern struct rb_root key_serial_tree;
+extern spinlock_t key_serial_lock;
+extern struct semaphore key_alloc_sem;
+extern struct rw_semaphore key_construction_sem;
+extern wait_queue_head_t request_key_conswq;
+
+
+extern void keyring_publish_name(struct key *keyring);
+
+extern int __key_link(struct key *keyring, struct key *key);
+
+extern struct key *__keyring_search_one(struct key *keyring,
+					const struct key_type *type,
+					const char *description,
+					key_perm_t perm);
+
+typedef int (*key_match_func_t)(const struct key *, const void *);
+
+extern struct key *keyring_search_aux(struct key *keyring,
+				      struct key_type *type,
+				      const void *description,
+				      key_match_func_t match);
+
+extern struct key *search_process_keyrings_aux(struct key_type *type,
+					       const void *description,
+					       key_match_func_t match);
+
+extern struct key *find_keyring_by_name(const char *name, key_serial_t bound);
+
+extern int install_thread_keyring(struct task_struct *tsk);
+
+/*
+ * keyctl functions
+ */
+extern long keyctl_get_keyring_ID(key_serial_t, int);
+extern long keyctl_join_session_keyring(const char __user *);
+extern long keyctl_update_key(key_serial_t, const void __user *, size_t);
+extern long keyctl_revoke_key(key_serial_t);
+extern long keyctl_keyring_clear(key_serial_t);
+extern long keyctl_keyring_link(key_serial_t, key_serial_t);
+extern long keyctl_keyring_unlink(key_serial_t, key_serial_t);
+extern long keyctl_describe_key(key_serial_t, char __user *, size_t);
+extern long keyctl_keyring_search(key_serial_t, const char __user *,
+				  const char __user *, key_serial_t);
+extern long keyctl_read_key(key_serial_t, char __user *, size_t);
+extern long keyctl_chown_key(key_serial_t, uid_t, gid_t);
+extern long keyctl_setperm_key(key_serial_t, key_perm_t);
+extern long keyctl_instantiate_key(key_serial_t, const void __user *,
+				   size_t, key_serial_t);
+extern long keyctl_negate_key(key_serial_t, unsigned, key_serial_t);
+
+
+/*
+ * debugging key validation
+ */
+#ifdef KEY_DEBUGGING
+extern void __key_check(const struct key *);
+
+static inline void key_check(const struct key *key)
+{
+	if (key && (IS_ERR(key) || key->magic != KEY_DEBUG_MAGIC))
+		__key_check(key);
+}
+
+#else
+
+#define key_check(key) do {} while(0)
+
+#endif
+
+#endif /* _INTERNAL_H */
diff --git a/security/keys/key.c b/security/keys/key.c
new file mode 100644
index 0000000..59402c8
--- /dev/null
+++ b/security/keys/key.c
@@ -0,0 +1,1040 @@
+/* key.c: basic authentication token and access key management
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@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; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/err.h>
+#include "internal.h"
+
+static kmem_cache_t	*key_jar;
+static key_serial_t	key_serial_next = 3;
+struct rb_root		key_serial_tree; /* tree of keys indexed by serial */
+DEFINE_SPINLOCK(key_serial_lock);
+
+struct rb_root	key_user_tree; /* tree of quota records indexed by UID */
+DEFINE_SPINLOCK(key_user_lock);
+
+static LIST_HEAD(key_types_list);
+static DECLARE_RWSEM(key_types_sem);
+
+static void key_cleanup(void *data);
+static DECLARE_WORK(key_cleanup_task, key_cleanup, NULL);
+
+/* we serialise key instantiation and link */
+DECLARE_RWSEM(key_construction_sem);
+
+/* any key who's type gets unegistered will be re-typed to this */
+struct key_type key_type_dead = {
+	.name		= "dead",
+};
+
+#ifdef KEY_DEBUGGING
+void __key_check(const struct key *key)
+{
+	printk("__key_check: key %p {%08x} should be {%08x}\n",
+	       key, key->magic, KEY_DEBUG_MAGIC);
+	BUG();
+}
+#endif
+
+/*****************************************************************************/
+/*
+ * get the key quota record for a user, allocating a new record if one doesn't
+ * already exist
+ */
+struct key_user *key_user_lookup(uid_t uid)
+{
+	struct key_user *candidate = NULL, *user;
+	struct rb_node *parent = NULL;
+	struct rb_node **p;
+
+ try_again:
+	p = &key_user_tree.rb_node;
+	spin_lock(&key_user_lock);
+
+	/* search the tree for a user record with a matching UID */
+	while (*p) {
+		parent = *p;
+		user = rb_entry(parent, struct key_user, node);
+
+		if (uid < user->uid)
+			p = &(*p)->rb_left;
+		else if (uid > user->uid)
+			p = &(*p)->rb_right;
+		else
+			goto found;
+	}
+
+	/* if we get here, we failed to find a match in the tree */
+	if (!candidate) {
+		/* allocate a candidate user record if we don't already have
+		 * one */
+		spin_unlock(&key_user_lock);
+
+		user = NULL;
+		candidate = kmalloc(sizeof(struct key_user), GFP_KERNEL);
+		if (unlikely(!candidate))
+			goto out;
+
+		/* the allocation may have scheduled, so we need to repeat the
+		 * search lest someone else added the record whilst we were
+		 * asleep */
+		goto try_again;
+	}
+
+	/* if we get here, then the user record still hadn't appeared on the
+	 * second pass - so we use the candidate record */
+	atomic_set(&candidate->usage, 1);
+	atomic_set(&candidate->nkeys, 0);
+	atomic_set(&candidate->nikeys, 0);
+	candidate->uid = uid;
+	candidate->qnkeys = 0;
+	candidate->qnbytes = 0;
+	spin_lock_init(&candidate->lock);
+	INIT_LIST_HEAD(&candidate->consq);
+
+	rb_link_node(&candidate->node, parent, p);
+	rb_insert_color(&candidate->node, &key_user_tree);
+	spin_unlock(&key_user_lock);
+	user = candidate;
+	goto out;
+
+	/* okay - we found a user record for this UID */
+ found:
+	atomic_inc(&user->usage);
+	spin_unlock(&key_user_lock);
+	if (candidate)
+		kfree(candidate);
+ out:
+	return user;
+
+} /* end key_user_lookup() */
+
+/*****************************************************************************/
+/*
+ * dispose of a user structure
+ */
+void key_user_put(struct key_user *user)
+{
+	if (atomic_dec_and_lock(&user->usage, &key_user_lock)) {
+		rb_erase(&user->node, &key_user_tree);
+		spin_unlock(&key_user_lock);
+
+		kfree(user);
+	}
+
+} /* end key_user_put() */
+
+/*****************************************************************************/
+/*
+ * insert a key with a fixed serial number
+ */
+static void __init __key_insert_serial(struct key *key)
+{
+	struct rb_node *parent, **p;
+	struct key *xkey;
+
+	parent = NULL;
+	p = &key_serial_tree.rb_node;
+
+	while (*p) {
+		parent = *p;
+		xkey = rb_entry(parent, struct key, serial_node);
+
+		if (key->serial < xkey->serial)
+			p = &(*p)->rb_left;
+		else if (key->serial > xkey->serial)
+			p = &(*p)->rb_right;
+		else
+			BUG();
+	}
+
+	/* we've found a suitable hole - arrange for this key to occupy it */
+	rb_link_node(&key->serial_node, parent, p);
+	rb_insert_color(&key->serial_node, &key_serial_tree);
+
+} /* end __key_insert_serial() */
+
+/*****************************************************************************/
+/*
+ * assign a key the next unique serial number
+ * - we work through all the serial numbers between 2 and 2^31-1 in turn and
+ *   then wrap
+ */
+static inline void key_alloc_serial(struct key *key)
+{
+	struct rb_node *parent, **p;
+	struct key *xkey;
+
+	spin_lock(&key_serial_lock);
+
+	/* propose a likely serial number and look for a hole for it in the
+	 * serial number tree */
+	key->serial = key_serial_next;
+	if (key->serial < 3)
+		key->serial = 3;
+	key_serial_next = key->serial + 1;
+
+	parent = NULL;
+	p = &key_serial_tree.rb_node;
+
+	while (*p) {
+		parent = *p;
+		xkey = rb_entry(parent, struct key, serial_node);
+
+		if (key->serial < xkey->serial)
+			p = &(*p)->rb_left;
+		else if (key->serial > xkey->serial)
+			p = &(*p)->rb_right;
+		else
+			goto serial_exists;
+	}
+	goto insert_here;
+
+	/* we found a key with the proposed serial number - walk the tree from
+	 * that point looking for the next unused serial number */
+ serial_exists:
+	for (;;) {
+		key->serial = key_serial_next;
+		if (key->serial < 2)
+			key->serial = 2;
+		key_serial_next = key->serial + 1;
+
+		if (!parent->rb_parent)
+			p = &key_serial_tree.rb_node;
+		else if (parent->rb_parent->rb_left == parent)
+			p = &parent->rb_parent->rb_left;
+		else
+			p = &parent->rb_parent->rb_right;
+
+		parent = rb_next(parent);
+		if (!parent)
+			break;
+
+		xkey = rb_entry(parent, struct key, serial_node);
+		if (key->serial < xkey->serial)
+			goto insert_here;
+	}
+
+	/* we've found a suitable hole - arrange for this key to occupy it */
+ insert_here:
+	rb_link_node(&key->serial_node, parent, p);
+	rb_insert_color(&key->serial_node, &key_serial_tree);
+
+	spin_unlock(&key_serial_lock);
+
+} /* end key_alloc_serial() */
+
+/*****************************************************************************/
+/*
+ * allocate a key of the specified type
+ * - update the user's quota to reflect the existence of the key
+ * - called from a key-type operation with key_types_sem read-locked by either
+ *   key_create_or_update() or by key_duplicate(); this prevents unregistration
+ *   of the key type
+ * - upon return the key is as yet uninstantiated; the caller needs to either
+ *   instantiate the key or discard it before returning
+ */
+struct key *key_alloc(struct key_type *type, const char *desc,
+		      uid_t uid, gid_t gid, key_perm_t perm,
+		      int not_in_quota)
+{
+	struct key_user *user = NULL;
+	struct key *key;
+	size_t desclen, quotalen;
+
+	key = ERR_PTR(-EINVAL);
+	if (!desc || !*desc)
+		goto error;
+
+	desclen = strlen(desc) + 1;
+	quotalen = desclen + type->def_datalen;
+
+	/* get hold of the key tracking for this user */
+	user = key_user_lookup(uid);
+	if (!user)
+		goto no_memory_1;
+
+	/* check that the user's quota permits allocation of another key and
+	 * its description */
+	if (!not_in_quota) {
+		spin_lock(&user->lock);
+		if (user->qnkeys + 1 >= KEYQUOTA_MAX_KEYS &&
+		    user->qnbytes + quotalen >= KEYQUOTA_MAX_BYTES
+		    )
+			goto no_quota;
+
+		user->qnkeys++;
+		user->qnbytes += quotalen;
+		spin_unlock(&user->lock);
+	}
+
+	/* allocate and initialise the key and its description */
+	key = kmem_cache_alloc(key_jar, SLAB_KERNEL);
+	if (!key)
+		goto no_memory_2;
+
+	if (desc) {
+		key->description = kmalloc(desclen, GFP_KERNEL);
+		if (!key->description)
+			goto no_memory_3;
+
+		memcpy(key->description, desc, desclen);
+	}
+
+	atomic_set(&key->usage, 1);
+	rwlock_init(&key->lock);
+	init_rwsem(&key->sem);
+	key->type = type;
+	key->user = user;
+	key->quotalen = quotalen;
+	key->datalen = type->def_datalen;
+	key->uid = uid;
+	key->gid = gid;
+	key->perm = perm;
+	key->flags = 0;
+	key->expiry = 0;
+	key->payload.data = NULL;
+
+	if (!not_in_quota)
+		key->flags |= KEY_FLAG_IN_QUOTA;
+
+	memset(&key->type_data, 0, sizeof(key->type_data));
+
+#ifdef KEY_DEBUGGING
+	key->magic = KEY_DEBUG_MAGIC;
+#endif
+
+	/* publish the key by giving it a serial number */
+	atomic_inc(&user->nkeys);
+	key_alloc_serial(key);
+
+ error:
+	return key;
+
+ no_memory_3:
+	kmem_cache_free(key_jar, key);
+ no_memory_2:
+	if (!not_in_quota) {
+		spin_lock(&user->lock);
+		user->qnkeys--;
+		user->qnbytes -= quotalen;
+		spin_unlock(&user->lock);
+	}
+	key_user_put(user);
+ no_memory_1:
+	key = ERR_PTR(-ENOMEM);
+	goto error;
+
+ no_quota:
+	spin_unlock(&user->lock);
+	key_user_put(user);
+	key = ERR_PTR(-EDQUOT);
+	goto error;
+
+} /* end key_alloc() */
+
+EXPORT_SYMBOL(key_alloc);
+
+/*****************************************************************************/
+/*
+ * reserve an amount of quota for the key's payload
+ */
+int key_payload_reserve(struct key *key, size_t datalen)
+{
+	int delta = (int) datalen - key->datalen;
+	int ret = 0;
+
+	key_check(key);
+
+	/* contemplate the quota adjustment */
+	if (delta != 0 && key->flags & KEY_FLAG_IN_QUOTA) {
+		spin_lock(&key->user->lock);
+
+		if (delta > 0 &&
+		    key->user->qnbytes + delta > KEYQUOTA_MAX_BYTES
+		    ) {
+			ret = -EDQUOT;
+		}
+		else {
+			key->user->qnbytes += delta;
+			key->quotalen += delta;
+		}
+		spin_unlock(&key->user->lock);
+	}
+
+	/* change the recorded data length if that didn't generate an error */
+	if (ret == 0)
+		key->datalen = datalen;
+
+	return ret;
+
+} /* end key_payload_reserve() */
+
+EXPORT_SYMBOL(key_payload_reserve);
+
+/*****************************************************************************/
+/*
+ * instantiate a key and link it into the target keyring atomically
+ * - called with the target keyring's semaphore writelocked
+ */
+static int __key_instantiate_and_link(struct key *key,
+				      const void *data,
+				      size_t datalen,
+				      struct key *keyring)
+{
+	int ret, awaken;
+
+	key_check(key);
+	key_check(keyring);
+
+	awaken = 0;
+	ret = -EBUSY;
+
+	down_write(&key_construction_sem);
+
+	/* can't instantiate twice */
+	if (!(key->flags & KEY_FLAG_INSTANTIATED)) {
+		/* instantiate the key */
+		ret = key->type->instantiate(key, data, datalen);
+
+		if (ret == 0) {
+			/* mark the key as being instantiated */
+			write_lock(&key->lock);
+
+			atomic_inc(&key->user->nikeys);
+			key->flags |= KEY_FLAG_INSTANTIATED;
+
+			if (key->flags & KEY_FLAG_USER_CONSTRUCT) {
+				key->flags &= ~KEY_FLAG_USER_CONSTRUCT;
+				awaken = 1;
+			}
+
+			write_unlock(&key->lock);
+
+			/* and link it into the destination keyring */
+			if (keyring)
+				ret = __key_link(keyring, key);
+		}
+	}
+
+	up_write(&key_construction_sem);
+
+	/* wake up anyone waiting for a key to be constructed */
+	if (awaken)
+		wake_up_all(&request_key_conswq);
+
+	return ret;
+
+} /* end __key_instantiate_and_link() */
+
+/*****************************************************************************/
+/*
+ * instantiate a key and link it into the target keyring atomically
+ */
+int key_instantiate_and_link(struct key *key,
+			     const void *data,
+			     size_t datalen,
+			     struct key *keyring)
+{
+	int ret;
+
+	if (keyring)
+		down_write(&keyring->sem);
+
+	ret = __key_instantiate_and_link(key, data, datalen, keyring);
+
+	if (keyring)
+		up_write(&keyring->sem);
+
+	return ret;
+} /* end key_instantiate_and_link() */
+
+EXPORT_SYMBOL(key_instantiate_and_link);
+
+/*****************************************************************************/
+/*
+ * negatively instantiate a key and link it into the target keyring atomically
+ */
+int key_negate_and_link(struct key *key,
+			unsigned timeout,
+			struct key *keyring)
+{
+	struct timespec now;
+	int ret, awaken;
+
+	key_check(key);
+	key_check(keyring);
+
+	awaken = 0;
+	ret = -EBUSY;
+
+	if (keyring)
+		down_write(&keyring->sem);
+
+	down_write(&key_construction_sem);
+
+	/* can't instantiate twice */
+	if (!(key->flags & KEY_FLAG_INSTANTIATED)) {
+		/* mark the key as being negatively instantiated */
+		write_lock(&key->lock);
+
+		atomic_inc(&key->user->nikeys);
+		key->flags |= KEY_FLAG_INSTANTIATED | KEY_FLAG_NEGATIVE;
+		now = current_kernel_time();
+		key->expiry = now.tv_sec + timeout;
+
+		if (key->flags & KEY_FLAG_USER_CONSTRUCT) {
+			key->flags &= ~KEY_FLAG_USER_CONSTRUCT;
+			awaken = 1;
+		}
+
+		write_unlock(&key->lock);
+		ret = 0;
+
+		/* and link it into the destination keyring */
+		if (keyring)
+			ret = __key_link(keyring, key);
+	}
+
+	up_write(&key_construction_sem);
+
+	if (keyring)
+		up_write(&keyring->sem);
+
+	/* wake up anyone waiting for a key to be constructed */
+	if (awaken)
+		wake_up_all(&request_key_conswq);
+
+	return ret;
+
+} /* end key_negate_and_link() */
+
+EXPORT_SYMBOL(key_negate_and_link);
+
+/*****************************************************************************/
+/*
+ * do cleaning up in process context so that we don't have to disable
+ * interrupts all over the place
+ */
+static void key_cleanup(void *data)
+{
+	struct rb_node *_n;
+	struct key *key;
+
+ go_again:
+	/* look for a dead key in the tree */
+	spin_lock(&key_serial_lock);
+
+	for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) {
+		key = rb_entry(_n, struct key, serial_node);
+
+		if (atomic_read(&key->usage) == 0)
+			goto found_dead_key;
+	}
+
+	spin_unlock(&key_serial_lock);
+	return;
+
+ found_dead_key:
+	/* we found a dead key - once we've removed it from the tree, we can
+	 * drop the lock */
+	rb_erase(&key->serial_node, &key_serial_tree);
+	spin_unlock(&key_serial_lock);
+
+	/* deal with the user's key tracking and quota */
+	if (key->flags & KEY_FLAG_IN_QUOTA) {
+		spin_lock(&key->user->lock);
+		key->user->qnkeys--;
+		key->user->qnbytes -= key->quotalen;
+		spin_unlock(&key->user->lock);
+	}
+
+	atomic_dec(&key->user->nkeys);
+	if (key->flags & KEY_FLAG_INSTANTIATED)
+		atomic_dec(&key->user->nikeys);
+
+	key_user_put(key->user);
+
+	/* now throw away the key memory */
+	if (key->type->destroy)
+		key->type->destroy(key);
+
+	kfree(key->description);
+
+#ifdef KEY_DEBUGGING
+	key->magic = KEY_DEBUG_MAGIC_X;
+#endif
+	kmem_cache_free(key_jar, key);
+
+	/* there may, of course, be more than one key to destroy */
+	goto go_again;
+
+} /* end key_cleanup() */
+
+/*****************************************************************************/
+/*
+ * dispose of a reference to a key
+ * - when all the references are gone, we schedule the cleanup task to come and
+ *   pull it out of the tree in definite process context
+ */
+void key_put(struct key *key)
+{
+	if (key) {
+		key_check(key);
+
+		if (atomic_dec_and_test(&key->usage))
+			schedule_work(&key_cleanup_task);
+	}
+
+} /* end key_put() */
+
+EXPORT_SYMBOL(key_put);
+
+/*****************************************************************************/
+/*
+ * find a key by its serial number
+ */
+struct key *key_lookup(key_serial_t id)
+{
+	struct rb_node *n;
+	struct key *key;
+
+	spin_lock(&key_serial_lock);
+
+	/* search the tree for the specified key */
+	n = key_serial_tree.rb_node;
+	while (n) {
+		key = rb_entry(n, struct key, serial_node);
+
+		if (id < key->serial)
+			n = n->rb_left;
+		else if (id > key->serial)
+			n = n->rb_right;
+		else
+			goto found;
+	}
+
+ not_found:
+	key = ERR_PTR(-ENOKEY);
+	goto error;
+
+ found:
+	/* pretent doesn't exist if it's dead */
+	if (atomic_read(&key->usage) == 0 ||
+	    (key->flags & KEY_FLAG_DEAD) ||
+	    key->type == &key_type_dead)
+		goto not_found;
+
+	/* this races with key_put(), but that doesn't matter since key_put()
+	 * doesn't actually change the key
+	 */
+	atomic_inc(&key->usage);
+
+ error:
+	spin_unlock(&key_serial_lock);
+	return key;
+
+} /* end key_lookup() */
+
+/*****************************************************************************/
+/*
+ * find and lock the specified key type against removal
+ * - we return with the sem readlocked
+ */
+struct key_type *key_type_lookup(const char *type)
+{
+	struct key_type *ktype;
+
+	down_read(&key_types_sem);
+
+	/* look up the key type to see if it's one of the registered kernel
+	 * types */
+	list_for_each_entry(ktype, &key_types_list, link) {
+		if (strcmp(ktype->name, type) == 0)
+			goto found_kernel_type;
+	}
+
+	up_read(&key_types_sem);
+	ktype = ERR_PTR(-ENOKEY);
+
+ found_kernel_type:
+	return ktype;
+
+} /* end key_type_lookup() */
+
+/*****************************************************************************/
+/*
+ * unlock a key type
+ */
+void key_type_put(struct key_type *ktype)
+{
+	up_read(&key_types_sem);
+
+} /* end key_type_put() */
+
+/*****************************************************************************/
+/*
+ * attempt to update an existing key
+ * - the key has an incremented refcount
+ * - we need to put the key if we get an error
+ */
+static inline struct key *__key_update(struct key *key, const void *payload,
+				       size_t plen)
+{
+	int ret;
+
+	/* need write permission on the key to update it */
+	ret = -EACCES;
+	if (!key_permission(key, KEY_WRITE))
+		goto error;
+
+	ret = -EEXIST;
+	if (!key->type->update)
+		goto error;
+
+	down_write(&key->sem);
+
+	ret = key->type->update(key, payload, plen);
+
+	if (ret == 0) {
+		/* updating a negative key instantiates it */
+		write_lock(&key->lock);
+		key->flags &= ~KEY_FLAG_NEGATIVE;
+		write_unlock(&key->lock);
+	}
+
+	up_write(&key->sem);
+
+	if (ret < 0)
+		goto error;
+ out:
+	return key;
+
+ error:
+	key_put(key);
+	key = ERR_PTR(ret);
+	goto out;
+
+} /* end __key_update() */
+
+/*****************************************************************************/
+/*
+ * search the specified keyring for a key of the same description; if one is
+ * found, update it, otherwise add a new one
+ */
+struct key *key_create_or_update(struct key *keyring,
+				 const char *type,
+				 const char *description,
+				 const void *payload,
+				 size_t plen,
+				 int not_in_quota)
+{
+	struct key_type *ktype;
+	struct key *key = NULL;
+	key_perm_t perm;
+	int ret;
+
+	key_check(keyring);
+
+	/* look up the key type to see if it's one of the registered kernel
+	 * types */
+	ktype = key_type_lookup(type);
+	if (IS_ERR(ktype)) {
+		key = ERR_PTR(-ENODEV);
+		goto error;
+	}
+
+	ret = -EINVAL;
+	if (!ktype->match || !ktype->instantiate)
+		goto error_2;
+
+	/* search for an existing key of the same type and description in the
+	 * destination keyring
+	 */
+	down_write(&keyring->sem);
+
+	key = __keyring_search_one(keyring, ktype, description, 0);
+	if (!IS_ERR(key))
+		goto found_matching_key;
+
+	/* if we're going to allocate a new key, we're going to have to modify
+	 * the keyring */
+	ret = -EACCES;
+	if (!key_permission(keyring, KEY_WRITE))
+		goto error_3;
+
+	/* decide on the permissions we want */
+	perm = KEY_USR_VIEW | KEY_USR_SEARCH | KEY_USR_LINK;
+
+	if (ktype->read)
+		perm |= KEY_USR_READ;
+
+	if (ktype == &key_type_keyring || ktype->update)
+		perm |= KEY_USR_WRITE;
+
+	/* allocate a new key */
+	key = key_alloc(ktype, description, current->fsuid, current->fsgid,
+			perm, not_in_quota);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto error_3;
+	}
+
+	/* instantiate it and link it into the target keyring */
+	ret = __key_instantiate_and_link(key, payload, plen, keyring);
+	if (ret < 0) {
+		key_put(key);
+		key = ERR_PTR(ret);
+	}
+
+ error_3:
+	up_write(&keyring->sem);
+ error_2:
+	key_type_put(ktype);
+ error:
+	return key;
+
+ found_matching_key:
+	/* we found a matching key, so we're going to try to update it
+	 * - we can drop the locks first as we have the key pinned
+	 */
+	up_write(&keyring->sem);
+	key_type_put(ktype);
+
+	key = __key_update(key, payload, plen);
+	goto error;
+
+} /* end key_create_or_update() */
+
+EXPORT_SYMBOL(key_create_or_update);
+
+/*****************************************************************************/
+/*
+ * update a key
+ */
+int key_update(struct key *key, const void *payload, size_t plen)
+{
+	int ret;
+
+	key_check(key);
+
+	/* the key must be writable */
+	ret = -EACCES;
+	if (!key_permission(key, KEY_WRITE))
+		goto error;
+
+	/* attempt to update it if supported */
+	ret = -EOPNOTSUPP;
+	if (key->type->update) {
+		down_write(&key->sem);
+		ret = key->type->update(key, payload, plen);
+
+		if (ret == 0) {
+			/* updating a negative key instantiates it */
+			write_lock(&key->lock);
+			key->flags &= ~KEY_FLAG_NEGATIVE;
+			write_unlock(&key->lock);
+		}
+
+		up_write(&key->sem);
+	}
+
+ error:
+	return ret;
+
+} /* end key_update() */
+
+EXPORT_SYMBOL(key_update);
+
+/*****************************************************************************/
+/*
+ * duplicate a key, potentially with a revised description
+ * - must be supported by the keytype (keyrings for instance can be duplicated)
+ */
+struct key *key_duplicate(struct key *source, const char *desc)
+{
+	struct key *key;
+	int ret;
+
+	key_check(source);
+
+	if (!desc)
+		desc = source->description;
+
+	down_read(&key_types_sem);
+
+	ret = -EINVAL;
+	if (!source->type->duplicate)
+		goto error;
+
+	/* allocate and instantiate a key */
+	key = key_alloc(source->type, desc, current->fsuid, current->fsgid,
+			source->perm, 0);
+	if (IS_ERR(key))
+		goto error_k;
+
+	down_read(&source->sem);
+	ret = key->type->duplicate(key, source);
+	up_read(&source->sem);
+	if (ret < 0)
+		goto error2;
+
+	atomic_inc(&key->user->nikeys);
+
+	write_lock(&key->lock);
+	key->flags |= KEY_FLAG_INSTANTIATED;
+	write_unlock(&key->lock);
+
+ error_k:
+	up_read(&key_types_sem);
+ out:
+	return key;
+
+ error2:
+	key_put(key);
+ error:
+	up_read(&key_types_sem);
+	key = ERR_PTR(ret);
+	goto out;
+
+} /* end key_duplicate() */
+
+/*****************************************************************************/
+/*
+ * revoke a key
+ */
+void key_revoke(struct key *key)
+{
+	key_check(key);
+
+	/* make sure no one's trying to change or use the key when we mark
+	 * it */
+	down_write(&key->sem);
+	write_lock(&key->lock);
+	key->flags |= KEY_FLAG_REVOKED;
+	write_unlock(&key->lock);
+	up_write(&key->sem);
+
+} /* end key_revoke() */
+
+EXPORT_SYMBOL(key_revoke);
+
+/*****************************************************************************/
+/*
+ * register a type of key
+ */
+int register_key_type(struct key_type *ktype)
+{
+	struct key_type *p;
+	int ret;
+
+	ret = -EEXIST;
+	down_write(&key_types_sem);
+
+	/* disallow key types with the same name */
+	list_for_each_entry(p, &key_types_list, link) {
+		if (strcmp(p->name, ktype->name) == 0)
+			goto out;
+	}
+
+	/* store the type */
+	list_add(&ktype->link, &key_types_list);
+	ret = 0;
+
+ out:
+	up_write(&key_types_sem);
+	return ret;
+
+} /* end register_key_type() */
+
+EXPORT_SYMBOL(register_key_type);
+
+/*****************************************************************************/
+/*
+ * unregister a type of key
+ */
+void unregister_key_type(struct key_type *ktype)
+{
+	struct rb_node *_n;
+	struct key *key;
+
+	down_write(&key_types_sem);
+
+	/* withdraw the key type */
+	list_del_init(&ktype->link);
+
+	/* need to withdraw all keys of this type */
+	spin_lock(&key_serial_lock);
+
+	for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) {
+		key = rb_entry(_n, struct key, serial_node);
+
+		if (key->type != ktype)
+			continue;
+
+		write_lock(&key->lock);
+		key->type = &key_type_dead;
+		write_unlock(&key->lock);
+
+		/* there shouldn't be anyone looking at the description or
+		 * payload now */
+		if (ktype->destroy)
+			ktype->destroy(key);
+		memset(&key->payload, 0xbd, sizeof(key->payload));
+	}
+
+	spin_unlock(&key_serial_lock);
+	up_write(&key_types_sem);
+
+} /* end unregister_key_type() */
+
+EXPORT_SYMBOL(unregister_key_type);
+
+/*****************************************************************************/
+/*
+ * initialise the key management stuff
+ */
+void __init key_init(void)
+{
+	/* allocate a slab in which we can store keys */
+	key_jar = kmem_cache_create("key_jar", sizeof(struct key),
+			0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
+
+	/* add the special key types */
+	list_add_tail(&key_type_keyring.link, &key_types_list);
+	list_add_tail(&key_type_dead.link, &key_types_list);
+	list_add_tail(&key_type_user.link, &key_types_list);
+
+	/* record the root user tracking */
+	rb_link_node(&root_key_user.node,
+		     NULL,
+		     &key_user_tree.rb_node);
+
+	rb_insert_color(&root_key_user.node,
+			&key_user_tree);
+
+	/* record root's user standard keyrings */
+	key_check(&root_user_keyring);
+	key_check(&root_session_keyring);
+
+	__key_insert_serial(&root_user_keyring);
+	__key_insert_serial(&root_session_keyring);
+
+	keyring_publish_name(&root_user_keyring);
+	keyring_publish_name(&root_session_keyring);
+
+	/* link the two root keyrings together */
+	key_link(&root_session_keyring, &root_user_keyring);
+} /* end key_init() */
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
new file mode 100644
index 0000000..dc0011b
--- /dev/null
+++ b/security/keys/keyctl.c
@@ -0,0 +1,987 @@
+/* keyctl.c: userspace keyctl operations
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@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; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+#include <linux/keyctl.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <asm/uaccess.h>
+#include "internal.h"
+
+/*****************************************************************************/
+/*
+ * extract the description of a new key from userspace and either add it as a
+ * new key to the specified keyring or update a matching key in that keyring
+ * - the keyring must be writable
+ * - returns the new key's serial number
+ * - implements add_key()
+ */
+asmlinkage long sys_add_key(const char __user *_type,
+			    const char __user *_description,
+			    const void __user *_payload,
+			    size_t plen,
+			    key_serial_t ringid)
+{
+	struct key *keyring, *key;
+	char type[32], *description;
+	void *payload;
+	long dlen, ret;
+
+	ret = -EINVAL;
+	if (plen > 32767)
+		goto error;
+
+	/* draw all the data into kernel space */
+	ret = strncpy_from_user(type, _type, sizeof(type) - 1);
+	if (ret < 0)
+		goto error;
+	type[31] = '\0';
+
+	ret = -EFAULT;
+	dlen = strnlen_user(_description, PAGE_SIZE - 1);
+	if (dlen <= 0)
+		goto error;
+
+	ret = -EINVAL;
+	if (dlen > PAGE_SIZE - 1)
+		goto error;
+
+	ret = -ENOMEM;
+	description = kmalloc(dlen + 1, GFP_KERNEL);
+	if (!description)
+		goto error;
+
+	ret = -EFAULT;
+	if (copy_from_user(description, _description, dlen + 1) != 0)
+		goto error2;
+
+	/* pull the payload in if one was supplied */
+	payload = NULL;
+
+	if (_payload) {
+		ret = -ENOMEM;
+		payload = kmalloc(plen, GFP_KERNEL);
+		if (!payload)
+			goto error2;
+
+		ret = -EFAULT;
+		if (copy_from_user(payload, _payload, plen) != 0)
+			goto error3;
+	}
+
+	/* find the target keyring (which must be writable) */
+	keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+	if (IS_ERR(keyring)) {
+		ret = PTR_ERR(keyring);
+		goto error3;
+	}
+
+	/* create or update the requested key and add it to the target
+	 * keyring */
+	key = key_create_or_update(keyring, type, description,
+				   payload, plen, 0);
+	if (!IS_ERR(key)) {
+		ret = key->serial;
+		key_put(key);
+	}
+	else {
+		ret = PTR_ERR(key);
+	}
+
+	key_put(keyring);
+ error3:
+	kfree(payload);
+ error2:
+	kfree(description);
+ error:
+	return ret;
+
+} /* end sys_add_key() */
+
+/*****************************************************************************/
+/*
+ * search the process keyrings for a matching key
+ * - nested keyrings may also be searched if they have Search permission
+ * - if a key is found, it will be attached to the destination keyring if
+ *   there's one specified
+ * - /sbin/request-key will be invoked if _callout_info is non-NULL
+ *   - the _callout_info string will be passed to /sbin/request-key
+ *   - if the _callout_info string is empty, it will be rendered as "-"
+ * - implements request_key()
+ */
+asmlinkage long sys_request_key(const char __user *_type,
+				const char __user *_description,
+				const char __user *_callout_info,
+				key_serial_t destringid)
+{
+	struct key_type *ktype;
+	struct key *key, *dest;
+	char type[32], *description, *callout_info;
+	long dlen, ret;
+
+	/* pull the type into kernel space */
+	ret = strncpy_from_user(type, _type, sizeof(type) - 1);
+	if (ret < 0)
+		goto error;
+	type[31] = '\0';
+
+	/* pull the description into kernel space */
+	ret = -EFAULT;
+	dlen = strnlen_user(_description, PAGE_SIZE - 1);
+	if (dlen <= 0)
+		goto error;
+
+	ret = -EINVAL;
+	if (dlen > PAGE_SIZE - 1)
+		goto error;
+
+	ret = -ENOMEM;
+	description = kmalloc(dlen + 1, GFP_KERNEL);
+	if (!description)
+		goto error;
+
+	ret = -EFAULT;
+	if (copy_from_user(description, _description, dlen + 1) != 0)
+		goto error2;
+
+	/* pull the callout info into kernel space */
+	callout_info = NULL;
+	if (_callout_info) {
+		ret = -EFAULT;
+		dlen = strnlen_user(_callout_info, PAGE_SIZE - 1);
+		if (dlen <= 0)
+			goto error2;
+
+		ret = -EINVAL;
+		if (dlen > PAGE_SIZE - 1)
+			goto error2;
+
+		ret = -ENOMEM;
+		callout_info = kmalloc(dlen + 1, GFP_KERNEL);
+		if (!callout_info)
+			goto error2;
+
+		ret = -EFAULT;
+		if (copy_from_user(callout_info, _callout_info, dlen + 1) != 0)
+			goto error3;
+	}
+
+	/* get the destination keyring if specified */
+	dest = NULL;
+	if (destringid) {
+		dest = lookup_user_key(destringid, 1, 0, KEY_WRITE);
+		if (IS_ERR(dest)) {
+			ret = PTR_ERR(dest);
+			goto error3;
+		}
+	}
+
+	/* find the key type */
+	ktype = key_type_lookup(type);
+	if (IS_ERR(ktype)) {
+		ret = PTR_ERR(ktype);
+		goto error4;
+	}
+
+	/* do the search */
+	key = request_key(ktype, description, callout_info);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto error5;
+	}
+
+	/* link the resulting key to the destination keyring */
+	if (dest) {
+		ret = key_link(dest, key);
+		if (ret < 0)
+			goto error6;
+	}
+
+	ret = key->serial;
+
+ error6:
+	key_put(key);
+ error5:
+	key_type_put(ktype);
+ error4:
+	key_put(dest);
+ error3:
+	kfree(callout_info);
+ error2:
+	kfree(description);
+ error:
+	return ret;
+
+} /* end sys_request_key() */
+
+/*****************************************************************************/
+/*
+ * get the ID of the specified process keyring
+ * - the keyring must have search permission to be found
+ * - implements keyctl(KEYCTL_GET_KEYRING_ID)
+ */
+long keyctl_get_keyring_ID(key_serial_t id, int create)
+{
+	struct key *key;
+	long ret;
+
+	key = lookup_user_key(id, create, 0, KEY_SEARCH);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto error;
+	}
+
+	ret = key->serial;
+	key_put(key);
+ error:
+	return ret;
+
+} /* end keyctl_get_keyring_ID() */
+
+/*****************************************************************************/
+/*
+ * join the session keyring
+ * - implements keyctl(KEYCTL_JOIN_SESSION_KEYRING)
+ */
+long keyctl_join_session_keyring(const char __user *_name)
+{
+	char *name;
+	long nlen, ret;
+
+	/* fetch the name from userspace */
+	name = NULL;
+	if (_name) {
+		ret = -EFAULT;
+		nlen = strnlen_user(_name, PAGE_SIZE - 1);
+		if (nlen <= 0)
+			goto error;
+
+		ret = -EINVAL;
+		if (nlen > PAGE_SIZE - 1)
+			goto error;
+
+		ret = -ENOMEM;
+		name = kmalloc(nlen + 1, GFP_KERNEL);
+		if (!name)
+			goto error;
+
+		ret = -EFAULT;
+		if (copy_from_user(name, _name, nlen + 1) != 0)
+			goto error2;
+	}
+
+	/* join the session */
+	ret = join_session_keyring(name);
+
+ error2:
+	kfree(name);
+ error:
+	return ret;
+
+} /* end keyctl_join_session_keyring() */
+
+/*****************************************************************************/
+/*
+ * update a key's data payload
+ * - the key must be writable
+ * - implements keyctl(KEYCTL_UPDATE)
+ */
+long keyctl_update_key(key_serial_t id,
+		       const void __user *_payload,
+		       size_t plen)
+{
+	struct key *key;
+	void *payload;
+	long ret;
+
+	ret = -EINVAL;
+	if (plen > PAGE_SIZE)
+		goto error;
+
+	/* pull the payload in if one was supplied */
+	payload = NULL;
+	if (_payload) {
+		ret = -ENOMEM;
+		payload = kmalloc(plen, GFP_KERNEL);
+		if (!payload)
+			goto error;
+
+		ret = -EFAULT;
+		if (copy_from_user(payload, _payload, plen) != 0)
+			goto error2;
+	}
+
+	/* find the target key (which must be writable) */
+	key = lookup_user_key(id, 0, 0, KEY_WRITE);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto error2;
+	}
+
+	/* update the key */
+	ret = key_update(key, payload, plen);
+
+	key_put(key);
+ error2:
+	kfree(payload);
+ error:
+	return ret;
+
+} /* end keyctl_update_key() */
+
+/*****************************************************************************/
+/*
+ * revoke a key
+ * - the key must be writable
+ * - implements keyctl(KEYCTL_REVOKE)
+ */
+long keyctl_revoke_key(key_serial_t id)
+{
+	struct key *key;
+	long ret;
+
+	key = lookup_user_key(id, 0, 0, KEY_WRITE);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto error;
+	}
+
+	key_revoke(key);
+	ret = 0;
+
+	key_put(key);
+ error:
+	return 0;
+
+} /* end keyctl_revoke_key() */
+
+/*****************************************************************************/
+/*
+ * clear the specified process keyring
+ * - the keyring must be writable
+ * - implements keyctl(KEYCTL_CLEAR)
+ */
+long keyctl_keyring_clear(key_serial_t ringid)
+{
+	struct key *keyring;
+	long ret;
+
+	keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+	if (IS_ERR(keyring)) {
+		ret = PTR_ERR(keyring);
+		goto error;
+	}
+
+	ret = keyring_clear(keyring);
+
+	key_put(keyring);
+ error:
+	return ret;
+
+} /* end keyctl_keyring_clear() */
+
+/*****************************************************************************/
+/*
+ * link a key into a keyring
+ * - the keyring must be writable
+ * - the key must be linkable
+ * - implements keyctl(KEYCTL_LINK)
+ */
+long keyctl_keyring_link(key_serial_t id, key_serial_t ringid)
+{
+	struct key *keyring, *key;
+	long ret;
+
+	keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+	if (IS_ERR(keyring)) {
+		ret = PTR_ERR(keyring);
+		goto error;
+	}
+
+	key = lookup_user_key(id, 1, 0, KEY_LINK);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto error2;
+	}
+
+	ret = key_link(keyring, key);
+
+	key_put(key);
+ error2:
+	key_put(keyring);
+ error:
+	return ret;
+
+} /* end keyctl_keyring_link() */
+
+/*****************************************************************************/
+/*
+ * unlink the first attachment of a key from a keyring
+ * - the keyring must be writable
+ * - we don't need any permissions on the key
+ * - implements keyctl(KEYCTL_UNLINK)
+ */
+long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid)
+{
+	struct key *keyring, *key;
+	long ret;
+
+	keyring = lookup_user_key(ringid, 0, 0, KEY_WRITE);
+	if (IS_ERR(keyring)) {
+		ret = PTR_ERR(keyring);
+		goto error;
+	}
+
+	key = lookup_user_key(id, 0, 0, 0);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto error2;
+	}
+
+	ret = key_unlink(keyring, key);
+
+	key_put(key);
+ error2:
+	key_put(keyring);
+ error:
+	return ret;
+
+} /* end keyctl_keyring_unlink() */
+
+/*****************************************************************************/
+/*
+ * describe a user key
+ * - the key must have view permission
+ * - if there's a buffer, we place up to buflen bytes of data into it
+ * - unless there's an error, we return the amount of description available,
+ *   irrespective of how much we may have copied
+ * - the description is formatted thus:
+ *	type;uid;gid;perm;description<NUL>
+ * - implements keyctl(KEYCTL_DESCRIBE)
+ */
+long keyctl_describe_key(key_serial_t keyid,
+			 char __user *buffer,
+			 size_t buflen)
+{
+	struct key *key;
+	char *tmpbuf;
+	long ret;
+
+	key = lookup_user_key(keyid, 0, 1, KEY_VIEW);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto error;
+	}
+
+	/* calculate how much description we're going to return */
+	ret = -ENOMEM;
+	tmpbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!tmpbuf)
+		goto error2;
+
+	ret = snprintf(tmpbuf, PAGE_SIZE - 1,
+		       "%s;%d;%d;%06x;%s",
+		       key->type->name,
+		       key->uid,
+		       key->gid,
+		       key->perm,
+		       key->description ? key->description :""
+		       );
+
+	/* include a NUL char at the end of the data */
+	if (ret > PAGE_SIZE - 1)
+		ret = PAGE_SIZE - 1;
+	tmpbuf[ret] = 0;
+	ret++;
+
+	/* consider returning the data */
+	if (buffer && buflen > 0) {
+		if (buflen > ret)
+			buflen = ret;
+
+		if (copy_to_user(buffer, tmpbuf, buflen) != 0)
+			ret = -EFAULT;
+	}
+
+	kfree(tmpbuf);
+ error2:
+	key_put(key);
+ error:
+	return ret;
+
+} /* end keyctl_describe_key() */
+
+/*****************************************************************************/
+/*
+ * search the specified keyring for a matching key
+ * - the start keyring must be searchable
+ * - nested keyrings may also be searched if they are searchable
+ * - only keys with search permission may be found
+ * - if a key is found, it will be attached to the destination keyring if
+ *   there's one specified
+ * - implements keyctl(KEYCTL_SEARCH)
+ */
+long keyctl_keyring_search(key_serial_t ringid,
+			   const char __user *_type,
+			   const char __user *_description,
+			   key_serial_t destringid)
+{
+	struct key_type *ktype;
+	struct key *keyring, *key, *dest;
+	char type[32], *description;
+	long dlen, ret;
+
+	/* pull the type and description into kernel space */
+	ret = strncpy_from_user(type, _type, sizeof(type) - 1);
+	if (ret < 0)
+		goto error;
+	type[31] = '\0';
+
+	ret = -EFAULT;
+	dlen = strnlen_user(_description, PAGE_SIZE - 1);
+	if (dlen <= 0)
+		goto error;
+
+	ret = -EINVAL;
+	if (dlen > PAGE_SIZE - 1)
+		goto error;
+
+	ret = -ENOMEM;
+	description = kmalloc(dlen + 1, GFP_KERNEL);
+	if (!description)
+		goto error;
+
+	ret = -EFAULT;
+	if (copy_from_user(description, _description, dlen + 1) != 0)
+		goto error2;
+
+	/* get the keyring at which to begin the search */
+	keyring = lookup_user_key(ringid, 0, 0, KEY_SEARCH);
+	if (IS_ERR(keyring)) {
+		ret = PTR_ERR(keyring);
+		goto error2;
+	}
+
+	/* get the destination keyring if specified */
+	dest = NULL;
+	if (destringid) {
+		dest = lookup_user_key(destringid, 1, 0, KEY_WRITE);
+		if (IS_ERR(dest)) {
+			ret = PTR_ERR(dest);
+			goto error3;
+		}
+	}
+
+	/* find the key type */
+	ktype = key_type_lookup(type);
+	if (IS_ERR(ktype)) {
+		ret = PTR_ERR(ktype);
+		goto error4;
+	}
+
+	/* do the search */
+	key = keyring_search(keyring, ktype, description);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+
+		/* treat lack or presence of a negative key the same */
+		if (ret == -EAGAIN)
+			ret = -ENOKEY;
+		goto error5;
+	}
+
+	/* link the resulting key to the destination keyring if we can */
+	if (dest) {
+		ret = -EACCES;
+		if (!key_permission(key, KEY_LINK))
+			goto error6;
+
+		ret = key_link(dest, key);
+		if (ret < 0)
+			goto error6;
+	}
+
+	ret = key->serial;
+
+ error6:
+	key_put(key);
+ error5:
+	key_type_put(ktype);
+ error4:
+	key_put(dest);
+ error3:
+	key_put(keyring);
+ error2:
+	kfree(description);
+ error:
+	return ret;
+
+} /* end keyctl_keyring_search() */
+
+/*****************************************************************************/
+/*
+ * see if the key we're looking at is the target key
+ */
+static int keyctl_read_key_same(const struct key *key, const void *target)
+{
+	return key == target;
+
+} /* end keyctl_read_key_same() */
+
+/*****************************************************************************/
+/*
+ * read a user key's payload
+ * - the keyring must be readable or the key must be searchable from the
+ *   process's keyrings
+ * - if there's a buffer, we place up to buflen bytes of data into it
+ * - unless there's an error, we return the amount of data in the key,
+ *   irrespective of how much we may have copied
+ * - implements keyctl(KEYCTL_READ)
+ */
+long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
+{
+	struct key *key, *skey;
+	long ret;
+
+	/* find the key first */
+	key = lookup_user_key(keyid, 0, 0, 0);
+	if (!IS_ERR(key)) {
+		/* see if we can read it directly */
+		if (key_permission(key, KEY_READ))
+			goto can_read_key;
+
+		/* can't; see if it's searchable from this process's
+		 * keyrings */
+		ret = -ENOKEY;
+		if (key_permission(key, KEY_SEARCH)) {
+			/* okay - we do have search permission on the key
+			 * itself, but do we have the key? */
+			skey = search_process_keyrings_aux(key->type, key,
+							   keyctl_read_key_same);
+			if (!IS_ERR(skey))
+				goto can_read_key2;
+		}
+
+		goto error2;
+	}
+
+	ret = -ENOKEY;
+	goto error;
+
+	/* the key is probably readable - now try to read it */
+ can_read_key2:
+	key_put(skey);
+ can_read_key:
+	ret = key_validate(key);
+	if (ret == 0) {
+		ret = -EOPNOTSUPP;
+		if (key->type->read) {
+			/* read the data with the semaphore held (since we
+			 * might sleep) */
+			down_read(&key->sem);
+			ret = key->type->read(key, buffer, buflen);
+			up_read(&key->sem);
+		}
+	}
+
+ error2:
+	key_put(key);
+ error:
+	return ret;
+
+} /* end keyctl_read_key() */
+
+/*****************************************************************************/
+/*
+ * change the ownership of a key
+ * - the keyring owned by the changer
+ * - if the uid or gid is -1, then that parameter is not changed
+ * - implements keyctl(KEYCTL_CHOWN)
+ */
+long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
+{
+	struct key *key;
+	long ret;
+
+	ret = 0;
+	if (uid == (uid_t) -1 && gid == (gid_t) -1)
+		goto error;
+
+	key = lookup_user_key(id, 1, 1, 0);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto error;
+	}
+
+	/* make the changes with the locks held to prevent chown/chown races */
+	ret = -EACCES;
+	down_write(&key->sem);
+	write_lock(&key->lock);
+
+	if (!capable(CAP_SYS_ADMIN)) {
+		/* only the sysadmin can chown a key to some other UID */
+		if (uid != (uid_t) -1 && key->uid != uid)
+			goto no_access;
+
+		/* only the sysadmin can set the key's GID to a group other
+		 * than one of those that the current process subscribes to */
+		if (gid != (gid_t) -1 && gid != key->gid && !in_group_p(gid))
+			goto no_access;
+	}
+
+	/* change the UID (have to update the quotas) */
+	if (uid != (uid_t) -1 && uid != key->uid) {
+		/* don't support UID changing yet */
+		ret = -EOPNOTSUPP;
+		goto no_access;
+	}
+
+	/* change the GID */
+	if (gid != (gid_t) -1)
+		key->gid = gid;
+
+	ret = 0;
+
+ no_access:
+	write_unlock(&key->lock);
+	up_write(&key->sem);
+	key_put(key);
+ error:
+	return ret;
+
+} /* end keyctl_chown_key() */
+
+/*****************************************************************************/
+/*
+ * change the permission mask on a key
+ * - the keyring owned by the changer
+ * - implements keyctl(KEYCTL_SETPERM)
+ */
+long keyctl_setperm_key(key_serial_t id, key_perm_t perm)
+{
+	struct key *key;
+	long ret;
+
+	ret = -EINVAL;
+	if (perm & ~(KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL))
+		goto error;
+
+	key = lookup_user_key(id, 1, 1, 0);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto error;
+	}
+
+	/* make the changes with the locks held to prevent chown/chmod
+	 * races */
+	ret = -EACCES;
+	down_write(&key->sem);
+	write_lock(&key->lock);
+
+	/* if we're not the sysadmin, we can only chmod a key that we
+	 * own */
+	if (!capable(CAP_SYS_ADMIN) && key->uid != current->fsuid)
+		goto no_access;
+
+	/* changing the permissions mask */
+	key->perm = perm;
+	ret = 0;
+
+ no_access:
+	write_unlock(&key->lock);
+	up_write(&key->sem);
+	key_put(key);
+ error:
+	return ret;
+
+} /* end keyctl_setperm_key() */
+
+/*****************************************************************************/
+/*
+ * instantiate the key with the specified payload, and, if one is given, link
+ * the key into the keyring
+ */
+long keyctl_instantiate_key(key_serial_t id,
+			    const void __user *_payload,
+			    size_t plen,
+			    key_serial_t ringid)
+{
+	struct key *key, *keyring;
+	void *payload;
+	long ret;
+
+	ret = -EINVAL;
+	if (plen > 32767)
+		goto error;
+
+	/* pull the payload in if one was supplied */
+	payload = NULL;
+
+	if (_payload) {
+		ret = -ENOMEM;
+		payload = kmalloc(plen, GFP_KERNEL);
+		if (!payload)
+			goto error;
+
+		ret = -EFAULT;
+		if (copy_from_user(payload, _payload, plen) != 0)
+			goto error2;
+	}
+
+	/* find the target key (which must be writable) */
+	key = lookup_user_key(id, 0, 1, KEY_WRITE);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto error2;
+	}
+
+	/* find the destination keyring if present (which must also be
+	 * writable) */
+	keyring = NULL;
+	if (ringid) {
+		keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+		if (IS_ERR(keyring)) {
+			ret = PTR_ERR(keyring);
+			goto error3;
+		}
+	}
+
+	/* instantiate the key and link it into a keyring */
+	ret = key_instantiate_and_link(key, payload, plen, keyring);
+
+	key_put(keyring);
+ error3:
+	key_put(key);
+ error2:
+	kfree(payload);
+ error:
+	return ret;
+
+} /* end keyctl_instantiate_key() */
+
+/*****************************************************************************/
+/*
+ * negatively instantiate the key with the given timeout (in seconds), and, if
+ * one is given, link the key into the keyring
+ */
+long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
+{
+	struct key *key, *keyring;
+	long ret;
+
+	/* find the target key (which must be writable) */
+	key = lookup_user_key(id, 0, 1, KEY_WRITE);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto error;
+	}
+
+	/* find the destination keyring if present (which must also be
+	 * writable) */
+	keyring = NULL;
+	if (ringid) {
+		keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+		if (IS_ERR(keyring)) {
+			ret = PTR_ERR(keyring);
+			goto error2;
+		}
+	}
+
+	/* instantiate the key and link it into a keyring */
+	ret = key_negate_and_link(key, timeout, keyring);
+
+	key_put(keyring);
+ error2:
+	key_put(key);
+ error:
+	return ret;
+
+} /* end keyctl_negate_key() */
+
+/*****************************************************************************/
+/*
+ * the key control system call
+ */
+asmlinkage long sys_keyctl(int option, unsigned long arg2, unsigned long arg3,
+			   unsigned long arg4, unsigned long arg5)
+{
+	switch (option) {
+	case KEYCTL_GET_KEYRING_ID:
+		return keyctl_get_keyring_ID((key_serial_t) arg2,
+					     (int) arg3);
+
+	case KEYCTL_JOIN_SESSION_KEYRING:
+		return keyctl_join_session_keyring((const char __user *) arg2);
+
+	case KEYCTL_UPDATE:
+		return keyctl_update_key((key_serial_t) arg2,
+					 (const void __user *) arg3,
+					 (size_t) arg4);
+
+	case KEYCTL_REVOKE:
+		return keyctl_revoke_key((key_serial_t) arg2);
+
+	case KEYCTL_DESCRIBE:
+		return keyctl_describe_key((key_serial_t) arg2,
+					   (char __user *) arg3,
+					   (unsigned) arg4);
+
+	case KEYCTL_CLEAR:
+		return keyctl_keyring_clear((key_serial_t) arg2);
+
+	case KEYCTL_LINK:
+		return keyctl_keyring_link((key_serial_t) arg2,
+					   (key_serial_t) arg3);
+
+	case KEYCTL_UNLINK:
+		return keyctl_keyring_unlink((key_serial_t) arg2,
+					     (key_serial_t) arg3);
+
+	case KEYCTL_SEARCH:
+		return keyctl_keyring_search((key_serial_t) arg2,
+					     (const char __user *) arg3,
+					     (const char __user *) arg4,
+					     (key_serial_t) arg5);
+
+	case KEYCTL_READ:
+		return keyctl_read_key((key_serial_t) arg2,
+				       (char __user *) arg3,
+				       (size_t) arg4);
+
+	case KEYCTL_CHOWN:
+		return keyctl_chown_key((key_serial_t) arg2,
+					(uid_t) arg3,
+					(gid_t) arg4);
+
+	case KEYCTL_SETPERM:
+		return keyctl_setperm_key((key_serial_t) arg2,
+					  (key_perm_t) arg3);
+
+	case KEYCTL_INSTANTIATE:
+		return keyctl_instantiate_key((key_serial_t) arg2,
+					      (const void __user *) arg3,
+					      (size_t) arg4,
+					      (key_serial_t) arg5);
+
+	case KEYCTL_NEGATE:
+		return keyctl_negate_key((key_serial_t) arg2,
+					 (unsigned) arg3,
+					 (key_serial_t) arg4);
+
+	default:
+		return -EOPNOTSUPP;
+	}
+
+} /* end sys_keyctl() */
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
new file mode 100644
index 0000000..e2ab4f8
--- /dev/null
+++ b/security/keys/keyring.c
@@ -0,0 +1,895 @@
+/* keyring.c: keyring handling
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@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; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <linux/err.h>
+#include <asm/uaccess.h>
+#include "internal.h"
+
+/*
+ * when plumbing the depths of the key tree, this sets a hard limit set on how
+ * deep we're willing to go
+ */
+#define KEYRING_SEARCH_MAX_DEPTH 6
+
+/*
+ * we keep all named keyrings in a hash to speed looking them up
+ */
+#define KEYRING_NAME_HASH_SIZE	(1 << 5)
+
+static struct list_head	keyring_name_hash[KEYRING_NAME_HASH_SIZE];
+static DEFINE_RWLOCK(keyring_name_lock);
+
+static inline unsigned keyring_hash(const char *desc)
+{
+	unsigned bucket = 0;
+
+	for (; *desc; desc++)
+		bucket += (unsigned char) *desc;
+
+	return bucket & (KEYRING_NAME_HASH_SIZE - 1);
+}
+
+/*
+ * the keyring type definition
+ */
+static int keyring_instantiate(struct key *keyring,
+			       const void *data, size_t datalen);
+static int keyring_duplicate(struct key *keyring, const struct key *source);
+static int keyring_match(const struct key *keyring, const void *criterion);
+static void keyring_destroy(struct key *keyring);
+static void keyring_describe(const struct key *keyring, struct seq_file *m);
+static long keyring_read(const struct key *keyring,
+			 char __user *buffer, size_t buflen);
+
+struct key_type key_type_keyring = {
+	.name		= "keyring",
+	.def_datalen	= sizeof(struct keyring_list),
+	.instantiate	= keyring_instantiate,
+	.duplicate	= keyring_duplicate,
+	.match		= keyring_match,
+	.destroy	= keyring_destroy,
+	.describe	= keyring_describe,
+	.read		= keyring_read,
+};
+
+/*
+ * semaphore to serialise link/link calls to prevent two link calls in parallel
+ * introducing a cycle
+ */
+DECLARE_RWSEM(keyring_serialise_link_sem);
+
+/*****************************************************************************/
+/*
+ * publish the name of a keyring so that it can be found by name (if it has
+ * one)
+ */
+void keyring_publish_name(struct key *keyring)
+{
+	int bucket;
+
+	if (keyring->description) {
+		bucket = keyring_hash(keyring->description);
+
+		write_lock(&keyring_name_lock);
+
+		if (!keyring_name_hash[bucket].next)
+			INIT_LIST_HEAD(&keyring_name_hash[bucket]);
+
+		list_add_tail(&keyring->type_data.link,
+			      &keyring_name_hash[bucket]);
+
+		write_unlock(&keyring_name_lock);
+	}
+
+} /* end keyring_publish_name() */
+
+/*****************************************************************************/
+/*
+ * initialise a keyring
+ * - we object if we were given any data
+ */
+static int keyring_instantiate(struct key *keyring,
+			       const void *data, size_t datalen)
+{
+	int ret;
+
+	ret = -EINVAL;
+	if (datalen == 0) {
+		/* make the keyring available by name if it has one */
+		keyring_publish_name(keyring);
+		ret = 0;
+	}
+
+	return ret;
+
+} /* end keyring_instantiate() */
+
+/*****************************************************************************/
+/*
+ * duplicate the list of subscribed keys from a source keyring into this one
+ */
+static int keyring_duplicate(struct key *keyring, const struct key *source)
+{
+	struct keyring_list *sklist, *klist;
+	unsigned max;
+	size_t size;
+	int loop, ret;
+
+	const unsigned limit =
+		(PAGE_SIZE - sizeof(*klist)) / sizeof(struct key);
+
+	ret = 0;
+	sklist = source->payload.subscriptions;
+
+	if (sklist && sklist->nkeys > 0) {
+		max = sklist->nkeys;
+		BUG_ON(max > limit);
+
+		max = (max + 3) & ~3;
+		if (max > limit)
+			max = limit;
+
+		ret = -ENOMEM;
+		size = sizeof(*klist) + sizeof(struct key) * max;
+		klist = kmalloc(size, GFP_KERNEL);
+		if (!klist)
+			goto error;
+
+		klist->maxkeys = max;
+		klist->nkeys = sklist->nkeys;
+		memcpy(klist->keys,
+		       sklist->keys,
+		       sklist->nkeys * sizeof(struct key));
+
+		for (loop = klist->nkeys - 1; loop >= 0; loop--)
+			atomic_inc(&klist->keys[loop]->usage);
+
+		keyring->payload.subscriptions = klist;
+		ret = 0;
+	}
+
+ error:
+	return ret;
+
+} /* end keyring_duplicate() */
+
+/*****************************************************************************/
+/*
+ * match keyrings on their name
+ */
+static int keyring_match(const struct key *keyring, const void *description)
+{
+	return keyring->description &&
+		strcmp(keyring->description, description) == 0;
+
+} /* end keyring_match() */
+
+/*****************************************************************************/
+/*
+ * dispose of the data dangling from the corpse of a keyring
+ */
+static void keyring_destroy(struct key *keyring)
+{
+	struct keyring_list *klist;
+	int loop;
+
+	if (keyring->description) {
+		write_lock(&keyring_name_lock);
+		list_del(&keyring->type_data.link);
+		write_unlock(&keyring_name_lock);
+	}
+
+	klist = keyring->payload.subscriptions;
+	if (klist) {
+		for (loop = klist->nkeys - 1; loop >= 0; loop--)
+			key_put(klist->keys[loop]);
+		kfree(klist);
+	}
+
+} /* end keyring_destroy() */
+
+/*****************************************************************************/
+/*
+ * describe the keyring
+ */
+static void keyring_describe(const struct key *keyring, struct seq_file *m)
+{
+	struct keyring_list *klist;
+
+	if (keyring->description) {
+		seq_puts(m, keyring->description);
+	}
+	else {
+		seq_puts(m, "[anon]");
+	}
+
+	klist = keyring->payload.subscriptions;
+	if (klist)
+		seq_printf(m, ": %u/%u", klist->nkeys, klist->maxkeys);
+	else
+		seq_puts(m, ": empty");
+
+} /* end keyring_describe() */
+
+/*****************************************************************************/
+/*
+ * read a list of key IDs from the keyring's contents
+ */
+static long keyring_read(const struct key *keyring,
+			 char __user *buffer, size_t buflen)
+{
+	struct keyring_list *klist;
+	struct key *key;
+	size_t qty, tmp;
+	int loop, ret;
+
+	ret = 0;
+	klist = keyring->payload.subscriptions;
+
+	if (klist) {
+		/* calculate how much data we could return */
+		qty = klist->nkeys * sizeof(key_serial_t);
+
+		if (buffer && buflen > 0) {
+			if (buflen > qty)
+				buflen = qty;
+
+			/* copy the IDs of the subscribed keys into the
+			 * buffer */
+			ret = -EFAULT;
+
+			for (loop = 0; loop < klist->nkeys; loop++) {
+				key = klist->keys[loop];
+
+				tmp = sizeof(key_serial_t);
+				if (tmp > buflen)
+					tmp = buflen;
+
+				if (copy_to_user(buffer,
+						 &key->serial,
+						 tmp) != 0)
+					goto error;
+
+				buflen -= tmp;
+				if (buflen == 0)
+					break;
+				buffer += tmp;
+			}
+		}
+
+		ret = qty;
+	}
+
+ error:
+	return ret;
+
+} /* end keyring_read() */
+
+/*****************************************************************************/
+/*
+ * allocate a keyring and link into the destination keyring
+ */
+struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
+			  int not_in_quota, struct key *dest)
+{
+	struct key *keyring;
+	int ret;
+
+	keyring = key_alloc(&key_type_keyring, description,
+			    uid, gid, KEY_USR_ALL, not_in_quota);
+
+	if (!IS_ERR(keyring)) {
+		ret = key_instantiate_and_link(keyring, NULL, 0, dest);
+		if (ret < 0) {
+			key_put(keyring);
+			keyring = ERR_PTR(ret);
+		}
+	}
+
+	return keyring;
+
+} /* end keyring_alloc() */
+
+/*****************************************************************************/
+/*
+ * search the supplied keyring tree for a key that matches the criterion
+ * - perform a breadth-then-depth search up to the prescribed limit
+ * - we only find keys on which we have search permission
+ * - we use the supplied match function to see if the description (or other
+ *   feature of interest) matches
+ * - we readlock the keyrings as we search down the tree
+ * - we return -EAGAIN if we didn't find any matching key
+ * - we return -ENOKEY if we only found negative matching keys
+ */
+struct key *keyring_search_aux(struct key *keyring,
+			       struct key_type *type,
+			       const void *description,
+			       key_match_func_t match)
+{
+	struct {
+		struct key *keyring;
+		int kix;
+	} stack[KEYRING_SEARCH_MAX_DEPTH];
+
+	struct keyring_list *keylist;
+	struct timespec now;
+	struct key *key;
+	long err;
+	int sp, psp, kix;
+
+	key_check(keyring);
+
+	/* top keyring must have search permission to begin the search */
+	key = ERR_PTR(-EACCES);
+	if (!key_permission(keyring, KEY_SEARCH))
+		goto error;
+
+	key = ERR_PTR(-ENOTDIR);
+	if (keyring->type != &key_type_keyring)
+		goto error;
+
+	now = current_kernel_time();
+	err = -EAGAIN;
+	sp = 0;
+
+	/* start processing a new keyring */
+ descend:
+	read_lock(&keyring->lock);
+	if (keyring->flags & KEY_FLAG_REVOKED)
+		goto not_this_keyring;
+
+	keylist = keyring->payload.subscriptions;
+	if (!keylist)
+		goto not_this_keyring;
+
+	/* iterate through the keys in this keyring first */
+	for (kix = 0; kix < keylist->nkeys; kix++) {
+		key = keylist->keys[kix];
+
+		/* ignore keys not of this type */
+		if (key->type != type)
+			continue;
+
+		/* skip revoked keys and expired keys */
+		if (key->flags & KEY_FLAG_REVOKED)
+			continue;
+
+		if (key->expiry && now.tv_sec >= key->expiry)
+			continue;
+
+		/* keys that don't match */
+		if (!match(key, description))
+			continue;
+
+		/* key must have search permissions */
+		if (!key_permission(key, KEY_SEARCH))
+			continue;
+
+		/* we set a different error code if we find a negative key */
+		if (key->flags & KEY_FLAG_NEGATIVE) {
+			err = -ENOKEY;
+			continue;
+		}
+
+		goto found;
+	}
+
+	/* search through the keyrings nested in this one */
+	kix = 0;
+ ascend:
+	while (kix < keylist->nkeys) {
+		key = keylist->keys[kix];
+		if (key->type != &key_type_keyring)
+			goto next;
+
+		/* recursively search nested keyrings
+		 * - only search keyrings for which we have search permission
+		 */
+		if (sp >= KEYRING_SEARCH_MAX_DEPTH)
+			goto next;
+
+		if (!key_permission(key, KEY_SEARCH))
+			goto next;
+
+		/* evade loops in the keyring tree */
+		for (psp = 0; psp < sp; psp++)
+			if (stack[psp].keyring == keyring)
+				goto next;
+
+		/* stack the current position */
+		stack[sp].keyring = keyring;
+		stack[sp].kix = kix;
+		sp++;
+
+		/* begin again with the new keyring */
+		keyring = key;
+		goto descend;
+
+	next:
+		kix++;
+	}
+
+	/* the keyring we're looking at was disqualified or didn't contain a
+	 * matching key */
+ not_this_keyring:
+	read_unlock(&keyring->lock);
+
+	if (sp > 0) {
+		/* resume the processing of a keyring higher up in the tree */
+		sp--;
+		keyring = stack[sp].keyring;
+		keylist = keyring->payload.subscriptions;
+		kix = stack[sp].kix + 1;
+		goto ascend;
+	}
+
+	key = ERR_PTR(err);
+	goto error;
+
+	/* we found a viable match */
+ found:
+	atomic_inc(&key->usage);
+	read_unlock(&keyring->lock);
+
+	/* unwind the keyring stack */
+	while (sp > 0) {
+		sp--;
+		read_unlock(&stack[sp].keyring->lock);
+	}
+
+	key_check(key);
+ error:
+	return key;
+
+} /* end keyring_search_aux() */
+
+/*****************************************************************************/
+/*
+ * search the supplied keyring tree for a key that matches the criterion
+ * - perform a breadth-then-depth search up to the prescribed limit
+ * - we only find keys on which we have search permission
+ * - we readlock the keyrings as we search down the tree
+ * - we return -EAGAIN if we didn't find any matching key
+ * - we return -ENOKEY if we only found negative matching keys
+ */
+struct key *keyring_search(struct key *keyring,
+			   struct key_type *type,
+			   const char *description)
+{
+	return keyring_search_aux(keyring, type, description, type->match);
+
+} /* end keyring_search() */
+
+EXPORT_SYMBOL(keyring_search);
+
+/*****************************************************************************/
+/*
+ * search the given keyring only (no recursion)
+ * - keyring must be locked by caller
+ */
+struct key *__keyring_search_one(struct key *keyring,
+				 const struct key_type *ktype,
+				 const char *description,
+				 key_perm_t perm)
+{
+	struct keyring_list *klist;
+	struct key *key;
+	int loop;
+
+	klist = keyring->payload.subscriptions;
+	if (klist) {
+		for (loop = 0; loop < klist->nkeys; loop++) {
+			key = klist->keys[loop];
+
+			if (key->type == ktype &&
+			    key->type->match(key, description) &&
+			    key_permission(key, perm) &&
+			    !(key->flags & KEY_FLAG_REVOKED)
+			    )
+				goto found;
+		}
+	}
+
+	key = ERR_PTR(-ENOKEY);
+	goto error;
+
+ found:
+	atomic_inc(&key->usage);
+ error:
+	return key;
+
+} /* end __keyring_search_one() */
+
+/*****************************************************************************/
+/*
+ * find a keyring with the specified name
+ * - all named keyrings are searched
+ * - only find keyrings with search permission for the process
+ * - only find keyrings with a serial number greater than the one specified
+ */
+struct key *find_keyring_by_name(const char *name, key_serial_t bound)
+{
+	struct key *keyring;
+	int bucket;
+
+	keyring = ERR_PTR(-EINVAL);
+	if (!name)
+		goto error;
+
+	bucket = keyring_hash(name);
+
+	read_lock(&keyring_name_lock);
+
+	if (keyring_name_hash[bucket].next) {
+		/* search this hash bucket for a keyring with a matching name
+		 * that's readable and that hasn't been revoked */
+		list_for_each_entry(keyring,
+				    &keyring_name_hash[bucket],
+				    type_data.link
+				    ) {
+			if (keyring->flags & KEY_FLAG_REVOKED)
+				continue;
+
+			if (strcmp(keyring->description, name) != 0)
+				continue;
+
+			if (!key_permission(keyring, KEY_SEARCH))
+				continue;
+
+			/* found a potential candidate, but we still need to
+			 * check the serial number */
+			if (keyring->serial <= bound)
+				continue;
+
+			/* we've got a match */
+			atomic_inc(&keyring->usage);
+			read_unlock(&keyring_name_lock);
+			goto error;
+		}
+	}
+
+	read_unlock(&keyring_name_lock);
+	keyring = ERR_PTR(-ENOKEY);
+
+ error:
+	return keyring;
+
+} /* end find_keyring_by_name() */
+
+/*****************************************************************************/
+/*
+ * see if a cycle will will be created by inserting acyclic tree B in acyclic
+ * tree A at the topmost level (ie: as a direct child of A)
+ * - since we are adding B to A at the top level, checking for cycles should
+ *   just be a matter of seeing if node A is somewhere in tree B
+ */
+static int keyring_detect_cycle(struct key *A, struct key *B)
+{
+	struct {
+		struct key *subtree;
+		int kix;
+	} stack[KEYRING_SEARCH_MAX_DEPTH];
+
+	struct keyring_list *keylist;
+	struct key *subtree, *key;
+	int sp, kix, ret;
+
+	ret = -EDEADLK;
+	if (A == B)
+		goto error;
+
+	subtree = B;
+	sp = 0;
+
+	/* start processing a new keyring */
+ descend:
+	read_lock(&subtree->lock);
+	if (subtree->flags & KEY_FLAG_REVOKED)
+		goto not_this_keyring;
+
+	keylist = subtree->payload.subscriptions;
+	if (!keylist)
+		goto not_this_keyring;
+	kix = 0;
+
+ ascend:
+	/* iterate through the remaining keys in this keyring */
+	for (; kix < keylist->nkeys; kix++) {
+		key = keylist->keys[kix];
+
+		if (key == A)
+			goto cycle_detected;
+
+		/* recursively check nested keyrings */
+		if (key->type == &key_type_keyring) {
+			if (sp >= KEYRING_SEARCH_MAX_DEPTH)
+				goto too_deep;
+
+			/* stack the current position */
+			stack[sp].subtree = subtree;
+			stack[sp].kix = kix;
+			sp++;
+
+			/* begin again with the new keyring */
+			subtree = key;
+			goto descend;
+		}
+	}
+
+	/* the keyring we're looking at was disqualified or didn't contain a
+	 * matching key */
+ not_this_keyring:
+	read_unlock(&subtree->lock);
+
+	if (sp > 0) {
+		/* resume the checking of a keyring higher up in the tree */
+		sp--;
+		subtree = stack[sp].subtree;
+		keylist = subtree->payload.subscriptions;
+		kix = stack[sp].kix + 1;
+		goto ascend;
+	}
+
+	ret = 0; /* no cycles detected */
+
+ error:
+	return ret;
+
+ too_deep:
+	ret = -ELOOP;
+	goto error_unwind;
+ cycle_detected:
+	ret = -EDEADLK;
+ error_unwind:
+	read_unlock(&subtree->lock);
+
+	/* unwind the keyring stack */
+	while (sp > 0) {
+		sp--;
+		read_unlock(&stack[sp].subtree->lock);
+	}
+
+	goto error;
+
+} /* end keyring_detect_cycle() */
+
+/*****************************************************************************/
+/*
+ * link a key into to a keyring
+ * - must be called with the keyring's semaphore held
+ */
+int __key_link(struct key *keyring, struct key *key)
+{
+	struct keyring_list *klist, *nklist;
+	unsigned max;
+	size_t size;
+	int ret;
+
+	ret = -EKEYREVOKED;
+	if (keyring->flags & KEY_FLAG_REVOKED)
+		goto error;
+
+	ret = -ENOTDIR;
+	if (keyring->type != &key_type_keyring)
+		goto error;
+
+	/* serialise link/link calls to prevent parallel calls causing a
+	 * cycle when applied to two keyring in opposite orders */
+	down_write(&keyring_serialise_link_sem);
+
+	/* check that we aren't going to create a cycle adding one keyring to
+	 * another */
+	if (key->type == &key_type_keyring) {
+		ret = keyring_detect_cycle(keyring, key);
+		if (ret < 0)
+			goto error2;
+	}
+
+	/* check that we aren't going to overrun the user's quota */
+	ret = key_payload_reserve(keyring,
+				  keyring->datalen + KEYQUOTA_LINK_BYTES);
+	if (ret < 0)
+		goto error2;
+
+	klist = keyring->payload.subscriptions;
+
+	if (klist && klist->nkeys < klist->maxkeys) {
+		/* there's sufficient slack space to add directly */
+		atomic_inc(&key->usage);
+
+		write_lock(&keyring->lock);
+		klist->keys[klist->nkeys++] = key;
+		write_unlock(&keyring->lock);
+
+		ret = 0;
+	}
+	else {
+		/* grow the key list */
+		max = 4;
+		if (klist)
+			max += klist->maxkeys;
+
+		ret = -ENFILE;
+		size = sizeof(*klist) + sizeof(*key) * max;
+		if (size > PAGE_SIZE)
+			goto error3;
+
+		ret = -ENOMEM;
+		nklist = kmalloc(size, GFP_KERNEL);
+		if (!nklist)
+			goto error3;
+		nklist->maxkeys = max;
+		nklist->nkeys = 0;
+
+		if (klist) {
+			nklist->nkeys = klist->nkeys;
+			memcpy(nklist->keys,
+			       klist->keys,
+			       sizeof(struct key *) * klist->nkeys);
+		}
+
+		/* add the key into the new space */
+		atomic_inc(&key->usage);
+
+		write_lock(&keyring->lock);
+		keyring->payload.subscriptions = nklist;
+		nklist->keys[nklist->nkeys++] = key;
+		write_unlock(&keyring->lock);
+
+		/* dispose of the old keyring list */
+		kfree(klist);
+
+		ret = 0;
+	}
+
+ error2:
+	up_write(&keyring_serialise_link_sem);
+ error:
+	return ret;
+
+ error3:
+	/* undo the quota changes */
+	key_payload_reserve(keyring,
+			    keyring->datalen - KEYQUOTA_LINK_BYTES);
+	goto error2;
+
+} /* end __key_link() */
+
+/*****************************************************************************/
+/*
+ * link a key to a keyring
+ */
+int key_link(struct key *keyring, struct key *key)
+{
+	int ret;
+
+	key_check(keyring);
+	key_check(key);
+
+	down_write(&keyring->sem);
+	ret = __key_link(keyring, key);
+	up_write(&keyring->sem);
+
+	return ret;
+
+} /* end key_link() */
+
+EXPORT_SYMBOL(key_link);
+
+/*****************************************************************************/
+/*
+ * unlink the first link to a key from a keyring
+ */
+int key_unlink(struct key *keyring, struct key *key)
+{
+	struct keyring_list *klist;
+	int loop, ret;
+
+	key_check(keyring);
+	key_check(key);
+
+	ret = -ENOTDIR;
+	if (keyring->type != &key_type_keyring)
+		goto error;
+
+	down_write(&keyring->sem);
+
+	klist = keyring->payload.subscriptions;
+	if (klist) {
+		/* search the keyring for the key */
+		for (loop = 0; loop < klist->nkeys; loop++)
+			if (klist->keys[loop] == key)
+				goto key_is_present;
+	}
+
+	up_write(&keyring->sem);
+	ret = -ENOENT;
+	goto error;
+
+ key_is_present:
+	/* adjust the user's quota */
+	key_payload_reserve(keyring,
+			    keyring->datalen - KEYQUOTA_LINK_BYTES);
+
+	/* shuffle down the key pointers
+	 * - it might be worth shrinking the allocated memory, but that runs
+	 *   the risk of ENOMEM as we would have to copy
+	 */
+	write_lock(&keyring->lock);
+
+	klist->nkeys--;
+	if (loop < klist->nkeys)
+		memcpy(&klist->keys[loop],
+		       &klist->keys[loop + 1],
+		       (klist->nkeys - loop) * sizeof(struct key *));
+
+	write_unlock(&keyring->lock);
+
+	up_write(&keyring->sem);
+	key_put(key);
+	ret = 0;
+
+ error:
+	return ret;
+
+} /* end key_unlink() */
+
+EXPORT_SYMBOL(key_unlink);
+
+/*****************************************************************************/
+/*
+ * clear the specified process keyring
+ * - implements keyctl(KEYCTL_CLEAR)
+ */
+int keyring_clear(struct key *keyring)
+{
+	struct keyring_list *klist;
+	int loop, ret;
+
+	ret = -ENOTDIR;
+	if (keyring->type == &key_type_keyring) {
+		/* detach the pointer block with the locks held */
+		down_write(&keyring->sem);
+
+		klist = keyring->payload.subscriptions;
+		if (klist) {
+			/* adjust the quota */
+			key_payload_reserve(keyring,
+					    sizeof(struct keyring_list));
+
+			write_lock(&keyring->lock);
+			keyring->payload.subscriptions = NULL;
+			write_unlock(&keyring->lock);
+		}
+
+		up_write(&keyring->sem);
+
+		/* free the keys after the locks have been dropped */
+		if (klist) {
+			for (loop = klist->nkeys - 1; loop >= 0; loop--)
+				key_put(klist->keys[loop]);
+
+			kfree(klist);
+		}
+
+		ret = 0;
+	}
+
+	return ret;
+
+} /* end keyring_clear() */
+
+EXPORT_SYMBOL(keyring_clear);
diff --git a/security/keys/proc.c b/security/keys/proc.c
new file mode 100644
index 0000000..91343b8
--- /dev/null
+++ b/security/keys/proc.c
@@ -0,0 +1,251 @@
+/* proc.c: proc files for key database enumeration
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@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; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <asm/errno.h>
+#include "internal.h"
+
+#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS
+static int proc_keys_open(struct inode *inode, struct file *file);
+static void *proc_keys_start(struct seq_file *p, loff_t *_pos);
+static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos);
+static void proc_keys_stop(struct seq_file *p, void *v);
+static int proc_keys_show(struct seq_file *m, void *v);
+
+static struct seq_operations proc_keys_ops = {
+	.start	= proc_keys_start,
+	.next	= proc_keys_next,
+	.stop	= proc_keys_stop,
+	.show	= proc_keys_show,
+};
+
+static struct file_operations proc_keys_fops = {
+	.open		= proc_keys_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+#endif
+
+static int proc_key_users_open(struct inode *inode, struct file *file);
+static void *proc_key_users_start(struct seq_file *p, loff_t *_pos);
+static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos);
+static void proc_key_users_stop(struct seq_file *p, void *v);
+static int proc_key_users_show(struct seq_file *m, void *v);
+
+static struct seq_operations proc_key_users_ops = {
+	.start	= proc_key_users_start,
+	.next	= proc_key_users_next,
+	.stop	= proc_key_users_stop,
+	.show	= proc_key_users_show,
+};
+
+static struct file_operations proc_key_users_fops = {
+	.open		= proc_key_users_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+/*****************************************************************************/
+/*
+ * declare the /proc files
+ */
+static int __init key_proc_init(void)
+{
+	struct proc_dir_entry *p;
+
+#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS
+	p = create_proc_entry("keys", 0, NULL);
+	if (!p)
+		panic("Cannot create /proc/keys\n");
+
+	p->proc_fops = &proc_keys_fops;
+#endif
+
+	p = create_proc_entry("key-users", 0, NULL);
+	if (!p)
+		panic("Cannot create /proc/key-users\n");
+
+	p->proc_fops = &proc_key_users_fops;
+
+	return 0;
+
+} /* end key_proc_init() */
+
+__initcall(key_proc_init);
+
+/*****************************************************************************/
+/*
+ * implement "/proc/keys" to provides a list of the keys on the system
+ */
+#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS
+
+static int proc_keys_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &proc_keys_ops);
+
+}
+
+static void *proc_keys_start(struct seq_file *p, loff_t *_pos)
+{
+	struct rb_node *_p;
+	loff_t pos = *_pos;
+
+	spin_lock(&key_serial_lock);
+
+	_p = rb_first(&key_serial_tree);
+	while (pos > 0 && _p) {
+		pos--;
+		_p = rb_next(_p);
+	}
+
+	return _p;
+
+}
+
+static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos)
+{
+	(*_pos)++;
+	return rb_next((struct rb_node *) v);
+
+}
+
+static void proc_keys_stop(struct seq_file *p, void *v)
+{
+	spin_unlock(&key_serial_lock);
+}
+
+static int proc_keys_show(struct seq_file *m, void *v)
+{
+	struct rb_node *_p = v;
+	struct key *key = rb_entry(_p, struct key, serial_node);
+	struct timespec now;
+	unsigned long timo;
+	char xbuf[12];
+
+	now = current_kernel_time();
+
+	read_lock(&key->lock);
+
+	/* come up with a suitable timeout value */
+	if (key->expiry == 0) {
+		memcpy(xbuf, "perm", 5);
+	}
+	else if (now.tv_sec >= key->expiry) {
+		memcpy(xbuf, "expd", 5);
+	}
+	else {
+		timo = key->expiry - now.tv_sec;
+
+		if (timo < 60)
+			sprintf(xbuf, "%lus", timo);
+		else if (timo < 60*60)
+			sprintf(xbuf, "%lum", timo / 60);
+		else if (timo < 60*60*24)
+			sprintf(xbuf, "%luh", timo / (60*60));
+		else if (timo < 60*60*24*7)
+			sprintf(xbuf, "%lud", timo / (60*60*24));
+		else
+			sprintf(xbuf, "%luw", timo / (60*60*24*7));
+	}
+
+	seq_printf(m, "%08x %c%c%c%c%c%c %5d %4s %06x %5d %5d %-9.9s ",
+		   key->serial,
+		   key->flags & KEY_FLAG_INSTANTIATED	? 'I' : '-',
+		   key->flags & KEY_FLAG_REVOKED	? 'R' : '-',
+		   key->flags & KEY_FLAG_DEAD		? 'D' : '-',
+		   key->flags & KEY_FLAG_IN_QUOTA	? 'Q' : '-',
+		   key->flags & KEY_FLAG_USER_CONSTRUCT	? 'U' : '-',
+		   key->flags & KEY_FLAG_NEGATIVE	? 'N' : '-',
+		   atomic_read(&key->usage),
+		   xbuf,
+		   key->perm,
+		   key->uid,
+		   key->gid,
+		   key->type->name);
+
+	if (key->type->describe)
+		key->type->describe(key, m);
+	seq_putc(m, '\n');
+
+	read_unlock(&key->lock);
+
+	return 0;
+
+}
+
+#endif /* CONFIG_KEYS_DEBUG_PROC_KEYS */
+
+/*****************************************************************************/
+/*
+ * implement "/proc/key-users" to provides a list of the key users
+ */
+static int proc_key_users_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &proc_key_users_ops);
+
+}
+
+static void *proc_key_users_start(struct seq_file *p, loff_t *_pos)
+{
+	struct rb_node *_p;
+	loff_t pos = *_pos;
+
+	spin_lock(&key_user_lock);
+
+	_p = rb_first(&key_user_tree);
+	while (pos > 0 && _p) {
+		pos--;
+		_p = rb_next(_p);
+	}
+
+	return _p;
+
+}
+
+static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos)
+{
+	(*_pos)++;
+	return rb_next((struct rb_node *) v);
+
+}
+
+static void proc_key_users_stop(struct seq_file *p, void *v)
+{
+	spin_unlock(&key_user_lock);
+}
+
+static int proc_key_users_show(struct seq_file *m, void *v)
+{
+	struct rb_node *_p = v;
+	struct key_user *user = rb_entry(_p, struct key_user, node);
+
+	seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n",
+		   user->uid,
+		   atomic_read(&user->usage),
+		   atomic_read(&user->nkeys),
+		   atomic_read(&user->nikeys),
+		   user->qnkeys,
+		   KEYQUOTA_MAX_KEYS,
+		   user->qnbytes,
+		   KEYQUOTA_MAX_BYTES
+		   );
+
+	return 0;
+
+}
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
new file mode 100644
index 0000000..2eb0e47
--- /dev/null
+++ b/security/keys/process_keys.c
@@ -0,0 +1,665 @@
+/* process_keys.c: management of a process's keyrings
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@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; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/keyctl.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <asm/uaccess.h>
+#include "internal.h"
+
+/* session keyring create vs join semaphore */
+static DECLARE_MUTEX(key_session_sem);
+
+/* the root user's tracking struct */
+struct key_user root_key_user = {
+	.usage		= ATOMIC_INIT(3),
+	.consq		= LIST_HEAD_INIT(root_key_user.consq),
+	.lock		= SPIN_LOCK_UNLOCKED,
+	.nkeys		= ATOMIC_INIT(2),
+	.nikeys		= ATOMIC_INIT(2),
+	.uid		= 0,
+};
+
+/* the root user's UID keyring */
+struct key root_user_keyring = {
+	.usage		= ATOMIC_INIT(1),
+	.serial		= 2,
+	.type		= &key_type_keyring,
+	.user		= &root_key_user,
+	.lock		= RW_LOCK_UNLOCKED,
+	.sem		= __RWSEM_INITIALIZER(root_user_keyring.sem),
+	.perm		= KEY_USR_ALL,
+	.flags		= KEY_FLAG_INSTANTIATED,
+	.description	= "_uid.0",
+#ifdef KEY_DEBUGGING
+	.magic		= KEY_DEBUG_MAGIC,
+#endif
+};
+
+/* the root user's default session keyring */
+struct key root_session_keyring = {
+	.usage		= ATOMIC_INIT(1),
+	.serial		= 1,
+	.type		= &key_type_keyring,
+	.user		= &root_key_user,
+	.lock		= RW_LOCK_UNLOCKED,
+	.sem		= __RWSEM_INITIALIZER(root_session_keyring.sem),
+	.perm		= KEY_USR_ALL,
+	.flags		= KEY_FLAG_INSTANTIATED,
+	.description	= "_uid_ses.0",
+#ifdef KEY_DEBUGGING
+	.magic		= KEY_DEBUG_MAGIC,
+#endif
+};
+
+/*****************************************************************************/
+/*
+ * allocate the keyrings to be associated with a UID
+ */
+int alloc_uid_keyring(struct user_struct *user)
+{
+	struct key *uid_keyring, *session_keyring;
+	char buf[20];
+	int ret;
+
+	/* concoct a default session keyring */
+	sprintf(buf, "_uid_ses.%u", user->uid);
+
+	session_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, 0, NULL);
+	if (IS_ERR(session_keyring)) {
+		ret = PTR_ERR(session_keyring);
+		goto error;
+	}
+
+	/* and a UID specific keyring, pointed to by the default session
+	 * keyring */
+	sprintf(buf, "_uid.%u", user->uid);
+
+	uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, 0,
+				    session_keyring);
+	if (IS_ERR(uid_keyring)) {
+		key_put(session_keyring);
+		ret = PTR_ERR(uid_keyring);
+		goto error;
+	}
+
+	/* install the keyrings */
+	user->uid_keyring = uid_keyring;
+	user->session_keyring = session_keyring;
+	ret = 0;
+
+ error:
+	return ret;
+
+} /* end alloc_uid_keyring() */
+
+/*****************************************************************************/
+/*
+ * deal with the UID changing
+ */
+void switch_uid_keyring(struct user_struct *new_user)
+{
+#if 0 /* do nothing for now */
+	struct key *old;
+
+	/* switch to the new user's session keyring if we were running under
+	 * root's default session keyring */
+	if (new_user->uid != 0 &&
+	    current->session_keyring == &root_session_keyring
+	    ) {
+		atomic_inc(&new_user->session_keyring->usage);
+
+		task_lock(current);
+		old = current->session_keyring;
+		current->session_keyring = new_user->session_keyring;
+		task_unlock(current);
+
+		key_put(old);
+	}
+#endif
+
+} /* end switch_uid_keyring() */
+
+/*****************************************************************************/
+/*
+ * install a fresh thread keyring, discarding the old one
+ */
+int install_thread_keyring(struct task_struct *tsk)
+{
+	struct key *keyring, *old;
+	char buf[20];
+	int ret;
+
+	sprintf(buf, "_tid.%u", tsk->pid);
+
+	keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL);
+	if (IS_ERR(keyring)) {
+		ret = PTR_ERR(keyring);
+		goto error;
+	}
+
+	task_lock(tsk);
+	old = tsk->thread_keyring;
+	tsk->thread_keyring = keyring;
+	task_unlock(tsk);
+
+	ret = 0;
+
+	key_put(old);
+ error:
+	return ret;
+
+} /* end install_thread_keyring() */
+
+/*****************************************************************************/
+/*
+ * make sure a process keyring is installed
+ */
+static int install_process_keyring(struct task_struct *tsk)
+{
+	unsigned long flags;
+	struct key *keyring;
+	char buf[20];
+	int ret;
+
+	if (!tsk->signal->process_keyring) {
+		sprintf(buf, "_pid.%u", tsk->tgid);
+
+		keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL);
+		if (IS_ERR(keyring)) {
+			ret = PTR_ERR(keyring);
+			goto error;
+		}
+
+		/* attach or swap keyrings */
+		spin_lock_irqsave(&tsk->sighand->siglock, flags);
+		if (!tsk->signal->process_keyring) {
+			tsk->signal->process_keyring = keyring;
+			keyring = NULL;
+		}
+		spin_unlock_irqrestore(&tsk->sighand->siglock, flags);
+
+		key_put(keyring);
+	}
+
+	ret = 0;
+ error:
+	return ret;
+
+} /* end install_process_keyring() */
+
+/*****************************************************************************/
+/*
+ * install a session keyring, discarding the old one
+ * - if a keyring is not supplied, an empty one is invented
+ */
+static int install_session_keyring(struct task_struct *tsk,
+				   struct key *keyring)
+{
+	unsigned long flags;
+	struct key *old;
+	char buf[20];
+	int ret;
+
+	/* create an empty session keyring */
+	if (!keyring) {
+		sprintf(buf, "_ses.%u", tsk->tgid);
+
+		keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL);
+		if (IS_ERR(keyring)) {
+			ret = PTR_ERR(keyring);
+			goto error;
+		}
+	}
+	else {
+		atomic_inc(&keyring->usage);
+	}
+
+	/* install the keyring */
+	spin_lock_irqsave(&tsk->sighand->siglock, flags);
+	old = tsk->signal->session_keyring;
+	tsk->signal->session_keyring = keyring;
+	spin_unlock_irqrestore(&tsk->sighand->siglock, flags);
+
+	ret = 0;
+
+	key_put(old);
+ error:
+	return ret;
+
+} /* end install_session_keyring() */
+
+/*****************************************************************************/
+/*
+ * copy the keys in a thread group for fork without CLONE_THREAD
+ */
+int copy_thread_group_keys(struct task_struct *tsk)
+{
+	unsigned long flags;
+
+	key_check(current->thread_group->session_keyring);
+	key_check(current->thread_group->process_keyring);
+
+	/* no process keyring yet */
+	tsk->signal->process_keyring = NULL;
+
+	/* same session keyring */
+	spin_lock_irqsave(&current->sighand->siglock, flags);
+	tsk->signal->session_keyring =
+		key_get(current->signal->session_keyring);
+	spin_unlock_irqrestore(&current->sighand->siglock, flags);
+
+	return 0;
+
+} /* end copy_thread_group_keys() */
+
+/*****************************************************************************/
+/*
+ * copy the keys for fork
+ */
+int copy_keys(unsigned long clone_flags, struct task_struct *tsk)
+{
+	key_check(tsk->thread_keyring);
+
+	/* no thread keyring yet */
+	tsk->thread_keyring = NULL;
+	return 0;
+
+} /* end copy_keys() */
+
+/*****************************************************************************/
+/*
+ * dispose of thread group keys upon thread group destruction
+ */
+void exit_thread_group_keys(struct signal_struct *tg)
+{
+	key_put(tg->session_keyring);
+	key_put(tg->process_keyring);
+
+} /* end exit_thread_group_keys() */
+
+/*****************************************************************************/
+/*
+ * dispose of keys upon thread exit
+ */
+void exit_keys(struct task_struct *tsk)
+{
+	key_put(tsk->thread_keyring);
+
+} /* end exit_keys() */
+
+/*****************************************************************************/
+/*
+ * deal with execve()
+ */
+int exec_keys(struct task_struct *tsk)
+{
+	unsigned long flags;
+	struct key *old;
+
+	/* newly exec'd tasks don't get a thread keyring */
+	task_lock(tsk);
+	old = tsk->thread_keyring;
+	tsk->thread_keyring = NULL;
+	task_unlock(tsk);
+
+	key_put(old);
+
+	/* discard the process keyring from a newly exec'd task */
+	spin_lock_irqsave(&tsk->sighand->siglock, flags);
+	old = tsk->signal->process_keyring;
+	tsk->signal->process_keyring = NULL;
+	spin_unlock_irqrestore(&tsk->sighand->siglock, flags);
+
+	key_put(old);
+
+	return 0;
+
+} /* end exec_keys() */
+
+/*****************************************************************************/
+/*
+ * deal with SUID programs
+ * - we might want to make this invent a new session keyring
+ */
+int suid_keys(struct task_struct *tsk)
+{
+	return 0;
+
+} /* end suid_keys() */
+
+/*****************************************************************************/
+/*
+ * the filesystem user ID changed
+ */
+void key_fsuid_changed(struct task_struct *tsk)
+{
+	/* update the ownership of the thread keyring */
+	if (tsk->thread_keyring) {
+		down_write(&tsk->thread_keyring->sem);
+		write_lock(&tsk->thread_keyring->lock);
+		tsk->thread_keyring->uid = tsk->fsuid;
+		write_unlock(&tsk->thread_keyring->lock);
+		up_write(&tsk->thread_keyring->sem);
+	}
+
+} /* end key_fsuid_changed() */
+
+/*****************************************************************************/
+/*
+ * the filesystem group ID changed
+ */
+void key_fsgid_changed(struct task_struct *tsk)
+{
+	/* update the ownership of the thread keyring */
+	if (tsk->thread_keyring) {
+		down_write(&tsk->thread_keyring->sem);
+		write_lock(&tsk->thread_keyring->lock);
+		tsk->thread_keyring->gid = tsk->fsgid;
+		write_unlock(&tsk->thread_keyring->lock);
+		up_write(&tsk->thread_keyring->sem);
+	}
+
+} /* end key_fsgid_changed() */
+
+/*****************************************************************************/
+/*
+ * search the process keyrings for the first matching key
+ * - we use the supplied match function to see if the description (or other
+ *   feature of interest) matches
+ * - we return -EAGAIN if we didn't find any matching key
+ * - we return -ENOKEY if we found only negative matching keys
+ */
+struct key *search_process_keyrings_aux(struct key_type *type,
+					const void *description,
+					key_match_func_t match)
+{
+	struct task_struct *tsk = current;
+	unsigned long flags;
+	struct key *key, *ret, *err, *tmp;
+
+	/* we want to return -EAGAIN or -ENOKEY if any of the keyrings were
+	 * searchable, but we failed to find a key or we found a negative key;
+	 * otherwise we want to return a sample error (probably -EACCES) if
+	 * none of the keyrings were searchable
+	 *
+	 * in terms of priority: success > -ENOKEY > -EAGAIN > other error
+	 */
+	key = NULL;
+	ret = NULL;
+	err = ERR_PTR(-EAGAIN);
+
+	/* search the thread keyring first */
+	if (tsk->thread_keyring) {
+		key = keyring_search_aux(tsk->thread_keyring, type,
+					 description, match);
+		if (!IS_ERR(key))
+			goto found;
+
+		switch (PTR_ERR(key)) {
+		case -EAGAIN: /* no key */
+			if (ret)
+				break;
+		case -ENOKEY: /* negative key */
+			ret = key;
+			break;
+		default:
+			err = key;
+			break;
+		}
+	}
+
+	/* search the process keyring second */
+	if (tsk->signal->process_keyring) {
+		key = keyring_search_aux(tsk->signal->process_keyring,
+					 type, description, match);
+		if (!IS_ERR(key))
+			goto found;
+
+		switch (PTR_ERR(key)) {
+		case -EAGAIN: /* no key */
+			if (ret)
+				break;
+		case -ENOKEY: /* negative key */
+			ret = key;
+			break;
+		default:
+			err = key;
+			break;
+		}
+	}
+
+	/* search the session keyring last */
+	spin_lock_irqsave(&tsk->sighand->siglock, flags);
+
+	tmp = tsk->signal->session_keyring;
+	if (!tmp)
+		tmp = tsk->user->session_keyring;
+	atomic_inc(&tmp->usage);
+
+	spin_unlock_irqrestore(&tsk->sighand->siglock, flags);
+
+	key = keyring_search_aux(tmp, type, description, match);
+	key_put(tmp);
+	if (!IS_ERR(key))
+		goto found;
+
+	switch (PTR_ERR(key)) {
+	case -EAGAIN: /* no key */
+		if (ret)
+			break;
+	case -ENOKEY: /* negative key */
+		ret = key;
+		break;
+	default:
+		err = key;
+		break;
+	}
+
+	/* no key - decide on the error we're going to go for */
+	key = ret ? ret : err;
+
+ found:
+	return key;
+
+} /* end search_process_keyrings_aux() */
+
+/*****************************************************************************/
+/*
+ * search the process keyrings for the first matching key
+ * - we return -EAGAIN if we didn't find any matching key
+ * - we return -ENOKEY if we found only negative matching keys
+ */
+struct key *search_process_keyrings(struct key_type *type,
+				    const char *description)
+{
+	return search_process_keyrings_aux(type, description, type->match);
+
+} /* end search_process_keyrings() */
+
+/*****************************************************************************/
+/*
+ * lookup a key given a key ID from userspace with a given permissions mask
+ * - don't create special keyrings unless so requested
+ * - partially constructed keys aren't found unless requested
+ */
+struct key *lookup_user_key(key_serial_t id, int create, int partial,
+			    key_perm_t perm)
+{
+	struct task_struct *tsk = current;
+	unsigned long flags;
+	struct key *key;
+	int ret;
+
+	key = ERR_PTR(-ENOKEY);
+
+	switch (id) {
+	case KEY_SPEC_THREAD_KEYRING:
+		if (!tsk->thread_keyring) {
+			if (!create)
+				goto error;
+
+			ret = install_thread_keyring(tsk);
+			if (ret < 0) {
+				key = ERR_PTR(ret);
+				goto error;
+			}
+		}
+
+		key = tsk->thread_keyring;
+		atomic_inc(&key->usage);
+		break;
+
+	case KEY_SPEC_PROCESS_KEYRING:
+		if (!tsk->signal->process_keyring) {
+			if (!create)
+				goto error;
+
+			ret = install_process_keyring(tsk);
+			if (ret < 0) {
+				key = ERR_PTR(ret);
+				goto error;
+			}
+		}
+
+		key = tsk->signal->process_keyring;
+		atomic_inc(&key->usage);
+		break;
+
+	case KEY_SPEC_SESSION_KEYRING:
+		if (!tsk->signal->session_keyring) {
+			/* always install a session keyring upon access if one
+			 * doesn't exist yet */
+			ret = install_session_keyring(
+			       tsk, tsk->user->session_keyring);
+			if (ret < 0)
+				goto error;
+		}
+
+		spin_lock_irqsave(&tsk->sighand->siglock, flags);
+		key = tsk->signal->session_keyring;
+		atomic_inc(&key->usage);
+		spin_unlock_irqrestore(&tsk->sighand->siglock, flags);
+		break;
+
+	case KEY_SPEC_USER_KEYRING:
+		key = tsk->user->uid_keyring;
+		atomic_inc(&key->usage);
+		break;
+
+	case KEY_SPEC_USER_SESSION_KEYRING:
+		key = tsk->user->session_keyring;
+		atomic_inc(&key->usage);
+		break;
+
+	case KEY_SPEC_GROUP_KEYRING:
+		/* group keyrings are not yet supported */
+		key = ERR_PTR(-EINVAL);
+		goto error;
+
+	default:
+		key = ERR_PTR(-EINVAL);
+		if (id < 1)
+			goto error;
+
+		key = key_lookup(id);
+		if (IS_ERR(key))
+			goto error;
+		break;
+	}
+
+	/* check the status and permissions */
+	if (perm) {
+		ret = key_validate(key);
+		if (ret < 0)
+			goto invalid_key;
+	}
+
+	ret = -EIO;
+	if (!partial && !(key->flags & KEY_FLAG_INSTANTIATED))
+		goto invalid_key;
+
+	ret = -EACCES;
+	if (!key_permission(key, perm))
+		goto invalid_key;
+
+ error:
+	return key;
+
+ invalid_key:
+	key_put(key);
+	key = ERR_PTR(ret);
+	goto error;
+
+} /* end lookup_user_key() */
+
+/*****************************************************************************/
+/*
+ * join the named keyring as the session keyring if possible, or attempt to
+ * create a new one of that name if not
+ * - if the name is NULL, an empty anonymous keyring is installed instead
+ * - named session keyring joining is done with a semaphore held
+ */
+long join_session_keyring(const char *name)
+{
+	struct task_struct *tsk = current;
+	unsigned long flags;
+	struct key *keyring;
+	long ret;
+
+	/* if no name is provided, install an anonymous keyring */
+	if (!name) {
+		ret = install_session_keyring(tsk, NULL);
+		if (ret < 0)
+			goto error;
+
+		spin_lock_irqsave(&tsk->sighand->siglock, flags);
+		ret = tsk->signal->session_keyring->serial;
+		spin_unlock_irqrestore(&tsk->sighand->siglock, flags);
+		goto error;
+	}
+
+	/* allow the user to join or create a named keyring */
+	down(&key_session_sem);
+
+	/* look for an existing keyring of this name */
+	keyring = find_keyring_by_name(name, 0);
+	if (PTR_ERR(keyring) == -ENOKEY) {
+		/* not found - try and create a new one */
+		keyring = keyring_alloc(name, tsk->uid, tsk->gid, 0, NULL);
+		if (IS_ERR(keyring)) {
+			ret = PTR_ERR(keyring);
+			goto error;
+		}
+	}
+	else if (IS_ERR(keyring)) {
+		ret = PTR_ERR(keyring);
+		goto error2;
+	}
+
+	/* we've got a keyring - now to install it */
+	ret = install_session_keyring(tsk, keyring);
+	if (ret < 0)
+		goto error2;
+
+	ret = keyring->serial;
+	key_put(keyring);
+
+ error2:
+	up(&key_session_sem);
+ error:
+	return ret;
+
+} /* end join_session_keyring() */
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
new file mode 100644
index 0000000..9705b1a
--- /dev/null
+++ b/security/keys/request_key.c
@@ -0,0 +1,359 @@
+/* request_key.c: request a key from userspace
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@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; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kmod.h>
+#include <linux/err.h>
+#include "internal.h"
+
+struct key_construction {
+	struct list_head	link;	/* link in construction queue */
+	struct key		*key;	/* key being constructed */
+};
+
+/* when waiting for someone else's keys, you get added to this */
+DECLARE_WAIT_QUEUE_HEAD(request_key_conswq);
+
+/*****************************************************************************/
+/*
+ * request userspace finish the construction of a key
+ * - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring> <info>"
+ * - if callout_info is an empty string, it'll be rendered as a "-" instead
+ */
+static int call_request_key(struct key *key,
+			    const char *op,
+			    const char *callout_info)
+{
+	struct task_struct *tsk = current;
+	unsigned long flags;
+	key_serial_t prkey, sskey;
+	char *argv[10], *envp[3], uid_str[12], gid_str[12];
+	char key_str[12], keyring_str[3][12];
+	int i;
+
+	/* record the UID and GID */
+	sprintf(uid_str, "%d", current->fsuid);
+	sprintf(gid_str, "%d", current->fsgid);
+
+	/* we say which key is under construction */
+	sprintf(key_str, "%d", key->serial);
+
+	/* we specify the process's default keyrings */
+	sprintf(keyring_str[0], "%d",
+		tsk->thread_keyring ? tsk->thread_keyring->serial : 0);
+
+	prkey = 0;
+	if (tsk->signal->process_keyring)
+		prkey = tsk->signal->process_keyring->serial;
+
+	sskey = 0;
+	spin_lock_irqsave(&tsk->sighand->siglock, flags);
+	if (tsk->signal->session_keyring)
+		sskey = tsk->signal->session_keyring->serial;
+	spin_unlock_irqrestore(&tsk->sighand->siglock, flags);
+
+
+	if (!sskey)
+		sskey = tsk->user->session_keyring->serial;
+
+	sprintf(keyring_str[1], "%d", prkey);
+	sprintf(keyring_str[2], "%d", sskey);
+
+	/* set up a minimal environment */
+	i = 0;
+	envp[i++] = "HOME=/";
+	envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+	envp[i] = NULL;
+
+	/* set up the argument list */
+	i = 0;
+	argv[i++] = "/sbin/request-key";
+	argv[i++] = (char *) op;
+	argv[i++] = key_str;
+	argv[i++] = uid_str;
+	argv[i++] = gid_str;
+	argv[i++] = keyring_str[0];
+	argv[i++] = keyring_str[1];
+	argv[i++] = keyring_str[2];
+	argv[i++] = callout_info[0] ? (char *) callout_info : "-";
+	argv[i] = NULL;
+
+	/* do it */
+	return call_usermodehelper(argv[0], argv, envp, 1);
+
+} /* end call_request_key() */
+
+/*****************************************************************************/
+/*
+ * call out to userspace for the key
+ * - called with the construction sem held, but the sem is dropped here
+ * - we ignore program failure and go on key status instead
+ */
+static struct key *__request_key_construction(struct key_type *type,
+					      const char *description,
+					      const char *callout_info)
+{
+	struct key_construction cons;
+	struct timespec now;
+	struct key *key;
+	int ret, negative;
+
+	/* create a key and add it to the queue */
+	key = key_alloc(type, description,
+			current->fsuid, current->fsgid, KEY_USR_ALL, 0);
+	if (IS_ERR(key))
+		goto alloc_failed;
+
+	write_lock(&key->lock);
+	key->flags |= KEY_FLAG_USER_CONSTRUCT;
+	write_unlock(&key->lock);
+
+	cons.key = key;
+	list_add_tail(&cons.link, &key->user->consq);
+
+	/* we drop the construction sem here on behalf of the caller */
+	up_write(&key_construction_sem);
+
+	/* make the call */
+	ret = call_request_key(key, "create", callout_info);
+	if (ret < 0)
+		goto request_failed;
+
+	/* if the key wasn't instantiated, then we want to give an error */
+	ret = -ENOKEY;
+	if (!(key->flags & KEY_FLAG_INSTANTIATED))
+		goto request_failed;
+
+	down_write(&key_construction_sem);
+	list_del(&cons.link);
+	up_write(&key_construction_sem);
+
+	/* also give an error if the key was negatively instantiated */
+ check_not_negative:
+	if (key->flags & KEY_FLAG_NEGATIVE) {
+		key_put(key);
+		key = ERR_PTR(-ENOKEY);
+	}
+
+ out:
+	return key;
+
+ request_failed:
+	/* it wasn't instantiated
+	 * - remove from construction queue
+	 * - mark the key as dead
+	 */
+	negative = 0;
+	down_write(&key_construction_sem);
+
+	list_del(&cons.link);
+
+	write_lock(&key->lock);
+	key->flags &= ~KEY_FLAG_USER_CONSTRUCT;
+
+	/* check it didn't get instantiated between the check and the down */
+	if (!(key->flags & KEY_FLAG_INSTANTIATED)) {
+		key->flags |= KEY_FLAG_INSTANTIATED | KEY_FLAG_NEGATIVE;
+		negative = 1;
+	}
+
+	write_unlock(&key->lock);
+	up_write(&key_construction_sem);
+
+	if (!negative)
+		goto check_not_negative; /* surprisingly, the key got
+					  * instantiated */
+
+	/* set the timeout and store in the session keyring if we can */
+	now = current_kernel_time();
+	key->expiry = now.tv_sec + key_negative_timeout;
+
+	if (current->signal->session_keyring) {
+		unsigned long flags;
+		struct key *keyring;
+
+		spin_lock_irqsave(&current->sighand->siglock, flags);
+		keyring = current->signal->session_keyring;
+		atomic_inc(&keyring->usage);
+		spin_unlock_irqrestore(&current->sighand->siglock, flags);
+
+		key_link(keyring, key);
+		key_put(keyring);
+	}
+
+	key_put(key);
+
+	/* notify anyone who was waiting */
+	wake_up_all(&request_key_conswq);
+
+	key = ERR_PTR(ret);
+	goto out;
+
+ alloc_failed:
+	up_write(&key_construction_sem);
+	goto out;
+
+} /* end __request_key_construction() */
+
+/*****************************************************************************/
+/*
+ * call out to userspace to request the key
+ * - we check the construction queue first to see if an appropriate key is
+ *   already being constructed by userspace
+ */
+static struct key *request_key_construction(struct key_type *type,
+					    const char *description,
+					    struct key_user *user,
+					    const char *callout_info)
+{
+	struct key_construction *pcons;
+	struct key *key, *ckey;
+
+	DECLARE_WAITQUEUE(myself, current);
+
+	/* see if there's such a key under construction already */
+	down_write(&key_construction_sem);
+
+	list_for_each_entry(pcons, &user->consq, link) {
+		ckey = pcons->key;
+
+		if (ckey->type != type)
+			continue;
+
+		if (type->match(ckey, description))
+			goto found_key_under_construction;
+	}
+
+	/* see about getting userspace to construct the key */
+	key = __request_key_construction(type, description, callout_info);
+ error:
+	return key;
+
+	/* someone else has the same key under construction
+	 * - we want to keep an eye on their key
+	 */
+ found_key_under_construction:
+	atomic_inc(&ckey->usage);
+	up_write(&key_construction_sem);
+
+	/* wait for the key to be completed one way or another */
+	add_wait_queue(&request_key_conswq, &myself);
+
+	for (;;) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		if (!(ckey->flags & KEY_FLAG_USER_CONSTRUCT))
+			break;
+		schedule();
+	}
+
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&request_key_conswq, &myself);
+
+	/* we'll need to search this process's keyrings to see if the key is
+	 * now there since we can't automatically assume it's also available
+	 * there */
+	key_put(ckey);
+	ckey = NULL;
+
+	key = NULL; /* request a retry */
+	goto error;
+
+} /* end request_key_construction() */
+
+/*****************************************************************************/
+/*
+ * request a key
+ * - search the process's keyrings
+ * - check the list of keys being created or updated
+ * - call out to userspace for a key if requested (supplementary info can be
+ *   passed)
+ */
+struct key *request_key(struct key_type *type,
+			const char *description,
+			const char *callout_info)
+{
+	struct key_user *user;
+	struct key *key;
+
+	/* search all the process keyrings for a key */
+	key = search_process_keyrings_aux(type, description, type->match);
+
+	if (PTR_ERR(key) == -EAGAIN) {
+		/* the search failed, but the keyrings were searchable, so we
+		 * should consult userspace if we can */
+		key = ERR_PTR(-ENOKEY);
+		if (!callout_info)
+			goto error;
+
+		/* - get hold of the user's construction queue */
+		user = key_user_lookup(current->fsuid);
+		if (!user) {
+			key = ERR_PTR(-ENOMEM);
+			goto error;
+		}
+
+		for (;;) {
+			/* ask userspace (returns NULL if it waited on a key
+			 * being constructed) */
+			key = request_key_construction(type, description,
+						       user, callout_info);
+			if (key)
+				break;
+
+			/* someone else made the key we want, so we need to
+			 * search again as it might now be available to us */
+			key = search_process_keyrings_aux(type, description,
+							  type->match);
+			if (PTR_ERR(key) != -EAGAIN)
+				break;
+		}
+
+		key_user_put(user);
+	}
+
+ error:
+	return key;
+
+} /* end request_key() */
+
+EXPORT_SYMBOL(request_key);
+
+/*****************************************************************************/
+/*
+ * validate a key
+ */
+int key_validate(struct key *key)
+{
+	struct timespec now;
+	int ret = 0;
+
+	if (key) {
+		/* check it's still accessible */
+		ret = -EKEYREVOKED;
+		if (key->flags & (KEY_FLAG_REVOKED | KEY_FLAG_DEAD))
+			goto error;
+
+		/* check it hasn't expired */
+		ret = 0;
+		if (key->expiry) {
+			now = current_kernel_time();
+			if (now.tv_sec >= key->expiry)
+				ret = -EKEYEXPIRED;
+		}
+	}
+
+ error:
+	return ret;
+
+} /* end key_validate() */
+
+EXPORT_SYMBOL(key_validate);
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c
new file mode 100644
index 0000000..8d65b3a
--- /dev/null
+++ b/security/keys/user_defined.c
@@ -0,0 +1,191 @@
+/* user_defined.c: user defined key type
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@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; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <linux/err.h>
+#include <asm/uaccess.h>
+#include "internal.h"
+
+static int user_instantiate(struct key *key, const void *data, size_t datalen);
+static int user_duplicate(struct key *key, const struct key *source);
+static int user_update(struct key *key, const void *data, size_t datalen);
+static int user_match(const struct key *key, const void *criterion);
+static void user_destroy(struct key *key);
+static void user_describe(const struct key *user, struct seq_file *m);
+static long user_read(const struct key *key,
+		      char __user *buffer, size_t buflen);
+
+/*
+ * user defined keys take an arbitrary string as the description and an
+ * arbitrary blob of data as the payload
+ */
+struct key_type key_type_user = {
+	.name		= "user",
+	.instantiate	= user_instantiate,
+	.duplicate	= user_duplicate,
+	.update		= user_update,
+	.match		= user_match,
+	.destroy	= user_destroy,
+	.describe	= user_describe,
+	.read		= user_read,
+};
+
+/*****************************************************************************/
+/*
+ * instantiate a user defined key
+ */
+static int user_instantiate(struct key *key, const void *data, size_t datalen)
+{
+	int ret;
+
+	ret = -EINVAL;
+	if (datalen <= 0 || datalen > 32767 || !data)
+		goto error;
+
+	ret = key_payload_reserve(key, datalen);
+	if (ret < 0)
+		goto error;
+
+	/* attach the data */
+	ret = -ENOMEM;
+	key->payload.data = kmalloc(datalen, GFP_KERNEL);
+	if (!key->payload.data)
+		goto error;
+
+	memcpy(key->payload.data, data, datalen);
+	ret = 0;
+
+ error:
+	return ret;
+
+} /* end user_instantiate() */
+
+/*****************************************************************************/
+/*
+ * duplicate a user defined key
+ */
+static int user_duplicate(struct key *key, const struct key *source)
+{
+	int ret;
+
+	/* just copy the payload */
+	ret = -ENOMEM;
+	key->payload.data = kmalloc(source->datalen, GFP_KERNEL);
+
+	if (key->payload.data) {
+		key->datalen = source->datalen;
+		memcpy(key->payload.data, source->payload.data, source->datalen);
+		ret = 0;
+	}
+
+	return ret;
+
+} /* end user_duplicate() */
+
+/*****************************************************************************/
+/*
+ * update a user defined key
+ */
+static int user_update(struct key *key, const void *data, size_t datalen)
+{
+	void *new, *zap;
+	int ret;
+
+	ret = -EINVAL;
+	if (datalen <= 0 || datalen > 32767 || !data)
+		goto error;
+
+	/* copy the data */
+	ret = -ENOMEM;
+	new = kmalloc(datalen, GFP_KERNEL);
+	if (!new)
+		goto error;
+
+	memcpy(new, data, datalen);
+
+	/* check the quota and attach the new data */
+	zap = new;
+	write_lock(&key->lock);
+
+	ret = key_payload_reserve(key, datalen);
+
+	if (ret == 0) {
+		/* attach the new data, displacing the old */
+		zap = key->payload.data;
+		key->payload.data = new;
+		key->expiry = 0;
+	}
+
+	write_unlock(&key->lock);
+	kfree(zap);
+
+ error:
+	return ret;
+
+} /* end user_update() */
+
+/*****************************************************************************/
+/*
+ * match users on their name
+ */
+static int user_match(const struct key *key, const void *description)
+{
+	return strcmp(key->description, description) == 0;
+
+} /* end user_match() */
+
+/*****************************************************************************/
+/*
+ * dispose of the data dangling from the corpse of a user
+ */
+static void user_destroy(struct key *key)
+{
+	kfree(key->payload.data);
+
+} /* end user_destroy() */
+
+/*****************************************************************************/
+/*
+ * describe the user
+ */
+static void user_describe(const struct key *key, struct seq_file *m)
+{
+	seq_puts(m, key->description);
+
+	seq_printf(m, ": %u", key->datalen);
+
+} /* end user_describe() */
+
+/*****************************************************************************/
+/*
+ * read the key data
+ */
+static long user_read(const struct key *key,
+		      char __user *buffer, size_t buflen)
+{
+	long ret = key->datalen;
+
+	/* we can return the data as is */
+	if (buffer && buflen > 0) {
+		if (buflen > key->datalen)
+			buflen = key->datalen;
+
+		if (copy_to_user(buffer, key->payload.data, buflen) != 0)
+			ret = -EFAULT;
+	}
+
+	return ret;
+
+} /* end user_read() */
diff --git a/security/root_plug.c b/security/root_plug.c
new file mode 100644
index 0000000..07651de
--- /dev/null
+++ b/security/root_plug.c
@@ -0,0 +1,142 @@
+/*
+ * Root Plug sample LSM module
+ *
+ * Originally written for a Linux Journal.
+ *
+ * Copyright (C) 2002 Greg Kroah-Hartman <greg@kroah.com>
+ *
+ * Prevents any programs running with egid == 0 if a specific USB device
+ * is not present in the system.  Yes, it can be gotten around, but is a
+ * nice starting point for people to play with, and learn the LSM
+ * interface.
+ *
+ * If you want to turn this into something with a semblance of security,
+ * you need to hook the task_* functions also.
+ *
+ * See http://www.linuxjournal.com/article.php?sid=6279 for more information
+ * about this code.
+ *
+ *	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 of the
+ *	License.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/security.h>
+#include <linux/usb.h>
+
+/* flag to keep track of how we were registered */
+static int secondary;
+
+/* default is a generic type of usb to serial converter */
+static int vendor_id = 0x0557;
+static int product_id = 0x2008;
+
+module_param(vendor_id, uint, 0400);
+MODULE_PARM_DESC(vendor_id, "USB Vendor ID of device to look for");
+
+module_param(product_id, uint, 0400);
+MODULE_PARM_DESC(product_id, "USB Product ID of device to look for");
+
+/* should we print out debug messages */
+static int debug = 0;
+
+module_param(debug, bool, 0600);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+#if defined(CONFIG_SECURITY_ROOTPLUG_MODULE)
+#define MY_NAME THIS_MODULE->name
+#else
+#define MY_NAME "root_plug"
+#endif
+
+#define root_dbg(fmt, arg...)					\
+	do {							\
+		if (debug)					\
+			printk(KERN_DEBUG "%s: %s: " fmt ,	\
+				MY_NAME , __FUNCTION__ , 	\
+				## arg);			\
+	} while (0)
+
+static int rootplug_bprm_check_security (struct linux_binprm *bprm)
+{
+	struct usb_device *dev;
+
+	root_dbg("file %s, e_uid = %d, e_gid = %d\n",
+		 bprm->filename, bprm->e_uid, bprm->e_gid);
+
+	if (bprm->e_gid == 0) {
+		dev = usb_find_device(vendor_id, product_id);
+		if (!dev) {
+			root_dbg("e_gid = 0, and device not found, "
+				 "task not allowed to run...\n");
+			return -EPERM;
+		}
+		usb_put_dev(dev);
+	}
+
+	return 0;
+}
+
+static struct security_operations rootplug_security_ops = {
+	/* Use the capability functions for some of the hooks */
+	.ptrace =			cap_ptrace,
+	.capget =			cap_capget,
+	.capset_check =			cap_capset_check,
+	.capset_set =			cap_capset_set,
+	.capable =			cap_capable,
+
+	.bprm_apply_creds =		cap_bprm_apply_creds,
+	.bprm_set_security =		cap_bprm_set_security,
+
+	.task_post_setuid =		cap_task_post_setuid,
+	.task_reparent_to_init =	cap_task_reparent_to_init,
+
+	.bprm_check_security =		rootplug_bprm_check_security,
+};
+
+static int __init rootplug_init (void)
+{
+	/* register ourselves with the security framework */
+	if (register_security (&rootplug_security_ops)) {
+		printk (KERN_INFO 
+			"Failure registering Root Plug module with the kernel\n");
+		/* try registering with primary module */
+		if (mod_reg_security (MY_NAME, &rootplug_security_ops)) {
+			printk (KERN_INFO "Failure registering Root Plug "
+				" module with primary security module.\n");
+			return -EINVAL;
+		}
+		secondary = 1;
+	}
+	printk (KERN_INFO "Root Plug module initialized, "
+		"vendor_id = %4.4x, product id = %4.4x\n", vendor_id, product_id);
+	return 0;
+}
+
+static void __exit rootplug_exit (void)
+{
+	/* remove ourselves from the security framework */
+	if (secondary) {
+		if (mod_unreg_security (MY_NAME, &rootplug_security_ops))
+			printk (KERN_INFO "Failure unregistering Root Plug "
+				" module with primary module.\n");
+	} else { 
+		if (unregister_security (&rootplug_security_ops)) {
+			printk (KERN_INFO "Failure unregistering Root Plug "
+				"module with the kernel\n");
+		}
+	}
+	printk (KERN_INFO "Root Plug module removed\n");
+}
+
+security_initcall (rootplug_init);
+module_exit (rootplug_exit);
+
+MODULE_DESCRIPTION("Root Plug sample LSM module, written for Linux Journal article");
+MODULE_LICENSE("GPL");
+
diff --git a/security/seclvl.c b/security/seclvl.c
new file mode 100644
index 0000000..8a0ab0d
--- /dev/null
+++ b/security/seclvl.c
@@ -0,0 +1,747 @@
+/**
+ * BSD Secure Levels LSM
+ *
+ * Maintainers:
+ *	Michael A. Halcrow <mike@halcrow.us>
+ *	Serge Hallyn <hallyn@cs.wm.edu>
+ *
+ * Copyright (c) 2001 WireX Communications, Inc <chris@wirex.com>
+ * Copyright (c) 2001 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (c) 2002 International Business Machines <robb@austin.ibm.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; either version 2 of the License, or
+ *	(at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/security.h>
+#include <linux/netlink.h>
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include <linux/capability.h>
+#include <linux/time.h>
+#include <linux/proc_fs.h>
+#include <linux/kobject.h>
+#include <linux/crypto.h>
+#include <asm/scatterlist.h>
+#include <linux/gfp.h>
+#include <linux/sysfs.h>
+
+#define SHA1_DIGEST_SIZE 20
+
+/**
+ * Module parameter that defines the initial secure level.
+ *
+ * When built as a module, it defaults to seclvl 1, which is the
+ * behavior of BSD secure levels.  Note that this default behavior
+ * wrecks havoc on a machine when the seclvl module is compiled into
+ * the kernel.	In that case, we default to seclvl 0.
+ */
+#ifdef CONFIG_SECURITY_SECLVL_MODULE
+static int initlvl = 1;
+#else
+static int initlvl;
+#endif
+module_param(initlvl, int, 0);
+MODULE_PARM_DESC(initlvl, "Initial secure level (defaults to 1)");
+
+/* Module parameter that defines the verbosity level */
+static int verbosity;
+module_param(verbosity, int, 0);
+MODULE_PARM_DESC(verbosity, "Initial verbosity level (0 or 1; defaults to "
+		 "0, which is Quiet)");
+
+/**
+ * Optional password which can be passed in to bring seclvl to 0
+ * (i.e., for halt/reboot).  Defaults to NULL (the passwd attribute
+ * file will not be registered in sysfs).
+ *
+ * This gets converted to its SHA1 hash when stored.  It's probably
+ * not a good idea to use this parameter when loading seclvl from a
+ * script; use sha1_passwd instead.
+ */
+
+#define MAX_PASSWD_SIZE	32
+static char passwd[MAX_PASSWD_SIZE];
+module_param_string(passwd, passwd, sizeof(passwd), 0);
+MODULE_PARM_DESC(passwd,
+		 "Plaintext of password that sets seclvl=0 when written to "
+		 "(sysfs mount point)/seclvl/passwd\n");
+
+/**
+ * SHA1 hashed version of the optional password which can be passed in
+ * to bring seclvl to 0 (i.e., for halt/reboot).  Must be in
+ * hexadecimal format (40 characters).	Defaults to NULL (the passwd
+ * attribute file will not be registered in sysfs).
+ *
+ * Use the sha1sum utility to generate the SHA1 hash of a password:
+ *
+ * echo -n "secret" | sha1sum
+ */
+#define MAX_SHA1_PASSWD	41
+static char sha1_passwd[MAX_SHA1_PASSWD];
+module_param_string(sha1_passwd, sha1_passwd, sizeof(sha1_passwd), 0);
+MODULE_PARM_DESC(sha1_passwd,
+		 "SHA1 hash (40 hexadecimal characters) of password that "
+		 "sets seclvl=0 when plaintext password is written to "
+		 "(sysfs mount point)/seclvl/passwd\n");
+
+static int hideHash = 1;
+module_param(hideHash, int, 0);
+MODULE_PARM_DESC(hideHash, "When set to 0, reading seclvl/passwd from sysfs "
+		 "will return the SHA1-hashed value of the password that "
+		 "lowers the secure level to 0.\n");
+
+#define MY_NAME "seclvl"
+
+/**
+ * This time-limits log writes to one per second.
+ */
+#define seclvl_printk(verb, type, fmt, arg...)			\
+	do {							\
+		if (verbosity >= verb) {			\
+			static unsigned long _prior;		\
+			unsigned long _now = jiffies;		\
+			if ((_now - _prior) > HZ) {		\
+				printk(type "%s: %s: " fmt,	\
+					MY_NAME, __FUNCTION__ ,	\
+					## arg);		\
+				_prior = _now;			\
+			}					\
+		}						\
+	} while (0)
+
+/**
+ * kobject stuff
+ */
+
+struct subsystem seclvl_subsys;
+
+struct seclvl_obj {
+	char *name;
+	struct list_head slot_list;
+	struct kobject kobj;
+};
+
+/**
+ * There is a seclvl_attribute struct for each file in sysfs.
+ *
+ * In our case, we have one of these structs for "passwd" and another
+ * for "seclvl".
+ */
+struct seclvl_attribute {
+	struct attribute attr;
+	ssize_t(*show) (struct seclvl_obj *, char *);
+	ssize_t(*store) (struct seclvl_obj *, const char *, size_t);
+};
+
+/**
+ * When this function is called, one of the files in sysfs is being
+ * written to.  attribute->store is a function pointer to whatever the
+ * struct seclvl_attribute store function pointer points to.  It is
+ * unique for "passwd" and "seclvl".
+ */
+static ssize_t
+seclvl_attr_store(struct kobject *kobj,
+		  struct attribute *attr, const char *buf, size_t len)
+{
+	struct seclvl_obj *obj = container_of(kobj, struct seclvl_obj, kobj);
+	struct seclvl_attribute *attribute =
+	    container_of(attr, struct seclvl_attribute, attr);
+	return (attribute->store ? attribute->store(obj, buf, len) : 0);
+}
+
+static ssize_t
+seclvl_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+	struct seclvl_obj *obj = container_of(kobj, struct seclvl_obj, kobj);
+	struct seclvl_attribute *attribute =
+	    container_of(attr, struct seclvl_attribute, attr);
+	return (attribute->show ? attribute->show(obj, buf) : 0);
+}
+
+/**
+ * Callback function pointers for show and store
+ */
+static struct sysfs_ops seclvlfs_sysfs_ops = {
+	.show = seclvl_attr_show,
+	.store = seclvl_attr_store,
+};
+
+static struct kobj_type seclvl_ktype = {
+	.sysfs_ops = &seclvlfs_sysfs_ops
+};
+
+decl_subsys(seclvl, &seclvl_ktype, NULL);
+
+/**
+ * The actual security level.  Ranges between -1 and 2 inclusive.
+ */
+static int seclvl;
+
+/**
+ * flag to keep track of how we were registered
+ */
+static int secondary;
+
+/**
+ * Verifies that the requested secure level is valid, given the current
+ * secure level.
+ */
+static int seclvl_sanity(int reqlvl)
+{
+	if ((reqlvl < -1) || (reqlvl > 2)) {
+		seclvl_printk(1, KERN_WARNING, "Attempt to set seclvl out of "
+			      "range: [%d]\n", reqlvl);
+		return -EINVAL;
+	}
+	if ((seclvl == 0) && (reqlvl == -1))
+		return 0;
+	if (reqlvl < seclvl) {
+		seclvl_printk(1, KERN_WARNING, "Attempt to lower seclvl to "
+			      "[%d]\n", reqlvl);
+		return -EPERM;
+	}
+	return 0;
+}
+
+/**
+ * Called whenever the user reads the sysfs handle to this kernel
+ * object
+ */
+static ssize_t seclvl_read_file(struct seclvl_obj *obj, char *buff)
+{
+	return snprintf(buff, PAGE_SIZE, "%d\n", seclvl);
+}
+
+/**
+ * security level advancement rules:
+ *   Valid levels are -1 through 2, inclusive.
+ *   From -1, stuck.  [ in case compiled into kernel ]
+ *   From 0 or above, can only increment.
+ */
+static int do_seclvl_advance(int newlvl)
+{
+	if (newlvl <= seclvl) {
+		seclvl_printk(1, KERN_WARNING, "Cannot advance to seclvl "
+			      "[%d]\n", newlvl);
+		return -EINVAL;
+	}
+	if (newlvl > 2) {
+		seclvl_printk(1, KERN_WARNING, "Cannot advance to seclvl "
+			      "[%d]\n", newlvl);
+		return -EINVAL;
+	}
+	if (seclvl == -1) {
+		seclvl_printk(1, KERN_WARNING, "Not allowed to advance to "
+			      "seclvl [%d]\n", seclvl);
+		return -EPERM;
+	}
+	seclvl = newlvl;
+	return 0;
+}
+
+/**
+ * Called whenever the user writes to the sysfs handle to this kernel
+ * object (seclvl/seclvl).  It expects a single-digit number.
+ */
+static ssize_t
+seclvl_write_file(struct seclvl_obj *obj, const char *buff, size_t count)
+{
+	unsigned long val;
+	if (count > 2 || (count == 2 && buff[1] != '\n')) {
+		seclvl_printk(1, KERN_WARNING, "Invalid value passed to "
+			      "seclvl: [%s]\n", buff);
+		return -EINVAL;
+	}
+	val = buff[0] - 48;
+	if (seclvl_sanity(val)) {
+		seclvl_printk(1, KERN_WARNING, "Illegal secure level "
+			      "requested: [%d]\n", (int)val);
+		return -EPERM;
+	}
+	if (do_seclvl_advance(val)) {
+		seclvl_printk(0, KERN_ERR, "Failure advancing security level "
+			      "to %lu\n", val);
+	}
+	return count;
+}
+
+/* Generate sysfs_attr_seclvl */
+static struct seclvl_attribute sysfs_attr_seclvl =
+__ATTR(seclvl, (S_IFREG | S_IRUGO | S_IWUSR), seclvl_read_file,
+       seclvl_write_file);
+
+static unsigned char hashedPassword[SHA1_DIGEST_SIZE];
+
+/**
+ * Called whenever the user reads the sysfs passwd handle.
+ */
+static ssize_t seclvl_read_passwd(struct seclvl_obj *obj, char *buff)
+{
+	/* So just how good *is* your password? :-) */
+	char tmp[3];
+	int i = 0;
+	buff[0] = '\0';
+	if (hideHash) {
+		/* Security through obscurity */
+		return 0;
+	}
+	while (i < SHA1_DIGEST_SIZE) {
+		snprintf(tmp, 3, "%02x", hashedPassword[i]);
+		strncat(buff, tmp, 2);
+		i++;
+	}
+	strcat(buff, "\n");
+	return ((SHA1_DIGEST_SIZE * 2) + 1);
+}
+
+/**
+ * Converts a block of plaintext of into its SHA1 hashed value.
+ *
+ * It would be nice if crypto had a wrapper to do this for us linear
+ * people...
+ */
+static int
+plaintext_to_sha1(unsigned char *hash, const char *plaintext, int len)
+{
+	char *pgVirtAddr;
+	struct crypto_tfm *tfm;
+	struct scatterlist sg[1];
+	if (len > PAGE_SIZE) {
+		seclvl_printk(0, KERN_ERR, "Plaintext password too large (%d "
+			      "characters).  Largest possible is %lu "
+			      "bytes.\n", len, PAGE_SIZE);
+		return -ENOMEM;
+	}
+	tfm = crypto_alloc_tfm("sha1", 0);
+	if (tfm == NULL) {
+		seclvl_printk(0, KERN_ERR,
+			      "Failed to load transform for SHA1\n");
+		return -ENOSYS;
+	}
+	// Just get a new page; don't play around with page boundaries
+	// and scatterlists.
+	pgVirtAddr = (char *)__get_free_page(GFP_KERNEL);
+	sg[0].page = virt_to_page(pgVirtAddr);
+	sg[0].offset = 0;
+	sg[0].length = len;
+	strncpy(pgVirtAddr, plaintext, len);
+	crypto_digest_init(tfm);
+	crypto_digest_update(tfm, sg, 1);
+	crypto_digest_final(tfm, hash);
+	crypto_free_tfm(tfm);
+	free_page((unsigned long)pgVirtAddr);
+	return 0;
+}
+
+/**
+ * Called whenever the user writes to the sysfs passwd handle to this kernel
+ * object.  It hashes the password and compares the hashed results.
+ */
+static ssize_t
+seclvl_write_passwd(struct seclvl_obj *obj, const char *buff, size_t count)
+{
+	int i;
+	unsigned char tmp[SHA1_DIGEST_SIZE];
+	int rc;
+	int len;
+	if (!*passwd && !*sha1_passwd) {
+		seclvl_printk(0, KERN_ERR, "Attempt to password-unlock the "
+			      "seclvl module, but neither a plain text "
+			      "password nor a SHA1 hashed password was "
+			      "passed in as a module parameter!  This is a "
+			      "bug, since it should not be possible to be in "
+			      "this part of the module; please tell a "
+			      "maintainer about this event.\n");
+		return -EINVAL;
+	}
+	len = strlen(buff);
+	/* ``echo "secret" > seclvl/passwd'' includes a newline */
+	if (buff[len - 1] == '\n') {
+		len--;
+	}
+	/* Hash the password, then compare the hashed values */
+	if ((rc = plaintext_to_sha1(tmp, buff, len))) {
+		seclvl_printk(0, KERN_ERR, "Error hashing password: rc = "
+			      "[%d]\n", rc);
+		return rc;
+	}
+	for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
+		if (hashedPassword[i] != tmp[i]) {
+			return -EPERM;
+		}
+	}
+	seclvl_printk(0, KERN_INFO,
+		      "Password accepted; seclvl reduced to 0.\n");
+	seclvl = 0;
+	return count;
+}
+
+/* Generate sysfs_attr_passwd */
+static struct seclvl_attribute sysfs_attr_passwd =
+__ATTR(passwd, (S_IFREG | S_IRUGO | S_IWUSR), seclvl_read_passwd,
+       seclvl_write_passwd);
+
+/**
+ * Explicitely disallow ptrace'ing the init process.
+ */
+static int seclvl_ptrace(struct task_struct *parent, struct task_struct *child)
+{
+	if (seclvl >= 0) {
+		if (child->pid == 1) {
+			seclvl_printk(1, KERN_WARNING, "Attempt to ptrace "
+				      "the init process dissallowed in "
+				      "secure level %d\n", seclvl);
+			return -EPERM;
+		}
+	}
+	return 0;
+}
+
+/**
+ * Capability checks for seclvl.  The majority of the policy
+ * enforcement for seclvl takes place here.
+ */
+static int seclvl_capable(struct task_struct *tsk, int cap)
+{
+	/* init can do anything it wants */
+	if (tsk->pid == 1)
+		return 0;
+
+	switch (seclvl) {
+	case 2:
+		/* fall through */
+	case 1:
+		if (cap == CAP_LINUX_IMMUTABLE) {
+			seclvl_printk(1, KERN_WARNING, "Attempt to modify "
+				      "the IMMUTABLE and/or APPEND extended "
+				      "attribute on a file with the IMMUTABLE "
+				      "and/or APPEND extended attribute set "
+				      "denied in seclvl [%d]\n", seclvl);
+			return -EPERM;
+		} else if (cap == CAP_SYS_RAWIO) {	// Somewhat broad...
+			seclvl_printk(1, KERN_WARNING, "Attempt to perform "
+				      "raw I/O while in secure level [%d] "
+				      "denied\n", seclvl);
+			return -EPERM;
+		} else if (cap == CAP_NET_ADMIN) {
+			seclvl_printk(1, KERN_WARNING, "Attempt to perform "
+				      "network administrative task while "
+				      "in secure level [%d] denied\n", seclvl);
+			return -EPERM;
+		} else if (cap == CAP_SETUID) {
+			seclvl_printk(1, KERN_WARNING, "Attempt to setuid "
+				      "while in secure level [%d] denied\n",
+				      seclvl);
+			return -EPERM;
+		} else if (cap == CAP_SETGID) {
+			seclvl_printk(1, KERN_WARNING, "Attempt to setgid "
+				      "while in secure level [%d] denied\n",
+				      seclvl);
+		} else if (cap == CAP_SYS_MODULE) {
+			seclvl_printk(1, KERN_WARNING, "Attempt to perform "
+				      "a module operation while in secure "
+				      "level [%d] denied\n", seclvl);
+			return -EPERM;
+		}
+		break;
+	default:
+		break;
+	}
+	/* from dummy.c */
+	if (cap_is_fs_cap(cap) ? tsk->fsuid == 0 : tsk->euid == 0)
+		return 0;	/* capability granted */
+	seclvl_printk(1, KERN_WARNING, "Capability denied\n");
+	return -EPERM;		/* capability denied */
+}
+
+/**
+ * Disallow reversing the clock in seclvl > 1
+ */
+static int seclvl_settime(struct timespec *tv, struct timezone *tz)
+{
+	struct timespec now;
+	if (seclvl > 1) {
+		now = current_kernel_time();
+		if (tv->tv_sec < now.tv_sec ||
+		    (tv->tv_sec == now.tv_sec && tv->tv_nsec < now.tv_nsec)) {
+			seclvl_printk(1, KERN_WARNING, "Attempt to decrement "
+				      "time in secure level %d denied: "
+				      "current->pid = [%d], "
+				      "current->group_leader->pid = [%d]\n",
+				      seclvl, current->pid,
+				      current->group_leader->pid);
+			return -EPERM;
+		}		/* if attempt to decrement time */
+	}			/* if seclvl > 1 */
+	return 0;
+}
+
+/* claim the blockdev to exclude mounters, release on file close */
+static int seclvl_bd_claim(struct inode *inode)
+{
+	int holder;
+	struct block_device *bdev = NULL;
+	dev_t dev = inode->i_rdev;
+	bdev = open_by_devnum(dev, FMODE_WRITE);
+	if (bdev) {
+		if (bd_claim(bdev, &holder)) {
+			blkdev_put(bdev);
+			return -EPERM;
+		}
+		/* claimed, mark it to release on close */
+		inode->i_security = current;
+	}
+	return 0;
+}
+
+/* release the blockdev if you claimed it */
+static void seclvl_bd_release(struct inode *inode)
+{
+	if (inode && S_ISBLK(inode->i_mode) && inode->i_security == current) {
+		struct block_device *bdev = inode->i_bdev;
+		if (bdev) {
+			bd_release(bdev);
+			blkdev_put(bdev);
+			inode->i_security = NULL;
+		}
+	}
+}
+
+/**
+ * Security for writes to block devices is regulated by this seclvl
+ * function.  Deny all writes to block devices in seclvl 2.  In
+ * seclvl 1, we only deny writes to *mounted* block devices.
+ */
+static int
+seclvl_inode_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+	if (current->pid != 1 && S_ISBLK(inode->i_mode) && (mask & MAY_WRITE)) {
+		switch (seclvl) {
+		case 2:
+			seclvl_printk(1, KERN_WARNING, "Write to block device "
+				      "denied in secure level [%d]\n", seclvl);
+			return -EPERM;
+		case 1:
+			if (seclvl_bd_claim(inode)) {
+				seclvl_printk(1, KERN_WARNING,
+					      "Write to mounted block device "
+					      "denied in secure level [%d]\n",
+					      seclvl);
+				return -EPERM;
+			}
+		}
+	}
+	return 0;
+}
+
+/**
+ * The SUID and SGID bits cannot be set in seclvl >= 1
+ */
+static int seclvl_inode_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+	if (seclvl > 0) {
+		if (iattr->ia_valid & ATTR_MODE)
+			if (iattr->ia_mode & S_ISUID ||
+			    iattr->ia_mode & S_ISGID) {
+				seclvl_printk(1, KERN_WARNING, "Attempt to "
+					      "modify SUID or SGID bit "
+					      "denied in seclvl [%d]\n",
+					      seclvl);
+				return -EPERM;
+			}
+	}
+	return 0;
+}
+
+/* release busied block devices */
+static void seclvl_file_free_security(struct file *filp)
+{
+	struct dentry *dentry = filp->f_dentry;
+	struct inode *inode = NULL;
+
+	if (dentry) {
+		inode = dentry->d_inode;
+		seclvl_bd_release(inode);
+	}
+}
+
+/**
+ * Cannot unmount in secure level 2
+ */
+static int seclvl_umount(struct vfsmount *mnt, int flags)
+{
+	if (current->pid == 1) {
+		return 0;
+	}
+	if (seclvl == 2) {
+		seclvl_printk(1, KERN_WARNING, "Attempt to unmount in secure "
+			      "level %d\n", seclvl);
+		return -EPERM;
+	}
+	return 0;
+}
+
+static struct security_operations seclvl_ops = {
+	.ptrace = seclvl_ptrace,
+	.capable = seclvl_capable,
+	.inode_permission = seclvl_inode_permission,
+	.inode_setattr = seclvl_inode_setattr,
+	.file_free_security = seclvl_file_free_security,
+	.settime = seclvl_settime,
+	.sb_umount = seclvl_umount,
+};
+
+/**
+ * Process the password-related module parameters
+ */
+static int processPassword(void)
+{
+	int rc = 0;
+	hashedPassword[0] = '\0';
+	if (*passwd) {
+		if (*sha1_passwd) {
+			seclvl_printk(0, KERN_ERR, "Error: Both "
+				      "passwd and sha1_passwd "
+				      "were set, but they are mutually "
+				      "exclusive.\n");
+			return -EINVAL;
+		}
+		if ((rc = plaintext_to_sha1(hashedPassword, passwd,
+					    strlen(passwd)))) {
+			seclvl_printk(0, KERN_ERR, "Error: SHA1 support not "
+				      "in kernel\n");
+			return rc;
+		}
+		/* All static data goes to the BSS, which zero's the
+		 * plaintext password out for us. */
+	} else if (*sha1_passwd) {	// Base 16
+		int i;
+		i = strlen(sha1_passwd);
+		if (i != (SHA1_DIGEST_SIZE * 2)) {
+			seclvl_printk(0, KERN_ERR, "Received [%d] bytes; "
+				      "expected [%d] for the hexadecimal "
+				      "representation of the SHA1 hash of "
+				      "the password.\n",
+				      i, (SHA1_DIGEST_SIZE * 2));
+			return -EINVAL;
+		}
+		while ((i -= 2) + 2) {
+			unsigned char tmp;
+			tmp = sha1_passwd[i + 2];
+			sha1_passwd[i + 2] = '\0';
+			hashedPassword[i / 2] = (unsigned char)
+			    simple_strtol(&sha1_passwd[i], NULL, 16);
+			sha1_passwd[i + 2] = tmp;
+		}
+	}
+	return 0;
+}
+
+/**
+ * Sysfs registrations
+ */
+static int doSysfsRegistrations(void)
+{
+	int rc = 0;
+	if ((rc = subsystem_register(&seclvl_subsys))) {
+		seclvl_printk(0, KERN_WARNING,
+			      "Error [%d] registering seclvl subsystem\n", rc);
+		return rc;
+	}
+	sysfs_create_file(&seclvl_subsys.kset.kobj, &sysfs_attr_seclvl.attr);
+	if (*passwd || *sha1_passwd) {
+		sysfs_create_file(&seclvl_subsys.kset.kobj,
+				  &sysfs_attr_passwd.attr);
+	}
+	return 0;
+}
+
+/**
+ * Initialize the seclvl module.
+ */
+static int __init seclvl_init(void)
+{
+	int rc = 0;
+	if (verbosity < 0 || verbosity > 1) {
+		printk(KERN_ERR "Error: bad verbosity [%d]; only 0 or 1 "
+		       "are valid values\n", verbosity);
+		rc = -EINVAL;
+		goto exit;
+	}
+	sysfs_attr_seclvl.attr.owner = THIS_MODULE;
+	sysfs_attr_passwd.attr.owner = THIS_MODULE;
+	if (initlvl < -1 || initlvl > 2) {
+		seclvl_printk(0, KERN_ERR, "Error: bad initial securelevel "
+			      "[%d].\n", initlvl);
+		rc = -EINVAL;
+		goto exit;
+	}
+	seclvl = initlvl;
+	if ((rc = processPassword())) {
+		seclvl_printk(0, KERN_ERR, "Error processing the password "
+			      "module parameter(s): rc = [%d]\n", rc);
+		goto exit;
+	}
+	/* register ourselves with the security framework */
+	if (register_security(&seclvl_ops)) {
+		seclvl_printk(0, KERN_ERR,
+			      "seclvl: Failure registering with the "
+			      "kernel.\n");
+		/* try registering with primary module */
+		rc = mod_reg_security(MY_NAME, &seclvl_ops);
+		if (rc) {
+			seclvl_printk(0, KERN_ERR, "seclvl: Failure "
+				      "registering with primary security "
+				      "module.\n");
+			goto exit;
+		}		/* if primary module registered */
+		secondary = 1;
+	}			/* if we registered ourselves with the security framework */
+	if ((rc = doSysfsRegistrations())) {
+		seclvl_printk(0, KERN_ERR, "Error registering with sysfs\n");
+		goto exit;
+	}
+	seclvl_printk(0, KERN_INFO, "seclvl: Successfully initialized.\n");
+ exit:
+	if (rc) {
+		printk(KERN_ERR "seclvl: Error during initialization: rc = "
+		       "[%d]\n", rc);
+	}
+	return rc;
+}
+
+/**
+ * Remove the seclvl module.
+ */
+static void __exit seclvl_exit(void)
+{
+	sysfs_remove_file(&seclvl_subsys.kset.kobj, &sysfs_attr_seclvl.attr);
+	if (*passwd || *sha1_passwd) {
+		sysfs_remove_file(&seclvl_subsys.kset.kobj,
+				  &sysfs_attr_passwd.attr);
+	}
+	subsystem_unregister(&seclvl_subsys);
+	if (secondary == 1) {
+		mod_unreg_security(MY_NAME, &seclvl_ops);
+	} else if (unregister_security(&seclvl_ops)) {
+		seclvl_printk(0, KERN_INFO,
+			      "seclvl: Failure unregistering with the "
+			      "kernel\n");
+	}
+}
+
+module_init(seclvl_init);
+module_exit(seclvl_exit);
+
+MODULE_AUTHOR("Michael A. Halcrow <mike@halcrow.us>");
+MODULE_DESCRIPTION("LSM implementation of the BSD Secure Levels");
+MODULE_LICENSE("GPL");
diff --git a/security/security.c b/security/security.c
new file mode 100644
index 0000000..ed5fb80
--- /dev/null
+++ b/security/security.c
@@ -0,0 +1,203 @@
+/*
+ * Security plug functions
+ *
+ * Copyright (C) 2001 WireX Communications, Inc <chris@wirex.com>
+ * Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2001 Networks Associates Technology, Inc <ssmalley@nai.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; either version 2 of the License, or
+ *	(at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/security.h>
+
+#define SECURITY_FRAMEWORK_VERSION	"1.0.0"
+
+/* things that live in dummy.c */
+extern struct security_operations dummy_security_ops;
+extern void security_fixup_ops(struct security_operations *ops);
+
+struct security_operations *security_ops;	/* Initialized to NULL */
+
+static inline int verify(struct security_operations *ops)
+{
+	/* verify the security_operations structure exists */
+	if (!ops)
+		return -EINVAL;
+	security_fixup_ops(ops);
+	return 0;
+}
+
+static void __init do_security_initcalls(void)
+{
+	initcall_t *call;
+	call = __security_initcall_start;
+	while (call < __security_initcall_end) {
+		(*call) ();
+		call++;
+	}
+}
+
+/**
+ * security_init - initializes the security framework
+ *
+ * This should be called early in the kernel initialization sequence.
+ */
+int __init security_init(void)
+{
+	printk(KERN_INFO "Security Framework v" SECURITY_FRAMEWORK_VERSION
+	       " initialized\n");
+
+	if (verify(&dummy_security_ops)) {
+		printk(KERN_ERR "%s could not verify "
+		       "dummy_security_ops structure.\n", __FUNCTION__);
+		return -EIO;
+	}
+
+	security_ops = &dummy_security_ops;
+	do_security_initcalls();
+
+	return 0;
+}
+
+/**
+ * register_security - registers a security framework with the kernel
+ * @ops: a pointer to the struct security_options that is to be registered
+ *
+ * This function is to allow a security module to register itself with the
+ * kernel security subsystem.  Some rudimentary checking is done on the @ops
+ * value passed to this function.  A call to unregister_security() should be
+ * done to remove this security_options structure from the kernel.
+ *
+ * If there is already a security module registered with the kernel,
+ * an error will be returned.  Otherwise 0 is returned on success.
+ */
+int register_security(struct security_operations *ops)
+{
+	if (verify(ops)) {
+		printk(KERN_DEBUG "%s could not verify "
+		       "security_operations structure.\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	if (security_ops != &dummy_security_ops)
+		return -EAGAIN;
+
+	security_ops = ops;
+
+	return 0;
+}
+
+/**
+ * unregister_security - unregisters a security framework with the kernel
+ * @ops: a pointer to the struct security_options that is to be registered
+ *
+ * This function removes a struct security_operations variable that had
+ * previously been registered with a successful call to register_security().
+ *
+ * If @ops does not match the valued previously passed to register_security()
+ * an error is returned.  Otherwise the default security options is set to the
+ * the dummy_security_ops structure, and 0 is returned.
+ */
+int unregister_security(struct security_operations *ops)
+{
+	if (ops != security_ops) {
+		printk(KERN_INFO "%s: trying to unregister "
+		       "a security_opts structure that is not "
+		       "registered, failing.\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	security_ops = &dummy_security_ops;
+
+	return 0;
+}
+
+/**
+ * mod_reg_security - allows security modules to be "stacked"
+ * @name: a pointer to a string with the name of the security_options to be registered
+ * @ops: a pointer to the struct security_options that is to be registered
+ *
+ * This function allows security modules to be stacked if the currently loaded
+ * security module allows this to happen.  It passes the @name and @ops to the
+ * register_security function of the currently loaded security module.
+ *
+ * The return value depends on the currently loaded security module, with 0 as
+ * success.
+ */
+int mod_reg_security(const char *name, struct security_operations *ops)
+{
+	if (verify(ops)) {
+		printk(KERN_INFO "%s could not verify "
+		       "security operations.\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	if (ops == security_ops) {
+		printk(KERN_INFO "%s security operations "
+		       "already registered.\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	return security_ops->register_security(name, ops);
+}
+
+/**
+ * mod_unreg_security - allows a security module registered with mod_reg_security() to be unloaded
+ * @name: a pointer to a string with the name of the security_options to be removed
+ * @ops: a pointer to the struct security_options that is to be removed
+ *
+ * This function allows security modules that have been successfully registered
+ * with a call to mod_reg_security() to be unloaded from the system.
+ * This calls the currently loaded security module's unregister_security() call
+ * with the @name and @ops variables.
+ *
+ * The return value depends on the currently loaded security module, with 0 as
+ * success.
+ */
+int mod_unreg_security(const char *name, struct security_operations *ops)
+{
+	if (ops == security_ops) {
+		printk(KERN_INFO "%s invalid attempt to unregister "
+		       " primary security ops.\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	return security_ops->unregister_security(name, ops);
+}
+
+/**
+ * capable - calls the currently loaded security module's capable() function with the specified capability
+ * @cap: the requested capability level.
+ *
+ * This function calls the currently loaded security module's capable()
+ * function with a pointer to the current task and the specified @cap value.
+ *
+ * This allows the security module to implement the capable function call
+ * however it chooses to.
+ */
+int capable(int cap)
+{
+	if (security_ops->capable(current, cap)) {
+		/* capability denied */
+		return 0;
+	}
+
+	/* capability granted */
+	current->flags |= PF_SUPERPRIV;
+	return 1;
+}
+
+EXPORT_SYMBOL_GPL(register_security);
+EXPORT_SYMBOL_GPL(unregister_security);
+EXPORT_SYMBOL_GPL(mod_reg_security);
+EXPORT_SYMBOL_GPL(mod_unreg_security);
+EXPORT_SYMBOL(capable);
+EXPORT_SYMBOL(security_ops);
diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig
new file mode 100644
index 0000000..b59582b
--- /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 0000000..b038cd0
--- /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 0000000..fe6285e
--- /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 0000000..8a2cc75
--- /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 0000000..9facb27
--- /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 0000000..903e8b3
--- /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 0000000..b0a12ac
--- /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 0000000..960ef18
--- /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 0000000..450a283
--- /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 0000000..519a77d
--- /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 0000000..ce5b6e2f
--- /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 0000000..67ce7a8
--- /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 0000000..4eef1b6
--- /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 0000000..d4fac82
--- /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 0000000..8bd6f99
--- /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 0000000..887937c
--- /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 0000000..fa187c9a
--- /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 0000000..718d7be
--- /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 0000000..18d08ac
--- /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 0000000..fa7fa03
--- /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 0000000..0722156
--- /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 0000000..bad7877
--- /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 0000000..f238c03
--- /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 0000000..519d4f6
--- /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 0000000..b534411
--- /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 0000000..f3a1fc6
--- /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 0000000..149dda7
--- /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 0000000..0562bac
--- /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 0000000..d8ce9cc
--- /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 0000000..4713702
--- /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 0000000..26661fc
--- /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 0000000..4cc8581
--- /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 0000000..756036b
--- /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 0000000..0d37bea
--- /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 0000000..0c692d5
--- /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 0000000..14190ef
--- /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 0000000..2470e2a
--- /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 0000000..5a820cf
--- /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 0000000..e8d907e
--- /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 0000000..871c33b
--- /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 0000000..2fe9dfa
--- /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 0000000..24a10d3
--- /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 0000000..ca422b4
--- /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_ */
+
+