summary refs log tree commit diff
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/Kconfig6
-rw-r--r--security/Makefile4
-rw-r--r--security/apparmor/apparmorfs.c2
-rw-r--r--security/apparmor/ipc.c1
-rw-r--r--security/apparmor/lib.c1
-rw-r--r--security/apparmor/policy_unpack.c12
-rw-r--r--security/apparmor/procattr.c1
-rw-r--r--security/commoncap.c16
-rw-r--r--security/integrity/Kconfig7
-rw-r--r--security/integrity/Makefile12
-rw-r--r--security/integrity/evm/Kconfig13
-rw-r--r--security/integrity/evm/Makefile7
-rw-r--r--security/integrity/evm/evm.h38
-rw-r--r--security/integrity/evm/evm_crypto.c216
-rw-r--r--security/integrity/evm/evm_main.c384
-rw-r--r--security/integrity/evm/evm_posix_acl.c26
-rw-r--r--security/integrity/evm/evm_secfs.c108
-rw-r--r--security/integrity/iint.c172
-rw-r--r--security/integrity/ima/Kconfig1
-rw-r--r--security/integrity/ima/Makefile2
-rw-r--r--security/integrity/ima/ima.h30
-rw-r--r--security/integrity/ima/ima_api.c7
-rw-r--r--security/integrity/ima/ima_fs.c2
-rw-r--r--security/integrity/ima/ima_iint.c169
-rw-r--r--security/integrity/ima/ima_main.c13
-rw-r--r--security/integrity/integrity.h50
-rw-r--r--security/keys/Makefile2
-rw-r--r--security/keys/encrypted-keys/Makefile6
-rw-r--r--security/keys/encrypted-keys/ecryptfs_format.c (renamed from security/keys/ecryptfs_format.c)0
-rw-r--r--security/keys/encrypted-keys/ecryptfs_format.h (renamed from security/keys/ecryptfs_format.h)0
-rw-r--r--security/keys/encrypted-keys/encrypted.c (renamed from security/keys/encrypted.c)49
-rw-r--r--security/keys/encrypted-keys/encrypted.h (renamed from security/keys/encrypted.h)11
-rw-r--r--security/keys/encrypted-keys/masterkey_trusted.c45
-rw-r--r--security/keys/gc.c386
-rw-r--r--security/keys/internal.h4
-rw-r--r--security/keys/key.c121
-rw-r--r--security/keys/keyring.c3
-rw-r--r--security/keys/process_keys.c16
-rw-r--r--security/keys/trusted.c19
-rw-r--r--security/security.c76
-rw-r--r--security/selinux/exports.c1
-rw-r--r--security/selinux/hooks.c13
-rw-r--r--security/selinux/include/avc_ss.h6
-rw-r--r--security/selinux/include/security.h8
-rw-r--r--security/selinux/netlink.c2
-rw-r--r--security/selinux/nlmsgtab.c1
-rw-r--r--security/selinux/selinuxfs.c5
-rw-r--r--security/selinux/ss/conditional.c2
-rw-r--r--security/selinux/ss/conditional.h1
-rw-r--r--security/selinux/ss/policydb.c2
-rw-r--r--security/selinux/ss/services.c3
-rw-r--r--security/smack/smack.h24
-rw-r--r--security/smack/smack_access.c134
-rw-r--r--security/smack/smack_lsm.c266
-rw-r--r--security/smack/smackfs.c277
-rw-r--r--security/tomoyo/Kconfig2
-rw-r--r--security/tomoyo/Makefile4
-rw-r--r--security/tomoyo/audit.c7
-rw-r--r--security/tomoyo/common.c228
-rw-r--r--security/tomoyo/common.h189
-rw-r--r--security/tomoyo/condition.c71
-rw-r--r--security/tomoyo/domain.c209
-rw-r--r--security/tomoyo/environ.c122
-rw-r--r--security/tomoyo/file.c42
-rw-r--r--security/tomoyo/gc.c540
-rw-r--r--security/tomoyo/group.c61
-rw-r--r--security/tomoyo/memory.c39
-rw-r--r--security/tomoyo/network.c771
-rw-r--r--security/tomoyo/realpath.c32
-rw-r--r--security/tomoyo/securityfs_if.c123
-rw-r--r--security/tomoyo/tomoyo.c62
-rw-r--r--security/tomoyo/util.c80
72 files changed, 4164 insertions, 1201 deletions
diff --git a/security/Kconfig b/security/Kconfig
index e0f08b52e4ab..51bd5a0b69ae 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -38,7 +38,9 @@ config TRUSTED_KEYS
 
 config ENCRYPTED_KEYS
 	tristate "ENCRYPTED KEYS"
-	depends on KEYS && TRUSTED_KEYS
+	depends on KEYS
+	select CRYPTO
+	select CRYPTO_HMAC
 	select CRYPTO_AES
 	select CRYPTO_CBC
 	select CRYPTO_SHA256
@@ -186,7 +188,7 @@ source security/smack/Kconfig
 source security/tomoyo/Kconfig
 source security/apparmor/Kconfig
 
-source security/integrity/ima/Kconfig
+source security/integrity/Kconfig
 
 choice
 	prompt "Default security module"
diff --git a/security/Makefile b/security/Makefile
index 8bb0fe9e1ca9..a5e502f8a05b 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -24,5 +24,5 @@ obj-$(CONFIG_SECURITY_APPARMOR)		+= apparmor/built-in.o
 obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
 
 # Object integrity file lists
-subdir-$(CONFIG_IMA)			+= integrity/ima
-obj-$(CONFIG_IMA)			+= integrity/ima/built-in.o
+subdir-$(CONFIG_INTEGRITY)		+= integrity
+obj-$(CONFIG_INTEGRITY)			+= integrity/built-in.o
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 0848292982a2..69ddb47787b2 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -200,7 +200,7 @@ void __init aa_destroy_aafs(void)
  *
  * Returns: error on failure
  */
-int __init aa_create_aafs(void)
+static int __init aa_create_aafs(void)
 {
 	int error;
 
diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c
index 649fad88869b..7ee05c6f3c64 100644
--- a/security/apparmor/ipc.c
+++ b/security/apparmor/ipc.c
@@ -19,6 +19,7 @@
 #include "include/capability.h"
 #include "include/context.h"
 #include "include/policy.h"
+#include "include/ipc.h"
 
 /* call back to audit ptrace fields */
 static void audit_cb(struct audit_buffer *ab, void *va)
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c
index b82e383beb77..9516948041ad 100644
--- a/security/apparmor/lib.c
+++ b/security/apparmor/lib.c
@@ -18,6 +18,7 @@
 #include <linux/vmalloc.h>
 
 #include "include/audit.h"
+#include "include/apparmor.h"
 
 
 /**
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
index d6d9a57b5652..741dd13e089b 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -381,11 +381,11 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
 		profile->file.trans.size = size;
 		for (i = 0; i < size; i++) {
 			char *str;
-			int c, j, size = unpack_strdup(e, &str, NULL);
+			int c, j, size2 = unpack_strdup(e, &str, NULL);
 			/* unpack_strdup verifies that the last character is
 			 * null termination byte.
 			 */
-			if (!size)
+			if (!size2)
 				goto fail;
 			profile->file.trans.table[i] = str;
 			/* verify that name doesn't start with space */
@@ -393,7 +393,7 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
 				goto fail;
 
 			/* count internal #  of internal \0 */
-			for (c = j = 0; j < size - 2; j++) {
+			for (c = j = 0; j < size2 - 2; j++) {
 				if (!str[j])
 					c++;
 			}
@@ -440,11 +440,11 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile)
 		if (size > RLIM_NLIMITS)
 			goto fail;
 		for (i = 0; i < size; i++) {
-			u64 tmp = 0;
+			u64 tmp2 = 0;
 			int a = aa_map_resource(i);
-			if (!unpack_u64(e, &tmp, NULL))
+			if (!unpack_u64(e, &tmp2, NULL))
 				goto fail;
-			profile->rlimits.limits[a].rlim_max = tmp;
+			profile->rlimits.limits[a].rlim_max = tmp2;
 		}
 		if (!unpack_nameX(e, AA_ARRAYEND, NULL))
 			goto fail;
diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c
index 04a2cf8d1b65..1b41c542d376 100644
--- a/security/apparmor/procattr.c
+++ b/security/apparmor/procattr.c
@@ -16,6 +16,7 @@
 #include "include/context.h"
 #include "include/policy.h"
 #include "include/domain.h"
+#include "include/procattr.h"
 
 
 /**
diff --git a/security/commoncap.c b/security/commoncap.c
index a93b3b733079..ee4f8486e5f5 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -332,7 +332,8 @@ int cap_inode_killpriv(struct dentry *dentry)
  */
 static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,
 					  struct linux_binprm *bprm,
-					  bool *effective)
+					  bool *effective,
+					  bool *has_cap)
 {
 	struct cred *new = bprm->cred;
 	unsigned i;
@@ -341,6 +342,9 @@ static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,
 	if (caps->magic_etc & VFS_CAP_FLAGS_EFFECTIVE)
 		*effective = true;
 
+	if (caps->magic_etc & VFS_CAP_REVISION_MASK)
+		*has_cap = true;
+
 	CAP_FOR_EACH_U32(i) {
 		__u32 permitted = caps->permitted.cap[i];
 		__u32 inheritable = caps->inheritable.cap[i];
@@ -424,7 +428,7 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data
  * its xattrs and, if present, apply them to the proposed credentials being
  * constructed by execve().
  */
-static int get_file_caps(struct linux_binprm *bprm, bool *effective)
+static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_cap)
 {
 	struct dentry *dentry;
 	int rc = 0;
@@ -450,7 +454,7 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective)
 		goto out;
 	}
 
-	rc = bprm_caps_from_vfs_caps(&vcaps, bprm, effective);
+	rc = bprm_caps_from_vfs_caps(&vcaps, bprm, effective, has_cap);
 	if (rc == -EINVAL)
 		printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n",
 		       __func__, rc, bprm->filename);
@@ -475,11 +479,11 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
 {
 	const struct cred *old = current_cred();
 	struct cred *new = bprm->cred;
-	bool effective;
+	bool effective, has_cap = false;
 	int ret;
 
 	effective = false;
-	ret = get_file_caps(bprm, &effective);
+	ret = get_file_caps(bprm, &effective, &has_cap);
 	if (ret < 0)
 		return ret;
 
@@ -489,7 +493,7 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
 		 * for a setuid root binary run by a non-root user.  Do set it
 		 * for a root user just to cause least surprise to an admin.
 		 */
-		if (effective && new->uid != 0 && new->euid == 0) {
+		if (has_cap && new->uid != 0 && new->euid == 0) {
 			warn_setuid_and_fcaps_mixed(bprm->filename);
 			goto skip;
 		}
diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig
new file mode 100644
index 000000000000..4bf00acf7937
--- /dev/null
+++ b/security/integrity/Kconfig
@@ -0,0 +1,7 @@
+#
+config INTEGRITY
+	def_bool y
+	depends on IMA || EVM
+
+source security/integrity/ima/Kconfig
+source security/integrity/evm/Kconfig
diff --git a/security/integrity/Makefile b/security/integrity/Makefile
new file mode 100644
index 000000000000..0ae44aea6516
--- /dev/null
+++ b/security/integrity/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for caching inode integrity data (iint)
+#
+
+obj-$(CONFIG_INTEGRITY) += integrity.o
+
+integrity-y := iint.o
+
+subdir-$(CONFIG_IMA)			+= ima
+obj-$(CONFIG_IMA)			+= ima/built-in.o
+subdir-$(CONFIG_EVM)			+= evm
+obj-$(CONFIG_EVM)			+= evm/built-in.o
diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig
new file mode 100644
index 000000000000..afbb59dd262d
--- /dev/null
+++ b/security/integrity/evm/Kconfig
@@ -0,0 +1,13 @@
+config EVM
+	boolean "EVM support"
+	depends on SECURITY && KEYS && (TRUSTED_KEYS=y || TRUSTED_KEYS=n)
+	select CRYPTO_HMAC
+	select CRYPTO_MD5
+	select CRYPTO_SHA1
+	select ENCRYPTED_KEYS
+	default n
+	help
+	  EVM protects a file's security extended attributes against
+	  integrity attacks.
+
+	  If you are unsure how to answer this question, answer N.
diff --git a/security/integrity/evm/Makefile b/security/integrity/evm/Makefile
new file mode 100644
index 000000000000..7393c415a066
--- /dev/null
+++ b/security/integrity/evm/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for building the Extended Verification Module(EVM)
+#
+obj-$(CONFIG_EVM) += evm.o
+
+evm-y := evm_main.o evm_crypto.o evm_secfs.o
+evm-$(CONFIG_FS_POSIX_ACL) += evm_posix_acl.o
diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h
new file mode 100644
index 000000000000..d320f5197437
--- /dev/null
+++ b/security/integrity/evm/evm.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2005-2010 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ * Kylene Hall <kjhall@us.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, version 2 of the License.
+ *
+ * File: evm.h
+ *
+ */
+#include <linux/xattr.h>
+#include <linux/security.h>
+#include "../integrity.h"
+
+extern int evm_initialized;
+extern char *evm_hmac;
+
+extern struct crypto_shash *hmac_tfm;
+
+/* List of EVM protected security xattrs */
+extern char *evm_config_xattrnames[];
+
+extern int evm_init_key(void);
+extern int evm_update_evmxattr(struct dentry *dentry,
+			       const char *req_xattr_name,
+			       const char *req_xattr_value,
+			       size_t req_xattr_value_len);
+extern int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
+			 const char *req_xattr_value,
+			 size_t req_xattr_value_len, char *digest);
+extern int evm_init_hmac(struct inode *inode, const struct xattr *xattr,
+			 char *hmac_val);
+extern int evm_init_secfs(void);
+extern void evm_cleanup_secfs(void);
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
new file mode 100644
index 000000000000..5dd5b140242c
--- /dev/null
+++ b/security/integrity/evm/evm_crypto.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2005-2010 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ * Kylene Hall <kjhall@us.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, version 2 of the License.
+ *
+ * File: evm_crypto.c
+ *	 Using root's kernel master key (kmk), calculate the HMAC
+ */
+
+#include <linux/module.h>
+#include <linux/crypto.h>
+#include <linux/xattr.h>
+#include <keys/encrypted-type.h>
+#include <crypto/hash.h>
+#include "evm.h"
+
+#define EVMKEY "evm-key"
+#define MAX_KEY_SIZE 128
+static unsigned char evmkey[MAX_KEY_SIZE];
+static int evmkey_len = MAX_KEY_SIZE;
+
+struct crypto_shash *hmac_tfm;
+
+static struct shash_desc *init_desc(void)
+{
+	int rc;
+	struct shash_desc *desc;
+
+	if (hmac_tfm == NULL) {
+		hmac_tfm = crypto_alloc_shash(evm_hmac, 0, CRYPTO_ALG_ASYNC);
+		if (IS_ERR(hmac_tfm)) {
+			pr_err("Can not allocate %s (reason: %ld)\n",
+			       evm_hmac, PTR_ERR(hmac_tfm));
+			rc = PTR_ERR(hmac_tfm);
+			hmac_tfm = NULL;
+			return ERR_PTR(rc);
+		}
+	}
+
+	desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(hmac_tfm),
+			GFP_KERNEL);
+	if (!desc)
+		return ERR_PTR(-ENOMEM);
+
+	desc->tfm = hmac_tfm;
+	desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	rc = crypto_shash_setkey(hmac_tfm, evmkey, evmkey_len);
+	if (rc)
+		goto out;
+	rc = crypto_shash_init(desc);
+out:
+	if (rc) {
+		kfree(desc);
+		return ERR_PTR(rc);
+	}
+	return desc;
+}
+
+/* Protect against 'cutting & pasting' security.evm xattr, include inode
+ * specific info.
+ *
+ * (Additional directory/file metadata needs to be added for more complete
+ * protection.)
+ */
+static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
+			  char *digest)
+{
+	struct h_misc {
+		unsigned long ino;
+		__u32 generation;
+		uid_t uid;
+		gid_t gid;
+		umode_t mode;
+	} hmac_misc;
+
+	memset(&hmac_misc, 0, sizeof hmac_misc);
+	hmac_misc.ino = inode->i_ino;
+	hmac_misc.generation = inode->i_generation;
+	hmac_misc.uid = inode->i_uid;
+	hmac_misc.gid = inode->i_gid;
+	hmac_misc.mode = inode->i_mode;
+	crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof hmac_misc);
+	crypto_shash_final(desc, digest);
+}
+
+/*
+ * Calculate the HMAC value across the set of protected security xattrs.
+ *
+ * Instead of retrieving the requested xattr, for performance, calculate
+ * the hmac using the requested xattr value. Don't alloc/free memory for
+ * each xattr, but attempt to re-use the previously allocated memory.
+ */
+int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
+		  const char *req_xattr_value, size_t req_xattr_value_len,
+		  char *digest)
+{
+	struct inode *inode = dentry->d_inode;
+	struct shash_desc *desc;
+	char **xattrname;
+	size_t xattr_size = 0;
+	char *xattr_value = NULL;
+	int error;
+	int size;
+
+	if (!inode->i_op || !inode->i_op->getxattr)
+		return -EOPNOTSUPP;
+	desc = init_desc();
+	if (IS_ERR(desc))
+		return PTR_ERR(desc);
+
+	error = -ENODATA;
+	for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
+		if ((req_xattr_name && req_xattr_value)
+		    && !strcmp(*xattrname, req_xattr_name)) {
+			error = 0;
+			crypto_shash_update(desc, (const u8 *)req_xattr_value,
+					     req_xattr_value_len);
+			continue;
+		}
+		size = vfs_getxattr_alloc(dentry, *xattrname,
+					  &xattr_value, xattr_size, GFP_NOFS);
+		if (size == -ENOMEM) {
+			error = -ENOMEM;
+			goto out;
+		}
+		if (size < 0)
+			continue;
+
+		error = 0;
+		xattr_size = size;
+		crypto_shash_update(desc, (const u8 *)xattr_value, xattr_size);
+	}
+	hmac_add_misc(desc, inode, digest);
+
+out:
+	kfree(xattr_value);
+	kfree(desc);
+	return error;
+}
+
+/*
+ * Calculate the hmac and update security.evm xattr
+ *
+ * Expects to be called with i_mutex locked.
+ */
+int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
+			const char *xattr_value, size_t xattr_value_len)
+{
+	struct inode *inode = dentry->d_inode;
+	struct evm_ima_xattr_data xattr_data;
+	int rc = 0;
+
+	rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
+			   xattr_value_len, xattr_data.digest);
+	if (rc == 0) {
+		xattr_data.type = EVM_XATTR_HMAC;
+		rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_EVM,
+					   &xattr_data,
+					   sizeof(xattr_data), 0);
+	}
+	else if (rc == -ENODATA)
+		rc = inode->i_op->removexattr(dentry, XATTR_NAME_EVM);
+	return rc;
+}
+
+int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr,
+		  char *hmac_val)
+{
+	struct shash_desc *desc;
+
+	desc = init_desc();
+	if (IS_ERR(desc)) {
+		printk(KERN_INFO "init_desc failed\n");
+		return PTR_ERR(desc);
+	}
+
+	crypto_shash_update(desc, lsm_xattr->value, lsm_xattr->value_len);
+	hmac_add_misc(desc, inode, hmac_val);
+	kfree(desc);
+	return 0;
+}
+
+/*
+ * Get the key from the TPM for the SHA1-HMAC
+ */
+int evm_init_key(void)
+{
+	struct key *evm_key;
+	struct encrypted_key_payload *ekp;
+	int rc = 0;
+
+	evm_key = request_key(&key_type_encrypted, EVMKEY, NULL);
+	if (IS_ERR(evm_key))
+		return -ENOENT;
+
+	down_read(&evm_key->sem);
+	ekp = evm_key->payload.data;
+	if (ekp->decrypted_datalen > MAX_KEY_SIZE) {
+		rc = -EINVAL;
+		goto out;
+	}
+	memcpy(evmkey, ekp->decrypted_data, ekp->decrypted_datalen);
+out:
+	/* burn the original key contents */
+	memset(ekp->decrypted_data, 0, ekp->decrypted_datalen);
+	up_read(&evm_key->sem);
+	key_put(evm_key);
+	return rc;
+}
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
new file mode 100644
index 000000000000..92d3d99a9f7b
--- /dev/null
+++ b/security/integrity/evm/evm_main.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2005-2010 IBM Corporation
+ *
+ * Author:
+ * Mimi Zohar <zohar@us.ibm.com>
+ * Kylene Hall <kjhall@us.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, version 2 of the License.
+ *
+ * File: evm_main.c
+ *	implements evm_inode_setxattr, evm_inode_post_setxattr,
+ *	evm_inode_removexattr, and evm_verifyxattr
+ */
+
+#include <linux/module.h>
+#include <linux/crypto.h>
+#include <linux/xattr.h>
+#include <linux/integrity.h>
+#include <linux/evm.h>
+#include <crypto/hash.h>
+#include "evm.h"
+
+int evm_initialized;
+
+char *evm_hmac = "hmac(sha1)";
+
+char *evm_config_xattrnames[] = {
+#ifdef CONFIG_SECURITY_SELINUX
+	XATTR_NAME_SELINUX,
+#endif
+#ifdef CONFIG_SECURITY_SMACK
+	XATTR_NAME_SMACK,
+#endif
+	XATTR_NAME_CAPS,
+	NULL
+};
+
+static int evm_fixmode;
+static int __init evm_set_fixmode(char *str)
+{
+	if (strncmp(str, "fix", 3) == 0)
+		evm_fixmode = 1;
+	return 0;
+}
+__setup("evm=", evm_set_fixmode);
+
+/*
+ * evm_verify_hmac - calculate and compare the HMAC with the EVM xattr
+ *
+ * Compute the HMAC on the dentry's protected set of extended attributes
+ * and compare it against the stored security.evm xattr.
+ *
+ * For performance:
+ * - use the previoulsy retrieved xattr value and length to calculate the
+ *   HMAC.)
+ * - cache the verification result in the iint, when available.
+ *
+ * Returns integrity status
+ */
+static enum integrity_status evm_verify_hmac(struct dentry *dentry,
+					     const char *xattr_name,
+					     char *xattr_value,
+					     size_t xattr_value_len,
+					     struct integrity_iint_cache *iint)
+{
+	struct evm_ima_xattr_data xattr_data;
+	enum integrity_status evm_status = INTEGRITY_PASS;
+	int rc;
+
+	if (iint && iint->evm_status == INTEGRITY_PASS)
+		return iint->evm_status;
+
+	/* if status is not PASS, try to check again - against -ENOMEM */
+
+	rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
+			   xattr_value_len, xattr_data.digest);
+	if (rc < 0) {
+		evm_status = (rc == -ENODATA)
+		    ? INTEGRITY_NOXATTRS : INTEGRITY_FAIL;
+		goto out;
+	}
+
+	xattr_data.type = EVM_XATTR_HMAC;
+	rc = vfs_xattr_cmp(dentry, XATTR_NAME_EVM, (u8 *)&xattr_data,
+			   sizeof xattr_data, GFP_NOFS);
+	if (rc < 0)
+		evm_status = (rc == -ENODATA)
+		    ? INTEGRITY_NOLABEL : INTEGRITY_FAIL;
+out:
+	if (iint)
+		iint->evm_status = evm_status;
+	return evm_status;
+}
+
+static int evm_protected_xattr(const char *req_xattr_name)
+{
+	char **xattrname;
+	int namelen;
+	int found = 0;
+
+	namelen = strlen(req_xattr_name);
+	for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
+		if ((strlen(*xattrname) == namelen)
+		    && (strncmp(req_xattr_name, *xattrname, namelen) == 0)) {
+			found = 1;
+			break;
+		}
+		if (strncmp(req_xattr_name,
+			    *xattrname + XATTR_SECURITY_PREFIX_LEN,
+			    strlen(req_xattr_name)) == 0) {
+			found = 1;
+			break;
+		}
+	}
+	return found;
+}
+
+/**
+ * evm_verifyxattr - verify the integrity of the requested xattr
+ * @dentry: object of the verify xattr
+ * @xattr_name: requested xattr
+ * @xattr_value: requested xattr value
+ * @xattr_value_len: requested xattr value length
+ *
+ * Calculate the HMAC for the given dentry and verify it against the stored
+ * security.evm xattr. For performance, use the xattr value and length
+ * previously retrieved to calculate the HMAC.
+ *
+ * Returns the xattr integrity status.
+ *
+ * This function requires the caller to lock the inode's i_mutex before it
+ * is executed.
+ */
+enum integrity_status evm_verifyxattr(struct dentry *dentry,
+				      const char *xattr_name,
+				      void *xattr_value, size_t xattr_value_len,
+				      struct integrity_iint_cache *iint)
+{
+	if (!evm_initialized || !evm_protected_xattr(xattr_name))
+		return INTEGRITY_UNKNOWN;
+
+	if (!iint) {
+		iint = integrity_iint_find(dentry->d_inode);
+		if (!iint)
+			return INTEGRITY_UNKNOWN;
+	}
+	return evm_verify_hmac(dentry, xattr_name, xattr_value,
+				 xattr_value_len, iint);
+}
+EXPORT_SYMBOL_GPL(evm_verifyxattr);
+
+/*
+ * evm_verify_current_integrity - verify the dentry's metadata integrity
+ * @dentry: pointer to the affected dentry
+ *
+ * Verify and return the dentry's metadata integrity. The exceptions are
+ * before EVM is initialized or in 'fix' mode.
+ */
+static enum integrity_status evm_verify_current_integrity(struct dentry *dentry)
+{
+	struct inode *inode = dentry->d_inode;
+
+	if (!evm_initialized || !S_ISREG(inode->i_mode) || evm_fixmode)
+		return 0;
+	return evm_verify_hmac(dentry, NULL, NULL, 0, NULL);
+}
+
+/*
+ * evm_protect_xattr - protect the EVM extended attribute
+ *
+ * Prevent security.evm from being modified or removed without the
+ * necessary permissions or when the existing value is invalid.
+ *
+ * The posix xattr acls are 'system' prefixed, which normally would not
+ * affect security.evm.  An interesting side affect of writing posix xattr
+ * acls is their modifying of the i_mode, which is included in security.evm.
+ * For posix xattr acls only, permit security.evm, even if it currently
+ * doesn't exist, to be updated.
+ */
+static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
+			     const void *xattr_value, size_t xattr_value_len)
+{
+	enum integrity_status evm_status;
+
+	if (strcmp(xattr_name, XATTR_NAME_EVM) == 0) {
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+	} else if (!evm_protected_xattr(xattr_name)) {
+		if (!posix_xattr_acl(xattr_name))
+			return 0;
+		evm_status = evm_verify_current_integrity(dentry);
+		if ((evm_status == INTEGRITY_PASS) ||
+		    (evm_status == INTEGRITY_NOXATTRS))
+			return 0;
+		return -EPERM;
+	}
+	evm_status = evm_verify_current_integrity(dentry);
+	return evm_status == INTEGRITY_PASS ? 0 : -EPERM;
+}
+
+/**
+ * evm_inode_setxattr - protect the EVM extended attribute
+ * @dentry: pointer to the affected dentry
+ * @xattr_name: pointer to the affected extended attribute name
+ * @xattr_value: pointer to the new extended attribute value
+ * @xattr_value_len: pointer to the new extended attribute value length
+ *
+ * Updating 'security.evm' requires CAP_SYS_ADMIN privileges and that
+ * the current value is valid.
+ */
+int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name,
+		       const void *xattr_value, size_t xattr_value_len)
+{
+	return evm_protect_xattr(dentry, xattr_name, xattr_value,
+				 xattr_value_len);
+}
+
+/**
+ * evm_inode_removexattr - protect the EVM extended attribute
+ * @dentry: pointer to the affected dentry
+ * @xattr_name: pointer to the affected extended attribute name
+ *
+ * Removing 'security.evm' requires CAP_SYS_ADMIN privileges and that
+ * the current value is valid.
+ */
+int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name)
+{
+	return evm_protect_xattr(dentry, xattr_name, NULL, 0);
+}
+
+/**
+ * evm_inode_post_setxattr - update 'security.evm' to reflect the changes
+ * @dentry: pointer to the affected dentry
+ * @xattr_name: pointer to the affected extended attribute name
+ * @xattr_value: pointer to the new extended attribute value
+ * @xattr_value_len: pointer to the new extended attribute value length
+ *
+ * Update the HMAC stored in 'security.evm' to reflect the change.
+ *
+ * No need to take the i_mutex lock here, as this function is called from
+ * __vfs_setxattr_noperm().  The caller of which has taken the inode's
+ * i_mutex lock.
+ */
+void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name,
+			     const void *xattr_value, size_t xattr_value_len)
+{
+	if (!evm_initialized || (!evm_protected_xattr(xattr_name)
+				 && !posix_xattr_acl(xattr_name)))
+		return;
+
+	evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len);
+	return;
+}
+
+/**
+ * evm_inode_post_removexattr - update 'security.evm' after removing the xattr
+ * @dentry: pointer to the affected dentry
+ * @xattr_name: pointer to the affected extended attribute name
+ *
+ * Update the HMAC stored in 'security.evm' to reflect removal of the xattr.
+ */
+void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name)
+{
+	struct inode *inode = dentry->d_inode;
+
+	if (!evm_initialized || !evm_protected_xattr(xattr_name))
+		return;
+
+	mutex_lock(&inode->i_mutex);
+	evm_update_evmxattr(dentry, xattr_name, NULL, 0);
+	mutex_unlock(&inode->i_mutex);
+	return;
+}
+
+/**
+ * evm_inode_setattr - prevent updating an invalid EVM extended attribute
+ * @dentry: pointer to the affected dentry
+ */
+int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
+{
+	unsigned int ia_valid = attr->ia_valid;
+	enum integrity_status evm_status;
+
+	if (!(ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)))
+		return 0;
+	evm_status = evm_verify_current_integrity(dentry);
+	if ((evm_status == INTEGRITY_PASS) ||
+	    (evm_status == INTEGRITY_NOXATTRS))
+		return 0;
+	return -EPERM;
+}
+
+/**
+ * evm_inode_post_setattr - update 'security.evm' after modifying metadata
+ * @dentry: pointer to the affected dentry
+ * @ia_valid: for the UID and GID status
+ *
+ * For now, update the HMAC stored in 'security.evm' to reflect UID/GID
+ * changes.
+ *
+ * This function is called from notify_change(), which expects the caller
+ * to lock the inode's i_mutex.
+ */
+void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
+{
+	if (!evm_initialized)
+		return;
+
+	if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
+		evm_update_evmxattr(dentry, NULL, NULL, 0);
+	return;
+}
+
+/*
+ * evm_inode_init_security - initializes security.evm
+ */
+int evm_inode_init_security(struct inode *inode,
+				 const struct xattr *lsm_xattr,
+				 struct xattr *evm_xattr)
+{
+	struct evm_ima_xattr_data *xattr_data;
+	int rc;
+
+	if (!evm_initialized || !evm_protected_xattr(lsm_xattr->name))
+		return 0;
+
+	xattr_data = kzalloc(sizeof(*xattr_data), GFP_NOFS);
+	if (!xattr_data)
+		return -ENOMEM;
+
+	xattr_data->type = EVM_XATTR_HMAC;
+	rc = evm_init_hmac(inode, lsm_xattr, xattr_data->digest);
+	if (rc < 0)
+		goto out;
+
+	evm_xattr->value = xattr_data;
+	evm_xattr->value_len = sizeof(*xattr_data);
+	evm_xattr->name = kstrdup(XATTR_EVM_SUFFIX, GFP_NOFS);
+	return 0;
+out:
+	kfree(xattr_data);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(evm_inode_init_security);
+
+static int __init init_evm(void)
+{
+	int error;
+
+	error = evm_init_secfs();
+	if (error < 0) {
+		printk(KERN_INFO "EVM: Error registering secfs\n");
+		goto err;
+	}
+err:
+	return error;
+}
+
+static void __exit cleanup_evm(void)
+{
+	evm_cleanup_secfs();
+	if (hmac_tfm)
+		crypto_free_shash(hmac_tfm);
+}
+
+/*
+ * evm_display_config - list the EVM protected security extended attributes
+ */
+static int __init evm_display_config(void)
+{
+	char **xattrname;
+
+	for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++)
+		printk(KERN_INFO "EVM: %s\n", *xattrname);
+	return 0;
+}
+
+pure_initcall(evm_display_config);
+late_initcall(init_evm);
+
+MODULE_DESCRIPTION("Extended Verification Module");
+MODULE_LICENSE("GPL");
diff --git a/security/integrity/evm/evm_posix_acl.c b/security/integrity/evm/evm_posix_acl.c
new file mode 100644
index 000000000000..b1753e98bf9a
--- /dev/null
+++ b/security/integrity/evm/evm_posix_acl.c
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2011 IBM Corporation
+ *
+ * Author:
+ * Mimi Zohar <zohar@us.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, version 2 of the License.
+ */
+
+#include <linux/module.h>
+#include <linux/xattr.h>
+
+int posix_xattr_acl(char *xattr)
+{
+	int xattr_len = strlen(xattr);
+
+	if ((strlen(XATTR_NAME_POSIX_ACL_ACCESS) == xattr_len)
+	     && (strncmp(XATTR_NAME_POSIX_ACL_ACCESS, xattr, xattr_len) == 0))
+		return 1;
+	if ((strlen(XATTR_NAME_POSIX_ACL_DEFAULT) == xattr_len)
+	     && (strncmp(XATTR_NAME_POSIX_ACL_DEFAULT, xattr, xattr_len) == 0))
+		return 1;
+	return 0;
+}
diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c
new file mode 100644
index 000000000000..ac7629950578
--- /dev/null
+++ b/security/integrity/evm/evm_secfs.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.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, version 2 of the License.
+ *
+ * File: evm_secfs.c
+ *	- Used to signal when key is on keyring
+ *	- Get the key and enable EVM
+ */
+
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include "evm.h"
+
+static struct dentry *evm_init_tpm;
+
+/**
+ * evm_read_key - read() for <securityfs>/evm
+ *
+ * @filp: file pointer, not actually used
+ * @buf: where to put the result
+ * @count: maximum to send along
+ * @ppos: where to start
+ *
+ * Returns number of bytes read or error code, as appropriate
+ */
+static ssize_t evm_read_key(struct file *filp, char __user *buf,
+			    size_t count, loff_t *ppos)
+{
+	char temp[80];
+	ssize_t rc;
+
+	if (*ppos != 0)
+		return 0;
+
+	sprintf(temp, "%d", evm_initialized);
+	rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
+
+	return rc;
+}
+
+/**
+ * evm_write_key - write() for <securityfs>/evm
+ * @file: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start
+ *
+ * Used to signal that key is on the kernel key ring.
+ * - get the integrity hmac key from the kernel key ring
+ * - create list of hmac protected extended attributes
+ * Returns number of bytes written or error code, as appropriate
+ */
+static ssize_t evm_write_key(struct file *file, const char __user *buf,
+			     size_t count, loff_t *ppos)
+{
+	char temp[80];
+	int i, error;
+
+	if (!capable(CAP_SYS_ADMIN) || evm_initialized)
+		return -EPERM;
+
+	if (count >= sizeof(temp) || count == 0)
+		return -EINVAL;
+
+	if (copy_from_user(temp, buf, count) != 0)
+		return -EFAULT;
+
+	temp[count] = '\0';
+
+	if ((sscanf(temp, "%d", &i) != 1) || (i != 1))
+		return -EINVAL;
+
+	error = evm_init_key();
+	if (!error) {
+		evm_initialized = 1;
+		pr_info("EVM: initialized\n");
+	} else
+		pr_err("EVM: initialization failed\n");
+	return count;
+}
+
+static const struct file_operations evm_key_ops = {
+	.read		= evm_read_key,
+	.write		= evm_write_key,
+};
+
+int __init evm_init_secfs(void)
+{
+	int error = 0;
+
+	evm_init_tpm = securityfs_create_file("evm", S_IRUSR | S_IRGRP,
+					      NULL, NULL, &evm_key_ops);
+	if (!evm_init_tpm || IS_ERR(evm_init_tpm))
+		error = -EFAULT;
+	return error;
+}
+
+void __exit evm_cleanup_secfs(void)
+{
+	if (evm_init_tpm)
+		securityfs_remove(evm_init_tpm);
+}
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
new file mode 100644
index 000000000000..399641c3e846
--- /dev/null
+++ b/security/integrity/iint.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2008 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.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, version 2 of the
+ * License.
+ *
+ * File: integrity_iint.c
+ *	- implements the integrity hooks: integrity_inode_alloc,
+ *	  integrity_inode_free
+ *	- cache integrity information associated with an inode
+ *	  using a rbtree tree.
+ */
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/rbtree.h>
+#include "integrity.h"
+
+static struct rb_root integrity_iint_tree = RB_ROOT;
+static DEFINE_SPINLOCK(integrity_iint_lock);
+static struct kmem_cache *iint_cache __read_mostly;
+
+int iint_initialized;
+
+/*
+ * __integrity_iint_find - return the iint associated with an inode
+ */
+static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode)
+{
+	struct integrity_iint_cache *iint;
+	struct rb_node *n = integrity_iint_tree.rb_node;
+
+	assert_spin_locked(&integrity_iint_lock);
+
+	while (n) {
+		iint = rb_entry(n, struct integrity_iint_cache, rb_node);
+
+		if (inode < iint->inode)
+			n = n->rb_left;
+		else if (inode > iint->inode)
+			n = n->rb_right;
+		else
+			break;
+	}
+	if (!n)
+		return NULL;
+
+	return iint;
+}
+
+/*
+ * integrity_iint_find - return the iint associated with an inode
+ */
+struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
+{
+	struct integrity_iint_cache *iint;
+
+	if (!IS_IMA(inode))
+		return NULL;
+
+	spin_lock(&integrity_iint_lock);
+	iint = __integrity_iint_find(inode);
+	spin_unlock(&integrity_iint_lock);
+
+	return iint;
+}
+
+static void iint_free(struct integrity_iint_cache *iint)
+{
+	iint->version = 0;
+	iint->flags = 0UL;
+	iint->evm_status = INTEGRITY_UNKNOWN;
+	kmem_cache_free(iint_cache, iint);
+}
+
+/**
+ * integrity_inode_alloc - allocate an iint associated with an inode
+ * @inode: pointer to the inode
+ */
+int integrity_inode_alloc(struct inode *inode)
+{
+	struct rb_node **p;
+	struct rb_node *new_node, *parent = NULL;
+	struct integrity_iint_cache *new_iint, *test_iint;
+	int rc;
+
+	new_iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
+	if (!new_iint)
+		return -ENOMEM;
+
+	new_iint->inode = inode;
+	new_node = &new_iint->rb_node;
+
+	mutex_lock(&inode->i_mutex);	/* i_flags */
+	spin_lock(&integrity_iint_lock);
+
+	p = &integrity_iint_tree.rb_node;
+	while (*p) {
+		parent = *p;
+		test_iint = rb_entry(parent, struct integrity_iint_cache,
+				     rb_node);
+		rc = -EEXIST;
+		if (inode < test_iint->inode)
+			p = &(*p)->rb_left;
+		else if (inode > test_iint->inode)
+			p = &(*p)->rb_right;
+		else
+			goto out_err;
+	}
+
+	inode->i_flags |= S_IMA;
+	rb_link_node(new_node, parent, p);
+	rb_insert_color(new_node, &integrity_iint_tree);
+
+	spin_unlock(&integrity_iint_lock);
+	mutex_unlock(&inode->i_mutex);	/* i_flags */
+
+	return 0;
+out_err:
+	spin_unlock(&integrity_iint_lock);
+	mutex_unlock(&inode->i_mutex);	/* i_flags */
+	iint_free(new_iint);
+
+	return rc;
+}
+
+/**
+ * integrity_inode_free - called on security_inode_free
+ * @inode: pointer to the inode
+ *
+ * Free the integrity information(iint) associated with an inode.
+ */
+void integrity_inode_free(struct inode *inode)
+{
+	struct integrity_iint_cache *iint;
+
+	if (!IS_IMA(inode))
+		return;
+
+	spin_lock(&integrity_iint_lock);
+	iint = __integrity_iint_find(inode);
+	rb_erase(&iint->rb_node, &integrity_iint_tree);
+	spin_unlock(&integrity_iint_lock);
+
+	iint_free(iint);
+}
+
+static void init_once(void *foo)
+{
+	struct integrity_iint_cache *iint = foo;
+
+	memset(iint, 0, sizeof *iint);
+	iint->version = 0;
+	iint->flags = 0UL;
+	mutex_init(&iint->mutex);
+	iint->evm_status = INTEGRITY_UNKNOWN;
+}
+
+static int __init integrity_iintcache_init(void)
+{
+	iint_cache =
+	    kmem_cache_create("iint_cache", sizeof(struct integrity_iint_cache),
+			      0, SLAB_PANIC, init_once);
+	iint_initialized = 1;
+	return 0;
+}
+security_initcall(integrity_iintcache_init);
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index b6ecfd4d8d78..19c053b82303 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -3,6 +3,7 @@
 config IMA
 	bool "Integrity Measurement Architecture(IMA)"
 	depends on SECURITY
