Browse Source

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6: (71 commits)
  SELinux: inode_doinit_with_dentry drop no dentry printk
  SELinux: new permission between tty audit and audit socket
  SELinux: open perm for sock files
  smack: fixes for unlabeled host support
  keys: make procfiles per-user-namespace
  keys: skip keys from another user namespace
  keys: consider user namespace in key_permission
  keys: distinguish per-uid keys in different namespaces
  integrity: ima iint radix_tree_lookup locking fix
  TOMOYO: Do not call tomoyo_realpath_init unless registered.
  integrity: ima scatterlist bug fix
  smack: fix lots of kernel-doc notation
  TOMOYO: Don't create securityfs entries unless registered.
  TOMOYO: Fix exception policy read failure.
  SELinux: convert the avc cache hash list to an hlist
  SELinux: code readability with avc_cache
  SELinux: remove unused av.decided field
  SELinux: more careful use of avd in avc_has_perm_noaudit
  SELinux: remove the unused ae.used
  SELinux: check seqno when updating an avc_node
  ...
Linus Torvalds 16 years ago
parent
commit
8d80ce80e1
63 changed files with 8818 additions and 626 deletions
  1. 61 0
      Documentation/ABI/testing/ima_policy
  2. 10 0
      Documentation/kernel-parameters.txt
  3. 19 0
      MAINTAINERS
  4. 248 282
      drivers/char/tpm/tpm.c
  5. 142 0
      drivers/char/tpm/tpm.h
  6. 3 0
      fs/compat.c
  7. 13 0
      fs/exec.c
  8. 2 0
      fs/file_table.c
  9. 17 7
      fs/inode.c
  10. 8 0
      fs/namei.c
  11. 8 1
      include/linux/audit.h
  12. 61 0
      include/linux/ima.h
  13. 2 0
      include/linux/sched.h
  14. 35 0
      include/linux/tpm.h
  15. 3 0
      ipc/shm.c
  16. 1 1
      kernel/user.c
  17. 4 0
      mm/mmap.c
  18. 2 0
      mm/shmem.c
  19. 5 1
      security/Kconfig
  20. 6 0
      security/Makefile
  21. 3 4
      security/inode.c
  22. 55 0
      security/integrity/ima/Kconfig
  23. 9 0
      security/integrity/ima/Makefile
  24. 166 0
      security/integrity/ima/ima.h
  25. 190 0
      security/integrity/ima/ima_api.c
  26. 81 0
      security/integrity/ima/ima_audit.c
  27. 140 0
      security/integrity/ima/ima_crypto.c
  28. 376 0
      security/integrity/ima/ima_fs.c
  29. 204 0
      security/integrity/ima/ima_iint.c
  30. 96 0
      security/integrity/ima/ima_init.c
  31. 327 0
      security/integrity/ima/ima_main.c
  32. 414 0
      security/integrity/ima/ima_policy.c
  33. 140 0
      security/integrity/ima/ima_queue.c
  34. 3 1
      security/keys/internal.h
  35. 9 2
      security/keys/key.c
  36. 1 1
      security/keys/keyctl.c
  37. 3 0
      security/keys/keyring.c
  38. 5 0
      security/keys/permission.c
  39. 49 6
      security/keys/proc.c
  40. 2 0
      security/keys/process_keys.c
  41. 1 1
      security/keys/request_key.c
  42. 95 74
      security/selinux/avc.c
  43. 107 201
      security/selinux/hooks.c
  44. 2 0
      security/selinux/include/av_perm_to_string.h
  45. 2 0
      security/selinux/include/av_permissions.h
  46. 0 2
      security/selinux/include/objsec.h
  47. 8 1
      security/selinux/include/security.h
  48. 1 1
      security/selinux/nlmsgtab.c
  49. 1 1
      security/selinux/selinuxfs.c
  50. 0 2
      security/selinux/ss/services.c
  51. 2 2
      security/smack/smack_access.c
  52. 24 28
      security/smack/smack_lsm.c
  53. 8 7
      security/smack/smackfs.c
  54. 11 0
      security/tomoyo/Kconfig
  55. 1 0
      security/tomoyo/Makefile
  56. 2206 0
      security/tomoyo/common.c
  57. 359 0
      security/tomoyo/common.h
  58. 878 0
      security/tomoyo/domain.c
  59. 1241 0
      security/tomoyo/file.c
  60. 482 0
      security/tomoyo/realpath.c
  61. 66 0
      security/tomoyo/realpath.h
  62. 294 0
      security/tomoyo/tomoyo.c
  63. 106 0
      security/tomoyo/tomoyo.h

+ 61 - 0
Documentation/ABI/testing/ima_policy

@@ -0,0 +1,61 @@
+What:		security/ima/policy
+Date:		May 2008
+Contact:	Mimi Zohar <zohar@us.ibm.com>
+Description:
+		The Trusted Computing Group(TCG) runtime Integrity
+		Measurement Architecture(IMA) maintains a list of hash
+		values of executables and other sensitive system files
+		loaded into the run-time of this system.  At runtime,
+		the policy can be constrained based on LSM specific data.
+		Policies are loaded into the securityfs file ima/policy
+		by opening the file, writing the rules one at a time and
+		then closing the file.  The new policy takes effect after
+		the file ima/policy is closed.
+
+		rule format: action [condition ...]
+
+		action: measure | dont_measure
+		condition:= base | lsm
+			base:	[[func=] [mask=] [fsmagic=] [uid=]]
+			lsm:	[[subj_user=] [subj_role=] [subj_type=]
+				 [obj_user=] [obj_role=] [obj_type=]]
+
+		base: 	func:= [BPRM_CHECK][FILE_MMAP][INODE_PERMISSION]
+			mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC]
+			fsmagic:= hex value
+			uid:= decimal value
+		lsm:  	are LSM specific
+
+		default policy:
+			# PROC_SUPER_MAGIC
+			dont_measure fsmagic=0x9fa0
+			# SYSFS_MAGIC
+			dont_measure fsmagic=0x62656572
+			# DEBUGFS_MAGIC
+			dont_measure fsmagic=0x64626720
+			# TMPFS_MAGIC
+			dont_measure fsmagic=0x01021994
+			# SECURITYFS_MAGIC
+			dont_measure fsmagic=0x73636673
+
+			measure func=BPRM_CHECK
+			measure func=FILE_MMAP mask=MAY_EXEC
+			measure func=INODE_PERM mask=MAY_READ uid=0
+
+		The default policy measures all executables in bprm_check,
+		all files mmapped executable in file_mmap, and all files
+		open for read by root in inode_permission.
+
+		Examples of LSM specific definitions:
+
+		SELinux:
+			# SELINUX_MAGIC
+			dont_measure fsmagic=0xF97CFF8C
+
+			dont_measure obj_type=var_log_t
+			dont_measure obj_type=auditd_log_t
+			measure subj_user=system_u func=INODE_PERM mask=MAY_READ
+			measure subj_role=system_r func=INODE_PERM mask=MAY_READ
+
+		Smack:
+			measure subj_user=_ func=INODE_PERM mask=MAY_READ

+ 10 - 0
Documentation/kernel-parameters.txt

@@ -44,6 +44,7 @@ parameter is applicable:
 	FB	The frame buffer device is enabled.
 	FB	The frame buffer device is enabled.
 	HW	Appropriate hardware is enabled.
 	HW	Appropriate hardware is enabled.
 	IA-64	IA-64 architecture is enabled.
 	IA-64	IA-64 architecture is enabled.
+	IMA     Integrity measurement architecture is enabled.
 	IOSCHED	More than one I/O scheduler is enabled.
 	IOSCHED	More than one I/O scheduler is enabled.
 	IP_PNP	IP DHCP, BOOTP, or RARP is enabled.
 	IP_PNP	IP DHCP, BOOTP, or RARP is enabled.
 	ISAPNP	ISA PnP code is enabled.
 	ISAPNP	ISA PnP code is enabled.
@@ -902,6 +903,15 @@ and is between 256 and 4096 characters. It is defined in the file
 	ihash_entries=	[KNL]
 	ihash_entries=	[KNL]
 			Set number of hash buckets for inode cache.
 			Set number of hash buckets for inode cache.
 
 
+	ima_audit=	[IMA]
+			Format: { "0" | "1" }
+			0 -- integrity auditing messages. (Default)
+			1 -- enable informational integrity auditing messages.
+
+	ima_hash=	[IMA]
+			Formt: { "sha1" | "md5" }
+			default: "sha1"
+
 	in2000=		[HW,SCSI]
 	in2000=		[HW,SCSI]
 			See header of drivers/scsi/in2000.c.
 			See header of drivers/scsi/in2000.c.
 
 

+ 19 - 0
MAINTAINERS

@@ -2216,6 +2216,11 @@ M:	stefanr@s5r6.in-berlin.de
 L:	linux1394-devel@lists.sourceforge.net
 L:	linux1394-devel@lists.sourceforge.net
 S:	Maintained
 S:	Maintained
 
 
+INTEGRITY MEASUREMENT ARCHITECTURE (IMA)
+P:	Mimi Zohar
+M:	zohar@us.ibm.com
+S:	Supported
+
 IMS TWINTURBO FRAMEBUFFER DRIVER
 IMS TWINTURBO FRAMEBUFFER DRIVER
 L:	linux-fbdev-devel@lists.sourceforge.net (moderated for non-subscribers)
 L:	linux-fbdev-devel@lists.sourceforge.net (moderated for non-subscribers)
 S:	Orphan
 S:	Orphan
@@ -3844,6 +3849,7 @@ M:	jmorris@namei.org
 L:	linux-kernel@vger.kernel.org
 L:	linux-kernel@vger.kernel.org
 L:	linux-security-module@vger.kernel.org (suggested Cc:)
 L:	linux-security-module@vger.kernel.org (suggested Cc:)
 T:	git kernel.org:pub/scm/linux/kernel/git/jmorris/security-testing-2.6.git
 T:	git kernel.org:pub/scm/linux/kernel/git/jmorris/security-testing-2.6.git
+W:	http://security.wiki.kernel.org/
 S:	Supported
 S:	Supported
 
 
 SECURITY CONTACT
 SECURITY CONTACT
@@ -4285,6 +4291,19 @@ L:	tlan-devel@lists.sourceforge.net (subscribers-only)
 W:	http://sourceforge.net/projects/tlan/
 W:	http://sourceforge.net/projects/tlan/
 S:	Maintained
 S:	Maintained
 
 
+TOMOYO SECURITY MODULE
+P:	Kentaro Takeda
+M:	takedakn@nttdata.co.jp
+P:	Tetsuo Handa
+M:	penguin-kernel@I-love.SAKURA.ne.jp
+L:	linux-kernel@vger.kernel.org (kernel issues)
+L:	tomoyo-users-en@lists.sourceforge.jp (subscribers-only, for developers and users in English)
+L:	tomoyo-dev@lists.sourceforge.jp (subscribers-only, for developers in Japanese)
+L:	tomoyo-users@lists.sourceforge.jp (subscribers-only, for users in Japanese)
+W:	http://tomoyo.sourceforge.jp/
+T:	quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.2.x/tomoyo-lsm/patches/
+S:	Maintained
+
 TOSHIBA ACPI EXTRAS DRIVER
 TOSHIBA ACPI EXTRAS DRIVER
 P:	John Belmonte
 P:	John Belmonte
 M:	toshiba_acpi@memebeam.org
 M:	toshiba_acpi@memebeam.org

+ 248 - 282
drivers/char/tpm/tpm.c

@@ -429,134 +429,148 @@ out:
 #define TPM_DIGEST_SIZE 20
 #define TPM_DIGEST_SIZE 20
 #define TPM_ERROR_SIZE 10
 #define TPM_ERROR_SIZE 10
 #define TPM_RET_CODE_IDX 6
 #define TPM_RET_CODE_IDX 6
-#define TPM_GET_CAP_RET_SIZE_IDX 10
-#define TPM_GET_CAP_RET_UINT32_1_IDX 14
-#define TPM_GET_CAP_RET_UINT32_2_IDX 18
-#define TPM_GET_CAP_RET_UINT32_3_IDX 22
-#define TPM_GET_CAP_RET_UINT32_4_IDX 26
-#define TPM_GET_CAP_PERM_DISABLE_IDX 16
-#define TPM_GET_CAP_PERM_INACTIVE_IDX 18
-#define TPM_GET_CAP_RET_BOOL_1_IDX 14
-#define TPM_GET_CAP_TEMP_INACTIVE_IDX 16
-
-#define TPM_CAP_IDX 13
-#define TPM_CAP_SUBCAP_IDX 21
 
 
 enum tpm_capabilities {
 enum tpm_capabilities {
-	TPM_CAP_FLAG = 4,
-	TPM_CAP_PROP = 5,
+	TPM_CAP_FLAG = cpu_to_be32(4),
+	TPM_CAP_PROP = cpu_to_be32(5),
+	CAP_VERSION_1_1 = cpu_to_be32(0x06),
+	CAP_VERSION_1_2 = cpu_to_be32(0x1A)
 };
 };
 
 
 enum tpm_sub_capabilities {
 enum tpm_sub_capabilities {
-	TPM_CAP_PROP_PCR = 0x1,
-	TPM_CAP_PROP_MANUFACTURER = 0x3,
-	TPM_CAP_FLAG_PERM = 0x8,
-	TPM_CAP_FLAG_VOL = 0x9,
-	TPM_CAP_PROP_OWNER = 0x11,
-	TPM_CAP_PROP_TIS_TIMEOUT = 0x15,
-	TPM_CAP_PROP_TIS_DURATION = 0x20,
-};
+	TPM_CAP_PROP_PCR = cpu_to_be32(0x101),
+	TPM_CAP_PROP_MANUFACTURER = cpu_to_be32(0x103),
+	TPM_CAP_FLAG_PERM = cpu_to_be32(0x108),
+	TPM_CAP_FLAG_VOL = cpu_to_be32(0x109),
+	TPM_CAP_PROP_OWNER = cpu_to_be32(0x111),
+	TPM_CAP_PROP_TIS_TIMEOUT = cpu_to_be32(0x115),
+	TPM_CAP_PROP_TIS_DURATION = cpu_to_be32(0x120),
 
 
-/*
- * This is a semi generic GetCapability command for use
- * with the capability type TPM_CAP_PROP or TPM_CAP_FLAG
- * and their associated sub_capabilities.
- */
-
-static const u8 tpm_cap[] = {
-	0, 193,			/* TPM_TAG_RQU_COMMAND */
-	0, 0, 0, 22,		/* length */
-	0, 0, 0, 101,		/* TPM_ORD_GetCapability */
-	0, 0, 0, 0,		/* TPM_CAP_<TYPE> */
-	0, 0, 0, 4,		/* TPM_CAP_SUB_<TYPE> size */
-	0, 0, 1, 0		/* TPM_CAP_SUB_<TYPE> */
 };
 };
 
 
-static ssize_t transmit_cmd(struct tpm_chip *chip, u8 *data, int len,
-			    char *desc)
+static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd,
+			    int len, const char *desc)
 {
 {
 	int err;
 	int err;
 
 
-	len = tpm_transmit(chip, data, len);
+	len = tpm_transmit(chip,(u8 *) cmd, len);
 	if (len <  0)
 	if (len <  0)
 		return len;
 		return len;
 	if (len == TPM_ERROR_SIZE) {
 	if (len == TPM_ERROR_SIZE) {
-		err = be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX)));
+		err = be32_to_cpu(cmd->header.out.return_code);
 		dev_dbg(chip->dev, "A TPM error (%d) occurred %s\n", err, desc);
 		dev_dbg(chip->dev, "A TPM error (%d) occurred %s\n", err, desc);
 		return err;
 		return err;
 	}
 	}
 	return 0;
 	return 0;
 }
 }
 
 
+#define TPM_INTERNAL_RESULT_SIZE 200
+#define TPM_TAG_RQU_COMMAND cpu_to_be16(193)
+#define TPM_ORD_GET_CAP cpu_to_be32(101)
+
+static const struct tpm_input_header tpm_getcap_header = {
+	.tag = TPM_TAG_RQU_COMMAND,
+	.length = cpu_to_be32(22),
+	.ordinal = TPM_ORD_GET_CAP
+};
+
+ssize_t tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap,
+		   const char *desc)
+{
+	struct tpm_cmd_t tpm_cmd;
+	int rc;
+	struct tpm_chip *chip = dev_get_drvdata(dev);
+
+	tpm_cmd.header.in = tpm_getcap_header;
+	if (subcap_id == CAP_VERSION_1_1 || subcap_id == CAP_VERSION_1_2) {
+		tpm_cmd.params.getcap_in.cap = subcap_id;
+		/*subcap field not necessary */
+		tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(0);
+		tpm_cmd.header.in.length -= cpu_to_be32(sizeof(__be32));
+	} else {
+		if (subcap_id == TPM_CAP_FLAG_PERM ||
+		    subcap_id == TPM_CAP_FLAG_VOL)
+			tpm_cmd.params.getcap_in.cap = TPM_CAP_FLAG;
+		else
+			tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
+		tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
+		tpm_cmd.params.getcap_in.subcap = subcap_id;
+	}
+	rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, desc);
+	if (!rc)
+		*cap = tpm_cmd.params.getcap_out.cap;
+	return rc;
+}
+
 void tpm_gen_interrupt(struct tpm_chip *chip)
 void tpm_gen_interrupt(struct tpm_chip *chip)
 {
 {
-	u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 30)];
+	struct	tpm_cmd_t tpm_cmd;
 	ssize_t rc;
 	ssize_t rc;
 
 
-	memcpy(data, tpm_cap, sizeof(tpm_cap));
-	data[TPM_CAP_IDX] = TPM_CAP_PROP;
-	data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_TIMEOUT;
+	tpm_cmd.header.in = tpm_getcap_header;
+	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
+	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
+	tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
 
 
-	rc = transmit_cmd(chip, data, sizeof(data),
+	rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
 			"attempting to determine the timeouts");
 			"attempting to determine the timeouts");
 }
 }
 EXPORT_SYMBOL_GPL(tpm_gen_interrupt);
 EXPORT_SYMBOL_GPL(tpm_gen_interrupt);
 
 
 void tpm_get_timeouts(struct tpm_chip *chip)
 void tpm_get_timeouts(struct tpm_chip *chip)
 {
 {
-	u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 30)];
+	struct tpm_cmd_t tpm_cmd;
+	struct timeout_t *timeout_cap;
+	struct duration_t *duration_cap;
 	ssize_t rc;
 	ssize_t rc;
 	u32 timeout;
 	u32 timeout;
 
 
-	memcpy(data, tpm_cap, sizeof(tpm_cap));
-	data[TPM_CAP_IDX] = TPM_CAP_PROP;
-	data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_TIMEOUT;
+	tpm_cmd.header.in = tpm_getcap_header;
+	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
+	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
+	tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
 
 
-	rc = transmit_cmd(chip, data, sizeof(data),
+	rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
 			"attempting to determine the timeouts");
 			"attempting to determine the timeouts");
 	if (rc)
 	if (rc)
 		goto duration;
 		goto duration;
 
 
-	if (be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_SIZE_IDX)))
+	if (be32_to_cpu(tpm_cmd.header.out.length)
 	    != 4 * sizeof(u32))
 	    != 4 * sizeof(u32))
 		goto duration;
 		goto duration;
 
 
+	timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout;
 	/* Don't overwrite default if value is 0 */
 	/* Don't overwrite default if value is 0 */
-	timeout =
-	    be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX)));
+	timeout = be32_to_cpu(timeout_cap->a);
 	if (timeout)
 	if (timeout)
 		chip->vendor.timeout_a = usecs_to_jiffies(timeout);
 		chip->vendor.timeout_a = usecs_to_jiffies(timeout);
-	timeout =
-	    be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_2_IDX)));
+	timeout = be32_to_cpu(timeout_cap->b);
 	if (timeout)
 	if (timeout)
 		chip->vendor.timeout_b = usecs_to_jiffies(timeout);
 		chip->vendor.timeout_b = usecs_to_jiffies(timeout);
-	timeout =
-	    be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_3_IDX)));
+	timeout = be32_to_cpu(timeout_cap->c);
 	if (timeout)
 	if (timeout)
 		chip->vendor.timeout_c = usecs_to_jiffies(timeout);
 		chip->vendor.timeout_c = usecs_to_jiffies(timeout);
-	timeout =
-	    be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_4_IDX)));
+	timeout = be32_to_cpu(timeout_cap->d);
 	if (timeout)
 	if (timeout)
 		chip->vendor.timeout_d = usecs_to_jiffies(timeout);
 		chip->vendor.timeout_d = usecs_to_jiffies(timeout);
 
 
 duration:
 duration:
-	memcpy(data, tpm_cap, sizeof(tpm_cap));
-	data[TPM_CAP_IDX] = TPM_CAP_PROP;
-	data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_DURATION;
+	tpm_cmd.header.in = tpm_getcap_header;
+	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
+	tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
+	tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION;
 
 
-	rc = transmit_cmd(chip, data, sizeof(data),
+	rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
 			"attempting to determine the durations");
 			"attempting to determine the durations");
 	if (rc)
 	if (rc)
 		return;
 		return;
 
 
-	if (be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_SIZE_IDX)))
+	if (be32_to_cpu(tpm_cmd.header.out.return_code)
 	    != 3 * sizeof(u32))
 	    != 3 * sizeof(u32))
 		return;
 		return;
-
+	duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
 	chip->vendor.duration[TPM_SHORT] =
 	chip->vendor.duration[TPM_SHORT] =
-	    usecs_to_jiffies(be32_to_cpu
-			     (*((__be32 *) (data +
-					    TPM_GET_CAP_RET_UINT32_1_IDX))));
+	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short));
 	/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
 	/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
 	 * value wrong and apparently reports msecs rather than usecs. So we
 	 * value wrong and apparently reports msecs rather than usecs. So we
 	 * fix up the resulting too-small TPM_SHORT value to make things work.
 	 * fix up the resulting too-small TPM_SHORT value to make things work.
@@ -565,13 +579,9 @@ duration:
 		chip->vendor.duration[TPM_SHORT] = HZ;
 		chip->vendor.duration[TPM_SHORT] = HZ;
 
 
 	chip->vendor.duration[TPM_MEDIUM] =
 	chip->vendor.duration[TPM_MEDIUM] =
-	    usecs_to_jiffies(be32_to_cpu
-			     (*((__be32 *) (data +
-					    TPM_GET_CAP_RET_UINT32_2_IDX))));
+	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium));
 	chip->vendor.duration[TPM_LONG] =
 	chip->vendor.duration[TPM_LONG] =
-	    usecs_to_jiffies(be32_to_cpu
-			     (*((__be32 *) (data +
-					    TPM_GET_CAP_RET_UINT32_3_IDX))));
+	    usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long));
 }
 }
 EXPORT_SYMBOL_GPL(tpm_get_timeouts);
 EXPORT_SYMBOL_GPL(tpm_get_timeouts);
 
 
@@ -587,36 +597,18 @@ void tpm_continue_selftest(struct tpm_chip *chip)
 }
 }
 EXPORT_SYMBOL_GPL(tpm_continue_selftest);
 EXPORT_SYMBOL_GPL(tpm_continue_selftest);
 
 
-#define  TPM_INTERNAL_RESULT_SIZE 200
-
 ssize_t tpm_show_enabled(struct device * dev, struct device_attribute * attr,
 ssize_t tpm_show_enabled(struct device * dev, struct device_attribute * attr,
 			char *buf)
 			char *buf)
 {
 {
-	u8 *data;
+	cap_t cap;
 	ssize_t rc;
 	ssize_t rc;
 
 
-	struct tpm_chip *chip = dev_get_drvdata(dev);
-	if (chip == NULL)
-		return -ENODEV;
-
-	data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
-	if (!data)
-		return -ENOMEM;
-
-	memcpy(data, tpm_cap, sizeof(tpm_cap));
-	data[TPM_CAP_IDX] = TPM_CAP_FLAG;
-	data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_PERM;
-
-	rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
-			"attemtping to determine the permanent enabled state");
-	if (rc) {
-		kfree(data);
+	rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap,
+			 "attempting to determine the permanent enabled state");
+	if (rc)
 		return 0;
 		return 0;
-	}
-
-	rc = sprintf(buf, "%d\n", !data[TPM_GET_CAP_PERM_DISABLE_IDX]);
 
 
-	kfree(data);
+	rc = sprintf(buf, "%d\n", !cap.perm_flags.disable);
 	return rc;
 	return rc;
 }
 }
 EXPORT_SYMBOL_GPL(tpm_show_enabled);
 EXPORT_SYMBOL_GPL(tpm_show_enabled);
@@ -624,31 +616,15 @@ EXPORT_SYMBOL_GPL(tpm_show_enabled);
 ssize_t tpm_show_active(struct device * dev, struct device_attribute * attr,
 ssize_t tpm_show_active(struct device * dev, struct device_attribute * attr,
 			char *buf)
 			char *buf)
 {
 {
-	u8 *data;
+	cap_t cap;
 	ssize_t rc;
 	ssize_t rc;
 
 
-	struct tpm_chip *chip = dev_get_drvdata(dev);
-	if (chip == NULL)
-		return -ENODEV;
-
-	data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
-	if (!data)
-		return -ENOMEM;
-
-	memcpy(data, tpm_cap, sizeof(tpm_cap));
-	data[TPM_CAP_IDX] = TPM_CAP_FLAG;
-	data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_PERM;
-
-	rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
-			"attemtping to determine the permanent active state");
-	if (rc) {
-		kfree(data);
+	rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap,
+			 "attempting to determine the permanent active state");
+	if (rc)
 		return 0;
 		return 0;
-	}
 
 
-	rc = sprintf(buf, "%d\n", !data[TPM_GET_CAP_PERM_INACTIVE_IDX]);
-
-	kfree(data);
+	rc = sprintf(buf, "%d\n", !cap.perm_flags.deactivated);
 	return rc;
 	return rc;
 }
 }
 EXPORT_SYMBOL_GPL(tpm_show_active);
 EXPORT_SYMBOL_GPL(tpm_show_active);
@@ -656,31 +632,15 @@ EXPORT_SYMBOL_GPL(tpm_show_active);
 ssize_t tpm_show_owned(struct device * dev, struct device_attribute * attr,
 ssize_t tpm_show_owned(struct device * dev, struct device_attribute * attr,
 			char *buf)
 			char *buf)
 {
 {
-	u8 *data;
+	cap_t cap;
 	ssize_t rc;
 	ssize_t rc;
 
 
-	struct tpm_chip *chip = dev_get_drvdata(dev);
-	if (chip == NULL)
-		return -ENODEV;
-
-	data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
-	if (!data)
-		return -ENOMEM;
-
-	memcpy(data, tpm_cap, sizeof(tpm_cap));
-	data[TPM_CAP_IDX] = TPM_CAP_PROP;
-	data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_OWNER;
-
-	rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
-			"attempting to determine the owner state");
-	if (rc) {
-		kfree(data);
+	rc = tpm_getcap(dev, TPM_CAP_PROP_OWNER, &cap,
+			 "attempting to determine the owner state");
+	if (rc)
 		return 0;
 		return 0;
-	}
-
-	rc = sprintf(buf, "%d\n", data[TPM_GET_CAP_RET_BOOL_1_IDX]);
 
 
-	kfree(data);
+	rc = sprintf(buf, "%d\n", cap.owned);
 	return rc;
 	return rc;
 }
 }
 EXPORT_SYMBOL_GPL(tpm_show_owned);
 EXPORT_SYMBOL_GPL(tpm_show_owned);
@@ -688,116 +648,180 @@ EXPORT_SYMBOL_GPL(tpm_show_owned);
 ssize_t tpm_show_temp_deactivated(struct device * dev,
 ssize_t tpm_show_temp_deactivated(struct device * dev,
 				struct device_attribute * attr, char *buf)
 				struct device_attribute * attr, char *buf)
 {
 {
-	u8 *data;
+	cap_t cap;
 	ssize_t rc;
 	ssize_t rc;
 
 
-	struct tpm_chip *chip = dev_get_drvdata(dev);
-	if (chip == NULL)
-		return -ENODEV;
+	rc = tpm_getcap(dev, TPM_CAP_FLAG_VOL, &cap,
+			 "attempting to determine the temporary state");
+	if (rc)
+		return 0;
 
 
-	data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
-	if (!data)
-		return -ENOMEM;
+	rc = sprintf(buf, "%d\n", cap.stclear_flags.deactivated);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_show_temp_deactivated);
 
 
-	memcpy(data, tpm_cap, sizeof(tpm_cap));
-	data[TPM_CAP_IDX] = TPM_CAP_FLAG;
-	data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_VOL;
+/*
+ * tpm_chip_find_get - return tpm_chip for given chip number
+ */
+static struct tpm_chip *tpm_chip_find_get(int chip_num)
+{
+	struct tpm_chip *pos, *chip = NULL;
 
 
-	rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
-			"attempting to determine the temporary state");
-	if (rc) {
-		kfree(data);
-		return 0;
+	rcu_read_lock();
+	list_for_each_entry_rcu(pos, &tpm_chip_list, list) {
+		if (chip_num != TPM_ANY_NUM && chip_num != pos->dev_num)
+			continue;
+
+		if (try_module_get(pos->dev->driver->owner)) {
+			chip = pos;
+			break;
+		}
 	}
 	}
+	rcu_read_unlock();
+	return chip;
+}
 
 
-	rc = sprintf(buf, "%d\n", data[TPM_GET_CAP_TEMP_INACTIVE_IDX]);
+#define TPM_ORDINAL_PCRREAD cpu_to_be32(21)
+#define READ_PCR_RESULT_SIZE 30
+static struct tpm_input_header pcrread_header = {
+	.tag = TPM_TAG_RQU_COMMAND,
+	.length = cpu_to_be32(14),
+	.ordinal = TPM_ORDINAL_PCRREAD
+};
 
 
-	kfree(data);
+int __tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
+{
+	int rc;
+	struct tpm_cmd_t cmd;
+
+	cmd.header.in = pcrread_header;
+	cmd.params.pcrread_in.pcr_idx = cpu_to_be32(pcr_idx);
+	BUILD_BUG_ON(cmd.header.in.length > READ_PCR_RESULT_SIZE);
+	rc = transmit_cmd(chip, &cmd, cmd.header.in.length,
+			  "attempting to read a pcr value");
+
+	if (rc == 0)
+		memcpy(res_buf, cmd.params.pcrread_out.pcr_result,
+		       TPM_DIGEST_SIZE);
 	return rc;
 	return rc;
 }
 }
-EXPORT_SYMBOL_GPL(tpm_show_temp_deactivated);
 
 
-static const u8 pcrread[] = {
-	0, 193,			/* TPM_TAG_RQU_COMMAND */
-	0, 0, 0, 14,		/* length */
-	0, 0, 0, 21,		/* TPM_ORD_PcrRead */
-	0, 0, 0, 0		/* PCR index */
+/**
+ * tpm_pcr_read - read a pcr value
+ * @chip_num: 	tpm idx # or ANY
+ * @pcr_idx:	pcr idx to retrieve
+ * @res_buf: 	TPM_PCR value
+ * 		size of res_buf is 20 bytes (or NULL if you don't care)
+ *
+ * The TPM driver should be built-in, but for whatever reason it
+ * isn't, protect against the chip disappearing, by incrementing
+ * the module usage count.
+ */
+int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf)
+{
+	struct tpm_chip *chip;
+	int rc;
+
+	chip = tpm_chip_find_get(chip_num);
+	if (chip == NULL)
+		return -ENODEV;
+	rc = __tpm_pcr_read(chip, pcr_idx, res_buf);
+	module_put(chip->dev->driver->owner);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_pcr_read);
+
+/**
+ * tpm_pcr_extend - extend pcr value with hash
+ * @chip_num: 	tpm idx # or AN&
+ * @pcr_idx:	pcr idx to extend
+ * @hash: 	hash value used to extend pcr value
+ *
+ * The TPM driver should be built-in, but for whatever reason it
+ * isn't, protect against the chip disappearing, by incrementing
+ * the module usage count.
+ */
+#define TPM_ORD_PCR_EXTEND cpu_to_be32(20)
+#define EXTEND_PCR_SIZE 34
+static struct tpm_input_header pcrextend_header = {
+	.tag = TPM_TAG_RQU_COMMAND,
+	.length = cpu_to_be32(34),
+	.ordinal = TPM_ORD_PCR_EXTEND
 };
 };
 
 
+int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
+{
+	struct tpm_cmd_t cmd;
+	int rc;
+	struct tpm_chip *chip;
+
+	chip = tpm_chip_find_get(chip_num);
+	if (chip == NULL)
+		return -ENODEV;
+
+	cmd.header.in = pcrextend_header;
+	BUILD_BUG_ON(be32_to_cpu(cmd.header.in.length) > EXTEND_PCR_SIZE);
+	cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx);
+	memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE);
+	rc = transmit_cmd(chip, &cmd, cmd.header.in.length,
+			  "attempting extend a PCR value");
+
+	module_put(chip->dev->driver->owner);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_pcr_extend);
+
 ssize_t tpm_show_pcrs(struct device *dev, struct device_attribute *attr,
 ssize_t tpm_show_pcrs(struct device *dev, struct device_attribute *attr,
 		      char *buf)
 		      char *buf)
 {
 {
-	u8 *data;
+	cap_t cap;
+	u8 digest[TPM_DIGEST_SIZE];
 	ssize_t rc;
 	ssize_t rc;
 	int i, j, num_pcrs;
 	int i, j, num_pcrs;
-	__be32 index;
 	char *str = buf;
 	char *str = buf;
-
 	struct tpm_chip *chip = dev_get_drvdata(dev);
 	struct tpm_chip *chip = dev_get_drvdata(dev);
-	if (chip == NULL)
-		return -ENODEV;
 
 
-	data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
-	if (!data)
-		return -ENOMEM;
-
-	memcpy(data, tpm_cap, sizeof(tpm_cap));
-	data[TPM_CAP_IDX] = TPM_CAP_PROP;
-	data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_PCR;
-
-	rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
+	rc = tpm_getcap(dev, TPM_CAP_PROP_PCR, &cap,
 			"attempting to determine the number of PCRS");
 			"attempting to determine the number of PCRS");
-	if (rc) {
-		kfree(data);
+	if (rc)
 		return 0;
 		return 0;
-	}
 
 
-	num_pcrs = be32_to_cpu(*((__be32 *) (data + 14)));
+	num_pcrs = be32_to_cpu(cap.num_pcrs);
 	for (i = 0; i < num_pcrs; i++) {
 	for (i = 0; i < num_pcrs; i++) {
-		memcpy(data, pcrread, sizeof(pcrread));
-		index = cpu_to_be32(i);
-		memcpy(data + 10, &index, 4);
-		rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
-				"attempting to read a PCR");
+		rc = __tpm_pcr_read(chip, i, digest);
 		if (rc)
 		if (rc)
-			goto out;
+			break;
 		str += sprintf(str, "PCR-%02d: ", i);
 		str += sprintf(str, "PCR-%02d: ", i);
 		for (j = 0; j < TPM_DIGEST_SIZE; j++)
 		for (j = 0; j < TPM_DIGEST_SIZE; j++)
-			str += sprintf(str, "%02X ", *(data + 10 + j));
+			str += sprintf(str, "%02X ", digest[j]);
 		str += sprintf(str, "\n");
 		str += sprintf(str, "\n");
 	}
 	}
-out:
-	kfree(data);
 	return str - buf;
 	return str - buf;
 }
 }
 EXPORT_SYMBOL_GPL(tpm_show_pcrs);
 EXPORT_SYMBOL_GPL(tpm_show_pcrs);
 
 
 #define  READ_PUBEK_RESULT_SIZE 314
 #define  READ_PUBEK_RESULT_SIZE 314
-static const u8 readpubek[] = {
-	0, 193,			/* TPM_TAG_RQU_COMMAND */
-	0, 0, 0, 30,		/* length */
-	0, 0, 0, 124,		/* TPM_ORD_ReadPubek */
+#define TPM_ORD_READPUBEK cpu_to_be32(124)
+struct tpm_input_header tpm_readpubek_header = {
+	.tag = TPM_TAG_RQU_COMMAND,
+	.length = cpu_to_be32(30),
+	.ordinal = TPM_ORD_READPUBEK
 };
 };
 
 
 ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr,
 ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr,
 		       char *buf)
 		       char *buf)
 {
 {
 	u8 *data;
 	u8 *data;
+	struct tpm_cmd_t tpm_cmd;
 	ssize_t err;
 	ssize_t err;
 	int i, rc;
 	int i, rc;
 	char *str = buf;
 	char *str = buf;
 
 
 	struct tpm_chip *chip = dev_get_drvdata(dev);
 	struct tpm_chip *chip = dev_get_drvdata(dev);
-	if (chip == NULL)
-		return -ENODEV;
 
 
-	data = kzalloc(READ_PUBEK_RESULT_SIZE, GFP_KERNEL);
-	if (!data)
-		return -ENOMEM;
-
-	memcpy(data, readpubek, sizeof(readpubek));
-
-	err = transmit_cmd(chip, data, READ_PUBEK_RESULT_SIZE,
+	tpm_cmd.header.in = tpm_readpubek_header;
+	err = transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE,
 			"attempting to read the PUBEK");
 			"attempting to read the PUBEK");
 	if (err)
 	if (err)
 		goto out;
 		goto out;
@@ -812,7 +836,7 @@ ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr,
 	   256 byte modulus
 	   256 byte modulus
 	   ignore checksum 20 bytes
 	   ignore checksum 20 bytes
 	 */
 	 */
-
+	data = tpm_cmd.params.readpubek_out_buffer;
 	str +=
 	str +=
 	    sprintf(str,
 	    sprintf(str,
 		    "Algorithm: %02X %02X %02X %02X\nEncscheme: %02X %02X\n"
 		    "Algorithm: %02X %02X %02X %02X\nEncscheme: %02X %02X\n"
@@ -832,65 +856,33 @@ ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr,
 	}
 	}
 out:
 out:
 	rc = str - buf;
 	rc = str - buf;
-	kfree(data);
 	return rc;
 	return rc;
 }
 }
 EXPORT_SYMBOL_GPL(tpm_show_pubek);
 EXPORT_SYMBOL_GPL(tpm_show_pubek);
 
 
-#define CAP_VERSION_1_1 6
-#define CAP_VERSION_1_2 0x1A
-#define CAP_VERSION_IDX 13
-static const u8 cap_version[] = {
-	0, 193,			/* TPM_TAG_RQU_COMMAND */
-	0, 0, 0, 18,		/* length */
-	0, 0, 0, 101,		/* TPM_ORD_GetCapability */
-	0, 0, 0, 0,
-	0, 0, 0, 0
-};
 
 
 ssize_t tpm_show_caps(struct device *dev, struct device_attribute *attr,
 ssize_t tpm_show_caps(struct device *dev, struct device_attribute *attr,
 		      char *buf)
 		      char *buf)
 {
 {
-	u8 *data;
+	cap_t cap;
 	ssize_t rc;
 	ssize_t rc;
 	char *str = buf;
 	char *str = buf;
 
 
-	struct tpm_chip *chip = dev_get_drvdata(dev);
-	if (chip == NULL)
-		return -ENODEV;
-
-	data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
-	if (!data)
-		return -ENOMEM;
-
-	memcpy(data, tpm_cap, sizeof(tpm_cap));
-	data[TPM_CAP_IDX] = TPM_CAP_PROP;
-	data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_MANUFACTURER;
-
-	rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
+	rc = tpm_getcap(dev, TPM_CAP_PROP_MANUFACTURER, &cap,
 			"attempting to determine the manufacturer");
 			"attempting to determine the manufacturer");
-	if (rc) {
-		kfree(data);
+	if (rc)
 		return 0;
 		return 0;
-	}
-
 	str += sprintf(str, "Manufacturer: 0x%x\n",
 	str += sprintf(str, "Manufacturer: 0x%x\n",
-		       be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX))));
+		       be32_to_cpu(cap.manufacturer_id));
 
 
-	memcpy(data, cap_version, sizeof(cap_version));
-	data[CAP_VERSION_IDX] = CAP_VERSION_1_1;
-	rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
-			"attempting to determine the 1.1 version");
+	rc = tpm_getcap(dev, CAP_VERSION_1_1, &cap,
+		        "attempting to determine the 1.1 version");
 	if (rc)
 	if (rc)
-		goto out;
-
+		return 0;
 	str += sprintf(str,
 	str += sprintf(str,
 		       "TCG version: %d.%d\nFirmware version: %d.%d\n",
 		       "TCG version: %d.%d\nFirmware version: %d.%d\n",
-		       (int) data[14], (int) data[15], (int) data[16],
-		       (int) data[17]);
-
-out:
-	kfree(data);
+		       cap.tpm_version.Major, cap.tpm_version.Minor,
+		       cap.tpm_version.revMajor, cap.tpm_version.revMinor);
 	return str - buf;
 	return str - buf;
 }
 }
 EXPORT_SYMBOL_GPL(tpm_show_caps);
 EXPORT_SYMBOL_GPL(tpm_show_caps);
@@ -898,51 +890,25 @@ EXPORT_SYMBOL_GPL(tpm_show_caps);
 ssize_t tpm_show_caps_1_2(struct device * dev,
 ssize_t tpm_show_caps_1_2(struct device * dev,
 			  struct device_attribute * attr, char *buf)
 			  struct device_attribute * attr, char *buf)
 {
 {
-	u8 *data;
-	ssize_t len;
+	cap_t cap;
+	ssize_t rc;
 	char *str = buf;
 	char *str = buf;
 
 
-	struct tpm_chip *chip = dev_get_drvdata(dev);
-	if (chip == NULL)
-		return -ENODEV;
-
-	data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
-	if (!data)
-		return -ENOMEM;
-
-	memcpy(data, tpm_cap, sizeof(tpm_cap));
-	data[TPM_CAP_IDX] = TPM_CAP_PROP;
-	data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_MANUFACTURER;
-
-	len = tpm_transmit(chip, data, TPM_INTERNAL_RESULT_SIZE);
-	if (len <= TPM_ERROR_SIZE) {
-		dev_dbg(chip->dev, "A TPM error (%d) occurred "
-			"attempting to determine the manufacturer\n",
-			be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX))));
-		kfree(data);
+	rc = tpm_getcap(dev, TPM_CAP_PROP_MANUFACTURER, &cap,
+			"attempting to determine the manufacturer");
+	if (rc)
 		return 0;
 		return 0;
-	}
-
 	str += sprintf(str, "Manufacturer: 0x%x\n",
 	str += sprintf(str, "Manufacturer: 0x%x\n",
-		       be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX))));
-
-	memcpy(data, cap_version, sizeof(cap_version));
-	data[CAP_VERSION_IDX] = CAP_VERSION_1_2;
-
-	len = tpm_transmit(chip, data, TPM_INTERNAL_RESULT_SIZE);
-	if (len <= TPM_ERROR_SIZE) {
-		dev_err(chip->dev, "A TPM error (%d) occurred "
-			"attempting to determine the 1.2 version\n",
-			be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX))));
-		goto out;
-	}
+		       be32_to_cpu(cap.manufacturer_id));
+	rc = tpm_getcap(dev, CAP_VERSION_1_2, &cap,
+			 "attempting to determine the 1.2 version");
+	if (rc)
+		return 0;
 	str += sprintf(str,
 	str += sprintf(str,
 		       "TCG version: %d.%d\nFirmware version: %d.%d\n",
 		       "TCG version: %d.%d\nFirmware version: %d.%d\n",
-		       (int) data[16], (int) data[17], (int) data[18],
-		       (int) data[19]);
-
-out:
-	kfree(data);
+		       cap.tpm_version_1_2.Major, cap.tpm_version_1_2.Minor,
+		       cap.tpm_version_1_2.revMajor,
+		       cap.tpm_version_1_2.revMinor);
 	return str - buf;
 	return str - buf;
 }
 }
 EXPORT_SYMBOL_GPL(tpm_show_caps_1_2);
 EXPORT_SYMBOL_GPL(tpm_show_caps_1_2);

+ 142 - 0
drivers/char/tpm/tpm.h

@@ -26,6 +26,7 @@
 #include <linux/miscdevice.h>
 #include <linux/miscdevice.h>
 #include <linux/platform_device.h>
 #include <linux/platform_device.h>
 #include <linux/io.h>
 #include <linux/io.h>
+#include <linux/tpm.h>
 
 
 enum tpm_timeout {
 enum tpm_timeout {
 	TPM_TIMEOUT = 5,	/* msecs */
 	TPM_TIMEOUT = 5,	/* msecs */
@@ -123,6 +124,147 @@ static inline void tpm_write_index(int base, int index, int value)
 	outb(index, base);
 	outb(index, base);
 	outb(value & 0xFF, base+1);
 	outb(value & 0xFF, base+1);
 }
 }
+struct tpm_input_header {
+	__be16	tag;
+	__be32	length;
+	__be32	ordinal;
+}__attribute__((packed));
+
+struct tpm_output_header {
+	__be16	tag;
+	__be32	length;
+	__be32	return_code;
+}__attribute__((packed));
+
+struct	stclear_flags_t {
+	__be16	tag;
+	u8	deactivated;
+	u8	disableForceClear;
+	u8	physicalPresence;
+	u8	physicalPresenceLock;
+	u8	bGlobalLock;
+}__attribute__((packed));
+
+struct	tpm_version_t {
+	u8	Major;
+	u8	Minor;
+	u8	revMajor;
+	u8	revMinor;
+}__attribute__((packed));
+
+struct	tpm_version_1_2_t {
+	__be16	tag;
+	u8	Major;
+	u8	Minor;
+	u8	revMajor;
+	u8	revMinor;
+}__attribute__((packed));
+
+struct	timeout_t {
+	__be32	a;
+	__be32	b;
+	__be32	c;
+	__be32	d;
+}__attribute__((packed));
+
+struct duration_t {
+	__be32	tpm_short;
+	__be32	tpm_medium;
+	__be32	tpm_long;
+}__attribute__((packed));
+
+struct permanent_flags_t {
+	__be16	tag;
+	u8	disable;
+	u8	ownership;
+	u8	deactivated;
+	u8	readPubek;
+	u8	disableOwnerClear;
+	u8	allowMaintenance;
+	u8	physicalPresenceLifetimeLock;
+	u8	physicalPresenceHWEnable;
+	u8	physicalPresenceCMDEnable;
+	u8	CEKPUsed;
+	u8	TPMpost;
+	u8	TPMpostLock;
+	u8	FIPS;
+	u8	operator;
+	u8	enableRevokeEK;
+	u8	nvLocked;
+	u8	readSRKPub;
+	u8	tpmEstablished;
+	u8	maintenanceDone;
+	u8	disableFullDALogicInfo;
+}__attribute__((packed));
+
+typedef union {
+	struct	permanent_flags_t perm_flags;
+	struct	stclear_flags_t	stclear_flags;
+	bool	owned;
+	__be32	num_pcrs;
+	struct	tpm_version_t	tpm_version;
+	struct	tpm_version_1_2_t tpm_version_1_2;
+	__be32	manufacturer_id;
+	struct timeout_t  timeout;
+	struct duration_t duration;
+} cap_t;
+
+struct	tpm_getcap_params_in {
+	__be32	cap;
+	__be32	subcap_size;
+	__be32	subcap;
+}__attribute__((packed));
+
+struct	tpm_getcap_params_out {
+	__be32	cap_size;
+	cap_t	cap;
+}__attribute__((packed));
+
+struct	tpm_readpubek_params_out {
+	u8	algorithm[4];
+	u8	encscheme[2];
+	u8	sigscheme[2];
+	u8	parameters[12]; /*assuming RSA*/
+	__be32	keysize;
+	u8	modulus[256];
+	u8	checksum[20];
+}__attribute__((packed));
+
+typedef union {
+	struct	tpm_input_header in;
+	struct	tpm_output_header out;
+} tpm_cmd_header;
+
+#define TPM_DIGEST_SIZE 20
+struct tpm_pcrread_out {
+	u8	pcr_result[TPM_DIGEST_SIZE];
+}__attribute__((packed));
+
+struct tpm_pcrread_in {
+	__be32	pcr_idx;
+}__attribute__((packed));
+
+struct tpm_pcrextend_in {
+	__be32	pcr_idx;
+	u8	hash[TPM_DIGEST_SIZE];
+}__attribute__((packed));
+
+typedef union {
+	struct	tpm_getcap_params_out getcap_out;
+	struct	tpm_readpubek_params_out readpubek_out;
+	u8	readpubek_out_buffer[sizeof(struct tpm_readpubek_params_out)];
+	struct	tpm_getcap_params_in getcap_in;
+	struct	tpm_pcrread_in	pcrread_in;
+	struct	tpm_pcrread_out	pcrread_out;
+	struct	tpm_pcrextend_in pcrextend_in;
+} tpm_cmd_params;
+
+struct tpm_cmd_t {
+	tpm_cmd_header	header;
+	tpm_cmd_params	params;
+}__attribute__((packed));
+
+ssize_t	tpm_getcap(struct device *, __be32, cap_t *, const char *);
 
 
 extern void tpm_get_timeouts(struct tpm_chip *);
 extern void tpm_get_timeouts(struct tpm_chip *);
 extern void tpm_gen_interrupt(struct tpm_chip *);
 extern void tpm_gen_interrupt(struct tpm_chip *);

+ 3 - 0
fs/compat.c

@@ -1402,6 +1402,7 @@ int compat_do_execve(char * filename,
 	retval = mutex_lock_interruptible(&current->cred_exec_mutex);
 	retval = mutex_lock_interruptible(&current->cred_exec_mutex);
 	if (retval < 0)
 	if (retval < 0)
 		goto out_free;
 		goto out_free;
+	current->in_execve = 1;
 
 
 	retval = -ENOMEM;
 	retval = -ENOMEM;
 	bprm->cred = prepare_exec_creds();
 	bprm->cred = prepare_exec_creds();
@@ -1454,6 +1455,7 @@ int compat_do_execve(char * filename,
 		goto out;
 		goto out;
 
 
 	/* execve succeeded */
 	/* execve succeeded */
+	current->in_execve = 0;
 	mutex_unlock(&current->cred_exec_mutex);
 	mutex_unlock(&current->cred_exec_mutex);
 	acct_update_integrals(current);
 	acct_update_integrals(current);
 	free_bprm(bprm);
 	free_bprm(bprm);
@@ -1470,6 +1472,7 @@ out_file:
 	}
 	}
 
 
 out_unlock:
 out_unlock:
+	current->in_execve = 0;
 	mutex_unlock(&current->cred_exec_mutex);
 	mutex_unlock(&current->cred_exec_mutex);
 
 
 out_free:
 out_free:

+ 13 - 0
fs/exec.c

@@ -45,6 +45,7 @@
 #include <linux/proc_fs.h>
 #include <linux/proc_fs.h>
 #include <linux/mount.h>
 #include <linux/mount.h>
 #include <linux/security.h>
 #include <linux/security.h>
+#include <linux/ima.h>
 #include <linux/syscalls.h>
 #include <linux/syscalls.h>
 #include <linux/tsacct_kern.h>
 #include <linux/tsacct_kern.h>
 #include <linux/cn_proc.h>
 #include <linux/cn_proc.h>
@@ -127,6 +128,9 @@ SYSCALL_DEFINE1(uselib, const char __user *, library)
 				 MAY_READ | MAY_EXEC | MAY_OPEN);
 				 MAY_READ | MAY_EXEC | MAY_OPEN);
 	if (error)
 	if (error)
 		goto exit;
 		goto exit;
+	error = ima_path_check(&nd.path, MAY_READ | MAY_EXEC | MAY_OPEN);
+	if (error)
+		goto exit;
 
 
 	file = nameidata_to_filp(&nd, O_RDONLY|O_LARGEFILE);
 	file = nameidata_to_filp(&nd, O_RDONLY|O_LARGEFILE);
 	error = PTR_ERR(file);
 	error = PTR_ERR(file);
@@ -672,6 +676,9 @@ struct file *open_exec(const char *name)
 		goto out_path_put;
 		goto out_path_put;
 
 
 	err = inode_permission(nd.path.dentry->d_inode, MAY_EXEC | MAY_OPEN);
 	err = inode_permission(nd.path.dentry->d_inode, MAY_EXEC | MAY_OPEN);
+	if (err)
+		goto out_path_put;
+	err = ima_path_check(&nd.path, MAY_EXEC | MAY_OPEN);
 	if (err)
 	if (err)
 		goto out_path_put;
 		goto out_path_put;
 
 
@@ -1182,6 +1189,9 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
 	struct linux_binfmt *fmt;
 	struct linux_binfmt *fmt;
 
 
 	retval = security_bprm_check(bprm);
 	retval = security_bprm_check(bprm);
+	if (retval)
+		return retval;
+	retval = ima_bprm_check(bprm);
 	if (retval)
 	if (retval)
 		return retval;
 		return retval;
 
 
@@ -1284,6 +1294,7 @@ int do_execve(char * filename,
 	retval = mutex_lock_interruptible(&current->cred_exec_mutex);
 	retval = mutex_lock_interruptible(&current->cred_exec_mutex);
 	if (retval < 0)
 	if (retval < 0)
 		goto out_free;
 		goto out_free;
+	current->in_execve = 1;
 
 
 	retval = -ENOMEM;
 	retval = -ENOMEM;
 	bprm->cred = prepare_exec_creds();
 	bprm->cred = prepare_exec_creds();
@@ -1337,6 +1348,7 @@ int do_execve(char * filename,
 		goto out;
 		goto out;
 
 
 	/* execve succeeded */
 	/* execve succeeded */
+	current->in_execve = 0;
 	mutex_unlock(&current->cred_exec_mutex);
 	mutex_unlock(&current->cred_exec_mutex);
 	acct_update_integrals(current);
 	acct_update_integrals(current);
 	free_bprm(bprm);
 	free_bprm(bprm);
@@ -1355,6 +1367,7 @@ out_file:
 	}
 	}
 
 
 out_unlock:
 out_unlock:
+	current->in_execve = 0;
 	mutex_unlock(&current->cred_exec_mutex);
 	mutex_unlock(&current->cred_exec_mutex);
 
 
 out_free:
 out_free:

+ 2 - 0
fs/file_table.c

@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/fs.h>
 #include <linux/fs.h>
 #include <linux/security.h>
 #include <linux/security.h>
+#include <linux/ima.h>
 #include <linux/eventpoll.h>
 #include <linux/eventpoll.h>
 #include <linux/rcupdate.h>
 #include <linux/rcupdate.h>
 #include <linux/mount.h>
 #include <linux/mount.h>
@@ -279,6 +280,7 @@ void __fput(struct file *file)
 	if (file->f_op && file->f_op->release)
 	if (file->f_op && file->f_op->release)
 		file->f_op->release(inode, file);
 		file->f_op->release(inode, file);
 	security_file_free(file);
 	security_file_free(file);
+	ima_file_free(file);
 	if (unlikely(S_ISCHR(inode->i_mode) && inode->i_cdev != NULL))
 	if (unlikely(S_ISCHR(inode->i_mode) && inode->i_cdev != NULL))
 		cdev_put(inode->i_cdev);
 		cdev_put(inode->i_cdev);
 	fops_put(file->f_op);
 	fops_put(file->f_op);

+ 17 - 7
fs/inode.c

@@ -17,6 +17,7 @@
 #include <linux/hash.h>
 #include <linux/hash.h>
 #include <linux/swap.h>
 #include <linux/swap.h>
 #include <linux/security.h>
 #include <linux/security.h>
+#include <linux/ima.h>
 #include <linux/pagemap.h>
 #include <linux/pagemap.h>
 #include <linux/cdev.h>
 #include <linux/cdev.h>
 #include <linux/bootmem.h>
 #include <linux/bootmem.h>
@@ -147,13 +148,13 @@ struct inode *inode_init_always(struct super_block *sb, struct inode *inode)
 	inode->i_cdev = NULL;
 	inode->i_cdev = NULL;
 	inode->i_rdev = 0;
 	inode->i_rdev = 0;
 	inode->dirtied_when = 0;
 	inode->dirtied_when = 0;
-	if (security_inode_alloc(inode)) {
-		if (inode->i_sb->s_op->destroy_inode)
-			inode->i_sb->s_op->destroy_inode(inode);
-		else
-			kmem_cache_free(inode_cachep, (inode));
-		return NULL;
-	}
+
+	if (security_inode_alloc(inode))
+		goto out_free_inode;
+
+	/* allocate and initialize an i_integrity */
+	if (ima_inode_alloc(inode))
+		goto out_free_security;
 
 
 	spin_lock_init(&inode->i_lock);
 	spin_lock_init(&inode->i_lock);
 	lockdep_set_class(&inode->i_lock, &sb->s_type->i_lock_key);
 	lockdep_set_class(&inode->i_lock, &sb->s_type->i_lock_key);
@@ -189,6 +190,15 @@ struct inode *inode_init_always(struct super_block *sb, struct inode *inode)
 	inode->i_mapping = mapping;
 	inode->i_mapping = mapping;
 
 
 	return inode;
 	return inode;
+
+out_free_security:
+	security_inode_free(inode);
+out_free_inode:
+	if (inode->i_sb->s_op->destroy_inode)
+		inode->i_sb->s_op->destroy_inode(inode);
+	else
+		kmem_cache_free(inode_cachep, (inode));
+	return NULL;
 }
 }
 EXPORT_SYMBOL(inode_init_always);
 EXPORT_SYMBOL(inode_init_always);
 
 

+ 8 - 0
fs/namei.c

@@ -24,6 +24,7 @@
 #include <linux/fsnotify.h>
 #include <linux/fsnotify.h>
 #include <linux/personality.h>
 #include <linux/personality.h>
 #include <linux/security.h>
 #include <linux/security.h>
+#include <linux/ima.h>
 #include <linux/syscalls.h>
 #include <linux/syscalls.h>
 #include <linux/mount.h>
 #include <linux/mount.h>
 #include <linux/audit.h>
 #include <linux/audit.h>
@@ -850,6 +851,8 @@ static int __link_path_walk(const char *name, struct nameidata *nd)
 		if (err == -EAGAIN)
 		if (err == -EAGAIN)
 			err = inode_permission(nd->path.dentry->d_inode,
 			err = inode_permission(nd->path.dentry->d_inode,
 					       MAY_EXEC);
 					       MAY_EXEC);
+		if (!err)
+			err = ima_path_check(&nd->path, MAY_EXEC);
  		if (err)
  		if (err)
 			break;
 			break;
 
 
@@ -1509,6 +1512,11 @@ int may_open(struct path *path, int acc_mode, int flag)
 	error = inode_permission(inode, acc_mode);
 	error = inode_permission(inode, acc_mode);
 	if (error)
 	if (error)
 		return error;
 		return error;
+
+	error = ima_path_check(path,
+			       acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC));
+	if (error)
+		return error;
 	/*
 	/*
 	 * An append-only file must be opened in append mode for writing.
 	 * An append-only file must be opened in append mode for writing.
 	 */
 	 */

+ 8 - 1
include/linux/audit.h

@@ -36,7 +36,8 @@
  * 1500 - 1599 kernel LSPP events
  * 1500 - 1599 kernel LSPP events
  * 1600 - 1699 kernel crypto events
  * 1600 - 1699 kernel crypto events
  * 1700 - 1799 kernel anomaly records
  * 1700 - 1799 kernel anomaly records
- * 1800 - 1999 future kernel use (maybe integrity labels and related events)
+ * 1800 - 1899 kernel integrity events
+ * 1900 - 1999 future kernel use
  * 2000 is for otherwise unclassified kernel audit messages (legacy)
  * 2000 is for otherwise unclassified kernel audit messages (legacy)
  * 2001 - 2099 unused (kernel)
  * 2001 - 2099 unused (kernel)
  * 2100 - 2199 user space anomaly records
  * 2100 - 2199 user space anomaly records
@@ -125,6 +126,12 @@
 #define AUDIT_LAST_KERN_ANOM_MSG    1799
 #define AUDIT_LAST_KERN_ANOM_MSG    1799
 #define AUDIT_ANOM_PROMISCUOUS      1700 /* Device changed promiscuous mode */
 #define AUDIT_ANOM_PROMISCUOUS      1700 /* Device changed promiscuous mode */
 #define AUDIT_ANOM_ABEND            1701 /* Process ended abnormally */
 #define AUDIT_ANOM_ABEND            1701 /* Process ended abnormally */
+#define AUDIT_INTEGRITY_DATA	    1800 /* Data integrity verification */
+#define AUDIT_INTEGRITY_METADATA    1801 /* Metadata integrity verification */
+#define AUDIT_INTEGRITY_STATUS	    1802 /* Integrity enable status */
+#define AUDIT_INTEGRITY_HASH	    1803 /* Integrity HASH type */
+#define AUDIT_INTEGRITY_PCR	    1804 /* PCR invalidation msgs */
+#define AUDIT_INTEGRITY_RULE	    1805 /* policy rule */
 
 
 #define AUDIT_KERNEL		2000	/* Asynchronous audit record. NOT A REQUEST. */
 #define AUDIT_KERNEL		2000	/* Asynchronous audit record. NOT A REQUEST. */
 
 

+ 61 - 0
include/linux/ima.h

@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#ifndef _LINUX_IMA_H
+#define _LINUX_IMA_H
+
+#include <linux/fs.h>
+struct linux_binprm;
+
+#ifdef CONFIG_IMA
+extern int ima_bprm_check(struct linux_binprm *bprm);
+extern int ima_inode_alloc(struct inode *inode);
+extern void ima_inode_free(struct inode *inode);
+extern int ima_path_check(struct path *path, int mask);
+extern void ima_file_free(struct file *file);
+extern int ima_file_mmap(struct file *file, unsigned long prot);
+extern void ima_shm_check(struct file *file);
+
+#else
+static inline int ima_bprm_check(struct linux_binprm *bprm)
+{
+	return 0;
+}
+
+static inline int ima_inode_alloc(struct inode *inode)
+{
+	return 0;
+}
+
+static inline void ima_inode_free(struct inode *inode)
+{
+	return;
+}
+
+static inline int ima_path_check(struct path *path, int mask)
+{
+	return 0;
+}
+
+static inline void ima_file_free(struct file *file)
+{
+	return;
+}
+
+static inline int ima_file_mmap(struct file *file, unsigned long prot)
+{
+	return 0;
+}
+
+static inline void ima_shm_check(struct file *file)
+{
+	return;
+}
+#endif /* CONFIG_IMA_H */
+#endif /* _LINUX_IMA_H */

+ 2 - 0
include/linux/sched.h

@@ -1175,6 +1175,8 @@ struct task_struct {
 	/* ??? */
 	/* ??? */
 	unsigned int personality;
 	unsigned int personality;
 	unsigned did_exec:1;
 	unsigned did_exec:1;
+	unsigned in_execve:1;	/* Tell the LSMs that the process is doing an
+				 * execve */
 	pid_t pid;
 	pid_t pid;
 	pid_t tgid;
 	pid_t tgid;
 
 

+ 35 - 0
include/linux/tpm.h

@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2004,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ * Debora Velarde <dvelarde@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org
+ *
+ * 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.
+ *
+ */
+#ifndef __LINUX_TPM_H__
+#define __LINUX_TPM_H__
+
+/*
+ * Chip num is this value or a valid tpm idx
+ */
+#define	TPM_ANY_NUM 0xFFFF
+
+#if defined(CONFIG_TCG_TPM)
+
+extern int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf);
+extern int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash);
+#endif
+#endif

+ 3 - 0
ipc/shm.c

@@ -39,6 +39,7 @@
 #include <linux/nsproxy.h>
 #include <linux/nsproxy.h>
 #include <linux/mount.h>
 #include <linux/mount.h>
 #include <linux/ipc_namespace.h>
 #include <linux/ipc_namespace.h>
+#include <linux/ima.h>
 
 
 #include <asm/uaccess.h>
 #include <asm/uaccess.h>
 
 
@@ -383,6 +384,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
 	error = PTR_ERR(file);
 	error = PTR_ERR(file);
 	if (IS_ERR(file))
 	if (IS_ERR(file))
 		goto no_file;
 		goto no_file;
+	ima_shm_check(file);
 
 
 	id = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni);
 	id = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni);
 	if (id < 0) {
 	if (id < 0) {
@@ -887,6 +889,7 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr)
 	file = alloc_file(path.mnt, path.dentry, f_mode, &shm_file_operations);
 	file = alloc_file(path.mnt, path.dentry, f_mode, &shm_file_operations);
 	if (!file)
 	if (!file)
 		goto out_free;
 		goto out_free;
+	ima_shm_check(file);
 
 
 	file->private_data = sfd;
 	file->private_data = sfd;
 	file->f_mapping = shp->shm_file->f_mapping;
 	file->f_mapping = shp->shm_file->f_mapping;

+ 1 - 1
kernel/user.c

@@ -20,7 +20,7 @@
 
 
 struct user_namespace init_user_ns = {
 struct user_namespace init_user_ns = {
 	.kref = {
 	.kref = {
-		.refcount	= ATOMIC_INIT(1),
+		.refcount	= ATOMIC_INIT(2),
 	},
 	},
 	.creator = &root_user,
 	.creator = &root_user,
 };
 };

+ 4 - 0
mm/mmap.c

@@ -20,6 +20,7 @@
 #include <linux/fs.h>
 #include <linux/fs.h>
 #include <linux/personality.h>
 #include <linux/personality.h>
 #include <linux/security.h>
 #include <linux/security.h>
+#include <linux/ima.h>
 #include <linux/hugetlb.h>
 #include <linux/hugetlb.h>
 #include <linux/profile.h>
 #include <linux/profile.h>
 #include <linux/module.h>
 #include <linux/module.h>
@@ -1047,6 +1048,9 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
 	}
 	}
 
 
 	error = security_file_mmap(file, reqprot, prot, flags, addr, 0);
 	error = security_file_mmap(file, reqprot, prot, flags, addr, 0);
+	if (error)
+		return error;
+	error = ima_file_mmap(file, prot);
 	if (error)
 	if (error)
 		return error;
 		return error;
 
 

+ 2 - 0
mm/shmem.c

@@ -28,6 +28,7 @@
 #include <linux/mm.h>
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/swap.h>
 #include <linux/swap.h>
+#include <linux/ima.h>
 
 
 static struct vfsmount *shm_mnt;
 static struct vfsmount *shm_mnt;
 
 
@@ -2665,6 +2666,7 @@ int shmem_zero_setup(struct vm_area_struct *vma)
 	if (IS_ERR(file))
 	if (IS_ERR(file))
 		return PTR_ERR(file);
 		return PTR_ERR(file);
 
 
+	ima_shm_check(file);
 	if (vma->vm_file)
 	if (vma->vm_file)
 		fput(vma->vm_file);
 		fput(vma->vm_file);
 	vma->vm_file = file;
 	vma->vm_file = file;

+ 5 - 1
security/Kconfig

@@ -55,7 +55,8 @@ config SECURITYFS
 	bool "Enable the securityfs filesystem"
 	bool "Enable the securityfs filesystem"
 	help
 	help
 	  This will build the securityfs filesystem.  It is currently used by
 	  This will build the securityfs filesystem.  It is currently used by
-	  the TPM bios character driver.  It is not used by SELinux or SMACK.
+	  the TPM bios character driver and IMA, an integrity provider.  It is
+	  not used by SELinux or SMACK.
 
 
 	  If you are unsure how to answer this question, answer N.
 	  If you are unsure how to answer this question, answer N.
 
 
@@ -134,6 +135,9 @@ config SECURITY_DEFAULT_MMAP_MIN_ADDR
 
 
 source security/selinux/Kconfig
 source security/selinux/Kconfig
 source security/smack/Kconfig
 source security/smack/Kconfig
+source security/tomoyo/Kconfig
+
+source security/integrity/ima/Kconfig
 
 
 endmenu
 endmenu
 
 

+ 6 - 0
security/Makefile

@@ -5,6 +5,7 @@
 obj-$(CONFIG_KEYS)			+= keys/
 obj-$(CONFIG_KEYS)			+= keys/
 subdir-$(CONFIG_SECURITY_SELINUX)	+= selinux
 subdir-$(CONFIG_SECURITY_SELINUX)	+= selinux
 subdir-$(CONFIG_SECURITY_SMACK)		+= smack
 subdir-$(CONFIG_SECURITY_SMACK)		+= smack
+subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo
 
 
 # always enable default capabilities
 # always enable default capabilities
 obj-y		+= commoncap.o
 obj-y		+= commoncap.o
@@ -15,5 +16,10 @@ obj-$(CONFIG_SECURITYFS)		+= inode.o
 # Must precede capability.o in order to stack properly.
 # Must precede capability.o in order to stack properly.
 obj-$(CONFIG_SECURITY_SELINUX)		+= selinux/built-in.o
 obj-$(CONFIG_SECURITY_SELINUX)		+= selinux/built-in.o
 obj-$(CONFIG_SECURITY_SMACK)		+= smack/built-in.o
 obj-$(CONFIG_SECURITY_SMACK)		+= smack/built-in.o
+obj-$(CONFIG_SECURITY_TOMOYO)		+= tomoyo/built-in.o
 obj-$(CONFIG_SECURITY_ROOTPLUG)		+= root_plug.o
 obj-$(CONFIG_SECURITY_ROOTPLUG)		+= root_plug.o
 obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.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

+ 3 - 4
security/inode.c

@@ -202,12 +202,11 @@ static int create_by_name(const char *name, mode_t mode,
  * This function returns a pointer to a dentry if it succeeds.  This
  * This function returns a pointer to a dentry if it succeeds.  This
  * pointer must be passed to the securityfs_remove() function when the file is
  * pointer must be passed to the securityfs_remove() function when the file is
  * to be removed (no automatic cleanup happens if your module is unloaded,
  * to be removed (no automatic cleanup happens if your module is unloaded,
- * you are responsible here).  If an error occurs, %NULL is returned.
+ * you are responsible here).  If an error occurs, the function will return
+ * the erorr value (via ERR_PTR).
  *
  *
  * If securityfs is not enabled in the kernel, the value %-ENODEV is
  * If securityfs is not enabled in the kernel, the value %-ENODEV is
- * returned.  It is not wise to check for this value, but rather, check for
- * %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
- * code.
+ * returned.
  */
  */
 struct dentry *securityfs_create_file(const char *name, mode_t mode,
 struct dentry *securityfs_create_file(const char *name, mode_t mode,
 				   struct dentry *parent, void *data,
 				   struct dentry *parent, void *data,

+ 55 - 0
security/integrity/ima/Kconfig

@@ -0,0 +1,55 @@
+# IBM Integrity Measurement Architecture
+#
+config IMA
+	bool "Integrity Measurement Architecture(IMA)"
+	depends on ACPI
+	select SECURITYFS
+	select CRYPTO
+	select CRYPTO_HMAC
+	select CRYPTO_MD5
+	select CRYPTO_SHA1
+	select TCG_TPM
+	select TCG_TIS
+	help
+	  The Trusted Computing Group(TCG) runtime Integrity
+	  Measurement Architecture(IMA) maintains a list of hash
+	  values of executables and other sensitive system files,
+	  as they are read or executed. If an attacker manages
+	  to change the contents of an important system file
+	  being measured, we can tell.
+
+	  If your system has a TPM chip, then IMA also maintains
+	  an aggregate integrity value over this list inside the
+	  TPM hardware, so that the TPM can prove to a third party
+	  whether or not critical system files have been modified.
+	  Read <http://www.usenix.org/events/sec04/tech/sailer.html>
+	  to learn more about IMA.
+	  If unsure, say N.
+
+config IMA_MEASURE_PCR_IDX
+	int
+	depends on IMA
+	range 8 14
+	default 10
+	help
+	  IMA_MEASURE_PCR_IDX determines the TPM PCR register index
+	  that IMA uses to maintain the integrity aggregate of the
+	  measurement list.  If unsure, use the default 10.
+
+config IMA_AUDIT
+	bool
+	depends on IMA
+	default y
+	help
+	  This option adds a kernel parameter 'ima_audit', which
+	  allows informational auditing messages to be enabled
+	  at boot.  If this option is selected, informational integrity
+	  auditing messages can be enabled with 'ima_audit=1' on
+	  the kernel command line.
+
+config IMA_LSM_RULES
+	bool
+	depends on IMA && AUDIT && (SECURITY_SELINUX || SECURITY_SMACK)
+	default y
+	help
+	  Disabling this option will disregard LSM based policy rules.

+ 9 - 0
security/integrity/ima/Makefile

@@ -0,0 +1,9 @@
+#
+# Makefile for building Trusted Computing Group's(TCG) runtime Integrity
+# Measurement Architecture(IMA).
+#
+
+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

+ 166 - 0
security/integrity/ima/ima.h

@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * 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.h
+ *	internal Integrity Measurement Architecture (IMA) definitions
+ */
+
+#ifndef __LINUX_IMA_H
+#define __LINUX_IMA_H
+
+#include <linux/types.h>
+#include <linux/crypto.h>
+#include <linux/security.h>
+#include <linux/hash.h>
+#include <linux/tpm.h>
+#include <linux/audit.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_EVENT_NAME_LEN_MAX	255
+
+#define IMA_HASH_BITS 9
+#define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS)
+
+/* set during initialization */
+extern int ima_initialized;
+extern int ima_used_chip;
+extern char *ima_hash;
+
+/* IMA inode template definition */
+struct ima_template_data {
+	u8 digest[IMA_DIGEST_SIZE];	/* sha1/md5 measurement hash */
+	char file_name[IMA_EVENT_NAME_LEN_MAX + 1];	/* name + \0 */
+};
+
+struct ima_template_entry {
+	u8 digest[IMA_DIGEST_SIZE];	/* sha1 or md5 measurement hash */
+	const char *template_name;
+	int template_len;
+	struct ima_template_data template;
+};
+
+struct ima_queue_entry {
+	struct hlist_node hnext;	/* place in hash collision list */
+	struct list_head later;		/* place in ima_measurements list */
+	struct ima_template_entry *entry;
+};
+extern struct list_head ima_measurements;	/* list of all measurements */
+
+/* declarations */
+void integrity_audit_msg(int audit_msgno, struct inode *inode,
+			 const unsigned char *fname, const char *op,
+			 const char *cause, int result, int info);
+
+/* Internal IMA function definitions */
+void ima_iintcache_init(void);
+int ima_init(void);
+void ima_cleanup(void);
+int ima_fs_init(void);
+void ima_fs_cleanup(void);
+int ima_add_template_entry(struct ima_template_entry *entry, int violation,
+			   const char *op, struct inode *inode);
+int ima_calc_hash(struct file *file, char *digest);
+int ima_calc_template_hash(int template_len, void *template, char *digest);
+int ima_calc_boot_aggregate(char *digest);
+void ima_add_violation(struct inode *inode, const unsigned char *filename,
+		       const char *op, const char *cause);
+
+/*
+ * used to protect h_table and sha_table
+ */
+extern spinlock_t ima_queue_lock;
+
+struct ima_h_table {
+	atomic_long_t len;	/* number of stored measurements in the list */
+	atomic_long_t violations;
+	struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE];
+};
+extern struct ima_h_table ima_htable;
+
+static inline unsigned long ima_hash_key(u8 *digest)
+{
+	return hash_long(*digest, IMA_HASH_BITS);
+}
+
+/* iint cache flags */
+#define IMA_MEASURED		1
+#define IMA_IINT_DUMP_STACK	512
+
+/* integrity data associated with an inode */
+struct ima_iint_cache {
+	u64 version;		/* track inode changes */
+	unsigned long flags;
+	u8 digest[IMA_DIGEST_SIZE];
+	struct mutex mutex;	/* protects: version, flags, digest */
+	long readcount;		/* measured files readcount */
+	long writecount;	/* measured files writecount */
+	long opencount;		/* opens reference count */
+	struct kref refcount;	/* ima_iint_cache reference count */
+	struct rcu_head rcu;
+};
+
+/* LIM API function definitions */
+int ima_must_measure(struct ima_iint_cache *iint, 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,
+			   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);
+
+/* radix 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_get(struct inode *inode);
+struct ima_iint_cache *ima_iint_find_insert_get(struct inode *inode);
+void ima_iint_delete(struct inode *inode);
+void iint_free(struct kref *kref);
+void iint_rcu_free(struct rcu_head *rcu);
+
+/* IMA policy related functions */
+enum ima_hooks { PATH_CHECK = 1, FILE_MMAP, BPRM_CHECK };
+
+int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask);
+void ima_init_policy(void);
+void ima_update_policy(void);
+int ima_parse_add_rule(char *);
+void ima_delete_rules(void);
+
+/* LSM based policy rules require audit */
+#ifdef CONFIG_IMA_LSM_RULES
+
+#define security_filter_rule_init security_audit_rule_init
+#define security_filter_rule_match security_audit_rule_match
+
+#else
+
+static inline int security_filter_rule_init(u32 field, u32 op, char *rulestr,
+					    void **lsmrule)
+{
+	return -EINVAL;
+}
+
+static inline int security_filter_rule_match(u32 secid, u32 field, u32 op,
+					     void *lsmrule,
+					     struct audit_context *actx)
+{
+	return -EINVAL;
+}
+#endif /* CONFIG_IMA_LSM_RULES */
+#endif

+ 190 - 0
security/integrity/ima/ima_api.c

@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2008 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.
+ *
+ * File: ima_api.c
+ *	Implements must_measure, collect_measurement, store_measurement,
+ *	and store_template.
+ */
+#include <linux/module.h>
+
+#include "ima.h"
+static const char *IMA_TEMPLATE_NAME = "ima";
+
+/*
+ * ima_store_template - store ima template measurements
+ *
+ * Calculate the hash of a template entry, add the template entry
+ * to an ordered list of measurement entries maintained inside the kernel,
+ * and also update the aggregate integrity value (maintained inside the
+ * configured TPM PCR) over the hashes of the current list of measurement
+ * entries.
+ *
+ * Applications retrieve the current kernel-held measurement list through
+ * the securityfs entries in /sys/kernel/security/ima. The signed aggregate
+ * TPM PCR (called quote) can be retrieved using a TPM user space library
+ * and is used to validate the measurement list.
+ *
+ * Returns 0 on success, error code otherwise
+ */
+int ima_store_template(struct ima_template_entry *entry,
+		       int violation, struct inode *inode)
+{
+	const char *op = "add_template_measure";
+	const char *audit_cause = "hashing_error";
+	int result;
+
+	memset(entry->digest, 0, sizeof(entry->digest));
+	entry->template_name = IMA_TEMPLATE_NAME;
+	entry->template_len = sizeof(entry->template);
+
+	if (!violation) {
+		result = ima_calc_template_hash(entry->template_len,
+						&entry->template,
+						entry->digest);
+		if (result < 0) {
+			integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
+					    entry->template_name, op,
+					    audit_cause, result, 0);
+			return result;
+		}
+	}
+	result = ima_add_template_entry(entry, violation, op, inode);
+	return result;
+}
+
+/*
+ * ima_add_violation - add violation to measurement list.
+ *
+ * Violations are flagged in the measurement list with zero hash values.
+ * By extending the PCR with 0xFF's instead of with zeroes, the PCR
+ * value is invalidated.
+ */
+void ima_add_violation(struct inode *inode, const unsigned char *filename,
+		       const char *op, const char *cause)
+{
+	struct ima_template_entry *entry;
+	int violation = 1;
+	int result;
+
+	/* can overflow, only indicator */
+	atomic_long_inc(&ima_htable.violations);
+
+	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+	if (!entry) {
+		result = -ENOMEM;
+		goto err_out;
+	}
+	memset(&entry->template, 0, sizeof(entry->template));
+	strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX);
+	result = ima_store_template(entry, violation, inode);
+	if (result < 0)
+		kfree(entry);
+err_out:
+	integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
+			    op, cause, result, 0);
+}
+
+/**
+ * ima_must_measure - measure decision based on policy.
+ * @inode: pointer to inode to measure
+ * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE)
+ * @function: calling function (PATH_CHECK, BPRM_CHECK, FILE_MMAP)
+ *
+ * The policy is defined in terms of keypairs:
+ * 		subj=, obj=, type=, func=, mask=, fsmagic=
+ *	subj,obj, and type: are LSM specific.
+ * 	func: PATH_CHECK | BPRM_CHECK | FILE_MMAP
+ * 	mask: contains the permission mask
+ *	fsmagic: hex value
+ *
+ * Must be called with iint->mutex held.
+ *
+ * Return 0 to measure. Return 1 if already measured.
+ * For matching a DONT_MEASURE policy, no policy, or other
+ * error, return an error code.
+*/
+int ima_must_measure(struct ima_iint_cache *iint, struct inode *inode,
+		     int mask, int function)
+{
+	int must_measure;
+
+	if (iint->flags & IMA_MEASURED)
+		return 1;
+
+	must_measure = ima_match_policy(inode, function, mask);
+	return must_measure ? 0 : -EACCES;
+}
+
+/*
+ * ima_collect_measurement - collect file measurement
+ *
+ * Calculate the file hash, if it doesn't already exist,
+ * storing the measurement and i_version in the iint.
+ *
+ * Must be called with iint->mutex held.
+ *
+ * Return 0 on success, error code otherwise
+ */
+int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file)
+{
+	int result = -EEXIST;
+
+	if (!(iint->flags & IMA_MEASURED)) {
+		u64 i_version = file->f_dentry->d_inode->i_version;
+
+		memset(iint->digest, 0, IMA_DIGEST_SIZE);
+		result = ima_calc_hash(file, iint->digest);
+		if (!result)
+			iint->version = i_version;
+	}
+	return result;
+}
+
+/*
+ * ima_store_measurement - store file measurement
+ *
+ * Create an "ima" template and then store the template by calling
+ * ima_store_template.
+ *
+ * We only get here if the inode has not already been measured,
+ * but the measurement could already exist:
+ * 	- multiple copies of the same file on either the same or
+ *	  different filesystems.
+ *	- the inode was previously flushed as well as the iint info,
+ *	  containing the hashing info.
+ *
+ * Must be called with iint->mutex held.
+ */
+void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
+			   const unsigned char *filename)
+{
+	const char *op = "add_template_measure";
+	const char *audit_cause = "ENOMEM";
+	int result = -ENOMEM;
+	struct inode *inode = file->f_dentry->d_inode;
+	struct ima_template_entry *entry;
+	int violation = 0;
+
+	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+	if (!entry) {
+		integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
+				    op, audit_cause, result, 0);
+		return;
+	}
+	memset(&entry->template, 0, sizeof(entry->template));
+	memcpy(entry->template.digest, iint->digest, IMA_DIGEST_SIZE);
+	strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX);
+
+	result = ima_store_template(entry, violation, inode);
+	if (!result)
+		iint->flags |= IMA_MEASURED;
+	else
+		kfree(entry);
+}

+ 81 - 0
security/integrity/ima/ima_audit.c

@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2008 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.
+ *
+ * File: integrity_audit.c
+ * 	Audit calls for the integrity subsystem
+ */
+
+#include <linux/fs.h>
+#include <linux/audit.h>
+#include "ima.h"
+
+static int ima_audit;
+
+#ifdef CONFIG_IMA_AUDIT
+
+/* ima_audit_setup - enable informational auditing messages */
+static int __init ima_audit_setup(char *str)
+{
+	unsigned long audit;
+	int rc, result = 0;
+	char *op = "ima_audit";
+	char *cause;
+
+	rc = strict_strtoul(str, 0, &audit);
+	if (rc || audit > 1)
+		result = 1;
+	else
+		ima_audit = audit;
+	cause = ima_audit ? "enabled" : "not_enabled";
+	integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
+			    op, cause, result, 0);
+	return 1;
+}
+__setup("ima_audit=", ima_audit_setup);
+#endif
+
+void integrity_audit_msg(int audit_msgno, struct inode *inode,
+			 const unsigned char *fname, const char *op,
+			 const char *cause, int result, int audit_info)
+{
+	struct audit_buffer *ab;
+
+	if (!ima_audit && audit_info == 1) /* Skip informational messages */
+		return;
+
+	ab = audit_log_start(current->audit_context, GFP_KERNEL, audit_msgno);
+	audit_log_format(ab, "integrity: pid=%d uid=%u auid=%u ses=%u",
+			 current->pid, current->cred->uid,
+			 audit_get_loginuid(current),
+			 audit_get_sessionid(current));
+	audit_log_task_context(ab);
+	switch (audit_msgno) {
+	case AUDIT_INTEGRITY_DATA:
+	case AUDIT_INTEGRITY_METADATA:
+	case AUDIT_INTEGRITY_PCR:
+	case AUDIT_INTEGRITY_STATUS:
+		audit_log_format(ab, " op=%s cause=%s", op, cause);
+		break;
+	case AUDIT_INTEGRITY_HASH:
+		audit_log_format(ab, " op=%s hash=%s", op, cause);
+		break;
+	default:
+		audit_log_format(ab, " op=%s", op);
+	}
+	audit_log_format(ab, " comm=");
+	audit_log_untrustedstring(ab, current->comm);
+	if (fname) {
+		audit_log_format(ab, " name=");
+		audit_log_untrustedstring(ab, fname);
+	}
+	if (inode)
+		audit_log_format(ab, " dev=%s ino=%lu",
+				 inode->i_sb->s_id, inode->i_ino);
+	audit_log_format(ab, " res=%d", !result ? 0 : 1);
+	audit_log_end(ab);
+}

+ 140 - 0
security/integrity/ima/ima_crypto.c

@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 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: ima_crypto.c
+ * 	Calculates md5/sha1 file hash, template hash, boot-aggreate hash
+ */
+
+#include <linux/kernel.h>
+#include <linux/file.h>
+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+#include <linux/err.h>
+#include "ima.h"
+
+static int init_desc(struct hash_desc *desc)
+{
+	int rc;
+
+	desc->tfm = crypto_alloc_hash(ima_hash, 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(desc->tfm)) {
+		pr_info("failed to load %s transform: %ld\n",
+			ima_hash, PTR_ERR(desc->tfm));
+		rc = PTR_ERR(desc->tfm);
+		return rc;
+	}
+	desc->flags = 0;
+	rc = crypto_hash_init(desc);
+	if (rc)
+		crypto_free_hash(desc->tfm);
+	return rc;
+}
+
+/*
+ * Calculate the MD5/SHA1 file digest
+ */
+int ima_calc_hash(struct file *file, char *digest)
+{
+	struct hash_desc desc;
+	struct scatterlist sg[1];
+	loff_t i_size;
+	char *rbuf;
+	int rc, offset = 0;
+
+	rc = init_desc(&desc);
+	if (rc != 0)
+		return rc;
+
+	rbuf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!rbuf) {
+		rc = -ENOMEM;
+		goto out;
+	}
+	i_size = i_size_read(file->f_dentry->d_inode);
+	while (offset < i_size) {
+		int rbuf_len;
+
+		rbuf_len = kernel_read(file, offset, rbuf, PAGE_SIZE);
+		if (rbuf_len < 0) {
+			rc = rbuf_len;
+			break;
+		}
+		offset += rbuf_len;
+		sg_init_one(sg, rbuf, rbuf_len);
+
+		rc = crypto_hash_update(&desc, sg, rbuf_len);
+		if (rc)
+			break;
+	}
+	kfree(rbuf);
+	if (!rc)
+		rc = crypto_hash_final(&desc, digest);
+out:
+	crypto_free_hash(desc.tfm);
+	return rc;
+}
+
+/*
+ * Calculate the hash of a given template
+ */
+int ima_calc_template_hash(int template_len, void *template, char *digest)
+{
+	struct hash_desc desc;
+	struct scatterlist sg[1];
+	int rc;
+
+	rc = init_desc(&desc);
+	if (rc != 0)
+		return rc;
+
+	sg_init_one(sg, template, template_len);
+	rc = crypto_hash_update(&desc, sg, template_len);
+	if (!rc)
+		rc = crypto_hash_final(&desc, digest);
+	crypto_free_hash(desc.tfm);
+	return rc;
+}
+
+static void ima_pcrread(int idx, u8 *pcr)
+{
+	if (!ima_used_chip)
+		return;
+
+	if (tpm_pcr_read(TPM_ANY_NUM, idx, pcr) != 0)
+		pr_err("Error Communicating to TPM chip\n");
+}
+
+/*
+ * Calculate the boot aggregate hash
+ */
+int ima_calc_boot_aggregate(char *digest)
+{
+	struct hash_desc desc;
+	struct scatterlist sg;
+	u8 pcr_i[IMA_DIGEST_SIZE];
+	int rc, i;
+
+	rc = init_desc(&desc);
+	if (rc != 0)
+		return rc;
+
+	/* cumulative sha1 over tpm registers 0-7 */
+	for (i = TPM_PCR0; i < TPM_PCR8; i++) {
+		ima_pcrread(i, pcr_i);
+		/* now accumulate with current aggregate */
+		sg_init_one(&sg, pcr_i, IMA_DIGEST_SIZE);
+		rc = crypto_hash_update(&desc, &sg, IMA_DIGEST_SIZE);
+	}
+	if (!rc)
+		crypto_hash_final(&desc, digest);
+	crypto_free_hash(desc.tfm);
+	return rc;
+}

+ 376 - 0
security/integrity/ima/ima_fs.c

@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Kylene Hall <kjhall@us.ibm.com>
+ * Reiner Sailer <sailer@us.ibm.com>
+ * 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_fs.c
+ *	implemenents security file system for reporting
+ *	current measurement list and IMA statistics
+ */
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/rculist.h>
+#include <linux/rcupdate.h>
+#include <linux/parser.h>
+
+#include "ima.h"
+
+static int valid_policy = 1;
+#define TMPBUFLEN 12
+static ssize_t ima_show_htable_value(char __user *buf, size_t count,
+				     loff_t *ppos, atomic_long_t *val)
+{
+	char tmpbuf[TMPBUFLEN];
+	ssize_t len;
+
+	len = scnprintf(tmpbuf, TMPBUFLEN, "%li\n", atomic_long_read(val));
+	return simple_read_from_buffer(buf, count, ppos, tmpbuf, len);
+}
+
+static ssize_t ima_show_htable_violations(struct file *filp,
+					  char __user *buf,
+					  size_t count, loff_t *ppos)
+{
+	return ima_show_htable_value(buf, count, ppos, &ima_htable.violations);
+}
+
+static struct file_operations ima_htable_violations_ops = {
+	.read = ima_show_htable_violations
+};
+
+static ssize_t ima_show_measurements_count(struct file *filp,
+					   char __user *buf,
+					   size_t count, loff_t *ppos)
+{
+	return ima_show_htable_value(buf, count, ppos, &ima_htable.len);
+
+}
+
+static struct file_operations ima_measurements_count_ops = {
+	.read = ima_show_measurements_count
+};
+
+/* returns pointer to hlist_node */
+static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
+{
+	loff_t l = *pos;
+	struct ima_queue_entry *qe;
+
+	/* we need a lock since pos could point beyond last element */
+	rcu_read_lock();
+	list_for_each_entry_rcu(qe, &ima_measurements, later) {
+		if (!l--) {
+			rcu_read_unlock();
+			return qe;
+		}
+	}
+	rcu_read_unlock();
+	return NULL;
+}
+
+static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	struct ima_queue_entry *qe = v;
+
+	/* lock protects when reading beyond last element
+	 * against concurrent list-extension
+	 */
+	rcu_read_lock();
+	qe = list_entry(rcu_dereference(qe->later.next),
+			struct ima_queue_entry, later);
+	rcu_read_unlock();
+	(*pos)++;
+
+	return (&qe->later == &ima_measurements) ? NULL : qe;
+}
+
+static void ima_measurements_stop(struct seq_file *m, void *v)
+{
+}
+
+static void ima_putc(struct seq_file *m, void *data, int datalen)
+{
+	while (datalen--)
+		seq_putc(m, *(char *)data++);
+}
+
+/* print format:
+ *       32bit-le=pcr#
+ *       char[20]=template digest
+ *       32bit-le=template name size
+ *       char[n]=template name
+ *       eventdata[n]=template specific data
+ */
+static int ima_measurements_show(struct seq_file *m, void *v)
+{
+	/* the list never shrinks, so we don't need a lock here */
+	struct ima_queue_entry *qe = v;
+	struct ima_template_entry *e;
+	int namelen;
+	u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX;
+
+	/* get entry */
+	e = qe->entry;
+	if (e == NULL)
+		return -1;
+
+	/*
+	 * 1st: PCRIndex
+	 * PCR used is always the same (config option) in
+	 * little-endian format
+	 */
+	ima_putc(m, &pcr, sizeof pcr);
+
+	/* 2nd: template digest */
+	ima_putc(m, e->digest, IMA_DIGEST_SIZE);
+
+	/* 3rd: template name size */
+	namelen = strlen(e->template_name);
+	ima_putc(m, &namelen, sizeof namelen);
+
+	/* 4th:  template name */
+	ima_putc(m, (void *)e->template_name, namelen);
+
+	/* 5th:  template specific data */
+	ima_template_show(m, (struct ima_template_data *)&e->template,
+			  IMA_SHOW_BINARY);
+	return 0;
+}
+
+static struct seq_operations ima_measurments_seqops = {
+	.start = ima_measurements_start,
+	.next = ima_measurements_next,
+	.stop = ima_measurements_stop,
+	.show = ima_measurements_show
+};
+
+static int ima_measurements_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &ima_measurments_seqops);
+}
+
+static struct file_operations ima_measurements_ops = {
+	.open = ima_measurements_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release,
+};
+
+static void ima_print_digest(struct seq_file *m, u8 *digest)
+{
+	int i;
+
+	for (i = 0; i < IMA_DIGEST_SIZE; i++)
+		seq_printf(m, "%02x", *(digest + i));
+}
+
+void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show)
+{
+	struct ima_template_data *entry = e;
+	int namelen;
+
+	switch (show) {
+	case IMA_SHOW_ASCII:
+		ima_print_digest(m, entry->digest);
+		seq_printf(m, " %s\n", entry->file_name);
+		break;
+	case IMA_SHOW_BINARY:
+		ima_putc(m, entry->digest, IMA_DIGEST_SIZE);
+
+		namelen = strlen(entry->file_name);
+		ima_putc(m, &namelen, sizeof namelen);
+		ima_putc(m, entry->file_name, namelen);
+	default:
+		break;
+	}
+}
+
+/* print in ascii */
+static int ima_ascii_measurements_show(struct seq_file *m, void *v)
+{
+	/* the list never shrinks, so we don't need a lock here */
+	struct ima_queue_entry *qe = v;
+	struct ima_template_entry *e;
+
+	/* get entry */
+	e = qe->entry;
+	if (e == NULL)
+		return -1;
+
+	/* 1st: PCR used (config option) */
+	seq_printf(m, "%2d ", CONFIG_IMA_MEASURE_PCR_IDX);
+
+	/* 2nd: SHA1 template hash */
+	ima_print_digest(m, e->digest);
+
+	/* 3th:  template name */
+	seq_printf(m, " %s ", e->template_name);
+
+	/* 4th:  template specific data */
+	ima_template_show(m, (struct ima_template_data *)&e->template,
+			  IMA_SHOW_ASCII);
+	return 0;
+}
+
+static struct seq_operations ima_ascii_measurements_seqops = {
+	.start = ima_measurements_start,
+	.next = ima_measurements_next,
+	.stop = ima_measurements_stop,
+	.show = ima_ascii_measurements_show
+};
+
+static int ima_ascii_measurements_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &ima_ascii_measurements_seqops);
+}
+
+static struct file_operations ima_ascii_measurements_ops = {
+	.open = ima_ascii_measurements_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release,
+};
+
+static ssize_t ima_write_policy(struct file *file, const char __user *buf,
+				size_t datalen, loff_t *ppos)
+{
+	char *data;
+	int rc;
+
+	if (datalen >= PAGE_SIZE)
+		return -ENOMEM;
+	if (*ppos != 0) {
+		/* No partial writes. */
+		return -EINVAL;
+	}
+	data = kmalloc(datalen + 1, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	if (copy_from_user(data, buf, datalen)) {
+		kfree(data);
+		return -EFAULT;
+	}
+	*(data + datalen) = '\0';
+	rc = ima_parse_add_rule(data);
+	if (rc < 0) {
+		datalen = -EINVAL;
+		valid_policy = 0;
+	}
+
+	kfree(data);
+	return datalen;
+}
+
+static struct dentry *ima_dir;
+static struct dentry *binary_runtime_measurements;
+static struct dentry *ascii_runtime_measurements;
+static struct dentry *runtime_measurements_count;
+static struct dentry *violations;
+static struct dentry *ima_policy;
+
+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)
+{
+	if (atomic_dec_and_test(&policy_opencount))
+		return 0;
+	return -EBUSY;
+}
+
+/*
+ * ima_release_policy - start using the new measure policy rules.
+ *
+ * Initially, ima_measure points to the default policy rules, now
+ * point to the new policy rules, and remove the securityfs policy file,
+ * assuming a valid policy.
+ */
+static int ima_release_policy(struct inode *inode, struct file *file)
+{
+	if (!valid_policy) {
+		ima_delete_rules();
+		valid_policy = 1;
+		atomic_set(&policy_opencount, 1);
+		return 0;
+	}
+	ima_update_policy();
+	securityfs_remove(ima_policy);
+	ima_policy = NULL;
+	return 0;
+}
+
+static struct file_operations ima_measure_policy_ops = {
+	.open = ima_open_policy,
+	.write = ima_write_policy,
+	.release = ima_release_policy
+};
+
+int ima_fs_init(void)
+{
+	ima_dir = securityfs_create_dir("ima", NULL);
+	if (IS_ERR(ima_dir))
+		return -1;
+
+	binary_runtime_measurements =
+	    securityfs_create_file("binary_runtime_measurements",
+				   S_IRUSR | S_IRGRP, ima_dir, NULL,
+				   &ima_measurements_ops);
+	if (IS_ERR(binary_runtime_measurements))
+		goto out;
+
+	ascii_runtime_measurements =
+	    securityfs_create_file("ascii_runtime_measurements",
+				   S_IRUSR | S_IRGRP, ima_dir, NULL,
+				   &ima_ascii_measurements_ops);
+	if (IS_ERR(ascii_runtime_measurements))
+		goto out;
+
+	runtime_measurements_count =
+	    securityfs_create_file("runtime_measurements_count",
+				   S_IRUSR | S_IRGRP, ima_dir, NULL,
+				   &ima_measurements_count_ops);
+	if (IS_ERR(runtime_measurements_count))
+		goto out;
+
+	violations =
+	    securityfs_create_file("violations", S_IRUSR | S_IRGRP,
+				   ima_dir, NULL, &ima_htable_violations_ops);
+	if (IS_ERR(violations))
+		goto out;
+
+	ima_policy = securityfs_create_file("policy",
+					    S_IRUSR | S_IRGRP | S_IWUSR,
+					    ima_dir, NULL,
+					    &ima_measure_policy_ops);
+	if (IS_ERR(ima_policy))
+		goto out;
+
+	return 0;
+out:
+	securityfs_remove(runtime_measurements_count);
+	securityfs_remove(ascii_runtime_measurements);
+	securityfs_remove(binary_runtime_measurements);
+	securityfs_remove(ima_dir);
+	securityfs_remove(ima_policy);
+	return -1;
+}
+
+void __exit ima_fs_cleanup(void)
+{
+	securityfs_remove(violations);
+	securityfs_remove(runtime_measurements_count);
+	securityfs_remove(ascii_runtime_measurements);
+	securityfs_remove(binary_runtime_measurements);
+	securityfs_remove(ima_dir);
+	securityfs_remove(ima_policy);
+}

+ 204 - 0
security/integrity/ima/ima_iint.c

@@ -0,0 +1,204 @@
+/*
+ * 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 radix tree.
+ */
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/radix-tree.h>
+#include "ima.h"
+
+#define ima_iint_delete ima_inode_free
+
+RADIX_TREE(ima_iint_store, GFP_ATOMIC);
+DEFINE_SPINLOCK(ima_iint_lock);
+
+static struct kmem_cache *iint_cache __read_mostly;
+
+/* ima_iint_find_get - return the iint associated with an inode
+ *
+ * ima_iint_find_get gets a reference to the iint. Caller must
+ * remember to put the iint reference.
+ */
+struct ima_iint_cache *ima_iint_find_get(struct inode *inode)
+{
+	struct ima_iint_cache *iint;
+
+	rcu_read_lock();
+	iint = radix_tree_lookup(&ima_iint_store, (unsigned long)inode);
+	if (!iint)
+		goto out;
+	kref_get(&iint->refcount);
+out:
+	rcu_read_unlock();
+	return iint;
+}
+
+/* Allocate memory for the iint associated with the inode
+ * from the iint_cache slab, initialize the iint, and
+ * insert it into the radix tree.
+ *
+ * On success return a pointer to the iint; on failure return NULL.
+ */
+struct ima_iint_cache *ima_iint_insert(struct inode *inode)
+{
+	struct ima_iint_cache *iint = NULL;
+	int rc = 0;
+
+	if (!ima_initialized)
+		return iint;
+	iint = kmem_cache_alloc(iint_cache, GFP_KERNEL);
+	if (!iint)
+		return iint;
+
+	rc = radix_tree_preload(GFP_KERNEL);
+	if (rc < 0)
+		goto out;
+
+	spin_lock(&ima_iint_lock);
+	rc = radix_tree_insert(&ima_iint_store, (unsigned long)inode, iint);
+	spin_unlock(&ima_iint_lock);
+out:
+	if (rc < 0) {
+		kmem_cache_free(iint_cache, iint);
+		if (rc == -EEXIST) {
+			spin_lock(&ima_iint_lock);
+			iint = radix_tree_lookup(&ima_iint_store,
+						 (unsigned long)inode);
+			spin_unlock(&ima_iint_lock);
+		} else
+			iint = NULL;
+	}
+	radix_tree_preload_end();
+	return iint;
+}
+
+/**
+ * ima_inode_alloc - allocate an iint associated with an inode
+ * @inode: pointer to the inode
+ *
+ * Return 0 on success, 1 on failure.
+ */
+int ima_inode_alloc(struct inode *inode)
+{
+	struct ima_iint_cache *iint;
+
+	if (!ima_initialized)
+		return 0;
+
+	iint = ima_iint_insert(inode);
+	if (!iint)
+		return 1;
+	return 0;
+}
+
+/* ima_iint_find_insert_get - get the iint associated with an inode
+ *
+ * Most insertions are done at inode_alloc, except those allocated
+ * before late_initcall. When the iint does not exist, allocate it,
+ * initialize and insert it, and increment the iint refcount.
+ *
+ * (Can't initialize at security_initcall before any inodes are
+ * allocated, got to wait at least until proc_init.)
+ *
+ *  Return the iint.
+ */
+struct ima_iint_cache *ima_iint_find_insert_get(struct inode *inode)
+{
+	struct ima_iint_cache *iint = NULL;
+
+	iint = ima_iint_find_get(inode);
+	if (iint)
+		return iint;
+
+	iint = ima_iint_insert(inode);
+	if (iint)
+		kref_get(&iint->refcount);
+
+	return iint;
+}
+EXPORT_SYMBOL_GPL(ima_iint_find_insert_get);
+
+/* iint_free - called when the iint refcount goes to zero */
+void iint_free(struct kref *kref)
+{
+	struct ima_iint_cache *iint = container_of(kref, struct ima_iint_cache,
+						   refcount);
+	iint->version = 0;
+	iint->flags = 0UL;
+	if (iint->readcount != 0) {
+		printk(KERN_INFO "%s: readcount: %ld\n", __FUNCTION__,
+		       iint->readcount);
+		iint->readcount = 0;
+	}
+	if (iint->writecount != 0) {
+		printk(KERN_INFO "%s: writecount: %ld\n", __FUNCTION__,
+		       iint->writecount);
+		iint->writecount = 0;
+	}
+	if (iint->opencount != 0) {
+		printk(KERN_INFO "%s: opencount: %ld\n", __FUNCTION__,
+		       iint->opencount);
+		iint->opencount = 0;
+	}
+	kref_set(&iint->refcount, 1);
+	kmem_cache_free(iint_cache, iint);
+}
+
+void iint_rcu_free(struct rcu_head *rcu_head)
+{
+	struct ima_iint_cache *iint = container_of(rcu_head,
+						   struct ima_iint_cache, rcu);
+	kref_put(&iint->refcount, iint_free);
+}
+
+/**
+ * ima_iint_delete - called on integrity_inode_free
+ * @inode: pointer to the inode
+ *
+ * Free the integrity information(iint) associated with an inode.
+ */
+void ima_iint_delete(struct inode *inode)
+{
+	struct ima_iint_cache *iint;
+
+	if (!ima_initialized)
+		return;
+	spin_lock(&ima_iint_lock);
+	iint = radix_tree_delete(&ima_iint_store, (unsigned long)inode);
+	spin_unlock(&ima_iint_lock);
+	if (iint)
+		call_rcu(&iint->rcu, iint_rcu_free);
+}
+
+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);
+	iint->readcount = 0;
+	iint->writecount = 0;
+	iint->opencount = 0;
+	kref_set(&iint->refcount, 1);
+}
+
+void ima_iintcache_init(void)
+{
+	iint_cache =
+	    kmem_cache_create("iint_cache", sizeof(struct ima_iint_cache), 0,
+			      SLAB_PANIC, init_once);
+}

+ 96 - 0
security/integrity/ima/ima_init.c

@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer      <sailer@watson.ibm.com>
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * 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_init.c
+ *             initialization and cleanup functions
+ */
+#include <linux/module.h>
+#include <linux/scatterlist.h>
+#include <linux/err.h>
+#include "ima.h"
+
+/* name for boot aggregate entry */
+static const char *boot_aggregate_name = "boot_aggregate";
+int ima_used_chip;
+
+/* Add the boot aggregate to the IMA measurement list and extend
+ * the PCR register.
+ *
+ * Calculate the boot aggregate, a SHA1 over tpm registers 0-7,
+ * assuming a TPM chip exists, and zeroes if the TPM chip does not
+ * exist.  Add the boot aggregate measurement to the measurement
+ * list and extend the PCR register.
+ *
+ * If a tpm chip does not exist, indicate the core root of trust is
+ * not hardware based by invalidating the aggregate PCR value.
+ * (The aggregate PCR value is invalidated by adding one value to
+ * the measurement list and extending the aggregate PCR value with
+ * a different value.) Violations add a zero entry to the measurement
+ * list and extend the aggregate PCR value with ff...ff's.
+ */
+static void ima_add_boot_aggregate(void)
+{
+	struct ima_template_entry *entry;
+	const char *op = "add_boot_aggregate";
+	const char *audit_cause = "ENOMEM";
+	int result = -ENOMEM;
+	int violation = 1;
+
+	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+	if (!entry)
+		goto err_out;
+
+	memset(&entry->template, 0, sizeof(entry->template));
+	strncpy(entry->template.file_name, boot_aggregate_name,
+		IMA_EVENT_NAME_LEN_MAX);
+	if (ima_used_chip) {
+		violation = 0;
+		result = ima_calc_boot_aggregate(entry->template.digest);
+		if (result < 0) {
+			audit_cause = "hashing_error";
+			kfree(entry);
+			goto err_out;
+		}
+	}
+	result = ima_store_template(entry, violation, NULL);
+	if (result < 0)
+		kfree(entry);
+	return;
+err_out:
+	integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, boot_aggregate_name, op,
+			    audit_cause, result, 0);
+}
+
+int ima_init(void)
+{
+	u8 pcr_i[IMA_DIGEST_SIZE];
+	int rc;
+
+	ima_used_chip = 0;
+	rc = tpm_pcr_read(TPM_ANY_NUM, 0, pcr_i);
+	if (rc == 0)
+		ima_used_chip = 1;
+
+	if (!ima_used_chip)
+		pr_info("No TPM chip found, activating TPM-bypass!\n");
+
+	ima_add_boot_aggregate();	/* boot aggregate must be first entry */
+	ima_init_policy();
+
+	return ima_fs_init();
+}
+
+void __exit ima_cleanup(void)
+{
+	ima_fs_cleanup();
+}

+ 327 - 0
security/integrity/ima/ima_main.c

@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Serge Hallyn <serue@us.ibm.com>
+ * Kylene Hall <kylene@us.ibm.com>
+ * 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_main.c
+ *             implements the IMA hooks: ima_bprm_check, ima_file_mmap,
+ *             and ima_path_check.
+ */
+#include <linux/module.h>
+#include <linux/file.h>
+#include <linux/binfmts.h>
+#include <linux/mount.h>
+#include <linux/mman.h>
+
+#include "ima.h"
+
+int ima_initialized;
+
+char *ima_hash = "sha1";
+static int __init hash_setup(char *str)
+{
+	const char *op = "hash_setup";
+	const char *hash = "sha1";
+	int result = 0;
+	int audit_info = 0;
+
+	if (strncmp(str, "md5", 3) == 0) {
+		hash = "md5";
+		ima_hash = str;
+	} else if (strncmp(str, "sha1", 4) != 0) {
+		hash = "invalid_hash_type";
+		result = 1;
+	}
+	integrity_audit_msg(AUDIT_INTEGRITY_HASH, NULL, NULL, op, hash,
+			    result, audit_info);
+	return 1;
+}
+__setup("ima_hash=", hash_setup);
+
+/**
+ * ima_file_free - called on __fput()
+ * @file: pointer to file structure being freed
+ *
+ * Flag files that changed, based on i_version;
+ * and decrement the iint readcount/writecount.
+ */
+void ima_file_free(struct file *file)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	struct ima_iint_cache *iint;
+
+	if (!ima_initialized || !S_ISREG(inode->i_mode))
+		return;
+	iint = ima_iint_find_get(inode);
+	if (!iint)
+		return;
+
+	mutex_lock(&iint->mutex);
+	if (iint->opencount <= 0) {
+		printk(KERN_INFO
+		       "%s: %s open/free imbalance (r:%ld w:%ld o:%ld f:%ld)\n",
+		       __FUNCTION__, file->f_dentry->d_name.name,
+		       iint->readcount, iint->writecount,
+		       iint->opencount, atomic_long_read(&file->f_count));
+		if (!(iint->flags & IMA_IINT_DUMP_STACK)) {
+			dump_stack();
+			iint->flags |= IMA_IINT_DUMP_STACK;
+		}
+	}
+	iint->opencount--;
+
+	if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
+		iint->readcount--;
+
+	if (file->f_mode & FMODE_WRITE) {
+		iint->writecount--;
+		if (iint->writecount == 0) {
+			if (iint->version != inode->i_version)
+				iint->flags &= ~IMA_MEASURED;
+		}
+	}
+	mutex_unlock(&iint->mutex);
+	kref_put(&iint->refcount, iint_free);
+}
+
+/* ima_read_write_check - reflect possible reading/writing errors in the PCR.
+ *
+ * When opening a file for read, if the file is already open for write,
+ * the file could change, resulting in a file measurement error.
+ *
+ * Opening a file for write, if the file is already open for read, results
+ * in a time of measure, time of use (ToMToU) error.
+ *
+ * In either case invalidate the PCR.
+ */
+enum iint_pcr_error { TOMTOU, OPEN_WRITERS };
+static void ima_read_write_check(enum iint_pcr_error error,
+				 struct ima_iint_cache *iint,
+				 struct inode *inode,
+				 const unsigned char *filename)
+{
+	switch (error) {
+	case TOMTOU:
+		if (iint->readcount > 0)
+			ima_add_violation(inode, filename, "invalid_pcr",
+					  "ToMToU");
+		break;
+	case OPEN_WRITERS:
+		if (iint->writecount > 0)
+			ima_add_violation(inode, filename, "invalid_pcr",
+					  "open_writers");
+		break;
+	}
+}
+
+static int get_path_measurement(struct ima_iint_cache *iint, struct file *file,
+				const unsigned char *filename)
+{
+	int rc = 0;
+
+	if (IS_ERR(file)) {
+		pr_info("%s dentry_open failed\n", filename);
+		return rc;
+	}
+	iint->opencount++;
+	iint->readcount++;
+
+	rc = ima_collect_measurement(iint, file);
+	if (!rc)
+		ima_store_measurement(iint, file, filename);
+	return rc;
+}
+
+/**
+ * ima_path_check - based on policy, collect/store measurement.
+ * @path: contains a pointer to the path to be measured
+ * @mask: contains MAY_READ, MAY_WRITE or MAY_EXECUTE
+ *
+ * Measure the file being open for readonly, based on the
+ * ima_must_measure() policy decision.
+ *
+ * Keep read/write counters for all files, but only
+ * invalidate the PCR for measured files:
+ * 	- Opening a file for write when already open for read,
+ *	  results in a time of measure, time of use (ToMToU) error.
+ *	- Opening a file for read when already open for write,
+ * 	  could result in a file measurement error.
+ *
+ * Return 0 on success, an error code on failure.
+ * (Based on the results of appraise_measurement().)
+ */
+int ima_path_check(struct path *path, int mask)
+{
+	struct inode *inode = path->dentry->d_inode;
+	struct ima_iint_cache *iint;
+	struct file *file = NULL;
+	int rc;
+
+	if (!ima_initialized || !S_ISREG(inode->i_mode))
+		return 0;
+	iint = ima_iint_find_insert_get(inode);
+	if (!iint)
+		return 0;
+
+	mutex_lock(&iint->mutex);
+	iint->opencount++;
+	if ((mask & MAY_WRITE) || (mask == 0))
+		iint->writecount++;
+	else if (mask & (MAY_READ | MAY_EXEC))
+		iint->readcount++;
+
+	rc = ima_must_measure(iint, inode, MAY_READ, PATH_CHECK);
+	if (rc < 0)
+		goto out;
+
+	if ((mask & MAY_WRITE) || (mask == 0))
+		ima_read_write_check(TOMTOU, iint, inode,
+				     path->dentry->d_name.name);
+
+	if ((mask & (MAY_WRITE | MAY_READ | MAY_EXEC)) != MAY_READ)
+		goto out;
+
+	ima_read_write_check(OPEN_WRITERS, iint, inode,
+			     path->dentry->d_name.name);
+	if (!(iint->flags & IMA_MEASURED)) {
+		struct dentry *dentry = dget(path->dentry);
+		struct vfsmount *mnt = mntget(path->mnt);
+
+		file = dentry_open(dentry, mnt, O_RDONLY, current->cred);
+		rc = get_path_measurement(iint, file, dentry->d_name.name);
+	}
+out:
+	mutex_unlock(&iint->mutex);
+	if (file)
+		fput(file);
+	kref_put(&iint->refcount, iint_free);
+	return 0;
+}
+
+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;
+	int rc;
+
+	if (!ima_initialized || !S_ISREG(inode->i_mode))
+		return 0;
+	iint = ima_iint_find_insert_get(inode);
+	if (!iint)
+		return -ENOMEM;
+
+	mutex_lock(&iint->mutex);
+	rc = ima_must_measure(iint, inode, mask, function);
+	if (rc != 0)
+		goto out;
+
+	rc = ima_collect_measurement(iint, file);
+	if (!rc)
+		ima_store_measurement(iint, file, filename);
+out:
+	mutex_unlock(&iint->mutex);
+	kref_put(&iint->refcount, iint_free);
+	return rc;
+}
+
+static void opencount_get(struct file *file)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	struct ima_iint_cache *iint;
+
+	if (!ima_initialized || !S_ISREG(inode->i_mode))
+		return;
+	iint = ima_iint_find_insert_get(inode);
+	if (!iint)
+		return;
+	mutex_lock(&iint->mutex);
+	iint->opencount++;
+	mutex_unlock(&iint->mutex);
+}
+
+/**
+ * ima_file_mmap - based on policy, collect/store measurement.
+ * @file: pointer to the file to be measured (May be NULL)
+ * @prot: contains the protection that will be applied by the kernel.
+ *
+ * Measure files being mmapped executable based on the ima_must_measure()
+ * policy decision.
+ *
+ * Return 0 on success, an error code on failure.
+ * (Based on the results of appraise_measurement().)
+ */
+int ima_file_mmap(struct file *file, unsigned long prot)
+{
+	int rc;
+
+	if (!file)
+		return 0;
+	if (prot & PROT_EXEC)
+		rc = process_measurement(file, file->f_dentry->d_name.name,
+					 MAY_EXEC, FILE_MMAP);
+	return 0;
+}
+
+/*
+ * ima_shm_check - IPC shm and shmat create/fput a file
+ *
+ * Maintain the opencount for these files to prevent unnecessary
+ * imbalance messages.
+ */
+void ima_shm_check(struct file *file)
+{
+	opencount_get(file);
+	return;
+}
+
+/**
+ * ima_bprm_check - based on policy, collect/store measurement.
+ * @bprm: contains the linux_binprm structure
+ *
+ * The OS protects against an executable file, already open for write,
+ * from being executed in deny_write_access() and an executable file,
+ * already open for execute, from being modified in get_write_access().
+ * So we can be certain that what we verify and measure here is actually
+ * what is being executed.
+ *
+ * Return 0 on success, an error code on failure.
+ * (Based on the results of appraise_measurement().)
+ */
+int ima_bprm_check(struct linux_binprm *bprm)
+{
+	int rc;
+
+	rc = process_measurement(bprm->file, bprm->filename,
+				 MAY_EXEC, BPRM_CHECK);
+	return 0;
+}
+
+static int __init init_ima(void)
+{
+	int error;
+
+	ima_iintcache_init();
+	error = ima_init();
+	ima_initialized = 1;
+	return error;
+}
+
+static void __exit cleanup_ima(void)
+{
+	ima_cleanup();
+}
+
+late_initcall(init_ima);	/* Start IMA after the TPM is available */
+
+MODULE_DESCRIPTION("Integrity Measurement Architecture");
+MODULE_LICENSE("GPL");

+ 414 - 0
security/integrity/ima/ima_policy.c

@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2008 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.
+ *
+ * ima_policy.c
+ * 	- initialize default measure policy rules
+ *
+ */
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/security.h>
+#include <linux/magic.h>
+#include <linux/parser.h>
+
+#include "ima.h"
+
+/* flags definitions */
+#define IMA_FUNC 	0x0001
+#define IMA_MASK 	0x0002
+#define IMA_FSMAGIC	0x0004
+#define IMA_UID		0x0008
+
+enum ima_action { UNKNOWN = -1, DONT_MEASURE = 0, MEASURE };
+
+#define MAX_LSM_RULES 6
+enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE,
+	LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE
+};
+
+struct ima_measure_rule_entry {
+	struct list_head list;
+	enum ima_action action;
+	unsigned int flags;
+	enum ima_hooks func;
+	int mask;
+	unsigned long fsmagic;
+	uid_t uid;
+	struct {
+		void *rule;	/* LSM file metadata specific */
+		int type;	/* audit type */
+	} lsm[MAX_LSM_RULES];
+};
+
+/* Without LSM specific knowledge, the default policy can only be
+ * written in terms of .action, .func, .mask, .fsmagic, and .uid
+ */
+static struct ima_measure_rule_entry default_rules[] = {
+	{.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC,
+	 .flags = IMA_FSMAGIC},
+	{.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC},
+	{.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC},
+	{.action = DONT_MEASURE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC},
+	{.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,
+	 .flags = IMA_FSMAGIC},
+	{.action = DONT_MEASURE,.fsmagic = 0xF97CFF8C,.flags = IMA_FSMAGIC},
+	{.action = MEASURE,.func = FILE_MMAP,.mask = MAY_EXEC,
+	 .flags = IMA_FUNC | IMA_MASK},
+	{.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC,
+	 .flags = IMA_FUNC | IMA_MASK},
+	{.action = MEASURE,.func = PATH_CHECK,.mask = MAY_READ,.uid = 0,
+	 .flags = IMA_FUNC | IMA_MASK | IMA_UID}
+};
+
+static LIST_HEAD(measure_default_rules);
+static LIST_HEAD(measure_policy_rules);
+static struct list_head *ima_measure;
+
+static DEFINE_MUTEX(ima_measure_mutex);
+
+/**
+ * ima_match_rules - determine whether an inode matches the measure rule.
+ * @rule: a pointer to a rule
+ * @inode: a pointer to an inode
+ * @func: LIM hook identifier
+ * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
+ *
+ * Returns true on rule match, false on failure.
+ */
+static bool ima_match_rules(struct ima_measure_rule_entry *rule,
+			    struct inode *inode, enum ima_hooks func, int mask)
+{
+	struct task_struct *tsk = current;
+	int i;
+
+	if ((rule->flags & IMA_FUNC) && rule->func != func)
+		return false;
+	if ((rule->flags & IMA_MASK) && rule->mask != mask)
+		return false;
+	if ((rule->flags & IMA_FSMAGIC)
+	    && rule->fsmagic != inode->i_sb->s_magic)
+		return false;
+	if ((rule->flags & IMA_UID) && rule->uid != tsk->cred->uid)
+		return false;
+	for (i = 0; i < MAX_LSM_RULES; i++) {
+		int rc;
+		u32 osid, sid;
+
+		if (!rule->lsm[i].rule)
+			continue;
+
+		switch (i) {
+		case LSM_OBJ_USER:
+		case LSM_OBJ_ROLE:
+		case LSM_OBJ_TYPE:
+			security_inode_getsecid(inode, &osid);
+			rc = security_filter_rule_match(osid,
+							rule->lsm[i].type,
+							AUDIT_EQUAL,
+							rule->lsm[i].rule,
+							NULL);
+			break;
+		case LSM_SUBJ_USER:
+		case LSM_SUBJ_ROLE:
+		case LSM_SUBJ_TYPE:
+			security_task_getsecid(tsk, &sid);
+			rc = security_filter_rule_match(sid,
+							rule->lsm[i].type,
+							AUDIT_EQUAL,
+							rule->lsm[i].rule,
+							NULL);
+		default:
+			break;
+		}
+		if (!rc)
+			return false;
+	}
+	return true;
+}
+
+/**
+ * ima_match_policy - decision based on LSM and other conditions
+ * @inode: pointer to an inode for which the policy decision is being made
+ * @func: IMA hook identifier
+ * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
+ *
+ * Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
+ * conditions.
+ *
+ * (There is no need for locking when walking the policy list,
+ * as elements in the list are never deleted, nor does the list
+ * change.)
+ */
+int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask)
+{
+	struct ima_measure_rule_entry *entry;
+
+	list_for_each_entry(entry, ima_measure, list) {
+		bool rc;
+
+		rc = ima_match_rules(entry, inode, func, mask);
+		if (rc)
+			return entry->action;
+	}
+	return 0;
+}
+
+/**
+ * ima_init_policy - initialize the default measure rules.
+ *
+ * ima_measure points to either the measure_default_rules or the
+ * the new measure_policy_rules.
+ */
+void ima_init_policy(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(default_rules); i++)
+		list_add_tail(&default_rules[i].list, &measure_default_rules);
+	ima_measure = &measure_default_rules;
+}
+
+/**
+ * ima_update_policy - update default_rules with new measure rules
+ *
+ * Called on file .release to update the default rules with a complete new
+ * policy.  Once updated, the policy is locked, no additional rules can be
+ * added to the policy.
+ */
+void ima_update_policy(void)
+{
+	const char *op = "policy_update";
+	const char *cause = "already exists";
+	int result = 1;
+	int audit_info = 0;
+
+	if (ima_measure == &measure_default_rules) {
+		ima_measure = &measure_policy_rules;
+		cause = "complete";
+		result = 0;
+	}
+	integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
+			    NULL, op, cause, result, audit_info);
+}
+
+enum {
+	Opt_err = -1,
+	Opt_measure = 1, Opt_dont_measure,
+	Opt_obj_user, Opt_obj_role, Opt_obj_type,
+	Opt_subj_user, Opt_subj_role, Opt_subj_type,
+	Opt_func, Opt_mask, Opt_fsmagic, Opt_uid
+};
+
+static match_table_t policy_tokens = {
+	{Opt_measure, "measure"},
+	{Opt_dont_measure, "dont_measure"},
+	{Opt_obj_user, "obj_user=%s"},
+	{Opt_obj_role, "obj_role=%s"},
+	{Opt_obj_type, "obj_type=%s"},
+	{Opt_subj_user, "subj_user=%s"},
+	{Opt_subj_role, "subj_role=%s"},
+	{Opt_subj_type, "subj_type=%s"},
+	{Opt_func, "func=%s"},
+	{Opt_mask, "mask=%s"},
+	{Opt_fsmagic, "fsmagic=%s"},
+	{Opt_uid, "uid=%s"},
+	{Opt_err, NULL}
+};
+
+static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry,
+			     char *args, int lsm_rule, int audit_type)
+{
+	int result;
+
+	entry->lsm[lsm_rule].type = audit_type;
+	result = security_filter_rule_init(entry->lsm[lsm_rule].type,
+					   AUDIT_EQUAL, args,
+					   &entry->lsm[lsm_rule].rule);
+	return result;
+}
+
+static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
+{
+	struct audit_buffer *ab;
+	char *p;
+	int result = 0;
+
+	ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE);
+
+	entry->action = -1;
+	while ((p = strsep(&rule, " \n")) != NULL) {
+		substring_t args[MAX_OPT_ARGS];
+		int token;
+		unsigned long lnum;
+
+		if (result < 0)
+			break;
+		if (!*p)
+			continue;
+		token = match_token(p, policy_tokens, args);
+		switch (token) {
+		case Opt_measure:
+			audit_log_format(ab, "%s ", "measure");
+			entry->action = MEASURE;
+			break;
+		case Opt_dont_measure:
+			audit_log_format(ab, "%s ", "dont_measure");
+			entry->action = DONT_MEASURE;
+			break;
+		case Opt_func:
+			audit_log_format(ab, "func=%s ", args[0].from);
+			if (strcmp(args[0].from, "PATH_CHECK") == 0)
+				entry->func = PATH_CHECK;
+			else if (strcmp(args[0].from, "FILE_MMAP") == 0)
+				entry->func = FILE_MMAP;
+			else if (strcmp(args[0].from, "BPRM_CHECK") == 0)
+				entry->func = BPRM_CHECK;
+			else
+				result = -EINVAL;
+			if (!result)
+				entry->flags |= IMA_FUNC;
+			break;
+		case Opt_mask:
+			audit_log_format(ab, "mask=%s ", args[0].from);
+			if ((strcmp(args[0].from, "MAY_EXEC")) == 0)
+				entry->mask = MAY_EXEC;
+			else if (strcmp(args[0].from, "MAY_WRITE") == 0)
+				entry->mask = MAY_WRITE;
+			else if (strcmp(args[0].from, "MAY_READ") == 0)
+				entry->mask = MAY_READ;
+			else if (strcmp(args[0].from, "MAY_APPEND") == 0)
+				entry->mask = MAY_APPEND;
+			else
+				result = -EINVAL;
+			if (!result)
+				entry->flags |= IMA_MASK;
+			break;
+		case Opt_fsmagic:
+			audit_log_format(ab, "fsmagic=%s ", args[0].from);
+			result = strict_strtoul(args[0].from, 16,
+						&entry->fsmagic);
+			if (!result)
+				entry->flags |= IMA_FSMAGIC;
+			break;
+		case Opt_uid:
+			audit_log_format(ab, "uid=%s ", args[0].from);
+			result = strict_strtoul(args[0].from, 10, &lnum);
+			if (!result) {
+				entry->uid = (uid_t) lnum;
+				if (entry->uid != lnum)
+					result = -EINVAL;
+				else
+					entry->flags |= IMA_UID;
+			}
+			break;
+		case Opt_obj_user:
+			audit_log_format(ab, "obj_user=%s ", args[0].from);
+			result = ima_lsm_rule_init(entry, args[0].from,
+						   LSM_OBJ_USER,
+						   AUDIT_OBJ_USER);
+			break;
+		case Opt_obj_role:
+			audit_log_format(ab, "obj_role=%s ", args[0].from);
+			result = ima_lsm_rule_init(entry, args[0].from,
+						   LSM_OBJ_ROLE,
+						   AUDIT_OBJ_ROLE);
+			break;
+		case Opt_obj_type:
+			audit_log_format(ab, "obj_type=%s ", args[0].from);
+			result = ima_lsm_rule_init(entry, args[0].from,
+						   LSM_OBJ_TYPE,
+						   AUDIT_OBJ_TYPE);
+			break;
+		case Opt_subj_user:
+			audit_log_format(ab, "subj_user=%s ", args[0].from);
+			result = ima_lsm_rule_init(entry, args[0].from,
+						   LSM_SUBJ_USER,
+						   AUDIT_SUBJ_USER);
+			break;
+		case Opt_subj_role:
+			audit_log_format(ab, "subj_role=%s ", args[0].from);
+			result = ima_lsm_rule_init(entry, args[0].from,
+						   LSM_SUBJ_ROLE,
+						   AUDIT_SUBJ_ROLE);
+			break;
+		case Opt_subj_type:
+			audit_log_format(ab, "subj_type=%s ", args[0].from);
+			result = ima_lsm_rule_init(entry, args[0].from,
+						   LSM_SUBJ_TYPE,
+						   AUDIT_SUBJ_TYPE);
+			break;
+		case Opt_err:
+			audit_log_format(ab, "UNKNOWN=%s ", p);
+			break;
+		}
+	}
+	if (entry->action == UNKNOWN)
+		result = -EINVAL;
+
+	audit_log_format(ab, "res=%d", !result ? 0 : 1);
+	audit_log_end(ab);
+	return result;
+}
+
+/**
+ * ima_parse_add_rule - add a rule to measure_policy_rules
+ * @rule - ima measurement policy rule
+ *
+ * Uses a mutex to protect the policy list from multiple concurrent writers.
+ * Returns 0 on success, an error code on failure.
+ */
+int ima_parse_add_rule(char *rule)
+{
+	const char *op = "update_policy";
+	struct ima_measure_rule_entry *entry;
+	int result = 0;
+	int audit_info = 0;
+
+	/* Prevent installed policy from changing */
+	if (ima_measure != &measure_default_rules) {
+		integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
+				    NULL, op, "already exists",
+				    -EACCES, audit_info);
+		return -EACCES;
+	}
+
+	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+	if (!entry) {
+		integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
+				    NULL, op, "-ENOMEM", -ENOMEM, audit_info);
+		return -ENOMEM;
+	}
+
+	INIT_LIST_HEAD(&entry->list);
+
+	result = ima_parse_rule(rule, entry);
+	if (!result) {
+		mutex_lock(&ima_measure_mutex);
+		list_add_tail(&entry->list, &measure_policy_rules);
+		mutex_unlock(&ima_measure_mutex);
+	} else {
+		kfree(entry);
+		integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
+				    NULL, op, "invalid policy", result,
+				    audit_info);
+	}
+	return result;
+}
+
+/* ima_delete_rules called to cleanup invalid policy */
+void ima_delete_rules(void)
+{
+	struct ima_measure_rule_entry *entry, *tmp;
+
+	mutex_lock(&ima_measure_mutex);
+	list_for_each_entry_safe(entry, tmp, &measure_policy_rules, list) {
+		list_del(&entry->list);
+		kfree(entry);
+	}
+	mutex_unlock(&ima_measure_mutex);
+}

+ 140 - 0
security/integrity/ima/ima_queue.c

@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Serge Hallyn <serue@us.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * 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_queue.c
+ *       Implements queues that store template measurements and
+ *       maintains aggregate over the stored measurements
+ *       in the pre-configured TPM PCR (if available).
+ *       The measurement list is append-only. No entry is
+ *       ever removed or changed during the boot-cycle.
+ */
+#include <linux/module.h>
+#include <linux/rculist.h>
+#include "ima.h"
+
+LIST_HEAD(ima_measurements);	/* list of all measurements */
+
+/* key: inode (before secure-hashing a file) */
+struct ima_h_table ima_htable = {
+	.len = ATOMIC_LONG_INIT(0),
+	.violations = ATOMIC_LONG_INIT(0),
+	.queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT
+};
+
+/* mutex protects atomicity of extending measurement list
+ * and extending the TPM PCR aggregate. Since tpm_extend can take
+ * long (and the tpm driver uses a mutex), we can't use the spinlock.
+ */
+static DEFINE_MUTEX(ima_extend_list_mutex);
+
+/* lookup up the digest value in the hash table, and return the entry */
+static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value)
+{
+	struct ima_queue_entry *qe, *ret = NULL;
+	unsigned int key;
+	struct hlist_node *pos;
+	int rc;
+
+	key = ima_hash_key(digest_value);
+	rcu_read_lock();
+	hlist_for_each_entry_rcu(qe, pos, &ima_htable.queue[key], hnext) {
+		rc = memcmp(qe->entry->digest, digest_value, IMA_DIGEST_SIZE);
+		if (rc == 0) {
+			ret = qe;
+			break;
+		}
+	}
+	rcu_read_unlock();
+	return ret;
+}
+
+/* ima_add_template_entry helper function:
+ * - Add template entry to measurement list and hash table.
+ *
+ * (Called with ima_extend_list_mutex held.)
+ */
+static int ima_add_digest_entry(struct ima_template_entry *entry)
+{
+	struct ima_queue_entry *qe;
+	unsigned int key;
+
+	qe = kmalloc(sizeof(*qe), GFP_KERNEL);
+	if (qe == NULL) {
+		pr_err("OUT OF MEMORY ERROR creating queue entry.\n");
+		return -ENOMEM;
+	}
+	qe->entry = entry;
+
+	INIT_LIST_HEAD(&qe->later);
+	list_add_tail_rcu(&qe->later, &ima_measurements);
+
+	atomic_long_inc(&ima_htable.len);
+	key = ima_hash_key(entry->digest);
+	hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]);
+	return 0;
+}
+
+static int ima_pcr_extend(const u8 *hash)
+{
+	int result = 0;
+
+	if (!ima_used_chip)
+		return result;
+
+	result = tpm_pcr_extend(TPM_ANY_NUM, CONFIG_IMA_MEASURE_PCR_IDX, hash);
+	if (result != 0)
+		pr_err("Error Communicating to TPM chip\n");
+	return result;
+}
+
+/* Add template entry to the measurement list and hash table,
+ * and extend the pcr.
+ */
+int ima_add_template_entry(struct ima_template_entry *entry, int violation,
+			   const char *op, struct inode *inode)
+{
+	u8 digest[IMA_DIGEST_SIZE];
+	const char *audit_cause = "hash_added";
+	int audit_info = 1;
+	int result = 0;
+
+	mutex_lock(&ima_extend_list_mutex);
+	if (!violation) {
+		memcpy(digest, entry->digest, sizeof digest);
+		if (ima_lookup_digest_entry(digest)) {
+			audit_cause = "hash_exists";
+			goto out;
+		}
+	}
+
+	result = ima_add_digest_entry(entry);
+	if (result < 0) {
+		audit_cause = "ENOMEM";
+		audit_info = 0;
+		goto out;
+	}
+
+	if (violation)		/* invalidate pcr */
+		memset(digest, 0xff, sizeof digest);
+
+	result = ima_pcr_extend(digest);
+	if (result != 0) {
+		audit_cause = "TPM error";
+		audit_info = 0;
+	}
+out:
+	mutex_unlock(&ima_extend_list_mutex);
+	integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, entry->template_name,
+			    op, audit_cause, result, audit_info);
+	return result;
+}

+ 3 - 1
security/keys/internal.h

@@ -53,6 +53,7 @@ struct key_user {
 	atomic_t		nkeys;		/* number of keys */
 	atomic_t		nkeys;		/* number of keys */
 	atomic_t		nikeys;		/* number of instantiated keys */
 	atomic_t		nikeys;		/* number of instantiated keys */
 	uid_t			uid;
 	uid_t			uid;
+	struct user_namespace	*user_ns;
 	int			qnkeys;		/* number of keys allocated to this user */
 	int			qnkeys;		/* number of keys allocated to this user */
 	int			qnbytes;	/* number of bytes allocated to this user */
 	int			qnbytes;	/* number of bytes allocated to this user */
 };
 };
@@ -61,7 +62,8 @@ extern struct rb_root	key_user_tree;
 extern spinlock_t	key_user_lock;
 extern spinlock_t	key_user_lock;
 extern struct key_user	root_key_user;
 extern struct key_user	root_key_user;
 
 
-extern struct key_user *key_user_lookup(uid_t uid);
+extern struct key_user *key_user_lookup(uid_t uid,
+					struct user_namespace *user_ns);
 extern void key_user_put(struct key_user *user);
 extern void key_user_put(struct key_user *user);
 
 
 /*
 /*

+ 9 - 2
security/keys/key.c

@@ -18,6 +18,7 @@
 #include <linux/workqueue.h>
 #include <linux/workqueue.h>
 #include <linux/random.h>
 #include <linux/random.h>
 #include <linux/err.h>
 #include <linux/err.h>
+#include <linux/user_namespace.h>
 #include "internal.h"
 #include "internal.h"
 
 
 static struct kmem_cache	*key_jar;
 static struct kmem_cache	*key_jar;
@@ -60,7 +61,7 @@ void __key_check(const struct key *key)
  * get the key quota record for a user, allocating a new record if one doesn't
  * get the key quota record for a user, allocating a new record if one doesn't
  * already exist
  * already exist
  */
  */
-struct key_user *key_user_lookup(uid_t uid)
+struct key_user *key_user_lookup(uid_t uid, struct user_namespace *user_ns)
 {
 {
 	struct key_user *candidate = NULL, *user;
 	struct key_user *candidate = NULL, *user;
 	struct rb_node *parent = NULL;
 	struct rb_node *parent = NULL;
@@ -79,6 +80,10 @@ struct key_user *key_user_lookup(uid_t uid)
 			p = &(*p)->rb_left;
 			p = &(*p)->rb_left;
 		else if (uid > user->uid)
 		else if (uid > user->uid)
 			p = &(*p)->rb_right;
 			p = &(*p)->rb_right;
+		else if (user_ns < user->user_ns)
+			p = &(*p)->rb_left;
+		else if (user_ns > user->user_ns)
+			p = &(*p)->rb_right;
 		else
 		else
 			goto found;
 			goto found;
 	}
 	}
@@ -106,6 +111,7 @@ struct key_user *key_user_lookup(uid_t uid)
 	atomic_set(&candidate->nkeys, 0);
 	atomic_set(&candidate->nkeys, 0);
 	atomic_set(&candidate->nikeys, 0);
 	atomic_set(&candidate->nikeys, 0);
 	candidate->uid = uid;
 	candidate->uid = uid;
+	candidate->user_ns = get_user_ns(user_ns);
 	candidate->qnkeys = 0;
 	candidate->qnkeys = 0;
 	candidate->qnbytes = 0;
 	candidate->qnbytes = 0;
 	spin_lock_init(&candidate->lock);
 	spin_lock_init(&candidate->lock);
@@ -136,6 +142,7 @@ void key_user_put(struct key_user *user)
 	if (atomic_dec_and_lock(&user->usage, &key_user_lock)) {
 	if (atomic_dec_and_lock(&user->usage, &key_user_lock)) {
 		rb_erase(&user->node, &key_user_tree);
 		rb_erase(&user->node, &key_user_tree);
 		spin_unlock(&key_user_lock);
 		spin_unlock(&key_user_lock);
+		put_user_ns(user->user_ns);
 
 
 		kfree(user);
 		kfree(user);
 	}
 	}
@@ -234,7 +241,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
 	quotalen = desclen + type->def_datalen;
 	quotalen = desclen + type->def_datalen;
 
 
 	/* get hold of the key tracking for this user */
 	/* get hold of the key tracking for this user */
-	user = key_user_lookup(uid);
+	user = key_user_lookup(uid, cred->user->user_ns);
 	if (!user)
 	if (!user)
 		goto no_memory_1;
 		goto no_memory_1;
 
 

+ 1 - 1
security/keys/keyctl.c

@@ -726,7 +726,7 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
 	/* change the UID */
 	/* change the UID */
 	if (uid != (uid_t) -1 && uid != key->uid) {
 	if (uid != (uid_t) -1 && uid != key->uid) {
 		ret = -ENOMEM;
 		ret = -ENOMEM;
-		newowner = key_user_lookup(uid);
+		newowner = key_user_lookup(uid, current_user_ns());
 		if (!newowner)
 		if (!newowner)
 			goto error_put;
 			goto error_put;
 
 

+ 3 - 0
security/keys/keyring.c

@@ -539,6 +539,9 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check)
 				    &keyring_name_hash[bucket],
 				    &keyring_name_hash[bucket],
 				    type_data.link
 				    type_data.link
 				    ) {
 				    ) {
+			if (keyring->user->user_ns != current_user_ns())
+				continue;
+
 			if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
 			if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
 				continue;
 				continue;
 
 

+ 5 - 0
security/keys/permission.c

@@ -35,6 +35,9 @@ int key_task_permission(const key_ref_t key_ref, const struct cred *cred,
 
 
 	key = key_ref_to_ptr(key_ref);
 	key = key_ref_to_ptr(key_ref);
 
 
+	if (key->user->user_ns != cred->user->user_ns)
+		goto use_other_perms;
+
 	/* use the second 8-bits of permissions for keys the caller owns */
 	/* use the second 8-bits of permissions for keys the caller owns */
 	if (key->uid == cred->fsuid) {
 	if (key->uid == cred->fsuid) {
 		kperm = key->perm >> 16;
 		kperm = key->perm >> 16;
@@ -56,6 +59,8 @@ int key_task_permission(const key_ref_t key_ref, const struct cred *cred,
 		}
 		}
 	}
 	}
 
 
+use_other_perms:
+
 	/* otherwise use the least-significant 8-bits */
 	/* otherwise use the least-significant 8-bits */
 	kperm = key->perm;
 	kperm = key->perm;
 
 

+ 49 - 6
security/keys/proc.c

@@ -91,6 +91,28 @@ __initcall(key_proc_init);
  */
  */
 #ifdef CONFIG_KEYS_DEBUG_PROC_KEYS
 #ifdef CONFIG_KEYS_DEBUG_PROC_KEYS
 
 
+static struct rb_node *__key_serial_next(struct rb_node *n)
+{
+	while (n) {
+		struct key *key = rb_entry(n, struct key, serial_node);
+		if (key->user->user_ns == current_user_ns())
+			break;
+		n = rb_next(n);
+	}
+	return n;
+}
+
+static struct rb_node *key_serial_next(struct rb_node *n)
+{
+	return __key_serial_next(rb_next(n));
+}
+
+static struct rb_node *key_serial_first(struct rb_root *r)
+{
+	struct rb_node *n = rb_first(r);
+	return __key_serial_next(n);
+}
+
 static int proc_keys_open(struct inode *inode, struct file *file)
 static int proc_keys_open(struct inode *inode, struct file *file)
 {
 {
 	return seq_open(file, &proc_keys_ops);
 	return seq_open(file, &proc_keys_ops);
@@ -104,10 +126,10 @@ static void *proc_keys_start(struct seq_file *p, loff_t *_pos)
 
 
 	spin_lock(&key_serial_lock);
 	spin_lock(&key_serial_lock);
 
 
-	_p = rb_first(&key_serial_tree);
+	_p = key_serial_first(&key_serial_tree);
 	while (pos > 0 && _p) {
 	while (pos > 0 && _p) {
 		pos--;
 		pos--;
-		_p = rb_next(_p);
+		_p = key_serial_next(_p);
 	}
 	}
 
 
 	return _p;
 	return _p;
@@ -117,7 +139,7 @@ static void *proc_keys_start(struct seq_file *p, loff_t *_pos)
 static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos)
 static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos)
 {
 {
 	(*_pos)++;
 	(*_pos)++;
-	return rb_next((struct rb_node *) v);
+	return key_serial_next((struct rb_node *) v);
 
 
 }
 }
 
 
@@ -203,6 +225,27 @@ static int proc_keys_show(struct seq_file *m, void *v)
 
 
 #endif /* CONFIG_KEYS_DEBUG_PROC_KEYS */
 #endif /* CONFIG_KEYS_DEBUG_PROC_KEYS */
 
 
+static struct rb_node *__key_user_next(struct rb_node *n)
+{
+	while (n) {
+		struct key_user *user = rb_entry(n, struct key_user, node);
+		if (user->user_ns == current_user_ns())
+			break;
+		n = rb_next(n);
+	}
+	return n;
+}
+
+static struct rb_node *key_user_next(struct rb_node *n)
+{
+	return __key_user_next(rb_next(n));
+}
+
+static struct rb_node *key_user_first(struct rb_root *r)
+{
+	struct rb_node *n = rb_first(r);
+	return __key_user_next(n);
+}
 /*****************************************************************************/
 /*****************************************************************************/
 /*
 /*
  * implement "/proc/key-users" to provides a list of the key users
  * implement "/proc/key-users" to provides a list of the key users
@@ -220,10 +263,10 @@ static void *proc_key_users_start(struct seq_file *p, loff_t *_pos)
 
 
 	spin_lock(&key_user_lock);
 	spin_lock(&key_user_lock);
 
 
-	_p = rb_first(&key_user_tree);
+	_p = key_user_first(&key_user_tree);
 	while (pos > 0 && _p) {
 	while (pos > 0 && _p) {
 		pos--;
 		pos--;
-		_p = rb_next(_p);
+		_p = key_user_next(_p);
 	}
 	}
 
 
 	return _p;
 	return _p;
@@ -233,7 +276,7 @@ static void *proc_key_users_start(struct seq_file *p, loff_t *_pos)
 static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos)
 static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos)
 {
 {
 	(*_pos)++;
 	(*_pos)++;
-	return rb_next((struct rb_node *) v);
+	return key_user_next((struct rb_node *) v);
 
 
 }
 }
 
 

+ 2 - 0
security/keys/process_keys.c

@@ -17,6 +17,7 @@
 #include <linux/fs.h>
 #include <linux/fs.h>
 #include <linux/err.h>
 #include <linux/err.h>
 #include <linux/mutex.h>
 #include <linux/mutex.h>
+#include <linux/user_namespace.h>
 #include <asm/uaccess.h>
 #include <asm/uaccess.h>
 #include "internal.h"
 #include "internal.h"
 
 
@@ -34,6 +35,7 @@ struct key_user root_key_user = {
 	.nkeys		= ATOMIC_INIT(2),
 	.nkeys		= ATOMIC_INIT(2),
 	.nikeys		= ATOMIC_INIT(2),
 	.nikeys		= ATOMIC_INIT(2),
 	.uid		= 0,
 	.uid		= 0,
+	.user_ns	= &init_user_ns,
 };
 };
 
 
 /*****************************************************************************/
 /*****************************************************************************/

+ 1 - 1
security/keys/request_key.c

@@ -365,7 +365,7 @@ static struct key *construct_key_and_link(struct key_type *type,
 
 
 	kenter("");
 	kenter("");
 
 
-	user = key_user_lookup(current_fsuid());
+	user = key_user_lookup(current_fsuid(), current_user_ns());
 	if (!user)
 	if (!user)
 		return ERR_PTR(-ENOMEM);
 		return ERR_PTR(-ENOMEM);
 
 

+ 95 - 74
security/selinux/avc.c

@@ -88,17 +88,16 @@ struct avc_entry {
 	u32			tsid;
 	u32			tsid;
 	u16			tclass;
 	u16			tclass;
 	struct av_decision	avd;
 	struct av_decision	avd;
-	atomic_t		used;	/* used recently */
 };
 };
 
 
 struct avc_node {
 struct avc_node {
 	struct avc_entry	ae;
 	struct avc_entry	ae;
-	struct list_head	list;
+	struct hlist_node	list; /* anchored in avc_cache->slots[i] */
 	struct rcu_head		rhead;
 	struct rcu_head		rhead;
 };
 };
 
 
 struct avc_cache {
 struct avc_cache {
-	struct list_head	slots[AVC_CACHE_SLOTS];
+	struct hlist_head	slots[AVC_CACHE_SLOTS]; /* head for avc_node->list */
 	spinlock_t		slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */
 	spinlock_t		slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */
 	atomic_t		lru_hint;	/* LRU hint for reclaim scan */
 	atomic_t		lru_hint;	/* LRU hint for reclaim scan */
 	atomic_t		active_nodes;
 	atomic_t		active_nodes;
@@ -234,7 +233,7 @@ void __init avc_init(void)
 	int i;
 	int i;
 
 
 	for (i = 0; i < AVC_CACHE_SLOTS; i++) {
 	for (i = 0; i < AVC_CACHE_SLOTS; i++) {
-		INIT_LIST_HEAD(&avc_cache.slots[i]);
+		INIT_HLIST_HEAD(&avc_cache.slots[i]);
 		spin_lock_init(&avc_cache.slots_lock[i]);
 		spin_lock_init(&avc_cache.slots_lock[i]);
 	}
 	}
 	atomic_set(&avc_cache.active_nodes, 0);
 	atomic_set(&avc_cache.active_nodes, 0);
@@ -250,16 +249,20 @@ int avc_get_hash_stats(char *page)
 {
 {
 	int i, chain_len, max_chain_len, slots_used;
 	int i, chain_len, max_chain_len, slots_used;
 	struct avc_node *node;
 	struct avc_node *node;
+	struct hlist_head *head;
 
 
 	rcu_read_lock();
 	rcu_read_lock();
 
 
 	slots_used = 0;
 	slots_used = 0;
 	max_chain_len = 0;
 	max_chain_len = 0;
 	for (i = 0; i < AVC_CACHE_SLOTS; i++) {
 	for (i = 0; i < AVC_CACHE_SLOTS; i++) {
-		if (!list_empty(&avc_cache.slots[i])) {
+		head = &avc_cache.slots[i];
+		if (!hlist_empty(head)) {
+			struct hlist_node *next;
+
 			slots_used++;
 			slots_used++;
 			chain_len = 0;
 			chain_len = 0;
-			list_for_each_entry_rcu(node, &avc_cache.slots[i], list)
+			hlist_for_each_entry_rcu(node, next, head, list)
 				chain_len++;
 				chain_len++;
 			if (chain_len > max_chain_len)
 			if (chain_len > max_chain_len)
 				max_chain_len = chain_len;
 				max_chain_len = chain_len;
@@ -283,7 +286,7 @@ static void avc_node_free(struct rcu_head *rhead)
 
 
 static void avc_node_delete(struct avc_node *node)
 static void avc_node_delete(struct avc_node *node)
 {
 {
-	list_del_rcu(&node->list);
+	hlist_del_rcu(&node->list);
 	call_rcu(&node->rhead, avc_node_free);
 	call_rcu(&node->rhead, avc_node_free);
 	atomic_dec(&avc_cache.active_nodes);
 	atomic_dec(&avc_cache.active_nodes);
 }
 }
@@ -297,7 +300,7 @@ static void avc_node_kill(struct avc_node *node)
 
 
 static void avc_node_replace(struct avc_node *new, struct avc_node *old)
 static void avc_node_replace(struct avc_node *new, struct avc_node *old)
 {
 {
-	list_replace_rcu(&old->list, &new->list);
+	hlist_replace_rcu(&old->list, &new->list);
 	call_rcu(&old->rhead, avc_node_free);
 	call_rcu(&old->rhead, avc_node_free);
 	atomic_dec(&avc_cache.active_nodes);
 	atomic_dec(&avc_cache.active_nodes);
 }
 }
@@ -307,29 +310,31 @@ static inline int avc_reclaim_node(void)
 	struct avc_node *node;
 	struct avc_node *node;
 	int hvalue, try, ecx;
 	int hvalue, try, ecx;
 	unsigned long flags;
 	unsigned long flags;
+	struct hlist_head *head;
+	struct hlist_node *next;
+	spinlock_t *lock;
 
 
 	for (try = 0, ecx = 0; try < AVC_CACHE_SLOTS; try++) {
 	for (try = 0, ecx = 0; try < AVC_CACHE_SLOTS; try++) {
 		hvalue = atomic_inc_return(&avc_cache.lru_hint) & (AVC_CACHE_SLOTS - 1);
 		hvalue = atomic_inc_return(&avc_cache.lru_hint) & (AVC_CACHE_SLOTS - 1);
+		head = &avc_cache.slots[hvalue];
+		lock = &avc_cache.slots_lock[hvalue];
 
 
-		if (!spin_trylock_irqsave(&avc_cache.slots_lock[hvalue], flags))
+		if (!spin_trylock_irqsave(lock, flags))
 			continue;
 			continue;
 
 
 		rcu_read_lock();
 		rcu_read_lock();
-		list_for_each_entry(node, &avc_cache.slots[hvalue], list) {
-			if (atomic_dec_and_test(&node->ae.used)) {
-				/* Recently Unused */
-				avc_node_delete(node);
-				avc_cache_stats_incr(reclaims);
-				ecx++;
-				if (ecx >= AVC_CACHE_RECLAIM) {
-					rcu_read_unlock();
-					spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flags);
-					goto out;
-				}
+		hlist_for_each_entry(node, next, head, list) {
+			avc_node_delete(node);
+			avc_cache_stats_incr(reclaims);
+			ecx++;
+			if (ecx >= AVC_CACHE_RECLAIM) {
+				rcu_read_unlock();
+				spin_unlock_irqrestore(lock, flags);
+				goto out;
 			}
 			}
 		}
 		}
 		rcu_read_unlock();
 		rcu_read_unlock();
-		spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flags);
+		spin_unlock_irqrestore(lock, flags);
 	}
 	}
 out:
 out:
 	return ecx;
 	return ecx;
@@ -344,8 +349,7 @@ static struct avc_node *avc_alloc_node(void)
 		goto out;
 		goto out;
 
 
 	INIT_RCU_HEAD(&node->rhead);
 	INIT_RCU_HEAD(&node->rhead);
-	INIT_LIST_HEAD(&node->list);
-	atomic_set(&node->ae.used, 1);
+	INIT_HLIST_NODE(&node->list);
 	avc_cache_stats_incr(allocations);
 	avc_cache_stats_incr(allocations);
 
 
 	if (atomic_inc_return(&avc_cache.active_nodes) > avc_cache_threshold)
 	if (atomic_inc_return(&avc_cache.active_nodes) > avc_cache_threshold)
@@ -355,21 +359,24 @@ out:
 	return node;
 	return node;
 }
 }
 
 
-static void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, u16 tclass, struct avc_entry *ae)
+static void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd)
 {
 {
 	node->ae.ssid = ssid;
 	node->ae.ssid = ssid;
 	node->ae.tsid = tsid;
 	node->ae.tsid = tsid;
 	node->ae.tclass = tclass;
 	node->ae.tclass = tclass;
-	memcpy(&node->ae.avd, &ae->avd, sizeof(node->ae.avd));
+	memcpy(&node->ae.avd, avd, sizeof(node->ae.avd));
 }
 }
 
 
 static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass)
 static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass)
 {
 {
 	struct avc_node *node, *ret = NULL;
 	struct avc_node *node, *ret = NULL;
 	int hvalue;
 	int hvalue;
+	struct hlist_head *head;
+	struct hlist_node *next;
 
 
 	hvalue = avc_hash(ssid, tsid, tclass);
 	hvalue = avc_hash(ssid, tsid, tclass);
-	list_for_each_entry_rcu(node, &avc_cache.slots[hvalue], list) {
+	head = &avc_cache.slots[hvalue];
+	hlist_for_each_entry_rcu(node, next, head, list) {
 		if (ssid == node->ae.ssid &&
 		if (ssid == node->ae.ssid &&
 		    tclass == node->ae.tclass &&
 		    tclass == node->ae.tclass &&
 		    tsid == node->ae.tsid) {
 		    tsid == node->ae.tsid) {
@@ -378,15 +385,6 @@ static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass)
 		}
 		}
 	}
 	}
 
 
-	if (ret == NULL) {
-		/* cache miss */
-		goto out;
-	}
-
-	/* cache hit */
-	if (atomic_read(&ret->ae.used) != 1)
-		atomic_set(&ret->ae.used, 1);
-out:
 	return ret;
 	return ret;
 }
 }
 
 
@@ -395,30 +393,25 @@ out:
  * @ssid: source security identifier
  * @ssid: source security identifier
  * @tsid: target security identifier
  * @tsid: target security identifier
  * @tclass: target security class
  * @tclass: target security class
- * @requested: requested permissions, interpreted based on @tclass
  *
  *
  * Look up an AVC entry that is valid for the
  * Look up an AVC entry that is valid for the
- * @requested permissions between the SID pair
  * (@ssid, @tsid), interpreting the permissions
  * (@ssid, @tsid), interpreting the permissions
  * based on @tclass.  If a valid AVC entry exists,
  * based on @tclass.  If a valid AVC entry exists,
  * then this function return the avc_node.
  * then this function return the avc_node.
  * Otherwise, this function returns NULL.
  * Otherwise, this function returns NULL.
  */
  */
-static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass, u32 requested)
+static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass)
 {
 {
 	struct avc_node *node;
 	struct avc_node *node;
 
 
 	avc_cache_stats_incr(lookups);
 	avc_cache_stats_incr(lookups);
 	node = avc_search_node(ssid, tsid, tclass);
 	node = avc_search_node(ssid, tsid, tclass);
 
 
-	if (node && ((node->ae.avd.decided & requested) == requested)) {
+	if (node)
 		avc_cache_stats_incr(hits);
 		avc_cache_stats_incr(hits);
-		goto out;
-	}
+	else
+		avc_cache_stats_incr(misses);
 
 
-	node = NULL;
-	avc_cache_stats_incr(misses);
-out:
 	return node;
 	return node;
 }
 }
 
 
@@ -449,34 +442,41 @@ static int avc_latest_notif_update(int seqno, int is_insert)
  * @ssid: source security identifier
  * @ssid: source security identifier
  * @tsid: target security identifier
  * @tsid: target security identifier
  * @tclass: target security class
  * @tclass: target security class
- * @ae: AVC entry
+ * @avd: resulting av decision
  *
  *
  * Insert an AVC entry for the SID pair
  * Insert an AVC entry for the SID pair
  * (@ssid, @tsid) and class @tclass.
  * (@ssid, @tsid) and class @tclass.
  * The access vectors and the sequence number are
  * The access vectors and the sequence number are
  * normally provided by the security server in
  * normally provided by the security server in
  * response to a security_compute_av() call.  If the
  * response to a security_compute_av() call.  If the
- * sequence number @ae->avd.seqno is not less than the latest
+ * sequence number @avd->seqno is not less than the latest
  * revocation notification, then the function copies
  * revocation notification, then the function copies
  * the access vectors into a cache entry, returns
  * the access vectors into a cache entry, returns
  * avc_node inserted. Otherwise, this function returns NULL.
  * avc_node inserted. Otherwise, this function returns NULL.
  */
  */
-static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct avc_entry *ae)
+static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd)
 {
 {
 	struct avc_node *pos, *node = NULL;
 	struct avc_node *pos, *node = NULL;
 	int hvalue;
 	int hvalue;
 	unsigned long flag;
 	unsigned long flag;
 
 
-	if (avc_latest_notif_update(ae->avd.seqno, 1))
+	if (avc_latest_notif_update(avd->seqno, 1))
 		goto out;
 		goto out;
 
 
 	node = avc_alloc_node();
 	node = avc_alloc_node();
 	if (node) {
 	if (node) {
+		struct hlist_head *head;
+		struct hlist_node *next;
+		spinlock_t *lock;
+
 		hvalue = avc_hash(ssid, tsid, tclass);
 		hvalue = avc_hash(ssid, tsid, tclass);
-		avc_node_populate(node, ssid, tsid, tclass, ae);
+		avc_node_populate(node, ssid, tsid, tclass, avd);
+
+		head = &avc_cache.slots[hvalue];
+		lock = &avc_cache.slots_lock[hvalue];
 
 
-		spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag);
-		list_for_each_entry(pos, &avc_cache.slots[hvalue], list) {
+		spin_lock_irqsave(lock, flag);
+		hlist_for_each_entry(pos, next, head, list) {
 			if (pos->ae.ssid == ssid &&
 			if (pos->ae.ssid == ssid &&
 			    pos->ae.tsid == tsid &&
 			    pos->ae.tsid == tsid &&
 			    pos->ae.tclass == tclass) {
 			    pos->ae.tclass == tclass) {
@@ -484,9 +484,9 @@ static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct avc_en
 				goto found;
 				goto found;
 			}
 			}
 		}
 		}
-		list_add_rcu(&node->list, &avc_cache.slots[hvalue]);
+		hlist_add_head_rcu(&node->list, head);
 found:
 found:
-		spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag);
+		spin_unlock_irqrestore(lock, flag);
 	}
 	}
 out:
 out:
 	return node;
 	return node;
@@ -742,17 +742,22 @@ static inline int avc_sidcmp(u32 x, u32 y)
  * @event : Updating event
  * @event : Updating event
  * @perms : Permission mask bits
  * @perms : Permission mask bits
  * @ssid,@tsid,@tclass : identifier of an AVC entry
  * @ssid,@tsid,@tclass : identifier of an AVC entry
+ * @seqno : sequence number when decision was made
  *
  *
  * if a valid AVC entry doesn't exist,this function returns -ENOENT.
  * if a valid AVC entry doesn't exist,this function returns -ENOENT.
  * if kmalloc() called internal returns NULL, this function returns -ENOMEM.
  * if kmalloc() called internal returns NULL, this function returns -ENOMEM.
  * otherwise, this function update the AVC entry. The original AVC-entry object
  * otherwise, this function update the AVC entry. The original AVC-entry object
  * will release later by RCU.
  * will release later by RCU.
  */
  */
-static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass)
+static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass,
+			   u32 seqno)
 {
 {
 	int hvalue, rc = 0;
 	int hvalue, rc = 0;
 	unsigned long flag;
 	unsigned long flag;
 	struct avc_node *pos, *node, *orig = NULL;
 	struct avc_node *pos, *node, *orig = NULL;
+	struct hlist_head *head;
+	struct hlist_node *next;
+	spinlock_t *lock;
 
 
 	node = avc_alloc_node();
 	node = avc_alloc_node();
 	if (!node) {
 	if (!node) {
@@ -762,12 +767,17 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass)
 
 
 	/* Lock the target slot */
 	/* Lock the target slot */
 	hvalue = avc_hash(ssid, tsid, tclass);
 	hvalue = avc_hash(ssid, tsid, tclass);
-	spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag);
 
 
-	list_for_each_entry(pos, &avc_cache.slots[hvalue], list) {
+	head = &avc_cache.slots[hvalue];
+	lock = &avc_cache.slots_lock[hvalue];
+
+	spin_lock_irqsave(lock, flag);
+
+	hlist_for_each_entry(pos, next, head, list) {
 		if (ssid == pos->ae.ssid &&
 		if (ssid == pos->ae.ssid &&
 		    tsid == pos->ae.tsid &&
 		    tsid == pos->ae.tsid &&
-		    tclass == pos->ae.tclass){
+		    tclass == pos->ae.tclass &&
+		    seqno == pos->ae.avd.seqno){
 			orig = pos;
 			orig = pos;
 			break;
 			break;
 		}
 		}
@@ -783,7 +793,7 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass)
 	 * Copy and replace original node.
 	 * Copy and replace original node.
 	 */
 	 */
 
 
-	avc_node_populate(node, ssid, tsid, tclass, &orig->ae);
+	avc_node_populate(node, ssid, tsid, tclass, &orig->ae.avd);
 
 
 	switch (event) {
 	switch (event) {
 	case AVC_CALLBACK_GRANT:
 	case AVC_CALLBACK_GRANT:
@@ -808,7 +818,7 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass)
 	}
 	}
 	avc_node_replace(node, orig);
 	avc_node_replace(node, orig);
 out_unlock:
 out_unlock:
-	spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag);
+	spin_unlock_irqrestore(lock, flag);
 out:
 out:
 	return rc;
 	return rc;
 }
 }
@@ -823,18 +833,24 @@ int avc_ss_reset(u32 seqno)
 	int i, rc = 0, tmprc;
 	int i, rc = 0, tmprc;
 	unsigned long flag;
 	unsigned long flag;
 	struct avc_node *node;
 	struct avc_node *node;
+	struct hlist_head *head;
+	struct hlist_node *next;
+	spinlock_t *lock;
 
 
 	for (i = 0; i < AVC_CACHE_SLOTS; i++) {
 	for (i = 0; i < AVC_CACHE_SLOTS; i++) {
-		spin_lock_irqsave(&avc_cache.slots_lock[i], flag);
+		head = &avc_cache.slots[i];
+		lock = &avc_cache.slots_lock[i];
+
+		spin_lock_irqsave(lock, flag);
 		/*
 		/*
 		 * With preemptable RCU, the outer spinlock does not
 		 * With preemptable RCU, the outer spinlock does not
 		 * prevent RCU grace periods from ending.
 		 * prevent RCU grace periods from ending.
 		 */
 		 */
 		rcu_read_lock();
 		rcu_read_lock();
-		list_for_each_entry(node, &avc_cache.slots[i], list)
+		hlist_for_each_entry(node, next, head, list)
 			avc_node_delete(node);
 			avc_node_delete(node);
 		rcu_read_unlock();
 		rcu_read_unlock();
-		spin_unlock_irqrestore(&avc_cache.slots_lock[i], flag);
+		spin_unlock_irqrestore(lock, flag);
 	}
 	}
 
 
 	for (c = avc_callbacks; c; c = c->next) {
 	for (c = avc_callbacks; c; c = c->next) {
@@ -875,10 +891,10 @@ int avc_ss_reset(u32 seqno)
 int avc_has_perm_noaudit(u32 ssid, u32 tsid,
 int avc_has_perm_noaudit(u32 ssid, u32 tsid,
 			 u16 tclass, u32 requested,
 			 u16 tclass, u32 requested,
 			 unsigned flags,
 			 unsigned flags,
-			 struct av_decision *avd)
+			 struct av_decision *in_avd)
 {
 {
 	struct avc_node *node;
 	struct avc_node *node;
-	struct avc_entry entry, *p_ae;
+	struct av_decision avd_entry, *avd;
 	int rc = 0;
 	int rc = 0;
 	u32 denied;
 	u32 denied;
 
 
@@ -886,29 +902,34 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
 
 
 	rcu_read_lock();
 	rcu_read_lock();
 
 
-	node = avc_lookup(ssid, tsid, tclass, requested);
+	node = avc_lookup(ssid, tsid, tclass);
 	if (!node) {
 	if (!node) {
 		rcu_read_unlock();
 		rcu_read_unlock();
-		rc = security_compute_av(ssid, tsid, tclass, requested, &entry.avd);
+
+		if (in_avd)
+			avd = in_avd;
+		else
+			avd = &avd_entry;
+
+		rc = security_compute_av(ssid, tsid, tclass, requested, avd);
 		if (rc)
 		if (rc)
 			goto out;
 			goto out;
 		rcu_read_lock();
 		rcu_read_lock();
-		node = avc_insert(ssid, tsid, tclass, &entry);
+		node = avc_insert(ssid, tsid, tclass, avd);
+	} else {
+		if (in_avd)
+			memcpy(in_avd, &node->ae.avd, sizeof(*in_avd));
+		avd = &node->ae.avd;
 	}
 	}
 
 
-	p_ae = node ? &node->ae : &entry;
-
-	if (avd)
-		memcpy(avd, &p_ae->avd, sizeof(*avd));
-
-	denied = requested & ~(p_ae->avd.allowed);
+	denied = requested & ~(avd->allowed);
 
 
 	if (denied) {
 	if (denied) {
 		if (flags & AVC_STRICT)
 		if (flags & AVC_STRICT)
 			rc = -EACCES;
 			rc = -EACCES;
 		else if (!selinux_enforcing || security_permissive_sid(ssid))
 		else if (!selinux_enforcing || security_permissive_sid(ssid))
 			avc_update_node(AVC_CALLBACK_GRANT, requested, ssid,
 			avc_update_node(AVC_CALLBACK_GRANT, requested, ssid,
-					tsid, tclass);
+					tsid, tclass, avd->seqno);
 		else
 		else
 			rc = -EACCES;
 			rc = -EACCES;
 	}
 	}

+ 107 - 201
security/selinux/hooks.c

@@ -89,7 +89,7 @@
 #define XATTR_SELINUX_SUFFIX "selinux"
 #define XATTR_SELINUX_SUFFIX "selinux"
 #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
 #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
 
 
-#define NUM_SEL_MNT_OPTS 4
+#define NUM_SEL_MNT_OPTS 5
 
 
 extern unsigned int policydb_loaded_version;
 extern unsigned int policydb_loaded_version;
 extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
 extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
@@ -353,6 +353,7 @@ enum {
 	Opt_fscontext = 2,
 	Opt_fscontext = 2,
 	Opt_defcontext = 3,
 	Opt_defcontext = 3,
 	Opt_rootcontext = 4,
 	Opt_rootcontext = 4,
+	Opt_labelsupport = 5,
 };
 };
 
 
 static const match_table_t tokens = {
 static const match_table_t tokens = {
@@ -360,6 +361,7 @@ static const match_table_t tokens = {
 	{Opt_fscontext, FSCONTEXT_STR "%s"},
 	{Opt_fscontext, FSCONTEXT_STR "%s"},
 	{Opt_defcontext, DEFCONTEXT_STR "%s"},
 	{Opt_defcontext, DEFCONTEXT_STR "%s"},
 	{Opt_rootcontext, ROOTCONTEXT_STR "%s"},
 	{Opt_rootcontext, ROOTCONTEXT_STR "%s"},
+	{Opt_labelsupport, LABELSUPP_STR},
 	{Opt_error, NULL},
 	{Opt_error, NULL},
 };
 };
 
 
@@ -431,7 +433,7 @@ static int sb_finish_set_opts(struct super_block *sb)
 		}
 		}
 	}
 	}
 
 
-	sbsec->initialized = 1;
+	sbsec->flags |= (SE_SBINITIALIZED | SE_SBLABELSUPP);
 
 
 	if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors))
 	if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors))
 		printk(KERN_ERR "SELinux: initialized (dev %s, type %s), unknown behavior\n",
 		printk(KERN_ERR "SELinux: initialized (dev %s, type %s), unknown behavior\n",
@@ -441,6 +443,12 @@ static int sb_finish_set_opts(struct super_block *sb)
 		       sb->s_id, sb->s_type->name,
 		       sb->s_id, sb->s_type->name,
 		       labeling_behaviors[sbsec->behavior-1]);
 		       labeling_behaviors[sbsec->behavior-1]);
 
 
+	if (sbsec->behavior == SECURITY_FS_USE_GENFS ||
+	    sbsec->behavior == SECURITY_FS_USE_MNTPOINT ||
+	    sbsec->behavior == SECURITY_FS_USE_NONE ||
+	    sbsec->behavior > ARRAY_SIZE(labeling_behaviors))
+		sbsec->flags &= ~SE_SBLABELSUPP;
+
 	/* Initialize the root inode. */
 	/* Initialize the root inode. */
 	rc = inode_doinit_with_dentry(root_inode, root);
 	rc = inode_doinit_with_dentry(root_inode, root);
 
 
@@ -487,23 +495,22 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
 
 
 	security_init_mnt_opts(opts);
 	security_init_mnt_opts(opts);
 
 
-	if (!sbsec->initialized)
+	if (!(sbsec->flags & SE_SBINITIALIZED))
 		return -EINVAL;
 		return -EINVAL;
 
 
 	if (!ss_initialized)
 	if (!ss_initialized)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	/*
-	 * if we ever use sbsec flags for anything other than tracking mount
-	 * settings this is going to need a mask
-	 */
-	tmp = sbsec->flags;
+	tmp = sbsec->flags & SE_MNTMASK;
 	/* count the number of mount options for this sb */
 	/* count the number of mount options for this sb */
 	for (i = 0; i < 8; i++) {
 	for (i = 0; i < 8; i++) {
 		if (tmp & 0x01)
 		if (tmp & 0x01)
 			opts->num_mnt_opts++;
 			opts->num_mnt_opts++;
 		tmp >>= 1;
 		tmp >>= 1;
 	}
 	}
+	/* Check if the Label support flag is set */
+	if (sbsec->flags & SE_SBLABELSUPP)
+		opts->num_mnt_opts++;
 
 
 	opts->mnt_opts = kcalloc(opts->num_mnt_opts, sizeof(char *), GFP_ATOMIC);
 	opts->mnt_opts = kcalloc(opts->num_mnt_opts, sizeof(char *), GFP_ATOMIC);
 	if (!opts->mnt_opts) {
 	if (!opts->mnt_opts) {
@@ -549,6 +556,10 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
 		opts->mnt_opts[i] = context;
 		opts->mnt_opts[i] = context;
 		opts->mnt_opts_flags[i++] = ROOTCONTEXT_MNT;
 		opts->mnt_opts_flags[i++] = ROOTCONTEXT_MNT;
 	}
 	}
+	if (sbsec->flags & SE_SBLABELSUPP) {
+		opts->mnt_opts[i] = NULL;
+		opts->mnt_opts_flags[i++] = SE_SBLABELSUPP;
+	}
 
 
 	BUG_ON(i != opts->num_mnt_opts);
 	BUG_ON(i != opts->num_mnt_opts);
 
 
@@ -562,8 +573,10 @@ out_free:
 static int bad_option(struct superblock_security_struct *sbsec, char flag,
 static int bad_option(struct superblock_security_struct *sbsec, char flag,
 		      u32 old_sid, u32 new_sid)
 		      u32 old_sid, u32 new_sid)
 {
 {
+	char mnt_flags = sbsec->flags & SE_MNTMASK;
+
 	/* check if the old mount command had the same options */
 	/* check if the old mount command had the same options */
-	if (sbsec->initialized)
+	if (sbsec->flags & SE_SBINITIALIZED)
 		if (!(sbsec->flags & flag) ||
 		if (!(sbsec->flags & flag) ||
 		    (old_sid != new_sid))
 		    (old_sid != new_sid))
 			return 1;
 			return 1;
@@ -571,8 +584,8 @@ static int bad_option(struct superblock_security_struct *sbsec, char flag,
 	/* check if we were passed the same options twice,
 	/* check if we were passed the same options twice,
 	 * aka someone passed context=a,context=b
 	 * aka someone passed context=a,context=b
 	 */
 	 */
-	if (!sbsec->initialized)
-		if (sbsec->flags & flag)
+	if (!(sbsec->flags & SE_SBINITIALIZED))
+		if (mnt_flags & flag)
 			return 1;
 			return 1;
 	return 0;
 	return 0;
 }
 }
@@ -626,7 +639,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
 	 * this sb does not set any security options.  (The first options
 	 * this sb does not set any security options.  (The first options
 	 * will be used for both mounts)
 	 * will be used for both mounts)
 	 */
 	 */
-	if (sbsec->initialized && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
+	if ((sbsec->flags & SE_SBINITIALIZED) && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
 	    && (num_opts == 0))
 	    && (num_opts == 0))
 		goto out;
 		goto out;
 
 
@@ -637,6 +650,9 @@ static int selinux_set_mnt_opts(struct super_block *sb,
 	 */
 	 */
 	for (i = 0; i < num_opts; i++) {
 	for (i = 0; i < num_opts; i++) {
 		u32 sid;
 		u32 sid;
+
+		if (flags[i] == SE_SBLABELSUPP)
+			continue;
 		rc = security_context_to_sid(mount_options[i],
 		rc = security_context_to_sid(mount_options[i],
 					     strlen(mount_options[i]), &sid);
 					     strlen(mount_options[i]), &sid);
 		if (rc) {
 		if (rc) {
@@ -690,19 +706,19 @@ static int selinux_set_mnt_opts(struct super_block *sb,
 		}
 		}
 	}
 	}
 
 
-	if (sbsec->initialized) {
+	if (sbsec->flags & SE_SBINITIALIZED) {
 		/* previously mounted with options, but not on this attempt? */
 		/* previously mounted with options, but not on this attempt? */
-		if (sbsec->flags && !num_opts)
+		if ((sbsec->flags & SE_MNTMASK) && !num_opts)
 			goto out_double_mount;
 			goto out_double_mount;
 		rc = 0;
 		rc = 0;
 		goto out;
 		goto out;
 	}
 	}
 
 
 	if (strcmp(sb->s_type->name, "proc") == 0)
 	if (strcmp(sb->s_type->name, "proc") == 0)
-		sbsec->proc = 1;
+		sbsec->flags |= SE_SBPROC;
 
 
 	/* Determine the labeling behavior to use for this filesystem type. */
 	/* Determine the labeling behavior to use for this filesystem type. */
-	rc = security_fs_use(sbsec->proc ? "proc" : sb->s_type->name, &sbsec->behavior, &sbsec->sid);
+	rc = security_fs_use((sbsec->flags & SE_SBPROC) ? "proc" : sb->s_type->name, &sbsec->behavior, &sbsec->sid);
 	if (rc) {
 	if (rc) {
 		printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
 		printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
 		       __func__, sb->s_type->name, rc);
 		       __func__, sb->s_type->name, rc);
@@ -806,10 +822,10 @@ static void selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
 	}
 	}
 
 
 	/* how can we clone if the old one wasn't set up?? */
 	/* how can we clone if the old one wasn't set up?? */
-	BUG_ON(!oldsbsec->initialized);
+	BUG_ON(!(oldsbsec->flags & SE_SBINITIALIZED));
 
 
 	/* if fs is reusing a sb, just let its options stand... */
 	/* if fs is reusing a sb, just let its options stand... */
-	if (newsbsec->initialized)
+	if (newsbsec->flags & SE_SBINITIALIZED)
 		return;
 		return;
 
 
 	mutex_lock(&newsbsec->lock);
 	mutex_lock(&newsbsec->lock);
@@ -917,7 +933,8 @@ static int selinux_parse_opts_str(char *options,
 				goto out_err;
 				goto out_err;
 			}
 			}
 			break;
 			break;
-
+		case Opt_labelsupport:
+			break;
 		default:
 		default:
 			rc = -EINVAL;
 			rc = -EINVAL;
 			printk(KERN_WARNING "SELinux:  unknown mount option\n");
 			printk(KERN_WARNING "SELinux:  unknown mount option\n");
@@ -999,7 +1016,12 @@ static void selinux_write_opts(struct seq_file *m,
 	char *prefix;
 	char *prefix;
 
 
 	for (i = 0; i < opts->num_mnt_opts; i++) {
 	for (i = 0; i < opts->num_mnt_opts; i++) {
-		char *has_comma = strchr(opts->mnt_opts[i], ',');
+		char *has_comma;
+
+		if (opts->mnt_opts[i])
+			has_comma = strchr(opts->mnt_opts[i], ',');
+		else
+			has_comma = NULL;
 
 
 		switch (opts->mnt_opts_flags[i]) {
 		switch (opts->mnt_opts_flags[i]) {
 		case CONTEXT_MNT:
 		case CONTEXT_MNT:
@@ -1014,6 +1036,10 @@ static void selinux_write_opts(struct seq_file *m,
 		case DEFCONTEXT_MNT:
 		case DEFCONTEXT_MNT:
 			prefix = DEFCONTEXT_STR;
 			prefix = DEFCONTEXT_STR;
 			break;
 			break;
+		case SE_SBLABELSUPP:
+			seq_putc(m, ',');
+			seq_puts(m, LABELSUPP_STR);
+			continue;
 		default:
 		default:
 			BUG();
 			BUG();
 		};
 		};
@@ -1209,7 +1235,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
 		goto out_unlock;
 		goto out_unlock;
 
 
 	sbsec = inode->i_sb->s_security;
 	sbsec = inode->i_sb->s_security;
-	if (!sbsec->initialized) {
+	if (!(sbsec->flags & SE_SBINITIALIZED)) {
 		/* Defer initialization until selinux_complete_init,
 		/* Defer initialization until selinux_complete_init,
 		   after the initial policy is loaded and the security
 		   after the initial policy is loaded and the security
 		   server is ready to handle calls. */
 		   server is ready to handle calls. */
@@ -1237,19 +1263,26 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
 			dentry = d_find_alias(inode);
 			dentry = d_find_alias(inode);
 		}
 		}
 		if (!dentry) {
 		if (!dentry) {
-			printk(KERN_WARNING "SELinux: %s:  no dentry for dev=%s "
-			       "ino=%ld\n", __func__, inode->i_sb->s_id,
-			       inode->i_ino);
+			/*
+			 * this is can be hit on boot when a file is accessed
+			 * before the policy is loaded.  When we load policy we
+			 * may find inodes that have no dentry on the
+			 * sbsec->isec_head list.  No reason to complain as these
+			 * will get fixed up the next time we go through
+			 * inode_doinit with a dentry, before these inodes could
+			 * be used again by userspace.
+			 */
 			goto out_unlock;
 			goto out_unlock;
 		}
 		}
 
 
 		len = INITCONTEXTLEN;
 		len = INITCONTEXTLEN;
-		context = kmalloc(len, GFP_NOFS);
+		context = kmalloc(len+1, GFP_NOFS);
 		if (!context) {
 		if (!context) {
 			rc = -ENOMEM;
 			rc = -ENOMEM;
 			dput(dentry);
 			dput(dentry);
 			goto out_unlock;
 			goto out_unlock;
 		}
 		}
+		context[len] = '\0';
 		rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX,
 		rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX,
 					   context, len);
 					   context, len);
 		if (rc == -ERANGE) {
 		if (rc == -ERANGE) {
@@ -1262,12 +1295,13 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
 			}
 			}
 			kfree(context);
 			kfree(context);
 			len = rc;
 			len = rc;
-			context = kmalloc(len, GFP_NOFS);
+			context = kmalloc(len+1, GFP_NOFS);
 			if (!context) {
 			if (!context) {
 				rc = -ENOMEM;
 				rc = -ENOMEM;
 				dput(dentry);
 				dput(dentry);
 				goto out_unlock;
 				goto out_unlock;
 			}
 			}
+			context[len] = '\0';
 			rc = inode->i_op->getxattr(dentry,
 			rc = inode->i_op->getxattr(dentry,
 						   XATTR_NAME_SELINUX,
 						   XATTR_NAME_SELINUX,
 						   context, len);
 						   context, len);
@@ -1289,10 +1323,19 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
 							     sbsec->def_sid,
 							     sbsec->def_sid,
 							     GFP_NOFS);
 							     GFP_NOFS);
 			if (rc) {
 			if (rc) {
-				printk(KERN_WARNING "SELinux: %s:  context_to_sid(%s) "
-				       "returned %d for dev=%s ino=%ld\n",
-				       __func__, context, -rc,
-				       inode->i_sb->s_id, inode->i_ino);
+				char *dev = inode->i_sb->s_id;
+				unsigned long ino = inode->i_ino;
+
+				if (rc == -EINVAL) {
+					if (printk_ratelimit())
+						printk(KERN_NOTICE "SELinux: inode=%lu on dev=%s was found to have an invalid "
+							"context=%s.  This indicates you may need to relabel the inode or the "
+							"filesystem in question.\n", ino, dev, context);
+				} else {
+					printk(KERN_WARNING "SELinux: %s:  context_to_sid(%s) "
+					       "returned %d for dev=%s ino=%ld\n",
+					       __func__, context, -rc, dev, ino);
+				}
 				kfree(context);
 				kfree(context);
 				/* Leave with the unlabeled SID */
 				/* Leave with the unlabeled SID */
 				rc = 0;
 				rc = 0;
@@ -1326,7 +1369,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
 		/* Default to the fs superblock SID. */
 		/* Default to the fs superblock SID. */
 		isec->sid = sbsec->sid;
 		isec->sid = sbsec->sid;
 
 
-		if (sbsec->proc && !S_ISLNK(inode->i_mode)) {
+		if ((sbsec->flags & SE_SBPROC) && !S_ISLNK(inode->i_mode)) {
 			struct proc_inode *proci = PROC_I(inode);
 			struct proc_inode *proci = PROC_I(inode);
 			if (proci->pde) {
 			if (proci->pde) {
 				isec->sclass = inode_mode_to_security_class(inode->i_mode);
 				isec->sclass = inode_mode_to_security_class(inode->i_mode);
@@ -1587,7 +1630,7 @@ static int may_create(struct inode *dir,
 	if (rc)
 	if (rc)
 		return rc;
 		return rc;
 
 
-	if (!newsid || sbsec->behavior == SECURITY_FS_USE_MNTPOINT) {
+	if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) {
 		rc = security_transition_sid(sid, dsec->sid, tclass, &newsid);
 		rc = security_transition_sid(sid, dsec->sid, tclass, &newsid);
 		if (rc)
 		if (rc)
 			return rc;
 			return rc;
@@ -1801,6 +1844,8 @@ static inline u32 open_file_to_av(struct file *file)
 			av |= FIFO_FILE__OPEN;
 			av |= FIFO_FILE__OPEN;
 		else if (S_ISDIR(mode))
 		else if (S_ISDIR(mode))
 			av |= DIR__OPEN;
 			av |= DIR__OPEN;
+		else if (S_ISSOCK(mode))
+			av |= SOCK_FILE__OPEN;
 		else
 		else
 			printk(KERN_ERR "SELinux: WARNING: inside %s with "
 			printk(KERN_ERR "SELinux: WARNING: inside %s with "
 				"unknown mode:%o\n", __func__, mode);
 				"unknown mode:%o\n", __func__, mode);
@@ -1815,7 +1860,7 @@ static int selinux_ptrace_may_access(struct task_struct *child,
 {
 {
 	int rc;
 	int rc;
 
 
-	rc = secondary_ops->ptrace_may_access(child, mode);
+	rc = cap_ptrace_may_access(child, mode);
 	if (rc)
 	if (rc)
 		return rc;
 		return rc;
 
 
@@ -1832,7 +1877,7 @@ static int selinux_ptrace_traceme(struct task_struct *parent)
 {
 {
 	int rc;
 	int rc;
 
 
-	rc = secondary_ops->ptrace_traceme(parent);
+	rc = cap_ptrace_traceme(parent);
 	if (rc)
 	if (rc)
 		return rc;
 		return rc;
 
 
@@ -1848,7 +1893,7 @@ static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
 	if (error)
 	if (error)
 		return error;
 		return error;
 
 
-	return secondary_ops->capget(target, effective, inheritable, permitted);
+	return cap_capget(target, effective, inheritable, permitted);
 }
 }
 
 
 static int selinux_capset(struct cred *new, const struct cred *old,
 static int selinux_capset(struct cred *new, const struct cred *old,
@@ -1858,7 +1903,7 @@ static int selinux_capset(struct cred *new, const struct cred *old,
 {
 {
 	int error;
 	int error;
 
 
-	error = secondary_ops->capset(new, old,
+	error = cap_capset(new, old,
 				      effective, inheritable, permitted);
 				      effective, inheritable, permitted);
 	if (error)
 	if (error)
 		return error;
 		return error;
@@ -1866,12 +1911,22 @@ static int selinux_capset(struct cred *new, const struct cred *old,
 	return cred_has_perm(old, new, PROCESS__SETCAP);
 	return cred_has_perm(old, new, PROCESS__SETCAP);
 }
 }
 
 
+/*
+ * (This comment used to live with the selinux_task_setuid hook,
+ * which was removed).
+ *
+ * Since setuid only affects the current process, and since the SELinux
+ * controls are not based on the Linux identity attributes, SELinux does not
+ * need to control this operation.  However, SELinux does control the use of
+ * the CAP_SETUID and CAP_SETGID capabilities using the capable hook.
+ */
+
 static int selinux_capable(struct task_struct *tsk, const struct cred *cred,
 static int selinux_capable(struct task_struct *tsk, const struct cred *cred,
 			   int cap, int audit)
 			   int cap, int audit)
 {
 {
 	int rc;
 	int rc;
 
 
-	rc = secondary_ops->capable(tsk, cred, cap, audit);
+	rc = cap_capable(tsk, cred, cap, audit);
 	if (rc)
 	if (rc)
 		return rc;
 		return rc;
 
 
@@ -1997,7 +2052,7 @@ static int selinux_syslog(int type)
 {
 {
 	int rc;
 	int rc;
 
 
-	rc = secondary_ops->syslog(type);
+	rc = cap_syslog(type);
 	if (rc)
 	if (rc)
 		return rc;
 		return rc;
 
 
@@ -2028,10 +2083,6 @@ static int selinux_syslog(int type)
  * mapping. 0 means there is enough memory for the allocation to
  * mapping. 0 means there is enough memory for the allocation to
  * succeed and -ENOMEM implies there is not.
  * succeed and -ENOMEM implies there is not.
  *
  *
- * Note that secondary_ops->capable and task_has_perm_noaudit return 0
- * if the capability is granted, but __vm_enough_memory requires 1 if
- * the capability is granted.
- *
  * Do not audit the selinux permission check, as this is applied to all
  * Do not audit the selinux permission check, as this is applied to all
  * processes that allocate mappings.
  * processes that allocate mappings.
  */
  */
@@ -2058,7 +2109,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
 	struct inode *inode = bprm->file->f_path.dentry->d_inode;
 	struct inode *inode = bprm->file->f_path.dentry->d_inode;
 	int rc;
 	int rc;
 
 
-	rc = secondary_ops->bprm_set_creds(bprm);
+	rc = cap_bprm_set_creds(bprm);
 	if (rc)
 	if (rc)
 		return rc;
 		return rc;
 
 
@@ -2156,11 +2207,6 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
 	return 0;
 	return 0;
 }
 }
 
 
-static int selinux_bprm_check_security(struct linux_binprm *bprm)
-{
-	return secondary_ops->bprm_check_security(bprm);
-}
-
 static int selinux_bprm_secureexec(struct linux_binprm *bprm)
 static int selinux_bprm_secureexec(struct linux_binprm *bprm)
 {
 {
 	const struct cred *cred = current_cred();
 	const struct cred *cred = current_cred();
@@ -2180,7 +2226,7 @@ static int selinux_bprm_secureexec(struct linux_binprm *bprm)
 					PROCESS__NOATSECURE, NULL);
 					PROCESS__NOATSECURE, NULL);
 	}
 	}
 
 
-	return (atsecure || secondary_ops->bprm_secureexec(bprm));
+	return (atsecure || cap_bprm_secureexec(bprm));
 }
 }
 
 
 extern struct vfsmount *selinuxfs_mount;
 extern struct vfsmount *selinuxfs_mount;
@@ -2290,8 +2336,6 @@ static void selinux_bprm_committing_creds(struct linux_binprm *bprm)
 	struct rlimit *rlim, *initrlim;
 	struct rlimit *rlim, *initrlim;
 	int rc, i;
 	int rc, i;
 
 
-	secondary_ops->bprm_committing_creds(bprm);
-
 	new_tsec = bprm->cred->security;
 	new_tsec = bprm->cred->security;
 	if (new_tsec->sid == new_tsec->osid)
 	if (new_tsec->sid == new_tsec->osid)
 		return;
 		return;
@@ -2337,8 +2381,6 @@ static void selinux_bprm_committed_creds(struct linux_binprm *bprm)
 	int rc, i;
 	int rc, i;
 	unsigned long flags;
 	unsigned long flags;
 
 
-	secondary_ops->bprm_committed_creds(bprm);
-
 	osid = tsec->osid;
 	osid = tsec->osid;
 	sid = tsec->sid;
 	sid = tsec->sid;
 
 
@@ -2400,7 +2442,8 @@ static inline int selinux_option(char *option, int len)
 	return (match_prefix(CONTEXT_STR, sizeof(CONTEXT_STR)-1, option, len) ||
 	return (match_prefix(CONTEXT_STR, sizeof(CONTEXT_STR)-1, option, len) ||
 		match_prefix(FSCONTEXT_STR, sizeof(FSCONTEXT_STR)-1, option, len) ||
 		match_prefix(FSCONTEXT_STR, sizeof(FSCONTEXT_STR)-1, option, len) ||
 		match_prefix(DEFCONTEXT_STR, sizeof(DEFCONTEXT_STR)-1, option, len) ||
 		match_prefix(DEFCONTEXT_STR, sizeof(DEFCONTEXT_STR)-1, option, len) ||
-		match_prefix(ROOTCONTEXT_STR, sizeof(ROOTCONTEXT_STR)-1, option, len));
+		match_prefix(ROOTCONTEXT_STR, sizeof(ROOTCONTEXT_STR)-1, option, len) ||
+		match_prefix(LABELSUPP_STR, sizeof(LABELSUPP_STR)-1, option, len));
 }
 }
 
 
 static inline void take_option(char **to, char *from, int *first, int len)
 static inline void take_option(char **to, char *from, int *first, int len)
@@ -2513,11 +2556,6 @@ static int selinux_mount(char *dev_name,
 			 void *data)
 			 void *data)
 {
 {
 	const struct cred *cred = current_cred();
 	const struct cred *cred = current_cred();
-	int rc;
-
-	rc = secondary_ops->sb_mount(dev_name, path, type, flags, data);
-	if (rc)
-		return rc;
 
 
 	if (flags & MS_REMOUNT)
 	if (flags & MS_REMOUNT)
 		return superblock_has_perm(cred, path->mnt->mnt_sb,
 		return superblock_has_perm(cred, path->mnt->mnt_sb,
@@ -2530,11 +2568,6 @@ static int selinux_mount(char *dev_name,
 static int selinux_umount(struct vfsmount *mnt, int flags)
 static int selinux_umount(struct vfsmount *mnt, int flags)
 {
 {
 	const struct cred *cred = current_cred();
 	const struct cred *cred = current_cred();
-	int rc;
-
-	rc = secondary_ops->sb_umount(mnt, flags);
-	if (rc)
-		return rc;
 
 
 	return superblock_has_perm(cred, mnt->mnt_sb,
 	return superblock_has_perm(cred, mnt->mnt_sb,
 				   FILESYSTEM__UNMOUNT, NULL);
 				   FILESYSTEM__UNMOUNT, NULL);
@@ -2570,7 +2603,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
 	sid = tsec->sid;
 	sid = tsec->sid;
 	newsid = tsec->create_sid;
 	newsid = tsec->create_sid;
 
 
-	if (!newsid || sbsec->behavior == SECURITY_FS_USE_MNTPOINT) {
+	if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) {
 		rc = security_transition_sid(sid, dsec->sid,
 		rc = security_transition_sid(sid, dsec->sid,
 					     inode_mode_to_security_class(inode->i_mode),
 					     inode_mode_to_security_class(inode->i_mode),
 					     &newsid);
 					     &newsid);
@@ -2585,14 +2618,14 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
 	}
 	}
 
 
 	/* Possibly defer initialization to selinux_complete_init. */
 	/* Possibly defer initialization to selinux_complete_init. */
-	if (sbsec->initialized) {
+	if (sbsec->flags & SE_SBINITIALIZED) {
 		struct inode_security_struct *isec = inode->i_security;
 		struct inode_security_struct *isec = inode->i_security;
 		isec->sclass = inode_mode_to_security_class(inode->i_mode);
 		isec->sclass = inode_mode_to_security_class(inode->i_mode);
 		isec->sid = newsid;
 		isec->sid = newsid;
 		isec->initialized = 1;
 		isec->initialized = 1;
 	}
 	}
 
 
-	if (!ss_initialized || sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
+	if (!ss_initialized || !(sbsec->flags & SE_SBLABELSUPP))
 		return -EOPNOTSUPP;
 		return -EOPNOTSUPP;
 
 
 	if (name) {
 	if (name) {
@@ -2622,21 +2655,11 @@ static int selinux_inode_create(struct inode *dir, struct dentry *dentry, int ma
 
 
 static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
 static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
 {
 {
-	int rc;
-
-	rc = secondary_ops->inode_link(old_dentry, dir, new_dentry);
-	if (rc)
-		return rc;
 	return may_link(dir, old_dentry, MAY_LINK);
 	return may_link(dir, old_dentry, MAY_LINK);
 }
 }
 
 
 static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry)
 static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry)
 {
 {
-	int rc;
-
-	rc = secondary_ops->inode_unlink(dir, dentry);
-	if (rc)
-		return rc;
 	return may_link(dir, dentry, MAY_UNLINK);
 	return may_link(dir, dentry, MAY_UNLINK);
 }
 }
 
 
@@ -2657,12 +2680,6 @@ static int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry)
 
 
 static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
 static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
 {
 {
-	int rc;
-
-	rc = secondary_ops->inode_mknod(dir, dentry, mode, dev);
-	if (rc)
-		return rc;
-
 	return may_create(dir, dentry, inode_mode_to_security_class(mode));
 	return may_create(dir, dentry, inode_mode_to_security_class(mode));
 }
 }
 
 
@@ -2682,22 +2699,13 @@ static int selinux_inode_readlink(struct dentry *dentry)
 static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *nameidata)
 static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *nameidata)
 {
 {
 	const struct cred *cred = current_cred();
 	const struct cred *cred = current_cred();
-	int rc;
 
 
-	rc = secondary_ops->inode_follow_link(dentry, nameidata);
-	if (rc)
-		return rc;
 	return dentry_has_perm(cred, NULL, dentry, FILE__READ);
 	return dentry_has_perm(cred, NULL, dentry, FILE__READ);
 }
 }
 
 
 static int selinux_inode_permission(struct inode *inode, int mask)
 static int selinux_inode_permission(struct inode *inode, int mask)
 {
 {
 	const struct cred *cred = current_cred();
 	const struct cred *cred = current_cred();
-	int rc;
-
-	rc = secondary_ops->inode_permission(inode, mask);
-	if (rc)
-		return rc;
 
 
 	if (!mask) {
 	if (!mask) {
 		/* No permission to check.  Existence test. */
 		/* No permission to check.  Existence test. */
@@ -2711,11 +2719,6 @@ static int selinux_inode_permission(struct inode *inode, int mask)
 static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
 static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
 {
 {
 	const struct cred *cred = current_cred();
 	const struct cred *cred = current_cred();
-	int rc;
-
-	rc = secondary_ops->inode_setattr(dentry, iattr);
-	if (rc)
-		return rc;
 
 
 	if (iattr->ia_valid & ATTR_FORCE)
 	if (iattr->ia_valid & ATTR_FORCE)
 		return 0;
 		return 0;
@@ -2769,7 +2772,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
 		return selinux_inode_setotherxattr(dentry, name);
 		return selinux_inode_setotherxattr(dentry, name);
 
 
 	sbsec = inode->i_sb->s_security;
 	sbsec = inode->i_sb->s_security;
-	if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
+	if (!(sbsec->flags & SE_SBLABELSUPP))
 		return -EOPNOTSUPP;
 		return -EOPNOTSUPP;
 
 
 	if (!is_owner_or_cap(inode))
 	if (!is_owner_or_cap(inode))
@@ -2931,16 +2934,6 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t
 	return len;
 	return len;
 }
 }
 
 
-static int selinux_inode_need_killpriv(struct dentry *dentry)
-{
-	return secondary_ops->inode_need_killpriv(dentry);
-}
-
-static int selinux_inode_killpriv(struct dentry *dentry)
-{
-	return secondary_ops->inode_killpriv(dentry);
-}
-
 static void selinux_inode_getsecid(const struct inode *inode, u32 *secid)
 static void selinux_inode_getsecid(const struct inode *inode, u32 *secid)
 {
 {
 	struct inode_security_struct *isec = inode->i_security;
 	struct inode_security_struct *isec = inode->i_security;
@@ -3078,18 +3071,13 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
 				 unsigned long prot)
 				 unsigned long prot)
 {
 {
 	const struct cred *cred = current_cred();
 	const struct cred *cred = current_cred();
-	int rc;
-
-	rc = secondary_ops->file_mprotect(vma, reqprot, prot);
-	if (rc)
-		return rc;
 
 
 	if (selinux_checkreqprot)
 	if (selinux_checkreqprot)
 		prot = reqprot;
 		prot = reqprot;
 
 
 #ifndef CONFIG_PPC32
 #ifndef CONFIG_PPC32
 	if ((prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) {
 	if ((prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) {
-		rc = 0;
+		int rc = 0;
 		if (vma->vm_start >= vma->vm_mm->start_brk &&
 		if (vma->vm_start >= vma->vm_mm->start_brk &&
 		    vma->vm_end <= vma->vm_mm->brk) {
 		    vma->vm_end <= vma->vm_mm->brk) {
 			rc = cred_has_perm(cred, cred, PROCESS__EXECHEAP);
 			rc = cred_has_perm(cred, cred, PROCESS__EXECHEAP);
@@ -3239,12 +3227,6 @@ static int selinux_dentry_open(struct file *file, const struct cred *cred)
 
 
 static int selinux_task_create(unsigned long clone_flags)
 static int selinux_task_create(unsigned long clone_flags)
 {
 {
-	int rc;
-
-	rc = secondary_ops->task_create(clone_flags);
-	if (rc)
-		return rc;
-
 	return current_has_perm(current, PROCESS__FORK);
 	return current_has_perm(current, PROCESS__FORK);
 }
 }
 
 
@@ -3277,14 +3259,6 @@ static int selinux_cred_prepare(struct cred *new, const struct cred *old,
 	return 0;
 	return 0;
 }
 }
 
 
-/*
- * commit new credentials
- */
-static void selinux_cred_commit(struct cred *new, const struct cred *old)
-{
-	secondary_ops->cred_commit(new, old);
-}
-
 /*
 /*
  * set the security data for a kernel service
  * set the security data for a kernel service
  * - all the creation contexts are set to unlabelled
  * - all the creation contexts are set to unlabelled
@@ -3329,29 +3303,6 @@ static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode)
 	return 0;
 	return 0;
 }
 }
 
 
-static int selinux_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
-{
-	/* Since setuid only affects the current process, and
-	   since the SELinux controls are not based on the Linux
-	   identity attributes, SELinux does not need to control
-	   this operation.  However, SELinux does control the use
-	   of the CAP_SETUID and CAP_SETGID capabilities using the
-	   capable hook. */
-	return 0;
-}
-
-static int selinux_task_fix_setuid(struct cred *new, const struct cred *old,
-				   int flags)
-{
-	return secondary_ops->task_fix_setuid(new, old, flags);
-}
-
-static int selinux_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
-{
-	/* See the comment for setuid above. */
-	return 0;
-}
-
 static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)
 static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)
 {
 {
 	return current_has_perm(p, PROCESS__SETPGID);
 	return current_has_perm(p, PROCESS__SETPGID);
@@ -3372,17 +3323,11 @@ static void selinux_task_getsecid(struct task_struct *p, u32 *secid)
 	*secid = task_sid(p);
 	*secid = task_sid(p);
 }
 }
 
 
-static int selinux_task_setgroups(struct group_info *group_info)
-{
-	/* See the comment for setuid above. */
-	return 0;
-}
-
 static int selinux_task_setnice(struct task_struct *p, int nice)
 static int selinux_task_setnice(struct task_struct *p, int nice)
 {
 {
 	int rc;
 	int rc;
 
 
-	rc = secondary_ops->task_setnice(p, nice);
+	rc = cap_task_setnice(p, nice);
 	if (rc)
 	if (rc)
 		return rc;
 		return rc;
 
 
@@ -3393,7 +3338,7 @@ static int selinux_task_setioprio(struct task_struct *p, int ioprio)
 {
 {
 	int rc;
 	int rc;
 
 
-	rc = secondary_ops->task_setioprio(p, ioprio);
+	rc = cap_task_setioprio(p, ioprio);
 	if (rc)
 	if (rc)
 		return rc;
 		return rc;
 
 
@@ -3408,11 +3353,6 @@ static int selinux_task_getioprio(struct task_struct *p)
 static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim)
 static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim)
 {
 {
 	struct rlimit *old_rlim = current->signal->rlim + resource;
 	struct rlimit *old_rlim = current->signal->rlim + resource;
-	int rc;
-
-	rc = secondary_ops->task_setrlimit(resource, new_rlim);
-	if (rc)
-		return rc;
 
 
 	/* Control the ability to change the hard limit (whether
 	/* Control the ability to change the hard limit (whether
 	   lowering or raising it), so that the hard limit can
 	   lowering or raising it), so that the hard limit can
@@ -3428,7 +3368,7 @@ static int selinux_task_setscheduler(struct task_struct *p, int policy, struct s
 {
 {
 	int rc;
 	int rc;
 
 
-	rc = secondary_ops->task_setscheduler(p, policy, lp);
+	rc = cap_task_setscheduler(p, policy, lp);
 	if (rc)
 	if (rc)
 		return rc;
 		return rc;
 
 
@@ -3451,10 +3391,6 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
 	u32 perm;
 	u32 perm;
 	int rc;
 	int rc;
 
 
-	rc = secondary_ops->task_kill(p, info, sig, secid);
-	if (rc)
-		return rc;
-
 	if (!sig)
 	if (!sig)
 		perm = PROCESS__SIGNULL; /* null signal; existence test */
 		perm = PROCESS__SIGNULL; /* null signal; existence test */
 	else
 	else
@@ -3467,18 +3403,6 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
 	return rc;
 	return rc;
 }
 }
 
 
-static int selinux_task_prctl(int option,
-			      unsigned long arg2,
-			      unsigned long arg3,
-			      unsigned long arg4,
-			      unsigned long arg5)
-{
-	/* The current prctl operations do not appear to require
-	   any SELinux controls since they merely observe or modify
-	   the state of the current process. */
-	return secondary_ops->task_prctl(option, arg2, arg3, arg4, arg5);
-}
-
 static int selinux_task_wait(struct task_struct *p)
 static int selinux_task_wait(struct task_struct *p)
 {
 {
 	return task_has_perm(p, current, PROCESS__SIGCHLD);
 	return task_has_perm(p, current, PROCESS__SIGCHLD);
@@ -4047,10 +3971,6 @@ static int selinux_socket_unix_stream_connect(struct socket *sock,
 	struct avc_audit_data ad;
 	struct avc_audit_data ad;
 	int err;
 	int err;
 
 
-	err = secondary_ops->unix_stream_connect(sock, other, newsk);
-	if (err)
-		return err;
-
 	isec = SOCK_INODE(sock)->i_security;
 	isec = SOCK_INODE(sock)->i_security;
 	other_isec = SOCK_INODE(other)->i_security;
 	other_isec = SOCK_INODE(other)->i_security;
 
 
@@ -4844,7 +4764,7 @@ static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
 {
 {
 	int err;
 	int err;
 
 
-	err = secondary_ops->netlink_send(sk, skb);
+	err = cap_netlink_send(sk, skb);
 	if (err)
 	if (err)
 		return err;
 		return err;
 
 
@@ -4859,7 +4779,7 @@ static int selinux_netlink_recv(struct sk_buff *skb, int capability)
 	int err;
 	int err;
 	struct avc_audit_data ad;
 	struct avc_audit_data ad;
 
 
-	err = secondary_ops->netlink_recv(skb, capability);
+	err = cap_netlink_recv(skb, capability);
 	if (err)
 	if (err)
 		return err;
 		return err;
 
 
@@ -5167,11 +5087,6 @@ static int selinux_shm_shmat(struct shmid_kernel *shp,
 			     char __user *shmaddr, int shmflg)
 			     char __user *shmaddr, int shmflg)
 {
 {
 	u32 perms;
 	u32 perms;
-	int rc;
-
-	rc = secondary_ops->shm_shmat(shp, shmaddr, shmflg);
-	if (rc)
-		return rc;
 
 
 	if (shmflg & SHM_RDONLY)
 	if (shmflg & SHM_RDONLY)
 		perms = SHM__READ;
 		perms = SHM__READ;
@@ -5581,7 +5496,6 @@ static struct security_operations selinux_ops = {
 	.netlink_recv =			selinux_netlink_recv,
 	.netlink_recv =			selinux_netlink_recv,
 
 
 	.bprm_set_creds =		selinux_bprm_set_creds,
 	.bprm_set_creds =		selinux_bprm_set_creds,
-	.bprm_check_security =		selinux_bprm_check_security,
 	.bprm_committing_creds =	selinux_bprm_committing_creds,
 	.bprm_committing_creds =	selinux_bprm_committing_creds,
 	.bprm_committed_creds =		selinux_bprm_committed_creds,
 	.bprm_committed_creds =		selinux_bprm_committed_creds,
 	.bprm_secureexec =		selinux_bprm_secureexec,
 	.bprm_secureexec =		selinux_bprm_secureexec,
@@ -5623,8 +5537,6 @@ static struct security_operations selinux_ops = {
 	.inode_getsecurity =		selinux_inode_getsecurity,
 	.inode_getsecurity =		selinux_inode_getsecurity,
 	.inode_setsecurity =		selinux_inode_setsecurity,
 	.inode_setsecurity =		selinux_inode_setsecurity,
 	.inode_listsecurity =		selinux_inode_listsecurity,
 	.inode_listsecurity =		selinux_inode_listsecurity,
-	.inode_need_killpriv =		selinux_inode_need_killpriv,
-	.inode_killpriv =		selinux_inode_killpriv,
 	.inode_getsecid =		selinux_inode_getsecid,
 	.inode_getsecid =		selinux_inode_getsecid,
 
 
 	.file_permission =		selinux_file_permission,
 	.file_permission =		selinux_file_permission,
@@ -5644,17 +5556,12 @@ static struct security_operations selinux_ops = {
 	.task_create =			selinux_task_create,
 	.task_create =			selinux_task_create,
 	.cred_free =			selinux_cred_free,
 	.cred_free =			selinux_cred_free,
 	.cred_prepare =			selinux_cred_prepare,
 	.cred_prepare =			selinux_cred_prepare,
-	.cred_commit =			selinux_cred_commit,
 	.kernel_act_as =		selinux_kernel_act_as,
 	.kernel_act_as =		selinux_kernel_act_as,
 	.kernel_create_files_as =	selinux_kernel_create_files_as,
 	.kernel_create_files_as =	selinux_kernel_create_files_as,
-	.task_setuid =			selinux_task_setuid,
-	.task_fix_setuid =		selinux_task_fix_setuid,
-	.task_setgid =			selinux_task_setgid,
 	.task_setpgid =			selinux_task_setpgid,
 	.task_setpgid =			selinux_task_setpgid,
 	.task_getpgid =			selinux_task_getpgid,
 	.task_getpgid =			selinux_task_getpgid,
 	.task_getsid =			selinux_task_getsid,
 	.task_getsid =			selinux_task_getsid,
 	.task_getsecid =		selinux_task_getsecid,
 	.task_getsecid =		selinux_task_getsecid,
-	.task_setgroups =		selinux_task_setgroups,
 	.task_setnice =			selinux_task_setnice,
 	.task_setnice =			selinux_task_setnice,
 	.task_setioprio =		selinux_task_setioprio,
 	.task_setioprio =		selinux_task_setioprio,
 	.task_getioprio =		selinux_task_getioprio,
 	.task_getioprio =		selinux_task_getioprio,
@@ -5664,7 +5571,6 @@ static struct security_operations selinux_ops = {
 	.task_movememory =		selinux_task_movememory,
 	.task_movememory =		selinux_task_movememory,
 	.task_kill =			selinux_task_kill,
 	.task_kill =			selinux_task_kill,
 	.task_wait =			selinux_task_wait,
 	.task_wait =			selinux_task_wait,
-	.task_prctl =			selinux_task_prctl,
 	.task_to_inode =		selinux_task_to_inode,
 	.task_to_inode =		selinux_task_to_inode,
 
 
 	.ipc_permission =		selinux_ipc_permission,
 	.ipc_permission =		selinux_ipc_permission,

+ 2 - 0
security/selinux/include/av_perm_to_string.h

@@ -24,6 +24,7 @@
    S_(SECCLASS_CHR_FILE, CHR_FILE__EXECMOD, "execmod")
    S_(SECCLASS_CHR_FILE, CHR_FILE__EXECMOD, "execmod")
    S_(SECCLASS_CHR_FILE, CHR_FILE__OPEN, "open")
    S_(SECCLASS_CHR_FILE, CHR_FILE__OPEN, "open")
    S_(SECCLASS_BLK_FILE, BLK_FILE__OPEN, "open")
    S_(SECCLASS_BLK_FILE, BLK_FILE__OPEN, "open")
+   S_(SECCLASS_SOCK_FILE, SOCK_FILE__OPEN, "open")
    S_(SECCLASS_FIFO_FILE, FIFO_FILE__OPEN, "open")
    S_(SECCLASS_FIFO_FILE, FIFO_FILE__OPEN, "open")
    S_(SECCLASS_FD, FD__USE, "use")
    S_(SECCLASS_FD, FD__USE, "use")
    S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__CONNECTTO, "connectto")
    S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__CONNECTTO, "connectto")
@@ -152,6 +153,7 @@
    S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE, "nlmsg_write")
    S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE, "nlmsg_write")
    S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_RELAY, "nlmsg_relay")
    S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_RELAY, "nlmsg_relay")
    S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV, "nlmsg_readpriv")
    S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV, "nlmsg_readpriv")
+   S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_TTY_AUDIT, "nlmsg_tty_audit")
    S_(SECCLASS_NETLINK_IP6FW_SOCKET, NETLINK_IP6FW_SOCKET__NLMSG_READ, "nlmsg_read")
    S_(SECCLASS_NETLINK_IP6FW_SOCKET, NETLINK_IP6FW_SOCKET__NLMSG_READ, "nlmsg_read")
    S_(SECCLASS_NETLINK_IP6FW_SOCKET, NETLINK_IP6FW_SOCKET__NLMSG_WRITE, "nlmsg_write")
    S_(SECCLASS_NETLINK_IP6FW_SOCKET, NETLINK_IP6FW_SOCKET__NLMSG_WRITE, "nlmsg_write")
    S_(SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, "sendto")
    S_(SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, "sendto")

+ 2 - 0
security/selinux/include/av_permissions.h

@@ -174,6 +174,7 @@
 #define SOCK_FILE__SWAPON                         0x00004000UL
 #define SOCK_FILE__SWAPON                         0x00004000UL
 #define SOCK_FILE__QUOTAON                        0x00008000UL
 #define SOCK_FILE__QUOTAON                        0x00008000UL
 #define SOCK_FILE__MOUNTON                        0x00010000UL
 #define SOCK_FILE__MOUNTON                        0x00010000UL
+#define SOCK_FILE__OPEN                           0x00020000UL
 #define FIFO_FILE__IOCTL                          0x00000001UL
 #define FIFO_FILE__IOCTL                          0x00000001UL
 #define FIFO_FILE__READ                           0x00000002UL
 #define FIFO_FILE__READ                           0x00000002UL
 #define FIFO_FILE__WRITE                          0x00000004UL
 #define FIFO_FILE__WRITE                          0x00000004UL
@@ -707,6 +708,7 @@
 #define NETLINK_AUDIT_SOCKET__NLMSG_WRITE         0x00800000UL
 #define NETLINK_AUDIT_SOCKET__NLMSG_WRITE         0x00800000UL
 #define NETLINK_AUDIT_SOCKET__NLMSG_RELAY         0x01000000UL
 #define NETLINK_AUDIT_SOCKET__NLMSG_RELAY         0x01000000UL
 #define NETLINK_AUDIT_SOCKET__NLMSG_READPRIV      0x02000000UL
 #define NETLINK_AUDIT_SOCKET__NLMSG_READPRIV      0x02000000UL
+#define NETLINK_AUDIT_SOCKET__NLMSG_TTY_AUDIT     0x04000000UL
 #define NETLINK_IP6FW_SOCKET__IOCTL               0x00000001UL
 #define NETLINK_IP6FW_SOCKET__IOCTL               0x00000001UL
 #define NETLINK_IP6FW_SOCKET__READ                0x00000002UL
 #define NETLINK_IP6FW_SOCKET__READ                0x00000002UL
 #define NETLINK_IP6FW_SOCKET__WRITE               0x00000004UL
 #define NETLINK_IP6FW_SOCKET__WRITE               0x00000004UL

+ 0 - 2
security/selinux/include/objsec.h

@@ -60,9 +60,7 @@ struct superblock_security_struct {
 	u32 def_sid;			/* default SID for labeling */
 	u32 def_sid;			/* default SID for labeling */
 	u32 mntpoint_sid;		/* SECURITY_FS_USE_MNTPOINT context for files */
 	u32 mntpoint_sid;		/* SECURITY_FS_USE_MNTPOINT context for files */
 	unsigned int behavior;		/* labeling behavior */
 	unsigned int behavior;		/* labeling behavior */
-	unsigned char initialized;	/* initialization flag */
 	unsigned char flags;		/* which mount options were specified */
 	unsigned char flags;		/* which mount options were specified */
-	unsigned char proc;		/* proc fs */
 	struct mutex lock;
 	struct mutex lock;
 	struct list_head isec_head;
 	struct list_head isec_head;
 	spinlock_t isec_lock;
 	spinlock_t isec_lock;

+ 8 - 1
security/selinux/include/security.h

@@ -37,15 +37,23 @@
 #define POLICYDB_VERSION_MAX	POLICYDB_VERSION_BOUNDARY
 #define POLICYDB_VERSION_MAX	POLICYDB_VERSION_BOUNDARY
 #endif
 #endif
 
 
+/* Mask for just the mount related flags */
+#define SE_MNTMASK	0x0f
+/* Super block security struct flags for mount options */
 #define CONTEXT_MNT	0x01
 #define CONTEXT_MNT	0x01
 #define FSCONTEXT_MNT	0x02
 #define FSCONTEXT_MNT	0x02
 #define ROOTCONTEXT_MNT	0x04
 #define ROOTCONTEXT_MNT	0x04
 #define DEFCONTEXT_MNT	0x08
 #define DEFCONTEXT_MNT	0x08
+/* Non-mount related flags */
+#define SE_SBINITIALIZED	0x10
+#define SE_SBPROC		0x20
+#define SE_SBLABELSUPP	0x40
 
 
 #define CONTEXT_STR	"context="
 #define CONTEXT_STR	"context="
 #define FSCONTEXT_STR	"fscontext="
 #define FSCONTEXT_STR	"fscontext="
 #define ROOTCONTEXT_STR	"rootcontext="
 #define ROOTCONTEXT_STR	"rootcontext="
 #define DEFCONTEXT_STR	"defcontext="
 #define DEFCONTEXT_STR	"defcontext="
+#define LABELSUPP_STR "seclabel"
 
 
 struct netlbl_lsm_secattr;
 struct netlbl_lsm_secattr;
 
 
@@ -80,7 +88,6 @@ int security_policycap_supported(unsigned int req_cap);
 #define SEL_VEC_MAX 32
 #define SEL_VEC_MAX 32
 struct av_decision {
 struct av_decision {
 	u32 allowed;
 	u32 allowed;
-	u32 decided;
 	u32 auditallow;
 	u32 auditallow;
 	u32 auditdeny;
 	u32 auditdeny;
 	u32 seqno;
 	u32 seqno;

+ 1 - 1
security/selinux/nlmsgtab.c

@@ -113,7 +113,7 @@ static struct nlmsg_perm nlmsg_audit_perms[] =
 	{ AUDIT_USER,		NETLINK_AUDIT_SOCKET__NLMSG_RELAY    },
 	{ AUDIT_USER,		NETLINK_AUDIT_SOCKET__NLMSG_RELAY    },
 	{ AUDIT_SIGNAL_INFO,	NETLINK_AUDIT_SOCKET__NLMSG_READ     },
 	{ AUDIT_SIGNAL_INFO,	NETLINK_AUDIT_SOCKET__NLMSG_READ     },
 	{ AUDIT_TTY_GET,	NETLINK_AUDIT_SOCKET__NLMSG_READ     },
 	{ AUDIT_TTY_GET,	NETLINK_AUDIT_SOCKET__NLMSG_READ     },
-	{ AUDIT_TTY_SET,	NETLINK_AUDIT_SOCKET__NLMSG_WRITE    },
+	{ AUDIT_TTY_SET,	NETLINK_AUDIT_SOCKET__NLMSG_TTY_AUDIT	},
 };
 };
 
 
 
 

+ 1 - 1
security/selinux/selinuxfs.c

@@ -595,7 +595,7 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
 
 
 	length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT,
 	length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT,
 			  "%x %x %x %x %u",
 			  "%x %x %x %x %u",
-			  avd.allowed, avd.decided,
+			  avd.allowed, 0xffffffff,
 			  avd.auditallow, avd.auditdeny,
 			  avd.auditallow, avd.auditdeny,
 			  avd.seqno);
 			  avd.seqno);
 out2:
 out2:

+ 0 - 2
security/selinux/ss/services.c

@@ -407,7 +407,6 @@ static int context_struct_compute_av(struct context *scontext,
 	 * Initialize the access vectors to the default values.
 	 * Initialize the access vectors to the default values.
 	 */
 	 */
 	avd->allowed = 0;
 	avd->allowed = 0;
-	avd->decided = 0xffffffff;
 	avd->auditallow = 0;
 	avd->auditallow = 0;
 	avd->auditdeny = 0xffffffff;
 	avd->auditdeny = 0xffffffff;
 	avd->seqno = latest_granting;
 	avd->seqno = latest_granting;
@@ -743,7 +742,6 @@ int security_compute_av(u32 ssid,
 
 
 	if (!ss_initialized) {
 	if (!ss_initialized) {
 		avd->allowed = 0xffffffff;
 		avd->allowed = 0xffffffff;
-		avd->decided = 0xffffffff;
 		avd->auditallow = 0;
 		avd->auditallow = 0;
 		avd->auditdeny = 0xffffffff;
 		avd->auditdeny = 0xffffffff;
 		avd->seqno = latest_granting;
 		avd->seqno = latest_granting;

+ 2 - 2
security/smack/smack_access.c

@@ -162,8 +162,8 @@ int smk_access(char *subject_label, char *object_label, int request)
 
 
 /**
 /**
  * smk_curacc - determine if current has a specific access to an object
  * smk_curacc - determine if current has a specific access to an object
- * @object_label: a pointer to the object's Smack label
- * @request: the access requested, in "MAY" format
+ * @obj_label: a pointer to the object's Smack label
+ * @mode: the access requested, in "MAY" format
  *
  *
  * This function checks the current subject label/object label pair
  * This function checks the current subject label/object label pair
  * in the access rule list and returns 0 if the access is permitted,
  * in the access rule list and returns 0 if the access is permitted,

+ 24 - 28
security/smack/smack_lsm.c

@@ -91,6 +91,7 @@ struct inode_smack *new_inode_smack(char *smack)
 /**
 /**
  * smack_ptrace_may_access - Smack approval on PTRACE_ATTACH
  * smack_ptrace_may_access - Smack approval on PTRACE_ATTACH
  * @ctp: child task pointer
  * @ctp: child task pointer
+ * @mode: ptrace attachment mode
  *
  *
  * Returns 0 if access is OK, an error code otherwise
  * Returns 0 if access is OK, an error code otherwise
  *
  *
@@ -203,9 +204,8 @@ static void smack_sb_free_security(struct super_block *sb)
 
 
 /**
 /**
  * smack_sb_copy_data - copy mount options data for processing
  * smack_sb_copy_data - copy mount options data for processing
- * @type: file system type
  * @orig: where to start
  * @orig: where to start
- * @smackopts
+ * @smackopts: mount options string
  *
  *
  * Returns 0 on success or -ENOMEM on error.
  * Returns 0 on success or -ENOMEM on error.
  *
  *
@@ -331,7 +331,7 @@ static int smack_sb_statfs(struct dentry *dentry)
 /**
 /**
  * smack_sb_mount - Smack check for mounting
  * smack_sb_mount - Smack check for mounting
  * @dev_name: unused
  * @dev_name: unused
- * @nd: mount point
+ * @path: mount point
  * @type: unused
  * @type: unused
  * @flags: unused
  * @flags: unused
  * @data: unused
  * @data: unused
@@ -370,7 +370,7 @@ static int smack_sb_umount(struct vfsmount *mnt, int flags)
 
 
 /**
 /**
  * smack_inode_alloc_security - allocate an inode blob
  * smack_inode_alloc_security - allocate an inode blob
- * @inode - the inode in need of a blob
+ * @inode: the inode in need of a blob
  *
  *
  * Returns 0 if it gets a blob, -ENOMEM otherwise
  * Returns 0 if it gets a blob, -ENOMEM otherwise
  */
  */
@@ -384,7 +384,7 @@ static int smack_inode_alloc_security(struct inode *inode)
 
 
 /**
 /**
  * smack_inode_free_security - free an inode blob
  * smack_inode_free_security - free an inode blob
- * @inode - the inode with a blob
+ * @inode: the inode with a blob
  *
  *
  * Clears the blob pointer in inode
  * Clears the blob pointer in inode
  */
  */
@@ -538,7 +538,6 @@ static int smack_inode_rename(struct inode *old_inode,
  * smack_inode_permission - Smack version of permission()
  * smack_inode_permission - Smack version of permission()
  * @inode: the inode in question
  * @inode: the inode in question
  * @mask: the access requested
  * @mask: the access requested
- * @nd: unused
  *
  *
  * This is the important Smack hook.
  * This is the important Smack hook.
  *
  *
@@ -701,8 +700,7 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name)
  * @inode: the object
  * @inode: the object
  * @name: attribute name
  * @name: attribute name
  * @buffer: where to put the result
  * @buffer: where to put the result
- * @size: size of the buffer
- * @err: unused
+ * @alloc: unused
  *
  *
  * Returns the size of the attribute or an error code
  * Returns the size of the attribute or an error code
  */
  */
@@ -864,7 +862,7 @@ static int smack_file_ioctl(struct file *file, unsigned int cmd,
 /**
 /**
  * smack_file_lock - Smack check on file locking
  * smack_file_lock - Smack check on file locking
  * @file: the object
  * @file: the object
- * @cmd unused
+ * @cmd: unused
  *
  *
  * Returns 0 if current has write access, error code otherwise
  * Returns 0 if current has write access, error code otherwise
  */
  */
@@ -1003,8 +1001,8 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
 	return 0;
 	return 0;
 }
 }
 
 
-/*
- * commit new credentials
+/**
+ * smack_cred_commit - commit new credentials
  * @new: the new credentials
  * @new: the new credentials
  * @old: the original credentials
  * @old: the original credentials
  */
  */
@@ -1014,8 +1012,8 @@ static void smack_cred_commit(struct cred *new, const struct cred *old)
 
 
 /**
 /**
  * smack_kernel_act_as - Set the subjective context in a set of credentials
  * smack_kernel_act_as - Set the subjective context in a set of credentials
- * @new points to the set of credentials to be modified.
- * @secid specifies the security ID to be set
+ * @new: points to the set of credentials to be modified.
+ * @secid: specifies the security ID to be set
  *
  *
  * Set the security data for a kernel service.
  * Set the security data for a kernel service.
  */
  */
@@ -1032,8 +1030,8 @@ static int smack_kernel_act_as(struct cred *new, u32 secid)
 
 
 /**
 /**
  * smack_kernel_create_files_as - Set the file creation label in a set of creds
  * smack_kernel_create_files_as - Set the file creation label in a set of creds
- * @new points to the set of credentials to be modified
- * @inode points to the inode to use as a reference
+ * @new: points to the set of credentials to be modified
+ * @inode: points to the inode to use as a reference
  *
  *
  * Set the file creation context in a set of credentials to the same
  * Set the file creation context in a set of credentials to the same
  * as the objective context of the specified inode
  * as the objective context of the specified inode
@@ -1242,7 +1240,7 @@ static int smack_task_wait(struct task_struct *p)
 /**
 /**
  * smack_task_to_inode - copy task smack into the inode blob
  * smack_task_to_inode - copy task smack into the inode blob
  * @p: task to copy from
  * @p: task to copy from
- * inode: inode to copy to
+ * @inode: inode to copy to
  *
  *
  * Sets the smack pointer in the inode security blob
  * Sets the smack pointer in the inode security blob
  */
  */
@@ -1260,7 +1258,7 @@ static void smack_task_to_inode(struct task_struct *p, struct inode *inode)
  * smack_sk_alloc_security - Allocate a socket blob
  * smack_sk_alloc_security - Allocate a socket blob
  * @sk: the socket
  * @sk: the socket
  * @family: unused
  * @family: unused
- * @priority: memory allocation priority
+ * @gfp_flags: memory allocation flags
  *
  *
  * Assign Smack pointers to current
  * Assign Smack pointers to current
  *
  *
@@ -1974,7 +1972,7 @@ static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag)
 
 
 /**
 /**
  * smack_ipc_getsecid - Extract smack security id
  * smack_ipc_getsecid - Extract smack security id
- * @ipcp: the object permissions
+ * @ipp: the object permissions
  * @secid: where result will be saved
  * @secid: where result will be saved
  */
  */
 static void smack_ipc_getsecid(struct kern_ipc_perm *ipp, u32 *secid)
 static void smack_ipc_getsecid(struct kern_ipc_perm *ipp, u32 *secid)
@@ -2251,7 +2249,7 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other)
 /**
 /**
  * smack_socket_sendmsg - Smack check based on destination host
  * smack_socket_sendmsg - Smack check based on destination host
  * @sock: the socket
  * @sock: the socket
- * @msghdr: the message
+ * @msg: the message
  * @size: the size of the message
  * @size: the size of the message
  *
  *
  * Return 0 if the current subject can write to the destination
  * Return 0 if the current subject can write to the destination
@@ -2292,8 +2290,7 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
 
 
 
 
 /**
 /**
- * smack_from_secattr - Convert a netlabel attr.mls.lvl/attr.mls.cat
- * 	pair to smack
+ * smack_from_secattr - Convert a netlabel attr.mls.lvl/attr.mls.cat pair to smack
  * @sap: netlabel secattr
  * @sap: netlabel secattr
  * @sip: where to put the result
  * @sip: where to put the result
  *
  *
@@ -2414,7 +2411,7 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
  * @sock: the socket
  * @sock: the socket
  * @optval: user's destination
  * @optval: user's destination
  * @optlen: size thereof
  * @optlen: size thereof
- * @len: max thereoe
+ * @len: max thereof
  *
  *
  * returns zero on success, an error code otherwise
  * returns zero on success, an error code otherwise
  */
  */
@@ -2749,7 +2746,7 @@ static void smack_audit_rule_free(void *vrule)
 
 
 #endif /* CONFIG_AUDIT */
 #endif /* CONFIG_AUDIT */
 
 
-/*
+/**
  * smack_secid_to_secctx - return the smack label for a secid
  * smack_secid_to_secctx - return the smack label for a secid
  * @secid: incoming integer
  * @secid: incoming integer
  * @secdata: destination
  * @secdata: destination
@@ -2766,7 +2763,7 @@ static int smack_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
 	return 0;
 	return 0;
 }
 }
 
 
-/*
+/**
  * smack_secctx_to_secid - return the secid for a smack label
  * smack_secctx_to_secid - return the secid for a smack label
  * @secdata: smack label
  * @secdata: smack label
  * @seclen: how long result is
  * @seclen: how long result is
@@ -2780,11 +2777,10 @@ static int smack_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
 	return 0;
 	return 0;
 }
 }
 
 
-/*
+/**
  * smack_release_secctx - don't do anything.
  * smack_release_secctx - don't do anything.
- * @key_ref: unused
- * @context: unused
- * @perm: unused
+ * @secdata: unused
+ * @seclen: unused
  *
  *
  * Exists to make sure nothing gets done, and properly
  * Exists to make sure nothing gets done, and properly
  */
  */

+ 8 - 7
security/smack/smackfs.c

@@ -245,7 +245,7 @@ out:
 
 
 /**
 /**
  * smk_write_load - write() for /smack/load
  * smk_write_load - write() for /smack/load
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
  * @buf: where to get the data from
  * @buf: where to get the data from
  * @count: bytes sent
  * @count: bytes sent
  * @ppos: where to start - must be 0
  * @ppos: where to start - must be 0
@@ -402,6 +402,7 @@ static void smk_cipso_doi(void)
 
 
 /**
 /**
  * smk_unlbl_ambient - initialize the unlabeled domain
  * smk_unlbl_ambient - initialize the unlabeled domain
+ * @oldambient: previous domain string
  */
  */
 static void smk_unlbl_ambient(char *oldambient)
 static void smk_unlbl_ambient(char *oldambient)
 {
 {
@@ -513,7 +514,7 @@ static int smk_open_cipso(struct inode *inode, struct file *file)
 
 
 /**
 /**
  * smk_write_cipso - write() for /smack/cipso
  * smk_write_cipso - write() for /smack/cipso
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
  * @buf: where to get the data from
  * @buf: where to get the data from
  * @count: bytes sent
  * @count: bytes sent
  * @ppos: where to start
  * @ppos: where to start
@@ -733,7 +734,7 @@ static void smk_netlbladdr_insert(struct smk_netlbladdr *new)
 
 
 /**
 /**
  * smk_write_netlbladdr - write() for /smack/netlabel
  * smk_write_netlbladdr - write() for /smack/netlabel
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
  * @buf: where to get the data from
  * @buf: where to get the data from
  * @count: bytes sent
  * @count: bytes sent
  * @ppos: where to start
  * @ppos: where to start
@@ -884,7 +885,7 @@ static ssize_t smk_read_doi(struct file *filp, char __user *buf,
 
 
 /**
 /**
  * smk_write_doi - write() for /smack/doi
  * smk_write_doi - write() for /smack/doi
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
  * @buf: where to get the data from
  * @buf: where to get the data from
  * @count: bytes sent
  * @count: bytes sent
  * @ppos: where to start
  * @ppos: where to start
@@ -949,7 +950,7 @@ static ssize_t smk_read_direct(struct file *filp, char __user *buf,
 
 
 /**
 /**
  * smk_write_direct - write() for /smack/direct
  * smk_write_direct - write() for /smack/direct
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
  * @buf: where to get the data from
  * @buf: where to get the data from
  * @count: bytes sent
  * @count: bytes sent
  * @ppos: where to start
  * @ppos: where to start
@@ -1024,7 +1025,7 @@ static ssize_t smk_read_ambient(struct file *filp, char __user *buf,
 
 
 /**
 /**
  * smk_write_ambient - write() for /smack/ambient
  * smk_write_ambient - write() for /smack/ambient
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
  * @buf: where to get the data from
  * @buf: where to get the data from
  * @count: bytes sent
  * @count: bytes sent
  * @ppos: where to start
  * @ppos: where to start
@@ -1099,7 +1100,7 @@ static ssize_t smk_read_onlycap(struct file *filp, char __user *buf,
 
 
 /**
 /**
  * smk_write_onlycap - write() for /smack/onlycap
  * smk_write_onlycap - write() for /smack/onlycap
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
  * @buf: where to get the data from
  * @buf: where to get the data from
  * @count: bytes sent
  * @count: bytes sent
  * @ppos: where to start
  * @ppos: where to start

+ 11 - 0
security/tomoyo/Kconfig

@@ -0,0 +1,11 @@
+config SECURITY_TOMOYO
+	bool "TOMOYO Linux Support"
+	depends on SECURITY
+	select SECURITYFS
+	select SECURITY_PATH
+	default n
+	help
+	  This selects TOMOYO Linux, pathname-based access control.
+	  Required userspace tools and further information may be
+	  found at <http://tomoyo.sourceforge.jp/>.
+	  If you are unsure how to answer this question, answer N.

+ 1 - 0
security/tomoyo/Makefile

@@ -0,0 +1 @@
+obj-y = common.o realpath.o tomoyo.o domain.o file.o

+ 2206 - 0
security/tomoyo/common.c

@@ -0,0 +1,2206 @@
+/*
+ * security/tomoyo/common.c
+ *
+ * Common functions for TOMOYO.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/02/01
+ *
+ */
+
+#include <linux/uaccess.h>
+#include <linux/security.h>
+#include <linux/hardirq.h>
+#include "realpath.h"
+#include "common.h"
+#include "tomoyo.h"
+
+/* Has loading policy done? */
+bool tomoyo_policy_loaded;
+
+/* String table for functionality that takes 4 modes. */
+static const char *tomoyo_mode_4[4] = {
+	"disabled", "learning", "permissive", "enforcing"
+};
+/* String table for functionality that takes 2 modes. */
+static const char *tomoyo_mode_2[4] = {
+	"disabled", "enabled", "enabled", "enabled"
+};
+
+/* Table for profile. */
+static struct {
+	const char *keyword;
+	unsigned int current_value;
+	const unsigned int max_value;
+} tomoyo_control_array[TOMOYO_MAX_CONTROL_INDEX] = {
+	[TOMOYO_MAC_FOR_FILE]     = { "MAC_FOR_FILE",        0,       3 },
+	[TOMOYO_MAX_ACCEPT_ENTRY] = { "MAX_ACCEPT_ENTRY", 2048, INT_MAX },
+	[TOMOYO_VERBOSE]          = { "TOMOYO_VERBOSE",      1,       1 },
+};
+
+/* Profile table. Memory is allocated as needed. */
+static struct tomoyo_profile {
+	unsigned int value[TOMOYO_MAX_CONTROL_INDEX];
+	const struct tomoyo_path_info *comment;
+} *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES];
+
+/* Permit policy management by non-root user? */
+static bool tomoyo_manage_by_non_root;
+
+/* Utility functions. */
+
+/* Open operation for /sys/kernel/security/tomoyo/ interface. */
+static int tomoyo_open_control(const u8 type, struct file *file);
+/* Close /sys/kernel/security/tomoyo/ interface. */
+static int tomoyo_close_control(struct file *file);
+/* Read operation for /sys/kernel/security/tomoyo/ interface. */
+static int tomoyo_read_control(struct file *file, char __user *buffer,
+			       const int buffer_len);
+/* Write operation for /sys/kernel/security/tomoyo/ interface. */
+static int tomoyo_write_control(struct file *file, const char __user *buffer,
+				const int buffer_len);
+
+/**
+ * tomoyo_is_byte_range - Check whether the string isa \ooo style octal value.
+ *
+ * @str: Pointer to the string.
+ *
+ * Returns true if @str is a \ooo style octal value, false otherwise.
+ *
+ * TOMOYO uses \ooo style representation for 0x01 - 0x20 and 0x7F - 0xFF.
+ * This function verifies that \ooo is in valid range.
+ */
+static inline bool tomoyo_is_byte_range(const char *str)
+{
+	return *str >= '0' && *str++ <= '3' &&
+		*str >= '0' && *str++ <= '7' &&
+		*str >= '0' && *str <= '7';
+}
+
+/**
+ * tomoyo_is_alphabet_char - Check whether the character is an alphabet.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is an alphabet character, false otherwise.
+ */
+static inline bool tomoyo_is_alphabet_char(const char c)
+{
+	return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
+}
+
+/**
+ * tomoyo_make_byte - Make byte value from three octal characters.
+ *
+ * @c1: The first character.
+ * @c2: The second character.
+ * @c3: The third character.
+ *
+ * Returns byte value.
+ */
+static inline u8 tomoyo_make_byte(const u8 c1, const u8 c2, const u8 c3)
+{
+	return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0');
+}
+
+/**
+ * tomoyo_str_starts - Check whether the given string starts with the given keyword.
+ *
+ * @src:  Pointer to pointer to the string.
+ * @find: Pointer to the keyword.
+ *
+ * Returns true if @src starts with @find, false otherwise.
+ *
+ * The @src is updated to point the first character after the @find
+ * if @src starts with @find.
+ */
+static bool tomoyo_str_starts(char **src, const char *find)
+{
+	const int len = strlen(find);
+	char *tmp = *src;
+
+	if (strncmp(tmp, find, len))
+		return false;
+	tmp += len;
+	*src = tmp;
+	return true;
+}
+
+/**
+ * tomoyo_normalize_line - Format string.
+ *
+ * @buffer: The line to normalize.
+ *
+ * Leading and trailing whitespaces are removed.
+ * Multiple whitespaces are packed into single space.
+ *
+ * Returns nothing.
+ */
+static void tomoyo_normalize_line(unsigned char *buffer)
+{
+	unsigned char *sp = buffer;
+	unsigned char *dp = buffer;
+	bool first = true;
+
+	while (tomoyo_is_invalid(*sp))
+		sp++;
+	while (*sp) {
+		if (!first)
+			*dp++ = ' ';
+		first = false;
+		while (tomoyo_is_valid(*sp))
+			*dp++ = *sp++;
+		while (tomoyo_is_invalid(*sp))
+			sp++;
+	}
+	*dp = '\0';
+}
+
+/**
+ * tomoyo_is_correct_path - Validate a pathname.
+ * @filename:     The pathname to check.
+ * @start_type:   Should the pathname start with '/'?
+ *                1 = must / -1 = must not / 0 = don't care
+ * @pattern_type: Can the pathname contain a wildcard?
+ *                1 = must / -1 = must not / 0 = don't care
+ * @end_type:     Should the pathname end with '/'?
+ *                1 = must / -1 = must not / 0 = don't care
+ * @function:     The name of function calling me.
+ *
+ * Check whether the given filename follows the naming rules.
+ * Returns true if @filename follows the naming rules, false otherwise.
+ */
+bool tomoyo_is_correct_path(const char *filename, const s8 start_type,
+			    const s8 pattern_type, const s8 end_type,
+			    const char *function)
+{
+	bool contains_pattern = false;
+	unsigned char c;
+	unsigned char d;
+	unsigned char e;
+	const char *original_filename = filename;
+
+	if (!filename)
+		goto out;
+	c = *filename;
+	if (start_type == 1) { /* Must start with '/' */
+		if (c != '/')
+			goto out;
+	} else if (start_type == -1) { /* Must not start with '/' */
+		if (c == '/')
+			goto out;
+	}
+	if (c)
+		c = *(filename + strlen(filename) - 1);
+	if (end_type == 1) { /* Must end with '/' */
+		if (c != '/')
+			goto out;
+	} else if (end_type == -1) { /* Must not end with '/' */
+		if (c == '/')
+			goto out;
+	}
+	while ((c = *filename++) != '\0') {
+		if (c == '\\') {
+			switch ((c = *filename++)) {
+			case '\\':  /* "\\" */
+				continue;
+			case '$':   /* "\$" */
+			case '+':   /* "\+" */
+			case '?':   /* "\?" */
+			case '*':   /* "\*" */
+			case '@':   /* "\@" */
+			case 'x':   /* "\x" */
+			case 'X':   /* "\X" */
+			case 'a':   /* "\a" */
+			case 'A':   /* "\A" */
+			case '-':   /* "\-" */
+				if (pattern_type == -1)
+					break; /* Must not contain pattern */
+				contains_pattern = true;
+				continue;
+			case '0':   /* "\ooo" */
+			case '1':
+			case '2':
+			case '3':
+				d = *filename++;
+				if (d < '0' || d > '7')
+					break;
+				e = *filename++;
+				if (e < '0' || e > '7')
+					break;
+				c = tomoyo_make_byte(c, d, e);
+				if (tomoyo_is_invalid(c))
+					continue; /* pattern is not \000 */
+			}
+			goto out;
+		} else if (tomoyo_is_invalid(c)) {
+			goto out;
+		}
+	}
+	if (pattern_type == 1) { /* Must contain pattern */
+		if (!contains_pattern)
+			goto out;
+	}
+	return true;
+ out:
+	printk(KERN_DEBUG "%s: Invalid pathname '%s'\n", function,
+	       original_filename);
+	return false;
+}
+
+/**
+ * tomoyo_is_correct_domain - Check whether the given domainname follows the naming rules.
+ * @domainname:   The domainname to check.
+ * @function:     The name of function calling me.
+ *
+ * Returns true if @domainname follows the naming rules, false otherwise.
+ */
+bool tomoyo_is_correct_domain(const unsigned char *domainname,
+			      const char *function)
+{
+	unsigned char c;
+	unsigned char d;
+	unsigned char e;
+	const char *org_domainname = domainname;
+
+	if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME,
+				   TOMOYO_ROOT_NAME_LEN))
+		goto out;
+	domainname += TOMOYO_ROOT_NAME_LEN;
+	if (!*domainname)
+		return true;
+	do {
+		if (*domainname++ != ' ')
+			goto out;
+		if (*domainname++ != '/')
+			goto out;
+		while ((c = *domainname) != '\0' && c != ' ') {
+			domainname++;
+			if (c == '\\') {
+				c = *domainname++;
+				switch ((c)) {
+				case '\\':  /* "\\" */
+					continue;
+				case '0':   /* "\ooo" */
+				case '1':
+				case '2':
+				case '3':
+					d = *domainname++;
+					if (d < '0' || d > '7')
+						break;
+					e = *domainname++;
+					if (e < '0' || e > '7')
+						break;
+					c = tomoyo_make_byte(c, d, e);
+					if (tomoyo_is_invalid(c))
+						/* pattern is not \000 */
+						continue;
+				}
+				goto out;
+			} else if (tomoyo_is_invalid(c)) {
+				goto out;
+			}
+		}
+	} while (*domainname);
+	return true;
+ out:
+	printk(KERN_DEBUG "%s: Invalid domainname '%s'\n", function,
+	       org_domainname);
+	return false;
+}
+
+/**
+ * tomoyo_is_domain_def - Check whether the given token can be a domainname.
+ *
+ * @buffer: The token to check.
+ *
+ * Returns true if @buffer possibly be a domainname, false otherwise.
+ */
+bool tomoyo_is_domain_def(const unsigned char *buffer)
+{
+	return !strncmp(buffer, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN);
+}
+
+/**
+ * tomoyo_find_domain - Find a domain by the given name.
+ *
+ * @domainname: The domainname to find.
+ *
+ * Caller must call down_read(&tomoyo_domain_list_lock); or
+ * down_write(&tomoyo_domain_list_lock); .
+ *
+ * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise.
+ */
+struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
+{
+	struct tomoyo_domain_info *domain;
+	struct tomoyo_path_info name;
+
+	name.name = domainname;
+	tomoyo_fill_path_info(&name);
+	list_for_each_entry(domain, &tomoyo_domain_list, list) {
+		if (!domain->is_deleted &&
+		    !tomoyo_pathcmp(&name, domain->domainname))
+			return domain;
+	}
+	return NULL;
+}
+
+/**
+ * tomoyo_path_depth - Evaluate the number of '/' in a string.
+ *
+ * @pathname: The string to evaluate.
+ *
+ * Returns path depth of the string.
+ *
+ * I score 2 for each of the '/' in the @pathname
+ * and score 1 if the @pathname ends with '/'.
+ */
+static int tomoyo_path_depth(const char *pathname)
+{
+	int i = 0;
+
+	if (pathname) {
+		const char *ep = pathname + strlen(pathname);
+		if (pathname < ep--) {
+			if (*ep != '/')
+				i++;
+			while (pathname <= ep)
+				if (*ep-- == '/')
+					i += 2;
+		}
+	}
+	return i;
+}
+
+/**
+ * tomoyo_const_part_length - Evaluate the initial length without a pattern in a token.
+ *
+ * @filename: The string to evaluate.
+ *
+ * Returns the initial length without a pattern in @filename.
+ */
+static int tomoyo_const_part_length(const char *filename)
+{
+	char c;
+	int len = 0;
+
+	if (!filename)
+		return 0;
+	while ((c = *filename++) != '\0') {
+		if (c != '\\') {
+			len++;
+			continue;
+		}
+		c = *filename++;
+		switch (c) {
+		case '\\':  /* "\\" */
+			len += 2;
+			continue;
+		case '0':   /* "\ooo" */
+		case '1':
+		case '2':
+		case '3':
+			c = *filename++;
+			if (c < '0' || c > '7')
+				break;
+			c = *filename++;
+			if (c < '0' || c > '7')
+				break;
+			len += 4;
+			continue;
+		}
+		break;
+	}
+	return len;
+}
+
+/**
+ * tomoyo_fill_path_info - Fill in "struct tomoyo_path_info" members.
+ *
+ * @ptr: Pointer to "struct tomoyo_path_info" to fill in.
+ *
+ * The caller sets "struct tomoyo_path_info"->name.
+ */
+void tomoyo_fill_path_info(struct tomoyo_path_info *ptr)
+{
+	const char *name = ptr->name;
+	const int len = strlen(name);
+
+	ptr->total_len = len;
+	ptr->const_len = tomoyo_const_part_length(name);
+	ptr->is_dir = len && (name[len - 1] == '/');
+	ptr->is_patterned = (ptr->const_len < len);
+	ptr->hash = full_name_hash(name, len);
+	ptr->depth = tomoyo_path_depth(name);
+}
+
+/**
+ * tomoyo_file_matches_to_pattern2 - Pattern matching without '/' character
+ * and "\-" pattern.
+ *
+ * @filename:     The start of string to check.
+ * @filename_end: The end of string to check.
+ * @pattern:      The start of pattern to compare.
+ * @pattern_end:  The end of pattern to compare.
+ *
+ * Returns true if @filename matches @pattern, false otherwise.
+ */
+static bool tomoyo_file_matches_to_pattern2(const char *filename,
+					    const char *filename_end,
+					    const char *pattern,
+					    const char *pattern_end)
+{
+	while (filename < filename_end && pattern < pattern_end) {
+		char c;
+		if (*pattern != '\\') {
+			if (*filename++ != *pattern++)
+				return false;
+			continue;
+		}
+		c = *filename;
+		pattern++;
+		switch (*pattern) {
+			int i;
+			int j;
+		case '?':
+			if (c == '/') {
+				return false;
+			} else if (c == '\\') {
+				if (filename[1] == '\\')
+					filename++;
+				else if (tomoyo_is_byte_range(filename + 1))
+					filename += 3;
+				else
+					return false;
+			}
+			break;
+		case '\\':
+			if (c != '\\')
+				return false;
+			if (*++filename != '\\')
+				return false;
+			break;
+		case '+':
+			if (!isdigit(c))
+				return false;
+			break;
+		case 'x':
+			if (!isxdigit(c))
+				return false;
+			break;
+		case 'a':
+			if (!tomoyo_is_alphabet_char(c))
+				return false;
+			break;
+		case '0':
+		case '1':
+		case '2':
+		case '3':
+			if (c == '\\' && tomoyo_is_byte_range(filename + 1)
+			    && strncmp(filename + 1, pattern, 3) == 0) {
+				filename += 3;
+				pattern += 2;
+				break;
+			}
+			return false; /* Not matched. */
+		case '*':
+		case '@':
+			for (i = 0; i <= filename_end - filename; i++) {
+				if (tomoyo_file_matches_to_pattern2(
+						    filename + i, filename_end,
+						    pattern + 1, pattern_end))
+					return true;
+				c = filename[i];
+				if (c == '.' && *pattern == '@')
+					break;
+				if (c != '\\')
+					continue;
+				if (filename[i + 1] == '\\')
+					i++;
+				else if (tomoyo_is_byte_range(filename + i + 1))
+					i += 3;
+				else
+					break; /* Bad pattern. */
+			}
+			return false; /* Not matched. */
+		default:
+			j = 0;
+			c = *pattern;
+			if (c == '$') {
+				while (isdigit(filename[j]))
+					j++;
+			} else if (c == 'X') {
+				while (isxdigit(filename[j]))
+					j++;
+			} else if (c == 'A') {
+				while (tomoyo_is_alphabet_char(filename[j]))
+					j++;
+			}
+			for (i = 1; i <= j; i++) {
+				if (tomoyo_file_matches_to_pattern2(
+						    filename + i, filename_end,
+						    pattern + 1, pattern_end))
+					return true;
+			}
+			return false; /* Not matched or bad pattern. */
+		}
+		filename++;
+		pattern++;
+	}
+	while (*pattern == '\\' &&
+	       (*(pattern + 1) == '*' || *(pattern + 1) == '@'))
+		pattern += 2;
+	return filename == filename_end && pattern == pattern_end;
+}
+
+/**
+ * tomoyo_file_matches_to_pattern - Pattern matching without without '/' character.
+ *
+ * @filename:     The start of string to check.
+ * @filename_end: The end of string to check.
+ * @pattern:      The start of pattern to compare.
+ * @pattern_end:  The end of pattern to compare.
+ *
+ * Returns true if @filename matches @pattern, false otherwise.
+ */
+static bool tomoyo_file_matches_to_pattern(const char *filename,
+					   const char *filename_end,
+					   const char *pattern,
+					   const char *pattern_end)
+{
+	const char *pattern_start = pattern;
+	bool first = true;
+	bool result;
+
+	while (pattern < pattern_end - 1) {
+		/* Split at "\-" pattern. */
+		if (*pattern++ != '\\' || *pattern++ != '-')
+			continue;
+		result = tomoyo_file_matches_to_pattern2(filename,
+							 filename_end,
+							 pattern_start,
+							 pattern - 2);
+		if (first)
+			result = !result;
+		if (result)
+			return false;
+		first = false;
+		pattern_start = pattern;
+	}
+	result = tomoyo_file_matches_to_pattern2(filename, filename_end,
+						 pattern_start, pattern_end);
+	return first ? result : !result;
+}
+
+/**
+ * tomoyo_path_matches_pattern - Check whether the given filename matches the given pattern.
+ * @filename: The filename to check.
+ * @pattern:  The pattern to compare.
+ *
+ * Returns true if matches, false otherwise.
+ *
+ * The following patterns are available.
+ *   \\     \ itself.
+ *   \ooo   Octal representation of a byte.
+ *   \*     More than or equals to 0 character other than '/'.
+ *   \@     More than or equals to 0 character other than '/' or '.'.
+ *   \?     1 byte character other than '/'.
+ *   \$     More than or equals to 1 decimal digit.
+ *   \+     1 decimal digit.
+ *   \X     More than or equals to 1 hexadecimal digit.
+ *   \x     1 hexadecimal digit.
+ *   \A     More than or equals to 1 alphabet character.
+ *   \a     1 alphabet character.
+ *   \-     Subtraction operator.
+ */
+bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
+				 const struct tomoyo_path_info *pattern)
+{
+	/*
+	  if (!filename || !pattern)
+	  return false;
+	*/
+	const char *f = filename->name;
+	const char *p = pattern->name;
+	const int len = pattern->const_len;
+
+	/* If @pattern doesn't contain pattern, I can use strcmp(). */
+	if (!pattern->is_patterned)
+		return !tomoyo_pathcmp(filename, pattern);
+	/* Dont compare if the number of '/' differs. */
+	if (filename->depth != pattern->depth)
+		return false;
+	/* Compare the initial length without patterns. */
+	if (strncmp(f, p, len))
+		return false;
+	f += len;
+	p += len;
+	/* Main loop. Compare each directory component. */
+	while (*f && *p) {
+		const char *f_delimiter = strchr(f, '/');
+		const char *p_delimiter = strchr(p, '/');
+		if (!f_delimiter)
+			f_delimiter = f + strlen(f);
+		if (!p_delimiter)
+			p_delimiter = p + strlen(p);
+		if (!tomoyo_file_matches_to_pattern(f, f_delimiter,
+						    p, p_delimiter))
+			return false;
+		f = f_delimiter;
+		if (*f)
+			f++;
+		p = p_delimiter;
+		if (*p)
+			p++;
+	}
+	/* Ignore trailing "\*" and "\@" in @pattern. */
+	while (*p == '\\' &&
+	       (*(p + 1) == '*' || *(p + 1) == '@'))
+		p += 2;
+	return !*f && !*p;
+}
+
+/**
+ * tomoyo_io_printf - Transactional printf() to "struct tomoyo_io_buffer" structure.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @fmt:  The printf()'s format string, followed by parameters.
+ *
+ * Returns true if output was written, false otherwise.
+ *
+ * The snprintf() will truncate, but tomoyo_io_printf() won't.
+ */
+bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
+{
+	va_list args;
+	int len;
+	int pos = head->read_avail;
+	int size = head->readbuf_size - pos;
+
+	if (size <= 0)
+		return false;
+	va_start(args, fmt);
+	len = vsnprintf(head->read_buf + pos, size, fmt, args);
+	va_end(args);
+	if (pos + len >= head->readbuf_size)
+		return false;
+	head->read_avail += len;
+	return true;
+}
+
+/**
+ * tomoyo_get_exe - Get tomoyo_realpath() of current process.
+ *
+ * Returns the tomoyo_realpath() of current process on success, NULL otherwise.
+ *
+ * This function uses tomoyo_alloc(), so the caller must call tomoyo_free()
+ * if this function didn't return NULL.
+ */
+static const char *tomoyo_get_exe(void)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+	const char *cp = NULL;
+
+	if (!mm)
+		return NULL;
+	down_read(&mm->mmap_sem);
+	for (vma = mm->mmap; vma; vma = vma->vm_next) {
+		if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) {
+			cp = tomoyo_realpath_from_path(&vma->vm_file->f_path);
+			break;
+		}
+	}
+	up_read(&mm->mmap_sem);
+	return cp;
+}
+
+/**
+ * tomoyo_get_msg - Get warning message.
+ *
+ * @is_enforce: Is it enforcing mode?
+ *
+ * Returns "ERROR" or "WARNING".
+ */
+const char *tomoyo_get_msg(const bool is_enforce)
+{
+	if (is_enforce)
+		return "ERROR";
+	else
+		return "WARNING";
+}
+
+/**
+ * tomoyo_check_flags - Check mode for specified functionality.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @index:  The functionality to check mode.
+ *
+ * TOMOYO checks only process context.
+ * This code disables TOMOYO's enforcement in case the function is called from
+ * interrupt context.
+ */
+unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
+				const u8 index)
+{
+	const u8 profile = domain->profile;
+
+	if (WARN_ON(in_interrupt()))
+		return 0;
+	return tomoyo_policy_loaded && index < TOMOYO_MAX_CONTROL_INDEX
+#if TOMOYO_MAX_PROFILES != 256
+		&& profile < TOMOYO_MAX_PROFILES
+#endif
+		&& tomoyo_profile_ptr[profile] ?
+		tomoyo_profile_ptr[profile]->value[index] : 0;
+}
+
+/**
+ * tomoyo_verbose_mode - Check whether TOMOYO is verbose mode.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ *
+ * Returns true if domain policy violation warning should be printed to
+ * console.
+ */
+bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain)
+{
+	return tomoyo_check_flags(domain, TOMOYO_VERBOSE) != 0;
+}
+
+/**
+ * tomoyo_domain_quota_is_ok - Check for domain's quota.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ *
+ * Returns true if the domain is not exceeded quota, false otherwise.
+ */
+bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
+{
+	unsigned int count = 0;
+	struct tomoyo_acl_info *ptr;
+
+	if (!domain)
+		return true;
+	down_read(&tomoyo_domain_acl_info_list_lock);
+	list_for_each_entry(ptr, &domain->acl_info_list, list) {
+		if (ptr->type & TOMOYO_ACL_DELETED)
+			continue;
+		switch (tomoyo_acl_type2(ptr)) {
+			struct tomoyo_single_path_acl_record *acl1;
+			struct tomoyo_double_path_acl_record *acl2;
+			u16 perm;
+		case TOMOYO_TYPE_SINGLE_PATH_ACL:
+			acl1 = container_of(ptr,
+				    struct tomoyo_single_path_acl_record,
+					    head);
+			perm = acl1->perm;
+			if (perm & (1 << TOMOYO_TYPE_EXECUTE_ACL))
+				count++;
+			if (perm &
+			    ((1 << TOMOYO_TYPE_READ_ACL) |
+			     (1 << TOMOYO_TYPE_WRITE_ACL)))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_CREATE_ACL))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_UNLINK_ACL))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_MKDIR_ACL))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_RMDIR_ACL))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_MKFIFO_ACL))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_MKSOCK_ACL))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_MKBLOCK_ACL))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_MKCHAR_ACL))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_TRUNCATE_ACL))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_SYMLINK_ACL))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_REWRITE_ACL))
+				count++;
+			break;
+		case TOMOYO_TYPE_DOUBLE_PATH_ACL:
+			acl2 = container_of(ptr,
+				    struct tomoyo_double_path_acl_record,
+					    head);
+			perm = acl2->perm;
+			if (perm & (1 << TOMOYO_TYPE_LINK_ACL))
+				count++;
+			if (perm & (1 << TOMOYO_TYPE_RENAME_ACL))
+				count++;
+			break;
+		}
+	}
+	up_read(&tomoyo_domain_acl_info_list_lock);
+	if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY))
+		return true;
+	if (!domain->quota_warned) {
+		domain->quota_warned = true;
+		printk(KERN_WARNING "TOMOYO-WARNING: "
+		       "Domain '%s' has so many ACLs to hold. "
+		       "Stopped learning mode.\n", domain->domainname->name);
+	}
+	return false;
+}
+
+/**
+ * tomoyo_find_or_assign_new_profile - Create a new profile.
+ *
+ * @profile: Profile number to create.
+ *
+ * Returns pointer to "struct tomoyo_profile" on success, NULL otherwise.
+ */
+static struct tomoyo_profile *tomoyo_find_or_assign_new_profile(const unsigned
+								int profile)
+{
+	static DEFINE_MUTEX(lock);
+	struct tomoyo_profile *ptr = NULL;
+	int i;
+
+	if (profile >= TOMOYO_MAX_PROFILES)
+		return NULL;
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&lock);
+	ptr = tomoyo_profile_ptr[profile];
+	if (ptr)
+		goto ok;
+	ptr = tomoyo_alloc_element(sizeof(*ptr));
+	if (!ptr)
+		goto ok;
+	for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++)
+		ptr->value[i] = tomoyo_control_array[i].current_value;
+	mb(); /* Avoid out-of-order execution. */
+	tomoyo_profile_ptr[profile] = ptr;
+ ok:
+	mutex_unlock(&lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return ptr;
+}
+
+/**
+ * tomoyo_write_profile - Write to profile table.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_write_profile(struct tomoyo_io_buffer *head)
+{
+	char *data = head->write_buf;
+	unsigned int i;
+	unsigned int value;
+	char *cp;
+	struct tomoyo_profile *profile;
+	unsigned long num;
+
+	cp = strchr(data, '-');
+	if (cp)
+		*cp = '\0';
+	if (strict_strtoul(data, 10, &num))
+		return -EINVAL;
+	if (cp)
+		data = cp + 1;
+	profile = tomoyo_find_or_assign_new_profile(num);
+	if (!profile)
+		return -EINVAL;
+	cp = strchr(data, '=');
+	if (!cp)
+		return -EINVAL;
+	*cp = '\0';
+	if (!strcmp(data, "COMMENT")) {
+		profile->comment = tomoyo_save_name(cp + 1);
+		return 0;
+	}
+	for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++) {
+		if (strcmp(data, tomoyo_control_array[i].keyword))
+			continue;
+		if (sscanf(cp + 1, "%u", &value) != 1) {
+			int j;
+			const char **modes;
+			switch (i) {
+			case TOMOYO_VERBOSE:
+				modes = tomoyo_mode_2;
+				break;
+			default:
+				modes = tomoyo_mode_4;
+				break;
+			}
+			for (j = 0; j < 4; j++) {
+				if (strcmp(cp + 1, modes[j]))
+					continue;
+				value = j;
+				break;
+			}
+			if (j == 4)
+				return -EINVAL;
+		} else if (value > tomoyo_control_array[i].max_value) {
+			value = tomoyo_control_array[i].max_value;
+		}
+		profile->value[i] = value;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+/**
+ * tomoyo_read_profile - Read from profile table.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_read_profile(struct tomoyo_io_buffer *head)
+{
+	static const int total = TOMOYO_MAX_CONTROL_INDEX + 1;
+	int step;
+
+	if (head->read_eof)
+		return 0;
+	for (step = head->read_step; step < TOMOYO_MAX_PROFILES * total;
+	     step++) {
+		const u8 index = step / total;
+		u8 type = step % total;
+		const struct tomoyo_profile *profile
+			= tomoyo_profile_ptr[index];
+		head->read_step = step;
+		if (!profile)
+			continue;
+		if (!type) { /* Print profile' comment tag. */
+			if (!tomoyo_io_printf(head, "%u-COMMENT=%s\n",
+					      index, profile->comment ?
+					      profile->comment->name : ""))
+				break;
+			continue;
+		}
+		type--;
+		if (type < TOMOYO_MAX_CONTROL_INDEX) {
+			const unsigned int value = profile->value[type];
+			const char **modes = NULL;
+			const char *keyword
+				= tomoyo_control_array[type].keyword;
+			switch (tomoyo_control_array[type].max_value) {
+			case 3:
+				modes = tomoyo_mode_4;
+				break;
+			case 1:
+				modes = tomoyo_mode_2;
+				break;
+			}
+			if (modes) {
+				if (!tomoyo_io_printf(head, "%u-%s=%s\n", index,
+						      keyword, modes[value]))
+					break;
+			} else {
+				if (!tomoyo_io_printf(head, "%u-%s=%u\n", index,
+						      keyword, value))
+					break;
+			}
+		}
+	}
+	if (step == TOMOYO_MAX_PROFILES * total)
+		head->read_eof = true;
+	return 0;
+}
+
+/* Structure for policy manager. */
+struct tomoyo_policy_manager_entry {
+	struct list_head list;
+	/* A path to program or a domainname. */
+	const struct tomoyo_path_info *manager;
+	bool is_domain;  /* True if manager is a domainname. */
+	bool is_deleted; /* True if this entry is deleted. */
+};
+
+/* The list for "struct tomoyo_policy_manager_entry". */
+static LIST_HEAD(tomoyo_policy_manager_list);
+static DECLARE_RWSEM(tomoyo_policy_manager_list_lock);
+
+/**
+ * tomoyo_update_manager_entry - Add a manager entry.
+ *
+ * @manager:   The path to manager or the domainnamme.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_manager_entry(const char *manager,
+				       const bool is_delete)
+{
+	struct tomoyo_policy_manager_entry *new_entry;
+	struct tomoyo_policy_manager_entry *ptr;
+	const struct tomoyo_path_info *saved_manager;
+	int error = -ENOMEM;
+	bool is_domain = false;
+
+	if (tomoyo_is_domain_def(manager)) {
+		if (!tomoyo_is_correct_domain(manager, __func__))
+			return -EINVAL;
+		is_domain = true;
+	} else {
+		if (!tomoyo_is_correct_path(manager, 1, -1, -1, __func__))
+			return -EINVAL;
+	}
+	saved_manager = tomoyo_save_name(manager);
+	if (!saved_manager)
+		return -ENOMEM;
+	/***** EXCLUSIVE SECTION START *****/
+	down_write(&tomoyo_policy_manager_list_lock);
+	list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+		if (ptr->manager != saved_manager)
+			continue;
+		ptr->is_deleted = is_delete;
+		error = 0;
+		goto out;
+	}
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+	new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+	if (!new_entry)
+		goto out;
+	new_entry->manager = saved_manager;
+	new_entry->is_domain = is_domain;
+	list_add_tail(&new_entry->list, &tomoyo_policy_manager_list);
+	error = 0;
+ out:
+	up_write(&tomoyo_policy_manager_list_lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return error;
+}
+
+/**
+ * tomoyo_write_manager_policy - Write manager policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head)
+{
+	char *data = head->write_buf;
+	bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE);
+
+	if (!strcmp(data, "manage_by_non_root")) {
+		tomoyo_manage_by_non_root = !is_delete;
+		return 0;
+	}
+	return tomoyo_update_manager_entry(data, is_delete);
+}
+
+/**
+ * tomoyo_read_manager_policy - Read manager policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
+{
+	struct list_head *pos;
+	bool done = true;
+
+	if (head->read_eof)
+		return 0;
+	down_read(&tomoyo_policy_manager_list_lock);
+	list_for_each_cookie(pos, head->read_var2,
+			     &tomoyo_policy_manager_list) {
+		struct tomoyo_policy_manager_entry *ptr;
+		ptr = list_entry(pos, struct tomoyo_policy_manager_entry,
+				 list);
+		if (ptr->is_deleted)
+			continue;
+		if (!tomoyo_io_printf(head, "%s\n", ptr->manager->name)) {
+			done = false;
+			break;
+		}
+	}
+	up_read(&tomoyo_policy_manager_list_lock);
+	head->read_eof = done;
+	return 0;
+}
+
+/**
+ * tomoyo_is_policy_manager - Check whether the current process is a policy manager.
+ *
+ * Returns true if the current process is permitted to modify policy
+ * via /sys/kernel/security/tomoyo/ interface.
+ */
+static bool tomoyo_is_policy_manager(void)
+{
+	struct tomoyo_policy_manager_entry *ptr;
+	const char *exe;
+	const struct task_struct *task = current;
+	const struct tomoyo_path_info *domainname = tomoyo_domain()->domainname;
+	bool found = false;
+
+	if (!tomoyo_policy_loaded)
+		return true;
+	if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid))
+		return false;
+	down_read(&tomoyo_policy_manager_list_lock);
+	list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+		if (!ptr->is_deleted && ptr->is_domain
+		    && !tomoyo_pathcmp(domainname, ptr->manager)) {
+			found = true;
+			break;
+		}
+	}
+	up_read(&tomoyo_policy_manager_list_lock);
+	if (found)
+		return true;
+	exe = tomoyo_get_exe();
+	if (!exe)
+		return false;
+	down_read(&tomoyo_policy_manager_list_lock);
+	list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+		if (!ptr->is_deleted && !ptr->is_domain
+		    && !strcmp(exe, ptr->manager->name)) {
+			found = true;
+			break;
+		}
+	}
+	up_read(&tomoyo_policy_manager_list_lock);
+	if (!found) { /* Reduce error messages. */
+		static pid_t last_pid;
+		const pid_t pid = current->pid;
+		if (last_pid != pid) {
+			printk(KERN_WARNING "%s ( %s ) is not permitted to "
+			       "update policies.\n", domainname->name, exe);
+			last_pid = pid;
+		}
+	}
+	tomoyo_free(exe);
+	return found;
+}
+
+/**
+ * tomoyo_is_select_one - Parse select command.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @data: String to parse.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
+				 const char *data)
+{
+	unsigned int pid;
+	struct tomoyo_domain_info *domain = NULL;
+
+	if (sscanf(data, "pid=%u", &pid) == 1) {
+		struct task_struct *p;
+		/***** CRITICAL SECTION START *****/
+		read_lock(&tasklist_lock);
+		p = find_task_by_vpid(pid);
+		if (p)
+			domain = tomoyo_real_domain(p);
+		read_unlock(&tasklist_lock);
+		/***** CRITICAL SECTION END *****/
+	} else if (!strncmp(data, "domain=", 7)) {
+		if (tomoyo_is_domain_def(data + 7)) {
+			down_read(&tomoyo_domain_list_lock);
+			domain = tomoyo_find_domain(data + 7);
+			up_read(&tomoyo_domain_list_lock);
+		}
+	} else
+		return false;
+	head->write_var1 = domain;
+	/* Accessing read_buf is safe because head->io_sem is held. */
+	if (!head->read_buf)
+		return true; /* Do nothing if open(O_WRONLY). */
+	head->read_avail = 0;
+	tomoyo_io_printf(head, "# select %s\n", data);
+	head->read_single_domain = true;
+	head->read_eof = !domain;
+	if (domain) {
+		struct tomoyo_domain_info *d;
+		head->read_var1 = NULL;
+		down_read(&tomoyo_domain_list_lock);
+		list_for_each_entry(d, &tomoyo_domain_list, list) {
+			if (d == domain)
+				break;
+			head->read_var1 = &d->list;
+		}
+		up_read(&tomoyo_domain_list_lock);
+		head->read_var2 = NULL;
+		head->read_bit = 0;
+		head->read_step = 0;
+		if (domain->is_deleted)
+			tomoyo_io_printf(head, "# This is a deleted domain.\n");
+	}
+	return true;
+}
+
+/**
+ * tomoyo_write_domain_policy - Write domain policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
+{
+	char *data = head->write_buf;
+	struct tomoyo_domain_info *domain = head->write_var1;
+	bool is_delete = false;
+	bool is_select = false;
+	bool is_undelete = false;
+	unsigned int profile;
+
+	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE))
+		is_delete = true;
+	else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_SELECT))
+		is_select = true;
+	else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_UNDELETE))
+		is_undelete = true;
+	if (is_select && tomoyo_is_select_one(head, data))
+		return 0;
+	/* Don't allow updating policies by non manager programs. */
+	if (!tomoyo_is_policy_manager())
+		return -EPERM;
+	if (tomoyo_is_domain_def(data)) {
+		domain = NULL;
+		if (is_delete)
+			tomoyo_delete_domain(data);
+		else if (is_select) {
+			down_read(&tomoyo_domain_list_lock);
+			domain = tomoyo_find_domain(data);
+			up_read(&tomoyo_domain_list_lock);
+		} else if (is_undelete)
+			domain = tomoyo_undelete_domain(data);
+		else
+			domain = tomoyo_find_or_assign_new_domain(data, 0);
+		head->write_var1 = domain;
+		return 0;
+	}
+	if (!domain)
+		return -EINVAL;
+
+	if (sscanf(data, TOMOYO_KEYWORD_USE_PROFILE "%u", &profile) == 1
+	    && profile < TOMOYO_MAX_PROFILES) {
+		if (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded)
+			domain->profile = (u8) profile;
+		return 0;
+	}
+	if (!strcmp(data, TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ)) {
+		tomoyo_set_domain_flag(domain, is_delete,
+			       TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ);
+		return 0;
+	}
+	return tomoyo_write_file_policy(data, domain, is_delete);
+}
+
+/**
+ * tomoyo_print_single_path_acl - Print a single path ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr:  Pointer to "struct tomoyo_single_path_acl_record".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_single_path_acl(struct tomoyo_io_buffer *head,
+					 struct tomoyo_single_path_acl_record *
+					 ptr)
+{
+	int pos;
+	u8 bit;
+	const char *atmark = "";
+	const char *filename;
+	const u16 perm = ptr->perm;
+
+	filename = ptr->filename->name;
+	for (bit = head->read_bit; bit < TOMOYO_MAX_SINGLE_PATH_OPERATION;
+	     bit++) {
+		const char *msg;
+		if (!(perm & (1 << bit)))
+			continue;
+		/* Print "read/write" instead of "read" and "write". */
+		if ((bit == TOMOYO_TYPE_READ_ACL ||
+		     bit == TOMOYO_TYPE_WRITE_ACL)
+		    && (perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL)))
+			continue;
+		msg = tomoyo_sp2keyword(bit);
+		pos = head->read_avail;
+		if (!tomoyo_io_printf(head, "allow_%s %s%s\n", msg,
+				      atmark, filename))
+			goto out;
+	}
+	head->read_bit = 0;
+	return true;
+ out:
+	head->read_bit = bit;
+	head->read_avail = pos;
+	return false;
+}
+
+/**
+ * tomoyo_print_double_path_acl - Print a double path ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr:  Pointer to "struct tomoyo_double_path_acl_record".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_double_path_acl(struct tomoyo_io_buffer *head,
+					 struct tomoyo_double_path_acl_record *
+					 ptr)
+{
+	int pos;
+	const char *atmark1 = "";
+	const char *atmark2 = "";
+	const char *filename1;
+	const char *filename2;
+	const u8 perm = ptr->perm;
+	u8 bit;
+
+	filename1 = ptr->filename1->name;
+	filename2 = ptr->filename2->name;
+	for (bit = head->read_bit; bit < TOMOYO_MAX_DOUBLE_PATH_OPERATION;
+	     bit++) {
+		const char *msg;
+		if (!(perm & (1 << bit)))
+			continue;
+		msg = tomoyo_dp2keyword(bit);
+		pos = head->read_avail;
+		if (!tomoyo_io_printf(head, "allow_%s %s%s %s%s\n", msg,
+				      atmark1, filename1, atmark2, filename2))
+			goto out;
+	}
+	head->read_bit = 0;
+	return true;
+ out:
+	head->read_bit = bit;
+	head->read_avail = pos;
+	return false;
+}
+
+/**
+ * tomoyo_print_entry - Print an ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr:  Pointer to an ACL entry.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
+			       struct tomoyo_acl_info *ptr)
+{
+	const u8 acl_type = tomoyo_acl_type2(ptr);
+
+	if (acl_type & TOMOYO_ACL_DELETED)
+		return true;
+	if (acl_type == TOMOYO_TYPE_SINGLE_PATH_ACL) {
+		struct tomoyo_single_path_acl_record *acl
+			= container_of(ptr,
+				       struct tomoyo_single_path_acl_record,
+				       head);
+		return tomoyo_print_single_path_acl(head, acl);
+	}
+	if (acl_type == TOMOYO_TYPE_DOUBLE_PATH_ACL) {
+		struct tomoyo_double_path_acl_record *acl
+			= container_of(ptr,
+				       struct tomoyo_double_path_acl_record,
+				       head);
+		return tomoyo_print_double_path_acl(head, acl);
+	}
+	BUG(); /* This must not happen. */
+	return false;
+}
+
+/**
+ * tomoyo_read_domain_policy - Read domain policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head)
+{
+	struct list_head *dpos;
+	struct list_head *apos;
+	bool done = true;
+
+	if (head->read_eof)
+		return 0;
+	if (head->read_step == 0)
+		head->read_step = 1;
+	down_read(&tomoyo_domain_list_lock);
+	list_for_each_cookie(dpos, head->read_var1, &tomoyo_domain_list) {
+		struct tomoyo_domain_info *domain;
+		const char *quota_exceeded = "";
+		const char *transition_failed = "";
+		const char *ignore_global_allow_read = "";
+		domain = list_entry(dpos, struct tomoyo_domain_info, list);
+		if (head->read_step != 1)
+			goto acl_loop;
+		if (domain->is_deleted && !head->read_single_domain)
+			continue;
+		/* Print domainname and flags. */
+		if (domain->quota_warned)
+			quota_exceeded = "quota_exceeded\n";
+		if (domain->flags & TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED)
+			transition_failed = "transition_failed\n";
+		if (domain->flags &
+		    TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ)
+			ignore_global_allow_read
+				= TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "\n";
+		if (!tomoyo_io_printf(head,
+				      "%s\n" TOMOYO_KEYWORD_USE_PROFILE "%u\n"
+				      "%s%s%s\n", domain->domainname->name,
+				      domain->profile, quota_exceeded,
+				      transition_failed,
+				      ignore_global_allow_read)) {
+			done = false;
+			break;
+		}
+		head->read_step = 2;
+acl_loop:
+		if (head->read_step == 3)
+			goto tail_mark;
+		/* Print ACL entries in the domain. */
+		down_read(&tomoyo_domain_acl_info_list_lock);
+		list_for_each_cookie(apos, head->read_var2,
+				      &domain->acl_info_list) {
+			struct tomoyo_acl_info *ptr
+				= list_entry(apos, struct tomoyo_acl_info,
+					      list);
+			if (!tomoyo_print_entry(head, ptr)) {
+				done = false;
+				break;
+			}
+		}
+		up_read(&tomoyo_domain_acl_info_list_lock);
+		if (!done)
+			break;
+		head->read_step = 3;
+tail_mark:
+		if (!tomoyo_io_printf(head, "\n")) {
+			done = false;
+			break;
+		}
+		head->read_step = 1;
+		if (head->read_single_domain)
+			break;
+	}
+	up_read(&tomoyo_domain_list_lock);
+	head->read_eof = done;
+	return 0;
+}
+
+/**
+ * tomoyo_write_domain_profile - Assign profile for specified domain.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, -EINVAL otherwise.
+ *
+ * This is equivalent to doing
+ *
+ *     ( echo "select " $domainname; echo "use_profile " $profile ) |
+ *     /usr/lib/ccs/loadpolicy -d
+ */
+static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
+{
+	char *data = head->write_buf;
+	char *cp = strchr(data, ' ');
+	struct tomoyo_domain_info *domain;
+	unsigned long profile;
+
+	if (!cp)
+		return -EINVAL;
+	*cp = '\0';
+	down_read(&tomoyo_domain_list_lock);
+	domain = tomoyo_find_domain(cp + 1);
+	up_read(&tomoyo_domain_list_lock);
+	if (strict_strtoul(data, 10, &profile))
+		return -EINVAL;
+	if (domain && profile < TOMOYO_MAX_PROFILES
+	    && (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded))
+		domain->profile = (u8) profile;
+	return 0;
+}
+
+/**
+ * tomoyo_read_domain_profile - Read only domainname and profile.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns list of profile number and domainname pairs.
+ *
+ * This is equivalent to doing
+ *
+ *     grep -A 1 '^<kernel>' /sys/kernel/security/tomoyo/domain_policy |
+ *     awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" )
+ *     domainname = $0; } else if ( $1 == "use_profile" ) {
+ *     print $2 " " domainname; domainname = ""; } } ; '
+ */
+static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
+{
+	struct list_head *pos;
+	bool done = true;
+
+	if (head->read_eof)
+		return 0;
+	down_read(&tomoyo_domain_list_lock);
+	list_for_each_cookie(pos, head->read_var1, &tomoyo_domain_list) {
+		struct tomoyo_domain_info *domain;
+		domain = list_entry(pos, struct tomoyo_domain_info, list);
+		if (domain->is_deleted)
+			continue;
+		if (!tomoyo_io_printf(head, "%u %s\n", domain->profile,
+				      domain->domainname->name)) {
+			done = false;
+			break;
+		}
+	}
+	up_read(&tomoyo_domain_list_lock);
+	head->read_eof = done;
+	return 0;
+}
+
+/**
+ * tomoyo_write_pid: Specify PID to obtain domainname.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_write_pid(struct tomoyo_io_buffer *head)
+{
+	unsigned long pid;
+	/* No error check. */
+	strict_strtoul(head->write_buf, 10, &pid);
+	head->read_step = (int) pid;
+	head->read_eof = false;
+	return 0;
+}
+
+/**
+ * tomoyo_read_pid - Get domainname of the specified PID.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns the domainname which the specified PID is in on success,
+ * empty string otherwise.
+ * The PID is specified by tomoyo_write_pid() so that the user can obtain
+ * using read()/write() interface rather than sysctl() interface.
+ */
+static int tomoyo_read_pid(struct tomoyo_io_buffer *head)
+{
+	if (head->read_avail == 0 && !head->read_eof) {
+		const int pid = head->read_step;
+		struct task_struct *p;
+		struct tomoyo_domain_info *domain = NULL;
+		/***** CRITICAL SECTION START *****/
+		read_lock(&tasklist_lock);
+		p = find_task_by_vpid(pid);
+		if (p)
+			domain = tomoyo_real_domain(p);
+		read_unlock(&tasklist_lock);
+		/***** CRITICAL SECTION END *****/
+		if (domain)
+			tomoyo_io_printf(head, "%d %u %s", pid, domain->profile,
+					 domain->domainname->name);
+		head->read_eof = true;
+	}
+	return 0;
+}
+
+/**
+ * tomoyo_write_exception_policy - Write exception policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head)
+{
+	char *data = head->write_buf;
+	bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE);
+
+	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_KEEP_DOMAIN))
+		return tomoyo_write_domain_keeper_policy(data, false,
+							 is_delete);
+	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NO_KEEP_DOMAIN))
+		return tomoyo_write_domain_keeper_policy(data, true, is_delete);
+	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_INITIALIZE_DOMAIN))
+		return tomoyo_write_domain_initializer_policy(data, false,
+							      is_delete);
+	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN))
+		return tomoyo_write_domain_initializer_policy(data, true,
+							      is_delete);
+	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALIAS))
+		return tomoyo_write_alias_policy(data, is_delete);
+	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_READ))
+		return tomoyo_write_globally_readable_policy(data, is_delete);
+	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_FILE_PATTERN))
+		return tomoyo_write_pattern_policy(data, is_delete);
+	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DENY_REWRITE))
+		return tomoyo_write_no_rewrite_policy(data, is_delete);
+	return -EINVAL;
+}
+
+/**
+ * tomoyo_read_exception_policy - Read exception policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, -EINVAL otherwise.
+ */
+static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head)
+{
+	if (!head->read_eof) {
+		switch (head->read_step) {
+		case 0:
+			head->read_var2 = NULL;
+			head->read_step = 1;
+		case 1:
+			if (!tomoyo_read_domain_keeper_policy(head))
+				break;
+			head->read_var2 = NULL;
+			head->read_step = 2;
+		case 2:
+			if (!tomoyo_read_globally_readable_policy(head))
+				break;
+			head->read_var2 = NULL;
+			head->read_step = 3;
+		case 3:
+			head->read_var2 = NULL;
+			head->read_step = 4;
+		case 4:
+			if (!tomoyo_read_domain_initializer_policy(head))
+				break;
+			head->read_var2 = NULL;
+			head->read_step = 5;
+		case 5:
+			if (!tomoyo_read_alias_policy(head))
+				break;
+			head->read_var2 = NULL;
+			head->read_step = 6;
+		case 6:
+			head->read_var2 = NULL;
+			head->read_step = 7;
+		case 7:
+			if (!tomoyo_read_file_pattern(head))
+				break;
+			head->read_var2 = NULL;
+			head->read_step = 8;
+		case 8:
+			if (!tomoyo_read_no_rewrite_policy(head))
+				break;
+			head->read_var2 = NULL;
+			head->read_step = 9;
+		case 9:
+			head->read_eof = true;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+/* path to policy loader */
+static const char *tomoyo_loader = "/sbin/tomoyo-init";
+
+/**
+ * tomoyo_policy_loader_exists - Check whether /sbin/tomoyo-init exists.
+ *
+ * Returns true if /sbin/tomoyo-init exists, false otherwise.
+ */
+static bool tomoyo_policy_loader_exists(void)
+{
+	/*
+	 * Don't activate MAC if the policy loader doesn't exist.
+	 * If the initrd includes /sbin/init but real-root-dev has not
+	 * mounted on / yet, activating MAC will block the system since
+	 * policies are not loaded yet.
+	 * Thus, let do_execve() call this function everytime.
+	 */
+	struct nameidata nd;
+
+	if (path_lookup(tomoyo_loader, LOOKUP_FOLLOW, &nd)) {
+		printk(KERN_INFO "Not activating Mandatory Access Control now "
+		       "since %s doesn't exist.\n", tomoyo_loader);
+		return false;
+	}
+	path_put(&nd.path);
+	return true;
+}
+
+/**
+ * tomoyo_load_policy - Run external policy loader to load policy.
+ *
+ * @filename: The program about to start.
+ *
+ * This function checks whether @filename is /sbin/init , and if so
+ * invoke /sbin/tomoyo-init and wait for the termination of /sbin/tomoyo-init
+ * and then continues invocation of /sbin/init.
+ * /sbin/tomoyo-init reads policy files in /etc/tomoyo/ directory and
+ * writes to /sys/kernel/security/tomoyo/ interfaces.
+ *
+ * Returns nothing.
+ */
+void tomoyo_load_policy(const char *filename)
+{
+	char *argv[2];
+	char *envp[3];
+
+	if (tomoyo_policy_loaded)
+		return;
+	/*
+	 * Check filename is /sbin/init or /sbin/tomoyo-start.
+	 * /sbin/tomoyo-start is a dummy filename in case where /sbin/init can't
+	 * be passed.
+	 * You can create /sbin/tomoyo-start by
+	 * "ln -s /bin/true /sbin/tomoyo-start".
+	 */
+	if (strcmp(filename, "/sbin/init") &&
+	    strcmp(filename, "/sbin/tomoyo-start"))
+		return;
+	if (!tomoyo_policy_loader_exists())
+		return;
+
+	printk(KERN_INFO "Calling %s to load policy. Please wait.\n",
+	       tomoyo_loader);
+	argv[0] = (char *) tomoyo_loader;
+	argv[1] = NULL;
+	envp[0] = "HOME=/";
+	envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+	envp[2] = NULL;
+	call_usermodehelper(argv[0], argv, envp, 1);
+
+	printk(KERN_INFO "TOMOYO: 2.2.0-pre   2009/02/01\n");
+	printk(KERN_INFO "Mandatory Access Control activated.\n");
+	tomoyo_policy_loaded = true;
+	{ /* Check all profiles currently assigned to domains are defined. */
+		struct tomoyo_domain_info *domain;
+		down_read(&tomoyo_domain_list_lock);
+		list_for_each_entry(domain, &tomoyo_domain_list, list) {
+			const u8 profile = domain->profile;
+			if (tomoyo_profile_ptr[profile])
+				continue;
+			panic("Profile %u (used by '%s') not defined.\n",
+			      profile, domain->domainname->name);
+		}
+		up_read(&tomoyo_domain_list_lock);
+	}
+}
+
+/**
+ * tomoyo_read_version: Get version.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns version information.
+ */
+static int tomoyo_read_version(struct tomoyo_io_buffer *head)
+{
+	if (!head->read_eof) {
+		tomoyo_io_printf(head, "2.2.0-pre");
+		head->read_eof = true;
+	}
+	return 0;
+}
+
+/**
+ * tomoyo_read_self_domain - Get the current process's domainname.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns the current process's domainname.
+ */
+static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head)
+{
+	if (!head->read_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);
+		head->read_eof = true;
+	}
+	return 0;
+}
+
+/**
+ * tomoyo_open_control - open() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @type: Type of interface.
+ * @file: Pointer to "struct file".
+ *
+ * Associates policy handler and returns 0 on success, -ENOMEM otherwise.
+ */
+static int tomoyo_open_control(const u8 type, struct file *file)
+{
+	struct tomoyo_io_buffer *head = tomoyo_alloc(sizeof(*head));
+
+	if (!head)
+		return -ENOMEM;
+	mutex_init(&head->io_sem);
+	switch (type) {
+	case TOMOYO_DOMAINPOLICY:
+		/* /sys/kernel/security/tomoyo/domain_policy */
+		head->write = tomoyo_write_domain_policy;
+		head->read = tomoyo_read_domain_policy;
+		break;
+	case TOMOYO_EXCEPTIONPOLICY:
+		/* /sys/kernel/security/tomoyo/exception_policy */
+		head->write = tomoyo_write_exception_policy;
+		head->read = tomoyo_read_exception_policy;
+		break;
+	case TOMOYO_SELFDOMAIN:
+		/* /sys/kernel/security/tomoyo/self_domain */
+		head->read = tomoyo_read_self_domain;
+		break;
+	case TOMOYO_DOMAIN_STATUS:
+		/* /sys/kernel/security/tomoyo/.domain_status */
+		head->write = tomoyo_write_domain_profile;
+		head->read = tomoyo_read_domain_profile;
+		break;
+	case TOMOYO_PROCESS_STATUS:
+		/* /sys/kernel/security/tomoyo/.process_status */
+		head->write = tomoyo_write_pid;
+		head->read = tomoyo_read_pid;
+		break;
+	case TOMOYO_VERSION:
+		/* /sys/kernel/security/tomoyo/version */
+		head->read = tomoyo_read_version;
+		head->readbuf_size = 128;
+		break;
+	case TOMOYO_MEMINFO:
+		/* /sys/kernel/security/tomoyo/meminfo */
+		head->write = tomoyo_write_memory_quota;
+		head->read = tomoyo_read_memory_counter;
+		head->readbuf_size = 512;
+		break;
+	case TOMOYO_PROFILE:
+		/* /sys/kernel/security/tomoyo/profile */
+		head->write = tomoyo_write_profile;
+		head->read = tomoyo_read_profile;
+		break;
+	case TOMOYO_MANAGER:
+		/* /sys/kernel/security/tomoyo/manager */
+		head->write = tomoyo_write_manager_policy;
+		head->read = tomoyo_read_manager_policy;
+		break;
+	}
+	if (!(file->f_mode & FMODE_READ)) {
+		/*
+		 * No need to allocate read_buf since it is not opened
+		 * for reading.
+		 */
+		head->read = NULL;
+	} else {
+		if (!head->readbuf_size)
+			head->readbuf_size = 4096 * 2;
+		head->read_buf = tomoyo_alloc(head->readbuf_size);
+		if (!head->read_buf) {
+			tomoyo_free(head);
+			return -ENOMEM;
+		}
+	}
+	if (!(file->f_mode & FMODE_WRITE)) {
+		/*
+		 * No need to allocate write_buf since it is not opened
+		 * for writing.
+		 */
+		head->write = NULL;
+	} else if (head->write) {
+		head->writebuf_size = 4096 * 2;
+		head->write_buf = tomoyo_alloc(head->writebuf_size);
+		if (!head->write_buf) {
+			tomoyo_free(head->read_buf);
+			tomoyo_free(head);
+			return -ENOMEM;
+		}
+	}
+	file->private_data = head;
+	/*
+	 * Call the handler now if the file is
+	 * /sys/kernel/security/tomoyo/self_domain
+	 * so that the user can use
+	 * cat < /sys/kernel/security/tomoyo/self_domain"
+	 * to know the current process's domainname.
+	 */
+	if (type == TOMOYO_SELFDOMAIN)
+		tomoyo_read_control(file, NULL, 0);
+	return 0;
+}
+
+/**
+ * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file:       Pointer to "struct file".
+ * @buffer:     Poiner to buffer to write to.
+ * @buffer_len: Size of @buffer.
+ *
+ * Returns bytes read on success, negative value otherwise.
+ */
+static int tomoyo_read_control(struct file *file, char __user *buffer,
+			       const int buffer_len)
+{
+	int len = 0;
+	struct tomoyo_io_buffer *head = file->private_data;
+	char *cp;
+
+	if (!head->read)
+		return -ENOSYS;
+	if (mutex_lock_interruptible(&head->io_sem))
+		return -EINTR;
+	/* Call the policy handler. */
+	len = head->read(head);
+	if (len < 0)
+		goto out;
+	/* Write to buffer. */
+	len = head->read_avail;
+	if (len > buffer_len)
+		len = buffer_len;
+	if (!len)
+		goto out;
+	/* head->read_buf changes by some functions. */
+	cp = head->read_buf;
+	if (copy_to_user(buffer, cp, len)) {
+		len = -EFAULT;
+		goto out;
+	}
+	head->read_avail -= len;
+	memmove(cp, cp + len, head->read_avail);
+ out:
+	mutex_unlock(&head->io_sem);
+	return len;
+}
+
+/**
+ * tomoyo_write_control - write() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file:       Pointer to "struct file".
+ * @buffer:     Pointer to buffer to read from.
+ * @buffer_len: Size of @buffer.
+ *
+ * Returns @buffer_len on success, negative value otherwise.
+ */
+static int tomoyo_write_control(struct file *file, const char __user *buffer,
+				const int buffer_len)
+{
+	struct tomoyo_io_buffer *head = file->private_data;
+	int error = buffer_len;
+	int avail_len = buffer_len;
+	char *cp0 = head->write_buf;
+
+	if (!head->write)
+		return -ENOSYS;
+	if (!access_ok(VERIFY_READ, buffer, buffer_len))
+		return -EFAULT;
+	/* Don't allow updating policies by non manager programs. */
+	if (head->write != tomoyo_write_pid &&
+	    head->write != tomoyo_write_domain_policy &&
+	    !tomoyo_is_policy_manager())
+		return -EPERM;
+	if (mutex_lock_interruptible(&head->io_sem))
+		return -EINTR;
+	/* Read a line and dispatch it to the policy handler. */
+	while (avail_len > 0) {
+		char c;
+		if (head->write_avail >= head->writebuf_size - 1) {
+			error = -ENOMEM;
+			break;
+		} else if (get_user(c, buffer)) {
+			error = -EFAULT;
+			break;
+		}
+		buffer++;
+		avail_len--;
+		cp0[head->write_avail++] = c;
+		if (c != '\n')
+			continue;
+		cp0[head->write_avail - 1] = '\0';
+		head->write_avail = 0;
+		tomoyo_normalize_line(cp0);
+		head->write(head);
+	}
+	mutex_unlock(&head->io_sem);
+	return error;
+}
+
+/**
+ * tomoyo_close_control - close() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file: Pointer to "struct file".
+ *
+ * Releases memory and returns 0.
+ */
+static int tomoyo_close_control(struct file *file)
+{
+	struct tomoyo_io_buffer *head = file->private_data;
+
+	/* Release memory used for policy I/O. */
+	tomoyo_free(head->read_buf);
+	head->read_buf = NULL;
+	tomoyo_free(head->write_buf);
+	head->write_buf = NULL;
+	tomoyo_free(head);
+	head = NULL;
+	file->private_data = NULL;
+	return 0;
+}
+
+/**
+ * tomoyo_alloc_acl_element - Allocate permanent memory for ACL entry.
+ *
+ * @acl_type:  Type of ACL entry.
+ *
+ * Returns pointer to the ACL entry on success, NULL otherwise.
+ */
+void *tomoyo_alloc_acl_element(const u8 acl_type)
+{
+	int len;
+	struct tomoyo_acl_info *ptr;
+
+	switch (acl_type) {
+	case TOMOYO_TYPE_SINGLE_PATH_ACL:
+		len = sizeof(struct tomoyo_single_path_acl_record);
+		break;
+	case TOMOYO_TYPE_DOUBLE_PATH_ACL:
+		len = sizeof(struct tomoyo_double_path_acl_record);
+		break;
+	default:
+		return NULL;
+	}
+	ptr = tomoyo_alloc_element(len);
+	if (!ptr)
+		return NULL;
+	ptr->type = acl_type;
+	return ptr;
+}
+
+/**
+ * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @inode: Pointer to "struct inode".
+ * @file:  Pointer to "struct file".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_open(struct inode *inode, struct file *file)
+{
+	const int key = ((u8 *) file->f_path.dentry->d_inode->i_private)
+		- ((u8 *) NULL);
+	return tomoyo_open_control(key, file);
+}
+
+/**
+ * tomoyo_release - close() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @inode: Pointer to "struct inode".
+ * @file:  Pointer to "struct file".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_release(struct inode *inode, struct file *file)
+{
+	return tomoyo_close_control(file);
+}
+
+/**
+ * tomoyo_read - read() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file:  Pointer to "struct file".
+ * @buf:   Pointer to buffer.
+ * @count: Size of @buf.
+ * @ppos:  Unused.
+ *
+ * Returns bytes read on success, negative value otherwise.
+ */
+static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count,
+			   loff_t *ppos)
+{
+	return tomoyo_read_control(file, buf, count);
+}
+
+/**
+ * tomoyo_write - write() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file:  Pointer to "struct file".
+ * @buf:   Pointer to buffer.
+ * @count: Size of @buf.
+ * @ppos:  Unused.
+ *
+ * Returns @count on success, negative value otherwise.
+ */
+static ssize_t tomoyo_write(struct file *file, const char __user *buf,
+			    size_t count, loff_t *ppos)
+{
+	return tomoyo_write_control(file, buf, count);
+}
+
+/* Operations for /sys/kernel/security/tomoyo/ interface. */
+static const struct file_operations tomoyo_operations = {
+	.open    = tomoyo_open,
+	.release = tomoyo_release,
+	.read    = tomoyo_read,
+	.write   = tomoyo_write,
+};
+
+/**
+ * tomoyo_create_entry - Create interface files under /sys/kernel/security/tomoyo/ directory.
+ *
+ * @name:   The name of the interface file.
+ * @mode:   The permission of the interface file.
+ * @parent: The parent directory.
+ * @key:    Type of interface.
+ *
+ * Returns nothing.
+ */
+static void __init tomoyo_create_entry(const char *name, const mode_t mode,
+				       struct dentry *parent, const u8 key)
+{
+	securityfs_create_file(name, mode, parent, ((u8 *) NULL) + key,
+			       &tomoyo_operations);
+}
+
+/**
+ * tomoyo_initerface_init - Initialize /sys/kernel/security/tomoyo/ interface.
+ *
+ * Returns 0.
+ */
+static int __init tomoyo_initerface_init(void)
+{
+	struct dentry *tomoyo_dir;
+
+	/* Don't create securityfs entries unless registered. */
+	if (current_cred()->security != &tomoyo_kernel_domain)
+		return 0;
+
+	tomoyo_dir = securityfs_create_dir("tomoyo", NULL);
+	tomoyo_create_entry("domain_policy",    0600, tomoyo_dir,
+			    TOMOYO_DOMAINPOLICY);
+	tomoyo_create_entry("exception_policy", 0600, tomoyo_dir,
+			    TOMOYO_EXCEPTIONPOLICY);
+	tomoyo_create_entry("self_domain",      0400, tomoyo_dir,
+			    TOMOYO_SELFDOMAIN);
+	tomoyo_create_entry(".domain_status",   0600, tomoyo_dir,
+			    TOMOYO_DOMAIN_STATUS);
+	tomoyo_create_entry(".process_status",  0600, tomoyo_dir,
+			    TOMOYO_PROCESS_STATUS);
+	tomoyo_create_entry("meminfo",          0600, tomoyo_dir,
+			    TOMOYO_MEMINFO);
+	tomoyo_create_entry("profile",          0600, tomoyo_dir,
+			    TOMOYO_PROFILE);
+	tomoyo_create_entry("manager",          0600, tomoyo_dir,
+			    TOMOYO_MANAGER);
+	tomoyo_create_entry("version",          0400, tomoyo_dir,
+			    TOMOYO_VERSION);
+	return 0;
+}
+
+fs_initcall(tomoyo_initerface_init);

+ 359 - 0
security/tomoyo/common.h

@@ -0,0 +1,359 @@
+/*
+ * security/tomoyo/common.h
+ *
+ * Common functions for TOMOYO.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/02/01
+ *
+ */
+
+#ifndef _SECURITY_TOMOYO_COMMON_H
+#define _SECURITY_TOMOYO_COMMON_H
+
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/file.h>
+#include <linux/kmod.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include <linux/list.h>
+
+struct dentry;
+struct vfsmount;
+
+/* Temporary buffer for holding pathnames. */
+struct tomoyo_page_buffer {
+	char buffer[4096];
+};
+
+/* Structure for holding a token. */
+struct tomoyo_path_info {
+	const char *name;
+	u32 hash;          /* = full_name_hash(name, strlen(name)) */
+	u16 total_len;     /* = strlen(name)                       */
+	u16 const_len;     /* = tomoyo_const_part_length(name)     */
+	bool is_dir;       /* = tomoyo_strendswith(name, "/")      */
+	bool is_patterned; /* = tomoyo_path_contains_pattern(name) */
+	u16 depth;         /* = tomoyo_path_depth(name)            */
+};
+
+/*
+ * This is the max length of a token.
+ *
+ * A token consists of only ASCII printable characters.
+ * Non printable characters in a token is represented in \ooo style
+ * octal string. Thus, \ itself is represented as \\.
+ */
+#define TOMOYO_MAX_PATHNAME_LEN 4000
+
+/* Structure for holding requested pathname. */
+struct tomoyo_path_info_with_data {
+	/* Keep "head" first, for this pointer is passed to tomoyo_free(). */
+	struct tomoyo_path_info head;
+	char bariier1[16]; /* Safeguard for overrun. */
+	char body[TOMOYO_MAX_PATHNAME_LEN];
+	char barrier2[16]; /* Safeguard for overrun. */
+};
+
+/*
+ * Common header for holding ACL entries.
+ *
+ * Packing "struct tomoyo_acl_info" allows
+ * "struct tomoyo_single_path_acl_record" to embed "u16" and
+ * "struct tomoyo_double_path_acl_record" to embed "u8"
+ * without enlarging their structure size.
+ */
+struct tomoyo_acl_info {
+	struct list_head list;
+	/*
+	 * Type of this ACL entry.
+	 *
+	 * MSB is is_deleted flag.
+	 */
+	u8 type;
+} __packed;
+
+/* This ACL entry is deleted.           */
+#define TOMOYO_ACL_DELETED        0x80
+
+/* Structure for domain information. */
+struct tomoyo_domain_info {
+	struct list_head list;
+	struct list_head acl_info_list;
+	/* Name of this domain. Never NULL.          */
+	const struct tomoyo_path_info *domainname;
+	u8 profile;        /* Profile number to use. */
+	u8 is_deleted;     /* Delete flag.
+			      0 = active.
+			      1 = deleted but undeletable.
+			      255 = deleted and no longer undeletable. */
+	bool quota_warned; /* Quota warnning flag.   */
+	/* DOMAIN_FLAGS_*. Use tomoyo_set_domain_flag() to modify. */
+	u8 flags;
+};
+
+/* Profile number is an integer between 0 and 255. */
+#define TOMOYO_MAX_PROFILES 256
+
+/* Ignore "allow_read" directive in exception policy. */
+#define TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ 1
+/*
+ * This domain was unable to create a new domain at tomoyo_find_next_domain()
+ * because the name of the domain to be created was too long or
+ * it could not allocate memory.
+ * More than one process continued execve() without domain transition.
+ */
+#define TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED        2
+
+/*
+ * Structure for "allow_read/write", "allow_execute", "allow_read",
+ * "allow_write", "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir",
+ * "allow_mkfifo", "allow_mksock", "allow_mkblock", "allow_mkchar",
+ * "allow_truncate", "allow_symlink" and "allow_rewrite" directive.
+ */
+struct tomoyo_single_path_acl_record {
+	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_SINGLE_PATH_ACL */
+	u16 perm;
+	/* Pointer to single pathname. */
+	const struct tomoyo_path_info *filename;
+};
+
+/* Structure for "allow_rename" and "allow_link" directive. */
+struct tomoyo_double_path_acl_record {
+	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_DOUBLE_PATH_ACL */
+	u8 perm;
+	/* Pointer to single pathname. */
+	const struct tomoyo_path_info *filename1;
+	/* Pointer to single pathname. */
+	const struct tomoyo_path_info *filename2;
+};
+
+/* Keywords for ACLs. */
+#define TOMOYO_KEYWORD_ALIAS                     "alias "
+#define TOMOYO_KEYWORD_ALLOW_READ                "allow_read "
+#define TOMOYO_KEYWORD_DELETE                    "delete "
+#define TOMOYO_KEYWORD_DENY_REWRITE              "deny_rewrite "
+#define TOMOYO_KEYWORD_FILE_PATTERN              "file_pattern "
+#define TOMOYO_KEYWORD_INITIALIZE_DOMAIN         "initialize_domain "
+#define TOMOYO_KEYWORD_KEEP_DOMAIN               "keep_domain "
+#define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN      "no_initialize_domain "
+#define TOMOYO_KEYWORD_NO_KEEP_DOMAIN            "no_keep_domain "
+#define TOMOYO_KEYWORD_SELECT                    "select "
+#define TOMOYO_KEYWORD_UNDELETE                  "undelete "
+#define TOMOYO_KEYWORD_USE_PROFILE               "use_profile "
+#define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ  "ignore_global_allow_read"
+/* A domain definition starts with <kernel>. */
+#define TOMOYO_ROOT_NAME                         "<kernel>"
+#define TOMOYO_ROOT_NAME_LEN                     (sizeof(TOMOYO_ROOT_NAME) - 1)
+
+/* Index numbers for Access Controls. */
+#define TOMOYO_MAC_FOR_FILE                  0  /* domain_policy.conf */
+#define TOMOYO_MAX_ACCEPT_ENTRY              1
+#define TOMOYO_VERBOSE                       2
+#define TOMOYO_MAX_CONTROL_INDEX             3
+
+/* Structure for reading/writing policy via securityfs interfaces. */
+struct tomoyo_io_buffer {
+	int (*read) (struct tomoyo_io_buffer *);
+	int (*write) (struct tomoyo_io_buffer *);
+	/* Exclusive lock for this structure.   */
+	struct mutex io_sem;
+	/* The position currently reading from. */
+	struct list_head *read_var1;
+	/* Extra variables for reading.         */
+	struct list_head *read_var2;
+	/* The position currently writing to.   */
+	struct tomoyo_domain_info *write_var1;
+	/* The step for reading.                */
+	int read_step;
+	/* Buffer for reading.                  */
+	char *read_buf;
+	/* EOF flag for reading.                */
+	bool read_eof;
+	/* Read domain ACL of specified PID?    */
+	bool read_single_domain;
+	/* Extra variable for reading.          */
+	u8 read_bit;
+	/* Bytes available for reading.         */
+	int read_avail;
+	/* Size of read buffer.                 */
+	int readbuf_size;
+	/* Buffer for writing.                  */
+	char *write_buf;
+	/* Bytes available for writing.         */
+	int write_avail;
+	/* Size of write buffer.                */
+	int writebuf_size;
+};
+
+/* Check whether the domain has too many ACL entries to hold. */
+bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain);
+/* Transactional sprintf() for policy dump. */
+bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
+	__attribute__ ((format(printf, 2, 3)));
+/* Check whether the domainname is correct. */
+bool tomoyo_is_correct_domain(const unsigned char *domainname,
+			      const char *function);
+/* Check whether the token is correct. */
+bool tomoyo_is_correct_path(const char *filename, const s8 start_type,
+			    const s8 pattern_type, const s8 end_type,
+			    const char *function);
+/* Check whether the token can be a domainname. */
+bool tomoyo_is_domain_def(const unsigned char *buffer);
+/* Check whether the given filename matches the given pattern. */
+bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
+				 const struct tomoyo_path_info *pattern);
+/* Read "alias" entry in exception policy. */
+bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head);
+/*
+ * Read "initialize_domain" and "no_initialize_domain" entry
+ * in exception policy.
+ */
+bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head);
+/* Read "keep_domain" and "no_keep_domain" entry in exception policy. */
+bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head);
+/* Read "file_pattern" entry in exception policy. */
+bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head);
+/* Read "allow_read" entry in exception policy. */
+bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head);
+/* Read "deny_rewrite" entry in exception policy. */
+bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head);
+/* Write domain policy violation warning message to console? */
+bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain);
+/* Convert double path operation to operation name. */
+const char *tomoyo_dp2keyword(const u8 operation);
+/* Get the last component of the given domainname. */
+const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain);
+/* Get warning message. */
+const char *tomoyo_get_msg(const bool is_enforce);
+/* Convert single path operation to operation name. */
+const char *tomoyo_sp2keyword(const u8 operation);
+/* Delete a domain. */
+int tomoyo_delete_domain(char *data);
+/* Create "alias" entry in exception policy. */
+int tomoyo_write_alias_policy(char *data, const bool is_delete);
+/*
+ * Create "initialize_domain" and "no_initialize_domain" entry
+ * in exception policy.
+ */
+int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
+					   const bool is_delete);
+/* Create "keep_domain" and "no_keep_domain" entry in exception policy. */
+int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
+				      const bool is_delete);
+/*
+ * Create "allow_read/write", "allow_execute", "allow_read", "allow_write",
+ * "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir",
+ * "allow_mkfifo", "allow_mksock", "allow_mkblock", "allow_mkchar",
+ * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_rename" and
+ * "allow_link" entry in domain policy.
+ */
+int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
+			     const bool is_delete);
+/* Create "allow_read" entry in exception policy. */
+int tomoyo_write_globally_readable_policy(char *data, const bool is_delete);
+/* Create "deny_rewrite" entry in exception policy. */
+int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete);
+/* Create "file_pattern" entry in exception policy. */
+int tomoyo_write_pattern_policy(char *data, const bool is_delete);
+/* Find a domain by the given name. */
+struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname);
+/* Find or create a domain by the given name. */
+struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
+							    domainname,
+							    const u8 profile);
+/* Undelete a domain. */
+struct tomoyo_domain_info *tomoyo_undelete_domain(const char *domainname);
+/* Check mode for specified functionality. */
+unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
+				const u8 index);
+/* Allocate memory for structures. */
+void *tomoyo_alloc_acl_element(const u8 acl_type);
+/* Fill in "struct tomoyo_path_info" members. */
+void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
+/* Run policy loader when /sbin/init starts. */
+void tomoyo_load_policy(const char *filename);
+/* Change "struct tomoyo_domain_info"->flags. */
+void tomoyo_set_domain_flag(struct tomoyo_domain_info *domain,
+			    const bool is_delete, const u8 flags);
+
+/* strcmp() for "struct tomoyo_path_info" structure. */
+static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a,
+				  const struct tomoyo_path_info *b)
+{
+	return a->hash != b->hash || strcmp(a->name, b->name);
+}
+
+/* Get type of an ACL entry. */
+static inline u8 tomoyo_acl_type1(struct tomoyo_acl_info *ptr)
+{
+	return ptr->type & ~TOMOYO_ACL_DELETED;
+}
+
+/* Get type of an ACL entry. */
+static inline u8 tomoyo_acl_type2(struct tomoyo_acl_info *ptr)
+{
+	return ptr->type;
+}
+
+/**
+ * tomoyo_is_valid - Check whether the character is a valid char.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is a valid character, false otherwise.
+ */
+static inline bool tomoyo_is_valid(const unsigned char c)
+{
+	return c > ' ' && c < 127;
+}
+
+/**
+ * tomoyo_is_invalid - Check whether the character is an invalid char.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is an invalid character, false otherwise.
+ */
+static inline bool tomoyo_is_invalid(const unsigned char c)
+{
+	return c && (c <= ' ' || c >= 127);
+}
+
+/* The list for "struct tomoyo_domain_info". */
+extern struct list_head tomoyo_domain_list;
+extern struct rw_semaphore tomoyo_domain_list_lock;
+
+/* Lock for domain->acl_info_list. */
+extern struct rw_semaphore tomoyo_domain_acl_info_list_lock;
+
+/* Has /sbin/init started? */
+extern bool tomoyo_policy_loaded;
+
+/* The kernel's domain. */
+extern struct tomoyo_domain_info tomoyo_kernel_domain;
+
+/**
+ * list_for_each_cookie - iterate over a list with cookie.
+ * @pos:        the &struct list_head to use as a loop cursor.
+ * @cookie:     the &struct list_head to use as a cookie.
+ * @head:       the head for your list.
+ *
+ * Same with list_for_each() except that this primitive uses @cookie
+ * so that we can continue iteration.
+ * @cookie must be NULL when iteration starts, and @cookie will become
+ * NULL when iteration finishes.
+ */
+#define list_for_each_cookie(pos, cookie, head)                       \
+	for (({ if (!cookie)                                          \
+				     cookie = head; }),               \
+	     pos = (cookie)->next;                                    \
+	     prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
+	     (cookie) = pos, pos = pos->next)
+
+#endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */

+ 878 - 0
security/tomoyo/domain.c

@@ -0,0 +1,878 @@
+/*
+ * security/tomoyo/domain.c
+ *
+ * Implementation of the Domain-Based Mandatory Access Control.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/02/01
+ *
+ */
+
+#include "common.h"
+#include "tomoyo.h"
+#include "realpath.h"
+#include <linux/binfmts.h>
+
+/* Variables definitions.*/
+
+/* The initial domain. */
+struct tomoyo_domain_info tomoyo_kernel_domain;
+
+/* The list for "struct tomoyo_domain_info". */
+LIST_HEAD(tomoyo_domain_list);
+DECLARE_RWSEM(tomoyo_domain_list_lock);
+
+/* Structure for "initialize_domain" and "no_initialize_domain" keyword. */
+struct tomoyo_domain_initializer_entry {
+	struct list_head list;
+	const struct tomoyo_path_info *domainname;    /* This may be NULL */
+	const struct tomoyo_path_info *program;
+	bool is_deleted;
+	bool is_not;       /* True if this entry is "no_initialize_domain".  */
+	/* True if the domainname is tomoyo_get_last_name(). */
+	bool is_last_name;
+};
+
+/* Structure for "keep_domain" and "no_keep_domain" keyword. */
+struct tomoyo_domain_keeper_entry {
+	struct list_head list;
+	const struct tomoyo_path_info *domainname;
+	const struct tomoyo_path_info *program;       /* This may be NULL */
+	bool is_deleted;
+	bool is_not;       /* True if this entry is "no_keep_domain".        */
+	/* True if the domainname is tomoyo_get_last_name(). */
+	bool is_last_name;
+};
+
+/* Structure for "alias" keyword. */
+struct tomoyo_alias_entry {
+	struct list_head list;
+	const struct tomoyo_path_info *original_name;
+	const struct tomoyo_path_info *aliased_name;
+	bool is_deleted;
+};
+
+/**
+ * tomoyo_set_domain_flag - Set or clear domain's attribute flags.
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ * @flags:     Flags to set or clear.
+ *
+ * Returns nothing.
+ */
+void tomoyo_set_domain_flag(struct tomoyo_domain_info *domain,
+			    const bool is_delete, const u8 flags)
+{
+	/* We need to serialize because this is bitfield operation. */
+	static DEFINE_SPINLOCK(lock);
+	/***** CRITICAL SECTION START *****/
+	spin_lock(&lock);
+	if (!is_delete)
+		domain->flags |= flags;
+	else
+		domain->flags &= ~flags;
+	spin_unlock(&lock);
+	/***** CRITICAL SECTION END *****/
+}
+
+/**
+ * tomoyo_get_last_name - Get last component of a domainname.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ *
+ * Returns the last component of the domainname.
+ */
+const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain)
+{
+	const char *cp0 = domain->domainname->name;
+	const char *cp1 = strrchr(cp0, ' ');
+
+	if (cp1)
+		return cp1 + 1;
+	return cp0;
+}
+
+/* The list for "struct tomoyo_domain_initializer_entry". */
+static LIST_HEAD(tomoyo_domain_initializer_list);
+static DECLARE_RWSEM(tomoyo_domain_initializer_list_lock);
+
+/**
+ * tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list.
+ *
+ * @domainname: The name of domain. May be NULL.
+ * @program:    The name of program.
+ * @is_not:     True if it is "no_initialize_domain" entry.
+ * @is_delete:  True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_domain_initializer_entry(const char *domainname,
+						  const char *program,
+						  const bool is_not,
+						  const bool is_delete)
+{
+	struct tomoyo_domain_initializer_entry *new_entry;
+	struct tomoyo_domain_initializer_entry *ptr;
+	const struct tomoyo_path_info *saved_program;
+	const struct tomoyo_path_info *saved_domainname = NULL;
+	int error = -ENOMEM;
+	bool is_last_name = false;
+
+	if (!tomoyo_is_correct_path(program, 1, -1, -1, __func__))
+		return -EINVAL; /* No patterns allowed. */
+	if (domainname) {
+		if (!tomoyo_is_domain_def(domainname) &&
+		    tomoyo_is_correct_path(domainname, 1, -1, -1, __func__))
+			is_last_name = true;
+		else if (!tomoyo_is_correct_domain(domainname, __func__))
+			return -EINVAL;
+		saved_domainname = tomoyo_save_name(domainname);
+		if (!saved_domainname)
+			return -ENOMEM;
+	}
+	saved_program = tomoyo_save_name(program);
+	if (!saved_program)
+		return -ENOMEM;
+	/***** EXCLUSIVE SECTION START *****/
+	down_write(&tomoyo_domain_initializer_list_lock);
+	list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
+		if (ptr->is_not != is_not ||
+		    ptr->domainname != saved_domainname ||
+		    ptr->program != saved_program)
+			continue;
+		ptr->is_deleted = is_delete;
+		error = 0;
+		goto out;
+	}
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+	new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+	if (!new_entry)
+		goto out;
+	new_entry->domainname = saved_domainname;
+	new_entry->program = saved_program;
+	new_entry->is_not = is_not;
+	new_entry->is_last_name = is_last_name;
+	list_add_tail(&new_entry->list, &tomoyo_domain_initializer_list);
+	error = 0;
+ out:
+	up_write(&tomoyo_domain_initializer_list_lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return error;
+}
+
+/**
+ * tomoyo_read_domain_initializer_policy - Read "struct tomoyo_domain_initializer_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
+{
+	struct list_head *pos;
+	bool done = true;
+
+	down_read(&tomoyo_domain_initializer_list_lock);
+	list_for_each_cookie(pos, head->read_var2,
+			     &tomoyo_domain_initializer_list) {
+		const char *no;
+		const char *from = "";
+		const char *domain = "";
+		struct tomoyo_domain_initializer_entry *ptr;
+		ptr = list_entry(pos, struct tomoyo_domain_initializer_entry,
+				  list);
+		if (ptr->is_deleted)
+			continue;
+		no = ptr->is_not ? "no_" : "";
+		if (ptr->domainname) {
+			from = " from ";
+			domain = ptr->domainname->name;
+		}
+		if (!tomoyo_io_printf(head,
+				      "%s" TOMOYO_KEYWORD_INITIALIZE_DOMAIN
+				      "%s%s%s\n", no, ptr->program->name, from,
+				      domain)) {
+			done = false;
+			break;
+		}
+	}
+	up_read(&tomoyo_domain_initializer_list_lock);
+	return done;
+}
+
+/**
+ * tomoyo_write_domain_initializer_policy - Write "struct tomoyo_domain_initializer_entry" list.
+ *
+ * @data:      String to parse.
+ * @is_not:    True if it is "no_initialize_domain" entry.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
+					   const bool is_delete)
+{
+	char *cp = strstr(data, " from ");
+
+	if (cp) {
+		*cp = '\0';
+		return tomoyo_update_domain_initializer_entry(cp + 6, data,
+							      is_not,
+							      is_delete);
+	}
+	return tomoyo_update_domain_initializer_entry(NULL, data, is_not,
+						      is_delete);
+}
+
+/**
+ * tomoyo_is_domain_initializer - Check whether the given program causes domainname reinitialization.
+ *
+ * @domainname: The name of domain.
+ * @program:    The name of program.
+ * @last_name:  The last component of @domainname.
+ *
+ * Returns true if executing @program reinitializes domain transition,
+ * false otherwise.
+ */
+static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
+					 domainname,
+					 const struct tomoyo_path_info *program,
+					 const struct tomoyo_path_info *
+					 last_name)
+{
+	struct tomoyo_domain_initializer_entry *ptr;
+	bool flag = false;
+
+	down_read(&tomoyo_domain_initializer_list_lock);
+	list_for_each_entry(ptr,  &tomoyo_domain_initializer_list, list) {
+		if (ptr->is_deleted)
+			continue;
+		if (ptr->domainname) {
+			if (!ptr->is_last_name) {
+				if (ptr->domainname != domainname)
+					continue;
+			} else {
+				if (tomoyo_pathcmp(ptr->domainname, last_name))
+					continue;
+			}
+		}
+		if (tomoyo_pathcmp(ptr->program, program))
+			continue;
+		if (ptr->is_not) {
+			flag = false;
+			break;
+		}
+		flag = true;
+	}
+	up_read(&tomoyo_domain_initializer_list_lock);
+	return flag;
+}
+
+/* The list for "struct tomoyo_domain_keeper_entry". */
+static LIST_HEAD(tomoyo_domain_keeper_list);
+static DECLARE_RWSEM(tomoyo_domain_keeper_list_lock);
+
+/**
+ * tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list.
+ *
+ * @domainname: The name of domain.
+ * @program:    The name of program. May be NULL.
+ * @is_not:     True if it is "no_keep_domain" entry.
+ * @is_delete:  True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_domain_keeper_entry(const char *domainname,
+					     const char *program,
+					     const bool is_not,
+					     const bool is_delete)
+{
+	struct tomoyo_domain_keeper_entry *new_entry;
+	struct tomoyo_domain_keeper_entry *ptr;
+	const struct tomoyo_path_info *saved_domainname;
+	const struct tomoyo_path_info *saved_program = NULL;
+	static DEFINE_MUTEX(lock);
+	int error = -ENOMEM;
+	bool is_last_name = false;
+
+	if (!tomoyo_is_domain_def(domainname) &&
+	    tomoyo_is_correct_path(domainname, 1, -1, -1, __func__))
+		is_last_name = true;
+	else if (!tomoyo_is_correct_domain(domainname, __func__))
+		return -EINVAL;
+	if (program) {
+		if (!tomoyo_is_correct_path(program, 1, -1, -1, __func__))
+			return -EINVAL;
+		saved_program = tomoyo_save_name(program);
+		if (!saved_program)
+			return -ENOMEM;
+	}
+	saved_domainname = tomoyo_save_name(domainname);
+	if (!saved_domainname)
+		return -ENOMEM;
+	/***** EXCLUSIVE SECTION START *****/
+	down_write(&tomoyo_domain_keeper_list_lock);
+	list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+		if (ptr->is_not != is_not ||
+		    ptr->domainname != saved_domainname ||
+		    ptr->program != saved_program)
+			continue;
+		ptr->is_deleted = is_delete;
+		error = 0;
+		goto out;
+	}
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+	new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+	if (!new_entry)
+		goto out;
+	new_entry->domainname = saved_domainname;
+	new_entry->program = saved_program;
+	new_entry->is_not = is_not;
+	new_entry->is_last_name = is_last_name;
+	list_add_tail(&new_entry->list, &tomoyo_domain_keeper_list);
+	error = 0;
+ out:
+	up_write(&tomoyo_domain_keeper_list_lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return error;
+}
+
+/**
+ * tomoyo_write_domain_keeper_policy - Write "struct tomoyo_domain_keeper_entry" list.
+ *
+ * @data:      String to parse.
+ * @is_not:    True if it is "no_keep_domain" entry.
+ * @is_delete: True if it is a delete request.
+ *
+ */
+int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
+				      const bool is_delete)
+{
+	char *cp = strstr(data, " from ");
+
+	if (cp) {
+		*cp = '\0';
+		return tomoyo_update_domain_keeper_entry(cp + 6, data, is_not,
+							 is_delete);
+	}
+	return tomoyo_update_domain_keeper_entry(data, NULL, is_not, is_delete);
+}
+
+/**
+ * tomoyo_read_domain_keeper_policy - Read "struct tomoyo_domain_keeper_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
+{
+	struct list_head *pos;
+	bool done = true;
+
+	down_read(&tomoyo_domain_keeper_list_lock);
+	list_for_each_cookie(pos, head->read_var2,
+			     &tomoyo_domain_keeper_list) {
+		struct tomoyo_domain_keeper_entry *ptr;
+		const char *no;
+		const char *from = "";
+		const char *program = "";
+
+		ptr = list_entry(pos, struct tomoyo_domain_keeper_entry, list);
+		if (ptr->is_deleted)
+			continue;
+		no = ptr->is_not ? "no_" : "";
+		if (ptr->program) {
+			from = " from ";
+			program = ptr->program->name;
+		}
+		if (!tomoyo_io_printf(head,
+				      "%s" TOMOYO_KEYWORD_KEEP_DOMAIN
+				      "%s%s%s\n", no, program, from,
+				      ptr->domainname->name)) {
+			done = false;
+			break;
+		}
+	}
+	up_read(&tomoyo_domain_keeper_list_lock);
+	return done;
+}
+
+/**
+ * tomoyo_is_domain_keeper - Check whether the given program causes domain transition suppression.
+ *
+ * @domainname: The name of domain.
+ * @program:    The name of program.
+ * @last_name:  The last component of @domainname.
+ *
+ * Returns true if executing @program supresses domain transition,
+ * false otherwise.
+ */
+static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
+				    const struct tomoyo_path_info *program,
+				    const struct tomoyo_path_info *last_name)
+{
+	struct tomoyo_domain_keeper_entry *ptr;
+	bool flag = false;
+
+	down_read(&tomoyo_domain_keeper_list_lock);
+	list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+		if (ptr->is_deleted)
+			continue;
+		if (!ptr->is_last_name) {
+			if (ptr->domainname != domainname)
+				continue;
+		} else {
+			if (tomoyo_pathcmp(ptr->domainname, last_name))
+				continue;
+		}
+		if (ptr->program && tomoyo_pathcmp(ptr->program, program))
+			continue;
+		if (ptr->is_not) {
+			flag = false;
+			break;
+		}
+		flag = true;
+	}
+	up_read(&tomoyo_domain_keeper_list_lock);
+	return flag;
+}
+
+/* The list for "struct tomoyo_alias_entry". */
+static LIST_HEAD(tomoyo_alias_list);
+static DECLARE_RWSEM(tomoyo_alias_list_lock);
+
+/**
+ * tomoyo_update_alias_entry - Update "struct tomoyo_alias_entry" list.
+ *
+ * @original_name: The original program's real name.
+ * @aliased_name:  The symbolic program's symbolic link's name.
+ * @is_delete:     True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_alias_entry(const char *original_name,
+				     const char *aliased_name,
+				     const bool is_delete)
+{
+	struct tomoyo_alias_entry *new_entry;
+	struct tomoyo_alias_entry *ptr;
+	const struct tomoyo_path_info *saved_original_name;
+	const struct tomoyo_path_info *saved_aliased_name;
+	int error = -ENOMEM;
+
+	if (!tomoyo_is_correct_path(original_name, 1, -1, -1, __func__) ||
+	    !tomoyo_is_correct_path(aliased_name, 1, -1, -1, __func__))
+		return -EINVAL; /* No patterns allowed. */
+	saved_original_name = tomoyo_save_name(original_name);
+	saved_aliased_name = tomoyo_save_name(aliased_name);
+	if (!saved_original_name || !saved_aliased_name)
+		return -ENOMEM;
+	/***** EXCLUSIVE SECTION START *****/
+	down_write(&tomoyo_alias_list_lock);
+	list_for_each_entry(ptr, &tomoyo_alias_list, list) {
+		if (ptr->original_name != saved_original_name ||
+		    ptr->aliased_name != saved_aliased_name)
+			continue;
+		ptr->is_deleted = is_delete;
+		error = 0;
+		goto out;
+	}
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+	new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+	if (!new_entry)
+		goto out;
+	new_entry->original_name = saved_original_name;
+	new_entry->aliased_name = saved_aliased_name;
+	list_add_tail(&new_entry->list, &tomoyo_alias_list);
+	error = 0;
+ out:
+	up_write(&tomoyo_alias_list_lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return error;
+}
+
+/**
+ * tomoyo_read_alias_policy - Read "struct tomoyo_alias_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
+{
+	struct list_head *pos;
+	bool done = true;
+
+	down_read(&tomoyo_alias_list_lock);
+	list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) {
+		struct tomoyo_alias_entry *ptr;
+
+		ptr = list_entry(pos, struct tomoyo_alias_entry, list);
+		if (ptr->is_deleted)
+			continue;
+		if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALIAS "%s %s\n",
+				      ptr->original_name->name,
+				      ptr->aliased_name->name)) {
+			done = false;
+			break;
+		}
+	}
+	up_read(&tomoyo_alias_list_lock);
+	return done;
+}
+
+/**
+ * tomoyo_write_alias_policy - Write "struct tomoyo_alias_entry" list.
+ *
+ * @data:      String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_alias_policy(char *data, const bool is_delete)
+{
+	char *cp = strchr(data, ' ');
+
+	if (!cp)
+		return -EINVAL;
+	*cp++ = '\0';
+	return tomoyo_update_alias_entry(data, cp, is_delete);
+}
+
+/* Domain create/delete/undelete handler. */
+
+/* #define TOMOYO_DEBUG_DOMAIN_UNDELETE */
+
+/**
+ * tomoyo_delete_domain - Delete a domain.
+ *
+ * @domainname: The name of domain.
+ *
+ * Returns 0.
+ */
+int tomoyo_delete_domain(char *domainname)
+{
+	struct tomoyo_domain_info *domain;
+	struct tomoyo_path_info name;
+
+	name.name = domainname;
+	tomoyo_fill_path_info(&name);
+	/***** EXCLUSIVE SECTION START *****/
+	down_write(&tomoyo_domain_list_lock);
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+	printk(KERN_DEBUG "tomoyo_delete_domain %s\n", domainname);
+	list_for_each_entry(domain, &tomoyo_domain_list, list) {
+		if (tomoyo_pathcmp(domain->domainname, &name))
+			continue;
+		printk(KERN_DEBUG "List: %p %u\n", domain, domain->is_deleted);
+	}
+#endif
+	/* Is there an active domain? */
+	list_for_each_entry(domain, &tomoyo_domain_list, list) {
+		struct tomoyo_domain_info *domain2;
+		/* Never delete tomoyo_kernel_domain */
+		if (domain == &tomoyo_kernel_domain)
+			continue;
+		if (domain->is_deleted ||
+		    tomoyo_pathcmp(domain->domainname, &name))
+			continue;
+		/* Mark already deleted domains as non undeletable. */
+		list_for_each_entry(domain2, &tomoyo_domain_list, list) {
+			if (!domain2->is_deleted ||
+			    tomoyo_pathcmp(domain2->domainname, &name))
+				continue;
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+			if (domain2->is_deleted != 255)
+				printk(KERN_DEBUG
+				       "Marked %p as non undeletable\n",
+				       domain2);
+#endif
+			domain2->is_deleted = 255;
+		}
+		/* Delete and mark active domain as undeletable. */
+		domain->is_deleted = 1;
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+		printk(KERN_DEBUG "Marked %p as undeletable\n", domain);
+#endif
+		break;
+	}
+	up_write(&tomoyo_domain_list_lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return 0;
+}
+
+/**
+ * tomoyo_undelete_domain - Undelete a domain.
+ *
+ * @domainname: The name of domain.
+ *
+ * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
+ */
+struct tomoyo_domain_info *tomoyo_undelete_domain(const char *domainname)
+{
+	struct tomoyo_domain_info *domain;
+	struct tomoyo_domain_info *candidate_domain = NULL;
+	struct tomoyo_path_info name;
+
+	name.name = domainname;
+	tomoyo_fill_path_info(&name);
+	/***** EXCLUSIVE SECTION START *****/
+	down_write(&tomoyo_domain_list_lock);
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+	printk(KERN_DEBUG "tomoyo_undelete_domain %s\n", domainname);
+	list_for_each_entry(domain, &tomoyo_domain_list, list) {
+		if (tomoyo_pathcmp(domain->domainname, &name))
+			continue;
+		printk(KERN_DEBUG "List: %p %u\n", domain, domain->is_deleted);
+	}
+#endif
+	list_for_each_entry(domain, &tomoyo_domain_list, list) {
+		if (tomoyo_pathcmp(&name, domain->domainname))
+			continue;
+		if (!domain->is_deleted) {
+			/* This domain is active. I can't undelete. */
+			candidate_domain = NULL;
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+			printk(KERN_DEBUG "%p is active. I can't undelete.\n",
+			       domain);
+#endif
+			break;
+		}
+		/* Is this domain undeletable? */
+		if (domain->is_deleted == 1)
+			candidate_domain = domain;
+	}
+	if (candidate_domain) {
+		candidate_domain->is_deleted = 0;
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+		printk(KERN_DEBUG "%p was undeleted.\n", candidate_domain);
+#endif
+	}
+	up_write(&tomoyo_domain_list_lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return candidate_domain;
+}
+
+/**
+ * tomoyo_find_or_assign_new_domain - Create a domain.
+ *
+ * @domainname: The name of domain.
+ * @profile:    Profile number to assign if the domain was newly created.
+ *
+ * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
+ */
+struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
+							    domainname,
+							    const u8 profile)
+{
+	struct tomoyo_domain_info *domain = NULL;
+	const struct tomoyo_path_info *saved_domainname;
+
+	/***** EXCLUSIVE SECTION START *****/
+	down_write(&tomoyo_domain_list_lock);
+	domain = tomoyo_find_domain(domainname);
+	if (domain)
+		goto out;
+	if (!tomoyo_is_correct_domain(domainname, __func__))
+		goto out;
+	saved_domainname = tomoyo_save_name(domainname);
+	if (!saved_domainname)
+		goto out;
+	/* Can I reuse memory of deleted domain? */
+	list_for_each_entry(domain, &tomoyo_domain_list, list) {
+		struct task_struct *p;
+		struct tomoyo_acl_info *ptr;
+		bool flag;
+		if (!domain->is_deleted ||
+		    domain->domainname != saved_domainname)
+			continue;
+		flag = false;
+		/***** CRITICAL SECTION START *****/
+		read_lock(&tasklist_lock);
+		for_each_process(p) {
+			if (tomoyo_real_domain(p) != domain)
+				continue;
+			flag = true;
+			break;
+		}
+		read_unlock(&tasklist_lock);
+		/***** CRITICAL SECTION END *****/
+		if (flag)
+			continue;
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+		printk(KERN_DEBUG "Reusing %p %s\n", domain,
+		       domain->domainname->name);
+#endif
+		list_for_each_entry(ptr, &domain->acl_info_list, list) {
+			ptr->type |= TOMOYO_ACL_DELETED;
+		}
+		tomoyo_set_domain_flag(domain, true, domain->flags);
+		domain->profile = profile;
+		domain->quota_warned = false;
+		mb(); /* Avoid out-of-order execution. */
+		domain->is_deleted = 0;
+		goto out;
+	}
+	/* No memory reusable. Create using new memory. */
+	domain = tomoyo_alloc_element(sizeof(*domain));
+	if (domain) {
+		INIT_LIST_HEAD(&domain->acl_info_list);
+		domain->domainname = saved_domainname;
+		domain->profile = profile;
+		list_add_tail(&domain->list, &tomoyo_domain_list);
+	}
+ out:
+	up_write(&tomoyo_domain_list_lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return domain;
+}
+
+/**
+ * tomoyo_find_next_domain - Find a domain.
+ *
+ * @bprm:           Pointer to "struct linux_binprm".
+ * @next_domain:    Pointer to pointer to "struct tomoyo_domain_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_find_next_domain(struct linux_binprm *bprm,
+			    struct tomoyo_domain_info **next_domain)
+{
+	/*
+	 * This function assumes that the size of buffer returned by
+	 * tomoyo_realpath() = TOMOYO_MAX_PATHNAME_LEN.
+	 */
+	struct tomoyo_page_buffer *tmp = tomoyo_alloc(sizeof(*tmp));
+	struct tomoyo_domain_info *old_domain = tomoyo_domain();
+	struct tomoyo_domain_info *domain = NULL;
+	const char *old_domain_name = old_domain->domainname->name;
+	const char *original_name = bprm->filename;
+	char *new_domain_name = NULL;
+	char *real_program_name = NULL;
+	char *symlink_program_name = NULL;
+	const u8 mode = tomoyo_check_flags(old_domain, TOMOYO_MAC_FOR_FILE);
+	const bool is_enforce = (mode == 3);
+	int retval = -ENOMEM;
+	struct tomoyo_path_info r; /* real name */
+	struct tomoyo_path_info s; /* symlink name */
+	struct tomoyo_path_info l; /* last name */
+	static bool initialized;
+
+	if (!tmp)
+		goto out;
+
+	if (!initialized) {
+		/*
+		 * Built-in initializers. This is needed because policies are
+		 * not loaded until starting /sbin/init.
+		 */
+		tomoyo_update_domain_initializer_entry(NULL, "/sbin/hotplug",
+						       false, false);
+		tomoyo_update_domain_initializer_entry(NULL, "/sbin/modprobe",
+						       false, false);
+		initialized = true;
+	}
+
+	/* Get tomoyo_realpath of program. */
+	retval = -ENOENT;
+	/* I hope tomoyo_realpath() won't fail with -ENOMEM. */
+	real_program_name = tomoyo_realpath(original_name);
+	if (!real_program_name)
+		goto out;
+	/* Get tomoyo_realpath of symbolic link. */
+	symlink_program_name = tomoyo_realpath_nofollow(original_name);
+	if (!symlink_program_name)
+		goto out;
+
+	r.name = real_program_name;
+	tomoyo_fill_path_info(&r);
+	s.name = symlink_program_name;
+	tomoyo_fill_path_info(&s);
+	l.name = tomoyo_get_last_name(old_domain);
+	tomoyo_fill_path_info(&l);
+
+	/* Check 'alias' directive. */
+	if (tomoyo_pathcmp(&r, &s)) {
+		struct tomoyo_alias_entry *ptr;
+		/* Is this program allowed to be called via symbolic links? */
+		down_read(&tomoyo_alias_list_lock);
+		list_for_each_entry(ptr, &tomoyo_alias_list, list) {
+			if (ptr->is_deleted ||
+			    tomoyo_pathcmp(&r, ptr->original_name) ||
+			    tomoyo_pathcmp(&s, ptr->aliased_name))
+				continue;
+			memset(real_program_name, 0, TOMOYO_MAX_PATHNAME_LEN);
+			strncpy(real_program_name, ptr->aliased_name->name,
+				TOMOYO_MAX_PATHNAME_LEN - 1);
+			tomoyo_fill_path_info(&r);
+			break;
+		}
+		up_read(&tomoyo_alias_list_lock);
+	}
+
+	/* Check execute permission. */
+	retval = tomoyo_check_exec_perm(old_domain, &r, tmp);
+	if (retval < 0)
+		goto out;
+
+	new_domain_name = tmp->buffer;
+	if (tomoyo_is_domain_initializer(old_domain->domainname, &r, &l)) {
+		/* Transit to the child of tomoyo_kernel_domain domain. */
+		snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1,
+			 TOMOYO_ROOT_NAME " " "%s", real_program_name);
+	} else if (old_domain == &tomoyo_kernel_domain &&
+		   !tomoyo_policy_loaded) {
+		/*
+		 * Needn't to transit from kernel domain before starting
+		 * /sbin/init. But transit from kernel domain if executing
+		 * initializers because they might start before /sbin/init.
+		 */
+		domain = old_domain;
+	} else if (tomoyo_is_domain_keeper(old_domain->domainname, &r, &l)) {
+		/* Keep current domain. */
+		domain = old_domain;
+	} else {
+		/* Normal domain transition. */
+		snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1,
+			 "%s %s", old_domain_name, real_program_name);
+	}
+	if (domain || strlen(new_domain_name) >= TOMOYO_MAX_PATHNAME_LEN)
+		goto done;
+	down_read(&tomoyo_domain_list_lock);
+	domain = tomoyo_find_domain(new_domain_name);
+	up_read(&tomoyo_domain_list_lock);
+	if (domain)
+		goto done;
+	if (is_enforce)
+		goto done;
+	domain = tomoyo_find_or_assign_new_domain(new_domain_name,
+						  old_domain->profile);
+ done:
+	if (domain)
+		goto out;
+	printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n",
+	       new_domain_name);
+	if (is_enforce)
+		retval = -EPERM;
+	else
+		tomoyo_set_domain_flag(old_domain, false,
+				       TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED);
+ out:
+	tomoyo_free(real_program_name);
+	tomoyo_free(symlink_program_name);
+	*next_domain = domain ? domain : old_domain;
+	tomoyo_free(tmp);
+	return retval;
+}

+ 1241 - 0
security/tomoyo/file.c

@@ -0,0 +1,1241 @@
+/*
+ * security/tomoyo/file.c
+ *
+ * Implementation of the Domain-Based Mandatory Access Control.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/02/01
+ *
+ */
+
+#include "common.h"
+#include "tomoyo.h"
+#include "realpath.h"
+#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
+
+/* Structure for "allow_read" keyword. */
+struct tomoyo_globally_readable_file_entry {
+	struct list_head list;
+	const struct tomoyo_path_info *filename;
+	bool is_deleted;
+};
+
+/* Structure for "file_pattern" keyword. */
+struct tomoyo_pattern_entry {
+	struct list_head list;
+	const struct tomoyo_path_info *pattern;
+	bool is_deleted;
+};
+
+/* Structure for "deny_rewrite" keyword. */
+struct tomoyo_no_rewrite_entry {
+	struct list_head list;
+	const struct tomoyo_path_info *pattern;
+	bool is_deleted;
+};
+
+/* Keyword array for single path operations. */
+static const char *tomoyo_sp_keyword[TOMOYO_MAX_SINGLE_PATH_OPERATION] = {
+	[TOMOYO_TYPE_READ_WRITE_ACL] = "read/write",
+	[TOMOYO_TYPE_EXECUTE_ACL]    = "execute",
+	[TOMOYO_TYPE_READ_ACL]       = "read",
+	[TOMOYO_TYPE_WRITE_ACL]      = "write",
+	[TOMOYO_TYPE_CREATE_ACL]     = "create",
+	[TOMOYO_TYPE_UNLINK_ACL]     = "unlink",
+	[TOMOYO_TYPE_MKDIR_ACL]      = "mkdir",
+	[TOMOYO_TYPE_RMDIR_ACL]      = "rmdir",
+	[TOMOYO_TYPE_MKFIFO_ACL]     = "mkfifo",
+	[TOMOYO_TYPE_MKSOCK_ACL]     = "mksock",
+	[TOMOYO_TYPE_MKBLOCK_ACL]    = "mkblock",
+	[TOMOYO_TYPE_MKCHAR_ACL]     = "mkchar",
+	[TOMOYO_TYPE_TRUNCATE_ACL]   = "truncate",
+	[TOMOYO_TYPE_SYMLINK_ACL]    = "symlink",
+	[TOMOYO_TYPE_REWRITE_ACL]    = "rewrite",
+};
+
+/* Keyword array for double path operations. */
+static const char *tomoyo_dp_keyword[TOMOYO_MAX_DOUBLE_PATH_OPERATION] = {
+	[TOMOYO_TYPE_LINK_ACL]    = "link",
+	[TOMOYO_TYPE_RENAME_ACL]  = "rename",
+};
+
+/**
+ * tomoyo_sp2keyword - Get the name of single path operation.
+ *
+ * @operation: Type of operation.
+ *
+ * Returns the name of single path operation.
+ */
+const char *tomoyo_sp2keyword(const u8 operation)
+{
+	return (operation < TOMOYO_MAX_SINGLE_PATH_OPERATION)
+		? tomoyo_sp_keyword[operation] : NULL;
+}
+
+/**
+ * tomoyo_dp2keyword - Get the name of double path operation.
+ *
+ * @operation: Type of operation.
+ *
+ * Returns the name of double path operation.
+ */
+const char *tomoyo_dp2keyword(const u8 operation)
+{
+	return (operation < TOMOYO_MAX_DOUBLE_PATH_OPERATION)
+		? tomoyo_dp_keyword[operation] : NULL;
+}
+
+/**
+ * tomoyo_strendswith - Check whether the token ends with the given token.
+ *
+ * @name: The token to check.
+ * @tail: The token to find.
+ *
+ * Returns true if @name ends with @tail, false otherwise.
+ */
+static bool tomoyo_strendswith(const char *name, const char *tail)
+{
+	int len;
+
+	if (!name || !tail)
+		return false;
+	len = strlen(name) - strlen(tail);
+	return len >= 0 && !strcmp(name + len, tail);
+}
+
+/**
+ * tomoyo_get_path - Get realpath.
+ *
+ * @path: Pointer to "struct path".
+ *
+ * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
+ */
+static struct tomoyo_path_info *tomoyo_get_path(struct path *path)
+{
+	int error;
+	struct tomoyo_path_info_with_data *buf = tomoyo_alloc(sizeof(*buf));
+
+	if (!buf)
+		return NULL;
+	/* Reserve one byte for appending "/". */
+	error = tomoyo_realpath_from_path2(path, buf->body,
+					   sizeof(buf->body) - 2);
+	if (!error) {
+		buf->head.name = buf->body;
+		tomoyo_fill_path_info(&buf->head);
+		return &buf->head;
+	}
+	tomoyo_free(buf);
+	return NULL;
+}
+
+/* Lock for domain->acl_info_list. */
+DECLARE_RWSEM(tomoyo_domain_acl_info_list_lock);
+
+static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
+					 const char *filename2,
+					 struct tomoyo_domain_info *
+					 const domain, const bool is_delete);
+static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
+					 struct tomoyo_domain_info *
+					 const domain, const bool is_delete);
+
+/* The list for "struct tomoyo_globally_readable_file_entry". */
+static LIST_HEAD(tomoyo_globally_readable_list);
+static DECLARE_RWSEM(tomoyo_globally_readable_list_lock);
+
+/**
+ * tomoyo_update_globally_readable_entry - Update "struct tomoyo_globally_readable_file_entry" list.
+ *
+ * @filename:  Filename unconditionally permitted to open() for reading.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_globally_readable_entry(const char *filename,
+						 const bool is_delete)
+{
+	struct tomoyo_globally_readable_file_entry *new_entry;
+	struct tomoyo_globally_readable_file_entry *ptr;
+	const struct tomoyo_path_info *saved_filename;
+	int error = -ENOMEM;
+
+	if (!tomoyo_is_correct_path(filename, 1, 0, -1, __func__))
+		return -EINVAL;
+	saved_filename = tomoyo_save_name(filename);
+	if (!saved_filename)
+		return -ENOMEM;
+	/***** EXCLUSIVE SECTION START *****/
+	down_write(&tomoyo_globally_readable_list_lock);
+	list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+		if (ptr->filename != saved_filename)
+			continue;
+		ptr->is_deleted = is_delete;
+		error = 0;
+		goto out;
+	}
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+	new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+	if (!new_entry)
+		goto out;
+	new_entry->filename = saved_filename;
+	list_add_tail(&new_entry->list, &tomoyo_globally_readable_list);
+	error = 0;
+ out:
+	up_write(&tomoyo_globally_readable_list_lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return error;
+}
+
+/**
+ * tomoyo_is_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading.
+ *
+ * @filename: The filename to check.
+ *
+ * Returns true if any domain can open @filename for reading, false otherwise.
+ */
+static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info *
+					     filename)
+{
+	struct tomoyo_globally_readable_file_entry *ptr;
+	bool found = false;
+	down_read(&tomoyo_globally_readable_list_lock);
+	list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+		if (!ptr->is_deleted &&
+		    tomoyo_path_matches_pattern(filename, ptr->filename)) {
+			found = true;
+			break;
+		}
+	}
+	up_read(&tomoyo_globally_readable_list_lock);
+	return found;
+}
+
+/**
+ * tomoyo_write_globally_readable_policy - Write "struct tomoyo_globally_readable_file_entry" list.
+ *
+ * @data:      String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
+{
+	return tomoyo_update_globally_readable_entry(data, is_delete);
+}
+
+/**
+ * tomoyo_read_globally_readable_policy - Read "struct tomoyo_globally_readable_file_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
+{
+	struct list_head *pos;
+	bool done = true;
+
+	down_read(&tomoyo_globally_readable_list_lock);
+	list_for_each_cookie(pos, head->read_var2,
+			     &tomoyo_globally_readable_list) {
+		struct tomoyo_globally_readable_file_entry *ptr;
+		ptr = list_entry(pos,
+				 struct tomoyo_globally_readable_file_entry,
+				 list);
+		if (ptr->is_deleted)
+			continue;
+		if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_READ "%s\n",
+				      ptr->filename->name)) {
+			done = false;
+			break;
+		}
+	}
+	up_read(&tomoyo_globally_readable_list_lock);
+	return done;
+}
+
+/* The list for "struct tomoyo_pattern_entry". */
+static LIST_HEAD(tomoyo_pattern_list);
+static DECLARE_RWSEM(tomoyo_pattern_list_lock);
+
+/**
+ * tomoyo_update_file_pattern_entry - Update "struct tomoyo_pattern_entry" list.
+ *
+ * @pattern:   Pathname pattern.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_file_pattern_entry(const char *pattern,
+					    const bool is_delete)
+{
+	struct tomoyo_pattern_entry *new_entry;
+	struct tomoyo_pattern_entry *ptr;
+	const struct tomoyo_path_info *saved_pattern;
+	int error = -ENOMEM;
+
+	if (!tomoyo_is_correct_path(pattern, 0, 1, 0, __func__))
+		return -EINVAL;
+	saved_pattern = tomoyo_save_name(pattern);
+	if (!saved_pattern)
+		return -ENOMEM;
+	/***** EXCLUSIVE SECTION START *****/
+	down_write(&tomoyo_pattern_list_lock);
+	list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+		if (saved_pattern != ptr->pattern)
+			continue;
+		ptr->is_deleted = is_delete;
+		error = 0;
+		goto out;
+	}
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+	new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+	if (!new_entry)
+		goto out;
+	new_entry->pattern = saved_pattern;
+	list_add_tail(&new_entry->list, &tomoyo_pattern_list);
+	error = 0;
+ out:
+	up_write(&tomoyo_pattern_list_lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return error;
+}
+
+/**
+ * tomoyo_get_file_pattern - Get patterned pathname.
+ *
+ * @filename: The filename to find patterned pathname.
+ *
+ * Returns pointer to pathname pattern if matched, @filename otherwise.
+ */
+static const struct tomoyo_path_info *
+tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
+{
+	struct tomoyo_pattern_entry *ptr;
+	const struct tomoyo_path_info *pattern = NULL;
+
+	down_read(&tomoyo_pattern_list_lock);
+	list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+		if (ptr->is_deleted)
+			continue;
+		if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
+			continue;
+		pattern = ptr->pattern;
+		if (tomoyo_strendswith(pattern->name, "/\\*")) {
+			/* Do nothing. Try to find the better match. */
+		} else {
+			/* This would be the better match. Use this. */
+			break;
+		}
+	}
+	up_read(&tomoyo_pattern_list_lock);
+	if (pattern)
+		filename = pattern;
+	return filename;
+}
+
+/**
+ * tomoyo_write_pattern_policy - Write "struct tomoyo_pattern_entry" list.
+ *
+ * @data:      String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_pattern_policy(char *data, const bool is_delete)
+{
+	return tomoyo_update_file_pattern_entry(data, is_delete);
+}
+
+/**
+ * tomoyo_read_file_pattern - Read "struct tomoyo_pattern_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
+{
+	struct list_head *pos;
+	bool done = true;
+
+	down_read(&tomoyo_pattern_list_lock);
+	list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) {
+		struct tomoyo_pattern_entry *ptr;
+		ptr = list_entry(pos, struct tomoyo_pattern_entry, list);
+		if (ptr->is_deleted)
+			continue;
+		if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_FILE_PATTERN "%s\n",
+				      ptr->pattern->name)) {
+			done = false;
+			break;
+		}
+	}
+	up_read(&tomoyo_pattern_list_lock);
+	return done;
+}
+
+/* The list for "struct tomoyo_no_rewrite_entry". */
+static LIST_HEAD(tomoyo_no_rewrite_list);
+static DECLARE_RWSEM(tomoyo_no_rewrite_list_lock);
+
+/**
+ * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite_entry" list.
+ *
+ * @pattern:   Pathname pattern that are not rewritable by default.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_no_rewrite_entry(const char *pattern,
+					  const bool is_delete)
+{
+	struct tomoyo_no_rewrite_entry *new_entry, *ptr;
+	const struct tomoyo_path_info *saved_pattern;
+	int error = -ENOMEM;
+
+	if (!tomoyo_is_correct_path(pattern, 0, 0, 0, __func__))
+		return -EINVAL;
+	saved_pattern = tomoyo_save_name(pattern);
+	if (!saved_pattern)
+		return -ENOMEM;
+	/***** EXCLUSIVE SECTION START *****/
+	down_write(&tomoyo_no_rewrite_list_lock);
+	list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+		if (ptr->pattern != saved_pattern)
+			continue;
+		ptr->is_deleted = is_delete;
+		error = 0;
+		goto out;
+	}
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+	new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+	if (!new_entry)
+		goto out;
+	new_entry->pattern = saved_pattern;
+	list_add_tail(&new_entry->list, &tomoyo_no_rewrite_list);
+	error = 0;
+ out:
+	up_write(&tomoyo_no_rewrite_list_lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return error;
+}
+
+/**
+ * tomoyo_is_no_rewrite_file - Check if the given pathname is not permitted to be rewrited.
+ *
+ * @filename: Filename to check.
+ *
+ * Returns true if @filename is specified by "deny_rewrite" directive,
+ * false otherwise.
+ */
+static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
+{
+	struct tomoyo_no_rewrite_entry *ptr;
+	bool found = false;
+
+	down_read(&tomoyo_no_rewrite_list_lock);
+	list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+		if (ptr->is_deleted)
+			continue;
+		if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
+			continue;
+		found = true;
+		break;
+	}
+	up_read(&tomoyo_no_rewrite_list_lock);
+	return found;
+}
+
+/**
+ * tomoyo_write_no_rewrite_policy - Write "struct tomoyo_no_rewrite_entry" list.
+ *
+ * @data:      String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
+{
+	return tomoyo_update_no_rewrite_entry(data, is_delete);
+}
+
+/**
+ * tomoyo_read_no_rewrite_policy - Read "struct tomoyo_no_rewrite_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
+{
+	struct list_head *pos;
+	bool done = true;
+
+	down_read(&tomoyo_no_rewrite_list_lock);
+	list_for_each_cookie(pos, head->read_var2, &tomoyo_no_rewrite_list) {
+		struct tomoyo_no_rewrite_entry *ptr;
+		ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list);
+		if (ptr->is_deleted)
+			continue;
+		if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_DENY_REWRITE "%s\n",
+				      ptr->pattern->name)) {
+			done = false;
+			break;
+		}
+	}
+	up_read(&tomoyo_no_rewrite_list_lock);
+	return done;
+}
+
+/**
+ * tomoyo_update_file_acl - Update file's read/write/execute ACL.
+ *
+ * @filename:  Filename.
+ * @perm:      Permission (between 1 to 7).
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * This is legacy support interface for older policy syntax.
+ * Current policy syntax uses "allow_read/write" instead of "6",
+ * "allow_read" instead of "4", "allow_write" instead of "2",
+ * "allow_execute" instead of "1".
+ */
+static int tomoyo_update_file_acl(const char *filename, u8 perm,
+				  struct tomoyo_domain_info * const domain,
+				  const bool is_delete)
+{
+	if (perm > 7 || !perm) {
+		printk(KERN_DEBUG "%s: Invalid permission '%d %s'\n",
+		       __func__, perm, filename);
+		return -EINVAL;
+	}
+	if (filename[0] != '@' && tomoyo_strendswith(filename, "/"))
+		/*
+		 * Only 'allow_mkdir' and 'allow_rmdir' are valid for
+		 * directory permissions.
+		 */
+		return 0;
+	if (perm & 4)
+		tomoyo_update_single_path_acl(TOMOYO_TYPE_READ_ACL, filename,
+					      domain, is_delete);
+	if (perm & 2)
+		tomoyo_update_single_path_acl(TOMOYO_TYPE_WRITE_ACL, filename,
+					      domain, is_delete);
+	if (perm & 1)
+		tomoyo_update_single_path_acl(TOMOYO_TYPE_EXECUTE_ACL,
+					      filename, domain, is_delete);
+	return 0;
+}
+
+/**
+ * tomoyo_check_single_path_acl2 - Check permission for single path operation.
+ *
+ * @domain:          Pointer to "struct tomoyo_domain_info".
+ * @filename:        Filename to check.
+ * @perm:            Permission.
+ * @may_use_pattern: True if patterned ACL is permitted.
+ *
+ * Returns 0 on success, -EPERM otherwise.
+ */
+static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
+					 domain,
+					 const struct tomoyo_path_info *
+					 filename,
+					 const u16 perm,
+					 const bool may_use_pattern)
+{
+	struct tomoyo_acl_info *ptr;
+	int error = -EPERM;
+
+	down_read(&tomoyo_domain_acl_info_list_lock);
+	list_for_each_entry(ptr, &domain->acl_info_list, list) {
+		struct tomoyo_single_path_acl_record *acl;
+		if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
+			continue;
+		acl = container_of(ptr, struct tomoyo_single_path_acl_record,
+				   head);
+		if (!(acl->perm & perm))
+			continue;
+		if (may_use_pattern || !acl->filename->is_patterned) {
+			if (!tomoyo_path_matches_pattern(filename,
+							 acl->filename))
+				continue;
+		} else {
+			continue;
+		}
+		error = 0;
+		break;
+	}
+	up_read(&tomoyo_domain_acl_info_list_lock);
+	return error;
+}
+
+/**
+ * tomoyo_check_file_acl - Check permission for opening files.
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @filename:  Filename to check.
+ * @operation: Mode ("read" or "write" or "read/write" or "execute").
+ *
+ * Returns 0 on success, -EPERM otherwise.
+ */
+static int tomoyo_check_file_acl(const struct tomoyo_domain_info *domain,
+				 const struct tomoyo_path_info *filename,
+				 const u8 operation)
+{
+	u16 perm = 0;
+
+	if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
+		return 0;
+	if (operation == 6)
+		perm = 1 << TOMOYO_TYPE_READ_WRITE_ACL;
+	else if (operation == 4)
+		perm = 1 << TOMOYO_TYPE_READ_ACL;
+	else if (operation == 2)
+		perm = 1 << TOMOYO_TYPE_WRITE_ACL;
+	else if (operation == 1)
+		perm = 1 << TOMOYO_TYPE_EXECUTE_ACL;
+	else
+		BUG();
+	return tomoyo_check_single_path_acl2(domain, filename, perm,
+					     operation != 1);
+}
+
+/**
+ * tomoyo_check_file_perm2 - Check permission for opening files.
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @filename:  Filename to check.
+ * @perm:      Mode ("read" or "write" or "read/write" or "execute").
+ * @operation: Operation name passed used for verbose mode.
+ * @mode:      Access control mode.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain,
+				   const struct tomoyo_path_info *filename,
+				   const u8 perm, const char *operation,
+				   const u8 mode)
+{
+	const bool is_enforce = (mode == 3);
+	const char *msg = "<unknown>";
+	int error = 0;
+
+	if (!filename)
+		return 0;
+	error = tomoyo_check_file_acl(domain, filename, perm);
+	if (error && perm == 4 &&
+	    (domain->flags & TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ) == 0
+	    && tomoyo_is_globally_readable_file(filename))
+		error = 0;
+	if (perm == 6)
+		msg = tomoyo_sp2keyword(TOMOYO_TYPE_READ_WRITE_ACL);
+	else if (perm == 4)
+		msg = tomoyo_sp2keyword(TOMOYO_TYPE_READ_ACL);
+	else if (perm == 2)
+		msg = tomoyo_sp2keyword(TOMOYO_TYPE_WRITE_ACL);
+	else if (perm == 1)
+		msg = tomoyo_sp2keyword(TOMOYO_TYPE_EXECUTE_ACL);
+	else
+		BUG();
+	if (!error)
+		return 0;
+	if (tomoyo_verbose_mode(domain))
+		printk(KERN_WARNING "TOMOYO-%s: Access '%s(%s) %s' denied "
+		       "for %s\n", tomoyo_get_msg(is_enforce), msg, operation,
+		       filename->name, tomoyo_get_last_name(domain));
+	if (is_enforce)
+		return error;
+	if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) {
+		/* Don't use patterns for execute permission. */
+		const struct tomoyo_path_info *patterned_file = (perm != 1) ?
+			tomoyo_get_file_pattern(filename) : filename;
+		tomoyo_update_file_acl(patterned_file->name, perm,
+				       domain, false);
+	}
+	return 0;
+}
+
+/**
+ * tomoyo_write_file_policy - Update file related list.
+ *
+ * @data:      String to parse.
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
+			     const bool is_delete)
+{
+	char *filename = strchr(data, ' ');
+	char *filename2;
+	unsigned int perm;
+	u8 type;
+
+	if (!filename)
+		return -EINVAL;
+	*filename++ = '\0';
+	if (sscanf(data, "%u", &perm) == 1)
+		return tomoyo_update_file_acl(filename, (u8) perm, domain,
+					      is_delete);
+	if (strncmp(data, "allow_", 6))
+		goto out;
+	data += 6;
+	for (type = 0; type < TOMOYO_MAX_SINGLE_PATH_OPERATION; type++) {
+		if (strcmp(data, tomoyo_sp_keyword[type]))
+			continue;
+		return tomoyo_update_single_path_acl(type, filename,
+						     domain, is_delete);
+	}
+	filename2 = strchr(filename, ' ');
+	if (!filename2)
+		goto out;
+	*filename2++ = '\0';
+	for (type = 0; type < TOMOYO_MAX_DOUBLE_PATH_OPERATION; type++) {
+		if (strcmp(data, tomoyo_dp_keyword[type]))
+			continue;
+		return tomoyo_update_double_path_acl(type, filename, filename2,
+						     domain, is_delete);
+	}
+ out:
+	return -EINVAL;
+}
+
+/**
+ * tomoyo_update_single_path_acl - Update "struct tomoyo_single_path_acl_record" list.
+ *
+ * @type:      Type of operation.
+ * @filename:  Filename.
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
+					 struct tomoyo_domain_info *
+					 const domain, const bool is_delete)
+{
+	static const u16 rw_mask =
+		(1 << TOMOYO_TYPE_READ_ACL) | (1 << TOMOYO_TYPE_WRITE_ACL);
+	const struct tomoyo_path_info *saved_filename;
+	struct tomoyo_acl_info *ptr;
+	struct tomoyo_single_path_acl_record *acl;
+	int error = -ENOMEM;
+	const u16 perm = 1 << type;
+
+	if (!domain)
+		return -EINVAL;
+	if (!tomoyo_is_correct_path(filename, 0, 0, 0, __func__))
+		return -EINVAL;
+	saved_filename = tomoyo_save_name(filename);
+	if (!saved_filename)
+		return -ENOMEM;
+	/***** EXCLUSIVE SECTION START *****/
+	down_write(&tomoyo_domain_acl_info_list_lock);
+	if (is_delete)
+		goto delete;
+	list_for_each_entry(ptr, &domain->acl_info_list, list) {
+		if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
+			continue;
+		acl = container_of(ptr, struct tomoyo_single_path_acl_record,
+				   head);
+		if (acl->filename != saved_filename)
+			continue;
+		/* Special case. Clear all bits if marked as deleted. */
+		if (ptr->type & TOMOYO_ACL_DELETED)
+			acl->perm = 0;
+		acl->perm |= perm;
+		if ((acl->perm & rw_mask) == rw_mask)
+			acl->perm |= 1 << TOMOYO_TYPE_READ_WRITE_ACL;
+		else if (acl->perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL))
+			acl->perm |= rw_mask;
+		ptr->type &= ~TOMOYO_ACL_DELETED;
+		error = 0;
+		goto out;
+	}
+	/* Not found. Append it to the tail. */
+	acl = tomoyo_alloc_acl_element(TOMOYO_TYPE_SINGLE_PATH_ACL);
+	if (!acl)
+		goto out;
+	acl->perm = perm;
+	if (perm == (1 << TOMOYO_TYPE_READ_WRITE_ACL))
+		acl->perm |= rw_mask;
+	acl->filename = saved_filename;
+	list_add_tail(&acl->head.list, &domain->acl_info_list);
+	error = 0;
+	goto out;
+ delete:
+	error = -ENOENT;
+	list_for_each_entry(ptr, &domain->acl_info_list, list) {
+		if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
+			continue;
+		acl = container_of(ptr, struct tomoyo_single_path_acl_record,
+				   head);
+		if (acl->filename != saved_filename)
+			continue;
+		acl->perm &= ~perm;
+		if ((acl->perm & rw_mask) != rw_mask)
+			acl->perm &= ~(1 << TOMOYO_TYPE_READ_WRITE_ACL);
+		else if (!(acl->perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL)))
+			acl->perm &= ~rw_mask;
+		if (!acl->perm)
+			ptr->type |= TOMOYO_ACL_DELETED;
+		error = 0;
+		break;
+	}
+ out:
+	up_write(&tomoyo_domain_acl_info_list_lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return error;
+}
+
+/**
+ * tomoyo_update_double_path_acl - Update "struct tomoyo_double_path_acl_record" list.
+ *
+ * @type:      Type of operation.
+ * @filename1: First filename.
+ * @filename2: Second filename.
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
+					 const char *filename2,
+					 struct tomoyo_domain_info *
+					 const domain, const bool is_delete)
+{
+	const struct tomoyo_path_info *saved_filename1;
+	const struct tomoyo_path_info *saved_filename2;
+	struct tomoyo_acl_info *ptr;
+	struct tomoyo_double_path_acl_record *acl;
+	int error = -ENOMEM;
+	const u8 perm = 1 << type;
+
+	if (!domain)
+		return -EINVAL;
+	if (!tomoyo_is_correct_path(filename1, 0, 0, 0, __func__) ||
+	    !tomoyo_is_correct_path(filename2, 0, 0, 0, __func__))
+		return -EINVAL;
+	saved_filename1 = tomoyo_save_name(filename1);
+	saved_filename2 = tomoyo_save_name(filename2);
+	if (!saved_filename1 || !saved_filename2)
+		return -ENOMEM;
+	/***** EXCLUSIVE SECTION START *****/
+	down_write(&tomoyo_domain_acl_info_list_lock);
+	if (is_delete)
+		goto delete;
+	list_for_each_entry(ptr, &domain->acl_info_list, list) {
+		if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
+			continue;
+		acl = container_of(ptr, struct tomoyo_double_path_acl_record,
+				   head);
+		if (acl->filename1 != saved_filename1 ||
+		    acl->filename2 != saved_filename2)
+			continue;
+		/* Special case. Clear all bits if marked as deleted. */
+		if (ptr->type & TOMOYO_ACL_DELETED)
+			acl->perm = 0;
+		acl->perm |= perm;
+		ptr->type &= ~TOMOYO_ACL_DELETED;
+		error = 0;
+		goto out;
+	}
+	/* Not found. Append it to the tail. */
+	acl = tomoyo_alloc_acl_element(TOMOYO_TYPE_DOUBLE_PATH_ACL);
+	if (!acl)
+		goto out;
+	acl->perm = perm;
+	acl->filename1 = saved_filename1;
+	acl->filename2 = saved_filename2;
+	list_add_tail(&acl->head.list, &domain->acl_info_list);
+	error = 0;
+	goto out;
+ delete:
+	error = -ENOENT;
+	list_for_each_entry(ptr, &domain->acl_info_list, list) {
+		if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
+			continue;
+		acl = container_of(ptr, struct tomoyo_double_path_acl_record,
+				   head);
+		if (acl->filename1 != saved_filename1 ||
+		    acl->filename2 != saved_filename2)
+			continue;
+		acl->perm &= ~perm;
+		if (!acl->perm)
+			ptr->type |= TOMOYO_ACL_DELETED;
+		error = 0;
+		break;
+	}
+ out:
+	up_write(&tomoyo_domain_acl_info_list_lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return error;
+}
+
+/**
+ * tomoyo_check_single_path_acl - Check permission for single path operation.
+ *
+ * @domain:   Pointer to "struct tomoyo_domain_info".
+ * @type:     Type of operation.
+ * @filename: Filename to check.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_check_single_path_acl(struct tomoyo_domain_info *domain,
+					const u8 type,
+					const struct tomoyo_path_info *filename)
+{
+	if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
+		return 0;
+	return tomoyo_check_single_path_acl2(domain, filename, 1 << type, 1);
+}
+
+/**
+ * tomoyo_check_double_path_acl - Check permission for double path operation.
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @type:      Type of operation.
+ * @filename1: First filename to check.
+ * @filename2: Second filename to check.
+ *
+ * Returns 0 on success, -EPERM otherwise.
+ */
+static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
+					const u8 type,
+					const struct tomoyo_path_info *
+					filename1,
+					const struct tomoyo_path_info *
+					filename2)
+{
+	struct tomoyo_acl_info *ptr;
+	const u8 perm = 1 << type;
+	int error = -EPERM;
+
+	if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
+		return 0;
+	down_read(&tomoyo_domain_acl_info_list_lock);
+	list_for_each_entry(ptr, &domain->acl_info_list, list) {
+		struct tomoyo_double_path_acl_record *acl;
+		if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
+			continue;
+		acl = container_of(ptr, struct tomoyo_double_path_acl_record,
+				   head);
+		if (!(acl->perm & perm))
+			continue;
+		if (!tomoyo_path_matches_pattern(filename1, acl->filename1))
+			continue;
+		if (!tomoyo_path_matches_pattern(filename2, acl->filename2))
+			continue;
+		error = 0;
+		break;
+	}
+	up_read(&tomoyo_domain_acl_info_list_lock);
+	return error;
+}
+
+/**
+ * tomoyo_check_single_path_permission2 - Check permission for single path operation.
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @operation: Type of operation.
+ * @filename:  Filename to check.
+ * @mode:      Access control mode.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
+						const domain, u8 operation,
+						const struct tomoyo_path_info *
+						filename, const u8 mode)
+{
+	const char *msg;
+	int error;
+	const bool is_enforce = (mode == 3);
+
+	if (!mode)
+		return 0;
+ next:
+	error = tomoyo_check_single_path_acl(domain, operation, filename);
+	msg = tomoyo_sp2keyword(operation);
+	if (!error)
+		goto ok;
+	if (tomoyo_verbose_mode(domain))
+		printk(KERN_WARNING "TOMOYO-%s: Access '%s %s' denied for %s\n",
+		       tomoyo_get_msg(is_enforce), msg, filename->name,
+		       tomoyo_get_last_name(domain));
+	if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) {
+		const char *name = tomoyo_get_file_pattern(filename)->name;
+		tomoyo_update_single_path_acl(operation, name, domain, false);
+	}
+	if (!is_enforce)
+		error = 0;
+ ok:
+	/*
+	 * Since "allow_truncate" doesn't imply "allow_rewrite" permission,
+	 * we need to check "allow_rewrite" permission if the filename is
+	 * specified by "deny_rewrite" keyword.
+	 */
+	if (!error && operation == TOMOYO_TYPE_TRUNCATE_ACL &&
+	    tomoyo_is_no_rewrite_file(filename)) {
+		operation = TOMOYO_TYPE_REWRITE_ACL;
+		goto next;
+	}
+	return error;
+}
+
+/**
+ * tomoyo_check_file_perm - Check permission for sysctl()'s "read" and "write".
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @filename:  Filename to check.
+ * @perm:      Mode ("read" or "write" or "read/write").
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_check_file_perm(struct tomoyo_domain_info *domain,
+			   const char *filename, const u8 perm)
+{
+	struct tomoyo_path_info name;
+	const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+
+	if (!mode)
+		return 0;
+	name.name = filename;
+	tomoyo_fill_path_info(&name);
+	return tomoyo_check_file_perm2(domain, &name, perm, "sysctl", mode);
+}
+
+/**
+ * tomoyo_check_exec_perm - Check permission for "execute".
+ *
+ * @domain:   Pointer to "struct tomoyo_domain_info".
+ * @filename: Check permission for "execute".
+ * @tmp:      Buffer for temporary use.
+ *
+ * Returns 0 on success, negativevalue otherwise.
+ */
+int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
+			   const struct tomoyo_path_info *filename,
+			   struct tomoyo_page_buffer *tmp)
+{
+	const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+
+	if (!mode)
+		return 0;
+	return tomoyo_check_file_perm2(domain, filename, 1, "do_execve", mode);
+}
+
+/**
+ * tomoyo_check_open_permission - Check permission for "read" and "write".
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @path:   Pointer to "struct path".
+ * @flag:   Flags for open().
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
+				 struct path *path, const int flag)
+{
+	const u8 acc_mode = ACC_MODE(flag);
+	int error = -ENOMEM;
+	struct tomoyo_path_info *buf;
+	const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+	const bool is_enforce = (mode == 3);
+
+	if (!mode || !path->mnt)
+		return 0;
+	if (acc_mode == 0)
+		return 0;
+	if (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode))
+		/*
+		 * I don't check directories here because mkdir() and rmdir()
+		 * don't call me.
+		 */
+		return 0;
+	buf = tomoyo_get_path(path);
+	if (!buf)
+		goto out;
+	error = 0;
+	/*
+	 * If the filename is specified by "deny_rewrite" keyword,
+	 * we need to check "allow_rewrite" permission when the filename is not
+	 * opened for append mode or the filename is truncated at open time.
+	 */
+	if ((acc_mode & MAY_WRITE) &&
+	    ((flag & O_TRUNC) || !(flag & O_APPEND)) &&
+	    (tomoyo_is_no_rewrite_file(buf))) {
+		error = tomoyo_check_single_path_permission2(domain,
+						     TOMOYO_TYPE_REWRITE_ACL,
+							     buf, mode);
+	}
+	if (!error)
+		error = tomoyo_check_file_perm2(domain, buf, acc_mode, "open",
+						mode);
+	if (!error && (flag & O_TRUNC))
+		error = tomoyo_check_single_path_permission2(domain,
+						     TOMOYO_TYPE_TRUNCATE_ACL,
+							     buf, mode);
+ out:
+	tomoyo_free(buf);
+	if (!is_enforce)
+		error = 0;
+	return error;
+}
+
+/**
+ * tomoyo_check_1path_perm - Check permission for "create", "unlink", "mkdir", "rmdir", "mkfifo", "mksock", "mkblock", "mkchar", "truncate" and "symlink".
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @operation: Type of operation.
+ * @path:      Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
+			    const u8 operation, struct path *path)
+{
+	int error = -ENOMEM;
+	struct tomoyo_path_info *buf;
+	const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+	const bool is_enforce = (mode == 3);
+
+	if (!mode || !path->mnt)
+		return 0;
+	buf = tomoyo_get_path(path);
+	if (!buf)
+		goto out;
+	switch (operation) {
+	case TOMOYO_TYPE_MKDIR_ACL:
+	case TOMOYO_TYPE_RMDIR_ACL:
+		if (!buf->is_dir) {
+			/*
+			 * tomoyo_get_path() reserves space for appending "/."
+			 */
+			strcat((char *) buf->name, "/");
+			tomoyo_fill_path_info(buf);
+		}
+	}
+	error = tomoyo_check_single_path_permission2(domain, operation, buf,
+						     mode);
+ out:
+	tomoyo_free(buf);
+	if (!is_enforce)
+		error = 0;
+	return error;
+}
+
+/**
+ * tomoyo_check_rewrite_permission - Check permission for "rewrite".
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @filp: Pointer to "struct file".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
+				    struct file *filp)
+{
+	int error = -ENOMEM;
+	const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+	const bool is_enforce = (mode == 3);
+	struct tomoyo_path_info *buf;
+
+	if (!mode || !filp->f_path.mnt)
+		return 0;
+	buf = tomoyo_get_path(&filp->f_path);
+	if (!buf)
+		goto out;
+	if (!tomoyo_is_no_rewrite_file(buf)) {
+		error = 0;
+		goto out;
+	}
+	error = tomoyo_check_single_path_permission2(domain,
+						     TOMOYO_TYPE_REWRITE_ACL,
+						     buf, mode);
+ out:
+	tomoyo_free(buf);
+	if (!is_enforce)
+		error = 0;
+	return error;
+}
+
+/**
+ * tomoyo_check_2path_perm - Check permission for "rename" and "link".
+ *
+ * @domain:    Pointer to "struct tomoyo_domain_info".
+ * @operation: Type of operation.
+ * @path1:      Pointer to "struct path".
+ * @path2:      Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_check_2path_perm(struct tomoyo_domain_info * const domain,
+			    const u8 operation, struct path *path1,
+			    struct path *path2)
+{
+	int error = -ENOMEM;
+	struct tomoyo_path_info *buf1, *buf2;
+	const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+	const bool is_enforce = (mode == 3);
+	const char *msg;
+
+	if (!mode || !path1->mnt || !path2->mnt)
+		return 0;
+	buf1 = tomoyo_get_path(path1);
+	buf2 = tomoyo_get_path(path2);
+	if (!buf1 || !buf2)
+		goto out;
+	{
+		struct dentry *dentry = path1->dentry;
+		if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) {
+			/*
+			 * tomoyo_get_path() reserves space for appending "/."
+			 */
+			if (!buf1->is_dir) {
+				strcat((char *) buf1->name, "/");
+				tomoyo_fill_path_info(buf1);
+			}
+			if (!buf2->is_dir) {
+				strcat((char *) buf2->name, "/");
+				tomoyo_fill_path_info(buf2);
+			}
+		}
+	}
+	error = tomoyo_check_double_path_acl(domain, operation, buf1, buf2);
+	msg = tomoyo_dp2keyword(operation);
+	if (!error)
+		goto out;
+	if (tomoyo_verbose_mode(domain))
+		printk(KERN_WARNING "TOMOYO-%s: Access '%s %s %s' "
+		       "denied for %s\n", tomoyo_get_msg(is_enforce),
+		       msg, buf1->name, buf2->name,
+		       tomoyo_get_last_name(domain));
+	if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) {
+		const char *name1 = tomoyo_get_file_pattern(buf1)->name;
+		const char *name2 = tomoyo_get_file_pattern(buf2)->name;
+		tomoyo_update_double_path_acl(operation, name1, name2, domain,
+					      false);
+	}
+ out:
+	tomoyo_free(buf1);
+	tomoyo_free(buf2);
+	if (!is_enforce)
+		error = 0;
+	return error;
+}

+ 482 - 0
security/tomoyo/realpath.c

@@ -0,0 +1,482 @@
+/*
+ * security/tomoyo/realpath.c
+ *
+ * Get the canonicalized absolute pathnames. The basis for TOMOYO.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/02/01
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/mount.h>
+#include <linux/mnt_namespace.h>
+#include "common.h"
+#include "realpath.h"
+
+/**
+ * tomoyo_encode: Convert binary string to ascii string.
+ *
+ * @buffer:  Buffer for ASCII string.
+ * @buflen:  Size of @buffer.
+ * @str:     Binary string.
+ *
+ * Returns 0 on success, -ENOMEM otherwise.
+ */
+int tomoyo_encode(char *buffer, int buflen, const char *str)
+{
+	while (1) {
+		const unsigned char c = *(unsigned char *) str++;
+
+		if (tomoyo_is_valid(c)) {
+			if (--buflen <= 0)
+				break;
+			*buffer++ = (char) c;
+			if (c != '\\')
+				continue;
+			if (--buflen <= 0)
+				break;
+			*buffer++ = (char) c;
+			continue;
+		}
+		if (!c) {
+			if (--buflen <= 0)
+				break;
+			*buffer = '\0';
+			return 0;
+		}
+		buflen -= 4;
+		if (buflen <= 0)
+			break;
+		*buffer++ = '\\';
+		*buffer++ = (c >> 6) + '0';
+		*buffer++ = ((c >> 3) & 7) + '0';
+		*buffer++ = (c & 7) + '0';
+	}
+	return -ENOMEM;
+}
+
+/**
+ * tomoyo_realpath_from_path2 - Returns realpath(3) of the given dentry but ignores chroot'ed root.
+ *
+ * @path:        Pointer to "struct path".
+ * @newname:     Pointer to buffer to return value in.
+ * @newname_len: Size of @newname.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * If dentry is a directory, trailing '/' is appended.
+ * Characters out of 0x20 < c < 0x7F range are converted to
+ * \ooo style octal string.
+ * Character \ is converted to \\ string.
+ */
+int tomoyo_realpath_from_path2(struct path *path, char *newname,
+			       int newname_len)
+{
+	int error = -ENOMEM;
+	struct dentry *dentry = path->dentry;
+	char *sp;
+
+	if (!dentry || !path->mnt || !newname || newname_len <= 2048)
+		return -EINVAL;
+	if (dentry->d_op && dentry->d_op->d_dname) {
+		/* For "socket:[\$]" and "pipe:[\$]". */
+		static const int offset = 1536;
+		sp = dentry->d_op->d_dname(dentry, newname + offset,
+					   newname_len - offset);
+	} else {
+		/* Taken from d_namespace_path(). */
+		struct path root;
+		struct path ns_root = { };
+		struct path tmp;
+
+		read_lock(&current->fs->lock);
+		root = current->fs->root;
+		path_get(&root);
+		read_unlock(&current->fs->lock);
+		spin_lock(&vfsmount_lock);
+		if (root.mnt && root.mnt->mnt_ns)
+			ns_root.mnt = mntget(root.mnt->mnt_ns->root);
+		if (ns_root.mnt)
+			ns_root.dentry = dget(ns_root.mnt->mnt_root);
+		spin_unlock(&vfsmount_lock);
+		spin_lock(&dcache_lock);
+		tmp = ns_root;
+		sp = __d_path(path, &tmp, newname, newname_len);
+		spin_unlock(&dcache_lock);
+		path_put(&root);
+		path_put(&ns_root);
+	}
+	if (IS_ERR(sp))
+		error = PTR_ERR(sp);
+	else
+		error = tomoyo_encode(newname, sp - newname, sp);
+	/* Append trailing '/' if dentry is a directory. */
+	if (!error && dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)
+	    && *newname) {
+		sp = newname + strlen(newname);
+		if (*(sp - 1) != '/') {
+			if (sp < newname + newname_len - 4) {
+				*sp++ = '/';
+				*sp = '\0';
+			} else {
+				error = -ENOMEM;
+			}
+		}
+	}
+	if (error)
+		printk(KERN_WARNING "tomoyo_realpath: Pathname too long.\n");
+	return error;
+}
+
+/**
+ * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root.
+ *
+ * @path: Pointer to "struct path".
+ *
+ * Returns the realpath of the given @path on success, NULL otherwise.
+ *
+ * These functions use tomoyo_alloc(), so the caller must call tomoyo_free()
+ * if these functions didn't return NULL.
+ */
+char *tomoyo_realpath_from_path(struct path *path)
+{
+	char *buf = tomoyo_alloc(sizeof(struct tomoyo_page_buffer));
+
+	BUILD_BUG_ON(sizeof(struct tomoyo_page_buffer)
+		     <= TOMOYO_MAX_PATHNAME_LEN - 1);
+	if (!buf)
+		return NULL;
+	if (tomoyo_realpath_from_path2(path, buf,
+				       TOMOYO_MAX_PATHNAME_LEN - 1) == 0)
+		return buf;
+	tomoyo_free(buf);
+	return NULL;
+}
+
+/**
+ * tomoyo_realpath - Get realpath of a pathname.
+ *
+ * @pathname: The pathname to solve.
+ *
+ * Returns the realpath of @pathname on success, NULL otherwise.
+ */
+char *tomoyo_realpath(const char *pathname)
+{
+	struct nameidata nd;
+
+	if (pathname && path_lookup(pathname, LOOKUP_FOLLOW, &nd) == 0) {
+		char *buf = tomoyo_realpath_from_path(&nd.path);
+		path_put(&nd.path);
+		return buf;
+	}
+	return NULL;
+}
+
+/**
+ * tomoyo_realpath_nofollow - Get realpath of a pathname.
+ *
+ * @pathname: The pathname to solve.
+ *
+ * Returns the realpath of @pathname on success, NULL otherwise.
+ */
+char *tomoyo_realpath_nofollow(const char *pathname)
+{
+	struct nameidata nd;
+
+	if (pathname && path_lookup(pathname, 0, &nd) == 0) {
+		char *buf = tomoyo_realpath_from_path(&nd.path);
+		path_put(&nd.path);
+		return buf;
+	}
+	return NULL;
+}
+
+/* Memory allocated for non-string data. */
+static unsigned int tomoyo_allocated_memory_for_elements;
+/* Quota for holding non-string data. */
+static unsigned int tomoyo_quota_for_elements;
+
+/**
+ * tomoyo_alloc_element - Allocate permanent memory for structures.
+ *
+ * @size: Size in bytes.
+ *
+ * Returns pointer to allocated memory on success, NULL otherwise.
+ *
+ * Memory has to be zeroed.
+ * The RAM is chunked, so NEVER try to kfree() the returned pointer.
+ */
+void *tomoyo_alloc_element(const unsigned int size)
+{
+	static char *buf;
+	static DEFINE_MUTEX(lock);
+	static unsigned int buf_used_len = PATH_MAX;
+	char *ptr = NULL;
+	/*Assumes sizeof(void *) >= sizeof(long) is true. */
+	const unsigned int word_aligned_size
+		= roundup(size, max(sizeof(void *), sizeof(long)));
+	if (word_aligned_size > PATH_MAX)
+		return NULL;
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&lock);
+	if (buf_used_len + word_aligned_size > PATH_MAX) {
+		if (!tomoyo_quota_for_elements ||
+		    tomoyo_allocated_memory_for_elements
+		    + PATH_MAX <= tomoyo_quota_for_elements)
+			ptr = kzalloc(PATH_MAX, GFP_KERNEL);
+		if (!ptr) {
+			printk(KERN_WARNING "ERROR: Out of memory "
+			       "for tomoyo_alloc_element().\n");
+			if (!tomoyo_policy_loaded)
+				panic("MAC Initialization failed.\n");
+		} else {
+			buf = ptr;
+			tomoyo_allocated_memory_for_elements += PATH_MAX;
+			buf_used_len = word_aligned_size;
+			ptr = buf;
+		}
+	} else if (word_aligned_size) {
+		int i;
+		ptr = buf + buf_used_len;
+		buf_used_len += word_aligned_size;
+		for (i = 0; i < word_aligned_size; i++) {
+			if (!ptr[i])
+				continue;
+			printk(KERN_ERR "WARNING: Reserved memory was tainted! "
+			       "The system might go wrong.\n");
+			ptr[i] = '\0';
+		}
+	}
+	mutex_unlock(&lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return ptr;
+}
+
+/* Memory allocated for string data in bytes. */
+static unsigned int tomoyo_allocated_memory_for_savename;
+/* Quota for holding string data in bytes. */
+static unsigned int tomoyo_quota_for_savename;
+
+/*
+ * TOMOYO uses this hash only when appending a string into the string
+ * table. Frequency of appending strings is very low. So we don't need
+ * large (e.g. 64k) hash size. 256 will be sufficient.
+ */
+#define TOMOYO_MAX_HASH 256
+
+/* Structure for string data. */
+struct tomoyo_name_entry {
+	struct list_head list;
+	struct tomoyo_path_info entry;
+};
+
+/* Structure for available memory region. */
+struct tomoyo_free_memory_block_list {
+	struct list_head list;
+	char *ptr;             /* Pointer to a free area. */
+	int len;               /* Length of the area.     */
+};
+
+/*
+ * The list for "struct tomoyo_name_entry".
+ *
+ * This list is updated only inside tomoyo_save_name(), thus
+ * no global mutex exists.
+ */
+static struct list_head tomoyo_name_list[TOMOYO_MAX_HASH];
+
+/**
+ * tomoyo_save_name - Allocate permanent memory for string data.
+ *
+ * @name: The string to store into the permernent memory.
+ *
+ * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
+ *
+ * The RAM is shared, so NEVER try to modify or kfree() the returned name.
+ */
+const struct tomoyo_path_info *tomoyo_save_name(const char *name)
+{
+	static LIST_HEAD(fmb_list);
+	static DEFINE_MUTEX(lock);
+	struct tomoyo_name_entry *ptr;
+	unsigned int hash;
+	/* fmb contains available size in bytes.
+	   fmb is removed from the fmb_list when fmb->len becomes 0. */
+	struct tomoyo_free_memory_block_list *fmb;
+	int len;
+	char *cp;
+
+	if (!name)
+		return NULL;
+	len = strlen(name) + 1;
+	if (len > TOMOYO_MAX_PATHNAME_LEN) {
+		printk(KERN_WARNING "ERROR: Name too long "
+		       "for tomoyo_save_name().\n");
+		return NULL;
+	}
+	hash = full_name_hash((const unsigned char *) name, len - 1);
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&lock);
+	list_for_each_entry(ptr, &tomoyo_name_list[hash % TOMOYO_MAX_HASH],
+			     list) {
+		if (hash == ptr->entry.hash && !strcmp(name, ptr->entry.name))
+			goto out;
+	}
+	list_for_each_entry(fmb, &fmb_list, list) {
+		if (len <= fmb->len)
+			goto ready;
+	}
+	if (!tomoyo_quota_for_savename ||
+	    tomoyo_allocated_memory_for_savename + PATH_MAX
+	    <= tomoyo_quota_for_savename)
+		cp = kzalloc(PATH_MAX, GFP_KERNEL);
+	else
+		cp = NULL;
+	fmb = kzalloc(sizeof(*fmb), GFP_KERNEL);
+	if (!cp || !fmb) {
+		kfree(cp);
+		kfree(fmb);
+		printk(KERN_WARNING "ERROR: Out of memory "
+		       "for tomoyo_save_name().\n");
+		if (!tomoyo_policy_loaded)
+			panic("MAC Initialization failed.\n");
+		ptr = NULL;
+		goto out;
+	}
+	tomoyo_allocated_memory_for_savename += PATH_MAX;
+	list_add(&fmb->list, &fmb_list);
+	fmb->ptr = cp;
+	fmb->len = PATH_MAX;
+ ready:
+	ptr = tomoyo_alloc_element(sizeof(*ptr));
+	if (!ptr)
+		goto out;
+	ptr->entry.name = fmb->ptr;
+	memmove(fmb->ptr, name, len);
+	tomoyo_fill_path_info(&ptr->entry);
+	fmb->ptr += len;
+	fmb->len -= len;
+	list_add_tail(&ptr->list, &tomoyo_name_list[hash % TOMOYO_MAX_HASH]);
+	if (fmb->len == 0) {
+		list_del(&fmb->list);
+		kfree(fmb);
+	}
+ out:
+	mutex_unlock(&lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return ptr ? &ptr->entry : NULL;
+}
+
+/**
+ * tomoyo_realpath_init - Initialize realpath related code.
+ */
+void __init tomoyo_realpath_init(void)
+{
+	int i;
+
+	BUILD_BUG_ON(TOMOYO_MAX_PATHNAME_LEN > PATH_MAX);
+	for (i = 0; i < TOMOYO_MAX_HASH; i++)
+		INIT_LIST_HEAD(&tomoyo_name_list[i]);
+	INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
+	tomoyo_kernel_domain.domainname = tomoyo_save_name(TOMOYO_ROOT_NAME);
+	list_add_tail(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
+	down_read(&tomoyo_domain_list_lock);
+	if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
+		panic("Can't register tomoyo_kernel_domain");
+	up_read(&tomoyo_domain_list_lock);
+}
+
+/* Memory allocated for temporary purpose. */
+static atomic_t tomoyo_dynamic_memory_size;
+
+/**
+ * tomoyo_alloc - Allocate memory for temporary purpose.
+ *
+ * @size: Size in bytes.
+ *
+ * Returns pointer to allocated memory on success, NULL otherwise.
+ */
+void *tomoyo_alloc(const size_t size)
+{
+	void *p = kzalloc(size, GFP_KERNEL);
+	if (p)
+		atomic_add(ksize(p), &tomoyo_dynamic_memory_size);
+	return p;
+}
+
+/**
+ * tomoyo_free - Release memory allocated by tomoyo_alloc().
+ *
+ * @p: Pointer returned by tomoyo_alloc(). May be NULL.
+ *
+ * Returns nothing.
+ */
+void tomoyo_free(const void *p)
+{
+	if (p) {
+		atomic_sub(ksize(p), &tomoyo_dynamic_memory_size);
+		kfree(p);
+	}
+}
+
+/**
+ * tomoyo_read_memory_counter - Check for memory usage in bytes.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns memory usage.
+ */
+int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head)
+{
+	if (!head->read_eof) {
+		const unsigned int shared
+			= tomoyo_allocated_memory_for_savename;
+		const unsigned int private
+			= tomoyo_allocated_memory_for_elements;
+		const unsigned int dynamic
+			= atomic_read(&tomoyo_dynamic_memory_size);
+		char buffer[64];
+
+		memset(buffer, 0, sizeof(buffer));
+		if (tomoyo_quota_for_savename)
+			snprintf(buffer, sizeof(buffer) - 1,
+				 "   (Quota: %10u)",
+				 tomoyo_quota_for_savename);
+		else
+			buffer[0] = '\0';
+		tomoyo_io_printf(head, "Shared:  %10u%s\n", shared, buffer);
+		if (tomoyo_quota_for_elements)
+			snprintf(buffer, sizeof(buffer) - 1,
+				 "   (Quota: %10u)",
+				 tomoyo_quota_for_elements);
+		else
+			buffer[0] = '\0';
+		tomoyo_io_printf(head, "Private: %10u%s\n", private, buffer);
+		tomoyo_io_printf(head, "Dynamic: %10u\n", dynamic);
+		tomoyo_io_printf(head, "Total:   %10u\n",
+				 shared + private + dynamic);
+		head->read_eof = true;
+	}
+	return 0;
+}
+
+/**
+ * tomoyo_write_memory_quota - Set memory quota.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head)
+{
+	char *data = head->write_buf;
+	unsigned int size;
+
+	if (sscanf(data, "Shared: %u", &size) == 1)
+		tomoyo_quota_for_savename = size;
+	else if (sscanf(data, "Private: %u", &size) == 1)
+		tomoyo_quota_for_elements = size;
+	return 0;
+}

+ 66 - 0
security/tomoyo/realpath.h

@@ -0,0 +1,66 @@
+/*
+ * security/tomoyo/realpath.h
+ *
+ * Get the canonicalized absolute pathnames. The basis for TOMOYO.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/02/01
+ *
+ */
+
+#ifndef _SECURITY_TOMOYO_REALPATH_H
+#define _SECURITY_TOMOYO_REALPATH_H
+
+struct path;
+struct tomoyo_path_info;
+struct tomoyo_io_buffer;
+
+/* Convert binary string to ascii string. */
+int tomoyo_encode(char *buffer, int buflen, const char *str);
+
+/* Returns realpath(3) of the given pathname but ignores chroot'ed root. */
+int tomoyo_realpath_from_path2(struct path *path, char *newname,
+			       int newname_len);
+
+/*
+ * Returns realpath(3) of the given pathname but ignores chroot'ed root.
+ * These functions use tomoyo_alloc(), so the caller must call tomoyo_free()
+ * if these functions didn't return NULL.
+ */
+char *tomoyo_realpath(const char *pathname);
+/*
+ * Same with tomoyo_realpath() except that it doesn't follow the final symlink.
+ */
+char *tomoyo_realpath_nofollow(const char *pathname);
+/* Same with tomoyo_realpath() except that the pathname is already solved. */
+char *tomoyo_realpath_from_path(struct path *path);
+
+/*
+ * Allocate memory for ACL entry.
+ * The RAM is chunked, so NEVER try to kfree() the returned pointer.
+ */
+void *tomoyo_alloc_element(const unsigned int size);
+
+/*
+ * Keep the given name on the RAM.
+ * The RAM is shared, so NEVER try to modify or kfree() the returned name.
+ */
+const struct tomoyo_path_info *tomoyo_save_name(const char *name);
+
+/* Allocate memory for temporary use (e.g. permission checks). */
+void *tomoyo_alloc(const size_t size);
+
+/* Free memory allocated by tomoyo_alloc(). */
+void tomoyo_free(const void *p);
+
+/* Check for memory usage. */
+int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head);
+
+/* Set memory quota. */
+int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head);
+
+/* Initialize realpath related code. */
+void __init tomoyo_realpath_init(void);
+
+#endif /* !defined(_SECURITY_TOMOYO_REALPATH_H) */

+ 294 - 0
security/tomoyo/tomoyo.c

@@ -0,0 +1,294 @@
+/*
+ * security/tomoyo/tomoyo.c
+ *
+ * LSM hooks for TOMOYO Linux.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/02/01
+ *
+ */
+
+#include <linux/security.h>
+#include "common.h"
+#include "tomoyo.h"
+#include "realpath.h"
+
+static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
+			       gfp_t gfp)
+{
+	/*
+	 * Since "struct tomoyo_domain_info *" is a sharable pointer,
+	 * we don't need to duplicate.
+	 */
+	new->security = old->security;
+	return 0;
+}
+
+static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
+{
+	/*
+	 * Do only if this function is called for the first time of an execve
+	 * operation.
+	 */
+	if (bprm->cred_prepared)
+		return 0;
+	/*
+	 * Load policy if /sbin/tomoyo-init exists and /sbin/init is requested
+	 * for the first time.
+	 */
+	if (!tomoyo_policy_loaded)
+		tomoyo_load_policy(bprm->filename);
+	/*
+	 * Tell tomoyo_bprm_check_security() is called for the first time of an
+	 * execve operation.
+	 */
+	bprm->cred->security = NULL;
+	return 0;
+}
+
+static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
+{
+	struct tomoyo_domain_info *domain = bprm->cred->security;
+
+	/*
+	 * Execute permission is checked against pathname passed to do_execve()
+	 * using current domain.
+	 */
+	if (!domain) {
+		struct tomoyo_domain_info *next_domain = NULL;
+		int retval = tomoyo_find_next_domain(bprm, &next_domain);
+
+		if (!retval)
+			bprm->cred->security = next_domain;
+		return retval;
+	}
+	/*
+	 * Read permission is checked against interpreters using next domain.
+	 * '1' is the result of open_to_namei_flags(O_RDONLY).
+	 */
+	return tomoyo_check_open_permission(domain, &bprm->file->f_path, 1);
+}
+
+#ifdef CONFIG_SYSCTL
+
+static int tomoyo_prepend(char **buffer, int *buflen, const char *str)
+{
+	int namelen = strlen(str);
+
+	if (*buflen < namelen)
+		return -ENOMEM;
+	*buflen -= namelen;
+	*buffer -= namelen;
+	memcpy(*buffer, str, namelen);
+	return 0;
+}
+
+/**
+ * tomoyo_sysctl_path - return the realpath of a ctl_table.
+ * @table: pointer to "struct ctl_table".
+ *
+ * Returns realpath(3) of the @table on success.
+ * Returns NULL on failure.
+ *
+ * This function uses tomoyo_alloc(), so the caller must call tomoyo_free()
+ * if this function didn't return NULL.
+ */
+static char *tomoyo_sysctl_path(struct ctl_table *table)
+{
+	int buflen = TOMOYO_MAX_PATHNAME_LEN;
+	char *buf = tomoyo_alloc(buflen);
+	char *end = buf + buflen;
+	int error = -ENOMEM;
+
+	if (!buf)
+		return NULL;
+
+	*--end = '\0';
+	buflen--;
+	while (table) {
+		char num[32];
+		const char *sp = table->procname;
+
+		if (!sp) {
+			memset(num, 0, sizeof(num));
+			snprintf(num, sizeof(num) - 1, "=%d=", table->ctl_name);
+			sp = num;
+		}
+		if (tomoyo_prepend(&end, &buflen, sp) ||
+		    tomoyo_prepend(&end, &buflen, "/"))
+			goto out;
+		table = table->parent;
+	}
+	if (tomoyo_prepend(&end, &buflen, "/proc/sys"))
+		goto out;
+	error = tomoyo_encode(buf, end - buf, end);
+ out:
+	if (!error)
+		return buf;
+	tomoyo_free(buf);
+	return NULL;
+}
+
+static int tomoyo_sysctl(struct ctl_table *table, int op)
+{
+	int error;
+	char *name;
+
+	op &= MAY_READ | MAY_WRITE;
+	if (!op)
+		return 0;
+	name = tomoyo_sysctl_path(table);
+	if (!name)
+		return -ENOMEM;
+	error = tomoyo_check_file_perm(tomoyo_domain(), name, op);
+	tomoyo_free(name);
+	return error;
+}
+#endif
+
+static int tomoyo_path_truncate(struct path *path, loff_t length,
+				unsigned int time_attrs)
+{
+	return tomoyo_check_1path_perm(tomoyo_domain(),
+				       TOMOYO_TYPE_TRUNCATE_ACL,
+				       path);
+}
+
+static int tomoyo_path_unlink(struct path *parent, struct dentry *dentry)
+{
+	struct path path = { parent->mnt, dentry };
+	return tomoyo_check_1path_perm(tomoyo_domain(),
+				       TOMOYO_TYPE_UNLINK_ACL,
+				       &path);
+}
+
+static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry,
+			     int mode)
+{
+	struct path path = { parent->mnt, dentry };
+	return tomoyo_check_1path_perm(tomoyo_domain(),
+				       TOMOYO_TYPE_MKDIR_ACL,
+				       &path);
+}
+
+static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry)
+{
+	struct path path = { parent->mnt, dentry };
+	return tomoyo_check_1path_perm(tomoyo_domain(),
+				       TOMOYO_TYPE_RMDIR_ACL,
+				       &path);
+}
+
+static int tomoyo_path_symlink(struct path *parent, struct dentry *dentry,
+			       const char *old_name)
+{
+	struct path path = { parent->mnt, dentry };
+	return tomoyo_check_1path_perm(tomoyo_domain(),
+				       TOMOYO_TYPE_SYMLINK_ACL,
+				       &path);
+}
+
+static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry,
+			     int mode, unsigned int dev)
+{
+	struct path path = { parent->mnt, dentry };
+	int type = TOMOYO_TYPE_CREATE_ACL;
+
+	switch (mode & S_IFMT) {
+	case S_IFCHR:
+		type = TOMOYO_TYPE_MKCHAR_ACL;
+		break;
+	case S_IFBLK:
+		type = TOMOYO_TYPE_MKBLOCK_ACL;
+		break;
+	case S_IFIFO:
+		type = TOMOYO_TYPE_MKFIFO_ACL;
+		break;
+	case S_IFSOCK:
+		type = TOMOYO_TYPE_MKSOCK_ACL;
+		break;
+	}
+	return tomoyo_check_1path_perm(tomoyo_domain(),
+				       type, &path);
+}
+
+static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir,
+			    struct dentry *new_dentry)
+{
+	struct path path1 = { new_dir->mnt, old_dentry };
+	struct path path2 = { new_dir->mnt, new_dentry };
+	return tomoyo_check_2path_perm(tomoyo_domain(),
+				       TOMOYO_TYPE_LINK_ACL,
+				       &path1, &path2);
+}
+
+static int tomoyo_path_rename(struct path *old_parent,
+			      struct dentry *old_dentry,
+			      struct path *new_parent,
+			      struct dentry *new_dentry)
+{
+	struct path path1 = { old_parent->mnt, old_dentry };
+	struct path path2 = { new_parent->mnt, new_dentry };
+	return tomoyo_check_2path_perm(tomoyo_domain(),
+				       TOMOYO_TYPE_RENAME_ACL,
+				       &path1, &path2);
+}
+
+static int tomoyo_file_fcntl(struct file *file, unsigned int cmd,
+			     unsigned long arg)
+{
+	if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND))
+		return tomoyo_check_rewrite_permission(tomoyo_domain(), file);
+	return 0;
+}
+
+static int tomoyo_dentry_open(struct file *f, const struct cred *cred)
+{
+	int flags = f->f_flags;
+
+	if ((flags + 1) & O_ACCMODE)
+		flags++;
+	flags |= f->f_flags & (O_APPEND | O_TRUNC);
+	/* Don't check read permission here if called from do_execve(). */
+	if (current->in_execve)
+		return 0;
+	return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, flags);
+}
+
+static struct security_operations tomoyo_security_ops = {
+	.name                = "tomoyo",
+	.cred_prepare        = tomoyo_cred_prepare,
+	.bprm_set_creds      = tomoyo_bprm_set_creds,
+	.bprm_check_security = tomoyo_bprm_check_security,
+#ifdef CONFIG_SYSCTL
+	.sysctl              = tomoyo_sysctl,
+#endif
+	.file_fcntl          = tomoyo_file_fcntl,
+	.dentry_open         = tomoyo_dentry_open,
+	.path_truncate       = tomoyo_path_truncate,
+	.path_unlink         = tomoyo_path_unlink,
+	.path_mkdir          = tomoyo_path_mkdir,
+	.path_rmdir          = tomoyo_path_rmdir,
+	.path_symlink        = tomoyo_path_symlink,
+	.path_mknod          = tomoyo_path_mknod,
+	.path_link           = tomoyo_path_link,
+	.path_rename         = tomoyo_path_rename,
+};
+
+static int __init tomoyo_init(void)
+{
+	struct cred *cred = (struct cred *) current_cred();
+
+	if (!security_module_enable(&tomoyo_security_ops))
+		return 0;
+	/* register ourselves with the security framework */
+	if (register_security(&tomoyo_security_ops))
+		panic("Failure registering TOMOYO Linux");
+	printk(KERN_INFO "TOMOYO Linux initialized\n");
+	cred->security = &tomoyo_kernel_domain;
+	tomoyo_realpath_init();
+	return 0;
+}
+
+security_initcall(tomoyo_init);

+ 106 - 0
security/tomoyo/tomoyo.h

@@ -0,0 +1,106 @@
+/*
+ * security/tomoyo/tomoyo.h
+ *
+ * Implementation of the Domain-Based Mandatory Access Control.
+ *
+ * Copyright (C) 2005-2009  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2009/02/01
+ *
+ */
+
+#ifndef _SECURITY_TOMOYO_TOMOYO_H
+#define _SECURITY_TOMOYO_TOMOYO_H
+
+struct tomoyo_path_info;
+struct path;
+struct inode;
+struct linux_binprm;
+struct pt_regs;
+struct tomoyo_page_buffer;
+
+int tomoyo_check_file_perm(struct tomoyo_domain_info *domain,
+			   const char *filename, const u8 perm);
+int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
+			   const struct tomoyo_path_info *filename,
+			   struct tomoyo_page_buffer *buf);
+int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
+				 struct path *path, const int flag);
+int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
+			    const u8 operation, struct path *path);
+int tomoyo_check_2path_perm(struct tomoyo_domain_info *domain,
+			    const u8 operation, struct path *path1,
+			    struct path *path2);
+int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
+				    struct file *filp);
+int tomoyo_find_next_domain(struct linux_binprm *bprm,
+			    struct tomoyo_domain_info **next_domain);
+
+/* Index numbers for Access Controls. */
+
+#define TOMOYO_TYPE_SINGLE_PATH_ACL                 0
+#define TOMOYO_TYPE_DOUBLE_PATH_ACL                 1
+
+/* Index numbers for File Controls. */
+
+/*
+ * TYPE_READ_WRITE_ACL is special. TYPE_READ_WRITE_ACL is automatically set
+ * if both TYPE_READ_ACL and TYPE_WRITE_ACL are set. Both TYPE_READ_ACL and
+ * TYPE_WRITE_ACL are automatically set if TYPE_READ_WRITE_ACL is set.
+ * TYPE_READ_WRITE_ACL is automatically cleared if either TYPE_READ_ACL or
+ * TYPE_WRITE_ACL is cleared. Both TYPE_READ_ACL and TYPE_WRITE_ACL are
+ * automatically cleared if TYPE_READ_WRITE_ACL is cleared.
+ */
+
+#define TOMOYO_TYPE_READ_WRITE_ACL    0
+#define TOMOYO_TYPE_EXECUTE_ACL       1
+#define TOMOYO_TYPE_READ_ACL          2
+#define TOMOYO_TYPE_WRITE_ACL         3
+#define TOMOYO_TYPE_CREATE_ACL        4
+#define TOMOYO_TYPE_UNLINK_ACL        5
+#define TOMOYO_TYPE_MKDIR_ACL         6
+#define TOMOYO_TYPE_RMDIR_ACL         7
+#define TOMOYO_TYPE_MKFIFO_ACL        8
+#define TOMOYO_TYPE_MKSOCK_ACL        9
+#define TOMOYO_TYPE_MKBLOCK_ACL      10
+#define TOMOYO_TYPE_MKCHAR_ACL       11
+#define TOMOYO_TYPE_TRUNCATE_ACL     12
+#define TOMOYO_TYPE_SYMLINK_ACL      13
+#define TOMOYO_TYPE_REWRITE_ACL      14
+#define TOMOYO_MAX_SINGLE_PATH_OPERATION 15
+
+#define TOMOYO_TYPE_LINK_ACL         0
+#define TOMOYO_TYPE_RENAME_ACL       1
+#define TOMOYO_MAX_DOUBLE_PATH_OPERATION 2
+
+#define TOMOYO_DOMAINPOLICY          0
+#define TOMOYO_EXCEPTIONPOLICY       1
+#define TOMOYO_DOMAIN_STATUS         2
+#define TOMOYO_PROCESS_STATUS        3
+#define TOMOYO_MEMINFO               4
+#define TOMOYO_SELFDOMAIN            5
+#define TOMOYO_VERSION               6
+#define TOMOYO_PROFILE               7
+#define TOMOYO_MANAGER               8
+
+extern struct tomoyo_domain_info tomoyo_kernel_domain;
+
+static inline struct tomoyo_domain_info *tomoyo_domain(void)
+{
+	return current_cred()->security;
+}
+
+/* Caller holds tasklist_lock spinlock. */
+static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
+							    *task)
+{
+	/***** CRITICAL SECTION START *****/
+	const struct cred *cred = get_task_cred(task);
+	struct tomoyo_domain_info *domain = cred->security;
+
+	put_cred(cred);
+	return domain;
+	/***** CRITICAL SECTION END *****/
+}
+
+#endif /* !defined(_SECURITY_TOMOYO_TOMOYO_H) */