瀏覽代碼

Merge branch 'next' of git://selinuxproject.org/~jmorris/linux-security

* 'next' of git://selinuxproject.org/~jmorris/linux-security: (95 commits)
  TOMOYO: Fix incomplete read after seek.
  Smack: allow to access /smack/access as normal user
  TOMOYO: Fix unused kernel config option.
  Smack: fix: invalid length set for the result of /smack/access
  Smack: compilation fix
  Smack: fix for /smack/access output, use string instead of byte
  Smack: domain transition protections (v3)
  Smack: Provide information for UDS getsockopt(SO_PEERCRED)
  Smack: Clean up comments
  Smack: Repair processing of fcntl
  Smack: Rule list lookup performance
  Smack: check permissions from user space (v2)
  TOMOYO: Fix quota and garbage collector.
  TOMOYO: Remove redundant tasklist_lock.
  TOMOYO: Fix domain transition failure warning.
  TOMOYO: Remove tomoyo_policy_memory_lock spinlock.
  TOMOYO: Simplify garbage collector.
  TOMOYO: Fix make namespacecheck warnings.
  target: check hex2bin result
  encrypted-keys: check hex2bin result
  ...
Linus Torvalds 13 年之前
父節點
當前提交
36b8d186e6
共有 99 個文件被更改,包括 4701 次插入1432 次删除
  1. 23 0
      Documentation/ABI/testing/evm
  2. 6 0
      Documentation/kernel-parameters.txt
  3. 6 1
      MAINTAINERS
  4. 3 0
      drivers/char/tpm/tpm.c
  5. 9 3
      drivers/target/target_core_fabric_lib.c
  6. 4 1
      fs/attr.c
  7. 25 25
      fs/btrfs/xattr.c
  8. 18 22
      fs/cifs/xattr.c
  9. 18 16
      fs/ext2/xattr_security.c
  10. 20 16
      fs/ext3/xattr_security.c
  11. 20 16
      fs/ext4/xattr_security.c
  12. 18 20
      fs/gfs2/inode.c
  13. 19 16
      fs/jffs2/security.c
  14. 28 29
      fs/jfs/xattr.c
  15. 24 14
      fs/ocfs2/xattr.c
  16. 2 2
      fs/reiserfs/xattr_security.c
  17. 62 1
      fs/xattr.c
  18. 20 19
      fs/xfs/xfs_iops.c
  19. 100 0
      include/linux/evm.h
  20. 0 13
      include/linux/ima.h
  21. 39 0
      include/linux/integrity.h
  22. 1 1
      include/linux/kernel.h
  23. 25 7
      include/linux/security.h
  24. 18 1
      include/linux/xattr.h
  25. 16 2
      kernel/cred.c
  26. 11 4
      lib/hexdump.c
  27. 2 2
      mm/shmem.c
  28. 4 2
      security/Kconfig
  29. 2 2
      security/Makefile
  30. 1 1
      security/apparmor/apparmorfs.c
  31. 1 0
      security/apparmor/ipc.c
  32. 1 0
      security/apparmor/lib.c
  33. 6 6
      security/apparmor/policy_unpack.c
  34. 1 0
      security/apparmor/procattr.c
  35. 10 6
      security/commoncap.c
  36. 7 0
      security/integrity/Kconfig
  37. 12 0
      security/integrity/Makefile
  38. 13 0
      security/integrity/evm/Kconfig
  39. 7 0
      security/integrity/evm/Makefile
  40. 38 0
      security/integrity/evm/evm.h
  41. 216 0
      security/integrity/evm/evm_crypto.c
  42. 384 0
      security/integrity/evm/evm_main.c
  43. 26 0
      security/integrity/evm/evm_posix_acl.c
  44. 108 0
      security/integrity/evm/evm_secfs.c
  45. 172 0
      security/integrity/iint.c
  46. 1 0
      security/integrity/ima/Kconfig
  47. 1 1
      security/integrity/ima/Makefile
  48. 9 21
      security/integrity/ima/ima.h
  49. 4 3
      security/integrity/ima/ima_api.c
  50. 1 1
      security/integrity/ima/ima_fs.c
  51. 0 169
      security/integrity/ima/ima_iint.c
  52. 7 6
      security/integrity/ima/ima_main.c
  53. 50 0
      security/integrity/integrity.h
  54. 1 1
      security/keys/Makefile
  55. 6 0
      security/keys/encrypted-keys/Makefile
  56. 0 0
      security/keys/encrypted-keys/ecryptfs_format.c
  57. 0 0
      security/keys/encrypted-keys/ecryptfs_format.h
  58. 19 30
      security/keys/encrypted-keys/encrypted.c
  59. 11 0
      security/keys/encrypted-keys/encrypted.h
  60. 45 0
      security/keys/encrypted-keys/masterkey_trusted.c
  61. 276 110
      security/keys/gc.c
  62. 4 0
      security/keys/internal.h
  63. 5 116
      security/keys/key.c
  64. 1 2
      security/keys/keyring.c
  65. 13 3
      security/keys/process_keys.c
  66. 15 4
      security/keys/trusted.c
  67. 65 11
      security/security.c
  68. 1 0
      security/selinux/exports.c
  69. 2 11
      security/selinux/hooks.c
  70. 6 0
      security/selinux/include/avc_ss.h
  71. 8 0
      security/selinux/include/security.h
  72. 2 0
      security/selinux/netlink.c
  73. 1 0
      security/selinux/nlmsgtab.c
  74. 1 4
      security/selinux/selinuxfs.c
  75. 1 1
      security/selinux/ss/conditional.c
  76. 1 0
      security/selinux/ss/conditional.h
  77. 0 2
      security/selinux/ss/policydb.c
  78. 0 3
      security/selinux/ss/services.c
  79. 15 9
      security/smack/smack.h
  80. 72 62
      security/smack/smack_access.c
  81. 179 87
      security/smack/smack_lsm.c
  82. 204 73
      security/smack/smackfs.c
  83. 2 0
      security/tomoyo/Kconfig
  84. 2 2
      security/tomoyo/Makefile
  85. 6 1
      security/tomoyo/audit.c
  86. 185 43
      security/tomoyo/common.c
  87. 178 11
      security/tomoyo/common.h
  88. 65 6
      security/tomoyo/condition.c
  89. 172 37
      security/tomoyo/domain.c
  90. 122 0
      security/tomoyo/environ.c
  91. 34 8
      security/tomoyo/file.c
  92. 235 305
      security/tomoyo/gc.c
  93. 60 1
      security/tomoyo/group.c
  94. 12 27
      security/tomoyo/memory.c
  95. 771 0
      security/tomoyo/network.c
  96. 25 7
      security/tomoyo/realpath.c
  97. 121 2
      security/tomoyo/securityfs_if.c
  98. 62 0
      security/tomoyo/tomoyo.c
  99. 76 4
      security/tomoyo/util.c

+ 23 - 0
Documentation/ABI/testing/evm

@@ -0,0 +1,23 @@
+What:		security/evm
+Date:		March 2011
+Contact:	Mimi Zohar <zohar@us.ibm.com>
+Description:
+		EVM protects a file's security extended attributes(xattrs)
+		against integrity attacks. The initial method maintains an
+		HMAC-sha1 value across the extended attributes, storing the
+		value as the extended attribute 'security.evm'.
+
+		EVM depends on the Kernel Key Retention System to provide it
+		with a trusted/encrypted key for the HMAC-sha1 operation.
+		The key is loaded onto the root's keyring using keyctl.  Until
+		EVM receives notification that the key has been successfully
+		loaded onto the keyring (echo 1 > <securityfs>/evm), EVM
+		can not create or validate the 'security.evm' xattr, but
+		returns INTEGRITY_UNKNOWN.  Loading the key and signaling EVM
+		should be done as early as possible.  Normally this is done
+		in the initramfs, which has already been measured as part
+		of the trusted boot.  For more information on creating and
+		loading existing trusted/encrypted keys, refer to:
+		Documentation/keys-trusted-encrypted.txt.  (A sample dracut
+		patch, which loads the trusted/encrypted key and enables
+		EVM, is available from http://linux-ima.sourceforge.net/#EVM.)

+ 6 - 0
Documentation/kernel-parameters.txt

@@ -49,6 +49,7 @@ parameter is applicable:
 	EDD	BIOS Enhanced Disk Drive Services (EDD) is enabled
 	EDD	BIOS Enhanced Disk Drive Services (EDD) is enabled
 	EFI	EFI Partitioning (GPT) is enabled
 	EFI	EFI Partitioning (GPT) is enabled
 	EIDE	EIDE/ATAPI support is enabled.
 	EIDE	EIDE/ATAPI support is enabled.
+	EVM	Extended Verification Module
 	FB	The frame buffer device is enabled.
 	FB	The frame buffer device is enabled.
 	FTRACE	Function tracing enabled.
 	FTRACE	Function tracing enabled.
 	GCOV	GCOV profiling is enabled.
 	GCOV	GCOV profiling is enabled.
@@ -760,6 +761,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
 			This option is obsoleted by the "netdev=" option, which
 			This option is obsoleted by the "netdev=" option, which
 			has equivalent usage. See its documentation for details.
 			has equivalent usage. See its documentation for details.
 
 
+	evm=		[EVM]
+			Format: { "fix" }
+			Permit 'security.evm' to be updated regardless of
+			current integrity status.
+
 	failslab=
 	failslab=
 	fail_page_alloc=
 	fail_page_alloc=
 	fail_make_request=[KNL]
 	fail_make_request=[KNL]

+ 6 - 1
MAINTAINERS

@@ -2552,6 +2552,11 @@ S:	Maintained
 F:	Documentation/filesystems/ext4.txt
 F:	Documentation/filesystems/ext4.txt
 F:	fs/ext4/
 F:	fs/ext4/
 
 
+Extended Verification Module (EVM)
+M:	Mimi Zohar <zohar@us.ibm.com>
+S:	Supported
+F:	security/integrity/evm/
+
 F71805F HARDWARE MONITORING DRIVER
 F71805F HARDWARE MONITORING DRIVER
 M:	Jean Delvare <khali@linux-fr.org>
 M:	Jean Delvare <khali@linux-fr.org>
 L:	lm-sensors@lm-sensors.org
 L:	lm-sensors@lm-sensors.org
@@ -6447,7 +6452,7 @@ L:	tomoyo-users-en@lists.sourceforge.jp (subscribers-only, for users in English)
 L:	tomoyo-dev@lists.sourceforge.jp (subscribers-only, for developers in Japanese)
 L:	tomoyo-dev@lists.sourceforge.jp (subscribers-only, for developers in Japanese)
 L:	tomoyo-users@lists.sourceforge.jp (subscribers-only, for users in Japanese)
 L:	tomoyo-users@lists.sourceforge.jp (subscribers-only, for users in Japanese)
 W:	http://tomoyo.sourceforge.jp/
 W:	http://tomoyo.sourceforge.jp/
-T:	quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.4.x/tomoyo-lsm/patches/
+T:	quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.5.x/tomoyo-lsm/patches/
 S:	Maintained
 S:	Maintained
 F:	security/tomoyo/
 F:	security/tomoyo/
 
 

+ 3 - 0
drivers/char/tpm/tpm.c

@@ -966,6 +966,9 @@ ssize_t tpm_show_durations(struct device *dev, struct device_attribute *attr,
 {
 {
 	struct tpm_chip *chip = dev_get_drvdata(dev);
 	struct tpm_chip *chip = dev_get_drvdata(dev);
 
 
+	if (chip->vendor.duration[TPM_LONG] == 0)
+		return 0;
+
 	return sprintf(buf, "%d %d %d [%s]\n",
 	return sprintf(buf, "%d %d %d [%s]\n",
 		       jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]),
 		       jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]),
 		       jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]),
 		       jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]),

+ 9 - 3
drivers/target/target_core_fabric_lib.c

@@ -63,6 +63,7 @@ u32 sas_get_pr_transport_id(
 	unsigned char *buf)
 	unsigned char *buf)
 {
 {
 	unsigned char *ptr;
 	unsigned char *ptr;
+	int ret;
 
 
 	/*
 	/*
 	 * Set PROTOCOL IDENTIFIER to 6h for SAS
 	 * Set PROTOCOL IDENTIFIER to 6h for SAS
@@ -74,7 +75,9 @@ u32 sas_get_pr_transport_id(
 	 */
 	 */
 	ptr = &se_nacl->initiatorname[4]; /* Skip over 'naa. prefix */
 	ptr = &se_nacl->initiatorname[4]; /* Skip over 'naa. prefix */
 
 
-	hex2bin(&buf[4], ptr, 8);
+	ret = hex2bin(&buf[4], ptr, 8);
+	if (ret < 0)
+		pr_debug("sas transport_id: invalid hex string\n");
 
 
 	/*
 	/*
 	 * The SAS Transport ID is a hardcoded 24-byte length
 	 * The SAS Transport ID is a hardcoded 24-byte length
@@ -156,8 +159,9 @@ u32 fc_get_pr_transport_id(
 	unsigned char *buf)
 	unsigned char *buf)
 {
 {
 	unsigned char *ptr;
 	unsigned char *ptr;
-	int i;
+	int i, ret;
 	u32 off = 8;
 	u32 off = 8;
+
 	/*
 	/*
 	 * PROTOCOL IDENTIFIER is 0h for FCP-2
 	 * PROTOCOL IDENTIFIER is 0h for FCP-2
 	 *
 	 *
@@ -174,7 +178,9 @@ u32 fc_get_pr_transport_id(
 			i++;
 			i++;
 			continue;
 			continue;
 		}
 		}
-		hex2bin(&buf[off++], &ptr[i], 1);
+		ret = hex2bin(&buf[off++], &ptr[i], 1);
+		if (ret < 0)
+			pr_debug("fc transport_id: invalid hex string\n");
 		i += 2;
 		i += 2;
 	}
 	}
 	/*
 	/*

+ 4 - 1
fs/attr.c

@@ -13,6 +13,7 @@
 #include <linux/fsnotify.h>
 #include <linux/fsnotify.h>
 #include <linux/fcntl.h>
 #include <linux/fcntl.h>
 #include <linux/security.h>
 #include <linux/security.h>
+#include <linux/evm.h>
 
 
 /**
 /**
  * inode_change_ok - check if attribute changes to an inode are allowed
  * inode_change_ok - check if attribute changes to an inode are allowed
@@ -237,8 +238,10 @@ int notify_change(struct dentry * dentry, struct iattr * attr)
 	else
 	else
 		error = simple_setattr(dentry, attr);
 		error = simple_setattr(dentry, attr);
 
 
-	if (!error)
+	if (!error) {
 		fsnotify_change(dentry, ia_valid);
 		fsnotify_change(dentry, ia_valid);
+		evm_inode_post_setattr(dentry, ia_valid);
+	}
 
 
 	return error;
 	return error;
 }
 }

+ 25 - 25
fs/btrfs/xattr.c

@@ -383,36 +383,36 @@ int btrfs_removexattr(struct dentry *dentry, const char *name)
 				XATTR_REPLACE);
 				XATTR_REPLACE);
 }
 }
 
 
-int btrfs_xattr_security_init(struct btrfs_trans_handle *trans,
-			      struct inode *inode, struct inode *dir,
-			      const struct qstr *qstr)
+int btrfs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+		     void *fs_info)
 {
 {
-	int err;
-	size_t len;
-	void *value;
-	char *suffix;
+	const struct xattr *xattr;
+	struct btrfs_trans_handle *trans = fs_info;
 	char *name;
 	char *name;
+	int err = 0;
 
 
-	err = security_inode_init_security(inode, dir, qstr, &suffix, &value,
-					   &len);
-	if (err) {
-		if (err == -EOPNOTSUPP)
-			return 0;
-		return err;
-	}
-
-	name = kmalloc(XATTR_SECURITY_PREFIX_LEN + strlen(suffix) + 1,
-		       GFP_NOFS);
-	if (!name) {
-		err = -ENOMEM;
-	} else {
+	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+		name = kmalloc(XATTR_SECURITY_PREFIX_LEN +
+			       strlen(xattr->name) + 1, GFP_NOFS);
+		if (!name) {
+			err = -ENOMEM;
+			break;
+		}
 		strcpy(name, XATTR_SECURITY_PREFIX);
 		strcpy(name, XATTR_SECURITY_PREFIX);
-		strcpy(name + XATTR_SECURITY_PREFIX_LEN, suffix);
-		err = __btrfs_setxattr(trans, inode, name, value, len, 0);
+		strcpy(name + XATTR_SECURITY_PREFIX_LEN, xattr->name);
+		err = __btrfs_setxattr(trans, inode, name,
+				       xattr->value, xattr->value_len, 0);
 		kfree(name);
 		kfree(name);
+		if (err < 0)
+			break;
 	}
 	}
-
-	kfree(suffix);
-	kfree(value);
 	return err;
 	return err;
 }
 }
+
+int btrfs_xattr_security_init(struct btrfs_trans_handle *trans,
+			      struct inode *inode, struct inode *dir,
+			      const struct qstr *qstr)
+{
+	return security_inode_init_security(inode, dir, qstr,
+					    &btrfs_initxattrs, trans);
+}

+ 18 - 22
fs/cifs/xattr.c

@@ -22,6 +22,7 @@
 #include <linux/fs.h>
 #include <linux/fs.h>
 #include <linux/posix_acl_xattr.h>
 #include <linux/posix_acl_xattr.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
+#include <linux/xattr.h>
 #include "cifsfs.h"
 #include "cifsfs.h"
 #include "cifspdu.h"
 #include "cifspdu.h"
 #include "cifsglob.h"
 #include "cifsglob.h"
@@ -31,16 +32,8 @@
 #define MAX_EA_VALUE_SIZE 65535
 #define MAX_EA_VALUE_SIZE 65535
 #define CIFS_XATTR_DOS_ATTRIB "user.DosAttrib"
 #define CIFS_XATTR_DOS_ATTRIB "user.DosAttrib"
 #define CIFS_XATTR_CIFS_ACL "system.cifs_acl"
 #define CIFS_XATTR_CIFS_ACL "system.cifs_acl"
-#define CIFS_XATTR_USER_PREFIX "user."
-#define CIFS_XATTR_SYSTEM_PREFIX "system."
-#define CIFS_XATTR_OS2_PREFIX "os2."
-#define CIFS_XATTR_SECURITY_PREFIX "security."
-#define CIFS_XATTR_TRUSTED_PREFIX "trusted."
-#define XATTR_TRUSTED_PREFIX_LEN  8
-#define XATTR_SECURITY_PREFIX_LEN 9
-/* BB need to add server (Samba e.g) support for security and trusted prefix */
-
 
 
+/* BB need to add server (Samba e.g) support for security and trusted prefix */
 
 
 int cifs_removexattr(struct dentry *direntry, const char *ea_name)
 int cifs_removexattr(struct dentry *direntry, const char *ea_name)
 {
 {
@@ -76,8 +69,8 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name)
 	}
 	}
 	if (ea_name == NULL) {
 	if (ea_name == NULL) {
 		cFYI(1, "Null xattr names not supported");
 		cFYI(1, "Null xattr names not supported");
-	} else if (strncmp(ea_name, CIFS_XATTR_USER_PREFIX, 5)
-		&& (strncmp(ea_name, CIFS_XATTR_OS2_PREFIX, 4))) {
+	} else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)
+		&& (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN))) {
 		cFYI(1,
 		cFYI(1,
 		     "illegal xattr request %s (only user namespace supported)",
 		     "illegal xattr request %s (only user namespace supported)",
 		     ea_name);
 		     ea_name);
@@ -88,7 +81,7 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name)
 		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
 		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
 			goto remove_ea_exit;
 			goto remove_ea_exit;
 
 
-		ea_name += 5; /* skip past user. prefix */
+		ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */
 		rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, NULL,
 		rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, NULL,
 			(__u16)0, cifs_sb->local_nls,
 			(__u16)0, cifs_sb->local_nls,
 			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
 			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
@@ -149,21 +142,23 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,
 
 
 	if (ea_name == NULL) {
 	if (ea_name == NULL) {
 		cFYI(1, "Null xattr names not supported");
 		cFYI(1, "Null xattr names not supported");
-	} else if (strncmp(ea_name, CIFS_XATTR_USER_PREFIX, 5) == 0) {
+	} else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)
+		   == 0) {
 		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
 		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
 			goto set_ea_exit;
 			goto set_ea_exit;
 		if (strncmp(ea_name, CIFS_XATTR_DOS_ATTRIB, 14) == 0)
 		if (strncmp(ea_name, CIFS_XATTR_DOS_ATTRIB, 14) == 0)
 			cFYI(1, "attempt to set cifs inode metadata");
 			cFYI(1, "attempt to set cifs inode metadata");
 
 
-		ea_name += 5; /* skip past user. prefix */
+		ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */
 		rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, ea_value,
 		rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, ea_value,
 			(__u16)value_size, cifs_sb->local_nls,
 			(__u16)value_size, cifs_sb->local_nls,
 			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
 			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
-	} else if (strncmp(ea_name, CIFS_XATTR_OS2_PREFIX, 4) == 0) {
+	} else if (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN)
+		   == 0) {
 		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
 		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
 			goto set_ea_exit;
 			goto set_ea_exit;
 
 
-		ea_name += 4; /* skip past os2. prefix */
+		ea_name += XATTR_OS2_PREFIX_LEN; /* skip past os2. prefix */
 		rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, ea_value,
 		rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, ea_value,
 			(__u16)value_size, cifs_sb->local_nls,
 			(__u16)value_size, cifs_sb->local_nls,
 			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
 			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
@@ -269,7 +264,8 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
 	/* return alt name if available as pseudo attr */
 	/* return alt name if available as pseudo attr */
 	if (ea_name == NULL) {
 	if (ea_name == NULL) {
 		cFYI(1, "Null xattr names not supported");
 		cFYI(1, "Null xattr names not supported");
-	} else if (strncmp(ea_name, CIFS_XATTR_USER_PREFIX, 5) == 0) {
+	} else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)
+		   == 0) {
 		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
 		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
 			goto get_ea_exit;
 			goto get_ea_exit;
 
 
@@ -277,15 +273,15 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
 			cFYI(1, "attempt to query cifs inode metadata");
 			cFYI(1, "attempt to query cifs inode metadata");
 			/* revalidate/getattr then populate from inode */
 			/* revalidate/getattr then populate from inode */
 		} /* BB add else when above is implemented */
 		} /* BB add else when above is implemented */
-		ea_name += 5; /* skip past user. prefix */
+		ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */
 		rc = CIFSSMBQAllEAs(xid, pTcon, full_path, ea_name, ea_value,
 		rc = CIFSSMBQAllEAs(xid, pTcon, full_path, ea_name, ea_value,
 			buf_size, cifs_sb->local_nls,
 			buf_size, cifs_sb->local_nls,
 			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
 			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
-	} else if (strncmp(ea_name, CIFS_XATTR_OS2_PREFIX, 4) == 0) {
+	} else if (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) == 0) {
 		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
 		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
 			goto get_ea_exit;
 			goto get_ea_exit;
 
 
-		ea_name += 4; /* skip past os2. prefix */
+		ea_name += XATTR_OS2_PREFIX_LEN; /* skip past os2. prefix */
 		rc = CIFSSMBQAllEAs(xid, pTcon, full_path, ea_name, ea_value,
 		rc = CIFSSMBQAllEAs(xid, pTcon, full_path, ea_name, ea_value,
 			buf_size, cifs_sb->local_nls,
 			buf_size, cifs_sb->local_nls,
 			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
 			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
@@ -339,10 +335,10 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
 		cFYI(1, "Query CIFS ACL not supported yet");
 		cFYI(1, "Query CIFS ACL not supported yet");
 #endif /* CONFIG_CIFS_ACL */
 #endif /* CONFIG_CIFS_ACL */
 	} else if (strncmp(ea_name,
 	} else if (strncmp(ea_name,
-		  CIFS_XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) == 0) {
+		  XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) == 0) {
 		cFYI(1, "Trusted xattr namespace not supported yet");
 		cFYI(1, "Trusted xattr namespace not supported yet");
 	} else if (strncmp(ea_name,
 	} else if (strncmp(ea_name,
-		  CIFS_XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) == 0) {
+		  XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) == 0) {
 		cFYI(1, "Security xattr namespace not supported yet");
 		cFYI(1, "Security xattr namespace not supported yet");
 	} else
 	} else
 		cFYI(1,
 		cFYI(1,

+ 18 - 16
fs/ext2/xattr_security.c

@@ -46,28 +46,30 @@ ext2_xattr_security_set(struct dentry *dentry, const char *name,
 			      value, size, flags);
 			      value, size, flags);
 }
 }
 
 
-int
-ext2_init_security(struct inode *inode, struct inode *dir,
-		   const struct qstr *qstr)
+int ext2_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+		    void *fs_info)
 {
 {
-	int err;
-	size_t len;
-	void *value;
-	char *name;
+	const struct xattr *xattr;
+	int err = 0;
 
 
-	err = security_inode_init_security(inode, dir, qstr, &name, &value, &len);
-	if (err) {
-		if (err == -EOPNOTSUPP)
-			return 0;
-		return err;
+	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+		err = ext2_xattr_set(inode, EXT2_XATTR_INDEX_SECURITY,
+				     xattr->name, xattr->value,
+				     xattr->value_len, 0);
+		if (err < 0)
+			break;
 	}
 	}
-	err = ext2_xattr_set(inode, EXT2_XATTR_INDEX_SECURITY,
-			     name, value, len, 0);
-	kfree(name);
-	kfree(value);
 	return err;
 	return err;
 }
 }
 
 
+int
+ext2_init_security(struct inode *inode, struct inode *dir,
+		   const struct qstr *qstr)
+{
+	return security_inode_init_security(inode, dir, qstr,
+					    &ext2_initxattrs, NULL);
+}
+
 const struct xattr_handler ext2_xattr_security_handler = {
 const struct xattr_handler ext2_xattr_security_handler = {
 	.prefix	= XATTR_SECURITY_PREFIX,
 	.prefix	= XATTR_SECURITY_PREFIX,
 	.list	= ext2_xattr_security_list,
 	.list	= ext2_xattr_security_list,

+ 20 - 16
fs/ext3/xattr_security.c

@@ -48,28 +48,32 @@ ext3_xattr_security_set(struct dentry *dentry, const char *name,
 			      name, value, size, flags);
 			      name, value, size, flags);
 }
 }
 
 
-int
-ext3_init_security(handle_t *handle, struct inode *inode, struct inode *dir,
-		   const struct qstr *qstr)
+int ext3_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+		    void *fs_info)
 {
 {
-	int err;
-	size_t len;
-	void *value;
-	char *name;
+	const struct xattr *xattr;
+	handle_t *handle = fs_info;
+	int err = 0;
 
 
-	err = security_inode_init_security(inode, dir, qstr, &name, &value, &len);
-	if (err) {
-		if (err == -EOPNOTSUPP)
-			return 0;
-		return err;
+	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+		err = ext3_xattr_set_handle(handle, inode,
+					    EXT3_XATTR_INDEX_SECURITY,
+					    xattr->name, xattr->value,
+					    xattr->value_len, 0);
+		if (err < 0)
+			break;
 	}
 	}
-	err = ext3_xattr_set_handle(handle, inode, EXT3_XATTR_INDEX_SECURITY,
-				    name, value, len, 0);
-	kfree(name);
-	kfree(value);
 	return err;
 	return err;
 }
 }
 
 
+int
+ext3_init_security(handle_t *handle, struct inode *inode, struct inode *dir,
+		   const struct qstr *qstr)
+{
+	return security_inode_init_security(inode, dir, qstr,
+					    &ext3_initxattrs, handle);
+}
+
 const struct xattr_handler ext3_xattr_security_handler = {
 const struct xattr_handler ext3_xattr_security_handler = {
 	.prefix	= XATTR_SECURITY_PREFIX,
 	.prefix	= XATTR_SECURITY_PREFIX,
 	.list	= ext3_xattr_security_list,
 	.list	= ext3_xattr_security_list,

+ 20 - 16
fs/ext4/xattr_security.c

@@ -48,28 +48,32 @@ ext4_xattr_security_set(struct dentry *dentry, const char *name,
 			      name, value, size, flags);
 			      name, value, size, flags);
 }
 }
 
 
-int
-ext4_init_security(handle_t *handle, struct inode *inode, struct inode *dir,
-		   const struct qstr *qstr)
+int ext4_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+		    void *fs_info)
 {
 {
-	int err;
-	size_t len;
-	void *value;
-	char *name;
+	const struct xattr *xattr;
+	handle_t *handle = fs_info;
+	int err = 0;
 
 
-	err = security_inode_init_security(inode, dir, qstr, &name, &value, &len);
-	if (err) {
-		if (err == -EOPNOTSUPP)
-			return 0;
-		return err;
+	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+		err = ext4_xattr_set_handle(handle, inode,
+					    EXT4_XATTR_INDEX_SECURITY,
+					    xattr->name, xattr->value,
+					    xattr->value_len, 0);
+		if (err < 0)
+			break;
 	}
 	}
-	err = ext4_xattr_set_handle(handle, inode, EXT4_XATTR_INDEX_SECURITY,
-				    name, value, len, 0);
-	kfree(name);
-	kfree(value);
 	return err;
 	return err;
 }
 }
 
 
+int
+ext4_init_security(handle_t *handle, struct inode *inode, struct inode *dir,
+		   const struct qstr *qstr)
+{
+	return security_inode_init_security(inode, dir, qstr,
+					    &ext4_initxattrs, handle);
+}
+
 const struct xattr_handler ext4_xattr_security_handler = {
 const struct xattr_handler ext4_xattr_security_handler = {
 	.prefix	= XATTR_SECURITY_PREFIX,
 	.prefix	= XATTR_SECURITY_PREFIX,
 	.list	= ext4_xattr_security_list,
 	.list	= ext4_xattr_security_list,

+ 18 - 20
fs/gfs2/inode.c

@@ -624,31 +624,29 @@ fail:
 	return error;
 	return error;
 }
 }
 
 
-static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip,
-			      const struct qstr *qstr)
+int gfs2_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+		    void *fs_info)
 {
 {
-	int err;
-	size_t len;
-	void *value;
-	char *name;
-
-	err = security_inode_init_security(&ip->i_inode, &dip->i_inode, qstr,
-					   &name, &value, &len);
-
-	if (err) {
-		if (err == -EOPNOTSUPP)
-			return 0;
-		return err;
+	const struct xattr *xattr;
+	int err = 0;
+
+	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+		err = __gfs2_xattr_set(inode, xattr->name, xattr->value,
+				       xattr->value_len, 0,
+				       GFS2_EATYPE_SECURITY);
+		if (err < 0)
+			break;
 	}
 	}
-
-	err = __gfs2_xattr_set(&ip->i_inode, name, value, len, 0,
-			       GFS2_EATYPE_SECURITY);
-	kfree(value);
-	kfree(name);
-
 	return err;
 	return err;
 }
 }
 
 
+static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip,
+			      const struct qstr *qstr)
+{
+	return security_inode_init_security(&ip->i_inode, &dip->i_inode, qstr,
+					    &gfs2_initxattrs, NULL);
+}
+
 /**
 /**
  * gfs2_create_inode - Create a new inode
  * gfs2_create_inode - Create a new inode
  * @dir: The parent directory
  * @dir: The parent directory

+ 19 - 16
fs/jffs2/security.c

@@ -22,26 +22,29 @@
 #include <linux/security.h>
 #include <linux/security.h>
 #include "nodelist.h"
 #include "nodelist.h"
 
 
-/* ---- Initial Security Label Attachment -------------- */
-int jffs2_init_security(struct inode *inode, struct inode *dir,
-			const struct qstr *qstr)
+/* ---- Initial Security Label(s) Attachment callback --- */
+int jffs2_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+		     void *fs_info)
 {
 {
-	int rc;
-	size_t len;
-	void *value;
-	char *name;
+	const struct xattr *xattr;
+	int err = 0;
 
 
-	rc = security_inode_init_security(inode, dir, qstr, &name, &value, &len);
-	if (rc) {
-		if (rc == -EOPNOTSUPP)
-			return 0;
-		return rc;
+	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+		err = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY,
+					xattr->name, xattr->value,
+					xattr->value_len, 0);
+		if (err < 0)
+			break;
 	}
 	}
-	rc = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, value, len, 0);
+	return err;
+}
 
 
-	kfree(name);
-	kfree(value);
-	return rc;
+/* ---- Initial Security Label(s) Attachment ----------- */
+int jffs2_init_security(struct inode *inode, struct inode *dir,
+			const struct qstr *qstr)
+{
+	return security_inode_init_security(inode, dir, qstr,
+					    &jffs2_initxattrs, NULL);
 }
 }
 
 
 /* ---- XATTR Handler for "security.*" ----------------- */
 /* ---- XATTR Handler for "security.*" ----------------- */

+ 28 - 29
fs/jfs/xattr.c

@@ -1089,38 +1089,37 @@ int jfs_removexattr(struct dentry *dentry, const char *name)
 }
 }
 
 
 #ifdef CONFIG_JFS_SECURITY
 #ifdef CONFIG_JFS_SECURITY
-int jfs_init_security(tid_t tid, struct inode *inode, struct inode *dir,
-		      const struct qstr *qstr)
+int jfs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+		   void *fs_info)
 {
 {
-	int rc;
-	size_t len;
-	void *value;
-	char *suffix;
+	const struct xattr *xattr;
+	tid_t *tid = fs_info;
 	char *name;
 	char *name;
-
-	rc = security_inode_init_security(inode, dir, qstr, &suffix, &value,
-					  &len);
-	if (rc) {
-		if (rc == -EOPNOTSUPP)
-			return 0;
-		return rc;
-	}
-	name = kmalloc(XATTR_SECURITY_PREFIX_LEN + 1 + strlen(suffix),
-		       GFP_NOFS);
-	if (!name) {
-		rc = -ENOMEM;
-		goto kmalloc_failed;
+	int err = 0;
+
+	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+		name = kmalloc(XATTR_SECURITY_PREFIX_LEN +
+			       strlen(xattr->name) + 1, GFP_NOFS);
+		if (!name) {
+			err = -ENOMEM;
+			break;
+		}
+		strcpy(name, XATTR_SECURITY_PREFIX);
+		strcpy(name + XATTR_SECURITY_PREFIX_LEN, xattr->name);
+
+		err = __jfs_setxattr(*tid, inode, name,
+				     xattr->value, xattr->value_len, 0);
+		kfree(name);
+		if (err < 0)
+			break;
 	}
 	}
-	strcpy(name, XATTR_SECURITY_PREFIX);
-	strcpy(name + XATTR_SECURITY_PREFIX_LEN, suffix);
-
-	rc = __jfs_setxattr(tid, inode, name, value, len, 0);
-
-	kfree(name);
-kmalloc_failed:
-	kfree(suffix);
-	kfree(value);
+	return err;
+}
 
 
-	return rc;
+int jfs_init_security(tid_t tid, struct inode *inode, struct inode *dir,
+		      const struct qstr *qstr)
+{
+	return security_inode_init_security(inode, dir, qstr,
+					    &jfs_initxattrs, &tid);
 }
 }
 #endif
 #endif

+ 24 - 14
fs/ocfs2/xattr.c

@@ -7185,20 +7185,9 @@ int ocfs2_init_security_and_acl(struct inode *dir,
 {
 {
 	int ret = 0;
 	int ret = 0;
 	struct buffer_head *dir_bh = NULL;
 	struct buffer_head *dir_bh = NULL;
-	struct ocfs2_security_xattr_info si = {
-		.enable = 1,
-	};
 
 
-	ret = ocfs2_init_security_get(inode, dir, qstr, &si);
+	ret = ocfs2_init_security_get(inode, dir, qstr, NULL);
 	if (!ret) {
 	if (!ret) {
-		ret = ocfs2_xattr_set(inode, OCFS2_XATTR_INDEX_SECURITY,
-				      si.name, si.value, si.value_len,
-				      XATTR_CREATE);
-		if (ret) {
-			mlog_errno(ret);
-			goto leave;
-		}
-	} else if (ret != -EOPNOTSUPP) {
 		mlog_errno(ret);
 		mlog_errno(ret);
 		goto leave;
 		goto leave;
 	}
 	}
@@ -7255,6 +7244,22 @@ static int ocfs2_xattr_security_set(struct dentry *dentry, const char *name,
 			       name, value, size, flags);
 			       name, value, size, flags);
 }
 }
 
 
+int ocfs2_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+		     void *fs_info)
+{
+	const struct xattr *xattr;
+	int err = 0;
+
+	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+		err = ocfs2_xattr_set(inode, OCFS2_XATTR_INDEX_SECURITY,
+				      xattr->name, xattr->value,
+				      xattr->value_len, XATTR_CREATE);
+		if (err)
+			break;
+	}
+	return err;
+}
+
 int ocfs2_init_security_get(struct inode *inode,
 int ocfs2_init_security_get(struct inode *inode,
 			    struct inode *dir,
 			    struct inode *dir,
 			    const struct qstr *qstr,
 			    const struct qstr *qstr,
@@ -7263,8 +7268,13 @@ int ocfs2_init_security_get(struct inode *inode,
 	/* check whether ocfs2 support feature xattr */
 	/* check whether ocfs2 support feature xattr */
 	if (!ocfs2_supports_xattr(OCFS2_SB(dir->i_sb)))
 	if (!ocfs2_supports_xattr(OCFS2_SB(dir->i_sb)))
 		return -EOPNOTSUPP;
 		return -EOPNOTSUPP;
-	return security_inode_init_security(inode, dir, qstr, &si->name,
-					    &si->value, &si->value_len);
+	if (si)
+		return security_old_inode_init_security(inode, dir, qstr,
+							&si->name, &si->value,
+							&si->value_len);
+
+	return security_inode_init_security(inode, dir, qstr,
+					    &ocfs2_initxattrs, NULL);
 }
 }
 
 
 int ocfs2_init_security_set(handle_t *handle,
 int ocfs2_init_security_set(handle_t *handle,

+ 2 - 2
fs/reiserfs/xattr_security.c

@@ -66,8 +66,8 @@ int reiserfs_security_init(struct inode *dir, struct inode *inode,
 	if (IS_PRIVATE(dir))
 	if (IS_PRIVATE(dir))
 		return 0;
 		return 0;
 
 
-	error = security_inode_init_security(inode, dir, qstr, &sec->name,
-					     &sec->value, &sec->length);
+	error = security_old_inode_init_security(inode, dir, qstr, &sec->name,
+						 &sec->value, &sec->length);
 	if (error) {
 	if (error) {
 		if (error == -EOPNOTSUPP)
 		if (error == -EOPNOTSUPP)
 			error = 0;
 			error = 0;

+ 62 - 1
fs/xattr.c

@@ -14,6 +14,7 @@
 #include <linux/mount.h>
 #include <linux/mount.h>
 #include <linux/namei.h>
 #include <linux/namei.h>
 #include <linux/security.h>
 #include <linux/security.h>
+#include <linux/evm.h>
 #include <linux/syscalls.h>
 #include <linux/syscalls.h>
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/fsnotify.h>
 #include <linux/fsnotify.h>
@@ -166,6 +167,64 @@ out_noalloc:
 }
 }
 EXPORT_SYMBOL_GPL(xattr_getsecurity);
 EXPORT_SYMBOL_GPL(xattr_getsecurity);
 
 
+/*
+ * vfs_getxattr_alloc - allocate memory, if necessary, before calling getxattr
+ *
+ * Allocate memory, if not already allocated, or re-allocate correct size,
+ * before retrieving the extended attribute.
+ *
+ * Returns the result of alloc, if failed, or the getxattr operation.
+ */
+ssize_t
+vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value,
+		   size_t xattr_size, gfp_t flags)
+{
+	struct inode *inode = dentry->d_inode;
+	char *value = *xattr_value;
+	int error;
+
+	error = xattr_permission(inode, name, MAY_READ);
+	if (error)
+		return error;
+
+	if (!inode->i_op->getxattr)
+		return -EOPNOTSUPP;
+
+	error = inode->i_op->getxattr(dentry, name, NULL, 0);
+	if (error < 0)
+		return error;
+
+	if (!value || (error > xattr_size)) {
+		value = krealloc(*xattr_value, error + 1, flags);
+		if (!value)
+			return -ENOMEM;
+		memset(value, 0, error + 1);
+	}
+
+	error = inode->i_op->getxattr(dentry, name, value, error);
+	*xattr_value = value;
+	return error;
+}
+
+/* Compare an extended attribute value with the given value */
+int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name,
+		  const char *value, size_t size, gfp_t flags)
+{
+	char *xattr_value = NULL;
+	int rc;
+
+	rc = vfs_getxattr_alloc(dentry, xattr_name, &xattr_value, 0, flags);
+	if (rc < 0)
+		return rc;
+
+	if ((rc != size) || (memcmp(xattr_value, value, rc) != 0))
+		rc = -EINVAL;
+	else
+		rc = 0;
+	kfree(xattr_value);
+	return rc;
+}
+
 ssize_t
 ssize_t
 vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size)
 vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size)
 {
 {
@@ -243,8 +302,10 @@ vfs_removexattr(struct dentry *dentry, const char *name)
 	error = inode->i_op->removexattr(dentry, name);
 	error = inode->i_op->removexattr(dentry, name);
 	mutex_unlock(&inode->i_mutex);
 	mutex_unlock(&inode->i_mutex);
 
 
-	if (!error)
+	if (!error) {
 		fsnotify_xattr(dentry);
 		fsnotify_xattr(dentry);
+		evm_inode_post_removexattr(dentry, name);
+	}
 	return error;
 	return error;
 }
 }
 EXPORT_SYMBOL_GPL(vfs_removexattr);
 EXPORT_SYMBOL_GPL(vfs_removexattr);

+ 20 - 19
fs/xfs/xfs_iops.c

@@ -102,37 +102,38 @@ xfs_mark_inode_dirty(
 
 
 }
 }
 
 
+
+int xfs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+		   void *fs_info)
+{
+	const struct xattr *xattr;
+	struct xfs_inode *ip = XFS_I(inode);
+	int error = 0;
+
+	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+		error = xfs_attr_set(ip, xattr->name, xattr->value,
+				     xattr->value_len, ATTR_SECURE);
+		if (error < 0)
+			break;
+	}
+	return error;
+}
+
 /*
 /*
  * Hook in SELinux.  This is not quite correct yet, what we really need
  * Hook in SELinux.  This is not quite correct yet, what we really need
  * here (as we do for default ACLs) is a mechanism by which creation of
  * here (as we do for default ACLs) is a mechanism by which creation of
  * these attrs can be journalled at inode creation time (along with the
  * these attrs can be journalled at inode creation time (along with the
  * inode, of course, such that log replay can't cause these to be lost).
  * inode, of course, such that log replay can't cause these to be lost).
  */
  */
+
 STATIC int
 STATIC int
 xfs_init_security(
 xfs_init_security(
 	struct inode	*inode,
 	struct inode	*inode,
 	struct inode	*dir,
 	struct inode	*dir,
 	const struct qstr *qstr)
 	const struct qstr *qstr)
 {
 {
-	struct xfs_inode *ip = XFS_I(inode);
-	size_t		length;
-	void		*value;
-	unsigned char	*name;
-	int		error;
-
-	error = security_inode_init_security(inode, dir, qstr, (char **)&name,
-					     &value, &length);
-	if (error) {
-		if (error == -EOPNOTSUPP)
-			return 0;
-		return -error;
-	}
-
-	error = xfs_attr_set(ip, name, value, length, ATTR_SECURE);
-
-	kfree(name);
-	kfree(value);
-	return error;
+	return security_inode_init_security(inode, dir, qstr,
+					    &xfs_initxattrs, NULL);
 }
 }
 
 
 static void
 static void

+ 100 - 0
include/linux/evm.h

@@ -0,0 +1,100 @@
+/*
+ * evm.h
+ *
+ * Copyright (c) 2009 IBM Corporation
+ * Author: Mimi Zohar <zohar@us.ibm.com>
+ */
+
+#ifndef _LINUX_EVM_H
+#define _LINUX_EVM_H
+
+#include <linux/integrity.h>
+#include <linux/xattr.h>
+
+struct integrity_iint_cache;
+
+#ifdef CONFIG_EVM
+extern enum integrity_status evm_verifyxattr(struct dentry *dentry,
+					     const char *xattr_name,
+					     void *xattr_value,
+					     size_t xattr_value_len,
+					     struct integrity_iint_cache *iint);
+extern int evm_inode_setattr(struct dentry *dentry, struct iattr *attr);
+extern void evm_inode_post_setattr(struct dentry *dentry, int ia_valid);
+extern int evm_inode_setxattr(struct dentry *dentry, const char *name,
+			      const void *value, size_t size);
+extern void evm_inode_post_setxattr(struct dentry *dentry,
+				    const char *xattr_name,
+				    const void *xattr_value,
+				    size_t xattr_value_len);
+extern int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name);
+extern void evm_inode_post_removexattr(struct dentry *dentry,
+				       const char *xattr_name);
+extern int evm_inode_init_security(struct inode *inode,
+				   const struct xattr *xattr_array,
+				   struct xattr *evm);
+#ifdef CONFIG_FS_POSIX_ACL
+extern int posix_xattr_acl(const char *xattrname);
+#else
+static inline int posix_xattr_acl(const char *xattrname)
+{
+	return 0;
+}
+#endif
+#else
+#ifdef CONFIG_INTEGRITY
+static inline enum integrity_status evm_verifyxattr(struct dentry *dentry,
+						    const char *xattr_name,
+						    void *xattr_value,
+						    size_t xattr_value_len,
+					struct integrity_iint_cache *iint)
+{
+	return INTEGRITY_UNKNOWN;
+}
+#endif
+
+static inline int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
+{
+	return 0;
+}
+
+static inline void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
+{
+	return;
+}
+
+static inline int evm_inode_setxattr(struct dentry *dentry, const char *name,
+				     const void *value, size_t size)
+{
+	return 0;
+}
+
+static inline void evm_inode_post_setxattr(struct dentry *dentry,
+					   const char *xattr_name,
+					   const void *xattr_value,
+					   size_t xattr_value_len)
+{
+	return;
+}
+
+static inline int evm_inode_removexattr(struct dentry *dentry,
+					const char *xattr_name)
+{
+	return 0;
+}
+
+static inline void evm_inode_post_removexattr(struct dentry *dentry,
+					      const char *xattr_name)
+{
+	return;
+}
+
+static inline int evm_inode_init_security(struct inode *inode,
+					  const struct xattr *xattr_array,
+					  struct xattr *evm)
+{
+	return 0;
+}
+
+#endif /* CONFIG_EVM_H */
+#endif /* LINUX_EVM_H */

+ 0 - 13
include/linux/ima.h

@@ -15,8 +15,6 @@ struct linux_binprm;
 
 
 #ifdef CONFIG_IMA
 #ifdef CONFIG_IMA
 extern int ima_bprm_check(struct linux_binprm *bprm);
 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_file_check(struct file *file, int mask);
 extern int ima_file_check(struct file *file, int mask);
 extern void ima_file_free(struct file *file);
 extern void ima_file_free(struct file *file);
 extern int ima_file_mmap(struct file *file, unsigned long prot);
 extern int ima_file_mmap(struct file *file, unsigned long prot);
@@ -27,16 +25,6 @@ static inline int ima_bprm_check(struct linux_binprm *bprm)
 	return 0;
 	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_file_check(struct file *file, int mask)
 static inline int ima_file_check(struct file *file, int mask)
 {
 {
 	return 0;
 	return 0;
@@ -51,6 +39,5 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot)
 {
 {
 	return 0;
 	return 0;
 }
 }
-
 #endif /* CONFIG_IMA_H */
 #endif /* CONFIG_IMA_H */
 #endif /* _LINUX_IMA_H */
 #endif /* _LINUX_IMA_H */

+ 39 - 0
include/linux/integrity.h

@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2009 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_INTEGRITY_H
+#define _LINUX_INTEGRITY_H
+
+#include <linux/fs.h>
+
+enum integrity_status {
+	INTEGRITY_PASS = 0,
+	INTEGRITY_FAIL,
+	INTEGRITY_NOLABEL,
+	INTEGRITY_NOXATTRS,
+	INTEGRITY_UNKNOWN,
+};
+
+/* List of EVM protected security xattrs */
+#ifdef CONFIG_INTEGRITY
+extern int integrity_inode_alloc(struct inode *inode);
+extern void integrity_inode_free(struct inode *inode);
+
+#else
+static inline int integrity_inode_alloc(struct inode *inode)
+{
+	return 0;
+}
+
+static inline void integrity_inode_free(struct inode *inode)
+{
+	return;
+}
+#endif /* CONFIG_INTEGRITY_H */
+#endif /* _LINUX_INTEGRITY_H */

+ 1 - 1
include/linux/kernel.h

@@ -382,7 +382,7 @@ static inline char *pack_hex_byte(char *buf, u8 byte)
 }
 }
 
 
 extern int hex_to_bin(char ch);
 extern int hex_to_bin(char ch);
-extern void hex2bin(u8 *dst, const char *src, size_t count);
+extern int __must_check hex2bin(u8 *dst, const char *src, size_t count);
 
 
 /*
 /*
  * General tracing related utility functions - trace_printk(),
  * General tracing related utility functions - trace_printk(),

+ 25 - 7
include/linux/security.h

@@ -36,6 +36,7 @@
 #include <linux/key.h>
 #include <linux/key.h>
 #include <linux/xfrm.h>
 #include <linux/xfrm.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
+#include <linux/xattr.h>
 #include <net/flow.h>
 #include <net/flow.h>
 
 
 /* Maximum number of letters for an LSM name string */
 /* Maximum number of letters for an LSM name string */
@@ -147,6 +148,10 @@ extern int mmap_min_addr_handler(struct ctl_table *table, int write,
 				 void __user *buffer, size_t *lenp, loff_t *ppos);
 				 void __user *buffer, size_t *lenp, loff_t *ppos);
 #endif
 #endif
 
 
+/* security_inode_init_security callback function to write xattrs */
+typedef int (*initxattrs) (struct inode *inode,
+			   const struct xattr *xattr_array, void *fs_data);
+
 #ifdef CONFIG_SECURITY
 #ifdef CONFIG_SECURITY
 
 
 struct security_mnt_opts {
 struct security_mnt_opts {
@@ -1367,7 +1372,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  * @inode_getsecctx:
  * @inode_getsecctx:
  * 	Returns a string containing all relavent security context information
  * 	Returns a string containing all relavent security context information
  *
  *
- * 	@inode we wish to set the security context of.
+ * 	@inode we wish to get the security context of.
  *	@ctx is a pointer in which to place the allocated security context.
  *	@ctx is a pointer in which to place the allocated security context.
  *	@ctxlen points to the place to put the length of @ctx.
  *	@ctxlen points to the place to put the length of @ctx.
  * This is the main security structure.
  * This is the main security structure.
@@ -1655,6 +1660,8 @@ struct security_operations {
 extern int security_init(void);
 extern int security_init(void);
 extern int security_module_enable(struct security_operations *ops);
 extern int security_module_enable(struct security_operations *ops);
 extern int register_security(struct security_operations *ops);
 extern int register_security(struct security_operations *ops);
+extern void __init security_fixup_ops(struct security_operations *ops);
+
 
 
 /* Security operations */
 /* Security operations */
 int security_ptrace_access_check(struct task_struct *child, unsigned int mode);
 int security_ptrace_access_check(struct task_struct *child, unsigned int mode);
@@ -1704,8 +1711,11 @@ int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts);
 int security_inode_alloc(struct inode *inode);
 int security_inode_alloc(struct inode *inode);
 void security_inode_free(struct inode *inode);
 void security_inode_free(struct inode *inode);
 int security_inode_init_security(struct inode *inode, struct inode *dir,
 int security_inode_init_security(struct inode *inode, struct inode *dir,
-				 const struct qstr *qstr, char **name,
-				 void **value, size_t *len);
+				 const struct qstr *qstr,
+				 initxattrs initxattrs, void *fs_data);
+int security_old_inode_init_security(struct inode *inode, struct inode *dir,
+				     const struct qstr *qstr, char **name,
+				     void **value, size_t *len);
 int security_inode_create(struct inode *dir, struct dentry *dentry, int mode);
 int security_inode_create(struct inode *dir, struct dentry *dentry, int mode);
 int security_inode_link(struct dentry *old_dentry, struct inode *dir,
 int security_inode_link(struct dentry *old_dentry, struct inode *dir,
 			 struct dentry *new_dentry);
 			 struct dentry *new_dentry);
@@ -2034,11 +2044,19 @@ static inline void security_inode_free(struct inode *inode)
 static inline int security_inode_init_security(struct inode *inode,
 static inline int security_inode_init_security(struct inode *inode,
 						struct inode *dir,
 						struct inode *dir,
 						const struct qstr *qstr,
 						const struct qstr *qstr,
-						char **name,
-						void **value,
-						size_t *len)
+						initxattrs initxattrs,
+						void *fs_data)
 {
 {
-	return -EOPNOTSUPP;
+	return 0;
+}
+
+static inline int security_old_inode_init_security(struct inode *inode,
+						   struct inode *dir,
+						   const struct qstr *qstr,
+						   char **name, void **value,
+						   size_t *len)
+{
+	return 0;
 }
 }
 
 
 static inline int security_inode_create(struct inode *dir,
 static inline int security_inode_create(struct inode *dir,

+ 18 - 1
include/linux/xattr.h

@@ -30,6 +30,9 @@
 #define XATTR_USER_PREFIX_LEN (sizeof (XATTR_USER_PREFIX) - 1)
 #define XATTR_USER_PREFIX_LEN (sizeof (XATTR_USER_PREFIX) - 1)
 
 
 /* Security namespace */
 /* Security namespace */
+#define XATTR_EVM_SUFFIX "evm"
+#define XATTR_NAME_EVM XATTR_SECURITY_PREFIX XATTR_EVM_SUFFIX
+
 #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
 
 
@@ -49,6 +52,11 @@
 #define XATTR_CAPS_SUFFIX "capability"
 #define XATTR_CAPS_SUFFIX "capability"
 #define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX
 #define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX
 
 
+#define XATTR_POSIX_ACL_ACCESS  "posix_acl_access"
+#define XATTR_NAME_POSIX_ACL_ACCESS XATTR_SYSTEM_PREFIX XATTR_POSIX_ACL_ACCESS
+#define XATTR_POSIX_ACL_DEFAULT  "posix_acl_default"
+#define XATTR_NAME_POSIX_ACL_DEFAULT XATTR_SYSTEM_PREFIX XATTR_POSIX_ACL_DEFAULT
+
 #ifdef  __KERNEL__
 #ifdef  __KERNEL__
 
 
 #include <linux/types.h>
 #include <linux/types.h>
@@ -67,6 +75,12 @@ struct xattr_handler {
 		   size_t size, int flags, int handler_flags);
 		   size_t size, int flags, int handler_flags);
 };
 };
 
 
+struct xattr {
+	char *name;
+	void *value;
+	size_t value_len;
+};
+
 ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t);
 ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t);
 ssize_t vfs_getxattr(struct dentry *, const char *, void *, size_t);
 ssize_t vfs_getxattr(struct dentry *, const char *, void *, size_t);
 ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size);
 ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size);
@@ -78,7 +92,10 @@ ssize_t generic_getxattr(struct dentry *dentry, const char *name, void *buffer,
 ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size);
 ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size);
 int generic_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags);
 int generic_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags);
 int generic_removexattr(struct dentry *dentry, const char *name);
 int generic_removexattr(struct dentry *dentry, const char *name);
-
+ssize_t vfs_getxattr_alloc(struct dentry *dentry, const char *name,
+			   char **xattr_value, size_t size, gfp_t flags);
+int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name,
+		  const char *value, size_t size, gfp_t flags);
 #endif  /*  __KERNEL__  */
 #endif  /*  __KERNEL__  */
 
 
 #endif	/* _LINUX_XATTR_H */
 #endif	/* _LINUX_XATTR_H */

+ 16 - 2
kernel/cred.c

@@ -644,6 +644,9 @@ void __init cred_init(void)
  */
  */
 struct cred *prepare_kernel_cred(struct task_struct *daemon)
 struct cred *prepare_kernel_cred(struct task_struct *daemon)
 {
 {
+#ifdef CONFIG_KEYS
+	struct thread_group_cred *tgcred;
+#endif
 	const struct cred *old;
 	const struct cred *old;
 	struct cred *new;
 	struct cred *new;
 
 
@@ -651,6 +654,14 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
 	if (!new)
 	if (!new)
 		return NULL;
 		return NULL;
 
 
+#ifdef CONFIG_KEYS
+	tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL);
+	if (!tgcred) {
+		kmem_cache_free(cred_jar, new);
+		return NULL;
+	}
+#endif
+
 	kdebug("prepare_kernel_cred() alloc %p", new);
 	kdebug("prepare_kernel_cred() alloc %p", new);
 
 
 	if (daemon)
 	if (daemon)
@@ -667,8 +678,11 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
 	get_group_info(new->group_info);
 	get_group_info(new->group_info);
 
 
 #ifdef CONFIG_KEYS
 #ifdef CONFIG_KEYS
-	atomic_inc(&init_tgcred.usage);
-	new->tgcred = &init_tgcred;
+	atomic_set(&tgcred->usage, 1);
+	spin_lock_init(&tgcred->lock);
+	tgcred->process_keyring = NULL;
+	tgcred->session_keyring = NULL;
+	new->tgcred = tgcred;
 	new->request_key_auth = NULL;
 	new->request_key_auth = NULL;
 	new->thread_keyring = NULL;
 	new->thread_keyring = NULL;
 	new->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
 	new->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;

+ 11 - 4
lib/hexdump.c

@@ -38,14 +38,21 @@ EXPORT_SYMBOL(hex_to_bin);
  * @dst: binary result
  * @dst: binary result
  * @src: ascii hexadecimal string
  * @src: ascii hexadecimal string
  * @count: result length
  * @count: result length
+ *
+ * Return 0 on success, -1 in case of bad input.
  */
  */
-void hex2bin(u8 *dst, const char *src, size_t count)
+int hex2bin(u8 *dst, const char *src, size_t count)
 {
 {
 	while (count--) {
 	while (count--) {
-		*dst = hex_to_bin(*src++) << 4;
-		*dst += hex_to_bin(*src++);
-		dst++;
+		int hi = hex_to_bin(*src++);
+		int lo = hex_to_bin(*src++);
+
+		if ((hi < 0) || (lo < 0))
+			return -1;
+
+		*dst++ = (hi << 4) | lo;
 	}
 	}
+	return 0;
 }
 }
 EXPORT_SYMBOL(hex2bin);
 EXPORT_SYMBOL(hex2bin);
 
 

+ 2 - 2
mm/shmem.c

@@ -1458,7 +1458,7 @@ shmem_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
 	inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE);
 	inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE);
 	if (inode) {
 	if (inode) {
 		error = security_inode_init_security(inode, dir,
 		error = security_inode_init_security(inode, dir,
-						     &dentry->d_name, NULL,
+						     &dentry->d_name,
 						     NULL, NULL);
 						     NULL, NULL);
 		if (error) {
 		if (error) {
 			if (error != -EOPNOTSUPP) {
 			if (error != -EOPNOTSUPP) {
@@ -1598,7 +1598,7 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
 	if (!inode)
 	if (!inode)
 		return -ENOSPC;
 		return -ENOSPC;
 
 
-	error = security_inode_init_security(inode, dir, &dentry->d_name, NULL,
+	error = security_inode_init_security(inode, dir, &dentry->d_name,
 					     NULL, NULL);
 					     NULL, NULL);
 	if (error) {
 	if (error) {
 		if (error != -EOPNOTSUPP) {
 		if (error != -EOPNOTSUPP) {

+ 4 - 2
security/Kconfig

@@ -38,7 +38,9 @@ config TRUSTED_KEYS
 
 
 config ENCRYPTED_KEYS
 config ENCRYPTED_KEYS
 	tristate "ENCRYPTED KEYS"
 	tristate "ENCRYPTED KEYS"
-	depends on KEYS && TRUSTED_KEYS
+	depends on KEYS
+	select CRYPTO
+	select CRYPTO_HMAC
 	select CRYPTO_AES
 	select CRYPTO_AES
 	select CRYPTO_CBC
 	select CRYPTO_CBC
 	select CRYPTO_SHA256
 	select CRYPTO_SHA256
@@ -186,7 +188,7 @@ source security/smack/Kconfig
 source security/tomoyo/Kconfig
 source security/tomoyo/Kconfig
 source security/apparmor/Kconfig
 source security/apparmor/Kconfig
 
 
-source security/integrity/ima/Kconfig
+source security/integrity/Kconfig
 
 
 choice
 choice
 	prompt "Default security module"
 	prompt "Default security module"

+ 2 - 2
security/Makefile

@@ -24,5 +24,5 @@ obj-$(CONFIG_SECURITY_APPARMOR)		+= apparmor/built-in.o
 obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
 obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
 
 
 # Object integrity file lists
 # Object integrity file lists
-subdir-$(CONFIG_IMA)			+= integrity/ima
-obj-$(CONFIG_IMA)			+= integrity/ima/built-in.o
+subdir-$(CONFIG_INTEGRITY)		+= integrity
+obj-$(CONFIG_INTEGRITY)			+= integrity/built-in.o

+ 1 - 1
security/apparmor/apparmorfs.c

@@ -200,7 +200,7 @@ void __init aa_destroy_aafs(void)
  *
  *
  * Returns: error on failure
  * Returns: error on failure
  */
  */
-int __init aa_create_aafs(void)
+static int __init aa_create_aafs(void)
 {
 {
 	int error;
 	int error;
 
 

+ 1 - 0
security/apparmor/ipc.c

@@ -19,6 +19,7 @@
 #include "include/capability.h"
 #include "include/capability.h"
 #include "include/context.h"
 #include "include/context.h"
 #include "include/policy.h"
 #include "include/policy.h"
+#include "include/ipc.h"
 
 
 /* call back to audit ptrace fields */
 /* call back to audit ptrace fields */
 static void audit_cb(struct audit_buffer *ab, void *va)
 static void audit_cb(struct audit_buffer *ab, void *va)

+ 1 - 0
security/apparmor/lib.c

@@ -18,6 +18,7 @@
 #include <linux/vmalloc.h>
 #include <linux/vmalloc.h>
 
 
 #include "include/audit.h"
 #include "include/audit.h"
+#include "include/apparmor.h"
 
 
 
 
 /**
 /**

+ 6 - 6
security/apparmor/policy_unpack.c

@@ -381,11 +381,11 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
 		profile->file.trans.size = size;
 		profile->file.trans.size = size;
 		for (i = 0; i < size; i++) {
 		for (i = 0; i < size; i++) {
 			char *str;
 			char *str;
-			int c, j, size = unpack_strdup(e, &str, NULL);
+			int c, j, size2 = unpack_strdup(e, &str, NULL);
 			/* unpack_strdup verifies that the last character is
 			/* unpack_strdup verifies that the last character is
 			 * null termination byte.
 			 * null termination byte.
 			 */
 			 */
-			if (!size)
+			if (!size2)
 				goto fail;
 				goto fail;
 			profile->file.trans.table[i] = str;
 			profile->file.trans.table[i] = str;
 			/* verify that name doesn't start with space */
 			/* verify that name doesn't start with space */
@@ -393,7 +393,7 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
 				goto fail;
 				goto fail;
 
 
 			/* count internal #  of internal \0 */
 			/* count internal #  of internal \0 */
-			for (c = j = 0; j < size - 2; j++) {
+			for (c = j = 0; j < size2 - 2; j++) {
 				if (!str[j])
 				if (!str[j])
 					c++;
 					c++;
 			}
 			}
@@ -440,11 +440,11 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile)
 		if (size > RLIM_NLIMITS)
 		if (size > RLIM_NLIMITS)
 			goto fail;
 			goto fail;
 		for (i = 0; i < size; i++) {
 		for (i = 0; i < size; i++) {
-			u64 tmp = 0;
+			u64 tmp2 = 0;
 			int a = aa_map_resource(i);
 			int a = aa_map_resource(i);
-			if (!unpack_u64(e, &tmp, NULL))
+			if (!unpack_u64(e, &tmp2, NULL))
 				goto fail;
 				goto fail;
-			profile->rlimits.limits[a].rlim_max = tmp;
+			profile->rlimits.limits[a].rlim_max = tmp2;
 		}
 		}
 		if (!unpack_nameX(e, AA_ARRAYEND, NULL))
 		if (!unpack_nameX(e, AA_ARRAYEND, NULL))
 			goto fail;
 			goto fail;

+ 1 - 0
security/apparmor/procattr.c

@@ -16,6 +16,7 @@
 #include "include/context.h"
 #include "include/context.h"
 #include "include/policy.h"
 #include "include/policy.h"
 #include "include/domain.h"
 #include "include/domain.h"
+#include "include/procattr.h"
 
 
 
 
 /**
 /**

+ 10 - 6
security/commoncap.c

@@ -332,7 +332,8 @@ int cap_inode_killpriv(struct dentry *dentry)
  */
  */
 static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,
 static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,
 					  struct linux_binprm *bprm,
 					  struct linux_binprm *bprm,
-					  bool *effective)
+					  bool *effective,
+					  bool *has_cap)
 {
 {
 	struct cred *new = bprm->cred;
 	struct cred *new = bprm->cred;
 	unsigned i;
 	unsigned i;
@@ -341,6 +342,9 @@ static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,
 	if (caps->magic_etc & VFS_CAP_FLAGS_EFFECTIVE)
 	if (caps->magic_etc & VFS_CAP_FLAGS_EFFECTIVE)
 		*effective = true;
 		*effective = true;
 
 
+	if (caps->magic_etc & VFS_CAP_REVISION_MASK)
+		*has_cap = true;
+
 	CAP_FOR_EACH_U32(i) {
 	CAP_FOR_EACH_U32(i) {
 		__u32 permitted = caps->permitted.cap[i];
 		__u32 permitted = caps->permitted.cap[i];
 		__u32 inheritable = caps->inheritable.cap[i];
 		__u32 inheritable = caps->inheritable.cap[i];
@@ -424,7 +428,7 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data
  * its xattrs and, if present, apply them to the proposed credentials being
  * its xattrs and, if present, apply them to the proposed credentials being
  * constructed by execve().
  * constructed by execve().
  */
  */
-static int get_file_caps(struct linux_binprm *bprm, bool *effective)
+static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_cap)
 {
 {
 	struct dentry *dentry;
 	struct dentry *dentry;
 	int rc = 0;
 	int rc = 0;
@@ -450,7 +454,7 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective)
 		goto out;
 		goto out;
 	}
 	}
 
 
-	rc = bprm_caps_from_vfs_caps(&vcaps, bprm, effective);
+	rc = bprm_caps_from_vfs_caps(&vcaps, bprm, effective, has_cap);
 	if (rc == -EINVAL)
 	if (rc == -EINVAL)
 		printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n",
 		printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n",
 		       __func__, rc, bprm->filename);
 		       __func__, rc, bprm->filename);
@@ -475,11 +479,11 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
 {
 {
 	const struct cred *old = current_cred();
 	const struct cred *old = current_cred();
 	struct cred *new = bprm->cred;
 	struct cred *new = bprm->cred;
-	bool effective;
+	bool effective, has_cap = false;
 	int ret;
 	int ret;
 
 
 	effective = false;
 	effective = false;
-	ret = get_file_caps(bprm, &effective);
+	ret = get_file_caps(bprm, &effective, &has_cap);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 
@@ -489,7 +493,7 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
 		 * for a setuid root binary run by a non-root user.  Do set it
 		 * for a setuid root binary run by a non-root user.  Do set it
 		 * for a root user just to cause least surprise to an admin.
 		 * for a root user just to cause least surprise to an admin.
 		 */
 		 */
-		if (effective && new->uid != 0 && new->euid == 0) {
+		if (has_cap && new->uid != 0 && new->euid == 0) {
 			warn_setuid_and_fcaps_mixed(bprm->filename);
 			warn_setuid_and_fcaps_mixed(bprm->filename);
 			goto skip;
 			goto skip;
 		}
 		}

+ 7 - 0
security/integrity/Kconfig

@@ -0,0 +1,7 @@
+#
+config INTEGRITY
+	def_bool y
+	depends on IMA || EVM
+
+source security/integrity/ima/Kconfig
+source security/integrity/evm/Kconfig

+ 12 - 0
security/integrity/Makefile

@@ -0,0 +1,12 @@
+#
+# Makefile for caching inode integrity data (iint)
+#
+
+obj-$(CONFIG_INTEGRITY) += integrity.o
+
+integrity-y := iint.o
+
+subdir-$(CONFIG_IMA)			+= ima
+obj-$(CONFIG_IMA)			+= ima/built-in.o
+subdir-$(CONFIG_EVM)			+= evm
+obj-$(CONFIG_EVM)			+= evm/built-in.o

+ 13 - 0
security/integrity/evm/Kconfig

@@ -0,0 +1,13 @@
+config EVM
+	boolean "EVM support"
+	depends on SECURITY && KEYS && (TRUSTED_KEYS=y || TRUSTED_KEYS=n)
+	select CRYPTO_HMAC
+	select CRYPTO_MD5
+	select CRYPTO_SHA1
+	select ENCRYPTED_KEYS
+	default n
+	help
+	  EVM protects a file's security extended attributes against
+	  integrity attacks.
+
+	  If you are unsure how to answer this question, answer N.

+ 7 - 0
security/integrity/evm/Makefile

@@ -0,0 +1,7 @@
+#
+# Makefile for building the Extended Verification Module(EVM)
+#
+obj-$(CONFIG_EVM) += evm.o
+
+evm-y := evm_main.o evm_crypto.o evm_secfs.o
+evm-$(CONFIG_FS_POSIX_ACL) += evm_posix_acl.o

+ 38 - 0
security/integrity/evm/evm.h

@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2005-2010 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * File: evm.h
+ *
+ */
+#include <linux/xattr.h>
+#include <linux/security.h>
+#include "../integrity.h"
+
+extern int evm_initialized;
+extern char *evm_hmac;
+
+extern struct crypto_shash *hmac_tfm;
+
+/* List of EVM protected security xattrs */
+extern char *evm_config_xattrnames[];
+
+extern int evm_init_key(void);
+extern int evm_update_evmxattr(struct dentry *dentry,
+			       const char *req_xattr_name,
+			       const char *req_xattr_value,
+			       size_t req_xattr_value_len);
+extern int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
+			 const char *req_xattr_value,
+			 size_t req_xattr_value_len, char *digest);
+extern int evm_init_hmac(struct inode *inode, const struct xattr *xattr,
+			 char *hmac_val);
+extern int evm_init_secfs(void);
+extern void evm_cleanup_secfs(void);

+ 216 - 0
security/integrity/evm/evm_crypto.c

@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2005-2010 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * File: evm_crypto.c
+ *	 Using root's kernel master key (kmk), calculate the HMAC
+ */
+
+#include <linux/module.h>
+#include <linux/crypto.h>
+#include <linux/xattr.h>
+#include <keys/encrypted-type.h>
+#include <crypto/hash.h>
+#include "evm.h"
+
+#define EVMKEY "evm-key"
+#define MAX_KEY_SIZE 128
+static unsigned char evmkey[MAX_KEY_SIZE];
+static int evmkey_len = MAX_KEY_SIZE;
+
+struct crypto_shash *hmac_tfm;
+
+static struct shash_desc *init_desc(void)
+{
+	int rc;
+	struct shash_desc *desc;
+
+	if (hmac_tfm == NULL) {
+		hmac_tfm = crypto_alloc_shash(evm_hmac, 0, CRYPTO_ALG_ASYNC);
+		if (IS_ERR(hmac_tfm)) {
+			pr_err("Can not allocate %s (reason: %ld)\n",
+			       evm_hmac, PTR_ERR(hmac_tfm));
+			rc = PTR_ERR(hmac_tfm);
+			hmac_tfm = NULL;
+			return ERR_PTR(rc);
+		}
+	}
+
+	desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(hmac_tfm),
+			GFP_KERNEL);
+	if (!desc)
+		return ERR_PTR(-ENOMEM);
+
+	desc->tfm = hmac_tfm;
+	desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	rc = crypto_shash_setkey(hmac_tfm, evmkey, evmkey_len);
+	if (rc)
+		goto out;
+	rc = crypto_shash_init(desc);
+out:
+	if (rc) {
+		kfree(desc);
+		return ERR_PTR(rc);
+	}
+	return desc;
+}
+
+/* Protect against 'cutting & pasting' security.evm xattr, include inode
+ * specific info.
+ *
+ * (Additional directory/file metadata needs to be added for more complete
+ * protection.)
+ */
+static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
+			  char *digest)
+{
+	struct h_misc {
+		unsigned long ino;
+		__u32 generation;
+		uid_t uid;
+		gid_t gid;
+		umode_t mode;
+	} hmac_misc;
+
+	memset(&hmac_misc, 0, sizeof hmac_misc);
+	hmac_misc.ino = inode->i_ino;
+	hmac_misc.generation = inode->i_generation;
+	hmac_misc.uid = inode->i_uid;
+	hmac_misc.gid = inode->i_gid;
+	hmac_misc.mode = inode->i_mode;
+	crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof hmac_misc);
+	crypto_shash_final(desc, digest);
+}
+
+/*
+ * Calculate the HMAC value across the set of protected security xattrs.
+ *
+ * Instead of retrieving the requested xattr, for performance, calculate
+ * the hmac using the requested xattr value. Don't alloc/free memory for
+ * each xattr, but attempt to re-use the previously allocated memory.
+ */
+int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
+		  const char *req_xattr_value, size_t req_xattr_value_len,
+		  char *digest)
+{
+	struct inode *inode = dentry->d_inode;
+	struct shash_desc *desc;
+	char **xattrname;
+	size_t xattr_size = 0;
+	char *xattr_value = NULL;
+	int error;
+	int size;
+
+	if (!inode->i_op || !inode->i_op->getxattr)
+		return -EOPNOTSUPP;
+	desc = init_desc();
+	if (IS_ERR(desc))
+		return PTR_ERR(desc);
+
+	error = -ENODATA;
+	for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
+		if ((req_xattr_name && req_xattr_value)
+		    && !strcmp(*xattrname, req_xattr_name)) {
+			error = 0;
+			crypto_shash_update(desc, (const u8 *)req_xattr_value,
+					     req_xattr_value_len);
+			continue;
+		}
+		size = vfs_getxattr_alloc(dentry, *xattrname,
+					  &xattr_value, xattr_size, GFP_NOFS);
+		if (size == -ENOMEM) {
+			error = -ENOMEM;
+			goto out;
+		}
+		if (size < 0)
+			continue;
+
+		error = 0;
+		xattr_size = size;
+		crypto_shash_update(desc, (const u8 *)xattr_value, xattr_size);
+	}
+	hmac_add_misc(desc, inode, digest);
+
+out:
+	kfree(xattr_value);
+	kfree(desc);
+	return error;
+}
+
+/*
+ * Calculate the hmac and update security.evm xattr
+ *
+ * Expects to be called with i_mutex locked.
+ */
+int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
+			const char *xattr_value, size_t xattr_value_len)
+{
+	struct inode *inode = dentry->d_inode;
+	struct evm_ima_xattr_data xattr_data;
+	int rc = 0;
+
+	rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
+			   xattr_value_len, xattr_data.digest);
+	if (rc == 0) {
+		xattr_data.type = EVM_XATTR_HMAC;
+		rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_EVM,
+					   &xattr_data,
+					   sizeof(xattr_data), 0);
+	}
+	else if (rc == -ENODATA)
+		rc = inode->i_op->removexattr(dentry, XATTR_NAME_EVM);
+	return rc;
+}
+
+int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr,
+		  char *hmac_val)
+{
+	struct shash_desc *desc;
+
+	desc = init_desc();
+	if (IS_ERR(desc)) {
+		printk(KERN_INFO "init_desc failed\n");
+		return PTR_ERR(desc);
+	}
+
+	crypto_shash_update(desc, lsm_xattr->value, lsm_xattr->value_len);
+	hmac_add_misc(desc, inode, hmac_val);
+	kfree(desc);
+	return 0;
+}
+
+/*
+ * Get the key from the TPM for the SHA1-HMAC
+ */
+int evm_init_key(void)
+{
+	struct key *evm_key;
+	struct encrypted_key_payload *ekp;
+	int rc = 0;
+
+	evm_key = request_key(&key_type_encrypted, EVMKEY, NULL);
+	if (IS_ERR(evm_key))
+		return -ENOENT;
+
+	down_read(&evm_key->sem);
+	ekp = evm_key->payload.data;
+	if (ekp->decrypted_datalen > MAX_KEY_SIZE) {
+		rc = -EINVAL;
+		goto out;
+	}
+	memcpy(evmkey, ekp->decrypted_data, ekp->decrypted_datalen);
+out:
+	/* burn the original key contents */
+	memset(ekp->decrypted_data, 0, ekp->decrypted_datalen);
+	up_read(&evm_key->sem);
+	key_put(evm_key);
+	return rc;
+}

+ 384 - 0
security/integrity/evm/evm_main.c

@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2005-2010 IBM Corporation
+ *
+ * Author:
+ * Mimi Zohar <zohar@us.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * File: evm_main.c
+ *	implements evm_inode_setxattr, evm_inode_post_setxattr,
+ *	evm_inode_removexattr, and evm_verifyxattr
+ */
+
+#include <linux/module.h>
+#include <linux/crypto.h>
+#include <linux/xattr.h>
+#include <linux/integrity.h>
+#include <linux/evm.h>
+#include <crypto/hash.h>
+#include "evm.h"
+
+int evm_initialized;
+
+char *evm_hmac = "hmac(sha1)";
+
+char *evm_config_xattrnames[] = {
+#ifdef CONFIG_SECURITY_SELINUX
+	XATTR_NAME_SELINUX,
+#endif
+#ifdef CONFIG_SECURITY_SMACK
+	XATTR_NAME_SMACK,
+#endif
+	XATTR_NAME_CAPS,
+	NULL
+};
+
+static int evm_fixmode;
+static int __init evm_set_fixmode(char *str)
+{
+	if (strncmp(str, "fix", 3) == 0)
+		evm_fixmode = 1;
+	return 0;
+}
+__setup("evm=", evm_set_fixmode);
+
+/*
+ * evm_verify_hmac - calculate and compare the HMAC with the EVM xattr
+ *
+ * Compute the HMAC on the dentry's protected set of extended attributes
+ * and compare it against the stored security.evm xattr.
+ *
+ * For performance:
+ * - use the previoulsy retrieved xattr value and length to calculate the
+ *   HMAC.)
+ * - cache the verification result in the iint, when available.
+ *
+ * Returns integrity status
+ */
+static enum integrity_status evm_verify_hmac(struct dentry *dentry,
+					     const char *xattr_name,
+					     char *xattr_value,
+					     size_t xattr_value_len,
+					     struct integrity_iint_cache *iint)
+{
+	struct evm_ima_xattr_data xattr_data;
+	enum integrity_status evm_status = INTEGRITY_PASS;
+	int rc;
+
+	if (iint && iint->evm_status == INTEGRITY_PASS)
+		return iint->evm_status;
+
+	/* if status is not PASS, try to check again - against -ENOMEM */
+
+	rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
+			   xattr_value_len, xattr_data.digest);
+	if (rc < 0) {
+		evm_status = (rc == -ENODATA)
+		    ? INTEGRITY_NOXATTRS : INTEGRITY_FAIL;
+		goto out;
+	}
+
+	xattr_data.type = EVM_XATTR_HMAC;
+	rc = vfs_xattr_cmp(dentry, XATTR_NAME_EVM, (u8 *)&xattr_data,
+			   sizeof xattr_data, GFP_NOFS);
+	if (rc < 0)
+		evm_status = (rc == -ENODATA)
+		    ? INTEGRITY_NOLABEL : INTEGRITY_FAIL;
+out:
+	if (iint)
+		iint->evm_status = evm_status;
+	return evm_status;
+}
+
+static int evm_protected_xattr(const char *req_xattr_name)
+{
+	char **xattrname;
+	int namelen;
+	int found = 0;
+
+	namelen = strlen(req_xattr_name);
+	for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
+		if ((strlen(*xattrname) == namelen)
+		    && (strncmp(req_xattr_name, *xattrname, namelen) == 0)) {
+			found = 1;
+			break;
+		}
+		if (strncmp(req_xattr_name,
+			    *xattrname + XATTR_SECURITY_PREFIX_LEN,
+			    strlen(req_xattr_name)) == 0) {
+			found = 1;
+			break;
+		}
+	}
+	return found;
+}
+
+/**
+ * evm_verifyxattr - verify the integrity of the requested xattr
+ * @dentry: object of the verify xattr
+ * @xattr_name: requested xattr
+ * @xattr_value: requested xattr value
+ * @xattr_value_len: requested xattr value length
+ *
+ * Calculate the HMAC for the given dentry and verify it against the stored
+ * security.evm xattr. For performance, use the xattr value and length
+ * previously retrieved to calculate the HMAC.
+ *
+ * Returns the xattr integrity status.
+ *
+ * This function requires the caller to lock the inode's i_mutex before it
+ * is executed.
+ */
+enum integrity_status evm_verifyxattr(struct dentry *dentry,
+				      const char *xattr_name,
+				      void *xattr_value, size_t xattr_value_len,
+				      struct integrity_iint_cache *iint)
+{
+	if (!evm_initialized || !evm_protected_xattr(xattr_name))
+		return INTEGRITY_UNKNOWN;
+
+	if (!iint) {
+		iint = integrity_iint_find(dentry->d_inode);
+		if (!iint)
+			return INTEGRITY_UNKNOWN;
+	}
+	return evm_verify_hmac(dentry, xattr_name, xattr_value,
+				 xattr_value_len, iint);
+}
+EXPORT_SYMBOL_GPL(evm_verifyxattr);
+
+/*
+ * evm_verify_current_integrity - verify the dentry's metadata integrity
+ * @dentry: pointer to the affected dentry
+ *
+ * Verify and return the dentry's metadata integrity. The exceptions are
+ * before EVM is initialized or in 'fix' mode.
+ */
+static enum integrity_status evm_verify_current_integrity(struct dentry *dentry)
+{
+	struct inode *inode = dentry->d_inode;
+
+	if (!evm_initialized || !S_ISREG(inode->i_mode) || evm_fixmode)
+		return 0;
+	return evm_verify_hmac(dentry, NULL, NULL, 0, NULL);
+}
+
+/*
+ * evm_protect_xattr - protect the EVM extended attribute
+ *
+ * Prevent security.evm from being modified or removed without the
+ * necessary permissions or when the existing value is invalid.
+ *
+ * The posix xattr acls are 'system' prefixed, which normally would not
+ * affect security.evm.  An interesting side affect of writing posix xattr
+ * acls is their modifying of the i_mode, which is included in security.evm.
+ * For posix xattr acls only, permit security.evm, even if it currently
+ * doesn't exist, to be updated.
+ */
+static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
+			     const void *xattr_value, size_t xattr_value_len)
+{
+	enum integrity_status evm_status;
+
+	if (strcmp(xattr_name, XATTR_NAME_EVM) == 0) {
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+	} else if (!evm_protected_xattr(xattr_name)) {
+		if (!posix_xattr_acl(xattr_name))
+			return 0;
+		evm_status = evm_verify_current_integrity(dentry);
+		if ((evm_status == INTEGRITY_PASS) ||
+		    (evm_status == INTEGRITY_NOXATTRS))
+			return 0;
+		return -EPERM;
+	}
+	evm_status = evm_verify_current_integrity(dentry);
+	return evm_status == INTEGRITY_PASS ? 0 : -EPERM;
+}
+
+/**
+ * evm_inode_setxattr - protect the EVM extended attribute
+ * @dentry: pointer to the affected dentry
+ * @xattr_name: pointer to the affected extended attribute name
+ * @xattr_value: pointer to the new extended attribute value
+ * @xattr_value_len: pointer to the new extended attribute value length
+ *
+ * Updating 'security.evm' requires CAP_SYS_ADMIN privileges and that
+ * the current value is valid.
+ */
+int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name,
+		       const void *xattr_value, size_t xattr_value_len)
+{
+	return evm_protect_xattr(dentry, xattr_name, xattr_value,
+				 xattr_value_len);
+}
+
+/**
+ * evm_inode_removexattr - protect the EVM extended attribute
+ * @dentry: pointer to the affected dentry
+ * @xattr_name: pointer to the affected extended attribute name
+ *
+ * Removing 'security.evm' requires CAP_SYS_ADMIN privileges and that
+ * the current value is valid.
+ */
+int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name)
+{
+	return evm_protect_xattr(dentry, xattr_name, NULL, 0);
+}
+
+/**
+ * evm_inode_post_setxattr - update 'security.evm' to reflect the changes
+ * @dentry: pointer to the affected dentry
+ * @xattr_name: pointer to the affected extended attribute name
+ * @xattr_value: pointer to the new extended attribute value
+ * @xattr_value_len: pointer to the new extended attribute value length
+ *
+ * Update the HMAC stored in 'security.evm' to reflect the change.
+ *
+ * No need to take the i_mutex lock here, as this function is called from
+ * __vfs_setxattr_noperm().  The caller of which has taken the inode's
+ * i_mutex lock.
+ */
+void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name,
+			     const void *xattr_value, size_t xattr_value_len)
+{
+	if (!evm_initialized || (!evm_protected_xattr(xattr_name)
+				 && !posix_xattr_acl(xattr_name)))
+		return;
+
+	evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len);
+	return;
+}
+
+/**
+ * evm_inode_post_removexattr - update 'security.evm' after removing the xattr
+ * @dentry: pointer to the affected dentry
+ * @xattr_name: pointer to the affected extended attribute name
+ *
+ * Update the HMAC stored in 'security.evm' to reflect removal of the xattr.
+ */
+void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name)
+{
+	struct inode *inode = dentry->d_inode;
+
+	if (!evm_initialized || !evm_protected_xattr(xattr_name))
+		return;
+
+	mutex_lock(&inode->i_mutex);
+	evm_update_evmxattr(dentry, xattr_name, NULL, 0);
+	mutex_unlock(&inode->i_mutex);
+	return;
+}
+
+/**
+ * evm_inode_setattr - prevent updating an invalid EVM extended attribute
+ * @dentry: pointer to the affected dentry
+ */
+int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
+{
+	unsigned int ia_valid = attr->ia_valid;
+	enum integrity_status evm_status;
+
+	if (!(ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)))
+		return 0;
+	evm_status = evm_verify_current_integrity(dentry);
+	if ((evm_status == INTEGRITY_PASS) ||
+	    (evm_status == INTEGRITY_NOXATTRS))
+		return 0;
+	return -EPERM;
+}
+
+/**
+ * evm_inode_post_setattr - update 'security.evm' after modifying metadata
+ * @dentry: pointer to the affected dentry
+ * @ia_valid: for the UID and GID status
+ *
+ * For now, update the HMAC stored in 'security.evm' to reflect UID/GID
+ * changes.
+ *
+ * This function is called from notify_change(), which expects the caller
+ * to lock the inode's i_mutex.
+ */
+void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
+{
+	if (!evm_initialized)
+		return;
+
+	if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
+		evm_update_evmxattr(dentry, NULL, NULL, 0);
+	return;
+}
+
+/*
+ * evm_inode_init_security - initializes security.evm
+ */
+int evm_inode_init_security(struct inode *inode,
+				 const struct xattr *lsm_xattr,
+				 struct xattr *evm_xattr)
+{
+	struct evm_ima_xattr_data *xattr_data;
+	int rc;
+
+	if (!evm_initialized || !evm_protected_xattr(lsm_xattr->name))
+		return 0;
+
+	xattr_data = kzalloc(sizeof(*xattr_data), GFP_NOFS);
+	if (!xattr_data)
+		return -ENOMEM;
+
+	xattr_data->type = EVM_XATTR_HMAC;
+	rc = evm_init_hmac(inode, lsm_xattr, xattr_data->digest);
+	if (rc < 0)
+		goto out;
+
+	evm_xattr->value = xattr_data;
+	evm_xattr->value_len = sizeof(*xattr_data);
+	evm_xattr->name = kstrdup(XATTR_EVM_SUFFIX, GFP_NOFS);
+	return 0;
+out:
+	kfree(xattr_data);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(evm_inode_init_security);
+
+static int __init init_evm(void)
+{
+	int error;
+
+	error = evm_init_secfs();
+	if (error < 0) {
+		printk(KERN_INFO "EVM: Error registering secfs\n");
+		goto err;
+	}
+err:
+	return error;
+}
+
+static void __exit cleanup_evm(void)
+{
+	evm_cleanup_secfs();
+	if (hmac_tfm)
+		crypto_free_shash(hmac_tfm);
+}
+
+/*
+ * evm_display_config - list the EVM protected security extended attributes
+ */
+static int __init evm_display_config(void)
+{
+	char **xattrname;
+
+	for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++)
+		printk(KERN_INFO "EVM: %s\n", *xattrname);
+	return 0;
+}
+
+pure_initcall(evm_display_config);
+late_initcall(init_evm);
+
+MODULE_DESCRIPTION("Extended Verification Module");
+MODULE_LICENSE("GPL");

+ 26 - 0
security/integrity/evm/evm_posix_acl.c

@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2011 IBM Corporation
+ *
+ * Author:
+ * Mimi Zohar <zohar@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ */
+
+#include <linux/module.h>
+#include <linux/xattr.h>
+
+int posix_xattr_acl(char *xattr)
+{
+	int xattr_len = strlen(xattr);
+
+	if ((strlen(XATTR_NAME_POSIX_ACL_ACCESS) == xattr_len)
+	     && (strncmp(XATTR_NAME_POSIX_ACL_ACCESS, xattr, xattr_len) == 0))
+		return 1;
+	if ((strlen(XATTR_NAME_POSIX_ACL_DEFAULT) == xattr_len)
+	     && (strncmp(XATTR_NAME_POSIX_ACL_DEFAULT, xattr, xattr_len) == 0))
+		return 1;
+	return 0;
+}

+ 108 - 0
security/integrity/evm/evm_secfs.c

@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * File: evm_secfs.c
+ *	- Used to signal when key is on keyring
+ *	- Get the key and enable EVM
+ */
+
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include "evm.h"
+
+static struct dentry *evm_init_tpm;
+
+/**
+ * evm_read_key - read() for <securityfs>/evm
+ *
+ * @filp: file pointer, not actually used
+ * @buf: where to put the result
+ * @count: maximum to send along
+ * @ppos: where to start
+ *
+ * Returns number of bytes read or error code, as appropriate
+ */
+static ssize_t evm_read_key(struct file *filp, char __user *buf,
+			    size_t count, loff_t *ppos)
+{
+	char temp[80];
+	ssize_t rc;
+
+	if (*ppos != 0)
+		return 0;
+
+	sprintf(temp, "%d", evm_initialized);
+	rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
+
+	return rc;
+}
+
+/**
+ * evm_write_key - write() for <securityfs>/evm
+ * @file: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start
+ *
+ * Used to signal that key is on the kernel key ring.
+ * - get the integrity hmac key from the kernel key ring
+ * - create list of hmac protected extended attributes
+ * Returns number of bytes written or error code, as appropriate
+ */
+static ssize_t evm_write_key(struct file *file, const char __user *buf,
+			     size_t count, loff_t *ppos)
+{
+	char temp[80];
+	int i, error;
+
+	if (!capable(CAP_SYS_ADMIN) || evm_initialized)
+		return -EPERM;
+
+	if (count >= sizeof(temp) || count == 0)
+		return -EINVAL;
+
+	if (copy_from_user(temp, buf, count) != 0)
+		return -EFAULT;
+
+	temp[count] = '\0';
+
+	if ((sscanf(temp, "%d", &i) != 1) || (i != 1))
+		return -EINVAL;
+
+	error = evm_init_key();
+	if (!error) {
+		evm_initialized = 1;
+		pr_info("EVM: initialized\n");
+	} else
+		pr_err("EVM: initialization failed\n");
+	return count;
+}
+
+static const struct file_operations evm_key_ops = {
+	.read		= evm_read_key,
+	.write		= evm_write_key,
+};
+
+int __init evm_init_secfs(void)
+{
+	int error = 0;
+
+	evm_init_tpm = securityfs_create_file("evm", S_IRUSR | S_IRGRP,
+					      NULL, NULL, &evm_key_ops);
+	if (!evm_init_tpm || IS_ERR(evm_init_tpm))
+		error = -EFAULT;
+	return error;
+}
+
+void __exit evm_cleanup_secfs(void)
+{
+	if (evm_init_tpm)
+		securityfs_remove(evm_init_tpm);
+}

+ 172 - 0
security/integrity/iint.c

@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2008 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: integrity_iint.c
+ *	- implements the integrity hooks: integrity_inode_alloc,
+ *	  integrity_inode_free
+ *	- cache integrity information associated with an inode
+ *	  using a rbtree tree.
+ */
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/rbtree.h>
+#include "integrity.h"
+
+static struct rb_root integrity_iint_tree = RB_ROOT;
+static DEFINE_SPINLOCK(integrity_iint_lock);
+static struct kmem_cache *iint_cache __read_mostly;
+
+int iint_initialized;
+
+/*
+ * __integrity_iint_find - return the iint associated with an inode
+ */
+static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode)
+{
+	struct integrity_iint_cache *iint;
+	struct rb_node *n = integrity_iint_tree.rb_node;
+
+	assert_spin_locked(&integrity_iint_lock);
+
+	while (n) {
+		iint = rb_entry(n, struct integrity_iint_cache, rb_node);
+
+		if (inode < iint->inode)
+			n = n->rb_left;
+		else if (inode > iint->inode)
+			n = n->rb_right;
+		else
+			break;
+	}
+	if (!n)
+		return NULL;
+
+	return iint;
+}
+
+/*
+ * integrity_iint_find - return the iint associated with an inode
+ */
+struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
+{
+	struct integrity_iint_cache *iint;
+
+	if (!IS_IMA(inode))
+		return NULL;
+
+	spin_lock(&integrity_iint_lock);
+	iint = __integrity_iint_find(inode);
+	spin_unlock(&integrity_iint_lock);
+
+	return iint;
+}
+
+static void iint_free(struct integrity_iint_cache *iint)
+{
+	iint->version = 0;
+	iint->flags = 0UL;
+	iint->evm_status = INTEGRITY_UNKNOWN;
+	kmem_cache_free(iint_cache, iint);
+}
+
+/**
+ * integrity_inode_alloc - allocate an iint associated with an inode
+ * @inode: pointer to the inode
+ */
+int integrity_inode_alloc(struct inode *inode)
+{
+	struct rb_node **p;
+	struct rb_node *new_node, *parent = NULL;
+	struct integrity_iint_cache *new_iint, *test_iint;
+	int rc;
+
+	new_iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
+	if (!new_iint)
+		return -ENOMEM;
+
+	new_iint->inode = inode;
+	new_node = &new_iint->rb_node;
+
+	mutex_lock(&inode->i_mutex);	/* i_flags */
+	spin_lock(&integrity_iint_lock);
+
+	p = &integrity_iint_tree.rb_node;
+	while (*p) {
+		parent = *p;
+		test_iint = rb_entry(parent, struct integrity_iint_cache,
+				     rb_node);
+		rc = -EEXIST;
+		if (inode < test_iint->inode)
+			p = &(*p)->rb_left;
+		else if (inode > test_iint->inode)
+			p = &(*p)->rb_right;
+		else
+			goto out_err;
+	}
+
+	inode->i_flags |= S_IMA;
+	rb_link_node(new_node, parent, p);
+	rb_insert_color(new_node, &integrity_iint_tree);
+
+	spin_unlock(&integrity_iint_lock);
+	mutex_unlock(&inode->i_mutex);	/* i_flags */
+
+	return 0;
+out_err:
+	spin_unlock(&integrity_iint_lock);
+	mutex_unlock(&inode->i_mutex);	/* i_flags */
+	iint_free(new_iint);
+
+	return rc;
+}
+
+/**
+ * integrity_inode_free - called on security_inode_free
+ * @inode: pointer to the inode
+ *
+ * Free the integrity information(iint) associated with an inode.
+ */
+void integrity_inode_free(struct inode *inode)
+{
+	struct integrity_iint_cache *iint;
+
+	if (!IS_IMA(inode))
+		return;
+
+	spin_lock(&integrity_iint_lock);
+	iint = __integrity_iint_find(inode);
+	rb_erase(&iint->rb_node, &integrity_iint_tree);
+	spin_unlock(&integrity_iint_lock);
+
+	iint_free(iint);
+}
+
+static void init_once(void *foo)
+{
+	struct integrity_iint_cache *iint = foo;
+
+	memset(iint, 0, sizeof *iint);
+	iint->version = 0;
+	iint->flags = 0UL;
+	mutex_init(&iint->mutex);
+	iint->evm_status = INTEGRITY_UNKNOWN;
+}
+
+static int __init integrity_iintcache_init(void)
+{
+	iint_cache =
+	    kmem_cache_create("iint_cache", sizeof(struct integrity_iint_cache),
+			      0, SLAB_PANIC, init_once);
+	iint_initialized = 1;
+	return 0;
+}
+security_initcall(integrity_iintcache_init);

+ 1 - 0
security/integrity/ima/Kconfig

@@ -3,6 +3,7 @@
 config IMA
 config IMA
 	bool "Integrity Measurement Architecture(IMA)"
 	bool "Integrity Measurement Architecture(IMA)"
 	depends on SECURITY
 	depends on SECURITY
+	select INTEGRITY
 	select SECURITYFS
 	select SECURITYFS
 	select CRYPTO
 	select CRYPTO
 	select CRYPTO_HMAC
 	select CRYPTO_HMAC

+ 1 - 1
security/integrity/ima/Makefile

@@ -6,4 +6,4 @@
 obj-$(CONFIG_IMA) += ima.o
 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-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
-	 ima_policy.o ima_iint.o ima_audit.o
+	 ima_policy.o ima_audit.o

+ 9 - 21
security/integrity/ima/ima.h

@@ -24,18 +24,19 @@
 #include <linux/tpm.h>
 #include <linux/tpm.h>
 #include <linux/audit.h>
 #include <linux/audit.h>
 
 
+#include "../integrity.h"
+
 enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_ASCII };
 enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_ASCII };
 enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
 enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
 
 
 /* digest size for IMA, fits SHA1 or MD5 */
 /* digest size for IMA, fits SHA1 or MD5 */
-#define IMA_DIGEST_SIZE		20
+#define IMA_DIGEST_SIZE		SHA1_DIGEST_SIZE
 #define IMA_EVENT_NAME_LEN_MAX	255
 #define IMA_EVENT_NAME_LEN_MAX	255
 
 
 #define IMA_HASH_BITS 9
 #define IMA_HASH_BITS 9
 #define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS)
 #define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS)
 
 
 /* set during initialization */
 /* set during initialization */
-extern int iint_initialized;
 extern int ima_initialized;
 extern int ima_initialized;
 extern int ima_used_chip;
 extern int ima_used_chip;
 extern char *ima_hash;
 extern char *ima_hash;
@@ -96,34 +97,21 @@ static inline unsigned long ima_hash_key(u8 *digest)
 	return hash_long(*digest, IMA_HASH_BITS);
 	return hash_long(*digest, IMA_HASH_BITS);
 }
 }
 
 
-/* iint cache flags */
-#define IMA_MEASURED		0x01
-
-/* integrity data associated with an inode */
-struct ima_iint_cache {
-	struct rb_node rb_node; /* rooted in ima_iint_tree */
-	struct inode *inode;	/* back pointer to inode in question */
-	u64 version;		/* track inode changes */
-	unsigned char flags;
-	u8 digest[IMA_DIGEST_SIZE];
-	struct mutex mutex;	/* protects: version, flags, digest */
-};
-
 /* LIM API function definitions */
 /* LIM API function definitions */
 int ima_must_measure(struct inode *inode, int mask, int function);
 int ima_must_measure(struct inode *inode, int mask, int function);
-int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file);
-void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
+int ima_collect_measurement(struct integrity_iint_cache *iint,
+			    struct file *file);
+void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
 			   const unsigned char *filename);
 			   const unsigned char *filename);
 int ima_store_template(struct ima_template_entry *entry, int violation,
 int ima_store_template(struct ima_template_entry *entry, int violation,
 		       struct inode *inode);
 		       struct inode *inode);
-void ima_template_show(struct seq_file *m, void *e,
-		       enum ima_show_type show);
+void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show);
 
 
 /* rbtree tree calls to lookup, insert, delete
 /* rbtree tree calls to lookup, insert, delete
  * integrity data associated with an inode.
  * integrity data associated with an inode.
  */
  */
-struct ima_iint_cache *ima_iint_insert(struct inode *inode);
-struct ima_iint_cache *ima_iint_find(struct inode *inode);
+struct integrity_iint_cache *integrity_iint_insert(struct inode *inode);
+struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
 
 
 /* IMA policy related functions */
 /* IMA policy related functions */
 enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK };
 enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK };

+ 4 - 3
security/integrity/ima/ima_api.c

@@ -126,7 +126,8 @@ int ima_must_measure(struct inode *inode, int mask, int function)
  *
  *
  * Return 0 on success, error code otherwise
  * Return 0 on success, error code otherwise
  */
  */
-int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file)
+int ima_collect_measurement(struct integrity_iint_cache *iint,
+			    struct file *file)
 {
 {
 	int result = -EEXIST;
 	int result = -EEXIST;
 
 
@@ -156,8 +157,8 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file)
  *
  *
  * Must be called with iint->mutex held.
  * Must be called with iint->mutex held.
  */
  */
-void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
-			   const unsigned char *filename)
+void ima_store_measurement(struct integrity_iint_cache *iint,
+			   struct file *file, const unsigned char *filename)
 {
 {
 	const char *op = "add_template_measure";
 	const char *op = "add_template_measure";
 	const char *audit_cause = "ENOMEM";
 	const char *audit_cause = "ENOMEM";

+ 1 - 1
security/integrity/ima/ima_fs.c

@@ -287,7 +287,7 @@ static atomic_t policy_opencount = ATOMIC_INIT(1);
 /*
 /*
  * ima_open_policy: sequentialize access to the policy file
  * ima_open_policy: sequentialize access to the policy file
  */
  */
-int ima_open_policy(struct inode * inode, struct file * filp)
+static int ima_open_policy(struct inode * inode, struct file * filp)
 {
 {
 	/* No point in being allowed to open it if you aren't going to write */
 	/* No point in being allowed to open it if you aren't going to write */
 	if (!(filp->f_flags & O_WRONLY))
 	if (!(filp->f_flags & O_WRONLY))

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

@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2008 IBM Corporation
- *
- * Authors:
- * Mimi Zohar <zohar@us.ibm.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2 of the
- * License.
- *
- * File: ima_iint.c
- * 	- implements the IMA hooks: ima_inode_alloc, ima_inode_free
- *	- cache integrity information associated with an inode
- *	  using a rbtree tree.
- */
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-#include <linux/rbtree.h>
-#include "ima.h"
-
-static struct rb_root ima_iint_tree = RB_ROOT;
-static DEFINE_SPINLOCK(ima_iint_lock);
-static struct kmem_cache *iint_cache __read_mostly;
-
-int iint_initialized = 0;
-
-/*
- * __ima_iint_find - return the iint associated with an inode
- */
-static struct ima_iint_cache *__ima_iint_find(struct inode *inode)
-{
-	struct ima_iint_cache *iint;
-	struct rb_node *n = ima_iint_tree.rb_node;
-
-	assert_spin_locked(&ima_iint_lock);
-
-	while (n) {
-		iint = rb_entry(n, struct ima_iint_cache, rb_node);
-
-		if (inode < iint->inode)
-			n = n->rb_left;
-		else if (inode > iint->inode)
-			n = n->rb_right;
-		else
-			break;
-	}
-	if (!n)
-		return NULL;
-
-	return iint;
-}
-
-/*
- * ima_iint_find - return the iint associated with an inode
- */
-struct ima_iint_cache *ima_iint_find(struct inode *inode)
-{
-	struct ima_iint_cache *iint;
-
-	if (!IS_IMA(inode))
-		return NULL;
-
-	spin_lock(&ima_iint_lock);
-	iint = __ima_iint_find(inode);
-	spin_unlock(&ima_iint_lock);
-
-	return iint;
-}
-
-static void iint_free(struct ima_iint_cache *iint)
-{
-	iint->version = 0;
-	iint->flags = 0UL;
-	kmem_cache_free(iint_cache, iint);
-}
-
-/**
- * ima_inode_alloc - allocate an iint associated with an inode
- * @inode: pointer to the inode
- */
-int ima_inode_alloc(struct inode *inode)
-{
-	struct rb_node **p;
-	struct rb_node *new_node, *parent = NULL;
-	struct ima_iint_cache *new_iint, *test_iint;
-	int rc;
-
-	new_iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
-	if (!new_iint)
-		return -ENOMEM;
-
-	new_iint->inode = inode;
-	new_node = &new_iint->rb_node;
-
-	mutex_lock(&inode->i_mutex); /* i_flags */
-	spin_lock(&ima_iint_lock);
-
-	p = &ima_iint_tree.rb_node;
-	while (*p) {
-		parent = *p;
-		test_iint = rb_entry(parent, struct ima_iint_cache, rb_node);
-
-		rc = -EEXIST;
-		if (inode < test_iint->inode)
-			p = &(*p)->rb_left;
-		else if (inode > test_iint->inode)
-			p = &(*p)->rb_right;
-		else
-			goto out_err;
-	}
-
-	inode->i_flags |= S_IMA;
-	rb_link_node(new_node, parent, p);
-	rb_insert_color(new_node, &ima_iint_tree);
-
-	spin_unlock(&ima_iint_lock);
-	mutex_unlock(&inode->i_mutex); /* i_flags */
-
-	return 0;
-out_err:
-	spin_unlock(&ima_iint_lock);
-	mutex_unlock(&inode->i_mutex); /* i_flags */
-	iint_free(new_iint);
-
-	return rc;
-}
-
-/**
- * ima_inode_free - called on security_inode_free
- * @inode: pointer to the inode
- *
- * Free the integrity information(iint) associated with an inode.
- */
-void ima_inode_free(struct inode *inode)
-{
-	struct ima_iint_cache *iint;
-
-	if (!IS_IMA(inode))
-		return;
-
-	spin_lock(&ima_iint_lock);
-	iint = __ima_iint_find(inode);
-	rb_erase(&iint->rb_node, &ima_iint_tree);
-	spin_unlock(&ima_iint_lock);
-
-	iint_free(iint);
-}
-
-static void init_once(void *foo)
-{
-	struct ima_iint_cache *iint = foo;
-
-	memset(iint, 0, sizeof *iint);
-	iint->version = 0;
-	iint->flags = 0UL;
-	mutex_init(&iint->mutex);
-}
-
-static int __init ima_iintcache_init(void)
-{
-	iint_cache =
-	    kmem_cache_create("iint_cache", sizeof(struct ima_iint_cache), 0,
-			      SLAB_PANIC, init_once);
-	iint_initialized = 1;
-	return 0;
-}
-security_initcall(ima_iintcache_init);

+ 7 - 6
security/integrity/ima/ima_main.c

@@ -22,6 +22,7 @@
 #include <linux/mount.h>
 #include <linux/mount.h>
 #include <linux/mman.h>
 #include <linux/mman.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
+#include <linux/ima.h>
 
 
 #include "ima.h"
 #include "ima.h"
 
 
@@ -82,7 +83,7 @@ out:
 				  "open_writers");
 				  "open_writers");
 }
 }
 
 
-static void ima_check_last_writer(struct ima_iint_cache *iint,
+static void ima_check_last_writer(struct integrity_iint_cache *iint,
 				  struct inode *inode,
 				  struct inode *inode,
 				  struct file *file)
 				  struct file *file)
 {
 {
@@ -105,12 +106,12 @@ static void ima_check_last_writer(struct ima_iint_cache *iint,
 void ima_file_free(struct file *file)
 void ima_file_free(struct file *file)
 {
 {
 	struct inode *inode = file->f_dentry->d_inode;
 	struct inode *inode = file->f_dentry->d_inode;
-	struct ima_iint_cache *iint;
+	struct integrity_iint_cache *iint;
 
 
 	if (!iint_initialized || !S_ISREG(inode->i_mode))
 	if (!iint_initialized || !S_ISREG(inode->i_mode))
 		return;
 		return;
 
 
-	iint = ima_iint_find(inode);
+	iint = integrity_iint_find(inode);
 	if (!iint)
 	if (!iint)
 		return;
 		return;
 
 
@@ -121,7 +122,7 @@ static int process_measurement(struct file *file, const unsigned char *filename,
 			       int mask, int function)
 			       int mask, int function)
 {
 {
 	struct inode *inode = file->f_dentry->d_inode;
 	struct inode *inode = file->f_dentry->d_inode;
-	struct ima_iint_cache *iint;
+	struct integrity_iint_cache *iint;
 	int rc = 0;
 	int rc = 0;
 
 
 	if (!ima_initialized || !S_ISREG(inode->i_mode))
 	if (!ima_initialized || !S_ISREG(inode->i_mode))
@@ -131,9 +132,9 @@ static int process_measurement(struct file *file, const unsigned char *filename,
 	if (rc != 0)
 	if (rc != 0)
 		return rc;
 		return rc;
 retry:
 retry:
-	iint = ima_iint_find(inode);
+	iint = integrity_iint_find(inode);
 	if (!iint) {
 	if (!iint) {
-		rc = ima_inode_alloc(inode);
+		rc = integrity_inode_alloc(inode);
 		if (!rc || rc == -EEXIST)
 		if (!rc || rc == -EEXIST)
 			goto retry;
 			goto retry;
 		return rc;
 		return rc;

+ 50 - 0
security/integrity/integrity.h

@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2009-2010 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/integrity.h>
+#include <crypto/sha.h>
+
+/* iint cache flags */
+#define IMA_MEASURED		0x01
+
+enum evm_ima_xattr_type {
+	IMA_XATTR_DIGEST = 0x01,
+	EVM_XATTR_HMAC,
+	EVM_IMA_XATTR_DIGSIG,
+};
+
+struct evm_ima_xattr_data {
+	u8 type;
+	u8 digest[SHA1_DIGEST_SIZE];
+}  __attribute__((packed));
+
+/* integrity data associated with an inode */
+struct integrity_iint_cache {
+	struct rb_node rb_node; /* rooted in integrity_iint_tree */
+	struct inode *inode;	/* back pointer to inode in question */
+	u64 version;		/* track inode changes */
+	unsigned char flags;
+	u8 digest[SHA1_DIGEST_SIZE];
+	struct mutex mutex;	/* protects: version, flags, digest */
+	enum integrity_status evm_status;
+};
+
+/* rbtree tree calls to lookup, insert, delete
+ * integrity data associated with an inode.
+ */
+struct integrity_iint_cache *integrity_iint_insert(struct inode *inode);
+struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
+
+/* set during initialization */
+extern int iint_initialized;

+ 1 - 1
security/keys/Makefile

@@ -14,7 +14,7 @@ obj-y := \
 	user_defined.o
 	user_defined.o
 
 
 obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
 obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
-obj-$(CONFIG_ENCRYPTED_KEYS) += ecryptfs_format.o encrypted.o
+obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
 obj-$(CONFIG_KEYS_COMPAT) += compat.o
 obj-$(CONFIG_KEYS_COMPAT) += compat.o
 obj-$(CONFIG_PROC_FS) += proc.o
 obj-$(CONFIG_PROC_FS) += proc.o
 obj-$(CONFIG_SYSCTL) += sysctl.o
 obj-$(CONFIG_SYSCTL) += sysctl.o

+ 6 - 0
security/keys/encrypted-keys/Makefile

@@ -0,0 +1,6 @@
+#
+# Makefile for encrypted keys
+#
+
+obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted.o ecryptfs_format.o
+obj-$(CONFIG_TRUSTED_KEYS) += masterkey_trusted.o

+ 0 - 0
security/keys/ecryptfs_format.c → security/keys/encrypted-keys/ecryptfs_format.c


+ 0 - 0
security/keys/ecryptfs_format.h → security/keys/encrypted-keys/ecryptfs_format.h


+ 19 - 30
security/keys/encrypted.c → security/keys/encrypted-keys/encrypted.c

@@ -298,31 +298,6 @@ out:
 	return ascii_buf;
 	return ascii_buf;
 }
 }
 
 
-/*
- * request_trusted_key - request the trusted key
- *
- * Trusted keys are sealed to PCRs and other metadata. Although userspace
- * manages both trusted/encrypted key-types, like the encrypted key type
- * data, trusted key type data is not visible decrypted from userspace.
- */
-static struct key *request_trusted_key(const char *trusted_desc,
-				       u8 **master_key, size_t *master_keylen)
-{
-	struct trusted_key_payload *tpayload;
-	struct key *tkey;
-
-	tkey = request_key(&key_type_trusted, trusted_desc, NULL);
-	if (IS_ERR(tkey))
-		goto error;
-
-	down_read(&tkey->sem);
-	tpayload = rcu_dereference(tkey->payload.data);
-	*master_key = tpayload->key;
-	*master_keylen = tpayload->key_len;
-error:
-	return tkey;
-}
-
 /*
 /*
  * request_user_key - request the user key
  * request_user_key - request the user key
  *
  *
@@ -469,8 +444,14 @@ static struct key *request_master_key(struct encrypted_key_payload *epayload,
 		goto out;
 		goto out;
 
 
 	if (IS_ERR(mkey)) {
 	if (IS_ERR(mkey)) {
-		pr_info("encrypted_key: key %s not found",
-			epayload->master_desc);
+		int ret = PTR_ERR(epayload);
+
+		if (ret == -ENOTSUPP)
+			pr_info("encrypted_key: key %s not supported",
+				epayload->master_desc);
+		else
+			pr_info("encrypted_key: key %s not found",
+				epayload->master_desc);
 		goto out;
 		goto out;
 	}
 	}
 
 
@@ -686,11 +667,19 @@ static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
 		return -EINVAL;
 		return -EINVAL;
 
 
 	hex_encoded_data = hex_encoded_iv + (2 * ivsize) + 2;
 	hex_encoded_data = hex_encoded_iv + (2 * ivsize) + 2;
-	hex2bin(epayload->iv, hex_encoded_iv, ivsize);
-	hex2bin(epayload->encrypted_data, hex_encoded_data, encrypted_datalen);
+	ret = hex2bin(epayload->iv, hex_encoded_iv, ivsize);
+	if (ret < 0)
+		return -EINVAL;
+	ret = hex2bin(epayload->encrypted_data, hex_encoded_data,
+		      encrypted_datalen);
+	if (ret < 0)
+		return -EINVAL;
 
 
 	hmac = epayload->format + epayload->datablob_len;
 	hmac = epayload->format + epayload->datablob_len;
-	hex2bin(hmac, hex_encoded_data + (encrypted_datalen * 2), HASH_SIZE);
+	ret = hex2bin(hmac, hex_encoded_data + (encrypted_datalen * 2),
+		      HASH_SIZE);
+	if (ret < 0)
+		return -EINVAL;
 
 
 	mkey = request_master_key(epayload, &master_key, &master_keylen);
 	mkey = request_master_key(epayload, &master_key, &master_keylen);
 	if (IS_ERR(mkey))
 	if (IS_ERR(mkey))

+ 11 - 0
security/keys/encrypted.h → security/keys/encrypted-keys/encrypted.h

@@ -2,6 +2,17 @@
 #define __ENCRYPTED_KEY_H
 #define __ENCRYPTED_KEY_H
 
 
 #define ENCRYPTED_DEBUG 0
 #define ENCRYPTED_DEBUG 0
+#ifdef CONFIG_TRUSTED_KEYS
+extern struct key *request_trusted_key(const char *trusted_desc,
+				       u8 **master_key, size_t *master_keylen);
+#else
+static inline struct key *request_trusted_key(const char *trusted_desc,
+					      u8 **master_key,
+					      size_t *master_keylen)
+{
+	return ERR_PTR(-EOPNOTSUPP);
+}
+#endif
 
 
 #if ENCRYPTED_DEBUG
 #if ENCRYPTED_DEBUG
 static inline void dump_master_key(const u8 *master_key, size_t master_keylen)
 static inline void dump_master_key(const u8 *master_key, size_t master_keylen)

+ 45 - 0
security/keys/encrypted-keys/masterkey_trusted.c

@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010 IBM Corporation
+ * Copyright (C) 2010 Politecnico di Torino, Italy
+ *                    TORSEC group -- http://security.polito.it
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ * Roberto Sassu <roberto.sassu@polito.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * See Documentation/security/keys-trusted-encrypted.txt
+ */
+
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <keys/trusted-type.h>
+
+/*
+ * request_trusted_key - request the trusted key
+ *
+ * Trusted keys are sealed to PCRs and other metadata. Although userspace
+ * manages both trusted/encrypted key-types, like the encrypted key type
+ * data, trusted key type data is not visible decrypted from userspace.
+ */
+struct key *request_trusted_key(const char *trusted_desc,
+				u8 **master_key, size_t *master_keylen)
+{
+	struct trusted_key_payload *tpayload;
+	struct key *tkey;
+
+	tkey = request_key(&key_type_trusted, trusted_desc, NULL);
+	if (IS_ERR(tkey))
+		goto error;
+
+	down_read(&tkey->sem);
+	tpayload = rcu_dereference(tkey->payload.data);
+	*master_key = tpayload->key;
+	*master_keylen = tpayload->key_len;
+error:
+	return tkey;
+}

+ 276 - 110
security/keys/gc.c

@@ -1,6 +1,6 @@
 /* Key garbage collector
 /* Key garbage collector
  *
  *
- * Copyright (C) 2009 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2009-2011 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  * Written by David Howells (dhowells@redhat.com)
  *
  *
  * This program is free software; you can redistribute it and/or
  * This program is free software; you can redistribute it and/or
@@ -10,6 +10,8 @@
  */
  */
 
 
 #include <linux/module.h>
 #include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/security.h>
 #include <keys/keyring-type.h>
 #include <keys/keyring-type.h>
 #include "internal.h"
 #include "internal.h"
 
 
@@ -19,17 +21,33 @@
 unsigned key_gc_delay = 5 * 60;
 unsigned key_gc_delay = 5 * 60;
 
 
 /*
 /*
- * Reaper
+ * Reaper for unused keys.
+ */
+static void key_garbage_collector(struct work_struct *work);
+DECLARE_WORK(key_gc_work, key_garbage_collector);
+
+/*
+ * Reaper for links from keyrings to dead keys.
  */
  */
 static void key_gc_timer_func(unsigned long);
 static void key_gc_timer_func(unsigned long);
-static void key_garbage_collector(struct work_struct *);
 static DEFINE_TIMER(key_gc_timer, key_gc_timer_func, 0, 0);
 static DEFINE_TIMER(key_gc_timer, key_gc_timer_func, 0, 0);
-static DECLARE_WORK(key_gc_work, key_garbage_collector);
-static key_serial_t key_gc_cursor; /* the last key the gc considered */
-static bool key_gc_again;
-static unsigned long key_gc_executing;
+
 static time_t key_gc_next_run = LONG_MAX;
 static time_t key_gc_next_run = LONG_MAX;
-static time_t key_gc_new_timer;
+static struct key_type *key_gc_dead_keytype;
+
+static unsigned long key_gc_flags;
+#define KEY_GC_KEY_EXPIRED	0	/* A key expired and needs unlinking */
+#define KEY_GC_REAP_KEYTYPE	1	/* A keytype is being unregistered */
+#define KEY_GC_REAPING_KEYTYPE	2	/* Cleared when keytype reaped */
+
+
+/*
+ * Any key whose type gets unregistered will be re-typed to this if it can't be
+ * immediately unlinked.
+ */
+struct key_type key_type_dead = {
+	.name = "dead",
+};
 
 
 /*
 /*
  * Schedule a garbage collection run.
  * Schedule a garbage collection run.
@@ -42,31 +60,75 @@ void key_schedule_gc(time_t gc_at)
 
 
 	kenter("%ld", gc_at - now);
 	kenter("%ld", gc_at - now);
 
 
-	if (gc_at <= now) {
-		schedule_work(&key_gc_work);
+	if (gc_at <= now || test_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags)) {
+		kdebug("IMMEDIATE");
+		queue_work(system_nrt_wq, &key_gc_work);
 	} else if (gc_at < key_gc_next_run) {
 	} else if (gc_at < key_gc_next_run) {
+		kdebug("DEFERRED");
+		key_gc_next_run = gc_at;
 		expires = jiffies + (gc_at - now) * HZ;
 		expires = jiffies + (gc_at - now) * HZ;
 		mod_timer(&key_gc_timer, expires);
 		mod_timer(&key_gc_timer, expires);
 	}
 	}
 }
 }
 
 
 /*
 /*
- * The garbage collector timer kicked off
+ * Some key's cleanup time was met after it expired, so we need to get the
+ * reaper to go through a cycle finding expired keys.
  */
  */
 static void key_gc_timer_func(unsigned long data)
 static void key_gc_timer_func(unsigned long data)
 {
 {
 	kenter("");
 	kenter("");
 	key_gc_next_run = LONG_MAX;
 	key_gc_next_run = LONG_MAX;
-	schedule_work(&key_gc_work);
+	set_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags);
+	queue_work(system_nrt_wq, &key_gc_work);
+}
+
+/*
+ * wait_on_bit() sleep function for uninterruptible waiting
+ */
+static int key_gc_wait_bit(void *flags)
+{
+	schedule();
+	return 0;
+}
+
+/*
+ * Reap keys of dead type.
+ *
+ * We use three flags to make sure we see three complete cycles of the garbage
+ * collector: the first to mark keys of that type as being dead, the second to
+ * collect dead links and the third to clean up the dead keys.  We have to be
+ * careful as there may already be a cycle in progress.
+ *
+ * The caller must be holding key_types_sem.
+ */
+void key_gc_keytype(struct key_type *ktype)
+{
+	kenter("%s", ktype->name);
+
+	key_gc_dead_keytype = ktype;
+	set_bit(KEY_GC_REAPING_KEYTYPE, &key_gc_flags);
+	smp_mb();
+	set_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags);
+
+	kdebug("schedule");
+	queue_work(system_nrt_wq, &key_gc_work);
+
+	kdebug("sleep");
+	wait_on_bit(&key_gc_flags, KEY_GC_REAPING_KEYTYPE, key_gc_wait_bit,
+		    TASK_UNINTERRUPTIBLE);
+
+	key_gc_dead_keytype = NULL;
+	kleave("");
 }
 }
 
 
 /*
 /*
  * Garbage collect pointers from a keyring.
  * Garbage collect pointers from a keyring.
  *
  *
- * Return true if we altered the keyring.
+ * Not called with any locks held.  The keyring's key struct will not be
+ * deallocated under us as only our caller may deallocate it.
  */
  */
-static bool key_gc_keyring(struct key *keyring, time_t limit)
-	__releases(key_serial_lock)
+static void key_gc_keyring(struct key *keyring, time_t limit)
 {
 {
 	struct keyring_list *klist;
 	struct keyring_list *klist;
 	struct key *key;
 	struct key *key;
@@ -93,130 +155,234 @@ static bool key_gc_keyring(struct key *keyring, time_t limit)
 unlock_dont_gc:
 unlock_dont_gc:
 	rcu_read_unlock();
 	rcu_read_unlock();
 dont_gc:
 dont_gc:
-	kleave(" = false");
-	return false;
+	kleave(" [no gc]");
+	return;
 
 
 do_gc:
 do_gc:
 	rcu_read_unlock();
 	rcu_read_unlock();
-	key_gc_cursor = keyring->serial;
-	key_get(keyring);
-	spin_unlock(&key_serial_lock);
+
 	keyring_gc(keyring, limit);
 	keyring_gc(keyring, limit);
-	key_put(keyring);
-	kleave(" = true");
-	return true;
+	kleave(" [gc]");
 }
 }
 
 
 /*
 /*
- * Garbage collector for keys.  This involves scanning the keyrings for dead,
- * expired and revoked keys that have overstayed their welcome
+ * Garbage collect an unreferenced, detached key
  */
  */
-static void key_garbage_collector(struct work_struct *work)
+static noinline void key_gc_unused_key(struct key *key)
 {
 {
-	struct rb_node *rb;
-	key_serial_t cursor;
-	struct key *key, *xkey;
-	time_t new_timer = LONG_MAX, limit, now;
-
-	now = current_kernel_time().tv_sec;
-	kenter("[%x,%ld]", key_gc_cursor, key_gc_new_timer - now);
-
-	if (test_and_set_bit(0, &key_gc_executing)) {
-		key_schedule_gc(current_kernel_time().tv_sec + 1);
-		kleave(" [busy; deferring]");
-		return;
+	key_check(key);
+
+	security_key_free(key);
+
+	/* deal with the user's key tracking and quota */
+	if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
+		spin_lock(&key->user->lock);
+		key->user->qnkeys--;
+		key->user->qnbytes -= key->quotalen;
+		spin_unlock(&key->user->lock);
 	}
 	}
 
 
-	limit = now;
+	atomic_dec(&key->user->nkeys);
+	if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
+		atomic_dec(&key->user->nikeys);
+
+	key_user_put(key->user);
+
+	/* now throw away the key memory */
+	if (key->type->destroy)
+		key->type->destroy(key);
+
+	kfree(key->description);
+
+#ifdef KEY_DEBUGGING
+	key->magic = KEY_DEBUG_MAGIC_X;
+#endif
+	kmem_cache_free(key_jar, key);
+}
+
+/*
+ * Garbage collector for unused keys.
+ *
+ * This is done in process context so that we don't have to disable interrupts
+ * all over the place.  key_put() schedules this rather than trying to do the
+ * cleanup itself, which means key_put() doesn't have to sleep.
+ */
+static void key_garbage_collector(struct work_struct *work)
+{
+	static u8 gc_state;		/* Internal persistent state */
+#define KEY_GC_REAP_AGAIN	0x01	/* - Need another cycle */
+#define KEY_GC_REAPING_LINKS	0x02	/* - We need to reap links */
+#define KEY_GC_SET_TIMER	0x04	/* - We need to restart the timer */
+#define KEY_GC_REAPING_DEAD_1	0x10	/* - We need to mark dead keys */
+#define KEY_GC_REAPING_DEAD_2	0x20	/* - We need to reap dead key links */
+#define KEY_GC_REAPING_DEAD_3	0x40	/* - We need to reap dead keys */
+#define KEY_GC_FOUND_DEAD_KEY	0x80	/* - We found at least one dead key */
+
+	struct rb_node *cursor;
+	struct key *key;
+	time_t new_timer, limit;
+
+	kenter("[%lx,%x]", key_gc_flags, gc_state);
+
+	limit = current_kernel_time().tv_sec;
 	if (limit > key_gc_delay)
 	if (limit > key_gc_delay)
 		limit -= key_gc_delay;
 		limit -= key_gc_delay;
 	else
 	else
 		limit = key_gc_delay;
 		limit = key_gc_delay;
 
 
+	/* Work out what we're going to be doing in this pass */
+	gc_state &= KEY_GC_REAPING_DEAD_1 | KEY_GC_REAPING_DEAD_2;
+	gc_state <<= 1;
+	if (test_and_clear_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags))
+		gc_state |= KEY_GC_REAPING_LINKS | KEY_GC_SET_TIMER;
+
+	if (test_and_clear_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags))
+		gc_state |= KEY_GC_REAPING_DEAD_1;
+	kdebug("new pass %x", gc_state);
+
+	new_timer = LONG_MAX;
+
+	/* As only this function is permitted to remove things from the key
+	 * serial tree, if cursor is non-NULL then it will always point to a
+	 * valid node in the tree - even if lock got dropped.
+	 */
 	spin_lock(&key_serial_lock);
 	spin_lock(&key_serial_lock);
+	cursor = rb_first(&key_serial_tree);
 
 
-	if (unlikely(RB_EMPTY_ROOT(&key_serial_tree))) {
-		spin_unlock(&key_serial_lock);
-		clear_bit(0, &key_gc_executing);
-		return;
-	}
+continue_scanning:
+	while (cursor) {
+		key = rb_entry(cursor, struct key, serial_node);
+		cursor = rb_next(cursor);
 
 
-	cursor = key_gc_cursor;
-	if (cursor < 0)
-		cursor = 0;
-	if (cursor > 0)
-		new_timer = key_gc_new_timer;
-	else
-		key_gc_again = false;
-
-	/* find the first key above the cursor */
-	key = NULL;
-	rb = key_serial_tree.rb_node;
-	while (rb) {
-		xkey = rb_entry(rb, struct key, serial_node);
-		if (cursor < xkey->serial) {
-			key = xkey;
-			rb = rb->rb_left;
-		} else if (cursor > xkey->serial) {
-			rb = rb->rb_right;
-		} else {
-			rb = rb_next(rb);
-			if (!rb)
-				goto reached_the_end;
-			key = rb_entry(rb, struct key, serial_node);
-			break;
+		if (atomic_read(&key->usage) == 0)
+			goto found_unreferenced_key;
+
+		if (unlikely(gc_state & KEY_GC_REAPING_DEAD_1)) {
+			if (key->type == key_gc_dead_keytype) {
+				gc_state |= KEY_GC_FOUND_DEAD_KEY;
+				set_bit(KEY_FLAG_DEAD, &key->flags);
+				key->perm = 0;
+				goto skip_dead_key;
+			}
+		}
+
+		if (gc_state & KEY_GC_SET_TIMER) {
+			if (key->expiry > limit && key->expiry < new_timer) {
+				kdebug("will expire %x in %ld",
+				       key_serial(key), key->expiry - limit);
+				new_timer = key->expiry;
+			}
 		}
 		}
-	}
 
 
-	if (!key)
-		goto reached_the_end;
+		if (unlikely(gc_state & KEY_GC_REAPING_DEAD_2))
+			if (key->type == key_gc_dead_keytype)
+				gc_state |= KEY_GC_FOUND_DEAD_KEY;
 
 
-	/* trawl through the keys looking for keyrings */
-	for (;;) {
-		if (key->expiry > limit && key->expiry < new_timer) {
-			kdebug("will expire %x in %ld",
-			       key_serial(key), key->expiry - limit);
-			new_timer = key->expiry;
+		if ((gc_state & KEY_GC_REAPING_LINKS) ||
+		    unlikely(gc_state & KEY_GC_REAPING_DEAD_2)) {
+			if (key->type == &key_type_keyring)
+				goto found_keyring;
 		}
 		}
 
 
-		if (key->type == &key_type_keyring &&
-		    key_gc_keyring(key, limit))
-			/* the gc had to release our lock so that the keyring
-			 * could be modified, so we have to get it again */
-			goto gc_released_our_lock;
+		if (unlikely(gc_state & KEY_GC_REAPING_DEAD_3))
+			if (key->type == key_gc_dead_keytype)
+				goto destroy_dead_key;
 
 
-		rb = rb_next(&key->serial_node);
-		if (!rb)
-			goto reached_the_end;
-		key = rb_entry(rb, struct key, serial_node);
+	skip_dead_key:
+		if (spin_is_contended(&key_serial_lock) || need_resched())
+			goto contended;
 	}
 	}
 
 
-gc_released_our_lock:
-	kdebug("gc_released_our_lock");
-	key_gc_new_timer = new_timer;
-	key_gc_again = true;
-	clear_bit(0, &key_gc_executing);
-	schedule_work(&key_gc_work);
-	kleave(" [continue]");
-	return;
-
-	/* when we reach the end of the run, we set the timer for the next one */
-reached_the_end:
-	kdebug("reached_the_end");
+contended:
 	spin_unlock(&key_serial_lock);
 	spin_unlock(&key_serial_lock);
-	key_gc_new_timer = new_timer;
-	key_gc_cursor = 0;
-	clear_bit(0, &key_gc_executing);
-
-	if (key_gc_again) {
-		/* there may have been a key that expired whilst we were
-		 * scanning, so if we discarded any links we should do another
-		 * scan */
-		new_timer = now + 1;
-		key_schedule_gc(new_timer);
-	} else if (new_timer < LONG_MAX) {
+
+maybe_resched:
+	if (cursor) {
+		cond_resched();
+		spin_lock(&key_serial_lock);
+		goto continue_scanning;
+	}
+
+	/* We've completed the pass.  Set the timer if we need to and queue a
+	 * new cycle if necessary.  We keep executing cycles until we find one
+	 * where we didn't reap any keys.
+	 */
+	kdebug("pass complete");
+
+	if (gc_state & KEY_GC_SET_TIMER && new_timer != (time_t)LONG_MAX) {
 		new_timer += key_gc_delay;
 		new_timer += key_gc_delay;
 		key_schedule_gc(new_timer);
 		key_schedule_gc(new_timer);
 	}
 	}
-	kleave(" [end]");
+
+	if (unlikely(gc_state & KEY_GC_REAPING_DEAD_2)) {
+		/* Make sure everyone revalidates their keys if we marked a
+		 * bunch as being dead and make sure all keyring ex-payloads
+		 * are destroyed.
+		 */
+		kdebug("dead sync");
+		synchronize_rcu();
+	}
+
+	if (unlikely(gc_state & (KEY_GC_REAPING_DEAD_1 |
+				 KEY_GC_REAPING_DEAD_2))) {
+		if (!(gc_state & KEY_GC_FOUND_DEAD_KEY)) {
+			/* No remaining dead keys: short circuit the remaining
+			 * keytype reap cycles.
+			 */
+			kdebug("dead short");
+			gc_state &= ~(KEY_GC_REAPING_DEAD_1 | KEY_GC_REAPING_DEAD_2);
+			gc_state |= KEY_GC_REAPING_DEAD_3;
+		} else {
+			gc_state |= KEY_GC_REAP_AGAIN;
+		}
+	}
+
+	if (unlikely(gc_state & KEY_GC_REAPING_DEAD_3)) {
+		kdebug("dead wake");
+		smp_mb();
+		clear_bit(KEY_GC_REAPING_KEYTYPE, &key_gc_flags);
+		wake_up_bit(&key_gc_flags, KEY_GC_REAPING_KEYTYPE);
+	}
+
+	if (gc_state & KEY_GC_REAP_AGAIN)
+		queue_work(system_nrt_wq, &key_gc_work);
+	kleave(" [end %x]", gc_state);
+	return;
+
+	/* We found an unreferenced key - once we've removed it from the tree,
+	 * we can safely drop the lock.
+	 */
+found_unreferenced_key:
+	kdebug("unrefd key %d", key->serial);
+	rb_erase(&key->serial_node, &key_serial_tree);
+	spin_unlock(&key_serial_lock);
+
+	key_gc_unused_key(key);
+	gc_state |= KEY_GC_REAP_AGAIN;
+	goto maybe_resched;
+
+	/* We found a keyring and we need to check the payload for links to
+	 * dead or expired keys.  We don't flag another reap immediately as we
+	 * have to wait for the old payload to be destroyed by RCU before we
+	 * can reap the keys to which it refers.
+	 */
+found_keyring:
+	spin_unlock(&key_serial_lock);
+	kdebug("scan keyring %d", key->serial);
+	key_gc_keyring(key, limit);
+	goto maybe_resched;
+
+	/* We found a dead key that is still referenced.  Reset its type and
+	 * destroy its payload with its semaphore held.
+	 */
+destroy_dead_key:
+	spin_unlock(&key_serial_lock);
+	kdebug("destroy key %d", key->serial);
+	down_write(&key->sem);
+	key->type = &key_type_dead;
+	if (key_gc_dead_keytype->destroy)
+		key_gc_dead_keytype->destroy(key);
+	memset(&key->payload, KEY_DESTROY, sizeof(key->payload));
+	up_write(&key->sem);
+	goto maybe_resched;
 }
 }

+ 4 - 0
security/keys/internal.h

@@ -31,6 +31,7 @@
 	no_printk(KERN_DEBUG FMT"\n", ##__VA_ARGS__)
 	no_printk(KERN_DEBUG FMT"\n", ##__VA_ARGS__)
 #endif
 #endif
 
 
+extern struct key_type key_type_dead;
 extern struct key_type key_type_user;
 extern struct key_type key_type_user;
 
 
 /*****************************************************************************/
 /*****************************************************************************/
@@ -75,6 +76,7 @@ extern unsigned key_quota_maxbytes;
 #define KEYQUOTA_LINK_BYTES	4		/* a link in a keyring is worth 4 bytes */
 #define KEYQUOTA_LINK_BYTES	4		/* a link in a keyring is worth 4 bytes */
 
 
 
 
+extern struct kmem_cache *key_jar;
 extern struct rb_root key_serial_tree;
 extern struct rb_root key_serial_tree;
 extern spinlock_t key_serial_lock;
 extern spinlock_t key_serial_lock;
 extern struct mutex key_construction_mutex;
 extern struct mutex key_construction_mutex;
@@ -146,9 +148,11 @@ extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags,
 
 
 extern long join_session_keyring(const char *name);
 extern long join_session_keyring(const char *name);
 
 
+extern struct work_struct key_gc_work;
 extern unsigned key_gc_delay;
 extern unsigned key_gc_delay;
 extern void keyring_gc(struct key *keyring, time_t limit);
 extern void keyring_gc(struct key *keyring, time_t limit);
 extern void key_schedule_gc(time_t expiry_at);
 extern void key_schedule_gc(time_t expiry_at);
+extern void key_gc_keytype(struct key_type *ktype);
 
 
 extern int key_task_permission(const key_ref_t key_ref,
 extern int key_task_permission(const key_ref_t key_ref,
 			       const struct cred *cred,
 			       const struct cred *cred,

+ 5 - 116
security/keys/key.c

@@ -21,7 +21,7 @@
 #include <linux/user_namespace.h>
 #include <linux/user_namespace.h>
 #include "internal.h"
 #include "internal.h"
 
 
-static struct kmem_cache	*key_jar;
+struct kmem_cache *key_jar;
 struct rb_root		key_serial_tree; /* tree of keys indexed by serial */
 struct rb_root		key_serial_tree; /* tree of keys indexed by serial */
 DEFINE_SPINLOCK(key_serial_lock);
 DEFINE_SPINLOCK(key_serial_lock);
 
 
@@ -36,17 +36,9 @@ unsigned int key_quota_maxbytes = 20000;	/* general key space quota */
 static LIST_HEAD(key_types_list);
 static LIST_HEAD(key_types_list);
 static DECLARE_RWSEM(key_types_sem);
 static DECLARE_RWSEM(key_types_sem);
 
 
-static void key_cleanup(struct work_struct *work);
-static DECLARE_WORK(key_cleanup_task, key_cleanup);
-
 /* We serialise key instantiation and link */
 /* We serialise key instantiation and link */
 DEFINE_MUTEX(key_construction_mutex);
 DEFINE_MUTEX(key_construction_mutex);
 
 
-/* Any key who's type gets unegistered will be re-typed to this */
-static struct key_type key_type_dead = {
-	.name		= "dead",
-};
-
 #ifdef KEY_DEBUGGING
 #ifdef KEY_DEBUGGING
 void __key_check(const struct key *key)
 void __key_check(const struct key *key)
 {
 {
@@ -591,71 +583,6 @@ int key_reject_and_link(struct key *key,
 }
 }
 EXPORT_SYMBOL(key_reject_and_link);
 EXPORT_SYMBOL(key_reject_and_link);
 
 
-/*
- * Garbage collect keys in process context so that we don't have to disable
- * interrupts all over the place.
- *
- * key_put() schedules this rather than trying to do the cleanup itself, which
- * means key_put() doesn't have to sleep.
- */
-static void key_cleanup(struct work_struct *work)
-{
-	struct rb_node *_n;
-	struct key *key;
-
-go_again:
-	/* look for a dead key in the tree */
-	spin_lock(&key_serial_lock);
-
-	for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) {
-		key = rb_entry(_n, struct key, serial_node);
-
-		if (atomic_read(&key->usage) == 0)
-			goto found_dead_key;
-	}
-
-	spin_unlock(&key_serial_lock);
-	return;
-
-found_dead_key:
-	/* we found a dead key - once we've removed it from the tree, we can
-	 * drop the lock */
-	rb_erase(&key->serial_node, &key_serial_tree);
-	spin_unlock(&key_serial_lock);
-
-	key_check(key);
-
-	security_key_free(key);
-
-	/* deal with the user's key tracking and quota */
-	if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
-		spin_lock(&key->user->lock);
-		key->user->qnkeys--;
-		key->user->qnbytes -= key->quotalen;
-		spin_unlock(&key->user->lock);
-	}
-
-	atomic_dec(&key->user->nkeys);
-	if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
-		atomic_dec(&key->user->nikeys);
-
-	key_user_put(key->user);
-
-	/* now throw away the key memory */
-	if (key->type->destroy)
-		key->type->destroy(key);
-
-	kfree(key->description);
-
-#ifdef KEY_DEBUGGING
-	key->magic = KEY_DEBUG_MAGIC_X;
-#endif
-	kmem_cache_free(key_jar, key);
-
-	/* there may, of course, be more than one key to destroy */
-	goto go_again;
-}
-
 /**
 /**
  * key_put - Discard a reference to a key.
  * key_put - Discard a reference to a key.
  * @key: The key to discard a reference from.
  * @key: The key to discard a reference from.
@@ -670,7 +597,7 @@ void key_put(struct key *key)
 		key_check(key);
 		key_check(key);
 
 
 		if (atomic_dec_and_test(&key->usage))
 		if (atomic_dec_and_test(&key->usage))
-			schedule_work(&key_cleanup_task);
+			queue_work(system_nrt_wq, &key_gc_work);
 	}
 	}
 }
 }
 EXPORT_SYMBOL(key_put);
 EXPORT_SYMBOL(key_put);
@@ -1048,49 +975,11 @@ EXPORT_SYMBOL(register_key_type);
  */
  */
 void unregister_key_type(struct key_type *ktype)
 void unregister_key_type(struct key_type *ktype)
 {
 {
-	struct rb_node *_n;
-	struct key *key;
-
 	down_write(&key_types_sem);
 	down_write(&key_types_sem);
-
-	/* withdraw the key type */
 	list_del_init(&ktype->link);
 	list_del_init(&ktype->link);
-
-	/* mark all the keys of this type dead */
-	spin_lock(&key_serial_lock);
-
-	for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) {
-		key = rb_entry(_n, struct key, serial_node);
-
-		if (key->type == ktype) {
-			key->type = &key_type_dead;
-			set_bit(KEY_FLAG_DEAD, &key->flags);
-		}
-	}
-
-	spin_unlock(&key_serial_lock);
-
-	/* make sure everyone revalidates their keys */
-	synchronize_rcu();
-
-	/* we should now be able to destroy the payloads of all the keys of
-	 * this type with impunity */
-	spin_lock(&key_serial_lock);
-
-	for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) {
-		key = rb_entry(_n, struct key, serial_node);
-
-		if (key->type == ktype) {
-			if (ktype->destroy)
-				ktype->destroy(key);
-			memset(&key->payload, KEY_DESTROY, sizeof(key->payload));
-		}
-	}
-
-	spin_unlock(&key_serial_lock);
-	up_write(&key_types_sem);
-
-	key_schedule_gc(0);
+	downgrade_write(&key_types_sem);
+	key_gc_keytype(ktype);
+	up_read(&key_types_sem);
 }
 }
 EXPORT_SYMBOL(unregister_key_type);
 EXPORT_SYMBOL(unregister_key_type);
 
 

+ 1 - 2
security/keys/keyring.c

@@ -860,8 +860,7 @@ void __key_link(struct key *keyring, struct key *key,
 
 
 	kenter("%d,%d,%p", keyring->serial, key->serial, nklist);
 	kenter("%d,%d,%p", keyring->serial, key->serial, nklist);
 
 
-	klist = rcu_dereference_protected(keyring->payload.subscriptions,
-					  rwsem_is_locked(&keyring->sem));
+	klist = rcu_dereference_locked_keyring(keyring);
 
 
 	atomic_inc(&key->usage);
 	atomic_inc(&key->usage);
 
 

+ 13 - 3
security/keys/process_keys.c

@@ -270,7 +270,7 @@ static int install_session_keyring(struct key *keyring)
 	if (!new)
 	if (!new)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	ret = install_session_keyring_to_cred(new, NULL);
+	ret = install_session_keyring_to_cred(new, keyring);
 	if (ret < 0) {
 	if (ret < 0) {
 		abort_creds(new);
 		abort_creds(new);
 		return ret;
 		return ret;
@@ -589,12 +589,22 @@ try_again:
 			ret = install_user_keyrings();
 			ret = install_user_keyrings();
 			if (ret < 0)
 			if (ret < 0)
 				goto error;
 				goto error;
-			ret = install_session_keyring(
-				cred->user->session_keyring);
+			if (lflags & KEY_LOOKUP_CREATE)
+				ret = join_session_keyring(NULL);
+			else
+				ret = install_session_keyring(
+					cred->user->session_keyring);
 
 
 			if (ret < 0)
 			if (ret < 0)
 				goto error;
 				goto error;
 			goto reget_creds;
 			goto reget_creds;
+		} else if (cred->tgcred->session_keyring ==
+			   cred->user->session_keyring &&
+			   lflags & KEY_LOOKUP_CREATE) {
+			ret = join_session_keyring(NULL);
+			if (ret < 0)
+				goto error;
+			goto reget_creds;
 		}
 		}
 
 
 		rcu_read_lock();
 		rcu_read_lock();

+ 15 - 4
security/keys/trusted.c

@@ -779,7 +779,10 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
 			opt->pcrinfo_len = strlen(args[0].from) / 2;
 			opt->pcrinfo_len = strlen(args[0].from) / 2;
 			if (opt->pcrinfo_len > MAX_PCRINFO_SIZE)
 			if (opt->pcrinfo_len > MAX_PCRINFO_SIZE)
 				return -EINVAL;
 				return -EINVAL;
-			hex2bin(opt->pcrinfo, args[0].from, opt->pcrinfo_len);
+			res = hex2bin(opt->pcrinfo, args[0].from,
+				      opt->pcrinfo_len);
+			if (res < 0)
+				return -EINVAL;
 			break;
 			break;
 		case Opt_keyhandle:
 		case Opt_keyhandle:
 			res = strict_strtoul(args[0].from, 16, &handle);
 			res = strict_strtoul(args[0].from, 16, &handle);
@@ -791,12 +794,18 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
 		case Opt_keyauth:
 		case Opt_keyauth:
 			if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE)
 			if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE)
 				return -EINVAL;
 				return -EINVAL;
-			hex2bin(opt->keyauth, args[0].from, SHA1_DIGEST_SIZE);
+			res = hex2bin(opt->keyauth, args[0].from,
+				      SHA1_DIGEST_SIZE);
+			if (res < 0)
+				return -EINVAL;
 			break;
 			break;
 		case Opt_blobauth:
 		case Opt_blobauth:
 			if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE)
 			if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE)
 				return -EINVAL;
 				return -EINVAL;
-			hex2bin(opt->blobauth, args[0].from, SHA1_DIGEST_SIZE);
+			res = hex2bin(opt->blobauth, args[0].from,
+				      SHA1_DIGEST_SIZE);
+			if (res < 0)
+				return -EINVAL;
 			break;
 			break;
 		case Opt_migratable:
 		case Opt_migratable:
 			if (*args[0].from == '0')
 			if (*args[0].from == '0')
@@ -860,7 +869,9 @@ static int datablob_parse(char *datablob, struct trusted_key_payload *p,
 		p->blob_len = strlen(c) / 2;
 		p->blob_len = strlen(c) / 2;
 		if (p->blob_len > MAX_BLOB_SIZE)
 		if (p->blob_len > MAX_BLOB_SIZE)
 			return -EINVAL;
 			return -EINVAL;
-		hex2bin(p->blob, c, p->blob_len);
+		ret = hex2bin(p->blob, c, p->blob_len);
+		if (ret < 0)
+			return -EINVAL;
 		ret = getoptions(datablob, p, o);
 		ret = getoptions(datablob, p, o);
 		if (ret < 0)
 		if (ret < 0)
 			return ret;
 			return ret;

+ 65 - 11
security/security.c

@@ -16,15 +16,16 @@
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/kernel.h>
 #include <linux/security.h>
 #include <linux/security.h>
+#include <linux/integrity.h>
 #include <linux/ima.h>
 #include <linux/ima.h>
+#include <linux/evm.h>
+
+#define MAX_LSM_EVM_XATTR	2
 
 
 /* Boot-time LSM user choice */
 /* Boot-time LSM user choice */
 static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
 static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
 	CONFIG_DEFAULT_SECURITY;
 	CONFIG_DEFAULT_SECURITY;
 
 
-/* things that live in capability.c */
-extern void __init security_fixup_ops(struct security_operations *ops);
-
 static struct security_operations *security_ops;
 static struct security_operations *security_ops;
 static struct security_operations default_security_ops = {
 static struct security_operations default_security_ops = {
 	.name	= "default",
 	.name	= "default",
@@ -334,20 +335,57 @@ int security_inode_alloc(struct inode *inode)
 
 
 void security_inode_free(struct inode *inode)
 void security_inode_free(struct inode *inode)
 {
 {
-	ima_inode_free(inode);
+	integrity_inode_free(inode);
 	security_ops->inode_free_security(inode);
 	security_ops->inode_free_security(inode);
 }
 }
 
 
 int security_inode_init_security(struct inode *inode, struct inode *dir,
 int security_inode_init_security(struct inode *inode, struct inode *dir,
-				 const struct qstr *qstr, char **name,
-				 void **value, size_t *len)
+				 const struct qstr *qstr,
+				 const initxattrs initxattrs, void *fs_data)
 {
 {
+	struct xattr new_xattrs[MAX_LSM_EVM_XATTR + 1];
+	struct xattr *lsm_xattr, *evm_xattr, *xattr;
+	int ret;
+
 	if (unlikely(IS_PRIVATE(inode)))
 	if (unlikely(IS_PRIVATE(inode)))
-		return -EOPNOTSUPP;
+		return 0;
+
+	memset(new_xattrs, 0, sizeof new_xattrs);
+	if (!initxattrs)
+		return security_ops->inode_init_security(inode, dir, qstr,
+							 NULL, NULL, NULL);
+	lsm_xattr = new_xattrs;
+	ret = security_ops->inode_init_security(inode, dir, qstr,
+						&lsm_xattr->name,
+						&lsm_xattr->value,
+						&lsm_xattr->value_len);
+	if (ret)
+		goto out;
+
+	evm_xattr = lsm_xattr + 1;
+	ret = evm_inode_init_security(inode, lsm_xattr, evm_xattr);
+	if (ret)
+		goto out;
+	ret = initxattrs(inode, new_xattrs, fs_data);
+out:
+	for (xattr = new_xattrs; xattr->name != NULL; xattr++) {
+		kfree(xattr->name);
+		kfree(xattr->value);
+	}
+	return (ret == -EOPNOTSUPP) ? 0 : ret;
+}
+EXPORT_SYMBOL(security_inode_init_security);
+
+int security_old_inode_init_security(struct inode *inode, struct inode *dir,
+				     const struct qstr *qstr, char **name,
+				     void **value, size_t *len)
+{
+	if (unlikely(IS_PRIVATE(inode)))
+		return 0;
 	return security_ops->inode_init_security(inode, dir, qstr, name, value,
 	return security_ops->inode_init_security(inode, dir, qstr, name, value,
 						 len);
 						 len);
 }
 }
-EXPORT_SYMBOL(security_inode_init_security);
+EXPORT_SYMBOL(security_old_inode_init_security);
 
 
 #ifdef CONFIG_SECURITY_PATH
 #ifdef CONFIG_SECURITY_PATH
 int security_path_mknod(struct path *dir, struct dentry *dentry, int mode,
 int security_path_mknod(struct path *dir, struct dentry *dentry, int mode,
@@ -523,9 +561,14 @@ int security_inode_permission(struct inode *inode, int mask)
 
 
 int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
 int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
 {
 {
+	int ret;
+
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return 0;
 		return 0;
-	return security_ops->inode_setattr(dentry, attr);
+	ret = security_ops->inode_setattr(dentry, attr);
+	if (ret)
+		return ret;
+	return evm_inode_setattr(dentry, attr);
 }
 }
 EXPORT_SYMBOL_GPL(security_inode_setattr);
 EXPORT_SYMBOL_GPL(security_inode_setattr);
 
 
@@ -539,9 +582,14 @@ int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
 int security_inode_setxattr(struct dentry *dentry, const char *name,
 int security_inode_setxattr(struct dentry *dentry, const char *name,
 			    const void *value, size_t size, int flags)
 			    const void *value, size_t size, int flags)
 {
 {
+	int ret;
+
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return 0;
 		return 0;
-	return security_ops->inode_setxattr(dentry, name, value, size, flags);
+	ret = security_ops->inode_setxattr(dentry, name, value, size, flags);
+	if (ret)
+		return ret;
+	return evm_inode_setxattr(dentry, name, value, size);
 }
 }
 
 
 void security_inode_post_setxattr(struct dentry *dentry, const char *name,
 void security_inode_post_setxattr(struct dentry *dentry, const char *name,
@@ -550,6 +598,7 @@ void security_inode_post_setxattr(struct dentry *dentry, const char *name,
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return;
 		return;
 	security_ops->inode_post_setxattr(dentry, name, value, size, flags);
 	security_ops->inode_post_setxattr(dentry, name, value, size, flags);
+	evm_inode_post_setxattr(dentry, name, value, size);
 }
 }
 
 
 int security_inode_getxattr(struct dentry *dentry, const char *name)
 int security_inode_getxattr(struct dentry *dentry, const char *name)
@@ -568,9 +617,14 @@ int security_inode_listxattr(struct dentry *dentry)
 
 
 int security_inode_removexattr(struct dentry *dentry, const char *name)
 int security_inode_removexattr(struct dentry *dentry, const char *name)
 {
 {
+	int ret;
+
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return 0;
 		return 0;
-	return security_ops->inode_removexattr(dentry, name);
+	ret = security_ops->inode_removexattr(dentry, name);
+	if (ret)
+		return ret;
+	return evm_inode_removexattr(dentry, name);
 }
 }
 
 
 int security_inode_need_killpriv(struct dentry *dentry)
 int security_inode_need_killpriv(struct dentry *dentry)

+ 1 - 0
security/selinux/exports.c

@@ -12,6 +12,7 @@
  * as published by the Free Software Foundation.
  * as published by the Free Software Foundation.
  */
  */
 #include <linux/module.h>
 #include <linux/module.h>
+#include <linux/selinux.h>
 
 
 #include "security.h"
 #include "security.h"
 
 

+ 2 - 11
security/selinux/hooks.c

@@ -89,14 +89,14 @@
 #include "xfrm.h"
 #include "xfrm.h"
 #include "netlabel.h"
 #include "netlabel.h"
 #include "audit.h"
 #include "audit.h"
+#include "avc_ss.h"
 
 
 #define NUM_SEL_MNT_OPTS 5
 #define NUM_SEL_MNT_OPTS 5
 
 
-extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
 extern struct security_operations *security_ops;
 extern struct security_operations *security_ops;
 
 
 /* SECMARK reference count */
 /* SECMARK reference count */
-atomic_t selinux_secmark_refcount = ATOMIC_INIT(0);
+static atomic_t selinux_secmark_refcount = ATOMIC_INIT(0);
 
 
 #ifdef CONFIG_SECURITY_SELINUX_DEVELOP
 #ifdef CONFIG_SECURITY_SELINUX_DEVELOP
 int selinux_enforcing;
 int selinux_enforcing;
@@ -279,10 +279,6 @@ static void superblock_free_security(struct super_block *sb)
 	kfree(sbsec);
 	kfree(sbsec);
 }
 }
 
 
-/* The security server must be initialized before
-   any labeling or access decisions can be provided. */
-extern int ss_initialized;
-
 /* The file system's label must be initialized prior to use. */
 /* The file system's label must be initialized prior to use. */
 
 
 static const char *labeling_behaviors[6] = {
 static const char *labeling_behaviors[6] = {
@@ -2097,9 +2093,6 @@ static int selinux_bprm_secureexec(struct linux_binprm *bprm)
 	return (atsecure || cap_bprm_secureexec(bprm));
 	return (atsecure || cap_bprm_secureexec(bprm));
 }
 }
 
 
-extern struct vfsmount *selinuxfs_mount;
-extern struct dentry *selinux_null;
-
 /* Derived from fs/exec.c:flush_old_files. */
 /* Derived from fs/exec.c:flush_old_files. */
 static inline void flush_unauthorized_files(const struct cred *cred,
 static inline void flush_unauthorized_files(const struct cred *cred,
 					    struct files_struct *files)
 					    struct files_struct *files)
@@ -5803,8 +5796,6 @@ static int selinux_disabled;
 
 
 int selinux_disable(void)
 int selinux_disable(void)
 {
 {
-	extern void exit_sel_fs(void);
-
 	if (ss_initialized) {
 	if (ss_initialized) {
 		/* Not permitted after initial policy load. */
 		/* Not permitted after initial policy load. */
 		return -EINVAL;
 		return -EINVAL;

+ 6 - 0
security/selinux/include/avc_ss.h

@@ -18,5 +18,11 @@ struct security_class_mapping {
 
 
 extern struct security_class_mapping secclass_map[];
 extern struct security_class_mapping secclass_map[];
 
 
+/*
+ * The security server must be initialized before
+ * any labeling or access decisions can be provided.
+ */
+extern int ss_initialized;
+
 #endif /* _SELINUX_AVC_SS_H_ */
 #endif /* _SELINUX_AVC_SS_H_ */
 
 

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

@@ -216,6 +216,14 @@ struct selinux_kernel_status {
 
 
 extern void selinux_status_update_setenforce(int enforcing);
 extern void selinux_status_update_setenforce(int enforcing);
 extern void selinux_status_update_policyload(int seqno);
 extern void selinux_status_update_policyload(int seqno);
+extern void selinux_complete_init(void);
+extern int selinux_disable(void);
+extern void exit_sel_fs(void);
+extern struct dentry *selinux_null;
+extern struct vfsmount *selinuxfs_mount;
+extern void selnl_notify_setenforce(int val);
+extern void selnl_notify_policyload(u32 seqno);
+extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
 
 
 #endif /* _SELINUX_SECURITY_H_ */
 #endif /* _SELINUX_SECURITY_H_ */
 
 

+ 2 - 0
security/selinux/netlink.c

@@ -19,6 +19,8 @@
 #include <linux/selinux_netlink.h>
 #include <linux/selinux_netlink.h>
 #include <net/net_namespace.h>
 #include <net/net_namespace.h>
 
 
+#include "security.h"
+
 static struct sock *selnl;
 static struct sock *selnl;
 
 
 static int selnl_msglen(int msgtype)
 static int selnl_msglen(int msgtype)

+ 1 - 0
security/selinux/nlmsgtab.c

@@ -21,6 +21,7 @@
 
 
 #include "flask.h"
 #include "flask.h"
 #include "av_permissions.h"
 #include "av_permissions.h"
+#include "security.h"
 
 
 struct nlmsg_perm {
 struct nlmsg_perm {
 	u16	nlmsg_type;
 	u16	nlmsg_type;

+ 1 - 4
security/selinux/selinuxfs.c

@@ -75,8 +75,6 @@ static char policy_opened;
 /* global data for policy capabilities */
 /* global data for policy capabilities */
 static struct dentry *policycap_dir;
 static struct dentry *policycap_dir;
 
 
-extern void selnl_notify_setenforce(int val);
-
 /* Check whether a task is allowed to use a security operation. */
 /* Check whether a task is allowed to use a security operation. */
 static int task_has_security(struct task_struct *tsk,
 static int task_has_security(struct task_struct *tsk,
 			     u32 perms)
 			     u32 perms)
@@ -278,7 +276,6 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf,
 	char *page = NULL;
 	char *page = NULL;
 	ssize_t length;
 	ssize_t length;
 	int new_value;
 	int new_value;
-	extern int selinux_disable(void);
 
 
 	length = -ENOMEM;
 	length = -ENOMEM;
 	if (count >= PAGE_SIZE)
 	if (count >= PAGE_SIZE)
@@ -478,7 +475,7 @@ static struct vm_operations_struct sel_mmap_policy_ops = {
 	.page_mkwrite = sel_mmap_policy_fault,
 	.page_mkwrite = sel_mmap_policy_fault,
 };
 };
 
 
-int sel_mmap_policy(struct file *filp, struct vm_area_struct *vma)
+static int sel_mmap_policy(struct file *filp, struct vm_area_struct *vma)
 {
 {
 	if (vma->vm_flags & VM_SHARED) {
 	if (vma->vm_flags & VM_SHARED) {
 		/* do not allow mprotect to make mapping writable */
 		/* do not allow mprotect to make mapping writable */

+ 1 - 1
security/selinux/ss/conditional.c

@@ -555,7 +555,7 @@ static int cond_write_av_list(struct policydb *p,
 	return 0;
 	return 0;
 }
 }
 
 
-int cond_write_node(struct policydb *p, struct cond_node *node,
+static int cond_write_node(struct policydb *p, struct cond_node *node,
 		    struct policy_file *fp)
 		    struct policy_file *fp)
 {
 {
 	struct cond_expr *cur_expr;
 	struct cond_expr *cur_expr;

+ 1 - 0
security/selinux/ss/conditional.h

@@ -13,6 +13,7 @@
 #include "avtab.h"
 #include "avtab.h"
 #include "symtab.h"
 #include "symtab.h"
 #include "policydb.h"
 #include "policydb.h"
+#include "../include/conditional.h"
 
 
 #define COND_EXPR_MAXDEPTH 10
 #define COND_EXPR_MAXDEPTH 10
 
 

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

@@ -1743,8 +1743,6 @@ static int policydb_bounds_sanity_check(struct policydb *p)
 	return 0;
 	return 0;
 }
 }
 
 
-extern int ss_initialized;
-
 u16 string_to_security_class(struct policydb *p, const char *name)
 u16 string_to_security_class(struct policydb *p, const char *name)
 {
 {
 	struct class_datum *cladatum;
 	struct class_datum *cladatum;

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

@@ -70,8 +70,6 @@
 #include "ebitmap.h"
 #include "ebitmap.h"
 #include "audit.h"
 #include "audit.h"
 
 
-extern void selnl_notify_policyload(u32 seqno);
-
 int selinux_policycap_netpeer;
 int selinux_policycap_netpeer;
 int selinux_policycap_openperm;
 int selinux_policycap_openperm;
 
 
@@ -1790,7 +1788,6 @@ static void security_load_policycaps(void)
 						  POLICYDB_CAPABILITY_OPENPERM);
 						  POLICYDB_CAPABILITY_OPENPERM);
 }
 }
 
 
-extern void selinux_complete_init(void);
 static int security_preserve_bools(struct policydb *p);
 static int security_preserve_bools(struct policydb *p);
 
 
 /**
 /**

+ 15 - 9
security/smack/smack.h

@@ -41,9 +41,9 @@ struct superblock_smack {
 };
 };
 
 
 struct socket_smack {
 struct socket_smack {
-	char		*smk_out;			/* outbound label */
-	char		*smk_in;			/* inbound label */
-	char		smk_packet[SMK_LABELLEN];	/* TCP peer label */
+	char		*smk_out;	/* outbound label */
+	char		*smk_in;	/* inbound label */
+	char		*smk_packet;	/* TCP peer label */
 };
 };
 
 
 /*
 /*
@@ -116,13 +116,19 @@ struct smk_netlbladdr {
  * If there is a cipso value associated with the label it
  * If there is a cipso value associated with the label it
  * gets stored here, too. This will most likely be rare as
  * gets stored here, too. This will most likely be rare as
  * the cipso direct mapping in used internally.
  * the cipso direct mapping in used internally.
+ *
+ * Keep the access rules for this subject label here so that
+ * the entire set of rules does not need to be examined every
+ * time.
  */
  */
 struct smack_known {
 struct smack_known {
 	struct list_head	list;
 	struct list_head	list;
 	char			smk_known[SMK_LABELLEN];
 	char			smk_known[SMK_LABELLEN];
 	u32			smk_secid;
 	u32			smk_secid;
 	struct smack_cipso	*smk_cipso;
 	struct smack_cipso	*smk_cipso;
-	spinlock_t		smk_cipsolock; /* for changing cipso map */
+	spinlock_t		smk_cipsolock;	/* for changing cipso map */
+	struct list_head	smk_rules;	/* access rules */
+	struct mutex		smk_rules_lock;	/* lock for the rules */
 };
 };
 
 
 /*
 /*
@@ -150,7 +156,6 @@ struct smack_known {
 
 
 /*
 /*
  * smackfs magic number
  * smackfs magic number
- * smackfs macic number
  */
  */
 #define SMACK_MAGIC	0x43415d53 /* "SMAC" */
 #define SMACK_MAGIC	0x43415d53 /* "SMAC" */
 
 
@@ -176,9 +181,9 @@ struct smack_known {
 #define MAY_NOT		0
 #define MAY_NOT		0
 
 
 /*
 /*
- * Number of access types used by Smack (rwxa)
+ * Number of access types used by Smack (rwxat)
  */
  */
-#define SMK_NUM_ACCESS_TYPE 4
+#define SMK_NUM_ACCESS_TYPE 5
 
 
 /*
 /*
  * Smack audit data; is empty if CONFIG_AUDIT not set
  * Smack audit data; is empty if CONFIG_AUDIT not set
@@ -201,10 +206,12 @@ int smk_access_entry(char *, char *, struct list_head *);
 int smk_access(char *, char *, int, struct smk_audit_info *);
 int smk_access(char *, char *, int, struct smk_audit_info *);
 int smk_curacc(char *, u32, struct smk_audit_info *);
 int smk_curacc(char *, u32, struct smk_audit_info *);
 int smack_to_cipso(const char *, struct smack_cipso *);
 int smack_to_cipso(const char *, struct smack_cipso *);
-void smack_from_cipso(u32, char *, char *);
+char *smack_from_cipso(u32, char *);
 char *smack_from_secid(const u32);
 char *smack_from_secid(const u32);
+void smk_parse_smack(const char *string, int len, char *smack);
 char *smk_import(const char *, int);
 char *smk_import(const char *, int);
 struct smack_known *smk_import_entry(const char *, int);
 struct smack_known *smk_import_entry(const char *, int);
+struct smack_known *smk_find_entry(const char *);
 u32 smack_to_secid(const char *);
 u32 smack_to_secid(const char *);
 
 
 /*
 /*
@@ -223,7 +230,6 @@ extern struct smack_known smack_known_star;
 extern struct smack_known smack_known_web;
 extern struct smack_known smack_known_web;
 
 
 extern struct list_head smack_known_list;
 extern struct list_head smack_known_list;
-extern struct list_head smack_rule_list;
 extern struct list_head smk_netlbladdr_list;
 extern struct list_head smk_netlbladdr_list;
 
 
 extern struct security_operations smack_ops;
 extern struct security_operations smack_ops;

+ 72 - 62
security/smack/smack_access.c

@@ -77,14 +77,19 @@ int log_policy = SMACK_AUDIT_DENIED;
  * entry is found returns -ENOENT.
  * entry is found returns -ENOENT.
  *
  *
  * NOTE:
  * NOTE:
- * Even though Smack labels are usually shared on smack_list
- * labels that come in off the network can't be imported
- * and added to the list for locking reasons.
  *
  *
- * Therefore, it is necessary to check the contents of the labels,
- * not just the pointer values. Of course, in most cases the labels
- * will be on the list, so checking the pointers may be a worthwhile
- * optimization.
+ * Earlier versions of this function allowed for labels that
+ * were not on the label list. This was done to allow for
+ * labels to come over the network that had never been seen
+ * before on this host. Unless the receiving socket has the
+ * star label this will always result in a failure check. The
+ * star labeled socket case is now handled in the networking
+ * hooks so there is no case where the label is not on the
+ * label list. Checking to see if the address of two labels
+ * is the same is now a reliable test.
+ *
+ * Do the object check first because that is more
+ * likely to differ.
  */
  */
 int smk_access_entry(char *subject_label, char *object_label,
 int smk_access_entry(char *subject_label, char *object_label,
 			struct list_head *rule_list)
 			struct list_head *rule_list)
@@ -93,13 +98,10 @@ int smk_access_entry(char *subject_label, char *object_label,
 	struct smack_rule *srp;
 	struct smack_rule *srp;
 
 
 	list_for_each_entry_rcu(srp, rule_list, list) {
 	list_for_each_entry_rcu(srp, rule_list, list) {
-		if (srp->smk_subject == subject_label ||
-		    strcmp(srp->smk_subject, subject_label) == 0) {
-			if (srp->smk_object == object_label ||
-			    strcmp(srp->smk_object, object_label) == 0) {
-				may = srp->smk_access;
-				break;
-			}
+		if (srp->smk_object == object_label &&
+		    srp->smk_subject == subject_label) {
+			may = srp->smk_access;
+			break;
 		}
 		}
 	}
 	}
 
 
@@ -117,18 +119,12 @@ int smk_access_entry(char *subject_label, char *object_label,
  * access rule list and returns 0 if the access is permitted,
  * access rule list and returns 0 if the access is permitted,
  * non zero otherwise.
  * non zero otherwise.
  *
  *
- * Even though Smack labels are usually shared on smack_list
- * labels that come in off the network can't be imported
- * and added to the list for locking reasons.
- *
- * Therefore, it is necessary to check the contents of the labels,
- * not just the pointer values. Of course, in most cases the labels
- * will be on the list, so checking the pointers may be a worthwhile
- * optimization.
+ * Smack labels are shared on smack_list
  */
  */
 int smk_access(char *subject_label, char *object_label, int request,
 int smk_access(char *subject_label, char *object_label, int request,
 	       struct smk_audit_info *a)
 	       struct smk_audit_info *a)
 {
 {
+	struct smack_known *skp;
 	int may = MAY_NOT;
 	int may = MAY_NOT;
 	int rc = 0;
 	int rc = 0;
 
 
@@ -137,8 +133,7 @@ int smk_access(char *subject_label, char *object_label, int request,
 	 *
 	 *
 	 * A star subject can't access any object.
 	 * A star subject can't access any object.
 	 */
 	 */
-	if (subject_label == smack_known_star.smk_known ||
-	    strcmp(subject_label, smack_known_star.smk_known) == 0) {
+	if (subject_label == smack_known_star.smk_known) {
 		rc = -EACCES;
 		rc = -EACCES;
 		goto out_audit;
 		goto out_audit;
 	}
 	}
@@ -148,33 +143,27 @@ int smk_access(char *subject_label, char *object_label, int request,
 	 * An internet subject can access any object.
 	 * An internet subject can access any object.
 	 */
 	 */
 	if (object_label == smack_known_web.smk_known ||
 	if (object_label == smack_known_web.smk_known ||
-	    subject_label == smack_known_web.smk_known ||
-	    strcmp(object_label, smack_known_web.smk_known) == 0 ||
-	    strcmp(subject_label, smack_known_web.smk_known) == 0)
+	    subject_label == smack_known_web.smk_known)
 		goto out_audit;
 		goto out_audit;
 	/*
 	/*
 	 * A star object can be accessed by any subject.
 	 * A star object can be accessed by any subject.
 	 */
 	 */
-	if (object_label == smack_known_star.smk_known ||
-	    strcmp(object_label, smack_known_star.smk_known) == 0)
+	if (object_label == smack_known_star.smk_known)
 		goto out_audit;
 		goto out_audit;
 	/*
 	/*
 	 * An object can be accessed in any way by a subject
 	 * An object can be accessed in any way by a subject
 	 * with the same label.
 	 * with the same label.
 	 */
 	 */
-	if (subject_label == object_label ||
-	    strcmp(subject_label, object_label) == 0)
+	if (subject_label == object_label)
 		goto out_audit;
 		goto out_audit;
 	/*
 	/*
 	 * A hat subject can read any object.
 	 * A hat subject can read any object.
 	 * A floor object can be read by any subject.
 	 * A floor object can be read by any subject.
 	 */
 	 */
 	if ((request & MAY_ANYREAD) == request) {
 	if ((request & MAY_ANYREAD) == request) {
-		if (object_label == smack_known_floor.smk_known ||
-		    strcmp(object_label, smack_known_floor.smk_known) == 0)
+		if (object_label == smack_known_floor.smk_known)
 			goto out_audit;
 			goto out_audit;
-		if (subject_label == smack_known_hat.smk_known ||
-		    strcmp(subject_label, smack_known_hat.smk_known) == 0)
+		if (subject_label == smack_known_hat.smk_known)
 			goto out_audit;
 			goto out_audit;
 	}
 	}
 	/*
 	/*
@@ -184,8 +173,9 @@ int smk_access(char *subject_label, char *object_label, int request,
 	 * good. A negative response from smk_access_entry()
 	 * good. A negative response from smk_access_entry()
 	 * indicates there is no entry for this pair.
 	 * indicates there is no entry for this pair.
 	 */
 	 */
+	skp = smk_find_entry(subject_label);
 	rcu_read_lock();
 	rcu_read_lock();
-	may = smk_access_entry(subject_label, object_label, &smack_rule_list);
+	may = smk_access_entry(subject_label, object_label, &skp->smk_rules);
 	rcu_read_unlock();
 	rcu_read_unlock();
 
 
 	if (may > 0 && (request & may) == request)
 	if (may > 0 && (request & may) == request)
@@ -344,17 +334,32 @@ void smack_log(char *subject_label, char *object_label, int request,
 static DEFINE_MUTEX(smack_known_lock);
 static DEFINE_MUTEX(smack_known_lock);
 
 
 /**
 /**
- * smk_import_entry - import a label, return the list entry
+ * smk_find_entry - find a label on the list, return the list entry
  * @string: a text string that might be a Smack label
  * @string: a text string that might be a Smack label
- * @len: the maximum size, or zero if it is NULL terminated.
  *
  *
  * Returns a pointer to the entry in the label list that
  * Returns a pointer to the entry in the label list that
- * matches the passed string, adding it if necessary.
+ * matches the passed string.
  */
  */
-struct smack_known *smk_import_entry(const char *string, int len)
+struct smack_known *smk_find_entry(const char *string)
 {
 {
 	struct smack_known *skp;
 	struct smack_known *skp;
-	char smack[SMK_LABELLEN];
+
+	list_for_each_entry_rcu(skp, &smack_known_list, list) {
+		if (strncmp(skp->smk_known, string, SMK_MAXLEN) == 0)
+			return skp;
+	}
+
+	return NULL;
+}
+
+/**
+ * smk_parse_smack - parse smack label from a text string
+ * @string: a text string that might contain a Smack label
+ * @len: the maximum size, or zero if it is NULL terminated.
+ * @smack: parsed smack label, or NULL if parse error
+ */
+void smk_parse_smack(const char *string, int len, char *smack)
+{
 	int found;
 	int found;
 	int i;
 	int i;
 
 
@@ -372,27 +377,38 @@ struct smack_known *smk_import_entry(const char *string, int len)
 		} else
 		} else
 			smack[i] = string[i];
 			smack[i] = string[i];
 	}
 	}
+}
+
+/**
+ * smk_import_entry - import a label, return the list entry
+ * @string: a text string that might be a Smack label
+ * @len: the maximum size, or zero if it is NULL terminated.
+ *
+ * Returns a pointer to the entry in the label list that
+ * matches the passed string, adding it if necessary.
+ */
+struct smack_known *smk_import_entry(const char *string, int len)
+{
+	struct smack_known *skp;
+	char smack[SMK_LABELLEN];
 
 
+	smk_parse_smack(string, len, smack);
 	if (smack[0] == '\0')
 	if (smack[0] == '\0')
 		return NULL;
 		return NULL;
 
 
 	mutex_lock(&smack_known_lock);
 	mutex_lock(&smack_known_lock);
 
 
-	found = 0;
-	list_for_each_entry_rcu(skp, &smack_known_list, list) {
-		if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) {
-			found = 1;
-			break;
-		}
-	}
+	skp = smk_find_entry(smack);
 
 
-	if (found == 0) {
+	if (skp == NULL) {
 		skp = kzalloc(sizeof(struct smack_known), GFP_KERNEL);
 		skp = kzalloc(sizeof(struct smack_known), GFP_KERNEL);
 		if (skp != NULL) {
 		if (skp != NULL) {
 			strncpy(skp->smk_known, smack, SMK_MAXLEN);
 			strncpy(skp->smk_known, smack, SMK_MAXLEN);
 			skp->smk_secid = smack_next_secid++;
 			skp->smk_secid = smack_next_secid++;
 			skp->smk_cipso = NULL;
 			skp->smk_cipso = NULL;
+			INIT_LIST_HEAD(&skp->smk_rules);
 			spin_lock_init(&skp->smk_cipsolock);
 			spin_lock_init(&skp->smk_cipsolock);
+			mutex_init(&skp->smk_rules_lock);
 			/*
 			/*
 			 * Make sure that the entry is actually
 			 * Make sure that the entry is actually
 			 * filled before putting it on the list.
 			 * filled before putting it on the list.
@@ -480,19 +496,12 @@ u32 smack_to_secid(const char *smack)
  * smack_from_cipso - find the Smack label associated with a CIPSO option
  * smack_from_cipso - find the Smack label associated with a CIPSO option
  * @level: Bell & LaPadula level from the network
  * @level: Bell & LaPadula level from the network
  * @cp: Bell & LaPadula categories from the network
  * @cp: Bell & LaPadula categories from the network
- * @result: where to put the Smack value
  *
  *
  * This is a simple lookup in the label table.
  * This is a simple lookup in the label table.
  *
  *
- * This is an odd duck as far as smack handling goes in that
- * it sends back a copy of the smack label rather than a pointer
- * to the master list. This is done because it is possible for
- * a foreign host to send a smack label that is new to this
- * machine and hence not on the list. That would not be an
- * issue except that adding an entry to the master list can't
- * be done at that point.
+ * Return the matching label from the label list or NULL.
  */
  */
-void smack_from_cipso(u32 level, char *cp, char *result)
+char *smack_from_cipso(u32 level, char *cp)
 {
 {
 	struct smack_known *kp;
 	struct smack_known *kp;
 	char *final = NULL;
 	char *final = NULL;
@@ -509,12 +518,13 @@ void smack_from_cipso(u32 level, char *cp, char *result)
 			final = kp->smk_known;
 			final = kp->smk_known;
 
 
 		spin_unlock_bh(&kp->smk_cipsolock);
 		spin_unlock_bh(&kp->smk_cipsolock);
+
+		if (final != NULL)
+			break;
 	}
 	}
 	rcu_read_unlock();
 	rcu_read_unlock();
-	if (final == NULL)
-		final = smack_known_huh.smk_known;
-	strncpy(result, final, SMK_MAXLEN);
-	return;
+
+	return final;
 }
 }
 
 
 /**
 /**

+ 179 - 87
security/smack/smack_lsm.c

@@ -5,12 +5,13 @@
  *
  *
  *  Authors:
  *  Authors:
  *	Casey Schaufler <casey@schaufler-ca.com>
  *	Casey Schaufler <casey@schaufler-ca.com>
- *	Jarkko Sakkinen <ext-jarkko.2.sakkinen@nokia.com>
+ *	Jarkko Sakkinen <jarkko.sakkinen@intel.com>
  *
  *
  *  Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
  *  Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
  *  Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
  *  Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
  *                Paul Moore <paul@paul-moore.com>
  *                Paul Moore <paul@paul-moore.com>
  *  Copyright (C) 2010 Nokia Corporation
  *  Copyright (C) 2010 Nokia Corporation
+ *  Copyright (C) 2011 Intel Corporation.
  *
  *
  *	This program is free software; you can redistribute it and/or modify
  *	This program is free software; you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License version 2,
  *	it under the terms of the GNU General Public License version 2,
@@ -34,6 +35,7 @@
 #include <linux/audit.h>
 #include <linux/audit.h>
 #include <linux/magic.h>
 #include <linux/magic.h>
 #include <linux/dcache.h>
 #include <linux/dcache.h>
+#include <linux/personality.h>
 #include "smack.h"
 #include "smack.h"
 
 
 #define task_security(task)	(task_cred_xxx((task), security))
 #define task_security(task)	(task_cred_xxx((task), security))
@@ -441,11 +443,17 @@ static int smack_sb_umount(struct vfsmount *mnt, int flags)
  * BPRM hooks
  * BPRM hooks
  */
  */
 
 
+/**
+ * smack_bprm_set_creds - set creds for exec
+ * @bprm: the exec information
+ *
+ * Returns 0 if it gets a blob, -ENOMEM otherwise
+ */
 static int smack_bprm_set_creds(struct linux_binprm *bprm)
 static int smack_bprm_set_creds(struct linux_binprm *bprm)
 {
 {
-	struct task_smack *tsp = bprm->cred->security;
+	struct inode *inode = bprm->file->f_path.dentry->d_inode;
+	struct task_smack *bsp = bprm->cred->security;
 	struct inode_smack *isp;
 	struct inode_smack *isp;
-	struct dentry *dp;
 	int rc;
 	int rc;
 
 
 	rc = cap_bprm_set_creds(bprm);
 	rc = cap_bprm_set_creds(bprm);
@@ -455,20 +463,48 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm)
 	if (bprm->cred_prepared)
 	if (bprm->cred_prepared)
 		return 0;
 		return 0;
 
 
-	if (bprm->file == NULL || bprm->file->f_dentry == NULL)
+	isp = inode->i_security;
+	if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task)
 		return 0;
 		return 0;
 
 
-	dp = bprm->file->f_dentry;
+	if (bprm->unsafe)
+		return -EPERM;
 
 
-	if (dp->d_inode == NULL)
-		return 0;
+	bsp->smk_task = isp->smk_task;
+	bprm->per_clear |= PER_CLEAR_ON_SETID;
 
 
-	isp = dp->d_inode->i_security;
+	return 0;
+}
 
 
-	if (isp->smk_task != NULL)
-		tsp->smk_task = isp->smk_task;
+/**
+ * smack_bprm_committing_creds - Prepare to install the new credentials
+ * from bprm.
+ *
+ * @bprm: binprm for exec
+ */
+static void smack_bprm_committing_creds(struct linux_binprm *bprm)
+{
+	struct task_smack *bsp = bprm->cred->security;
 
 
-	return 0;
+	if (bsp->smk_task != bsp->smk_forked)
+		current->pdeath_signal = 0;
+}
+
+/**
+ * smack_bprm_secureexec - Return the decision to use secureexec.
+ * @bprm: binprm for exec
+ *
+ * Returns 0 on success.
+ */
+static int smack_bprm_secureexec(struct linux_binprm *bprm)
+{
+	struct task_smack *tsp = current_security();
+	int ret = cap_bprm_secureexec(bprm);
+
+	if (!ret && (tsp->smk_task != tsp->smk_forked))
+		ret = 1;
+
+	return ret;
 }
 }
 
 
 /*
 /*
@@ -516,6 +552,8 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
 				     const struct qstr *qstr, char **name,
 				     const struct qstr *qstr, char **name,
 				     void **value, size_t *len)
 				     void **value, size_t *len)
 {
 {
+	struct smack_known *skp;
+	char *csp = smk_of_current();
 	char *isp = smk_of_inode(inode);
 	char *isp = smk_of_inode(inode);
 	char *dsp = smk_of_inode(dir);
 	char *dsp = smk_of_inode(dir);
 	int may;
 	int may;
@@ -527,8 +565,9 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
 	}
 	}
 
 
 	if (value) {
 	if (value) {
+		skp = smk_find_entry(csp);
 		rcu_read_lock();
 		rcu_read_lock();
-		may = smk_access_entry(smk_of_current(), dsp, &smack_rule_list);
+		may = smk_access_entry(csp, dsp, &skp->smk_rules);
 		rcu_read_unlock();
 		rcu_read_unlock();
 
 
 		/*
 		/*
@@ -841,7 +880,7 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
 	return;
 	return;
 }
 }
 
 
-/*
+/**
  * smack_inode_getxattr - Smack check on getxattr
  * smack_inode_getxattr - Smack check on getxattr
  * @dentry: the object
  * @dentry: the object
  * @name: unused
  * @name: unused
@@ -858,7 +897,7 @@ static int smack_inode_getxattr(struct dentry *dentry, const char *name)
 	return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ, &ad);
 	return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ, &ad);
 }
 }
 
 
-/*
+/**
  * smack_inode_removexattr - Smack check on removexattr
  * smack_inode_removexattr - Smack check on removexattr
  * @dentry: the object
  * @dentry: the object
  * @name: name of the attribute
  * @name: name of the attribute
@@ -1088,36 +1127,31 @@ static int smack_file_lock(struct file *file, unsigned int cmd)
  * @cmd: what action to check
  * @cmd: what action to check
  * @arg: unused
  * @arg: unused
  *
  *
+ * Generally these operations are harmless.
+ * File locking operations present an obvious mechanism
+ * for passing information, so they require write access.
+ *
  * Returns 0 if current has access, error code otherwise
  * Returns 0 if current has access, error code otherwise
  */
  */
 static int smack_file_fcntl(struct file *file, unsigned int cmd,
 static int smack_file_fcntl(struct file *file, unsigned int cmd,
 			    unsigned long arg)
 			    unsigned long arg)
 {
 {
 	struct smk_audit_info ad;
 	struct smk_audit_info ad;
-	int rc;
+	int rc = 0;
 
 
-	smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
-	smk_ad_setfield_u_fs_path(&ad, file->f_path);
 
 
 	switch (cmd) {
 	switch (cmd) {
-	case F_DUPFD:
-	case F_GETFD:
-	case F_GETFL:
 	case F_GETLK:
 	case F_GETLK:
-	case F_GETOWN:
-	case F_GETSIG:
-		rc = smk_curacc(file->f_security, MAY_READ, &ad);
-		break;
-	case F_SETFD:
-	case F_SETFL:
 	case F_SETLK:
 	case F_SETLK:
 	case F_SETLKW:
 	case F_SETLKW:
 	case F_SETOWN:
 	case F_SETOWN:
 	case F_SETSIG:
 	case F_SETSIG:
+		smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
+		smk_ad_setfield_u_fs_path(&ad, file->f_path);
 		rc = smk_curacc(file->f_security, MAY_WRITE, &ad);
 		rc = smk_curacc(file->f_security, MAY_WRITE, &ad);
 		break;
 		break;
 	default:
 	default:
-		rc = smk_curacc(file->f_security, MAY_READWRITE, &ad);
+		break;
 	}
 	}
 
 
 	return rc;
 	return rc;
@@ -1138,6 +1172,7 @@ static int smack_file_mmap(struct file *file,
 			   unsigned long flags, unsigned long addr,
 			   unsigned long flags, unsigned long addr,
 			   unsigned long addr_only)
 			   unsigned long addr_only)
 {
 {
+	struct smack_known *skp;
 	struct smack_rule *srp;
 	struct smack_rule *srp;
 	struct task_smack *tsp;
 	struct task_smack *tsp;
 	char *sp;
 	char *sp;
@@ -1170,6 +1205,7 @@ static int smack_file_mmap(struct file *file,
 
 
 	tsp = current_security();
 	tsp = current_security();
 	sp = smk_of_current();
 	sp = smk_of_current();
+	skp = smk_find_entry(sp);
 	rc = 0;
 	rc = 0;
 
 
 	rcu_read_lock();
 	rcu_read_lock();
@@ -1177,15 +1213,8 @@ static int smack_file_mmap(struct file *file,
 	 * For each Smack rule associated with the subject
 	 * For each Smack rule associated with the subject
 	 * label verify that the SMACK64MMAP also has access
 	 * label verify that the SMACK64MMAP also has access
 	 * to that rule's object label.
 	 * to that rule's object label.
-	 *
-	 * Because neither of the labels comes
-	 * from the networking code it is sufficient
-	 * to compare pointers.
 	 */
 	 */
-	list_for_each_entry_rcu(srp, &smack_rule_list, list) {
-		if (srp->smk_subject != sp)
-			continue;
-
+	list_for_each_entry_rcu(srp, &skp->smk_rules, list) {
 		osmack = srp->smk_object;
 		osmack = srp->smk_object;
 		/*
 		/*
 		 * Matching labels always allows access.
 		 * Matching labels always allows access.
@@ -1214,7 +1243,8 @@ static int smack_file_mmap(struct file *file,
 		 * If there isn't one a SMACK64MMAP subject
 		 * If there isn't one a SMACK64MMAP subject
 		 * can't have as much access as current.
 		 * can't have as much access as current.
 		 */
 		 */
-		mmay = smk_access_entry(msmack, osmack, &smack_rule_list);
+		skp = smk_find_entry(msmack);
+		mmay = smk_access_entry(msmack, osmack, &skp->smk_rules);
 		if (mmay == -ENOENT) {
 		if (mmay == -ENOENT) {
 			rc = -EACCES;
 			rc = -EACCES;
 			break;
 			break;
@@ -1315,6 +1345,24 @@ static int smack_file_receive(struct file *file)
 	return smk_curacc(file->f_security, may, &ad);
 	return smk_curacc(file->f_security, may, &ad);
 }
 }
 
 
+/**
+ * smack_dentry_open - Smack dentry open processing
+ * @file: the object
+ * @cred: unused
+ *
+ * Set the security blob in the file structure.
+ *
+ * Returns 0
+ */
+static int smack_dentry_open(struct file *file, const struct cred *cred)
+{
+	struct inode_smack *isp = file->f_path.dentry->d_inode->i_security;
+
+	file->f_security = isp->smk_inode;
+
+	return 0;
+}
+
 /*
 /*
  * Task hooks
  * Task hooks
  */
  */
@@ -1455,15 +1503,17 @@ static int smack_kernel_create_files_as(struct cred *new,
 /**
 /**
  * smk_curacc_on_task - helper to log task related access
  * smk_curacc_on_task - helper to log task related access
  * @p: the task object
  * @p: the task object
- * @access : the access requested
+ * @access: the access requested
+ * @caller: name of the calling function for audit
  *
  *
  * Return 0 if access is permitted
  * Return 0 if access is permitted
  */
  */
-static int smk_curacc_on_task(struct task_struct *p, int access)
+static int smk_curacc_on_task(struct task_struct *p, int access,
+				const char *caller)
 {
 {
 	struct smk_audit_info ad;
 	struct smk_audit_info ad;
 
 
-	smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
+	smk_ad_init(&ad, caller, LSM_AUDIT_DATA_TASK);
 	smk_ad_setfield_u_tsk(&ad, p);
 	smk_ad_setfield_u_tsk(&ad, p);
 	return smk_curacc(smk_of_task(task_security(p)), access, &ad);
 	return smk_curacc(smk_of_task(task_security(p)), access, &ad);
 }
 }
@@ -1477,7 +1527,7 @@ static int smk_curacc_on_task(struct task_struct *p, int access)
  */
  */
 static int smack_task_setpgid(struct task_struct *p, pid_t pgid)
 static int smack_task_setpgid(struct task_struct *p, pid_t pgid)
 {
 {
-	return smk_curacc_on_task(p, MAY_WRITE);
+	return smk_curacc_on_task(p, MAY_WRITE, __func__);
 }
 }
 
 
 /**
 /**
@@ -1488,7 +1538,7 @@ static int smack_task_setpgid(struct task_struct *p, pid_t pgid)
  */
  */
 static int smack_task_getpgid(struct task_struct *p)
 static int smack_task_getpgid(struct task_struct *p)
 {
 {
-	return smk_curacc_on_task(p, MAY_READ);
+	return smk_curacc_on_task(p, MAY_READ, __func__);
 }
 }
 
 
 /**
 /**
@@ -1499,7 +1549,7 @@ static int smack_task_getpgid(struct task_struct *p)
  */
  */
 static int smack_task_getsid(struct task_struct *p)
 static int smack_task_getsid(struct task_struct *p)
 {
 {
-	return smk_curacc_on_task(p, MAY_READ);
+	return smk_curacc_on_task(p, MAY_READ, __func__);
 }
 }
 
 
 /**
 /**
@@ -1527,7 +1577,7 @@ static int smack_task_setnice(struct task_struct *p, int nice)
 
 
 	rc = cap_task_setnice(p, nice);
 	rc = cap_task_setnice(p, nice);
 	if (rc == 0)
 	if (rc == 0)
-		rc = smk_curacc_on_task(p, MAY_WRITE);
+		rc = smk_curacc_on_task(p, MAY_WRITE, __func__);
 	return rc;
 	return rc;
 }
 }
 
 
@@ -1544,7 +1594,7 @@ static int smack_task_setioprio(struct task_struct *p, int ioprio)
 
 
 	rc = cap_task_setioprio(p, ioprio);
 	rc = cap_task_setioprio(p, ioprio);
 	if (rc == 0)
 	if (rc == 0)
-		rc = smk_curacc_on_task(p, MAY_WRITE);
+		rc = smk_curacc_on_task(p, MAY_WRITE, __func__);
 	return rc;
 	return rc;
 }
 }
 
 
@@ -1556,7 +1606,7 @@ static int smack_task_setioprio(struct task_struct *p, int ioprio)
  */
  */
 static int smack_task_getioprio(struct task_struct *p)
 static int smack_task_getioprio(struct task_struct *p)
 {
 {
-	return smk_curacc_on_task(p, MAY_READ);
+	return smk_curacc_on_task(p, MAY_READ, __func__);
 }
 }
 
 
 /**
 /**
@@ -1573,7 +1623,7 @@ static int smack_task_setscheduler(struct task_struct *p)
 
 
 	rc = cap_task_setscheduler(p);
 	rc = cap_task_setscheduler(p);
 	if (rc == 0)
 	if (rc == 0)
-		rc = smk_curacc_on_task(p, MAY_WRITE);
+		rc = smk_curacc_on_task(p, MAY_WRITE, __func__);
 	return rc;
 	return rc;
 }
 }
 
 
@@ -1585,7 +1635,7 @@ static int smack_task_setscheduler(struct task_struct *p)
  */
  */
 static int smack_task_getscheduler(struct task_struct *p)
 static int smack_task_getscheduler(struct task_struct *p)
 {
 {
-	return smk_curacc_on_task(p, MAY_READ);
+	return smk_curacc_on_task(p, MAY_READ, __func__);
 }
 }
 
 
 /**
 /**
@@ -1596,7 +1646,7 @@ static int smack_task_getscheduler(struct task_struct *p)
  */
  */
 static int smack_task_movememory(struct task_struct *p)
 static int smack_task_movememory(struct task_struct *p)
 {
 {
-	return smk_curacc_on_task(p, MAY_WRITE);
+	return smk_curacc_on_task(p, MAY_WRITE, __func__);
 }
 }
 
 
 /**
 /**
@@ -1711,7 +1761,7 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
 
 
 	ssp->smk_in = csp;
 	ssp->smk_in = csp;
 	ssp->smk_out = csp;
 	ssp->smk_out = csp;
-	ssp->smk_packet[0] = '\0';
+	ssp->smk_packet = NULL;
 
 
 	sk->sk_security = ssp;
 	sk->sk_security = ssp;
 
 
@@ -2753,6 +2803,7 @@ static int smack_unix_stream_connect(struct sock *sock,
 {
 {
 	struct socket_smack *ssp = sock->sk_security;
 	struct socket_smack *ssp = sock->sk_security;
 	struct socket_smack *osp = other->sk_security;
 	struct socket_smack *osp = other->sk_security;
+	struct socket_smack *nsp = newsk->sk_security;
 	struct smk_audit_info ad;
 	struct smk_audit_info ad;
 	int rc = 0;
 	int rc = 0;
 
 
@@ -2762,6 +2813,14 @@ static int smack_unix_stream_connect(struct sock *sock,
 	if (!capable(CAP_MAC_OVERRIDE))
 	if (!capable(CAP_MAC_OVERRIDE))
 		rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad);
 		rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad);
 
 
+	/*
+	 * Cross reference the peer labels for SO_PEERSEC.
+	 */
+	if (rc == 0) {
+		nsp->smk_packet = ssp->smk_out;
+		ssp->smk_packet = osp->smk_out;
+	}
+
 	return rc;
 	return rc;
 }
 }
 
 
@@ -2813,16 +2872,17 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
 	return smack_netlabel_send(sock->sk, sip);
 	return smack_netlabel_send(sock->sk, sip);
 }
 }
 
 
-
 /**
 /**
  * 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
+ * @ssp: socket security information
  *
  *
- * Copies a smack label into sip
+ * Returns a pointer to a Smack label found on the label list.
  */
  */
-static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip)
+static char *smack_from_secattr(struct netlbl_lsm_secattr *sap,
+				struct socket_smack *ssp)
 {
 {
+	struct smack_known *skp;
 	char smack[SMK_LABELLEN];
 	char smack[SMK_LABELLEN];
 	char *sp;
 	char *sp;
 	int pcat;
 	int pcat;
@@ -2852,15 +2912,43 @@ static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip)
 		 * we are already done. WeeHee.
 		 * we are already done. WeeHee.
 		 */
 		 */
 		if (sap->attr.mls.lvl == smack_cipso_direct) {
 		if (sap->attr.mls.lvl == smack_cipso_direct) {
-			memcpy(sip, smack, SMK_MAXLEN);
-			return;
+			/*
+			 * The label sent is usually on the label list.
+			 *
+			 * If it is not we may still want to allow the
+			 * delivery.
+			 *
+			 * If the recipient is accepting all packets
+			 * because it is using the star ("*") label
+			 * for SMACK64IPIN provide the web ("@") label
+			 * so that a directed response will succeed.
+			 * This is not very correct from a MAC point
+			 * of view, but gets around the problem that
+			 * locking prevents adding the newly discovered
+			 * label to the list.
+			 * The case where the recipient is not using
+			 * the star label should obviously fail.
+			 * The easy way to do this is to provide the
+			 * star label as the subject label.
+			 */
+			skp = smk_find_entry(smack);
+			if (skp != NULL)
+				return skp->smk_known;
+			if (ssp != NULL &&
+			    ssp->smk_in == smack_known_star.smk_known)
+				return smack_known_web.smk_known;
+			return smack_known_star.smk_known;
 		}
 		}
 		/*
 		/*
 		 * Look it up in the supplied table if it is not
 		 * Look it up in the supplied table if it is not
 		 * a direct mapping.
 		 * a direct mapping.
 		 */
 		 */
-		smack_from_cipso(sap->attr.mls.lvl, smack, sip);
-		return;
+		sp = smack_from_cipso(sap->attr.mls.lvl, smack);
+		if (sp != NULL)
+			return sp;
+		if (ssp != NULL && ssp->smk_in == smack_known_star.smk_known)
+			return smack_known_web.smk_known;
+		return smack_known_star.smk_known;
 	}
 	}
 	if ((sap->flags & NETLBL_SECATTR_SECID) != 0) {
 	if ((sap->flags & NETLBL_SECATTR_SECID) != 0) {
 		/*
 		/*
@@ -2875,16 +2963,14 @@ static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip)
 		 * secid is from a fallback.
 		 * secid is from a fallback.
 		 */
 		 */
 		BUG_ON(sp == NULL);
 		BUG_ON(sp == NULL);
-		strncpy(sip, sp, SMK_MAXLEN);
-		return;
+		return sp;
 	}
 	}
 	/*
 	/*
 	 * Without guidance regarding the smack value
 	 * Without guidance regarding the smack value
 	 * for the packet fall back on the network
 	 * for the packet fall back on the network
 	 * ambient value.
 	 * ambient value.
 	 */
 	 */
-	strncpy(sip, smack_net_ambient, SMK_MAXLEN);
-	return;
+	return smack_net_ambient;
 }
 }
 
 
 /**
 /**
@@ -2898,7 +2984,6 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
 {
 	struct netlbl_lsm_secattr secattr;
 	struct netlbl_lsm_secattr secattr;
 	struct socket_smack *ssp = sk->sk_security;
 	struct socket_smack *ssp = sk->sk_security;
-	char smack[SMK_LABELLEN];
 	char *csp;
 	char *csp;
 	int rc;
 	int rc;
 	struct smk_audit_info ad;
 	struct smk_audit_info ad;
@@ -2911,10 +2996,9 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
 	netlbl_secattr_init(&secattr);
 	netlbl_secattr_init(&secattr);
 
 
 	rc = netlbl_skbuff_getattr(skb, sk->sk_family, &secattr);
 	rc = netlbl_skbuff_getattr(skb, sk->sk_family, &secattr);
-	if (rc == 0) {
-		smack_from_secattr(&secattr, smack);
-		csp = smack;
-	} else
+	if (rc == 0)
+		csp = smack_from_secattr(&secattr, ssp);
+	else
 		csp = smack_net_ambient;
 		csp = smack_net_ambient;
 
 
 	netlbl_secattr_destroy(&secattr);
 	netlbl_secattr_destroy(&secattr);
@@ -2951,15 +3035,19 @@ static int smack_socket_getpeersec_stream(struct socket *sock,
 					  int __user *optlen, unsigned len)
 					  int __user *optlen, unsigned len)
 {
 {
 	struct socket_smack *ssp;
 	struct socket_smack *ssp;
-	int slen;
+	char *rcp = "";
+	int slen = 1;
 	int rc = 0;
 	int rc = 0;
 
 
 	ssp = sock->sk->sk_security;
 	ssp = sock->sk->sk_security;
-	slen = strlen(ssp->smk_packet) + 1;
+	if (ssp->smk_packet != NULL) {
+		rcp = ssp->smk_packet;
+		slen = strlen(rcp) + 1;
+	}
 
 
 	if (slen > len)
 	if (slen > len)
 		rc = -ERANGE;
 		rc = -ERANGE;
-	else if (copy_to_user(optval, ssp->smk_packet, slen) != 0)
+	else if (copy_to_user(optval, rcp, slen) != 0)
 		rc = -EFAULT;
 		rc = -EFAULT;
 
 
 	if (put_user(slen, optlen) != 0)
 	if (put_user(slen, optlen) != 0)
@@ -2982,8 +3070,8 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
 
 
 {
 {
 	struct netlbl_lsm_secattr secattr;
 	struct netlbl_lsm_secattr secattr;
-	struct socket_smack *sp;
-	char smack[SMK_LABELLEN];
+	struct socket_smack *ssp = NULL;
+	char *sp;
 	int family = PF_UNSPEC;
 	int family = PF_UNSPEC;
 	u32 s = 0;	/* 0 is the invalid secid */
 	u32 s = 0;	/* 0 is the invalid secid */
 	int rc;
 	int rc;
@@ -2998,17 +3086,19 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
 		family = sock->sk->sk_family;
 		family = sock->sk->sk_family;
 
 
 	if (family == PF_UNIX) {
 	if (family == PF_UNIX) {
-		sp = sock->sk->sk_security;
-		s = smack_to_secid(sp->smk_out);
+		ssp = sock->sk->sk_security;
+		s = smack_to_secid(ssp->smk_out);
 	} else if (family == PF_INET || family == PF_INET6) {
 	} else if (family == PF_INET || family == PF_INET6) {
 		/*
 		/*
 		 * Translate what netlabel gave us.
 		 * Translate what netlabel gave us.
 		 */
 		 */
+		if (sock != NULL && sock->sk != NULL)
+			ssp = sock->sk->sk_security;
 		netlbl_secattr_init(&secattr);
 		netlbl_secattr_init(&secattr);
 		rc = netlbl_skbuff_getattr(skb, family, &secattr);
 		rc = netlbl_skbuff_getattr(skb, family, &secattr);
 		if (rc == 0) {
 		if (rc == 0) {
-			smack_from_secattr(&secattr, smack);
-			s = smack_to_secid(smack);
+			sp = smack_from_secattr(&secattr, ssp);
+			s = smack_to_secid(sp);
 		}
 		}
 		netlbl_secattr_destroy(&secattr);
 		netlbl_secattr_destroy(&secattr);
 	}
 	}
@@ -3056,7 +3146,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
 	struct netlbl_lsm_secattr secattr;
 	struct netlbl_lsm_secattr secattr;
 	struct sockaddr_in addr;
 	struct sockaddr_in addr;
 	struct iphdr *hdr;
 	struct iphdr *hdr;
-	char smack[SMK_LABELLEN];
+	char *sp;
 	int rc;
 	int rc;
 	struct smk_audit_info ad;
 	struct smk_audit_info ad;
 
 
@@ -3067,9 +3157,9 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
 	netlbl_secattr_init(&secattr);
 	netlbl_secattr_init(&secattr);
 	rc = netlbl_skbuff_getattr(skb, family, &secattr);
 	rc = netlbl_skbuff_getattr(skb, family, &secattr);
 	if (rc == 0)
 	if (rc == 0)
-		smack_from_secattr(&secattr, smack);
+		sp = smack_from_secattr(&secattr, ssp);
 	else
 	else
-		strncpy(smack, smack_known_huh.smk_known, SMK_MAXLEN);
+		sp = smack_known_huh.smk_known;
 	netlbl_secattr_destroy(&secattr);
 	netlbl_secattr_destroy(&secattr);
 
 
 #ifdef CONFIG_AUDIT
 #ifdef CONFIG_AUDIT
@@ -3082,7 +3172,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
 	 * Receiving a packet requires that the other end be able to write
 	 * Receiving a packet requires that the other end be able to write
 	 * here. Read access is not required.
 	 * here. Read access is not required.
 	 */
 	 */
-	rc = smk_access(smack, ssp->smk_in, MAY_WRITE, &ad);
+	rc = smk_access(sp, ssp->smk_in, MAY_WRITE, &ad);
 	if (rc != 0)
 	if (rc != 0)
 		return rc;
 		return rc;
 
 
@@ -3090,7 +3180,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
 	 * Save the peer's label in the request_sock so we can later setup
 	 * Save the peer's label in the request_sock so we can later setup
 	 * smk_packet in the child socket so that SO_PEERCRED can report it.
 	 * smk_packet in the child socket so that SO_PEERCRED can report it.
 	 */
 	 */
-	req->peer_secid = smack_to_secid(smack);
+	req->peer_secid = smack_to_secid(sp);
 
 
 	/*
 	/*
 	 * We need to decide if we want to label the incoming connection here
 	 * We need to decide if we want to label the incoming connection here
@@ -3103,7 +3193,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
 	if (smack_host_label(&addr) == NULL) {
 	if (smack_host_label(&addr) == NULL) {
 		rcu_read_unlock();
 		rcu_read_unlock();
 		netlbl_secattr_init(&secattr);
 		netlbl_secattr_init(&secattr);
-		smack_to_secattr(smack, &secattr);
+		smack_to_secattr(sp, &secattr);
 		rc = netlbl_req_setattr(req, &secattr);
 		rc = netlbl_req_setattr(req, &secattr);
 		netlbl_secattr_destroy(&secattr);
 		netlbl_secattr_destroy(&secattr);
 	} else {
 	} else {
@@ -3125,13 +3215,11 @@ static void smack_inet_csk_clone(struct sock *sk,
 				 const struct request_sock *req)
 				 const struct request_sock *req)
 {
 {
 	struct socket_smack *ssp = sk->sk_security;
 	struct socket_smack *ssp = sk->sk_security;
-	char *smack;
 
 
-	if (req->peer_secid != 0) {
-		smack = smack_from_secid(req->peer_secid);
-		strncpy(ssp->smk_packet, smack, SMK_MAXLEN);
-	} else
-		ssp->smk_packet[0] = '\0';
+	if (req->peer_secid != 0)
+		ssp->smk_packet = smack_from_secid(req->peer_secid);
+	else
+		ssp->smk_packet = NULL;
 }
 }
 
 
 /*
 /*
@@ -3409,6 +3497,8 @@ struct security_operations smack_ops = {
 	.sb_umount = 			smack_sb_umount,
 	.sb_umount = 			smack_sb_umount,
 
 
 	.bprm_set_creds =		smack_bprm_set_creds,
 	.bprm_set_creds =		smack_bprm_set_creds,
+	.bprm_committing_creds =	smack_bprm_committing_creds,
+	.bprm_secureexec =		smack_bprm_secureexec,
 
 
 	.inode_alloc_security = 	smack_inode_alloc_security,
 	.inode_alloc_security = 	smack_inode_alloc_security,
 	.inode_free_security = 		smack_inode_free_security,
 	.inode_free_security = 		smack_inode_free_security,
@@ -3440,6 +3530,8 @@ struct security_operations smack_ops = {
 	.file_send_sigiotask = 		smack_file_send_sigiotask,
 	.file_send_sigiotask = 		smack_file_send_sigiotask,
 	.file_receive = 		smack_file_receive,
 	.file_receive = 		smack_file_receive,
 
 
+	.dentry_open =			smack_dentry_open,
+
 	.cred_alloc_blank =		smack_cred_alloc_blank,
 	.cred_alloc_blank =		smack_cred_alloc_blank,
 	.cred_free =			smack_cred_free,
 	.cred_free =			smack_cred_free,
 	.cred_prepare =			smack_cred_prepare,
 	.cred_prepare =			smack_cred_prepare,

+ 204 - 73
security/smack/smackfs.c

@@ -44,6 +44,7 @@ enum smk_inos {
 	SMK_ONLYCAP	= 9,	/* the only "capable" label */
 	SMK_ONLYCAP	= 9,	/* the only "capable" label */
 	SMK_LOGGING	= 10,	/* logging */
 	SMK_LOGGING	= 10,	/* logging */
 	SMK_LOAD_SELF	= 11,	/* task specific rules */
 	SMK_LOAD_SELF	= 11,	/* task specific rules */
+	SMK_ACCESSES	= 12,	/* access policy */
 };
 };
 
 
 /*
 /*
@@ -85,6 +86,16 @@ char *smack_onlycap;
  */
  */
 
 
 LIST_HEAD(smk_netlbladdr_list);
 LIST_HEAD(smk_netlbladdr_list);
+
+/*
+ * Rule lists are maintained for each label.
+ * This master list is just for reading /smack/load.
+ */
+struct smack_master_list {
+	struct list_head	list;
+	struct smack_rule	*smk_rule;
+};
+
 LIST_HEAD(smack_rule_list);
 LIST_HEAD(smack_rule_list);
 
 
 static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
 static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
@@ -92,7 +103,7 @@ static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
 const char *smack_cipso_option = SMACK_CIPSO_OPTION;
 const char *smack_cipso_option = SMACK_CIPSO_OPTION;
 
 
 
 
-#define	SEQ_READ_FINISHED	1
+#define	SEQ_READ_FINISHED	((loff_t)-1)
 
 
 /*
 /*
  * Values for parsing cipso rules
  * Values for parsing cipso rules
@@ -159,9 +170,13 @@ static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list,
 
 
 	mutex_lock(rule_lock);
 	mutex_lock(rule_lock);
 
 
+	/*
+	 * Because the object label is less likely to match
+	 * than the subject label check it first
+	 */
 	list_for_each_entry_rcu(sp, rule_list, list) {
 	list_for_each_entry_rcu(sp, rule_list, list) {
-		if (sp->smk_subject == srp->smk_subject &&
-		    sp->smk_object == srp->smk_object) {
+		if (sp->smk_object == srp->smk_object &&
+		    sp->smk_subject == srp->smk_subject) {
 			found = 1;
 			found = 1;
 			sp->smk_access = srp->smk_access;
 			sp->smk_access = srp->smk_access;
 			break;
 			break;
@@ -175,6 +190,99 @@ static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list,
 	return found;
 	return found;
 }
 }
 
 
+/**
+ * smk_parse_rule - parse Smack rule from load string
+ * @data: string to be parsed whose size is SMK_LOADLEN
+ * @rule: Smack rule
+ * @import: if non-zero, import labels
+ */
+static int smk_parse_rule(const char *data, struct smack_rule *rule, int import)
+{
+	char smack[SMK_LABELLEN];
+	struct smack_known *skp;
+
+	if (import) {
+		rule->smk_subject = smk_import(data, 0);
+		if (rule->smk_subject == NULL)
+			return -1;
+
+		rule->smk_object = smk_import(data + SMK_LABELLEN, 0);
+		if (rule->smk_object == NULL)
+			return -1;
+	} else {
+		smk_parse_smack(data, 0, smack);
+		skp = smk_find_entry(smack);
+		if (skp == NULL)
+			return -1;
+		rule->smk_subject = skp->smk_known;
+
+		smk_parse_smack(data + SMK_LABELLEN, 0, smack);
+		skp = smk_find_entry(smack);
+		if (skp == NULL)
+			return -1;
+		rule->smk_object = skp->smk_known;
+	}
+
+	rule->smk_access = 0;
+
+	switch (data[SMK_LABELLEN + SMK_LABELLEN]) {
+	case '-':
+		break;
+	case 'r':
+	case 'R':
+		rule->smk_access |= MAY_READ;
+		break;
+	default:
+		return -1;
+	}
+
+	switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) {
+	case '-':
+		break;
+	case 'w':
+	case 'W':
+		rule->smk_access |= MAY_WRITE;
+		break;
+	default:
+		return -1;
+	}
+
+	switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) {
+	case '-':
+		break;
+	case 'x':
+	case 'X':
+		rule->smk_access |= MAY_EXEC;
+		break;
+	default:
+		return -1;
+	}
+
+	switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) {
+	case '-':
+		break;
+	case 'a':
+	case 'A':
+		rule->smk_access |= MAY_APPEND;
+		break;
+	default:
+		return -1;
+	}
+
+	switch (data[SMK_LABELLEN + SMK_LABELLEN + 4]) {
+	case '-':
+		break;
+	case 't':
+	case 'T':
+		rule->smk_access |= MAY_TRANSMUTE;
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
 /**
 /**
  * smk_write_load_list - write() for any /smack/load
  * smk_write_load_list - write() for any /smack/load
  * @file: file pointer, not actually used
  * @file: file pointer, not actually used
@@ -197,9 +305,12 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf,
 				struct list_head *rule_list,
 				struct list_head *rule_list,
 				struct mutex *rule_lock)
 				struct mutex *rule_lock)
 {
 {
+	struct smack_master_list *smlp;
+	struct smack_known *skp;
 	struct smack_rule *rule;
 	struct smack_rule *rule;
 	char *data;
 	char *data;
 	int rc = -EINVAL;
 	int rc = -EINVAL;
+	int load = 0;
 
 
 	/*
 	/*
 	 * No partial writes.
 	 * No partial writes.
@@ -234,69 +345,14 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf,
 		goto out;
 		goto out;
 	}
 	}
 
 
-	rule->smk_subject = smk_import(data, 0);
-	if (rule->smk_subject == NULL)
-		goto out_free_rule;
-
-	rule->smk_object = smk_import(data + SMK_LABELLEN, 0);
-	if (rule->smk_object == NULL)
+	if (smk_parse_rule(data, rule, 1))
 		goto out_free_rule;
 		goto out_free_rule;
 
 
-	rule->smk_access = 0;
-
-	switch (data[SMK_LABELLEN + SMK_LABELLEN]) {
-	case '-':
-		break;
-	case 'r':
-	case 'R':
-		rule->smk_access |= MAY_READ;
-		break;
-	default:
-		goto out_free_rule;
-	}
-
-	switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) {
-	case '-':
-		break;
-	case 'w':
-	case 'W':
-		rule->smk_access |= MAY_WRITE;
-		break;
-	default:
-		goto out_free_rule;
-	}
-
-	switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) {
-	case '-':
-		break;
-	case 'x':
-	case 'X':
-		rule->smk_access |= MAY_EXEC;
-		break;
-	default:
-		goto out_free_rule;
-	}
-
-	switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) {
-	case '-':
-		break;
-	case 'a':
-	case 'A':
-		rule->smk_access |= MAY_APPEND;
-		break;
-	default:
-		goto out_free_rule;
-	}
-
-	switch (data[SMK_LABELLEN + SMK_LABELLEN + 4]) {
-	case '-':
-		break;
-	case 't':
-	case 'T':
-		rule->smk_access |= MAY_TRANSMUTE;
-		break;
-	default:
-		goto out_free_rule;
+	if (rule_list == NULL) {
+		load = 1;
+		skp = smk_find_entry(rule->smk_subject);
+		rule_list = &skp->smk_rules;
+		rule_lock = &skp->smk_rules_lock;
 	}
 	}
 
 
 	rc = count;
 	rc = count;
@@ -304,8 +360,15 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf,
 	 * smk_set_access returns true if there was already a rule
 	 * smk_set_access returns true if there was already a rule
 	 * for the subject/object pair, and false if it was new.
 	 * for the subject/object pair, and false if it was new.
 	 */
 	 */
-	if (!smk_set_access(rule, rule_list, rule_lock))
+	if (!smk_set_access(rule, rule_list, rule_lock)) {
+		smlp = kzalloc(sizeof(*smlp), GFP_KERNEL);
+		if (smlp != NULL) {
+			smlp->smk_rule = rule;
+			list_add_rcu(&smlp->list, &smack_rule_list);
+		} else
+			rc = -ENOMEM;
 		goto out;
 		goto out;
+	}
 
 
 out_free_rule:
 out_free_rule:
 	kfree(rule);
 	kfree(rule);
@@ -321,11 +384,24 @@ out:
 
 
 static void *load_seq_start(struct seq_file *s, loff_t *pos)
 static void *load_seq_start(struct seq_file *s, loff_t *pos)
 {
 {
-	if (*pos == SEQ_READ_FINISHED)
+	struct list_head *list;
+
+	/*
+	 * This is 0 the first time through.
+	 */
+	if (s->index == 0)
+		s->private = &smack_rule_list;
+
+	if (s->private == NULL)
 		return NULL;
 		return NULL;
-	if (list_empty(&smack_rule_list))
+
+	list = s->private;
+	if (list_empty(list))
 		return NULL;
 		return NULL;
-	return smack_rule_list.next;
+
+	if (s->index == 0)
+		return list->next;
+	return list;
 }
 }
 
 
 static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos)
 static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos)
@@ -333,17 +409,19 @@ static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos)
 	struct list_head *list = v;
 	struct list_head *list = v;
 
 
 	if (list_is_last(list, &smack_rule_list)) {
 	if (list_is_last(list, &smack_rule_list)) {
-		*pos = SEQ_READ_FINISHED;
+		s->private = NULL;
 		return NULL;
 		return NULL;
 	}
 	}
+	s->private = list->next;
 	return list->next;
 	return list->next;
 }
 }
 
 
 static int load_seq_show(struct seq_file *s, void *v)
 static int load_seq_show(struct seq_file *s, void *v)
 {
 {
 	struct list_head *list = v;
 	struct list_head *list = v;
-	struct smack_rule *srp =
-		 list_entry(list, struct smack_rule, list);
+	struct smack_master_list *smlp =
+		 list_entry(list, struct smack_master_list, list);
+	struct smack_rule *srp = smlp->smk_rule;
 
 
 	seq_printf(s, "%s %s", (char *)srp->smk_subject,
 	seq_printf(s, "%s %s", (char *)srp->smk_subject,
 		   (char *)srp->smk_object);
 		   (char *)srp->smk_object);
@@ -412,8 +490,7 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf,
 	if (!capable(CAP_MAC_ADMIN))
 	if (!capable(CAP_MAC_ADMIN))
 		return -EPERM;
 		return -EPERM;
 
 
-	return smk_write_load_list(file, buf, count, ppos, &smack_rule_list,
-					&smack_list_lock);
+	return smk_write_load_list(file, buf, count, ppos, NULL, NULL);
 }
 }
 
 
 static const struct file_operations smk_load_ops = {
 static const struct file_operations smk_load_ops = {
@@ -1425,6 +1502,44 @@ static const struct file_operations smk_load_self_ops = {
 	.write		= smk_write_load_self,
 	.write		= smk_write_load_self,
 	.release        = seq_release,
 	.release        = seq_release,
 };
 };
+
+/**
+ * smk_write_access - handle access check transaction
+ * @file: file pointer
+ * @buf: data from user space
+ * @count: bytes sent
+ * @ppos: where to start - must be 0
+ */
+static ssize_t smk_write_access(struct file *file, const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	struct smack_rule rule;
+	char *data;
+	int res;
+
+	data = simple_transaction_get(file, buf, count);
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	if (count < SMK_LOADLEN || smk_parse_rule(data, &rule, 0))
+		return -EINVAL;
+
+	res = smk_access(rule.smk_subject, rule.smk_object, rule.smk_access,
+			  NULL);
+	data[0] = res == 0 ? '1' : '0';
+	data[1] = '\0';
+
+	simple_transaction_set(file, 2);
+	return SMK_LOADLEN;
+}
+
+static const struct file_operations smk_access_ops = {
+	.write		= smk_write_access,
+	.read		= simple_transaction_read,
+	.release	= simple_transaction_release,
+	.llseek		= generic_file_llseek,
+};
+
 /**
 /**
  * smk_fill_super - fill the /smackfs superblock
  * smk_fill_super - fill the /smackfs superblock
  * @sb: the empty superblock
  * @sb: the empty superblock
@@ -1459,6 +1574,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
 			"logging", &smk_logging_ops, S_IRUGO|S_IWUSR},
 			"logging", &smk_logging_ops, S_IRUGO|S_IWUSR},
 		[SMK_LOAD_SELF] = {
 		[SMK_LOAD_SELF] = {
 			"load-self", &smk_load_self_ops, S_IRUGO|S_IWUGO},
 			"load-self", &smk_load_self_ops, S_IRUGO|S_IWUGO},
+		[SMK_ACCESSES] = {
+			"access", &smk_access_ops, S_IRUGO|S_IWUGO},
 		/* last one */
 		/* last one */
 			{""}
 			{""}
 	};
 	};
@@ -1534,6 +1651,20 @@ static int __init init_smk_fs(void)
 	smk_cipso_doi();
 	smk_cipso_doi();
 	smk_unlbl_ambient(NULL);
 	smk_unlbl_ambient(NULL);
 
 
+	mutex_init(&smack_known_floor.smk_rules_lock);
+	mutex_init(&smack_known_hat.smk_rules_lock);
+	mutex_init(&smack_known_huh.smk_rules_lock);
+	mutex_init(&smack_known_invalid.smk_rules_lock);
+	mutex_init(&smack_known_star.smk_rules_lock);
+	mutex_init(&smack_known_web.smk_rules_lock);
+
+	INIT_LIST_HEAD(&smack_known_floor.smk_rules);
+	INIT_LIST_HEAD(&smack_known_hat.smk_rules);
+	INIT_LIST_HEAD(&smack_known_huh.smk_rules);
+	INIT_LIST_HEAD(&smack_known_invalid.smk_rules);
+	INIT_LIST_HEAD(&smack_known_star.smk_rules);
+	INIT_LIST_HEAD(&smack_known_web.smk_rules);
+
 	return err;
 	return err;
 }
 }
 
 

+ 2 - 0
security/tomoyo/Kconfig

@@ -1,8 +1,10 @@
 config SECURITY_TOMOYO
 config SECURITY_TOMOYO
 	bool "TOMOYO Linux Support"
 	bool "TOMOYO Linux Support"
 	depends on SECURITY
 	depends on SECURITY
+	depends on NET
 	select SECURITYFS
 	select SECURITYFS
 	select SECURITY_PATH
 	select SECURITY_PATH
+	select SECURITY_NETWORK
 	default n
 	default n
 	help
 	help
 	  This selects TOMOYO Linux, pathname-based access control.
 	  This selects TOMOYO Linux, pathname-based access control.

+ 2 - 2
security/tomoyo/Makefile

@@ -1,4 +1,4 @@
-obj-y = audit.o common.o condition.o domain.o file.o gc.o group.o load_policy.o memory.o mount.o realpath.o securityfs_if.o tomoyo.o util.o
+obj-y = audit.o common.o condition.o domain.o environ.o file.o gc.o group.o load_policy.o memory.o mount.o network.o realpath.o securityfs_if.o tomoyo.o util.o
 
 
 $(obj)/policy/profile.conf:
 $(obj)/policy/profile.conf:
 	@mkdir -p $(obj)/policy/
 	@mkdir -p $(obj)/policy/
@@ -27,7 +27,7 @@ $(obj)/policy/stat.conf:
 	@touch $@
 	@touch $@
 
 
 $(obj)/builtin-policy.h: $(obj)/policy/profile.conf $(obj)/policy/exception_policy.conf $(obj)/policy/domain_policy.conf $(obj)/policy/manager.conf $(obj)/policy/stat.conf
 $(obj)/builtin-policy.h: $(obj)/policy/profile.conf $(obj)/policy/exception_policy.conf $(obj)/policy/domain_policy.conf $(obj)/policy/manager.conf $(obj)/policy/stat.conf
-	@echo Generating built-in policy for TOMOYO 2.4.x.
+	@echo Generating built-in policy for TOMOYO 2.5.x.
 	@echo "static char tomoyo_builtin_profile[] __initdata =" > $@.tmp
 	@echo "static char tomoyo_builtin_profile[] __initdata =" > $@.tmp
 	@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/profile.conf >> $@.tmp
 	@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/profile.conf >> $@.tmp
 	@echo "\"\";" >> $@.tmp
 	@echo "\"\";" >> $@.tmp

+ 6 - 1
security/tomoyo/audit.c

@@ -313,6 +313,7 @@ static unsigned int tomoyo_log_count;
  */
  */
 static bool tomoyo_get_audit(const struct tomoyo_policy_namespace *ns,
 static bool tomoyo_get_audit(const struct tomoyo_policy_namespace *ns,
 			     const u8 profile, const u8 index,
 			     const u8 profile, const u8 index,
+			     const struct tomoyo_acl_info *matched_acl,
 			     const bool is_granted)
 			     const bool is_granted)
 {
 {
 	u8 mode;
 	u8 mode;
@@ -324,6 +325,9 @@ static bool tomoyo_get_audit(const struct tomoyo_policy_namespace *ns,
 	p = tomoyo_profile(ns, profile);
 	p = tomoyo_profile(ns, profile);
 	if (tomoyo_log_count >= p->pref[TOMOYO_PREF_MAX_AUDIT_LOG])
 	if (tomoyo_log_count >= p->pref[TOMOYO_PREF_MAX_AUDIT_LOG])
 		return false;
 		return false;
+	if (is_granted && matched_acl && matched_acl->cond &&
+	    matched_acl->cond->grant_log != TOMOYO_GRANTLOG_AUTO)
+		return matched_acl->cond->grant_log == TOMOYO_GRANTLOG_YES;
 	mode = p->config[index];
 	mode = p->config[index];
 	if (mode == TOMOYO_CONFIG_USE_DEFAULT)
 	if (mode == TOMOYO_CONFIG_USE_DEFAULT)
 		mode = p->config[category];
 		mode = p->config[category];
@@ -350,7 +354,8 @@ void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt,
 	char *buf;
 	char *buf;
 	struct tomoyo_log *entry;
 	struct tomoyo_log *entry;
 	bool quota_exceeded = false;
 	bool quota_exceeded = false;
-	if (!tomoyo_get_audit(r->domain->ns, r->profile, r->type, r->granted))
+	if (!tomoyo_get_audit(r->domain->ns, r->profile, r->type,
+			      r->matched_acl, r->granted))
 		goto out;
 		goto out;
 	buf = tomoyo_init_log(r, len, fmt, args);
 	buf = tomoyo_init_log(r, len, fmt, args);
 	if (!buf)
 	if (!buf)

+ 185 - 43
security/tomoyo/common.c

@@ -20,6 +20,7 @@ const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE] = {
 /* String table for /sys/kernel/security/tomoyo/profile */
 /* String table for /sys/kernel/security/tomoyo/profile */
 const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
 const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
 				       + TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
 				       + TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
+	/* CONFIG::file group */
 	[TOMOYO_MAC_FILE_EXECUTE]    = "execute",
 	[TOMOYO_MAC_FILE_EXECUTE]    = "execute",
 	[TOMOYO_MAC_FILE_OPEN]       = "open",
 	[TOMOYO_MAC_FILE_OPEN]       = "open",
 	[TOMOYO_MAC_FILE_CREATE]     = "create",
 	[TOMOYO_MAC_FILE_CREATE]     = "create",
@@ -43,7 +44,28 @@ const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
 	[TOMOYO_MAC_FILE_MOUNT]      = "mount",
 	[TOMOYO_MAC_FILE_MOUNT]      = "mount",
 	[TOMOYO_MAC_FILE_UMOUNT]     = "unmount",
 	[TOMOYO_MAC_FILE_UMOUNT]     = "unmount",
 	[TOMOYO_MAC_FILE_PIVOT_ROOT] = "pivot_root",
 	[TOMOYO_MAC_FILE_PIVOT_ROOT] = "pivot_root",
+	/* CONFIG::network group */
+	[TOMOYO_MAC_NETWORK_INET_STREAM_BIND]       = "inet_stream_bind",
+	[TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN]     = "inet_stream_listen",
+	[TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT]    = "inet_stream_connect",
+	[TOMOYO_MAC_NETWORK_INET_DGRAM_BIND]        = "inet_dgram_bind",
+	[TOMOYO_MAC_NETWORK_INET_DGRAM_SEND]        = "inet_dgram_send",
+	[TOMOYO_MAC_NETWORK_INET_RAW_BIND]          = "inet_raw_bind",
+	[TOMOYO_MAC_NETWORK_INET_RAW_SEND]          = "inet_raw_send",
+	[TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND]       = "unix_stream_bind",
+	[TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN]     = "unix_stream_listen",
+	[TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT]    = "unix_stream_connect",
+	[TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND]        = "unix_dgram_bind",
+	[TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND]        = "unix_dgram_send",
+	[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND]    = "unix_seqpacket_bind",
+	[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN]  = "unix_seqpacket_listen",
+	[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] = "unix_seqpacket_connect",
+	/* CONFIG::misc group */
+	[TOMOYO_MAC_ENVIRON] = "env",
+	/* CONFIG group */
 	[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file",
 	[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file",
+	[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_NETWORK] = "network",
+	[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_MISC] = "misc",
 };
 };
 
 
 /* String table for conditions. */
 /* String table for conditions. */
@@ -130,10 +152,20 @@ const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = {
 	[TOMOYO_TYPE_UMOUNT]     = "unmount",
 	[TOMOYO_TYPE_UMOUNT]     = "unmount",
 };
 };
 
 
+/* String table for socket's operation. */
+const char * const tomoyo_socket_keyword[TOMOYO_MAX_NETWORK_OPERATION] = {
+	[TOMOYO_NETWORK_BIND]    = "bind",
+	[TOMOYO_NETWORK_LISTEN]  = "listen",
+	[TOMOYO_NETWORK_CONNECT] = "connect",
+	[TOMOYO_NETWORK_SEND]    = "send",
+};
+
 /* String table for categories. */
 /* String table for categories. */
 static const char * const tomoyo_category_keywords
 static const char * const tomoyo_category_keywords
 [TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
 [TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
-	[TOMOYO_MAC_CATEGORY_FILE]       = "file",
+	[TOMOYO_MAC_CATEGORY_FILE]    = "file",
+	[TOMOYO_MAC_CATEGORY_NETWORK] = "network",
+	[TOMOYO_MAC_CATEGORY_MISC]    = "misc",
 };
 };
 
 
 /* Permit policy management by non-root user? */
 /* Permit policy management by non-root user? */
@@ -230,13 +262,17 @@ static void tomoyo_set_string(struct tomoyo_io_buffer *head, const char *string)
 		WARN_ON(1);
 		WARN_ON(1);
 }
 }
 
 
+static void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt,
+			     ...) __printf(2, 3);
+
 /**
 /**
  * tomoyo_io_printf - printf() to "struct tomoyo_io_buffer" structure.
  * tomoyo_io_printf - printf() to "struct tomoyo_io_buffer" structure.
  *
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
  * @head: Pointer to "struct tomoyo_io_buffer".
  * @fmt:  The printf()'s format string, followed by parameters.
  * @fmt:  The printf()'s format string, followed by parameters.
  */
  */
-void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
+static void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt,
+			     ...)
 {
 {
 	va_list args;
 	va_list args;
 	size_t len;
 	size_t len;
@@ -313,7 +349,7 @@ void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns)
 		INIT_LIST_HEAD(&ns->group_list[idx]);
 		INIT_LIST_HEAD(&ns->group_list[idx]);
 	for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++)
 	for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++)
 		INIT_LIST_HEAD(&ns->policy_list[idx]);
 		INIT_LIST_HEAD(&ns->policy_list[idx]);
-	ns->profile_version = 20100903;
+	ns->profile_version = 20110903;
 	tomoyo_namespace_enabled = !list_empty(&tomoyo_namespace_list);
 	tomoyo_namespace_enabled = !list_empty(&tomoyo_namespace_list);
 	list_add_tail_rcu(&ns->namespace_list, &tomoyo_namespace_list);
 	list_add_tail_rcu(&ns->namespace_list, &tomoyo_namespace_list);
 }
 }
@@ -466,8 +502,10 @@ static struct tomoyo_profile *tomoyo_assign_profile
 			TOMOYO_CONFIG_WANT_REJECT_LOG;
 			TOMOYO_CONFIG_WANT_REJECT_LOG;
 		memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT,
 		memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT,
 		       sizeof(ptr->config));
 		       sizeof(ptr->config));
-		ptr->pref[TOMOYO_PREF_MAX_AUDIT_LOG] = 1024;
-		ptr->pref[TOMOYO_PREF_MAX_LEARNING_ENTRY] = 2048;
+		ptr->pref[TOMOYO_PREF_MAX_AUDIT_LOG] =
+			CONFIG_SECURITY_TOMOYO_MAX_AUDIT_LOG;
+		ptr->pref[TOMOYO_PREF_MAX_LEARNING_ENTRY] =
+			CONFIG_SECURITY_TOMOYO_MAX_ACCEPT_ENTRY;
 		mb(); /* Avoid out-of-order execution. */
 		mb(); /* Avoid out-of-order execution. */
 		ns->profile_ptr[profile] = ptr;
 		ns->profile_ptr[profile] = ptr;
 		entry = NULL;
 		entry = NULL;
@@ -951,14 +989,12 @@ static bool tomoyo_select_domain(struct tomoyo_io_buffer *head,
 	    (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) {
 	    (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) {
 		struct task_struct *p;
 		struct task_struct *p;
 		rcu_read_lock();
 		rcu_read_lock();
-		read_lock(&tasklist_lock);
 		if (global_pid)
 		if (global_pid)
 			p = find_task_by_pid_ns(pid, &init_pid_ns);
 			p = find_task_by_pid_ns(pid, &init_pid_ns);
 		else
 		else
 			p = find_task_by_vpid(pid);
 			p = find_task_by_vpid(pid);
 		if (p)
 		if (p)
 			domain = tomoyo_real_domain(p);
 			domain = tomoyo_real_domain(p);
-		read_unlock(&tasklist_lock);
 		rcu_read_unlock();
 		rcu_read_unlock();
 	} else if (!strncmp(data, "domain=", 7)) {
 	} else if (!strncmp(data, "domain=", 7)) {
 		if (tomoyo_domain_def(data + 7))
 		if (tomoyo_domain_def(data + 7))
@@ -981,6 +1017,48 @@ static bool tomoyo_select_domain(struct tomoyo_io_buffer *head,
 	return true;
 	return true;
 }
 }
 
 
+/**
+ * tomoyo_same_task_acl - Check for duplicated "struct tomoyo_task_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool tomoyo_same_task_acl(const struct tomoyo_acl_info *a,
+			      const struct tomoyo_acl_info *b)
+{
+	const struct tomoyo_task_acl *p1 = container_of(a, typeof(*p1), head);
+	const struct tomoyo_task_acl *p2 = container_of(b, typeof(*p2), head);
+	return p1->domainname == p2->domainname;
+}
+
+/**
+ * tomoyo_write_task - Update task related list.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_write_task(struct tomoyo_acl_param *param)
+{
+	int error = -EINVAL;
+	if (tomoyo_str_starts(&param->data, "manual_domain_transition ")) {
+		struct tomoyo_task_acl e = {
+			.head.type = TOMOYO_TYPE_MANUAL_TASK_ACL,
+			.domainname = tomoyo_get_domainname(param),
+		};
+		if (e.domainname)
+			error = tomoyo_update_domain(&e.head, sizeof(e), param,
+						     tomoyo_same_task_acl,
+						     NULL);
+		tomoyo_put_name(e.domainname);
+	}
+	return error;
+}
+
 /**
 /**
  * tomoyo_delete_domain - Delete a domain.
  * tomoyo_delete_domain - Delete a domain.
  *
  *
@@ -1039,11 +1117,16 @@ static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns,
 	static const struct {
 	static const struct {
 		const char *keyword;
 		const char *keyword;
 		int (*write) (struct tomoyo_acl_param *);
 		int (*write) (struct tomoyo_acl_param *);
-	} tomoyo_callback[1] = {
+	} tomoyo_callback[5] = {
 		{ "file ", tomoyo_write_file },
 		{ "file ", tomoyo_write_file },
+		{ "network inet ", tomoyo_write_inet_network },
+		{ "network unix ", tomoyo_write_unix_network },
+		{ "misc ", tomoyo_write_misc },
+		{ "task ", tomoyo_write_task },
 	};
 	};
 	u8 i;
 	u8 i;
-	for (i = 0; i < 1; i++) {
+
+	for (i = 0; i < ARRAY_SIZE(tomoyo_callback); i++) {
 		if (!tomoyo_str_starts(&param.data,
 		if (!tomoyo_str_starts(&param.data,
 				       tomoyo_callback[i].keyword))
 				       tomoyo_callback[i].keyword))
 			continue;
 			continue;
@@ -1127,6 +1210,10 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
 	case 0:
 	case 0:
 		head->r.cond_index = 0;
 		head->r.cond_index = 0;
 		head->r.cond_step++;
 		head->r.cond_step++;
+		if (cond->transit) {
+			tomoyo_set_space(head);
+			tomoyo_set_string(head, cond->transit->name);
+		}
 		/* fall through */
 		/* fall through */
 	case 1:
 	case 1:
 		{
 		{
@@ -1239,6 +1326,10 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
 		head->r.cond_step++;
 		head->r.cond_step++;
 		/* fall through */
 		/* fall through */
 	case 3:
 	case 3:
+		if (cond->grant_log != TOMOYO_GRANTLOG_AUTO)
+			tomoyo_io_printf(head, " grant_log=%s",
+					 tomoyo_yesno(cond->grant_log ==
+						      TOMOYO_GRANTLOG_YES));
 		tomoyo_set_lf(head);
 		tomoyo_set_lf(head);
 		return true;
 		return true;
 	}
 	}
@@ -1306,6 +1397,12 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
 		if (first)
 		if (first)
 			return true;
 			return true;
 		tomoyo_print_name_union(head, &ptr->name);
 		tomoyo_print_name_union(head, &ptr->name);
+	} else if (acl_type == TOMOYO_TYPE_MANUAL_TASK_ACL) {
+		struct tomoyo_task_acl *ptr =
+			container_of(acl, typeof(*ptr), head);
+		tomoyo_set_group(head, "task ");
+		tomoyo_set_string(head, "manual_domain_transition ");
+		tomoyo_set_string(head, ptr->domainname->name);
 	} else if (head->r.print_transition_related_only) {
 	} else if (head->r.print_transition_related_only) {
 		return true;
 		return true;
 	} else if (acl_type == TOMOYO_TYPE_PATH2_ACL) {
 	} else if (acl_type == TOMOYO_TYPE_PATH2_ACL) {
@@ -1370,6 +1467,60 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
 		tomoyo_print_number_union(head, &ptr->mode);
 		tomoyo_print_number_union(head, &ptr->mode);
 		tomoyo_print_number_union(head, &ptr->major);
 		tomoyo_print_number_union(head, &ptr->major);
 		tomoyo_print_number_union(head, &ptr->minor);
 		tomoyo_print_number_union(head, &ptr->minor);
+	} else if (acl_type == TOMOYO_TYPE_INET_ACL) {
+		struct tomoyo_inet_acl *ptr =
+			container_of(acl, typeof(*ptr), head);
+		const u8 perm = ptr->perm;
+
+		for (bit = 0; bit < TOMOYO_MAX_NETWORK_OPERATION; bit++) {
+			if (!(perm & (1 << bit)))
+				continue;
+			if (first) {
+				tomoyo_set_group(head, "network inet ");
+				tomoyo_set_string(head, tomoyo_proto_keyword
+						  [ptr->protocol]);
+				tomoyo_set_space(head);
+				first = false;
+			} else {
+				tomoyo_set_slash(head);
+			}
+			tomoyo_set_string(head, tomoyo_socket_keyword[bit]);
+		}
+		if (first)
+			return true;
+		tomoyo_set_space(head);
+		if (ptr->address.group) {
+			tomoyo_set_string(head, "@");
+			tomoyo_set_string(head, ptr->address.group->group_name
+					  ->name);
+		} else {
+			char buf[128];
+			tomoyo_print_ip(buf, sizeof(buf), &ptr->address);
+			tomoyo_io_printf(head, "%s", buf);
+		}
+		tomoyo_print_number_union(head, &ptr->port);
+	} else if (acl_type == TOMOYO_TYPE_UNIX_ACL) {
+		struct tomoyo_unix_acl *ptr =
+			container_of(acl, typeof(*ptr), head);
+		const u8 perm = ptr->perm;
+
+		for (bit = 0; bit < TOMOYO_MAX_NETWORK_OPERATION; bit++) {
+			if (!(perm & (1 << bit)))
+				continue;
+			if (first) {
+				tomoyo_set_group(head, "network unix ");
+				tomoyo_set_string(head, tomoyo_proto_keyword
+						  [ptr->protocol]);
+				tomoyo_set_space(head);
+				first = false;
+			} else {
+				tomoyo_set_slash(head);
+			}
+			tomoyo_set_string(head, tomoyo_socket_keyword[bit]);
+		}
+		if (first)
+			return true;
+		tomoyo_print_name_union(head, &ptr->name);
 	} else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) {
 	} else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) {
 		struct tomoyo_mount_acl *ptr =
 		struct tomoyo_mount_acl *ptr =
 			container_of(acl, typeof(*ptr), head);
 			container_of(acl, typeof(*ptr), head);
@@ -1378,6 +1529,12 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
 		tomoyo_print_name_union(head, &ptr->dir_name);
 		tomoyo_print_name_union(head, &ptr->dir_name);
 		tomoyo_print_name_union(head, &ptr->fs_type);
 		tomoyo_print_name_union(head, &ptr->fs_type);
 		tomoyo_print_number_union(head, &ptr->flags);
 		tomoyo_print_number_union(head, &ptr->flags);
+	} else if (acl_type == TOMOYO_TYPE_ENV_ACL) {
+		struct tomoyo_env_acl *ptr =
+			container_of(acl, typeof(*ptr), head);
+
+		tomoyo_set_group(head, "misc env ");
+		tomoyo_set_string(head, ptr->env->name);
 	}
 	}
 	if (acl->cond) {
 	if (acl->cond) {
 		head->r.print_cond_part = true;
 		head->r.print_cond_part = true;
@@ -1510,14 +1667,12 @@ static void tomoyo_read_pid(struct tomoyo_io_buffer *head)
 		global_pid = true;
 		global_pid = true;
 	pid = (unsigned int) simple_strtoul(buf, NULL, 10);
 	pid = (unsigned int) simple_strtoul(buf, NULL, 10);
 	rcu_read_lock();
 	rcu_read_lock();
-	read_lock(&tasklist_lock);
 	if (global_pid)
 	if (global_pid)
 		p = find_task_by_pid_ns(pid, &init_pid_ns);
 		p = find_task_by_pid_ns(pid, &init_pid_ns);
 	else
 	else
 		p = find_task_by_vpid(pid);
 		p = find_task_by_vpid(pid);
 	if (p)
 	if (p)
 		domain = tomoyo_real_domain(p);
 		domain = tomoyo_real_domain(p);
-	read_unlock(&tasklist_lock);
 	rcu_read_unlock();
 	rcu_read_unlock();
 	if (!domain)
 	if (!domain)
 		return;
 		return;
@@ -1537,8 +1692,9 @@ static const char *tomoyo_transition_type[TOMOYO_MAX_TRANSITION_TYPE] = {
 
 
 /* String table for grouping keywords. */
 /* String table for grouping keywords. */
 static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = {
 static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = {
-	[TOMOYO_PATH_GROUP]   = "path_group ",
-	[TOMOYO_NUMBER_GROUP] = "number_group ",
+	[TOMOYO_PATH_GROUP]    = "path_group ",
+	[TOMOYO_NUMBER_GROUP]  = "number_group ",
+	[TOMOYO_ADDRESS_GROUP] = "address_group ",
 };
 };
 
 
 /**
 /**
@@ -1580,7 +1736,7 @@ static int tomoyo_write_exception(struct tomoyo_io_buffer *head)
 }
 }
 
 
 /**
 /**
- * tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_group" list.
+ * tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_group"/"struct tomoyo_address_group" list.
  *
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
  * @head: Pointer to "struct tomoyo_io_buffer".
  * @idx:  Index number.
  * @idx:  Index number.
@@ -1617,6 +1773,15 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx)
 							  (ptr,
 							  (ptr,
 						   struct tomoyo_number_group,
 						   struct tomoyo_number_group,
 							   head)->number);
 							   head)->number);
+			} else if (idx == TOMOYO_ADDRESS_GROUP) {
+				char buffer[128];
+
+				struct tomoyo_address_group *member =
+					container_of(ptr, typeof(*member),
+						     head);
+				tomoyo_print_ip(buffer, sizeof(buffer),
+						&member->address);
+				tomoyo_io_printf(head, " %s", buffer);
 			}
 			}
 			tomoyo_set_lf(head);
 			tomoyo_set_lf(head);
 		}
 		}
@@ -2066,27 +2231,7 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head)
 static void tomoyo_read_version(struct tomoyo_io_buffer *head)
 static void tomoyo_read_version(struct tomoyo_io_buffer *head)
 {
 {
 	if (!head->r.eof) {
 	if (!head->r.eof) {
-		tomoyo_io_printf(head, "2.4.0");
-		head->r.eof = true;
-	}
-}
-
-/**
- * tomoyo_read_self_domain - Get the current process's domainname.
- *
- * @head: Pointer to "struct tomoyo_io_buffer".
- *
- * Returns the current process's domainname.
- */
-static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head)
-{
-	if (!head->r.eof) {
-		/*
-		 * tomoyo_domain()->domainname != NULL
-		 * because every process belongs to a domain and
-		 * the domain's name cannot be NULL.
-		 */
-		tomoyo_io_printf(head, "%s", tomoyo_domain()->domainname->name);
+		tomoyo_io_printf(head, "2.5.0");
 		head->r.eof = true;
 		head->r.eof = true;
 	}
 	}
 }
 }
@@ -2221,10 +2366,6 @@ int tomoyo_open_control(const u8 type, struct file *file)
 		head->poll = tomoyo_poll_log;
 		head->poll = tomoyo_poll_log;
 		head->read = tomoyo_read_log;
 		head->read = tomoyo_read_log;
 		break;
 		break;
-	case TOMOYO_SELFDOMAIN:
-		/* /sys/kernel/security/tomoyo/self_domain */
-		head->read = tomoyo_read_self_domain;
-		break;
 	case TOMOYO_PROCESS_STATUS:
 	case TOMOYO_PROCESS_STATUS:
 		/* /sys/kernel/security/tomoyo/.process_status */
 		/* /sys/kernel/security/tomoyo/.process_status */
 		head->write = tomoyo_write_pid;
 		head->write = tomoyo_write_pid;
@@ -2453,6 +2594,7 @@ ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head,
 		return -EFAULT;
 		return -EFAULT;
 	if (mutex_lock_interruptible(&head->io_sem))
 	if (mutex_lock_interruptible(&head->io_sem))
 		return -EINTR;
 		return -EINTR;
+	head->read_user_buf_avail = 0;
 	idx = tomoyo_read_lock();
 	idx = tomoyo_read_lock();
 	/* Read a line and dispatch it to the policy handler. */
 	/* Read a line and dispatch it to the policy handler. */
 	while (avail_len > 0) {
 	while (avail_len > 0) {
@@ -2562,11 +2704,11 @@ void tomoyo_check_profile(void)
 	struct tomoyo_domain_info *domain;
 	struct tomoyo_domain_info *domain;
 	const int idx = tomoyo_read_lock();
 	const int idx = tomoyo_read_lock();
 	tomoyo_policy_loaded = true;
 	tomoyo_policy_loaded = true;
-	printk(KERN_INFO "TOMOYO: 2.4.0\n");
+	printk(KERN_INFO "TOMOYO: 2.5.0\n");
 	list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
 	list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
 		const u8 profile = domain->profile;
 		const u8 profile = domain->profile;
 		const struct tomoyo_policy_namespace *ns = domain->ns;
 		const struct tomoyo_policy_namespace *ns = domain->ns;
-		if (ns->profile_version != 20100903)
+		if (ns->profile_version != 20110903)
 			printk(KERN_ERR
 			printk(KERN_ERR
 			       "Profile version %u is not supported.\n",
 			       "Profile version %u is not supported.\n",
 			       ns->profile_version);
 			       ns->profile_version);
@@ -2577,9 +2719,9 @@ void tomoyo_check_profile(void)
 		else
 		else
 			continue;
 			continue;
 		printk(KERN_ERR
 		printk(KERN_ERR
-		       "Userland tools for TOMOYO 2.4 must be installed and "
+		       "Userland tools for TOMOYO 2.5 must be installed and "
 		       "policy must be initialized.\n");
 		       "policy must be initialized.\n");
-		printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.4/ "
+		printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.5/ "
 		       "for more information.\n");
 		       "for more information.\n");
 		panic("STOP!");
 		panic("STOP!");
 	}
 	}

+ 178 - 11
security/tomoyo/common.h

@@ -3,7 +3,7 @@
  *
  *
  * Header file for TOMOYO.
  * Header file for TOMOYO.
  *
  *
- * Copyright (C) 2005-2010  NTT DATA CORPORATION
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
  */
  */
 
 
 #ifndef _SECURITY_TOMOYO_COMMON_H
 #ifndef _SECURITY_TOMOYO_COMMON_H
@@ -23,6 +23,16 @@
 #include <linux/poll.h>
 #include <linux/poll.h>
 #include <linux/binfmts.h>
 #include <linux/binfmts.h>
 #include <linux/highmem.h>
 #include <linux/highmem.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/un.h>
+#include <net/sock.h>
+#include <net/af_unix.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/udp.h>
 
 
 /********** Constants definitions. **********/
 /********** Constants definitions. **********/
 
 
@@ -34,8 +44,17 @@
 #define TOMOYO_HASH_BITS  8
 #define TOMOYO_HASH_BITS  8
 #define TOMOYO_MAX_HASH (1u<<TOMOYO_HASH_BITS)
 #define TOMOYO_MAX_HASH (1u<<TOMOYO_HASH_BITS)
 
 
+/*
+ * TOMOYO checks only SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_SEQPACKET.
+ * Therefore, we don't need SOCK_MAX.
+ */
+#define TOMOYO_SOCK_MAX 6
+
 #define TOMOYO_EXEC_TMPSIZE     4096
 #define TOMOYO_EXEC_TMPSIZE     4096
 
 
+/* Garbage collector is trying to kfree() this element. */
+#define TOMOYO_GC_IN_PROGRESS -1
+
 /* Profile number is an integer between 0 and 255. */
 /* Profile number is an integer between 0 and 255. */
 #define TOMOYO_MAX_PROFILES 256
 #define TOMOYO_MAX_PROFILES 256
 
 
@@ -136,6 +155,7 @@ enum tomoyo_mode_index {
 /* Index numbers for entry type. */
 /* Index numbers for entry type. */
 enum tomoyo_policy_id {
 enum tomoyo_policy_id {
 	TOMOYO_ID_GROUP,
 	TOMOYO_ID_GROUP,
+	TOMOYO_ID_ADDRESS_GROUP,
 	TOMOYO_ID_PATH_GROUP,
 	TOMOYO_ID_PATH_GROUP,
 	TOMOYO_ID_NUMBER_GROUP,
 	TOMOYO_ID_NUMBER_GROUP,
 	TOMOYO_ID_TRANSITION_CONTROL,
 	TOMOYO_ID_TRANSITION_CONTROL,
@@ -162,10 +182,21 @@ enum tomoyo_domain_info_flags_index {
 	TOMOYO_MAX_DOMAIN_INFO_FLAGS
 	TOMOYO_MAX_DOMAIN_INFO_FLAGS
 };
 };
 
 
+/* Index numbers for audit type. */
+enum tomoyo_grant_log {
+	/* Follow profile's configuration. */
+	TOMOYO_GRANTLOG_AUTO,
+	/* Do not generate grant log. */
+	TOMOYO_GRANTLOG_NO,
+	/* Generate grant_log. */
+	TOMOYO_GRANTLOG_YES,
+};
+
 /* Index numbers for group entries. */
 /* Index numbers for group entries. */
 enum tomoyo_group_id {
 enum tomoyo_group_id {
 	TOMOYO_PATH_GROUP,
 	TOMOYO_PATH_GROUP,
 	TOMOYO_NUMBER_GROUP,
 	TOMOYO_NUMBER_GROUP,
+	TOMOYO_ADDRESS_GROUP,
 	TOMOYO_MAX_GROUP
 	TOMOYO_MAX_GROUP
 };
 };
 
 
@@ -196,6 +227,10 @@ enum tomoyo_acl_entry_type_index {
 	TOMOYO_TYPE_PATH_NUMBER_ACL,
 	TOMOYO_TYPE_PATH_NUMBER_ACL,
 	TOMOYO_TYPE_MKDEV_ACL,
 	TOMOYO_TYPE_MKDEV_ACL,
 	TOMOYO_TYPE_MOUNT_ACL,
 	TOMOYO_TYPE_MOUNT_ACL,
+	TOMOYO_TYPE_INET_ACL,
+	TOMOYO_TYPE_UNIX_ACL,
+	TOMOYO_TYPE_ENV_ACL,
+	TOMOYO_TYPE_MANUAL_TASK_ACL,
 };
 };
 
 
 /* Index numbers for access controls with one pathname. */
 /* Index numbers for access controls with one pathname. */
@@ -228,6 +263,15 @@ enum tomoyo_mkdev_acl_index {
 	TOMOYO_MAX_MKDEV_OPERATION
 	TOMOYO_MAX_MKDEV_OPERATION
 };
 };
 
 
+/* Index numbers for socket operations. */
+enum tomoyo_network_acl_index {
+	TOMOYO_NETWORK_BIND,    /* bind() operation. */
+	TOMOYO_NETWORK_LISTEN,  /* listen() operation. */
+	TOMOYO_NETWORK_CONNECT, /* connect() operation. */
+	TOMOYO_NETWORK_SEND,    /* send() operation. */
+	TOMOYO_MAX_NETWORK_OPERATION
+};
+
 /* Index numbers for access controls with two pathnames. */
 /* Index numbers for access controls with two pathnames. */
 enum tomoyo_path2_acl_index {
 enum tomoyo_path2_acl_index {
 	TOMOYO_TYPE_LINK,
 	TOMOYO_TYPE_LINK,
@@ -255,7 +299,6 @@ enum tomoyo_securityfs_interface_index {
 	TOMOYO_EXCEPTIONPOLICY,
 	TOMOYO_EXCEPTIONPOLICY,
 	TOMOYO_PROCESS_STATUS,
 	TOMOYO_PROCESS_STATUS,
 	TOMOYO_STAT,
 	TOMOYO_STAT,
-	TOMOYO_SELFDOMAIN,
 	TOMOYO_AUDIT,
 	TOMOYO_AUDIT,
 	TOMOYO_VERSION,
 	TOMOYO_VERSION,
 	TOMOYO_PROFILE,
 	TOMOYO_PROFILE,
@@ -300,12 +343,30 @@ enum tomoyo_mac_index {
 	TOMOYO_MAC_FILE_MOUNT,
 	TOMOYO_MAC_FILE_MOUNT,
 	TOMOYO_MAC_FILE_UMOUNT,
 	TOMOYO_MAC_FILE_UMOUNT,
 	TOMOYO_MAC_FILE_PIVOT_ROOT,
 	TOMOYO_MAC_FILE_PIVOT_ROOT,
+	TOMOYO_MAC_NETWORK_INET_STREAM_BIND,
+	TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN,
+	TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT,
+	TOMOYO_MAC_NETWORK_INET_DGRAM_BIND,
+	TOMOYO_MAC_NETWORK_INET_DGRAM_SEND,
+	TOMOYO_MAC_NETWORK_INET_RAW_BIND,
+	TOMOYO_MAC_NETWORK_INET_RAW_SEND,
+	TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND,
+	TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN,
+	TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT,
+	TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND,
+	TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND,
+	TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND,
+	TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN,
+	TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT,
+	TOMOYO_MAC_ENVIRON,
 	TOMOYO_MAX_MAC_INDEX
 	TOMOYO_MAX_MAC_INDEX
 };
 };
 
 
 /* Index numbers for category of functionality. */
 /* Index numbers for category of functionality. */
 enum tomoyo_mac_category_index {
 enum tomoyo_mac_category_index {
 	TOMOYO_MAC_CATEGORY_FILE,
 	TOMOYO_MAC_CATEGORY_FILE,
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	TOMOYO_MAC_CATEGORY_MISC,
 	TOMOYO_MAX_MAC_CATEGORY_INDEX
 	TOMOYO_MAX_MAC_CATEGORY_INDEX
 };
 };
 
 
@@ -340,7 +401,7 @@ enum tomoyo_pref_index {
 /* Common header for holding ACL entries. */
 /* Common header for holding ACL entries. */
 struct tomoyo_acl_head {
 struct tomoyo_acl_head {
 	struct list_head list;
 	struct list_head list;
-	bool is_deleted;
+	s8 is_deleted; /* true or false or TOMOYO_GC_IN_PROGRESS */
 } __packed;
 } __packed;
 
 
 /* Common header for shared entries. */
 /* Common header for shared entries. */
@@ -396,6 +457,25 @@ struct tomoyo_request_info {
 			 */
 			 */
 			u8 operation;
 			u8 operation;
 		} path_number;
 		} path_number;
+		struct {
+			const struct tomoyo_path_info *name;
+		} environ;
+		struct {
+			const __be32 *address;
+			u16 port;
+			/* One of values smaller than TOMOYO_SOCK_MAX. */
+			u8 protocol;
+			/* One of values in "enum tomoyo_network_acl_index". */
+			u8 operation;
+			bool is_ipv6;
+		} inet_network;
+		struct {
+			const struct tomoyo_path_info *address;
+			/* One of values smaller than TOMOYO_SOCK_MAX. */
+			u8 protocol;
+			/* One of values in "enum tomoyo_network_acl_index". */
+			u8 operation;
+		} unix_network;
 		struct {
 		struct {
 			const struct tomoyo_path_info *type;
 			const struct tomoyo_path_info *type;
 			const struct tomoyo_path_info *dir;
 			const struct tomoyo_path_info *dir;
@@ -403,7 +483,11 @@ struct tomoyo_request_info {
 			unsigned long flags;
 			unsigned long flags;
 			int need_dev;
 			int need_dev;
 		} mount;
 		} mount;
+		struct {
+			const struct tomoyo_path_info *domainname;
+		} task;
 	} param;
 	} param;
+	struct tomoyo_acl_info *matched_acl;
 	u8 param_type;
 	u8 param_type;
 	bool granted;
 	bool granted;
 	u8 retry;
 	u8 retry;
@@ -442,7 +526,14 @@ struct tomoyo_number_union {
 	u8 value_type[2];
 	u8 value_type[2];
 };
 };
 
 
-/* Structure for "path_group"/"number_group" directive. */
+/* Structure for holding an IP address. */
+struct tomoyo_ipaddr_union {
+	struct in6_addr ip[2]; /* Big endian. */
+	struct tomoyo_group *group; /* Pointer to address group. */
+	bool is_ipv6; /* Valid only if @group == NULL. */
+};
+
+/* Structure for "path_group"/"number_group"/"address_group" directive. */
 struct tomoyo_group {
 struct tomoyo_group {
 	struct tomoyo_shared_acl_head head;
 	struct tomoyo_shared_acl_head head;
 	const struct tomoyo_path_info *group_name;
 	const struct tomoyo_path_info *group_name;
@@ -461,6 +552,13 @@ struct tomoyo_number_group {
 	struct tomoyo_number_union number;
 	struct tomoyo_number_union number;
 };
 };
 
 
+/* Structure for "address_group" directive. */
+struct tomoyo_address_group {
+	struct tomoyo_acl_head head;
+	/* Structure for holding an IP address. */
+	struct tomoyo_ipaddr_union address;
+};
+
 /* Subset of "struct stat". Used by conditional ACL and audit logs. */
 /* Subset of "struct stat". Used by conditional ACL and audit logs. */
 struct tomoyo_mini_stat {
 struct tomoyo_mini_stat {
 	uid_t uid;
 	uid_t uid;
@@ -520,6 +618,7 @@ struct tomoyo_execve {
 	struct tomoyo_request_info r;
 	struct tomoyo_request_info r;
 	struct tomoyo_obj_info obj;
 	struct tomoyo_obj_info obj;
 	struct linux_binprm *bprm;
 	struct linux_binprm *bprm;
+	const struct tomoyo_path_info *transition;
 	/* For dumping argv[] and envp[]. */
 	/* For dumping argv[] and envp[]. */
 	struct tomoyo_page_dump dump;
 	struct tomoyo_page_dump dump;
 	/* For temporary use. */
 	/* For temporary use. */
@@ -554,6 +653,8 @@ struct tomoyo_condition {
 	u16 names_count; /* Number of "struct tomoyo_name_union names". */
 	u16 names_count; /* Number of "struct tomoyo_name_union names". */
 	u16 argc; /* Number of "struct tomoyo_argv". */
 	u16 argc; /* Number of "struct tomoyo_argv". */
 	u16 envc; /* Number of "struct tomoyo_envp". */
 	u16 envc; /* Number of "struct tomoyo_envp". */
+	u8 grant_log; /* One of values in "enum tomoyo_grant_log". */
+	const struct tomoyo_path_info *transit; /* Maybe NULL. */
 	/*
 	/*
 	 * struct tomoyo_condition_element condition[condc];
 	 * struct tomoyo_condition_element condition[condc];
 	 * struct tomoyo_number_union values[numbers_count];
 	 * struct tomoyo_number_union values[numbers_count];
@@ -567,7 +668,7 @@ struct tomoyo_condition {
 struct tomoyo_acl_info {
 struct tomoyo_acl_info {
 	struct list_head list;
 	struct list_head list;
 	struct tomoyo_condition *cond; /* Maybe NULL. */
 	struct tomoyo_condition *cond; /* Maybe NULL. */
-	bool is_deleted;
+	s8 is_deleted; /* true or false or TOMOYO_GC_IN_PROGRESS */
 	u8 type; /* One of values in "enum tomoyo_acl_entry_type_index". */
 	u8 type; /* One of values in "enum tomoyo_acl_entry_type_index". */
 } __packed;
 } __packed;
 
 
@@ -586,6 +687,15 @@ struct tomoyo_domain_info {
 	atomic_t users; /* Number of referring credentials. */
 	atomic_t users; /* Number of referring credentials. */
 };
 };
 
 
+/*
+ * Structure for "task manual_domain_transition" directive.
+ */
+struct tomoyo_task_acl {
+	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MANUAL_TASK_ACL */
+	/* Pointer to domainname. */
+	const struct tomoyo_path_info *domainname;
+};
+
 /*
 /*
  * Structure for "file execute", "file read", "file write", "file append",
  * Structure for "file execute", "file read", "file write", "file append",
  * "file unlink", "file getattr", "file rmdir", "file truncate",
  * "file unlink", "file getattr", "file rmdir", "file truncate",
@@ -638,6 +748,29 @@ struct tomoyo_mount_acl {
 	struct tomoyo_number_union flags;
 	struct tomoyo_number_union flags;
 };
 };
 
 
+/* Structure for "misc env" directive in domain policy. */
+struct tomoyo_env_acl {
+	struct tomoyo_acl_info head;        /* type = TOMOYO_TYPE_ENV_ACL  */
+	const struct tomoyo_path_info *env; /* environment variable */
+};
+
+/* Structure for "network inet" directive. */
+struct tomoyo_inet_acl {
+	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_INET_ACL */
+	u8 protocol;
+	u8 perm; /* Bitmask of values in "enum tomoyo_network_acl_index" */
+	struct tomoyo_ipaddr_union address;
+	struct tomoyo_number_union port;
+};
+
+/* Structure for "network unix" directive. */
+struct tomoyo_unix_acl {
+	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_UNIX_ACL */
+	u8 protocol;
+	u8 perm; /* Bitmask of values in "enum tomoyo_network_acl_index" */
+	struct tomoyo_name_union name;
+};
+
 /* Structure for holding a line from /sys/kernel/security/tomoyo/ interface. */
 /* Structure for holding a line from /sys/kernel/security/tomoyo/ interface. */
 struct tomoyo_acl_param {
 struct tomoyo_acl_param {
 	char *data;
 	char *data;
@@ -773,7 +906,7 @@ struct tomoyo_policy_namespace {
 	struct list_head acl_group[TOMOYO_MAX_ACL_GROUPS];
 	struct list_head acl_group[TOMOYO_MAX_ACL_GROUPS];
 	/* List for connecting to tomoyo_namespace_list list. */
 	/* List for connecting to tomoyo_namespace_list list. */
 	struct list_head namespace_list;
 	struct list_head namespace_list;
-	/* Profile version. Currently only 20100903 is defined. */
+	/* Profile version. Currently only 20110903 is defined. */
 	unsigned int profile_version;
 	unsigned int profile_version;
 	/* Name of this namespace (e.g. "<kernel>", "</usr/sbin/httpd>" ). */
 	/* Name of this namespace (e.g. "<kernel>", "</usr/sbin/httpd>" ). */
 	const char *name;
 	const char *name;
@@ -781,6 +914,8 @@ struct tomoyo_policy_namespace {
 
 
 /********** Function prototypes. **********/
 /********** Function prototypes. **********/
 
 
+bool tomoyo_address_matches_group(const bool is_ipv6, const __be32 *address,
+				  const struct tomoyo_group *group);
 bool tomoyo_compare_number_union(const unsigned long value,
 bool tomoyo_compare_number_union(const unsigned long value,
 				 const struct tomoyo_number_union *ptr);
 				 const struct tomoyo_number_union *ptr);
 bool tomoyo_condition(struct tomoyo_request_info *r,
 bool tomoyo_condition(struct tomoyo_request_info *r,
@@ -796,6 +931,8 @@ bool tomoyo_memory_ok(void *ptr);
 bool tomoyo_number_matches_group(const unsigned long min,
 bool tomoyo_number_matches_group(const unsigned long min,
 				 const unsigned long max,
 				 const unsigned long max,
 				 const struct tomoyo_group *group);
 				 const struct tomoyo_group *group);
+bool tomoyo_parse_ipaddr_union(struct tomoyo_acl_param *param,
+			       struct tomoyo_ipaddr_union *ptr);
 bool tomoyo_parse_name_union(struct tomoyo_acl_param *param,
 bool tomoyo_parse_name_union(struct tomoyo_acl_param *param,
 			     struct tomoyo_name_union *ptr);
 			     struct tomoyo_name_union *ptr);
 bool tomoyo_parse_number_union(struct tomoyo_acl_param *param,
 bool tomoyo_parse_number_union(struct tomoyo_acl_param *param,
@@ -805,6 +942,7 @@ bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
 bool tomoyo_permstr(const char *string, const char *keyword);
 bool tomoyo_permstr(const char *string, const char *keyword);
 bool tomoyo_str_starts(char **src, const char *find);
 bool tomoyo_str_starts(char **src, const char *find);
 char *tomoyo_encode(const char *str);
 char *tomoyo_encode(const char *str);
+char *tomoyo_encode2(const char *str, int str_len);
 char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt,
 char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt,
 		      va_list args);
 		      va_list args);
 char *tomoyo_read_token(struct tomoyo_acl_param *param);
 char *tomoyo_read_token(struct tomoyo_acl_param *param);
@@ -814,12 +952,17 @@ const char *tomoyo_get_exe(void);
 const char *tomoyo_yesno(const unsigned int value);
 const char *tomoyo_yesno(const unsigned int value);
 const struct tomoyo_path_info *tomoyo_compare_name_union
 const struct tomoyo_path_info *tomoyo_compare_name_union
 (const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr);
 (const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr);
+const struct tomoyo_path_info *tomoyo_get_domainname
+(struct tomoyo_acl_param *param);
 const struct tomoyo_path_info *tomoyo_get_name(const char *name);
 const struct tomoyo_path_info *tomoyo_get_name(const char *name);
 const struct tomoyo_path_info *tomoyo_path_matches_group
 const struct tomoyo_path_info *tomoyo_path_matches_group
 (const struct tomoyo_path_info *pathname, const struct tomoyo_group *group);
 (const struct tomoyo_path_info *pathname, const struct tomoyo_group *group);
 int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
 int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
 				 struct path *path, const int flag);
 				 struct path *path, const int flag);
 int tomoyo_close_control(struct tomoyo_io_buffer *head);
 int tomoyo_close_control(struct tomoyo_io_buffer *head);
+int tomoyo_env_perm(struct tomoyo_request_info *r, const char *env);
+int tomoyo_execute_permission(struct tomoyo_request_info *r,
+			      const struct tomoyo_path_info *filename);
 int tomoyo_find_next_domain(struct linux_binprm *bprm);
 int tomoyo_find_next_domain(struct linux_binprm *bprm);
 int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile,
 int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile,
 		    const u8 index);
 		    const u8 index);
@@ -838,10 +981,15 @@ int tomoyo_path_number_perm(const u8 operation, struct path *path,
 			    unsigned long number);
 			    unsigned long number);
 int tomoyo_path_perm(const u8 operation, struct path *path,
 int tomoyo_path_perm(const u8 operation, struct path *path,
 		     const char *target);
 		     const char *target);
-int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
-			   const struct tomoyo_path_info *filename);
 int tomoyo_poll_control(struct file *file, poll_table *wait);
 int tomoyo_poll_control(struct file *file, poll_table *wait);
 int tomoyo_poll_log(struct file *file, poll_table *wait);
 int tomoyo_poll_log(struct file *file, poll_table *wait);
+int tomoyo_socket_bind_permission(struct socket *sock, struct sockaddr *addr,
+				  int addr_len);
+int tomoyo_socket_connect_permission(struct socket *sock,
+				     struct sockaddr *addr, int addr_len);
+int tomoyo_socket_listen_permission(struct socket *sock);
+int tomoyo_socket_sendmsg_permission(struct socket *sock, struct msghdr *msg,
+				     int size);
 int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
 int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
 	__printf(2, 3);
 	__printf(2, 3);
 int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
 int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
@@ -860,8 +1008,11 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
 int tomoyo_write_aggregator(struct tomoyo_acl_param *param);
 int tomoyo_write_aggregator(struct tomoyo_acl_param *param);
 int tomoyo_write_file(struct tomoyo_acl_param *param);
 int tomoyo_write_file(struct tomoyo_acl_param *param);
 int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type);
 int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type);
+int tomoyo_write_misc(struct tomoyo_acl_param *param);
+int tomoyo_write_inet_network(struct tomoyo_acl_param *param);
 int tomoyo_write_transition_control(struct tomoyo_acl_param *param,
 int tomoyo_write_transition_control(struct tomoyo_acl_param *param,
 				    const u8 type);
 				    const u8 type);
+int tomoyo_write_unix_network(struct tomoyo_acl_param *param);
 ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer,
 ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer,
 			    const int buffer_len);
 			    const int buffer_len);
 ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head,
 ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head,
@@ -891,12 +1042,11 @@ void tomoyo_del_condition(struct list_head *element);
 void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
 void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
 void tomoyo_get_attributes(struct tomoyo_obj_info *obj);
 void tomoyo_get_attributes(struct tomoyo_obj_info *obj);
 void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns);
 void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns);
-void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
-	 __printf(2, 3);
 void tomoyo_load_policy(const char *filename);
 void tomoyo_load_policy(const char *filename);
-void tomoyo_memory_free(void *ptr);
 void tomoyo_normalize_line(unsigned char *buffer);
 void tomoyo_normalize_line(unsigned char *buffer);
 void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register);
 void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register);
+void tomoyo_print_ip(char *buf, const unsigned int size,
+		     const struct tomoyo_ipaddr_union *ptr);
 void tomoyo_print_ulong(char *buffer, const int buffer_len,
 void tomoyo_print_ulong(char *buffer, const int buffer_len,
 			const unsigned long value, const u8 type);
 			const unsigned long value, const u8 type);
 void tomoyo_put_name_union(struct tomoyo_name_union *ptr);
 void tomoyo_put_name_union(struct tomoyo_name_union *ptr);
@@ -919,6 +1069,8 @@ extern const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
 					      + TOMOYO_MAX_MAC_CATEGORY_INDEX];
 					      + TOMOYO_MAX_MAC_CATEGORY_INDEX];
 extern const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE];
 extern const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE];
 extern const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION];
 extern const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION];
+extern const char * const tomoyo_proto_keyword[TOMOYO_SOCK_MAX];
+extern const char * const tomoyo_socket_keyword[TOMOYO_MAX_NETWORK_OPERATION];
 extern const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX];
 extern const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX];
 extern const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION];
 extern const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION];
 extern const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION];
 extern const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION];
@@ -1097,6 +1249,21 @@ static inline bool tomoyo_same_number_union
 		a->value_type[1] == b->value_type[1];
 		a->value_type[1] == b->value_type[1];
 }
 }
 
 
+/**
+ * tomoyo_same_ipaddr_union - Check for duplicated "struct tomoyo_ipaddr_union" entry.
+ *
+ * @a: Pointer to "struct tomoyo_ipaddr_union".
+ * @b: Pointer to "struct tomoyo_ipaddr_union".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static inline bool tomoyo_same_ipaddr_union
+(const struct tomoyo_ipaddr_union *a, const struct tomoyo_ipaddr_union *b)
+{
+	return !memcmp(a->ip, b->ip, sizeof(a->ip)) && a->group == b->group &&
+		a->is_ipv6 == b->is_ipv6;
+}
+
 /**
 /**
  * tomoyo_current_namespace - Get "struct tomoyo_policy_namespace" for current thread.
  * tomoyo_current_namespace - Get "struct tomoyo_policy_namespace" for current thread.
  *
  *

+ 65 - 6
security/tomoyo/condition.c

@@ -348,6 +348,7 @@ static inline bool tomoyo_same_condition(const struct tomoyo_condition *a,
 		a->numbers_count == b->numbers_count &&
 		a->numbers_count == b->numbers_count &&
 		a->names_count == b->names_count &&
 		a->names_count == b->names_count &&
 		a->argc == b->argc && a->envc == b->envc &&
 		a->argc == b->argc && a->envc == b->envc &&
+		a->grant_log == b->grant_log && a->transit == b->transit &&
 		!memcmp(a + 1, b + 1, a->size - sizeof(*a));
 		!memcmp(a + 1, b + 1, a->size - sizeof(*a));
 }
 }
 
 
@@ -399,8 +400,9 @@ static struct tomoyo_condition *tomoyo_commit_condition
 		found = true;
 		found = true;
 		goto out;
 		goto out;
 	}
 	}
-	list_for_each_entry_rcu(ptr, &tomoyo_condition_list, head.list) {
-		if (!tomoyo_same_condition(ptr, entry))
+	list_for_each_entry(ptr, &tomoyo_condition_list, head.list) {
+		if (!tomoyo_same_condition(ptr, entry) ||
+		    atomic_read(&ptr->head.users) == TOMOYO_GC_IN_PROGRESS)
 			continue;
 			continue;
 		/* Same entry found. Share this entry. */
 		/* Same entry found. Share this entry. */
 		atomic_inc(&ptr->head.users);
 		atomic_inc(&ptr->head.users);
@@ -410,8 +412,7 @@ static struct tomoyo_condition *tomoyo_commit_condition
 	if (!found) {
 	if (!found) {
 		if (tomoyo_memory_ok(entry)) {
 		if (tomoyo_memory_ok(entry)) {
 			atomic_set(&entry->head.users, 1);
 			atomic_set(&entry->head.users, 1);
-			list_add_rcu(&entry->head.list,
-				     &tomoyo_condition_list);
+			list_add(&entry->head.list, &tomoyo_condition_list);
 		} else {
 		} else {
 			found = true;
 			found = true;
 			ptr = NULL;
 			ptr = NULL;
@@ -427,6 +428,46 @@ out:
 	return entry;
 	return entry;
 }
 }
 
 
+/**
+ * tomoyo_get_transit_preference - Parse domain transition preference for execve().
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ * @e:     Pointer to "struct tomoyo_condition".
+ *
+ * Returns the condition string part.
+ */
+static char *tomoyo_get_transit_preference(struct tomoyo_acl_param *param,
+					   struct tomoyo_condition *e)
+{
+	char * const pos = param->data;
+	bool flag;
+	if (*pos == '<') {
+		e->transit = tomoyo_get_domainname(param);
+		goto done;
+	}
+	{
+		char *cp = strchr(pos, ' ');
+		if (cp)
+			*cp = '\0';
+		flag = tomoyo_correct_path(pos) || !strcmp(pos, "keep") ||
+			!strcmp(pos, "initialize") || !strcmp(pos, "reset") ||
+			!strcmp(pos, "child") || !strcmp(pos, "parent");
+		if (cp)
+			*cp = ' ';
+	}
+	if (!flag)
+		return pos;
+	e->transit = tomoyo_get_name(tomoyo_read_token(param));
+done:
+	if (e->transit)
+		return param->data;
+	/*
+	 * Return a bad read-only condition string that will let
+	 * tomoyo_get_condition() return NULL.
+	 */
+	return "/";
+}
+
 /**
 /**
  * tomoyo_get_condition - Parse condition part.
  * tomoyo_get_condition - Parse condition part.
  *
  *
@@ -443,7 +484,8 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param)
 	struct tomoyo_argv *argv = NULL;
 	struct tomoyo_argv *argv = NULL;
 	struct tomoyo_envp *envp = NULL;
 	struct tomoyo_envp *envp = NULL;
 	struct tomoyo_condition e = { };
 	struct tomoyo_condition e = { };
-	char * const start_of_string = param->data;
+	char * const start_of_string =
+		tomoyo_get_transit_preference(param, &e);
 	char * const end_of_string = start_of_string + strlen(start_of_string);
 	char * const end_of_string = start_of_string + strlen(start_of_string);
 	char *pos;
 	char *pos;
 rerun:
 rerun:
@@ -486,6 +528,20 @@ rerun:
 			goto out;
 			goto out;
 		dprintk(KERN_WARNING "%u: <%s>%s=<%s>\n", __LINE__, left_word,
 		dprintk(KERN_WARNING "%u: <%s>%s=<%s>\n", __LINE__, left_word,
 			is_not ? "!" : "", right_word);
 			is_not ? "!" : "", right_word);
+		if (!strcmp(left_word, "grant_log")) {
+			if (entry) {
+				if (is_not ||
+				    entry->grant_log != TOMOYO_GRANTLOG_AUTO)
+					goto out;
+				else if (!strcmp(right_word, "yes"))
+					entry->grant_log = TOMOYO_GRANTLOG_YES;
+				else if (!strcmp(right_word, "no"))
+					entry->grant_log = TOMOYO_GRANTLOG_NO;
+				else
+					goto out;
+			}
+			continue;
+		}
 		if (!strncmp(left_word, "exec.argv[", 10)) {
 		if (!strncmp(left_word, "exec.argv[", 10)) {
 			if (!argv) {
 			if (!argv) {
 				e.argc++;
 				e.argc++;
@@ -593,8 +649,9 @@ store_value:
 		+ e.envc * sizeof(struct tomoyo_envp);
 		+ e.envc * sizeof(struct tomoyo_envp);
 	entry = kzalloc(e.size, GFP_NOFS);
 	entry = kzalloc(e.size, GFP_NOFS);
 	if (!entry)
 	if (!entry)
-		return NULL;
+		goto out2;
 	*entry = e;
 	*entry = e;
+	e.transit = NULL;
 	condp = (struct tomoyo_condition_element *) (entry + 1);
 	condp = (struct tomoyo_condition_element *) (entry + 1);
 	numbers_p = (struct tomoyo_number_union *) (condp + e.condc);
 	numbers_p = (struct tomoyo_number_union *) (condp + e.condc);
 	names_p = (struct tomoyo_name_union *) (numbers_p + e.numbers_count);
 	names_p = (struct tomoyo_name_union *) (numbers_p + e.numbers_count);
@@ -621,6 +678,8 @@ out:
 		tomoyo_del_condition(&entry->head.list);
 		tomoyo_del_condition(&entry->head.list);
 		kfree(entry);
 		kfree(entry);
 	}
 	}
+out2:
+	tomoyo_put_name(e.transit);
 	return NULL;
 	return NULL;
 }
 }
 
 

+ 172 - 37
security/tomoyo/domain.c

@@ -39,6 +39,8 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
 		return -ENOMEM;
 		return -ENOMEM;
 	list_for_each_entry_rcu(entry, list, list) {
 	list_for_each_entry_rcu(entry, list, list) {
+		if (entry->is_deleted == TOMOYO_GC_IN_PROGRESS)
+			continue;
 		if (!check_duplicate(entry, new_entry))
 		if (!check_duplicate(entry, new_entry))
 			continue;
 			continue;
 		entry->is_deleted = param->is_delete;
 		entry->is_deleted = param->is_delete;
@@ -102,10 +104,21 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
 		new_entry->cond = tomoyo_get_condition(param);
 		new_entry->cond = tomoyo_get_condition(param);
 		if (!new_entry->cond)
 		if (!new_entry->cond)
 			return -EINVAL;
 			return -EINVAL;
+		/*
+		 * Domain transition preference is allowed for only
+		 * "file execute" entries.
+		 */
+		if (new_entry->cond->transit &&
+		    !(new_entry->type == TOMOYO_TYPE_PATH_ACL &&
+		      container_of(new_entry, struct tomoyo_path_acl, head)
+		      ->perm == 1 << TOMOYO_TYPE_EXECUTE))
+			goto out;
 	}
 	}
 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
 		goto out;
 		goto out;
 	list_for_each_entry_rcu(entry, list, list) {
 	list_for_each_entry_rcu(entry, list, list) {
+		if (entry->is_deleted == TOMOYO_GC_IN_PROGRESS)
+			continue;
 		if (!tomoyo_same_acl_head(entry, new_entry) ||
 		if (!tomoyo_same_acl_head(entry, new_entry) ||
 		    !check_duplicate(entry, new_entry))
 		    !check_duplicate(entry, new_entry))
 			continue;
 			continue;
@@ -157,6 +170,7 @@ retry:
 			continue;
 			continue;
 		if (!tomoyo_condition(r, ptr->cond))
 		if (!tomoyo_condition(r, ptr->cond))
 			continue;
 			continue;
+		r->matched_acl = ptr;
 		r->granted = true;
 		r->granted = true;
 		return;
 		return;
 	}
 	}
@@ -501,7 +515,8 @@ struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
 			 * that domain. Do not perform domain transition if
 			 * that domain. Do not perform domain transition if
 			 * profile for that domain is not yet created.
 			 * profile for that domain is not yet created.
 			 */
 			 */
-			if (!entry->ns->profile_ptr[entry->profile])
+			if (tomoyo_policy_loaded &&
+			    !entry->ns->profile_ptr[entry->profile])
 				return NULL;
 				return NULL;
 		}
 		}
 		return entry;
 		return entry;
@@ -557,11 +572,98 @@ out:
 			tomoyo_write_log(&r, "use_profile %u\n",
 			tomoyo_write_log(&r, "use_profile %u\n",
 					 entry->profile);
 					 entry->profile);
 			tomoyo_write_log(&r, "use_group %u\n", entry->group);
 			tomoyo_write_log(&r, "use_group %u\n", entry->group);
+			tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES);
 		}
 		}
 	}
 	}
 	return entry;
 	return entry;
 }
 }
 
 
+/**
+ * tomoyo_environ - Check permission for environment variable names.
+ *
+ * @ee: Pointer to "struct tomoyo_execve".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_environ(struct tomoyo_execve *ee)
+{
+	struct tomoyo_request_info *r = &ee->r;
+	struct linux_binprm *bprm = ee->bprm;
+	/* env_page.data is allocated by tomoyo_dump_page(). */
+	struct tomoyo_page_dump env_page = { };
+	char *arg_ptr; /* Size is TOMOYO_EXEC_TMPSIZE bytes */
+	int arg_len = 0;
+	unsigned long pos = bprm->p;
+	int offset = pos % PAGE_SIZE;
+	int argv_count = bprm->argc;
+	int envp_count = bprm->envc;
+	int error = -ENOMEM;
+
+	ee->r.type = TOMOYO_MAC_ENVIRON;
+	ee->r.profile = r->domain->profile;
+	ee->r.mode = tomoyo_get_mode(r->domain->ns, ee->r.profile,
+				     TOMOYO_MAC_ENVIRON);
+	if (!r->mode || !envp_count)
+		return 0;
+	arg_ptr = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
+	if (!arg_ptr)
+		goto out;
+	while (error == -ENOMEM) {
+		if (!tomoyo_dump_page(bprm, pos, &env_page))
+			goto out;
+		pos += PAGE_SIZE - offset;
+		/* Read. */
+		while (argv_count && offset < PAGE_SIZE) {
+			if (!env_page.data[offset++])
+				argv_count--;
+		}
+		if (argv_count) {
+			offset = 0;
+			continue;
+		}
+		while (offset < PAGE_SIZE) {
+			const unsigned char c = env_page.data[offset++];
+
+			if (c && arg_len < TOMOYO_EXEC_TMPSIZE - 10) {
+				if (c == '=') {
+					arg_ptr[arg_len++] = '\0';
+				} else if (c == '\\') {
+					arg_ptr[arg_len++] = '\\';
+					arg_ptr[arg_len++] = '\\';
+				} else if (c > ' ' && c < 127) {
+					arg_ptr[arg_len++] = c;
+				} else {
+					arg_ptr[arg_len++] = '\\';
+					arg_ptr[arg_len++] = (c >> 6) + '0';
+					arg_ptr[arg_len++]
+						= ((c >> 3) & 7) + '0';
+					arg_ptr[arg_len++] = (c & 7) + '0';
+				}
+			} else {
+				arg_ptr[arg_len] = '\0';
+			}
+			if (c)
+				continue;
+			if (tomoyo_env_perm(r, arg_ptr)) {
+				error = -EPERM;
+				break;
+			}
+			if (!--envp_count) {
+				error = 0;
+				break;
+			}
+			arg_len = 0;
+		}
+		offset = 0;
+	}
+out:
+	if (r->mode != TOMOYO_CONFIG_ENFORCING)
+		error = 0;
+	kfree(env_page.data);
+	kfree(arg_ptr);
+	return error;
+}
+
 /**
 /**
  * tomoyo_find_next_domain - Find a domain.
  * tomoyo_find_next_domain - Find a domain.
  *
  *
@@ -577,10 +679,11 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
 	struct tomoyo_domain_info *domain = NULL;
 	struct tomoyo_domain_info *domain = NULL;
 	const char *original_name = bprm->filename;
 	const char *original_name = bprm->filename;
 	int retval = -ENOMEM;
 	int retval = -ENOMEM;
-	bool need_kfree = false;
 	bool reject_on_transition_failure = false;
 	bool reject_on_transition_failure = false;
-	struct tomoyo_path_info rn = { }; /* real name */
+	const struct tomoyo_path_info *candidate;
+	struct tomoyo_path_info exename;
 	struct tomoyo_execve *ee = kzalloc(sizeof(*ee), GFP_NOFS);
 	struct tomoyo_execve *ee = kzalloc(sizeof(*ee), GFP_NOFS);
+
 	if (!ee)
 	if (!ee)
 		return -ENOMEM;
 		return -ENOMEM;
 	ee->tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
 	ee->tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
@@ -594,40 +697,32 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
 	ee->bprm = bprm;
 	ee->bprm = bprm;
 	ee->r.obj = &ee->obj;
 	ee->r.obj = &ee->obj;
 	ee->obj.path1 = bprm->file->f_path;
 	ee->obj.path1 = bprm->file->f_path;
- retry:
-	if (need_kfree) {
-		kfree(rn.name);
-		need_kfree = false;
-	}
 	/* Get symlink's pathname of program. */
 	/* Get symlink's pathname of program. */
 	retval = -ENOENT;
 	retval = -ENOENT;
-	rn.name = tomoyo_realpath_nofollow(original_name);
-	if (!rn.name)
+	exename.name = tomoyo_realpath_nofollow(original_name);
+	if (!exename.name)
 		goto out;
 		goto out;
-	tomoyo_fill_path_info(&rn);
-	need_kfree = true;
-
+	tomoyo_fill_path_info(&exename);
+retry:
 	/* Check 'aggregator' directive. */
 	/* Check 'aggregator' directive. */
 	{
 	{
 		struct tomoyo_aggregator *ptr;
 		struct tomoyo_aggregator *ptr;
 		struct list_head *list =
 		struct list_head *list =
 			&old_domain->ns->policy_list[TOMOYO_ID_AGGREGATOR];
 			&old_domain->ns->policy_list[TOMOYO_ID_AGGREGATOR];
 		/* Check 'aggregator' directive. */
 		/* Check 'aggregator' directive. */
+		candidate = &exename;
 		list_for_each_entry_rcu(ptr, list, head.list) {
 		list_for_each_entry_rcu(ptr, list, head.list) {
 			if (ptr->head.is_deleted ||
 			if (ptr->head.is_deleted ||
-			    !tomoyo_path_matches_pattern(&rn,
+			    !tomoyo_path_matches_pattern(&exename,
 							 ptr->original_name))
 							 ptr->original_name))
 				continue;
 				continue;
-			kfree(rn.name);
-			need_kfree = false;
-			/* This is OK because it is read only. */
-			rn = *ptr->aggregated_name;
+			candidate = ptr->aggregated_name;
 			break;
 			break;
 		}
 		}
 	}
 	}
 
 
 	/* Check execute permission. */
 	/* Check execute permission. */
-	retval = tomoyo_path_permission(&ee->r, TOMOYO_TYPE_EXECUTE, &rn);
+	retval = tomoyo_execute_permission(&ee->r, candidate);
 	if (retval == TOMOYO_RETRY_REQUEST)
 	if (retval == TOMOYO_RETRY_REQUEST)
 		goto retry;
 		goto retry;
 	if (retval < 0)
 	if (retval < 0)
@@ -638,20 +733,51 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
 	 * wildcard) rather than the pathname passed to execve()
 	 * wildcard) rather than the pathname passed to execve()
 	 * (which never contains wildcard).
 	 * (which never contains wildcard).
 	 */
 	 */
-	if (ee->r.param.path.matched_path) {
-		if (need_kfree)
-			kfree(rn.name);
-		need_kfree = false;
-		/* This is OK because it is read only. */
-		rn = *ee->r.param.path.matched_path;
-	}
+	if (ee->r.param.path.matched_path)
+		candidate = ee->r.param.path.matched_path;
 
 
-	/* Calculate domain to transit to. */
+	/*
+	 * Check for domain transition preference if "file execute" matched.
+	 * If preference is given, make do_execve() fail if domain transition
+	 * has failed, for domain transition preference should be used with
+	 * destination domain defined.
+	 */
+	if (ee->transition) {
+		const char *domainname = ee->transition->name;
+		reject_on_transition_failure = true;
+		if (!strcmp(domainname, "keep"))
+			goto force_keep_domain;
+		if (!strcmp(domainname, "child"))
+			goto force_child_domain;
+		if (!strcmp(domainname, "reset"))
+			goto force_reset_domain;
+		if (!strcmp(domainname, "initialize"))
+			goto force_initialize_domain;
+		if (!strcmp(domainname, "parent")) {
+			char *cp;
+			strncpy(ee->tmp, old_domain->domainname->name,
+				TOMOYO_EXEC_TMPSIZE - 1);
+			cp = strrchr(ee->tmp, ' ');
+			if (cp)
+				*cp = '\0';
+		} else if (*domainname == '<')
+			strncpy(ee->tmp, domainname, TOMOYO_EXEC_TMPSIZE - 1);
+		else
+			snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
+				 old_domain->domainname->name, domainname);
+		goto force_jump_domain;
+	}
+	/*
+	 * No domain transition preference specified.
+	 * Calculate domain to transit to.
+	 */
 	switch (tomoyo_transition_type(old_domain->ns, old_domain->domainname,
 	switch (tomoyo_transition_type(old_domain->ns, old_domain->domainname,
-				       &rn)) {
+				       candidate)) {
 	case TOMOYO_TRANSITION_CONTROL_RESET:
 	case TOMOYO_TRANSITION_CONTROL_RESET:
+force_reset_domain:
 		/* Transit to the root of specified namespace. */
 		/* Transit to the root of specified namespace. */
-		snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>", rn.name);
+		snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>",
+			 candidate->name);
 		/*
 		/*
 		 * Make do_execve() fail if domain transition across namespaces
 		 * Make do_execve() fail if domain transition across namespaces
 		 * has failed.
 		 * has failed.
@@ -659,11 +785,13 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
 		reject_on_transition_failure = true;
 		reject_on_transition_failure = true;
 		break;
 		break;
 	case TOMOYO_TRANSITION_CONTROL_INITIALIZE:
 	case TOMOYO_TRANSITION_CONTROL_INITIALIZE:
+force_initialize_domain:
 		/* Transit to the child of current namespace's root. */
 		/* Transit to the child of current namespace's root. */
 		snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
 		snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
-			 old_domain->ns->name, rn.name);
+			 old_domain->ns->name, candidate->name);
 		break;
 		break;
 	case TOMOYO_TRANSITION_CONTROL_KEEP:
 	case TOMOYO_TRANSITION_CONTROL_KEEP:
+force_keep_domain:
 		/* Keep current domain. */
 		/* Keep current domain. */
 		domain = old_domain;
 		domain = old_domain;
 		break;
 		break;
@@ -677,13 +805,15 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
 			 * before /sbin/init.
 			 * before /sbin/init.
 			 */
 			 */
 			domain = old_domain;
 			domain = old_domain;
-		} else {
-			/* Normal domain transition. */
-			snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
-				 old_domain->domainname->name, rn.name);
+			break;
 		}
 		}
+force_child_domain:
+		/* Normal domain transition. */
+		snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
+			 old_domain->domainname->name, candidate->name);
 		break;
 		break;
 	}
 	}
+force_jump_domain:
 	if (!domain)
 	if (!domain)
 		domain = tomoyo_assign_domain(ee->tmp, true);
 		domain = tomoyo_assign_domain(ee->tmp, true);
 	if (domain)
 	if (domain)
@@ -711,8 +841,11 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
 	/* Update reference count on "struct tomoyo_domain_info". */
 	/* Update reference count on "struct tomoyo_domain_info". */
 	atomic_inc(&domain->users);
 	atomic_inc(&domain->users);
 	bprm->cred->security = domain;
 	bprm->cred->security = domain;
-	if (need_kfree)
-		kfree(rn.name);
+	kfree(exename.name);
+	if (!retval) {
+		ee->r.domain = domain;
+		retval = tomoyo_environ(ee);
+	}
 	kfree(ee->tmp);
 	kfree(ee->tmp);
 	kfree(ee->dump.data);
 	kfree(ee->dump.data);
 	kfree(ee);
 	kfree(ee);
@@ -732,7 +865,8 @@ bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos,
 		      struct tomoyo_page_dump *dump)
 		      struct tomoyo_page_dump *dump)
 {
 {
 	struct page *page;
 	struct page *page;
-	/* dump->data is released by tomoyo_finish_execve(). */
+
+	/* dump->data is released by tomoyo_find_next_domain(). */
 	if (!dump->data) {
 	if (!dump->data) {
 		dump->data = kzalloc(PAGE_SIZE, GFP_NOFS);
 		dump->data = kzalloc(PAGE_SIZE, GFP_NOFS);
 		if (!dump->data)
 		if (!dump->data)
@@ -753,6 +887,7 @@ bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos,
 		 * So do I.
 		 * So do I.
 		 */
 		 */
 		char *kaddr = kmap_atomic(page, KM_USER0);
 		char *kaddr = kmap_atomic(page, KM_USER0);
+
 		dump->page = page;
 		dump->page = page;
 		memcpy(dump->data + offset, kaddr + offset,
 		memcpy(dump->data + offset, kaddr + offset,
 		       PAGE_SIZE - offset);
 		       PAGE_SIZE - offset);

+ 122 - 0
security/tomoyo/environ.c

@@ -0,0 +1,122 @@
+/*
+ * security/tomoyo/environ.c
+ *
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
+ */
+
+#include "common.h"
+
+/**
+ * tomoyo_check_env_acl - Check permission for environment variable's name.
+ *
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if granted, false otherwise.
+ */
+static bool tomoyo_check_env_acl(struct tomoyo_request_info *r,
+				 const struct tomoyo_acl_info *ptr)
+{
+	const struct tomoyo_env_acl *acl =
+		container_of(ptr, typeof(*acl), head);
+
+	return tomoyo_path_matches_pattern(r->param.environ.name, acl->env);
+}
+
+/**
+ * tomoyo_audit_env_log - Audit environment variable name log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_env_log(struct tomoyo_request_info *r)
+{
+	return tomoyo_supervisor(r, "misc env %s\n",
+				 r->param.environ.name->name);
+}
+
+/**
+ * tomoyo_env_perm - Check permission for environment variable's name.
+ *
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @env: The name of environment variable.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_env_perm(struct tomoyo_request_info *r, const char *env)
+{
+	struct tomoyo_path_info environ;
+	int error;
+
+	if (!env || !*env)
+		return 0;
+	environ.name = env;
+	tomoyo_fill_path_info(&environ);
+	r->param_type = TOMOYO_TYPE_ENV_ACL;
+	r->param.environ.name = &environ;
+	do {
+		tomoyo_check_acl(r, tomoyo_check_env_acl);
+		error = tomoyo_audit_env_log(r);
+	} while (error == TOMOYO_RETRY_REQUEST);
+	return error;
+}
+
+/**
+ * tomoyo_same_env_acl - Check for duplicated "struct tomoyo_env_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool tomoyo_same_env_acl(const struct tomoyo_acl_info *a,
+				const struct tomoyo_acl_info *b)
+{
+	const struct tomoyo_env_acl *p1 = container_of(a, typeof(*p1), head);
+	const struct tomoyo_env_acl *p2 = container_of(b, typeof(*p2), head);
+
+	return p1->env == p2->env;
+}
+
+/**
+ * tomoyo_write_env - Write "struct tomoyo_env_acl" list.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_write_env(struct tomoyo_acl_param *param)
+{
+	struct tomoyo_env_acl e = { .head.type = TOMOYO_TYPE_ENV_ACL };
+	int error = -ENOMEM;
+	const char *data = tomoyo_read_token(param);
+
+	if (!tomoyo_correct_word(data) || strchr(data, '='))
+		return -EINVAL;
+	e.env = tomoyo_get_name(data);
+	if (!e.env)
+		return error;
+	error = tomoyo_update_domain(&e.head, sizeof(e), param,
+				  tomoyo_same_env_acl, NULL);
+	tomoyo_put_name(e.env);
+	return error;
+}
+
+/**
+ * tomoyo_write_misc - Update environment variable list.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_misc(struct tomoyo_acl_param *param)
+{
+	if (tomoyo_str_starts(&param->data, "env "))
+		return tomoyo_write_env(param);
+	return -EINVAL;
+}

+ 34 - 8
security/tomoyo/file.c

@@ -555,8 +555,8 @@ static int tomoyo_update_path2_acl(const u8 perm,
  *
  *
  * Caller holds tomoyo_read_lock().
  * Caller holds tomoyo_read_lock().
  */
  */
-int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
-			   const struct tomoyo_path_info *filename)
+static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
+				  const struct tomoyo_path_info *filename)
 {
 {
 	int error;
 	int error;
 
 
@@ -570,15 +570,41 @@ int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
 	do {
 	do {
 		tomoyo_check_acl(r, tomoyo_check_path_acl);
 		tomoyo_check_acl(r, tomoyo_check_path_acl);
 		error = tomoyo_audit_path_log(r);
 		error = tomoyo_audit_path_log(r);
-		/*
-		 * Do not retry for execute request, for alias may have
-		 * changed.
-		 */
-	} while (error == TOMOYO_RETRY_REQUEST &&
-		 operation != TOMOYO_TYPE_EXECUTE);
+	} while (error == TOMOYO_RETRY_REQUEST);
 	return error;
 	return error;
 }
 }
 
 
+/**
+ * tomoyo_execute_permission - Check permission for execute operation.
+ *
+ * @r:         Pointer to "struct tomoyo_request_info".
+ * @filename:  Filename to check.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_execute_permission(struct tomoyo_request_info *r,
+			      const struct tomoyo_path_info *filename)
+{
+	/*
+	 * Unlike other permission checks, this check is done regardless of
+	 * profile mode settings in order to check for domain transition
+	 * preference.
+	 */
+	r->type = TOMOYO_MAC_FILE_EXECUTE;
+	r->mode = tomoyo_get_mode(r->domain->ns, r->profile, r->type);
+	r->param_type = TOMOYO_TYPE_PATH_ACL;
+	r->param.path.filename = filename;
+	r->param.path.operation = TOMOYO_TYPE_EXECUTE;
+	tomoyo_check_acl(r, tomoyo_check_path_acl);
+	r->ee->transition = r->matched_acl && r->matched_acl->cond ?
+		r->matched_acl->cond->transit : NULL;
+	if (r->mode != TOMOYO_CONFIG_DISABLED)
+		return tomoyo_audit_path_log(r);
+	return 0;
+}
+
 /**
 /**
  * tomoyo_same_path_number_acl - Check for duplicated "struct tomoyo_path_number_acl" entry.
  * tomoyo_same_path_number_acl - Check for duplicated "struct tomoyo_path_number_acl" entry.
  *
  *

+ 235 - 305
security/tomoyo/gc.c

@@ -8,36 +8,26 @@
 #include <linux/kthread.h>
 #include <linux/kthread.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
 
 
+/**
+ * tomoyo_memory_free - Free memory for elements.
+ *
+ * @ptr:  Pointer to allocated memory.
+ *
+ * Returns nothing.
+ *
+ * Caller holds tomoyo_policy_lock mutex.
+ */
+static inline void tomoyo_memory_free(void *ptr)
+{
+	tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= ksize(ptr);
+	kfree(ptr);
+}
+
 /* The list for "struct tomoyo_io_buffer". */
 /* The list for "struct tomoyo_io_buffer". */
 static LIST_HEAD(tomoyo_io_buffer_list);
 static LIST_HEAD(tomoyo_io_buffer_list);
 /* Lock for protecting tomoyo_io_buffer_list. */
 /* Lock for protecting tomoyo_io_buffer_list. */
 static DEFINE_SPINLOCK(tomoyo_io_buffer_list_lock);
 static DEFINE_SPINLOCK(tomoyo_io_buffer_list_lock);
 
 
-/* Size of an element. */
-static const u8 tomoyo_element_size[TOMOYO_MAX_POLICY] = {
-	[TOMOYO_ID_GROUP] = sizeof(struct tomoyo_group),
-	[TOMOYO_ID_PATH_GROUP] = sizeof(struct tomoyo_path_group),
-	[TOMOYO_ID_NUMBER_GROUP] = sizeof(struct tomoyo_number_group),
-	[TOMOYO_ID_AGGREGATOR] = sizeof(struct tomoyo_aggregator),
-	[TOMOYO_ID_TRANSITION_CONTROL] =
-	sizeof(struct tomoyo_transition_control),
-	[TOMOYO_ID_MANAGER] = sizeof(struct tomoyo_manager),
-	/* [TOMOYO_ID_CONDITION] = "struct tomoyo_condition"->size, */
-	/* [TOMOYO_ID_NAME] = "struct tomoyo_name"->size, */
-	/* [TOMOYO_ID_ACL] =
-	   tomoyo_acl_size["struct tomoyo_acl_info"->type], */
-	[TOMOYO_ID_DOMAIN] = sizeof(struct tomoyo_domain_info),
-};
-
-/* Size of a domain ACL element. */
-static const u8 tomoyo_acl_size[] = {
-	[TOMOYO_TYPE_PATH_ACL] = sizeof(struct tomoyo_path_acl),
-	[TOMOYO_TYPE_PATH2_ACL] = sizeof(struct tomoyo_path2_acl),
-	[TOMOYO_TYPE_PATH_NUMBER_ACL] = sizeof(struct tomoyo_path_number_acl),
-	[TOMOYO_TYPE_MKDEV_ACL] = sizeof(struct tomoyo_mkdev_acl),
-	[TOMOYO_TYPE_MOUNT_ACL] = sizeof(struct tomoyo_mount_acl),
-};
-
 /**
 /**
  * tomoyo_struct_used_by_io_buffer - Check whether the list element is used by /sys/kernel/security/tomoyo/ users or not.
  * tomoyo_struct_used_by_io_buffer - Check whether the list element is used by /sys/kernel/security/tomoyo/ users or not.
  *
  *
@@ -55,15 +45,11 @@ static bool tomoyo_struct_used_by_io_buffer(const struct list_head *element)
 	list_for_each_entry(head, &tomoyo_io_buffer_list, list) {
 	list_for_each_entry(head, &tomoyo_io_buffer_list, list) {
 		head->users++;
 		head->users++;
 		spin_unlock(&tomoyo_io_buffer_list_lock);
 		spin_unlock(&tomoyo_io_buffer_list_lock);
-		if (mutex_lock_interruptible(&head->io_sem)) {
-			in_use = true;
-			goto out;
-		}
+		mutex_lock(&head->io_sem);
 		if (head->r.domain == element || head->r.group == element ||
 		if (head->r.domain == element || head->r.group == element ||
 		    head->r.acl == element || &head->w.domain->list == element)
 		    head->r.acl == element || &head->w.domain->list == element)
 			in_use = true;
 			in_use = true;
 		mutex_unlock(&head->io_sem);
 		mutex_unlock(&head->io_sem);
-out:
 		spin_lock(&tomoyo_io_buffer_list_lock);
 		spin_lock(&tomoyo_io_buffer_list_lock);
 		head->users--;
 		head->users--;
 		if (in_use)
 		if (in_use)
@@ -77,15 +63,14 @@ out:
  * tomoyo_name_used_by_io_buffer - Check whether the string is used by /sys/kernel/security/tomoyo/ users or not.
  * tomoyo_name_used_by_io_buffer - Check whether the string is used by /sys/kernel/security/tomoyo/ users or not.
  *
  *
  * @string: String to check.
  * @string: String to check.
- * @size:   Memory allocated for @string .
  *
  *
  * Returns true if @string is used by /sys/kernel/security/tomoyo/ users,
  * Returns true if @string is used by /sys/kernel/security/tomoyo/ users,
  * false otherwise.
  * false otherwise.
  */
  */
-static bool tomoyo_name_used_by_io_buffer(const char *string,
-					  const size_t size)
+static bool tomoyo_name_used_by_io_buffer(const char *string)
 {
 {
 	struct tomoyo_io_buffer *head;
 	struct tomoyo_io_buffer *head;
+	const size_t size = strlen(string) + 1;
 	bool in_use = false;
 	bool in_use = false;
 
 
 	spin_lock(&tomoyo_io_buffer_list_lock);
 	spin_lock(&tomoyo_io_buffer_list_lock);
@@ -93,10 +78,7 @@ static bool tomoyo_name_used_by_io_buffer(const char *string,
 		int i;
 		int i;
 		head->users++;
 		head->users++;
 		spin_unlock(&tomoyo_io_buffer_list_lock);
 		spin_unlock(&tomoyo_io_buffer_list_lock);
-		if (mutex_lock_interruptible(&head->io_sem)) {
-			in_use = true;
-			goto out;
-		}
+		mutex_lock(&head->io_sem);
 		for (i = 0; i < TOMOYO_MAX_IO_READ_QUEUE; i++) {
 		for (i = 0; i < TOMOYO_MAX_IO_READ_QUEUE; i++) {
 			const char *w = head->r.w[i];
 			const char *w = head->r.w[i];
 			if (w < string || w > string + size)
 			if (w < string || w > string + size)
@@ -105,7 +87,6 @@ static bool tomoyo_name_used_by_io_buffer(const char *string,
 			break;
 			break;
 		}
 		}
 		mutex_unlock(&head->io_sem);
 		mutex_unlock(&head->io_sem);
-out:
 		spin_lock(&tomoyo_io_buffer_list_lock);
 		spin_lock(&tomoyo_io_buffer_list_lock);
 		head->users--;
 		head->users--;
 		if (in_use)
 		if (in_use)
@@ -115,84 +96,6 @@ out:
 	return in_use;
 	return in_use;
 }
 }
 
 
-/* Structure for garbage collection. */
-struct tomoyo_gc {
-	struct list_head list;
-	enum tomoyo_policy_id type;
-	size_t size;
-	struct list_head *element;
-};
-/* List of entries to be deleted. */
-static LIST_HEAD(tomoyo_gc_list);
-/* Length of tomoyo_gc_list. */
-static int tomoyo_gc_list_len;
-
-/**
- * tomoyo_add_to_gc - Add an entry to to be deleted list.
- *
- * @type:    One of values in "enum tomoyo_policy_id".
- * @element: Pointer to "struct list_head".
- *
- * Returns true on success, false otherwise.
- *
- * Caller holds tomoyo_policy_lock mutex.
- *
- * Adding an entry needs kmalloc(). Thus, if we try to add thousands of
- * entries at once, it will take too long time. Thus, do not add more than 128
- * entries per a scan. But to be able to handle worst case where all entries
- * are in-use, we accept one more entry per a scan.
- *
- * If we use singly linked list using "struct list_head"->prev (which is
- * LIST_POISON2), we can avoid kmalloc().
- */
-static bool tomoyo_add_to_gc(const int type, struct list_head *element)
-{
-	struct tomoyo_gc *entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
-	if (!entry)
-		return false;
-	entry->type = type;
-	if (type == TOMOYO_ID_ACL)
-		entry->size = tomoyo_acl_size[
-			      container_of(element,
-					   typeof(struct tomoyo_acl_info),
-					   list)->type];
-	else if (type == TOMOYO_ID_NAME)
-		entry->size = strlen(container_of(element,
-						  typeof(struct tomoyo_name),
-						  head.list)->entry.name) + 1;
-	else if (type == TOMOYO_ID_CONDITION)
-		entry->size =
-			container_of(element, typeof(struct tomoyo_condition),
-				     head.list)->size;
-	else
-		entry->size = tomoyo_element_size[type];
-	entry->element = element;
-	list_add(&entry->list, &tomoyo_gc_list);
-	list_del_rcu(element);
-	return tomoyo_gc_list_len++ < 128;
-}
-
-/**
- * tomoyo_element_linked_by_gc - Validate next element of an entry.
- *
- * @element: Pointer to an element.
- * @size:    Size of @element in byte.
- *
- * Returns true if @element is linked by other elements in the garbage
- * collector's queue, false otherwise.
- */
-static bool tomoyo_element_linked_by_gc(const u8 *element, const size_t size)
-{
-	struct tomoyo_gc *p;
-	list_for_each_entry(p, &tomoyo_gc_list, list) {
-		const u8 *ptr = (const u8 *) p->element->next;
-		if (ptr < element || element + size < ptr)
-			continue;
-		return true;
-	}
-	return false;
-}
-
 /**
 /**
  * tomoyo_del_transition_control - Delete members in "struct tomoyo_transition_control".
  * tomoyo_del_transition_control - Delete members in "struct tomoyo_transition_control".
  *
  *
@@ -200,7 +103,7 @@ static bool tomoyo_element_linked_by_gc(const u8 *element, const size_t size)
  *
  *
  * Returns nothing.
  * Returns nothing.
  */
  */
-static void tomoyo_del_transition_control(struct list_head *element)
+static inline void tomoyo_del_transition_control(struct list_head *element)
 {
 {
 	struct tomoyo_transition_control *ptr =
 	struct tomoyo_transition_control *ptr =
 		container_of(element, typeof(*ptr), head.list);
 		container_of(element, typeof(*ptr), head.list);
@@ -215,7 +118,7 @@ static void tomoyo_del_transition_control(struct list_head *element)
  *
  *
  * Returns nothing.
  * Returns nothing.
  */
  */
-static void tomoyo_del_aggregator(struct list_head *element)
+static inline void tomoyo_del_aggregator(struct list_head *element)
 {
 {
 	struct tomoyo_aggregator *ptr =
 	struct tomoyo_aggregator *ptr =
 		container_of(element, typeof(*ptr), head.list);
 		container_of(element, typeof(*ptr), head.list);
@@ -230,7 +133,7 @@ static void tomoyo_del_aggregator(struct list_head *element)
  *
  *
  * Returns nothing.
  * Returns nothing.
  */
  */
-static void tomoyo_del_manager(struct list_head *element)
+static inline void tomoyo_del_manager(struct list_head *element)
 {
 {
 	struct tomoyo_manager *ptr =
 	struct tomoyo_manager *ptr =
 		container_of(element, typeof(*ptr), head.list);
 		container_of(element, typeof(*ptr), head.list);
@@ -293,6 +196,38 @@ static void tomoyo_del_acl(struct list_head *element)
 			tomoyo_put_number_union(&entry->flags);
 			tomoyo_put_number_union(&entry->flags);
 		}
 		}
 		break;
 		break;
+	case TOMOYO_TYPE_ENV_ACL:
+		{
+			struct tomoyo_env_acl *entry =
+				container_of(acl, typeof(*entry), head);
+
+			tomoyo_put_name(entry->env);
+		}
+		break;
+	case TOMOYO_TYPE_INET_ACL:
+		{
+			struct tomoyo_inet_acl *entry =
+				container_of(acl, typeof(*entry), head);
+
+			tomoyo_put_group(entry->address.group);
+			tomoyo_put_number_union(&entry->port);
+		}
+		break;
+	case TOMOYO_TYPE_UNIX_ACL:
+		{
+			struct tomoyo_unix_acl *entry =
+				container_of(acl, typeof(*entry), head);
+
+			tomoyo_put_name_union(&entry->name);
+		}
+		break;
+	case TOMOYO_TYPE_MANUAL_TASK_ACL:
+		{
+			struct tomoyo_task_acl *entry =
+				container_of(acl, typeof(*entry), head);
+			tomoyo_put_name(entry->domainname);
+		}
+		break;
 	}
 	}
 }
 }
 
 
@@ -301,44 +236,26 @@ static void tomoyo_del_acl(struct list_head *element)
  *
  *
  * @element: Pointer to "struct list_head".
  * @element: Pointer to "struct list_head".
  *
  *
- * Returns true if deleted, false otherwise.
+ * Returns nothing.
+ *
+ * Caller holds tomoyo_policy_lock mutex.
  */
  */
-static bool tomoyo_del_domain(struct list_head *element)
+static inline void tomoyo_del_domain(struct list_head *element)
 {
 {
 	struct tomoyo_domain_info *domain =
 	struct tomoyo_domain_info *domain =
 		container_of(element, typeof(*domain), list);
 		container_of(element, typeof(*domain), list);
 	struct tomoyo_acl_info *acl;
 	struct tomoyo_acl_info *acl;
 	struct tomoyo_acl_info *tmp;
 	struct tomoyo_acl_info *tmp;
 	/*
 	/*
-	 * Since we don't protect whole execve() operation using SRCU,
-	 * we need to recheck domain->users at this point.
-	 *
-	 * (1) Reader starts SRCU section upon execve().
-	 * (2) Reader traverses tomoyo_domain_list and finds this domain.
-	 * (3) Writer marks this domain as deleted.
-	 * (4) Garbage collector removes this domain from tomoyo_domain_list
-	 *     because this domain is marked as deleted and used by nobody.
-	 * (5) Reader saves reference to this domain into
-	 *     "struct linux_binprm"->cred->security .
-	 * (6) Reader finishes SRCU section, although execve() operation has
-	 *     not finished yet.
-	 * (7) Garbage collector waits for SRCU synchronization.
-	 * (8) Garbage collector kfree() this domain because this domain is
-	 *     used by nobody.
-	 * (9) Reader finishes execve() operation and restores this domain from
-	 *     "struct linux_binprm"->cred->security.
-	 *
-	 * By updating domain->users at (5), we can solve this race problem
-	 * by rechecking domain->users at (8).
+	 * Since this domain is referenced from neither
+	 * "struct tomoyo_io_buffer" nor "struct cred"->security, we can delete
+	 * elements without checking for is_deleted flag.
 	 */
 	 */
-	if (atomic_read(&domain->users))
-		return false;
 	list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) {
 	list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) {
 		tomoyo_del_acl(&acl->list);
 		tomoyo_del_acl(&acl->list);
 		tomoyo_memory_free(acl);
 		tomoyo_memory_free(acl);
 	}
 	}
 	tomoyo_put_name(domain->domainname);
 	tomoyo_put_name(domain->domainname);
-	return true;
 }
 }
 
 
 /**
 /**
@@ -387,10 +304,9 @@ void tomoyo_del_condition(struct list_head *element)
  *
  *
  * Returns nothing.
  * Returns nothing.
  */
  */
-static void tomoyo_del_name(struct list_head *element)
+static inline void tomoyo_del_name(struct list_head *element)
 {
 {
-	const struct tomoyo_name *ptr =
-		container_of(element, typeof(*ptr), head.list);
+	/* Nothing to do. */
 }
 }
 
 
 /**
 /**
@@ -400,7 +316,7 @@ static void tomoyo_del_name(struct list_head *element)
  *
  *
  * Returns nothing.
  * Returns nothing.
  */
  */
-static void tomoyo_del_path_group(struct list_head *element)
+static inline void tomoyo_del_path_group(struct list_head *element)
 {
 {
 	struct tomoyo_path_group *member =
 	struct tomoyo_path_group *member =
 		container_of(element, typeof(*member), head.list);
 		container_of(element, typeof(*member), head.list);
@@ -414,13 +330,25 @@ static void tomoyo_del_path_group(struct list_head *element)
  *
  *
  * Returns nothing.
  * Returns nothing.
  */
  */
-static void tomoyo_del_group(struct list_head *element)
+static inline void tomoyo_del_group(struct list_head *element)
 {
 {
 	struct tomoyo_group *group =
 	struct tomoyo_group *group =
 		container_of(element, typeof(*group), head.list);
 		container_of(element, typeof(*group), head.list);
 	tomoyo_put_name(group->group_name);
 	tomoyo_put_name(group->group_name);
 }
 }
 
 
+/**
+ * tomoyo_del_address_group - Delete members in "struct tomoyo_address_group".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
+static inline void tomoyo_del_address_group(struct list_head *element)
+{
+	/* Nothing to do. */
+}
+
 /**
 /**
  * tomoyo_del_number_group - Delete members in "struct tomoyo_number_group".
  * tomoyo_del_number_group - Delete members in "struct tomoyo_number_group".
  *
  *
@@ -428,10 +356,110 @@ static void tomoyo_del_group(struct list_head *element)
  *
  *
  * Returns nothing.
  * Returns nothing.
  */
  */
-static void tomoyo_del_number_group(struct list_head *element)
+static inline void tomoyo_del_number_group(struct list_head *element)
 {
 {
-	struct tomoyo_number_group *member =
-		container_of(element, typeof(*member), head.list);
+	/* Nothing to do. */
+}
+
+/**
+ * tomoyo_try_to_gc - Try to kfree() an entry.
+ *
+ * @type:    One of values in "enum tomoyo_policy_id".
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ *
+ * Caller holds tomoyo_policy_lock mutex.
+ */
+static void tomoyo_try_to_gc(const enum tomoyo_policy_id type,
+			     struct list_head *element)
+{
+	/*
+	 * __list_del_entry() guarantees that the list element became no longer
+	 * reachable from the list which the element was originally on (e.g.
+	 * tomoyo_domain_list). Also, synchronize_srcu() guarantees that the
+	 * list element became no longer referenced by syscall users.
+	 */
+	__list_del_entry(element);
+	mutex_unlock(&tomoyo_policy_lock);
+	synchronize_srcu(&tomoyo_ss);
+	/*
+	 * However, there are two users which may still be using the list
+	 * element. We need to defer until both users forget this element.
+	 *
+	 * Don't kfree() until "struct tomoyo_io_buffer"->r.{domain,group,acl}
+	 * and "struct tomoyo_io_buffer"->w.domain forget this element.
+	 */
+	if (tomoyo_struct_used_by_io_buffer(element))
+		goto reinject;
+	switch (type) {
+	case TOMOYO_ID_TRANSITION_CONTROL:
+		tomoyo_del_transition_control(element);
+		break;
+	case TOMOYO_ID_MANAGER:
+		tomoyo_del_manager(element);
+		break;
+	case TOMOYO_ID_AGGREGATOR:
+		tomoyo_del_aggregator(element);
+		break;
+	case TOMOYO_ID_GROUP:
+		tomoyo_del_group(element);
+		break;
+	case TOMOYO_ID_PATH_GROUP:
+		tomoyo_del_path_group(element);
+		break;
+	case TOMOYO_ID_ADDRESS_GROUP:
+		tomoyo_del_address_group(element);
+		break;
+	case TOMOYO_ID_NUMBER_GROUP:
+		tomoyo_del_number_group(element);
+		break;
+	case TOMOYO_ID_CONDITION:
+		tomoyo_del_condition(element);
+		break;
+	case TOMOYO_ID_NAME:
+		/*
+		 * Don't kfree() until all "struct tomoyo_io_buffer"->r.w[]
+		 * forget this element.
+		 */
+		if (tomoyo_name_used_by_io_buffer
+		    (container_of(element, typeof(struct tomoyo_name),
+				  head.list)->entry.name))
+			goto reinject;
+		tomoyo_del_name(element);
+		break;
+	case TOMOYO_ID_ACL:
+		tomoyo_del_acl(element);
+		break;
+	case TOMOYO_ID_DOMAIN:
+		/*
+		 * Don't kfree() until all "struct cred"->security forget this
+		 * element.
+		 */
+		if (atomic_read(&container_of
+				(element, typeof(struct tomoyo_domain_info),
+				 list)->users))
+			goto reinject;
+		break;
+	case TOMOYO_MAX_POLICY:
+		break;
+	}
+	mutex_lock(&tomoyo_policy_lock);
+	if (type == TOMOYO_ID_DOMAIN)
+		tomoyo_del_domain(element);
+	tomoyo_memory_free(element);
+	return;
+reinject:
+	/*
+	 * We can safely reinject this element here bacause
+	 * (1) Appending list elements and removing list elements are protected
+	 *     by tomoyo_policy_lock mutex.
+	 * (2) Only this function removes list elements and this function is
+	 *     exclusively executed by tomoyo_gc_mutex mutex.
+	 * are true.
+	 */
+	mutex_lock(&tomoyo_policy_lock);
+	list_add_rcu(element, element->prev);
 }
 }
 
 
 /**
 /**
@@ -440,19 +468,19 @@ static void tomoyo_del_number_group(struct list_head *element)
  * @id:          One of values in "enum tomoyo_policy_id".
  * @id:          One of values in "enum tomoyo_policy_id".
  * @member_list: Pointer to "struct list_head".
  * @member_list: Pointer to "struct list_head".
  *
  *
- * Returns true if some elements are deleted, false otherwise.
+ * Returns nothing.
  */
  */
-static bool tomoyo_collect_member(const enum tomoyo_policy_id id,
+static void tomoyo_collect_member(const enum tomoyo_policy_id id,
 				  struct list_head *member_list)
 				  struct list_head *member_list)
 {
 {
 	struct tomoyo_acl_head *member;
 	struct tomoyo_acl_head *member;
-	list_for_each_entry(member, member_list, list) {
+	struct tomoyo_acl_head *tmp;
+	list_for_each_entry_safe(member, tmp, member_list, list) {
 		if (!member->is_deleted)
 		if (!member->is_deleted)
 			continue;
 			continue;
-		if (!tomoyo_add_to_gc(id, &member->list))
-			return false;
+		member->is_deleted = TOMOYO_GC_IN_PROGRESS;
+		tomoyo_try_to_gc(id, &member->list);
 	}
 	}
-	return true;
 }
 }
 
 
 /**
 /**
@@ -460,22 +488,22 @@ static bool tomoyo_collect_member(const enum tomoyo_policy_id id,
  *
  *
  * @list: Pointer to "struct list_head".
  * @list: Pointer to "struct list_head".
  *
  *
- * Returns true if some elements are deleted, false otherwise.
+ * Returns nothing.
  */
  */
-static bool tomoyo_collect_acl(struct list_head *list)
+static void tomoyo_collect_acl(struct list_head *list)
 {
 {
 	struct tomoyo_acl_info *acl;
 	struct tomoyo_acl_info *acl;
-	list_for_each_entry(acl, list, list) {
+	struct tomoyo_acl_info *tmp;
+	list_for_each_entry_safe(acl, tmp, list, list) {
 		if (!acl->is_deleted)
 		if (!acl->is_deleted)
 			continue;
 			continue;
-		if (!tomoyo_add_to_gc(TOMOYO_ID_ACL, &acl->list))
-			return false;
+		acl->is_deleted = TOMOYO_GC_IN_PROGRESS;
+		tomoyo_try_to_gc(TOMOYO_ID_ACL, &acl->list);
 	}
 	}
-	return true;
 }
 }
 
 
 /**
 /**
- * tomoyo_collect_entry - Scan lists for deleted elements.
+ * tomoyo_collect_entry - Try to kfree() deleted elements.
  *
  *
  * Returns nothing.
  * Returns nothing.
  */
  */
@@ -484,174 +512,82 @@ static void tomoyo_collect_entry(void)
 	int i;
 	int i;
 	enum tomoyo_policy_id id;
 	enum tomoyo_policy_id id;
 	struct tomoyo_policy_namespace *ns;
 	struct tomoyo_policy_namespace *ns;
-	int idx;
-	if (mutex_lock_interruptible(&tomoyo_policy_lock))
-		return;
-	idx = tomoyo_read_lock();
+	mutex_lock(&tomoyo_policy_lock);
 	{
 	{
 		struct tomoyo_domain_info *domain;
 		struct tomoyo_domain_info *domain;
-		list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
-			if (!tomoyo_collect_acl(&domain->acl_info_list))
-				goto unlock;
+		struct tomoyo_domain_info *tmp;
+		list_for_each_entry_safe(domain, tmp, &tomoyo_domain_list,
+					 list) {
+			tomoyo_collect_acl(&domain->acl_info_list);
 			if (!domain->is_deleted || atomic_read(&domain->users))
 			if (!domain->is_deleted || atomic_read(&domain->users))
 				continue;
 				continue;
-			/*
-			 * Nobody is referring this domain. But somebody may
-			 * refer this domain after successful execve().
-			 * We recheck domain->users after SRCU synchronization.
-			 */
-			if (!tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, &domain->list))
-				goto unlock;
+			tomoyo_try_to_gc(TOMOYO_ID_DOMAIN, &domain->list);
 		}
 		}
 	}
 	}
-	list_for_each_entry_rcu(ns, &tomoyo_namespace_list, namespace_list) {
+	list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) {
 		for (id = 0; id < TOMOYO_MAX_POLICY; id++)
 		for (id = 0; id < TOMOYO_MAX_POLICY; id++)
-			if (!tomoyo_collect_member(id, &ns->policy_list[id]))
-				goto unlock;
+			tomoyo_collect_member(id, &ns->policy_list[id]);
 		for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++)
 		for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++)
-			if (!tomoyo_collect_acl(&ns->acl_group[i]))
-				goto unlock;
+			tomoyo_collect_acl(&ns->acl_group[i]);
+	}
+	{
+		struct tomoyo_shared_acl_head *ptr;
+		struct tomoyo_shared_acl_head *tmp;
+		list_for_each_entry_safe(ptr, tmp, &tomoyo_condition_list,
+					 list) {
+			if (atomic_read(&ptr->users) > 0)
+				continue;
+			atomic_set(&ptr->users, TOMOYO_GC_IN_PROGRESS);
+			tomoyo_try_to_gc(TOMOYO_ID_CONDITION, &ptr->list);
+		}
+	}
+	list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) {
 		for (i = 0; i < TOMOYO_MAX_GROUP; i++) {
 		for (i = 0; i < TOMOYO_MAX_GROUP; i++) {
 			struct list_head *list = &ns->group_list[i];
 			struct list_head *list = &ns->group_list[i];
 			struct tomoyo_group *group;
 			struct tomoyo_group *group;
+			struct tomoyo_group *tmp;
 			switch (i) {
 			switch (i) {
 			case 0:
 			case 0:
 				id = TOMOYO_ID_PATH_GROUP;
 				id = TOMOYO_ID_PATH_GROUP;
 				break;
 				break;
-			default:
+			case 1:
 				id = TOMOYO_ID_NUMBER_GROUP;
 				id = TOMOYO_ID_NUMBER_GROUP;
 				break;
 				break;
+			default:
+				id = TOMOYO_ID_ADDRESS_GROUP;
+				break;
 			}
 			}
-			list_for_each_entry(group, list, head.list) {
-				if (!tomoyo_collect_member
-				    (id, &group->member_list))
-					goto unlock;
+			list_for_each_entry_safe(group, tmp, list, head.list) {
+				tomoyo_collect_member(id, &group->member_list);
 				if (!list_empty(&group->member_list) ||
 				if (!list_empty(&group->member_list) ||
-				    atomic_read(&group->head.users))
+				    atomic_read(&group->head.users) > 0)
 					continue;
 					continue;
-				if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP,
-						      &group->head.list))
-					goto unlock;
+				atomic_set(&group->head.users,
+					   TOMOYO_GC_IN_PROGRESS);
+				tomoyo_try_to_gc(TOMOYO_ID_GROUP,
+						 &group->head.list);
 			}
 			}
 		}
 		}
 	}
 	}
-	id = TOMOYO_ID_CONDITION;
-	for (i = 0; i < TOMOYO_MAX_HASH + 1; i++) {
-		struct list_head *list = !i ?
-			&tomoyo_condition_list : &tomoyo_name_list[i - 1];
+	for (i = 0; i < TOMOYO_MAX_HASH; i++) {
+		struct list_head *list = &tomoyo_name_list[i];
 		struct tomoyo_shared_acl_head *ptr;
 		struct tomoyo_shared_acl_head *ptr;
-		list_for_each_entry(ptr, list, list) {
-			if (atomic_read(&ptr->users))
+		struct tomoyo_shared_acl_head *tmp;
+		list_for_each_entry_safe(ptr, tmp, list, list) {
+			if (atomic_read(&ptr->users) > 0)
 				continue;
 				continue;
-			if (!tomoyo_add_to_gc(id, &ptr->list))
-				goto unlock;
+			atomic_set(&ptr->users, TOMOYO_GC_IN_PROGRESS);
+			tomoyo_try_to_gc(TOMOYO_ID_NAME, &ptr->list);
 		}
 		}
-		id = TOMOYO_ID_NAME;
 	}
 	}
-unlock:
-	tomoyo_read_unlock(idx);
 	mutex_unlock(&tomoyo_policy_lock);
 	mutex_unlock(&tomoyo_policy_lock);
 }
 }
 
 
-/**
- * tomoyo_kfree_entry - Delete entries in tomoyo_gc_list.
- *
- * Returns true if some entries were kfree()d, false otherwise.
- */
-static bool tomoyo_kfree_entry(void)
-{
-	struct tomoyo_gc *p;
-	struct tomoyo_gc *tmp;
-	bool result = false;
-
-	list_for_each_entry_safe(p, tmp, &tomoyo_gc_list, list) {
-		struct list_head *element = p->element;
-
-		/*
-		 * list_del_rcu() in tomoyo_add_to_gc() guarantees that the
-		 * list element became no longer reachable from the list which
-		 * the element was originally on (e.g. tomoyo_domain_list).
-		 * Also, synchronize_srcu() in tomoyo_gc_thread() guarantees
-		 * that the list element became no longer referenced by syscall
-		 * users.
-		 *
-		 * However, there are three users which may still be using the
-		 * list element. We need to defer until all of these users
-		 * forget the list element.
-		 *
-		 * Firstly, defer until "struct tomoyo_io_buffer"->r.{domain,
-		 * group,acl} and "struct tomoyo_io_buffer"->w.domain forget
-		 * the list element.
-		 */
-		if (tomoyo_struct_used_by_io_buffer(element))
-			continue;
-		/*
-		 * Secondly, defer until all other elements in the
-		 * tomoyo_gc_list list forget the list element.
-		 */
-		if (tomoyo_element_linked_by_gc((const u8 *) element, p->size))
-			continue;
-		switch (p->type) {
-		case TOMOYO_ID_TRANSITION_CONTROL:
-			tomoyo_del_transition_control(element);
-			break;
-		case TOMOYO_ID_AGGREGATOR:
-			tomoyo_del_aggregator(element);
-			break;
-		case TOMOYO_ID_MANAGER:
-			tomoyo_del_manager(element);
-			break;
-		case TOMOYO_ID_CONDITION:
-			tomoyo_del_condition(element);
-			break;
-		case TOMOYO_ID_NAME:
-			/*
-			 * Thirdly, defer until all "struct tomoyo_io_buffer"
-			 * ->r.w[] forget the list element.
-			 */
-			if (tomoyo_name_used_by_io_buffer(
-			    container_of(element, typeof(struct tomoyo_name),
-					 head.list)->entry.name, p->size))
-				continue;
-			tomoyo_del_name(element);
-			break;
-		case TOMOYO_ID_ACL:
-			tomoyo_del_acl(element);
-			break;
-		case TOMOYO_ID_DOMAIN:
-			if (!tomoyo_del_domain(element))
-				continue;
-			break;
-		case TOMOYO_ID_PATH_GROUP:
-			tomoyo_del_path_group(element);
-			break;
-		case TOMOYO_ID_GROUP:
-			tomoyo_del_group(element);
-			break;
-		case TOMOYO_ID_NUMBER_GROUP:
-			tomoyo_del_number_group(element);
-			break;
-		case TOMOYO_MAX_POLICY:
-			break;
-		}
-		tomoyo_memory_free(element);
-		list_del(&p->list);
-		kfree(p);
-		tomoyo_gc_list_len--;
-		result = true;
-	}
-	return result;
-}
-
 /**
 /**
  * tomoyo_gc_thread - Garbage collector thread function.
  * tomoyo_gc_thread - Garbage collector thread function.
  *
  *
  * @unused: Unused.
  * @unused: Unused.
  *
  *
- * In case OOM-killer choose this thread for termination, we create this thread
- * as a short live thread whenever /sys/kernel/security/tomoyo/ interface was
- * close()d.
- *
  * Returns 0.
  * Returns 0.
  */
  */
 static int tomoyo_gc_thread(void *unused)
 static int tomoyo_gc_thread(void *unused)
@@ -660,13 +596,7 @@ static int tomoyo_gc_thread(void *unused)
 	static DEFINE_MUTEX(tomoyo_gc_mutex);
 	static DEFINE_MUTEX(tomoyo_gc_mutex);
 	if (!mutex_trylock(&tomoyo_gc_mutex))
 	if (!mutex_trylock(&tomoyo_gc_mutex))
 		goto out;
 		goto out;
-	daemonize("GC for TOMOYO");
-	do {
-		tomoyo_collect_entry();
-		if (list_empty(&tomoyo_gc_list))
-			break;
-		synchronize_srcu(&tomoyo_ss);
-	} while (tomoyo_kfree_entry());
+	tomoyo_collect_entry();
 	{
 	{
 		struct tomoyo_io_buffer *head;
 		struct tomoyo_io_buffer *head;
 		struct tomoyo_io_buffer *tmp;
 		struct tomoyo_io_buffer *tmp;

+ 60 - 1
security/tomoyo/group.c

@@ -42,7 +42,26 @@ static bool tomoyo_same_number_group(const struct tomoyo_acl_head *a,
 }
 }
 
 
 /**
 /**
- * tomoyo_write_group - Write "struct tomoyo_path_group"/"struct tomoyo_number_group" list.
+ * tomoyo_same_address_group - Check for duplicated "struct tomoyo_address_group" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_head".
+ * @b: Pointer to "struct tomoyo_acl_head".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool tomoyo_same_address_group(const struct tomoyo_acl_head *a,
+				      const struct tomoyo_acl_head *b)
+{
+	const struct tomoyo_address_group *p1 = container_of(a, typeof(*p1),
+							     head);
+	const struct tomoyo_address_group *p2 = container_of(b, typeof(*p2),
+							     head);
+
+	return tomoyo_same_ipaddr_union(&p1->address, &p2->address);
+}
+
+/**
+ * tomoyo_write_group - Write "struct tomoyo_path_group"/"struct tomoyo_number_group"/"struct tomoyo_address_group" list.
  *
  *
  * @param: Pointer to "struct tomoyo_acl_param".
  * @param: Pointer to "struct tomoyo_acl_param".
  * @type:  Type of this group.
  * @type:  Type of this group.
@@ -77,6 +96,14 @@ int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type)
 		 * tomoyo_put_number_union() is not needed because
 		 * tomoyo_put_number_union() is not needed because
 		 * param->data[0] != '@'.
 		 * param->data[0] != '@'.
 		 */
 		 */
+	} else {
+		struct tomoyo_address_group e = { };
+
+		if (param->data[0] == '@' ||
+		    !tomoyo_parse_ipaddr_union(param, &e.address))
+			goto out;
+		error = tomoyo_update_policy(&e.head, sizeof(e), param,
+					     tomoyo_same_address_group);
 	}
 	}
 out:
 out:
 	tomoyo_put_group(group);
 	tomoyo_put_group(group);
@@ -137,3 +164,35 @@ bool tomoyo_number_matches_group(const unsigned long min,
 	}
 	}
 	return matched;
 	return matched;
 }
 }
+
+/**
+ * tomoyo_address_matches_group - Check whether the given address matches members of the given address group.
+ *
+ * @is_ipv6: True if @address is an IPv6 address.
+ * @address: An IPv4 or IPv6 address.
+ * @group:   Pointer to "struct tomoyo_address_group".
+ *
+ * Returns true if @address matches addresses in @group group, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_address_matches_group(const bool is_ipv6, const __be32 *address,
+				  const struct tomoyo_group *group)
+{
+	struct tomoyo_address_group *member;
+	bool matched = false;
+	const u8 size = is_ipv6 ? 16 : 4;
+
+	list_for_each_entry_rcu(member, &group->member_list, head.list) {
+		if (member->head.is_deleted)
+			continue;
+		if (member->address.is_ipv6 != is_ipv6)
+			continue;
+		if (memcmp(&member->address.ip[0], address, size) > 0 ||
+		    memcmp(address, &member->address.ip[1], size) > 0)
+			continue;
+		matched = true;
+		break;
+	}
+	return matched;
+}

+ 12 - 27
security/tomoyo/memory.c

@@ -27,8 +27,6 @@ void tomoyo_warn_oom(const char *function)
 		panic("MAC Initialization failed.\n");
 		panic("MAC Initialization failed.\n");
 }
 }
 
 
-/* Lock for protecting tomoyo_memory_used. */
-static DEFINE_SPINLOCK(tomoyo_policy_memory_lock);
 /* Memoy currently used by policy/audit log/query. */
 /* Memoy currently used by policy/audit log/query. */
 unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT];
 unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT];
 /* Memory quota for "policy"/"audit log"/"query". */
 /* Memory quota for "policy"/"audit log"/"query". */
@@ -42,22 +40,19 @@ unsigned int tomoyo_memory_quota[TOMOYO_MAX_MEMORY_STAT];
  * Returns true on success, false otherwise.
  * Returns true on success, false otherwise.
  *
  *
  * Returns true if @ptr is not NULL and quota not exceeded, false otherwise.
  * Returns true if @ptr is not NULL and quota not exceeded, false otherwise.
+ *
+ * Caller holds tomoyo_policy_lock mutex.
  */
  */
 bool tomoyo_memory_ok(void *ptr)
 bool tomoyo_memory_ok(void *ptr)
 {
 {
 	if (ptr) {
 	if (ptr) {
 		const size_t s = ksize(ptr);
 		const size_t s = ksize(ptr);
-		bool result;
-		spin_lock(&tomoyo_policy_memory_lock);
 		tomoyo_memory_used[TOMOYO_MEMORY_POLICY] += s;
 		tomoyo_memory_used[TOMOYO_MEMORY_POLICY] += s;
-		result = !tomoyo_memory_quota[TOMOYO_MEMORY_POLICY] ||
-			tomoyo_memory_used[TOMOYO_MEMORY_POLICY] <=
-			tomoyo_memory_quota[TOMOYO_MEMORY_POLICY];
-		if (!result)
-			tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s;
-		spin_unlock(&tomoyo_policy_memory_lock);
-		if (result)
+		if (!tomoyo_memory_quota[TOMOYO_MEMORY_POLICY] ||
+		    tomoyo_memory_used[TOMOYO_MEMORY_POLICY] <=
+		    tomoyo_memory_quota[TOMOYO_MEMORY_POLICY])
 			return true;
 			return true;
+		tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s;
 	}
 	}
 	tomoyo_warn_oom(__func__);
 	tomoyo_warn_oom(__func__);
 	return false;
 	return false;
@@ -71,6 +66,8 @@ bool tomoyo_memory_ok(void *ptr)
  *
  *
  * Returns pointer to allocated memory on success, NULL otherwise.
  * Returns pointer to allocated memory on success, NULL otherwise.
  * @data is zero-cleared on success.
  * @data is zero-cleared on success.
+ *
+ * Caller holds tomoyo_policy_lock mutex.
  */
  */
 void *tomoyo_commit_ok(void *data, const unsigned int size)
 void *tomoyo_commit_ok(void *data, const unsigned int size)
 {
 {
@@ -84,20 +81,6 @@ void *tomoyo_commit_ok(void *data, const unsigned int size)
 	return NULL;
 	return NULL;
 }
 }
 
 
-/**
- * tomoyo_memory_free - Free memory for elements.
- *
- * @ptr:  Pointer to allocated memory.
- */
-void tomoyo_memory_free(void *ptr)
-{
-	size_t s = ksize(ptr);
-	spin_lock(&tomoyo_policy_memory_lock);
-	tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s;
-	spin_unlock(&tomoyo_policy_memory_lock);
-	kfree(ptr);
-}
-
 /**
 /**
  * tomoyo_get_group - Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group".
  * tomoyo_get_group - Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group".
  *
  *
@@ -123,7 +106,8 @@ struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param,
 		goto out;
 		goto out;
 	list = &param->ns->group_list[idx];
 	list = &param->ns->group_list[idx];
 	list_for_each_entry(group, list, head.list) {
 	list_for_each_entry(group, list, head.list) {
-		if (e.group_name != group->group_name)
+		if (e.group_name != group->group_name ||
+		    atomic_read(&group->head.users) == TOMOYO_GC_IN_PROGRESS)
 			continue;
 			continue;
 		atomic_inc(&group->head.users);
 		atomic_inc(&group->head.users);
 		found = true;
 		found = true;
@@ -175,7 +159,8 @@ const struct tomoyo_path_info *tomoyo_get_name(const char *name)
 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
 	if (mutex_lock_interruptible(&tomoyo_policy_lock))
 		return NULL;
 		return NULL;
 	list_for_each_entry(ptr, head, head.list) {
 	list_for_each_entry(ptr, head, head.list) {
-		if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name))
+		if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name) ||
+		    atomic_read(&ptr->head.users) == TOMOYO_GC_IN_PROGRESS)
 			continue;
 			continue;
 		atomic_inc(&ptr->head.users);
 		atomic_inc(&ptr->head.users);
 		goto out;
 		goto out;

+ 771 - 0
security/tomoyo/network.c

@@ -0,0 +1,771 @@
+/*
+ * security/tomoyo/network.c
+ *
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
+ */
+
+#include "common.h"
+#include <linux/slab.h>
+
+/* Structure for holding inet domain socket's address. */
+struct tomoyo_inet_addr_info {
+	__be16 port;           /* In network byte order. */
+	const __be32 *address; /* In network byte order. */
+	bool is_ipv6;
+};
+
+/* Structure for holding unix domain socket's address. */
+struct tomoyo_unix_addr_info {
+	u8 *addr; /* This may not be '\0' terminated string. */
+	unsigned int addr_len;
+};
+
+/* Structure for holding socket address. */
+struct tomoyo_addr_info {
+	u8 protocol;
+	u8 operation;
+	struct tomoyo_inet_addr_info inet;
+	struct tomoyo_unix_addr_info unix0;
+};
+
+/* String table for socket's protocols. */
+const char * const tomoyo_proto_keyword[TOMOYO_SOCK_MAX] = {
+	[SOCK_STREAM]    = "stream",
+	[SOCK_DGRAM]     = "dgram",
+	[SOCK_RAW]       = "raw",
+	[SOCK_SEQPACKET] = "seqpacket",
+	[0] = " ", /* Dummy for avoiding NULL pointer dereference. */
+	[4] = " ", /* Dummy for avoiding NULL pointer dereference. */
+};
+
+/**
+ * tomoyo_parse_ipaddr_union - Parse an IP address.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ * @ptr:   Pointer to "struct tomoyo_ipaddr_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_parse_ipaddr_union(struct tomoyo_acl_param *param,
+			       struct tomoyo_ipaddr_union *ptr)
+{
+	u8 * const min = ptr->ip[0].in6_u.u6_addr8;
+	u8 * const max = ptr->ip[1].in6_u.u6_addr8;
+	char *address = tomoyo_read_token(param);
+	const char *end;
+
+	if (!strchr(address, ':') &&
+	    in4_pton(address, -1, min, '-', &end) > 0) {
+		ptr->is_ipv6 = false;
+		if (!*end)
+			ptr->ip[1].s6_addr32[0] = ptr->ip[0].s6_addr32[0];
+		else if (*end++ != '-' ||
+			 in4_pton(end, -1, max, '\0', &end) <= 0 || *end)
+			return false;
+		return true;
+	}
+	if (in6_pton(address, -1, min, '-', &end) > 0) {
+		ptr->is_ipv6 = true;
+		if (!*end)
+			memmove(max, min, sizeof(u16) * 8);
+		else if (*end++ != '-' ||
+			 in6_pton(end, -1, max, '\0', &end) <= 0 || *end)
+			return false;
+		return true;
+	}
+	return false;
+}
+
+/**
+ * tomoyo_print_ipv4 - Print an IPv4 address.
+ *
+ * @buffer:     Buffer to write to.
+ * @buffer_len: Size of @buffer.
+ * @min_ip:     Pointer to __be32.
+ * @max_ip:     Pointer to __be32.
+ *
+ * Returns nothing.
+ */
+static void tomoyo_print_ipv4(char *buffer, const unsigned int buffer_len,
+			      const __be32 *min_ip, const __be32 *max_ip)
+{
+	snprintf(buffer, buffer_len, "%pI4%c%pI4", min_ip,
+		 *min_ip == *max_ip ? '\0' : '-', max_ip);
+}
+
+/**
+ * tomoyo_print_ipv6 - Print an IPv6 address.
+ *
+ * @buffer:     Buffer to write to.
+ * @buffer_len: Size of @buffer.
+ * @min_ip:     Pointer to "struct in6_addr".
+ * @max_ip:     Pointer to "struct in6_addr".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_print_ipv6(char *buffer, const unsigned int buffer_len,
+			      const struct in6_addr *min_ip,
+			      const struct in6_addr *max_ip)
+{
+	snprintf(buffer, buffer_len, "%pI6c%c%pI6c", min_ip,
+		 !memcmp(min_ip, max_ip, 16) ? '\0' : '-', max_ip);
+}
+
+/**
+ * tomoyo_print_ip - Print an IP address.
+ *
+ * @buf:  Buffer to write to.
+ * @size: Size of @buf.
+ * @ptr:  Pointer to "struct ipaddr_union".
+ *
+ * Returns nothing.
+ */
+void tomoyo_print_ip(char *buf, const unsigned int size,
+		     const struct tomoyo_ipaddr_union *ptr)
+{
+	if (ptr->is_ipv6)
+		tomoyo_print_ipv6(buf, size, &ptr->ip[0], &ptr->ip[1]);
+	else
+		tomoyo_print_ipv4(buf, size, &ptr->ip[0].s6_addr32[0],
+				  &ptr->ip[1].s6_addr32[0]);
+}
+
+/*
+ * Mapping table from "enum tomoyo_network_acl_index" to
+ * "enum tomoyo_mac_index" for inet domain socket.
+ */
+static const u8 tomoyo_inet2mac
+[TOMOYO_SOCK_MAX][TOMOYO_MAX_NETWORK_OPERATION] = {
+	[SOCK_STREAM] = {
+		[TOMOYO_NETWORK_BIND]    = TOMOYO_MAC_NETWORK_INET_STREAM_BIND,
+		[TOMOYO_NETWORK_LISTEN]  =
+		TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN,
+		[TOMOYO_NETWORK_CONNECT] =
+		TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT,
+	},
+	[SOCK_DGRAM] = {
+		[TOMOYO_NETWORK_BIND]    = TOMOYO_MAC_NETWORK_INET_DGRAM_BIND,
+		[TOMOYO_NETWORK_SEND]    = TOMOYO_MAC_NETWORK_INET_DGRAM_SEND,
+	},
+	[SOCK_RAW]    = {
+		[TOMOYO_NETWORK_BIND]    = TOMOYO_MAC_NETWORK_INET_RAW_BIND,
+		[TOMOYO_NETWORK_SEND]    = TOMOYO_MAC_NETWORK_INET_RAW_SEND,
+	},
+};
+
+/*
+ * Mapping table from "enum tomoyo_network_acl_index" to
+ * "enum tomoyo_mac_index" for unix domain socket.
+ */
+static const u8 tomoyo_unix2mac
+[TOMOYO_SOCK_MAX][TOMOYO_MAX_NETWORK_OPERATION] = {
+	[SOCK_STREAM] = {
+		[TOMOYO_NETWORK_BIND]    = TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND,
+		[TOMOYO_NETWORK_LISTEN]  =
+		TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN,
+		[TOMOYO_NETWORK_CONNECT] =
+		TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT,
+	},
+	[SOCK_DGRAM] = {
+		[TOMOYO_NETWORK_BIND]    = TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND,
+		[TOMOYO_NETWORK_SEND]    = TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND,
+	},
+	[SOCK_SEQPACKET] = {
+		[TOMOYO_NETWORK_BIND]    =
+		TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND,
+		[TOMOYO_NETWORK_LISTEN]  =
+		TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN,
+		[TOMOYO_NETWORK_CONNECT] =
+		TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT,
+	},
+};
+
+/**
+ * tomoyo_same_inet_acl - Check for duplicated "struct tomoyo_inet_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
+static bool tomoyo_same_inet_acl(const struct tomoyo_acl_info *a,
+				 const struct tomoyo_acl_info *b)
+{
+	const struct tomoyo_inet_acl *p1 = container_of(a, typeof(*p1), head);
+	const struct tomoyo_inet_acl *p2 = container_of(b, typeof(*p2), head);
+
+	return p1->protocol == p2->protocol &&
+		tomoyo_same_ipaddr_union(&p1->address, &p2->address) &&
+		tomoyo_same_number_union(&p1->port, &p2->port);
+}
+
+/**
+ * tomoyo_same_unix_acl - Check for duplicated "struct tomoyo_unix_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
+static bool tomoyo_same_unix_acl(const struct tomoyo_acl_info *a,
+				 const struct tomoyo_acl_info *b)
+{
+	const struct tomoyo_unix_acl *p1 = container_of(a, typeof(*p1), head);
+	const struct tomoyo_unix_acl *p2 = container_of(b, typeof(*p2), head);
+
+	return p1->protocol == p2->protocol &&
+		tomoyo_same_name_union(&p1->name, &p2->name);
+}
+
+/**
+ * tomoyo_merge_inet_acl - Merge duplicated "struct tomoyo_inet_acl" entry.
+ *
+ * @a:         Pointer to "struct tomoyo_acl_info".
+ * @b:         Pointer to "struct tomoyo_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
+static bool tomoyo_merge_inet_acl(struct tomoyo_acl_info *a,
+				  struct tomoyo_acl_info *b,
+				  const bool is_delete)
+{
+	u8 * const a_perm =
+		&container_of(a, struct tomoyo_inet_acl, head)->perm;
+	u8 perm = *a_perm;
+	const u8 b_perm = container_of(b, struct tomoyo_inet_acl, head)->perm;
+
+	if (is_delete)
+		perm &= ~b_perm;
+	else
+		perm |= b_perm;
+	*a_perm = perm;
+	return !perm;
+}
+
+/**
+ * tomoyo_merge_unix_acl - Merge duplicated "struct tomoyo_unix_acl" entry.
+ *
+ * @a:         Pointer to "struct tomoyo_acl_info".
+ * @b:         Pointer to "struct tomoyo_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
+static bool tomoyo_merge_unix_acl(struct tomoyo_acl_info *a,
+				  struct tomoyo_acl_info *b,
+				  const bool is_delete)
+{
+	u8 * const a_perm =
+		&container_of(a, struct tomoyo_unix_acl, head)->perm;
+	u8 perm = *a_perm;
+	const u8 b_perm = container_of(b, struct tomoyo_unix_acl, head)->perm;
+
+	if (is_delete)
+		perm &= ~b_perm;
+	else
+		perm |= b_perm;
+	*a_perm = perm;
+	return !perm;
+}
+
+/**
+ * tomoyo_write_inet_network - Write "struct tomoyo_inet_acl" list.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_write_inet_network(struct tomoyo_acl_param *param)
+{
+	struct tomoyo_inet_acl e = { .head.type = TOMOYO_TYPE_INET_ACL };
+	int error = -EINVAL;
+	u8 type;
+	const char *protocol = tomoyo_read_token(param);
+	const char *operation = tomoyo_read_token(param);
+
+	for (e.protocol = 0; e.protocol < TOMOYO_SOCK_MAX; e.protocol++)
+		if (!strcmp(protocol, tomoyo_proto_keyword[e.protocol]))
+			break;
+	for (type = 0; type < TOMOYO_MAX_NETWORK_OPERATION; type++)
+		if (tomoyo_permstr(operation, tomoyo_socket_keyword[type]))
+			e.perm |= 1 << type;
+	if (e.protocol == TOMOYO_SOCK_MAX || !e.perm)
+		return -EINVAL;
+	if (param->data[0] == '@') {
+		param->data++;
+		e.address.group =
+			tomoyo_get_group(param, TOMOYO_ADDRESS_GROUP);
+		if (!e.address.group)
+			return -ENOMEM;
+	} else {
+		if (!tomoyo_parse_ipaddr_union(param, &e.address))
+			goto out;
+	}
+	if (!tomoyo_parse_number_union(param, &e.port) ||
+	    e.port.values[1] > 65535)
+		goto out;
+	error = tomoyo_update_domain(&e.head, sizeof(e), param,
+				     tomoyo_same_inet_acl,
+				     tomoyo_merge_inet_acl);
+out:
+	tomoyo_put_group(e.address.group);
+	tomoyo_put_number_union(&e.port);
+	return error;
+}
+
+/**
+ * tomoyo_write_unix_network - Write "struct tomoyo_unix_acl" list.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_unix_network(struct tomoyo_acl_param *param)
+{
+	struct tomoyo_unix_acl e = { .head.type = TOMOYO_TYPE_UNIX_ACL };
+	int error;
+	u8 type;
+	const char *protocol = tomoyo_read_token(param);
+	const char *operation = tomoyo_read_token(param);
+
+	for (e.protocol = 0; e.protocol < TOMOYO_SOCK_MAX; e.protocol++)
+		if (!strcmp(protocol, tomoyo_proto_keyword[e.protocol]))
+			break;
+	for (type = 0; type < TOMOYO_MAX_NETWORK_OPERATION; type++)
+		if (tomoyo_permstr(operation, tomoyo_socket_keyword[type]))
+			e.perm |= 1 << type;
+	if (e.protocol == TOMOYO_SOCK_MAX || !e.perm)
+		return -EINVAL;
+	if (!tomoyo_parse_name_union(param, &e.name))
+		return -EINVAL;
+	error = tomoyo_update_domain(&e.head, sizeof(e), param,
+				     tomoyo_same_unix_acl,
+				     tomoyo_merge_unix_acl);
+	tomoyo_put_name_union(&e.name);
+	return error;
+}
+
+/**
+ * tomoyo_audit_net_log - Audit network log.
+ *
+ * @r:         Pointer to "struct tomoyo_request_info".
+ * @family:    Name of socket family ("inet" or "unix").
+ * @protocol:  Name of protocol in @family.
+ * @operation: Name of socket operation.
+ * @address:   Name of address.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_net_log(struct tomoyo_request_info *r,
+				const char *family, const u8 protocol,
+				const u8 operation, const char *address)
+{
+	return tomoyo_supervisor(r, "network %s %s %s %s\n", family,
+				 tomoyo_proto_keyword[protocol],
+				 tomoyo_socket_keyword[operation], address);
+}
+
+/**
+ * tomoyo_audit_inet_log - Audit INET network log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_inet_log(struct tomoyo_request_info *r)
+{
+	char buf[128];
+	int len;
+	const __be32 *address = r->param.inet_network.address;
+
+	if (r->param.inet_network.is_ipv6)
+		tomoyo_print_ipv6(buf, sizeof(buf), (const struct in6_addr *)
+				  address, (const struct in6_addr *) address);
+	else
+		tomoyo_print_ipv4(buf, sizeof(buf), address, address);
+	len = strlen(buf);
+	snprintf(buf + len, sizeof(buf) - len, " %u",
+		 r->param.inet_network.port);
+	return tomoyo_audit_net_log(r, "inet", r->param.inet_network.protocol,
+				    r->param.inet_network.operation, buf);
+}
+
+/**
+ * tomoyo_audit_unix_log - Audit UNIX network log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_unix_log(struct tomoyo_request_info *r)
+{
+	return tomoyo_audit_net_log(r, "unix", r->param.unix_network.protocol,
+				    r->param.unix_network.operation,
+				    r->param.unix_network.address->name);
+}
+
+/**
+ * tomoyo_check_inet_acl - Check permission for inet domain socket operation.
+ *
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if granted, false otherwise.
+ */
+static bool tomoyo_check_inet_acl(struct tomoyo_request_info *r,
+				  const struct tomoyo_acl_info *ptr)
+{
+	const struct tomoyo_inet_acl *acl =
+		container_of(ptr, typeof(*acl), head);
+	const u8 size = r->param.inet_network.is_ipv6 ? 16 : 4;
+
+	if (!(acl->perm & (1 << r->param.inet_network.operation)) ||
+	    !tomoyo_compare_number_union(r->param.inet_network.port,
+					 &acl->port))
+		return false;
+	if (acl->address.group)
+		return tomoyo_address_matches_group
+			(r->param.inet_network.is_ipv6,
+			 r->param.inet_network.address, acl->address.group);
+	return acl->address.is_ipv6 == r->param.inet_network.is_ipv6 &&
+		memcmp(&acl->address.ip[0],
+		       r->param.inet_network.address, size) <= 0 &&
+		memcmp(r->param.inet_network.address,
+		       &acl->address.ip[1], size) <= 0;
+}
+
+/**
+ * tomoyo_check_unix_acl - Check permission for unix domain socket operation.
+ *
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if granted, false otherwise.
+ */
+static bool tomoyo_check_unix_acl(struct tomoyo_request_info *r,
+				  const struct tomoyo_acl_info *ptr)
+{
+	const struct tomoyo_unix_acl *acl =
+		container_of(ptr, typeof(*acl), head);
+
+	return (acl->perm & (1 << r->param.unix_network.operation)) &&
+		tomoyo_compare_name_union(r->param.unix_network.address,
+					  &acl->name);
+}
+
+/**
+ * tomoyo_inet_entry - Check permission for INET network operation.
+ *
+ * @address: Pointer to "struct tomoyo_addr_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_inet_entry(const struct tomoyo_addr_info *address)
+{
+	const int idx = tomoyo_read_lock();
+	struct tomoyo_request_info r;
+	int error = 0;
+	const u8 type = tomoyo_inet2mac[address->protocol][address->operation];
+
+	if (type && tomoyo_init_request_info(&r, NULL, type)
+	    != TOMOYO_CONFIG_DISABLED) {
+		r.param_type = TOMOYO_TYPE_INET_ACL;
+		r.param.inet_network.protocol = address->protocol;
+		r.param.inet_network.operation = address->operation;
+		r.param.inet_network.is_ipv6 = address->inet.is_ipv6;
+		r.param.inet_network.address = address->inet.address;
+		r.param.inet_network.port = ntohs(address->inet.port);
+		do {
+			tomoyo_check_acl(&r, tomoyo_check_inet_acl);
+			error = tomoyo_audit_inet_log(&r);
+		} while (error == TOMOYO_RETRY_REQUEST);
+	}
+	tomoyo_read_unlock(idx);
+	return error;
+}
+
+/**
+ * tomoyo_check_inet_address - Check permission for inet domain socket's operation.
+ *
+ * @addr:     Pointer to "struct sockaddr".
+ * @addr_len: Size of @addr.
+ * @port:     Port number.
+ * @address:  Pointer to "struct tomoyo_addr_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_check_inet_address(const struct sockaddr *addr,
+				     const unsigned int addr_len,
+				     const u16 port,
+				     struct tomoyo_addr_info *address)
+{
+	struct tomoyo_inet_addr_info *i = &address->inet;
+
+	switch (addr->sa_family) {
+	case AF_INET6:
+		if (addr_len < SIN6_LEN_RFC2133)
+			goto skip;
+		i->is_ipv6 = true;
+		i->address = (__be32 *)
+			((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr;
+		i->port = ((struct sockaddr_in6 *) addr)->sin6_port;
+		break;
+	case AF_INET:
+		if (addr_len < sizeof(struct sockaddr_in))
+			goto skip;
+		i->is_ipv6 = false;
+		i->address = (__be32 *)
+			&((struct sockaddr_in *) addr)->sin_addr;
+		i->port = ((struct sockaddr_in *) addr)->sin_port;
+		break;
+	default:
+		goto skip;
+	}
+	if (address->protocol == SOCK_RAW)
+		i->port = htons(port);
+	return tomoyo_inet_entry(address);
+skip:
+	return 0;
+}
+
+/**
+ * tomoyo_unix_entry - Check permission for UNIX network operation.
+ *
+ * @address: Pointer to "struct tomoyo_addr_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_unix_entry(const struct tomoyo_addr_info *address)
+{
+	const int idx = tomoyo_read_lock();
+	struct tomoyo_request_info r;
+	int error = 0;
+	const u8 type = tomoyo_unix2mac[address->protocol][address->operation];
+
+	if (type && tomoyo_init_request_info(&r, NULL, type)
+	    != TOMOYO_CONFIG_DISABLED) {
+		char *buf = address->unix0.addr;
+		int len = address->unix0.addr_len - sizeof(sa_family_t);
+
+		if (len <= 0) {
+			buf = "anonymous";
+			len = 9;
+		} else if (buf[0]) {
+			len = strnlen(buf, len);
+		}
+		buf = tomoyo_encode2(buf, len);
+		if (buf) {
+			struct tomoyo_path_info addr;
+
+			addr.name = buf;
+			tomoyo_fill_path_info(&addr);
+			r.param_type = TOMOYO_TYPE_UNIX_ACL;
+			r.param.unix_network.protocol = address->protocol;
+			r.param.unix_network.operation = address->operation;
+			r.param.unix_network.address = &addr;
+			do {
+				tomoyo_check_acl(&r, tomoyo_check_unix_acl);
+				error = tomoyo_audit_unix_log(&r);
+			} while (error == TOMOYO_RETRY_REQUEST);
+			kfree(buf);
+		} else
+			error = -ENOMEM;
+	}
+	tomoyo_read_unlock(idx);
+	return error;
+}
+
+/**
+ * tomoyo_check_unix_address - Check permission for unix domain socket's operation.
+ *
+ * @addr:     Pointer to "struct sockaddr".
+ * @addr_len: Size of @addr.
+ * @address:  Pointer to "struct tomoyo_addr_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_check_unix_address(struct sockaddr *addr,
+				     const unsigned int addr_len,
+				     struct tomoyo_addr_info *address)
+{
+	struct tomoyo_unix_addr_info *u = &address->unix0;
+
+	if (addr->sa_family != AF_UNIX)
+		return 0;
+	u->addr = ((struct sockaddr_un *) addr)->sun_path;
+	u->addr_len = addr_len;
+	return tomoyo_unix_entry(address);
+}
+
+/**
+ * tomoyo_kernel_service - Check whether I'm kernel service or not.
+ *
+ * Returns true if I'm kernel service, false otherwise.
+ */
+static bool tomoyo_kernel_service(void)
+{
+	/* Nothing to do if I am a kernel service. */
+	return segment_eq(get_fs(), KERNEL_DS);
+}
+
+/**
+ * tomoyo_sock_family - Get socket's family.
+ *
+ * @sk: Pointer to "struct sock".
+ *
+ * Returns one of PF_INET, PF_INET6, PF_UNIX or 0.
+ */
+static u8 tomoyo_sock_family(struct sock *sk)
+{
+	u8 family;
+
+	if (tomoyo_kernel_service())
+		return 0;
+	family = sk->sk_family;
+	switch (family) {
+	case PF_INET:
+	case PF_INET6:
+	case PF_UNIX:
+		return family;
+	default:
+		return 0;
+	}
+}
+
+/**
+ * tomoyo_socket_listen_permission - Check permission for listening a socket.
+ *
+ * @sock: Pointer to "struct socket".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_socket_listen_permission(struct socket *sock)
+{
+	struct tomoyo_addr_info address;
+	const u8 family = tomoyo_sock_family(sock->sk);
+	const unsigned int type = sock->type;
+	struct sockaddr_storage addr;
+	int addr_len;
+
+	if (!family || (type != SOCK_STREAM && type != SOCK_SEQPACKET))
+		return 0;
+	{
+		const int error = sock->ops->getname(sock, (struct sockaddr *)
+						     &addr, &addr_len, 0);
+
+		if (error)
+			return error;
+	}
+	address.protocol = type;
+	address.operation = TOMOYO_NETWORK_LISTEN;
+	if (family == PF_UNIX)
+		return tomoyo_check_unix_address((struct sockaddr *) &addr,
+						 addr_len, &address);
+	return tomoyo_check_inet_address((struct sockaddr *) &addr, addr_len,
+					 0, &address);
+}
+
+/**
+ * tomoyo_socket_connect_permission - Check permission for setting the remote address of a socket.
+ *
+ * @sock:     Pointer to "struct socket".
+ * @addr:     Pointer to "struct sockaddr".
+ * @addr_len: Size of @addr.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_socket_connect_permission(struct socket *sock,
+				     struct sockaddr *addr, int addr_len)
+{
+	struct tomoyo_addr_info address;
+	const u8 family = tomoyo_sock_family(sock->sk);
+	const unsigned int type = sock->type;
+
+	if (!family)
+		return 0;
+	address.protocol = type;
+	switch (type) {
+	case SOCK_DGRAM:
+	case SOCK_RAW:
+		address.operation = TOMOYO_NETWORK_SEND;
+		break;
+	case SOCK_STREAM:
+	case SOCK_SEQPACKET:
+		address.operation = TOMOYO_NETWORK_CONNECT;
+		break;
+	default:
+		return 0;
+	}
+	if (family == PF_UNIX)
+		return tomoyo_check_unix_address(addr, addr_len, &address);
+	return tomoyo_check_inet_address(addr, addr_len, sock->sk->sk_protocol,
+					 &address);
+}
+
+/**
+ * tomoyo_socket_bind_permission - Check permission for setting the local address of a socket.
+ *
+ * @sock:     Pointer to "struct socket".
+ * @addr:     Pointer to "struct sockaddr".
+ * @addr_len: Size of @addr.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_socket_bind_permission(struct socket *sock, struct sockaddr *addr,
+				  int addr_len)
+{
+	struct tomoyo_addr_info address;
+	const u8 family = tomoyo_sock_family(sock->sk);
+	const unsigned int type = sock->type;
+
+	if (!family)
+		return 0;
+	switch (type) {
+	case SOCK_STREAM:
+	case SOCK_DGRAM:
+	case SOCK_RAW:
+	case SOCK_SEQPACKET:
+		address.protocol = type;
+		address.operation = TOMOYO_NETWORK_BIND;
+		break;
+	default:
+		return 0;
+	}
+	if (family == PF_UNIX)
+		return tomoyo_check_unix_address(addr, addr_len, &address);
+	return tomoyo_check_inet_address(addr, addr_len, sock->sk->sk_protocol,
+					 &address);
+}
+
+/**
+ * tomoyo_socket_sendmsg_permission - Check permission for sending a datagram.
+ *
+ * @sock: Pointer to "struct socket".
+ * @msg:  Pointer to "struct msghdr".
+ * @size: Unused.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_socket_sendmsg_permission(struct socket *sock, struct msghdr *msg,
+				     int size)
+{
+	struct tomoyo_addr_info address;
+	const u8 family = tomoyo_sock_family(sock->sk);
+	const unsigned int type = sock->type;
+
+	if (!msg->msg_name || !family ||
+	    (type != SOCK_DGRAM && type != SOCK_RAW))
+		return 0;
+	address.protocol = type;
+	address.operation = TOMOYO_NETWORK_SEND;
+	if (family == PF_UNIX)
+		return tomoyo_check_unix_address((struct sockaddr *)
+						 msg->msg_name,
+						 msg->msg_namelen, &address);
+	return tomoyo_check_inet_address((struct sockaddr *) msg->msg_name,
+					 msg->msg_namelen,
+					 sock->sk->sk_protocol, &address);
+}

+ 25 - 7
security/tomoyo/realpath.c

@@ -15,17 +15,19 @@
 #include "../../fs/internal.h"
 #include "../../fs/internal.h"
 
 
 /**
 /**
- * tomoyo_encode: Convert binary string to ascii string.
+ * tomoyo_encode2 - Encode binary string to ascii string.
  *
  *
- * @str: String in binary format.
+ * @str:     String in binary format.
+ * @str_len: Size of @str in byte.
  *
  *
  * Returns pointer to @str in ascii format on success, NULL otherwise.
  * Returns pointer to @str in ascii format on success, NULL otherwise.
  *
  *
  * This function uses kzalloc(), so caller must kfree() if this function
  * This function uses kzalloc(), so caller must kfree() if this function
  * didn't return NULL.
  * didn't return NULL.
  */
  */
-char *tomoyo_encode(const char *str)
+char *tomoyo_encode2(const char *str, int str_len)
 {
 {
+	int i;
 	int len = 0;
 	int len = 0;
 	const char *p = str;
 	const char *p = str;
 	char *cp;
 	char *cp;
@@ -33,8 +35,9 @@ char *tomoyo_encode(const char *str)
 
 
 	if (!p)
 	if (!p)
 		return NULL;
 		return NULL;
-	while (*p) {
-		const unsigned char c = *p++;
+	for (i = 0; i < str_len; i++) {
+		const unsigned char c = p[i];
+
 		if (c == '\\')
 		if (c == '\\')
 			len += 2;
 			len += 2;
 		else if (c > ' ' && c < 127)
 		else if (c > ' ' && c < 127)
@@ -49,8 +52,8 @@ char *tomoyo_encode(const char *str)
 		return NULL;
 		return NULL;
 	cp0 = cp;
 	cp0 = cp;
 	p = str;
 	p = str;
-	while (*p) {
-		const unsigned char c = *p++;
+	for (i = 0; i < str_len; i++) {
+		const unsigned char c = p[i];
 
 
 		if (c == '\\') {
 		if (c == '\\') {
 			*cp++ = '\\';
 			*cp++ = '\\';
@@ -67,6 +70,21 @@ char *tomoyo_encode(const char *str)
 	return cp0;
 	return cp0;
 }
 }
 
 
+/**
+ * tomoyo_encode - Encode binary string to ascii string.
+ *
+ * @str: String in binary format.
+ *
+ * Returns pointer to @str in ascii format on success, NULL otherwise.
+ *
+ * This function uses kzalloc(), so caller must kfree() if this function
+ * didn't return NULL.
+ */
+char *tomoyo_encode(const char *str)
+{
+	return str ? tomoyo_encode2(str, strlen(str)) : NULL;
+}
+
 /**
 /**
  * tomoyo_get_absolute_path - Get the path of a dentry but ignores chroot'ed root.
  * tomoyo_get_absolute_path - Get the path of a dentry but ignores chroot'ed root.
  *
  *

+ 121 - 2
security/tomoyo/securityfs_if.c

@@ -7,6 +7,124 @@
 #include <linux/security.h>
 #include <linux/security.h>
 #include "common.h"
 #include "common.h"
 
 
+/**
+ * tomoyo_check_task_acl - Check permission for task operation.
+ *
+ * @r:   Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if granted, false otherwise.
+ */
+static bool tomoyo_check_task_acl(struct tomoyo_request_info *r,
+				  const struct tomoyo_acl_info *ptr)
+{
+	const struct tomoyo_task_acl *acl = container_of(ptr, typeof(*acl),
+							 head);
+	return !tomoyo_pathcmp(r->param.task.domainname, acl->domainname);
+}
+
+/**
+ * tomoyo_write_self - write() for /sys/kernel/security/tomoyo/self_domain interface.
+ *
+ * @file:  Pointer to "struct file".
+ * @buf:   Domainname to transit to.
+ * @count: Size of @buf.
+ * @ppos:  Unused.
+ *
+ * Returns @count on success, negative value otherwise.
+ *
+ * If domain transition was permitted but the domain transition failed, this
+ * function returns error rather than terminating current thread with SIGKILL.
+ */
+static ssize_t tomoyo_write_self(struct file *file, const char __user *buf,
+			      size_t count, loff_t *ppos)
+{
+	char *data;
+	int error;
+	if (!count || count >= TOMOYO_EXEC_TMPSIZE - 10)
+		return -ENOMEM;
+	data = kzalloc(count + 1, GFP_NOFS);
+	if (!data)
+		return -ENOMEM;
+	if (copy_from_user(data, buf, count)) {
+		error = -EFAULT;
+		goto out;
+	}
+	tomoyo_normalize_line(data);
+	if (tomoyo_correct_domain(data)) {
+		const int idx = tomoyo_read_lock();
+		struct tomoyo_path_info name;
+		struct tomoyo_request_info r;
+		name.name = data;
+		tomoyo_fill_path_info(&name);
+		/* Check "task manual_domain_transition" permission. */
+		tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE);
+		r.param_type = TOMOYO_TYPE_MANUAL_TASK_ACL;
+		r.param.task.domainname = &name;
+		tomoyo_check_acl(&r, tomoyo_check_task_acl);
+		if (!r.granted)
+			error = -EPERM;
+		else {
+			struct tomoyo_domain_info *new_domain =
+				tomoyo_assign_domain(data, true);
+			if (!new_domain) {
+				error = -ENOENT;
+			} else {
+				struct cred *cred = prepare_creds();
+				if (!cred) {
+					error = -ENOMEM;
+				} else {
+					struct tomoyo_domain_info *old_domain =
+						cred->security;
+					cred->security = new_domain;
+					atomic_inc(&new_domain->users);
+					atomic_dec(&old_domain->users);
+					commit_creds(cred);
+					error = 0;
+				}
+			}
+		}
+		tomoyo_read_unlock(idx);
+	} else
+		error = -EINVAL;
+out:
+	kfree(data);
+	return error ? error : count;
+}
+
+/**
+ * tomoyo_read_self - read() for /sys/kernel/security/tomoyo/self_domain interface.
+ *
+ * @file:  Pointer to "struct file".
+ * @buf:   Domainname which current thread belongs to.
+ * @count: Size of @buf.
+ * @ppos:  Bytes read by now.
+ *
+ * Returns read size on success, negative value otherwise.
+ */
+static ssize_t tomoyo_read_self(struct file *file, char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	const char *domain = tomoyo_domain()->domainname->name;
+	loff_t len = strlen(domain);
+	loff_t pos = *ppos;
+	if (pos >= len || !count)
+		return 0;
+	len -= pos;
+	if (count < len)
+		len = count;
+	if (copy_to_user(buf, domain + pos, len))
+		return -EFAULT;
+	*ppos += len;
+	return len;
+}
+
+/* Operations for /sys/kernel/security/tomoyo/self_domain interface. */
+static const struct file_operations tomoyo_self_operations = {
+	.write = tomoyo_write_self,
+	.read  = tomoyo_read_self,
+};
+
 /**
 /**
  * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface.
  * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface.
  *
  *
@@ -135,8 +253,6 @@ static int __init tomoyo_initerface_init(void)
 			    TOMOYO_EXCEPTIONPOLICY);
 			    TOMOYO_EXCEPTIONPOLICY);
 	tomoyo_create_entry("audit",            0400, tomoyo_dir,
 	tomoyo_create_entry("audit",            0400, tomoyo_dir,
 			    TOMOYO_AUDIT);
 			    TOMOYO_AUDIT);
-	tomoyo_create_entry("self_domain",      0400, tomoyo_dir,
-			    TOMOYO_SELFDOMAIN);
 	tomoyo_create_entry(".process_status",  0600, tomoyo_dir,
 	tomoyo_create_entry(".process_status",  0600, tomoyo_dir,
 			    TOMOYO_PROCESS_STATUS);
 			    TOMOYO_PROCESS_STATUS);
 	tomoyo_create_entry("stat",             0644, tomoyo_dir,
 	tomoyo_create_entry("stat",             0644, tomoyo_dir,
@@ -147,6 +263,9 @@ static int __init tomoyo_initerface_init(void)
 			    TOMOYO_MANAGER);
 			    TOMOYO_MANAGER);
 	tomoyo_create_entry("version",          0400, tomoyo_dir,
 	tomoyo_create_entry("version",          0400, tomoyo_dir,
 			    TOMOYO_VERSION);
 			    TOMOYO_VERSION);
+	securityfs_create_file("self_domain", 0666, tomoyo_dir, NULL,
+			       &tomoyo_self_operations);
+	tomoyo_load_builtin_policy();
 	return 0;
 	return 0;
 }
 }
 
 

+ 62 - 0
security/tomoyo/tomoyo.c

@@ -442,6 +442,64 @@ static int tomoyo_sb_pivotroot(struct path *old_path, struct path *new_path)
 	return tomoyo_path2_perm(TOMOYO_TYPE_PIVOT_ROOT, new_path, old_path);
 	return tomoyo_path2_perm(TOMOYO_TYPE_PIVOT_ROOT, new_path, old_path);
 }
 }
 
 
+/**
+ * tomoyo_socket_listen - Check permission for listen().
+ *
+ * @sock:    Pointer to "struct socket".
+ * @backlog: Backlog parameter.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_socket_listen(struct socket *sock, int backlog)
+{
+	return tomoyo_socket_listen_permission(sock);
+}
+
+/**
+ * tomoyo_socket_connect - Check permission for connect().
+ *
+ * @sock:     Pointer to "struct socket".
+ * @addr:     Pointer to "struct sockaddr".
+ * @addr_len: Size of @addr.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_socket_connect(struct socket *sock, struct sockaddr *addr,
+				 int addr_len)
+{
+	return tomoyo_socket_connect_permission(sock, addr, addr_len);
+}
+
+/**
+ * tomoyo_socket_bind - Check permission for bind().
+ *
+ * @sock:     Pointer to "struct socket".
+ * @addr:     Pointer to "struct sockaddr".
+ * @addr_len: Size of @addr.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_socket_bind(struct socket *sock, struct sockaddr *addr,
+			      int addr_len)
+{
+	return tomoyo_socket_bind_permission(sock, addr, addr_len);
+}
+
+/**
+ * tomoyo_socket_sendmsg - Check permission for sendmsg().
+ *
+ * @sock: Pointer to "struct socket".
+ * @msg:  Pointer to "struct msghdr".
+ * @size: Size of message.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_socket_sendmsg(struct socket *sock, struct msghdr *msg,
+				 int size)
+{
+	return tomoyo_socket_sendmsg_permission(sock, msg, size);
+}
+
 /*
 /*
  * tomoyo_security_ops is a "struct security_operations" which is used for
  * tomoyo_security_ops is a "struct security_operations" which is used for
  * registering TOMOYO.
  * registering TOMOYO.
@@ -472,6 +530,10 @@ static struct security_operations tomoyo_security_ops = {
 	.sb_mount            = tomoyo_sb_mount,
 	.sb_mount            = tomoyo_sb_mount,
 	.sb_umount           = tomoyo_sb_umount,
 	.sb_umount           = tomoyo_sb_umount,
 	.sb_pivotroot        = tomoyo_sb_pivotroot,
 	.sb_pivotroot        = tomoyo_sb_pivotroot,
+	.socket_bind         = tomoyo_socket_bind,
+	.socket_connect      = tomoyo_socket_connect,
+	.socket_listen       = tomoyo_socket_listen,
+	.socket_sendmsg      = tomoyo_socket_sendmsg,
 };
 };
 
 
 /* Lock for GC. */
 /* Lock for GC. */

+ 76 - 4
security/tomoyo/util.c

@@ -42,6 +42,39 @@ const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX] = {
 	[TOMOYO_MAC_FILE_MOUNT]      = TOMOYO_MAC_CATEGORY_FILE,
 	[TOMOYO_MAC_FILE_MOUNT]      = TOMOYO_MAC_CATEGORY_FILE,
 	[TOMOYO_MAC_FILE_UMOUNT]     = TOMOYO_MAC_CATEGORY_FILE,
 	[TOMOYO_MAC_FILE_UMOUNT]     = TOMOYO_MAC_CATEGORY_FILE,
 	[TOMOYO_MAC_FILE_PIVOT_ROOT] = TOMOYO_MAC_CATEGORY_FILE,
 	[TOMOYO_MAC_FILE_PIVOT_ROOT] = TOMOYO_MAC_CATEGORY_FILE,
+	/* CONFIG::network group */
+	[TOMOYO_MAC_NETWORK_INET_STREAM_BIND]       =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN]     =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT]    =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_INET_DGRAM_BIND]        =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_INET_DGRAM_SEND]        =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_INET_RAW_BIND]          =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_INET_RAW_SEND]          =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND]       =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN]     =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT]    =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND]        =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND]        =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND]    =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN]  =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] =
+	TOMOYO_MAC_CATEGORY_NETWORK,
+	/* CONFIG::misc group */
+	[TOMOYO_MAC_ENVIRON]         = TOMOYO_MAC_CATEGORY_MISC,
 };
 };
 
 
 /**
 /**
@@ -125,6 +158,31 @@ char *tomoyo_read_token(struct tomoyo_acl_param *param)
 	return pos;
 	return pos;
 }
 }
 
 
+/**
+ * tomoyo_get_domainname - Read a domainname from a line.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns a domainname on success, NULL otherwise.
+ */
+const struct tomoyo_path_info *tomoyo_get_domainname
+(struct tomoyo_acl_param *param)
+{
+	char *start = param->data;
+	char *pos = start;
+	while (*pos) {
+		if (*pos++ != ' ' || *pos++ == '/')
+			continue;
+		pos -= 2;
+		*pos++ = '\0';
+		break;
+	}
+	param->data = pos;
+	if (tomoyo_correct_domain(start))
+		return tomoyo_get_name(start);
+	return NULL;
+}
+
 /**
 /**
  * tomoyo_parse_ulong - Parse an "unsigned long" value.
  * tomoyo_parse_ulong - Parse an "unsigned long" value.
  *
  *
@@ -920,14 +978,17 @@ int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile,
 		    const u8 index)
 		    const u8 index)
 {
 {
 	u8 mode;
 	u8 mode;
-	const u8 category = TOMOYO_MAC_CATEGORY_FILE;
+	struct tomoyo_profile *p;
+
 	if (!tomoyo_policy_loaded)
 	if (!tomoyo_policy_loaded)
 		return TOMOYO_CONFIG_DISABLED;
 		return TOMOYO_CONFIG_DISABLED;
-	mode = tomoyo_profile(ns, profile)->config[index];
+	p = tomoyo_profile(ns, profile);
+	mode = p->config[index];
 	if (mode == TOMOYO_CONFIG_USE_DEFAULT)
 	if (mode == TOMOYO_CONFIG_USE_DEFAULT)
-		mode = tomoyo_profile(ns, profile)->config[category];
+		mode = p->config[tomoyo_index2category[index]
+				 + TOMOYO_MAX_MAC_INDEX];
 	if (mode == TOMOYO_CONFIG_USE_DEFAULT)
 	if (mode == TOMOYO_CONFIG_USE_DEFAULT)
-		mode = tomoyo_profile(ns, profile)->default_config;
+		mode = p->default_config;
 	return mode & 3;
 	return mode & 3;
 }
 }
 
 
@@ -996,6 +1057,17 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r)
 			perm = container_of(ptr, struct tomoyo_mkdev_acl,
 			perm = container_of(ptr, struct tomoyo_mkdev_acl,
 					    head)->perm;
 					    head)->perm;
 			break;
 			break;
+		case TOMOYO_TYPE_INET_ACL:
+			perm = container_of(ptr, struct tomoyo_inet_acl,
+					    head)->perm;
+			break;
+		case TOMOYO_TYPE_UNIX_ACL:
+			perm = container_of(ptr, struct tomoyo_unix_acl,
+					    head)->perm;
+			break;
+		case TOMOYO_TYPE_MANUAL_TASK_ACL:
+			perm = 0;
+			break;
 		default:
 		default:
 			perm = 1;
 			perm = 1;
 		}
 		}