+	select INTEGRITY
 	select SECURITYFS
 	select CRYPTO
 	select CRYPTO_HMAC
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index 787c4cb916cd..5690c021de8f 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -6,4 +6,4 @@
 obj-$(CONFIG_IMA) += ima.o
 
 ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
-	 ima_policy.o ima_iint.o ima_audit.o
+	 ima_policy.o ima_audit.o
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 08408bd71462..3ccf7acac6df 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -24,18 +24,19 @@
 #include <linux/tpm.h>
 #include <linux/audit.h>
 
+#include "../integrity.h"
+
 enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_ASCII };
 enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
 
 /* digest size for IMA, fits SHA1 or MD5 */
-#define IMA_DIGEST_SIZE		20
+#define IMA_DIGEST_SIZE		SHA1_DIGEST_SIZE
 #define IMA_EVENT_NAME_LEN_MAX	255
 
 #define IMA_HASH_BITS 9
 #define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS)
 
 /* set during initialization */
-extern int iint_initialized;
 extern int ima_initialized;
 extern int ima_used_chip;
 extern char *ima_hash;
@@ -96,34 +97,21 @@ static inline unsigned long ima_hash_key(u8 *digest)
 	return hash_long(*digest, IMA_HASH_BITS);
 }
 
-/* iint cache flags */
-#define IMA_MEASURED		0x01
-
-/* integrity data associated with an inode */
-struct ima_iint_cache {
-	struct rb_node rb_node; /* rooted in ima_iint_tree */
-	struct inode *inode;	/* back pointer to inode in question */
-	u64 version;		/* track inode changes */
-	unsigned char flags;
-	u8 digest[IMA_DIGEST_SIZE];
-	struct mutex mutex;	/* protects: version, flags, digest */
-};
-
 /* LIM API function definitions */
 int ima_must_measure(struct inode *inode, int mask, int function);
-int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file);
-void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
+int ima_collect_measurement(struct integrity_iint_cache *iint,
+			    struct file *file);
+void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
 			   const unsigned char *filename);
 int ima_store_template(struct ima_template_entry *entry, int violation,
 		       struct inode *inode);
-void ima_template_show(struct seq_file *m, void *e,
-		       enum ima_show_type show);
+void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show);
 
 /* rbtree tree calls to lookup, insert, delete
  * integrity data associated with an inode.
  */
-struct ima_iint_cache *ima_iint_insert(struct inode *inode);
-struct ima_iint_cache *ima_iint_find(struct inode *inode);
+struct integrity_iint_cache *integrity_iint_insert(struct inode *inode);
+struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
 
 /* IMA policy related functions */
 enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK };
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index da36d2c085a4..0d50df04ccc4 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -126,7 +126,8 @@ int ima_must_measure(struct inode *inode, int mask, int function)
  *
  * Return 0 on success, error code otherwise
  */
-int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file)
+int ima_collect_measurement(struct integrity_iint_cache *iint,
+			    struct file *file)
 {
 	int result = -EEXIST;
 
@@ -156,8 +157,8 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file)
  *
  * Must be called with iint->mutex held.
  */
-void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
-			   const unsigned char *filename)
+void ima_store_measurement(struct integrity_iint_cache *iint,
+			   struct file *file, const unsigned char *filename)
 {
 	const char *op = "add_template_measure";
 	const char *audit_cause = "ENOMEM";
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index ef21b96a0b42..e1aa2b482dd2 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -287,7 +287,7 @@ static atomic_t policy_opencount = ATOMIC_INIT(1);
 /*
  * ima_open_policy: sequentialize access to the policy file
  */
-int ima_open_policy(struct inode * inode, struct file * filp)
+static int ima_open_policy(struct inode * inode, struct file * filp)
 {
 	/* No point in being allowed to open it if you aren't going to write */
 	if (!(filp->f_flags & O_WRONLY))
diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c
deleted file mode 100644
index 4ae73040ab7b..000000000000
--- a/security/integrity/ima/ima_iint.c
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2008 IBM Corporation
- *
- * Authors:
- * Mimi Zohar <zohar@us.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, version 2 of the
- * License.
- *
- * File: ima_iint.c
- * 	- implements the IMA hooks: ima_inode_alloc, ima_inode_free
- *	- cache integrity information associated with an inode
- *	  using a rbtree tree.
- */
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-#include <linux/rbtree.h>
-#include "ima.h"
-
-static struct rb_root ima_iint_tree = RB_ROOT;
-static DEFINE_SPINLOCK(ima_iint_lock);
-static struct kmem_cache *iint_cache __read_mostly;
-
-int iint_initialized = 0;
-
-/*
- * __ima_iint_find - return the iint associated with an inode
- */
-static struct ima_iint_cache *__ima_iint_find(struct inode *inode)
-{
-	struct ima_iint_cache *iint;
-	struct rb_node *n = ima_iint_tree.rb_node;
-
-	assert_spin_locked(&ima_iint_lock);
-
-	while (n) {
-		iint = rb_entry(n, struct ima_iint_cache, rb_node);
-
-		if (inode < iint->inode)
-			n = n->rb_left;
-		else if (inode > iint->inode)
-			n = n->rb_right;
-		else
-			break;
-	}
-	if (!n)
-		return NULL;
-
-	return iint;
-}
-
-/*
- * ima_iint_find - return the iint associated with an inode
- */
-struct ima_iint_cache *ima_iint_find(struct inode *inode)
-{
-	struct ima_iint_cache *iint;
-
-	if (!IS_IMA(inode))
-		return NULL;
-
-	spin_lock(&ima_iint_lock);
-	iint = __ima_iint_find(inode);
-	spin_unlock(&ima_iint_lock);
-
-	return iint;
-}
-
-static void iint_free(struct ima_iint_cache *iint)
-{
-	iint->version = 0;
-	iint->flags = 0UL;
-	kmem_cache_free(iint_cache, iint);
-}
-
-/**
- * ima_inode_alloc - allocate an iint associated with an inode
- * @inode: pointer to the inode
- */
-int ima_inode_alloc(struct inode *inode)
-{
-	struct rb_node **p;
-	struct rb_node *new_node, *parent = NULL;
-	struct ima_iint_cache *new_iint, *test_iint;
-	int rc;
-
-	new_iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
-	if (!new_iint)
-		return -ENOMEM;
-
-	new_iint->inode = inode;
-	new_node = &new_iint->rb_node;
-
-	mutex_lock(&inode->i_mutex); /* i_flags */
-	spin_lock(&ima_iint_lock);
-
-	p = &ima_iint_tree.rb_node;
-	while (*p) {
-		parent = *p;
-		test_iint = rb_entry(parent, struct ima_iint_cache, rb_node);
-
-		rc = -EEXIST;
-		if (inode < test_iint->inode)
-			p = &(*p)->rb_left;
-		else if (inode > test_iint->inode)
-			p = &(*p)->rb_right;
-		else
-			goto out_err;
-	}
-
-	inode->i_flags |= S_IMA;
-	rb_link_node(new_node, parent, p);
-	rb_insert_color(new_node, &ima_iint_tree);
-
-	spin_unlock(&ima_iint_lock);
-	mutex_unlock(&inode->i_mutex); /* i_flags */
-
-	return 0;
-out_err:
-	spin_unlock(&ima_iint_lock);
-	mutex_unlock(&inode->i_mutex); /* i_flags */
-	iint_free(new_iint);
-
-	return rc;
-}
-
-/**
- * ima_inode_free - called on security_inode_free
- * @inode: pointer to the inode
- *
- * Free the integrity information(iint) associated with an inode.
- */
-void ima_inode_free(struct inode *inode)
-{
-	struct ima_iint_cache *iint;
-
-	if (!IS_IMA(inode))
-		return;
-
-	spin_lock(&ima_iint_lock);
-	iint = __ima_iint_find(inode);
-	rb_erase(&iint->rb_node, &ima_iint_tree);
-	spin_unlock(&ima_iint_lock);
-
-	iint_free(iint);
-}
-
-static void init_once(void *foo)
-{
-	struct ima_iint_cache *iint = foo;
-
-	memset(iint, 0, sizeof *iint);
-	iint->version = 0;
-	iint->flags = 0UL;
-	mutex_init(&iint->mutex);
-}
-
-static int __init ima_iintcache_init(void)
-{
-	iint_cache =
-	    kmem_cache_create("iint_cache", sizeof(struct ima_iint_cache), 0,
-			      SLAB_PANIC, init_once);
-	iint_initialized = 1;
-	return 0;
-}
-security_initcall(ima_iintcache_init);
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 26b46ff74663..1eff5cb001e5 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -22,6 +22,7 @@
 #include <linux/mount.h>
 #include <linux/mman.h>
 #include <linux/slab.h>
+#include <linux/ima.h>
 
 #include "ima.h"
 
@@ -82,7 +83,7 @@ out:
 				  "open_writers");
 }
 
-static void ima_check_last_writer(struct ima_iint_cache *iint,
+static void ima_check_last_writer(struct integrity_iint_cache *iint,
 				  struct inode *inode,
 				  struct file *file)
 {
@@ -105,12 +106,12 @@ static void ima_check_last_writer(struct ima_iint_cache *iint,
 void ima_file_free(struct file *file)
 {
 	struct inode *inode = file->f_dentry->d_inode;
-	struct ima_iint_cache *iint;
+	struct integrity_iint_cache *iint;
 
 	if (!iint_initialized || !S_ISREG(inode->i_mode))
 		return;
 
-	iint = ima_iint_find(inode);
+	iint = integrity_iint_find(inode);
 	if (!iint)
 		return;
 
@@ -121,7 +122,7 @@ static int process_measurement(struct file *file, const unsigned char *filename,
 			       int mask, int function)
 {
 	struct inode *inode = file->f_dentry->d_inode;
-	struct ima_iint_cache *iint;
+	struct integrity_iint_cache *iint;
 	int rc = 0;
 
 	if (!ima_initialized || !S_ISREG(inode->i_mode))
@@ -131,9 +132,9 @@ static int process_measurement(struct file *file, const unsigned char *filename,
 	if (rc != 0)
 		return rc;
 retry:
-	iint = ima_iint_find(inode);
+	iint = integrity_iint_find(inode);
 	if (!iint) {
-		rc = ima_inode_alloc(inode);
+		rc = integrity_inode_alloc(inode);
 		if (!rc || rc == -EEXIST)
 			goto retry;
 		return rc;
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
new file mode 100644
index 000000000000..3143a3c39868
--- /dev/null
+++ b/security/integrity/integrity.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2009-2010 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.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, version 2 of the
+ * License.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/integrity.h>
+#include <crypto/sha.h>
+
+/* iint cache flags */
+#define IMA_MEASURED		0x01
+
+enum evm_ima_xattr_type {
+	IMA_XATTR_DIGEST = 0x01,
+	EVM_XATTR_HMAC,
+	EVM_IMA_XATTR_DIGSIG,
+};
+
+struct evm_ima_xattr_data {
+	u8 type;
+	u8 digest[SHA1_DIGEST_SIZE];
+}  __attribute__((packed));
+
+/* integrity data associated with an inode */
+struct integrity_iint_cache {
+	struct rb_node rb_node; /* rooted in integrity_iint_tree */
+	struct inode *inode;	/* back pointer to inode in question */
+	u64 version;		/* track inode changes */
+	unsigned char flags;
+	u8 digest[SHA1_DIGEST_SIZE];
+	struct mutex mutex;	/* protects: version, flags, digest */
+	enum integrity_status evm_status;
+};
+
+/* rbtree tree calls to lookup, insert, delete
+ * integrity data associated with an inode.
+ */
+struct integrity_iint_cache *integrity_iint_insert(struct inode *inode);
+struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
+
+/* set during initialization */
+extern int iint_initialized;
diff --git a/security/keys/Makefile b/security/keys/Makefile
index b34cc6ee6900..a56f1ffdc64d 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -14,7 +14,7 @@ obj-y := \
 	user_defined.o
 
 obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
-obj-$(CONFIG_ENCRYPTED_KEYS) += ecryptfs_format.o encrypted.o
+obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
 obj-$(CONFIG_KEYS_COMPAT) += compat.o
 obj-$(CONFIG_PROC_FS) += proc.o
 obj-$(CONFIG_SYSCTL) += sysctl.o
diff --git a/security/keys/encrypted-keys/Makefile b/security/keys/encrypted-keys/Makefile
new file mode 100644
index 000000000000..6bc7a86d1027
--- /dev/null
+++ b/security/keys/encrypted-keys/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for encrypted keys
+#
+
+obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted.o ecryptfs_format.o
+obj-$(CONFIG_TRUSTED_KEYS) += masterkey_trusted.o
diff --git a/security/keys/ecryptfs_format.c b/security/keys/encrypted-keys/ecryptfs_format.c
index 6daa3b6ff9ed..6daa3b6ff9ed 100644
--- a/security/keys/ecryptfs_format.c
+++ b/security/keys/encrypted-keys/ecryptfs_format.c
diff --git a/security/keys/ecryptfs_format.h b/security/keys/encrypted-keys/ecryptfs_format.h
index 40294de238bb..40294de238bb 100644
--- a/security/keys/ecryptfs_format.h
+++ b/security/keys/encrypted-keys/ecryptfs_format.h
diff --git a/security/keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
index e7eca9ec4c65..f33804c1b4c8 100644
--- a/security/keys/encrypted.c
+++ b/security/keys/encrypted-keys/encrypted.c
@@ -299,31 +299,6 @@ out:
 }
 
 /*
- * request_trusted_key - request the trusted key
- *
- * Trusted keys are sealed to PCRs and other metadata. Although userspace
- * manages both trusted/encrypted key-types, like the encrypted key type
- * data, trusted key type data is not visible decrypted from userspace.
- */
-static struct key *request_trusted_key(const char *trusted_desc,
-				       u8 **master_key, size_t *master_keylen)
-{
-	struct trusted_key_payload *tpayload;
-	struct key *tkey;
-
-	tkey = request_key(&key_type_trusted, trusted_desc, NULL);
-	if (IS_ERR(tkey))
-		goto error;
-
-	down_read(&tkey->sem);
-	tpayload = rcu_dereference(tkey->payload.data);
-	*master_key = tpayload->key;
-	*master_keylen = tpayload->key_len;
-error:
-	return tkey;
-}
-
-/*
  * request_user_key - request the user key
  *
  * Use a user provided key to encrypt/decrypt an encrypted-key.
@@ -469,8 +444,14 @@ static struct key *request_master_key(struct encrypted_key_payload *epayload,
 		goto out;
 
 	if (IS_ERR(mkey)) {
-		pr_info("encrypted_key: key %s not found",
-			epayload->master_desc);
+		int ret = PTR_ERR(epayload);
+
+		if (ret == -ENOTSUPP)
+			pr_info("encrypted_key: key %s not supported",
+				epayload->master_desc);
+		else
+			pr_info("encrypted_key: key %s not found",
+				epayload->master_desc);
 		goto out;
 	}
 
@@ -686,11 +667,19 @@ static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
 		return -EINVAL;
 
 	hex_encoded_data = hex_encoded_iv + (2 * ivsize) + 2;
-	hex2bin(epayload->iv, hex_encoded_iv, ivsize);
-	hex2bin(epayload->encrypted_data, hex_encoded_data, encrypted_datalen);
+	ret = hex2bin(epayload->iv, hex_encoded_iv, ivsize);
+	if (ret < 0)
+		return -EINVAL;
+	ret = hex2bin(epayload->encrypted_data, hex_encoded_data,
+		      encrypted_datalen);
+	if (ret < 0)
+		return -EINVAL;
 
 	hmac = epayload->format + epayload->datablob_len;
-	hex2bin(hmac, hex_encoded_data + (encrypted_datalen * 2), HASH_SIZE);
+	ret = hex2bin(hmac, hex_encoded_data + (encrypted_datalen * 2),
+		      HASH_SIZE);
+	if (ret < 0)
+		return -EINVAL;
 
 	mkey = request_master_key(epayload, &master_key, &master_keylen);
 	if (IS_ERR(mkey))
diff --git a/security/keys/encrypted.h b/security/keys/encrypted-keys/encrypted.h
index cef5e2f2b7d1..b6ade8945250 100644
--- a/security/keys/encrypted.h
+++ b/security/keys/encrypted-keys/encrypted.h
@@ -2,6 +2,17 @@
 #define __ENCRYPTED_KEY_H
 
 #define ENCRYPTED_DEBUG 0
+#ifdef CONFIG_TRUSTED_KEYS
+extern struct key *request_trusted_key(const char *trusted_desc,
+				       u8 **master_key, size_t *master_keylen);
+#else
+static inline struct key *request_trusted_key(const char *trusted_desc,
+					      u8 **master_key,
+					      size_t *master_keylen)
+{
+	return ERR_PTR(-EOPNOTSUPP);
+}
+#endif
 
 #if ENCRYPTED_DEBUG
 static inline void dump_master_key(const u8 *master_key, size_t master_keylen)
diff --git a/security/keys/encrypted-keys/masterkey_trusted.c b/security/keys/encrypted-keys/masterkey_trusted.c
new file mode 100644
index 000000000000..df87272e3f51
--- /dev/null
+++ b/security/keys/encrypted-keys/masterkey_trusted.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010 IBM Corporation
+ * Copyright (C) 2010 Politecnico di Torino, Italy
+ *                    TORSEC group -- http://security.polito.it
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ * Roberto Sassu <roberto.sassu@polito.it>
+ *
+ * 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.
+ *
+ * See Documentation/security/keys-trusted-encrypted.txt
+ */
+
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <keys/trusted-type.h>
+
+/*
+ * request_trusted_key - request the trusted key
+ *
+ * Trusted keys are sealed to PCRs and other metadata. Although userspace
+ * manages both trusted/encrypted key-types, like the encrypted key type
+ * data, trusted key type data is not visible decrypted from userspace.
+ */
+struct key *request_trusted_key(const char *trusted_desc,
+				u8 **master_key, size_t *master_keylen)
+{
+	struct trusted_key_payload *tpayload;
+	struct key *tkey;
+
+	tkey = request_key(&key_type_trusted, trusted_desc, NULL);
+	if (IS_ERR(tkey))
+		goto error;
+
+	down_read(&tkey->sem);
+	tpayload = rcu_dereference(tkey->payload.data);
+	*master_key = tpayload->key;
+	*master_keylen = tpayload->key_len;
+error:
+	return tkey;
+}
diff --git a/security/keys/gc.c b/security/keys/gc.c
index 89df6b5f203c..bf4d8da5a795 100644
--- a/security/keys/gc.c
+++ b/security/keys/gc.c
@@ -1,6 +1,6 @@
 /* Key garbage collector
  *
- * Copyright (C) 2009 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2009-2011 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
  * This program is free software; you can redistribute it and/or
@@ -10,6 +10,8 @@
  */
 
 #include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/security.h>
 #include <keys/keyring-type.h>
 #include "internal.h"
 
@@ -19,17 +21,33 @@
 unsigned key_gc_delay = 5 * 60;
 
 /*
- * Reaper
+ * Reaper for unused keys.
+ */
+static void key_garbage_collector(struct work_struct *work);
+DECLARE_WORK(key_gc_work, key_garbage_collector);
+
+/*
+ * Reaper for links from keyrings to dead keys.
  */
 static void key_gc_timer_func(unsigned long);
-static void key_garbage_collector(struct work_struct *);
 static DEFINE_TIMER(key_gc_timer, key_gc_timer_func, 0, 0);
-static DECLARE_WORK(key_gc_work, key_garbage_collector);
-static key_serial_t key_gc_cursor; /* the last key the gc considered */
-static bool key_gc_again;
-static unsigned long key_gc_executing;
+
 static time_t key_gc_next_run = LONG_MAX;
-static time_t key_gc_new_timer;
+static struct key_type *key_gc_dead_keytype;
+
+static unsigned long key_gc_flags;
+#define KEY_GC_KEY_EXPIRED	0	/* A key expired and needs unlinking */
+#define KEY_GC_REAP_KEYTYPE	1	/* A keytype is being unregistered */
+#define KEY_GC_REAPING_KEYTYPE	2	/* Cleared when keytype reaped */
+
+
+/*
+ * Any key whose type gets unregistered will be re-typed to this if it can't be
+ * immediately unlinked.
+ */
+struct key_type key_type_dead = {
+	.name = "dead",
+};
 
 /*
  * Schedule a garbage collection run.
@@ -42,31 +60,75 @@ void key_schedule_gc(time_t gc_at)
 
 	kenter("%ld", gc_at - now);
 
-	if (gc_at <= now) {
-		schedule_work(&key_gc_work);
+	if (gc_at <= now || test_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags)) {
+		kdebug("IMMEDIATE");
+		queue_work(system_nrt_wq, &key_gc_work);
 	} else if (gc_at < key_gc_next_run) {
+		kdebug("DEFERRED");
+		key_gc_next_run = gc_at;
 		expires = jiffies + (gc_at - now) * HZ;
 		mod_timer(&key_gc_timer, expires);
 	}
 }
 
 /*
- * The garbage collector timer kicked off
+ * Some key's cleanup time was met after it expired, so we need to get the
+ * reaper to go through a cycle finding expired keys.
  */
 static void key_gc_timer_func(unsigned long data)
 {
 	kenter("");
 	key_gc_next_run = LONG_MAX;
-	schedule_work(&key_gc_work);
+	set_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags);
+	queue_work(system_nrt_wq, &key_gc_work);
+}
+
+/*
+ * wait_on_bit() sleep function for uninterruptible waiting
+ */
+static int key_gc_wait_bit(void *flags)
+{
+	schedule();
+	return 0;
+}
+
+/*
+ * Reap keys of dead type.
+ *
+ * We use three flags to make sure we see three complete cycles of the garbage
+ * collector: the first to mark keys of that type as being dead, the second to
+ * collect dead links and the third to clean up the dead keys.  We have to be
+ * careful as there may already be a cycle in progress.
+ *
+ * The caller must be holding key_types_sem.
+ */
+void key_gc_keytype(struct key_type *ktype)
+{
+	kenter("%s", ktype->name);
+
+	key_gc_dead_keytype = ktype;
+	set_bit(KEY_GC_REAPING_KEYTYPE, &key_gc_flags);
+	smp_mb();
+	set_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags);
+
+	kdebug("schedule");
+	queue_work(system_nrt_wq, &key_gc_work);
+
+	kdebug("sleep");
+	wait_on_bit(&key_gc_flags, KEY_GC_REAPING_KEYTYPE, key_gc_wait_bit,
+		    TASK_UNINTERRUPTIBLE);
+
+	key_gc_dead_keytype = NULL;
+	kleave("");
 }
 
 /*
  * Garbage collect pointers from a keyring.
  *
- * Return true if we altered the keyring.
+ * Not called with any locks held.  The keyring's key struct will not be
+ * deallocated under us as only our caller may deallocate it.
  */
-static bool key_gc_keyring(struct key *keyring, time_t limit)
-	__releases(key_serial_lock)
+static void key_gc_keyring(struct key *keyring, time_t limit)
 {
 	struct keyring_list *klist;
 	struct key *key;
@@ -93,130 +155,234 @@ static bool key_gc_keyring(struct key *keyring, time_t limit)
 unlock_dont_gc:
 	rcu_read_unlock();
 dont_gc:
-	kleave(" = false");
-	return false;
+	kleave(" [no gc]");
+	return;
 
 do_gc:
 	rcu_read_unlock();
-	key_gc_cursor = keyring->serial;
-	key_get(keyring);
-	spin_unlock(&key_serial_lock);
+
 	keyring_gc(keyring, limit);
-	key_put(keyring);
-	kleave(" = true");
-	return true;
+	kleave(" [gc]");
 }
 
 /*
- * Garbage collector for keys.  This involves scanning the keyrings for dead,
- * expired and revoked keys that have overstayed their welcome
+ * Garbage collect an unreferenced, detached key
  */
-static void key_garbage_collector(struct work_struct *work)
+static noinline void key_gc_unused_key(struct key *key)
 {
-	struct rb_node *rb;
-	key_serial_t cursor;
-	struct key *key, *xkey;
-	time_t new_timer = LONG_MAX, limit, now;
-
-	now = current_kernel_time().tv_sec;
-	kenter("[%x,%ld]", key_gc_cursor, key_gc_new_timer - now);
-
-	if (test_and_set_bit(0, &key_gc_executing)) {
-		key_schedule_gc(current_kernel_time().tv_sec + 1);
-		kleave(" [busy; deferring]");
-		return;
+	key_check(key);
+
+	security_key_free(key);
+
+	/* deal with the user's key tracking and quota */
+	if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
+		spin_lock(&key->user->lock);
+		key->user->qnkeys--;
+		key->user->qnbytes -= key->quotalen;
+		spin_unlock(&key->user->lock);
 	}
 
-	limit = now;
+	atomic_dec(&key->user->nkeys);
+	if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
+		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);
+}
+
+/*
+ * Garbage collector for unused keys.
+ *
+ * This is done in process context so that we don't have to disable interrupts
+ * all over the place.  key_put() schedules this rather than trying to do the
+ * cleanup itself, which means key_put() doesn't have to sleep.
+ */
+static void key_garbage_collector(struct work_struct *work)
+{
+	static u8 gc_state;		/* Internal persistent state */
+#define KEY_GC_REAP_AGAIN	0x01	/* - Need another cycle */
+#define KEY_GC_REAPING_LINKS	0x02	/* - We need to reap links */
+#define KEY_GC_SET_TIMER	0x04	/* - We need to restart the timer */
+#define KEY_GC_REAPING_DEAD_1	0x10	/* - We need to mark dead keys */
+#define KEY_GC_REAPING_DEAD_2	0x20	/* - We need to reap dead key links */
+#define KEY_GC_REAPING_DEAD_3	0x40	/* - We need to reap dead keys */
+#define KEY_GC_FOUND_DEAD_KEY	0x80	/* - We found at least one dead key */
+
+	struct rb_node *cursor;
+	struct key *key;
+	time_t new_timer, limit;
+
+	kenter("[%lx,%x]", key_gc_flags, gc_state);
+
+	limit = current_kernel_time().tv_sec;
 	if (limit > key_gc_delay)
 		limit -= key_gc_delay;
 	else
 		limit = key_gc_delay;
 
+	/* Work out what we're going to be doing in this pass */
+	gc_state &= KEY_GC_REAPING_DEAD_1 | KEY_GC_REAPING_DEAD_2;
+	gc_state <<= 1;
+	if (test_and_clear_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags))
+		gc_state |= KEY_GC_REAPING_LINKS | KEY_GC_SET_TIMER;
+
+	if (test_and_clear_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags))
+		gc_state |= KEY_GC_REAPING_DEAD_1;
+	kdebug("new pass %x", gc_state);
+
+	new_timer = LONG_MAX;
+
+	/* As only this function is permitted to remove things from the key
+	 * serial tree, if cursor is non-NULL then it will always point to a
+	 * valid node in the tree - even if lock got dropped.
+	 */
 	spin_lock(&key_serial_lock);
+	cursor = rb_first(&key_serial_tree);
 
-	if (unlikely(RB_EMPTY_ROOT(&key_serial_tree))) {
-		spin_unlock(&key_serial_lock);
-		clear_bit(0, &key_gc_executing);
-		return;
-	}
+continue_scanning:
+	while (cursor) {
+		key = rb_entry(cursor, struct key, serial_node);
+		cursor = rb_next(cursor);
 
-	cursor = key_gc_cursor;
-	if (cursor < 0)
-		cursor = 0;
-	if (cursor > 0)
-		new_timer = key_gc_new_timer;
-	else
-		key_gc_again = false;
-
-	/* find the first key above the cursor */
-	key = NULL;
-	rb = key_serial_tree.rb_node;
-	while (rb) {
-		xkey = rb_entry(rb, struct key, serial_node);
-		if (cursor < xkey->serial) {
-			key = xkey;
-			rb = rb->rb_left;
-		} else if (cursor > xkey->serial) {
-			rb = rb->rb_right;
-		} else {
-			rb = rb_next(rb);
-			if (!rb)
-				goto reached_the_end;
-			key = rb_entry(rb, struct key, serial_node);
-			break;
+		if (atomic_read(&key->usage) == 0)
+			goto found_unreferenced_key;
+
+		if (unlikely(gc_state & KEY_GC_REAPING_DEAD_1)) {
+			if (key->type == key_gc_dead_keytype) {
+				gc_state |= KEY_GC_FOUND_DEAD_KEY;
+				set_bit(KEY_FLAG_DEAD, &key->flags);
+				key->perm = 0;
+				goto skip_dead_key;
+			}
+		}
+
+		if (gc_state & KEY_GC_SET_TIMER) {
+			if (key->expiry > limit && key->expiry < new_timer) {
+				kdebug("will expire %x in %ld",
+				       key_serial(key), key->expiry - limit);
+				new_timer = key->expiry;
+			}
 		}
-	}
 
-	if (!key)
-		goto reached_the_end;
+		if (unlikely(gc_state & KEY_GC_REAPING_DEAD_2))
+			if (key->type == key_gc_dead_keytype)
+				gc_state |= KEY_GC_FOUND_DEAD_KEY;
 
-	/* trawl through the keys looking for keyrings */
-	for (;;) {
-		if (key->expiry > limit && key->expiry < new_timer) {
-			kdebug("will expire %x in %ld",
-			       key_serial(key), key->expiry - limit);
-			new_timer = key->expiry;
+		if ((gc_state & KEY_GC_REAPING_LINKS) ||
+		    unlikely(gc_state & KEY_GC_REAPING_DEAD_2)) {
+			if (key->type == &key_type_keyring)
+				goto found_keyring;
 		}
 
-		if (key->type == &key_type_keyring &&
-		    key_gc_keyring(key, limit))
-			/* the gc had to release our lock so that the keyring
-			 * could be modified, so we have to get it again */
-			goto gc_released_our_lock;
+		if (unlikely(gc_state & KEY_GC_REAPING_DEAD_3))
+			if (key->type == key_gc_dead_keytype)
+				goto destroy_dead_key;
 
-		rb = rb_next(&key->serial_node);
-		if (!rb)
-			goto reached_the_end;
-		key = rb_entry(rb, struct key, serial_node);
+	skip_dead_key:
+		if (spin_is_contended(&key_serial_lock) || need_resched())
+			goto contended;
 	}
 
-gc_released_our_lock:
-	kdebug("gc_released_our_lock");
-	key_gc_new_timer = new_timer;
-	key_gc_again = true;
-	clear_bit(0, &key_gc_executing);
-	schedule_work(&key_gc_work);
-	kleave(" [continue]");
-	return;
-
-	/* when we reach the end of the run, we set the timer for the next one */
-reached_the_end:
-	kdebug("reached_the_end");
+contended:
 	spin_unlock(&key_serial_lock);
-	key_gc_new_timer = new_timer;
-	key_gc_cursor = 0;
-	clear_bit(0, &key_gc_executing);
-
-	if (key_gc_again) {
-		/* there may have been a key that expired whilst we were
-		 * scanning, so if we discarded any links we should do another
-		 * scan */
-		new_timer = now + 1;
-		key_schedule_gc(new_timer);
-	} else if (new_timer < LONG_MAX) {
+
+maybe_resched:
+	if (cursor) {
+		cond_resched();
+		spin_lock(&key_serial_lock);
+		goto continue_scanning;
+	}
+
+	/* We've completed the pass.  Set the timer if we need to and queue a
+	 * new cycle if necessary.  We keep executing cycles until we find one
+	 * where we didn't reap any keys.
+	 */
+	kdebug("pass complete");
+
+	if (gc_state & KEY_GC_SET_TIMER && new_timer != (time_t)LONG_MAX) {
 		new_timer += key_gc_delay;
 		key_schedule_gc(new_timer);
 	}
-	kleave(" [end]");
+
+	if (unlikely(gc_state & KEY_GC_REAPING_DEAD_2)) {
+		/* Make sure everyone revalidates their keys if we marked a
+		 * bunch as being dead and make sure all keyring ex-payloads
+		 * are destroyed.
+		 */
+		kdebug("dead sync");
+		synchronize_rcu();
+	}
+
+	if (unlikely(gc_state & (KEY_GC_REAPING_DEAD_1 |
+				 KEY_GC_REAPING_DEAD_2))) {
+		if (!(gc_state & KEY_GC_FOUND_DEAD_KEY)) {
+			/* No remaining dead keys: short circuit the remaining
+			 * keytype reap cycles.
+			 */
+			kdebug("dead short");
+			gc_state &= ~(KEY_GC_REAPING_DEAD_1 | KEY_GC_REAPING_DEAD_2);
+			gc_state |= KEY_GC_REAPING_DEAD_3;
+		} else {
+			gc_state |= KEY_GC_REAP_AGAIN;
+		}
+	}
+
+	if (unlikely(gc_state & KEY_GC_REAPING_DEAD_3)) {
+		kdebug("dead wake");
+		smp_mb();
+		clear_bit(KEY_GC_REAPING_KEYTYPE, &key_gc_flags);
+		wake_up_bit(&key_gc_flags, KEY_GC_REAPING_KEYTYPE);
+	}
+
+	if (gc_state & KEY_GC_REAP_AGAIN)
+		queue_work(system_nrt_wq, &key_gc_work);
+	kleave(" [end %x]", gc_state);
+	return;
+
+	/* We found an unreferenced key - once we've removed it from the tree,
+	 * we can safely drop the lock.
+	 */
+found_unreferenced_key:
+	kdebug("unrefd key %d", key->serial);
+	rb_erase(&key->serial_node, &key_serial_tree);
+	spin_unlock(&key_serial_lock);
+
+	key_gc_unused_key(key);
+	gc_state |= KEY_GC_REAP_AGAIN;
+	goto maybe_resched;
+
+	/* We found a keyring and we need to check the payload for links to
+	 * dead or expired keys.  We don't flag another reap immediately as we
+	 * have to wait for the old payload to be destroyed by RCU before we
+	 * can reap the keys to which it refers.
+	 */
+found_keyring:
+	spin_unlock(&key_serial_lock);
+	kdebug("scan keyring %d", key->serial);
+	key_gc_keyring(key, limit);
+	goto maybe_resched;
+
+	/* We found a dead key that is still referenced.  Reset its type and
+	 * destroy its payload with its semaphore held.
+	 */
+destroy_dead_key:
+	spin_unlock(&key_serial_lock);
+	kdebug("destroy key %d", key->serial);
+	down_write(&key->sem);
+	key->type = &key_type_dead;
+	if (key_gc_dead_keytype->destroy)
+		key_gc_dead_keytype->destroy(key);
+	memset(&key->payload, KEY_DESTROY, sizeof(key->payload));
+	up_write(&key->sem);
+	goto maybe_resched;
 }
diff --git a/security/keys/internal.h b/security/keys/internal.h
index f375152a2500..c7a7caec4830 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -31,6 +31,7 @@
 	no_printk(KERN_DEBUG FMT"\n", ##__VA_ARGS__)
 #endif
 
+extern struct key_type key_type_dead;
 extern struct key_type key_type_user;
 
 /*****************************************************************************/
@@ -75,6 +76,7 @@ extern unsigned key_quota_maxbytes;
 #define KEYQUOTA_LINK_BYTES	4		/* a link in a keyring is worth 4 bytes */
 
 
+extern struct kmem_cache *key_jar;
 extern struct rb_root key_serial_tree;
 extern spinlock_t key_serial_lock;
 extern struct mutex key_construction_mutex;
@@ -146,9 +148,11 @@ extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags,
 
 extern long join_session_keyring(const char *name);
 
+extern struct work_struct key_gc_work;
 extern unsigned key_gc_delay;
 extern void keyring_gc(struct key *keyring, time_t limit);
 extern void key_schedule_gc(time_t expiry_at);
+extern void key_gc_keytype(struct key_type *ktype);
 
 extern int key_task_permission(const key_ref_t key_ref,
 			       const struct cred *cred,
diff --git a/security/keys/key.c b/security/keys/key.c
index f7f9d93f08d9..4414abddcb5b 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -21,7 +21,7 @@
 #include <linux/user_namespace.h>
 #include "internal.h"
 
-static struct kmem_cache	*key_jar;
+struct kmem_cache *key_jar;
 struct rb_root		key_serial_tree; /* tree of keys indexed by serial */
 DEFINE_SPINLOCK(key_serial_lock);
 
@@ -36,17 +36,9 @@ unsigned int key_quota_maxbytes = 20000;	/* general key space quota */
 static LIST_HEAD(key_types_list);
 static DECLARE_RWSEM(key_types_sem);
 
-static void key_cleanup(struct work_struct *work);
-static DECLARE_WORK(key_cleanup_task, key_cleanup);
-
 /* We serialise key instantiation and link */
 DEFINE_MUTEX(key_construction_mutex);
 
-/* Any key who's type gets unegistered will be re-typed to this */
-static struct key_type key_type_dead = {
-	.name		= "dead",
-};
-
 #ifdef KEY_DEBUGGING
 void __key_check(const struct key *key)
 {
@@ -591,71 +583,6 @@ int key_reject_and_link(struct key *key,
 }
 EXPORT_SYMBOL(key_reject_and_link);
 
-/*
- * Garbage collect keys in process context so that we don't have to disable
- * interrupts all over the place.
- *
- * key_put() schedules this rather than trying to do the cleanup itself, which
- * means key_put() doesn't have to sleep.
- */
-static void key_cleanup(struct work_struct *work)
-{
-	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);
-
-	key_check(key);
-
-	security_key_free(key);
-
-	/* deal with the user's key tracking and quota */
-	if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
-		spin_lock(&key->user->lock);
-		key->user->qnkeys--;
-		key->user->qnbytes -= key->quotalen;
-		spin_unlock(&key->user->lock);
-	}
-
-	atomic_dec(&key->user->nkeys);
-	if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
-		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;
-}
-
 /**
  * key_put - Discard a reference to a key.
  * @key: The key to discard a reference from.
@@ -670,7 +597,7 @@ void key_put(struct key *key)
 		key_check(key);
 
 		if (atomic_dec_and_test(&key->usage))
-			schedule_work(&key_cleanup_task);
+			queue_work(system_nrt_wq, &key_gc_work);
 	}
 }
 EXPORT_SYMBOL(key_put);
@@ -1048,49 +975,11 @@ EXPORT_SYMBOL(register_key_type);
  */
 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);
-
-	/* mark all the keys of this type dead */
-	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) {
-			key->type = &key_type_dead;
-			set_bit(KEY_FLAG_DEAD, &key->flags);
-		}
-	}
-
-	spin_unlock(&key_serial_lock);
-
-	/* make sure everyone revalidates their keys */
-	synchronize_rcu();
-
-	/* we should now be able to destroy the payloads of all the keys of
-	 * this type with impunity */
-	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) {
-			if (ktype->destroy)
-				ktype->destroy(key);
-			memset(&key->payload, KEY_DESTROY, sizeof(key->payload));
-		}
-	}
-
-	spin_unlock(&key_serial_lock);
-	up_write(&key_types_sem);
-
-	key_schedule_gc(0);
+	downgrade_write(&key_types_sem);
+	key_gc_keytype(ktype);
+	up_read(&key_types_sem);
 }
 EXPORT_SYMBOL(unregister_key_type);
 
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index 30e242f7bd0e..37a7f3b28852 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -860,8 +860,7 @@ void __key_link(struct key *keyring, struct key *key,
 
 	kenter("%d,%d,%p", keyring->serial, key->serial, nklist);
 
-	klist = rcu_dereference_protected(keyring->payload.subscriptions,
-					  rwsem_is_locked(&keyring->sem));
+	klist = rcu_dereference_locked_keyring(keyring);
 
 	atomic_inc(&key->usage);
 
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index a3063eb3dc23..1068cb1939b3 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -270,7 +270,7 @@ static int install_session_keyring(struct key *keyring)
 	if (!new)
 		return -ENOMEM;
 
-	ret = install_session_keyring_to_cred(new, NULL);
+	ret = install_session_keyring_to_cred(new, keyring);
 	if (ret < 0) {
 		abort_creds(new);
 		return ret;
@@ -589,12 +589,22 @@ try_again:
 			ret = install_user_keyrings();
 			if (ret < 0)
 				goto error;
-			ret = install_session_keyring(
-				cred->user->session_keyring);
+			if (lflags & KEY_LOOKUP_CREATE)
+				ret = join_session_keyring(NULL);
+			else
+				ret = install_session_keyring(
+					cred->user->session_keyring);
 
 			if (ret < 0)
 				goto error;
 			goto reget_creds;
+		} else if (cred->tgcred->session_keyring ==
+			   cred->user->session_keyring &&
+			   lflags & KEY_LOOKUP_CREATE) {
+			ret = join_session_keyring(NULL);
+			if (ret < 0)
+				goto error;
+			goto reget_creds;
 		}
 
 		rcu_read_lock();
diff --git a/security/keys/trusted.c b/security/keys/trusted.c
index 0c33e2ea1f3c..0964fc236946 100644
--- a/security/keys/trusted.c
+++ b/security/keys/trusted.c
@@ -779,7 +779,10 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
 			opt->pcrinfo_len = strlen(args[0].from) / 2;
 			if (opt->pcrinfo_len > MAX_PCRINFO_SIZE)
 				return -EINVAL;
-			hex2bin(opt->pcrinfo, args[0].from, opt->pcrinfo_len);
+			res = hex2bin(opt->pcrinfo, args[0].from,
+				      opt->pcrinfo_len);
+			if (res < 0)
+				return -EINVAL;
 			break;
 		case Opt_keyhandle:
 			res = strict_strtoul(args[0].from, 16, &handle);
@@ -791,12 +794,18 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
 		case Opt_keyauth:
 			if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE)
 				return -EINVAL;
-			hex2bin(opt->keyauth, args[0].from, SHA1_DIGEST_SIZE);
+			res = hex2bin(opt->keyauth, args[0].from,
+				      SHA1_DIGEST_SIZE);
+			if (res < 0)
+				return -EINVAL;
 			break;
 		case Opt_blobauth:
 			if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE)
 				return -EINVAL;
-			hex2bin(opt->blobauth, args[0].from, SHA1_DIGEST_SIZE);
+			res = hex2bin(opt->blobauth, args[0].from,
+				      SHA1_DIGEST_SIZE);
+			if (res < 0)
+				return -EINVAL;
 			break;
 		case Opt_migratable:
 			if (*args[0].from == '0')
@@ -860,7 +869,9 @@ static int datablob_parse(char *datablob, struct trusted_key_payload *p,
 		p->blob_len = strlen(c) / 2;
 		if (p->blob_len > MAX_BLOB_SIZE)
 			return -EINVAL;
-		hex2bin(p->blob, c, p->blob_len);
+		ret = hex2bin(p->blob, c, p->blob_len);
+		if (ret < 0)
+			return -EINVAL;
 		ret = getoptions(datablob, p, o);
 		if (ret < 0)
 			return ret;
diff --git a/security/security.c b/security/security.c
index d9e153390926..0c6cc69c8f86 100644
--- a/security/security.c
+++ b/security/security.c
@@ -16,15 +16,16 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/security.h>
+#include <linux/integrity.h>
 #include <linux/ima.h>
+#include <linux/evm.h>
+
+#define MAX_LSM_EVM_XATTR	2
 
 /* Boot-time LSM user choice */
 static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
 	CONFIG_DEFAULT_SECURITY;
 
-/* things that live in capability.c */
-extern void __init security_fixup_ops(struct security_operations *ops);
-
 static struct security_operations *security_ops;
 static struct security_operations default_security_ops = {
 	.name	= "default",
@@ -334,20 +335,57 @@ int security_inode_alloc(struct inode *inode)
 
 void security_inode_free(struct inode *inode)
 {
-	ima_inode_free(inode);
+	integrity_inode_free(inode);
 	security_ops->inode_free_security(inode);
 }
 
 int security_inode_init_security(struct inode *inode, struct inode *dir,
-				 const struct qstr *qstr, char **name,
-				 void **value, size_t *len)
+				 const struct qstr *qstr,
+				 const initxattrs initxattrs, void *fs_data)
 {
+	struct xattr new_xattrs[MAX_LSM_EVM_XATTR + 1];
+	struct xattr *lsm_xattr, *evm_xattr, *xattr;
+	int ret;
+
 	if (unlikely(IS_PRIVATE(inode)))
-		return -EOPNOTSUPP;
+		return 0;
+
+	memset(new_xattrs, 0, sizeof new_xattrs);
+	if (!initxattrs)
+		return security_ops->inode_init_security(inode, dir, qstr,
+							 NULL, NULL, NULL);
+	lsm_xattr = new_xattrs;
+	ret = security_ops->inode_init_security(inode, dir, qstr,
+						&lsm_xattr->name,
+						&lsm_xattr->value,
+						&lsm_xattr->value_len);
+	if (ret)
+		goto out;
+
+	evm_xattr = lsm_xattr + 1;
+	ret = evm_inode_init_security(inode, lsm_xattr, evm_xattr);
+	if (ret)
+		goto out;
+	ret = initxattrs(inode, new_xattrs, fs_data);
+out:
+	for (xattr = new_xattrs; xattr->name != NULL; xattr++) {
+		kfree(xattr->name);
+		kfree(xattr->value);
+	}
+	return (ret == -EOPNOTSUPP) ? 0 : ret;
+}
+EXPORT_SYMBOL(security_inode_init_security);
+
+int security_old_inode_init_security(struct inode *inode, struct inode *dir,
+				     const struct qstr *qstr, char **name,
+				     void **value, size_t *len)
+{
+	if (unlikely(IS_PRIVATE(inode)))
+		return 0;
 	return security_ops->inode_init_security(inode, dir, qstr, name, value,
 						 len);
 }
-EXPORT_SYMBOL(security_inode_init_security);
+EXPORT_SYMBOL(security_old_inode_init_security);
 
 #ifdef CONFIG_SECURITY_PATH
 int security_path_mknod(struct path *dir, struct dentry *dentry, int mode,
@@ -523,9 +561,14 @@ int security_inode_permission(struct inode *inode, int mask)
 
 int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
 {
+	int ret;
+
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return 0;
-	return security_ops->inode_setattr(dentry, attr);
+	ret = security_ops->inode_setattr(dentry, attr);
+	if (ret)
+		return ret;
+	return evm_inode_setattr(dentry, attr);
 }
 EXPORT_SYMBOL_GPL(security_inode_setattr);
 
@@ -539,9 +582,14 @@ int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
 int security_inode_setxattr(struct dentry *dentry, const char *name,
 			    const void *value, size_t size, int flags)
 {
+	int ret;
+
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return 0;
-	return security_ops->inode_setxattr(dentry, name, value, size, flags);
+	ret = security_ops->inode_setxattr(dentry, name, value, size, flags);
+	if (ret)
+		return ret;
+	return evm_inode_setxattr(dentry, name, value, size);
 }
 
 void security_inode_post_setxattr(struct dentry *dentry, const char *name,
@@ -550,6 +598,7 @@ void security_inode_post_setxattr(struct dentry *dentry, const char *name,
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return;
 	security_ops->inode_post_setxattr(dentry, name, value, size, flags);
+	evm_inode_post_setxattr(dentry, name, value, size);
 }
 
 int security_inode_getxattr(struct dentry *dentry, const char *name)
@@ -568,9 +617,14 @@ int security_inode_listxattr(struct dentry *dentry)
 
 int security_inode_removexattr(struct dentry *dentry, const char *name)
 {
+	int ret;
+
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return 0;
-	return security_ops->inode_removexattr(dentry, name);
+	ret = security_ops->inode_removexattr(dentry, name);
+	if (ret)
+		return ret;
+	return evm_inode_removexattr(dentry, name);
 }
 
 int security_inode_need_killpriv(struct dentry *dentry)
diff --git a/security/selinux/exports.c b/security/selinux/exports.c
index 90664385dead..e75dd94e2d2b 100644
--- a/security/selinux/exports.c
+++ b/security/selinux/exports.c
@@ -12,6 +12,7 @@
  * as published by the Free Software Foundation.
  */
 #include <linux/module.h>
+#include <linux/selinux.h>
 
 #include "security.h"
 
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 266a2292451d..e545b9f67072 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -89,14 +89,14 @@
 #include "xfrm.h"
 #include "netlabel.h"
 #include "audit.h"
+#include "avc_ss.h"
 
 #define NUM_SEL_MNT_OPTS 5
 
-extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
 extern struct security_operations *security_ops;
 
 /* SECMARK reference count */
-atomic_t selinux_secmark_refcount = ATOMIC_INIT(0);
+static atomic_t selinux_secmark_refcount = ATOMIC_INIT(0);
 
 #ifdef CONFIG_SECURITY_SELINUX_DEVELOP
 int selinux_enforcing;
@@ -279,10 +279,6 @@ static void superblock_free_security(struct super_block *sb)
 	kfree(sbsec);
 }
 
-/* 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 const char *labeling_behaviors[6] = {
@@ -2097,9 +2093,6 @@ static int selinux_bprm_secureexec(struct linux_binprm *bprm)
 	return (atsecure || cap_bprm_secureexec(bprm));
 }
 
-extern struct vfsmount *selinuxfs_mount;
-extern struct dentry *selinux_null;
-
 /* Derived from fs/exec.c:flush_old_files. */
 static inline void flush_unauthorized_files(const struct cred *cred,
 					    struct files_struct *files)
@@ -5803,8 +5796,6 @@ static int selinux_disabled;
 
 int selinux_disable(void)
 {
-	extern void exit_sel_fs(void);
-
 	if (ss_initialized) {
 		/* Not permitted after initial policy load. */
 		return -EINVAL;
diff --git a/security/selinux/include/avc_ss.h b/security/selinux/include/avc_ss.h
index 4677aa519b04..d5c328452df0 100644
--- a/security/selinux/include/avc_ss.h
+++ b/security/selinux/include/avc_ss.h
@@ -18,5 +18,11 @@ struct security_class_mapping {
 
 extern struct security_class_mapping secclass_map[];
 
+/*
+ * The security server must be initialized before
+ * any labeling or access decisions can be provided.
+ */
+extern int ss_initialized;
+
 #endif /* _SELINUX_AVC_SS_H_ */
 
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 3ba4feba048a..d871e8ad2103 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -216,6 +216,14 @@ struct selinux_kernel_status {
 
 extern void selinux_status_update_setenforce(int enforcing);
 extern void selinux_status_update_policyload(int seqno);
+extern void selinux_complete_init(void);
+extern int selinux_disable(void);
+extern void exit_sel_fs(void);
+extern struct dentry *selinux_null;
+extern struct vfsmount *selinuxfs_mount;
+extern void selnl_notify_setenforce(int val);
+extern void selnl_notify_policyload(u32 seqno);
+extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
 
 #endif /* _SELINUX_SECURITY_H_ */
 
diff --git a/security/selinux/netlink.c b/security/selinux/netlink.c
index 36ac257cec9a..ce3f481558d8 100644
--- a/security/selinux/netlink.c
+++ b/security/selinux/netlink.c
@@ -19,6 +19,8 @@
 #include <linux/selinux_netlink.h>
 #include <net/net_namespace.h>
 
+#include "security.h"
+
 static struct sock *selnl;
 
 static int selnl_msglen(int msgtype)
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
index 8b02b2137da2..0920ea3bf599 100644
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -21,6 +21,7 @@
 
 #include "flask.h"
 #include "av_permissions.h"
+#include "security.h"
 
 struct nlmsg_perm {
 	u16	nlmsg_type;
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 55d92cbb177a..f46658722c78 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -75,8 +75,6 @@ static char policy_opened;
 /* global data for policy capabilities */
 static struct dentry *policycap_dir;
 
-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)
@@ -278,7 +276,6 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf,
 	char *page = NULL;
 	ssize_t length;
 	int new_value;
-	extern int selinux_disable(void);
 
 	length = -ENOMEM;
 	if (count >= PAGE_SIZE)
@@ -478,7 +475,7 @@ static struct vm_operations_struct sel_mmap_policy_ops = {
 	.page_mkwrite = sel_mmap_policy_fault,
 };
 
-int sel_mmap_policy(struct file *filp, struct vm_area_struct *vma)
+static int sel_mmap_policy(struct file *filp, struct vm_area_struct *vma)
 {
 	if (vma->vm_flags & VM_SHARED) {
 		/* do not allow mprotect to make mapping writable */
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c
index a53373207fb4..2ec904177fe0 100644
--- a/security/selinux/ss/conditional.c
+++ b/security/selinux/ss/conditional.c
@@ -555,7 +555,7 @@ static int cond_write_av_list(struct policydb *p,
 	return 0;
 }
 
-int cond_write_node(struct policydb *p, struct cond_node *node,
+static int cond_write_node(struct policydb *p, struct cond_node *node,
 		    struct policy_file *fp)
 {
 	struct cond_expr *cur_expr;
diff --git a/security/selinux/ss/conditional.h b/security/selinux/ss/conditional.h
index 3f209c635295..4d1f87466508 100644
--- a/security/selinux/ss/conditional.h
+++ b/security/selinux/ss/conditional.h
@@ -13,6 +13,7 @@
 #include "avtab.h"
 #include "symtab.h"
 #include "policydb.h"
+#include "../include/conditional.h"
 
 #define COND_EXPR_MAXDEPTH 10
 
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 2381d0ded228..a7f61d52f05c 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1743,8 +1743,6 @@ static int policydb_bounds_sanity_check(struct policydb *p)
 	return 0;
 }
 
-extern int ss_initialized;
-
 u16 string_to_security_class(struct policydb *p, const char *name)
 {
 	struct class_datum *cladatum;
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index f6917bc0aa05..185f849a26f6 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -70,8 +70,6 @@
 #include "ebitmap.h"
 #include "audit.h"
 
-extern void selnl_notify_policyload(u32 seqno);
-
 int selinux_policycap_netpeer;
 int selinux_policycap_openperm;
 
@@ -1790,7 +1788,6 @@ static void security_load_policycaps(void)
 						  POLICYDB_CAPABILITY_OPENPERM);
 }
 
-extern void selinux_complete_init(void);
 static int security_preserve_bools(struct policydb *p);
 
 /**
diff --git a/security/smack/smack.h b/security/smack/smack.h
index 2b6c6a516123..2ad00657b801 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -41,9 +41,9 @@ struct superblock_smack {
 };
 
 struct socket_smack {
-	char		*smk_out;			/* outbound label */
-	char		*smk_in;			/* inbound label */
-	char		smk_packet[SMK_LABELLEN];	/* TCP peer label */
+	char		*smk_out;	/* outbound label */
+	char		*smk_in;	/* inbound label */
+	char		*smk_packet;	/* TCP peer label */
 };
 
 /*
@@ -116,13 +116,19 @@ struct smk_netlbladdr {
  * If there is a cipso value associated with the label it
  * gets stored here, too. This will most likely be rare as
  * the cipso direct mapping in used internally.
+ *
+ * Keep the access rules for this subject label here so that
+ * the entire set of rules does not need to be examined every
+ * time.
  */
 struct smack_known {
 	struct list_head	list;
 	char			smk_known[SMK_LABELLEN];
 	u32			smk_secid;
 	struct smack_cipso	*smk_cipso;
-	spinlock_t		smk_cipsolock; /* for changing cipso map */
+	spinlock_t		smk_cipsolock;	/* for changing cipso map */
+	struct list_head	smk_rules;	/* access rules */
+	struct mutex		smk_rules_lock;	/* lock for the rules */
 };
 
 /*
@@ -150,7 +156,6 @@ struct smack_known {
 
 /*
  * smackfs magic number
- * smackfs macic number
  */
 #define SMACK_MAGIC	0x43415d53 /* "SMAC" */
 
@@ -176,9 +181,9 @@ struct smack_known {
 #define MAY_NOT		0
 
 /*
- * Number of access types used by Smack (rwxa)
+ * Number of access types used by Smack (rwxat)
  */
-#define SMK_NUM_ACCESS_TYPE 4
+#define SMK_NUM_ACCESS_TYPE 5
 
 /*
  * Smack audit data; is empty if CONFIG_AUDIT not set
@@ -201,10 +206,12 @@ int smk_access_entry(char *, char *, struct list_head *);
 int smk_access(char *, char *, int, struct smk_audit_info *);
 int smk_curacc(char *, u32, struct smk_audit_info *);
 int smack_to_cipso(const char *, struct smack_cipso *);
-void smack_from_cipso(u32, char *, char *);
+char *smack_from_cipso(u32, char *);
 char *smack_from_secid(const u32);
+void smk_parse_smack(const char *string, int len, char *smack);
 char *smk_import(const char *, int);
 struct smack_known *smk_import_entry(const char *, int);
+struct smack_known *smk_find_entry(const char *);
 u32 smack_to_secid(const char *);
 
 /*
@@ -223,7 +230,6 @@ extern struct smack_known smack_known_star;
 extern struct smack_known smack_known_web;
 
 extern struct list_head smack_known_list;
-extern struct list_head smack_rule_list;
 extern struct list_head smk_netlbladdr_list;
 
 extern struct security_operations smack_ops;
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c
index 9637e107f7ea..cc7cb6edba08 100644
--- a/security/smack/smack_access.c
+++ b/security/smack/smack_access.c
@@ -77,14 +77,19 @@ int log_policy = SMACK_AUDIT_DENIED;
  * entry is found returns -ENOENT.
  *
  * NOTE:
- * Even though Smack labels are usually shared on smack_list
- * labels that come in off the network can't be imported
- * and added to the list for locking reasons.
  *
- * Therefore, it is necessary to check the contents of the labels,
- * not just the pointer values. Of course, in most cases the labels
- * will be on the list, so checking the pointers may be a worthwhile
- * optimization.
+ * Earlier versions of this function allowed for labels that
+ * were not on the label list. This was done to allow for
+ * labels to come over the network that had never been seen
+ * before on this host. Unless the receiving socket has the
+ * star label this will always result in a failure check. The
+ * star labeled socket case is now handled in the networking
+ * hooks so there is no case where the label is not on the
+ * label list. Checking to see if the address of two labels
+ * is the same is now a reliable test.
+ *
+ * Do the object check first because that is more
+ * likely to differ.
  */
 int smk_access_entry(char *subject_label, char *object_label,
 			struct list_head *rule_list)
@@ -93,13 +98,10 @@ int smk_access_entry(char *subject_label, char *object_label,
 	struct smack_rule *srp;
 
 	list_for_each_entry_rcu(srp, rule_list, list) {
-		if (srp->smk_subject == subject_label ||
-		    strcmp(srp->smk_subject, subject_label) == 0) {
-			if (srp->smk_object == object_label ||
-			    strcmp(srp->smk_object, object_label) == 0) {
-				may = srp->smk_access;
-				break;
-			}
+		if (srp->smk_object == object_label &&
+		    srp->smk_subject == subject_label) {
+			may = srp->smk_access;
+			break;
 		}
 	}
 
@@ -117,18 +119,12 @@ int smk_access_entry(char *subject_label, char *object_label,
  * access rule list and returns 0 if the access is permitted,
  * non zero otherwise.
  *
- * Even though Smack labels are usually shared on smack_list
- * labels that come in off the network can't be imported
- * and added to the list for locking reasons.
- *
- * Therefore, it is necessary to check the contents of the labels,
- * not just the pointer values. Of course, in most cases the labels
- * will be on the list, so checking the pointers may be a worthwhile
- * optimization.
+ * Smack labels are shared on smack_list
  */
 int smk_access(char *subject_label, char *object_label, int request,
 	       struct smk_audit_info *a)
 {
+	struct smack_known *skp;
 	int may = MAY_NOT;
 	int rc = 0;
 
@@ -137,8 +133,7 @@ int smk_access(char *subject_label, char *object_label, int request,
 	 *
 	 * A star subject can't access any object.
 	 */
-	if (subject_label == smack_known_star.smk_known ||
-	    strcmp(subject_label, smack_known_star.smk_known) == 0) {
+	if (subject_label == smack_known_star.smk_known) {
 		rc = -EACCES;
 		goto out_audit;
 	}
@@ -148,33 +143,27 @@ int smk_access(char *subject_label, char *object_label, int request,
 	 * An internet subject can access any object.
 	 */
 	if (object_label == smack_known_web.smk_known ||
-	    subject_label == smack_known_web.smk_known ||
-	    strcmp(object_label, smack_known_web.smk_known) == 0 ||
-	    strcmp(subject_label, smack_known_web.smk_known) == 0)
+	    subject_label == smack_known_web.smk_known)
 		goto out_audit;
 	/*
 	 * A star object can be accessed by any subject.
 	 */
-	if (object_label == smack_known_star.smk_known ||
-	    strcmp(object_label, smack_known_star.smk_known) == 0)
+	if (object_label == smack_known_star.smk_known)
 		goto out_audit;
 	/*
 	 * An object can be accessed in any way by a subject
 	 * with the same label.
 	 */
-	if (subject_label == object_label ||
-	    strcmp(subject_label, object_label) == 0)
+	if (subject_label == object_label)
 		goto out_audit;
 	/*
 	 * A hat subject can read any object.
 	 * A floor object can be read by any subject.
 	 */
 	if ((request & MAY_ANYREAD) == request) {
-		if (object_label == smack_known_floor.smk_known ||
-		    strcmp(object_label, smack_known_floor.smk_known) == 0)
+		if (object_label == smack_known_floor.smk_known)
 			goto out_audit;
-		if (subject_label == smack_known_hat.smk_known ||
-		    strcmp(subject_label, smack_known_hat.smk_known) == 0)
+		if (subject_label == smack_known_hat.smk_known)
 			goto out_audit;
 	}
 	/*
@@ -184,8 +173,9 @@ int smk_access(char *subject_label, char *object_label, int request,
 	 * good. A negative response from smk_access_entry()
 	 * indicates there is no entry for this pair.
 	 */
+	skp = smk_find_entry(subject_label);
 	rcu_read_lock();
-	may = smk_access_entry(subject_label, object_label, &smack_rule_list);
+	may = smk_access_entry(subject_label, object_label, &skp->smk_rules);
 	rcu_read_unlock();
 
 	if (may > 0 && (request & may) == request)
@@ -344,17 +334,32 @@ void smack_log(char *subject_label, char *object_label, int request,
 static DEFINE_MUTEX(smack_known_lock);
 
 /**
- * smk_import_entry - import a label, return the list entry
+ * smk_find_entry - find a label on the list, return the list entry
  * @string: a text string that might be a Smack label
- * @len: the maximum size, or zero if it is NULL terminated.
  *
  * Returns a pointer to the entry in the label list that
- * matches the passed string, adding it if necessary.
+ * matches the passed string.
  */
-struct smack_known *smk_import_entry(const char *string, int len)
+struct smack_known *smk_find_entry(const char *string)
 {
 	struct smack_known *skp;
-	char smack[SMK_LABELLEN];
+
+	list_for_each_entry_rcu(skp, &smack_known_list, list) {
+		if (strncmp(skp->smk_known, string, SMK_MAXLEN) == 0)
+			return skp;
+	}
+
+	return NULL;
+}
+
+/**
+ * smk_parse_smack - parse smack label from a text string
+ * @string: a text string that might contain a Smack label
+ * @len: the maximum size, or zero if it is NULL terminated.
+ * @smack: parsed smack label, or NULL if parse error
+ */
+void smk_parse_smack(const char *string, int len, char *smack)
+{
 	int found;
 	int i;
 
@@ -372,27 +377,38 @@ struct smack_known *smk_import_entry(const char *string, int len)
 		} else
 			smack[i] = string[i];
 	}
+}
+
+/**
+ * smk_import_entry - import a label, return the list entry
+ * @string: a text string that might be a Smack label
+ * @len: the maximum size, or zero if it is NULL terminated.
+ *
+ * Returns a pointer to the entry in the label list that
+ * matches the passed string, adding it if necessary.
+ */
+struct smack_known *smk_import_entry(const char *string, int len)
+{
+	struct smack_known *skp;
+	char smack[SMK_LABELLEN];
 
+	smk_parse_smack(string, len, smack);
 	if (smack[0] == '\0')
 		return NULL;
 
 	mutex_lock(&smack_known_lock);
 
-	found = 0;
-	list_for_each_entry_rcu(skp, &smack_known_list, list) {
-		if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) {
-			found = 1;
-			break;
-		}
-	}
+	skp = smk_find_entry(smack);
 
-	if (found == 0) {
+	if (skp == NULL) {
 		skp = kzalloc(sizeof(struct smack_known), GFP_KERNEL);
 		if (skp != NULL) {
 			strncpy(skp->smk_known, smack, SMK_MAXLEN);
 			skp->smk_secid = smack_next_secid++;
 			skp->smk_cipso = NULL;
+			INIT_LIST_HEAD(&skp->smk_rules);
 			spin_lock_init(&skp->smk_cipsolock);
+			mutex_init(&skp->smk_rules_lock);
 			/*
 			 * Make sure that the entry is actually
 			 * filled before putting it on the list.
@@ -480,19 +496,12 @@ u32 smack_to_secid(const char *smack)
  * smack_from_cipso - find the Smack label associated with a CIPSO option
  * @level: Bell & LaPadula level from the network
  * @cp: Bell & LaPadula categories from the network
- * @result: where to put the Smack value
  *
  * This is a simple lookup in the label table.
  *
- * This is an odd duck as far as smack handling goes in that
- * it sends back a copy of the smack label rather than a pointer
- * to the master list. This is done because it is possible for
- * a foreign host to send a smack label that is new to this
- * machine and hence not on the list. That would not be an
- * issue except that adding an entry to the master list can't
- * be done at that point.
+ * Return the matching label from the label list or NULL.
  */
-void smack_from_cipso(u32 level, char *cp, char *result)
+char *smack_from_cipso(u32 level, char *cp)
 {
 	struct smack_known *kp;
 	char *final = NULL;
@@ -509,12 +518,13 @@ void smack_from_cipso(u32 level, char *cp, char *result)
 			final = kp->smk_known;
 
 		spin_unlock_bh(&kp->smk_cipsolock);
+
+		if (final != NULL)
+			break;
 	}
 	rcu_read_unlock();
-	if (final == NULL)
-		final = smack_known_huh.smk_known;
-	strncpy(result, final, SMK_MAXLEN);
-	return;
+
+	return final;
 }
 
 /**
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index b9c5e149903b..7db62b48eb42 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -5,12 +5,13 @@
  *
  *  Authors:
  *	Casey Schaufler <casey@schaufler-ca.com>
- *	Jarkko Sakkinen <ext-jarkko.2.sakkinen@nokia.com>
+ *	Jarkko Sakkinen <jarkko.sakkinen@intel.com>
  *
  *  Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
  *  Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
  *                Paul Moore <paul@paul-moore.com>
  *  Copyright (C) 2010 Nokia Corporation
+ *  Copyright (C) 2011 Intel Corporation.
  *
  *	This program is free software; you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License version 2,
@@ -34,6 +35,7 @@
 #include <linux/audit.h>
 #include <linux/magic.h>
 #include <linux/dcache.h>
+#include <linux/personality.h>
 #include "smack.h"
 
 #define task_security(task)	(task_cred_xxx((task), security))
@@ -441,11 +443,17 @@ static int smack_sb_umount(struct vfsmount *mnt, int flags)
  * BPRM hooks
  */
 
+/**
+ * smack_bprm_set_creds - set creds for exec
+ * @bprm: the exec information
+ *
+ * Returns 0 if it gets a blob, -ENOMEM otherwise
+ */
 static int smack_bprm_set_creds(struct linux_binprm *bprm)
 {
-	struct task_smack *tsp = bprm->cred->security;
+	struct inode *inode = bprm->file->f_path.dentry->d_inode;
+	struct task_smack *bsp = bprm->cred->security;
 	struct inode_smack *isp;
-	struct dentry *dp;
 	int rc;
 
 	rc = cap_bprm_set_creds(bprm);
@@ -455,20 +463,48 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm)
 	if (bprm->cred_prepared)
 		return 0;
 
-	if (bprm->file == NULL || bprm->file->f_dentry == NULL)
+	isp = inode->i_security;
+	if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task)
 		return 0;
 
-	dp = bprm->file->f_dentry;
+	if (bprm->unsafe)
+		return -EPERM;
 
-	if (dp->d_inode == NULL)
-		return 0;
+	bsp->smk_task = isp->smk_task;
+	bprm->per_clear |= PER_CLEAR_ON_SETID;
 
-	isp = dp->d_inode->i_security;
+	return 0;
+}
 
-	if (isp->smk_task != NULL)
-		tsp->smk_task = isp->smk_task;
+/**
+ * smack_bprm_committing_creds - Prepare to install the new credentials
+ * from bprm.
+ *
+ * @bprm: binprm for exec
+ */
+static void smack_bprm_committing_creds(struct linux_binprm *bprm)
+{
+	struct task_smack *bsp = bprm->cred->security;
 
-	return 0;
+	if (bsp->smk_task != bsp->smk_forked)
+		current->pdeath_signal = 0;
+}
+
+/**
+ * smack_bprm_secureexec - Return the decision to use secureexec.
+ * @bprm: binprm for exec
+ *
+ * Returns 0 on success.
+ */
+static int smack_bprm_secureexec(struct linux_binprm *bprm)
+{
+	struct task_smack *tsp = current_security();
+	int ret = cap_bprm_secureexec(bprm);
+
+	if (!ret && (tsp->smk_task != tsp->smk_forked))
+		ret = 1;
+
+	return ret;
 }
 
 /*
@@ -516,6 +552,8 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
 				     const struct qstr *qstr, char **name,
 				     void **value, size_t *len)
 {
+	struct smack_known *skp;
+	char *csp = smk_of_current();
 	char *isp = smk_of_inode(inode);
 	char *dsp = smk_of_inode(dir);
 	int may;
@@ -527,8 +565,9 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
 	}
 
 	if (value) {
+		skp = smk_find_entry(csp);
 		rcu_read_lock();
-		may = smk_access_entry(smk_of_current(), dsp, &smack_rule_list);
+		may = smk_access_entry(csp, dsp, &skp->smk_rules);
 		rcu_read_unlock();
 
 		/*
@@ -841,7 +880,7 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
 	return;
 }
 
-/*
+/**
  * smack_inode_getxattr - Smack check on getxattr
  * @dentry: the object
  * @name: unused
@@ -858,7 +897,7 @@ static int smack_inode_getxattr(struct dentry *dentry, const char *name)
 	return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ, &ad);
 }
 
-/*
+/**
  * smack_inode_removexattr - Smack check on removexattr
  * @dentry: the object
  * @name: name of the attribute
@@ -1088,36 +1127,31 @@ static int smack_file_lock(struct file *file, unsigned int cmd)
  * @cmd: what action to check
  * @arg: unused
  *
+ * Generally these operations are harmless.
+ * File locking operations present an obvious mechanism
+ * for passing information, so they require write access.
+ *
  * Returns 0 if current has access, error code otherwise
  */
 static int smack_file_fcntl(struct file *file, unsigned int cmd,
 			    unsigned long arg)
 {
 	struct smk_audit_info ad;
-	int rc;
+	int rc = 0;
 
-	smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
-	smk_ad_setfield_u_fs_path(&ad, file->f_path);
 
 	switch (cmd) {
-	case F_DUPFD:
-	case F_GETFD:
-	case F_GETFL:
 	case F_GETLK:
-	case F_GETOWN:
-	case F_GETSIG:
-		rc = smk_curacc(file->f_security, MAY_READ, &ad);
-		break;
-	case F_SETFD:
-	case F_SETFL:
 	case F_SETLK:
 	case F_SETLKW:
 	case F_SETOWN:
 	case F_SETSIG:
+		smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
+		smk_ad_setfield_u_fs_path(&ad, file->f_path);
 		rc = smk_curacc(file->f_security, MAY_WRITE, &ad);
 		break;
 	default:
-		rc = smk_curacc(file->f_security, MAY_READWRITE, &ad);
+		break;
 	}
 
 	return rc;
@@ -1138,6 +1172,7 @@ static int smack_file_mmap(struct file *file,
 			   unsigned long flags, unsigned long addr,
 			   unsigned long addr_only)
 {
+	struct smack_known *skp;
 	struct smack_rule *srp;
 	struct task_smack *tsp;
 	char *sp;
@@ -1170,6 +1205,7 @@ static int smack_file_mmap(struct file *file,
 
 	tsp = current_security();
 	sp = smk_of_current();
+	skp = smk_find_entry(sp);
 	rc = 0;
 
 	rcu_read_lock();
@@ -1177,15 +1213,8 @@ static int smack_file_mmap(struct file *file,
 	 * For each Smack rule associated with the subject
 	 * label verify that the SMACK64MMAP also has access
 	 * to that rule's object label.
-	 *
-	 * Because neither of the labels comes
-	 * from the networking code it is sufficient
-	 * to compare pointers.
 	 */
-	list_for_each_entry_rcu(srp, &smack_rule_list, list) {
-		if (srp->smk_subject != sp)
-			continue;
-
+	list_for_each_entry_rcu(srp, &skp->smk_rules, list) {
 		osmack = srp->smk_object;
 		/*
 		 * Matching labels always allows access.
@@ -1214,7 +1243,8 @@ static int smack_file_mmap(struct file *file,
 		 * If there isn't one a SMACK64MMAP subject
 		 * can't have as much access as current.
 		 */
-		mmay = smk_access_entry(msmack, osmack, &smack_rule_list);
+		skp = smk_find_entry(msmack);
+		mmay = smk_access_entry(msmack, osmack, &skp->smk_rules);
 		if (mmay == -ENOENT) {
 			rc = -EACCES;
 			break;
@@ -1315,6 +1345,24 @@ static int smack_file_receive(struct file *file)
 	return smk_curacc(file->f_security, may, &ad);
 }
 
+/**
+ * smack_dentry_open - Smack dentry open processing
+ * @file: the object
+ * @cred: unused
+ *
+ * Set the security blob in the file structure.
+ *
+ * Returns 0
+ */
+static int smack_dentry_open(struct file *file, const struct cred *cred)
+{
+	struct inode_smack *isp = file->f_path.dentry->d_inode->i_security;
+
+	file->f_security = isp->smk_inode;
+
+	return 0;
+}
+
 /*
  * Task hooks
  */
@@ -1455,15 +1503,17 @@ static int smack_kernel_create_files_as(struct cred *new,
 /**
  * smk_curacc_on_task - helper to log task related access
  * @p: the task object
- * @access : the access requested
+ * @access: the access requested
+ * @caller: name of the calling function for audit
  *
  * Return 0 if access is permitted
  */
-static int smk_curacc_on_task(struct task_struct *p, int access)
+static int smk_curacc_on_task(struct task_struct *p, int access,
+				const char *caller)
 {
 	struct smk_audit_info ad;
 
-	smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
+	smk_ad_init(&ad, caller, LSM_AUDIT_DATA_TASK);
 	smk_ad_setfield_u_tsk(&ad, p);
 	return smk_curacc(smk_of_task(task_security(p)), access, &ad);
 }
@@ -1477,7 +1527,7 @@ static int smk_curacc_on_task(struct task_struct *p, int access)
  */
 static int smack_task_setpgid(struct task_struct *p, pid_t pgid)
 {
-	return smk_curacc_on_task(p, MAY_WRITE);
+	return smk_curacc_on_task(p, MAY_WRITE, __func__);
 }
 
 /**
@@ -1488,7 +1538,7 @@ static int smack_task_setpgid(struct task_struct *p, pid_t pgid)
  */
 static int smack_task_getpgid(struct task_struct *p)
 {
-	return smk_curacc_on_task(p, MAY_READ);
+	return smk_curacc_on_task(p, MAY_READ, __func__);
 }
 
 /**
@@ -1499,7 +1549,7 @@ static int smack_task_getpgid(struct task_struct *p)
  */
 static int smack_task_getsid(struct task_struct *p)
 {
-	return smk_curacc_on_task(p, MAY_READ);
+	return smk_curacc_on_task(p, MAY_READ, __func__);
 }
 
 /**
@@ -1527,7 +1577,7 @@ static int smack_task_setnice(struct task_struct *p, int nice)
 
 	rc = cap_task_setnice(p, nice);
 	if (rc == 0)
-		rc = smk_curacc_on_task(p, MAY_WRITE);
+		rc = smk_curacc_on_task(p, MAY_WRITE, __func__);
 	return rc;
 }
 
@@ -1544,7 +1594,7 @@ static int smack_task_setioprio(struct task_struct *p, int ioprio)
 
 	rc = cap_task_setioprio(p, ioprio);
 	if (rc == 0)
-		rc = smk_curacc_on_task(p, MAY_WRITE);
+		rc = smk_curacc_on_task(p, MAY_WRITE, __func__);
 	return rc;
 }
 
@@ -1556,7 +1606,7 @@ static int smack_task_setioprio(struct task_struct *p, int ioprio)
  */
 static int smack_task_getioprio(struct task_struct *p)
 {
-	return smk_curacc_on_task(p, MAY_READ);
+	return smk_curacc_on_task(p, MAY_READ, __func__);
 }
 
 /**
@@ -1573,7 +1623,7 @@ static int smack_task_setscheduler(struct task_struct *p)
 
 	rc = cap_task_setscheduler(p);
 	if (rc == 0)
-		rc = smk_curacc_on_task(p, MAY_WRITE);
+		rc = smk_curacc_on_task(p, MAY_WRITE, __func__);
 	return rc;
 }
 
@@ -1585,7 +1635,7 @@ static int smack_task_setscheduler(struct task_struct *p)
  */
 static int smack_task_getscheduler(struct task_struct *p)
 {
-	return smk_curacc_on_task(p, MAY_READ);
+	return smk_curacc_on_task(p, MAY_READ, __func__);
 }
 
 /**
@@ -1596,7 +1646,7 @@ static int smack_task_getscheduler(struct task_struct *p)
  */
 static int smack_task_movememory(struct task_struct *p)
 {
-	return smk_curacc_on_task(p, MAY_WRITE);
+	return smk_curacc_on_task(p, MAY_WRITE, __func__);
 }
 
 /**
@@ -1711,7 +1761,7 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
 
 	ssp->smk_in = csp;
 	ssp->smk_out = csp;
-	ssp->smk_packet[0] = '\0';
+	ssp->smk_packet = NULL;
 
 	sk->sk_security = ssp;
 
@@ -2753,6 +2803,7 @@ static int smack_unix_stream_connect(struct sock *sock,
 {
 	struct socket_smack *ssp = sock->sk_security;
 	struct socket_smack *osp = other->sk_security;
+	struct socket_smack *nsp = newsk->sk_security;
 	struct smk_audit_info ad;
 	int rc = 0;
 
@@ -2762,6 +2813,14 @@ static int smack_unix_stream_connect(struct sock *sock,
 	if (!capable(CAP_MAC_OVERRIDE))
 		rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad);
 
+	/*
+	 * Cross reference the peer labels for SO_PEERSEC.
+	 */
+	if (rc == 0) {
+		nsp->smk_packet = ssp->smk_out;
+		ssp->smk_packet = osp->smk_out;
+	}
+
 	return rc;
 }
 
@@ -2813,16 +2872,17 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
 	return smack_netlabel_send(sock->sk, sip);
 }
 
-
 /**
  * smack_from_secattr - Convert a netlabel attr.mls.lvl/attr.mls.cat pair to smack
  * @sap: netlabel secattr
- * @sip: where to put the result
+ * @ssp: socket security information
  *
- * Copies a smack label into sip
+ * Returns a pointer to a Smack label found on the label list.
  */
-static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip)
+static char *smack_from_secattr(struct netlbl_lsm_secattr *sap,
+				struct socket_smack *ssp)
 {
+	struct smack_known *skp;
 	char smack[SMK_LABELLEN];
 	char *sp;
 	int pcat;
@@ -2852,15 +2912,43 @@ static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip)
 		 * we are already done. WeeHee.
 		 */
 		if (sap->attr.mls.lvl == smack_cipso_direct) {
-			memcpy(sip, smack, SMK_MAXLEN);
-			return;
+			/*
+			 * The label sent is usually on the label list.
+			 *
+			 * If it is not we may still want to allow the
+			 * delivery.
+			 *
+			 * If the recipient is accepting all packets
+			 * because it is using the star ("*") label
+			 * for SMACK64IPIN provide the web ("@") label
+			 * so that a directed response will succeed.
+			 * This is not very correct from a MAC point
+			 * of view, but gets around the problem that
+			 * locking prevents adding the newly discovered
+			 * label to the list.
+			 * The case where the recipient is not using
+			 * the star label should obviously fail.
+			 * The easy way to do this is to provide the
+			 * star label as the subject label.
+			 */
+			skp = smk_find_entry(smack);
+			if (skp != NULL)
+				return skp->smk_known;
+			if (ssp != NULL &&
+			    ssp->smk_in == smack_known_star.smk_known)
+				return smack_known_web.smk_known;
+			return smack_known_star.smk_known;
 		}
 		/*
 		 * Look it up in the supplied table if it is not
 		 * a direct mapping.
 		 */
-		smack_from_cipso(sap->attr.mls.lvl, smack, sip);
-		return;
+		sp = smack_from_cipso(sap->attr.mls.lvl, smack);
+		if (sp != NULL)
+			return sp;
+		if (ssp != NULL && ssp->smk_in == smack_known_star.smk_known)
+			return smack_known_web.smk_known;
+		return smack_known_star.smk_known;
 	}
 	if ((sap->flags & NETLBL_SECATTR_SECID) != 0) {
 		/*
@@ -2875,16 +2963,14 @@ static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip)
 		 * secid is from a fallback.
 		 */
 		BUG_ON(sp == NULL);
-		strncpy(sip, sp, SMK_MAXLEN);
-		return;
+		return sp;
 	}
 	/*
 	 * Without guidance regarding the smack value
 	 * for the packet fall back on the network
 	 * ambient value.
 	 */
-	strncpy(sip, smack_net_ambient, SMK_MAXLEN);
-	return;
+	return smack_net_ambient;
 }
 
 /**
@@ -2898,7 +2984,6 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
 	struct netlbl_lsm_secattr secattr;
 	struct socket_smack *ssp = sk->sk_security;
-	char smack[SMK_LABELLEN];
 	char *csp;
 	int rc;
 	struct smk_audit_info ad;
@@ -2911,10 +2996,9 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
 	netlbl_secattr_init(&secattr);
 
 	rc = netlbl_skbuff_getattr(skb, sk->sk_family, &secattr);
-	if (rc == 0) {
-		smack_from_secattr(&secattr, smack);
-		csp = smack;
-	} else
+	if (rc == 0)
+		csp = smack_from_secattr(&secattr, ssp);
+	else
 		csp = smack_net_ambient;
 
 	netlbl_secattr_destroy(&secattr);
@@ -2951,15 +3035,19 @@ static int smack_socket_getpeersec_stream(struct socket *sock,
 					  int __user *optlen, unsigned len)
 {
 	struct socket_smack *ssp;
-	int slen;
+	char *rcp = "";
+	int slen = 1;
 	int rc = 0;
 
 	ssp = sock->sk->sk_security;
-	slen = strlen(ssp->smk_packet) + 1;
+	if (ssp->smk_packet != NULL) {
+		rcp = ssp->smk_packet;
+		slen = strlen(rcp) + 1;
+	}
 
 	if (slen > len)
 		rc = -ERANGE;
-	else if (copy_to_user(optval, ssp->smk_packet, slen) != 0)
+	else if (copy_to_user(optval, rcp, slen) != 0)
 		rc = -EFAULT;
 
 	if (put_user(slen, optlen) != 0)
@@ -2982,8 +3070,8 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
 
 {
 	struct netlbl_lsm_secattr secattr;
-	struct socket_smack *sp;
-	char smack[SMK_LABELLEN];
+	struct socket_smack *ssp = NULL;
+	char *sp;
 	int family = PF_UNSPEC;
 	u32 s = 0;	/* 0 is the invalid secid */
 	int rc;
@@ -2998,17 +3086,19 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
 		family = sock->sk->sk_family;
 
 	if (family == PF_UNIX) {
-		sp = sock->sk->sk_security;
-		s = smack_to_secid(sp->smk_out);
+		ssp = sock->sk->sk_security;
+		s = smack_to_secid(ssp->smk_out);
 	} else if (family == PF_INET || family == PF_INET6) {
 		/*
 		 * Translate what netlabel gave us.
 		 */
+		if (sock != NULL && sock->sk != NULL)
+			ssp = sock->sk->sk_security;
 		netlbl_secattr_init(&secattr);
 		rc = netlbl_skbuff_getattr(skb, family, &secattr);
 		if (rc == 0) {
-			smack_from_secattr(&secattr, smack);
-			s = smack_to_secid(smack);
+			sp = smack_from_secattr(&secattr, ssp);
+			s = smack_to_secid(sp);
 		}
 		netlbl_secattr_destroy(&secattr);
 	}
@@ -3056,7 +3146,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
 	struct netlbl_lsm_secattr secattr;
 	struct sockaddr_in addr;
 	struct iphdr *hdr;
-	char smack[SMK_LABELLEN];
+	char *sp;
 	int rc;
 	struct smk_audit_info ad;
 
@@ -3067,9 +3157,9 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
 	netlbl_secattr_init(&secattr);
 	rc = netlbl_skbuff_getattr(skb, family, &secattr);
 	if (rc == 0)
-		smack_from_secattr(&secattr, smack);
+		sp = smack_from_secattr(&secattr, ssp);
 	else
-		strncpy(smack, smack_known_huh.smk_known, SMK_MAXLEN);
+		sp = smack_known_huh.smk_known;
 	netlbl_secattr_destroy(&secattr);
 
 #ifdef CONFIG_AUDIT
@@ -3082,7 +3172,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
 	 * Receiving a packet requires that the other end be able to write
 	 * here. Read access is not required.
 	 */
-	rc = smk_access(smack, ssp->smk_in, MAY_WRITE, &ad);
+	rc = smk_access(sp, ssp->smk_in, MAY_WRITE, &ad);
 	if (rc != 0)
 		return rc;
 
@@ -3090,7 +3180,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
 	 * Save the peer's label in the request_sock so we can later setup
 	 * smk_packet in the child socket so that SO_PEERCRED can report it.
 	 */
-	req->peer_secid = smack_to_secid(smack);
+	req->peer_secid = smack_to_secid(sp);
 
 	/*
 	 * We need to decide if we want to label the incoming connection here
@@ -3103,7 +3193,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
 	if (smack_host_label(&addr) == NULL) {
 		rcu_read_unlock();
 		netlbl_secattr_init(&secattr);
-		smack_to_secattr(smack, &secattr);
+		smack_to_secattr(sp, &secattr);
 		rc = netlbl_req_setattr(req, &secattr);
 		netlbl_secattr_destroy(&secattr);
 	} else {
@@ -3125,13 +3215,11 @@ static void smack_inet_csk_clone(struct sock *sk,
 				 const struct request_sock *req)
 {
 	struct socket_smack *ssp = sk->sk_security;
-	char *smack;
 
-	if (req->peer_secid != 0) {
-		smack = smack_from_secid(req->peer_secid);
-		strncpy(ssp->smk_packet, smack, SMK_MAXLEN);
-	} else
-		ssp->smk_packet[0] = '\0';
+	if (req->peer_secid != 0)
+		ssp->smk_packet = smack_from_secid(req->peer_secid);
+	else
+		ssp->smk_packet = NULL;
 }
 
 /*
@@ -3409,6 +3497,8 @@ struct security_operations smack_ops = {
 	.sb_umount = 			smack_sb_umount,
 
 	.bprm_set_creds =		smack_bprm_set_creds,
+	.bprm_committing_creds =	smack_bprm_committing_creds,
+	.bprm_secureexec =		smack_bprm_secureexec,
 
 	.inode_alloc_security = 	smack_inode_alloc_security,
 	.inode_free_security = 		smack_inode_free_security,
@@ -3440,6 +3530,8 @@ struct security_operations smack_ops = {
 	.file_send_sigiotask = 		smack_file_send_sigiotask,
 	.file_receive = 		smack_file_receive,
 
+	.dentry_open =			smack_dentry_open,
+
 	.cred_alloc_blank =		smack_cred_alloc_blank,
 	.cred_free =			smack_cred_free,
 	.cred_prepare =			smack_cred_prepare,
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index f93460156dce..6aceef518a41 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -44,6 +44,7 @@ enum smk_inos {
 	SMK_ONLYCAP	= 9,	/* the only "capable" label */
 	SMK_LOGGING	= 10,	/* logging */
 	SMK_LOAD_SELF	= 11,	/* task specific rules */
+	SMK_ACCESSES	= 12,	/* access policy */
 };
 
 /*
@@ -85,6 +86,16 @@ char *smack_onlycap;
  */
 
 LIST_HEAD(smk_netlbladdr_list);
+
+/*
+ * Rule lists are maintained for each label.
+ * This master list is just for reading /smack/load.
+ */
+struct smack_master_list {
+	struct list_head	list;
+	struct smack_rule	*smk_rule;
+};
+
 LIST_HEAD(smack_rule_list);
 
 static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
@@ -92,7 +103,7 @@ static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
 const char *smack_cipso_option = SMACK_CIPSO_OPTION;
 
 
-#define	SEQ_READ_FINISHED	1
+#define	SEQ_READ_FINISHED	((loff_t)-1)
 
 /*
  * Values for parsing cipso rules
@@ -159,9 +170,13 @@ static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list,
 
 	mutex_lock(rule_lock);
 
+	/*
+	 * Because the object label is less likely to match
+	 * than the subject label check it first
+	 */
 	list_for_each_entry_rcu(sp, rule_list, list) {
-		if (sp->smk_subject == srp->smk_subject &&
-		    sp->smk_object == srp->smk_object) {
+		if (sp->smk_object == srp->smk_object &&
+		    sp->smk_subject == srp->smk_subject) {
 			found = 1;
 			sp->smk_access = srp->smk_access;
 			break;
@@ -176,6 +191,99 @@ static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list,
 }
 
 /**
+ * smk_parse_rule - parse Smack rule from load string
+ * @data: string to be parsed whose size is SMK_LOADLEN
+ * @rule: Smack rule
+ * @import: if non-zero, import labels
+ */
+static int smk_parse_rule(const char *data, struct smack_rule *rule, int import)
+{
+	char smack[SMK_LABELLEN];
+	struct smack_known *skp;
+
+	if (import) {
+		rule->smk_subject = smk_import(data, 0);
+		if (rule->smk_subject == NULL)
+			return -1;
+
+		rule->smk_object = smk_import(data + SMK_LABELLEN, 0);
+		if (rule->smk_object == NULL)
+			return -1;
+	} else {
+		smk_parse_smack(data, 0, smack);
+		skp = smk_find_entry(smack);
+		if (skp == NULL)
+			return -1;
+		rule->smk_subject = skp->smk_known;
+
+		smk_parse_smack(data + SMK_LABELLEN, 0, smack);
+		skp = smk_find_entry(smack);
+		if (skp == NULL)
+			return -1;
+		rule->smk_object = skp->smk_known;
+	}
+
+	rule->smk_access = 0;
+
+	switch (data[SMK_LABELLEN + SMK_LABELLEN]) {
+	case '-':
+		break;
+	case 'r':
+	case 'R':
+		rule->smk_access |= MAY_READ;
+		break;
+	default:
+		return -1;
+	}
+
+	switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) {
+	case '-':
+		break;
+	case 'w':
+	case 'W':
+		rule->smk_access |= MAY_WRITE;
+		break;
+	default:
+		return -1;
+	}
+
+	switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) {
+	case '-':
+		break;
+	case 'x':
+	case 'X':
+		rule->smk_access |= MAY_EXEC;
+		break;
+	default:
+		return -1;
+	}
+
+	switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) {
+	case '-':
+		break;
+	case 'a':
+	case 'A':
+		rule->smk_access |= MAY_APPEND;
+		break;
+	default:
+		return -1;
+	}
+
+	switch (data[SMK_LABELLEN + SMK_LABELLEN + 4]) {
+	case '-':
+		break;
+	case 't':
+	case 'T':
+		rule->smk_access |= MAY_TRANSMUTE;
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
  * smk_write_load_list - write() for any /smack/load
  * @file: file pointer, not actually used
  * @buf: where to get the data from
@@ -197,9 +305,12 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf,
 				struct list_head *rule_list,
 				struct mutex *rule_lock)
 {
+	struct smack_master_list *smlp;
+	struct smack_known *skp;
 	struct smack_rule *rule;
 	char *data;
 	int rc = -EINVAL;
+	int load = 0;
 
 	/*
 	 * No partial writes.
@@ -234,69 +345,14 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf,
 		goto out;
 	}
 
-	rule->smk_subject = smk_import(data, 0);
-	if (rule->smk_subject == NULL)
-		goto out_free_rule;
-
-	rule->smk_object = smk_import(data + SMK_LABELLEN, 0);
-	if (rule->smk_object == NULL)
+	if (smk_parse_rule(data, rule, 1))
 		goto out_free_rule;
 
-	rule->smk_access = 0;
-
-	switch (data[SMK_LABELLEN + SMK_LABELLEN]) {
-	case '-':
-		break;
-	case 'r':
-	case 'R':
-		rule->smk_access |= MAY_READ;
-		break;
-	default:
-		goto out_free_rule;
-	}
-
-	switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) {
-	case '-':
-		break;
-	case 'w':
-	case 'W':
-		rule->smk_access |= MAY_WRITE;
-		break;
-	default:
-		goto out_free_rule;
-	}
-
-	switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) {
-	case '-':
-		break;
-	case 'x':
-	case 'X':
-		rule->smk_access |= MAY_EXEC;
-		break;
-	default:
-		goto out_free_rule;
-	}
-
-	switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) {
-	case '-':
-		break;
-	case 'a':
-	case 'A':
-		rule->smk_access |= MAY_APPEND;
-		break;
-	default:
-		goto out_free_rule;
-	}
-
-	switch (data[SMK_LABELLEN + SMK_LABELLEN + 4]) {
-	case '-':
-		break;
-	case 't':
-	case 'T':
-		rule->smk_access |= MAY_TRANSMUTE;
-		break;
-	default:
-		goto out_free_rule;
+	if (rule_list == NULL) {
+		load = 1;
+		skp = smk_find_entry(rule->smk_subject);
+		rule_list = &skp->smk_rules;
+		rule_lock = &skp->smk_rules_lock;
 	}
 
 	rc = count;
@@ -304,8 +360,15 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf,
 	 * smk_set_access returns true if there was already a rule
 	 * for the subject/object pair, and false if it was new.
 	 */
-	if (!smk_set_access(rule, rule_list, rule_lock))
+	if (!smk_set_access(rule, rule_list, rule_lock)) {
+		smlp = kzalloc(sizeof(*smlp), GFP_KERNEL);
+		if (smlp != NULL) {
+			smlp->smk_rule = rule;
+			list_add_rcu(&smlp->list, &smack_rule_list);
+		} else
+			rc = -ENOMEM;
 		goto out;
+	}
 
 out_free_rule:
 	kfree(rule);
@@ -321,11 +384,24 @@ out:
 
 static void *load_seq_start(struct seq_file *s, loff_t *pos)
 {
-	if (*pos == SEQ_READ_FINISHED)
+	struct list_head *list;
+
+	/*
+	 * This is 0 the first time through.
+	 */
+	if (s->index == 0)
+		s->private = &smack_rule_list;
+
+	if (s->private == NULL)
 		return NULL;
-	if (list_empty(&smack_rule_list))
+
+	list = s->private;
+	if (list_empty(list))
 		return NULL;
-	return smack_rule_list.next;
+
+	if (s->index == 0)
+		return list->next;
+	return list;
 }
 
 static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos)
@@ -333,17 +409,19 @@ static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos)
 	struct list_head *list = v;
 
 	if (list_is_last(list, &smack_rule_list)) {
-		*pos = SEQ_READ_FINISHED;
+		s->private = NULL;
 		return NULL;
 	}
+	s->private = list->next;
 	return list->next;
 }
 
 static int load_seq_show(struct seq_file *s, void *v)
 {
 	struct list_head *list = v;
-	struct smack_rule *srp =
-		 list_entry(list, struct smack_rule, list);
+	struct smack_master_list *smlp =
+		 list_entry(list, struct smack_master_list, list);
+	struct smack_rule *srp = smlp->smk_rule;
 
 	seq_printf(s, "%s %s", (char *)srp->smk_subject,
 		   (char *)srp->smk_object);
@@ -412,8 +490,7 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf,
 	if (!capable(CAP_MAC_ADMIN))
 		return -EPERM;
 
-	return smk_write_load_list(file, buf, count, ppos, &smack_rule_list,
-					&smack_list_lock);
+	return smk_write_load_list(file, buf, count, ppos, NULL, NULL);
 }
 
 static const struct file_operations smk_load_ops = {
@@ -1425,6 +1502,44 @@ static const struct file_operations smk_load_self_ops = {
 	.write		= smk_write_load_self,
 	.release        = seq_release,
 };
+
+/**
+ * smk_write_access - handle access check transaction
+ * @file: file pointer
+ * @buf: data from user space
+ * @count: bytes sent
+ * @ppos: where to start - must be 0
+ */
+static ssize_t smk_write_access(struct file *file, const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	struct smack_rule rule;
+	char *data;
+	int res;
+
+	data = simple_transaction_get(file, buf, count);
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	if (count < SMK_LOADLEN || smk_parse_rule(data, &rule, 0))
+		return -EINVAL;
+
+	res = smk_access(rule.smk_subject, rule.smk_object, rule.smk_access,
+			  NULL);
+	data[0] = res == 0 ? '1' : '0';
+	data[1] = '\0';
+
+	simple_transaction_set(file, 2);
+	return SMK_LOADLEN;
+}
+
+static const struct file_operations smk_access_ops = {
+	.write		= smk_write_access,
+	.read		= simple_transaction_read,
+	.release	= simple_transaction_release,
+	.llseek		= generic_file_llseek,
+};
+
 /**
  * smk_fill_super - fill the /smackfs superblock
  * @sb: the empty superblock
@@ -1459,6 +1574,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
 			"logging", &smk_logging_ops, S_IRUGO|S_IWUSR},
 		[SMK_LOAD_SELF] = {
 			"load-self", &smk_load_self_ops, S_IRUGO|S_IWUGO},
+		[SMK_ACCESSES] = {
+			"access", &smk_access_ops, S_IRUGO|S_IWUGO},
 		/* last one */
 			{""}
 	};
@@ -1534,6 +1651,20 @@ static int __init init_smk_fs(void)
 	smk_cipso_doi();
 	smk_unlbl_ambient(NULL);
 
+	mutex_init(&smack_known_floor.smk_rules_lock);
+	mutex_init(&smack_known_hat.smk_rules_lock);
+	mutex_init(&smack_known_huh.smk_rules_lock);
+	mutex_init(&smack_known_invalid.smk_rules_lock);
+	mutex_init(&smack_known_star.smk_rules_lock);
+	mutex_init(&smack_known_web.smk_rules_lock);
+
+	INIT_LIST_HEAD(&smack_known_floor.smk_rules);
+	INIT_LIST_HEAD(&smack_known_hat.smk_rules);
+	INIT_LIST_HEAD(&smack_known_huh.smk_rules);
+	INIT_LIST_HEAD(&smack_known_invalid.smk_rules);
+	INIT_LIST_HEAD(&smack_known_star.smk_rules);
+	INIT_LIST_HEAD(&smack_known_web.smk_rules);
+
 	return err;
 }
 
diff --git a/security/tomoyo/Kconfig b/security/tomoyo/Kconfig
index 7c7f8c16c10f..8eb779b9d77f 100644
--- a/security/tomoyo/Kconfig
+++ b/security/tomoyo/Kconfig
@@ -1,8 +1,10 @@
 config SECURITY_TOMOYO
 	bool "TOMOYO Linux Support"
 	depends on SECURITY
+	depends on NET
 	select SECURITYFS
 	select SECURITY_PATH
+	select SECURITY_NETWORK
 	default n
 	help
 	  This selects TOMOYO Linux, pathname-based access control.
diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile
index 95278b71fc21..56a0c7be409e 100644
--- a/security/tomoyo/Makefile
+++ b/security/tomoyo/Makefile
@@ -1,4 +1,4 @@
-obj-y = audit.o common.o condition.o domain.o file.o gc.o group.o load_policy.o memory.o mount.o realpath.o securityfs_if.o tomoyo.o util.o
+obj-y = audit.o common.o condition.o domain.o environ.o file.o gc.o group.o load_policy.o memory.o mount.o network.o realpath.o securityfs_if.o tomoyo.o util.o
 
 $(obj)/policy/profile.conf:
 	@mkdir -p $(obj)/policy/
@@ -27,7 +27,7 @@ $(obj)/policy/stat.conf:
 	@touch $@
 
 $(obj)/builtin-policy.h: $(obj)/policy/profile.conf $(obj)/policy/exception_policy.conf $(obj)/policy/domain_policy.conf $(obj)/policy/manager.conf $(obj)/policy/stat.conf
-	@echo Generating built-in policy for TOMOYO 2.4.x.
+	@echo Generating built-in policy for TOMOYO 2.5.x.
 	@echo "static char tomoyo_builtin_profile[] __initdata =" > $@.tmp
 	@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/profile.conf >> $@.tmp
 	@echo "\"\";" >> $@.tmp
diff --git a/security/tomoyo/audit.c b/security/tomoyo/audit.c
index 5dbb1f7617c0..075c3a6d1649 100644
--- a/security/tomoyo/audit.c
+++ b/security/tomoyo/audit.c
@@ -313,6 +313,7 @@ static unsigned int tomoyo_log_count;
  */
 static bool tomoyo_get_audit(const struct tomoyo_policy_namespace *ns,
 			     const u8 profile, const u8 index,
+			     const struct tomoyo_acl_info *matched_acl,
 			     const bool is_granted)
 {
 	u8 mode;
@@ -324,6 +325,9 @@ static bool tomoyo_get_audit(const struct tomoyo_policy_namespace *ns,
 	p = tomoyo_profile(ns, profile);
 	if (tomoyo_log_count >= p->pref[TOMOYO_PREF_MAX_AUDIT_LOG])
 		return false;
+	if (is_granted && matched_acl && matched_acl->cond &&
+	    matched_acl->cond->grant_log != TOMOYO_GRANTLOG_AUTO)
+		return matched_acl->cond->grant_log == TOMOYO_GRANTLOG_YES;
 	mode = p->config[index];
 	if (mode == TOMOYO_CONFIG_USE_DEFAULT)
 		mode = p->config[category];
@@ -350,7 +354,8 @@ void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt,
 	char *buf;
 	struct tomoyo_log *entry;
 	bool quota_exceeded = false;
-	if (!tomoyo_get_audit(r->domain->ns, r->profile, r->type, r->granted))
+	if (!tomoyo_get_audit(r->domain->ns, r->profile, r->type,
+			      r->matched_acl, r->granted))
 		goto out;
 	buf = tomoyo_init_log(r, len, fmt, args);
 	if (!buf)
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
index 2e43aec1c36b..150911c7ff08 100644
--- a/security/tomoyo/common.c
+++ b/security/tomoyo/common.c
@@ -20,6 +20,7 @@ const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE] = {
 /* String table for /sys/kernel/security/tomoyo/profile */
 const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
 				       + TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
+	/* CONFIG::file group */
 	[TOMOYO_MAC_FILE_EXECUTE]    = "execute",
 	[TOMOYO_MAC_FILE_OPEN]       = "open",
 	[TOMOYO_MAC_FILE_CREATE]     = "create",
@@ -43,7 +44,28 @@ const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
 	[TOMOYO_MAC_FILE_MOUNT]      = "mount",
 	[TOMOYO_MAC_FILE_UMOUNT]     = "unmount",
 	[TOMOYO_MAC_FILE_PIVOT_ROOT] = "pivot_root",
+	/* CONFIG::network group */
+	[TOMOYO_MAC_NETWORK_INET_STREAM_BIND]       = "inet_stream_bind",
+	[TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN]     = "inet_stream_listen",
+	[TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT]    = "inet_stream_connect",
+	[TOMOYO_MAC_NETWORK_INET_DGRAM_BIND]        = "inet_dgram_bind",
+	[TOMOYO_MAC_NETWORK_INET_DGRAM_SEND]        = "inet_dgram_send",
+	[TOMOYO_MAC_NETWORK_INET_RAW_BIND]          = "inet_raw_bind",
+	[TOMOYO_MAC_NETWORK_INET_RAW_SEND]          = "inet_raw_send",
+	[TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND]       = "unix_stream_bind",
+	[TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN]     = "unix_stream_listen",
+	[TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT]    = "unix_stream_connect",
+	[TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND]        = "unix_dgram_bind",
+	[TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND]        = "unix_dgram_send",
+	[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND]    = "unix_seqpacket_bind",
+	[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN]  = "unix_seqpacket_listen",
+	[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] = "unix_seqpacket_connect",
+	/* CONFIG::misc group */
+	[TOMOYO_MAC_ENVIRON] = "env",
+	/* CONFIG group */
 	[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file",
+	[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_NETWORK] = "network",
+	[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_MISC] = "misc",
 };
 
 /* String table for conditions. */
@@ -130,10 +152,20 @@ const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = {
 	[TOMOYO_TYPE_UMOUNT]     = "unmount",
 };
 
+/* String table for socket's operation. */
+const char * const tomoyo_socket_keyword[TOMOYO_MAX_NETWORK_OPERATION] = {
+	[TOMOYO_NETWORK_BIND]    = "bind",
+	[TOMOYO_NETWORK_LISTEN]  = "listen",
+	[TOMOYO_NETWORK_CONNECT] = "connect",
+	[TOMOYO_NETWORK_SEND]    = "send",
+};
+
 /* String table for categories. */
 static const char * const tomoyo_category_keywords
 [TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
-	[TOMOYO_MAC_CATEGORY_FILE]       = "file",
+	[TOMOYO_MAC_CATEGORY_FILE]    = "file",
+	[TOMOYO_MAC_CATEGORY_NETWORK] = "network",
+	[TOMOYO_MAC_CATEGORY_MISC]    = "misc",
 };
 
 /* Permit policy management by non-root user? */
@@ -230,13 +262,17 @@ static void tomoyo_set_string(struct tomoyo_io_buffer *head, const char *string)
 		WARN_ON(1);
 }
 
+static void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt,
+			     ...) __printf(2, 3);
+
 /**
  * tomoyo_io_printf - printf() to "struct tomoyo_io_buffer" structure.
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
  * @fmt:  The printf()'s format string, followed by parameters.
  */
-void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
+static void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt,
+			     ...)
 {
 	va_list args;
 	size_t len;
@@ -313,7 +349,7 @@ void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns)
 		INIT_LIST_HEAD(&ns->group_list[idx]);
 	for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++)
 		INIT_LIST_HEAD(&ns->policy_list[idx]);
-	ns->profile_version = 20100903;
+	ns->profile_version = 20110903;
 	tomoyo_namespace_enabled = !list_empty(&tomoyo_namespace_list);
 	list_add_tail_rcu(&ns->namespace_list, &tomoyo_namespace_list);
 }
@@ -466,8 +502,10 @@ static struct tomoyo_profile *tomoyo_assign_profile
 			TOMOYO_CONFIG_WANT_REJECT_LOG;
 		memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT,
 		       sizeof(ptr->config));
-		ptr->pref[TOMOYO_PREF_MAX_AUDIT_LOG] = 1024;
-		ptr->pref[TOMOYO_PREF_MAX_LEARNING_ENTRY] = 2048;
+		ptr->pref[TOMOYO_PREF_MAX_AUDIT_LOG] =
+			CONFIG_SECURITY_TOMOYO_MAX_AUDIT_LOG;
+		ptr->pref[TOMOYO_PREF_MAX_LEARNING_ENTRY] =
+			CONFIG_SECURITY_TOMOYO_MAX_ACCEPT_ENTRY;
 		mb(); /* Avoid out-of-order execution. */
 		ns->profile_ptr[profile] = ptr;
 		entry = NULL;
@@ -951,14 +989,12 @@ static bool tomoyo_select_domain(struct tomoyo_io_buffer *head,
 	    (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) {
 		struct task_struct *p;
 		rcu_read_lock();
-		read_lock(&tasklist_lock);
 		if (global_pid)
 			p = find_task_by_pid_ns(pid, &init_pid_ns);
 		else
 			p = find_task_by_vpid(pid);
 		if (p)
 			domain = tomoyo_real_domain(p);
-		read_unlock(&tasklist_lock);
 		rcu_read_unlock();
 	} else if (!strncmp(data, "domain=", 7)) {
 		if (tomoyo_domain_def(data + 7))
@@ -982,6 +1018,48 @@ static bool tomoyo_select_domain(struct tomoyo_io_buffer *head,
 }
 
 /**
+ * tomoyo_same_task_acl - Check for duplicated "struct tomoyo_task_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool tomoyo_same_task_acl(const struct tomoyo_acl_info *a,
+			      const struct tomoyo_acl_info *b)
+{
+	const struct tomoyo_task_acl *p1 = container_of(a, typeof(*p1), head);
+	const struct tomoyo_task_acl *p2 = container_of(b, typeof(*p2), head);
+	return p1->domainname == p2->domainname;
+}
+
+/**
+ * tomoyo_write_task - Update task related list.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_write_task(struct tomoyo_acl_param *param)
+{
+	int error = -EINVAL;
+	if (tomoyo_str_starts(&param->data, "manual_domain_transition ")) {
+		struct tomoyo_task_acl e = {
+			.head.type = TOMOYO_TYPE_MANUAL_TASK_ACL,
+			.domainname = tomoyo_get_domainname(param),
+		};
+		if (e.domainname)
+			error = tomoyo_update_domain(&e.head, sizeof(e), param,
+						     tomoyo_same_task_acl,
+						     NULL);
+		tomoyo_put_name(e.domainname);
+	}
+	return error;
+}
+
+/**
  * tomoyo_delete_domain - Delete a domain.
  *
  * @domainname: The name of domain.
@@ -1039,11 +1117,16 @@ static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns,
 	static const struct {
 		const char *keyword;
 		int (*write) (struct tomoyo_acl_param *);
-	} tomoyo_callback[1] = {
+	} tomoyo_callback[5] = {
 		{ "file ", tomoyo_write_file },
+		{ "network inet ", tomoyo_write_inet_network },
+		{ "network unix ", tomoyo_write_unix_network },
+		{ "misc ", tomoyo_write_misc },
+		{ "task ", tomoyo_write_task },
 	};
 	u8 i;
-	for (i = 0; i < 1; i++) {
+
+	for (i = 0; i < ARRAY_SIZE(tomoyo_callback); i++) {
 		if (!tomoyo_str_starts(&param.data,
 				       tomoyo_callback[i].keyword))
 			continue;
@@ -1127,6 +1210,10 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
 	case 0:
 		head->r.cond_index = 0;
 		head->r.cond_step++;
+		if (cond->transit) {
+			tomoyo_set_space(head);
+			tomoyo_set_string(head, cond->transit->name);
+		}
 		/* fall through */
 	case 1:
 		{
@@ -1239,6 +1326,10 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
 		head->r.cond_step++;
 		/* fall through */
 	case 3:
+		if (cond->grant_log != TOMOYO_GRANTLOG_AUTO)
+			tomoyo_io_printf(head, " grant_log=%s",
+					 tomoyo_yesno(cond->grant_log ==
+						      TOMOYO_GRANTLOG_YES));
 		tomoyo_set_lf(head);
 		return true;
 	}
@@ -1306,6 +1397,12 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
 		if (first)
 			return true;
 		tomoyo_print_name_union(head, &ptr->name);
+	} else if (acl_type == TOMOYO_TYPE_MANUAL_TASK_ACL) {
+		struct tomoyo_task_acl *ptr =
+			container_of(acl, typeof(*ptr), head);
+		tomoyo_set_group(head, "task ");
+		tomoyo_set_string(head, "manual_domain_transition ");
+		tomoyo_set_string(head, ptr->domainname->name);
 	} else if (head->r.print_transition_related_only) {
 		return true;
 	} else if (acl_type == TOMOYO_TYPE_PATH2_ACL) {
@@ -1370,6 +1467,60 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
 		tomoyo_print_number_union(head, &ptr->mode);
 		tomoyo_print_number_union(head, &ptr->major);
 		tomoyo_print_number_union(head, &ptr->minor);
+	} else if (acl_type == TOMOYO_TYPE_INET_ACL) {
+		struct tomoyo_inet_acl *ptr =
+			container_of(acl, typeof(*ptr), head);
+		const u8 perm = ptr->perm;
+
+		for (bit = 0; bit < TOMOYO_MAX_NETWORK_OPERATION; bit++) {
+			if (!(perm & (1 << bit)))
+				continue;
+			if (first) {
+				tomoyo_set_group(head, "network inet ");
+				tomoyo_set_string(head, tomoyo_proto_keyword
+						  [ptr->protocol]);
+				tomoyo_set_space(head);
+				first = false;
+			} else {
+				tomoyo_set_slash(head);
+			}
+			tomoyo_set_string(head, tomoyo_socket_keyword[bit]);
+		}
+		if (first)
+			return true;
+		tomoyo_set_space(head);
+		if (ptr->address.group) {
+			tomoyo_set_string(head, "@");
+			tomoyo_set_string(head, ptr->address.group->group_name
+					  ->name);
+		} else {
+			char buf[128];
+			tomoyo_print_ip(buf, sizeof(buf), &ptr->address);
+			tomoyo_io_printf(head, "%s", buf);
+		}
+		tomoyo_print_number_union(head, &ptr->port);
+	} else if (acl_type == TOMOYO_TYPE_UNIX_ACL) {
+		struct tomoyo_unix_acl *ptr =
+			container_of(acl, typeof(*ptr), head);
+		const u8 perm = ptr->perm;
+
+		for (bit = 0; bit < TOMOYO_MAX_NETWORK_OPERATION; bit++) {
+			if (!(perm & (1 << bit)))
+				continue;
+			if (first) {
+				tomoyo_set_group(head, "network unix ");
+				tomoyo_set_string(head, tomoyo_proto_keyword
+						  [ptr->protocol]);
+				tomoyo_set_space(head);
+				first = false;
+			} else {
+				tomoyo_set_slash(head);
+			}
+			tomoyo_set_string(head, tomoyo_socket_keyword[bit]);
+		}
+		if (first)
+			return true;
+		tomoyo_print_name_union(head, &ptr->name);
 	} else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) {
 		struct tomoyo_mount_acl *ptr =
 			container_of(acl, typeof(*ptr), head);
@@ -1378,6 +1529,12 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
 		tomoyo_print_name_union(head, &ptr->dir_name);
 		tomoyo_print_name_union(head, &ptr->fs_type);
 		tomoyo_print_number_union(head, &ptr->flags);
+	} else if (acl_type == TOMOYO_TYPE_ENV_ACL) {
+		struct tomoyo_env_acl *ptr =
+			container_of(acl, typeof(*ptr), head);
+
+		tomoyo_set_group(head, "misc env ");
+		tomoyo_set_string(head, ptr->env->name);
 	}
 	if (acl->cond) {
 		head->r.print_cond_part = true;
@@ -1510,14 +1667,12 @@ static void tomoyo_read_pid(struct tomoyo_io_buffer *head)
 		global_pid = true;
 	pid = (unsigned int) simple_strtoul(buf, NULL, 10);
 	rcu_read_lock();
-	read_lock(&tasklist_lock);
 	if (global_pid)
 		p = find_task_by_pid_ns(pid, &init_pid_ns);
 	else
 		p = find_task_by_vpid(pid);
 	if (p)
 		domain = tomoyo_real_domain(p);
-	read_unlock(&tasklist_lock);
 	rcu_read_unlock();
 	if (!domain)
 		return;
@@ -1537,8 +1692,9 @@ static const char *tomoyo_transition_type[TOMOYO_MAX_TRANSITION_TYPE] = {
 
 /* String table for grouping keywords. */
 static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = {
-	[TOMOYO_PATH_GROUP]   = "path_group ",
-	[TOMOYO_NUMBER_GROUP] = "number_group ",
+	[TOMOYO_PATH_GROUP]    = "path_group ",
+	[TOMOYO_NUMBER_GROUP]  = "number_group ",
+	[TOMOYO_ADDRESS_GROUP] = "address_group ",
 };
 
 /**
@@ -1580,7 +1736,7 @@ static int tomoyo_write_exception(struct tomoyo_io_buffer *head)
 }
 
 /**
- * tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_group" list.
+ * tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_group"/"struct tomoyo_address_group" list.
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
  * @idx:  Index number.
@@ -1617,6 +1773,15 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx)
 							  (ptr,
 						   struct tomoyo_number_group,
 							   head)->number);
+			} else if (idx == TOMOYO_ADDRESS_GROUP) {
+				char buffer[128];
+
+				struct tomoyo_address_group *member =
+					container_of(ptr, typeof(*member),
+						     head);
+				tomoyo_print_ip(buffer, sizeof(buffer),
+						&member->address);
+				tomoyo_io_printf(head, " %s", buffer);
 			}
 			tomoyo_set_lf(head);
 		}
@@ -2066,27 +2231,7 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head)
 static void tomoyo_read_version(struct tomoyo_io_buffer *head)
 {
 	if (!head->r.eof) {
-		tomoyo_io_printf(head, "2.4.0");
-		head->r.eof = true;
-	}
-}
-
-/**
- * tomoyo_read_self_domain - Get the current process's domainname.
- *
- * @head: Pointer to "struct tomoyo_io_buffer".
- *
- * Returns the current process's domainname.
- */
-static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head)
-{
-	if (!head->r.eof) {
-		/*
-		 * tomoyo_domain()->domainname != NULL
-		 * because every process belongs to a domain and
-		 * the domain's name cannot be NULL.
-		 */
-		tomoyo_io_printf(head, "%s", tomoyo_domain()->domainname->name);
+		tomoyo_io_printf(head, "2.5.0");
 		head->r.eof = true;
 	}
 }
@@ -2221,10 +2366,6 @@ int tomoyo_open_control(const u8 type, struct file *file)
 		head->poll = tomoyo_poll_log;
 		head->read = tomoyo_read_log;
 		break;
-	case TOMOYO_SELFDOMAIN:
-		/* /sys/kernel/security/tomoyo/self_domain */
-		head->read = tomoyo_read_self_domain;
-		break;
 	case TOMOYO_PROCESS_STATUS:
 		/* /sys/kernel/security/tomoyo/.process_status */
 		head->write = tomoyo_write_pid;
@@ -2453,6 +2594,7 @@ ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head,
 		return -EFAULT;
 	if (mutex_lock_interruptible(&head->io_sem))
 		return -EINTR;
+	head->read_user_buf_avail = 0;
 	idx = tomoyo_read_lock();
 	/* Read a line and dispatch it to the policy handler. */
 	while (avail_len > 0) {
@@ -2562,11 +2704,11 @@ void tomoyo_check_profile(void)
 	struct tomoyo_domain_info *domain;
 	const int idx = tomoyo_read_lock();
 	tomoyo_policy_loaded = true;
-	printk(KERN_INFO "TOMOYO: 2.4.0\n");
+	printk(KERN_INFO "TOMOYO: 2.5.0\n");
 	list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
 		const u8 profile = domain->profile;
 		const struct tomoyo_policy_namespace *ns = domain->ns;
-		if (ns->profile_version != 20100903)
+		if (ns->profile_version != 20110903)
 			printk(KERN_ERR
 			       "Profile version %u is not supported.\n",
 			       ns->profile_version);
@@ -2577,9 +2719,9 @@ void tomoyo_check_profile(void)
 		else
 			continue;
 		printk(KERN_ERR
-		       "Userland tools for TOMOYO 2.4 must be installed and "
+		       "Userland tools for TOMOYO 2.5 must be installed and "
 		       "policy must be initialized.\n");
-		printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.4/ "
+		printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.5/ "
 		       "for more information.\n");
 		panic("STOP!");
 	}
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
index f7fbaa66e443..ed311d7a8ce0 100644
--- a/security/tomoyo/common.h
+++ b/security/tomoyo/common.h
@@ -3,7 +3,7 @@
  *
  * Header file for TOMOYO.
  *
- * Copyright (C) 2005-2010  NTT DATA CORPORATION
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
  */
 
 #ifndef _SECURITY_TOMOYO_COMMON_H
@@ -23,6 +23,16 @@
 #include <linux/poll.h>
 #include <linux/binfmts.h>
 #include <linux/highmem.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/un.h>
+#include <net/sock.h>
+#include <net/af_unix.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/udp.h>
 
 /********** Constants definitions. **********/
 
@@ -34,8 +44,17 @@
 #define TOMOYO_HASH_BITS  8
 #define TOMOYO_MAX_HASH (1u<<TOMOYO_HASH_BITS)
 
+/*
+ * TOMOYO checks only SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_SEQPACKET.
+ * Therefore, we don't need SOCK_MAX.
+ */
+#define TOMOYO_SOCK_MAX 6
+
 #define TOMOYO_EXEC_TMPSIZE     4096
 
+/* Garbage collector is trying to kfree() this element. */
+#define TOMOYO_GC_IN_PROGRESS -1
+
 /* Profile number is an integer between 0 and 255. */
 #define TOMOYO_MAX_PROFILES 256
 
@@ -136,6 +155,7 @@ enum tomoyo_mode_index {
 /* Index numbers for entry type. */
 enum tomoyo_policy_id {
 	TOMOYO_ID_GROUP,
+	TOMOYO_ID_ADDRESS_GROUP,
 	TOMOYO_ID_PATH_GROUP,
 	TOMOYO_ID_NUMBER_GROUP,
 	TOMOYO_ID_TRANSITION_CONTROL,
@@ -162,10 +182,21 @@ enum tomoyo_domain_info_flags_index {
 	TOMOYO_MAX_DOMAIN_INFO_FLAGS
 };
 
+/* Index numbers for audit type. */
+enum tomoyo_grant_log {
+	/* Follow profile's configuration. */
+	TOMOYO_GRANTLOG_AUTO,
+	/* Do not generate grant log. */
+	TOMOYO_GRANTLOG_NO,
+	/* Generate grant_log. */
+	TOMOYO_GRANTLOG_YES,
+};
+
 /* Index numbers for group entries. */
 enum tomoyo_group_id {
 	TOMOYO_PATH_GROUP,
 	TOMOYO_NUMBER_GROUP,
+	TOMOYO_ADDRESS_GROUP,
 	TOMOYO_MAX_GROUP
 };
 
@@ -196,6 +227,10 @@ enum tomoyo_acl_entry_type_index {
 	TOMOYO_TYPE_PATH_NUMBER_ACL,
 	TOMOYO_TYPE_MKDEV_ACL,
 	TOMOYO_TYPE_MOUNT_ACL,
+	TOMOYO_TYPE_INET_ACL,
+	TOMOYO_TYPE_UNIX_ACL,
+	TOMOYO_TYPE_ENV_ACL,
+	TOMOYO_TYPE_MANUAL_TASK_ACL,
 };
 
 /* Index numbers for access controls with one pathname. */
@@ -228,6 +263,15 @@ enum tomoyo_mkdev_acl_index {
 	TOMOYO_MAX_MKDEV_OPERATION
 };
 
+/* Index numbers for socket operations. */
+enum tomoyo_network_acl_index {
+	TOMOYO_NETWORK_BIND,    /* bind() operation. */
+	TOMOYO_NETWORK_LISTEN,  /* listen() operation. */
+	TOMOYO_NETWORK_CONNECT, /* connect() operation. */
+	TOMOYO_NETWORK_SEND,    /* send() operation. */
+	TOMOYO_MAX_NETWORK_OPERATION
+};
+
 /* Index numbers for access controls with two pathnames. */
 enum tomoyo_path2_acl_index {
 	TOMOYO_TYPE_LINK,
@@ -255,7 +299,6 @@ enum tomoyo_securityfs_interface_index {
 	TOMOYO_EXCEPTIONPOLICY,
 	TOMOYO_PROCESS_STATUS,
 	TOMOYO_STAT,
-	TOMOYO_SELFDOMAIN,
 	TOMOYO_AUDIT,
 	TOMOYO_VERSION,
 	TOMOYO_PROFILE,
@@ -300,12 +343,30 @@ enum tomoyo_mac_index {
 	TOMOYO_MAC_FILE_MOUNT,
 	TOMOYO_MAC_FILE_UMOUNT,
 	TOMOYO_MAC_FILE_PIVOT_ROOT,
+	TOMOYO_MAC_NETWORK_INET_STREAM_BIND,
+	TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN,
+	TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT,
+	TOMOYO_MAC_NETWORK_INET_DGRAM_BIND,
+	TOMOYO_MAC_NETWORK_INET_DGRAM_SEND,
+	TOMOYO_MAC_NETWORK_INET_RAW_BIND,
+	TOMOYO_MAC_NETWORK_INET_RAW_SEND,
+	TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND,
+	TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN,
+	TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT,
+	TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND,
+	TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND,
+	TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND,
+	TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN,
+	TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT,
+	TOMOYO_MAC_ENVIRON,
 	TOMOYO_MAX_MAC_INDEX
 };
 
 /* Index numbers for category of functionality. */
 enum tomoyo_mac_category_index {
 	TOMOYO_MAC_CATEGORY_FILE,
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	TOMOYO_MAC_CATEGORY_MISC,
 	TOMOYO_MAX_MAC_CATEGORY_INDEX
 };
 
@@ -340,7 +401,7 @@ enum tomoyo_pref_index {
 /* Common header for holding ACL entries. */
 struct tomoyo_acl_head {
 	struct list_head list;
-	bool is_deleted;
+	s8 is_deleted; /* true or false or TOMOYO_GC_IN_PROGRESS */
 } __packed;
 
 /* Common header for shared entries. */
@@ -397,13 +458,36 @@ struct tomoyo_request_info {
 			u8 operation;
 		} path_number;
 		struct {
+			const struct tomoyo_path_info *name;
+		} environ;
+		struct {
+			const __be32 *address;
+			u16 port;
+			/* One of values smaller than TOMOYO_SOCK_MAX. */
+			u8 protocol;
+			/* One of values in "enum tomoyo_network_acl_index". */
+			u8 operation;
+			bool is_ipv6;
+		} inet_network;
+		struct {
+			const struct tomoyo_path_info *address;
+			/* One of values smaller than TOMOYO_SOCK_MAX. */
+			u8 protocol;
+			/* One of values in "enum tomoyo_network_acl_index". */
+			u8 operation;
+		} unix_network;
+		struct {
 			const struct tomoyo_path_info *type;
 			const struct tomoyo_path_info *dir;
 			const struct tomoyo_path_info *dev;
 			unsigned long flags;
 			int need_dev;
 		} mount;
+		struct {
+			const struct tomoyo_path_info *domainname;
+		} task;
 	} param;
+	struct tomoyo_acl_info *matched_acl;
 	u8 param_type;
 	bool granted;
 	u8 retry;
@@ -442,7 +526,14 @@ struct tomoyo_number_union {
 	u8 value_type[2];
 };
 
-/* Structure for "path_group"/"number_group" directive. */
+/* Structure for holding an IP address. */
+struct tomoyo_ipaddr_union {
+	struct in6_addr ip[2]; /* Big endian. */
+	struct tomoyo_group *group; /* Pointer to address group. */
+	bool is_ipv6; /* Valid only if @group == NULL. */
+};
+
+/* Structure for "path_group"/"number_group"/"address_group" directive. */
 struct tomoyo_group {
 	struct tomoyo_shared_acl_head head;
 	const struct tomoyo_path_info *group_name;
@@ -461,6 +552,13 @@ struct tomoyo_number_group {
 	struct tomoyo_number_union number;
 };
 
+/* Structure for "address_group" directive. */
+struct tomoyo_address_group {
+	struct tomoyo_acl_head head;
+	/* Structure for holding an IP address. */
+	struct tomoyo_ipaddr_union address;
+};
+
 /* Subset of "struct stat". Used by conditional ACL and audit logs. */
 struct tomoyo_mini_stat {
 	uid_t uid;
@@ -520,6 +618,7 @@ struct tomoyo_execve {
 	struct tomoyo_request_info r;
 	struct tomoyo_obj_info obj;
 	struct linux_binprm *bprm;
+	const struct tomoyo_path_info *transition;
 	/* For dumping argv[] and envp[]. */
 	struct tomoyo_page_dump dump;
 	/* For temporary use. */
@@ -554,6 +653,8 @@ struct tomoyo_condition {
 	u16 names_count; /* Number of "struct tomoyo_name_union names". */
 	u16 argc; /* Number of "struct tomoyo_argv". */
 	u16 envc; /* Number of "struct tomoyo_envp". */
+	u8 grant_log; /* One of values in "enum tomoyo_grant_log". */
+	const struct tomoyo_path_info *transit; /* Maybe NULL. */
 	/*
 	 * struct tomoyo_condition_element condition[condc];
 	 * struct tomoyo_number_union values[numbers_count];
@@ -567,7 +668,7 @@ struct tomoyo_condition {
 struct tomoyo_acl_info {
 	struct list_head list;
 	struct tomoyo_condition *cond; /* Maybe NULL. */
-	bool is_deleted;
+	s8 is_deleted; /* true or false or TOMOYO_GC_IN_PROGRESS */
 	u8 type; /* One of values in "enum tomoyo_acl_entry_type_index". */
 } __packed;
 
@@ -587,6 +688,15 @@ struct tomoyo_domain_info {
 };
 
 /*
+ * Structure for "task manual_domain_transition" directive.
+ */
+struct tomoyo_task_acl {
+	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MANUAL_TASK_ACL */
+	/* Pointer to domainname. */
+	const struct tomoyo_path_info *domainname;
+};
+
+/*
  * Structure for "file execute", "file read", "file write", "file append",
  * "file unlink", "file getattr", "file rmdir", "file truncate",
  * "file symlink", "file chroot" and "file unmount" directive.
@@ -638,6 +748,29 @@ struct tomoyo_mount_acl {
 	struct tomoyo_number_union flags;
 };
 
+/* Structure for "misc env" directive in domain policy. */
+struct tomoyo_env_acl {
+	struct tomoyo_acl_info head;        /* type = TOMOYO_TYPE_ENV_ACL  */
+	const struct tomoyo_path_info *env; /* environment variable */
+};
+
+/* Structure for "network inet" directive. */
+struct tomoyo_inet_acl {
+	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_INET_ACL */
+	u8 protocol;
+	u8 perm; /* Bitmask of values in "enum tomoyo_network_acl_index" */
+	struct tomoyo_ipaddr_union address;
+	struct tomoyo_number_union port;
+};
+
+/* Structure for "network unix" directive. */
+struct tomoyo_unix_acl {
+	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_UNIX_ACL */
+	u8 protocol;
+	u8 perm; /* Bitmask of values in "enum tomoyo_network_acl_index" */
+	struct tomoyo_name_union name;
+};
+
 /* Structure for holding a line from /sys/kernel/security/tomoyo/ interface. */
 struct tomoyo_acl_param {
 	char *data;
@@ -773,7 +906,7 @@ struct tomoyo_policy_namespace {
 	struct list_head acl_group[TOMOYO_MAX_ACL_GROUPS];
 	/* List for connecting to tomoyo_namespace_list list. */
 	struct list_head namespace_list;
-	/* Profile version. Currently only 20100903 is defined. */
+	/* Profile version. Currently only 20110903 is defined. */
 	unsigned int profile_version;
 	/* Name of this namespace (e.g. "<kernel>", "</usr/sbin/httpd>" ). */
 	const char *name;
@@ -781,6 +914,8 @@ struct tomoyo_policy_namespace {
 
 /********** Function prototypes. **********/
 
+bool tomoyo_address_matches_group(const bool is_ipv6, const __be32 *address,
+				  const struct tomoyo_group *group);
 bool tomoyo_compare_number_union(const unsigned long value,
 				 const struct tomoyo_number_union *ptr);
 bool tomoyo_condition(struct tomoyo_request_info *r,
@@ -796,6 +931,8 @@ bool tomoyo_memory_ok(void *ptr);
 bool tomoyo_number_matches_group(const unsigned long min,
 				 const unsigned long max,
 				 const struct tomoyo_group *group);
+bool tomoyo_parse_ipaddr_union(struct tomoyo_acl_param *param,
+			       struct tomoyo_ipaddr_union *ptr);
 bool tomoyo_parse_name_union(struct tomoyo_acl_param *param,
 			     struct tomoyo_name_union *ptr);
 bool tomoyo_parse_number_union(struct tomoyo_acl_param *param,
@@ -805,6 +942,7 @@ bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
 bool tomoyo_permstr(const char *string, const char *keyword);
 bool tomoyo_str_starts(char **src, const char *find);
 char *tomoyo_encode(const char *str);
+char *tomoyo_encode2(const char *str, int str_len);
 char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt,
 		      va_list args);
 char *tomoyo_read_token(struct tomoyo_acl_param *param);
@@ -814,12 +952,17 @@ const char *tomoyo_get_exe(void);
 const char *tomoyo_yesno(const unsigned int value);
 const struct tomoyo_path_info *tomoyo_compare_name_union
 (const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr);
+const struct tomoyo_path_info *tomoyo_get_domainname
+(struct tomoyo_acl_param *param);
 const struct tomoyo_path_info *tomoyo_get_name(const char *name);
 const struct tomoyo_path_info *tomoyo_path_matches_group
 (const struct tomoyo_path_info *pathname, const struct tomoyo_group *group);
 int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
 				 struct path *path, const int flag);
 int tomoyo_close_control(struct tomoyo_io_buffer *head);
+int tomoyo_env_perm(struct tomoyo_request_info *r, const char *env);
+int tomoyo_execute_permission(struct tomoyo_request_info *r,
+			      const struct tomoyo_path_info *filename);
 int tomoyo_find_next_domain(struct linux_binprm *bprm);
 int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile,
 		    const u8 index);
@@ -838,10 +981,15 @@ int tomoyo_path_number_perm(const u8 operation, struct path *path,
 			    unsigned long number);
 int tomoyo_path_perm(const u8 operation, struct path *path,
 		     const char *target);
-int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
-			   const struct tomoyo_path_info *filename);
 int tomoyo_poll_control(struct file *file, poll_table *wait);
 int tomoyo_poll_log(struct file *file, poll_table *wait);
+int tomoyo_socket_bind_permission(struct socket *sock, struct sockaddr *addr,
+				  int addr_len);
+int tomoyo_socket_connect_permission(struct socket *sock,
+				     struct sockaddr *addr, int addr_len);
+int tomoyo_socket_listen_permission(struct socket *sock);
+int tomoyo_socket_sendmsg_permission(struct socket *sock, struct msghdr *msg,
+				     int size);
 int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
 	__printf(2, 3);
 int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
@@ -860,8 +1008,11 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
 int tomoyo_write_aggregator(struct tomoyo_acl_param *param);
 int tomoyo_write_file(struct tomoyo_acl_param *param);
 int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type);
+int tomoyo_write_misc(struct tomoyo_acl_param *param);
+int tomoyo_write_inet_network(struct tomoyo_acl_param *param);
 int tomoyo_write_transition_control(struct tomoyo_acl_param *param,
 				    const u8 type);
+int tomoyo_write_unix_network(struct tomoyo_acl_param *param);
 ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer,
 			    const int buffer_len);
 ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head,
@@ -891,12 +1042,11 @@ void tomoyo_del_condition(struct list_head *element);
 void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
 void tomoyo_get_attributes(struct tomoyo_obj_info *obj);
 void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns);
-void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
-	 __printf(2, 3);
 void tomoyo_load_policy(const char *filename);
-void tomoyo_memory_free(void *ptr);
 void tomoyo_normalize_line(unsigned char *buffer);
 void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register);
+void tomoyo_print_ip(char *buf, const unsigned int size,
+		     const struct tomoyo_ipaddr_union *ptr);
 void tomoyo_print_ulong(char *buffer, const int buffer_len,
 			const unsigned long value, const u8 type);
 void tomoyo_put_name_union(struct tomoyo_name_union *ptr);
@@ -919,6 +1069,8 @@ extern const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
 					      + TOMOYO_MAX_MAC_CATEGORY_INDEX];
 extern const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE];
 extern const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION];
+extern const char * const tomoyo_proto_keyword[TOMOYO_SOCK_MAX];
+extern const char * const tomoyo_socket_keyword[TOMOYO_MAX_NETWORK_OPERATION];
 extern const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX];
 extern const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION];
 extern const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION];
@@ -1098,6 +1250,21 @@ static inline bool tomoyo_same_number_union
 }
 
 /**
+ * tomoyo_same_ipaddr_union - Check for duplicated "struct tomoyo_ipaddr_union" entry.
+ *
+ * @a: Pointer to "struct tomoyo_ipaddr_union".
+ * @b: Pointer to "struct tomoyo_ipaddr_union".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static inline bool tomoyo_same_ipaddr_union
+(const struct tomoyo_ipaddr_union *a, const struct tomoyo_ipaddr_union *b)
+{
+	return !memcmp(a->ip, b->ip, sizeof(a->ip)) && a->group == b->group &&
+		a->is_ipv6 == b->is_ipv6;
+}
+
+/**
  * tomoyo_current_namespace - Get "struct tomoyo_policy_namespace" for current thread.
  *
  * Returns pointer to "struct tomoyo_policy_namespace" for current thread.
diff --git a/security/tomoyo/condition.c b/security/tomoyo/condition.c
index 8a05f71eaf67..986330b8c73e 100644
--- a/security/tomoyo/condition.c
+++ b/security/tomoyo/condition.c
@@ -348,6 +348,7 @@ static inline bool tomoyo_same_condition(const struct tomoyo_condition *a,
 		a->numbers_count == b->numbers_count &&
 		a->names_count == b->names_count &&
 		a->argc == b->argc && a->envc == b->envc &&
+		a->grant_log == b->grant_log && a->transit == b->transit &&
 		!memcmp(a + 1, b + 1, a->size - sizeof(*a));
 }
 
@@ -399,8 +400,9 @@ static struct tomoyo_condition *tomoyo_commit_condition
 		found = true;
 		goto out;
 	}
-	list_for_each_entry_rcu(ptr, &tomoyo_condition_list, head.list) {
-		if (!tomoyo_same_condition(ptr, entry))
+	list_for_each_entry(ptr, &tomoyo_condition_list, head.list) {
+		if (!tomoyo_same_condition(ptr, entry) ||
+		    atomic_read(&ptr->head.users) == TOMOYO_GC_IN_PROGRESS)
 			continue;
 		/* Same entry found. Share this entry. */
 		atomic_inc(&ptr->head.users);
@@ -410,8 +412,7 @@ static struct tomoyo_condition *tomoyo_commit_condition
 	if (!found) {
 		if (tomoyo_memory_ok(entry)) {
 			atomic_set(&entry->head.users, 1);
-			list_add_rcu(&entry->head.list,
-				     &tomoyo_condition_list);
+			list_add(&entry->head.list, &tomoyo_condition_list);
 		} else {
 			found = true;
 			ptr = NULL;
@@ -428,6 +429,46 @@ out:
 }
 
 /**
+ * tomoyo_get_transit_preference - Parse domain transition preference for execve().
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ * @e:     Pointer to "struct tomoyo_condition".
+ *
+ * Returns the condition string part.
+ */
+static char *tomoyo_get_transit_preference(struct tomoyo_acl_param *param,
+					   struct tomoyo_condition *e)
+{
+	char * const pos = param->data;
+	bool flag;
+	if (*pos == '<') {
+		e->transit = tomoyo_get_domainname(param);
+		goto done;
+	}
+	{
+		char *cp = strchr(pos, ' ');
+		if (cp)
+			*cp = '\0';
+		flag = tomoyo_correct_path(pos) || !strcmp(pos, "keep") ||
+			!strcmp(pos, "initialize") || !strcmp(pos, "reset") ||
+			!strcmp(pos, "child") || !strcmp(pos, "parent");
+		if (cp)
+			*cp = ' ';
+	}
+	if (!flag)
+		return pos;
+	e->transit = tomoyo_get_name(tomoyo_read_token(param));
+done:
+	if (e->transit)
+		return param->data;
+	/*
+	 * Return a bad read-only condition string that will let
+	 * tomoyo_get_condition() return NULL.
+	 */
+	return "/";
+}
+
+/**
  * tomoyo_get_condition - Parse condition part.
  *
  * @param: Pointer to "struct tomoyo_acl_param".
@@ -443,7 +484,8 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param)
 	struct tomoyo_argv *argv = NULL;
 	struct tomoyo_envp *envp = NULL;
 	struct tomoyo_condition e = { };
-	char * const start_of_string = param->data;
+	char * const start_of_string =
+		tomoyo_get_transit_preference(param, &e);
 	char * const end_of_string = start_of_string + strlen(start_of_string);
 	char *pos;
 rerun:
@@ -486,6 +528,20 @@ rerun:
 			goto out;
 		dprintk(KERN_WARNING "%u: <%s>%s=<%s>\n", __LINE__, left_word,
 			is_not ? "!" : "", right_word);
+		if (!strcmp(left_word, "grant_log")) {
+			if (entry) {
+				if (is_not ||
+				    entry->grant_log != TOMOYO_GRANTLOG_AUTO)
+					goto out;
+				else if (!strcmp(right_word, "yes"))
+					entry->grant_log = TOMOYO_GRANTLOG_YES;
+				else if (!strcmp(right_word, "no"))
+					entry->grant_log = TOMOYO_GRANTLOG_NO;
+				else
+					goto out;
+			}
+			continue;
+		}
 		if (!strncmp(left_word, "exec.argv[", 10)) {
 			if (!argv) {
 				e.argc++;
@@ -593,8 +649,9 @@ store_value:
 		+ e.envc * sizeof(struct tomoyo_envp);
 	entry = kzalloc(e.size, GFP_NOFS);
 	if (!entry)
-		return NULL;
+		goto out2;
 	*entry = e;
+	e.transit = NULL;
 	condp = (struct tomoyo_condition_element *) (entry + 1);
 	numbers_p = (struct tomoyo_number_union *) (condp + e.condc);
 	names_p = (struct tomoyo_name_union *) (numbers_p + e.numbers_count);
@@ -621,6 +678,8 @@ out:
 		tomoyo_del_condition(&entry->head.list);
 		kfree(entry);
 	}
+out2:
+	tomoyo_put_name(e.transit);
 	return NULL;
 }
 
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
index cd0f92d88bb4..9027ac1534af 100644
--- a/security/tomoyo/domain.c
+++ b/security/tomoyo/domain.c
@@ -39,6 +39,8 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
 		return -ENOMEM;
 	list_for_each_entry_rcu(entry, list, list) {
+		if (entry->is_deleted == TOMOYO_GC_IN_PROGRESS)
+			continue;
 		if (!check_duplicate(entry, new_entry))
 			continue;
 		entry->is_deleted = param->is_delete;
@@ -102,10 +104,21 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
 		new_entry->cond = tomoyo_get_condition(param);
 		if (!new_entry->cond)
 			return -EINVAL;
+		/*
+		 * Domain transition preference is allowed for only
+		 * "file execute" entries.
+		 */
+		if (new_entry->cond->transit &&
+		    !(new_entry->type == TOMOYO_TYPE_PATH_ACL &&
+		      container_of(new_entry, struct tomoyo_path_acl, head)
+		      ->perm == 1 << TOMOYO_TYPE_EXECUTE))
+			goto out;
 	}
 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
 		goto out;
 	list_for_each_entry_rcu(entry, list, list) {
+		if (entry->is_deleted == TOMOYO_GC_IN_PROGRESS)
+			continue;
 		if (!tomoyo_same_acl_head(entry, new_entry) ||
 		    !check_duplicate(entry, new_entry))
 			continue;
@@ -157,6 +170,7 @@ retry:
 			continue;
 		if (!tomoyo_condition(r, ptr->cond))
 			continue;
+		r->matched_acl = ptr;
 		r->granted = true;
 		return;
 	}
@@ -501,7 +515,8 @@ struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
 			 * that domain. Do not perform domain transition if
 			 * profile for that domain is not yet created.
 			 */
-			if (!entry->ns->profile_ptr[entry->profile])
+			if (tomoyo_policy_loaded &&
+			    !entry->ns->profile_ptr[entry->profile])
 				return NULL;
 		}
 		return entry;
@@ -557,12 +572,99 @@ out:
 			tomoyo_write_log(&r, "use_profile %u\n",
 					 entry->profile);
 			tomoyo_write_log(&r, "use_group %u\n", entry->group);
+			tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES);
 		}
 	}
 	return entry;
 }
 
 /**
+ * tomoyo_environ - Check permission for environment variable names.
+ *
+ * @ee: Pointer to "struct tomoyo_execve".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_environ(struct tomoyo_execve *ee)
+{
+	struct tomoyo_request_info *r = &ee->r;
+	struct linux_binprm *bprm = ee->bprm;
+	/* env_page.data is allocated by tomoyo_dump_page(). */
+	struct tomoyo_page_dump env_page = { };
+	char *arg_ptr; /* Size is TOMOYO_EXEC_TMPSIZE bytes */
+	int arg_len = 0;
+	unsigned long pos = bprm->p;
+	int offset = pos % PAGE_SIZE;
+	int argv_count = bprm->argc;
+	int envp_count = bprm->envc;
+	int error = -ENOMEM;
+
+	ee->r.type = TOMOYO_MAC_ENVIRON;
+	ee->r.profile = r->domain->profile;
+	ee->r.mode = tomoyo_get_mode(r->domain->ns, ee->r.profile,
+				     TOMOYO_MAC_ENVIRON);
+	if (!r->mode || !envp_count)
+		return 0;
+	arg_ptr = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
+	if (!arg_ptr)
+		goto out;
+	while (error == -ENOMEM) {
+		if (!tomoyo_dump_page(bprm, pos, &env_page))
+			goto out;
+		pos += PAGE_SIZE - offset;
+		/* Read. */
+		while (argv_count && offset < PAGE_SIZE) {
+			if (!env_page.data[offset++])
+				argv_count--;
+		}
+		if (argv_count) {
+			offset = 0;
+			continue;
+		}
+		while (offset < PAGE_SIZE) {
+			const unsigned char c = env_page.data[offset++];
+
+			if (c && arg_len < TOMOYO_EXEC_TMPSIZE - 10) {
+				if (c == '=') {
+					arg_ptr[arg_len++] = '\0';
+				} else if (c == '\\') {
+					arg_ptr[arg_len++] = '\\';
+					arg_ptr[arg_len++] = '\\';
+				} else if (c > ' ' && c < 127) {
+					arg_ptr[arg_len++] = c;
+				} else {
+					arg_ptr[arg_len++] = '\\';
+					arg_ptr[arg_len++] = (c >> 6) + '0';
+					arg_ptr[arg_len++]
+						= ((c >> 3) & 7) + '0';
+					arg_ptr[arg_len++] = (c & 7) + '0';
+				}
+			} else {
+				arg_ptr[arg_len] = '\0';
+			}
+			if (c)
+				continue;
+			if (tomoyo_env_perm(r, arg_ptr)) {
+				error = -EPERM;
+				break;
+			}
+			if (!--envp_count) {
+				error = 0;
+				break;
+			}
+			arg_len = 0;
+		}
+		offset = 0;
+	}
+out:
+	if (r->mode != TOMOYO_CONFIG_ENFORCING)
+		error = 0;
+	kfree(env_page.data);
+	kfree(arg_ptr);
+	return error;
+}
+
+/**
  * tomoyo_find_next_domain - Find a domain.
  *
  * @bprm: Pointer to "struct linux_binprm".
@@ -577,10 +679,11 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
 	struct tomoyo_domain_info *domain = NULL;
 	const char *original_name = bprm->filename;
 	int retval = -ENOMEM;
-	bool need_kfree = false;
 	bool reject_on_transition_failure = false;
-	struct tomoyo_path_info rn = { }; /* real name */
+	const struct tomoyo_path_info *candidate;
+	struct tomoyo_path_info exename;
 	struct tomoyo_execve *ee = kzalloc(sizeof(*ee), GFP_NOFS);
+
 	if (!ee)
 		return -ENOMEM;
 	ee->tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
@@ -594,40 +697,32 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
 	ee->bprm = bprm;
 	ee->r.obj = &ee->obj;
 	ee->obj.path1 = bprm->file->f_path;
- retry:
-	if (need_kfree) {
-		kfree(rn.name);
-		need_kfree = false;
-	}
 	/* Get symlink's pathname of program. */
 	retval = -ENOENT;
-	rn.name = tomoyo_realpath_nofollow(original_name);
-	if (!rn.name)
+	exename.name = tomoyo_realpath_nofollow(original_name);
+	if (!exename.name)
 		goto out;
-	tomoyo_fill_path_info(&rn);
-	need_kfree = true;
-
+	tomoyo_fill_path_info(&exename);
+retry:
 	/* Check 'aggregator' directive. */
 	{
 		struct tomoyo_aggregator *ptr;
 		struct list_head *list =
 			&old_domain->ns->policy_list[TOMOYO_ID_AGGREGATOR];
 		/* Check 'aggregator' directive. */
+		candidate = &exename;
 		list_for_each_entry_rcu(ptr, list, head.list) {
 			if (ptr->head.is_deleted ||
-			    !tomoyo_path_matches_pattern(&rn,
+			    !tomoyo_path_matches_pattern(&exename,
 							 ptr->original_name))
 				continue;
-			kfree(rn.name);
-			need_kfree = false;
-			/* This is OK because it is read only. */
-			rn = *ptr->aggregated_name;
+			candidate = ptr->aggregated_name;
 			break;
 		}
 	}
 
 	/* Check execute permission. */
-	retval = tomoyo_path_permission(&ee->r, TOMOYO_TYPE_EXECUTE, &rn);
+	retval = tomoyo_execute_permission(&ee->r, candidate);
 	if (retval == TOMOYO_RETRY_REQUEST)
 		goto retry;
 	if (retval < 0)
@@ -638,20 +733,51 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
 	 * wildcard) rather than the pathname passed to execve()
 	 * (which never contains wildcard).
 	 */
-	if (ee->r.param.path.matched_path) {
-		if (need_kfree)
-			kfree(rn.name);
-		need_kfree = false;
-		/* This is OK because it is read only. */
-		rn = *ee->r.param.path.matched_path;
-	}
+	if (ee->r.param.path.matched_path)
+		candidate = ee->r.param.path.matched_path;
 
-	/* Calculate domain to transit to. */
+	/*
+	 * Check for domain transition preference if "file execute" matched.
+	 * If preference is given, make do_execve() fail if domain transition
+	 * has failed, for domain transition preference should be used with
+	 * destination domain defined.
+	 */
+	if (ee->transition) {
+		const char *domainname = ee->transition->name;
+		reject_on_transition_failure = true;
+		if (!strcmp(domainname, "keep"))
+			goto force_keep_domain;
+		if (!strcmp(domainname, "child"))
+			goto force_child_domain;
+		if (!strcmp(domainname, "reset"))
+			goto force_reset_domain;
+		if (!strcmp(domainname, "initialize"))
+			goto force_initialize_domain;
+		if (!strcmp(domainname, "parent")) {
+			char *cp;
+			strncpy(ee->tmp, old_domain->domainname->name,
+				TOMOYO_EXEC_TMPSIZE - 1);
+			cp = strrchr(ee->tmp, ' ');
+			if (cp)
+				*cp = '\0';
+		} else if (*domainname == '<')
+			strncpy(ee->tmp, domainname, TOMOYO_EXEC_TMPSIZE - 1);
+		else
+			snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
+				 old_domain->domainname->name, domainname);
+		goto force_jump_domain;
+	}
+	/*
+	 * No domain transition preference specified.
+	 * Calculate domain to transit to.
+	 */
 	switch (tomoyo_transition_type(old_domain->ns, old_domain->domainname,
-				       &rn)) {
+				       candidate)) {
 	case TOMOYO_TRANSITION_CONTROL_RESET:
+force_reset_domain:
 		/* Transit to the root of specified namespace. */
-		snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>", rn.name);
+		snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>",
+			 candidate->name);
 		/*
 		 * Make do_execve() fail if domain transition across namespaces
 		 * has failed.
@@ -659,11 +785,13 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
 		reject_on_transition_failure = true;
 		break;
 	case TOMOYO_TRANSITION_CONTROL_INITIALIZE:
+force_initialize_domain:
 		/* Transit to the child of current namespace's root. */
 		snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
-			 old_domain->ns->name, rn.name);
+			 old_domain->ns->name, candidate->name);
 		break;
 	case TOMOYO_TRANSITION_CONTROL_KEEP:
+force_keep_domain:
 		/* Keep current domain. */
 		domain = old_domain;
 		break;
@@ -677,13 +805,15 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
 			 * before /sbin/init.
 			 */
 			domain = old_domain;
-		} else {
-			/* Normal domain transition. */
-			snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
-				 old_domain->domainname->name, rn.name);
+			break;
 		}
+force_child_domain:
+		/* Normal domain transition. */
+		snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
+			 old_domain->domainname->name, candidate->name);
 		break;
 	}
+force_jump_domain:
 	if (!domain)
 		domain = tomoyo_assign_domain(ee->tmp, true);
 	if (domain)
@@ -711,8 +841,11 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
 	/* Update reference count on "struct tomoyo_domain_info". */
 	atomic_inc(&domain->users);
 	bprm->cred->security = domain;
-	if (need_kfree)
-		kfree(rn.name);
+	kfree(exename.name);
+	if (!retval) {
+		ee->r.domain = domain;
+		retval = tomoyo_environ(ee);
+	}
 	kfree(ee->tmp);
 	kfree(ee->dump.data);
 	kfree(ee);
@@ -732,7 +865,8 @@ bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos,
 		      struct tomoyo_page_dump *dump)
 {
 	struct page *page;
-	/* dump->data is released by tomoyo_finish_execve(). */
+
+	/* dump->data is released by tomoyo_find_next_domain(). */
 	if (!dump->data) {
 		dump->data = kzalloc(PAGE_SIZE, GFP_NOFS);
 		if (!dump->data)
@@ -753,6 +887,7 @@ bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos,
 		 * So do I.
 		 */
 		char *kaddr = kmap_atomic(page, KM_USER0);
+
 		dump->page = page;
 		memcpy(dump->data + offset, kaddr + offset,
 		       PAGE_SIZE - offset);
diff --git a/security/tomoyo/environ.c b/security/tomoyo/environ.c
new file mode 100644
index 000000000000..ad4c6e18a437
--- /dev/null
+++ b/security/tomoyo/environ.c
@@ -0,0 +1,122 @@
+/*
+ * security/tomoyo/environ.c
+ *
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
+ */
+
+#include "common.h"
+
+/**
+ * tomoyo_check_env_acl - Check permission for environment variable's name.
+ *
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if granted, false otherwise.
+ */
+static bool tomoyo_check_env_acl(struct tomoyo_request_info *r,
+				 const struct tomoyo_acl_info *ptr)
+{
+	const struct tomoyo_env_acl *acl =
+		container_of(ptr, typeof(*acl), head);
+
+	return tomoyo_path_matches_pattern(r->param.environ.name, acl->env);
+}
+
+/**
+ * tomoyo_audit_env_log - Audit environment variable name log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_env_log(struct tomoyo_request_info *r)
+{
+	return tomoyo_supervisor(r, "misc env %s\n",
+				 r->param.environ.name->name);
+}
+
+/**
+ * tomoyo_env_perm - Check permission for environment variable's name.
+ *
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @env: The name of environment variable.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_env_perm(struct tomoyo_request_info *r, const char *env)
+{
+	struct tomoyo_path_info environ;
+	int error;
+
+	if (!env || !*env)
+		return 0;
+	environ.name = env;
+	tomoyo_fill_path_info(&environ);
+	r->param_type = TOMOYO_TYPE_ENV_ACL;
+	r->param.environ.name = &environ;
+	do {
+		tomoyo_check_acl(r, tomoyo_check_env_acl);
+		error = tomoyo_audit_env_log(r);
+	} while (error == TOMOYO_RETRY_REQUEST);
+	return error;
+}
+
+/**
+ * tomoyo_same_env_acl - Check for duplicated "struct tomoyo_env_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool tomoyo_same_env_acl(const struct tomoyo_acl_info *a,
+				const struct tomoyo_acl_info *b)
+{
+	const struct tomoyo_env_acl *p1 = container_of(a, typeof(*p1), head);
+	const struct tomoyo_env_acl *p2 = container_of(b, typeof(*p2), head);
+
+	return p1->env == p2->env;
+}
+
+/**
+ * tomoyo_write_env - Write "struct tomoyo_env_acl" list.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_write_env(struct tomoyo_acl_param *param)
+{
+	struct tomoyo_env_acl e = { .head.type = TOMOYO_TYPE_ENV_ACL };
+	int error = -ENOMEM;
+	const char *data = tomoyo_read_token(param);
+
+	if (!tomoyo_correct_word(data) || strchr(data, '='))
+		return -EINVAL;
+	e.env = tomoyo_get_name(data);
+	if (!e.env)
+		return error;
+	error = tomoyo_update_domain(&e.head, sizeof(e), param,
+				  tomoyo_same_env_acl, NULL);
+	tomoyo_put_name(e.env);
+	return error;
+}
+
+/**
+ * tomoyo_write_misc - Update environment variable list.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_misc(struct tomoyo_acl_param *param)
+{
+	if (tomoyo_str_starts(&param->data, "env "))
+		return tomoyo_write_env(param);
+	return -EINVAL;
+}
diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c
index 743c35f5084a..400390790745 100644
--- a/security/tomoyo/file.c
+++ b/security/tomoyo/file.c
@@ -555,8 +555,8 @@ static int tomoyo_update_path2_acl(const u8 perm,
  *
  * Caller holds tomoyo_read_lock().
  */
-int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
-			   const struct tomoyo_path_info *filename)
+static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
+				  const struct tomoyo_path_info *filename)
 {
 	int error;
 
@@ -570,16 +570,42 @@ int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
 	do {
 		tomoyo_check_acl(r, tomoyo_check_path_acl);
 		error = tomoyo_audit_path_log(r);
-		/*
-		 * Do not retry for execute request, for alias may have
-		 * changed.
-		 */
-	} while (error == TOMOYO_RETRY_REQUEST &&
-		 operation != TOMOYO_TYPE_EXECUTE);
+	} while (error == TOMOYO_RETRY_REQUEST);
 	return error;
 }
 
 /**
+ * tomoyo_execute_permission - Check permission for execute operation.
+ *
+ * @r:         Pointer to "struct tomoyo_request_info".
+ * @filename:  Filename to check.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_execute_permission(struct tomoyo_request_info *r,
+			      const struct tomoyo_path_info *filename)
+{
+	/*
+	 * Unlike other permission checks, this check is done regardless of
+	 * profile mode settings in order to check for domain transition
+	 * preference.
+	 */
+	r->type = TOMOYO_MAC_FILE_EXECUTE;
+	r->mode = tomoyo_get_mode(r->domain->ns, r->profile, r->type);
+	r->param_type = TOMOYO_TYPE_PATH_ACL;
+	r->param.path.filename = filename;
+	r->param.path.operation = TOMOYO_TYPE_EXECUTE;
+	tomoyo_check_acl(r, tomoyo_check_path_acl);
+	r->ee->transition = r->matched_acl && r->matched_acl->cond ?
+		r->matched_acl->cond->transit : NULL;
+	if (r->mode != TOMOYO_CONFIG_DISABLED)
+		return tomoyo_audit_path_log(r);
+	return 0;
+}
+
+/**
  * tomoyo_same_path_number_acl - Check for duplicated "struct tomoyo_path_number_acl" entry.
  *
  * @a: Pointer to "struct tomoyo_acl_info".
diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c
index ae135fbbbe95..986a6a756868 100644
--- a/security/tomoyo/gc.c
+++ b/security/tomoyo/gc.c
@@ -8,36 +8,26 @@
 #include <linux/kthread.h>
 #include <linux/slab.h>
 
+/**
+ * tomoyo_memory_free - Free memory for elements.
+ *
+ * @ptr:  Pointer to allocated memory.
+ *
+ * Returns nothing.
+ *
+ * Caller holds tomoyo_policy_lock mutex.
+ */
+static inline void tomoyo_memory_free(void *ptr)
+{
+	tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= ksize(ptr);
+	kfree(ptr);
+}
+
 /* The list for "struct tomoyo_io_buffer". */
 static LIST_HEAD(tomoyo_io_buffer_list);
 /* Lock for protecting tomoyo_io_buffer_list. */
 static DEFINE_SPINLOCK(tomoyo_io_buffer_list_lock);
 
-/* Size of an element. */
-static const u8 tomoyo_element_size[TOMOYO_MAX_POLICY] = {
-	[TOMOYO_ID_GROUP] = sizeof(struct tomoyo_group),
-	[TOMOYO_ID_PATH_GROUP] = sizeof(struct tomoyo_path_group),
-	[TOMOYO_ID_NUMBER_GROUP] = sizeof(struct tomoyo_number_group),
-	[TOMOYO_ID_AGGREGATOR] = sizeof(struct tomoyo_aggregator),
-	[TOMOYO_ID_TRANSITION_CONTROL] =
-	sizeof(struct tomoyo_transition_control),
-	[TOMOYO_ID_MANAGER] = sizeof(struct tomoyo_manager),
-	/* [TOMOYO_ID_CONDITION] = "struct tomoyo_condition"->size, */
-	/* [TOMOYO_ID_NAME] = "struct tomoyo_name"->size, */
-	/* [TOMOYO_ID_ACL] =
-	   tomoyo_acl_size["struct tomoyo_acl_info"->type], */
-	[TOMOYO_ID_DOMAIN] = sizeof(struct tomoyo_domain_info),
-};
-
-/* Size of a domain ACL element. */
-static const u8 tomoyo_acl_size[] = {
-	[TOMOYO_TYPE_PATH_ACL] = sizeof(struct tomoyo_path_acl),
-	[TOMOYO_TYPE_PATH2_ACL] = sizeof(struct tomoyo_path2_acl),
-	[TOMOYO_TYPE_PATH_NUMBER_ACL] = sizeof(struct tomoyo_path_number_acl),
-	[TOMOYO_TYPE_MKDEV_ACL] = sizeof(struct tomoyo_mkdev_acl),
-	[TOMOYO_TYPE_MOUNT_ACL] = sizeof(struct tomoyo_mount_acl),
-};
-
 /**
  * tomoyo_struct_used_by_io_buffer - Check whether the list element is used by /sys/kernel/security/tomoyo/ users or not.
  *
@@ -55,15 +45,11 @@ static bool tomoyo_struct_used_by_io_buffer(const struct list_head *element)
 	list_for_each_entry(head, &tomoyo_io_buffer_list, list) {
 		head->users++;
 		spin_unlock(&tomoyo_io_buffer_list_lock);
-		if (mutex_lock_interruptible(&head->io_sem)) {
-			in_use = true;
-			goto out;
-		}
+		mutex_lock(&head->io_sem);
 		if (head->r.domain == element || head->r.group == element ||
 		    head->r.acl == element || &head->w.domain->list == element)
 			in_use = true;
 		mutex_unlock(&head->io_sem);
-out:
 		spin_lock(&tomoyo_io_buffer_list_lock);
 		head->users--;
 		if (in_use)
@@ -77,15 +63,14 @@ out:
  * tomoyo_name_used_by_io_buffer - Check whether the string is used by /sys/kernel/security/tomoyo/ users or not.
  *
  * @string: String to check.
- * @size:   Memory allocated for @string .
  *
  * Returns true if @string is used by /sys/kernel/security/tomoyo/ users,
  * false otherwise.
  */
-static bool tomoyo_name_used_by_io_buffer(const char *string,
-					  const size_t size)
+static bool tomoyo_name_used_by_io_buffer(const char *string)
 {
 	struct tomoyo_io_buffer *head;
+	const size_t size = strlen(string) + 1;
 	bool in_use = false;
 
 	spin_lock(&tomoyo_io_buffer_list_lock);
@@ -93,10 +78,7 @@ static bool tomoyo_name_used_by_io_buffer(const char *string,
 		int i;
 		head->users++;
 		spin_unlock(&tomoyo_io_buffer_list_lock);
-		if (mutex_lock_interruptible(&head->io_sem)) {
-			in_use = true;
-			goto out;
-		}
+		mutex_lock(&head->io_sem);
 		for (i = 0; i < TOMOYO_MAX_IO_READ_QUEUE; i++) {
 			const char *w = head->r.w[i];
 			if (w < string || w > string + size)
@@ -105,7 +87,6 @@ static bool tomoyo_name_used_by_io_buffer(const char *string,
 			break;
 		}
 		mutex_unlock(&head->io_sem);
-out:
 		spin_lock(&tomoyo_io_buffer_list_lock);
 		head->users--;
 		if (in_use)
@@ -115,84 +96,6 @@ out:
 	return in_use;
 }
 
-/* Structure for garbage collection. */
-struct tomoyo_gc {
-	struct list_head list;
-	enum tomoyo_policy_id type;
-	size_t size;
-	struct list_head *element;
-};
-/* List of entries to be deleted. */
-static LIST_HEAD(tomoyo_gc_list);
-/* Length of tomoyo_gc_list. */
-static int tomoyo_gc_list_len;
-
-/**
- * tomoyo_add_to_gc - Add an entry to to be deleted list.
- *
- * @type:    One of values in "enum tomoyo_policy_id".
- * @element: Pointer to "struct list_head".
- *
- * Returns true on success, false otherwise.
- *
- * Caller holds tomoyo_policy_lock mutex.
- *
- * Adding an entry needs kmalloc(). Thus, if we try to add thousands of
- * entries at once, it will take too long time. Thus, do not add more than 128
- * entries per a scan. But to be able to handle worst case where all entries
- * are in-use, we accept one more entry per a scan.
- *
- * If we use singly linked list using "struct list_head"->prev (which is
- * LIST_POISON2), we can avoid kmalloc().
- */
-static bool tomoyo_add_to_gc(const int type, struct list_head *element)
-{
-	struct tomoyo_gc *entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
-	if (!entry)
-		return false;
-	entry->type = type;
-	if (type == TOMOYO_ID_ACL)
-		entry->size = tomoyo_acl_size[
-			      container_of(element,
-					   typeof(struct tomoyo_acl_info),
-					   list)->type];
-	else if (type == TOMOYO_ID_NAME)
-		entry->size = strlen(container_of(element,
-						  typeof(struct tomoyo_name),
-						  head.list)->entry.name) + 1;
-	else if (type == TOMOYO_ID_CONDITION)
-		entry->size =
-			container_of(element, typeof(struct tomoyo_condition),
-				     head.list)->size;
-	else
-		entry->size = tomoyo_element_size[type];
-	entry->element = element;
-	list_add(&entry->list, &tomoyo_gc_list);
-	list_del_rcu(element);
-	return tomoyo_gc_list_len++ < 128;
-}
-
-/**
- * tomoyo_element_linked_by_gc - Validate next element of an entry.
- *
- * @element: Pointer to an element.
- * @size:    Size of @element in byte.
- *
- * Returns true if @element is linked by other elements in the garbage
- * collector's queue, false otherwise.
- */
-static bool tomoyo_element_linked_by_gc(const u8 *element, const size_t size)
-{
-	struct tomoyo_gc *p;
-	list_for_each_entry(p, &tomoyo_gc_list, list) {
-		const u8 *ptr = (const u8 *) p->element->next;
-		if (ptr < element || element + size < ptr)
-			continue;
-		return true;
-	}
-	return false;
-}
-
 /**
  * tomoyo_del_transition_control - Delete members in "struct tomoyo_transition_control".
  *
@@ -200,7 +103,7 @@ static bool tomoyo_element_linked_by_gc(const u8 *element, const size_t size)
  *
  * Returns nothing.
  */
-static void tomoyo_del_transition_control(struct list_head *element)
+static inline void tomoyo_del_transition_control(struct list_head *element)
 {
 	struct tomoyo_transition_control *ptr =
 		container_of(element, typeof(*ptr), head.list);
@@ -215,7 +118,7 @@ static void tomoyo_del_transition_control(struct list_head *element)
  *
  * Returns nothing.
  */
-static void tomoyo_del_aggregator(struct list_head *element)
+static inline void tomoyo_del_aggregator(struct list_head *element)
 {
 	struct tomoyo_aggregator *ptr =
 		container_of(element, typeof(*ptr), head.list);
@@ -230,7 +133,7 @@ static void tomoyo_del_aggregator(struct list_head *element)
  *
  * Returns nothing.
  */
-static void tomoyo_del_manager(struct list_head *element)
+static inline void tomoyo_del_manager(struct list_head *element)
 {
 	struct tomoyo_manager *ptr =
 		container_of(element, typeof(*ptr), head.list);
@@ -293,6 +196,38 @@ static void tomoyo_del_acl(struct list_head *element)
 			tomoyo_put_number_union(&entry->flags);
 		}
 		break;
+	case TOMOYO_TYPE_ENV_ACL:
+		{
+			struct tomoyo_env_acl *entry =
+				container_of(acl, typeof(*entry), head);
+
+			tomoyo_put_name(entry->env);
+		}
+		break;
+	case TOMOYO_TYPE_INET_ACL:
+		{
+			struct tomoyo_inet_acl *entry =
+				container_of(acl, typeof(*entry), head);
+
+			tomoyo_put_group(entry->address.group);
+			tomoyo_put_number_union(&entry->port);
+		}
+		break;
+	case TOMOYO_TYPE_UNIX_ACL:
+		{
+			struct tomoyo_unix_acl *entry =
+				container_of(acl, typeof(*entry), head);
+
+			tomoyo_put_name_union(&entry->name);
+		}
+		break;
+	case TOMOYO_TYPE_MANUAL_TASK_ACL:
+		{
+			struct tomoyo_task_acl *entry =
+				container_of(acl, typeof(*entry), head);
+			tomoyo_put_name(entry->domainname);
+		}
+		break;
 	}
 }
 
@@ -301,44 +236,26 @@ static void tomoyo_del_acl(struct list_head *element)
  *
  * @element: Pointer to "struct list_head".
  *
- * Returns true if deleted, false otherwise.
+ * Returns nothing.
+ *
+ * Caller holds tomoyo_policy_lock mutex.
  */
-static bool tomoyo_del_domain(struct list_head *element)
+static inline void tomoyo_del_domain(struct list_head *element)
 {
 	struct tomoyo_domain_info *domain =
 		container_of(element, typeof(*domain), list);
 	struct tomoyo_acl_info *acl;
 	struct tomoyo_acl_info *tmp;
 	/*
-	 * Since we don't protect whole execve() operation using SRCU,
-	 * we need to recheck domain->users at this point.
-	 *
-	 * (1) Reader starts SRCU section upon execve().
-	 * (2) Reader traverses tomoyo_domain_list and finds this domain.
-	 * (3) Writer marks this domain as deleted.
-	 * (4) Garbage collector removes this domain from tomoyo_domain_list
-	 *     because this domain is marked as deleted and used by nobody.
-	 * (5) Reader saves reference to this domain into
-	 *     "struct linux_binprm"->cred->security .
-	 * (6) Reader finishes SRCU section, although execve() operation has
-	 *     not finished yet.
-	 * (7) Garbage collector waits for SRCU synchronization.
-	 * (8) Garbage collector kfree() this domain because this domain is
-	 *     used by nobody.
-	 * (9) Reader finishes execve() operation and restores this domain from
-	 *     "struct linux_binprm"->cred->security.
-	 *
-	 * By updating domain->users at (5), we can solve this race problem
-	 * by rechecking domain->users at (8).
+	 * Since this domain is referenced from neither
+	 * "struct tomoyo_io_buffer" nor "struct cred"->security, we can delete
+	 * elements without checking for is_deleted flag.
 	 */
-	if (atomic_read(&domain->users))
-		return false;
 	list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) {
 		tomoyo_del_acl(&acl->list);
 		tomoyo_memory_free(acl);
 	}
 	tomoyo_put_name(domain->domainname);
-	return true;
 }
 
 /**
@@ -387,10 +304,9 @@ void tomoyo_del_condition(struct list_head *element)
  *
  * Returns nothing.
  */
-static void tomoyo_del_name(struct list_head *element)
+static inline void tomoyo_del_name(struct list_head *element)
 {
-	const struct tomoyo_name *ptr =
-		container_of(element, typeof(*ptr), head.list);
+	/* Nothing to do. */
 }
 
 /**
@@ -400,7 +316,7 @@ static void tomoyo_del_name(struct list_head *element)
  *
  * Returns nothing.
  */
-static void tomoyo_del_path_group(struct list_head *element)
+static inline void tomoyo_del_path_group(struct list_head *element)
 {
 	struct tomoyo_path_group *member =
 		container_of(element, typeof(*member), head.list);
@@ -414,7 +330,7 @@ static void tomoyo_del_path_group(struct list_head *element)
  *
  * Returns nothing.
  */
-static void tomoyo_del_group(struct list_head *element)
+static inline void tomoyo_del_group(struct list_head *element)
 {
 	struct tomoyo_group *group =
 		container_of(element, typeof(*group), head.list);
@@ -422,16 +338,128 @@ static void tomoyo_del_group(struct list_head *element)
 }
 
 /**
+ * tomoyo_del_address_group - Delete members in "struct tomoyo_address_group".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
+static inline void tomoyo_del_address_group(struct list_head *element)
+{
+	/* Nothing to do. */
+}
+
+/**
  * tomoyo_del_number_group - Delete members in "struct tomoyo_number_group".
  *
  * @element: Pointer to "struct list_head".
  *
  * Returns nothing.
  */
-static void tomoyo_del_number_group(struct list_head *element)
+static inline void tomoyo_del_number_group(struct list_head *element)
 {
-	struct tomoyo_number_group *member =
-		container_of(element, typeof(*member), head.list);
+	/* Nothing to do. */
+}
+
+/**
+ * tomoyo_try_to_gc - Try to kfree() an entry.
+ *
+ * @type:    One of values in "enum tomoyo_policy_id".
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ *
+ * Caller holds tomoyo_policy_lock mutex.
+ */
+static void tomoyo_try_to_gc(const enum tomoyo_policy_id type,
+			     struct list_head *element)
+{
+	/*
+	 * __list_del_entry() guarantees that the list element became no longer
+	 * reachable from the list which the element was originally on (e.g.
+	 * tomoyo_domain_list). Also, synchronize_srcu() guarantees that the
+	 * list element became no longer referenced by syscall users.
+	 */
+	__list_del_entry(element);
+	mutex_unlock(&tomoyo_policy_lock);
+	synchronize_srcu(&tomoyo_ss);
+	/*
+	 * However, there are two users which may still be using the list
+	 * element. We need to defer until both users forget this element.
+	 *
+	 * Don't kfree() until "struct tomoyo_io_buffer"->r.{domain,group,acl}
+	 * and "struct tomoyo_io_buffer"->w.domain forget this element.
+	 */
+	if (tomoyo_struct_used_by_io_buffer(element))
+		goto reinject;
+	switch (type) {
+	case TOMOYO_ID_TRANSITION_CONTROL:
+		tomoyo_del_transition_control(element);
+		break;
+	case TOMOYO_ID_MANAGER:
+		tomoyo_del_manager(element);
+		break;
+	case TOMOYO_ID_AGGREGATOR:
+		tomoyo_del_aggregator(element);
+		break;
+	case TOMOYO_ID_GROUP:
+		tomoyo_del_group(element);
+		break;
+	case TOMOYO_ID_PATH_GROUP:
+		tomoyo_del_path_group(element);
+		break;
+	case TOMOYO_ID_ADDRESS_GROUP:
+		tomoyo_del_address_group(element);
+		break;
+	case TOMOYO_ID_NUMBER_GROUP:
+		tomoyo_del_number_group(element);
+		break;
+	case TOMOYO_ID_CONDITION:
+		tomoyo_del_condition(element);
+		break;
+	case TOMOYO_ID_NAME:
+		/*
+		 * Don't kfree() until all "struct tomoyo_io_buffer"->r.w[]
+		 * forget this element.
+		 */
+		if (tomoyo_name_used_by_io_buffer
+		    (container_of(element, typeof(struct tomoyo_name),
+				  head.list)->entry.name))
+			goto reinject;
+		tomoyo_del_name(element);
+		break;
+	case TOMOYO_ID_ACL:
+		tomoyo_del_acl(element);
+		break;
+	case TOMOYO_ID_DOMAIN:
+		/*
+		 * Don't kfree() until all "struct cred"->security forget this
+		 * element.
+		 */
+		if (atomic_read(&container_of
+				(element, typeof(struct tomoyo_domain_info),
+				 list)->users))
+			goto reinject;
+		break;
+	case TOMOYO_MAX_POLICY:
+		break;
+	}
+	mutex_lock(&tomoyo_policy_lock);
+	if (type == TOMOYO_ID_DOMAIN)
+		tomoyo_del_domain(element);
+	tomoyo_memory_free(element);
+	return;
+reinject:
+	/*
+	 * We can safely reinject this element here bacause
+	 * (1) Appending list elements and removing list elements are protected
+	 *     by tomoyo_policy_lock mutex.
+	 * (2) Only this function removes list elements and this function is
+	 *     exclusively executed by tomoyo_gc_mutex mutex.
+	 * are true.
+	 */
+	mutex_lock(&tomoyo_policy_lock);
+	list_add_rcu(element, element->prev);
 }
 
 /**
@@ -440,19 +468,19 @@ static void tomoyo_del_number_group(struct list_head *element)
  * @id:          One of values in "enum tomoyo_policy_id".
  * @member_list: Pointer to "struct list_head".
  *
- * Returns true if some elements are deleted, false otherwise.
+ * Returns nothing.
  */
-static bool tomoyo_collect_member(const enum tomoyo_policy_id id,
+static void tomoyo_collect_member(const enum tomoyo_policy_id id,
 				  struct list_head *member_list)
 {
 	struct tomoyo_acl_head *member;
-	list_for_each_entry(member, member_list, list) {
+	struct tomoyo_acl_head *tmp;
+	list_for_each_entry_safe(member, tmp, member_list, list) {
 		if (!member->is_deleted)
 			continue;
-		if (!tomoyo_add_to_gc(id, &member->list))
-			return false;
+		member->is_deleted = TOMOYO_GC_IN_PROGRESS;
+		tomoyo_try_to_gc(id, &member->list);
 	}
-	return true;
 }
 
 /**
@@ -460,22 +488,22 @@ static bool tomoyo_collect_member(const enum tomoyo_policy_id id,
  *
  * @list: Pointer to "struct list_head".
  *
- * Returns true if some elements are deleted, false otherwise.
+ * Returns nothing.
  */
-static bool tomoyo_collect_acl(struct list_head *list)
+static void tomoyo_collect_acl(struct list_head *list)
 {
 	struct tomoyo_acl_info *acl;
-	list_for_each_entry(acl, list, list) {
+	struct tomoyo_acl_info *tmp;
+	list_for_each_entry_safe(acl, tmp, list, list) {
 		if (!acl->is_deleted)
 			continue;
-		if (!tomoyo_add_to_gc(TOMOYO_ID_ACL, &acl->list))
-			return false;
+		acl->is_deleted = TOMOYO_GC_IN_PROGRESS;
+		tomoyo_try_to_gc(TOMOYO_ID_ACL, &acl->list);
 	}
-	return true;
 }
 
 /**
- * tomoyo_collect_entry - Scan lists for deleted elements.
+ * tomoyo_collect_entry - Try to kfree() deleted elements.
  *
  * Returns nothing.
  */
@@ -484,174 +512,82 @@ static void tomoyo_collect_entry(void)
 	int i;
 	enum tomoyo_policy_id id;
 	struct tomoyo_policy_namespace *ns;
-	int idx;
-	if (mutex_lock_interruptible(&tomoyo_policy_lock))
-		return;
-	idx = tomoyo_read_lock();
+	mutex_lock(&tomoyo_policy_lock);
 	{
 		struct tomoyo_domain_info *domain;
-		list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
-			if (!tomoyo_collect_acl(&domain->acl_info_list))
-				goto unlock;
+		struct tomoyo_domain_info *tmp;
+		list_for_each_entry_safe(domain, tmp, &tomoyo_domain_list,
+					 list) {
+			tomoyo_collect_acl(&domain->acl_info_list);
 			if (!domain->is_deleted || atomic_read(&domain->users))
 				continue;
-			/*
-			 * Nobody is referring this domain. But somebody may
-			 * refer this domain after successful execve().
-			 * We recheck domain->users after SRCU synchronization.
-			 */
-			if (!tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, &domain->list))
-				goto unlock;
+			tomoyo_try_to_gc(TOMOYO_ID_DOMAIN, &domain->list);
 		}
 	}
-	list_for_each_entry_rcu(ns, &tomoyo_namespace_list, namespace_list) {
+	list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) {
 		for (id = 0; id < TOMOYO_MAX_POLICY; id++)
-			if (!tomoyo_collect_member(id, &ns->policy_list[id]))
-				goto unlock;
+			tomoyo_collect_member(id, &ns->policy_list[id]);
 		for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++)
-			if (!tomoyo_collect_acl(&ns->acl_group[i]))
-				goto unlock;
+			tomoyo_collect_acl(&ns->acl_group[i]);
+	}
+	{
+		struct tomoyo_shared_acl_head *ptr;
+		struct tomoyo_shared_acl_head *tmp;
+		list_for_each_entry_safe(ptr, tmp, &tomoyo_condition_list,
+					 list) {
+			if (atomic_read(&ptr->users) > 0)
+				continue;
+			atomic_set(&ptr->users, TOMOYO_GC_IN_PROGRESS);
+			tomoyo_try_to_gc(TOMOYO_ID_CONDITION, &ptr->list);
+		}
+	}
+	list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) {
 		for (i = 0; i < TOMOYO_MAX_GROUP; i++) {
 			struct list_head *list = &ns->group_list[i];
 			struct tomoyo_group *group;
+			struct tomoyo_group *tmp;
 			switch (i) {
 			case 0:
 				id = TOMOYO_ID_PATH_GROUP;
 				break;
-			default:
+			case 1:
 				id = TOMOYO_ID_NUMBER_GROUP;
 				break;
+			default:
+				id = TOMOYO_ID_ADDRESS_GROUP;
+				break;
 			}
-			list_for_each_entry(group, list, head.list) {
-				if (!tomoyo_collect_member
-				    (id, &group->member_list))
-					goto unlock;
+			list_for_each_entry_safe(group, tmp, list, head.list) {
+				tomoyo_collect_member(id, &group->member_list);
 				if (!list_empty(&group->member_list) ||
-				    atomic_read(&group->head.users))
+				    atomic_read(&group->head.users) > 0)
 					continue;
-				if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP,
-						      &group->head.list))
-					goto unlock;
+				atomic_set(&group->head.users,
+					   TOMOYO_GC_IN_PROGRESS);
+				tomoyo_try_to_gc(TOMOYO_ID_GROUP,
+						 &group->head.list);
 			}
 		}
 	}
-	id = TOMOYO_ID_CONDITION;
-	for (i = 0; i < TOMOYO_MAX_HASH + 1; i++) {
-		struct list_head *list = !i ?
-			&tomoyo_condition_list : &tomoyo_name_list[i - 1];
+	for (i = 0; i < TOMOYO_MAX_HASH; i++) {
+		struct list_head *list = &tomoyo_name_list[i];
 		struct tomoyo_shared_acl_head *ptr;
-		list_for_each_entry(ptr, list, list) {
-			if (atomic_read(&ptr->users))
+		struct tomoyo_shared_acl_head *tmp;
+		list_for_each_entry_safe(ptr, tmp, list, list) {
+			if (atomic_read(&ptr->users) > 0)
 				continue;
-			if (!tomoyo_add_to_gc(id, &ptr->list))
-				goto unlock;
+			atomic_set(&ptr->users, TOMOYO_GC_IN_PROGRESS);
+			tomoyo_try_to_gc(TOMOYO_ID_NAME, &ptr->list);
 		}
-		id = TOMOYO_ID_NAME;
 	}
-unlock:
-	tomoyo_read_unlock(idx);
 	mutex_unlock(&tomoyo_policy_lock);
 }
 
 /**
- * tomoyo_kfree_entry - Delete entries in tomoyo_gc_list.
- *
- * Returns true if some entries were kfree()d, false otherwise.
- */
-static bool tomoyo_kfree_entry(void)
-{
-	struct tomoyo_gc *p;
-	struct tomoyo_gc *tmp;
-	bool result = false;
-
-	list_for_each_entry_safe(p, tmp, &tomoyo_gc_list, list) {
-		struct list_head *element = p->element;
-
-		/*
-		 * list_del_rcu() in tomoyo_add_to_gc() guarantees that the
-		 * list element became no longer reachable from the list which
-		 * the element was originally on (e.g. tomoyo_domain_list).
-		 * Also, synchronize_srcu() in tomoyo_gc_thread() guarantees
-		 * that the list element became no longer referenced by syscall
-		 * users.
-		 *
-		 * However, there are three users which may still be using the
-		 * list element. We need to defer until all of these users
-		 * forget the list element.
-		 *
-		 * Firstly, defer until "struct tomoyo_io_buffer"->r.{domain,
-		 * group,acl} and "struct tomoyo_io_buffer"->w.domain forget
-		 * the list element.
-		 */
-		if (tomoyo_struct_used_by_io_buffer(element))
-			continue;
-		/*
-		 * Secondly, defer until all other elements in the
-		 * tomoyo_gc_list list forget the list element.
-		 */
-		if (tomoyo_element_linked_by_gc((const u8 *) element, p->size))
-			continue;
-		switch (p->type) {
-		case TOMOYO_ID_TRANSITION_CONTROL:
-			tomoyo_del_transition_control(element);
-			break;
-		case TOMOYO_ID_AGGREGATOR:
-			tomoyo_del_aggregator(element);
-			break;
-		case TOMOYO_ID_MANAGER:
-			tomoyo_del_manager(element);
-			break;
-		case TOMOYO_ID_CONDITION:
-			tomoyo_del_condition(element);
-			break;
-		case TOMOYO_ID_NAME:
-			/*
-			 * Thirdly, defer until all "struct tomoyo_io_buffer"
-			 * ->r.w[] forget the list element.
-			 */
-			if (tomoyo_name_used_by_io_buffer(
-			    container_of(element, typeof(struct tomoyo_name),
-					 head.list)->entry.name, p->size))
-				continue;
-			tomoyo_del_name(element);
-			break;
-		case TOMOYO_ID_ACL:
-			tomoyo_del_acl(element);
-			break;
-		case TOMOYO_ID_DOMAIN:
-			if (!tomoyo_del_domain(element))
-				continue;
-			break;
-		case TOMOYO_ID_PATH_GROUP:
-			tomoyo_del_path_group(element);
-			break;
-		case TOMOYO_ID_GROUP:
-			tomoyo_del_group(element);
-			break;
-		case TOMOYO_ID_NUMBER_GROUP:
-			tomoyo_del_number_group(element);
-			break;
-		case TOMOYO_MAX_POLICY:
-			break;
-		}
-		tomoyo_memory_free(element);
-		list_del(&p->list);
-		kfree(p);
-		tomoyo_gc_list_len--;
-		result = true;
-	}
-	return result;
-}
-
-/**
  * tomoyo_gc_thread - Garbage collector thread function.
  *
  * @unused: Unused.
  *
- * In case OOM-killer choose this thread for termination, we create this thread
- * as a short live thread whenever /sys/kernel/security/tomoyo/ interface was
- * close()d.
- *
  * Returns 0.
  */
 static int tomoyo_gc_thread(void *unused)
@@ -660,13 +596,7 @@ static int tomoyo_gc_thread(void *unused)
 	static DEFINE_MUTEX(tomoyo_gc_mutex);
 	if (!mutex_trylock(&tomoyo_gc_mutex))
 		goto out;
-	daemonize("GC for TOMOYO");
-	do {
-		tomoyo_collect_entry();
-		if (list_empty(&tomoyo_gc_list))
-			break;
-		synchronize_srcu(&tomoyo_ss);
-	} while (tomoyo_kfree_entry());
+	tomoyo_collect_entry();
 	{
 		struct tomoyo_io_buffer *head;
 		struct tomoyo_io_buffer *tmp;
diff --git a/security/tomoyo/group.c b/security/tomoyo/group.c
index 5fb0e1298400..50092534ec54 100644
--- a/security/tomoyo/group.c
+++ b/security/tomoyo/group.c
@@ -42,7 +42,26 @@ static bool tomoyo_same_number_group(const struct tomoyo_acl_head *a,
 }
 
 /**
- * tomoyo_write_group - Write "struct tomoyo_path_group"/"struct tomoyo_number_group" list.
+ * tomoyo_same_address_group - Check for duplicated "struct tomoyo_address_group" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_head".
+ * @b: Pointer to "struct tomoyo_acl_head".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool tomoyo_same_address_group(const struct tomoyo_acl_head *a,
+				      const struct tomoyo_acl_head *b)
+{
+	const struct tomoyo_address_group *p1 = container_of(a, typeof(*p1),
+							     head);
+	const struct tomoyo_address_group *p2 = container_of(b, typeof(*p2),
+							     head);
+
+	return tomoyo_same_ipaddr_union(&p1->address, &p2->address);
+}
+
+/**
+ * tomoyo_write_group - Write "struct tomoyo_path_group"/"struct tomoyo_number_group"/"struct tomoyo_address_group" list.
  *
  * @param: Pointer to "struct tomoyo_acl_param".
  * @type:  Type of this group.
@@ -77,6 +96,14 @@ int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type)
 		 * tomoyo_put_number_union() is not needed because
 		 * param->data[0] != '@'.
 		 */
+	} else {
+		struct tomoyo_address_group e = { };
+
+		if (param->data[0] == '@' ||
+		    !tomoyo_parse_ipaddr_union(param, &e.address))
+			goto out;
+		error = tomoyo_update_policy(&e.head, sizeof(e), param,
+					     tomoyo_same_address_group);
 	}
 out:
 	tomoyo_put_group(group);
@@ -137,3 +164,35 @@ bool tomoyo_number_matches_group(const unsigned long min,
 	}
 	return matched;
 }
+
+/**
+ * tomoyo_address_matches_group - Check whether the given address matches members of the given address group.
+ *
+ * @is_ipv6: True if @address is an IPv6 address.
+ * @address: An IPv4 or IPv6 address.
+ * @group:   Pointer to "struct tomoyo_address_group".
+ *
+ * Returns true if @address matches addresses in @group group, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_address_matches_group(const bool is_ipv6, const __be32 *address,
+				  const struct tomoyo_group *group)
+{
+	struct tomoyo_address_group *member;
+	bool matched = false;
+	const u8 size = is_ipv6 ? 16 : 4;
+
+	list_for_each_entry_rcu(member, &group->member_list, head.list) {
+		if (member->head.is_deleted)
+			continue;
+		if (member->address.is_ipv6 != is_ipv6)
+			continue;
+		if (memcmp(&member->address.ip[0], address, size) > 0 ||
+		    memcmp(address, &member->address.ip[1], size) > 0)
+			continue;
+		matched = true;
+		break;
+	}
+	return matched;
+}
diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c
index 7a56051146c2..0e995716cc25 100644
--- a/security/tomoyo/memory.c
+++ b/security/tomoyo/memory.c
@@ -27,8 +27,6 @@ void tomoyo_warn_oom(const char *function)
 		panic("MAC Initialization failed.\n");
 }
 
-/* Lock for protecting tomoyo_memory_used. */
-static DEFINE_SPINLOCK(tomoyo_policy_memory_lock);
 /* Memoy currently used by policy/audit log/query. */
 unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT];
 /* Memory quota for "policy"/"audit log"/"query". */
@@ -42,22 +40,19 @@ unsigned int tomoyo_memory_quota[TOMOYO_MAX_MEMORY_STAT];
  * Returns true on success, false otherwise.
  *
  * Returns true if @ptr is not NULL and quota not exceeded, false otherwise.
+ *
+ * Caller holds tomoyo_policy_lock mutex.
  */
 bool tomoyo_memory_ok(void *ptr)
 {
 	if (ptr) {
 		const size_t s = ksize(ptr);
-		bool result;
-		spin_lock(&tomoyo_policy_memory_lock);
 		tomoyo_memory_used[TOMOYO_MEMORY_POLICY] += s;
-		result = !tomoyo_memory_quota[TOMOYO_MEMORY_POLICY] ||
-			tomoyo_memory_used[TOMOYO_MEMORY_POLICY] <=
-			tomoyo_memory_quota[TOMOYO_MEMORY_POLICY];
-		if (!result)
-			tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s;
-		spin_unlock(&tomoyo_policy_memory_lock);
-		if (result)
+		if (!tomoyo_memory_quota[TOMOYO_MEMORY_POLICY] ||
+		    tomoyo_memory_used[TOMOYO_MEMORY_POLICY] <=
+		    tomoyo_memory_quota[TOMOYO_MEMORY_POLICY])
 			return true;
+		tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s;
 	}
 	tomoyo_warn_oom(__func__);
 	return false;
@@ -71,6 +66,8 @@ bool tomoyo_memory_ok(void *ptr)
  *
  * Returns pointer to allocated memory on success, NULL otherwise.
  * @data is zero-cleared on success.
+ *
+ * Caller holds tomoyo_policy_lock mutex.
  */
 void *tomoyo_commit_ok(void *data, const unsigned int size)
 {
@@ -85,20 +82,6 @@ void *tomoyo_commit_ok(void *data, const unsigned int size)
 }
 
 /**
- * tomoyo_memory_free - Free memory for elements.
- *
- * @ptr:  Pointer to allocated memory.
- */
-void tomoyo_memory_free(void *ptr)
-{
-	size_t s = ksize(ptr);
-	spin_lock(&tomoyo_policy_memory_lock);
-	tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s;
-	spin_unlock(&tomoyo_policy_memory_lock);
-	kfree(ptr);
-}
-
-/**
  * tomoyo_get_group - Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group".
  *
  * @param: Pointer to "struct tomoyo_acl_param".
@@ -123,7 +106,8 @@ struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param,
 		goto out;
 	list = &param->ns->group_list[idx];
 	list_for_each_entry(group, list, head.list) {
-		if (e.group_name != group->group_name)
+		if (e.group_name != group->group_name ||
+		    atomic_read(&group->head.users) == TOMOYO_GC_IN_PROGRESS)
 			continue;
 		atomic_inc(&group->head.users);
 		found = true;
@@ -175,7 +159,8 @@ const struct tomoyo_path_info *tomoyo_get_name(const char *name)
 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
 		return NULL;
 	list_for_each_entry(ptr, head, head.list) {
-		if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name))
+		if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name) ||
+		    atomic_read(&ptr->head.users) == TOMOYO_GC_IN_PROGRESS)
 			continue;
 		atomic_inc(&ptr->head.users);
 		goto out;
diff --git a/security/tomoyo/network.c b/security/tomoyo/network.c
new file mode 100644
index 000000000000..97527710a72a
--- /dev/null
+++ b/security/tomoyo/network.c
@@ -0,0 +1,771 @@
+/*
+ * security/tomoyo/network.c
+ *
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
+ */
+
+#include "common.h"
+#include <linux/slab.h>
+
+/* Structure for holding inet domain socket's address. */
+struct tomoyo_inet_addr_info {
+	__be16 port;           /* In network byte order. */
+	const __be32 *address; /* In network byte order. */
+	bool is_ipv6;
+};
+
+/* Structure for holding unix domain socket's address. */
+struct tomoyo_unix_addr_info {
+	u8 *addr; /* This may not be '\0' terminated string. */
+	unsigned int addr_len;
+};
+
+/* Structure for holding socket address. */
+struct tomoyo_addr_info {
+	u8 protocol;
+	u8 operation;
+	struct tomoyo_inet_addr_info inet;
+	struct tomoyo_unix_addr_info unix0;
+};
+
+/* String table for socket's protocols. */
+const char * const tomoyo_proto_keyword[TOMOYO_SOCK_MAX] = {
+	[SOCK_STREAM]    = "stream",
+	[SOCK_DGRAM]     = "dgram",
+	[SOCK_RAW]       = "raw",
+	[SOCK_SEQPACKET] = "seqpacket",
+	[0] = " ", /* Dummy for avoiding NULL pointer dereference. */
+	[4] = " ", /* Dummy for avoiding NULL pointer dereference. */
+};
+
+/**
+ * tomoyo_parse_ipaddr_union - Parse an IP address.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ * @ptr:   Pointer to "struct tomoyo_ipaddr_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_parse_ipaddr_union(struct tomoyo_acl_param *param,
+			       struct tomoyo_ipaddr_union *ptr)
+{
+	u8 * const min = ptr->ip[0].in6_u.u6_addr8;
+	u8 * const max = ptr->ip[1].in6_u.u6_addr8;
+	char *address = tomoyo_read_token(param);
+	const char *end;
+
+	if (!strchr(address, ':') &&
+	    in4_pton(address, -1, min, '-', &end) > 0) {
+		ptr->is_ipv6 = false;
+		if (!*end)
+			ptr->ip[1].s6_addr32[0] = ptr->ip[0].s6_addr32[0];
+		else if (*end++ != '-' ||
+			 in4_pton(end, -1, max, '\0', &end) <= 0 || *end)
+			return false;
+		return true;
+	}
+	if (in6_pton(address, -1, min, '-', &end) > 0) {
+		ptr->is_ipv6 = true;
+		if (!*end)
+			memmove(max, min, sizeof(u16) * 8);
+		else if (*end++ != '-' ||
+			 in6_pton(end, -1, max, '\0', &end) <= 0 || *end)
+			return false;
+		return true;
+	}
+	return false;
+}
+
+/**
+ * tomoyo_print_ipv4 - Print an IPv4 address.
+ *
+ * @buffer:     Buffer to write to.
+ * @buffer_len: Size of @buffer.
+ * @min_ip:     Pointer to __be32.
+ * @max_ip:     Pointer to __be32.
+ *
+ * Returns nothing.
+ */
+static void tomoyo_print_ipv4(char *buffer, const unsigned int buffer_len,
+			      const __be32 *min_ip, const __be32 *max_ip)
+{
+	snprintf(buffer, buffer_len, "%pI4%c%pI4", min_ip,
+		 *min_ip == *max_ip ? '\0' : '-', max_ip);
+}
+
+/**
+ * tomoyo_print_ipv6 - Print an IPv6 address.
+ *
+ * @buffer:     Buffer to write to.
+ * @buffer_len: Size of @buffer.
+ * @min_ip:     Pointer to "struct in6_addr".
+ * @max_ip:     Pointer to "struct in6_addr".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_print_ipv6(char *buffer, const unsigned int buffer_len,
+			      const struct in6_addr *min_ip,
+			      const struct in6_addr *max_ip)
+{
+	snprintf(buffer, buffer_len, "%pI6c%c%pI6c", min_ip,
+		 !memcmp(min_ip, max_ip, 16) ? '\0' : '-', max_ip);
+}
+
+/**
+ * tomoyo_print_ip - Print an IP address.
+ *
+ * @buf:  Buffer to write to.
+ * @size: Size of @buf.
+ * @ptr:  Pointer to "struct ipaddr_union".
+ *
+ * Returns nothing.
+ */
+void tomoyo_print_ip(char *buf, const unsigned int size,
+		     const struct tomoyo_ipaddr_union *ptr)
+{
+	if (ptr->is_ipv6)
+		tomoyo_print_ipv6(buf, size, &ptr->ip[0], &ptr->ip[1]);
+	else
+		tomoyo_print_ipv4(buf, size, &ptr->ip[0].s6_addr32[0],
+				  &ptr->ip[1].s6_addr32[0]);
+}
+
+/*
+ * Mapping table from "enum tomoyo_network_acl_index" to
+ * "enum tomoyo_mac_index" for inet domain socket.
+ */
+static const u8 tomoyo_inet2mac
+[TOMOYO_SOCK_MAX][TOMOYO_MAX_NETWORK_OPERATION] = {
+	[SOCK_STREAM] = {
+		[TOMOYO_NETWORK_BIND]    = TOMOYO_MAC_NETWORK_INET_STREAM_BIND,
+		[TOMOYO_NETWORK_LISTEN]  =
+		TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN,
+		[TOMOYO_NETWORK_CONNECT] =
+		TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT,
+	},
+	[SOCK_DGRAM] = {
+		[TOMOYO_NETWORK_BIND]    = TOMOYO_MAC_NETWORK_INET_DGRAM_BIND,
+		[TOMOYO_NETWORK_SEND]    = TOMOYO_MAC_NETWORK_INET_DGRAM_SEND,
+	},
+	[SOCK_RAW]    = {
+		[TOMOYO_NETWORK_BIND]    = TOMOYO_MAC_NETWORK_INET_RAW_BIND,
+		[TOMOYO_NETWORK_SEND]    = TOMOYO_MAC_NETWORK_INET_RAW_SEND,
+	},
+};
+
+/*
+ * Mapping table from "enum tomoyo_network_acl_index" to
+ * "enum tomoyo_mac_index" for unix domain socket.
+ */
+static const u8 tomoyo_unix2mac
+[TOMOYO_SOCK_MAX][TOMOYO_MAX_NETWORK_OPERATION] = {
+	[SOCK_STREAM] = {
+		[TOMOYO_NETWORK_BIND]    = TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND,
+		[TOMOYO_NETWORK_LISTEN]  =
+		TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN,
+		[TOMOYO_NETWORK_CONNECT] =
+		TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT,
+	},
+	[SOCK_DGRAM] = {
+		[TOMOYO_NETWORK_BIND]    = TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND,
+		[TOMOYO_NETWORK_SEND]    = TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND,
+	},
+	[SOCK_SEQPACKET] = {
+		[TOMOYO_NETWORK_BIND]    =
+		TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND,
+		[TOMOYO_NETWORK_LISTEN]  =
+		TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN,
+		[TOMOYO_NETWORK_CONNECT] =
+		TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT,
+	},
+};
+
+/**
+ * tomoyo_same_inet_acl - Check for duplicated "struct tomoyo_inet_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
+static bool tomoyo_same_inet_acl(const struct tomoyo_acl_info *a,
+				 const struct tomoyo_acl_info *b)
+{
+	const struct tomoyo_inet_acl *p1 = container_of(a, typeof(*p1), head);
+	const struct tomoyo_inet_acl *p2 = container_of(b, typeof(*p2), head);
+
+	return p1->protocol == p2->protocol &&
+		tomoyo_same_ipaddr_union(&p1->address, &p2->address) &&
+		tomoyo_same_number_union(&p1->port, &p2->port);
+}
+
+/**
+ * tomoyo_same_unix_acl - Check for duplicated "struct tomoyo_unix_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
+static bool tomoyo_same_unix_acl(const struct tomoyo_acl_info *a,
+				 const struct tomoyo_acl_info *b)
+{
+	const struct tomoyo_unix_acl *p1 = container_of(a, typeof(*p1), head);
+	const struct tomoyo_unix_acl *p2 = container_of(b, typeof(*p2), head);
+
+	return p1->protocol == p2->protocol &&
+		tomoyo_same_name_union(&p1->name, &p2->name);
+}
+
+/**
+ * tomoyo_merge_inet_acl - Merge duplicated "struct tomoyo_inet_acl" entry.
+ *
+ * @a:         Pointer to "struct tomoyo_acl_info".
+ * @b:         Pointer to "struct tomoyo_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
+static bool tomoyo_merge_inet_acl(struct tomoyo_acl_info *a,
+				  struct tomoyo_acl_info *b,
+				  const bool is_delete)
+{
+	u8 * const a_perm =
+		&container_of(a, struct tomoyo_inet_acl, head)->perm;
+	u8 perm = *a_perm;
+	const u8 b_perm = container_of(b, struct tomoyo_inet_acl, head)->perm;
+
+	if (is_delete)
+		perm &= ~b_perm;
+	else
+		perm |= b_perm;
+	*a_perm = perm;
+	return !perm;
+}
+
+/**
+ * tomoyo_merge_unix_acl - Merge duplicated "struct tomoyo_unix_acl" entry.
+ *
+ * @a:         Pointer to "struct tomoyo_acl_info".
+ * @b:         Pointer to "struct tomoyo_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
+static bool tomoyo_merge_unix_acl(struct tomoyo_acl_info *a,
+				  struct tomoyo_acl_info *b,
+				  const bool is_delete)
+{
+	u8 * const a_perm =
+		&container_of(a, struct tomoyo_unix_acl, head)->perm;
+	u8 perm = *a_perm;
+	const u8 b_perm = container_of(b, struct tomoyo_unix_acl, head)->perm;
+
+	if (is_delete)
+		perm &= ~b_perm;
+	else
+		perm |= b_perm;
+	*a_perm = perm;
+	return !perm;
+}
+
+/**
+ * tomoyo_write_inet_network - Write "struct tomoyo_inet_acl" list.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_write_inet_network(struct tomoyo_acl_param *param)
+{
+	struct tomoyo_inet_acl e = { .head.type = TOMOYO_TYPE_INET_ACL };
+	int error = -EINVAL;
+	u8 type;
+	const char *protocol = tomoyo_read_token(param);
+	const char *operation = tomoyo_read_token(param);
+
+	for (e.protocol = 0; e.protocol < TOMOYO_SOCK_MAX; e.protocol++)
+		if (!strcmp(protocol, tomoyo_proto_keyword[e.protocol]))
+			break;
+	for (type = 0; type < TOMOYO_MAX_NETWORK_OPERATION; type++)
+		if (tomoyo_permstr(operation, tomoyo_socket_keyword[type]))
+			e.perm |= 1 << type;
+	if (e.protocol == TOMOYO_SOCK_MAX || !e.perm)
+		return -EINVAL;
+	if (param->data[0] == '@') {
+		param->data++;
+		e.address.group =
+			tomoyo_get_group(param, TOMOYO_ADDRESS_GROUP);
+		if (!e.address.group)
+			return -ENOMEM;
+	} else {
+		if (!tomoyo_parse_ipaddr_union(param, &e.address))
+			goto out;
+	}
+	if (!tomoyo_parse_number_union(param, &e.port) ||
+	    e.port.values[1] > 65535)
+		goto out;
+	error = tomoyo_update_domain(&e.head, sizeof(e), param,
+				     tomoyo_same_inet_acl,
+				     tomoyo_merge_inet_acl);
+out:
+	tomoyo_put_group(e.address.group);
+	tomoyo_put_number_union(&e.port);
+	return error;
+}
+
+/**
+ * tomoyo_write_unix_network - Write "struct tomoyo_unix_acl" list.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_unix_network(struct tomoyo_acl_param *param)
+{
+	struct tomoyo_unix_acl e = { .head.type = TOMOYO_TYPE_UNIX_ACL };
+	int error;
+	u8 type;
+	const char *protocol = tomoyo_read_token(param);
+	const char *operation = tomoyo_read_token(param);
+
+	for (e.protocol = 0; e.protocol < TOMOYO_SOCK_MAX; e.protocol++)
+		if (!strcmp(protocol, tomoyo_proto_keyword[e.protocol]))
+			break;
+	for (type = 0; type < TOMOYO_MAX_NETWORK_OPERATION; type++)
+		if (tomoyo_permstr(operation, tomoyo_socket_keyword[type]))
+			e.perm |= 1 << type;
+	if (e.protocol == TOMOYO_SOCK_MAX || !e.perm)
+		return -EINVAL;
+	if (!tomoyo_parse_name_union(param, &e.name))
+		return -EINVAL;
+	error = tomoyo_update_domain(&e.head, sizeof(e), param,
+				     tomoyo_same_unix_acl,
+				     tomoyo_merge_unix_acl);
+	tomoyo_put_name_union(&e.name);
+	return error;
+}
+
+/**
+ * tomoyo_audit_net_log - Audit network log.
+ *
+ * @r:         Pointer to "struct tomoyo_request_info".
+ * @family:    Name of socket family ("inet" or "unix").
+ * @protocol:  Name of protocol in @family.
+ * @operation: Name of socket operation.
+ * @address:   Name of address.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_net_log(struct tomoyo_request_info *r,
+				const char *family, const u8 protocol,
+				const u8 operation, const char *address)
+{
+	return tomoyo_supervisor(r, "network %s %s %s %s\n", family,
+				 tomoyo_proto_keyword[protocol],
+				 tomoyo_socket_keyword[operation], address);
+}
+
+/**
+ * tomoyo_audit_inet_log - Audit INET network log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_inet_log(struct tomoyo_request_info *r)
+{
+	char buf[128];
+	int len;
+	const __be32 *address = r->param.inet_network.address;
+
+	if (r->param.inet_network.is_ipv6)
+		tomoyo_print_ipv6(buf, sizeof(buf), (const struct in6_addr *)
+				  address, (const struct in6_addr *) address);
+	else
+		tomoyo_print_ipv4(buf, sizeof(buf), address, address);
+	len = strlen(buf);
+	snprintf(buf + len, sizeof(buf) - len, " %u",
+		 r->param.inet_network.port);
+	return tomoyo_audit_net_log(r, "inet", r->param.inet_network.protocol,
+				    r->param.inet_network.operation, buf);
+}
+
+/**
+ * tomoyo_audit_unix_log - Audit UNIX network log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_unix_log(struct tomoyo_request_info *r)
+{
+	return tomoyo_audit_net_log(r, "unix", r->param.unix_network.protocol,
+				    r->param.unix_network.operation,
+				    r->param.unix_network.address->name);
+}
+
+/**
+ * tomoyo_check_inet_acl - Check permission for inet domain socket operation.
+ *
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if granted, false otherwise.
+ */
+static bool tomoyo_check_inet_acl(struct tomoyo_request_info *r,
+				  const struct tomoyo_acl_info *ptr)
+{
+	const struct tomoyo_inet_acl *acl =
+		container_of(ptr, typeof(*acl), head);
+	const u8 size = r->param.inet_network.is_ipv6 ? 16 : 4;
+
+	if (!(acl->perm & (1 << r->param.inet_network.operation)) ||
+	    !tomoyo_compare_number_union(r->param.inet_network.port,
+					 &acl->port))
+		return false;
+	if (acl->address.group)
+		return tomoyo_address_matches_group
+			(r->param.inet_network.is_ipv6,
+			 r->param.inet_network.address, acl->address.group);
+	return acl->address.is_ipv6 == r->param.inet_network.is_ipv6 &&
+		memcmp(&acl->address.ip[0],
+		       r->param.inet_network.address, size) <= 0 &&
+		memcmp(r->param.inet_network.address,
+		       &acl->address.ip[1], size) <= 0;
+}
+
+/**
+ * tomoyo_check_unix_acl - Check permission for unix domain socket operation.
+ *
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if granted, false otherwise.
+ */
+static bool tomoyo_check_unix_acl(struct tomoyo_request_info *r,
+				  const struct tomoyo_acl_info *ptr)
+{
+	const struct tomoyo_unix_acl *acl =
+		container_of(ptr, typeof(*acl), head);
+
+	return (acl->perm & (1 << r->param.unix_network.operation)) &&
+		tomoyo_compare_name_union(r->param.unix_network.address,
+					  &acl->name);
+}
+
+/**
+ * tomoyo_inet_entry - Check permission for INET network operation.
+ *
+ * @address: Pointer to "struct tomoyo_addr_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_inet_entry(const struct tomoyo_addr_info *address)
+{
+	const int idx = tomoyo_read_lock();
+	struct tomoyo_request_info r;
+	int error = 0;
+	const u8 type = tomoyo_inet2mac[address->protocol][address->operation];
+
+	if (type && tomoyo_init_request_info(&r, NULL, type)
+	    != TOMOYO_CONFIG_DISABLED) {
+		r.param_type = TOMOYO_TYPE_INET_ACL;
+		r.param.inet_network.protocol = address->protocol;
+		r.param.inet_network.operation = address->operation;
+		r.param.inet_network.is_ipv6 = address->inet.is_ipv6;
+		r.param.inet_network.address = address->inet.address;
+		r.param.inet_network.port = ntohs(address->inet.port);
+		do {
+			tomoyo_check_acl(&r, tomoyo_check_inet_acl);
+			error = tomoyo_audit_inet_log(&r);
+		} while (error == TOMOYO_RETRY_REQUEST);
+	}
+	tomoyo_read_unlock(idx);
+	return error;
+}
+
+/**
+ * tomoyo_check_inet_address - Check permission for inet domain socket's operation.
+ *
+ * @addr:     Pointer to "struct sockaddr".
+ * @addr_len: Size of @addr.
+ * @port:     Port number.
+ * @address:  Pointer to "struct tomoyo_addr_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_check_inet_address(const struct sockaddr *addr,
+				     const unsigned int addr_len,
+				     const u16 port,
+				     struct tomoyo_addr_info *address)
+{
+	struct tomoyo_inet_addr_info *i = &address->inet;
+
+	switch (addr->sa_family) {
+	case AF_INET6:
+		if (addr_len < SIN6_LEN_RFC2133)
+			goto skip;
+		i->is_ipv6 = true;
+		i->address = (__be32 *)
+			((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr;
+		i->port = ((struct sockaddr_in6 *) addr)->sin6_port;
+		break;
+	case AF_INET:
+		if (addr_len < sizeof(struct sockaddr_in))
+			goto skip;
+		i->is_ipv6 = false;
+		i->address = (__be32 *)
+			&((struct sockaddr_in *) addr)->sin_addr;
+		i->port = ((struct sockaddr_in *) addr)->sin_port;
+		break;
+	default:
+		goto skip;
+	}
+	if (address->protocol == SOCK_RAW)
+		i->port = htons(port);
+	return tomoyo_inet_entry(address);
+skip:
+	return 0;
+}
+
+/**
+ * tomoyo_unix_entry - Check permission for UNIX network operation.
+ *
+ * @address: Pointer to "struct tomoyo_addr_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_unix_entry(const struct tomoyo_addr_info *address)
+{
+	const int idx = tomoyo_read_lock();
+	struct tomoyo_request_info r;
+	int error = 0;
+	const u8 type = tomoyo_unix2mac[address->protocol][address->operation];
+
+	if (type && tomoyo_init_request_info(&r, NULL, type)
+	    != TOMOYO_CONFIG_DISABLED) {
+		char *buf = address->unix0.addr;
+		int len = address->unix0.addr_len - sizeof(sa_family_t);
+
+		if (len <= 0) {
+			buf = "anonymous";
+			len = 9;
+		} else if (buf[0]) {
+			len = strnlen(buf, len);
+		}
+		buf = tomoyo_encode2(buf, len);
+		if (buf) {
+			struct tomoyo_path_info addr;
+
+			addr.name = buf;
+			tomoyo_fill_path_info(&addr);
+			r.param_type = TOMOYO_TYPE_UNIX_ACL;
+			r.param.unix_network.protocol = address->protocol;
+			r.param.unix_network.operation = address->operation;
+			r.param.unix_network.address = &addr;
+			do {
+				tomoyo_check_acl(&r, tomoyo_check_unix_acl);
+				error = tomoyo_audit_unix_log(&r);
+			} while (error == TOMOYO_RETRY_REQUEST);
+			kfree(buf);
+		} else
+			error = -ENOMEM;
+	}
+	tomoyo_read_unlock(idx);
+	return error;
+}
+
+/**
+ * tomoyo_check_unix_address - Check permission for unix domain socket's operation.
+ *
+ * @addr:     Pointer to "struct sockaddr".
+ * @addr_len: Size of @addr.
+ * @address:  Pointer to "struct tomoyo_addr_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_check_unix_address(struct sockaddr *addr,
+				     const unsigned int addr_len,
+				     struct tomoyo_addr_info *address)
+{
+	struct tomoyo_unix_addr_info *u = &address->unix0;
+
+	if (addr->sa_family != AF_UNIX)
+		return 0;
+	u->addr = ((struct sockaddr_un *) addr)->sun_path;
+	u->addr_len = addr_len;
+	return tomoyo_unix_entry(address);
+}
+
+/**
+ * tomoyo_kernel_service - Check whether I'm kernel service or not.
+ *
+ * Returns true if I'm kernel service, false otherwise.
+ */
+static bool tomoyo_kernel_service(void)
+{
+	/* Nothing to do if I am a kernel service. */
+	return segment_eq(get_fs(), KERNEL_DS);
+}
+
+/**
+ * tomoyo_sock_family - Get socket's family.
+ *
+ * @sk: Pointer to "struct sock".
+ *
+ * Returns one of PF_INET, PF_INET6, PF_UNIX or 0.
+ */
+static u8 tomoyo_sock_family(struct sock *sk)
+{
+	u8 family;
+
+	if (tomoyo_kernel_service())
+		return 0;
+	family = sk->sk_family;
+	switch (family) {
+	case PF_INET:
+	case PF_INET6:
+	case PF_UNIX:
+		return family;
+	default:
+		return 0;
+	}
+}
+
+/**
+ * tomoyo_socket_listen_permission - Check permission for listening a socket.
+ *
+ * @sock: Pointer to "struct socket".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_socket_listen_permission(struct socket *sock)
+{
+	struct tomoyo_addr_info address;
+	const u8 family = tomoyo_sock_family(sock->sk);
+	const unsigned int type = sock->type;
+	struct sockaddr_storage addr;
+	int addr_len;
+
+	if (!family || (type != SOCK_STREAM && type != SOCK_SEQPACKET))
+		return 0;
+	{
+		const int error = sock->ops->getname(sock, (struct sockaddr *)
+						     &addr, &addr_len, 0);
+
+		if (error)
+			return error;
+	}
+	address.protocol = type;
+	address.operation = TOMOYO_NETWORK_LISTEN;
+	if (family == PF_UNIX)
+		return tomoyo_check_unix_address((struct sockaddr *) &addr,
+						 addr_len, &address);
+	return tomoyo_check_inet_address((struct sockaddr *) &addr, addr_len,
+					 0, &address);
+}
+
+/**
+ * tomoyo_socket_connect_permission - Check permission for setting the remote address of a socket.
+ *
+ * @sock:     Pointer to "struct socket".
+ * @addr:     Pointer to "struct sockaddr".
+ * @addr_len: Size of @addr.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_socket_connect_permission(struct socket *sock,
+				     struct sockaddr *addr, int addr_len)
+{
+	struct tomoyo_addr_info address;
+	const u8 family = tomoyo_sock_family(sock->sk);
+	const unsigned int type = sock->type;
+
+	if (!family)
+		return 0;
+	address.protocol = type;
+	switch (type) {
+	case SOCK_DGRAM:
+	case SOCK_RAW:
+		address.operation = TOMOYO_NETWORK_SEND;
+		break;
+	case SOCK_STREAM:
+	case SOCK_SEQPACKET:
+		address.operation = TOMOYO_NETWORK_CONNECT;
+		break;
+	default:
+		return 0;
+	}
+	if (family == PF_UNIX)
+		return tomoyo_check_unix_address(addr, addr_len, &address);
+	return tomoyo_check_inet_address(addr, addr_len, sock->sk->sk_protocol,
+					 &address);
+}
+
+/**
+ * tomoyo_socket_bind_permission - Check permission for setting the local address of a socket.
+ *
+ * @sock:     Pointer to "struct socket".
+ * @addr:     Pointer to "struct sockaddr".
+ * @addr_len: Size of @addr.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_socket_bind_permission(struct socket *sock, struct sockaddr *addr,
+				  int addr_len)
+{
+	struct tomoyo_addr_info address;
+	const u8 family = tomoyo_sock_family(sock->sk);
+	const unsigned int type = sock->type;
+
+	if (!family)
+		return 0;
+	switch (type) {
+	case SOCK_STREAM:
+	case SOCK_DGRAM:
+	case SOCK_RAW:
+	case SOCK_SEQPACKET:
+		address.protocol = type;
+		address.operation = TOMOYO_NETWORK_BIND;
+		break;
+	default:
+		return 0;
+	}
+	if (family == PF_UNIX)
+		return tomoyo_check_unix_address(addr, addr_len, &address);
+	return tomoyo_check_inet_address(addr, addr_len, sock->sk->sk_protocol,
+					 &address);
+}
+
+/**
+ * tomoyo_socket_sendmsg_permission - Check permission for sending a datagram.
+ *
+ * @sock: Pointer to "struct socket".
+ * @msg:  Pointer to "struct msghdr".
+ * @size: Unused.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_socket_sendmsg_permission(struct socket *sock, struct msghdr *msg,
+				     int size)
+{
+	struct tomoyo_addr_info address;
+	const u8 family = tomoyo_sock_family(sock->sk);
+	const unsigned int type = sock->type;
+
+	if (!msg->msg_name || !family ||
+	    (type != SOCK_DGRAM && type != SOCK_RAW))
+		return 0;
+	address.protocol = type;
+	address.operation = TOMOYO_NETWORK_SEND;
+	if (family == PF_UNIX)
+		return tomoyo_check_unix_address((struct sockaddr *)
+						 msg->msg_name,
+						 msg->msg_namelen, &address);
+	return tomoyo_check_inet_address((struct sockaddr *) msg->msg_name,
+					 msg->msg_namelen,
+					 sock->sk->sk_protocol, &address);
+}
diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c
index 6c601bd300f3..738bbdf8d4c7 100644
--- a/security/tomoyo/realpath.c
+++ b/security/tomoyo/realpath.c
@@ -15,17 +15,19 @@
 #include "../../fs/internal.h"
 
 /**
- * tomoyo_encode: Convert binary string to ascii string.
+ * tomoyo_encode2 - Encode binary string to ascii string.
  *
- * @str: String in binary format.
+ * @str:     String in binary format.
+ * @str_len: Size of @str in byte.
  *
  * Returns pointer to @str in ascii format on success, NULL otherwise.
  *
  * This function uses kzalloc(), so caller must kfree() if this function
  * didn't return NULL.
  */
-char *tomoyo_encode(const char *str)
+char *tomoyo_encode2(const char *str, int str_len)
 {
+	int i;
 	int len = 0;
 	const char *p = str;
 	char *cp;
@@ -33,8 +35,9 @@ char *tomoyo_encode(const char *str)
 
 	if (!p)
 		return NULL;
-	while (*p) {
-		const unsigned char c = *p++;
+	for (i = 0; i < str_len; i++) {
+		const unsigned char c = p[i];
+
 		if (c == '\\')
 			len += 2;
 		else if (c > ' ' && c < 127)
@@ -49,8 +52,8 @@ char *tomoyo_encode(const char *str)
 		return NULL;
 	cp0 = cp;
 	p = str;
-	while (*p) {
-		const unsigned char c = *p++;
+	for (i = 0; i < str_len; i++) {
+		const unsigned char c = p[i];
 
 		if (c == '\\') {
 			*cp++ = '\\';
@@ -68,6 +71,21 @@ char *tomoyo_encode(const char *str)
 }
 
 /**
+ * tomoyo_encode - Encode binary string to ascii string.
+ *
+ * @str: String in binary format.
+ *
+ * Returns pointer to @str in ascii format on success, NULL otherwise.
+ *
+ * This function uses kzalloc(), so caller must kfree() if this function
+ * didn't return NULL.
+ */
+char *tomoyo_encode(const char *str)
+{
+	return str ? tomoyo_encode2(str, strlen(str)) : NULL;
+}
+
+/**
  * tomoyo_get_absolute_path - Get the path of a dentry but ignores chroot'ed root.
  *
  * @path:   Pointer to "struct path".
diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c
index a49c3bfd4dd5..2672ac4f3beb 100644
--- a/security/tomoyo/securityfs_if.c
+++ b/security/tomoyo/securityfs_if.c
@@ -8,6 +8,124 @@
 #include "common.h"
 
 /**
+ * tomoyo_check_task_acl - Check permission for task operation.
+ *
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if granted, false otherwise.
+ */
+static bool tomoyo_check_task_acl(struct tomoyo_request_info *r,
+				  const struct tomoyo_acl_info *ptr)
+{
+	const struct tomoyo_task_acl *acl = container_of(ptr, typeof(*acl),
+							 head);
+	return !tomoyo_pathcmp(r->param.task.domainname, acl->domainname);
+}
+
+/**
+ * tomoyo_write_self - write() for /sys/kernel/security/tomoyo/self_domain interface.
+ *
+ * @file:  Pointer to "struct file".
+ * @buf:   Domainname to transit to.
+ * @count: Size of @buf.
+ * @ppos:  Unused.
+ *
+ * Returns @count on success, negative value otherwise.
+ *
+ * If domain transition was permitted but the domain transition failed, this
+ * function returns error rather than terminating current thread with SIGKILL.
+ */
+static ssize_t tomoyo_write_self(struct file *file, const char __user *buf,
+			      size_t count, loff_t *ppos)
+{
+	char *data;
+	int error;
+	if (!count || count >= TOMOYO_EXEC_TMPSIZE - 10)
+		return -ENOMEM;
+	data = kzalloc(count + 1, GFP_NOFS);
+	if (!data)
+		return -ENOMEM;
+	if (copy_from_user(data, buf, count)) {
+		error = -EFAULT;
+		goto out;
+	}
+	tomoyo_normalize_line(data);
+	if (tomoyo_correct_domain(data)) {
+		const int idx = tomoyo_read_lock();
+		struct tomoyo_path_info name;
+		struct tomoyo_request_info r;
+		name.name = data;
+		tomoyo_fill_path_info(&name);
+		/* Check "task manual_domain_transition" permission. */
+		tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE);
+		r.param_type = TOMOYO_TYPE_MANUAL_TASK_ACL;
+		r.param.task.domainname = &name;
+		tomoyo_check_acl(&r, tomoyo_check_task_acl);
+		if (!r.granted)
+			error = -EPERM;
+		else {
+			struct tomoyo_domain_info *new_domain =
+				tomoyo_assign_domain(data, true);
+			if (!new_domain) {
+				error = -ENOENT;
+			} else {
+				struct cred *cred = prepare_creds();
+				if (!cred) {
+					error = -ENOMEM;
+				} else {
+					struct tomoyo_domain_info *old_domain =
+						cred->security;
+					cred->security = new_domain;
+					atomic_inc(&new_domain->users);
+					atomic_dec(&old_domain->users);
+					commit_creds(cred);
+					error = 0;
+				}
+			}
+		}
+		tomoyo_read_unlock(idx);
+	} else
+		error = -EINVAL;
+out:
+	kfree(data);
+	return error ? error : count;
+}
+
+/**
+ * tomoyo_read_self - read() for /sys/kernel/security/tomoyo/self_domain interface.
+ *
+ * @file:  Pointer to "struct file".
+ * @buf:   Domainname which current thread belongs to.
+ * @count: Size of @buf.
+ * @ppos:  Bytes read by now.
+ *
+ * Returns read size on success, negative value otherwise.
+ */
+static ssize_t tomoyo_read_self(struct file *file, char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	const char *domain = tomoyo_domain()->domainname->name;
+	loff_t len = strlen(domain);
+	loff_t pos = *ppos;
+	if (pos >= len || !count)
+		return 0;
+	len -= pos;
+	if (count < len)
+		len = count;
+	if (copy_to_user(buf, domain + pos, len))
+		return -EFAULT;
+	*ppos += len;
+	return len;
+}
+
+/* Operations for /sys/kernel/security/tomoyo/self_domain interface. */
+static const struct file_operations tomoyo_self_operations = {
+	.write = tomoyo_write_self,
+	.read  = tomoyo_read_self,
+};
+
+/**
  * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface.
  *
  * @inode: Pointer to "struct inode".
@@ -135,8 +253,6 @@ static int __init tomoyo_initerface_init(void)
 			    TOMOYO_EXCEPTIONPOLICY);
 	tomoyo_create_entry("audit",            0400, tomoyo_dir,
 			    TOMOYO_AUDIT);
-	tomoyo_create_entry("self_domain",      0400, tomoyo_dir,
-			    TOMOYO_SELFDOMAIN);
 	tomoyo_create_entry(".process_status",  0600, tomoyo_dir,
 			    TOMOYO_PROCESS_STATUS);
 	tomoyo_create_entry("stat",             0644, tomoyo_dir,
@@ -147,6 +263,9 @@ static int __init tomoyo_initerface_init(void)
 			    TOMOYO_MANAGER);
 	tomoyo_create_entry("version",          0400, tomoyo_dir,
 			    TOMOYO_VERSION);
+	securityfs_create_file("self_domain", 0666, tomoyo_dir, NULL,
+			       &tomoyo_self_operations);
+	tomoyo_load_builtin_policy();
 	return 0;
 }
 
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index f776400a8f31..4b327b691745 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -442,6 +442,64 @@ static int tomoyo_sb_pivotroot(struct path *old_path, struct path *new_path)
 	return tomoyo_path2_perm(TOMOYO_TYPE_PIVOT_ROOT, new_path, old_path);
 }
 
+/**
+ * tomoyo_socket_listen - Check permission for listen().
+ *
+ * @sock:    Pointer to "struct socket".
+ * @backlog: Backlog parameter.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_socket_listen(struct socket *sock, int backlog)
+{
+	return tomoyo_socket_listen_permission(sock);
+}
+
+/**
+ * tomoyo_socket_connect - Check permission for connect().
+ *
+ * @sock:     Pointer to "struct socket".
+ * @addr:     Pointer to "struct sockaddr".
+ * @addr_len: Size of @addr.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_socket_connect(struct socket *sock, struct sockaddr *addr,
+				 int addr_len)
+{
+	return tomoyo_socket_connect_permission(sock, addr, addr_len);
+}
+
+/**
+ * tomoyo_socket_bind - Check permission for bind().
+ *
+ * @sock:     Pointer to "struct socket".
+ * @addr:     Pointer to "struct sockaddr".
+ * @addr_len: Size of @addr.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_socket_bind(struct socket *sock, struct sockaddr *addr,
+			      int addr_len)
+{
+	return tomoyo_socket_bind_permission(sock, addr, addr_len);
+}
+
+/**
+ * tomoyo_socket_sendmsg - Check permission for sendmsg().
+ *
+ * @sock: Pointer to "struct socket".
+ * @msg:  Pointer to "struct msghdr".
+ * @size: Size of message.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_socket_sendmsg(struct socket *sock, struct msghdr *msg,
+				 int size)
+{
+	return tomoyo_socket_sendmsg_permission(sock, msg, size);
+}
+
 /*
  * tomoyo_security_ops is a "struct security_operations" which is used for
  * registering TOMOYO.
@@ -472,6 +530,10 @@ static struct security_operations tomoyo_security_ops = {
 	.sb_mount            = tomoyo_sb_mount,
 	.sb_umount           = tomoyo_sb_umount,
 	.sb_pivotroot        = tomoyo_sb_pivotroot,
+	.socket_bind         = tomoyo_socket_bind,
+	.socket_connect      = tomoyo_socket_connect,
+	.socket_listen       = tomoyo_socket_listen,
+	.socket_sendmsg      = tomoyo_socket_sendmsg,
 };
 
 /* Lock for GC. */
diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c
index c36bd1107fc8..4a9b4b2eb755 100644
--- a/security/tomoyo/util.c
+++ b/security/tomoyo/util.c
@@ -42,6 +42,39 @@ const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX] = {
 	[TOMOYO_MAC_FILE_MOUNT]      = TOMOYO_MAC_CATEGORY_FILE,
 	[TOMOYO_MAC_FILE_UMOUNT]     = TOMOYO_MAC_CATEGORY_FILE,
 	[TOMOYO_MAC_FILE_PIVOT_ROOT] = TOMOYO_MAC_CATEGORY_FILE,
+	/* CONFIG::network group */
+	[TOMOYO_MAC_NETWORK_INET_STREAM_BIND]       =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN]     =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT]    =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_INET_DGRAM_BIND]        =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_INET_DGRAM_SEND]        =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_INET_RAW_BIND]          =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_INET_RAW_SEND]          =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND]       =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN]     =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT]    =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND]        =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND]        =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND]    =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN]  =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	/* CONFIG::misc group */
+	[TOMOYO_MAC_ENVIRON]         = TOMOYO_MAC_CATEGORY_MISC,
 };
 
 /**
@@ -126,6 +159,31 @@ char *tomoyo_read_token(struct tomoyo_acl_param *param)
 }
 
 /**
+ * tomoyo_get_domainname - Read a domainname from a line.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns a domainname on success, NULL otherwise.
+ */
+const struct tomoyo_path_info *tomoyo_get_domainname
+(struct tomoyo_acl_param *param)
+{
+	char *start = param->data;
+	char *pos = start;
+	while (*pos) {
+		if (*pos++ != ' ' || *pos++ == '/')
+			continue;
+		pos -= 2;
+		*pos++ = '\0';
+		break;
+	}
+	param->data = pos;
+	if (tomoyo_correct_domain(start))
+		return tomoyo_get_name(start);
+	return NULL;
+}
+
+/**
  * tomoyo_parse_ulong - Parse an "unsigned long" value.
  *
  * @result: Pointer to "unsigned long".
@@ -920,14 +978,17 @@ int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile,
 		    const u8 index)
 {
 	u8 mode;
-	const u8 category = TOMOYO_MAC_CATEGORY_FILE;
+	struct tomoyo_profile *p;
+
 	if (!tomoyo_policy_loaded)
 		return TOMOYO_CONFIG_DISABLED;
-	mode = tomoyo_profile(ns, profile)->config[index];
+	p = tomoyo_profile(ns, profile);
+	mode = p->config[index];
 	if (mode == TOMOYO_CONFIG_USE_DEFAULT)
-		mode = tomoyo_profile(ns, profile)->config[category];
+		mode = p->config[tomoyo_index2category[index]
+				 + TOMOYO_MAX_MAC_INDEX];
 	if (mode == TOMOYO_CONFIG_USE_DEFAULT)
-		mode = tomoyo_profile(ns, profile)->default_config;
+		mode = p->default_config;
 	return mode & 3;
 }
 
@@ -996,6 +1057,17 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r)
 			perm = container_of(ptr, struct tomoyo_mkdev_acl,
 					    head)->perm;
 			break;
+		case TOMOYO_TYPE_INET_ACL:
+			perm = container_of(ptr, struct tomoyo_inet_acl,
+					    head)->perm;
+			break;
+		case TOMOYO_TYPE_UNIX_ACL:
+			perm = container_of(ptr, struct tomoyo_unix_acl,
+					    head)->perm;
+			break;
+		case TOMOYO_TYPE_MANUAL_TASK_ACL:
+			perm = 0;
+			break;
 		default:
 			perm = 1;
 		}