diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-10-25 03:45:31 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-10-25 03:45:31 -0400 |
| commit | 36b8d186e6cc8e32cb5227f5645a58e1bc0af190 (patch) | |
| tree | 1000ad26e189e6ff2c53fb7eeff605f59c7ad94e | |
| parent | cd85b557414fe4cd44ea6608825e96612a5fe2b2 (diff) | |
| parent | c45ed235abf1b0b6666417e3c394f18717976acd (diff) | |
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
...
99 files changed, 4701 insertions, 1432 deletions
diff --git a/Documentation/ABI/testing/evm b/Documentation/ABI/testing/evm new file mode 100644 index 000000000000..8374d4557e5d --- /dev/null +++ b/Documentation/ABI/testing/evm | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | What: security/evm | ||
| 2 | Date: March 2011 | ||
| 3 | Contact: Mimi Zohar <zohar@us.ibm.com> | ||
| 4 | Description: | ||
| 5 | EVM protects a file's security extended attributes(xattrs) | ||
| 6 | against integrity attacks. The initial method maintains an | ||
| 7 | HMAC-sha1 value across the extended attributes, storing the | ||
| 8 | value as the extended attribute 'security.evm'. | ||
| 9 | |||
| 10 | EVM depends on the Kernel Key Retention System to provide it | ||
| 11 | with a trusted/encrypted key for the HMAC-sha1 operation. | ||
| 12 | The key is loaded onto the root's keyring using keyctl. Until | ||
| 13 | EVM receives notification that the key has been successfully | ||
| 14 | loaded onto the keyring (echo 1 > <securityfs>/evm), EVM | ||
| 15 | can not create or validate the 'security.evm' xattr, but | ||
| 16 | returns INTEGRITY_UNKNOWN. Loading the key and signaling EVM | ||
| 17 | should be done as early as possible. Normally this is done | ||
| 18 | in the initramfs, which has already been measured as part | ||
| 19 | of the trusted boot. For more information on creating and | ||
| 20 | loading existing trusted/encrypted keys, refer to: | ||
| 21 | Documentation/keys-trusted-encrypted.txt. (A sample dracut | ||
| 22 | patch, which loads the trusted/encrypted key and enables | ||
| 23 | EVM, is available from http://linux-ima.sourceforge.net/#EVM.) | ||
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index d6e6724446c8..d317f6cf0d35 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt | |||
| @@ -49,6 +49,7 @@ parameter is applicable: | |||
| 49 | EDD BIOS Enhanced Disk Drive Services (EDD) is enabled | 49 | EDD BIOS Enhanced Disk Drive Services (EDD) is enabled |
| 50 | EFI EFI Partitioning (GPT) is enabled | 50 | EFI EFI Partitioning (GPT) is enabled |
| 51 | EIDE EIDE/ATAPI support is enabled. | 51 | EIDE EIDE/ATAPI support is enabled. |
| 52 | EVM Extended Verification Module | ||
| 52 | FB The frame buffer device is enabled. | 53 | FB The frame buffer device is enabled. |
| 53 | FTRACE Function tracing enabled. | 54 | FTRACE Function tracing enabled. |
| 54 | GCOV GCOV profiling is enabled. | 55 | GCOV GCOV profiling is enabled. |
| @@ -760,6 +761,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted. | |||
| 760 | This option is obsoleted by the "netdev=" option, which | 761 | This option is obsoleted by the "netdev=" option, which |
| 761 | has equivalent usage. See its documentation for details. | 762 | has equivalent usage. See its documentation for details. |
| 762 | 763 | ||
| 764 | evm= [EVM] | ||
| 765 | Format: { "fix" } | ||
| 766 | Permit 'security.evm' to be updated regardless of | ||
| 767 | current integrity status. | ||
| 768 | |||
| 763 | failslab= | 769 | failslab= |
| 764 | fail_page_alloc= | 770 | fail_page_alloc= |
| 765 | fail_make_request=[KNL] | 771 | fail_make_request=[KNL] |
diff --git a/MAINTAINERS b/MAINTAINERS index 6185d0513584..51a8160dc549 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
| @@ -2552,6 +2552,11 @@ S: Maintained | |||
| 2552 | F: Documentation/filesystems/ext4.txt | 2552 | F: Documentation/filesystems/ext4.txt |
| 2553 | F: fs/ext4/ | 2553 | F: fs/ext4/ |
| 2554 | 2554 | ||
| 2555 | Extended Verification Module (EVM) | ||
| 2556 | M: Mimi Zohar <zohar@us.ibm.com> | ||
| 2557 | S: Supported | ||
| 2558 | F: security/integrity/evm/ | ||
| 2559 | |||
| 2555 | F71805F HARDWARE MONITORING DRIVER | 2560 | F71805F HARDWARE MONITORING DRIVER |
| 2556 | M: Jean Delvare <khali@linux-fr.org> | 2561 | M: Jean Delvare <khali@linux-fr.org> |
| 2557 | L: lm-sensors@lm-sensors.org | 2562 | L: lm-sensors@lm-sensors.org |
| @@ -6447,7 +6452,7 @@ L: tomoyo-users-en@lists.sourceforge.jp (subscribers-only, for users in English) | |||
| 6447 | L: tomoyo-dev@lists.sourceforge.jp (subscribers-only, for developers in Japanese) | 6452 | L: tomoyo-dev@lists.sourceforge.jp (subscribers-only, for developers in Japanese) |
| 6448 | L: tomoyo-users@lists.sourceforge.jp (subscribers-only, for users in Japanese) | 6453 | L: tomoyo-users@lists.sourceforge.jp (subscribers-only, for users in Japanese) |
| 6449 | W: http://tomoyo.sourceforge.jp/ | 6454 | W: http://tomoyo.sourceforge.jp/ |
| 6450 | T: quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.4.x/tomoyo-lsm/patches/ | 6455 | T: quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.5.x/tomoyo-lsm/patches/ |
| 6451 | S: Maintained | 6456 | S: Maintained |
| 6452 | F: security/tomoyo/ | 6457 | F: security/tomoyo/ |
| 6453 | 6458 | ||
diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 9ca5c021d0b6..361a1dff8f77 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c | |||
| @@ -966,6 +966,9 @@ ssize_t tpm_show_durations(struct device *dev, struct device_attribute *attr, | |||
| 966 | { | 966 | { |
| 967 | struct tpm_chip *chip = dev_get_drvdata(dev); | 967 | struct tpm_chip *chip = dev_get_drvdata(dev); |
| 968 | 968 | ||
| 969 | if (chip->vendor.duration[TPM_LONG] == 0) | ||
| 970 | return 0; | ||
| 971 | |||
| 969 | return sprintf(buf, "%d %d %d [%s]\n", | 972 | return sprintf(buf, "%d %d %d [%s]\n", |
| 970 | jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]), | 973 | jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]), |
| 971 | jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]), | 974 | jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]), |
diff --git a/drivers/target/target_core_fabric_lib.c b/drivers/target/target_core_fabric_lib.c index c4ea3a9a555b..39f021b855ef 100644 --- a/drivers/target/target_core_fabric_lib.c +++ b/drivers/target/target_core_fabric_lib.c | |||
| @@ -63,6 +63,7 @@ u32 sas_get_pr_transport_id( | |||
| 63 | unsigned char *buf) | 63 | unsigned char *buf) |
| 64 | { | 64 | { |
| 65 | unsigned char *ptr; | 65 | unsigned char *ptr; |
| 66 | int ret; | ||
| 66 | 67 | ||
| 67 | /* | 68 | /* |
| 68 | * Set PROTOCOL IDENTIFIER to 6h for SAS | 69 | * Set PROTOCOL IDENTIFIER to 6h for SAS |
| @@ -74,7 +75,9 @@ u32 sas_get_pr_transport_id( | |||
| 74 | */ | 75 | */ |
| 75 | ptr = &se_nacl->initiatorname[4]; /* Skip over 'naa. prefix */ | 76 | ptr = &se_nacl->initiatorname[4]; /* Skip over 'naa. prefix */ |
| 76 | 77 | ||
| 77 | hex2bin(&buf[4], ptr, 8); | 78 | ret = hex2bin(&buf[4], ptr, 8); |
| 79 | if (ret < 0) | ||
| 80 | pr_debug("sas transport_id: invalid hex string\n"); | ||
| 78 | 81 | ||
| 79 | /* | 82 | /* |
| 80 | * The SAS Transport ID is a hardcoded 24-byte length | 83 | * The SAS Transport ID is a hardcoded 24-byte length |
| @@ -156,8 +159,9 @@ u32 fc_get_pr_transport_id( | |||
| 156 | unsigned char *buf) | 159 | unsigned char *buf) |
| 157 | { | 160 | { |
| 158 | unsigned char *ptr; | 161 | unsigned char *ptr; |
| 159 | int i; | 162 | int i, ret; |
| 160 | u32 off = 8; | 163 | u32 off = 8; |
| 164 | |||
| 161 | /* | 165 | /* |
| 162 | * PROTOCOL IDENTIFIER is 0h for FCP-2 | 166 | * PROTOCOL IDENTIFIER is 0h for FCP-2 |
| 163 | * | 167 | * |
| @@ -174,7 +178,9 @@ u32 fc_get_pr_transport_id( | |||
| 174 | i++; | 178 | i++; |
| 175 | continue; | 179 | continue; |
| 176 | } | 180 | } |
| 177 | hex2bin(&buf[off++], &ptr[i], 1); | 181 | ret = hex2bin(&buf[off++], &ptr[i], 1); |
| 182 | if (ret < 0) | ||
| 183 | pr_debug("fc transport_id: invalid hex string\n"); | ||
| 178 | i += 2; | 184 | i += 2; |
| 179 | } | 185 | } |
| 180 | /* | 186 | /* |
| @@ -13,6 +13,7 @@ | |||
| 13 | #include <linux/fsnotify.h> | 13 | #include <linux/fsnotify.h> |
| 14 | #include <linux/fcntl.h> | 14 | #include <linux/fcntl.h> |
| 15 | #include <linux/security.h> | 15 | #include <linux/security.h> |
| 16 | #include <linux/evm.h> | ||
| 16 | 17 | ||
| 17 | /** | 18 | /** |
| 18 | * inode_change_ok - check if attribute changes to an inode are allowed | 19 | * 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) | |||
| 237 | else | 238 | else |
| 238 | error = simple_setattr(dentry, attr); | 239 | error = simple_setattr(dentry, attr); |
| 239 | 240 | ||
| 240 | if (!error) | 241 | if (!error) { |
| 241 | fsnotify_change(dentry, ia_valid); | 242 | fsnotify_change(dentry, ia_valid); |
| 243 | evm_inode_post_setattr(dentry, ia_valid); | ||
| 244 | } | ||
| 242 | 245 | ||
| 243 | return error; | 246 | return error; |
| 244 | } | 247 | } |
diff --git a/fs/btrfs/xattr.c b/fs/btrfs/xattr.c index 69565e5fc6a0..426aa464f1af 100644 --- a/fs/btrfs/xattr.c +++ b/fs/btrfs/xattr.c | |||
| @@ -383,36 +383,36 @@ int btrfs_removexattr(struct dentry *dentry, const char *name) | |||
| 383 | XATTR_REPLACE); | 383 | XATTR_REPLACE); |
| 384 | } | 384 | } |
| 385 | 385 | ||
| 386 | int btrfs_xattr_security_init(struct btrfs_trans_handle *trans, | 386 | int btrfs_initxattrs(struct inode *inode, const struct xattr *xattr_array, |
| 387 | struct inode *inode, struct inode *dir, | 387 | void *fs_info) |
| 388 | const struct qstr *qstr) | ||
| 389 | { | 388 | { |
| 390 | int err; | 389 | const struct xattr *xattr; |
| 391 | size_t len; | 390 | struct btrfs_trans_handle *trans = fs_info; |
| 392 | void *value; | ||
| 393 | char *suffix; | ||
| 394 | char *name; | 391 | char *name; |
| 392 | int err = 0; | ||
| 395 | 393 | ||
| 396 | err = security_inode_init_security(inode, dir, qstr, &suffix, &value, | 394 | for (xattr = xattr_array; xattr->name != NULL; xattr++) { |
| 397 | &len); | 395 | name = kmalloc(XATTR_SECURITY_PREFIX_LEN + |
| 398 | if (err) { | 396 | strlen(xattr->name) + 1, GFP_NOFS); |
| 399 | if (err == -EOPNOTSUPP) | 397 | if (!name) { |
| 400 | return 0; | 398 | err = -ENOMEM; |
| 401 | return err; | 399 | break; |
| 402 | } | 400 | } |
| 403 | |||
| 404 | name = kmalloc(XATTR_SECURITY_PREFIX_LEN + strlen(suffix) + 1, | ||
| 405 | GFP_NOFS); | ||
| 406 | if (!name) { | ||
| 407 | err = -ENOMEM; | ||
| 408 | } else { | ||
| 409 | strcpy(name, XATTR_SECURITY_PREFIX); | 401 | strcpy(name, XATTR_SECURITY_PREFIX); |
| 410 | strcpy(name + XATTR_SECURITY_PREFIX_LEN, suffix); | 402 | strcpy(name + XATTR_SECURITY_PREFIX_LEN, xattr->name); |
| 411 | err = __btrfs_setxattr(trans, inode, name, value, len, 0); | 403 | err = __btrfs_setxattr(trans, inode, name, |
| 404 | xattr->value, xattr->value_len, 0); | ||
| 412 | kfree(name); | 405 | kfree(name); |
| 406 | if (err < 0) | ||
| 407 | break; | ||
| 413 | } | 408 | } |
| 414 | |||
| 415 | kfree(suffix); | ||
| 416 | kfree(value); | ||
| 417 | return err; | 409 | return err; |
| 418 | } | 410 | } |
| 411 | |||
| 412 | int btrfs_xattr_security_init(struct btrfs_trans_handle *trans, | ||
| 413 | struct inode *inode, struct inode *dir, | ||
| 414 | const struct qstr *qstr) | ||
| 415 | { | ||
| 416 | return security_inode_init_security(inode, dir, qstr, | ||
| 417 | &btrfs_initxattrs, trans); | ||
| 418 | } | ||
diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c index 2a22fb2989e4..c32308882148 100644 --- a/fs/cifs/xattr.c +++ b/fs/cifs/xattr.c | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include <linux/fs.h> | 22 | #include <linux/fs.h> |
| 23 | #include <linux/posix_acl_xattr.h> | 23 | #include <linux/posix_acl_xattr.h> |
| 24 | #include <linux/slab.h> | 24 | #include <linux/slab.h> |
| 25 | #include <linux/xattr.h> | ||
| 25 | #include "cifsfs.h" | 26 | #include "cifsfs.h" |
| 26 | #include "cifspdu.h" | 27 | #include "cifspdu.h" |
| 27 | #include "cifsglob.h" | 28 | #include "cifsglob.h" |
| @@ -31,16 +32,8 @@ | |||
| 31 | #define MAX_EA_VALUE_SIZE 65535 | 32 | #define MAX_EA_VALUE_SIZE 65535 |
| 32 | #define CIFS_XATTR_DOS_ATTRIB "user.DosAttrib" | 33 | #define CIFS_XATTR_DOS_ATTRIB "user.DosAttrib" |
| 33 | #define CIFS_XATTR_CIFS_ACL "system.cifs_acl" | 34 | #define CIFS_XATTR_CIFS_ACL "system.cifs_acl" |
| 34 | #define CIFS_XATTR_USER_PREFIX "user." | ||
| 35 | #define CIFS_XATTR_SYSTEM_PREFIX "system." | ||
| 36 | #define CIFS_XATTR_OS2_PREFIX "os2." | ||
| 37 | #define CIFS_XATTR_SECURITY_PREFIX "security." | ||
| 38 | #define CIFS_XATTR_TRUSTED_PREFIX "trusted." | ||
| 39 | #define XATTR_TRUSTED_PREFIX_LEN 8 | ||
| 40 | #define XATTR_SECURITY_PREFIX_LEN 9 | ||
| 41 | /* BB need to add server (Samba e.g) support for security and trusted prefix */ | ||
| 42 | |||
| 43 | 35 | ||
| 36 | /* BB need to add server (Samba e.g) support for security and trusted prefix */ | ||
| 44 | 37 | ||
| 45 | int cifs_removexattr(struct dentry *direntry, const char *ea_name) | 38 | int cifs_removexattr(struct dentry *direntry, const char *ea_name) |
| 46 | { | 39 | { |
| @@ -76,8 +69,8 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name) | |||
| 76 | } | 69 | } |
| 77 | if (ea_name == NULL) { | 70 | if (ea_name == NULL) { |
| 78 | cFYI(1, "Null xattr names not supported"); | 71 | cFYI(1, "Null xattr names not supported"); |
| 79 | } else if (strncmp(ea_name, CIFS_XATTR_USER_PREFIX, 5) | 72 | } else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) |
| 80 | && (strncmp(ea_name, CIFS_XATTR_OS2_PREFIX, 4))) { | 73 | && (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN))) { |
| 81 | cFYI(1, | 74 | cFYI(1, |
| 82 | "illegal xattr request %s (only user namespace supported)", | 75 | "illegal xattr request %s (only user namespace supported)", |
| 83 | ea_name); | 76 | ea_name); |
| @@ -88,7 +81,7 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name) | |||
| 88 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) | 81 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) |
| 89 | goto remove_ea_exit; | 82 | goto remove_ea_exit; |
| 90 | 83 | ||
| 91 | ea_name += 5; /* skip past user. prefix */ | 84 | ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */ |
| 92 | rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, NULL, | 85 | rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, NULL, |
| 93 | (__u16)0, cifs_sb->local_nls, | 86 | (__u16)0, cifs_sb->local_nls, |
| 94 | cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); | 87 | cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); |
| @@ -149,21 +142,23 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name, | |||
| 149 | 142 | ||
| 150 | if (ea_name == NULL) { | 143 | if (ea_name == NULL) { |
| 151 | cFYI(1, "Null xattr names not supported"); | 144 | cFYI(1, "Null xattr names not supported"); |
| 152 | } else if (strncmp(ea_name, CIFS_XATTR_USER_PREFIX, 5) == 0) { | 145 | } else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) |
| 146 | == 0) { | ||
| 153 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) | 147 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) |
| 154 | goto set_ea_exit; | 148 | goto set_ea_exit; |
| 155 | if (strncmp(ea_name, CIFS_XATTR_DOS_ATTRIB, 14) == 0) | 149 | if (strncmp(ea_name, CIFS_XATTR_DOS_ATTRIB, 14) == 0) |
| 156 | cFYI(1, "attempt to set cifs inode metadata"); | 150 | cFYI(1, "attempt to set cifs inode metadata"); |
| 157 | 151 | ||
| 158 | ea_name += 5; /* skip past user. prefix */ | 152 | ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */ |
| 159 | rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, ea_value, | 153 | rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, ea_value, |
| 160 | (__u16)value_size, cifs_sb->local_nls, | 154 | (__u16)value_size, cifs_sb->local_nls, |
| 161 | cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); | 155 | cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); |
| 162 | } else if (strncmp(ea_name, CIFS_XATTR_OS2_PREFIX, 4) == 0) { | 156 | } else if (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) |
| 157 | == 0) { | ||
| 163 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) | 158 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) |
| 164 | goto set_ea_exit; | 159 | goto set_ea_exit; |
| 165 | 160 | ||
| 166 | ea_name += 4; /* skip past os2. prefix */ | 161 | ea_name += XATTR_OS2_PREFIX_LEN; /* skip past os2. prefix */ |
| 167 | rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, ea_value, | 162 | rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, ea_value, |
| 168 | (__u16)value_size, cifs_sb->local_nls, | 163 | (__u16)value_size, cifs_sb->local_nls, |
| 169 | cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); | 164 | 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, | |||
| 269 | /* return alt name if available as pseudo attr */ | 264 | /* return alt name if available as pseudo attr */ |
| 270 | if (ea_name == NULL) { | 265 | if (ea_name == NULL) { |
| 271 | cFYI(1, "Null xattr names not supported"); | 266 | cFYI(1, "Null xattr names not supported"); |
| 272 | } else if (strncmp(ea_name, CIFS_XATTR_USER_PREFIX, 5) == 0) { | 267 | } else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) |
| 268 | == 0) { | ||
| 273 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) | 269 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) |
| 274 | goto get_ea_exit; | 270 | goto get_ea_exit; |
| 275 | 271 | ||
| @@ -277,15 +273,15 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name, | |||
| 277 | cFYI(1, "attempt to query cifs inode metadata"); | 273 | cFYI(1, "attempt to query cifs inode metadata"); |
| 278 | /* revalidate/getattr then populate from inode */ | 274 | /* revalidate/getattr then populate from inode */ |
| 279 | } /* BB add else when above is implemented */ | 275 | } /* BB add else when above is implemented */ |
| 280 | ea_name += 5; /* skip past user. prefix */ | 276 | ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */ |
| 281 | rc = CIFSSMBQAllEAs(xid, pTcon, full_path, ea_name, ea_value, | 277 | rc = CIFSSMBQAllEAs(xid, pTcon, full_path, ea_name, ea_value, |
| 282 | buf_size, cifs_sb->local_nls, | 278 | buf_size, cifs_sb->local_nls, |
| 283 | cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); | 279 | cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); |
| 284 | } else if (strncmp(ea_name, CIFS_XATTR_OS2_PREFIX, 4) == 0) { | 280 | } else if (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) == 0) { |
| 285 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) | 281 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) |
| 286 | goto get_ea_exit; | 282 | goto get_ea_exit; |
| 287 | 283 | ||
| 288 | ea_name += 4; /* skip past os2. prefix */ | 284 | ea_name += XATTR_OS2_PREFIX_LEN; /* skip past os2. prefix */ |
| 289 | rc = CIFSSMBQAllEAs(xid, pTcon, full_path, ea_name, ea_value, | 285 | rc = CIFSSMBQAllEAs(xid, pTcon, full_path, ea_name, ea_value, |
| 290 | buf_size, cifs_sb->local_nls, | 286 | buf_size, cifs_sb->local_nls, |
| 291 | cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); | 287 | 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, | |||
| 339 | cFYI(1, "Query CIFS ACL not supported yet"); | 335 | cFYI(1, "Query CIFS ACL not supported yet"); |
| 340 | #endif /* CONFIG_CIFS_ACL */ | 336 | #endif /* CONFIG_CIFS_ACL */ |
| 341 | } else if (strncmp(ea_name, | 337 | } else if (strncmp(ea_name, |
| 342 | CIFS_XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) == 0) { | 338 | XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) == 0) { |
| 343 | cFYI(1, "Trusted xattr namespace not supported yet"); | 339 | cFYI(1, "Trusted xattr namespace not supported yet"); |
| 344 | } else if (strncmp(ea_name, | 340 | } else if (strncmp(ea_name, |
| 345 | CIFS_XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) == 0) { | 341 | XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) == 0) { |
| 346 | cFYI(1, "Security xattr namespace not supported yet"); | 342 | cFYI(1, "Security xattr namespace not supported yet"); |
| 347 | } else | 343 | } else |
| 348 | cFYI(1, | 344 | cFYI(1, |
diff --git a/fs/ext2/xattr_security.c b/fs/ext2/xattr_security.c index 5d979b4347b0..c922adc8ef41 100644 --- a/fs/ext2/xattr_security.c +++ b/fs/ext2/xattr_security.c | |||
| @@ -46,28 +46,30 @@ ext2_xattr_security_set(struct dentry *dentry, const char *name, | |||
| 46 | value, size, flags); | 46 | value, size, flags); |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | int | 49 | int ext2_initxattrs(struct inode *inode, const struct xattr *xattr_array, |
| 50 | ext2_init_security(struct inode *inode, struct inode *dir, | 50 | void *fs_info) |
| 51 | const struct qstr *qstr) | ||
| 52 | { | 51 | { |
| 53 | int err; | 52 | const struct xattr *xattr; |
| 54 | size_t len; | 53 | int err = 0; |
| 55 | void *value; | ||
| 56 | char *name; | ||
| 57 | 54 | ||
| 58 | err = security_inode_init_security(inode, dir, qstr, &name, &value, &len); | 55 | for (xattr = xattr_array; xattr->name != NULL; xattr++) { |
| 59 | if (err) { | 56 | err = ext2_xattr_set(inode, EXT2_XATTR_INDEX_SECURITY, |
| 60 | if (err == -EOPNOTSUPP) | 57 | xattr->name, xattr->value, |
| 61 | return 0; | 58 | xattr->value_len, 0); |
| 62 | return err; | 59 | if (err < 0) |
| 60 | break; | ||
| 63 | } | 61 | } |
| 64 | err = ext2_xattr_set(inode, EXT2_XATTR_INDEX_SECURITY, | ||
| 65 | name, value, len, 0); | ||
| 66 | kfree(name); | ||
| 67 | kfree(value); | ||
| 68 | return err; | 62 | return err; |
| 69 | } | 63 | } |
| 70 | 64 | ||
| 65 | int | ||
| 66 | ext2_init_security(struct inode *inode, struct inode *dir, | ||
| 67 | const struct qstr *qstr) | ||
| 68 | { | ||
| 69 | return security_inode_init_security(inode, dir, qstr, | ||
| 70 | &ext2_initxattrs, NULL); | ||
| 71 | } | ||
| 72 | |||
| 71 | const struct xattr_handler ext2_xattr_security_handler = { | 73 | const struct xattr_handler ext2_xattr_security_handler = { |
| 72 | .prefix = XATTR_SECURITY_PREFIX, | 74 | .prefix = XATTR_SECURITY_PREFIX, |
| 73 | .list = ext2_xattr_security_list, | 75 | .list = ext2_xattr_security_list, |
diff --git a/fs/ext3/xattr_security.c b/fs/ext3/xattr_security.c index b8d9f83aa5c5..3c218b8a51d4 100644 --- a/fs/ext3/xattr_security.c +++ b/fs/ext3/xattr_security.c | |||
| @@ -48,28 +48,32 @@ ext3_xattr_security_set(struct dentry *dentry, const char *name, | |||
| 48 | name, value, size, flags); | 48 | name, value, size, flags); |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | int | 51 | int ext3_initxattrs(struct inode *inode, const struct xattr *xattr_array, |
| 52 | ext3_init_security(handle_t *handle, struct inode *inode, struct inode *dir, | 52 | void *fs_info) |
| 53 | const struct qstr *qstr) | ||
| 54 | { | 53 | { |
| 55 | int err; | 54 | const struct xattr *xattr; |
| 56 | size_t len; | 55 | handle_t *handle = fs_info; |
| 57 | void *value; | 56 | int err = 0; |
| 58 | char *name; | ||
| 59 | 57 | ||
| 60 | err = security_inode_init_security(inode, dir, qstr, &name, &value, &len); | 58 | for (xattr = xattr_array; xattr->name != NULL; xattr++) { |
| 61 | if (err) { | 59 | err = ext3_xattr_set_handle(handle, inode, |
| 62 | if (err == -EOPNOTSUPP) | 60 | EXT3_XATTR_INDEX_SECURITY, |
| 63 | return 0; | 61 | xattr->name, xattr->value, |
| 64 | return err; | 62 | xattr->value_len, 0); |
| 63 | if (err < 0) | ||
| 64 | break; | ||
| 65 | } | 65 | } |
| 66 | err = ext3_xattr_set_handle(handle, inode, EXT3_XATTR_INDEX_SECURITY, | ||
| 67 | name, value, len, 0); | ||
| 68 | kfree(name); | ||
| 69 | kfree(value); | ||
| 70 | return err; | 66 | return err; |
| 71 | } | 67 | } |
| 72 | 68 | ||
| 69 | int | ||
| 70 | ext3_init_security(handle_t *handle, struct inode *inode, struct inode *dir, | ||
| 71 | const struct qstr *qstr) | ||
| 72 | { | ||
| 73 | return security_inode_init_security(inode, dir, qstr, | ||
| 74 | &ext3_initxattrs, handle); | ||
| 75 | } | ||
| 76 | |||
| 73 | const struct xattr_handler ext3_xattr_security_handler = { | 77 | const struct xattr_handler ext3_xattr_security_handler = { |
| 74 | .prefix = XATTR_SECURITY_PREFIX, | 78 | .prefix = XATTR_SECURITY_PREFIX, |
| 75 | .list = ext3_xattr_security_list, | 79 | .list = ext3_xattr_security_list, |
diff --git a/fs/ext4/xattr_security.c b/fs/ext4/xattr_security.c index 007c3bfbf094..34e4350dd4d9 100644 --- a/fs/ext4/xattr_security.c +++ b/fs/ext4/xattr_security.c | |||
| @@ -48,28 +48,32 @@ ext4_xattr_security_set(struct dentry *dentry, const char *name, | |||
| 48 | name, value, size, flags); | 48 | name, value, size, flags); |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | int | 51 | int ext4_initxattrs(struct inode *inode, const struct xattr *xattr_array, |
| 52 | ext4_init_security(handle_t *handle, struct inode *inode, struct inode *dir, | 52 | void *fs_info) |
| 53 | const struct qstr *qstr) | ||
| 54 | { | 53 | { |
| 55 | int err; | 54 | const struct xattr *xattr; |
| 56 | size_t len; | 55 | handle_t *handle = fs_info; |
| 57 | void *value; | 56 | int err = 0; |
| 58 | char *name; | ||
| 59 | 57 | ||
| 60 | err = security_inode_init_security(inode, dir, qstr, &name, &value, &len); | 58 | for (xattr = xattr_array; xattr->name != NULL; xattr++) { |
| 61 | if (err) { | 59 | err = ext4_xattr_set_handle(handle, inode, |
| 62 | if (err == -EOPNOTSUPP) | 60 | EXT4_XATTR_INDEX_SECURITY, |
| 63 | return 0; | 61 | xattr->name, xattr->value, |
| 64 | return err; | 62 | xattr->value_len, 0); |
| 63 | if (err < 0) | ||
| 64 | break; | ||
| 65 | } | 65 | } |
| 66 | err = ext4_xattr_set_handle(handle, inode, EXT4_XATTR_INDEX_SECURITY, | ||
| 67 | name, value, len, 0); | ||
| 68 | kfree(name); | ||
| 69 | kfree(value); | ||
| 70 | return err; | 66 | return err; |
| 71 | } | 67 | } |
| 72 | 68 | ||
| 69 | int | ||
| 70 | ext4_init_security(handle_t *handle, struct inode *inode, struct inode *dir, | ||
| 71 | const struct qstr *qstr) | ||
| 72 | { | ||
| 73 | return security_inode_init_security(inode, dir, qstr, | ||
| 74 | &ext4_initxattrs, handle); | ||
| 75 | } | ||
| 76 | |||
| 73 | const struct xattr_handler ext4_xattr_security_handler = { | 77 | const struct xattr_handler ext4_xattr_security_handler = { |
| 74 | .prefix = XATTR_SECURITY_PREFIX, | 78 | .prefix = XATTR_SECURITY_PREFIX, |
| 75 | .list = ext4_xattr_security_list, | 79 | .list = ext4_xattr_security_list, |
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 900cf986aadc..6525b804d5ec 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c | |||
| @@ -624,31 +624,29 @@ fail: | |||
| 624 | return error; | 624 | return error; |
| 625 | } | 625 | } |
| 626 | 626 | ||
| 627 | static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip, | 627 | int gfs2_initxattrs(struct inode *inode, const struct xattr *xattr_array, |
| 628 | const struct qstr *qstr) | 628 | void *fs_info) |
| 629 | { | 629 | { |
| 630 | int err; | 630 | const struct xattr *xattr; |
| 631 | size_t len; | 631 | int err = 0; |
| 632 | void *value; | 632 | |
| 633 | char *name; | 633 | for (xattr = xattr_array; xattr->name != NULL; xattr++) { |
| 634 | 634 | err = __gfs2_xattr_set(inode, xattr->name, xattr->value, | |
| 635 | err = security_inode_init_security(&ip->i_inode, &dip->i_inode, qstr, | 635 | xattr->value_len, 0, |
| 636 | &name, &value, &len); | 636 | GFS2_EATYPE_SECURITY); |
| 637 | 637 | if (err < 0) | |
| 638 | if (err) { | 638 | break; |
| 639 | if (err == -EOPNOTSUPP) | ||
| 640 | return 0; | ||
| 641 | return err; | ||
| 642 | } | 639 | } |
| 643 | |||
| 644 | err = __gfs2_xattr_set(&ip->i_inode, name, value, len, 0, | ||
| 645 | GFS2_EATYPE_SECURITY); | ||
| 646 | kfree(value); | ||
| 647 | kfree(name); | ||
| 648 | |||
| 649 | return err; | 640 | return err; |
| 650 | } | 641 | } |
| 651 | 642 | ||
| 643 | static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip, | ||
| 644 | const struct qstr *qstr) | ||
| 645 | { | ||
| 646 | return security_inode_init_security(&ip->i_inode, &dip->i_inode, qstr, | ||
| 647 | &gfs2_initxattrs, NULL); | ||
| 648 | } | ||
| 649 | |||
| 652 | /** | 650 | /** |
| 653 | * gfs2_create_inode - Create a new inode | 651 | * gfs2_create_inode - Create a new inode |
| 654 | * @dir: The parent directory | 652 | * @dir: The parent directory |
diff --git a/fs/jffs2/security.c b/fs/jffs2/security.c index cfeb7164b085..0f20208df602 100644 --- a/fs/jffs2/security.c +++ b/fs/jffs2/security.c | |||
| @@ -22,26 +22,29 @@ | |||
| 22 | #include <linux/security.h> | 22 | #include <linux/security.h> |
| 23 | #include "nodelist.h" | 23 | #include "nodelist.h" |
| 24 | 24 | ||
| 25 | /* ---- Initial Security Label Attachment -------------- */ | 25 | /* ---- Initial Security Label(s) Attachment callback --- */ |
| 26 | int jffs2_init_security(struct inode *inode, struct inode *dir, | 26 | int jffs2_initxattrs(struct inode *inode, const struct xattr *xattr_array, |
| 27 | const struct qstr *qstr) | 27 | void *fs_info) |
| 28 | { | 28 | { |
| 29 | int rc; | 29 | const struct xattr *xattr; |
| 30 | size_t len; | 30 | int err = 0; |
| 31 | void *value; | ||
| 32 | char *name; | ||
| 33 | 31 | ||
| 34 | rc = security_inode_init_security(inode, dir, qstr, &name, &value, &len); | 32 | for (xattr = xattr_array; xattr->name != NULL; xattr++) { |
| 35 | if (rc) { | 33 | err = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, |
| 36 | if (rc == -EOPNOTSUPP) | 34 | xattr->name, xattr->value, |
| 37 | return 0; | 35 | xattr->value_len, 0); |
| 38 | return rc; | 36 | if (err < 0) |
| 37 | break; | ||
| 39 | } | 38 | } |
| 40 | rc = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, value, len, 0); | 39 | return err; |
| 40 | } | ||
| 41 | 41 | ||
| 42 | kfree(name); | 42 | /* ---- Initial Security Label(s) Attachment ----------- */ |
| 43 | kfree(value); | 43 | int jffs2_init_security(struct inode *inode, struct inode *dir, |
| 44 | return rc; | 44 | const struct qstr *qstr) |
| 45 | { | ||
| 46 | return security_inode_init_security(inode, dir, qstr, | ||
| 47 | &jffs2_initxattrs, NULL); | ||
| 45 | } | 48 | } |
| 46 | 49 | ||
| 47 | /* ---- XATTR Handler for "security.*" ----------------- */ | 50 | /* ---- XATTR Handler for "security.*" ----------------- */ |
diff --git a/fs/jfs/xattr.c b/fs/jfs/xattr.c index e87fedef23db..26683e15b3ac 100644 --- a/fs/jfs/xattr.c +++ b/fs/jfs/xattr.c | |||
| @@ -1089,38 +1089,37 @@ int jfs_removexattr(struct dentry *dentry, const char *name) | |||
| 1089 | } | 1089 | } |
| 1090 | 1090 | ||
| 1091 | #ifdef CONFIG_JFS_SECURITY | 1091 | #ifdef CONFIG_JFS_SECURITY |
| 1092 | int jfs_init_security(tid_t tid, struct inode *inode, struct inode *dir, | 1092 | int jfs_initxattrs(struct inode *inode, const struct xattr *xattr_array, |
| 1093 | const struct qstr *qstr) | 1093 | void *fs_info) |
| 1094 | { | 1094 | { |
| 1095 | int rc; | 1095 | const struct xattr *xattr; |
| 1096 | size_t len; | 1096 | tid_t *tid = fs_info; |
| 1097 | void *value; | ||
| 1098 | char *suffix; | ||
| 1099 | char *name; | 1097 | char *name; |
| 1100 | 1098 | int err = 0; | |
| 1101 | rc = security_inode_init_security(inode, dir, qstr, &suffix, &value, | 1099 | |
| 1102 | &len); | 1100 | for (xattr = xattr_array; xattr->name != NULL; xattr++) { |
| 1103 | if (rc) { | 1101 | name = kmalloc(XATTR_SECURITY_PREFIX_LEN + |
| 1104 | if (rc == -EOPNOTSUPP) | 1102 | strlen(xattr->name) + 1, GFP_NOFS); |
| 1105 | return 0; | 1103 | if (!name) { |
| 1106 | return rc; | 1104 | err = -ENOMEM; |
| 1107 | } | 1105 | break; |
| 1108 | name = kmalloc(XATTR_SECURITY_PREFIX_LEN + 1 + strlen(suffix), | 1106 | } |
| 1109 | GFP_NOFS); | 1107 | strcpy(name, XATTR_SECURITY_PREFIX); |
| 1110 | if (!name) { | 1108 | strcpy(name + XATTR_SECURITY_PREFIX_LEN, xattr->name); |
| 1111 | rc = -ENOMEM; | 1109 | |
| 1112 | goto kmalloc_failed; | 1110 | err = __jfs_setxattr(*tid, inode, name, |
| 1111 | xattr->value, xattr->value_len, 0); | ||
| 1112 | kfree(name); | ||
| 1113 | if (err < 0) | ||
| 1114 | break; | ||
| 1113 | } | 1115 | } |
| 1114 | strcpy(name, XATTR_SECURITY_PREFIX); | 1116 | return err; |
| 1115 | strcpy(name + XATTR_SECURITY_PREFIX_LEN, suffix); | 1117 | } |
| 1116 | |||
| 1117 | rc = __jfs_setxattr(tid, inode, name, value, len, 0); | ||
| 1118 | |||
| 1119 | kfree(name); | ||
| 1120 | kmalloc_failed: | ||
| 1121 | kfree(suffix); | ||
| 1122 | kfree(value); | ||
| 1123 | 1118 | ||
| 1124 | return rc; | 1119 | int jfs_init_security(tid_t tid, struct inode *inode, struct inode *dir, |
| 1120 | const struct qstr *qstr) | ||
| 1121 | { | ||
| 1122 | return security_inode_init_security(inode, dir, qstr, | ||
| 1123 | &jfs_initxattrs, &tid); | ||
| 1125 | } | 1124 | } |
| 1126 | #endif | 1125 | #endif |
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 81ecf9c0bf0a..194fb22ef79d 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c | |||
| @@ -7185,20 +7185,9 @@ int ocfs2_init_security_and_acl(struct inode *dir, | |||
| 7185 | { | 7185 | { |
| 7186 | int ret = 0; | 7186 | int ret = 0; |
| 7187 | struct buffer_head *dir_bh = NULL; | 7187 | struct buffer_head *dir_bh = NULL; |
| 7188 | struct ocfs2_security_xattr_info si = { | ||
| 7189 | .enable = 1, | ||
| 7190 | }; | ||
| 7191 | 7188 | ||
| 7192 | ret = ocfs2_init_security_get(inode, dir, qstr, &si); | 7189 | ret = ocfs2_init_security_get(inode, dir, qstr, NULL); |
| 7193 | if (!ret) { | 7190 | if (!ret) { |
| 7194 | ret = ocfs2_xattr_set(inode, OCFS2_XATTR_INDEX_SECURITY, | ||
| 7195 | si.name, si.value, si.value_len, | ||
| 7196 | XATTR_CREATE); | ||
| 7197 | if (ret) { | ||
| 7198 | mlog_errno(ret); | ||
| 7199 | goto leave; | ||
| 7200 | } | ||
| 7201 | } else if (ret != -EOPNOTSUPP) { | ||
| 7202 | mlog_errno(ret); | 7191 | mlog_errno(ret); |
| 7203 | goto leave; | 7192 | goto leave; |
| 7204 | } | 7193 | } |
| @@ -7255,6 +7244,22 @@ static int ocfs2_xattr_security_set(struct dentry *dentry, const char *name, | |||
| 7255 | name, value, size, flags); | 7244 | name, value, size, flags); |
| 7256 | } | 7245 | } |
| 7257 | 7246 | ||
| 7247 | int ocfs2_initxattrs(struct inode *inode, const struct xattr *xattr_array, | ||
| 7248 | void *fs_info) | ||
| 7249 | { | ||
| 7250 | const struct xattr *xattr; | ||
| 7251 | int err = 0; | ||
| 7252 | |||
| 7253 | for (xattr = xattr_array; xattr->name != NULL; xattr++) { | ||
| 7254 | err = ocfs2_xattr_set(inode, OCFS2_XATTR_INDEX_SECURITY, | ||
| 7255 | xattr->name, xattr->value, | ||
| 7256 | xattr->value_len, XATTR_CREATE); | ||
| 7257 | if (err) | ||
| 7258 | break; | ||
| 7259 | } | ||
| 7260 | return err; | ||
| 7261 | } | ||
| 7262 | |||
| 7258 | int ocfs2_init_security_get(struct inode *inode, | 7263 | int ocfs2_init_security_get(struct inode *inode, |
| 7259 | struct inode *dir, | 7264 | struct inode *dir, |
| 7260 | const struct qstr *qstr, | 7265 | const struct qstr *qstr, |
| @@ -7263,8 +7268,13 @@ int ocfs2_init_security_get(struct inode *inode, | |||
| 7263 | /* check whether ocfs2 support feature xattr */ | 7268 | /* check whether ocfs2 support feature xattr */ |
| 7264 | if (!ocfs2_supports_xattr(OCFS2_SB(dir->i_sb))) | 7269 | if (!ocfs2_supports_xattr(OCFS2_SB(dir->i_sb))) |
| 7265 | return -EOPNOTSUPP; | 7270 | return -EOPNOTSUPP; |
| 7266 | return security_inode_init_security(inode, dir, qstr, &si->name, | 7271 | if (si) |
| 7267 | &si->value, &si->value_len); | 7272 | return security_old_inode_init_security(inode, dir, qstr, |
| 7273 | &si->name, &si->value, | ||
| 7274 | &si->value_len); | ||
| 7275 | |||
| 7276 | return security_inode_init_security(inode, dir, qstr, | ||
| 7277 | &ocfs2_initxattrs, NULL); | ||
| 7268 | } | 7278 | } |
| 7269 | 7279 | ||
| 7270 | int ocfs2_init_security_set(handle_t *handle, | 7280 | int ocfs2_init_security_set(handle_t *handle, |
diff --git a/fs/reiserfs/xattr_security.c b/fs/reiserfs/xattr_security.c index ef66c18a9332..534668fa41be 100644 --- a/fs/reiserfs/xattr_security.c +++ b/fs/reiserfs/xattr_security.c | |||
| @@ -66,8 +66,8 @@ int reiserfs_security_init(struct inode *dir, struct inode *inode, | |||
| 66 | if (IS_PRIVATE(dir)) | 66 | if (IS_PRIVATE(dir)) |
| 67 | return 0; | 67 | return 0; |
| 68 | 68 | ||
| 69 | error = security_inode_init_security(inode, dir, qstr, &sec->name, | 69 | error = security_old_inode_init_security(inode, dir, qstr, &sec->name, |
| 70 | &sec->value, &sec->length); | 70 | &sec->value, &sec->length); |
| 71 | if (error) { | 71 | if (error) { |
| 72 | if (error == -EOPNOTSUPP) | 72 | if (error == -EOPNOTSUPP) |
| 73 | error = 0; | 73 | error = 0; |
diff --git a/fs/xattr.c b/fs/xattr.c index f060663ab70c..67583de8218c 100644 --- a/fs/xattr.c +++ b/fs/xattr.c | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include <linux/mount.h> | 14 | #include <linux/mount.h> |
| 15 | #include <linux/namei.h> | 15 | #include <linux/namei.h> |
| 16 | #include <linux/security.h> | 16 | #include <linux/security.h> |
| 17 | #include <linux/evm.h> | ||
| 17 | #include <linux/syscalls.h> | 18 | #include <linux/syscalls.h> |
| 18 | #include <linux/module.h> | 19 | #include <linux/module.h> |
| 19 | #include <linux/fsnotify.h> | 20 | #include <linux/fsnotify.h> |
| @@ -166,6 +167,64 @@ out_noalloc: | |||
| 166 | } | 167 | } |
| 167 | EXPORT_SYMBOL_GPL(xattr_getsecurity); | 168 | EXPORT_SYMBOL_GPL(xattr_getsecurity); |
| 168 | 169 | ||
| 170 | /* | ||
| 171 | * vfs_getxattr_alloc - allocate memory, if necessary, before calling getxattr | ||
| 172 | * | ||
| 173 | * Allocate memory, if not already allocated, or re-allocate correct size, | ||
| 174 | * before retrieving the extended attribute. | ||
| 175 | * | ||
| 176 | * Returns the result of alloc, if failed, or the getxattr operation. | ||
| 177 | */ | ||
| 178 | ssize_t | ||
| 179 | vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value, | ||
| 180 | size_t xattr_size, gfp_t flags) | ||
| 181 | { | ||
| 182 | struct inode *inode = dentry->d_inode; | ||
| 183 | char *value = *xattr_value; | ||
| 184 | int error; | ||
| 185 | |||
| 186 | error = xattr_permission(inode, name, MAY_READ); | ||
| 187 | if (error) | ||
| 188 | return error; | ||
| 189 | |||
| 190 | if (!inode->i_op->getxattr) | ||
| 191 | return -EOPNOTSUPP; | ||
| 192 | |||
| 193 | error = inode->i_op->getxattr(dentry, name, NULL, 0); | ||
| 194 | if (error < 0) | ||
| 195 | return error; | ||
| 196 | |||
| 197 | if (!value || (error > xattr_size)) { | ||
| 198 | value = krealloc(*xattr_value, error + 1, flags); | ||
| 199 | if (!value) | ||
| 200 | return -ENOMEM; | ||
| 201 | memset(value, 0, error + 1); | ||
| 202 | } | ||
| 203 | |||
| 204 | error = inode->i_op->getxattr(dentry, name, value, error); | ||
| 205 | *xattr_value = value; | ||
| 206 | return error; | ||
| 207 | } | ||
| 208 | |||
| 209 | /* Compare an extended attribute value with the given value */ | ||
| 210 | int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name, | ||
| 211 | const char *value, size_t size, gfp_t flags) | ||
| 212 | { | ||
| 213 | char *xattr_value = NULL; | ||
| 214 | int rc; | ||
| 215 | |||
| 216 | rc = vfs_getxattr_alloc(dentry, xattr_name, &xattr_value, 0, flags); | ||
| 217 | if (rc < 0) | ||
| 218 | return rc; | ||
| 219 | |||
| 220 | if ((rc != size) || (memcmp(xattr_value, value, rc) != 0)) | ||
| 221 | rc = -EINVAL; | ||
| 222 | else | ||
| 223 | rc = 0; | ||
| 224 | kfree(xattr_value); | ||
| 225 | return rc; | ||
| 226 | } | ||
| 227 | |||
| 169 | ssize_t | 228 | ssize_t |
| 170 | vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) | 229 | vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) |
| 171 | { | 230 | { |
| @@ -243,8 +302,10 @@ vfs_removexattr(struct dentry *dentry, const char *name) | |||
| 243 | error = inode->i_op->removexattr(dentry, name); | 302 | error = inode->i_op->removexattr(dentry, name); |
| 244 | mutex_unlock(&inode->i_mutex); | 303 | mutex_unlock(&inode->i_mutex); |
| 245 | 304 | ||
| 246 | if (!error) | 305 | if (!error) { |
| 247 | fsnotify_xattr(dentry); | 306 | fsnotify_xattr(dentry); |
| 307 | evm_inode_post_removexattr(dentry, name); | ||
| 308 | } | ||
| 248 | return error; | 309 | return error; |
| 249 | } | 310 | } |
| 250 | EXPORT_SYMBOL_GPL(vfs_removexattr); | 311 | EXPORT_SYMBOL_GPL(vfs_removexattr); |
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index 673704fab748..28856accb4fa 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c | |||
| @@ -102,37 +102,38 @@ xfs_mark_inode_dirty( | |||
| 102 | 102 | ||
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | |||
| 106 | int xfs_initxattrs(struct inode *inode, const struct xattr *xattr_array, | ||
| 107 | void *fs_info) | ||
| 108 | { | ||
| 109 | const struct xattr *xattr; | ||
| 110 | struct xfs_inode *ip = XFS_I(inode); | ||
| 111 | int error = 0; | ||
| 112 | |||
| 113 | for (xattr = xattr_array; xattr->name != NULL; xattr++) { | ||
| 114 | error = xfs_attr_set(ip, xattr->name, xattr->value, | ||
| 115 | xattr->value_len, ATTR_SECURE); | ||
| 116 | if (error < 0) | ||
| 117 | break; | ||
| 118 | } | ||
| 119 | return error; | ||
| 120 | } | ||
| 121 | |||
| 105 | /* | 122 | /* |
| 106 | * Hook in SELinux. This is not quite correct yet, what we really need | 123 | * Hook in SELinux. This is not quite correct yet, what we really need |
| 107 | * here (as we do for default ACLs) is a mechanism by which creation of | 124 | * here (as we do for default ACLs) is a mechanism by which creation of |
| 108 | * these attrs can be journalled at inode creation time (along with the | 125 | * these attrs can be journalled at inode creation time (along with the |
| 109 | * inode, of course, such that log replay can't cause these to be lost). | 126 | * inode, of course, such that log replay can't cause these to be lost). |
| 110 | */ | 127 | */ |
| 128 | |||
| 111 | STATIC int | 129 | STATIC int |
| 112 | xfs_init_security( | 130 | xfs_init_security( |
| 113 | struct inode *inode, | 131 | struct inode *inode, |
| 114 | struct inode *dir, | 132 | struct inode *dir, |
| 115 | const struct qstr *qstr) | 133 | const struct qstr *qstr) |
| 116 | { | 134 | { |
| 117 | struct xfs_inode *ip = XFS_I(inode); | 135 | return security_inode_init_security(inode, dir, qstr, |
| 118 | size_t length; | 136 | &xfs_initxattrs, NULL); |
| 119 | void *value; | ||
| 120 | unsigned char *name; | ||
| 121 | int error; | ||
| 122 | |||
| 123 | error = security_inode_init_security(inode, dir, qstr, (char **)&name, | ||
| 124 | &value, &length); | ||
| 125 | if (error) { | ||
| 126 | if (error == -EOPNOTSUPP) | ||
| 127 | return 0; | ||
| 128 | return -error; | ||
| 129 | } | ||
| 130 | |||
| 131 | error = xfs_attr_set(ip, name, value, length, ATTR_SECURE); | ||
| 132 | |||
| 133 | kfree(name); | ||
| 134 | kfree(value); | ||
| 135 | return error; | ||
| 136 | } | 137 | } |
| 137 | 138 | ||
| 138 | static void | 139 | static void |
diff --git a/include/linux/evm.h b/include/linux/evm.h new file mode 100644 index 000000000000..9fc13a760928 --- /dev/null +++ b/include/linux/evm.h | |||
| @@ -0,0 +1,100 @@ | |||
| 1 | /* | ||
| 2 | * evm.h | ||
| 3 | * | ||
| 4 | * Copyright (c) 2009 IBM Corporation | ||
| 5 | * Author: Mimi Zohar <zohar@us.ibm.com> | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef _LINUX_EVM_H | ||
| 9 | #define _LINUX_EVM_H | ||
| 10 | |||
| 11 | #include <linux/integrity.h> | ||
| 12 | #include <linux/xattr.h> | ||
| 13 | |||
| 14 | struct integrity_iint_cache; | ||
| 15 | |||
| 16 | #ifdef CONFIG_EVM | ||
| 17 | extern enum integrity_status evm_verifyxattr(struct dentry *dentry, | ||
| 18 | const char *xattr_name, | ||
| 19 | void *xattr_value, | ||
| 20 | size_t xattr_value_len, | ||
| 21 | struct integrity_iint_cache *iint); | ||
| 22 | extern int evm_inode_setattr(struct dentry *dentry, struct iattr *attr); | ||
| 23 | extern void evm_inode_post_setattr(struct dentry *dentry, int ia_valid); | ||
| 24 | extern int evm_inode_setxattr(struct dentry *dentry, const char *name, | ||
| 25 | const void *value, size_t size); | ||
| 26 | extern void evm_inode_post_setxattr(struct dentry *dentry, | ||
| 27 | const char *xattr_name, | ||
| 28 | const void *xattr_value, | ||
| 29 | size_t xattr_value_len); | ||
| 30 | extern int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name); | ||
| 31 | extern void evm_inode_post_removexattr(struct dentry *dentry, | ||
| 32 | const char *xattr_name); | ||
| 33 | extern int evm_inode_init_security(struct inode *inode, | ||
| 34 | const struct xattr *xattr_array, | ||
| 35 | struct xattr *evm); | ||
| 36 | #ifdef CONFIG_FS_POSIX_ACL | ||
| 37 | extern int posix_xattr_acl(const char *xattrname); | ||
| 38 | #else | ||
| 39 | static inline int posix_xattr_acl(const char *xattrname) | ||
| 40 | { | ||
| 41 | return 0; | ||
| 42 | } | ||
| 43 | #endif | ||
| 44 | #else | ||
| 45 | #ifdef CONFIG_INTEGRITY | ||
| 46 | static inline enum integrity_status evm_verifyxattr(struct dentry *dentry, | ||
| 47 | const char *xattr_name, | ||
| 48 | void *xattr_value, | ||
| 49 | size_t xattr_value_len, | ||
| 50 | struct integrity_iint_cache *iint) | ||
| 51 | { | ||
| 52 | return INTEGRITY_UNKNOWN; | ||
| 53 | } | ||
| 54 | #endif | ||
| 55 | |||
| 56 | static inline int evm_inode_setattr(struct dentry *dentry, struct iattr *attr) | ||
| 57 | { | ||
| 58 | return 0; | ||
| 59 | } | ||
| 60 | |||
| 61 | static inline void evm_inode_post_setattr(struct dentry *dentry, int ia_valid) | ||
| 62 | { | ||
| 63 | return; | ||
| 64 | } | ||
| 65 | |||
| 66 | static inline int evm_inode_setxattr(struct dentry *dentry, const char *name, | ||
| 67 | const void *value, size_t size) | ||
| 68 | { | ||
| 69 | return 0; | ||
| 70 | } | ||
| 71 | |||
| 72 | static inline void evm_inode_post_setxattr(struct dentry *dentry, | ||
| 73 | const char *xattr_name, | ||
| 74 | const void *xattr_value, | ||
| 75 | size_t xattr_value_len) | ||
| 76 | { | ||
| 77 | return; | ||
| 78 | } | ||
| 79 | |||
| 80 | static inline int evm_inode_removexattr(struct dentry *dentry, | ||
| 81 | const char *xattr_name) | ||
| 82 | { | ||
| 83 | return 0; | ||
| 84 | } | ||
| 85 | |||
| 86 | static inline void evm_inode_post_removexattr(struct dentry *dentry, | ||
| 87 | const char *xattr_name) | ||
| 88 | { | ||
| 89 | return; | ||
| 90 | } | ||
| 91 | |||
| 92 | static inline int evm_inode_init_security(struct inode *inode, | ||
| 93 | const struct xattr *xattr_array, | ||
| 94 | struct xattr *evm) | ||
| 95 | { | ||
| 96 | return 0; | ||
| 97 | } | ||
| 98 | |||
| 99 | #endif /* CONFIG_EVM_H */ | ||
| 100 | #endif /* LINUX_EVM_H */ | ||
diff --git a/include/linux/ima.h b/include/linux/ima.h index 09e6e62f9953..6ac8e50c6cf5 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h | |||
| @@ -15,8 +15,6 @@ struct linux_binprm; | |||
| 15 | 15 | ||
| 16 | #ifdef CONFIG_IMA | 16 | #ifdef CONFIG_IMA |
| 17 | extern int ima_bprm_check(struct linux_binprm *bprm); | 17 | extern int ima_bprm_check(struct linux_binprm *bprm); |
| 18 | extern int ima_inode_alloc(struct inode *inode); | ||
| 19 | extern void ima_inode_free(struct inode *inode); | ||
| 20 | extern int ima_file_check(struct file *file, int mask); | 18 | extern int ima_file_check(struct file *file, int mask); |
| 21 | extern void ima_file_free(struct file *file); | 19 | extern void ima_file_free(struct file *file); |
| 22 | extern int ima_file_mmap(struct file *file, unsigned long prot); | 20 | 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) | |||
| 27 | return 0; | 25 | return 0; |
| 28 | } | 26 | } |
| 29 | 27 | ||
| 30 | static inline int ima_inode_alloc(struct inode *inode) | ||
| 31 | { | ||
| 32 | return 0; | ||
| 33 | } | ||
| 34 | |||
| 35 | static inline void ima_inode_free(struct inode *inode) | ||
| 36 | { | ||
| 37 | return; | ||
| 38 | } | ||
| 39 | |||
| 40 | static inline int ima_file_check(struct file *file, int mask) | 28 | static inline int ima_file_check(struct file *file, int mask) |
| 41 | { | 29 | { |
| 42 | return 0; | 30 | return 0; |
| @@ -51,6 +39,5 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot) | |||
| 51 | { | 39 | { |
| 52 | return 0; | 40 | return 0; |
| 53 | } | 41 | } |
| 54 | |||
| 55 | #endif /* CONFIG_IMA_H */ | 42 | #endif /* CONFIG_IMA_H */ |
| 56 | #endif /* _LINUX_IMA_H */ | 43 | #endif /* _LINUX_IMA_H */ |
diff --git a/include/linux/integrity.h b/include/linux/integrity.h new file mode 100644 index 000000000000..a0c41256cb92 --- /dev/null +++ b/include/linux/integrity.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2009 IBM Corporation | ||
| 3 | * Author: Mimi Zohar <zohar@us.ibm.com> | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License as published by | ||
| 7 | * the Free Software Foundation, version 2 of the License. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #ifndef _LINUX_INTEGRITY_H | ||
| 11 | #define _LINUX_INTEGRITY_H | ||
| 12 | |||
| 13 | #include <linux/fs.h> | ||
| 14 | |||
| 15 | enum integrity_status { | ||
| 16 | INTEGRITY_PASS = 0, | ||
| 17 | INTEGRITY_FAIL, | ||
| 18 | INTEGRITY_NOLABEL, | ||
| 19 | INTEGRITY_NOXATTRS, | ||
| 20 | INTEGRITY_UNKNOWN, | ||
| 21 | }; | ||
| 22 | |||
| 23 | /* List of EVM protected security xattrs */ | ||
| 24 | #ifdef CONFIG_INTEGRITY | ||
| 25 | extern int integrity_inode_alloc(struct inode *inode); | ||
| 26 | extern void integrity_inode_free(struct inode *inode); | ||
| 27 | |||
| 28 | #else | ||
| 29 | static inline int integrity_inode_alloc(struct inode *inode) | ||
| 30 | { | ||
| 31 | return 0; | ||
| 32 | } | ||
| 33 | |||
| 34 | static inline void integrity_inode_free(struct inode *inode) | ||
| 35 | { | ||
| 36 | return; | ||
| 37 | } | ||
| 38 | #endif /* CONFIG_INTEGRITY_H */ | ||
| 39 | #endif /* _LINUX_INTEGRITY_H */ | ||
diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 46ac9a50528d..8eefcf7e95eb 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h | |||
| @@ -382,7 +382,7 @@ static inline char *pack_hex_byte(char *buf, u8 byte) | |||
| 382 | } | 382 | } |
| 383 | 383 | ||
| 384 | extern int hex_to_bin(char ch); | 384 | extern int hex_to_bin(char ch); |
| 385 | extern void hex2bin(u8 *dst, const char *src, size_t count); | 385 | extern int __must_check hex2bin(u8 *dst, const char *src, size_t count); |
| 386 | 386 | ||
| 387 | /* | 387 | /* |
| 388 | * General tracing related utility functions - trace_printk(), | 388 | * General tracing related utility functions - trace_printk(), |
diff --git a/include/linux/security.h b/include/linux/security.h index ebd2a53a3d07..19d8e04e1688 100644 --- a/include/linux/security.h +++ b/include/linux/security.h | |||
| @@ -36,6 +36,7 @@ | |||
| 36 | #include <linux/key.h> | 36 | #include <linux/key.h> |
| 37 | #include <linux/xfrm.h> | 37 | #include <linux/xfrm.h> |
| 38 | #include <linux/slab.h> | 38 | #include <linux/slab.h> |
| 39 | #include <linux/xattr.h> | ||
| 39 | #include <net/flow.h> | 40 | #include <net/flow.h> |
| 40 | 41 | ||
| 41 | /* Maximum number of letters for an LSM name string */ | 42 | /* 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, | |||
| 147 | void __user *buffer, size_t *lenp, loff_t *ppos); | 148 | void __user *buffer, size_t *lenp, loff_t *ppos); |
| 148 | #endif | 149 | #endif |
| 149 | 150 | ||
| 151 | /* security_inode_init_security callback function to write xattrs */ | ||
| 152 | typedef int (*initxattrs) (struct inode *inode, | ||
| 153 | const struct xattr *xattr_array, void *fs_data); | ||
| 154 | |||
| 150 | #ifdef CONFIG_SECURITY | 155 | #ifdef CONFIG_SECURITY |
| 151 | 156 | ||
| 152 | struct security_mnt_opts { | 157 | struct security_mnt_opts { |
| @@ -1367,7 +1372,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) | |||
| 1367 | * @inode_getsecctx: | 1372 | * @inode_getsecctx: |
| 1368 | * Returns a string containing all relavent security context information | 1373 | * Returns a string containing all relavent security context information |
| 1369 | * | 1374 | * |
| 1370 | * @inode we wish to set the security context of. | 1375 | * @inode we wish to get the security context of. |
| 1371 | * @ctx is a pointer in which to place the allocated security context. | 1376 | * @ctx is a pointer in which to place the allocated security context. |
| 1372 | * @ctxlen points to the place to put the length of @ctx. | 1377 | * @ctxlen points to the place to put the length of @ctx. |
| 1373 | * This is the main security structure. | 1378 | * This is the main security structure. |
| @@ -1655,6 +1660,8 @@ struct security_operations { | |||
| 1655 | extern int security_init(void); | 1660 | extern int security_init(void); |
| 1656 | extern int security_module_enable(struct security_operations *ops); | 1661 | extern int security_module_enable(struct security_operations *ops); |
| 1657 | extern int register_security(struct security_operations *ops); | 1662 | extern int register_security(struct security_operations *ops); |
| 1663 | extern void __init security_fixup_ops(struct security_operations *ops); | ||
| 1664 | |||
| 1658 | 1665 | ||
| 1659 | /* Security operations */ | 1666 | /* Security operations */ |
| 1660 | int security_ptrace_access_check(struct task_struct *child, unsigned int mode); | 1667 | 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); | |||
| 1704 | int security_inode_alloc(struct inode *inode); | 1711 | int security_inode_alloc(struct inode *inode); |
| 1705 | void security_inode_free(struct inode *inode); | 1712 | void security_inode_free(struct inode *inode); |
| 1706 | int security_inode_init_security(struct inode *inode, struct inode *dir, | 1713 | int security_inode_init_security(struct inode *inode, struct inode *dir, |
| 1707 | const struct qstr *qstr, char **name, | 1714 | const struct qstr *qstr, |
| 1708 | void **value, size_t *len); | 1715 | initxattrs initxattrs, void *fs_data); |
| 1716 | int security_old_inode_init_security(struct inode *inode, struct inode *dir, | ||
| 1717 | const struct qstr *qstr, char **name, | ||
| 1718 | void **value, size_t *len); | ||
| 1709 | int security_inode_create(struct inode *dir, struct dentry *dentry, int mode); | 1719 | int security_inode_create(struct inode *dir, struct dentry *dentry, int mode); |
| 1710 | int security_inode_link(struct dentry *old_dentry, struct inode *dir, | 1720 | int security_inode_link(struct dentry *old_dentry, struct inode *dir, |
| 1711 | struct dentry *new_dentry); | 1721 | struct dentry *new_dentry); |
| @@ -2034,11 +2044,19 @@ static inline void security_inode_free(struct inode *inode) | |||
| 2034 | static inline int security_inode_init_security(struct inode *inode, | 2044 | static inline int security_inode_init_security(struct inode *inode, |
| 2035 | struct inode *dir, | 2045 | struct inode *dir, |
| 2036 | const struct qstr *qstr, | 2046 | const struct qstr *qstr, |
| 2037 | char **name, | 2047 | initxattrs initxattrs, |
| 2038 | void **value, | 2048 | void *fs_data) |
| 2039 | size_t *len) | ||
| 2040 | { | 2049 | { |
| 2041 | return -EOPNOTSUPP; | 2050 | return 0; |
| 2051 | } | ||
| 2052 | |||
| 2053 | static inline int security_old_inode_init_security(struct inode *inode, | ||
| 2054 | struct inode *dir, | ||
| 2055 | const struct qstr *qstr, | ||
| 2056 | char **name, void **value, | ||
| 2057 | size_t *len) | ||
| 2058 | { | ||
| 2059 | return 0; | ||
| 2042 | } | 2060 | } |
| 2043 | 2061 | ||
| 2044 | static inline int security_inode_create(struct inode *dir, | 2062 | static inline int security_inode_create(struct inode *dir, |
diff --git a/include/linux/xattr.h b/include/linux/xattr.h index aed54c50aa66..e5d122031542 100644 --- a/include/linux/xattr.h +++ b/include/linux/xattr.h | |||
| @@ -30,6 +30,9 @@ | |||
| 30 | #define XATTR_USER_PREFIX_LEN (sizeof (XATTR_USER_PREFIX) - 1) | 30 | #define XATTR_USER_PREFIX_LEN (sizeof (XATTR_USER_PREFIX) - 1) |
| 31 | 31 | ||
| 32 | /* Security namespace */ | 32 | /* Security namespace */ |
| 33 | #define XATTR_EVM_SUFFIX "evm" | ||
| 34 | #define XATTR_NAME_EVM XATTR_SECURITY_PREFIX XATTR_EVM_SUFFIX | ||
| 35 | |||
| 33 | #define XATTR_SELINUX_SUFFIX "selinux" | 36 | #define XATTR_SELINUX_SUFFIX "selinux" |
| 34 | #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX | 37 | #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX |
| 35 | 38 | ||
| @@ -49,6 +52,11 @@ | |||
| 49 | #define XATTR_CAPS_SUFFIX "capability" | 52 | #define XATTR_CAPS_SUFFIX "capability" |
| 50 | #define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX | 53 | #define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX |
| 51 | 54 | ||
| 55 | #define XATTR_POSIX_ACL_ACCESS "posix_acl_access" | ||
| 56 | #define XATTR_NAME_POSIX_ACL_ACCESS XATTR_SYSTEM_PREFIX XATTR_POSIX_ACL_ACCESS | ||
| 57 | #define XATTR_POSIX_ACL_DEFAULT "posix_acl_default" | ||
| 58 | #define XATTR_NAME_POSIX_ACL_DEFAULT XATTR_SYSTEM_PREFIX XATTR_POSIX_ACL_DEFAULT | ||
| 59 | |||
| 52 | #ifdef __KERNEL__ | 60 | #ifdef __KERNEL__ |
| 53 | 61 | ||
| 54 | #include <linux/types.h> | 62 | #include <linux/types.h> |
| @@ -67,6 +75,12 @@ struct xattr_handler { | |||
| 67 | size_t size, int flags, int handler_flags); | 75 | size_t size, int flags, int handler_flags); |
| 68 | }; | 76 | }; |
| 69 | 77 | ||
| 78 | struct xattr { | ||
| 79 | char *name; | ||
| 80 | void *value; | ||
| 81 | size_t value_len; | ||
| 82 | }; | ||
| 83 | |||
| 70 | ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t); | 84 | ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t); |
| 71 | ssize_t vfs_getxattr(struct dentry *, const char *, void *, size_t); | 85 | ssize_t vfs_getxattr(struct dentry *, const char *, void *, size_t); |
| 72 | ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size); | 86 | 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, | |||
| 78 | ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size); | 92 | ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size); |
| 79 | int generic_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags); | 93 | int generic_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags); |
| 80 | int generic_removexattr(struct dentry *dentry, const char *name); | 94 | int generic_removexattr(struct dentry *dentry, const char *name); |
| 81 | 95 | ssize_t vfs_getxattr_alloc(struct dentry *dentry, const char *name, | |
| 96 | char **xattr_value, size_t size, gfp_t flags); | ||
| 97 | int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name, | ||
| 98 | const char *value, size_t size, gfp_t flags); | ||
| 82 | #endif /* __KERNEL__ */ | 99 | #endif /* __KERNEL__ */ |
| 83 | 100 | ||
| 84 | #endif /* _LINUX_XATTR_H */ | 101 | #endif /* _LINUX_XATTR_H */ |
diff --git a/kernel/cred.c b/kernel/cred.c index 8ef31f53c44c..bb55d052d858 100644 --- a/kernel/cred.c +++ b/kernel/cred.c | |||
| @@ -644,6 +644,9 @@ void __init cred_init(void) | |||
| 644 | */ | 644 | */ |
| 645 | struct cred *prepare_kernel_cred(struct task_struct *daemon) | 645 | struct cred *prepare_kernel_cred(struct task_struct *daemon) |
| 646 | { | 646 | { |
| 647 | #ifdef CONFIG_KEYS | ||
| 648 | struct thread_group_cred *tgcred; | ||
| 649 | #endif | ||
| 647 | const struct cred *old; | 650 | const struct cred *old; |
| 648 | struct cred *new; | 651 | struct cred *new; |
| 649 | 652 | ||
| @@ -651,6 +654,14 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon) | |||
| 651 | if (!new) | 654 | if (!new) |
| 652 | return NULL; | 655 | return NULL; |
| 653 | 656 | ||
| 657 | #ifdef CONFIG_KEYS | ||
| 658 | tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL); | ||
| 659 | if (!tgcred) { | ||
| 660 | kmem_cache_free(cred_jar, new); | ||
| 661 | return NULL; | ||
| 662 | } | ||
| 663 | #endif | ||
| 664 | |||
| 654 | kdebug("prepare_kernel_cred() alloc %p", new); | 665 | kdebug("prepare_kernel_cred() alloc %p", new); |
| 655 | 666 | ||
| 656 | if (daemon) | 667 | if (daemon) |
| @@ -667,8 +678,11 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon) | |||
| 667 | get_group_info(new->group_info); | 678 | get_group_info(new->group_info); |
| 668 | 679 | ||
| 669 | #ifdef CONFIG_KEYS | 680 | #ifdef CONFIG_KEYS |
| 670 | atomic_inc(&init_tgcred.usage); | 681 | atomic_set(&tgcred->usage, 1); |
| 671 | new->tgcred = &init_tgcred; | 682 | spin_lock_init(&tgcred->lock); |
| 683 | tgcred->process_keyring = NULL; | ||
| 684 | tgcred->session_keyring = NULL; | ||
| 685 | new->tgcred = tgcred; | ||
| 672 | new->request_key_auth = NULL; | 686 | new->request_key_auth = NULL; |
| 673 | new->thread_keyring = NULL; | 687 | new->thread_keyring = NULL; |
| 674 | new->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; | 688 | new->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; |
diff --git a/lib/hexdump.c b/lib/hexdump.c index f5fe6ba7a3ab..51d5ae210244 100644 --- a/lib/hexdump.c +++ b/lib/hexdump.c | |||
| @@ -38,14 +38,21 @@ EXPORT_SYMBOL(hex_to_bin); | |||
| 38 | * @dst: binary result | 38 | * @dst: binary result |
| 39 | * @src: ascii hexadecimal string | 39 | * @src: ascii hexadecimal string |
| 40 | * @count: result length | 40 | * @count: result length |
| 41 | * | ||
| 42 | * Return 0 on success, -1 in case of bad input. | ||
| 41 | */ | 43 | */ |
| 42 | void hex2bin(u8 *dst, const char *src, size_t count) | 44 | int hex2bin(u8 *dst, const char *src, size_t count) |
| 43 | { | 45 | { |
| 44 | while (count--) { | 46 | while (count--) { |
| 45 | *dst = hex_to_bin(*src++) << 4; | 47 | int hi = hex_to_bin(*src++); |
| 46 | *dst += hex_to_bin(*src++); | 48 | int lo = hex_to_bin(*src++); |
| 47 | dst++; | 49 | |
| 50 | if ((hi < 0) || (lo < 0)) | ||
| 51 | return -1; | ||
| 52 | |||
| 53 | *dst++ = (hi << 4) | lo; | ||
| 48 | } | 54 | } |
| 55 | return 0; | ||
| 49 | } | 56 | } |
| 50 | EXPORT_SYMBOL(hex2bin); | 57 | EXPORT_SYMBOL(hex2bin); |
| 51 | 58 | ||
diff --git a/mm/shmem.c b/mm/shmem.c index 32f6763f16fb..2d3577295298 100644 --- a/mm/shmem.c +++ b/mm/shmem.c | |||
| @@ -1458,7 +1458,7 @@ shmem_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) | |||
| 1458 | inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE); | 1458 | inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE); |
| 1459 | if (inode) { | 1459 | if (inode) { |
| 1460 | error = security_inode_init_security(inode, dir, | 1460 | error = security_inode_init_security(inode, dir, |
| 1461 | &dentry->d_name, NULL, | 1461 | &dentry->d_name, |
| 1462 | NULL, NULL); | 1462 | NULL, NULL); |
| 1463 | if (error) { | 1463 | if (error) { |
| 1464 | if (error != -EOPNOTSUPP) { | 1464 | if (error != -EOPNOTSUPP) { |
| @@ -1598,7 +1598,7 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s | |||
| 1598 | if (!inode) | 1598 | if (!inode) |
| 1599 | return -ENOSPC; | 1599 | return -ENOSPC; |
| 1600 | 1600 | ||
| 1601 | error = security_inode_init_security(inode, dir, &dentry->d_name, NULL, | 1601 | error = security_inode_init_security(inode, dir, &dentry->d_name, |
| 1602 | NULL, NULL); | 1602 | NULL, NULL); |
| 1603 | if (error) { | 1603 | if (error) { |
| 1604 | if (error != -EOPNOTSUPP) { | 1604 | if (error != -EOPNOTSUPP) { |
diff --git a/security/Kconfig b/security/Kconfig index e0f08b52e4ab..51bd5a0b69ae 100644 --- a/security/Kconfig +++ b/security/Kconfig | |||
| @@ -38,7 +38,9 @@ config TRUSTED_KEYS | |||
| 38 | 38 | ||
| 39 | config ENCRYPTED_KEYS | 39 | config ENCRYPTED_KEYS |
| 40 | tristate "ENCRYPTED KEYS" | 40 | tristate "ENCRYPTED KEYS" |
| 41 | depends on KEYS && TRUSTED_KEYS | 41 | depends on KEYS |
| 42 | select CRYPTO | ||
| 43 | select CRYPTO_HMAC | ||
| 42 | select CRYPTO_AES | 44 | select CRYPTO_AES |
| 43 | select CRYPTO_CBC | 45 | select CRYPTO_CBC |
| 44 | select CRYPTO_SHA256 | 46 | select CRYPTO_SHA256 |
| @@ -186,7 +188,7 @@ source security/smack/Kconfig | |||
| 186 | source security/tomoyo/Kconfig | 188 | source security/tomoyo/Kconfig |
| 187 | source security/apparmor/Kconfig | 189 | source security/apparmor/Kconfig |
| 188 | 190 | ||
| 189 | source security/integrity/ima/Kconfig | 191 | source security/integrity/Kconfig |
| 190 | 192 | ||
| 191 | choice | 193 | choice |
| 192 | prompt "Default security module" | 194 | prompt "Default security module" |
diff --git a/security/Makefile b/security/Makefile index 8bb0fe9e1ca9..a5e502f8a05b 100644 --- a/security/Makefile +++ b/security/Makefile | |||
| @@ -24,5 +24,5 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/built-in.o | |||
| 24 | obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o | 24 | obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o |
| 25 | 25 | ||
| 26 | # Object integrity file lists | 26 | # Object integrity file lists |
| 27 | subdir-$(CONFIG_IMA) += integrity/ima | 27 | subdir-$(CONFIG_INTEGRITY) += integrity |
| 28 | obj-$(CONFIG_IMA) += integrity/ima/built-in.o | 28 | obj-$(CONFIG_INTEGRITY) += integrity/built-in.o |
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 0848292982a2..69ddb47787b2 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c | |||
| @@ -200,7 +200,7 @@ void __init aa_destroy_aafs(void) | |||
| 200 | * | 200 | * |
| 201 | * Returns: error on failure | 201 | * Returns: error on failure |
| 202 | */ | 202 | */ |
| 203 | int __init aa_create_aafs(void) | 203 | static int __init aa_create_aafs(void) |
| 204 | { | 204 | { |
| 205 | int error; | 205 | int error; |
| 206 | 206 | ||
diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index 649fad88869b..7ee05c6f3c64 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include "include/capability.h" | 19 | #include "include/capability.h" |
| 20 | #include "include/context.h" | 20 | #include "include/context.h" |
| 21 | #include "include/policy.h" | 21 | #include "include/policy.h" |
| 22 | #include "include/ipc.h" | ||
| 22 | 23 | ||
| 23 | /* call back to audit ptrace fields */ | 24 | /* call back to audit ptrace fields */ |
| 24 | static void audit_cb(struct audit_buffer *ab, void *va) | 25 | static void audit_cb(struct audit_buffer *ab, void *va) |
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index b82e383beb77..9516948041ad 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include <linux/vmalloc.h> | 18 | #include <linux/vmalloc.h> |
| 19 | 19 | ||
| 20 | #include "include/audit.h" | 20 | #include "include/audit.h" |
| 21 | #include "include/apparmor.h" | ||
| 21 | 22 | ||
| 22 | 23 | ||
| 23 | /** | 24 | /** |
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index d6d9a57b5652..741dd13e089b 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c | |||
| @@ -381,11 +381,11 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) | |||
| 381 | profile->file.trans.size = size; | 381 | profile->file.trans.size = size; |
| 382 | for (i = 0; i < size; i++) { | 382 | for (i = 0; i < size; i++) { |
| 383 | char *str; | 383 | char *str; |
| 384 | int c, j, size = unpack_strdup(e, &str, NULL); | 384 | int c, j, size2 = unpack_strdup(e, &str, NULL); |
| 385 | /* unpack_strdup verifies that the last character is | 385 | /* unpack_strdup verifies that the last character is |
| 386 | * null termination byte. | 386 | * null termination byte. |
| 387 | */ | 387 | */ |
| 388 | if (!size) | 388 | if (!size2) |
| 389 | goto fail; | 389 | goto fail; |
| 390 | profile->file.trans.table[i] = str; | 390 | profile->file.trans.table[i] = str; |
| 391 | /* verify that name doesn't start with space */ | 391 | /* 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) | |||
| 393 | goto fail; | 393 | goto fail; |
| 394 | 394 | ||
| 395 | /* count internal # of internal \0 */ | 395 | /* count internal # of internal \0 */ |
| 396 | for (c = j = 0; j < size - 2; j++) { | 396 | for (c = j = 0; j < size2 - 2; j++) { |
| 397 | if (!str[j]) | 397 | if (!str[j]) |
| 398 | c++; | 398 | c++; |
| 399 | } | 399 | } |
| @@ -440,11 +440,11 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) | |||
| 440 | if (size > RLIM_NLIMITS) | 440 | if (size > RLIM_NLIMITS) |
| 441 | goto fail; | 441 | goto fail; |
| 442 | for (i = 0; i < size; i++) { | 442 | for (i = 0; i < size; i++) { |
| 443 | u64 tmp = 0; | 443 | u64 tmp2 = 0; |
| 444 | int a = aa_map_resource(i); | 444 | int a = aa_map_resource(i); |
| 445 | if (!unpack_u64(e, &tmp, NULL)) | 445 | if (!unpack_u64(e, &tmp2, NULL)) |
| 446 | goto fail; | 446 | goto fail; |
| 447 | profile->rlimits.limits[a].rlim_max = tmp; | 447 | profile->rlimits.limits[a].rlim_max = tmp2; |
| 448 | } | 448 | } |
| 449 | if (!unpack_nameX(e, AA_ARRAYEND, NULL)) | 449 | if (!unpack_nameX(e, AA_ARRAYEND, NULL)) |
| 450 | goto fail; | 450 | goto fail; |
diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index 04a2cf8d1b65..1b41c542d376 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | #include "include/context.h" | 16 | #include "include/context.h" |
| 17 | #include "include/policy.h" | 17 | #include "include/policy.h" |
| 18 | #include "include/domain.h" | 18 | #include "include/domain.h" |
| 19 | #include "include/procattr.h" | ||
| 19 | 20 | ||
| 20 | 21 | ||
| 21 | /** | 22 | /** |
diff --git a/security/commoncap.c b/security/commoncap.c index a93b3b733079..ee4f8486e5f5 100644 --- a/security/commoncap.c +++ b/security/commoncap.c | |||
| @@ -332,7 +332,8 @@ int cap_inode_killpriv(struct dentry *dentry) | |||
| 332 | */ | 332 | */ |
| 333 | static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps, | 333 | static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps, |
| 334 | struct linux_binprm *bprm, | 334 | struct linux_binprm *bprm, |
| 335 | bool *effective) | 335 | bool *effective, |
| 336 | bool *has_cap) | ||
| 336 | { | 337 | { |
| 337 | struct cred *new = bprm->cred; | 338 | struct cred *new = bprm->cred; |
| 338 | unsigned i; | 339 | unsigned i; |
| @@ -341,6 +342,9 @@ static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps, | |||
| 341 | if (caps->magic_etc & VFS_CAP_FLAGS_EFFECTIVE) | 342 | if (caps->magic_etc & VFS_CAP_FLAGS_EFFECTIVE) |
| 342 | *effective = true; | 343 | *effective = true; |
| 343 | 344 | ||
| 345 | if (caps->magic_etc & VFS_CAP_REVISION_MASK) | ||
| 346 | *has_cap = true; | ||
| 347 | |||
| 344 | CAP_FOR_EACH_U32(i) { | 348 | CAP_FOR_EACH_U32(i) { |
| 345 | __u32 permitted = caps->permitted.cap[i]; | 349 | __u32 permitted = caps->permitted.cap[i]; |
| 346 | __u32 inheritable = caps->inheritable.cap[i]; | 350 | __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 | |||
| 424 | * its xattrs and, if present, apply them to the proposed credentials being | 428 | * its xattrs and, if present, apply them to the proposed credentials being |
| 425 | * constructed by execve(). | 429 | * constructed by execve(). |
| 426 | */ | 430 | */ |
| 427 | static int get_file_caps(struct linux_binprm *bprm, bool *effective) | 431 | static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_cap) |
| 428 | { | 432 | { |
| 429 | struct dentry *dentry; | 433 | struct dentry *dentry; |
| 430 | int rc = 0; | 434 | int rc = 0; |
| @@ -450,7 +454,7 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective) | |||
| 450 | goto out; | 454 | goto out; |
| 451 | } | 455 | } |
| 452 | 456 | ||
| 453 | rc = bprm_caps_from_vfs_caps(&vcaps, bprm, effective); | 457 | rc = bprm_caps_from_vfs_caps(&vcaps, bprm, effective, has_cap); |
| 454 | if (rc == -EINVAL) | 458 | if (rc == -EINVAL) |
| 455 | printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n", | 459 | printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n", |
| 456 | __func__, rc, bprm->filename); | 460 | __func__, rc, bprm->filename); |
| @@ -475,11 +479,11 @@ int cap_bprm_set_creds(struct linux_binprm *bprm) | |||
| 475 | { | 479 | { |
| 476 | const struct cred *old = current_cred(); | 480 | const struct cred *old = current_cred(); |
| 477 | struct cred *new = bprm->cred; | 481 | struct cred *new = bprm->cred; |
| 478 | bool effective; | 482 | bool effective, has_cap = false; |
| 479 | int ret; | 483 | int ret; |
| 480 | 484 | ||
| 481 | effective = false; | 485 | effective = false; |
| 482 | ret = get_file_caps(bprm, &effective); | 486 | ret = get_file_caps(bprm, &effective, &has_cap); |
| 483 | if (ret < 0) | 487 | if (ret < 0) |
| 484 | return ret; | 488 | return ret; |
| 485 | 489 | ||
| @@ -489,7 +493,7 @@ int cap_bprm_set_creds(struct linux_binprm *bprm) | |||
| 489 | * for a setuid root binary run by a non-root user. Do set it | 493 | * for a setuid root binary run by a non-root user. Do set it |
| 490 | * for a root user just to cause least surprise to an admin. | 494 | * for a root user just to cause least surprise to an admin. |
| 491 | */ | 495 | */ |
| 492 | if (effective && new->uid != 0 && new->euid == 0) { | 496 | if (has_cap && new->uid != 0 && new->euid == 0) { |
| 493 | warn_setuid_and_fcaps_mixed(bprm->filename); | 497 | warn_setuid_and_fcaps_mixed(bprm->filename); |
| 494 | goto skip; | 498 | goto skip; |
| 495 | } | 499 | } |
diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig new file mode 100644 index 000000000000..4bf00acf7937 --- /dev/null +++ b/security/integrity/Kconfig | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # | ||
| 2 | config INTEGRITY | ||
| 3 | def_bool y | ||
| 4 | depends on IMA || EVM | ||
| 5 | |||
| 6 | source security/integrity/ima/Kconfig | ||
| 7 | source security/integrity/evm/Kconfig | ||
diff --git a/security/integrity/Makefile b/security/integrity/Makefile new file mode 100644 index 000000000000..0ae44aea6516 --- /dev/null +++ b/security/integrity/Makefile | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | # | ||
| 2 | # Makefile for caching inode integrity data (iint) | ||
| 3 | # | ||
| 4 | |||
| 5 | obj-$(CONFIG_INTEGRITY) += integrity.o | ||
| 6 | |||
| 7 | integrity-y := iint.o | ||
| 8 | |||
| 9 | subdir-$(CONFIG_IMA) += ima | ||
| 10 | obj-$(CONFIG_IMA) += ima/built-in.o | ||
| 11 | subdir-$(CONFIG_EVM) += evm | ||
| 12 | obj-$(CONFIG_EVM) += evm/built-in.o | ||
diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig new file mode 100644 index 000000000000..afbb59dd262d --- /dev/null +++ b/security/integrity/evm/Kconfig | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | config EVM | ||
| 2 | boolean "EVM support" | ||
| 3 | depends on SECURITY && KEYS && (TRUSTED_KEYS=y || TRUSTED_KEYS=n) | ||
| 4 | select CRYPTO_HMAC | ||
| 5 | select CRYPTO_MD5 | ||
| 6 | select CRYPTO_SHA1 | ||
| 7 | select ENCRYPTED_KEYS | ||
| 8 | default n | ||
| 9 | help | ||
| 10 | EVM protects a file's security extended attributes against | ||
| 11 | integrity attacks. | ||
| 12 | |||
| 13 | If you are unsure how to answer this question, answer N. | ||
diff --git a/security/integrity/evm/Makefile b/security/integrity/evm/Makefile new file mode 100644 index 000000000000..7393c415a066 --- /dev/null +++ b/security/integrity/evm/Makefile | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # | ||
| 2 | # Makefile for building the Extended Verification Module(EVM) | ||
| 3 | # | ||
| 4 | obj-$(CONFIG_EVM) += evm.o | ||
| 5 | |||
| 6 | evm-y := evm_main.o evm_crypto.o evm_secfs.o | ||
| 7 | evm-$(CONFIG_FS_POSIX_ACL) += evm_posix_acl.o | ||
diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h new file mode 100644 index 000000000000..d320f5197437 --- /dev/null +++ b/security/integrity/evm/evm.h | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2005-2010 IBM Corporation | ||
| 3 | * | ||
| 4 | * Authors: | ||
| 5 | * Mimi Zohar <zohar@us.ibm.com> | ||
| 6 | * Kylene Hall <kjhall@us.ibm.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation, version 2 of the License. | ||
| 11 | * | ||
| 12 | * File: evm.h | ||
| 13 | * | ||
| 14 | */ | ||
| 15 | #include <linux/xattr.h> | ||
| 16 | #include <linux/security.h> | ||
| 17 | #include "../integrity.h" | ||
| 18 | |||
| 19 | extern int evm_initialized; | ||
| 20 | extern char *evm_hmac; | ||
| 21 | |||
| 22 | extern struct crypto_shash *hmac_tfm; | ||
| 23 | |||
| 24 | /* List of EVM protected security xattrs */ | ||
| 25 | extern char *evm_config_xattrnames[]; | ||
| 26 | |||
| 27 | extern int evm_init_key(void); | ||
| 28 | extern int evm_update_evmxattr(struct dentry *dentry, | ||
| 29 | const char *req_xattr_name, | ||
| 30 | const char *req_xattr_value, | ||
| 31 | size_t req_xattr_value_len); | ||
| 32 | extern int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, | ||
| 33 | const char *req_xattr_value, | ||
| 34 | size_t req_xattr_value_len, char *digest); | ||
| 35 | extern int evm_init_hmac(struct inode *inode, const struct xattr *xattr, | ||
| 36 | char *hmac_val); | ||
| 37 | extern int evm_init_secfs(void); | ||
| 38 | extern void evm_cleanup_secfs(void); | ||
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c new file mode 100644 index 000000000000..5dd5b140242c --- /dev/null +++ b/security/integrity/evm/evm_crypto.c | |||
| @@ -0,0 +1,216 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2005-2010 IBM Corporation | ||
| 3 | * | ||
| 4 | * Authors: | ||
| 5 | * Mimi Zohar <zohar@us.ibm.com> | ||
| 6 | * Kylene Hall <kjhall@us.ibm.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation, version 2 of the License. | ||
| 11 | * | ||
| 12 | * File: evm_crypto.c | ||
| 13 | * Using root's kernel master key (kmk), calculate the HMAC | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/module.h> | ||
| 17 | #include <linux/crypto.h> | ||
| 18 | #include <linux/xattr.h> | ||
| 19 | #include <keys/encrypted-type.h> | ||
| 20 | #include <crypto/hash.h> | ||
| 21 | #include "evm.h" | ||
| 22 | |||
| 23 | #define EVMKEY "evm-key" | ||
| 24 | #define MAX_KEY_SIZE 128 | ||
| 25 | static unsigned char evmkey[MAX_KEY_SIZE]; | ||
| 26 | static int evmkey_len = MAX_KEY_SIZE; | ||
| 27 | |||
| 28 | struct crypto_shash *hmac_tfm; | ||
| 29 | |||
| 30 | static struct shash_desc *init_desc(void) | ||
| 31 | { | ||
| 32 | int rc; | ||
| 33 | struct shash_desc *desc; | ||
| 34 | |||
| 35 | if (hmac_tfm == NULL) { | ||
| 36 | hmac_tfm = crypto_alloc_shash(evm_hmac, 0, CRYPTO_ALG_ASYNC); | ||
| 37 | if (IS_ERR(hmac_tfm)) { | ||
| 38 | pr_err("Can not allocate %s (reason: %ld)\n", | ||
| 39 | evm_hmac, PTR_ERR(hmac_tfm)); | ||
| 40 | rc = PTR_ERR(hmac_tfm); | ||
| 41 | hmac_tfm = NULL; | ||
| 42 | return ERR_PTR(rc); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(hmac_tfm), | ||
| 47 | GFP_KERNEL); | ||
| 48 | if (!desc) | ||
| 49 | return ERR_PTR(-ENOMEM); | ||
| 50 | |||
| 51 | desc->tfm = hmac_tfm; | ||
| 52 | desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; | ||
| 53 | |||
| 54 | rc = crypto_shash_setkey(hmac_tfm, evmkey, evmkey_len); | ||
| 55 | if (rc) | ||
| 56 | goto out; | ||
| 57 | rc = crypto_shash_init(desc); | ||
| 58 | out: | ||
| 59 | if (rc) { | ||
| 60 | kfree(desc); | ||
| 61 | return ERR_PTR(rc); | ||
| 62 | } | ||
| 63 | return desc; | ||
| 64 | } | ||
| 65 | |||
| 66 | /* Protect against 'cutting & pasting' security.evm xattr, include inode | ||
| 67 | * specific info. | ||
| 68 | * | ||
| 69 | * (Additional directory/file metadata needs to be added for more complete | ||
| 70 | * protection.) | ||
| 71 | */ | ||
| 72 | static void hmac_add_misc(struct shash_desc *desc, struct inode *inode, | ||
| 73 | char *digest) | ||
| 74 | { | ||
| 75 | struct h_misc { | ||
| 76 | unsigned long ino; | ||
| 77 | __u32 generation; | ||
| 78 | uid_t uid; | ||
| 79 | gid_t gid; | ||
| 80 | umode_t mode; | ||
| 81 | } hmac_misc; | ||
| 82 | |||
| 83 | memset(&hmac_misc, 0, sizeof hmac_misc); | ||
| 84 | hmac_misc.ino = inode->i_ino; | ||
| 85 | hmac_misc.generation = inode->i_generation; | ||
| 86 | hmac_misc.uid = inode->i_uid; | ||
| 87 | hmac_misc.gid = inode->i_gid; | ||
| 88 | hmac_misc.mode = inode->i_mode; | ||
| 89 | crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof hmac_misc); | ||
| 90 | crypto_shash_final(desc, digest); | ||
| 91 | } | ||
| 92 | |||
| 93 | /* | ||
| 94 | * Calculate the HMAC value across the set of protected security xattrs. | ||
| 95 | * | ||
| 96 | * Instead of retrieving the requested xattr, for performance, calculate | ||
| 97 | * the hmac using the requested xattr value. Don't alloc/free memory for | ||
| 98 | * each xattr, but attempt to re-use the previously allocated memory. | ||
| 99 | */ | ||
| 100 | int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, | ||
| 101 | const char *req_xattr_value, size_t req_xattr_value_len, | ||
| 102 | char *digest) | ||
| 103 | { | ||
| 104 | struct inode *inode = dentry->d_inode; | ||
| 105 | struct shash_desc *desc; | ||
| 106 | char **xattrname; | ||
| 107 | size_t xattr_size = 0; | ||
| 108 | char *xattr_value = NULL; | ||
| 109 | int error; | ||
| 110 | int size; | ||
| 111 | |||
| 112 | if (!inode->i_op || !inode->i_op->getxattr) | ||
| 113 | return -EOPNOTSUPP; | ||
| 114 | desc = init_desc(); | ||
| 115 | if (IS_ERR(desc)) | ||
| 116 | return PTR_ERR(desc); | ||
| 117 | |||
| 118 | error = -ENODATA; | ||
| 119 | for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) { | ||
| 120 | if ((req_xattr_name && req_xattr_value) | ||
| 121 | && !strcmp(*xattrname, req_xattr_name)) { | ||
| 122 | error = 0; | ||
| 123 | crypto_shash_update(desc, (const u8 *)req_xattr_value, | ||
| 124 | req_xattr_value_len); | ||
| 125 | continue; | ||
| 126 | } | ||
| 127 | size = vfs_getxattr_alloc(dentry, *xattrname, | ||
| 128 | &xattr_value, xattr_size, GFP_NOFS); | ||
| 129 | if (size == -ENOMEM) { | ||
| 130 | error = -ENOMEM; | ||
| 131 | goto out; | ||
| 132 | } | ||
| 133 | if (size < 0) | ||
| 134 | continue; | ||
| 135 | |||
| 136 | error = 0; | ||
| 137 | xattr_size = size; | ||
| 138 | crypto_shash_update(desc, (const u8 *)xattr_value, xattr_size); | ||
| 139 | } | ||
| 140 | hmac_add_misc(desc, inode, digest); | ||
| 141 | |||
| 142 | out: | ||
| 143 | kfree(xattr_value); | ||
| 144 | kfree(desc); | ||
| 145 | return error; | ||
| 146 | } | ||
| 147 | |||
| 148 | /* | ||
| 149 | * Calculate the hmac and update security.evm xattr | ||
| 150 | * | ||
| 151 | * Expects to be called with i_mutex locked. | ||
| 152 | */ | ||
| 153 | int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name, | ||
| 154 | const char *xattr_value, size_t xattr_value_len) | ||
| 155 | { | ||
| 156 | struct inode *inode = dentry->d_inode; | ||
| 157 | struct evm_ima_xattr_data xattr_data; | ||
| 158 | int rc = 0; | ||
| 159 | |||
| 160 | rc = evm_calc_hmac(dentry, xattr_name, xattr_value, | ||
| 161 | xattr_value_len, xattr_data.digest); | ||
| 162 | if (rc == 0) { | ||
| 163 | xattr_data.type = EVM_XATTR_HMAC; | ||
| 164 | rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_EVM, | ||
| 165 | &xattr_data, | ||
| 166 | sizeof(xattr_data), 0); | ||
| 167 | } | ||
| 168 | else if (rc == -ENODATA) | ||
| 169 | rc = inode->i_op->removexattr(dentry, XATTR_NAME_EVM); | ||
| 170 | return rc; | ||
| 171 | } | ||
| 172 | |||
| 173 | int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr, | ||
| 174 | char *hmac_val) | ||
| 175 | { | ||
| 176 | struct shash_desc *desc; | ||
| 177 | |||
| 178 | desc = init_desc(); | ||
| 179 | if (IS_ERR(desc)) { | ||
| 180 | printk(KERN_INFO "init_desc failed\n"); | ||
| 181 | return PTR_ERR(desc); | ||
| 182 | } | ||
| 183 | |||
| 184 | crypto_shash_update(desc, lsm_xattr->value, lsm_xattr->value_len); | ||
| 185 | hmac_add_misc(desc, inode, hmac_val); | ||
| 186 | kfree(desc); | ||
| 187 | return 0; | ||
| 188 | } | ||
| 189 | |||
| 190 | /* | ||
| 191 | * Get the key from the TPM for the SHA1-HMAC | ||
| 192 | */ | ||
| 193 | int evm_init_key(void) | ||
| 194 | { | ||
| 195 | struct key *evm_key; | ||
| 196 | struct encrypted_key_payload *ekp; | ||
| 197 | int rc = 0; | ||
| 198 | |||
| 199 | evm_key = request_key(&key_type_encrypted, EVMKEY, NULL); | ||
| 200 | if (IS_ERR(evm_key)) | ||
| 201 | return -ENOENT; | ||
| 202 | |||
| 203 | down_read(&evm_key->sem); | ||
| 204 | ekp = evm_key->payload.data; | ||
| 205 | if (ekp->decrypted_datalen > MAX_KEY_SIZE) { | ||
| 206 | rc = -EINVAL; | ||
| 207 | goto out; | ||
| 208 | } | ||
| 209 | memcpy(evmkey, ekp->decrypted_data, ekp->decrypted_datalen); | ||
| 210 | out: | ||
| 211 | /* burn the original key contents */ | ||
| 212 | memset(ekp->decrypted_data, 0, ekp->decrypted_datalen); | ||
| 213 | up_read(&evm_key->sem); | ||
| 214 | key_put(evm_key); | ||
| 215 | return rc; | ||
| 216 | } | ||
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c new file mode 100644 index 000000000000..92d3d99a9f7b --- /dev/null +++ b/security/integrity/evm/evm_main.c | |||
| @@ -0,0 +1,384 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2005-2010 IBM Corporation | ||
| 3 | * | ||
| 4 | * Author: | ||
| 5 | * Mimi Zohar <zohar@us.ibm.com> | ||
| 6 | * Kylene Hall <kjhall@us.ibm.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation, version 2 of the License. | ||
| 11 | * | ||
| 12 | * File: evm_main.c | ||
| 13 | * implements evm_inode_setxattr, evm_inode_post_setxattr, | ||
| 14 | * evm_inode_removexattr, and evm_verifyxattr | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/crypto.h> | ||
| 19 | #include <linux/xattr.h> | ||
| 20 | #include <linux/integrity.h> | ||
| 21 | #include <linux/evm.h> | ||
| 22 | #include <crypto/hash.h> | ||
| 23 | #include "evm.h" | ||
| 24 | |||
| 25 | int evm_initialized; | ||
| 26 | |||
| 27 | char *evm_hmac = "hmac(sha1)"; | ||
| 28 | |||
| 29 | char *evm_config_xattrnames[] = { | ||
| 30 | #ifdef CONFIG_SECURITY_SELINUX | ||
| 31 | XATTR_NAME_SELINUX, | ||
| 32 | #endif | ||
| 33 | #ifdef CONFIG_SECURITY_SMACK | ||
| 34 | XATTR_NAME_SMACK, | ||
| 35 | #endif | ||
| 36 | XATTR_NAME_CAPS, | ||
| 37 | NULL | ||
| 38 | }; | ||
| 39 | |||
| 40 | static int evm_fixmode; | ||
| 41 | static int __init evm_set_fixmode(char *str) | ||
| 42 | { | ||
| 43 | if (strncmp(str, "fix", 3) == 0) | ||
| 44 | evm_fixmode = 1; | ||
| 45 | return 0; | ||
| 46 | } | ||
| 47 | __setup("evm=", evm_set_fixmode); | ||
| 48 | |||
| 49 | /* | ||
| 50 | * evm_verify_hmac - calculate and compare the HMAC with the EVM xattr | ||
| 51 | * | ||
| 52 | * Compute the HMAC on the dentry's protected set of extended attributes | ||
| 53 | * and compare it against the stored security.evm xattr. | ||
| 54 | * | ||
| 55 | * For performance: | ||
| 56 | * - use the previoulsy retrieved xattr value and length to calculate the | ||
| 57 | * HMAC.) | ||
| 58 | * - cache the verification result in the iint, when available. | ||
| 59 | * | ||
| 60 | * Returns integrity status | ||
| 61 | */ | ||
| 62 | static enum integrity_status evm_verify_hmac(struct dentry *dentry, | ||
| 63 | const char *xattr_name, | ||
| 64 | char *xattr_value, | ||
| 65 | size_t xattr_value_len, | ||
| 66 | struct integrity_iint_cache *iint) | ||
| 67 | { | ||
| 68 | struct evm_ima_xattr_data xattr_data; | ||
| 69 | enum integrity_status evm_status = INTEGRITY_PASS; | ||
| 70 | int rc; | ||
| 71 | |||
| 72 | if (iint && iint->evm_status == INTEGRITY_PASS) | ||
| 73 | return iint->evm_status; | ||
| 74 | |||
| 75 | /* if status is not PASS, try to check again - against -ENOMEM */ | ||
| 76 | |||
| 77 | rc = evm_calc_hmac(dentry, xattr_name, xattr_value, | ||
| 78 | xattr_value_len, xattr_data.digest); | ||
| 79 | if (rc < 0) { | ||
| 80 | evm_status = (rc == -ENODATA) | ||
| 81 | ? INTEGRITY_NOXATTRS : INTEGRITY_FAIL; | ||
| 82 | goto out; | ||
| 83 | } | ||
| 84 | |||
| 85 | xattr_data.type = EVM_XATTR_HMAC; | ||
| 86 | rc = vfs_xattr_cmp(dentry, XATTR_NAME_EVM, (u8 *)&xattr_data, | ||
| 87 | sizeof xattr_data, GFP_NOFS); | ||
| 88 | if (rc < 0) | ||
| 89 | evm_status = (rc == -ENODATA) | ||
| 90 | ? INTEGRITY_NOLABEL : INTEGRITY_FAIL; | ||
| 91 | out: | ||
| 92 | if (iint) | ||
| 93 | iint->evm_status = evm_status; | ||
| 94 | return evm_status; | ||
| 95 | } | ||
| 96 | |||
| 97 | static int evm_protected_xattr(const char *req_xattr_name) | ||
| 98 | { | ||
| 99 | char **xattrname; | ||
| 100 | int namelen; | ||
| 101 | int found = 0; | ||
| 102 | |||
| 103 | namelen = strlen(req_xattr_name); | ||
| 104 | for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) { | ||
| 105 | if ((strlen(*xattrname) == namelen) | ||
| 106 | && (strncmp(req_xattr_name, *xattrname, namelen) == 0)) { | ||
| 107 | found = 1; | ||
| 108 | break; | ||
| 109 | } | ||
| 110 | if (strncmp(req_xattr_name, | ||
| 111 | *xattrname + XATTR_SECURITY_PREFIX_LEN, | ||
| 112 | strlen(req_xattr_name)) == 0) { | ||
| 113 | found = 1; | ||
| 114 | break; | ||
| 115 | } | ||
| 116 | } | ||
| 117 | return found; | ||
| 118 | } | ||
| 119 | |||
| 120 | /** | ||
| 121 | * evm_verifyxattr - verify the integrity of the requested xattr | ||
| 122 | * @dentry: object of the verify xattr | ||
| 123 | * @xattr_name: requested xattr | ||
| 124 | * @xattr_value: requested xattr value | ||
| 125 | * @xattr_value_len: requested xattr value length | ||
| 126 | * | ||
| 127 | * Calculate the HMAC for the given dentry and verify it against the stored | ||
| 128 | * security.evm xattr. For performance, use the xattr value and length | ||
| 129 | * previously retrieved to calculate the HMAC. | ||
| 130 | * | ||
| 131 | * Returns the xattr integrity status. | ||
| 132 | * | ||
| 133 | * This function requires the caller to lock the inode's i_mutex before it | ||
| 134 | * is executed. | ||
| 135 | */ | ||
| 136 | enum integrity_status evm_verifyxattr(struct dentry *dentry, | ||
| 137 | const char *xattr_name, | ||
| 138 | void *xattr_value, size_t xattr_value_len, | ||
| 139 | struct integrity_iint_cache *iint) | ||
| 140 | { | ||
| 141 | if (!evm_initialized || !evm_protected_xattr(xattr_name)) | ||
| 142 | return INTEGRITY_UNKNOWN; | ||
| 143 | |||
| 144 | if (!iint) { | ||
| 145 | iint = integrity_iint_find(dentry->d_inode); | ||
| 146 | if (!iint) | ||
| 147 | return INTEGRITY_UNKNOWN; | ||
| 148 | } | ||
| 149 | return evm_verify_hmac(dentry, xattr_name, xattr_value, | ||
| 150 | xattr_value_len, iint); | ||
| 151 | } | ||
| 152 | EXPORT_SYMBOL_GPL(evm_verifyxattr); | ||
| 153 | |||
| 154 | /* | ||
| 155 | * evm_verify_current_integrity - verify the dentry's metadata integrity | ||
| 156 | * @dentry: pointer to the affected dentry | ||
| 157 | * | ||
| 158 | * Verify and return the dentry's metadata integrity. The exceptions are | ||
| 159 | * before EVM is initialized or in 'fix' mode. | ||
| 160 | */ | ||
| 161 | static enum integrity_status evm_verify_current_integrity(struct dentry *dentry) | ||
| 162 | { | ||
| 163 | struct inode *inode = dentry->d_inode; | ||
| 164 | |||
| 165 | if (!evm_initialized || !S_ISREG(inode->i_mode) || evm_fixmode) | ||
| 166 | return 0; | ||
| 167 | return evm_verify_hmac(dentry, NULL, NULL, 0, NULL); | ||
| 168 | } | ||
| 169 | |||
| 170 | /* | ||
| 171 | * evm_protect_xattr - protect the EVM extended attribute | ||
| 172 | * | ||
| 173 | * Prevent security.evm from being modified or removed without the | ||
| 174 | * necessary permissions or when the existing value is invalid. | ||
| 175 | * | ||
| 176 | * The posix xattr acls are 'system' prefixed, which normally would not | ||
| 177 | * affect security.evm. An interesting side affect of writing posix xattr | ||
| 178 | * acls is their modifying of the i_mode, which is included in security.evm. | ||
| 179 | * For posix xattr acls only, permit security.evm, even if it currently | ||
| 180 | * doesn't exist, to be updated. | ||
| 181 | */ | ||
| 182 | static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name, | ||
| 183 | const void *xattr_value, size_t xattr_value_len) | ||
| 184 | { | ||
| 185 | enum integrity_status evm_status; | ||
| 186 | |||
| 187 | if (strcmp(xattr_name, XATTR_NAME_EVM) == 0) { | ||
| 188 | if (!capable(CAP_SYS_ADMIN)) | ||
| 189 | return -EPERM; | ||
| 190 | } else if (!evm_protected_xattr(xattr_name)) { | ||
| 191 | if (!posix_xattr_acl(xattr_name)) | ||
| 192 | return 0; | ||
| 193 | evm_status = evm_verify_current_integrity(dentry); | ||
| 194 | if ((evm_status == INTEGRITY_PASS) || | ||
| 195 | (evm_status == INTEGRITY_NOXATTRS)) | ||
| 196 | return 0; | ||
| 197 | return -EPERM; | ||
| 198 | } | ||
| 199 | evm_status = evm_verify_current_integrity(dentry); | ||
| 200 | return evm_status == INTEGRITY_PASS ? 0 : -EPERM; | ||
| 201 | } | ||
| 202 | |||
| 203 | /** | ||
| 204 | * evm_inode_setxattr - protect the EVM extended attribute | ||
| 205 | * @dentry: pointer to the affected dentry | ||
| 206 | * @xattr_name: pointer to the affected extended attribute name | ||
| 207 | * @xattr_value: pointer to the new extended attribute value | ||
| 208 | * @xattr_value_len: pointer to the new extended attribute value length | ||
| 209 | * | ||
| 210 | * Updating 'security.evm' requires CAP_SYS_ADMIN privileges and that | ||
| 211 | * the current value is valid. | ||
| 212 | */ | ||
| 213 | int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name, | ||
| 214 | const void *xattr_value, size_t xattr_value_len) | ||
| 215 | { | ||
| 216 | return evm_protect_xattr(dentry, xattr_name, xattr_value, | ||
| 217 | xattr_value_len); | ||
| 218 | } | ||
| 219 | |||
| 220 | /** | ||
| 221 | * evm_inode_removexattr - protect the EVM extended attribute | ||
| 222 | * @dentry: pointer to the affected dentry | ||
| 223 | * @xattr_name: pointer to the affected extended attribute name | ||
| 224 | * | ||
| 225 | * Removing 'security.evm' requires CAP_SYS_ADMIN privileges and that | ||
| 226 | * the current value is valid. | ||
| 227 | */ | ||
| 228 | int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name) | ||
| 229 | { | ||
| 230 | return evm_protect_xattr(dentry, xattr_name, NULL, 0); | ||
| 231 | } | ||
| 232 | |||
| 233 | /** | ||
| 234 | * evm_inode_post_setxattr - update 'security.evm' to reflect the changes | ||
| 235 | * @dentry: pointer to the affected dentry | ||
| 236 | * @xattr_name: pointer to the affected extended attribute name | ||
| 237 | * @xattr_value: pointer to the new extended attribute value | ||
| 238 | * @xattr_value_len: pointer to the new extended attribute value length | ||
| 239 | * | ||
| 240 | * Update the HMAC stored in 'security.evm' to reflect the change. | ||
| 241 | * | ||
| 242 | * No need to take the i_mutex lock here, as this function is called from | ||
| 243 | * __vfs_setxattr_noperm(). The caller of which has taken the inode's | ||
| 244 | * i_mutex lock. | ||
| 245 | */ | ||
| 246 | void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, | ||
| 247 | const void *xattr_value, size_t xattr_value_len) | ||
| 248 | { | ||
| 249 | if (!evm_initialized || (!evm_protected_xattr(xattr_name) | ||
| 250 | && !posix_xattr_acl(xattr_name))) | ||
| 251 | return; | ||
| 252 | |||
| 253 | evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len); | ||
| 254 | return; | ||
| 255 | } | ||
| 256 | |||
| 257 | /** | ||
| 258 | * evm_inode_post_removexattr - update 'security.evm' after removing the xattr | ||
| 259 | * @dentry: pointer to the affected dentry | ||
| 260 | * @xattr_name: pointer to the affected extended attribute name | ||
| 261 | * | ||
| 262 | * Update the HMAC stored in 'security.evm' to reflect removal of the xattr. | ||
| 263 | */ | ||
| 264 | void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name) | ||
| 265 | { | ||
| 266 | struct inode *inode = dentry->d_inode; | ||
| 267 | |||
| 268 | if (!evm_initialized || !evm_protected_xattr(xattr_name)) | ||
| 269 | return; | ||
| 270 | |||
| 271 | mutex_lock(&inode->i_mutex); | ||
| 272 | evm_update_evmxattr(dentry, xattr_name, NULL, 0); | ||
| 273 | mutex_unlock(&inode->i_mutex); | ||
| 274 | return; | ||
| 275 | } | ||
| 276 | |||
| 277 | /** | ||
| 278 | * evm_inode_setattr - prevent updating an invalid EVM extended attribute | ||
| 279 | * @dentry: pointer to the affected dentry | ||
| 280 | */ | ||
| 281 | int evm_inode_setattr(struct dentry *dentry, struct iattr *attr) | ||
| 282 | { | ||
| 283 | unsigned int ia_valid = attr->ia_valid; | ||
| 284 | enum integrity_status evm_status; | ||
| 285 | |||
| 286 | if (!(ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))) | ||
| 287 | return 0; | ||
| 288 | evm_status = evm_verify_current_integrity(dentry); | ||
| 289 | if ((evm_status == INTEGRITY_PASS) || | ||
| 290 | (evm_status == INTEGRITY_NOXATTRS)) | ||
| 291 | return 0; | ||
| 292 | return -EPERM; | ||
| 293 | } | ||
| 294 | |||
| 295 | /** | ||
| 296 | * evm_inode_post_setattr - update 'security.evm' after modifying metadata | ||
| 297 | * @dentry: pointer to the affected dentry | ||
| 298 | * @ia_valid: for the UID and GID status | ||
| 299 | * | ||
| 300 | * For now, update the HMAC stored in 'security.evm' to reflect UID/GID | ||
| 301 | * changes. | ||
| 302 | * | ||
| 303 | * This function is called from notify_change(), which expects the caller | ||
| 304 | * to lock the inode's i_mutex. | ||
| 305 | */ | ||
| 306 | void evm_inode_post_setattr(struct dentry *dentry, int ia_valid) | ||
| 307 | { | ||
| 308 | if (!evm_initialized) | ||
| 309 | return; | ||
| 310 | |||
| 311 | if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)) | ||
| 312 | evm_update_evmxattr(dentry, NULL, NULL, 0); | ||
| 313 | return; | ||
| 314 | } | ||
| 315 | |||
| 316 | /* | ||
| 317 | * evm_inode_init_security - initializes security.evm | ||
| 318 | */ | ||
| 319 | int evm_inode_init_security(struct inode *inode, | ||
| 320 | const struct xattr *lsm_xattr, | ||
| 321 | struct xattr *evm_xattr) | ||
| 322 | { | ||
| 323 | struct evm_ima_xattr_data *xattr_data; | ||
| 324 | int rc; | ||
| 325 | |||
| 326 | if (!evm_initialized || !evm_protected_xattr(lsm_xattr->name)) | ||
| 327 | return 0; | ||
| 328 | |||
| 329 | xattr_data = kzalloc(sizeof(*xattr_data), GFP_NOFS); | ||
| 330 | if (!xattr_data) | ||
| 331 | return -ENOMEM; | ||
| 332 | |||
| 333 | xattr_data->type = EVM_XATTR_HMAC; | ||
| 334 | rc = evm_init_hmac(inode, lsm_xattr, xattr_data->digest); | ||
| 335 | if (rc < 0) | ||
| 336 | goto out; | ||
| 337 | |||
| 338 | evm_xattr->value = xattr_data; | ||
| 339 | evm_xattr->value_len = sizeof(*xattr_data); | ||
| 340 | evm_xattr->name = kstrdup(XATTR_EVM_SUFFIX, GFP_NOFS); | ||
| 341 | return 0; | ||
| 342 | out: | ||
| 343 | kfree(xattr_data); | ||
| 344 | return rc; | ||
| 345 | } | ||
| 346 | EXPORT_SYMBOL_GPL(evm_inode_init_security); | ||
| 347 | |||
| 348 | static int __init init_evm(void) | ||
| 349 | { | ||
| 350 | int error; | ||
| 351 | |||
| 352 | error = evm_init_secfs(); | ||
| 353 | if (error < 0) { | ||
| 354 | printk(KERN_INFO "EVM: Error registering secfs\n"); | ||
| 355 | goto err; | ||
| 356 | } | ||
| 357 | err: | ||
| 358 | return error; | ||
| 359 | } | ||
| 360 | |||
| 361 | static void __exit cleanup_evm(void) | ||
| 362 | { | ||
| 363 | evm_cleanup_secfs(); | ||
| 364 | if (hmac_tfm) | ||
| 365 | crypto_free_shash(hmac_tfm); | ||
| 366 | } | ||
| 367 | |||
| 368 | /* | ||
| 369 | * evm_display_config - list the EVM protected security extended attributes | ||
| 370 | */ | ||
| 371 | static int __init evm_display_config(void) | ||
| 372 | { | ||
| 373 | char **xattrname; | ||
| 374 | |||
| 375 | for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) | ||
| 376 | printk(KERN_INFO "EVM: %s\n", *xattrname); | ||
| 377 | return 0; | ||
| 378 | } | ||
| 379 | |||
| 380 | pure_initcall(evm_display_config); | ||
| 381 | late_initcall(init_evm); | ||
| 382 | |||
| 383 | MODULE_DESCRIPTION("Extended Verification Module"); | ||
| 384 | MODULE_LICENSE("GPL"); | ||
diff --git a/security/integrity/evm/evm_posix_acl.c b/security/integrity/evm/evm_posix_acl.c new file mode 100644 index 000000000000..b1753e98bf9a --- /dev/null +++ b/security/integrity/evm/evm_posix_acl.c | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2011 IBM Corporation | ||
| 3 | * | ||
| 4 | * Author: | ||
| 5 | * Mimi Zohar <zohar@us.ibm.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation, version 2 of the License. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/xattr.h> | ||
| 14 | |||
| 15 | int posix_xattr_acl(char *xattr) | ||
| 16 | { | ||
| 17 | int xattr_len = strlen(xattr); | ||
| 18 | |||
| 19 | if ((strlen(XATTR_NAME_POSIX_ACL_ACCESS) == xattr_len) | ||
| 20 | && (strncmp(XATTR_NAME_POSIX_ACL_ACCESS, xattr, xattr_len) == 0)) | ||
| 21 | return 1; | ||
| 22 | if ((strlen(XATTR_NAME_POSIX_ACL_DEFAULT) == xattr_len) | ||
| 23 | && (strncmp(XATTR_NAME_POSIX_ACL_DEFAULT, xattr, xattr_len) == 0)) | ||
| 24 | return 1; | ||
| 25 | return 0; | ||
| 26 | } | ||
diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c new file mode 100644 index 000000000000..ac7629950578 --- /dev/null +++ b/security/integrity/evm/evm_secfs.c | |||
| @@ -0,0 +1,108 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2010 IBM Corporation | ||
| 3 | * | ||
| 4 | * Authors: | ||
| 5 | * Mimi Zohar <zohar@us.ibm.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation, version 2 of the License. | ||
| 10 | * | ||
| 11 | * File: evm_secfs.c | ||
| 12 | * - Used to signal when key is on keyring | ||
| 13 | * - Get the key and enable EVM | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/uaccess.h> | ||
| 17 | #include <linux/module.h> | ||
| 18 | #include "evm.h" | ||
| 19 | |||
| 20 | static struct dentry *evm_init_tpm; | ||
| 21 | |||
| 22 | /** | ||
| 23 | * evm_read_key - read() for <securityfs>/evm | ||
| 24 | * | ||
| 25 | * @filp: file pointer, not actually used | ||
| 26 | * @buf: where to put the result | ||
| 27 | * @count: maximum to send along | ||
| 28 | * @ppos: where to start | ||
| 29 | * | ||
| 30 | * Returns number of bytes read or error code, as appropriate | ||
| 31 | */ | ||
| 32 | static ssize_t evm_read_key(struct file *filp, char __user *buf, | ||
| 33 | size_t count, loff_t *ppos) | ||
| 34 | { | ||
| 35 | char temp[80]; | ||
| 36 | ssize_t rc; | ||
| 37 | |||
| 38 | if (*ppos != 0) | ||
| 39 | return 0; | ||
| 40 | |||
| 41 | sprintf(temp, "%d", evm_initialized); | ||
| 42 | rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp)); | ||
| 43 | |||
| 44 | return rc; | ||
| 45 | } | ||
| 46 | |||
| 47 | /** | ||
| 48 | * evm_write_key - write() for <securityfs>/evm | ||
| 49 | * @file: file pointer, not actually used | ||
| 50 | * @buf: where to get the data from | ||
| 51 | * @count: bytes sent | ||
| 52 | * @ppos: where to start | ||
| 53 | * | ||
| 54 | * Used to signal that key is on the kernel key ring. | ||
| 55 | * - get the integrity hmac key from the kernel key ring | ||
| 56 | * - create list of hmac protected extended attributes | ||
| 57 | * Returns number of bytes written or error code, as appropriate | ||
| 58 | */ | ||
| 59 | static ssize_t evm_write_key(struct file *file, const char __user *buf, | ||
| 60 | size_t count, loff_t *ppos) | ||
| 61 | { | ||
| 62 | char temp[80]; | ||
| 63 | int i, error; | ||
| 64 | |||
| 65 | if (!capable(CAP_SYS_ADMIN) || evm_initialized) | ||
| 66 | return -EPERM; | ||
| 67 | |||
| 68 | if (count >= sizeof(temp) || count == 0) | ||
| 69 | return -EINVAL; | ||
| 70 | |||
| 71 | if (copy_from_user(temp, buf, count) != 0) | ||
| 72 | return -EFAULT; | ||
| 73 | |||
| 74 | temp[count] = '\0'; | ||
| 75 | |||
| 76 | if ((sscanf(temp, "%d", &i) != 1) || (i != 1)) | ||
| 77 | return -EINVAL; | ||
| 78 | |||
| 79 | error = evm_init_key(); | ||
| 80 | if (!error) { | ||
| 81 | evm_initialized = 1; | ||
| 82 | pr_info("EVM: initialized\n"); | ||
| 83 | } else | ||
| 84 | pr_err("EVM: initialization failed\n"); | ||
| 85 | return count; | ||
| 86 | } | ||
| 87 | |||
| 88 | static const struct file_operations evm_key_ops = { | ||
| 89 | .read = evm_read_key, | ||
| 90 | .write = evm_write_key, | ||
| 91 | }; | ||
| 92 | |||
| 93 | int __init evm_init_secfs(void) | ||
| 94 | { | ||
| 95 | int error = 0; | ||
| 96 | |||
| 97 | evm_init_tpm = securityfs_create_file("evm", S_IRUSR | S_IRGRP, | ||
| 98 | NULL, NULL, &evm_key_ops); | ||
| 99 | if (!evm_init_tpm || IS_ERR(evm_init_tpm)) | ||
| 100 | error = -EFAULT; | ||
| 101 | return error; | ||
| 102 | } | ||
| 103 | |||
| 104 | void __exit evm_cleanup_secfs(void) | ||
| 105 | { | ||
| 106 | if (evm_init_tpm) | ||
| 107 | securityfs_remove(evm_init_tpm); | ||
| 108 | } | ||
diff --git a/security/integrity/iint.c b/security/integrity/iint.c new file mode 100644 index 000000000000..399641c3e846 --- /dev/null +++ b/security/integrity/iint.c | |||
| @@ -0,0 +1,172 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2008 IBM Corporation | ||
| 3 | * | ||
| 4 | * Authors: | ||
| 5 | * Mimi Zohar <zohar@us.ibm.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License as | ||
| 9 | * published by the Free Software Foundation, version 2 of the | ||
| 10 | * License. | ||
| 11 | * | ||
| 12 | * File: integrity_iint.c | ||
| 13 | * - implements the integrity hooks: integrity_inode_alloc, | ||
| 14 | * integrity_inode_free | ||
| 15 | * - cache integrity information associated with an inode | ||
| 16 | * using a rbtree tree. | ||
| 17 | */ | ||
| 18 | #include <linux/slab.h> | ||
| 19 | #include <linux/module.h> | ||
| 20 | #include <linux/spinlock.h> | ||
| 21 | #include <linux/rbtree.h> | ||
| 22 | #include "integrity.h" | ||
| 23 | |||
| 24 | static struct rb_root integrity_iint_tree = RB_ROOT; | ||
| 25 | static DEFINE_SPINLOCK(integrity_iint_lock); | ||
| 26 | static struct kmem_cache *iint_cache __read_mostly; | ||
| 27 | |||
| 28 | int iint_initialized; | ||
| 29 | |||
| 30 | /* | ||
| 31 | * __integrity_iint_find - return the iint associated with an inode | ||
| 32 | */ | ||
| 33 | static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode) | ||
| 34 | { | ||
| 35 | struct integrity_iint_cache *iint; | ||
| 36 | struct rb_node *n = integrity_iint_tree.rb_node; | ||
| 37 | |||
| 38 | assert_spin_locked(&integrity_iint_lock); | ||
| 39 | |||
| 40 | while (n) { | ||
| 41 | iint = rb_entry(n, struct integrity_iint_cache, rb_node); | ||
| 42 | |||
| 43 | if (inode < iint->inode) | ||
| 44 | n = n->rb_left; | ||
| 45 | else if (inode > iint->inode) | ||
| 46 | n = n->rb_right; | ||
| 47 | else | ||
| 48 | break; | ||
| 49 | } | ||
| 50 | if (!n) | ||
| 51 | return NULL; | ||
| 52 | |||
| 53 | return iint; | ||
| 54 | } | ||
| 55 | |||
| 56 | /* | ||
| 57 | * integrity_iint_find - return the iint associated with an inode | ||
| 58 | */ | ||
| 59 | struct integrity_iint_cache *integrity_iint_find(struct inode *inode) | ||
| 60 | { | ||
| 61 | struct integrity_iint_cache *iint; | ||
| 62 | |||
| 63 | if (!IS_IMA(inode)) | ||
| 64 | return NULL; | ||
| 65 | |||
| 66 | spin_lock(&integrity_iint_lock); | ||
| 67 | iint = __integrity_iint_find(inode); | ||
| 68 | spin_unlock(&integrity_iint_lock); | ||
| 69 | |||
| 70 | return iint; | ||
| 71 | } | ||
| 72 | |||
| 73 | static void iint_free(struct integrity_iint_cache *iint) | ||
| 74 | { | ||
| 75 | iint->version = 0; | ||
| 76 | iint->flags = 0UL; | ||
| 77 | iint->evm_status = INTEGRITY_UNKNOWN; | ||
| 78 | kmem_cache_free(iint_cache, iint); | ||
| 79 | } | ||
| 80 | |||
| 81 | /** | ||
| 82 | * integrity_inode_alloc - allocate an iint associated with an inode | ||
| 83 | * @inode: pointer to the inode | ||
| 84 | */ | ||
| 85 | int integrity_inode_alloc(struct inode *inode) | ||
| 86 | { | ||
| 87 | struct rb_node **p; | ||
| 88 | struct rb_node *new_node, *parent = NULL; | ||
| 89 | struct integrity_iint_cache *new_iint, *test_iint; | ||
| 90 | int rc; | ||
| 91 | |||
| 92 | new_iint = kmem_cache_alloc(iint_cache, GFP_NOFS); | ||
| 93 | if (!new_iint) | ||
| 94 | return -ENOMEM; | ||
| 95 | |||
| 96 | new_iint->inode = inode; | ||
| 97 | new_node = &new_iint->rb_node; | ||
| 98 | |||
| 99 | mutex_lock(&inode->i_mutex); /* i_flags */ | ||
| 100 | spin_lock(&integrity_iint_lock); | ||
| 101 | |||
| 102 | p = &integrity_iint_tree.rb_node; | ||
| 103 | while (*p) { | ||
| 104 | parent = *p; | ||
| 105 | test_iint = rb_entry(parent, struct integrity_iint_cache, | ||
| 106 | rb_node); | ||
| 107 | rc = -EEXIST; | ||
| 108 | if (inode < test_iint->inode) | ||
| 109 | p = &(*p)->rb_left; | ||
| 110 | else if (inode > test_iint->inode) | ||
| 111 | p = &(*p)->rb_right; | ||
| 112 | else | ||
| 113 | goto out_err; | ||
| 114 | } | ||
| 115 | |||
| 116 | inode->i_flags |= S_IMA; | ||
| 117 | rb_link_node(new_node, parent, p); | ||
| 118 | rb_insert_color(new_node, &integrity_iint_tree); | ||
| 119 | |||
| 120 | spin_unlock(&integrity_iint_lock); | ||
| 121 | mutex_unlock(&inode->i_mutex); /* i_flags */ | ||
| 122 | |||
| 123 | return 0; | ||
| 124 | out_err: | ||
| 125 | spin_unlock(&integrity_iint_lock); | ||
| 126 | mutex_unlock(&inode->i_mutex); /* i_flags */ | ||
| 127 | iint_free(new_iint); | ||
| 128 | |||
| 129 | return rc; | ||
| 130 | } | ||
| 131 | |||
| 132 | /** | ||
| 133 | * integrity_inode_free - called on security_inode_free | ||
| 134 | * @inode: pointer to the inode | ||
| 135 | * | ||
| 136 | * Free the integrity information(iint) associated with an inode. | ||
| 137 | */ | ||
| 138 | void integrity_inode_free(struct inode *inode) | ||
| 139 | { | ||
| 140 | struct integrity_iint_cache *iint; | ||
| 141 | |||
| 142 | if (!IS_IMA(inode)) | ||
| 143 | return; | ||
| 144 | |||
| 145 | spin_lock(&integrity_iint_lock); | ||
| 146 | iint = __integrity_iint_find(inode); | ||
| 147 | rb_erase(&iint->rb_node, &integrity_iint_tree); | ||
| 148 | spin_unlock(&integrity_iint_lock); | ||
| 149 | |||
| 150 | iint_free(iint); | ||
| 151 | } | ||
| 152 | |||
| 153 | static void init_once(void *foo) | ||
| 154 | { | ||
| 155 | struct integrity_iint_cache *iint = foo; | ||
| 156 | |||
| 157 | memset(iint, 0, sizeof *iint); | ||
| 158 | iint->version = 0; | ||
| 159 | iint->flags = 0UL; | ||
| 160 | mutex_init(&iint->mutex); | ||
| 161 | iint->evm_status = INTEGRITY_UNKNOWN; | ||
| 162 | } | ||
| 163 | |||
| 164 | static int __init integrity_iintcache_init(void) | ||
| 165 | { | ||
| 166 | iint_cache = | ||
| 167 | kmem_cache_create("iint_cache", sizeof(struct integrity_iint_cache), | ||
| 168 | 0, SLAB_PANIC, init_once); | ||
| 169 | iint_initialized = 1; | ||
| 170 | return 0; | ||
| 171 | } | ||
| 172 | security_initcall(integrity_iintcache_init); | ||
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index b6ecfd4d8d78..19c053b82303 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | config IMA | 3 | config IMA |
| 4 | bool "Integrity Measurement Architecture(IMA)" | 4 | bool "Integrity Measurement Architecture(IMA)" |
| 5 | depends on SECURITY | 5 | depends on SECURITY |
| 6 | select INTEGRITY | ||
| 6 | select SECURITYFS | 7 | select SECURITYFS |
| 7 | select CRYPTO | 8 | select CRYPTO |
| 8 | select CRYPTO_HMAC | 9 | select CRYPTO_HMAC |
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index 787c4cb916cd..5690c021de8f 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile | |||
| @@ -6,4 +6,4 @@ | |||
| 6 | obj-$(CONFIG_IMA) += ima.o | 6 | obj-$(CONFIG_IMA) += ima.o |
| 7 | 7 | ||
| 8 | ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ | 8 | ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ |
| 9 | ima_policy.o ima_iint.o ima_audit.o | 9 | ima_policy.o ima_audit.o |
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 08408bd71462..3ccf7acac6df 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h | |||
| @@ -24,18 +24,19 @@ | |||
| 24 | #include <linux/tpm.h> | 24 | #include <linux/tpm.h> |
| 25 | #include <linux/audit.h> | 25 | #include <linux/audit.h> |
| 26 | 26 | ||
| 27 | #include "../integrity.h" | ||
| 28 | |||
| 27 | enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_ASCII }; | 29 | enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_ASCII }; |
| 28 | enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 }; | 30 | enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 }; |
| 29 | 31 | ||
| 30 | /* digest size for IMA, fits SHA1 or MD5 */ | 32 | /* digest size for IMA, fits SHA1 or MD5 */ |
| 31 | #define IMA_DIGEST_SIZE 20 | 33 | #define IMA_DIGEST_SIZE SHA1_DIGEST_SIZE |
| 32 | #define IMA_EVENT_NAME_LEN_MAX 255 | 34 | #define IMA_EVENT_NAME_LEN_MAX 255 |
| 33 | 35 | ||
| 34 | #define IMA_HASH_BITS 9 | 36 | #define IMA_HASH_BITS 9 |
| 35 | #define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS) | 37 | #define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS) |
| 36 | 38 | ||
| 37 | /* set during initialization */ | 39 | /* set during initialization */ |
| 38 | extern int iint_initialized; | ||
| 39 | extern int ima_initialized; | 40 | extern int ima_initialized; |
| 40 | extern int ima_used_chip; | 41 | extern int ima_used_chip; |
| 41 | extern char *ima_hash; | 42 | extern char *ima_hash; |
| @@ -96,34 +97,21 @@ static inline unsigned long ima_hash_key(u8 *digest) | |||
| 96 | return hash_long(*digest, IMA_HASH_BITS); | 97 | return hash_long(*digest, IMA_HASH_BITS); |
| 97 | } | 98 | } |
| 98 | 99 | ||
| 99 | /* iint cache flags */ | ||
| 100 | #define IMA_MEASURED 0x01 | ||
| 101 | |||
| 102 | /* integrity data associated with an inode */ | ||
| 103 | struct ima_iint_cache { | ||
| 104 | struct rb_node rb_node; /* rooted in ima_iint_tree */ | ||
| 105 | struct inode *inode; /* back pointer to inode in question */ | ||
| 106 | u64 version; /* track inode changes */ | ||
| 107 | unsigned char flags; | ||
| 108 | u8 digest[IMA_DIGEST_SIZE]; | ||
| 109 | struct mutex mutex; /* protects: version, flags, digest */ | ||
| 110 | }; | ||
| 111 | |||
| 112 | /* LIM API function definitions */ | 100 | /* LIM API function definitions */ |
| 113 | int ima_must_measure(struct inode *inode, int mask, int function); | 101 | int ima_must_measure(struct inode *inode, int mask, int function); |
| 114 | int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file); | 102 | int ima_collect_measurement(struct integrity_iint_cache *iint, |
| 115 | void ima_store_measurement(struct ima_iint_cache *iint, struct file *file, | 103 | struct file *file); |
| 104 | void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, | ||
| 116 | const unsigned char *filename); | 105 | const unsigned char *filename); |
| 117 | int ima_store_template(struct ima_template_entry *entry, int violation, | 106 | int ima_store_template(struct ima_template_entry *entry, int violation, |
| 118 | struct inode *inode); | 107 | struct inode *inode); |
| 119 | void ima_template_show(struct seq_file *m, void *e, | 108 | void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show); |
| 120 | enum ima_show_type show); | ||
| 121 | 109 | ||
| 122 | /* rbtree tree calls to lookup, insert, delete | 110 | /* rbtree tree calls to lookup, insert, delete |
| 123 | * integrity data associated with an inode. | 111 | * integrity data associated with an inode. |
| 124 | */ | 112 | */ |
| 125 | struct ima_iint_cache *ima_iint_insert(struct inode *inode); | 113 | struct integrity_iint_cache *integrity_iint_insert(struct inode *inode); |
| 126 | struct ima_iint_cache *ima_iint_find(struct inode *inode); | 114 | struct integrity_iint_cache *integrity_iint_find(struct inode *inode); |
| 127 | 115 | ||
| 128 | /* IMA policy related functions */ | 116 | /* IMA policy related functions */ |
| 129 | enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK }; | 117 | enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK }; |
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index da36d2c085a4..0d50df04ccc4 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c | |||
| @@ -126,7 +126,8 @@ int ima_must_measure(struct inode *inode, int mask, int function) | |||
| 126 | * | 126 | * |
| 127 | * Return 0 on success, error code otherwise | 127 | * Return 0 on success, error code otherwise |
| 128 | */ | 128 | */ |
| 129 | int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file) | 129 | int ima_collect_measurement(struct integrity_iint_cache *iint, |
| 130 | struct file *file) | ||
| 130 | { | 131 | { |
| 131 | int result = -EEXIST; | 132 | int result = -EEXIST; |
| 132 | 133 | ||
| @@ -156,8 +157,8 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file) | |||
| 156 | * | 157 | * |
| 157 | * Must be called with iint->mutex held. | 158 | * Must be called with iint->mutex held. |
| 158 | */ | 159 | */ |
| 159 | void ima_store_measurement(struct ima_iint_cache *iint, struct file *file, | 160 | void ima_store_measurement(struct integrity_iint_cache *iint, |
| 160 | const unsigned char *filename) | 161 | struct file *file, const unsigned char *filename) |
| 161 | { | 162 | { |
| 162 | const char *op = "add_template_measure"; | 163 | const char *op = "add_template_measure"; |
| 163 | const char *audit_cause = "ENOMEM"; | 164 | const char *audit_cause = "ENOMEM"; |
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index ef21b96a0b42..e1aa2b482dd2 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c | |||
| @@ -287,7 +287,7 @@ static atomic_t policy_opencount = ATOMIC_INIT(1); | |||
| 287 | /* | 287 | /* |
| 288 | * ima_open_policy: sequentialize access to the policy file | 288 | * ima_open_policy: sequentialize access to the policy file |
| 289 | */ | 289 | */ |
| 290 | int ima_open_policy(struct inode * inode, struct file * filp) | 290 | static int ima_open_policy(struct inode * inode, struct file * filp) |
| 291 | { | 291 | { |
| 292 | /* No point in being allowed to open it if you aren't going to write */ | 292 | /* No point in being allowed to open it if you aren't going to write */ |
| 293 | if (!(filp->f_flags & O_WRONLY)) | 293 | if (!(filp->f_flags & O_WRONLY)) |
diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c deleted file mode 100644 index 4ae73040ab7b..000000000000 --- a/security/integrity/ima/ima_iint.c +++ /dev/null | |||
| @@ -1,169 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2008 IBM Corporation | ||
| 3 | * | ||
| 4 | * Authors: | ||
| 5 | * Mimi Zohar <zohar@us.ibm.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License as | ||
| 9 | * published by the Free Software Foundation, version 2 of the | ||
| 10 | * License. | ||
| 11 | * | ||
| 12 | * File: ima_iint.c | ||
| 13 | * - implements the IMA hooks: ima_inode_alloc, ima_inode_free | ||
| 14 | * - cache integrity information associated with an inode | ||
| 15 | * using a rbtree tree. | ||
| 16 | */ | ||
| 17 | #include <linux/slab.h> | ||
| 18 | #include <linux/module.h> | ||
| 19 | #include <linux/spinlock.h> | ||
| 20 | #include <linux/rbtree.h> | ||
| 21 | #include "ima.h" | ||
| 22 | |||
| 23 | static struct rb_root ima_iint_tree = RB_ROOT; | ||
| 24 | static DEFINE_SPINLOCK(ima_iint_lock); | ||
| 25 | static struct kmem_cache *iint_cache __read_mostly; | ||
| 26 | |||
| 27 | int iint_initialized = 0; | ||
| 28 | |||
| 29 | /* | ||
| 30 | * __ima_iint_find - return the iint associated with an inode | ||
| 31 | */ | ||
| 32 | static struct ima_iint_cache *__ima_iint_find(struct inode *inode) | ||
| 33 | { | ||
| 34 | struct ima_iint_cache *iint; | ||
| 35 | struct rb_node *n = ima_iint_tree.rb_node; | ||
| 36 | |||
| 37 | assert_spin_locked(&ima_iint_lock); | ||
| 38 | |||
| 39 | while (n) { | ||
| 40 | iint = rb_entry(n, struct ima_iint_cache, rb_node); | ||
| 41 | |||
| 42 | if (inode < iint->inode) | ||
| 43 | n = n->rb_left; | ||
| 44 | else if (inode > iint->inode) | ||
| 45 | n = n->rb_right; | ||
| 46 | else | ||
| 47 | break; | ||
| 48 | } | ||
| 49 | if (!n) | ||
| 50 | return NULL; | ||
| 51 | |||
| 52 | return iint; | ||
| 53 | } | ||
| 54 | |||
| 55 | /* | ||
| 56 | * ima_iint_find - return the iint associated with an inode | ||
| 57 | */ | ||
| 58 | struct ima_iint_cache *ima_iint_find(struct inode *inode) | ||
| 59 | { | ||
| 60 | struct ima_iint_cache *iint; | ||
| 61 | |||
| 62 | if (!IS_IMA(inode)) | ||
| 63 | return NULL; | ||
| 64 | |||
| 65 | spin_lock(&ima_iint_lock); | ||
| 66 | iint = __ima_iint_find(inode); | ||
| 67 | spin_unlock(&ima_iint_lock); | ||
| 68 | |||
| 69 | return iint; | ||
| 70 | } | ||
| 71 | |||
| 72 | static void iint_free(struct ima_iint_cache *iint) | ||
| 73 | { | ||
| 74 | iint->version = 0; | ||
| 75 | iint->flags = 0UL; | ||
| 76 | kmem_cache_free(iint_cache, iint); | ||
| 77 | } | ||
| 78 | |||
| 79 | /** | ||
| 80 | * ima_inode_alloc - allocate an iint associated with an inode | ||
| 81 | * @inode: pointer to the inode | ||
| 82 | */ | ||
| 83 | int ima_inode_alloc(struct inode *inode) | ||
| 84 | { | ||
| 85 | struct rb_node **p; | ||
| 86 | struct rb_node *new_node, *parent = NULL; | ||
| 87 | struct ima_iint_cache *new_iint, *test_iint; | ||
| 88 | int rc; | ||
| 89 | |||
| 90 | new_iint = kmem_cache_alloc(iint_cache, GFP_NOFS); | ||
| 91 | if (!new_iint) | ||
| 92 | return -ENOMEM; | ||
| 93 | |||
| 94 | new_iint->inode = inode; | ||
| 95 | new_node = &new_iint->rb_node; | ||
| 96 | |||
| 97 | mutex_lock(&inode->i_mutex); /* i_flags */ | ||
| 98 | spin_lock(&ima_iint_lock); | ||
| 99 | |||
| 100 | p = &ima_iint_tree.rb_node; | ||
| 101 | while (*p) { | ||
| 102 | parent = *p; | ||
| 103 | test_iint = rb_entry(parent, struct ima_iint_cache, rb_node); | ||
| 104 | |||
| 105 | rc = -EEXIST; | ||
| 106 | if (inode < test_iint->inode) | ||
| 107 | p = &(*p)->rb_left; | ||
| 108 | else if (inode > test_iint->inode) | ||
| 109 | p = &(*p)->rb_right; | ||
| 110 | else | ||
| 111 | goto out_err; | ||
| 112 | } | ||
| 113 | |||
| 114 | inode->i_flags |= S_IMA; | ||
| 115 | rb_link_node(new_node, parent, p); | ||
| 116 | rb_insert_color(new_node, &ima_iint_tree); | ||
| 117 | |||
| 118 | spin_unlock(&ima_iint_lock); | ||
| 119 | mutex_unlock(&inode->i_mutex); /* i_flags */ | ||
| 120 | |||
| 121 | return 0; | ||
| 122 | out_err: | ||
| 123 | spin_unlock(&ima_iint_lock); | ||
| 124 | mutex_unlock(&inode->i_mutex); /* i_flags */ | ||
| 125 | iint_free(new_iint); | ||
| 126 | |||
| 127 | return rc; | ||
| 128 | } | ||
| 129 | |||
| 130 | /** | ||
| 131 | * ima_inode_free - called on security_inode_free | ||
| 132 | * @inode: pointer to the inode | ||
| 133 | * | ||
| 134 | * Free the integrity information(iint) associated with an inode. | ||
| 135 | */ | ||
| 136 | void ima_inode_free(struct inode *inode) | ||
| 137 | { | ||
| 138 | struct ima_iint_cache *iint; | ||
| 139 | |||
| 140 | if (!IS_IMA(inode)) | ||
| 141 | return; | ||
| 142 | |||
| 143 | spin_lock(&ima_iint_lock); | ||
| 144 | iint = __ima_iint_find(inode); | ||
| 145 | rb_erase(&iint->rb_node, &ima_iint_tree); | ||
| 146 | spin_unlock(&ima_iint_lock); | ||
| 147 | |||
| 148 | iint_free(iint); | ||
| 149 | } | ||
| 150 | |||
| 151 | static void init_once(void *foo) | ||
| 152 | { | ||
| 153 | struct ima_iint_cache *iint = foo; | ||
| 154 | |||
| 155 | memset(iint, 0, sizeof *iint); | ||
| 156 | iint->version = 0; | ||
| 157 | iint->flags = 0UL; | ||
| 158 | mutex_init(&iint->mutex); | ||
| 159 | } | ||
| 160 | |||
| 161 | static int __init ima_iintcache_init(void) | ||
| 162 | { | ||
| 163 | iint_cache = | ||
| 164 | kmem_cache_create("iint_cache", sizeof(struct ima_iint_cache), 0, | ||
| 165 | SLAB_PANIC, init_once); | ||
| 166 | iint_initialized = 1; | ||
| 167 | return 0; | ||
| 168 | } | ||
| 169 | security_initcall(ima_iintcache_init); | ||
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 26b46ff74663..1eff5cb001e5 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include <linux/mount.h> | 22 | #include <linux/mount.h> |
| 23 | #include <linux/mman.h> | 23 | #include <linux/mman.h> |
| 24 | #include <linux/slab.h> | 24 | #include <linux/slab.h> |
| 25 | #include <linux/ima.h> | ||
| 25 | 26 | ||
| 26 | #include "ima.h" | 27 | #include "ima.h" |
| 27 | 28 | ||
| @@ -82,7 +83,7 @@ out: | |||
| 82 | "open_writers"); | 83 | "open_writers"); |
| 83 | } | 84 | } |
| 84 | 85 | ||
| 85 | static void ima_check_last_writer(struct ima_iint_cache *iint, | 86 | static void ima_check_last_writer(struct integrity_iint_cache *iint, |
| 86 | struct inode *inode, | 87 | struct inode *inode, |
| 87 | struct file *file) | 88 | struct file *file) |
| 88 | { | 89 | { |
| @@ -105,12 +106,12 @@ static void ima_check_last_writer(struct ima_iint_cache *iint, | |||
| 105 | void ima_file_free(struct file *file) | 106 | void ima_file_free(struct file *file) |
| 106 | { | 107 | { |
| 107 | struct inode *inode = file->f_dentry->d_inode; | 108 | struct inode *inode = file->f_dentry->d_inode; |
| 108 | struct ima_iint_cache *iint; | 109 | struct integrity_iint_cache *iint; |
| 109 | 110 | ||
| 110 | if (!iint_initialized || !S_ISREG(inode->i_mode)) | 111 | if (!iint_initialized || !S_ISREG(inode->i_mode)) |
| 111 | return; | 112 | return; |
| 112 | 113 | ||
| 113 | iint = ima_iint_find(inode); | 114 | iint = integrity_iint_find(inode); |
| 114 | if (!iint) | 115 | if (!iint) |
| 115 | return; | 116 | return; |
| 116 | 117 | ||
| @@ -121,7 +122,7 @@ static int process_measurement(struct file *file, const unsigned char *filename, | |||
| 121 | int mask, int function) | 122 | int mask, int function) |
| 122 | { | 123 | { |
| 123 | struct inode *inode = file->f_dentry->d_inode; | 124 | struct inode *inode = file->f_dentry->d_inode; |
| 124 | struct ima_iint_cache *iint; | 125 | struct integrity_iint_cache *iint; |
| 125 | int rc = 0; | 126 | int rc = 0; |
| 126 | 127 | ||
| 127 | if (!ima_initialized || !S_ISREG(inode->i_mode)) | 128 | if (!ima_initialized || !S_ISREG(inode->i_mode)) |
| @@ -131,9 +132,9 @@ static int process_measurement(struct file *file, const unsigned char *filename, | |||
| 131 | if (rc != 0) | 132 | if (rc != 0) |
| 132 | return rc; | 133 | return rc; |
| 133 | retry: | 134 | retry: |
| 134 | iint = ima_iint_find(inode); | 135 | iint = integrity_iint_find(inode); |
| 135 | if (!iint) { | 136 | if (!iint) { |
| 136 | rc = ima_inode_alloc(inode); | 137 | rc = integrity_inode_alloc(inode); |
| 137 | if (!rc || rc == -EEXIST) | 138 | if (!rc || rc == -EEXIST) |
| 138 | goto retry; | 139 | goto retry; |
| 139 | return rc; | 140 | return rc; |
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h new file mode 100644 index 000000000000..3143a3c39868 --- /dev/null +++ b/security/integrity/integrity.h | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2009-2010 IBM Corporation | ||
| 3 | * | ||
| 4 | * Authors: | ||
| 5 | * Mimi Zohar <zohar@us.ibm.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License as | ||
| 9 | * published by the Free Software Foundation, version 2 of the | ||
| 10 | * License. | ||
| 11 | * | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/types.h> | ||
| 15 | #include <linux/integrity.h> | ||
| 16 | #include <crypto/sha.h> | ||
| 17 | |||
| 18 | /* iint cache flags */ | ||
| 19 | #define IMA_MEASURED 0x01 | ||
| 20 | |||
| 21 | enum evm_ima_xattr_type { | ||
| 22 | IMA_XATTR_DIGEST = 0x01, | ||
| 23 | EVM_XATTR_HMAC, | ||
| 24 | EVM_IMA_XATTR_DIGSIG, | ||
| 25 | }; | ||
| 26 | |||
| 27 | struct evm_ima_xattr_data { | ||
| 28 | u8 type; | ||
| 29 | u8 digest[SHA1_DIGEST_SIZE]; | ||
| 30 | } __attribute__((packed)); | ||
| 31 | |||
| 32 | /* integrity data associated with an inode */ | ||
| 33 | struct integrity_iint_cache { | ||
| 34 | struct rb_node rb_node; /* rooted in integrity_iint_tree */ | ||
| 35 | struct inode *inode; /* back pointer to inode in question */ | ||
| 36 | u64 version; /* track inode changes */ | ||
| 37 | unsigned char flags; | ||
| 38 | u8 digest[SHA1_DIGEST_SIZE]; | ||
| 39 | struct mutex mutex; /* protects: version, flags, digest */ | ||
| 40 | enum integrity_status evm_status; | ||
| 41 | }; | ||
| 42 | |||
| 43 | /* rbtree tree calls to lookup, insert, delete | ||
| 44 | * integrity data associated with an inode. | ||
| 45 | */ | ||
| 46 | struct integrity_iint_cache *integrity_iint_insert(struct inode *inode); | ||
| 47 | struct integrity_iint_cache *integrity_iint_find(struct inode *inode); | ||
| 48 | |||
| 49 | /* set during initialization */ | ||
| 50 | extern int iint_initialized; | ||
diff --git a/security/keys/Makefile b/security/keys/Makefile index b34cc6ee6900..a56f1ffdc64d 100644 --- a/security/keys/Makefile +++ b/security/keys/Makefile | |||
| @@ -14,7 +14,7 @@ obj-y := \ | |||
| 14 | user_defined.o | 14 | user_defined.o |
| 15 | 15 | ||
| 16 | obj-$(CONFIG_TRUSTED_KEYS) += trusted.o | 16 | obj-$(CONFIG_TRUSTED_KEYS) += trusted.o |
| 17 | obj-$(CONFIG_ENCRYPTED_KEYS) += ecryptfs_format.o encrypted.o | 17 | obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/ |
| 18 | obj-$(CONFIG_KEYS_COMPAT) += compat.o | 18 | obj-$(CONFIG_KEYS_COMPAT) += compat.o |
| 19 | obj-$(CONFIG_PROC_FS) += proc.o | 19 | obj-$(CONFIG_PROC_FS) += proc.o |
| 20 | obj-$(CONFIG_SYSCTL) += sysctl.o | 20 | obj-$(CONFIG_SYSCTL) += sysctl.o |
diff --git a/security/keys/encrypted-keys/Makefile b/security/keys/encrypted-keys/Makefile new file mode 100644 index 000000000000..6bc7a86d1027 --- /dev/null +++ b/security/keys/encrypted-keys/Makefile | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # | ||
| 2 | # Makefile for encrypted keys | ||
| 3 | # | ||
| 4 | |||
| 5 | obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted.o ecryptfs_format.o | ||
| 6 | obj-$(CONFIG_TRUSTED_KEYS) += masterkey_trusted.o | ||
diff --git a/security/keys/ecryptfs_format.c b/security/keys/encrypted-keys/ecryptfs_format.c index 6daa3b6ff9ed..6daa3b6ff9ed 100644 --- a/security/keys/ecryptfs_format.c +++ b/security/keys/encrypted-keys/ecryptfs_format.c | |||
diff --git a/security/keys/ecryptfs_format.h b/security/keys/encrypted-keys/ecryptfs_format.h index 40294de238bb..40294de238bb 100644 --- a/security/keys/ecryptfs_format.h +++ b/security/keys/encrypted-keys/ecryptfs_format.h | |||
diff --git a/security/keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index e7eca9ec4c65..f33804c1b4c8 100644 --- a/security/keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c | |||
| @@ -299,31 +299,6 @@ out: | |||
| 299 | } | 299 | } |
| 300 | 300 | ||
| 301 | /* | 301 | /* |
| 302 | * request_trusted_key - request the trusted key | ||
| 303 | * | ||
| 304 | * Trusted keys are sealed to PCRs and other metadata. Although userspace | ||
| 305 | * manages both trusted/encrypted key-types, like the encrypted key type | ||
| 306 | * data, trusted key type data is not visible decrypted from userspace. | ||
| 307 | */ | ||
| 308 | static struct key *request_trusted_key(const char *trusted_desc, | ||
| 309 | u8 **master_key, size_t *master_keylen) | ||
| 310 | { | ||
| 311 | struct trusted_key_payload *tpayload; | ||
| 312 | struct key *tkey; | ||
| 313 | |||
| 314 | tkey = request_key(&key_type_trusted, trusted_desc, NULL); | ||
| 315 | if (IS_ERR(tkey)) | ||
| 316 | goto error; | ||
| 317 | |||
| 318 | down_read(&tkey->sem); | ||
| 319 | tpayload = rcu_dereference(tkey->payload.data); | ||
| 320 | *master_key = tpayload->key; | ||
| 321 | *master_keylen = tpayload->key_len; | ||
| 322 | error: | ||
| 323 | return tkey; | ||
| 324 | } | ||
| 325 | |||
| 326 | /* | ||
| 327 | * request_user_key - request the user key | 302 | * request_user_key - request the user key |
| 328 | * | 303 | * |
| 329 | * Use a user provided key to encrypt/decrypt an encrypted-key. | 304 | * Use a user provided key to encrypt/decrypt an encrypted-key. |
| @@ -469,8 +444,14 @@ static struct key *request_master_key(struct encrypted_key_payload *epayload, | |||
| 469 | goto out; | 444 | goto out; |
| 470 | 445 | ||
| 471 | if (IS_ERR(mkey)) { | 446 | if (IS_ERR(mkey)) { |
| 472 | pr_info("encrypted_key: key %s not found", | 447 | int ret = PTR_ERR(epayload); |
| 473 | epayload->master_desc); | 448 | |
| 449 | if (ret == -ENOTSUPP) | ||
| 450 | pr_info("encrypted_key: key %s not supported", | ||
| 451 | epayload->master_desc); | ||
| 452 | else | ||
| 453 | pr_info("encrypted_key: key %s not found", | ||
| 454 | epayload->master_desc); | ||
| 474 | goto out; | 455 | goto out; |
| 475 | } | 456 | } |
| 476 | 457 | ||
| @@ -686,11 +667,19 @@ static int encrypted_key_decrypt(struct encrypted_key_payload *epayload, | |||
| 686 | return -EINVAL; | 667 | return -EINVAL; |
| 687 | 668 | ||
| 688 | hex_encoded_data = hex_encoded_iv + (2 * ivsize) + 2; | 669 | hex_encoded_data = hex_encoded_iv + (2 * ivsize) + 2; |
| 689 | hex2bin(epayload->iv, hex_encoded_iv, ivsize); | 670 | ret = hex2bin(epayload->iv, hex_encoded_iv, ivsize); |
| 690 | hex2bin(epayload->encrypted_data, hex_encoded_data, encrypted_datalen); | 671 | if (ret < 0) |
| 672 | return -EINVAL; | ||
| 673 | ret = hex2bin(epayload->encrypted_data, hex_encoded_data, | ||
| 674 | encrypted_datalen); | ||
| 675 | if (ret < 0) | ||
| 676 | return -EINVAL; | ||
| 691 | 677 | ||
| 692 | hmac = epayload->format + epayload->datablob_len; | 678 | hmac = epayload->format + epayload->datablob_len; |
| 693 | hex2bin(hmac, hex_encoded_data + (encrypted_datalen * 2), HASH_SIZE); | 679 | ret = hex2bin(hmac, hex_encoded_data + (encrypted_datalen * 2), |
| 680 | HASH_SIZE); | ||
| 681 | if (ret < 0) | ||
| 682 | return -EINVAL; | ||
| 694 | 683 | ||
| 695 | mkey = request_master_key(epayload, &master_key, &master_keylen); | 684 | mkey = request_master_key(epayload, &master_key, &master_keylen); |
| 696 | if (IS_ERR(mkey)) | 685 | if (IS_ERR(mkey)) |
diff --git a/security/keys/encrypted.h b/security/keys/encrypted-keys/encrypted.h index cef5e2f2b7d1..b6ade8945250 100644 --- a/security/keys/encrypted.h +++ b/security/keys/encrypted-keys/encrypted.h | |||
| @@ -2,6 +2,17 @@ | |||
| 2 | #define __ENCRYPTED_KEY_H | 2 | #define __ENCRYPTED_KEY_H |
| 3 | 3 | ||
| 4 | #define ENCRYPTED_DEBUG 0 | 4 | #define ENCRYPTED_DEBUG 0 |
| 5 | #ifdef CONFIG_TRUSTED_KEYS | ||
| 6 | extern struct key *request_trusted_key(const char *trusted_desc, | ||
| 7 | u8 **master_key, size_t *master_keylen); | ||
| 8 | #else | ||
| 9 | static inline struct key *request_trusted_key(const char *trusted_desc, | ||
| 10 | u8 **master_key, | ||
| 11 | size_t *master_keylen) | ||
| 12 | { | ||
| 13 | return ERR_PTR(-EOPNOTSUPP); | ||
| 14 | } | ||
| 15 | #endif | ||
| 5 | 16 | ||
| 6 | #if ENCRYPTED_DEBUG | 17 | #if ENCRYPTED_DEBUG |
| 7 | static inline void dump_master_key(const u8 *master_key, size_t master_keylen) | 18 | static inline void dump_master_key(const u8 *master_key, size_t master_keylen) |
diff --git a/security/keys/encrypted-keys/masterkey_trusted.c b/security/keys/encrypted-keys/masterkey_trusted.c new file mode 100644 index 000000000000..df87272e3f51 --- /dev/null +++ b/security/keys/encrypted-keys/masterkey_trusted.c | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2010 IBM Corporation | ||
| 3 | * Copyright (C) 2010 Politecnico di Torino, Italy | ||
| 4 | * TORSEC group -- http://security.polito.it | ||
| 5 | * | ||
| 6 | * Authors: | ||
| 7 | * Mimi Zohar <zohar@us.ibm.com> | ||
| 8 | * Roberto Sassu <roberto.sassu@polito.it> | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or modify | ||
| 11 | * it under the terms of the GNU General Public License as published by | ||
| 12 | * the Free Software Foundation, version 2 of the License. | ||
| 13 | * | ||
| 14 | * See Documentation/security/keys-trusted-encrypted.txt | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/uaccess.h> | ||
| 18 | #include <linux/module.h> | ||
| 19 | #include <linux/err.h> | ||
| 20 | #include <keys/trusted-type.h> | ||
| 21 | |||
| 22 | /* | ||
| 23 | * request_trusted_key - request the trusted key | ||
| 24 | * | ||
| 25 | * Trusted keys are sealed to PCRs and other metadata. Although userspace | ||
| 26 | * manages both trusted/encrypted key-types, like the encrypted key type | ||
| 27 | * data, trusted key type data is not visible decrypted from userspace. | ||
| 28 | */ | ||
| 29 | struct key *request_trusted_key(const char *trusted_desc, | ||
| 30 | u8 **master_key, size_t *master_keylen) | ||
| 31 | { | ||
| 32 | struct trusted_key_payload *tpayload; | ||
| 33 | struct key *tkey; | ||
| 34 | |||
| 35 | tkey = request_key(&key_type_trusted, trusted_desc, NULL); | ||
| 36 | if (IS_ERR(tkey)) | ||
| 37 | goto error; | ||
| 38 | |||
| 39 | down_read(&tkey->sem); | ||
| 40 | tpayload = rcu_dereference(tkey->payload.data); | ||
| 41 | *master_key = tpayload->key; | ||
| 42 | *master_keylen = tpayload->key_len; | ||
| 43 | error: | ||
| 44 | return tkey; | ||
| 45 | } | ||
diff --git a/security/keys/gc.c b/security/keys/gc.c index 89df6b5f203c..bf4d8da5a795 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | /* Key garbage collector | 1 | /* Key garbage collector |
| 2 | * | 2 | * |
| 3 | * Copyright (C) 2009 Red Hat, Inc. All Rights Reserved. | 3 | * Copyright (C) 2009-2011 Red Hat, Inc. All Rights Reserved. |
| 4 | * Written by David Howells (dhowells@redhat.com) | 4 | * Written by David Howells (dhowells@redhat.com) |
| 5 | * | 5 | * |
| 6 | * This program is free software; you can redistribute it and/or | 6 | * This program is free software; you can redistribute it and/or |
| @@ -10,6 +10,8 @@ | |||
| 10 | */ | 10 | */ |
| 11 | 11 | ||
| 12 | #include <linux/module.h> | 12 | #include <linux/module.h> |
| 13 | #include <linux/slab.h> | ||
| 14 | #include <linux/security.h> | ||
| 13 | #include <keys/keyring-type.h> | 15 | #include <keys/keyring-type.h> |
| 14 | #include "internal.h" | 16 | #include "internal.h" |
| 15 | 17 | ||
| @@ -19,17 +21,33 @@ | |||
| 19 | unsigned key_gc_delay = 5 * 60; | 21 | unsigned key_gc_delay = 5 * 60; |
| 20 | 22 | ||
| 21 | /* | 23 | /* |
| 22 | * Reaper | 24 | * Reaper for unused keys. |
| 25 | */ | ||
| 26 | static void key_garbage_collector(struct work_struct *work); | ||
| 27 | DECLARE_WORK(key_gc_work, key_garbage_collector); | ||
| 28 | |||
| 29 | /* | ||
| 30 | * Reaper for links from keyrings to dead keys. | ||
| 23 | */ | 31 | */ |
| 24 | static void key_gc_timer_func(unsigned long); | 32 | static void key_gc_timer_func(unsigned long); |
| 25 | static void key_garbage_collector(struct work_struct *); | ||
| 26 | static DEFINE_TIMER(key_gc_timer, key_gc_timer_func, 0, 0); | 33 | static DEFINE_TIMER(key_gc_timer, key_gc_timer_func, 0, 0); |
| 27 | static DECLARE_WORK(key_gc_work, key_garbage_collector); | 34 | |
| 28 | static key_serial_t key_gc_cursor; /* the last key the gc considered */ | ||
| 29 | static bool key_gc_again; | ||
| 30 | static unsigned long key_gc_executing; | ||
| 31 | static time_t key_gc_next_run = LONG_MAX; | 35 | static time_t key_gc_next_run = LONG_MAX; |
| 32 | static time_t key_gc_new_timer; | 36 | static struct key_type *key_gc_dead_keytype; |
| 37 | |||
| 38 | static unsigned long key_gc_flags; | ||
| 39 | #define KEY_GC_KEY_EXPIRED 0 /* A key expired and needs unlinking */ | ||
| 40 | #define KEY_GC_REAP_KEYTYPE 1 /* A keytype is being unregistered */ | ||
| 41 | #define KEY_GC_REAPING_KEYTYPE 2 /* Cleared when keytype reaped */ | ||
| 42 | |||
| 43 | |||
| 44 | /* | ||
| 45 | * Any key whose type gets unregistered will be re-typed to this if it can't be | ||
| 46 | * immediately unlinked. | ||
| 47 | */ | ||
| 48 | struct key_type key_type_dead = { | ||
| 49 | .name = "dead", | ||
| 50 | }; | ||
| 33 | 51 | ||
| 34 | /* | 52 | /* |
| 35 | * Schedule a garbage collection run. | 53 | * Schedule a garbage collection run. |
| @@ -42,31 +60,75 @@ void key_schedule_gc(time_t gc_at) | |||
| 42 | 60 | ||
| 43 | kenter("%ld", gc_at - now); | 61 | kenter("%ld", gc_at - now); |
| 44 | 62 | ||
| 45 | if (gc_at <= now) { | 63 | if (gc_at <= now || test_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags)) { |
| 46 | schedule_work(&key_gc_work); | 64 | kdebug("IMMEDIATE"); |
| 65 | queue_work(system_nrt_wq, &key_gc_work); | ||
| 47 | } else if (gc_at < key_gc_next_run) { | 66 | } else if (gc_at < key_gc_next_run) { |
| 67 | kdebug("DEFERRED"); | ||
| 68 | key_gc_next_run = gc_at; | ||
| 48 | expires = jiffies + (gc_at - now) * HZ; | 69 | expires = jiffies + (gc_at - now) * HZ; |
| 49 | mod_timer(&key_gc_timer, expires); | 70 | mod_timer(&key_gc_timer, expires); |
| 50 | } | 71 | } |
| 51 | } | 72 | } |
| 52 | 73 | ||
| 53 | /* | 74 | /* |
| 54 | * The garbage collector timer kicked off | 75 | * Some key's cleanup time was met after it expired, so we need to get the |
| 76 | * reaper to go through a cycle finding expired keys. | ||
| 55 | */ | 77 | */ |
| 56 | static void key_gc_timer_func(unsigned long data) | 78 | static void key_gc_timer_func(unsigned long data) |
| 57 | { | 79 | { |
| 58 | kenter(""); | 80 | kenter(""); |
| 59 | key_gc_next_run = LONG_MAX; | 81 | key_gc_next_run = LONG_MAX; |
| 60 | schedule_work(&key_gc_work); | 82 | set_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags); |
| 83 | queue_work(system_nrt_wq, &key_gc_work); | ||
| 84 | } | ||
| 85 | |||
| 86 | /* | ||
| 87 | * wait_on_bit() sleep function for uninterruptible waiting | ||
| 88 | */ | ||
| 89 | static int key_gc_wait_bit(void *flags) | ||
| 90 | { | ||
| 91 | schedule(); | ||
| 92 | return 0; | ||
| 93 | } | ||
| 94 | |||
| 95 | /* | ||
| 96 | * Reap keys of dead type. | ||
| 97 | * | ||
| 98 | * We use three flags to make sure we see three complete cycles of the garbage | ||
| 99 | * collector: the first to mark keys of that type as being dead, the second to | ||
| 100 | * collect dead links and the third to clean up the dead keys. We have to be | ||
| 101 | * careful as there may already be a cycle in progress. | ||
| 102 | * | ||
| 103 | * The caller must be holding key_types_sem. | ||
| 104 | */ | ||
| 105 | void key_gc_keytype(struct key_type *ktype) | ||
| 106 | { | ||
| 107 | kenter("%s", ktype->name); | ||
| 108 | |||
| 109 | key_gc_dead_keytype = ktype; | ||
| 110 | set_bit(KEY_GC_REAPING_KEYTYPE, &key_gc_flags); | ||
| 111 | smp_mb(); | ||
| 112 | set_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags); | ||
| 113 | |||
| 114 | kdebug("schedule"); | ||
| 115 | queue_work(system_nrt_wq, &key_gc_work); | ||
| 116 | |||
| 117 | kdebug("sleep"); | ||
| 118 | wait_on_bit(&key_gc_flags, KEY_GC_REAPING_KEYTYPE, key_gc_wait_bit, | ||
| 119 | TASK_UNINTERRUPTIBLE); | ||
| 120 | |||
| 121 | key_gc_dead_keytype = NULL; | ||
| 122 | kleave(""); | ||
| 61 | } | 123 | } |
| 62 | 124 | ||
| 63 | /* | 125 | /* |
| 64 | * Garbage collect pointers from a keyring. | 126 | * Garbage collect pointers from a keyring. |
| 65 | * | 127 | * |
| 66 | * Return true if we altered the keyring. | 128 | * Not called with any locks held. The keyring's key struct will not be |
| 129 | * deallocated under us as only our caller may deallocate it. | ||
| 67 | */ | 130 | */ |
| 68 | static bool key_gc_keyring(struct key *keyring, time_t limit) | 131 | static void key_gc_keyring(struct key *keyring, time_t limit) |
| 69 | __releases(key_serial_lock) | ||
| 70 | { | 132 | { |
| 71 | struct keyring_list *klist; | 133 | struct keyring_list *klist; |
| 72 | struct key *key; | 134 | struct key *key; |
| @@ -93,130 +155,234 @@ static bool key_gc_keyring(struct key *keyring, time_t limit) | |||
| 93 | unlock_dont_gc: | 155 | unlock_dont_gc: |
| 94 | rcu_read_unlock(); | 156 | rcu_read_unlock(); |
| 95 | dont_gc: | 157 | dont_gc: |
| 96 | kleave(" = false"); | 158 | kleave(" [no gc]"); |
| 97 | return false; | 159 | return; |
| 98 | 160 | ||
| 99 | do_gc: | 161 | do_gc: |
| 100 | rcu_read_unlock(); | 162 | rcu_read_unlock(); |
| 101 | key_gc_cursor = keyring->serial; | 163 | |
| 102 | key_get(keyring); | ||
| 103 | spin_unlock(&key_serial_lock); | ||
| 104 | keyring_gc(keyring, limit); | 164 | keyring_gc(keyring, limit); |
| 105 | key_put(keyring); | 165 | kleave(" [gc]"); |
| 106 | kleave(" = true"); | ||
| 107 | return true; | ||
| 108 | } | 166 | } |
| 109 | 167 | ||
| 110 | /* | 168 | /* |
| 111 | * Garbage collector for keys. This involves scanning the keyrings for dead, | 169 | * Garbage collect an unreferenced, detached key |
| 112 | * expired and revoked keys that have overstayed their welcome | ||
| 113 | */ | 170 | */ |
| 114 | static void key_garbage_collector(struct work_struct *work) | 171 | static noinline void key_gc_unused_key(struct key *key) |
| 115 | { | 172 | { |
| 116 | struct rb_node *rb; | 173 | key_check(key); |
| 117 | key_serial_t cursor; | 174 | |
| 118 | struct key *key, *xkey; | 175 | security_key_free(key); |
| 119 | time_t new_timer = LONG_MAX, limit, now; | 176 | |
| 120 | 177 | /* deal with the user's key tracking and quota */ | |
| 121 | now = current_kernel_time().tv_sec; | 178 | if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { |
| 122 | kenter("[%x,%ld]", key_gc_cursor, key_gc_new_timer - now); | 179 | spin_lock(&key->user->lock); |
| 123 | 180 | key->user->qnkeys--; | |
| 124 | if (test_and_set_bit(0, &key_gc_executing)) { | 181 | key->user->qnbytes -= key->quotalen; |
| 125 | key_schedule_gc(current_kernel_time().tv_sec + 1); | 182 | spin_unlock(&key->user->lock); |
| 126 | kleave(" [busy; deferring]"); | ||
| 127 | return; | ||
| 128 | } | 183 | } |
| 129 | 184 | ||
| 130 | limit = now; | 185 | atomic_dec(&key->user->nkeys); |
| 186 | if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) | ||
| 187 | atomic_dec(&key->user->nikeys); | ||
| 188 | |||
| 189 | key_user_put(key->user); | ||
| 190 | |||
| 191 | /* now throw away the key memory */ | ||
| 192 | if (key->type->destroy) | ||
| 193 | key->type->destroy(key); | ||
| 194 | |||
| 195 | kfree(key->description); | ||
| 196 | |||
| 197 | #ifdef KEY_DEBUGGING | ||
| 198 | key->magic = KEY_DEBUG_MAGIC_X; | ||
| 199 | #endif | ||
| 200 | kmem_cache_free(key_jar, key); | ||
| 201 | } | ||
| 202 | |||
| 203 | /* | ||
| 204 | * Garbage collector for unused keys. | ||
| 205 | * | ||
| 206 | * This is done in process context so that we don't have to disable interrupts | ||
| 207 | * all over the place. key_put() schedules this rather than trying to do the | ||
| 208 | * cleanup itself, which means key_put() doesn't have to sleep. | ||
| 209 | */ | ||
| 210 | static void key_garbage_collector(struct work_struct *work) | ||
| 211 | { | ||
| 212 | static u8 gc_state; /* Internal persistent state */ | ||
| 213 | #define KEY_GC_REAP_AGAIN 0x01 /* - Need another cycle */ | ||
| 214 | #define KEY_GC_REAPING_LINKS 0x02 /* - We need to reap links */ | ||
| 215 | #define KEY_GC_SET_TIMER 0x04 /* - We need to restart the timer */ | ||
| 216 | #define KEY_GC_REAPING_DEAD_1 0x10 /* - We need to mark dead keys */ | ||
| 217 | #define KEY_GC_REAPING_DEAD_2 0x20 /* - We need to reap dead key links */ | ||
| 218 | #define KEY_GC_REAPING_DEAD_3 0x40 /* - We need to reap dead keys */ | ||
| 219 | #define KEY_GC_FOUND_DEAD_KEY 0x80 /* - We found at least one dead key */ | ||
| 220 | |||
| 221 | struct rb_node *cursor; | ||
| 222 | struct key *key; | ||
| 223 | time_t new_timer, limit; | ||
| 224 | |||
| 225 | kenter("[%lx,%x]", key_gc_flags, gc_state); | ||
| 226 | |||
| 227 | limit = current_kernel_time().tv_sec; | ||
| 131 | if (limit > key_gc_delay) | 228 | if (limit > key_gc_delay) |
| 132 | limit -= key_gc_delay; | 229 | limit -= key_gc_delay; |
| 133 | else | 230 | else |
| 134 | limit = key_gc_delay; | 231 | limit = key_gc_delay; |
| 135 | 232 | ||
| 233 | /* Work out what we're going to be doing in this pass */ | ||
| 234 | gc_state &= KEY_GC_REAPING_DEAD_1 | KEY_GC_REAPING_DEAD_2; | ||
| 235 | gc_state <<= 1; | ||
| 236 | if (test_and_clear_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags)) | ||
| 237 | gc_state |= KEY_GC_REAPING_LINKS | KEY_GC_SET_TIMER; | ||
| 238 | |||
| 239 | if (test_and_clear_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags)) | ||
| 240 | gc_state |= KEY_GC_REAPING_DEAD_1; | ||
| 241 | kdebug("new pass %x", gc_state); | ||
| 242 | |||
| 243 | new_timer = LONG_MAX; | ||
| 244 | |||
| 245 | /* As only this function is permitted to remove things from the key | ||
| 246 | * serial tree, if cursor is non-NULL then it will always point to a | ||
| 247 | * valid node in the tree - even if lock got dropped. | ||
| 248 | */ | ||
| 136 | spin_lock(&key_serial_lock); | 249 | spin_lock(&key_serial_lock); |
| 250 | cursor = rb_first(&key_serial_tree); | ||
| 137 | 251 | ||
| 138 | if (unlikely(RB_EMPTY_ROOT(&key_serial_tree))) { | 252 | continue_scanning: |
| 139 | spin_unlock(&key_serial_lock); | 253 | while (cursor) { |
| 140 | clear_bit(0, &key_gc_executing); | 254 | key = rb_entry(cursor, struct key, serial_node); |
| 141 | return; | 255 | cursor = rb_next(cursor); |
| 142 | } | ||
| 143 | 256 | ||
| 144 | cursor = key_gc_cursor; | 257 | if (atomic_read(&key->usage) == 0) |
| 145 | if (cursor < 0) | 258 | goto found_unreferenced_key; |
| 146 | cursor = 0; | 259 | |
| 147 | if (cursor > 0) | 260 | if (unlikely(gc_state & KEY_GC_REAPING_DEAD_1)) { |
| 148 | new_timer = key_gc_new_timer; | 261 | if (key->type == key_gc_dead_keytype) { |
| 149 | else | 262 | gc_state |= KEY_GC_FOUND_DEAD_KEY; |
| 150 | key_gc_again = false; | 263 | set_bit(KEY_FLAG_DEAD, &key->flags); |
| 151 | 264 | key->perm = 0; | |
| 152 | /* find the first key above the cursor */ | 265 | goto skip_dead_key; |
| 153 | key = NULL; | 266 | } |
| 154 | rb = key_serial_tree.rb_node; | 267 | } |
| 155 | while (rb) { | 268 | |
| 156 | xkey = rb_entry(rb, struct key, serial_node); | 269 | if (gc_state & KEY_GC_SET_TIMER) { |
| 157 | if (cursor < xkey->serial) { | 270 | if (key->expiry > limit && key->expiry < new_timer) { |
| 158 | key = xkey; | 271 | kdebug("will expire %x in %ld", |
| 159 | rb = rb->rb_left; | 272 | key_serial(key), key->expiry - limit); |
| 160 | } else if (cursor > xkey->serial) { | 273 | new_timer = key->expiry; |
| 161 | rb = rb->rb_right; | 274 | } |
| 162 | } else { | ||
| 163 | rb = rb_next(rb); | ||
| 164 | if (!rb) | ||
| 165 | goto reached_the_end; | ||
| 166 | key = rb_entry(rb, struct key, serial_node); | ||
| 167 | break; | ||
| 168 | } | 275 | } |
| 169 | } | ||
| 170 | 276 | ||
| 171 | if (!key) | 277 | if (unlikely(gc_state & KEY_GC_REAPING_DEAD_2)) |
| 172 | goto reached_the_end; | 278 | if (key->type == key_gc_dead_keytype) |
| 279 | gc_state |= KEY_GC_FOUND_DEAD_KEY; | ||
| 173 | 280 | ||
| 174 | /* trawl through the keys looking for keyrings */ | 281 | if ((gc_state & KEY_GC_REAPING_LINKS) || |
| 175 | for (;;) { | 282 | unlikely(gc_state & KEY_GC_REAPING_DEAD_2)) { |
| 176 | if (key->expiry > limit && key->expiry < new_timer) { | 283 | if (key->type == &key_type_keyring) |
| 177 | kdebug("will expire %x in %ld", | 284 | goto found_keyring; |
| 178 | key_serial(key), key->expiry - limit); | ||
| 179 | new_timer = key->expiry; | ||
| 180 | } | 285 | } |
| 181 | 286 | ||
| 182 | if (key->type == &key_type_keyring && | 287 | if (unlikely(gc_state & KEY_GC_REAPING_DEAD_3)) |
| 183 | key_gc_keyring(key, limit)) | 288 | if (key->type == key_gc_dead_keytype) |
| 184 | /* the gc had to release our lock so that the keyring | 289 | goto destroy_dead_key; |
| 185 | * could be modified, so we have to get it again */ | ||
| 186 | goto gc_released_our_lock; | ||
| 187 | 290 | ||
| 188 | rb = rb_next(&key->serial_node); | 291 | skip_dead_key: |
| 189 | if (!rb) | 292 | if (spin_is_contended(&key_serial_lock) || need_resched()) |
| 190 | goto reached_the_end; | 293 | goto contended; |
| 191 | key = rb_entry(rb, struct key, serial_node); | ||
| 192 | } | 294 | } |
| 193 | 295 | ||
| 194 | gc_released_our_lock: | 296 | contended: |
| 195 | kdebug("gc_released_our_lock"); | ||
| 196 | key_gc_new_timer = new_timer; | ||
| 197 | key_gc_again = true; | ||
| 198 | clear_bit(0, &key_gc_executing); | ||
| 199 | schedule_work(&key_gc_work); | ||
| 200 | kleave(" [continue]"); | ||
| 201 | return; | ||
| 202 | |||
| 203 | /* when we reach the end of the run, we set the timer for the next one */ | ||
| 204 | reached_the_end: | ||
| 205 | kdebug("reached_the_end"); | ||
| 206 | spin_unlock(&key_serial_lock); | 297 | spin_unlock(&key_serial_lock); |
| 207 | key_gc_new_timer = new_timer; | 298 | |
| 208 | key_gc_cursor = 0; | 299 | maybe_resched: |
| 209 | clear_bit(0, &key_gc_executing); | 300 | if (cursor) { |
| 210 | 301 | cond_resched(); | |
| 211 | if (key_gc_again) { | 302 | spin_lock(&key_serial_lock); |
| 212 | /* there may have been a key that expired whilst we were | 303 | goto continue_scanning; |
| 213 | * scanning, so if we discarded any links we should do another | 304 | } |
| 214 | * scan */ | 305 | |
| 215 | new_timer = now + 1; | 306 | /* We've completed the pass. Set the timer if we need to and queue a |
| 216 | key_schedule_gc(new_timer); | 307 | * new cycle if necessary. We keep executing cycles until we find one |
| 217 | } else if (new_timer < LONG_MAX) { | 308 | * where we didn't reap any keys. |
| 309 | */ | ||
| 310 | kdebug("pass complete"); | ||
| 311 | |||
| 312 | if (gc_state & KEY_GC_SET_TIMER && new_timer != (time_t)LONG_MAX) { | ||
| 218 | new_timer += key_gc_delay; | 313 | new_timer += key_gc_delay; |
| 219 | key_schedule_gc(new_timer); | 314 | key_schedule_gc(new_timer); |
| 220 | } | 315 | } |
| 221 | kleave(" [end]"); | 316 | |
| 317 | if (unlikely(gc_state & KEY_GC_REAPING_DEAD_2)) { | ||
| 318 | /* Make sure everyone revalidates their keys if we marked a | ||
| 319 | * bunch as being dead and make sure all keyring ex-payloads | ||
| 320 | * are destroyed. | ||
| 321 | */ | ||
| 322 | kdebug("dead sync"); | ||
| 323 | synchronize_rcu(); | ||
| 324 | } | ||
| 325 | |||
| 326 | if (unlikely(gc_state & (KEY_GC_REAPING_DEAD_1 | | ||
| 327 | KEY_GC_REAPING_DEAD_2))) { | ||
| 328 | if (!(gc_state & KEY_GC_FOUND_DEAD_KEY)) { | ||
| 329 | /* No remaining dead keys: short circuit the remaining | ||
| 330 | * keytype reap cycles. | ||
| 331 | */ | ||
| 332 | kdebug("dead short"); | ||
| 333 | gc_state &= ~(KEY_GC_REAPING_DEAD_1 | KEY_GC_REAPING_DEAD_2); | ||
| 334 | gc_state |= KEY_GC_REAPING_DEAD_3; | ||
| 335 | } else { | ||
| 336 | gc_state |= KEY_GC_REAP_AGAIN; | ||
| 337 | } | ||
| 338 | } | ||
| 339 | |||
| 340 | if (unlikely(gc_state & KEY_GC_REAPING_DEAD_3)) { | ||
| 341 | kdebug("dead wake"); | ||
| 342 | smp_mb(); | ||
| 343 | clear_bit(KEY_GC_REAPING_KEYTYPE, &key_gc_flags); | ||
| 344 | wake_up_bit(&key_gc_flags, KEY_GC_REAPING_KEYTYPE); | ||
| 345 | } | ||
| 346 | |||
| 347 | if (gc_state & KEY_GC_REAP_AGAIN) | ||
| 348 | queue_work(system_nrt_wq, &key_gc_work); | ||
| 349 | kleave(" [end %x]", gc_state); | ||
| 350 | return; | ||
| 351 | |||
| 352 | /* We found an unreferenced key - once we've removed it from the tree, | ||
| 353 | * we can safely drop the lock. | ||
| 354 | */ | ||
| 355 | found_unreferenced_key: | ||
| 356 | kdebug("unrefd key %d", key->serial); | ||
| 357 | rb_erase(&key->serial_node, &key_serial_tree); | ||
| 358 | spin_unlock(&key_serial_lock); | ||
| 359 | |||
| 360 | key_gc_unused_key(key); | ||
| 361 | gc_state |= KEY_GC_REAP_AGAIN; | ||
| 362 | goto maybe_resched; | ||
| 363 | |||
| 364 | /* We found a keyring and we need to check the payload for links to | ||
| 365 | * dead or expired keys. We don't flag another reap immediately as we | ||
| 366 | * have to wait for the old payload to be destroyed by RCU before we | ||
| 367 | * can reap the keys to which it refers. | ||
| 368 | */ | ||
| 369 | found_keyring: | ||
| 370 | spin_unlock(&key_serial_lock); | ||
| 371 | kdebug("scan keyring %d", key->serial); | ||
| 372 | key_gc_keyring(key, limit); | ||
| 373 | goto maybe_resched; | ||
| 374 | |||
| 375 | /* We found a dead key that is still referenced. Reset its type and | ||
| 376 | * destroy its payload with its semaphore held. | ||
| 377 | */ | ||
| 378 | destroy_dead_key: | ||
| 379 | spin_unlock(&key_serial_lock); | ||
| 380 | kdebug("destroy key %d", key->serial); | ||
| 381 | down_write(&key->sem); | ||
| 382 | key->type = &key_type_dead; | ||
| 383 | if (key_gc_dead_keytype->destroy) | ||
| 384 | key_gc_dead_keytype->destroy(key); | ||
| 385 | memset(&key->payload, KEY_DESTROY, sizeof(key->payload)); | ||
| 386 | up_write(&key->sem); | ||
| 387 | goto maybe_resched; | ||
| 222 | } | 388 | } |
diff --git a/security/keys/internal.h b/security/keys/internal.h index f375152a2500..c7a7caec4830 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h | |||
| @@ -31,6 +31,7 @@ | |||
| 31 | no_printk(KERN_DEBUG FMT"\n", ##__VA_ARGS__) | 31 | no_printk(KERN_DEBUG FMT"\n", ##__VA_ARGS__) |
| 32 | #endif | 32 | #endif |
| 33 | 33 | ||
| 34 | extern struct key_type key_type_dead; | ||
| 34 | extern struct key_type key_type_user; | 35 | extern struct key_type key_type_user; |
| 35 | 36 | ||
| 36 | /*****************************************************************************/ | 37 | /*****************************************************************************/ |
| @@ -75,6 +76,7 @@ extern unsigned key_quota_maxbytes; | |||
| 75 | #define KEYQUOTA_LINK_BYTES 4 /* a link in a keyring is worth 4 bytes */ | 76 | #define KEYQUOTA_LINK_BYTES 4 /* a link in a keyring is worth 4 bytes */ |
| 76 | 77 | ||
| 77 | 78 | ||
| 79 | extern struct kmem_cache *key_jar; | ||
| 78 | extern struct rb_root key_serial_tree; | 80 | extern struct rb_root key_serial_tree; |
| 79 | extern spinlock_t key_serial_lock; | 81 | extern spinlock_t key_serial_lock; |
| 80 | extern struct mutex key_construction_mutex; | 82 | extern struct mutex key_construction_mutex; |
| @@ -146,9 +148,11 @@ extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags, | |||
| 146 | 148 | ||
| 147 | extern long join_session_keyring(const char *name); | 149 | extern long join_session_keyring(const char *name); |
| 148 | 150 | ||
| 151 | extern struct work_struct key_gc_work; | ||
| 149 | extern unsigned key_gc_delay; | 152 | extern unsigned key_gc_delay; |
| 150 | extern void keyring_gc(struct key *keyring, time_t limit); | 153 | extern void keyring_gc(struct key *keyring, time_t limit); |
| 151 | extern void key_schedule_gc(time_t expiry_at); | 154 | extern void key_schedule_gc(time_t expiry_at); |
| 155 | extern void key_gc_keytype(struct key_type *ktype); | ||
| 152 | 156 | ||
| 153 | extern int key_task_permission(const key_ref_t key_ref, | 157 | extern int key_task_permission(const key_ref_t key_ref, |
| 154 | const struct cred *cred, | 158 | const struct cred *cred, |
diff --git a/security/keys/key.c b/security/keys/key.c index f7f9d93f08d9..4414abddcb5b 100644 --- a/security/keys/key.c +++ b/security/keys/key.c | |||
| @@ -21,7 +21,7 @@ | |||
| 21 | #include <linux/user_namespace.h> | 21 | #include <linux/user_namespace.h> |
| 22 | #include "internal.h" | 22 | #include "internal.h" |
| 23 | 23 | ||
| 24 | static struct kmem_cache *key_jar; | 24 | struct kmem_cache *key_jar; |
| 25 | struct rb_root key_serial_tree; /* tree of keys indexed by serial */ | 25 | struct rb_root key_serial_tree; /* tree of keys indexed by serial */ |
| 26 | DEFINE_SPINLOCK(key_serial_lock); | 26 | DEFINE_SPINLOCK(key_serial_lock); |
| 27 | 27 | ||
| @@ -36,17 +36,9 @@ unsigned int key_quota_maxbytes = 20000; /* general key space quota */ | |||
| 36 | static LIST_HEAD(key_types_list); | 36 | static LIST_HEAD(key_types_list); |
| 37 | static DECLARE_RWSEM(key_types_sem); | 37 | static DECLARE_RWSEM(key_types_sem); |
| 38 | 38 | ||
| 39 | static void key_cleanup(struct work_struct *work); | ||
| 40 | static DECLARE_WORK(key_cleanup_task, key_cleanup); | ||
| 41 | |||
| 42 | /* We serialise key instantiation and link */ | 39 | /* We serialise key instantiation and link */ |
| 43 | DEFINE_MUTEX(key_construction_mutex); | 40 | DEFINE_MUTEX(key_construction_mutex); |
| 44 | 41 | ||
| 45 | /* Any key who's type gets unegistered will be re-typed to this */ | ||
| 46 | static struct key_type key_type_dead = { | ||
| 47 | .name = "dead", | ||
| 48 | }; | ||
| 49 | |||
| 50 | #ifdef KEY_DEBUGGING | 42 | #ifdef KEY_DEBUGGING |
| 51 | void __key_check(const struct key *key) | 43 | void __key_check(const struct key *key) |
| 52 | { | 44 | { |
| @@ -591,71 +583,6 @@ int key_reject_and_link(struct key *key, | |||
| 591 | } | 583 | } |
| 592 | EXPORT_SYMBOL(key_reject_and_link); | 584 | EXPORT_SYMBOL(key_reject_and_link); |
| 593 | 585 | ||
| 594 | /* | ||
| 595 | * Garbage collect keys in process context so that we don't have to disable | ||
| 596 | * interrupts all over the place. | ||
| 597 | * | ||
| 598 | * key_put() schedules this rather than trying to do the cleanup itself, which | ||
| 599 | * means key_put() doesn't have to sleep. | ||
| 600 | */ | ||
| 601 | static void key_cleanup(struct work_struct *work) | ||
| 602 | { | ||
| 603 | struct rb_node *_n; | ||
| 604 | struct key *key; | ||
| 605 | |||
| 606 | go_again: | ||
| 607 | /* look for a dead key in the tree */ | ||
| 608 | spin_lock(&key_serial_lock); | ||
| 609 | |||
| 610 | for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) { | ||
| 611 | key = rb_entry(_n, struct key, serial_node); | ||
| 612 | |||
| 613 | if (atomic_read(&key->usage) == 0) | ||
| 614 | goto found_dead_key; | ||
| 615 | } | ||
| 616 | |||
| 617 | spin_unlock(&key_serial_lock); | ||
| 618 | return; | ||
| 619 | |||
| 620 | found_dead_key: | ||
| 621 | /* we found a dead key - once we've removed it from the tree, we can | ||
| 622 | * drop the lock */ | ||
| 623 | rb_erase(&key->serial_node, &key_serial_tree); | ||
| 624 | spin_unlock(&key_serial_lock); | ||
| 625 | |||
| 626 | key_check(key); | ||
| 627 | |||
| 628 | security_key_free(key); | ||
| 629 | |||
| 630 | /* deal with the user's key tracking and quota */ | ||
| 631 | if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { | ||
| 632 | spin_lock(&key->user->lock); | ||
| 633 | key->user->qnkeys--; | ||
| 634 | key->user->qnbytes -= key->quotalen; | ||
| 635 | spin_unlock(&key->user->lock); | ||
| 636 | } | ||
| 637 | |||
| 638 | atomic_dec(&key->user->nkeys); | ||
| 639 | if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) | ||
| 640 | atomic_dec(&key->user->nikeys); | ||
| 641 | |||
| 642 | key_user_put(key->user); | ||
| 643 | |||
| 644 | /* now throw away the key memory */ | ||
| 645 | if (key->type->destroy) | ||
| 646 | key->type->destroy(key); | ||
| 647 | |||
| 648 | kfree(key->description); | ||
| 649 | |||
| 650 | #ifdef KEY_DEBUGGING | ||
| 651 | key->magic = KEY_DEBUG_MAGIC_X; | ||
| 652 | #endif | ||
| 653 | kmem_cache_free(key_jar, key); | ||
| 654 | |||
| 655 | /* there may, of course, be more than one key to destroy */ | ||
| 656 | goto go_again; | ||
| 657 | } | ||
| 658 | |||
| 659 | /** | 586 | /** |
| 660 | * key_put - Discard a reference to a key. | 587 | * key_put - Discard a reference to a key. |
| 661 | * @key: The key to discard a reference from. | 588 | * @key: The key to discard a reference from. |
| @@ -670,7 +597,7 @@ void key_put(struct key *key) | |||
| 670 | key_check(key); | 597 | key_check(key); |
| 671 | 598 | ||
| 672 | if (atomic_dec_and_test(&key->usage)) | 599 | if (atomic_dec_and_test(&key->usage)) |
| 673 | schedule_work(&key_cleanup_task); | 600 | queue_work(system_nrt_wq, &key_gc_work); |
| 674 | } | 601 | } |
| 675 | } | 602 | } |
| 676 | EXPORT_SYMBOL(key_put); | 603 | EXPORT_SYMBOL(key_put); |
| @@ -1048,49 +975,11 @@ EXPORT_SYMBOL(register_key_type); | |||
| 1048 | */ | 975 | */ |
| 1049 | void unregister_key_type(struct key_type *ktype) | 976 | void unregister_key_type(struct key_type *ktype) |
| 1050 | { | 977 | { |
| 1051 | struct rb_node *_n; | ||
| 1052 | struct key *key; | ||
| 1053 | |||
| 1054 | down_write(&key_types_sem); | 978 | down_write(&key_types_sem); |
| 1055 | |||
| 1056 | /* withdraw the key type */ | ||
| 1057 | list_del_init(&ktype->link); | 979 | list_del_init(&ktype->link); |
| 1058 | 980 | downgrade_write(&key_types_sem); | |
| 1059 | /* mark all the keys of this type dead */ | 981 | key_gc_keytype(ktype); |
| 1060 | spin_lock(&key_serial_lock); | 982 | up_read(&key_types_sem); |
| 1061 | |||
| 1062 | for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) { | ||
| 1063 | key = rb_entry(_n, struct key, serial_node); | ||
| 1064 | |||
| 1065 | if (key->type == ktype) { | ||
| 1066 | key->type = &key_type_dead; | ||
| 1067 | set_bit(KEY_FLAG_DEAD, &key->flags); | ||
| 1068 | } | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | spin_unlock(&key_serial_lock); | ||
| 1072 | |||
| 1073 | /* make sure everyone revalidates their keys */ | ||
| 1074 | synchronize_rcu(); | ||
| 1075 | |||
| 1076 | /* we should now be able to destroy the payloads of all the keys of | ||
| 1077 | * this type with impunity */ | ||
| 1078 | spin_lock(&key_serial_lock); | ||
| 1079 | |||
| 1080 | for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) { | ||
| 1081 | key = rb_entry(_n, struct key, serial_node); | ||
| 1082 | |||
| 1083 | if (key->type == ktype) { | ||
| 1084 | if (ktype->destroy) | ||
| 1085 | ktype->destroy(key); | ||
| 1086 | memset(&key->payload, KEY_DESTROY, sizeof(key->payload)); | ||
| 1087 | } | ||
| 1088 | } | ||
| 1089 | |||
| 1090 | spin_unlock(&key_serial_lock); | ||
| 1091 | up_write(&key_types_sem); | ||
| 1092 | |||
| 1093 | key_schedule_gc(0); | ||
| 1094 | } | 983 | } |
| 1095 | EXPORT_SYMBOL(unregister_key_type); | 984 | EXPORT_SYMBOL(unregister_key_type); |
| 1096 | 985 | ||
diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 30e242f7bd0e..37a7f3b28852 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c | |||
| @@ -860,8 +860,7 @@ void __key_link(struct key *keyring, struct key *key, | |||
| 860 | 860 | ||
| 861 | kenter("%d,%d,%p", keyring->serial, key->serial, nklist); | 861 | kenter("%d,%d,%p", keyring->serial, key->serial, nklist); |
| 862 | 862 | ||
| 863 | klist = rcu_dereference_protected(keyring->payload.subscriptions, | 863 | klist = rcu_dereference_locked_keyring(keyring); |
| 864 | rwsem_is_locked(&keyring->sem)); | ||
| 865 | 864 | ||
| 866 | atomic_inc(&key->usage); | 865 | atomic_inc(&key->usage); |
| 867 | 866 | ||
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index a3063eb3dc23..1068cb1939b3 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c | |||
| @@ -270,7 +270,7 @@ static int install_session_keyring(struct key *keyring) | |||
| 270 | if (!new) | 270 | if (!new) |
| 271 | return -ENOMEM; | 271 | return -ENOMEM; |
| 272 | 272 | ||
| 273 | ret = install_session_keyring_to_cred(new, NULL); | 273 | ret = install_session_keyring_to_cred(new, keyring); |
| 274 | if (ret < 0) { | 274 | if (ret < 0) { |
| 275 | abort_creds(new); | 275 | abort_creds(new); |
| 276 | return ret; | 276 | return ret; |
| @@ -589,12 +589,22 @@ try_again: | |||
| 589 | ret = install_user_keyrings(); | 589 | ret = install_user_keyrings(); |
| 590 | if (ret < 0) | 590 | if (ret < 0) |
| 591 | goto error; | 591 | goto error; |
| 592 | ret = install_session_keyring( | 592 | if (lflags & KEY_LOOKUP_CREATE) |
| 593 | cred->user->session_keyring); | 593 | ret = join_session_keyring(NULL); |
| 594 | else | ||
| 595 | ret = install_session_keyring( | ||
| 596 | cred->user->session_keyring); | ||
| 594 | 597 | ||
| 595 | if (ret < 0) | 598 | if (ret < 0) |
| 596 | goto error; | 599 | goto error; |
| 597 | goto reget_creds; | 600 | goto reget_creds; |
| 601 | } else if (cred->tgcred->session_keyring == | ||
| 602 | cred->user->session_keyring && | ||
| 603 | lflags & KEY_LOOKUP_CREATE) { | ||
| 604 | ret = join_session_keyring(NULL); | ||
| 605 | if (ret < 0) | ||
| 606 | goto error; | ||
| 607 | goto reget_creds; | ||
| 598 | } | 608 | } |
| 599 | 609 | ||
| 600 | rcu_read_lock(); | 610 | rcu_read_lock(); |
diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 0c33e2ea1f3c..0964fc236946 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c | |||
| @@ -779,7 +779,10 @@ static int getoptions(char *c, struct trusted_key_payload *pay, | |||
| 779 | opt->pcrinfo_len = strlen(args[0].from) / 2; | 779 | opt->pcrinfo_len = strlen(args[0].from) / 2; |
| 780 | if (opt->pcrinfo_len > MAX_PCRINFO_SIZE) | 780 | if (opt->pcrinfo_len > MAX_PCRINFO_SIZE) |
| 781 | return -EINVAL; | 781 | return -EINVAL; |
| 782 | hex2bin(opt->pcrinfo, args[0].from, opt->pcrinfo_len); | 782 | res = hex2bin(opt->pcrinfo, args[0].from, |
| 783 | opt->pcrinfo_len); | ||
| 784 | if (res < 0) | ||
| 785 | return -EINVAL; | ||
| 783 | break; | 786 | break; |
| 784 | case Opt_keyhandle: | 787 | case Opt_keyhandle: |
| 785 | res = strict_strtoul(args[0].from, 16, &handle); | 788 | res = strict_strtoul(args[0].from, 16, &handle); |
| @@ -791,12 +794,18 @@ static int getoptions(char *c, struct trusted_key_payload *pay, | |||
| 791 | case Opt_keyauth: | 794 | case Opt_keyauth: |
| 792 | if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE) | 795 | if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE) |
| 793 | return -EINVAL; | 796 | return -EINVAL; |
| 794 | hex2bin(opt->keyauth, args[0].from, SHA1_DIGEST_SIZE); | 797 | res = hex2bin(opt->keyauth, args[0].from, |
| 798 | SHA1_DIGEST_SIZE); | ||
| 799 | if (res < 0) | ||
| 800 | return -EINVAL; | ||
| 795 | break; | 801 | break; |
| 796 | case Opt_blobauth: | 802 | case Opt_blobauth: |
| 797 | if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE) | 803 | if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE) |
| 798 | return -EINVAL; | 804 | return -EINVAL; |
| 799 | hex2bin(opt->blobauth, args[0].from, SHA1_DIGEST_SIZE); | 805 | res = hex2bin(opt->blobauth, args[0].from, |
| 806 | SHA1_DIGEST_SIZE); | ||
| 807 | if (res < 0) | ||
| 808 | return -EINVAL; | ||
| 800 | break; | 809 | break; |
| 801 | case Opt_migratable: | 810 | case Opt_migratable: |
| 802 | if (*args[0].from == '0') | 811 | if (*args[0].from == '0') |
| @@ -860,7 +869,9 @@ static int datablob_parse(char *datablob, struct trusted_key_payload *p, | |||
| 860 | p->blob_len = strlen(c) / 2; | 869 | p->blob_len = strlen(c) / 2; |
| 861 | if (p->blob_len > MAX_BLOB_SIZE) | 870 | if (p->blob_len > MAX_BLOB_SIZE) |
| 862 | return -EINVAL; | 871 | return -EINVAL; |
| 863 | hex2bin(p->blob, c, p->blob_len); | 872 | ret = hex2bin(p->blob, c, p->blob_len); |
| 873 | if (ret < 0) | ||
| 874 | return -EINVAL; | ||
| 864 | ret = getoptions(datablob, p, o); | 875 | ret = getoptions(datablob, p, o); |
| 865 | if (ret < 0) | 876 | if (ret < 0) |
| 866 | return ret; | 877 | return ret; |
diff --git a/security/security.c b/security/security.c index d9e153390926..0c6cc69c8f86 100644 --- a/security/security.c +++ b/security/security.c | |||
| @@ -16,15 +16,16 @@ | |||
| 16 | #include <linux/init.h> | 16 | #include <linux/init.h> |
| 17 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
| 18 | #include <linux/security.h> | 18 | #include <linux/security.h> |
| 19 | #include <linux/integrity.h> | ||
| 19 | #include <linux/ima.h> | 20 | #include <linux/ima.h> |
| 21 | #include <linux/evm.h> | ||
| 22 | |||
| 23 | #define MAX_LSM_EVM_XATTR 2 | ||
| 20 | 24 | ||
| 21 | /* Boot-time LSM user choice */ | 25 | /* Boot-time LSM user choice */ |
| 22 | static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] = | 26 | static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] = |
| 23 | CONFIG_DEFAULT_SECURITY; | 27 | CONFIG_DEFAULT_SECURITY; |
| 24 | 28 | ||
| 25 | /* things that live in capability.c */ | ||
| 26 | extern void __init security_fixup_ops(struct security_operations *ops); | ||
| 27 | |||
| 28 | static struct security_operations *security_ops; | 29 | static struct security_operations *security_ops; |
| 29 | static struct security_operations default_security_ops = { | 30 | static struct security_operations default_security_ops = { |
| 30 | .name = "default", | 31 | .name = "default", |
| @@ -334,20 +335,57 @@ int security_inode_alloc(struct inode *inode) | |||
| 334 | 335 | ||
| 335 | void security_inode_free(struct inode *inode) | 336 | void security_inode_free(struct inode *inode) |
| 336 | { | 337 | { |
| 337 | ima_inode_free(inode); | 338 | integrity_inode_free(inode); |
| 338 | security_ops->inode_free_security(inode); | 339 | security_ops->inode_free_security(inode); |
| 339 | } | 340 | } |
| 340 | 341 | ||
| 341 | int security_inode_init_security(struct inode *inode, struct inode *dir, | 342 | int security_inode_init_security(struct inode *inode, struct inode *dir, |
| 342 | const struct qstr *qstr, char **name, | 343 | const struct qstr *qstr, |
| 343 | void **value, size_t *len) | 344 | const initxattrs initxattrs, void *fs_data) |
| 344 | { | 345 | { |
| 346 | struct xattr new_xattrs[MAX_LSM_EVM_XATTR + 1]; | ||
| 347 | struct xattr *lsm_xattr, *evm_xattr, *xattr; | ||
| 348 | int ret; | ||
| 349 | |||
| 345 | if (unlikely(IS_PRIVATE(inode))) | 350 | if (unlikely(IS_PRIVATE(inode))) |
| 346 | return -EOPNOTSUPP; | 351 | return 0; |
| 352 | |||
| 353 | memset(new_xattrs, 0, sizeof new_xattrs); | ||
| 354 | if (!initxattrs) | ||
| 355 | return security_ops->inode_init_security(inode, dir, qstr, | ||
| 356 | NULL, NULL, NULL); | ||
| 357 | lsm_xattr = new_xattrs; | ||
| 358 | ret = security_ops->inode_init_security(inode, dir, qstr, | ||
| 359 | &lsm_xattr->name, | ||
| 360 | &lsm_xattr->value, | ||
| 361 | &lsm_xattr->value_len); | ||
| 362 | if (ret) | ||
| 363 | goto out; | ||
| 364 | |||
| 365 | evm_xattr = lsm_xattr + 1; | ||
| 366 | ret = evm_inode_init_security(inode, lsm_xattr, evm_xattr); | ||
| 367 | if (ret) | ||
| 368 | goto out; | ||
| 369 | ret = initxattrs(inode, new_xattrs, fs_data); | ||
| 370 | out: | ||
| 371 | for (xattr = new_xattrs; xattr->name != NULL; xattr++) { | ||
| 372 | kfree(xattr->name); | ||
| 373 | kfree(xattr->value); | ||
| 374 | } | ||
| 375 | return (ret == -EOPNOTSUPP) ? 0 : ret; | ||
| 376 | } | ||
| 377 | EXPORT_SYMBOL(security_inode_init_security); | ||
| 378 | |||
| 379 | int security_old_inode_init_security(struct inode *inode, struct inode *dir, | ||
| 380 | const struct qstr *qstr, char **name, | ||
| 381 | void **value, size_t *len) | ||
| 382 | { | ||
| 383 | if (unlikely(IS_PRIVATE(inode))) | ||
| 384 | return 0; | ||
| 347 | return security_ops->inode_init_security(inode, dir, qstr, name, value, | 385 | return security_ops->inode_init_security(inode, dir, qstr, name, value, |
| 348 | len); | 386 | len); |
| 349 | } | 387 | } |
| 350 | EXPORT_SYMBOL(security_inode_init_security); | 388 | EXPORT_SYMBOL(security_old_inode_init_security); |
| 351 | 389 | ||
| 352 | #ifdef CONFIG_SECURITY_PATH | 390 | #ifdef CONFIG_SECURITY_PATH |
| 353 | int security_path_mknod(struct path *dir, struct dentry *dentry, int mode, | 391 | 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) | |||
| 523 | 561 | ||
| 524 | int security_inode_setattr(struct dentry *dentry, struct iattr *attr) | 562 | int security_inode_setattr(struct dentry *dentry, struct iattr *attr) |
| 525 | { | 563 | { |
| 564 | int ret; | ||
| 565 | |||
| 526 | if (unlikely(IS_PRIVATE(dentry->d_inode))) | 566 | if (unlikely(IS_PRIVATE(dentry->d_inode))) |
| 527 | return 0; | 567 | return 0; |
| 528 | return security_ops->inode_setattr(dentry, attr); | 568 | ret = security_ops->inode_setattr(dentry, attr); |
| 569 | if (ret) | ||
| 570 | return ret; | ||
| 571 | return evm_inode_setattr(dentry, attr); | ||
| 529 | } | 572 | } |
| 530 | EXPORT_SYMBOL_GPL(security_inode_setattr); | 573 | EXPORT_SYMBOL_GPL(security_inode_setattr); |
| 531 | 574 | ||
| @@ -539,9 +582,14 @@ int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) | |||
| 539 | int security_inode_setxattr(struct dentry *dentry, const char *name, | 582 | int security_inode_setxattr(struct dentry *dentry, const char *name, |
| 540 | const void *value, size_t size, int flags) | 583 | const void *value, size_t size, int flags) |
| 541 | { | 584 | { |
| 585 | int ret; | ||
| 586 | |||
| 542 | if (unlikely(IS_PRIVATE(dentry->d_inode))) | 587 | if (unlikely(IS_PRIVATE(dentry->d_inode))) |
| 543 | return 0; | 588 | return 0; |
| 544 | return security_ops->inode_setxattr(dentry, name, value, size, flags); | 589 | ret = security_ops->inode_setxattr(dentry, name, value, size, flags); |
| 590 | if (ret) | ||
| 591 | return ret; | ||
| 592 | return evm_inode_setxattr(dentry, name, value, size); | ||
| 545 | } | 593 | } |
| 546 | 594 | ||
| 547 | void security_inode_post_setxattr(struct dentry *dentry, const char *name, | 595 | 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, | |||
| 550 | if (unlikely(IS_PRIVATE(dentry->d_inode))) | 598 | if (unlikely(IS_PRIVATE(dentry->d_inode))) |
| 551 | return; | 599 | return; |
| 552 | security_ops->inode_post_setxattr(dentry, name, value, size, flags); | 600 | security_ops->inode_post_setxattr(dentry, name, value, size, flags); |
| 601 | evm_inode_post_setxattr(dentry, name, value, size); | ||
| 553 | } | 602 | } |
| 554 | 603 | ||
| 555 | int security_inode_getxattr(struct dentry *dentry, const char *name) | 604 | int security_inode_getxattr(struct dentry *dentry, const char *name) |
| @@ -568,9 +617,14 @@ int security_inode_listxattr(struct dentry *dentry) | |||
| 568 | 617 | ||
| 569 | int security_inode_removexattr(struct dentry *dentry, const char *name) | 618 | int security_inode_removexattr(struct dentry *dentry, const char *name) |
| 570 | { | 619 | { |
| 620 | int ret; | ||
| 621 | |||
| 571 | if (unlikely(IS_PRIVATE(dentry->d_inode))) | 622 | if (unlikely(IS_PRIVATE(dentry->d_inode))) |
| 572 | return 0; | 623 | return 0; |
| 573 | return security_ops->inode_removexattr(dentry, name); | 624 | ret = security_ops->inode_removexattr(dentry, name); |
| 625 | if (ret) | ||
| 626 | return ret; | ||
| 627 | return evm_inode_removexattr(dentry, name); | ||
| 574 | } | 628 | } |
| 575 | 629 | ||
| 576 | int security_inode_need_killpriv(struct dentry *dentry) | 630 | int security_inode_need_killpriv(struct dentry *dentry) |
diff --git a/security/selinux/exports.c b/security/selinux/exports.c index 90664385dead..e75dd94e2d2b 100644 --- a/security/selinux/exports.c +++ b/security/selinux/exports.c | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | * as published by the Free Software Foundation. | 12 | * as published by the Free Software Foundation. |
| 13 | */ | 13 | */ |
| 14 | #include <linux/module.h> | 14 | #include <linux/module.h> |
| 15 | #include <linux/selinux.h> | ||
| 15 | 16 | ||
| 16 | #include "security.h" | 17 | #include "security.h" |
| 17 | 18 | ||
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 266a2292451d..e545b9f67072 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
| @@ -89,14 +89,14 @@ | |||
| 89 | #include "xfrm.h" | 89 | #include "xfrm.h" |
| 90 | #include "netlabel.h" | 90 | #include "netlabel.h" |
| 91 | #include "audit.h" | 91 | #include "audit.h" |
| 92 | #include "avc_ss.h" | ||
| 92 | 93 | ||
| 93 | #define NUM_SEL_MNT_OPTS 5 | 94 | #define NUM_SEL_MNT_OPTS 5 |
| 94 | 95 | ||
| 95 | extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm); | ||
| 96 | extern struct security_operations *security_ops; | 96 | extern struct security_operations *security_ops; |
| 97 | 97 | ||
| 98 | /* SECMARK reference count */ | 98 | /* SECMARK reference count */ |
| 99 | atomic_t selinux_secmark_refcount = ATOMIC_INIT(0); | 99 | static atomic_t selinux_secmark_refcount = ATOMIC_INIT(0); |
| 100 | 100 | ||
| 101 | #ifdef CONFIG_SECURITY_SELINUX_DEVELOP | 101 | #ifdef CONFIG_SECURITY_SELINUX_DEVELOP |
| 102 | int selinux_enforcing; | 102 | int selinux_enforcing; |
| @@ -279,10 +279,6 @@ static void superblock_free_security(struct super_block *sb) | |||
| 279 | kfree(sbsec); | 279 | kfree(sbsec); |
| 280 | } | 280 | } |
| 281 | 281 | ||
| 282 | /* The security server must be initialized before | ||
| 283 | any labeling or access decisions can be provided. */ | ||
| 284 | extern int ss_initialized; | ||
| 285 | |||
| 286 | /* The file system's label must be initialized prior to use. */ | 282 | /* The file system's label must be initialized prior to use. */ |
| 287 | 283 | ||
| 288 | static const char *labeling_behaviors[6] = { | 284 | static const char *labeling_behaviors[6] = { |
| @@ -2097,9 +2093,6 @@ static int selinux_bprm_secureexec(struct linux_binprm *bprm) | |||
| 2097 | return (atsecure || cap_bprm_secureexec(bprm)); | 2093 | return (atsecure || cap_bprm_secureexec(bprm)); |
| 2098 | } | 2094 | } |
| 2099 | 2095 | ||
| 2100 | extern struct vfsmount *selinuxfs_mount; | ||
| 2101 | extern struct dentry *selinux_null; | ||
| 2102 | |||
| 2103 | /* Derived from fs/exec.c:flush_old_files. */ | 2096 | /* Derived from fs/exec.c:flush_old_files. */ |
| 2104 | static inline void flush_unauthorized_files(const struct cred *cred, | 2097 | static inline void flush_unauthorized_files(const struct cred *cred, |
| 2105 | struct files_struct *files) | 2098 | struct files_struct *files) |
| @@ -5803,8 +5796,6 @@ static int selinux_disabled; | |||
| 5803 | 5796 | ||
| 5804 | int selinux_disable(void) | 5797 | int selinux_disable(void) |
| 5805 | { | 5798 | { |
| 5806 | extern void exit_sel_fs(void); | ||
| 5807 | |||
| 5808 | if (ss_initialized) { | 5799 | if (ss_initialized) { |
| 5809 | /* Not permitted after initial policy load. */ | 5800 | /* Not permitted after initial policy load. */ |
| 5810 | return -EINVAL; | 5801 | return -EINVAL; |
diff --git a/security/selinux/include/avc_ss.h b/security/selinux/include/avc_ss.h index 4677aa519b04..d5c328452df0 100644 --- a/security/selinux/include/avc_ss.h +++ b/security/selinux/include/avc_ss.h | |||
| @@ -18,5 +18,11 @@ struct security_class_mapping { | |||
| 18 | 18 | ||
| 19 | extern struct security_class_mapping secclass_map[]; | 19 | extern struct security_class_mapping secclass_map[]; |
| 20 | 20 | ||
| 21 | /* | ||
| 22 | * The security server must be initialized before | ||
| 23 | * any labeling or access decisions can be provided. | ||
| 24 | */ | ||
| 25 | extern int ss_initialized; | ||
| 26 | |||
| 21 | #endif /* _SELINUX_AVC_SS_H_ */ | 27 | #endif /* _SELINUX_AVC_SS_H_ */ |
| 22 | 28 | ||
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 3ba4feba048a..d871e8ad2103 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h | |||
| @@ -216,6 +216,14 @@ struct selinux_kernel_status { | |||
| 216 | 216 | ||
| 217 | extern void selinux_status_update_setenforce(int enforcing); | 217 | extern void selinux_status_update_setenforce(int enforcing); |
| 218 | extern void selinux_status_update_policyload(int seqno); | 218 | extern void selinux_status_update_policyload(int seqno); |
| 219 | extern void selinux_complete_init(void); | ||
| 220 | extern int selinux_disable(void); | ||
| 221 | extern void exit_sel_fs(void); | ||
| 222 | extern struct dentry *selinux_null; | ||
| 223 | extern struct vfsmount *selinuxfs_mount; | ||
| 224 | extern void selnl_notify_setenforce(int val); | ||
| 225 | extern void selnl_notify_policyload(u32 seqno); | ||
| 226 | extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm); | ||
| 219 | 227 | ||
| 220 | #endif /* _SELINUX_SECURITY_H_ */ | 228 | #endif /* _SELINUX_SECURITY_H_ */ |
| 221 | 229 | ||
diff --git a/security/selinux/netlink.c b/security/selinux/netlink.c index 36ac257cec9a..ce3f481558d8 100644 --- a/security/selinux/netlink.c +++ b/security/selinux/netlink.c | |||
| @@ -19,6 +19,8 @@ | |||
| 19 | #include <linux/selinux_netlink.h> | 19 | #include <linux/selinux_netlink.h> |
| 20 | #include <net/net_namespace.h> | 20 | #include <net/net_namespace.h> |
| 21 | 21 | ||
| 22 | #include "security.h" | ||
| 23 | |||
| 22 | static struct sock *selnl; | 24 | static struct sock *selnl; |
| 23 | 25 | ||
| 24 | static int selnl_msglen(int msgtype) | 26 | static int selnl_msglen(int msgtype) |
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index 8b02b2137da2..0920ea3bf599 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | 21 | ||
| 22 | #include "flask.h" | 22 | #include "flask.h" |
| 23 | #include "av_permissions.h" | 23 | #include "av_permissions.h" |
| 24 | #include "security.h" | ||
| 24 | 25 | ||
| 25 | struct nlmsg_perm { | 26 | struct nlmsg_perm { |
| 26 | u16 nlmsg_type; | 27 | u16 nlmsg_type; |
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 55d92cbb177a..f46658722c78 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c | |||
| @@ -75,8 +75,6 @@ static char policy_opened; | |||
| 75 | /* global data for policy capabilities */ | 75 | /* global data for policy capabilities */ |
| 76 | static struct dentry *policycap_dir; | 76 | static struct dentry *policycap_dir; |
| 77 | 77 | ||
| 78 | extern void selnl_notify_setenforce(int val); | ||
| 79 | |||
| 80 | /* Check whether a task is allowed to use a security operation. */ | 78 | /* Check whether a task is allowed to use a security operation. */ |
| 81 | static int task_has_security(struct task_struct *tsk, | 79 | static int task_has_security(struct task_struct *tsk, |
| 82 | u32 perms) | 80 | u32 perms) |
| @@ -278,7 +276,6 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf, | |||
| 278 | char *page = NULL; | 276 | char *page = NULL; |
| 279 | ssize_t length; | 277 | ssize_t length; |
| 280 | int new_value; | 278 | int new_value; |
| 281 | extern int selinux_disable(void); | ||
| 282 | 279 | ||
| 283 | length = -ENOMEM; | 280 | length = -ENOMEM; |
| 284 | if (count >= PAGE_SIZE) | 281 | if (count >= PAGE_SIZE) |
| @@ -478,7 +475,7 @@ static struct vm_operations_struct sel_mmap_policy_ops = { | |||
| 478 | .page_mkwrite = sel_mmap_policy_fault, | 475 | .page_mkwrite = sel_mmap_policy_fault, |
| 479 | }; | 476 | }; |
| 480 | 477 | ||
| 481 | int sel_mmap_policy(struct file *filp, struct vm_area_struct *vma) | 478 | static int sel_mmap_policy(struct file *filp, struct vm_area_struct *vma) |
| 482 | { | 479 | { |
| 483 | if (vma->vm_flags & VM_SHARED) { | 480 | if (vma->vm_flags & VM_SHARED) { |
| 484 | /* do not allow mprotect to make mapping writable */ | 481 | /* do not allow mprotect to make mapping writable */ |
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index a53373207fb4..2ec904177fe0 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c | |||
| @@ -555,7 +555,7 @@ static int cond_write_av_list(struct policydb *p, | |||
| 555 | return 0; | 555 | return 0; |
| 556 | } | 556 | } |
| 557 | 557 | ||
| 558 | int cond_write_node(struct policydb *p, struct cond_node *node, | 558 | static int cond_write_node(struct policydb *p, struct cond_node *node, |
| 559 | struct policy_file *fp) | 559 | struct policy_file *fp) |
| 560 | { | 560 | { |
| 561 | struct cond_expr *cur_expr; | 561 | struct cond_expr *cur_expr; |
diff --git a/security/selinux/ss/conditional.h b/security/selinux/ss/conditional.h index 3f209c635295..4d1f87466508 100644 --- a/security/selinux/ss/conditional.h +++ b/security/selinux/ss/conditional.h | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include "avtab.h" | 13 | #include "avtab.h" |
| 14 | #include "symtab.h" | 14 | #include "symtab.h" |
| 15 | #include "policydb.h" | 15 | #include "policydb.h" |
| 16 | #include "../include/conditional.h" | ||
| 16 | 17 | ||
| 17 | #define COND_EXPR_MAXDEPTH 10 | 18 | #define COND_EXPR_MAXDEPTH 10 |
| 18 | 19 | ||
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 2381d0ded228..a7f61d52f05c 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c | |||
| @@ -1743,8 +1743,6 @@ static int policydb_bounds_sanity_check(struct policydb *p) | |||
| 1743 | return 0; | 1743 | return 0; |
| 1744 | } | 1744 | } |
| 1745 | 1745 | ||
| 1746 | extern int ss_initialized; | ||
| 1747 | |||
| 1748 | u16 string_to_security_class(struct policydb *p, const char *name) | 1746 | u16 string_to_security_class(struct policydb *p, const char *name) |
| 1749 | { | 1747 | { |
| 1750 | struct class_datum *cladatum; | 1748 | struct class_datum *cladatum; |
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index f6917bc0aa05..185f849a26f6 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c | |||
| @@ -70,8 +70,6 @@ | |||
| 70 | #include "ebitmap.h" | 70 | #include "ebitmap.h" |
| 71 | #include "audit.h" | 71 | #include "audit.h" |
| 72 | 72 | ||
| 73 | extern void selnl_notify_policyload(u32 seqno); | ||
| 74 | |||
| 75 | int selinux_policycap_netpeer; | 73 | int selinux_policycap_netpeer; |
| 76 | int selinux_policycap_openperm; | 74 | int selinux_policycap_openperm; |
| 77 | 75 | ||
| @@ -1790,7 +1788,6 @@ static void security_load_policycaps(void) | |||
| 1790 | POLICYDB_CAPABILITY_OPENPERM); | 1788 | POLICYDB_CAPABILITY_OPENPERM); |
| 1791 | } | 1789 | } |
| 1792 | 1790 | ||
| 1793 | extern void selinux_complete_init(void); | ||
| 1794 | static int security_preserve_bools(struct policydb *p); | 1791 | static int security_preserve_bools(struct policydb *p); |
| 1795 | 1792 | ||
| 1796 | /** | 1793 | /** |
diff --git a/security/smack/smack.h b/security/smack/smack.h index 2b6c6a516123..2ad00657b801 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h | |||
| @@ -41,9 +41,9 @@ struct superblock_smack { | |||
| 41 | }; | 41 | }; |
| 42 | 42 | ||
| 43 | struct socket_smack { | 43 | struct socket_smack { |
| 44 | char *smk_out; /* outbound label */ | 44 | char *smk_out; /* outbound label */ |
| 45 | char *smk_in; /* inbound label */ | 45 | char *smk_in; /* inbound label */ |
| 46 | char smk_packet[SMK_LABELLEN]; /* TCP peer label */ | 46 | char *smk_packet; /* TCP peer label */ |
| 47 | }; | 47 | }; |
| 48 | 48 | ||
| 49 | /* | 49 | /* |
| @@ -116,13 +116,19 @@ struct smk_netlbladdr { | |||
| 116 | * If there is a cipso value associated with the label it | 116 | * If there is a cipso value associated with the label it |
| 117 | * gets stored here, too. This will most likely be rare as | 117 | * gets stored here, too. This will most likely be rare as |
| 118 | * the cipso direct mapping in used internally. | 118 | * the cipso direct mapping in used internally. |
| 119 | * | ||
| 120 | * Keep the access rules for this subject label here so that | ||
| 121 | * the entire set of rules does not need to be examined every | ||
| 122 | * time. | ||
| 119 | */ | 123 | */ |
| 120 | struct smack_known { | 124 | struct smack_known { |
| 121 | struct list_head list; | 125 | struct list_head list; |
| 122 | char smk_known[SMK_LABELLEN]; | 126 | char smk_known[SMK_LABELLEN]; |
| 123 | u32 smk_secid; | 127 | u32 smk_secid; |
| 124 | struct smack_cipso *smk_cipso; | 128 | struct smack_cipso *smk_cipso; |
| 125 | spinlock_t smk_cipsolock; /* for changing cipso map */ | 129 | spinlock_t smk_cipsolock; /* for changing cipso map */ |
| 130 | struct list_head smk_rules; /* access rules */ | ||
| 131 | struct mutex smk_rules_lock; /* lock for the rules */ | ||
| 126 | }; | 132 | }; |
| 127 | 133 | ||
| 128 | /* | 134 | /* |
| @@ -150,7 +156,6 @@ struct smack_known { | |||
| 150 | 156 | ||
| 151 | /* | 157 | /* |
| 152 | * smackfs magic number | 158 | * smackfs magic number |
| 153 | * smackfs macic number | ||
| 154 | */ | 159 | */ |
| 155 | #define SMACK_MAGIC 0x43415d53 /* "SMAC" */ | 160 | #define SMACK_MAGIC 0x43415d53 /* "SMAC" */ |
| 156 | 161 | ||
| @@ -176,9 +181,9 @@ struct smack_known { | |||
| 176 | #define MAY_NOT 0 | 181 | #define MAY_NOT 0 |
| 177 | 182 | ||
| 178 | /* | 183 | /* |
| 179 | * Number of access types used by Smack (rwxa) | 184 | * Number of access types used by Smack (rwxat) |
| 180 | */ | 185 | */ |
| 181 | #define SMK_NUM_ACCESS_TYPE 4 | 186 | #define SMK_NUM_ACCESS_TYPE 5 |
| 182 | 187 | ||
| 183 | /* | 188 | /* |
| 184 | * Smack audit data; is empty if CONFIG_AUDIT not set | 189 | * Smack audit data; is empty if CONFIG_AUDIT not set |
| @@ -201,10 +206,12 @@ int smk_access_entry(char *, char *, struct list_head *); | |||
| 201 | int smk_access(char *, char *, int, struct smk_audit_info *); | 206 | int smk_access(char *, char *, int, struct smk_audit_info *); |
| 202 | int smk_curacc(char *, u32, struct smk_audit_info *); | 207 | int smk_curacc(char *, u32, struct smk_audit_info *); |
| 203 | int smack_to_cipso(const char *, struct smack_cipso *); | 208 | int smack_to_cipso(const char *, struct smack_cipso *); |
| 204 | void smack_from_cipso(u32, char *, char *); | 209 | char *smack_from_cipso(u32, char *); |
| 205 | char *smack_from_secid(const u32); | 210 | char *smack_from_secid(const u32); |
| 211 | void smk_parse_smack(const char *string, int len, char *smack); | ||
| 206 | char *smk_import(const char *, int); | 212 | char *smk_import(const char *, int); |
| 207 | struct smack_known *smk_import_entry(const char *, int); | 213 | struct smack_known *smk_import_entry(const char *, int); |
| 214 | struct smack_known *smk_find_entry(const char *); | ||
| 208 | u32 smack_to_secid(const char *); | 215 | u32 smack_to_secid(const char *); |
| 209 | 216 | ||
| 210 | /* | 217 | /* |
| @@ -223,7 +230,6 @@ extern struct smack_known smack_known_star; | |||
| 223 | extern struct smack_known smack_known_web; | 230 | extern struct smack_known smack_known_web; |
| 224 | 231 | ||
| 225 | extern struct list_head smack_known_list; | 232 | extern struct list_head smack_known_list; |
| 226 | extern struct list_head smack_rule_list; | ||
| 227 | extern struct list_head smk_netlbladdr_list; | 233 | extern struct list_head smk_netlbladdr_list; |
| 228 | 234 | ||
| 229 | extern struct security_operations smack_ops; | 235 | extern struct security_operations smack_ops; |
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 9637e107f7ea..cc7cb6edba08 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c | |||
| @@ -77,14 +77,19 @@ int log_policy = SMACK_AUDIT_DENIED; | |||
| 77 | * entry is found returns -ENOENT. | 77 | * entry is found returns -ENOENT. |
| 78 | * | 78 | * |
| 79 | * NOTE: | 79 | * NOTE: |
| 80 | * Even though Smack labels are usually shared on smack_list | ||
| 81 | * labels that come in off the network can't be imported | ||
| 82 | * and added to the list for locking reasons. | ||
| 83 | * | 80 | * |
| 84 | * Therefore, it is necessary to check the contents of the labels, | 81 | * Earlier versions of this function allowed for labels that |
| 85 | * not just the pointer values. Of course, in most cases the labels | 82 | * were not on the label list. This was done to allow for |
| 86 | * will be on the list, so checking the pointers may be a worthwhile | 83 | * labels to come over the network that had never been seen |
| 87 | * optimization. | 84 | * before on this host. Unless the receiving socket has the |
| 85 | * star label this will always result in a failure check. The | ||
| 86 | * star labeled socket case is now handled in the networking | ||
| 87 | * hooks so there is no case where the label is not on the | ||
| 88 | * label list. Checking to see if the address of two labels | ||
| 89 | * is the same is now a reliable test. | ||
| 90 | * | ||
| 91 | * Do the object check first because that is more | ||
| 92 | * likely to differ. | ||
| 88 | */ | 93 | */ |
| 89 | int smk_access_entry(char *subject_label, char *object_label, | 94 | int smk_access_entry(char *subject_label, char *object_label, |
| 90 | struct list_head *rule_list) | 95 | struct list_head *rule_list) |
| @@ -93,13 +98,10 @@ int smk_access_entry(char *subject_label, char *object_label, | |||
| 93 | struct smack_rule *srp; | 98 | struct smack_rule *srp; |
| 94 | 99 | ||
| 95 | list_for_each_entry_rcu(srp, rule_list, list) { | 100 | list_for_each_entry_rcu(srp, rule_list, list) { |
| 96 | if (srp->smk_subject == subject_label || | 101 | if (srp->smk_object == object_label && |
| 97 | strcmp(srp->smk_subject, subject_label) == 0) { | 102 | srp->smk_subject == subject_label) { |
| 98 | if (srp->smk_object == object_label || | 103 | may = srp->smk_access; |
| 99 | strcmp(srp->smk_object, object_label) == 0) { | 104 | break; |
| 100 | may = srp->smk_access; | ||
| 101 | break; | ||
| 102 | } | ||
| 103 | } | 105 | } |
| 104 | } | 106 | } |
| 105 | 107 | ||
| @@ -117,18 +119,12 @@ int smk_access_entry(char *subject_label, char *object_label, | |||
| 117 | * access rule list and returns 0 if the access is permitted, | 119 | * access rule list and returns 0 if the access is permitted, |
| 118 | * non zero otherwise. | 120 | * non zero otherwise. |
| 119 | * | 121 | * |
| 120 | * Even though Smack labels are usually shared on smack_list | 122 | * Smack labels are shared on smack_list |
| 121 | * labels that come in off the network can't be imported | ||
| 122 | * and added to the list for locking reasons. | ||
| 123 | * | ||
| 124 | * Therefore, it is necessary to check the contents of the labels, | ||
| 125 | * not just the pointer values. Of course, in most cases the labels | ||
| 126 | * will be on the list, so checking the pointers may be a worthwhile | ||
| 127 | * optimization. | ||
| 128 | */ | 123 | */ |
| 129 | int smk_access(char *subject_label, char *object_label, int request, | 124 | int smk_access(char *subject_label, char *object_label, int request, |
| 130 | struct smk_audit_info *a) | 125 | struct smk_audit_info *a) |
| 131 | { | 126 | { |
| 127 | struct smack_known *skp; | ||
| 132 | int may = MAY_NOT; | 128 | int may = MAY_NOT; |
| 133 | int rc = 0; | 129 | int rc = 0; |
| 134 | 130 | ||
| @@ -137,8 +133,7 @@ int smk_access(char *subject_label, char *object_label, int request, | |||
| 137 | * | 133 | * |
| 138 | * A star subject can't access any object. | 134 | * A star subject can't access any object. |
| 139 | */ | 135 | */ |
| 140 | if (subject_label == smack_known_star.smk_known || | 136 | if (subject_label == smack_known_star.smk_known) { |
| 141 | strcmp(subject_label, smack_known_star.smk_known) == 0) { | ||
| 142 | rc = -EACCES; | 137 | rc = -EACCES; |
| 143 | goto out_audit; | 138 | goto out_audit; |
| 144 | } | 139 | } |
| @@ -148,33 +143,27 @@ int smk_access(char *subject_label, char *object_label, int request, | |||
| 148 | * An internet subject can access any object. | 143 | * An internet subject can access any object. |
| 149 | */ | 144 | */ |
| 150 | if (object_label == smack_known_web.smk_known || | 145 | if (object_label == smack_known_web.smk_known || |
| 151 | subject_label == smack_known_web.smk_known || | 146 | subject_label == smack_known_web.smk_known) |
| 152 | strcmp(object_label, smack_known_web.smk_known) == 0 || | ||
| 153 | strcmp(subject_label, smack_known_web.smk_known) == 0) | ||
| 154 | goto out_audit; | 147 | goto out_audit; |
| 155 | /* | 148 | /* |
| 156 | * A star object can be accessed by any subject. | 149 | * A star object can be accessed by any subject. |
| 157 | */ | 150 | */ |
| 158 | if (object_label == smack_known_star.smk_known || | 151 | if (object_label == smack_known_star.smk_known) |
| 159 | strcmp(object_label, smack_known_star.smk_known) == 0) | ||
| 160 | goto out_audit; | 152 | goto out_audit; |
| 161 | /* | 153 | /* |
| 162 | * An object can be accessed in any way by a subject | 154 | * An object can be accessed in any way by a subject |
| 163 | * with the same label. | 155 | * with the same label. |
| 164 | */ | 156 | */ |
| 165 | if (subject_label == object_label || | 157 | if (subject_label == object_label) |
| 166 | strcmp(subject_label, object_label) == 0) | ||
| 167 | goto out_audit; | 158 | goto out_audit; |
| 168 | /* | 159 | /* |
| 169 | * A hat subject can read any object. | 160 | * A hat subject can read any object. |
| 170 | * A floor object can be read by any subject. | 161 | * A floor object can be read by any subject. |
| 171 | */ | 162 | */ |
| 172 | if ((request & MAY_ANYREAD) == request) { | 163 | if ((request & MAY_ANYREAD) == request) { |
| 173 | if (object_label == smack_known_floor.smk_known || | 164 | if (object_label == smack_known_floor.smk_known) |
| 174 | strcmp(object_label, smack_known_floor.smk_known) == 0) | ||
| 175 | goto out_audit; | 165 | goto out_audit; |
| 176 | if (subject_label == smack_known_hat.smk_known || | 166 | if (subject_label == smack_known_hat.smk_known) |
| 177 | strcmp(subject_label, smack_known_hat.smk_known) == 0) | ||
| 178 | goto out_audit; | 167 | goto out_audit; |
| 179 | } | 168 | } |
| 180 | /* | 169 | /* |
| @@ -184,8 +173,9 @@ int smk_access(char *subject_label, char *object_label, int request, | |||
| 184 | * good. A negative response from smk_access_entry() | 173 | * good. A negative response from smk_access_entry() |
| 185 | * indicates there is no entry for this pair. | 174 | * indicates there is no entry for this pair. |
| 186 | */ | 175 | */ |
| 176 | skp = smk_find_entry(subject_label); | ||
| 187 | rcu_read_lock(); | 177 | rcu_read_lock(); |
| 188 | may = smk_access_entry(subject_label, object_label, &smack_rule_list); | 178 | may = smk_access_entry(subject_label, object_label, &skp->smk_rules); |
| 189 | rcu_read_unlock(); | 179 | rcu_read_unlock(); |
| 190 | 180 | ||
| 191 | if (may > 0 && (request & may) == request) | 181 | if (may > 0 && (request & may) == request) |
| @@ -344,17 +334,32 @@ void smack_log(char *subject_label, char *object_label, int request, | |||
| 344 | static DEFINE_MUTEX(smack_known_lock); | 334 | static DEFINE_MUTEX(smack_known_lock); |
| 345 | 335 | ||
| 346 | /** | 336 | /** |
| 347 | * smk_import_entry - import a label, return the list entry | 337 | * smk_find_entry - find a label on the list, return the list entry |
| 348 | * @string: a text string that might be a Smack label | 338 | * @string: a text string that might be a Smack label |
| 349 | * @len: the maximum size, or zero if it is NULL terminated. | ||
| 350 | * | 339 | * |
| 351 | * Returns a pointer to the entry in the label list that | 340 | * Returns a pointer to the entry in the label list that |
| 352 | * matches the passed string, adding it if necessary. | 341 | * matches the passed string. |
| 353 | */ | 342 | */ |
| 354 | struct smack_known *smk_import_entry(const char *string, int len) | 343 | struct smack_known *smk_find_entry(const char *string) |
| 355 | { | 344 | { |
| 356 | struct smack_known *skp; | 345 | struct smack_known *skp; |
| 357 | char smack[SMK_LABELLEN]; | 346 | |
| 347 | list_for_each_entry_rcu(skp, &smack_known_list, list) { | ||
| 348 | if (strncmp(skp->smk_known, string, SMK_MAXLEN) == 0) | ||
| 349 | return skp; | ||
| 350 | } | ||
| 351 | |||
| 352 | return NULL; | ||
| 353 | } | ||
| 354 | |||
| 355 | /** | ||
| 356 | * smk_parse_smack - parse smack label from a text string | ||
| 357 | * @string: a text string that might contain a Smack label | ||
| 358 | * @len: the maximum size, or zero if it is NULL terminated. | ||
| 359 | * @smack: parsed smack label, or NULL if parse error | ||
| 360 | */ | ||
| 361 | void smk_parse_smack(const char *string, int len, char *smack) | ||
| 362 | { | ||
| 358 | int found; | 363 | int found; |
| 359 | int i; | 364 | int i; |
| 360 | 365 | ||
| @@ -372,27 +377,38 @@ struct smack_known *smk_import_entry(const char *string, int len) | |||
| 372 | } else | 377 | } else |
| 373 | smack[i] = string[i]; | 378 | smack[i] = string[i]; |
| 374 | } | 379 | } |
| 380 | } | ||
| 381 | |||
| 382 | /** | ||
| 383 | * smk_import_entry - import a label, return the list entry | ||
| 384 | * @string: a text string that might be a Smack label | ||
| 385 | * @len: the maximum size, or zero if it is NULL terminated. | ||
| 386 | * | ||
| 387 | * Returns a pointer to the entry in the label list that | ||
| 388 | * matches the passed string, adding it if necessary. | ||
| 389 | */ | ||
| 390 | struct smack_known *smk_import_entry(const char *string, int len) | ||
| 391 | { | ||
| 392 | struct smack_known *skp; | ||
| 393 | char smack[SMK_LABELLEN]; | ||
| 375 | 394 | ||
| 395 | smk_parse_smack(string, len, smack); | ||
| 376 | if (smack[0] == '\0') | 396 | if (smack[0] == '\0') |
| 377 | return NULL; | 397 | return NULL; |
| 378 | 398 | ||
| 379 | mutex_lock(&smack_known_lock); | 399 | mutex_lock(&smack_known_lock); |
| 380 | 400 | ||
| 381 | found = 0; | 401 | skp = smk_find_entry(smack); |
| 382 | list_for_each_entry_rcu(skp, &smack_known_list, list) { | ||
| 383 | if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) { | ||
| 384 | found = 1; | ||
| 385 | break; | ||
| 386 | } | ||
| 387 | } | ||
| 388 | 402 | ||
| 389 | if (found == 0) { | 403 | if (skp == NULL) { |
| 390 | skp = kzalloc(sizeof(struct smack_known), GFP_KERNEL); | 404 | skp = kzalloc(sizeof(struct smack_known), GFP_KERNEL); |
| 391 | if (skp != NULL) { | 405 | if (skp != NULL) { |
| 392 | strncpy(skp->smk_known, smack, SMK_MAXLEN); | 406 | strncpy(skp->smk_known, smack, SMK_MAXLEN); |
| 393 | skp->smk_secid = smack_next_secid++; | 407 | skp->smk_secid = smack_next_secid++; |
| 394 | skp->smk_cipso = NULL; | 408 | skp->smk_cipso = NULL; |
| 409 | INIT_LIST_HEAD(&skp->smk_rules); | ||
| 395 | spin_lock_init(&skp->smk_cipsolock); | 410 | spin_lock_init(&skp->smk_cipsolock); |
| 411 | mutex_init(&skp->smk_rules_lock); | ||
| 396 | /* | 412 | /* |
| 397 | * Make sure that the entry is actually | 413 | * Make sure that the entry is actually |
| 398 | * filled before putting it on the list. | 414 | * filled before putting it on the list. |
| @@ -480,19 +496,12 @@ u32 smack_to_secid(const char *smack) | |||
| 480 | * smack_from_cipso - find the Smack label associated with a CIPSO option | 496 | * smack_from_cipso - find the Smack label associated with a CIPSO option |
| 481 | * @level: Bell & LaPadula level from the network | 497 | * @level: Bell & LaPadula level from the network |
| 482 | * @cp: Bell & LaPadula categories from the network | 498 | * @cp: Bell & LaPadula categories from the network |
| 483 | * @result: where to put the Smack value | ||
| 484 | * | 499 | * |
| 485 | * This is a simple lookup in the label table. | 500 | * This is a simple lookup in the label table. |
| 486 | * | 501 | * |
| 487 | * This is an odd duck as far as smack handling goes in that | 502 | * Return the matching label from the label list or NULL. |
| 488 | * it sends back a copy of the smack label rather than a pointer | ||
| 489 | * to the master list. This is done because it is possible for | ||
| 490 | * a foreign host to send a smack label that is new to this | ||
| 491 | * machine and hence not on the list. That would not be an | ||
| 492 | * issue except that adding an entry to the master list can't | ||
| 493 | * be done at that point. | ||
| 494 | */ | 503 | */ |
| 495 | void smack_from_cipso(u32 level, char *cp, char *result) | 504 | char *smack_from_cipso(u32 level, char *cp) |
| 496 | { | 505 | { |
| 497 | struct smack_known *kp; | 506 | struct smack_known *kp; |
| 498 | char *final = NULL; | 507 | char *final = NULL; |
| @@ -509,12 +518,13 @@ void smack_from_cipso(u32 level, char *cp, char *result) | |||
| 509 | final = kp->smk_known; | 518 | final = kp->smk_known; |
| 510 | 519 | ||
| 511 | spin_unlock_bh(&kp->smk_cipsolock); | 520 | spin_unlock_bh(&kp->smk_cipsolock); |
| 521 | |||
| 522 | if (final != NULL) | ||
| 523 | break; | ||
| 512 | } | 524 | } |
| 513 | rcu_read_unlock(); | 525 | rcu_read_unlock(); |
| 514 | if (final == NULL) | 526 | |
| 515 | final = smack_known_huh.smk_known; | 527 | return final; |
| 516 | strncpy(result, final, SMK_MAXLEN); | ||
| 517 | return; | ||
| 518 | } | 528 | } |
| 519 | 529 | ||
| 520 | /** | 530 | /** |
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index b9c5e149903b..7db62b48eb42 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c | |||
| @@ -5,12 +5,13 @@ | |||
| 5 | * | 5 | * |
| 6 | * Authors: | 6 | * Authors: |
| 7 | * Casey Schaufler <casey@schaufler-ca.com> | 7 | * Casey Schaufler <casey@schaufler-ca.com> |
| 8 | * Jarkko Sakkinen <ext-jarkko.2.sakkinen@nokia.com> | 8 | * Jarkko Sakkinen <jarkko.sakkinen@intel.com> |
| 9 | * | 9 | * |
| 10 | * Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com> | 10 | * Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com> |
| 11 | * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. | 11 | * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. |
| 12 | * Paul Moore <paul@paul-moore.com> | 12 | * Paul Moore <paul@paul-moore.com> |
| 13 | * Copyright (C) 2010 Nokia Corporation | 13 | * Copyright (C) 2010 Nokia Corporation |
| 14 | * Copyright (C) 2011 Intel Corporation. | ||
| 14 | * | 15 | * |
| 15 | * This program is free software; you can redistribute it and/or modify | 16 | * This program is free software; you can redistribute it and/or modify |
| 16 | * it under the terms of the GNU General Public License version 2, | 17 | * it under the terms of the GNU General Public License version 2, |
| @@ -34,6 +35,7 @@ | |||
| 34 | #include <linux/audit.h> | 35 | #include <linux/audit.h> |
| 35 | #include <linux/magic.h> | 36 | #include <linux/magic.h> |
| 36 | #include <linux/dcache.h> | 37 | #include <linux/dcache.h> |
| 38 | #include <linux/personality.h> | ||
| 37 | #include "smack.h" | 39 | #include "smack.h" |
| 38 | 40 | ||
| 39 | #define task_security(task) (task_cred_xxx((task), security)) | 41 | #define task_security(task) (task_cred_xxx((task), security)) |
| @@ -441,11 +443,17 @@ static int smack_sb_umount(struct vfsmount *mnt, int flags) | |||
| 441 | * BPRM hooks | 443 | * BPRM hooks |
| 442 | */ | 444 | */ |
| 443 | 445 | ||
| 446 | /** | ||
| 447 | * smack_bprm_set_creds - set creds for exec | ||
| 448 | * @bprm: the exec information | ||
| 449 | * | ||
| 450 | * Returns 0 if it gets a blob, -ENOMEM otherwise | ||
| 451 | */ | ||
| 444 | static int smack_bprm_set_creds(struct linux_binprm *bprm) | 452 | static int smack_bprm_set_creds(struct linux_binprm *bprm) |
| 445 | { | 453 | { |
| 446 | struct task_smack *tsp = bprm->cred->security; | 454 | struct inode *inode = bprm->file->f_path.dentry->d_inode; |
| 455 | struct task_smack *bsp = bprm->cred->security; | ||
| 447 | struct inode_smack *isp; | 456 | struct inode_smack *isp; |
| 448 | struct dentry *dp; | ||
| 449 | int rc; | 457 | int rc; |
| 450 | 458 | ||
| 451 | rc = cap_bprm_set_creds(bprm); | 459 | rc = cap_bprm_set_creds(bprm); |
| @@ -455,20 +463,48 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm) | |||
| 455 | if (bprm->cred_prepared) | 463 | if (bprm->cred_prepared) |
| 456 | return 0; | 464 | return 0; |
| 457 | 465 | ||
| 458 | if (bprm->file == NULL || bprm->file->f_dentry == NULL) | 466 | isp = inode->i_security; |
| 467 | if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task) | ||
| 459 | return 0; | 468 | return 0; |
| 460 | 469 | ||
| 461 | dp = bprm->file->f_dentry; | 470 | if (bprm->unsafe) |
| 471 | return -EPERM; | ||
| 462 | 472 | ||
| 463 | if (dp->d_inode == NULL) | 473 | bsp->smk_task = isp->smk_task; |
| 464 | return 0; | 474 | bprm->per_clear |= PER_CLEAR_ON_SETID; |
| 465 | 475 | ||
| 466 | isp = dp->d_inode->i_security; | 476 | return 0; |
| 477 | } | ||
| 467 | 478 | ||
| 468 | if (isp->smk_task != NULL) | 479 | /** |
| 469 | tsp->smk_task = isp->smk_task; | 480 | * smack_bprm_committing_creds - Prepare to install the new credentials |
| 481 | * from bprm. | ||
| 482 | * | ||
| 483 | * @bprm: binprm for exec | ||
| 484 | */ | ||
| 485 | static void smack_bprm_committing_creds(struct linux_binprm *bprm) | ||
| 486 | { | ||
| 487 | struct task_smack *bsp = bprm->cred->security; | ||
| 470 | 488 | ||
| 471 | return 0; | 489 | if (bsp->smk_task != bsp->smk_forked) |
| 490 | current->pdeath_signal = 0; | ||
| 491 | } | ||
| 492 | |||
| 493 | /** | ||
| 494 | * smack_bprm_secureexec - Return the decision to use secureexec. | ||
| 495 | * @bprm: binprm for exec | ||
| 496 | * | ||
| 497 | * Returns 0 on success. | ||
| 498 | */ | ||
| 499 | static int smack_bprm_secureexec(struct linux_binprm *bprm) | ||
| 500 | { | ||
| 501 | struct task_smack *tsp = current_security(); | ||
| 502 | int ret = cap_bprm_secureexec(bprm); | ||
| 503 | |||
| 504 | if (!ret && (tsp->smk_task != tsp->smk_forked)) | ||
| 505 | ret = 1; | ||
| 506 | |||
| 507 | return ret; | ||
| 472 | } | 508 | } |
| 473 | 509 | ||
| 474 | /* | 510 | /* |
| @@ -516,6 +552,8 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, | |||
| 516 | const struct qstr *qstr, char **name, | 552 | const struct qstr *qstr, char **name, |
| 517 | void **value, size_t *len) | 553 | void **value, size_t *len) |
| 518 | { | 554 | { |
| 555 | struct smack_known *skp; | ||
| 556 | char *csp = smk_of_current(); | ||
| 519 | char *isp = smk_of_inode(inode); | 557 | char *isp = smk_of_inode(inode); |
| 520 | char *dsp = smk_of_inode(dir); | 558 | char *dsp = smk_of_inode(dir); |
| 521 | int may; | 559 | int may; |
| @@ -527,8 +565,9 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, | |||
| 527 | } | 565 | } |
| 528 | 566 | ||
| 529 | if (value) { | 567 | if (value) { |
| 568 | skp = smk_find_entry(csp); | ||
| 530 | rcu_read_lock(); | 569 | rcu_read_lock(); |
| 531 | may = smk_access_entry(smk_of_current(), dsp, &smack_rule_list); | 570 | may = smk_access_entry(csp, dsp, &skp->smk_rules); |
| 532 | rcu_read_unlock(); | 571 | rcu_read_unlock(); |
| 533 | 572 | ||
| 534 | /* | 573 | /* |
| @@ -841,7 +880,7 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name, | |||
| 841 | return; | 880 | return; |
| 842 | } | 881 | } |
| 843 | 882 | ||
| 844 | /* | 883 | /** |
| 845 | * smack_inode_getxattr - Smack check on getxattr | 884 | * smack_inode_getxattr - Smack check on getxattr |
| 846 | * @dentry: the object | 885 | * @dentry: the object |
| 847 | * @name: unused | 886 | * @name: unused |
| @@ -858,7 +897,7 @@ static int smack_inode_getxattr(struct dentry *dentry, const char *name) | |||
| 858 | return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ, &ad); | 897 | return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ, &ad); |
| 859 | } | 898 | } |
| 860 | 899 | ||
| 861 | /* | 900 | /** |
| 862 | * smack_inode_removexattr - Smack check on removexattr | 901 | * smack_inode_removexattr - Smack check on removexattr |
| 863 | * @dentry: the object | 902 | * @dentry: the object |
| 864 | * @name: name of the attribute | 903 | * @name: name of the attribute |
| @@ -1088,36 +1127,31 @@ static int smack_file_lock(struct file *file, unsigned int cmd) | |||
| 1088 | * @cmd: what action to check | 1127 | * @cmd: what action to check |
| 1089 | * @arg: unused | 1128 | * @arg: unused |
| 1090 | * | 1129 | * |
| 1130 | * Generally these operations are harmless. | ||
| 1131 | * File locking operations present an obvious mechanism | ||
| 1132 | * for passing information, so they require write access. | ||
| 1133 | * | ||
| 1091 | * Returns 0 if current has access, error code otherwise | 1134 | * Returns 0 if current has access, error code otherwise |
| 1092 | */ | 1135 | */ |
| 1093 | static int smack_file_fcntl(struct file *file, unsigned int cmd, | 1136 | static int smack_file_fcntl(struct file *file, unsigned int cmd, |
| 1094 | unsigned long arg) | 1137 | unsigned long arg) |
| 1095 | { | 1138 | { |
| 1096 | struct smk_audit_info ad; | 1139 | struct smk_audit_info ad; |
| 1097 | int rc; | 1140 | int rc = 0; |
| 1098 | 1141 | ||
| 1099 | smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); | ||
| 1100 | smk_ad_setfield_u_fs_path(&ad, file->f_path); | ||
| 1101 | 1142 | ||
| 1102 | switch (cmd) { | 1143 | switch (cmd) { |
| 1103 | case F_DUPFD: | ||
| 1104 | case F_GETFD: | ||
| 1105 | case F_GETFL: | ||
| 1106 | case F_GETLK: | 1144 | case F_GETLK: |
| 1107 | case F_GETOWN: | ||
| 1108 | case F_GETSIG: | ||
| 1109 | rc = smk_curacc(file->f_security, MAY_READ, &ad); | ||
| 1110 | break; | ||
| 1111 | case F_SETFD: | ||
| 1112 | case F_SETFL: | ||
| 1113 | case F_SETLK: | 1145 | case F_SETLK: |
| 1114 | case F_SETLKW: | 1146 | case F_SETLKW: |
| 1115 | case F_SETOWN: | 1147 | case F_SETOWN: |
| 1116 | case F_SETSIG: | 1148 | case F_SETSIG: |
| 1149 | smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); | ||
| 1150 | smk_ad_setfield_u_fs_path(&ad, file->f_path); | ||
| 1117 | rc = smk_curacc(file->f_security, MAY_WRITE, &ad); | 1151 | rc = smk_curacc(file->f_security, MAY_WRITE, &ad); |
| 1118 | break; | 1152 | break; |
| 1119 | default: | 1153 | default: |
| 1120 | rc = smk_curacc(file->f_security, MAY_READWRITE, &ad); | 1154 | break; |
| 1121 | } | 1155 | } |
| 1122 | 1156 | ||
| 1123 | return rc; | 1157 | return rc; |
| @@ -1138,6 +1172,7 @@ static int smack_file_mmap(struct file *file, | |||
| 1138 | unsigned long flags, unsigned long addr, | 1172 | unsigned long flags, unsigned long addr, |
| 1139 | unsigned long addr_only) | 1173 | unsigned long addr_only) |
| 1140 | { | 1174 | { |
| 1175 | struct smack_known *skp; | ||
| 1141 | struct smack_rule *srp; | 1176 | struct smack_rule *srp; |
| 1142 | struct task_smack *tsp; | 1177 | struct task_smack *tsp; |
| 1143 | char *sp; | 1178 | char *sp; |
| @@ -1170,6 +1205,7 @@ static int smack_file_mmap(struct file *file, | |||
| 1170 | 1205 | ||
| 1171 | tsp = current_security(); | 1206 | tsp = current_security(); |
| 1172 | sp = smk_of_current(); | 1207 | sp = smk_of_current(); |
| 1208 | skp = smk_find_entry(sp); | ||
| 1173 | rc = 0; | 1209 | rc = 0; |
| 1174 | 1210 | ||
| 1175 | rcu_read_lock(); | 1211 | rcu_read_lock(); |
| @@ -1177,15 +1213,8 @@ static int smack_file_mmap(struct file *file, | |||
| 1177 | * For each Smack rule associated with the subject | 1213 | * For each Smack rule associated with the subject |
| 1178 | * label verify that the SMACK64MMAP also has access | 1214 | * label verify that the SMACK64MMAP also has access |
| 1179 | * to that rule's object label. | 1215 | * to that rule's object label. |
| 1180 | * | ||
| 1181 | * Because neither of the labels comes | ||
| 1182 | * from the networking code it is sufficient | ||
| 1183 | * to compare pointers. | ||
| 1184 | */ | 1216 | */ |
| 1185 | list_for_each_entry_rcu(srp, &smack_rule_list, list) { | 1217 | list_for_each_entry_rcu(srp, &skp->smk_rules, list) { |
| 1186 | if (srp->smk_subject != sp) | ||
| 1187 | continue; | ||
| 1188 | |||
| 1189 | osmack = srp->smk_object; | 1218 | osmack = srp->smk_object; |
| 1190 | /* | 1219 | /* |
| 1191 | * Matching labels always allows access. | 1220 | * Matching labels always allows access. |
| @@ -1214,7 +1243,8 @@ static int smack_file_mmap(struct file *file, | |||
| 1214 | * If there isn't one a SMACK64MMAP subject | 1243 | * If there isn't one a SMACK64MMAP subject |
| 1215 | * can't have as much access as current. | 1244 | * can't have as much access as current. |
| 1216 | */ | 1245 | */ |
| 1217 | mmay = smk_access_entry(msmack, osmack, &smack_rule_list); | 1246 | skp = smk_find_entry(msmack); |
| 1247 | mmay = smk_access_entry(msmack, osmack, &skp->smk_rules); | ||
| 1218 | if (mmay == -ENOENT) { | 1248 | if (mmay == -ENOENT) { |
| 1219 | rc = -EACCES; | 1249 | rc = -EACCES; |
| 1220 | break; | 1250 | break; |
| @@ -1315,6 +1345,24 @@ static int smack_file_receive(struct file *file) | |||
| 1315 | return smk_curacc(file->f_security, may, &ad); | 1345 | return smk_curacc(file->f_security, may, &ad); |
| 1316 | } | 1346 | } |
| 1317 | 1347 | ||
| 1348 | /** | ||
| 1349 | * smack_dentry_open - Smack dentry open processing | ||
| 1350 | * @file: the object | ||
| 1351 | * @cred: unused | ||
| 1352 | * | ||
| 1353 | * Set the security blob in the file structure. | ||
| 1354 | * | ||
| 1355 | * Returns 0 | ||
| 1356 | */ | ||
| 1357 | static int smack_dentry_open(struct file *file, const struct cred *cred) | ||
| 1358 | { | ||
| 1359 | struct inode_smack *isp = file->f_path.dentry->d_inode->i_security; | ||
| 1360 | |||
| 1361 | file->f_security = isp->smk_inode; | ||
| 1362 | |||
| 1363 | return 0; | ||
| 1364 | } | ||
| 1365 | |||
| 1318 | /* | 1366 | /* |
| 1319 | * Task hooks | 1367 | * Task hooks |
| 1320 | */ | 1368 | */ |
| @@ -1455,15 +1503,17 @@ static int smack_kernel_create_files_as(struct cred *new, | |||
| 1455 | /** | 1503 | /** |
| 1456 | * smk_curacc_on_task - helper to log task related access | 1504 | * smk_curacc_on_task - helper to log task related access |
| 1457 | * @p: the task object | 1505 | * @p: the task object |
| 1458 | * @access : the access requested | 1506 | * @access: the access requested |
| 1507 | * @caller: name of the calling function for audit | ||
| 1459 | * | 1508 | * |
| 1460 | * Return 0 if access is permitted | 1509 | * Return 0 if access is permitted |
| 1461 | */ | 1510 | */ |
| 1462 | static int smk_curacc_on_task(struct task_struct *p, int access) | 1511 | static int smk_curacc_on_task(struct task_struct *p, int access, |
| 1512 | const char *caller) | ||
| 1463 | { | 1513 | { |
| 1464 | struct smk_audit_info ad; | 1514 | struct smk_audit_info ad; |
| 1465 | 1515 | ||
| 1466 | smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); | 1516 | smk_ad_init(&ad, caller, LSM_AUDIT_DATA_TASK); |
| 1467 | smk_ad_setfield_u_tsk(&ad, p); | 1517 | smk_ad_setfield_u_tsk(&ad, p); |
| 1468 | return smk_curacc(smk_of_task(task_security(p)), access, &ad); | 1518 | return smk_curacc(smk_of_task(task_security(p)), access, &ad); |
| 1469 | } | 1519 | } |
| @@ -1477,7 +1527,7 @@ static int smk_curacc_on_task(struct task_struct *p, int access) | |||
| 1477 | */ | 1527 | */ |
| 1478 | static int smack_task_setpgid(struct task_struct *p, pid_t pgid) | 1528 | static int smack_task_setpgid(struct task_struct *p, pid_t pgid) |
| 1479 | { | 1529 | { |
| 1480 | return smk_curacc_on_task(p, MAY_WRITE); | 1530 | return smk_curacc_on_task(p, MAY_WRITE, __func__); |
| 1481 | } | 1531 | } |
| 1482 | 1532 | ||
| 1483 | /** | 1533 | /** |
| @@ -1488,7 +1538,7 @@ static int smack_task_setpgid(struct task_struct *p, pid_t pgid) | |||
| 1488 | */ | 1538 | */ |
| 1489 | static int smack_task_getpgid(struct task_struct *p) | 1539 | static int smack_task_getpgid(struct task_struct *p) |
| 1490 | { | 1540 | { |
| 1491 | return smk_curacc_on_task(p, MAY_READ); | 1541 | return smk_curacc_on_task(p, MAY_READ, __func__); |
| 1492 | } | 1542 | } |
| 1493 | 1543 | ||
| 1494 | /** | 1544 | /** |
| @@ -1499,7 +1549,7 @@ static int smack_task_getpgid(struct task_struct *p) | |||
| 1499 | */ | 1549 | */ |
| 1500 | static int smack_task_getsid(struct task_struct *p) | 1550 | static int smack_task_getsid(struct task_struct *p) |
| 1501 | { | 1551 | { |
| 1502 | return smk_curacc_on_task(p, MAY_READ); | 1552 | return smk_curacc_on_task(p, MAY_READ, __func__); |
| 1503 | } | 1553 | } |
| 1504 | 1554 | ||
| 1505 | /** | 1555 | /** |
| @@ -1527,7 +1577,7 @@ static int smack_task_setnice(struct task_struct *p, int nice) | |||
| 1527 | 1577 | ||
| 1528 | rc = cap_task_setnice(p, nice); | 1578 | rc = cap_task_setnice(p, nice); |
| 1529 | if (rc == 0) | 1579 | if (rc == 0) |
| 1530 | rc = smk_curacc_on_task(p, MAY_WRITE); | 1580 | rc = smk_curacc_on_task(p, MAY_WRITE, __func__); |
| 1531 | return rc; | 1581 | return rc; |
| 1532 | } | 1582 | } |
| 1533 | 1583 | ||
| @@ -1544,7 +1594,7 @@ static int smack_task_setioprio(struct task_struct *p, int ioprio) | |||
| 1544 | 1594 | ||
| 1545 | rc = cap_task_setioprio(p, ioprio); | 1595 | rc = cap_task_setioprio(p, ioprio); |
| 1546 | if (rc == 0) | 1596 | if (rc == 0) |
| 1547 | rc = smk_curacc_on_task(p, MAY_WRITE); | 1597 | rc = smk_curacc_on_task(p, MAY_WRITE, __func__); |
| 1548 | return rc; | 1598 | return rc; |
| 1549 | } | 1599 | } |
| 1550 | 1600 | ||
| @@ -1556,7 +1606,7 @@ static int smack_task_setioprio(struct task_struct *p, int ioprio) | |||
| 1556 | */ | 1606 | */ |
| 1557 | static int smack_task_getioprio(struct task_struct *p) | 1607 | static int smack_task_getioprio(struct task_struct *p) |
| 1558 | { | 1608 | { |
| 1559 | return smk_curacc_on_task(p, MAY_READ); | 1609 | return smk_curacc_on_task(p, MAY_READ, __func__); |
| 1560 | } | 1610 | } |
| 1561 | 1611 | ||
| 1562 | /** | 1612 | /** |
| @@ -1573,7 +1623,7 @@ static int smack_task_setscheduler(struct task_struct *p) | |||
| 1573 | 1623 | ||
| 1574 | rc = cap_task_setscheduler(p); | 1624 | rc = cap_task_setscheduler(p); |
| 1575 | if (rc == 0) | 1625 | if (rc == 0) |
| 1576 | rc = smk_curacc_on_task(p, MAY_WRITE); | 1626 | rc = smk_curacc_on_task(p, MAY_WRITE, __func__); |
| 1577 | return rc; | 1627 | return rc; |
| 1578 | } | 1628 | } |
| 1579 | 1629 | ||
| @@ -1585,7 +1635,7 @@ static int smack_task_setscheduler(struct task_struct *p) | |||
| 1585 | */ | 1635 | */ |
| 1586 | static int smack_task_getscheduler(struct task_struct *p) | 1636 | static int smack_task_getscheduler(struct task_struct *p) |
| 1587 | { | 1637 | { |
| 1588 | return smk_curacc_on_task(p, MAY_READ); | 1638 | return smk_curacc_on_task(p, MAY_READ, __func__); |
| 1589 | } | 1639 | } |
| 1590 | 1640 | ||
| 1591 | /** | 1641 | /** |
| @@ -1596,7 +1646,7 @@ static int smack_task_getscheduler(struct task_struct *p) | |||
| 1596 | */ | 1646 | */ |
| 1597 | static int smack_task_movememory(struct task_struct *p) | 1647 | static int smack_task_movememory(struct task_struct *p) |
| 1598 | { | 1648 | { |
| 1599 | return smk_curacc_on_task(p, MAY_WRITE); | 1649 | return smk_curacc_on_task(p, MAY_WRITE, __func__); |
| 1600 | } | 1650 | } |
| 1601 | 1651 | ||
| 1602 | /** | 1652 | /** |
| @@ -1711,7 +1761,7 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags) | |||
| 1711 | 1761 | ||
| 1712 | ssp->smk_in = csp; | 1762 | ssp->smk_in = csp; |
| 1713 | ssp->smk_out = csp; | 1763 | ssp->smk_out = csp; |
| 1714 | ssp->smk_packet[0] = '\0'; | 1764 | ssp->smk_packet = NULL; |
| 1715 | 1765 | ||
| 1716 | sk->sk_security = ssp; | 1766 | sk->sk_security = ssp; |
| 1717 | 1767 | ||
| @@ -2753,6 +2803,7 @@ static int smack_unix_stream_connect(struct sock *sock, | |||
| 2753 | { | 2803 | { |
| 2754 | struct socket_smack *ssp = sock->sk_security; | 2804 | struct socket_smack *ssp = sock->sk_security; |
| 2755 | struct socket_smack *osp = other->sk_security; | 2805 | struct socket_smack *osp = other->sk_security; |
| 2806 | struct socket_smack *nsp = newsk->sk_security; | ||
| 2756 | struct smk_audit_info ad; | 2807 | struct smk_audit_info ad; |
| 2757 | int rc = 0; | 2808 | int rc = 0; |
| 2758 | 2809 | ||
| @@ -2762,6 +2813,14 @@ static int smack_unix_stream_connect(struct sock *sock, | |||
| 2762 | if (!capable(CAP_MAC_OVERRIDE)) | 2813 | if (!capable(CAP_MAC_OVERRIDE)) |
| 2763 | rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad); | 2814 | rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad); |
| 2764 | 2815 | ||
| 2816 | /* | ||
| 2817 | * Cross reference the peer labels for SO_PEERSEC. | ||
| 2818 | */ | ||
| 2819 | if (rc == 0) { | ||
| 2820 | nsp->smk_packet = ssp->smk_out; | ||
| 2821 | ssp->smk_packet = osp->smk_out; | ||
| 2822 | } | ||
| 2823 | |||
| 2765 | return rc; | 2824 | return rc; |
| 2766 | } | 2825 | } |
| 2767 | 2826 | ||
| @@ -2813,16 +2872,17 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg, | |||
| 2813 | return smack_netlabel_send(sock->sk, sip); | 2872 | return smack_netlabel_send(sock->sk, sip); |
| 2814 | } | 2873 | } |
| 2815 | 2874 | ||
| 2816 | |||
| 2817 | /** | 2875 | /** |
| 2818 | * smack_from_secattr - Convert a netlabel attr.mls.lvl/attr.mls.cat pair to smack | 2876 | * smack_from_secattr - Convert a netlabel attr.mls.lvl/attr.mls.cat pair to smack |
| 2819 | * @sap: netlabel secattr | 2877 | * @sap: netlabel secattr |
| 2820 | * @sip: where to put the result | 2878 | * @ssp: socket security information |
| 2821 | * | 2879 | * |
| 2822 | * Copies a smack label into sip | 2880 | * Returns a pointer to a Smack label found on the label list. |
| 2823 | */ | 2881 | */ |
| 2824 | static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip) | 2882 | static char *smack_from_secattr(struct netlbl_lsm_secattr *sap, |
| 2883 | struct socket_smack *ssp) | ||
| 2825 | { | 2884 | { |
| 2885 | struct smack_known *skp; | ||
| 2826 | char smack[SMK_LABELLEN]; | 2886 | char smack[SMK_LABELLEN]; |
| 2827 | char *sp; | 2887 | char *sp; |
| 2828 | int pcat; | 2888 | int pcat; |
| @@ -2852,15 +2912,43 @@ static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip) | |||
| 2852 | * we are already done. WeeHee. | 2912 | * we are already done. WeeHee. |
| 2853 | */ | 2913 | */ |
| 2854 | if (sap->attr.mls.lvl == smack_cipso_direct) { | 2914 | if (sap->attr.mls.lvl == smack_cipso_direct) { |
| 2855 | memcpy(sip, smack, SMK_MAXLEN); | 2915 | /* |
| 2856 | return; | 2916 | * The label sent is usually on the label list. |
| 2917 | * | ||
| 2918 | * If it is not we may still want to allow the | ||
| 2919 | * delivery. | ||
| 2920 | * | ||
| 2921 | * If the recipient is accepting all packets | ||
| 2922 | * because it is using the star ("*") label | ||
| 2923 | * for SMACK64IPIN provide the web ("@") label | ||
| 2924 | * so that a directed response will succeed. | ||
| 2925 | * This is not very correct from a MAC point | ||
| 2926 | * of view, but gets around the problem that | ||
| 2927 | * locking prevents adding the newly discovered | ||
| 2928 | * label to the list. | ||
| 2929 | * The case where the recipient is not using | ||
| 2930 | * the star label should obviously fail. | ||
| 2931 | * The easy way to do this is to provide the | ||
| 2932 | * star label as the subject label. | ||
| 2933 | */ | ||
| 2934 | skp = smk_find_entry(smack); | ||
| 2935 | if (skp != NULL) | ||
| 2936 | return skp->smk_known; | ||
| 2937 | if (ssp != NULL && | ||
| 2938 | ssp->smk_in == smack_known_star.smk_known) | ||
| 2939 | return smack_known_web.smk_known; | ||
| 2940 | return smack_known_star.smk_known; | ||
| 2857 | } | 2941 | } |
| 2858 | /* | 2942 | /* |
| 2859 | * Look it up in the supplied table if it is not | 2943 | * Look it up in the supplied table if it is not |
| 2860 | * a direct mapping. | 2944 | * a direct mapping. |
| 2861 | */ | 2945 | */ |
| 2862 | smack_from_cipso(sap->attr.mls.lvl, smack, sip); | 2946 | sp = smack_from_cipso(sap->attr.mls.lvl, smack); |
| 2863 | return; | 2947 | if (sp != NULL) |
| 2948 | return sp; | ||
| 2949 | if (ssp != NULL && ssp->smk_in == smack_known_star.smk_known) | ||
| 2950 | return smack_known_web.smk_known; | ||
| 2951 | return smack_known_star.smk_known; | ||
| 2864 | } | 2952 | } |
| 2865 | if ((sap->flags & NETLBL_SECATTR_SECID) != 0) { | 2953 | if ((sap->flags & NETLBL_SECATTR_SECID) != 0) { |
| 2866 | /* | 2954 | /* |
| @@ -2875,16 +2963,14 @@ static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip) | |||
| 2875 | * secid is from a fallback. | 2963 | * secid is from a fallback. |
| 2876 | */ | 2964 | */ |
| 2877 | BUG_ON(sp == NULL); | 2965 | BUG_ON(sp == NULL); |
| 2878 | strncpy(sip, sp, SMK_MAXLEN); | 2966 | return sp; |
| 2879 | return; | ||
| 2880 | } | 2967 | } |
| 2881 | /* | 2968 | /* |
| 2882 | * Without guidance regarding the smack value | 2969 | * Without guidance regarding the smack value |
| 2883 | * for the packet fall back on the network | 2970 | * for the packet fall back on the network |
| 2884 | * ambient value. | 2971 | * ambient value. |
| 2885 | */ | 2972 | */ |
| 2886 | strncpy(sip, smack_net_ambient, SMK_MAXLEN); | 2973 | return smack_net_ambient; |
| 2887 | return; | ||
| 2888 | } | 2974 | } |
| 2889 | 2975 | ||
| 2890 | /** | 2976 | /** |
| @@ -2898,7 +2984,6 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) | |||
| 2898 | { | 2984 | { |
| 2899 | struct netlbl_lsm_secattr secattr; | 2985 | struct netlbl_lsm_secattr secattr; |
| 2900 | struct socket_smack *ssp = sk->sk_security; | 2986 | struct socket_smack *ssp = sk->sk_security; |
| 2901 | char smack[SMK_LABELLEN]; | ||
| 2902 | char *csp; | 2987 | char *csp; |
| 2903 | int rc; | 2988 | int rc; |
| 2904 | struct smk_audit_info ad; | 2989 | struct smk_audit_info ad; |
| @@ -2911,10 +2996,9 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) | |||
| 2911 | netlbl_secattr_init(&secattr); | 2996 | netlbl_secattr_init(&secattr); |
| 2912 | 2997 | ||
| 2913 | rc = netlbl_skbuff_getattr(skb, sk->sk_family, &secattr); | 2998 | rc = netlbl_skbuff_getattr(skb, sk->sk_family, &secattr); |
| 2914 | if (rc == 0) { | 2999 | if (rc == 0) |
| 2915 | smack_from_secattr(&secattr, smack); | 3000 | csp = smack_from_secattr(&secattr, ssp); |
| 2916 | csp = smack; | 3001 | else |
| 2917 | } else | ||
| 2918 | csp = smack_net_ambient; | 3002 | csp = smack_net_ambient; |
| 2919 | 3003 | ||
| 2920 | netlbl_secattr_destroy(&secattr); | 3004 | netlbl_secattr_destroy(&secattr); |
| @@ -2951,15 +3035,19 @@ static int smack_socket_getpeersec_stream(struct socket *sock, | |||
| 2951 | int __user *optlen, unsigned len) | 3035 | int __user *optlen, unsigned len) |
| 2952 | { | 3036 | { |
| 2953 | struct socket_smack *ssp; | 3037 | struct socket_smack *ssp; |
| 2954 | int slen; | 3038 | char *rcp = ""; |
| 3039 | int slen = 1; | ||
| 2955 | int rc = 0; | 3040 | int rc = 0; |
| 2956 | 3041 | ||
| 2957 | ssp = sock->sk->sk_security; | 3042 | ssp = sock->sk->sk_security; |
| 2958 | slen = strlen(ssp->smk_packet) + 1; | 3043 | if (ssp->smk_packet != NULL) { |
| 3044 | rcp = ssp->smk_packet; | ||
| 3045 | slen = strlen(rcp) + 1; | ||
| 3046 | } | ||
| 2959 | 3047 | ||
| 2960 | if (slen > len) | 3048 | if (slen > len) |
| 2961 | rc = -ERANGE; | 3049 | rc = -ERANGE; |
| 2962 | else if (copy_to_user(optval, ssp->smk_packet, slen) != 0) | 3050 | else if (copy_to_user(optval, rcp, slen) != 0) |
| 2963 | rc = -EFAULT; | 3051 | rc = -EFAULT; |
| 2964 | 3052 | ||
| 2965 | if (put_user(slen, optlen) != 0) | 3053 | if (put_user(slen, optlen) != 0) |
| @@ -2982,8 +3070,8 @@ static int smack_socket_getpeersec_dgram(struct socket *sock, | |||
| 2982 | 3070 | ||
| 2983 | { | 3071 | { |
| 2984 | struct netlbl_lsm_secattr secattr; | 3072 | struct netlbl_lsm_secattr secattr; |
| 2985 | struct socket_smack *sp; | 3073 | struct socket_smack *ssp = NULL; |
| 2986 | char smack[SMK_LABELLEN]; | 3074 | char *sp; |
| 2987 | int family = PF_UNSPEC; | 3075 | int family = PF_UNSPEC; |
| 2988 | u32 s = 0; /* 0 is the invalid secid */ | 3076 | u32 s = 0; /* 0 is the invalid secid */ |
| 2989 | int rc; | 3077 | int rc; |
| @@ -2998,17 +3086,19 @@ static int smack_socket_getpeersec_dgram(struct socket *sock, | |||
| 2998 | family = sock->sk->sk_family; | 3086 | family = sock->sk->sk_family; |
| 2999 | 3087 | ||
| 3000 | if (family == PF_UNIX) { | 3088 | if (family == PF_UNIX) { |
| 3001 | sp = sock->sk->sk_security; | 3089 | ssp = sock->sk->sk_security; |
| 3002 | s = smack_to_secid(sp->smk_out); | 3090 | s = smack_to_secid(ssp->smk_out); |
| 3003 | } else if (family == PF_INET || family == PF_INET6) { | 3091 | } else if (family == PF_INET || family == PF_INET6) { |
| 3004 | /* | 3092 | /* |
| 3005 | * Translate what netlabel gave us. | 3093 | * Translate what netlabel gave us. |
| 3006 | */ | 3094 | */ |
| 3095 | if (sock != NULL && sock->sk != NULL) | ||
| 3096 | ssp = sock->sk->sk_security; | ||
| 3007 | netlbl_secattr_init(&secattr); | 3097 | netlbl_secattr_init(&secattr); |
| 3008 | rc = netlbl_skbuff_getattr(skb, family, &secattr); | 3098 | rc = netlbl_skbuff_getattr(skb, family, &secattr); |
| 3009 | if (rc == 0) { | 3099 | if (rc == 0) { |
| 3010 | smack_from_secattr(&secattr, smack); | 3100 | sp = smack_from_secattr(&secattr, ssp); |
| 3011 | s = smack_to_secid(smack); | 3101 | s = smack_to_secid(sp); |
| 3012 | } | 3102 | } |
| 3013 | netlbl_secattr_destroy(&secattr); | 3103 | netlbl_secattr_destroy(&secattr); |
| 3014 | } | 3104 | } |
| @@ -3056,7 +3146,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, | |||
| 3056 | struct netlbl_lsm_secattr secattr; | 3146 | struct netlbl_lsm_secattr secattr; |
| 3057 | struct sockaddr_in addr; | 3147 | struct sockaddr_in addr; |
| 3058 | struct iphdr *hdr; | 3148 | struct iphdr *hdr; |
| 3059 | char smack[SMK_LABELLEN]; | 3149 | char *sp; |
| 3060 | int rc; | 3150 | int rc; |
| 3061 | struct smk_audit_info ad; | 3151 | struct smk_audit_info ad; |
| 3062 | 3152 | ||
| @@ -3067,9 +3157,9 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, | |||
| 3067 | netlbl_secattr_init(&secattr); | 3157 | netlbl_secattr_init(&secattr); |
| 3068 | rc = netlbl_skbuff_getattr(skb, family, &secattr); | 3158 | rc = netlbl_skbuff_getattr(skb, family, &secattr); |
| 3069 | if (rc == 0) | 3159 | if (rc == 0) |
| 3070 | smack_from_secattr(&secattr, smack); | 3160 | sp = smack_from_secattr(&secattr, ssp); |
| 3071 | else | 3161 | else |
| 3072 | strncpy(smack, smack_known_huh.smk_known, SMK_MAXLEN); | 3162 | sp = smack_known_huh.smk_known; |
| 3073 | netlbl_secattr_destroy(&secattr); | 3163 | netlbl_secattr_destroy(&secattr); |
| 3074 | 3164 | ||
| 3075 | #ifdef CONFIG_AUDIT | 3165 | #ifdef CONFIG_AUDIT |
| @@ -3082,7 +3172,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, | |||
| 3082 | * Receiving a packet requires that the other end be able to write | 3172 | * Receiving a packet requires that the other end be able to write |
| 3083 | * here. Read access is not required. | 3173 | * here. Read access is not required. |
| 3084 | */ | 3174 | */ |
| 3085 | rc = smk_access(smack, ssp->smk_in, MAY_WRITE, &ad); | 3175 | rc = smk_access(sp, ssp->smk_in, MAY_WRITE, &ad); |
| 3086 | if (rc != 0) | 3176 | if (rc != 0) |
| 3087 | return rc; | 3177 | return rc; |
| 3088 | 3178 | ||
| @@ -3090,7 +3180,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, | |||
| 3090 | * Save the peer's label in the request_sock so we can later setup | 3180 | * Save the peer's label in the request_sock so we can later setup |
| 3091 | * smk_packet in the child socket so that SO_PEERCRED can report it. | 3181 | * smk_packet in the child socket so that SO_PEERCRED can report it. |
| 3092 | */ | 3182 | */ |
| 3093 | req->peer_secid = smack_to_secid(smack); | 3183 | req->peer_secid = smack_to_secid(sp); |
| 3094 | 3184 | ||
| 3095 | /* | 3185 | /* |
| 3096 | * We need to decide if we want to label the incoming connection here | 3186 | * 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, | |||
| 3103 | if (smack_host_label(&addr) == NULL) { | 3193 | if (smack_host_label(&addr) == NULL) { |
| 3104 | rcu_read_unlock(); | 3194 | rcu_read_unlock(); |
| 3105 | netlbl_secattr_init(&secattr); | 3195 | netlbl_secattr_init(&secattr); |
| 3106 | smack_to_secattr(smack, &secattr); | 3196 | smack_to_secattr(sp, &secattr); |
| 3107 | rc = netlbl_req_setattr(req, &secattr); | 3197 | rc = netlbl_req_setattr(req, &secattr); |
| 3108 | netlbl_secattr_destroy(&secattr); | 3198 | netlbl_secattr_destroy(&secattr); |
| 3109 | } else { | 3199 | } else { |
| @@ -3125,13 +3215,11 @@ static void smack_inet_csk_clone(struct sock *sk, | |||
| 3125 | const struct request_sock *req) | 3215 | const struct request_sock *req) |
| 3126 | { | 3216 | { |
| 3127 | struct socket_smack *ssp = sk->sk_security; | 3217 | struct socket_smack *ssp = sk->sk_security; |
| 3128 | char *smack; | ||
| 3129 | 3218 | ||
| 3130 | if (req->peer_secid != 0) { | 3219 | if (req->peer_secid != 0) |
| 3131 | smack = smack_from_secid(req->peer_secid); | 3220 | ssp->smk_packet = smack_from_secid(req->peer_secid); |
| 3132 | strncpy(ssp->smk_packet, smack, SMK_MAXLEN); | 3221 | else |
| 3133 | } else | 3222 | ssp->smk_packet = NULL; |
| 3134 | ssp->smk_packet[0] = '\0'; | ||
| 3135 | } | 3223 | } |
| 3136 | 3224 | ||
| 3137 | /* | 3225 | /* |
| @@ -3409,6 +3497,8 @@ struct security_operations smack_ops = { | |||
| 3409 | .sb_umount = smack_sb_umount, | 3497 | .sb_umount = smack_sb_umount, |
| 3410 | 3498 | ||
| 3411 | .bprm_set_creds = smack_bprm_set_creds, | 3499 | .bprm_set_creds = smack_bprm_set_creds, |
| 3500 | .bprm_committing_creds = smack_bprm_committing_creds, | ||
| 3501 | .bprm_secureexec = smack_bprm_secureexec, | ||
| 3412 | 3502 | ||
| 3413 | .inode_alloc_security = smack_inode_alloc_security, | 3503 | .inode_alloc_security = smack_inode_alloc_security, |
| 3414 | .inode_free_security = smack_inode_free_security, | 3504 | .inode_free_security = smack_inode_free_security, |
| @@ -3440,6 +3530,8 @@ struct security_operations smack_ops = { | |||
| 3440 | .file_send_sigiotask = smack_file_send_sigiotask, | 3530 | .file_send_sigiotask = smack_file_send_sigiotask, |
| 3441 | .file_receive = smack_file_receive, | 3531 | .file_receive = smack_file_receive, |
| 3442 | 3532 | ||
| 3533 | .dentry_open = smack_dentry_open, | ||
| 3534 | |||
| 3443 | .cred_alloc_blank = smack_cred_alloc_blank, | 3535 | .cred_alloc_blank = smack_cred_alloc_blank, |
| 3444 | .cred_free = smack_cred_free, | 3536 | .cred_free = smack_cred_free, |
| 3445 | .cred_prepare = smack_cred_prepare, | 3537 | .cred_prepare = smack_cred_prepare, |
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index f93460156dce..6aceef518a41 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c | |||
| @@ -44,6 +44,7 @@ enum smk_inos { | |||
| 44 | SMK_ONLYCAP = 9, /* the only "capable" label */ | 44 | SMK_ONLYCAP = 9, /* the only "capable" label */ |
| 45 | SMK_LOGGING = 10, /* logging */ | 45 | SMK_LOGGING = 10, /* logging */ |
| 46 | SMK_LOAD_SELF = 11, /* task specific rules */ | 46 | SMK_LOAD_SELF = 11, /* task specific rules */ |
| 47 | SMK_ACCESSES = 12, /* access policy */ | ||
| 47 | }; | 48 | }; |
| 48 | 49 | ||
| 49 | /* | 50 | /* |
| @@ -85,6 +86,16 @@ char *smack_onlycap; | |||
| 85 | */ | 86 | */ |
| 86 | 87 | ||
| 87 | LIST_HEAD(smk_netlbladdr_list); | 88 | LIST_HEAD(smk_netlbladdr_list); |
| 89 | |||
| 90 | /* | ||
| 91 | * Rule lists are maintained for each label. | ||
| 92 | * This master list is just for reading /smack/load. | ||
| 93 | */ | ||
| 94 | struct smack_master_list { | ||
| 95 | struct list_head list; | ||
| 96 | struct smack_rule *smk_rule; | ||
| 97 | }; | ||
| 98 | |||
| 88 | LIST_HEAD(smack_rule_list); | 99 | LIST_HEAD(smack_rule_list); |
| 89 | 100 | ||
| 90 | static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT; | 101 | static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT; |
| @@ -92,7 +103,7 @@ static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT; | |||
| 92 | const char *smack_cipso_option = SMACK_CIPSO_OPTION; | 103 | const char *smack_cipso_option = SMACK_CIPSO_OPTION; |
| 93 | 104 | ||
| 94 | 105 | ||
| 95 | #define SEQ_READ_FINISHED 1 | 106 | #define SEQ_READ_FINISHED ((loff_t)-1) |
| 96 | 107 | ||
| 97 | /* | 108 | /* |
| 98 | * Values for parsing cipso rules | 109 | * Values for parsing cipso rules |
| @@ -159,9 +170,13 @@ static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list, | |||
| 159 | 170 | ||
| 160 | mutex_lock(rule_lock); | 171 | mutex_lock(rule_lock); |
| 161 | 172 | ||
| 173 | /* | ||
| 174 | * Because the object label is less likely to match | ||
| 175 | * than the subject label check it first | ||
| 176 | */ | ||
| 162 | list_for_each_entry_rcu(sp, rule_list, list) { | 177 | list_for_each_entry_rcu(sp, rule_list, list) { |
| 163 | if (sp->smk_subject == srp->smk_subject && | 178 | if (sp->smk_object == srp->smk_object && |
| 164 | sp->smk_object == srp->smk_object) { | 179 | sp->smk_subject == srp->smk_subject) { |
| 165 | found = 1; | 180 | found = 1; |
| 166 | sp->smk_access = srp->smk_access; | 181 | sp->smk_access = srp->smk_access; |
| 167 | break; | 182 | break; |
| @@ -176,6 +191,99 @@ static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list, | |||
| 176 | } | 191 | } |
| 177 | 192 | ||
| 178 | /** | 193 | /** |
| 194 | * smk_parse_rule - parse Smack rule from load string | ||
| 195 | * @data: string to be parsed whose size is SMK_LOADLEN | ||
| 196 | * @rule: Smack rule | ||
| 197 | * @import: if non-zero, import labels | ||
| 198 | */ | ||
| 199 | static int smk_parse_rule(const char *data, struct smack_rule *rule, int import) | ||
| 200 | { | ||
| 201 | char smack[SMK_LABELLEN]; | ||
| 202 | struct smack_known *skp; | ||
| 203 | |||
| 204 | if (import) { | ||
| 205 | rule->smk_subject = smk_import(data, 0); | ||
| 206 | if (rule->smk_subject == NULL) | ||
| 207 | return -1; | ||
| 208 | |||
| 209 | rule->smk_object = smk_import(data + SMK_LABELLEN, 0); | ||
| 210 | if (rule->smk_object == NULL) | ||
| 211 | return -1; | ||
| 212 | } else { | ||
| 213 | smk_parse_smack(data, 0, smack); | ||
| 214 | skp = smk_find_entry(smack); | ||
| 215 | if (skp == NULL) | ||
| 216 | return -1; | ||
| 217 | rule->smk_subject = skp->smk_known; | ||
| 218 | |||
| 219 | smk_parse_smack(data + SMK_LABELLEN, 0, smack); | ||
| 220 | skp = smk_find_entry(smack); | ||
| 221 | if (skp == NULL) | ||
| 222 | return -1; | ||
| 223 | rule->smk_object = skp->smk_known; | ||
| 224 | } | ||
| 225 | |||
| 226 | rule->smk_access = 0; | ||
| 227 | |||
| 228 | switch (data[SMK_LABELLEN + SMK_LABELLEN]) { | ||
| 229 | case '-': | ||
| 230 | break; | ||
| 231 | case 'r': | ||
| 232 | case 'R': | ||
| 233 | rule->smk_access |= MAY_READ; | ||
| 234 | break; | ||
| 235 | default: | ||
| 236 | return -1; | ||
| 237 | } | ||
| 238 | |||
| 239 | switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) { | ||
| 240 | case '-': | ||
| 241 | break; | ||
| 242 | case 'w': | ||
| 243 | case 'W': | ||
| 244 | rule->smk_access |= MAY_WRITE; | ||
| 245 | break; | ||
| 246 | default: | ||
| 247 | return -1; | ||
| 248 | } | ||
| 249 | |||
| 250 | switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) { | ||
| 251 | case '-': | ||
| 252 | break; | ||
| 253 | case 'x': | ||
| 254 | case 'X': | ||
| 255 | rule->smk_access |= MAY_EXEC; | ||
| 256 | break; | ||
| 257 | default: | ||
| 258 | return -1; | ||
| 259 | } | ||
| 260 | |||
| 261 | switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) { | ||
| 262 | case '-': | ||
| 263 | break; | ||
| 264 | case 'a': | ||
| 265 | case 'A': | ||
| 266 | rule->smk_access |= MAY_APPEND; | ||
| 267 | break; | ||
| 268 | default: | ||
| 269 | return -1; | ||
| 270 | } | ||
| 271 | |||
| 272 | switch (data[SMK_LABELLEN + SMK_LABELLEN + 4]) { | ||
| 273 | case '-': | ||
| 274 | break; | ||
| 275 | case 't': | ||
| 276 | case 'T': | ||
| 277 | rule->smk_access |= MAY_TRANSMUTE; | ||
| 278 | break; | ||
| 279 | default: | ||
| 280 | return -1; | ||
| 281 | } | ||
| 282 | |||
| 283 | return 0; | ||
| 284 | } | ||
| 285 | |||
| 286 | /** | ||
| 179 | * smk_write_load_list - write() for any /smack/load | 287 | * smk_write_load_list - write() for any /smack/load |
| 180 | * @file: file pointer, not actually used | 288 | * @file: file pointer, not actually used |
| 181 | * @buf: where to get the data from | 289 | * @buf: where to get the data from |
| @@ -197,9 +305,12 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf, | |||
| 197 | struct list_head *rule_list, | 305 | struct list_head *rule_list, |
| 198 | struct mutex *rule_lock) | 306 | struct mutex *rule_lock) |
| 199 | { | 307 | { |
| 308 | struct smack_master_list *smlp; | ||
| 309 | struct smack_known *skp; | ||
| 200 | struct smack_rule *rule; | 310 | struct smack_rule *rule; |
| 201 | char *data; | 311 | char *data; |
| 202 | int rc = -EINVAL; | 312 | int rc = -EINVAL; |
| 313 | int load = 0; | ||
| 203 | 314 | ||
| 204 | /* | 315 | /* |
| 205 | * No partial writes. | 316 | * No partial writes. |
| @@ -234,69 +345,14 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf, | |||
| 234 | goto out; | 345 | goto out; |
| 235 | } | 346 | } |
| 236 | 347 | ||
| 237 | rule->smk_subject = smk_import(data, 0); | 348 | if (smk_parse_rule(data, rule, 1)) |
| 238 | if (rule->smk_subject == NULL) | ||
| 239 | goto out_free_rule; | ||
| 240 | |||
| 241 | rule->smk_object = smk_import(data + SMK_LABELLEN, 0); | ||
| 242 | if (rule->smk_object == NULL) | ||
| 243 | goto out_free_rule; | 349 | goto out_free_rule; |
| 244 | 350 | ||
| 245 | rule->smk_access = 0; | 351 | if (rule_list == NULL) { |
| 246 | 352 | load = 1; | |
| 247 | switch (data[SMK_LABELLEN + SMK_LABELLEN]) { | 353 | skp = smk_find_entry(rule->smk_subject); |
| 248 | case '-': | 354 | rule_list = &skp->smk_rules; |
| 249 | break; | 355 | rule_lock = &skp->smk_rules_lock; |
| 250 | case 'r': | ||
| 251 | case 'R': | ||
| 252 | rule->smk_access |= MAY_READ; | ||
| 253 | break; | ||
| 254 | default: | ||
| 255 | goto out_free_rule; | ||
| 256 | } | ||
| 257 | |||
| 258 | switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) { | ||
| 259 | case '-': | ||
| 260 | break; | ||
| 261 | case 'w': | ||
| 262 | case 'W': | ||
| 263 | rule->smk_access |= MAY_WRITE; | ||
| 264 | break; | ||
| 265 | default: | ||
| 266 | goto out_free_rule; | ||
| 267 | } | ||
| 268 | |||
| 269 | switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) { | ||
| 270 | case '-': | ||
| 271 | break; | ||
| 272 | case 'x': | ||
| 273 | case 'X': | ||
| 274 | rule->smk_access |= MAY_EXEC; | ||
| 275 | break; | ||
| 276 | default: | ||
| 277 | goto out_free_rule; | ||
| 278 | } | ||
| 279 | |||
| 280 | switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) { | ||
| 281 | case '-': | ||
| 282 | break; | ||
| 283 | case 'a': | ||
| 284 | case 'A': | ||
| 285 | rule->smk_access |= MAY_APPEND; | ||
| 286 | break; | ||
| 287 | default: | ||
| 288 | goto out_free_rule; | ||
| 289 | } | ||
| 290 | |||
| 291 | switch (data[SMK_LABELLEN + SMK_LABELLEN + 4]) { | ||
| 292 | case '-': | ||
| 293 | break; | ||
| 294 | case 't': | ||
| 295 | case 'T': | ||
| 296 | rule->smk_access |= MAY_TRANSMUTE; | ||
| 297 | break; | ||
| 298 | default: | ||
| 299 | goto out_free_rule; | ||
| 300 | } | 356 | } |
| 301 | 357 | ||
| 302 | rc = count; | 358 | rc = count; |
| @@ -304,8 +360,15 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf, | |||
| 304 | * smk_set_access returns true if there was already a rule | 360 | * smk_set_access returns true if there was already a rule |
| 305 | * for the subject/object pair, and false if it was new. | 361 | * for the subject/object pair, and false if it was new. |
| 306 | */ | 362 | */ |
| 307 | if (!smk_set_access(rule, rule_list, rule_lock)) | 363 | if (!smk_set_access(rule, rule_list, rule_lock)) { |
| 364 | smlp = kzalloc(sizeof(*smlp), GFP_KERNEL); | ||
| 365 | if (smlp != NULL) { | ||
| 366 | smlp->smk_rule = rule; | ||
| 367 | list_add_rcu(&smlp->list, &smack_rule_list); | ||
| 368 | } else | ||
| 369 | rc = -ENOMEM; | ||
| 308 | goto out; | 370 | goto out; |
| 371 | } | ||
| 309 | 372 | ||
| 310 | out_free_rule: | 373 | out_free_rule: |
| 311 | kfree(rule); | 374 | kfree(rule); |
| @@ -321,11 +384,24 @@ out: | |||
| 321 | 384 | ||
| 322 | static void *load_seq_start(struct seq_file *s, loff_t *pos) | 385 | static void *load_seq_start(struct seq_file *s, loff_t *pos) |
| 323 | { | 386 | { |
| 324 | if (*pos == SEQ_READ_FINISHED) | 387 | struct list_head *list; |
| 388 | |||
| 389 | /* | ||
| 390 | * This is 0 the first time through. | ||
| 391 | */ | ||
| 392 | if (s->index == 0) | ||
| 393 | s->private = &smack_rule_list; | ||
| 394 | |||
| 395 | if (s->private == NULL) | ||
| 325 | return NULL; | 396 | return NULL; |
| 326 | if (list_empty(&smack_rule_list)) | 397 | |
| 398 | list = s->private; | ||
| 399 | if (list_empty(list)) | ||
| 327 | return NULL; | 400 | return NULL; |
| 328 | return smack_rule_list.next; | 401 | |
| 402 | if (s->index == 0) | ||
| 403 | return list->next; | ||
| 404 | return list; | ||
| 329 | } | 405 | } |
| 330 | 406 | ||
| 331 | static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos) | 407 | 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) | |||
| 333 | struct list_head *list = v; | 409 | struct list_head *list = v; |
| 334 | 410 | ||
| 335 | if (list_is_last(list, &smack_rule_list)) { | 411 | if (list_is_last(list, &smack_rule_list)) { |
| 336 | *pos = SEQ_READ_FINISHED; | 412 | s->private = NULL; |
| 337 | return NULL; | 413 | return NULL; |
| 338 | } | 414 | } |
| 415 | s->private = list->next; | ||
| 339 | return list->next; | 416 | return list->next; |
| 340 | } | 417 | } |
| 341 | 418 | ||
| 342 | static int load_seq_show(struct seq_file *s, void *v) | 419 | static int load_seq_show(struct seq_file *s, void *v) |
| 343 | { | 420 | { |
| 344 | struct list_head *list = v; | 421 | struct list_head *list = v; |
| 345 | struct smack_rule *srp = | 422 | struct smack_master_list *smlp = |
| 346 | list_entry(list, struct smack_rule, list); | 423 | list_entry(list, struct smack_master_list, list); |
| 424 | struct smack_rule *srp = smlp->smk_rule; | ||
| 347 | 425 | ||
| 348 | seq_printf(s, "%s %s", (char *)srp->smk_subject, | 426 | seq_printf(s, "%s %s", (char *)srp->smk_subject, |
| 349 | (char *)srp->smk_object); | 427 | (char *)srp->smk_object); |
| @@ -412,8 +490,7 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, | |||
| 412 | if (!capable(CAP_MAC_ADMIN)) | 490 | if (!capable(CAP_MAC_ADMIN)) |
| 413 | return -EPERM; | 491 | return -EPERM; |
| 414 | 492 | ||
| 415 | return smk_write_load_list(file, buf, count, ppos, &smack_rule_list, | 493 | return smk_write_load_list(file, buf, count, ppos, NULL, NULL); |
| 416 | &smack_list_lock); | ||
| 417 | } | 494 | } |
| 418 | 495 | ||
| 419 | static const struct file_operations smk_load_ops = { | 496 | static const struct file_operations smk_load_ops = { |
| @@ -1425,6 +1502,44 @@ static const struct file_operations smk_load_self_ops = { | |||
| 1425 | .write = smk_write_load_self, | 1502 | .write = smk_write_load_self, |
| 1426 | .release = seq_release, | 1503 | .release = seq_release, |
| 1427 | }; | 1504 | }; |
| 1505 | |||
| 1506 | /** | ||
| 1507 | * smk_write_access - handle access check transaction | ||
| 1508 | * @file: file pointer | ||
| 1509 | * @buf: data from user space | ||
| 1510 | * @count: bytes sent | ||
| 1511 | * @ppos: where to start - must be 0 | ||
| 1512 | */ | ||
| 1513 | static ssize_t smk_write_access(struct file *file, const char __user *buf, | ||
| 1514 | size_t count, loff_t *ppos) | ||
| 1515 | { | ||
| 1516 | struct smack_rule rule; | ||
| 1517 | char *data; | ||
| 1518 | int res; | ||
| 1519 | |||
| 1520 | data = simple_transaction_get(file, buf, count); | ||
| 1521 | if (IS_ERR(data)) | ||
| 1522 | return PTR_ERR(data); | ||
| 1523 | |||
| 1524 | if (count < SMK_LOADLEN || smk_parse_rule(data, &rule, 0)) | ||
| 1525 | return -EINVAL; | ||
| 1526 | |||
| 1527 | res = smk_access(rule.smk_subject, rule.smk_object, rule.smk_access, | ||
| 1528 | NULL); | ||
| 1529 | data[0] = res == 0 ? '1' : '0'; | ||
| 1530 | data[1] = '\0'; | ||
| 1531 | |||
| 1532 | simple_transaction_set(file, 2); | ||
| 1533 | return SMK_LOADLEN; | ||
| 1534 | } | ||
| 1535 | |||
| 1536 | static const struct file_operations smk_access_ops = { | ||
| 1537 | .write = smk_write_access, | ||
| 1538 | .read = simple_transaction_read, | ||
| 1539 | .release = simple_transaction_release, | ||
| 1540 | .llseek = generic_file_llseek, | ||
| 1541 | }; | ||
| 1542 | |||
| 1428 | /** | 1543 | /** |
| 1429 | * smk_fill_super - fill the /smackfs superblock | 1544 | * smk_fill_super - fill the /smackfs superblock |
| 1430 | * @sb: the empty superblock | 1545 | * @sb: the empty superblock |
| @@ -1459,6 +1574,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent) | |||
| 1459 | "logging", &smk_logging_ops, S_IRUGO|S_IWUSR}, | 1574 | "logging", &smk_logging_ops, S_IRUGO|S_IWUSR}, |
| 1460 | [SMK_LOAD_SELF] = { | 1575 | [SMK_LOAD_SELF] = { |
| 1461 | "load-self", &smk_load_self_ops, S_IRUGO|S_IWUGO}, | 1576 | "load-self", &smk_load_self_ops, S_IRUGO|S_IWUGO}, |
| 1577 | [SMK_ACCESSES] = { | ||
| 1578 | "access", &smk_access_ops, S_IRUGO|S_IWUGO}, | ||
| 1462 | /* last one */ | 1579 | /* last one */ |
| 1463 | {""} | 1580 | {""} |
| 1464 | }; | 1581 | }; |
| @@ -1534,6 +1651,20 @@ static int __init init_smk_fs(void) | |||
| 1534 | smk_cipso_doi(); | 1651 | smk_cipso_doi(); |
| 1535 | smk_unlbl_ambient(NULL); | 1652 | smk_unlbl_ambient(NULL); |
| 1536 | 1653 | ||
| 1654 | mutex_init(&smack_known_floor.smk_rules_lock); | ||
| 1655 | mutex_init(&smack_known_hat.smk_rules_lock); | ||
| 1656 | mutex_init(&smack_known_huh.smk_rules_lock); | ||
| 1657 | mutex_init(&smack_known_invalid.smk_rules_lock); | ||
| 1658 | mutex_init(&smack_known_star.smk_rules_lock); | ||
| 1659 | mutex_init(&smack_known_web.smk_rules_lock); | ||
| 1660 | |||
| 1661 | INIT_LIST_HEAD(&smack_known_floor.smk_rules); | ||
| 1662 | INIT_LIST_HEAD(&smack_known_hat.smk_rules); | ||
| 1663 | INIT_LIST_HEAD(&smack_known_huh.smk_rules); | ||
| 1664 | INIT_LIST_HEAD(&smack_known_invalid.smk_rules); | ||
| 1665 | INIT_LIST_HEAD(&smack_known_star.smk_rules); | ||
| 1666 | INIT_LIST_HEAD(&smack_known_web.smk_rules); | ||
| 1667 | |||
| 1537 | return err; | 1668 | return err; |
| 1538 | } | 1669 | } |
| 1539 | 1670 | ||
diff --git a/security/tomoyo/Kconfig b/security/tomoyo/Kconfig index 7c7f8c16c10f..8eb779b9d77f 100644 --- a/security/tomoyo/Kconfig +++ b/security/tomoyo/Kconfig | |||
| @@ -1,8 +1,10 @@ | |||
| 1 | config SECURITY_TOMOYO | 1 | config SECURITY_TOMOYO |
| 2 | bool "TOMOYO Linux Support" | 2 | bool "TOMOYO Linux Support" |
| 3 | depends on SECURITY | 3 | depends on SECURITY |
| 4 | depends on NET | ||
| 4 | select SECURITYFS | 5 | select SECURITYFS |
| 5 | select SECURITY_PATH | 6 | select SECURITY_PATH |
| 7 | select SECURITY_NETWORK | ||
| 6 | default n | 8 | default n |
| 7 | help | 9 | help |
| 8 | This selects TOMOYO Linux, pathname-based access control. | 10 | This selects TOMOYO Linux, pathname-based access control. |
diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile index 95278b71fc21..56a0c7be409e 100644 --- a/security/tomoyo/Makefile +++ b/security/tomoyo/Makefile | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | 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 | 1 | 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 |
| 2 | 2 | ||
| 3 | $(obj)/policy/profile.conf: | 3 | $(obj)/policy/profile.conf: |
| 4 | @mkdir -p $(obj)/policy/ | 4 | @mkdir -p $(obj)/policy/ |
| @@ -27,7 +27,7 @@ $(obj)/policy/stat.conf: | |||
| 27 | @touch $@ | 27 | @touch $@ |
| 28 | 28 | ||
| 29 | $(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 | 29 | $(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 |
| 30 | @echo Generating built-in policy for TOMOYO 2.4.x. | 30 | @echo Generating built-in policy for TOMOYO 2.5.x. |
| 31 | @echo "static char tomoyo_builtin_profile[] __initdata =" > $@.tmp | 31 | @echo "static char tomoyo_builtin_profile[] __initdata =" > $@.tmp |
| 32 | @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/profile.conf >> $@.tmp | 32 | @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/profile.conf >> $@.tmp |
| 33 | @echo "\"\";" >> $@.tmp | 33 | @echo "\"\";" >> $@.tmp |
diff --git a/security/tomoyo/audit.c b/security/tomoyo/audit.c index 5dbb1f7617c0..075c3a6d1649 100644 --- a/security/tomoyo/audit.c +++ b/security/tomoyo/audit.c | |||
| @@ -313,6 +313,7 @@ static unsigned int tomoyo_log_count; | |||
| 313 | */ | 313 | */ |
| 314 | static bool tomoyo_get_audit(const struct tomoyo_policy_namespace *ns, | 314 | static bool tomoyo_get_audit(const struct tomoyo_policy_namespace *ns, |
| 315 | const u8 profile, const u8 index, | 315 | const u8 profile, const u8 index, |
| 316 | const struct tomoyo_acl_info *matched_acl, | ||
| 316 | const bool is_granted) | 317 | const bool is_granted) |
| 317 | { | 318 | { |
| 318 | u8 mode; | 319 | u8 mode; |
| @@ -324,6 +325,9 @@ static bool tomoyo_get_audit(const struct tomoyo_policy_namespace *ns, | |||
| 324 | p = tomoyo_profile(ns, profile); | 325 | p = tomoyo_profile(ns, profile); |
| 325 | if (tomoyo_log_count >= p->pref[TOMOYO_PREF_MAX_AUDIT_LOG]) | 326 | if (tomoyo_log_count >= p->pref[TOMOYO_PREF_MAX_AUDIT_LOG]) |
| 326 | return false; | 327 | return false; |
| 328 | if (is_granted && matched_acl && matched_acl->cond && | ||
| 329 | matched_acl->cond->grant_log != TOMOYO_GRANTLOG_AUTO) | ||
| 330 | return matched_acl->cond->grant_log == TOMOYO_GRANTLOG_YES; | ||
| 327 | mode = p->config[index]; | 331 | mode = p->config[index]; |
| 328 | if (mode == TOMOYO_CONFIG_USE_DEFAULT) | 332 | if (mode == TOMOYO_CONFIG_USE_DEFAULT) |
| 329 | mode = p->config[category]; | 333 | mode = p->config[category]; |
| @@ -350,7 +354,8 @@ void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt, | |||
| 350 | char *buf; | 354 | char *buf; |
| 351 | struct tomoyo_log *entry; | 355 | struct tomoyo_log *entry; |
| 352 | bool quota_exceeded = false; | 356 | bool quota_exceeded = false; |
| 353 | if (!tomoyo_get_audit(r->domain->ns, r->profile, r->type, r->granted)) | 357 | if (!tomoyo_get_audit(r->domain->ns, r->profile, r->type, |
| 358 | r->matched_acl, r->granted)) | ||
| 354 | goto out; | 359 | goto out; |
| 355 | buf = tomoyo_init_log(r, len, fmt, args); | 360 | buf = tomoyo_init_log(r, len, fmt, args); |
| 356 | if (!buf) | 361 | if (!buf) |
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 2e43aec1c36b..150911c7ff08 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c | |||
| @@ -20,6 +20,7 @@ const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE] = { | |||
| 20 | /* String table for /sys/kernel/security/tomoyo/profile */ | 20 | /* String table for /sys/kernel/security/tomoyo/profile */ |
| 21 | const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX | 21 | const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX |
| 22 | + TOMOYO_MAX_MAC_CATEGORY_INDEX] = { | 22 | + TOMOYO_MAX_MAC_CATEGORY_INDEX] = { |
| 23 | /* CONFIG::file group */ | ||
| 23 | [TOMOYO_MAC_FILE_EXECUTE] = "execute", | 24 | [TOMOYO_MAC_FILE_EXECUTE] = "execute", |
| 24 | [TOMOYO_MAC_FILE_OPEN] = "open", | 25 | [TOMOYO_MAC_FILE_OPEN] = "open", |
| 25 | [TOMOYO_MAC_FILE_CREATE] = "create", | 26 | [TOMOYO_MAC_FILE_CREATE] = "create", |
| @@ -43,7 +44,28 @@ const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX | |||
| 43 | [TOMOYO_MAC_FILE_MOUNT] = "mount", | 44 | [TOMOYO_MAC_FILE_MOUNT] = "mount", |
| 44 | [TOMOYO_MAC_FILE_UMOUNT] = "unmount", | 45 | [TOMOYO_MAC_FILE_UMOUNT] = "unmount", |
| 45 | [TOMOYO_MAC_FILE_PIVOT_ROOT] = "pivot_root", | 46 | [TOMOYO_MAC_FILE_PIVOT_ROOT] = "pivot_root", |
| 47 | /* CONFIG::network group */ | ||
| 48 | [TOMOYO_MAC_NETWORK_INET_STREAM_BIND] = "inet_stream_bind", | ||
| 49 | [TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN] = "inet_stream_listen", | ||
| 50 | [TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT] = "inet_stream_connect", | ||
| 51 | [TOMOYO_MAC_NETWORK_INET_DGRAM_BIND] = "inet_dgram_bind", | ||
| 52 | [TOMOYO_MAC_NETWORK_INET_DGRAM_SEND] = "inet_dgram_send", | ||
| 53 | [TOMOYO_MAC_NETWORK_INET_RAW_BIND] = "inet_raw_bind", | ||
| 54 | [TOMOYO_MAC_NETWORK_INET_RAW_SEND] = "inet_raw_send", | ||
| 55 | [TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND] = "unix_stream_bind", | ||
| 56 | [TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN] = "unix_stream_listen", | ||
| 57 | [TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT] = "unix_stream_connect", | ||
| 58 | [TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND] = "unix_dgram_bind", | ||
| 59 | [TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND] = "unix_dgram_send", | ||
| 60 | [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND] = "unix_seqpacket_bind", | ||
| 61 | [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN] = "unix_seqpacket_listen", | ||
| 62 | [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] = "unix_seqpacket_connect", | ||
| 63 | /* CONFIG::misc group */ | ||
| 64 | [TOMOYO_MAC_ENVIRON] = "env", | ||
| 65 | /* CONFIG group */ | ||
| 46 | [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file", | 66 | [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file", |
| 67 | [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_NETWORK] = "network", | ||
| 68 | [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_MISC] = "misc", | ||
| 47 | }; | 69 | }; |
| 48 | 70 | ||
| 49 | /* String table for conditions. */ | 71 | /* String table for conditions. */ |
| @@ -130,10 +152,20 @@ const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = { | |||
| 130 | [TOMOYO_TYPE_UMOUNT] = "unmount", | 152 | [TOMOYO_TYPE_UMOUNT] = "unmount", |
| 131 | }; | 153 | }; |
| 132 | 154 | ||
| 155 | /* String table for socket's operation. */ | ||
| 156 | const char * const tomoyo_socket_keyword[TOMOYO_MAX_NETWORK_OPERATION] = { | ||
| 157 | [TOMOYO_NETWORK_BIND] = "bind", | ||
| 158 | [TOMOYO_NETWORK_LISTEN] = "listen", | ||
| 159 | [TOMOYO_NETWORK_CONNECT] = "connect", | ||
| 160 | [TOMOYO_NETWORK_SEND] = "send", | ||
| 161 | }; | ||
| 162 | |||
| 133 | /* String table for categories. */ | 163 | /* String table for categories. */ |
| 134 | static const char * const tomoyo_category_keywords | 164 | static const char * const tomoyo_category_keywords |
| 135 | [TOMOYO_MAX_MAC_CATEGORY_INDEX] = { | 165 | [TOMOYO_MAX_MAC_CATEGORY_INDEX] = { |
| 136 | [TOMOYO_MAC_CATEGORY_FILE] = "file", | 166 | [TOMOYO_MAC_CATEGORY_FILE] = "file", |
| 167 | [TOMOYO_MAC_CATEGORY_NETWORK] = "network", | ||
| 168 | [TOMOYO_MAC_CATEGORY_MISC] = "misc", | ||
| 137 | }; | 169 | }; |
| 138 | 170 | ||
| 139 | /* Permit policy management by non-root user? */ | 171 | /* Permit policy management by non-root user? */ |
| @@ -230,13 +262,17 @@ static void tomoyo_set_string(struct tomoyo_io_buffer *head, const char *string) | |||
| 230 | WARN_ON(1); | 262 | WARN_ON(1); |
| 231 | } | 263 | } |
| 232 | 264 | ||
| 265 | static void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, | ||
| 266 | ...) __printf(2, 3); | ||
| 267 | |||
| 233 | /** | 268 | /** |
| 234 | * tomoyo_io_printf - printf() to "struct tomoyo_io_buffer" structure. | 269 | * tomoyo_io_printf - printf() to "struct tomoyo_io_buffer" structure. |
| 235 | * | 270 | * |
| 236 | * @head: Pointer to "struct tomoyo_io_buffer". | 271 | * @head: Pointer to "struct tomoyo_io_buffer". |
| 237 | * @fmt: The printf()'s format string, followed by parameters. | 272 | * @fmt: The printf()'s format string, followed by parameters. |
| 238 | */ | 273 | */ |
| 239 | void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) | 274 | static void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, |
| 275 | ...) | ||
| 240 | { | 276 | { |
| 241 | va_list args; | 277 | va_list args; |
| 242 | size_t len; | 278 | size_t len; |
| @@ -313,7 +349,7 @@ void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns) | |||
| 313 | INIT_LIST_HEAD(&ns->group_list[idx]); | 349 | INIT_LIST_HEAD(&ns->group_list[idx]); |
| 314 | for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++) | 350 | for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++) |
| 315 | INIT_LIST_HEAD(&ns->policy_list[idx]); | 351 | INIT_LIST_HEAD(&ns->policy_list[idx]); |
| 316 | ns->profile_version = 20100903; | 352 | ns->profile_version = 20110903; |
| 317 | tomoyo_namespace_enabled = !list_empty(&tomoyo_namespace_list); | 353 | tomoyo_namespace_enabled = !list_empty(&tomoyo_namespace_list); |
| 318 | list_add_tail_rcu(&ns->namespace_list, &tomoyo_namespace_list); | 354 | list_add_tail_rcu(&ns->namespace_list, &tomoyo_namespace_list); |
| 319 | } | 355 | } |
| @@ -466,8 +502,10 @@ static struct tomoyo_profile *tomoyo_assign_profile | |||
| 466 | TOMOYO_CONFIG_WANT_REJECT_LOG; | 502 | TOMOYO_CONFIG_WANT_REJECT_LOG; |
| 467 | memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT, | 503 | memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT, |
| 468 | sizeof(ptr->config)); | 504 | sizeof(ptr->config)); |
| 469 | ptr->pref[TOMOYO_PREF_MAX_AUDIT_LOG] = 1024; | 505 | ptr->pref[TOMOYO_PREF_MAX_AUDIT_LOG] = |
| 470 | ptr->pref[TOMOYO_PREF_MAX_LEARNING_ENTRY] = 2048; | 506 | CONFIG_SECURITY_TOMOYO_MAX_AUDIT_LOG; |
| 507 | ptr->pref[TOMOYO_PREF_MAX_LEARNING_ENTRY] = | ||
| 508 | CONFIG_SECURITY_TOMOYO_MAX_ACCEPT_ENTRY; | ||
| 471 | mb(); /* Avoid out-of-order execution. */ | 509 | mb(); /* Avoid out-of-order execution. */ |
| 472 | ns->profile_ptr[profile] = ptr; | 510 | ns->profile_ptr[profile] = ptr; |
| 473 | entry = NULL; | 511 | entry = NULL; |
| @@ -951,14 +989,12 @@ static bool tomoyo_select_domain(struct tomoyo_io_buffer *head, | |||
| 951 | (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) { | 989 | (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) { |
| 952 | struct task_struct *p; | 990 | struct task_struct *p; |
| 953 | rcu_read_lock(); | 991 | rcu_read_lock(); |
| 954 | read_lock(&tasklist_lock); | ||
| 955 | if (global_pid) | 992 | if (global_pid) |
| 956 | p = find_task_by_pid_ns(pid, &init_pid_ns); | 993 | p = find_task_by_pid_ns(pid, &init_pid_ns); |
| 957 | else | 994 | else |
| 958 | p = find_task_by_vpid(pid); | 995 | p = find_task_by_vpid(pid); |
| 959 | if (p) | 996 | if (p) |
| 960 | domain = tomoyo_real_domain(p); | 997 | domain = tomoyo_real_domain(p); |
| 961 | read_unlock(&tasklist_lock); | ||
| 962 | rcu_read_unlock(); | 998 | rcu_read_unlock(); |
| 963 | } else if (!strncmp(data, "domain=", 7)) { | 999 | } else if (!strncmp(data, "domain=", 7)) { |
| 964 | if (tomoyo_domain_def(data + 7)) | 1000 | if (tomoyo_domain_def(data + 7)) |
| @@ -982,6 +1018,48 @@ static bool tomoyo_select_domain(struct tomoyo_io_buffer *head, | |||
| 982 | } | 1018 | } |
| 983 | 1019 | ||
| 984 | /** | 1020 | /** |
| 1021 | * tomoyo_same_task_acl - Check for duplicated "struct tomoyo_task_acl" entry. | ||
| 1022 | * | ||
| 1023 | * @a: Pointer to "struct tomoyo_acl_info". | ||
| 1024 | * @b: Pointer to "struct tomoyo_acl_info". | ||
| 1025 | * | ||
| 1026 | * Returns true if @a == @b, false otherwise. | ||
| 1027 | */ | ||
| 1028 | static bool tomoyo_same_task_acl(const struct tomoyo_acl_info *a, | ||
| 1029 | const struct tomoyo_acl_info *b) | ||
| 1030 | { | ||
| 1031 | const struct tomoyo_task_acl *p1 = container_of(a, typeof(*p1), head); | ||
| 1032 | const struct tomoyo_task_acl *p2 = container_of(b, typeof(*p2), head); | ||
| 1033 | return p1->domainname == p2->domainname; | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | /** | ||
| 1037 | * tomoyo_write_task - Update task related list. | ||
| 1038 | * | ||
| 1039 | * @param: Pointer to "struct tomoyo_acl_param". | ||
| 1040 | * | ||
| 1041 | * Returns 0 on success, negative value otherwise. | ||
| 1042 | * | ||
| 1043 | * Caller holds tomoyo_read_lock(). | ||
| 1044 | */ | ||
| 1045 | static int tomoyo_write_task(struct tomoyo_acl_param *param) | ||
| 1046 | { | ||
| 1047 | int error = -EINVAL; | ||
| 1048 | if (tomoyo_str_starts(¶m->data, "manual_domain_transition ")) { | ||
| 1049 | struct tomoyo_task_acl e = { | ||
| 1050 | .head.type = TOMOYO_TYPE_MANUAL_TASK_ACL, | ||
| 1051 | .domainname = tomoyo_get_domainname(param), | ||
| 1052 | }; | ||
| 1053 | if (e.domainname) | ||
| 1054 | error = tomoyo_update_domain(&e.head, sizeof(e), param, | ||
| 1055 | tomoyo_same_task_acl, | ||
| 1056 | NULL); | ||
| 1057 | tomoyo_put_name(e.domainname); | ||
| 1058 | } | ||
| 1059 | return error; | ||
| 1060 | } | ||
| 1061 | |||
| 1062 | /** | ||
| 985 | * tomoyo_delete_domain - Delete a domain. | 1063 | * tomoyo_delete_domain - Delete a domain. |
| 986 | * | 1064 | * |
| 987 | * @domainname: The name of domain. | 1065 | * @domainname: The name of domain. |
| @@ -1039,11 +1117,16 @@ static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns, | |||
| 1039 | static const struct { | 1117 | static const struct { |
| 1040 | const char *keyword; | 1118 | const char *keyword; |
| 1041 | int (*write) (struct tomoyo_acl_param *); | 1119 | int (*write) (struct tomoyo_acl_param *); |
| 1042 | } tomoyo_callback[1] = { | 1120 | } tomoyo_callback[5] = { |
| 1043 | { "file ", tomoyo_write_file }, | 1121 | { "file ", tomoyo_write_file }, |
| 1122 | { "network inet ", tomoyo_write_inet_network }, | ||
| 1123 | { "network unix ", tomoyo_write_unix_network }, | ||
| 1124 | { "misc ", tomoyo_write_misc }, | ||
| 1125 | { "task ", tomoyo_write_task }, | ||
| 1044 | }; | 1126 | }; |
| 1045 | u8 i; | 1127 | u8 i; |
| 1046 | for (i = 0; i < 1; i++) { | 1128 | |
| 1129 | for (i = 0; i < ARRAY_SIZE(tomoyo_callback); i++) { | ||
| 1047 | if (!tomoyo_str_starts(¶m.data, | 1130 | if (!tomoyo_str_starts(¶m.data, |
| 1048 | tomoyo_callback[i].keyword)) | 1131 | tomoyo_callback[i].keyword)) |
| 1049 | continue; | 1132 | continue; |
| @@ -1127,6 +1210,10 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, | |||
| 1127 | case 0: | 1210 | case 0: |
| 1128 | head->r.cond_index = 0; | 1211 | head->r.cond_index = 0; |
| 1129 | head->r.cond_step++; | 1212 | head->r.cond_step++; |
| 1213 | if (cond->transit) { | ||
| 1214 | tomoyo_set_space(head); | ||
| 1215 | tomoyo_set_string(head, cond->transit->name); | ||
| 1216 | } | ||
| 1130 | /* fall through */ | 1217 | /* fall through */ |
| 1131 | case 1: | 1218 | case 1: |
| 1132 | { | 1219 | { |
| @@ -1239,6 +1326,10 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, | |||
| 1239 | head->r.cond_step++; | 1326 | head->r.cond_step++; |
| 1240 | /* fall through */ | 1327 | /* fall through */ |
| 1241 | case 3: | 1328 | case 3: |
| 1329 | if (cond->grant_log != TOMOYO_GRANTLOG_AUTO) | ||
| 1330 | tomoyo_io_printf(head, " grant_log=%s", | ||
| 1331 | tomoyo_yesno(cond->grant_log == | ||
| 1332 | TOMOYO_GRANTLOG_YES)); | ||
| 1242 | tomoyo_set_lf(head); | 1333 | tomoyo_set_lf(head); |
| 1243 | return true; | 1334 | return true; |
| 1244 | } | 1335 | } |
| @@ -1306,6 +1397,12 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, | |||
| 1306 | if (first) | 1397 | if (first) |
| 1307 | return true; | 1398 | return true; |
| 1308 | tomoyo_print_name_union(head, &ptr->name); | 1399 | tomoyo_print_name_union(head, &ptr->name); |
| 1400 | } else if (acl_type == TOMOYO_TYPE_MANUAL_TASK_ACL) { | ||
| 1401 | struct tomoyo_task_acl *ptr = | ||
| 1402 | container_of(acl, typeof(*ptr), head); | ||
| 1403 | tomoyo_set_group(head, "task "); | ||
| 1404 | tomoyo_set_string(head, "manual_domain_transition "); | ||
| 1405 | tomoyo_set_string(head, ptr->domainname->name); | ||
| 1309 | } else if (head->r.print_transition_related_only) { | 1406 | } else if (head->r.print_transition_related_only) { |
| 1310 | return true; | 1407 | return true; |
| 1311 | } else if (acl_type == TOMOYO_TYPE_PATH2_ACL) { | 1408 | } else if (acl_type == TOMOYO_TYPE_PATH2_ACL) { |
| @@ -1370,6 +1467,60 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, | |||
| 1370 | tomoyo_print_number_union(head, &ptr->mode); | 1467 | tomoyo_print_number_union(head, &ptr->mode); |
| 1371 | tomoyo_print_number_union(head, &ptr->major); | 1468 | tomoyo_print_number_union(head, &ptr->major); |
| 1372 | tomoyo_print_number_union(head, &ptr->minor); | 1469 | tomoyo_print_number_union(head, &ptr->minor); |
| 1470 | } else if (acl_type == TOMOYO_TYPE_INET_ACL) { | ||
| 1471 | struct tomoyo_inet_acl *ptr = | ||
| 1472 | container_of(acl, typeof(*ptr), head); | ||
| 1473 | const u8 perm = ptr->perm; | ||
| 1474 | |||
| 1475 | for (bit = 0; bit < TOMOYO_MAX_NETWORK_OPERATION; bit++) { | ||
| 1476 | if (!(perm & (1 << bit))) | ||
| 1477 | continue; | ||
| 1478 | if (first) { | ||
| 1479 | tomoyo_set_group(head, "network inet "); | ||
| 1480 | tomoyo_set_string(head, tomoyo_proto_keyword | ||
| 1481 | [ptr->protocol]); | ||
| 1482 | tomoyo_set_space(head); | ||
| 1483 | first = false; | ||
| 1484 | } else { | ||
| 1485 | tomoyo_set_slash(head); | ||
| 1486 | } | ||
| 1487 | tomoyo_set_string(head, tomoyo_socket_keyword[bit]); | ||
| 1488 | } | ||
| 1489 | if (first) | ||
| 1490 | return true; | ||
| 1491 | tomoyo_set_space(head); | ||
| 1492 | if (ptr->address.group) { | ||
| 1493 | tomoyo_set_string(head, "@"); | ||
| 1494 | tomoyo_set_string(head, ptr->address.group->group_name | ||
| 1495 | ->name); | ||
| 1496 | } else { | ||
| 1497 | char buf[128]; | ||
| 1498 | tomoyo_print_ip(buf, sizeof(buf), &ptr->address); | ||
| 1499 | tomoyo_io_printf(head, "%s", buf); | ||
| 1500 | } | ||
| 1501 | tomoyo_print_number_union(head, &ptr->port); | ||
| 1502 | } else if (acl_type == TOMOYO_TYPE_UNIX_ACL) { | ||
| 1503 | struct tomoyo_unix_acl *ptr = | ||
| 1504 | container_of(acl, typeof(*ptr), head); | ||
| 1505 | const u8 perm = ptr->perm; | ||
| 1506 | |||
| 1507 | for (bit = 0; bit < TOMOYO_MAX_NETWORK_OPERATION; bit++) { | ||
| 1508 | if (!(perm & (1 << bit))) | ||
| 1509 | continue; | ||
| 1510 | if (first) { | ||
| 1511 | tomoyo_set_group(head, "network unix "); | ||
| 1512 | tomoyo_set_string(head, tomoyo_proto_keyword | ||
| 1513 | [ptr->protocol]); | ||
| 1514 | tomoyo_set_space(head); | ||
| 1515 | first = false; | ||
| 1516 | } else { | ||
| 1517 | tomoyo_set_slash(head); | ||
| 1518 | } | ||
| 1519 | tomoyo_set_string(head, tomoyo_socket_keyword[bit]); | ||
| 1520 | } | ||
| 1521 | if (first) | ||
| 1522 | return true; | ||
| 1523 | tomoyo_print_name_union(head, &ptr->name); | ||
| 1373 | } else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) { | 1524 | } else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) { |
| 1374 | struct tomoyo_mount_acl *ptr = | 1525 | struct tomoyo_mount_acl *ptr = |
| 1375 | container_of(acl, typeof(*ptr), head); | 1526 | container_of(acl, typeof(*ptr), head); |
| @@ -1378,6 +1529,12 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, | |||
| 1378 | tomoyo_print_name_union(head, &ptr->dir_name); | 1529 | tomoyo_print_name_union(head, &ptr->dir_name); |
| 1379 | tomoyo_print_name_union(head, &ptr->fs_type); | 1530 | tomoyo_print_name_union(head, &ptr->fs_type); |
| 1380 | tomoyo_print_number_union(head, &ptr->flags); | 1531 | tomoyo_print_number_union(head, &ptr->flags); |
| 1532 | } else if (acl_type == TOMOYO_TYPE_ENV_ACL) { | ||
| 1533 | struct tomoyo_env_acl *ptr = | ||
| 1534 | container_of(acl, typeof(*ptr), head); | ||
| 1535 | |||
| 1536 | tomoyo_set_group(head, "misc env "); | ||
| 1537 | tomoyo_set_string(head, ptr->env->name); | ||
| 1381 | } | 1538 | } |
| 1382 | if (acl->cond) { | 1539 | if (acl->cond) { |
| 1383 | head->r.print_cond_part = true; | 1540 | head->r.print_cond_part = true; |
| @@ -1510,14 +1667,12 @@ static void tomoyo_read_pid(struct tomoyo_io_buffer *head) | |||
| 1510 | global_pid = true; | 1667 | global_pid = true; |
| 1511 | pid = (unsigned int) simple_strtoul(buf, NULL, 10); | 1668 | pid = (unsigned int) simple_strtoul(buf, NULL, 10); |
| 1512 | rcu_read_lock(); | 1669 | rcu_read_lock(); |
| 1513 | read_lock(&tasklist_lock); | ||
| 1514 | if (global_pid) | 1670 | if (global_pid) |
| 1515 | p = find_task_by_pid_ns(pid, &init_pid_ns); | 1671 | p = find_task_by_pid_ns(pid, &init_pid_ns); |
| 1516 | else | 1672 | else |
| 1517 | p = find_task_by_vpid(pid); | 1673 | p = find_task_by_vpid(pid); |
| 1518 | if (p) | 1674 | if (p) |
| 1519 | domain = tomoyo_real_domain(p); | 1675 | domain = tomoyo_real_domain(p); |
| 1520 | read_unlock(&tasklist_lock); | ||
| 1521 | rcu_read_unlock(); | 1676 | rcu_read_unlock(); |
| 1522 | if (!domain) | 1677 | if (!domain) |
| 1523 | return; | 1678 | return; |
| @@ -1537,8 +1692,9 @@ static const char *tomoyo_transition_type[TOMOYO_MAX_TRANSITION_TYPE] = { | |||
| 1537 | 1692 | ||
| 1538 | /* String table for grouping keywords. */ | 1693 | /* String table for grouping keywords. */ |
| 1539 | static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = { | 1694 | static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = { |
| 1540 | [TOMOYO_PATH_GROUP] = "path_group ", | 1695 | [TOMOYO_PATH_GROUP] = "path_group ", |
| 1541 | [TOMOYO_NUMBER_GROUP] = "number_group ", | 1696 | [TOMOYO_NUMBER_GROUP] = "number_group ", |
| 1697 | [TOMOYO_ADDRESS_GROUP] = "address_group ", | ||
| 1542 | }; | 1698 | }; |
| 1543 | 1699 | ||
| 1544 | /** | 1700 | /** |
| @@ -1580,7 +1736,7 @@ static int tomoyo_write_exception(struct tomoyo_io_buffer *head) | |||
| 1580 | } | 1736 | } |
| 1581 | 1737 | ||
| 1582 | /** | 1738 | /** |
| 1583 | * tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_group" list. | 1739 | * tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_group"/"struct tomoyo_address_group" list. |
| 1584 | * | 1740 | * |
| 1585 | * @head: Pointer to "struct tomoyo_io_buffer". | 1741 | * @head: Pointer to "struct tomoyo_io_buffer". |
| 1586 | * @idx: Index number. | 1742 | * @idx: Index number. |
| @@ -1617,6 +1773,15 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx) | |||
| 1617 | (ptr, | 1773 | (ptr, |
| 1618 | struct tomoyo_number_group, | 1774 | struct tomoyo_number_group, |
| 1619 | head)->number); | 1775 | head)->number); |
| 1776 | } else if (idx == TOMOYO_ADDRESS_GROUP) { | ||
| 1777 | char buffer[128]; | ||
| 1778 | |||
| 1779 | struct tomoyo_address_group *member = | ||
| 1780 | container_of(ptr, typeof(*member), | ||
| 1781 | head); | ||
| 1782 | tomoyo_print_ip(buffer, sizeof(buffer), | ||
| 1783 | &member->address); | ||
| 1784 | tomoyo_io_printf(head, " %s", buffer); | ||
| 1620 | } | 1785 | } |
| 1621 | tomoyo_set_lf(head); | 1786 | tomoyo_set_lf(head); |
| 1622 | } | 1787 | } |
| @@ -2066,27 +2231,7 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head) | |||
| 2066 | static void tomoyo_read_version(struct tomoyo_io_buffer *head) | 2231 | static void tomoyo_read_version(struct tomoyo_io_buffer *head) |
| 2067 | { | 2232 | { |
| 2068 | if (!head->r.eof) { | 2233 | if (!head->r.eof) { |
| 2069 | tomoyo_io_printf(head, "2.4.0"); | 2234 | tomoyo_io_printf(head, "2.5.0"); |
| 2070 | head->r.eof = true; | ||
| 2071 | } | ||
| 2072 | } | ||
| 2073 | |||
| 2074 | /** | ||
| 2075 | * tomoyo_read_self_domain - Get the current process's domainname. | ||
| 2076 | * | ||
| 2077 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
| 2078 | * | ||
| 2079 | * Returns the current process's domainname. | ||
| 2080 | */ | ||
| 2081 | static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head) | ||
| 2082 | { | ||
| 2083 | if (!head->r.eof) { | ||
| 2084 | /* | ||
| 2085 | * tomoyo_domain()->domainname != NULL | ||
| 2086 | * because every process belongs to a domain and | ||
| 2087 | * the domain's name cannot be NULL. | ||
| 2088 | */ | ||
| 2089 | tomoyo_io_printf(head, "%s", tomoyo_domain()->domainname->name); | ||
| 2090 | head->r.eof = true; | 2235 | head->r.eof = true; |
| 2091 | } | 2236 | } |
| 2092 | } | 2237 | } |
| @@ -2221,10 +2366,6 @@ int tomoyo_open_control(const u8 type, struct file *file) | |||
| 2221 | head->poll = tomoyo_poll_log; | 2366 | head->poll = tomoyo_poll_log; |
| 2222 | head->read = tomoyo_read_log; | 2367 | head->read = tomoyo_read_log; |
| 2223 | break; | 2368 | break; |
| 2224 | case TOMOYO_SELFDOMAIN: | ||
| 2225 | /* /sys/kernel/security/tomoyo/self_domain */ | ||
| 2226 | head->read = tomoyo_read_self_domain; | ||
| 2227 | break; | ||
| 2228 | case TOMOYO_PROCESS_STATUS: | 2369 | case TOMOYO_PROCESS_STATUS: |
| 2229 | /* /sys/kernel/security/tomoyo/.process_status */ | 2370 | /* /sys/kernel/security/tomoyo/.process_status */ |
| 2230 | head->write = tomoyo_write_pid; | 2371 | head->write = tomoyo_write_pid; |
| @@ -2453,6 +2594,7 @@ ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head, | |||
| 2453 | return -EFAULT; | 2594 | return -EFAULT; |
| 2454 | if (mutex_lock_interruptible(&head->io_sem)) | 2595 | if (mutex_lock_interruptible(&head->io_sem)) |
| 2455 | return -EINTR; | 2596 | return -EINTR; |
| 2597 | head->read_user_buf_avail = 0; | ||
| 2456 | idx = tomoyo_read_lock(); | 2598 | idx = tomoyo_read_lock(); |
| 2457 | /* Read a line and dispatch it to the policy handler. */ | 2599 | /* Read a line and dispatch it to the policy handler. */ |
| 2458 | while (avail_len > 0) { | 2600 | while (avail_len > 0) { |
| @@ -2562,11 +2704,11 @@ void tomoyo_check_profile(void) | |||
| 2562 | struct tomoyo_domain_info *domain; | 2704 | struct tomoyo_domain_info *domain; |
| 2563 | const int idx = tomoyo_read_lock(); | 2705 | const int idx = tomoyo_read_lock(); |
| 2564 | tomoyo_policy_loaded = true; | 2706 | tomoyo_policy_loaded = true; |
| 2565 | printk(KERN_INFO "TOMOYO: 2.4.0\n"); | 2707 | printk(KERN_INFO "TOMOYO: 2.5.0\n"); |
| 2566 | list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { | 2708 | list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { |
| 2567 | const u8 profile = domain->profile; | 2709 | const u8 profile = domain->profile; |
| 2568 | const struct tomoyo_policy_namespace *ns = domain->ns; | 2710 | const struct tomoyo_policy_namespace *ns = domain->ns; |
| 2569 | if (ns->profile_version != 20100903) | 2711 | if (ns->profile_version != 20110903) |
| 2570 | printk(KERN_ERR | 2712 | printk(KERN_ERR |
| 2571 | "Profile version %u is not supported.\n", | 2713 | "Profile version %u is not supported.\n", |
| 2572 | ns->profile_version); | 2714 | ns->profile_version); |
| @@ -2577,9 +2719,9 @@ void tomoyo_check_profile(void) | |||
| 2577 | else | 2719 | else |
| 2578 | continue; | 2720 | continue; |
| 2579 | printk(KERN_ERR | 2721 | printk(KERN_ERR |
| 2580 | "Userland tools for TOMOYO 2.4 must be installed and " | 2722 | "Userland tools for TOMOYO 2.5 must be installed and " |
| 2581 | "policy must be initialized.\n"); | 2723 | "policy must be initialized.\n"); |
| 2582 | printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.4/ " | 2724 | printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.5/ " |
| 2583 | "for more information.\n"); | 2725 | "for more information.\n"); |
| 2584 | panic("STOP!"); | 2726 | panic("STOP!"); |
| 2585 | } | 2727 | } |
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index f7fbaa66e443..ed311d7a8ce0 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | * | 3 | * |
| 4 | * Header file for TOMOYO. | 4 | * Header file for TOMOYO. |
| 5 | * | 5 | * |
| 6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | 6 | * Copyright (C) 2005-2011 NTT DATA CORPORATION |
| 7 | */ | 7 | */ |
| 8 | 8 | ||
| 9 | #ifndef _SECURITY_TOMOYO_COMMON_H | 9 | #ifndef _SECURITY_TOMOYO_COMMON_H |
| @@ -23,6 +23,16 @@ | |||
| 23 | #include <linux/poll.h> | 23 | #include <linux/poll.h> |
| 24 | #include <linux/binfmts.h> | 24 | #include <linux/binfmts.h> |
| 25 | #include <linux/highmem.h> | 25 | #include <linux/highmem.h> |
| 26 | #include <linux/net.h> | ||
| 27 | #include <linux/inet.h> | ||
| 28 | #include <linux/in.h> | ||
| 29 | #include <linux/in6.h> | ||
| 30 | #include <linux/un.h> | ||
| 31 | #include <net/sock.h> | ||
| 32 | #include <net/af_unix.h> | ||
| 33 | #include <net/ip.h> | ||
| 34 | #include <net/ipv6.h> | ||
| 35 | #include <net/udp.h> | ||
| 26 | 36 | ||
| 27 | /********** Constants definitions. **********/ | 37 | /********** Constants definitions. **********/ |
| 28 | 38 | ||
| @@ -34,8 +44,17 @@ | |||
| 34 | #define TOMOYO_HASH_BITS 8 | 44 | #define TOMOYO_HASH_BITS 8 |
| 35 | #define TOMOYO_MAX_HASH (1u<<TOMOYO_HASH_BITS) | 45 | #define TOMOYO_MAX_HASH (1u<<TOMOYO_HASH_BITS) |
| 36 | 46 | ||
| 47 | /* | ||
| 48 | * TOMOYO checks only SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_SEQPACKET. | ||
| 49 | * Therefore, we don't need SOCK_MAX. | ||
| 50 | */ | ||
| 51 | #define TOMOYO_SOCK_MAX 6 | ||
| 52 | |||
| 37 | #define TOMOYO_EXEC_TMPSIZE 4096 | 53 | #define TOMOYO_EXEC_TMPSIZE 4096 |
| 38 | 54 | ||
| 55 | /* Garbage collector is trying to kfree() this element. */ | ||
| 56 | #define TOMOYO_GC_IN_PROGRESS -1 | ||
| 57 | |||
| 39 | /* Profile number is an integer between 0 and 255. */ | 58 | /* Profile number is an integer between 0 and 255. */ |
| 40 | #define TOMOYO_MAX_PROFILES 256 | 59 | #define TOMOYO_MAX_PROFILES 256 |
| 41 | 60 | ||
| @@ -136,6 +155,7 @@ enum tomoyo_mode_index { | |||
| 136 | /* Index numbers for entry type. */ | 155 | /* Index numbers for entry type. */ |
| 137 | enum tomoyo_policy_id { | 156 | enum tomoyo_policy_id { |
| 138 | TOMOYO_ID_GROUP, | 157 | TOMOYO_ID_GROUP, |
| 158 | TOMOYO_ID_ADDRESS_GROUP, | ||
| 139 | TOMOYO_ID_PATH_GROUP, | 159 | TOMOYO_ID_PATH_GROUP, |
| 140 | TOMOYO_ID_NUMBER_GROUP, | 160 | TOMOYO_ID_NUMBER_GROUP, |
| 141 | TOMOYO_ID_TRANSITION_CONTROL, | 161 | TOMOYO_ID_TRANSITION_CONTROL, |
| @@ -162,10 +182,21 @@ enum tomoyo_domain_info_flags_index { | |||
| 162 | TOMOYO_MAX_DOMAIN_INFO_FLAGS | 182 | TOMOYO_MAX_DOMAIN_INFO_FLAGS |
| 163 | }; | 183 | }; |
| 164 | 184 | ||
| 185 | /* Index numbers for audit type. */ | ||
| 186 | enum tomoyo_grant_log { | ||
| 187 | /* Follow profile's configuration. */ | ||
| 188 | TOMOYO_GRANTLOG_AUTO, | ||
| 189 | /* Do not generate grant log. */ | ||
| 190 | TOMOYO_GRANTLOG_NO, | ||
| 191 | /* Generate grant_log. */ | ||
| 192 | TOMOYO_GRANTLOG_YES, | ||
| 193 | }; | ||
| 194 | |||
| 165 | /* Index numbers for group entries. */ | 195 | /* Index numbers for group entries. */ |
| 166 | enum tomoyo_group_id { | 196 | enum tomoyo_group_id { |
| 167 | TOMOYO_PATH_GROUP, | 197 | TOMOYO_PATH_GROUP, |
| 168 | TOMOYO_NUMBER_GROUP, | 198 | TOMOYO_NUMBER_GROUP, |
| 199 | TOMOYO_ADDRESS_GROUP, | ||
| 169 | TOMOYO_MAX_GROUP | 200 | TOMOYO_MAX_GROUP |
| 170 | }; | 201 | }; |
| 171 | 202 | ||
| @@ -196,6 +227,10 @@ enum tomoyo_acl_entry_type_index { | |||
| 196 | TOMOYO_TYPE_PATH_NUMBER_ACL, | 227 | TOMOYO_TYPE_PATH_NUMBER_ACL, |
| 197 | TOMOYO_TYPE_MKDEV_ACL, | 228 | TOMOYO_TYPE_MKDEV_ACL, |
| 198 | TOMOYO_TYPE_MOUNT_ACL, | 229 | TOMOYO_TYPE_MOUNT_ACL, |
| 230 | TOMOYO_TYPE_INET_ACL, | ||
| 231 | TOMOYO_TYPE_UNIX_ACL, | ||
| 232 | TOMOYO_TYPE_ENV_ACL, | ||
| 233 | TOMOYO_TYPE_MANUAL_TASK_ACL, | ||
| 199 | }; | 234 | }; |
| 200 | 235 | ||
| 201 | /* Index numbers for access controls with one pathname. */ | 236 | /* Index numbers for access controls with one pathname. */ |
| @@ -228,6 +263,15 @@ enum tomoyo_mkdev_acl_index { | |||
| 228 | TOMOYO_MAX_MKDEV_OPERATION | 263 | TOMOYO_MAX_MKDEV_OPERATION |
| 229 | }; | 264 | }; |
| 230 | 265 | ||
| 266 | /* Index numbers for socket operations. */ | ||
| 267 | enum tomoyo_network_acl_index { | ||
| 268 | TOMOYO_NETWORK_BIND, /* bind() operation. */ | ||
| 269 | TOMOYO_NETWORK_LISTEN, /* listen() operation. */ | ||
| 270 | TOMOYO_NETWORK_CONNECT, /* connect() operation. */ | ||
| 271 | TOMOYO_NETWORK_SEND, /* send() operation. */ | ||
| 272 | TOMOYO_MAX_NETWORK_OPERATION | ||
| 273 | }; | ||
| 274 | |||
| 231 | /* Index numbers for access controls with two pathnames. */ | 275 | /* Index numbers for access controls with two pathnames. */ |
| 232 | enum tomoyo_path2_acl_index { | 276 | enum tomoyo_path2_acl_index { |
| 233 | TOMOYO_TYPE_LINK, | 277 | TOMOYO_TYPE_LINK, |
| @@ -255,7 +299,6 @@ enum tomoyo_securityfs_interface_index { | |||
| 255 | TOMOYO_EXCEPTIONPOLICY, | 299 | TOMOYO_EXCEPTIONPOLICY, |
| 256 | TOMOYO_PROCESS_STATUS, | 300 | TOMOYO_PROCESS_STATUS, |
| 257 | TOMOYO_STAT, | 301 | TOMOYO_STAT, |
| 258 | TOMOYO_SELFDOMAIN, | ||
| 259 | TOMOYO_AUDIT, | 302 | TOMOYO_AUDIT, |
| 260 | TOMOYO_VERSION, | 303 | TOMOYO_VERSION, |
| 261 | TOMOYO_PROFILE, | 304 | TOMOYO_PROFILE, |
| @@ -300,12 +343,30 @@ enum tomoyo_mac_index { | |||
| 300 | TOMOYO_MAC_FILE_MOUNT, | 343 | TOMOYO_MAC_FILE_MOUNT, |
| 301 | TOMOYO_MAC_FILE_UMOUNT, | 344 | TOMOYO_MAC_FILE_UMOUNT, |
| 302 | TOMOYO_MAC_FILE_PIVOT_ROOT, | 345 | TOMOYO_MAC_FILE_PIVOT_ROOT, |
| 346 | TOMOYO_MAC_NETWORK_INET_STREAM_BIND, | ||
| 347 | TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN, | ||
| 348 | TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT, | ||
| 349 | TOMOYO_MAC_NETWORK_INET_DGRAM_BIND, | ||
| 350 | TOMOYO_MAC_NETWORK_INET_DGRAM_SEND, | ||
| 351 | TOMOYO_MAC_NETWORK_INET_RAW_BIND, | ||
| 352 | TOMOYO_MAC_NETWORK_INET_RAW_SEND, | ||
| 353 | TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND, | ||
| 354 | TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN, | ||
| 355 | TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT, | ||
| 356 | TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND, | ||
| 357 | TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND, | ||
| 358 | TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND, | ||
| 359 | TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN, | ||
| 360 | TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT, | ||
| 361 | TOMOYO_MAC_ENVIRON, | ||
| 303 | TOMOYO_MAX_MAC_INDEX | 362 | TOMOYO_MAX_MAC_INDEX |
| 304 | }; | 363 | }; |
| 305 | 364 | ||
| 306 | /* Index numbers for category of functionality. */ | 365 | /* Index numbers for category of functionality. */ |
| 307 | enum tomoyo_mac_category_index { | 366 | enum tomoyo_mac_category_index { |
| 308 | TOMOYO_MAC_CATEGORY_FILE, | 367 | TOMOYO_MAC_CATEGORY_FILE, |
| 368 | TOMOYO_MAC_CATEGORY_NETWORK, | ||
| 369 | TOMOYO_MAC_CATEGORY_MISC, | ||
| 309 | TOMOYO_MAX_MAC_CATEGORY_INDEX | 370 | TOMOYO_MAX_MAC_CATEGORY_INDEX |
| 310 | }; | 371 | }; |
| 311 | 372 | ||
| @@ -340,7 +401,7 @@ enum tomoyo_pref_index { | |||
| 340 | /* Common header for holding ACL entries. */ | 401 | /* Common header for holding ACL entries. */ |
| 341 | struct tomoyo_acl_head { | 402 | struct tomoyo_acl_head { |
| 342 | struct list_head list; | 403 | struct list_head list; |
| 343 | bool is_deleted; | 404 | s8 is_deleted; /* true or false or TOMOYO_GC_IN_PROGRESS */ |
| 344 | } __packed; | 405 | } __packed; |
| 345 | 406 | ||
| 346 | /* Common header for shared entries. */ | 407 | /* Common header for shared entries. */ |
| @@ -397,13 +458,36 @@ struct tomoyo_request_info { | |||
| 397 | u8 operation; | 458 | u8 operation; |
| 398 | } path_number; | 459 | } path_number; |
| 399 | struct { | 460 | struct { |
| 461 | const struct tomoyo_path_info *name; | ||
| 462 | } environ; | ||
| 463 | struct { | ||
| 464 | const __be32 *address; | ||
| 465 | u16 port; | ||
| 466 | /* One of values smaller than TOMOYO_SOCK_MAX. */ | ||
| 467 | u8 protocol; | ||
| 468 | /* One of values in "enum tomoyo_network_acl_index". */ | ||
| 469 | u8 operation; | ||
| 470 | bool is_ipv6; | ||
| 471 | } inet_network; | ||
| 472 | struct { | ||
| 473 | const struct tomoyo_path_info *address; | ||
| 474 | /* One of values smaller than TOMOYO_SOCK_MAX. */ | ||
| 475 | u8 protocol; | ||
| 476 | /* One of values in "enum tomoyo_network_acl_index". */ | ||
| 477 | u8 operation; | ||
| 478 | } unix_network; | ||
| 479 | struct { | ||
| 400 | const struct tomoyo_path_info *type; | 480 | const struct tomoyo_path_info *type; |
| 401 | const struct tomoyo_path_info *dir; | 481 | const struct tomoyo_path_info *dir; |
| 402 | const struct tomoyo_path_info *dev; | 482 | const struct tomoyo_path_info *dev; |
| 403 | unsigned long flags; | 483 | unsigned long flags; |
| 404 | int need_dev; | 484 | int need_dev; |
| 405 | } mount; | 485 | } mount; |
| 486 | struct { | ||
| 487 | const struct tomoyo_path_info *domainname; | ||
| 488 | } task; | ||
| 406 | } param; | 489 | } param; |
| 490 | struct tomoyo_acl_info *matched_acl; | ||
| 407 | u8 param_type; | 491 | u8 param_type; |
| 408 | bool granted; | 492 | bool granted; |
| 409 | u8 retry; | 493 | u8 retry; |
| @@ -442,7 +526,14 @@ struct tomoyo_number_union { | |||
| 442 | u8 value_type[2]; | 526 | u8 value_type[2]; |
| 443 | }; | 527 | }; |
| 444 | 528 | ||
| 445 | /* Structure for "path_group"/"number_group" directive. */ | 529 | /* Structure for holding an IP address. */ |
| 530 | struct tomoyo_ipaddr_union { | ||
| 531 | struct in6_addr ip[2]; /* Big endian. */ | ||
| 532 | struct tomoyo_group *group; /* Pointer to address group. */ | ||
| 533 | bool is_ipv6; /* Valid only if @group == NULL. */ | ||
| 534 | }; | ||
| 535 | |||
| 536 | /* Structure for "path_group"/"number_group"/"address_group" directive. */ | ||
| 446 | struct tomoyo_group { | 537 | struct tomoyo_group { |
| 447 | struct tomoyo_shared_acl_head head; | 538 | struct tomoyo_shared_acl_head head; |
| 448 | const struct tomoyo_path_info *group_name; | 539 | const struct tomoyo_path_info *group_name; |
| @@ -461,6 +552,13 @@ struct tomoyo_number_group { | |||
| 461 | struct tomoyo_number_union number; | 552 | struct tomoyo_number_union number; |
| 462 | }; | 553 | }; |
| 463 | 554 | ||
| 555 | /* Structure for "address_group" directive. */ | ||
| 556 | struct tomoyo_address_group { | ||
| 557 | struct tomoyo_acl_head head; | ||
| 558 | /* Structure for holding an IP address. */ | ||
| 559 | struct tomoyo_ipaddr_union address; | ||
| 560 | }; | ||
| 561 | |||
| 464 | /* Subset of "struct stat". Used by conditional ACL and audit logs. */ | 562 | /* Subset of "struct stat". Used by conditional ACL and audit logs. */ |
| 465 | struct tomoyo_mini_stat { | 563 | struct tomoyo_mini_stat { |
| 466 | uid_t uid; | 564 | uid_t uid; |
| @@ -520,6 +618,7 @@ struct tomoyo_execve { | |||
| 520 | struct tomoyo_request_info r; | 618 | struct tomoyo_request_info r; |
| 521 | struct tomoyo_obj_info obj; | 619 | struct tomoyo_obj_info obj; |
| 522 | struct linux_binprm *bprm; | 620 | struct linux_binprm *bprm; |
| 621 | const struct tomoyo_path_info *transition; | ||
| 523 | /* For dumping argv[] and envp[]. */ | 622 | /* For dumping argv[] and envp[]. */ |
| 524 | struct tomoyo_page_dump dump; | 623 | struct tomoyo_page_dump dump; |
| 525 | /* For temporary use. */ | 624 | /* For temporary use. */ |
| @@ -554,6 +653,8 @@ struct tomoyo_condition { | |||
| 554 | u16 names_count; /* Number of "struct tomoyo_name_union names". */ | 653 | u16 names_count; /* Number of "struct tomoyo_name_union names". */ |
| 555 | u16 argc; /* Number of "struct tomoyo_argv". */ | 654 | u16 argc; /* Number of "struct tomoyo_argv". */ |
| 556 | u16 envc; /* Number of "struct tomoyo_envp". */ | 655 | u16 envc; /* Number of "struct tomoyo_envp". */ |
| 656 | u8 grant_log; /* One of values in "enum tomoyo_grant_log". */ | ||
| 657 | const struct tomoyo_path_info *transit; /* Maybe NULL. */ | ||
| 557 | /* | 658 | /* |
| 558 | * struct tomoyo_condition_element condition[condc]; | 659 | * struct tomoyo_condition_element condition[condc]; |
| 559 | * struct tomoyo_number_union values[numbers_count]; | 660 | * struct tomoyo_number_union values[numbers_count]; |
| @@ -567,7 +668,7 @@ struct tomoyo_condition { | |||
| 567 | struct tomoyo_acl_info { | 668 | struct tomoyo_acl_info { |
| 568 | struct list_head list; | 669 | struct list_head list; |
| 569 | struct tomoyo_condition *cond; /* Maybe NULL. */ | 670 | struct tomoyo_condition *cond; /* Maybe NULL. */ |
| 570 | bool is_deleted; | 671 | s8 is_deleted; /* true or false or TOMOYO_GC_IN_PROGRESS */ |
| 571 | u8 type; /* One of values in "enum tomoyo_acl_entry_type_index". */ | 672 | u8 type; /* One of values in "enum tomoyo_acl_entry_type_index". */ |
| 572 | } __packed; | 673 | } __packed; |
| 573 | 674 | ||
| @@ -587,6 +688,15 @@ struct tomoyo_domain_info { | |||
| 587 | }; | 688 | }; |
| 588 | 689 | ||
| 589 | /* | 690 | /* |
| 691 | * Structure for "task manual_domain_transition" directive. | ||
| 692 | */ | ||
| 693 | struct tomoyo_task_acl { | ||
| 694 | struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MANUAL_TASK_ACL */ | ||
| 695 | /* Pointer to domainname. */ | ||
| 696 | const struct tomoyo_path_info *domainname; | ||
| 697 | }; | ||
| 698 | |||
| 699 | /* | ||
| 590 | * Structure for "file execute", "file read", "file write", "file append", | 700 | * Structure for "file execute", "file read", "file write", "file append", |
| 591 | * "file unlink", "file getattr", "file rmdir", "file truncate", | 701 | * "file unlink", "file getattr", "file rmdir", "file truncate", |
| 592 | * "file symlink", "file chroot" and "file unmount" directive. | 702 | * "file symlink", "file chroot" and "file unmount" directive. |
| @@ -638,6 +748,29 @@ struct tomoyo_mount_acl { | |||
| 638 | struct tomoyo_number_union flags; | 748 | struct tomoyo_number_union flags; |
| 639 | }; | 749 | }; |
| 640 | 750 | ||
| 751 | /* Structure for "misc env" directive in domain policy. */ | ||
| 752 | struct tomoyo_env_acl { | ||
| 753 | struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_ENV_ACL */ | ||
| 754 | const struct tomoyo_path_info *env; /* environment variable */ | ||
| 755 | }; | ||
| 756 | |||
| 757 | /* Structure for "network inet" directive. */ | ||
| 758 | struct tomoyo_inet_acl { | ||
| 759 | struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_INET_ACL */ | ||
| 760 | u8 protocol; | ||
| 761 | u8 perm; /* Bitmask of values in "enum tomoyo_network_acl_index" */ | ||
| 762 | struct tomoyo_ipaddr_union address; | ||
| 763 | struct tomoyo_number_union port; | ||
| 764 | }; | ||
| 765 | |||
| 766 | /* Structure for "network unix" directive. */ | ||
| 767 | struct tomoyo_unix_acl { | ||
| 768 | struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_UNIX_ACL */ | ||
| 769 | u8 protocol; | ||
| 770 | u8 perm; /* Bitmask of values in "enum tomoyo_network_acl_index" */ | ||
| 771 | struct tomoyo_name_union name; | ||
| 772 | }; | ||
| 773 | |||
| 641 | /* Structure for holding a line from /sys/kernel/security/tomoyo/ interface. */ | 774 | /* Structure for holding a line from /sys/kernel/security/tomoyo/ interface. */ |
| 642 | struct tomoyo_acl_param { | 775 | struct tomoyo_acl_param { |
| 643 | char *data; | 776 | char *data; |
| @@ -773,7 +906,7 @@ struct tomoyo_policy_namespace { | |||
| 773 | struct list_head acl_group[TOMOYO_MAX_ACL_GROUPS]; | 906 | struct list_head acl_group[TOMOYO_MAX_ACL_GROUPS]; |
| 774 | /* List for connecting to tomoyo_namespace_list list. */ | 907 | /* List for connecting to tomoyo_namespace_list list. */ |
| 775 | struct list_head namespace_list; | 908 | struct list_head namespace_list; |
| 776 | /* Profile version. Currently only 20100903 is defined. */ | 909 | /* Profile version. Currently only 20110903 is defined. */ |
| 777 | unsigned int profile_version; | 910 | unsigned int profile_version; |
| 778 | /* Name of this namespace (e.g. "<kernel>", "</usr/sbin/httpd>" ). */ | 911 | /* Name of this namespace (e.g. "<kernel>", "</usr/sbin/httpd>" ). */ |
| 779 | const char *name; | 912 | const char *name; |
| @@ -781,6 +914,8 @@ struct tomoyo_policy_namespace { | |||
| 781 | 914 | ||
| 782 | /********** Function prototypes. **********/ | 915 | /********** Function prototypes. **********/ |
| 783 | 916 | ||
| 917 | bool tomoyo_address_matches_group(const bool is_ipv6, const __be32 *address, | ||
| 918 | const struct tomoyo_group *group); | ||
| 784 | bool tomoyo_compare_number_union(const unsigned long value, | 919 | bool tomoyo_compare_number_union(const unsigned long value, |
| 785 | const struct tomoyo_number_union *ptr); | 920 | const struct tomoyo_number_union *ptr); |
| 786 | bool tomoyo_condition(struct tomoyo_request_info *r, | 921 | bool tomoyo_condition(struct tomoyo_request_info *r, |
| @@ -796,6 +931,8 @@ bool tomoyo_memory_ok(void *ptr); | |||
| 796 | bool tomoyo_number_matches_group(const unsigned long min, | 931 | bool tomoyo_number_matches_group(const unsigned long min, |
| 797 | const unsigned long max, | 932 | const unsigned long max, |
| 798 | const struct tomoyo_group *group); | 933 | const struct tomoyo_group *group); |
| 934 | bool tomoyo_parse_ipaddr_union(struct tomoyo_acl_param *param, | ||
| 935 | struct tomoyo_ipaddr_union *ptr); | ||
| 799 | bool tomoyo_parse_name_union(struct tomoyo_acl_param *param, | 936 | bool tomoyo_parse_name_union(struct tomoyo_acl_param *param, |
| 800 | struct tomoyo_name_union *ptr); | 937 | struct tomoyo_name_union *ptr); |
| 801 | bool tomoyo_parse_number_union(struct tomoyo_acl_param *param, | 938 | 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, | |||
| 805 | bool tomoyo_permstr(const char *string, const char *keyword); | 942 | bool tomoyo_permstr(const char *string, const char *keyword); |
| 806 | bool tomoyo_str_starts(char **src, const char *find); | 943 | bool tomoyo_str_starts(char **src, const char *find); |
| 807 | char *tomoyo_encode(const char *str); | 944 | char *tomoyo_encode(const char *str); |
| 945 | char *tomoyo_encode2(const char *str, int str_len); | ||
| 808 | char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt, | 946 | char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt, |
| 809 | va_list args); | 947 | va_list args); |
| 810 | char *tomoyo_read_token(struct tomoyo_acl_param *param); | 948 | char *tomoyo_read_token(struct tomoyo_acl_param *param); |
| @@ -814,12 +952,17 @@ const char *tomoyo_get_exe(void); | |||
| 814 | const char *tomoyo_yesno(const unsigned int value); | 952 | const char *tomoyo_yesno(const unsigned int value); |
| 815 | const struct tomoyo_path_info *tomoyo_compare_name_union | 953 | const struct tomoyo_path_info *tomoyo_compare_name_union |
| 816 | (const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr); | 954 | (const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr); |
| 955 | const struct tomoyo_path_info *tomoyo_get_domainname | ||
| 956 | (struct tomoyo_acl_param *param); | ||
| 817 | const struct tomoyo_path_info *tomoyo_get_name(const char *name); | 957 | const struct tomoyo_path_info *tomoyo_get_name(const char *name); |
| 818 | const struct tomoyo_path_info *tomoyo_path_matches_group | 958 | const struct tomoyo_path_info *tomoyo_path_matches_group |
| 819 | (const struct tomoyo_path_info *pathname, const struct tomoyo_group *group); | 959 | (const struct tomoyo_path_info *pathname, const struct tomoyo_group *group); |
| 820 | int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, | 960 | int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, |
| 821 | struct path *path, const int flag); | 961 | struct path *path, const int flag); |
| 822 | int tomoyo_close_control(struct tomoyo_io_buffer *head); | 962 | int tomoyo_close_control(struct tomoyo_io_buffer *head); |
| 963 | int tomoyo_env_perm(struct tomoyo_request_info *r, const char *env); | ||
| 964 | int tomoyo_execute_permission(struct tomoyo_request_info *r, | ||
| 965 | const struct tomoyo_path_info *filename); | ||
| 823 | int tomoyo_find_next_domain(struct linux_binprm *bprm); | 966 | int tomoyo_find_next_domain(struct linux_binprm *bprm); |
| 824 | int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile, | 967 | int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile, |
| 825 | const u8 index); | 968 | const u8 index); |
| @@ -838,10 +981,15 @@ int tomoyo_path_number_perm(const u8 operation, struct path *path, | |||
| 838 | unsigned long number); | 981 | unsigned long number); |
| 839 | int tomoyo_path_perm(const u8 operation, struct path *path, | 982 | int tomoyo_path_perm(const u8 operation, struct path *path, |
| 840 | const char *target); | 983 | const char *target); |
| 841 | int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, | ||
| 842 | const struct tomoyo_path_info *filename); | ||
| 843 | int tomoyo_poll_control(struct file *file, poll_table *wait); | 984 | int tomoyo_poll_control(struct file *file, poll_table *wait); |
| 844 | int tomoyo_poll_log(struct file *file, poll_table *wait); | 985 | int tomoyo_poll_log(struct file *file, poll_table *wait); |
| 986 | int tomoyo_socket_bind_permission(struct socket *sock, struct sockaddr *addr, | ||
| 987 | int addr_len); | ||
| 988 | int tomoyo_socket_connect_permission(struct socket *sock, | ||
| 989 | struct sockaddr *addr, int addr_len); | ||
| 990 | int tomoyo_socket_listen_permission(struct socket *sock); | ||
| 991 | int tomoyo_socket_sendmsg_permission(struct socket *sock, struct msghdr *msg, | ||
| 992 | int size); | ||
| 845 | int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) | 993 | int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) |
| 846 | __printf(2, 3); | 994 | __printf(2, 3); |
| 847 | int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, | 995 | 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, | |||
| 860 | int tomoyo_write_aggregator(struct tomoyo_acl_param *param); | 1008 | int tomoyo_write_aggregator(struct tomoyo_acl_param *param); |
| 861 | int tomoyo_write_file(struct tomoyo_acl_param *param); | 1009 | int tomoyo_write_file(struct tomoyo_acl_param *param); |
| 862 | int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type); | 1010 | int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type); |
| 1011 | int tomoyo_write_misc(struct tomoyo_acl_param *param); | ||
| 1012 | int tomoyo_write_inet_network(struct tomoyo_acl_param *param); | ||
| 863 | int tomoyo_write_transition_control(struct tomoyo_acl_param *param, | 1013 | int tomoyo_write_transition_control(struct tomoyo_acl_param *param, |
| 864 | const u8 type); | 1014 | const u8 type); |
| 1015 | int tomoyo_write_unix_network(struct tomoyo_acl_param *param); | ||
| 865 | ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer, | 1016 | ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer, |
| 866 | const int buffer_len); | 1017 | const int buffer_len); |
| 867 | ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head, | 1018 | ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head, |
| @@ -891,12 +1042,11 @@ void tomoyo_del_condition(struct list_head *element); | |||
| 891 | void tomoyo_fill_path_info(struct tomoyo_path_info *ptr); | 1042 | void tomoyo_fill_path_info(struct tomoyo_path_info *ptr); |
| 892 | void tomoyo_get_attributes(struct tomoyo_obj_info *obj); | 1043 | void tomoyo_get_attributes(struct tomoyo_obj_info *obj); |
| 893 | void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns); | 1044 | void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns); |
| 894 | void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) | ||
| 895 | __printf(2, 3); | ||
| 896 | void tomoyo_load_policy(const char *filename); | 1045 | void tomoyo_load_policy(const char *filename); |
| 897 | void tomoyo_memory_free(void *ptr); | ||
| 898 | void tomoyo_normalize_line(unsigned char *buffer); | 1046 | void tomoyo_normalize_line(unsigned char *buffer); |
| 899 | void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register); | 1047 | void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register); |
| 1048 | void tomoyo_print_ip(char *buf, const unsigned int size, | ||
| 1049 | const struct tomoyo_ipaddr_union *ptr); | ||
| 900 | void tomoyo_print_ulong(char *buffer, const int buffer_len, | 1050 | void tomoyo_print_ulong(char *buffer, const int buffer_len, |
| 901 | const unsigned long value, const u8 type); | 1051 | const unsigned long value, const u8 type); |
| 902 | void tomoyo_put_name_union(struct tomoyo_name_union *ptr); | 1052 | 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 | |||
| 919 | + TOMOYO_MAX_MAC_CATEGORY_INDEX]; | 1069 | + TOMOYO_MAX_MAC_CATEGORY_INDEX]; |
| 920 | extern const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE]; | 1070 | extern const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE]; |
| 921 | extern const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION]; | 1071 | extern const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION]; |
| 1072 | extern const char * const tomoyo_proto_keyword[TOMOYO_SOCK_MAX]; | ||
| 1073 | extern const char * const tomoyo_socket_keyword[TOMOYO_MAX_NETWORK_OPERATION]; | ||
| 922 | extern const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX]; | 1074 | extern const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX]; |
| 923 | extern const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION]; | 1075 | extern const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION]; |
| 924 | extern const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION]; | 1076 | extern const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION]; |
| @@ -1098,6 +1250,21 @@ static inline bool tomoyo_same_number_union | |||
| 1098 | } | 1250 | } |
| 1099 | 1251 | ||
| 1100 | /** | 1252 | /** |
| 1253 | * tomoyo_same_ipaddr_union - Check for duplicated "struct tomoyo_ipaddr_union" entry. | ||
| 1254 | * | ||
| 1255 | * @a: Pointer to "struct tomoyo_ipaddr_union". | ||
| 1256 | * @b: Pointer to "struct tomoyo_ipaddr_union". | ||
| 1257 | * | ||
| 1258 | * Returns true if @a == @b, false otherwise. | ||
| 1259 | */ | ||
| 1260 | static inline bool tomoyo_same_ipaddr_union | ||
| 1261 | (const struct tomoyo_ipaddr_union *a, const struct tomoyo_ipaddr_union *b) | ||
| 1262 | { | ||
| 1263 | return !memcmp(a->ip, b->ip, sizeof(a->ip)) && a->group == b->group && | ||
| 1264 | a->is_ipv6 == b->is_ipv6; | ||
| 1265 | } | ||
| 1266 | |||
| 1267 | /** | ||
| 1101 | * tomoyo_current_namespace - Get "struct tomoyo_policy_namespace" for current thread. | 1268 | * tomoyo_current_namespace - Get "struct tomoyo_policy_namespace" for current thread. |
| 1102 | * | 1269 | * |
| 1103 | * Returns pointer to "struct tomoyo_policy_namespace" for current thread. | 1270 | * Returns pointer to "struct tomoyo_policy_namespace" for current thread. |
diff --git a/security/tomoyo/condition.c b/security/tomoyo/condition.c index 8a05f71eaf67..986330b8c73e 100644 --- a/security/tomoyo/condition.c +++ b/security/tomoyo/condition.c | |||
| @@ -348,6 +348,7 @@ static inline bool tomoyo_same_condition(const struct tomoyo_condition *a, | |||
| 348 | a->numbers_count == b->numbers_count && | 348 | a->numbers_count == b->numbers_count && |
| 349 | a->names_count == b->names_count && | 349 | a->names_count == b->names_count && |
| 350 | a->argc == b->argc && a->envc == b->envc && | 350 | a->argc == b->argc && a->envc == b->envc && |
| 351 | a->grant_log == b->grant_log && a->transit == b->transit && | ||
| 351 | !memcmp(a + 1, b + 1, a->size - sizeof(*a)); | 352 | !memcmp(a + 1, b + 1, a->size - sizeof(*a)); |
| 352 | } | 353 | } |
| 353 | 354 | ||
| @@ -399,8 +400,9 @@ static struct tomoyo_condition *tomoyo_commit_condition | |||
| 399 | found = true; | 400 | found = true; |
| 400 | goto out; | 401 | goto out; |
| 401 | } | 402 | } |
| 402 | list_for_each_entry_rcu(ptr, &tomoyo_condition_list, head.list) { | 403 | list_for_each_entry(ptr, &tomoyo_condition_list, head.list) { |
| 403 | if (!tomoyo_same_condition(ptr, entry)) | 404 | if (!tomoyo_same_condition(ptr, entry) || |
| 405 | atomic_read(&ptr->head.users) == TOMOYO_GC_IN_PROGRESS) | ||
| 404 | continue; | 406 | continue; |
| 405 | /* Same entry found. Share this entry. */ | 407 | /* Same entry found. Share this entry. */ |
| 406 | atomic_inc(&ptr->head.users); | 408 | atomic_inc(&ptr->head.users); |
| @@ -410,8 +412,7 @@ static struct tomoyo_condition *tomoyo_commit_condition | |||
| 410 | if (!found) { | 412 | if (!found) { |
| 411 | if (tomoyo_memory_ok(entry)) { | 413 | if (tomoyo_memory_ok(entry)) { |
| 412 | atomic_set(&entry->head.users, 1); | 414 | atomic_set(&entry->head.users, 1); |
| 413 | list_add_rcu(&entry->head.list, | 415 | list_add(&entry->head.list, &tomoyo_condition_list); |
| 414 | &tomoyo_condition_list); | ||
| 415 | } else { | 416 | } else { |
| 416 | found = true; | 417 | found = true; |
| 417 | ptr = NULL; | 418 | ptr = NULL; |
| @@ -428,6 +429,46 @@ out: | |||
| 428 | } | 429 | } |
| 429 | 430 | ||
| 430 | /** | 431 | /** |
| 432 | * tomoyo_get_transit_preference - Parse domain transition preference for execve(). | ||
| 433 | * | ||
| 434 | * @param: Pointer to "struct tomoyo_acl_param". | ||
| 435 | * @e: Pointer to "struct tomoyo_condition". | ||
| 436 | * | ||
| 437 | * Returns the condition string part. | ||
| 438 | */ | ||
| 439 | static char *tomoyo_get_transit_preference(struct tomoyo_acl_param *param, | ||
| 440 | struct tomoyo_condition *e) | ||
| 441 | { | ||
| 442 | char * const pos = param->data; | ||
| 443 | bool flag; | ||
| 444 | if (*pos == '<') { | ||
| 445 | e->transit = tomoyo_get_domainname(param); | ||
| 446 | goto done; | ||
| 447 | } | ||
| 448 | { | ||
| 449 | char *cp = strchr(pos, ' '); | ||
| 450 | if (cp) | ||
| 451 | *cp = '\0'; | ||
| 452 | flag = tomoyo_correct_path(pos) || !strcmp(pos, "keep") || | ||
| 453 | !strcmp(pos, "initialize") || !strcmp(pos, "reset") || | ||
| 454 | !strcmp(pos, "child") || !strcmp(pos, "parent"); | ||
| 455 | if (cp) | ||
| 456 | *cp = ' '; | ||
| 457 | } | ||
| 458 | if (!flag) | ||
| 459 | return pos; | ||
| 460 | e->transit = tomoyo_get_name(tomoyo_read_token(param)); | ||
| 461 | done: | ||
| 462 | if (e->transit) | ||
| 463 | return param->data; | ||
| 464 | /* | ||
| 465 | * Return a bad read-only condition string that will let | ||
| 466 | * tomoyo_get_condition() return NULL. | ||
| 467 | */ | ||
| 468 | return "/"; | ||
| 469 | } | ||
| 470 | |||
| 471 | /** | ||
| 431 | * tomoyo_get_condition - Parse condition part. | 472 | * tomoyo_get_condition - Parse condition part. |
| 432 | * | 473 | * |
| 433 | * @param: Pointer to "struct tomoyo_acl_param". | 474 | * @param: Pointer to "struct tomoyo_acl_param". |
| @@ -443,7 +484,8 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param) | |||
| 443 | struct tomoyo_argv *argv = NULL; | 484 | struct tomoyo_argv *argv = NULL; |
| 444 | struct tomoyo_envp *envp = NULL; | 485 | struct tomoyo_envp *envp = NULL; |
| 445 | struct tomoyo_condition e = { }; | 486 | struct tomoyo_condition e = { }; |
| 446 | char * const start_of_string = param->data; | 487 | char * const start_of_string = |
| 488 | tomoyo_get_transit_preference(param, &e); | ||
| 447 | char * const end_of_string = start_of_string + strlen(start_of_string); | 489 | char * const end_of_string = start_of_string + strlen(start_of_string); |
| 448 | char *pos; | 490 | char *pos; |
| 449 | rerun: | 491 | rerun: |
| @@ -486,6 +528,20 @@ rerun: | |||
| 486 | goto out; | 528 | goto out; |
| 487 | dprintk(KERN_WARNING "%u: <%s>%s=<%s>\n", __LINE__, left_word, | 529 | dprintk(KERN_WARNING "%u: <%s>%s=<%s>\n", __LINE__, left_word, |
| 488 | is_not ? "!" : "", right_word); | 530 | is_not ? "!" : "", right_word); |
| 531 | if (!strcmp(left_word, "grant_log")) { | ||
| 532 | if (entry) { | ||
| 533 | if (is_not || | ||
| 534 | entry->grant_log != TOMOYO_GRANTLOG_AUTO) | ||
| 535 | goto out; | ||
| 536 | else if (!strcmp(right_word, "yes")) | ||
| 537 | entry->grant_log = TOMOYO_GRANTLOG_YES; | ||
| 538 | else if (!strcmp(right_word, "no")) | ||
| 539 | entry->grant_log = TOMOYO_GRANTLOG_NO; | ||
| 540 | else | ||
| 541 | goto out; | ||
| 542 | } | ||
| 543 | continue; | ||
| 544 | } | ||
| 489 | if (!strncmp(left_word, "exec.argv[", 10)) { | 545 | if (!strncmp(left_word, "exec.argv[", 10)) { |
| 490 | if (!argv) { | 546 | if (!argv) { |
| 491 | e.argc++; | 547 | e.argc++; |
| @@ -593,8 +649,9 @@ store_value: | |||
| 593 | + e.envc * sizeof(struct tomoyo_envp); | 649 | + e.envc * sizeof(struct tomoyo_envp); |
| 594 | entry = kzalloc(e.size, GFP_NOFS); | 650 | entry = kzalloc(e.size, GFP_NOFS); |
| 595 | if (!entry) | 651 | if (!entry) |
| 596 | return NULL; | 652 | goto out2; |
| 597 | *entry = e; | 653 | *entry = e; |
| 654 | e.transit = NULL; | ||
| 598 | condp = (struct tomoyo_condition_element *) (entry + 1); | 655 | condp = (struct tomoyo_condition_element *) (entry + 1); |
| 599 | numbers_p = (struct tomoyo_number_union *) (condp + e.condc); | 656 | numbers_p = (struct tomoyo_number_union *) (condp + e.condc); |
| 600 | names_p = (struct tomoyo_name_union *) (numbers_p + e.numbers_count); | 657 | names_p = (struct tomoyo_name_union *) (numbers_p + e.numbers_count); |
| @@ -621,6 +678,8 @@ out: | |||
| 621 | tomoyo_del_condition(&entry->head.list); | 678 | tomoyo_del_condition(&entry->head.list); |
| 622 | kfree(entry); | 679 | kfree(entry); |
| 623 | } | 680 | } |
| 681 | out2: | ||
| 682 | tomoyo_put_name(e.transit); | ||
| 624 | return NULL; | 683 | return NULL; |
| 625 | } | 684 | } |
| 626 | 685 | ||
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index cd0f92d88bb4..9027ac1534af 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c | |||
| @@ -39,6 +39,8 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, | |||
| 39 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 39 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) |
| 40 | return -ENOMEM; | 40 | return -ENOMEM; |
| 41 | list_for_each_entry_rcu(entry, list, list) { | 41 | list_for_each_entry_rcu(entry, list, list) { |
| 42 | if (entry->is_deleted == TOMOYO_GC_IN_PROGRESS) | ||
| 43 | continue; | ||
| 42 | if (!check_duplicate(entry, new_entry)) | 44 | if (!check_duplicate(entry, new_entry)) |
| 43 | continue; | 45 | continue; |
| 44 | entry->is_deleted = param->is_delete; | 46 | entry->is_deleted = param->is_delete; |
| @@ -102,10 +104,21 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, | |||
| 102 | new_entry->cond = tomoyo_get_condition(param); | 104 | new_entry->cond = tomoyo_get_condition(param); |
| 103 | if (!new_entry->cond) | 105 | if (!new_entry->cond) |
| 104 | return -EINVAL; | 106 | return -EINVAL; |
| 107 | /* | ||
| 108 | * Domain transition preference is allowed for only | ||
| 109 | * "file execute" entries. | ||
| 110 | */ | ||
| 111 | if (new_entry->cond->transit && | ||
| 112 | !(new_entry->type == TOMOYO_TYPE_PATH_ACL && | ||
| 113 | container_of(new_entry, struct tomoyo_path_acl, head) | ||
| 114 | ->perm == 1 << TOMOYO_TYPE_EXECUTE)) | ||
| 115 | goto out; | ||
| 105 | } | 116 | } |
| 106 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 117 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) |
| 107 | goto out; | 118 | goto out; |
| 108 | list_for_each_entry_rcu(entry, list, list) { | 119 | list_for_each_entry_rcu(entry, list, list) { |
| 120 | if (entry->is_deleted == TOMOYO_GC_IN_PROGRESS) | ||
| 121 | continue; | ||
| 109 | if (!tomoyo_same_acl_head(entry, new_entry) || | 122 | if (!tomoyo_same_acl_head(entry, new_entry) || |
| 110 | !check_duplicate(entry, new_entry)) | 123 | !check_duplicate(entry, new_entry)) |
| 111 | continue; | 124 | continue; |
| @@ -157,6 +170,7 @@ retry: | |||
| 157 | continue; | 170 | continue; |
| 158 | if (!tomoyo_condition(r, ptr->cond)) | 171 | if (!tomoyo_condition(r, ptr->cond)) |
| 159 | continue; | 172 | continue; |
| 173 | r->matched_acl = ptr; | ||
| 160 | r->granted = true; | 174 | r->granted = true; |
| 161 | return; | 175 | return; |
| 162 | } | 176 | } |
| @@ -501,7 +515,8 @@ struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, | |||
| 501 | * that domain. Do not perform domain transition if | 515 | * that domain. Do not perform domain transition if |
| 502 | * profile for that domain is not yet created. | 516 | * profile for that domain is not yet created. |
| 503 | */ | 517 | */ |
| 504 | if (!entry->ns->profile_ptr[entry->profile]) | 518 | if (tomoyo_policy_loaded && |
| 519 | !entry->ns->profile_ptr[entry->profile]) | ||
| 505 | return NULL; | 520 | return NULL; |
| 506 | } | 521 | } |
| 507 | return entry; | 522 | return entry; |
| @@ -557,12 +572,99 @@ out: | |||
| 557 | tomoyo_write_log(&r, "use_profile %u\n", | 572 | tomoyo_write_log(&r, "use_profile %u\n", |
| 558 | entry->profile); | 573 | entry->profile); |
| 559 | tomoyo_write_log(&r, "use_group %u\n", entry->group); | 574 | tomoyo_write_log(&r, "use_group %u\n", entry->group); |
| 575 | tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES); | ||
| 560 | } | 576 | } |
| 561 | } | 577 | } |
| 562 | return entry; | 578 | return entry; |
| 563 | } | 579 | } |
| 564 | 580 | ||
| 565 | /** | 581 | /** |
| 582 | * tomoyo_environ - Check permission for environment variable names. | ||
| 583 | * | ||
| 584 | * @ee: Pointer to "struct tomoyo_execve". | ||
| 585 | * | ||
| 586 | * Returns 0 on success, negative value otherwise. | ||
| 587 | */ | ||
| 588 | static int tomoyo_environ(struct tomoyo_execve *ee) | ||
| 589 | { | ||
| 590 | struct tomoyo_request_info *r = &ee->r; | ||
| 591 | struct linux_binprm *bprm = ee->bprm; | ||
| 592 | /* env_page.data is allocated by tomoyo_dump_page(). */ | ||
| 593 | struct tomoyo_page_dump env_page = { }; | ||
| 594 | char *arg_ptr; /* Size is TOMOYO_EXEC_TMPSIZE bytes */ | ||
| 595 | int arg_len = 0; | ||
| 596 | unsigned long pos = bprm->p; | ||
| 597 | int offset = pos % PAGE_SIZE; | ||
| 598 | int argv_count = bprm->argc; | ||
| 599 | int envp_count = bprm->envc; | ||
| 600 | int error = -ENOMEM; | ||
| 601 | |||
| 602 | ee->r.type = TOMOYO_MAC_ENVIRON; | ||
| 603 | ee->r.profile = r->domain->profile; | ||
| 604 | ee->r.mode = tomoyo_get_mode(r->domain->ns, ee->r.profile, | ||
| 605 | TOMOYO_MAC_ENVIRON); | ||
| 606 | if (!r->mode || !envp_count) | ||
| 607 | return 0; | ||
| 608 | arg_ptr = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS); | ||
| 609 | if (!arg_ptr) | ||
| 610 | goto out; | ||
| 611 | while (error == -ENOMEM) { | ||
| 612 | if (!tomoyo_dump_page(bprm, pos, &env_page)) | ||
| 613 | goto out; | ||
| 614 | pos += PAGE_SIZE - offset; | ||
| 615 | /* Read. */ | ||
| 616 | while (argv_count && offset < PAGE_SIZE) { | ||
| 617 | if (!env_page.data[offset++]) | ||
| 618 | argv_count--; | ||
| 619 | } | ||
| 620 | if (argv_count) { | ||
| 621 | offset = 0; | ||
| 622 | continue; | ||
| 623 | } | ||
| 624 | while (offset < PAGE_SIZE) { | ||
| 625 | const unsigned char c = env_page.data[offset++]; | ||
| 626 | |||
| 627 | if (c && arg_len < TOMOYO_EXEC_TMPSIZE - 10) { | ||
| 628 | if (c == '=') { | ||
| 629 | arg_ptr[arg_len++] = '\0'; | ||
| 630 | } else if (c == '\\') { | ||
| 631 | arg_ptr[arg_len++] = '\\'; | ||
| 632 | arg_ptr[arg_len++] = '\\'; | ||
| 633 | } else if (c > ' ' && c < 127) { | ||
| 634 | arg_ptr[arg_len++] = c; | ||
| 635 | } else { | ||
| 636 | arg_ptr[arg_len++] = '\\'; | ||
| 637 | arg_ptr[arg_len++] = (c >> 6) + '0'; | ||
| 638 | arg_ptr[arg_len++] | ||
| 639 | = ((c >> 3) & 7) + '0'; | ||
| 640 | arg_ptr[arg_len++] = (c & 7) + '0'; | ||
| 641 | } | ||
| 642 | } else { | ||
| 643 | arg_ptr[arg_len] = '\0'; | ||
| 644 | } | ||
| 645 | if (c) | ||
| 646 | continue; | ||
| 647 | if (tomoyo_env_perm(r, arg_ptr)) { | ||
| 648 | error = -EPERM; | ||
| 649 | break; | ||
| 650 | } | ||
| 651 | if (!--envp_count) { | ||
| 652 | error = 0; | ||
| 653 | break; | ||
| 654 | } | ||
| 655 | arg_len = 0; | ||
| 656 | } | ||
| 657 | offset = 0; | ||
| 658 | } | ||
| 659 | out: | ||
| 660 | if (r->mode != TOMOYO_CONFIG_ENFORCING) | ||
| 661 | error = 0; | ||
| 662 | kfree(env_page.data); | ||
| 663 | kfree(arg_ptr); | ||
| 664 | return error; | ||
| 665 | } | ||
| 666 | |||
| 667 | /** | ||
| 566 | * tomoyo_find_next_domain - Find a domain. | 668 | * tomoyo_find_next_domain - Find a domain. |
| 567 | * | 669 | * |
| 568 | * @bprm: Pointer to "struct linux_binprm". | 670 | * @bprm: Pointer to "struct linux_binprm". |
| @@ -577,10 +679,11 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) | |||
| 577 | struct tomoyo_domain_info *domain = NULL; | 679 | struct tomoyo_domain_info *domain = NULL; |
| 578 | const char *original_name = bprm->filename; | 680 | const char *original_name = bprm->filename; |
| 579 | int retval = -ENOMEM; | 681 | int retval = -ENOMEM; |
| 580 | bool need_kfree = false; | ||
| 581 | bool reject_on_transition_failure = false; | 682 | bool reject_on_transition_failure = false; |
| 582 | struct tomoyo_path_info rn = { }; /* real name */ | 683 | const struct tomoyo_path_info *candidate; |
| 684 | struct tomoyo_path_info exename; | ||
| 583 | struct tomoyo_execve *ee = kzalloc(sizeof(*ee), GFP_NOFS); | 685 | struct tomoyo_execve *ee = kzalloc(sizeof(*ee), GFP_NOFS); |
| 686 | |||
| 584 | if (!ee) | 687 | if (!ee) |
| 585 | return -ENOMEM; | 688 | return -ENOMEM; |
| 586 | ee->tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS); | 689 | ee->tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS); |
| @@ -594,40 +697,32 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) | |||
| 594 | ee->bprm = bprm; | 697 | ee->bprm = bprm; |
| 595 | ee->r.obj = &ee->obj; | 698 | ee->r.obj = &ee->obj; |
| 596 | ee->obj.path1 = bprm->file->f_path; | 699 | ee->obj.path1 = bprm->file->f_path; |
| 597 | retry: | ||
| 598 | if (need_kfree) { | ||
| 599 | kfree(rn.name); | ||
| 600 | need_kfree = false; | ||
| 601 | } | ||
| 602 | /* Get symlink's pathname of program. */ | 700 | /* Get symlink's pathname of program. */ |
| 603 | retval = -ENOENT; | 701 | retval = -ENOENT; |
| 604 | rn.name = tomoyo_realpath_nofollow(original_name); | 702 | exename.name = tomoyo_realpath_nofollow(original_name); |
| 605 | if (!rn.name) | 703 | if (!exename.name) |
| 606 | goto out; | 704 | goto out; |
| 607 | tomoyo_fill_path_info(&rn); | 705 | tomoyo_fill_path_info(&exename); |
| 608 | need_kfree = true; | 706 | retry: |
| 609 | |||
| 610 | /* Check 'aggregator' directive. */ | 707 | /* Check 'aggregator' directive. */ |
| 611 | { | 708 | { |
| 612 | struct tomoyo_aggregator *ptr; | 709 | struct tomoyo_aggregator *ptr; |
| 613 | struct list_head *list = | 710 | struct list_head *list = |
| 614 | &old_domain->ns->policy_list[TOMOYO_ID_AGGREGATOR]; | 711 | &old_domain->ns->policy_list[TOMOYO_ID_AGGREGATOR]; |
| 615 | /* Check 'aggregator' directive. */ | 712 | /* Check 'aggregator' directive. */ |
| 713 | candidate = &exename; | ||
| 616 | list_for_each_entry_rcu(ptr, list, head.list) { | 714 | list_for_each_entry_rcu(ptr, list, head.list) { |
| 617 | if (ptr->head.is_deleted || | 715 | if (ptr->head.is_deleted || |
| 618 | !tomoyo_path_matches_pattern(&rn, | 716 | !tomoyo_path_matches_pattern(&exename, |
| 619 | ptr->original_name)) | 717 | ptr->original_name)) |
| 620 | continue; | 718 | continue; |
| 621 | kfree(rn.name); | 719 | candidate = ptr->aggregated_name; |
| 622 | need_kfree = false; | ||
| 623 | /* This is OK because it is read only. */ | ||
| 624 | rn = *ptr->aggregated_name; | ||
| 625 | break; | 720 | break; |
| 626 | } | 721 | } |
| 627 | } | 722 | } |
| 628 | 723 | ||
| 629 | /* Check execute permission. */ | 724 | /* Check execute permission. */ |
| 630 | retval = tomoyo_path_permission(&ee->r, TOMOYO_TYPE_EXECUTE, &rn); | 725 | retval = tomoyo_execute_permission(&ee->r, candidate); |
| 631 | if (retval == TOMOYO_RETRY_REQUEST) | 726 | if (retval == TOMOYO_RETRY_REQUEST) |
| 632 | goto retry; | 727 | goto retry; |
| 633 | if (retval < 0) | 728 | if (retval < 0) |
| @@ -638,20 +733,51 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) | |||
| 638 | * wildcard) rather than the pathname passed to execve() | 733 | * wildcard) rather than the pathname passed to execve() |
| 639 | * (which never contains wildcard). | 734 | * (which never contains wildcard). |
| 640 | */ | 735 | */ |
| 641 | if (ee->r.param.path.matched_path) { | 736 | if (ee->r.param.path.matched_path) |
| 642 | if (need_kfree) | 737 | candidate = ee->r.param.path.matched_path; |
| 643 | kfree(rn.name); | ||
| 644 | need_kfree = false; | ||
| 645 | /* This is OK because it is read only. */ | ||
| 646 | rn = *ee->r.param.path.matched_path; | ||
| 647 | } | ||
| 648 | 738 | ||
| 649 | /* Calculate domain to transit to. */ | 739 | /* |
| 740 | * Check for domain transition preference if "file execute" matched. | ||
| 741 | * If preference is given, make do_execve() fail if domain transition | ||
| 742 | * has failed, for domain transition preference should be used with | ||
| 743 | * destination domain defined. | ||
| 744 | */ | ||
| 745 | if (ee->transition) { | ||
| 746 | const char *domainname = ee->transition->name; | ||
| 747 | reject_on_transition_failure = true; | ||
| 748 | if (!strcmp(domainname, "keep")) | ||
| 749 | goto force_keep_domain; | ||
| 750 | if (!strcmp(domainname, "child")) | ||
| 751 | goto force_child_domain; | ||
| 752 | if (!strcmp(domainname, "reset")) | ||
| 753 | goto force_reset_domain; | ||
| 754 | if (!strcmp(domainname, "initialize")) | ||
| 755 | goto force_initialize_domain; | ||
| 756 | if (!strcmp(domainname, "parent")) { | ||
| 757 | char *cp; | ||
| 758 | strncpy(ee->tmp, old_domain->domainname->name, | ||
| 759 | TOMOYO_EXEC_TMPSIZE - 1); | ||
| 760 | cp = strrchr(ee->tmp, ' '); | ||
| 761 | if (cp) | ||
| 762 | *cp = '\0'; | ||
| 763 | } else if (*domainname == '<') | ||
| 764 | strncpy(ee->tmp, domainname, TOMOYO_EXEC_TMPSIZE - 1); | ||
| 765 | else | ||
| 766 | snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", | ||
| 767 | old_domain->domainname->name, domainname); | ||
| 768 | goto force_jump_domain; | ||
| 769 | } | ||
| 770 | /* | ||
| 771 | * No domain transition preference specified. | ||
| 772 | * Calculate domain to transit to. | ||
| 773 | */ | ||
| 650 | switch (tomoyo_transition_type(old_domain->ns, old_domain->domainname, | 774 | switch (tomoyo_transition_type(old_domain->ns, old_domain->domainname, |
| 651 | &rn)) { | 775 | candidate)) { |
| 652 | case TOMOYO_TRANSITION_CONTROL_RESET: | 776 | case TOMOYO_TRANSITION_CONTROL_RESET: |
| 777 | force_reset_domain: | ||
| 653 | /* Transit to the root of specified namespace. */ | 778 | /* Transit to the root of specified namespace. */ |
| 654 | snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>", rn.name); | 779 | snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>", |
| 780 | candidate->name); | ||
| 655 | /* | 781 | /* |
| 656 | * Make do_execve() fail if domain transition across namespaces | 782 | * Make do_execve() fail if domain transition across namespaces |
| 657 | * has failed. | 783 | * has failed. |
| @@ -659,11 +785,13 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) | |||
| 659 | reject_on_transition_failure = true; | 785 | reject_on_transition_failure = true; |
| 660 | break; | 786 | break; |
| 661 | case TOMOYO_TRANSITION_CONTROL_INITIALIZE: | 787 | case TOMOYO_TRANSITION_CONTROL_INITIALIZE: |
| 788 | force_initialize_domain: | ||
| 662 | /* Transit to the child of current namespace's root. */ | 789 | /* Transit to the child of current namespace's root. */ |
| 663 | snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", | 790 | snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", |
| 664 | old_domain->ns->name, rn.name); | 791 | old_domain->ns->name, candidate->name); |
| 665 | break; | 792 | break; |
| 666 | case TOMOYO_TRANSITION_CONTROL_KEEP: | 793 | case TOMOYO_TRANSITION_CONTROL_KEEP: |
| 794 | force_keep_domain: | ||
| 667 | /* Keep current domain. */ | 795 | /* Keep current domain. */ |
| 668 | domain = old_domain; | 796 | domain = old_domain; |
| 669 | break; | 797 | break; |
| @@ -677,13 +805,15 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) | |||
| 677 | * before /sbin/init. | 805 | * before /sbin/init. |
| 678 | */ | 806 | */ |
| 679 | domain = old_domain; | 807 | domain = old_domain; |
| 680 | } else { | 808 | break; |
| 681 | /* Normal domain transition. */ | ||
| 682 | snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", | ||
| 683 | old_domain->domainname->name, rn.name); | ||
| 684 | } | 809 | } |
| 810 | force_child_domain: | ||
| 811 | /* Normal domain transition. */ | ||
| 812 | snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", | ||
| 813 | old_domain->domainname->name, candidate->name); | ||
| 685 | break; | 814 | break; |
| 686 | } | 815 | } |
| 816 | force_jump_domain: | ||
| 687 | if (!domain) | 817 | if (!domain) |
| 688 | domain = tomoyo_assign_domain(ee->tmp, true); | 818 | domain = tomoyo_assign_domain(ee->tmp, true); |
| 689 | if (domain) | 819 | if (domain) |
| @@ -711,8 +841,11 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) | |||
| 711 | /* Update reference count on "struct tomoyo_domain_info". */ | 841 | /* Update reference count on "struct tomoyo_domain_info". */ |
| 712 | atomic_inc(&domain->users); | 842 | atomic_inc(&domain->users); |
| 713 | bprm->cred->security = domain; | 843 | bprm->cred->security = domain; |
| 714 | if (need_kfree) | 844 | kfree(exename.name); |
| 715 | kfree(rn.name); | 845 | if (!retval) { |
| 846 | ee->r.domain = domain; | ||
| 847 | retval = tomoyo_environ(ee); | ||
| 848 | } | ||
| 716 | kfree(ee->tmp); | 849 | kfree(ee->tmp); |
| 717 | kfree(ee->dump.data); | 850 | kfree(ee->dump.data); |
| 718 | kfree(ee); | 851 | kfree(ee); |
| @@ -732,7 +865,8 @@ bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos, | |||
| 732 | struct tomoyo_page_dump *dump) | 865 | struct tomoyo_page_dump *dump) |
| 733 | { | 866 | { |
| 734 | struct page *page; | 867 | struct page *page; |
| 735 | /* dump->data is released by tomoyo_finish_execve(). */ | 868 | |
| 869 | /* dump->data is released by tomoyo_find_next_domain(). */ | ||
| 736 | if (!dump->data) { | 870 | if (!dump->data) { |
| 737 | dump->data = kzalloc(PAGE_SIZE, GFP_NOFS); | 871 | dump->data = kzalloc(PAGE_SIZE, GFP_NOFS); |
| 738 | if (!dump->data) | 872 | if (!dump->data) |
| @@ -753,6 +887,7 @@ bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos, | |||
| 753 | * So do I. | 887 | * So do I. |
| 754 | */ | 888 | */ |
| 755 | char *kaddr = kmap_atomic(page, KM_USER0); | 889 | char *kaddr = kmap_atomic(page, KM_USER0); |
| 890 | |||
| 756 | dump->page = page; | 891 | dump->page = page; |
| 757 | memcpy(dump->data + offset, kaddr + offset, | 892 | memcpy(dump->data + offset, kaddr + offset, |
| 758 | PAGE_SIZE - offset); | 893 | PAGE_SIZE - offset); |
diff --git a/security/tomoyo/environ.c b/security/tomoyo/environ.c new file mode 100644 index 000000000000..ad4c6e18a437 --- /dev/null +++ b/security/tomoyo/environ.c | |||
| @@ -0,0 +1,122 @@ | |||
| 1 | /* | ||
| 2 | * security/tomoyo/environ.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005-2011 NTT DATA CORPORATION | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include "common.h" | ||
| 8 | |||
| 9 | /** | ||
| 10 | * tomoyo_check_env_acl - Check permission for environment variable's name. | ||
| 11 | * | ||
| 12 | * @r: Pointer to "struct tomoyo_request_info". | ||
| 13 | * @ptr: Pointer to "struct tomoyo_acl_info". | ||
| 14 | * | ||
| 15 | * Returns true if granted, false otherwise. | ||
| 16 | */ | ||
| 17 | static bool tomoyo_check_env_acl(struct tomoyo_request_info *r, | ||
| 18 | const struct tomoyo_acl_info *ptr) | ||
| 19 | { | ||
| 20 | const struct tomoyo_env_acl *acl = | ||
| 21 | container_of(ptr, typeof(*acl), head); | ||
| 22 | |||
| 23 | return tomoyo_path_matches_pattern(r->param.environ.name, acl->env); | ||
| 24 | } | ||
| 25 | |||
| 26 | /** | ||
| 27 | * tomoyo_audit_env_log - Audit environment variable name log. | ||
| 28 | * | ||
| 29 | * @r: Pointer to "struct tomoyo_request_info". | ||
| 30 | * | ||
| 31 | * Returns 0 on success, negative value otherwise. | ||
| 32 | */ | ||
| 33 | static int tomoyo_audit_env_log(struct tomoyo_request_info *r) | ||
| 34 | { | ||
| 35 | return tomoyo_supervisor(r, "misc env %s\n", | ||
| 36 | r->param.environ.name->name); | ||
| 37 | } | ||
| 38 | |||
| 39 | /** | ||
| 40 | * tomoyo_env_perm - Check permission for environment variable's name. | ||
| 41 | * | ||
| 42 | * @r: Pointer to "struct tomoyo_request_info". | ||
| 43 | * @env: The name of environment variable. | ||
| 44 | * | ||
| 45 | * Returns 0 on success, negative value otherwise. | ||
| 46 | * | ||
| 47 | * Caller holds tomoyo_read_lock(). | ||
| 48 | */ | ||
| 49 | int tomoyo_env_perm(struct tomoyo_request_info *r, const char *env) | ||
| 50 | { | ||
| 51 | struct tomoyo_path_info environ; | ||
| 52 | int error; | ||
| 53 | |||
| 54 | if (!env || !*env) | ||
| 55 | return 0; | ||
| 56 | environ.name = env; | ||
| 57 | tomoyo_fill_path_info(&environ); | ||
| 58 | r->param_type = TOMOYO_TYPE_ENV_ACL; | ||
| 59 | r->param.environ.name = &environ; | ||
| 60 | do { | ||
| 61 | tomoyo_check_acl(r, tomoyo_check_env_acl); | ||
| 62 | error = tomoyo_audit_env_log(r); | ||
| 63 | } while (error == TOMOYO_RETRY_REQUEST); | ||
| 64 | return error; | ||
| 65 | } | ||
| 66 | |||
| 67 | /** | ||
| 68 | * tomoyo_same_env_acl - Check for duplicated "struct tomoyo_env_acl" entry. | ||
| 69 | * | ||
| 70 | * @a: Pointer to "struct tomoyo_acl_info". | ||
| 71 | * @b: Pointer to "struct tomoyo_acl_info". | ||
| 72 | * | ||
| 73 | * Returns true if @a == @b, false otherwise. | ||
| 74 | */ | ||
| 75 | static bool tomoyo_same_env_acl(const struct tomoyo_acl_info *a, | ||
| 76 | const struct tomoyo_acl_info *b) | ||
| 77 | { | ||
| 78 | const struct tomoyo_env_acl *p1 = container_of(a, typeof(*p1), head); | ||
| 79 | const struct tomoyo_env_acl *p2 = container_of(b, typeof(*p2), head); | ||
| 80 | |||
| 81 | return p1->env == p2->env; | ||
| 82 | } | ||
| 83 | |||
| 84 | /** | ||
| 85 | * tomoyo_write_env - Write "struct tomoyo_env_acl" list. | ||
| 86 | * | ||
| 87 | * @param: Pointer to "struct tomoyo_acl_param". | ||
| 88 | * | ||
| 89 | * Returns 0 on success, negative value otherwise. | ||
| 90 | * | ||
| 91 | * Caller holds tomoyo_read_lock(). | ||
| 92 | */ | ||
| 93 | static int tomoyo_write_env(struct tomoyo_acl_param *param) | ||
| 94 | { | ||
| 95 | struct tomoyo_env_acl e = { .head.type = TOMOYO_TYPE_ENV_ACL }; | ||
| 96 | int error = -ENOMEM; | ||
| 97 | const char *data = tomoyo_read_token(param); | ||
| 98 | |||
| 99 | if (!tomoyo_correct_word(data) || strchr(data, '=')) | ||
| 100 | return -EINVAL; | ||
| 101 | e.env = tomoyo_get_name(data); | ||
| 102 | if (!e.env) | ||
| 103 | return error; | ||
| 104 | error = tomoyo_update_domain(&e.head, sizeof(e), param, | ||
| 105 | tomoyo_same_env_acl, NULL); | ||
| 106 | tomoyo_put_name(e.env); | ||
| 107 | return error; | ||
| 108 | } | ||
| 109 | |||
| 110 | /** | ||
| 111 | * tomoyo_write_misc - Update environment variable list. | ||
| 112 | * | ||
| 113 | * @param: Pointer to "struct tomoyo_acl_param". | ||
| 114 | * | ||
| 115 | * Returns 0 on success, negative value otherwise. | ||
| 116 | */ | ||
| 117 | int tomoyo_write_misc(struct tomoyo_acl_param *param) | ||
| 118 | { | ||
| 119 | if (tomoyo_str_starts(¶m->data, "env ")) | ||
| 120 | return tomoyo_write_env(param); | ||
| 121 | return -EINVAL; | ||
| 122 | } | ||
diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 743c35f5084a..400390790745 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c | |||
| @@ -555,8 +555,8 @@ static int tomoyo_update_path2_acl(const u8 perm, | |||
| 555 | * | 555 | * |
| 556 | * Caller holds tomoyo_read_lock(). | 556 | * Caller holds tomoyo_read_lock(). |
| 557 | */ | 557 | */ |
| 558 | int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, | 558 | static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, |
| 559 | const struct tomoyo_path_info *filename) | 559 | const struct tomoyo_path_info *filename) |
| 560 | { | 560 | { |
| 561 | int error; | 561 | int error; |
| 562 | 562 | ||
| @@ -570,16 +570,42 @@ int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, | |||
| 570 | do { | 570 | do { |
| 571 | tomoyo_check_acl(r, tomoyo_check_path_acl); | 571 | tomoyo_check_acl(r, tomoyo_check_path_acl); |
| 572 | error = tomoyo_audit_path_log(r); | 572 | error = tomoyo_audit_path_log(r); |
| 573 | /* | 573 | } while (error == TOMOYO_RETRY_REQUEST); |
| 574 | * Do not retry for execute request, for alias may have | ||
| 575 | * changed. | ||
| 576 | */ | ||
| 577 | } while (error == TOMOYO_RETRY_REQUEST && | ||
| 578 | operation != TOMOYO_TYPE_EXECUTE); | ||
| 579 | return error; | 574 | return error; |
| 580 | } | 575 | } |
| 581 | 576 | ||
| 582 | /** | 577 | /** |
| 578 | * tomoyo_execute_permission - Check permission for execute operation. | ||
| 579 | * | ||
| 580 | * @r: Pointer to "struct tomoyo_request_info". | ||
| 581 | * @filename: Filename to check. | ||
| 582 | * | ||
| 583 | * Returns 0 on success, negative value otherwise. | ||
| 584 | * | ||
| 585 | * Caller holds tomoyo_read_lock(). | ||
| 586 | */ | ||
| 587 | int tomoyo_execute_permission(struct tomoyo_request_info *r, | ||
| 588 | const struct tomoyo_path_info *filename) | ||
| 589 | { | ||
| 590 | /* | ||
| 591 | * Unlike other permission checks, this check is done regardless of | ||
| 592 | * profile mode settings in order to check for domain transition | ||
| 593 | * preference. | ||
| 594 | */ | ||
| 595 | r->type = TOMOYO_MAC_FILE_EXECUTE; | ||
| 596 | r->mode = tomoyo_get_mode(r->domain->ns, r->profile, r->type); | ||
| 597 | r->param_type = TOMOYO_TYPE_PATH_ACL; | ||
| 598 | r->param.path.filename = filename; | ||
| 599 | r->param.path.operation = TOMOYO_TYPE_EXECUTE; | ||
| 600 | tomoyo_check_acl(r, tomoyo_check_path_acl); | ||
| 601 | r->ee->transition = r->matched_acl && r->matched_acl->cond ? | ||
| 602 | r->matched_acl->cond->transit : NULL; | ||
| 603 | if (r->mode != TOMOYO_CONFIG_DISABLED) | ||
| 604 | return tomoyo_audit_path_log(r); | ||
| 605 | return 0; | ||
| 606 | } | ||
| 607 | |||
| 608 | /** | ||
| 583 | * tomoyo_same_path_number_acl - Check for duplicated "struct tomoyo_path_number_acl" entry. | 609 | * tomoyo_same_path_number_acl - Check for duplicated "struct tomoyo_path_number_acl" entry. |
| 584 | * | 610 | * |
| 585 | * @a: Pointer to "struct tomoyo_acl_info". | 611 | * @a: Pointer to "struct tomoyo_acl_info". |
diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index ae135fbbbe95..986a6a756868 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c | |||
| @@ -8,36 +8,26 @@ | |||
| 8 | #include <linux/kthread.h> | 8 | #include <linux/kthread.h> |
| 9 | #include <linux/slab.h> | 9 | #include <linux/slab.h> |
| 10 | 10 | ||
| 11 | /** | ||
| 12 | * tomoyo_memory_free - Free memory for elements. | ||
| 13 | * | ||
| 14 | * @ptr: Pointer to allocated memory. | ||
| 15 | * | ||
| 16 | * Returns nothing. | ||
| 17 | * | ||
| 18 | * Caller holds tomoyo_policy_lock mutex. | ||
| 19 | */ | ||
| 20 | static inline void tomoyo_memory_free(void *ptr) | ||
| 21 | { | ||
| 22 | tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= ksize(ptr); | ||
| 23 | kfree(ptr); | ||
| 24 | } | ||
| 25 | |||
| 11 | /* The list for "struct tomoyo_io_buffer". */ | 26 | /* The list for "struct tomoyo_io_buffer". */ |
| 12 | static LIST_HEAD(tomoyo_io_buffer_list); | 27 | static LIST_HEAD(tomoyo_io_buffer_list); |
| 13 | /* Lock for protecting tomoyo_io_buffer_list. */ | 28 | /* Lock for protecting tomoyo_io_buffer_list. */ |
| 14 | static DEFINE_SPINLOCK(tomoyo_io_buffer_list_lock); | 29 | static DEFINE_SPINLOCK(tomoyo_io_buffer_list_lock); |
| 15 | 30 | ||
| 16 | /* Size of an element. */ | ||
| 17 | static const u8 tomoyo_element_size[TOMOYO_MAX_POLICY] = { | ||
| 18 | [TOMOYO_ID_GROUP] = sizeof(struct tomoyo_group), | ||
| 19 | [TOMOYO_ID_PATH_GROUP] = sizeof(struct tomoyo_path_group), | ||
| 20 | [TOMOYO_ID_NUMBER_GROUP] = sizeof(struct tomoyo_number_group), | ||
| 21 | [TOMOYO_ID_AGGREGATOR] = sizeof(struct tomoyo_aggregator), | ||
| 22 | [TOMOYO_ID_TRANSITION_CONTROL] = | ||
| 23 | sizeof(struct tomoyo_transition_control), | ||
| 24 | [TOMOYO_ID_MANAGER] = sizeof(struct tomoyo_manager), | ||
| 25 | /* [TOMOYO_ID_CONDITION] = "struct tomoyo_condition"->size, */ | ||
| 26 | /* [TOMOYO_ID_NAME] = "struct tomoyo_name"->size, */ | ||
| 27 | /* [TOMOYO_ID_ACL] = | ||
| 28 | tomoyo_acl_size["struct tomoyo_acl_info"->type], */ | ||
| 29 | [TOMOYO_ID_DOMAIN] = sizeof(struct tomoyo_domain_info), | ||
| 30 | }; | ||
| 31 | |||
| 32 | /* Size of a domain ACL element. */ | ||
| 33 | static const u8 tomoyo_acl_size[] = { | ||
| 34 | [TOMOYO_TYPE_PATH_ACL] = sizeof(struct tomoyo_path_acl), | ||
| 35 | [TOMOYO_TYPE_PATH2_ACL] = sizeof(struct tomoyo_path2_acl), | ||
| 36 | [TOMOYO_TYPE_PATH_NUMBER_ACL] = sizeof(struct tomoyo_path_number_acl), | ||
| 37 | [TOMOYO_TYPE_MKDEV_ACL] = sizeof(struct tomoyo_mkdev_acl), | ||
| 38 | [TOMOYO_TYPE_MOUNT_ACL] = sizeof(struct tomoyo_mount_acl), | ||
| 39 | }; | ||
| 40 | |||
| 41 | /** | 31 | /** |
| 42 | * tomoyo_struct_used_by_io_buffer - Check whether the list element is used by /sys/kernel/security/tomoyo/ users or not. | 32 | * tomoyo_struct_used_by_io_buffer - Check whether the list element is used by /sys/kernel/security/tomoyo/ users or not. |
| 43 | * | 33 | * |
| @@ -55,15 +45,11 @@ static bool tomoyo_struct_used_by_io_buffer(const struct list_head *element) | |||
| 55 | list_for_each_entry(head, &tomoyo_io_buffer_list, list) { | 45 | list_for_each_entry(head, &tomoyo_io_buffer_list, list) { |
| 56 | head->users++; | 46 | head->users++; |
| 57 | spin_unlock(&tomoyo_io_buffer_list_lock); | 47 | spin_unlock(&tomoyo_io_buffer_list_lock); |
| 58 | if (mutex_lock_interruptible(&head->io_sem)) { | 48 | mutex_lock(&head->io_sem); |
| 59 | in_use = true; | ||
| 60 | goto out; | ||
| 61 | } | ||
| 62 | if (head->r.domain == element || head->r.group == element || | 49 | if (head->r.domain == element || head->r.group == element || |
| 63 | head->r.acl == element || &head->w.domain->list == element) | 50 | head->r.acl == element || &head->w.domain->list == element) |
| 64 | in_use = true; | 51 | in_use = true; |
| 65 | mutex_unlock(&head->io_sem); | 52 | mutex_unlock(&head->io_sem); |
| 66 | out: | ||
| 67 | spin_lock(&tomoyo_io_buffer_list_lock); | 53 | spin_lock(&tomoyo_io_buffer_list_lock); |
| 68 | head->users--; | 54 | head->users--; |
| 69 | if (in_use) | 55 | if (in_use) |
| @@ -77,15 +63,14 @@ out: | |||
| 77 | * tomoyo_name_used_by_io_buffer - Check whether the string is used by /sys/kernel/security/tomoyo/ users or not. | 63 | * tomoyo_name_used_by_io_buffer - Check whether the string is used by /sys/kernel/security/tomoyo/ users or not. |
| 78 | * | 64 | * |
| 79 | * @string: String to check. | 65 | * @string: String to check. |
| 80 | * @size: Memory allocated for @string . | ||
| 81 | * | 66 | * |
| 82 | * Returns true if @string is used by /sys/kernel/security/tomoyo/ users, | 67 | * Returns true if @string is used by /sys/kernel/security/tomoyo/ users, |
| 83 | * false otherwise. | 68 | * false otherwise. |
| 84 | */ | 69 | */ |
| 85 | static bool tomoyo_name_used_by_io_buffer(const char *string, | 70 | static bool tomoyo_name_used_by_io_buffer(const char *string) |
| 86 | const size_t size) | ||
| 87 | { | 71 | { |
| 88 | struct tomoyo_io_buffer *head; | 72 | struct tomoyo_io_buffer *head; |
| 73 | const size_t size = strlen(string) + 1; | ||
| 89 | bool in_use = false; | 74 | bool in_use = false; |
| 90 | 75 | ||
| 91 | spin_lock(&tomoyo_io_buffer_list_lock); | 76 | spin_lock(&tomoyo_io_buffer_list_lock); |
| @@ -93,10 +78,7 @@ static bool tomoyo_name_used_by_io_buffer(const char *string, | |||
| 93 | int i; | 78 | int i; |
| 94 | head->users++; | 79 | head->users++; |
| 95 | spin_unlock(&tomoyo_io_buffer_list_lock); | 80 | spin_unlock(&tomoyo_io_buffer_list_lock); |
| 96 | if (mutex_lock_interruptible(&head->io_sem)) { | 81 | mutex_lock(&head->io_sem); |
| 97 | in_use = true; | ||
| 98 | goto out; | ||
| 99 | } | ||
| 100 | for (i = 0; i < TOMOYO_MAX_IO_READ_QUEUE; i++) { | 82 | for (i = 0; i < TOMOYO_MAX_IO_READ_QUEUE; i++) { |
| 101 | const char *w = head->r.w[i]; | 83 | const char *w = head->r.w[i]; |
| 102 | if (w < string || w > string + size) | 84 | if (w < string || w > string + size) |
| @@ -105,7 +87,6 @@ static bool tomoyo_name_used_by_io_buffer(const char *string, | |||
| 105 | break; | 87 | break; |
| 106 | } | 88 | } |
| 107 | mutex_unlock(&head->io_sem); | 89 | mutex_unlock(&head->io_sem); |
| 108 | out: | ||
| 109 | spin_lock(&tomoyo_io_buffer_list_lock); | 90 | spin_lock(&tomoyo_io_buffer_list_lock); |
| 110 | head->users--; | 91 | head->users--; |
| 111 | if (in_use) | 92 | if (in_use) |
| @@ -115,84 +96,6 @@ out: | |||
| 115 | return in_use; | 96 | return in_use; |
| 116 | } | 97 | } |
| 117 | 98 | ||
| 118 | /* Structure for garbage collection. */ | ||
| 119 | struct tomoyo_gc { | ||
| 120 | struct list_head list; | ||
| 121 | enum tomoyo_policy_id type; | ||
| 122 | size_t size; | ||
| 123 | struct list_head *element; | ||
| 124 | }; | ||
| 125 | /* List of entries to be deleted. */ | ||
| 126 | static LIST_HEAD(tomoyo_gc_list); | ||
| 127 | /* Length of tomoyo_gc_list. */ | ||
| 128 | static int tomoyo_gc_list_len; | ||
| 129 | |||
| 130 | /** | ||
| 131 | * tomoyo_add_to_gc - Add an entry to to be deleted list. | ||
| 132 | * | ||
| 133 | * @type: One of values in "enum tomoyo_policy_id". | ||
| 134 | * @element: Pointer to "struct list_head". | ||
| 135 | * | ||
| 136 | * Returns true on success, false otherwise. | ||
| 137 | * | ||
| 138 | * Caller holds tomoyo_policy_lock mutex. | ||
| 139 | * | ||
| 140 | * Adding an entry needs kmalloc(). Thus, if we try to add thousands of | ||
| 141 | * entries at once, it will take too long time. Thus, do not add more than 128 | ||
| 142 | * entries per a scan. But to be able to handle worst case where all entries | ||
| 143 | * are in-use, we accept one more entry per a scan. | ||
| 144 | * | ||
| 145 | * If we use singly linked list using "struct list_head"->prev (which is | ||
| 146 | * LIST_POISON2), we can avoid kmalloc(). | ||
| 147 | */ | ||
| 148 | static bool tomoyo_add_to_gc(const int type, struct list_head *element) | ||
| 149 | { | ||
| 150 | struct tomoyo_gc *entry = kzalloc(sizeof(*entry), GFP_ATOMIC); | ||
| 151 | if (!entry) | ||
| 152 | return false; | ||
| 153 | entry->type = type; | ||
| 154 | if (type == TOMOYO_ID_ACL) | ||
| 155 | entry->size = tomoyo_acl_size[ | ||
| 156 | container_of(element, | ||
| 157 | typeof(struct tomoyo_acl_info), | ||
| 158 | list)->type]; | ||
| 159 | else if (type == TOMOYO_ID_NAME) | ||
| 160 | entry->size = strlen(container_of(element, | ||
| 161 | typeof(struct tomoyo_name), | ||
| 162 | head.list)->entry.name) + 1; | ||
| 163 | else if (type == TOMOYO_ID_CONDITION) | ||
| 164 | entry->size = | ||
| 165 | container_of(element, typeof(struct tomoyo_condition), | ||
| 166 | head.list)->size; | ||
| 167 | else | ||
| 168 | entry->size = tomoyo_element_size[type]; | ||
| 169 | entry->element = element; | ||
| 170 | list_add(&entry->list, &tomoyo_gc_list); | ||
| 171 | list_del_rcu(element); | ||
| 172 | return tomoyo_gc_list_len++ < 128; | ||
| 173 | } | ||
| 174 | |||
| 175 | /** | ||
| 176 | * tomoyo_element_linked_by_gc - Validate next element of an entry. | ||
| 177 | * | ||
| 178 | * @element: Pointer to an element. | ||
| 179 | * @size: Size of @element in byte. | ||
| 180 | * | ||
| 181 | * Returns true if @element is linked by other elements in the garbage | ||
| 182 | * collector's queue, false otherwise. | ||
| 183 | */ | ||
| 184 | static bool tomoyo_element_linked_by_gc(const u8 *element, const size_t size) | ||
| 185 | { | ||
| 186 | struct tomoyo_gc *p; | ||
| 187 | list_for_each_entry(p, &tomoyo_gc_list, list) { | ||
| 188 | const u8 *ptr = (const u8 *) p->element->next; | ||
| 189 | if (ptr < element || element + size < ptr) | ||
| 190 | continue; | ||
| 191 | return true; | ||
| 192 | } | ||
| 193 | return false; | ||
| 194 | } | ||
| 195 | |||
| 196 | /** | 99 | /** |
| 197 | * tomoyo_del_transition_control - Delete members in "struct tomoyo_transition_control". | 100 | * tomoyo_del_transition_control - Delete members in "struct tomoyo_transition_control". |
| 198 | * | 101 | * |
| @@ -200,7 +103,7 @@ static bool tomoyo_element_linked_by_gc(const u8 *element, const size_t size) | |||
| 200 | * | 103 | * |
| 201 | * Returns nothing. | 104 | * Returns nothing. |
| 202 | */ | 105 | */ |
| 203 | static void tomoyo_del_transition_control(struct list_head *element) | 106 | static inline void tomoyo_del_transition_control(struct list_head *element) |
| 204 | { | 107 | { |
| 205 | struct tomoyo_transition_control *ptr = | 108 | struct tomoyo_transition_control *ptr = |
| 206 | container_of(element, typeof(*ptr), head.list); | 109 | container_of(element, typeof(*ptr), head.list); |
| @@ -215,7 +118,7 @@ static void tomoyo_del_transition_control(struct list_head *element) | |||
| 215 | * | 118 | * |
| 216 | * Returns nothing. | 119 | * Returns nothing. |
| 217 | */ | 120 | */ |
| 218 | static void tomoyo_del_aggregator(struct list_head *element) | 121 | static inline void tomoyo_del_aggregator(struct list_head *element) |
| 219 | { | 122 | { |
| 220 | struct tomoyo_aggregator *ptr = | 123 | struct tomoyo_aggregator *ptr = |
| 221 | container_of(element, typeof(*ptr), head.list); | 124 | container_of(element, typeof(*ptr), head.list); |
| @@ -230,7 +133,7 @@ static void tomoyo_del_aggregator(struct list_head *element) | |||
| 230 | * | 133 | * |
| 231 | * Returns nothing. | 134 | * Returns nothing. |
| 232 | */ | 135 | */ |
| 233 | static void tomoyo_del_manager(struct list_head *element) | 136 | static inline void tomoyo_del_manager(struct list_head *element) |
| 234 | { | 137 | { |
| 235 | struct tomoyo_manager *ptr = | 138 | struct tomoyo_manager *ptr = |
| 236 | container_of(element, typeof(*ptr), head.list); | 139 | container_of(element, typeof(*ptr), head.list); |
| @@ -293,6 +196,38 @@ static void tomoyo_del_acl(struct list_head *element) | |||
| 293 | tomoyo_put_number_union(&entry->flags); | 196 | tomoyo_put_number_union(&entry->flags); |
| 294 | } | 197 | } |
| 295 | break; | 198 | break; |
| 199 | case TOMOYO_TYPE_ENV_ACL: | ||
| 200 | { | ||
| 201 | struct tomoyo_env_acl *entry = | ||
| 202 | container_of(acl, typeof(*entry), head); | ||
| 203 | |||
| 204 | tomoyo_put_name(entry->env); | ||
| 205 | } | ||
| 206 | break; | ||
| 207 | case TOMOYO_TYPE_INET_ACL: | ||
| 208 | { | ||
| 209 | struct tomoyo_inet_acl *entry = | ||
| 210 | container_of(acl, typeof(*entry), head); | ||
| 211 | |||
| 212 | tomoyo_put_group(entry->address.group); | ||
| 213 | tomoyo_put_number_union(&entry->port); | ||
| 214 | } | ||
| 215 | break; | ||
| 216 | case TOMOYO_TYPE_UNIX_ACL: | ||
| 217 | { | ||
| 218 | struct tomoyo_unix_acl *entry = | ||
| 219 | container_of(acl, typeof(*entry), head); | ||
| 220 | |||
| 221 | tomoyo_put_name_union(&entry->name); | ||
| 222 | } | ||
| 223 | break; | ||
| 224 | case TOMOYO_TYPE_MANUAL_TASK_ACL: | ||
| 225 | { | ||
| 226 | struct tomoyo_task_acl *entry = | ||
| 227 | container_of(acl, typeof(*entry), head); | ||
| 228 | tomoyo_put_name(entry->domainname); | ||
| 229 | } | ||
| 230 | break; | ||
| 296 | } | 231 | } |
| 297 | } | 232 | } |
| 298 | 233 | ||
| @@ -301,44 +236,26 @@ static void tomoyo_del_acl(struct list_head *element) | |||
| 301 | * | 236 | * |
| 302 | * @element: Pointer to "struct list_head". | 237 | * @element: Pointer to "struct list_head". |
| 303 | * | 238 | * |
| 304 | * Returns true if deleted, false otherwise. | 239 | * Returns nothing. |
| 240 | * | ||
| 241 | * Caller holds tomoyo_policy_lock mutex. | ||
| 305 | */ | 242 | */ |
| 306 | static bool tomoyo_del_domain(struct list_head *element) | 243 | static inline void tomoyo_del_domain(struct list_head *element) |
| 307 | { | 244 | { |
| 308 | struct tomoyo_domain_info *domain = | 245 | struct tomoyo_domain_info *domain = |
| 309 | container_of(element, typeof(*domain), list); | 246 | container_of(element, typeof(*domain), list); |
| 310 | struct tomoyo_acl_info *acl; | 247 | struct tomoyo_acl_info *acl; |
| 311 | struct tomoyo_acl_info *tmp; | 248 | struct tomoyo_acl_info *tmp; |
| 312 | /* | 249 | /* |
| 313 | * Since we don't protect whole execve() operation using SRCU, | 250 | * Since this domain is referenced from neither |
| 314 | * we need to recheck domain->users at this point. | 251 | * "struct tomoyo_io_buffer" nor "struct cred"->security, we can delete |
| 315 | * | 252 | * elements without checking for is_deleted flag. |
| 316 | * (1) Reader starts SRCU section upon execve(). | ||
| 317 | * (2) Reader traverses tomoyo_domain_list and finds this domain. | ||
| 318 | * (3) Writer marks this domain as deleted. | ||
| 319 | * (4) Garbage collector removes this domain from tomoyo_domain_list | ||
| 320 | * because this domain is marked as deleted and used by nobody. | ||
| 321 | * (5) Reader saves reference to this domain into | ||
| 322 | * "struct linux_binprm"->cred->security . | ||
| 323 | * (6) Reader finishes SRCU section, although execve() operation has | ||
| 324 | * not finished yet. | ||
| 325 | * (7) Garbage collector waits for SRCU synchronization. | ||
| 326 | * (8) Garbage collector kfree() this domain because this domain is | ||
| 327 | * used by nobody. | ||
| 328 | * (9) Reader finishes execve() operation and restores this domain from | ||
| 329 | * "struct linux_binprm"->cred->security. | ||
| 330 | * | ||
| 331 | * By updating domain->users at (5), we can solve this race problem | ||
| 332 | * by rechecking domain->users at (8). | ||
| 333 | */ | 253 | */ |
| 334 | if (atomic_read(&domain->users)) | ||
| 335 | return false; | ||
| 336 | list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) { | 254 | list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) { |
| 337 | tomoyo_del_acl(&acl->list); | 255 | tomoyo_del_acl(&acl->list); |
| 338 | tomoyo_memory_free(acl); | 256 | tomoyo_memory_free(acl); |
| 339 | } | 257 | } |
| 340 | tomoyo_put_name(domain->domainname); | 258 | tomoyo_put_name(domain->domainname); |
| 341 | return true; | ||
| 342 | } | 259 | } |
| 343 | 260 | ||
| 344 | /** | 261 | /** |
| @@ -387,10 +304,9 @@ void tomoyo_del_condition(struct list_head *element) | |||
| 387 | * | 304 | * |
| 388 | * Returns nothing. | 305 | * Returns nothing. |
| 389 | */ | 306 | */ |
| 390 | static void tomoyo_del_name(struct list_head *element) | 307 | static inline void tomoyo_del_name(struct list_head *element) |
| 391 | { | 308 | { |
| 392 | const struct tomoyo_name *ptr = | 309 | /* Nothing to do. */ |
| 393 | container_of(element, typeof(*ptr), head.list); | ||
| 394 | } | 310 | } |
| 395 | 311 | ||
| 396 | /** | 312 | /** |
| @@ -400,7 +316,7 @@ static void tomoyo_del_name(struct list_head *element) | |||
| 400 | * | 316 | * |
| 401 | * Returns nothing. | 317 | * Returns nothing. |
| 402 | */ | 318 | */ |
| 403 | static void tomoyo_del_path_group(struct list_head *element) | 319 | static inline void tomoyo_del_path_group(struct list_head *element) |
| 404 | { | 320 | { |
| 405 | struct tomoyo_path_group *member = | 321 | struct tomoyo_path_group *member = |
| 406 | container_of(element, typeof(*member), head.list); | 322 | container_of(element, typeof(*member), head.list); |
| @@ -414,7 +330,7 @@ static void tomoyo_del_path_group(struct list_head *element) | |||
| 414 | * | 330 | * |
| 415 | * Returns nothing. | 331 | * Returns nothing. |
| 416 | */ | 332 | */ |
| 417 | static void tomoyo_del_group(struct list_head *element) | 333 | static inline void tomoyo_del_group(struct list_head *element) |
| 418 | { | 334 | { |
| 419 | struct tomoyo_group *group = | 335 | struct tomoyo_group *group = |
| 420 | container_of(element, typeof(*group), head.list); | 336 | container_of(element, typeof(*group), head.list); |
| @@ -422,16 +338,128 @@ static void tomoyo_del_group(struct list_head *element) | |||
| 422 | } | 338 | } |
| 423 | 339 | ||
| 424 | /** | 340 | /** |
| 341 | * tomoyo_del_address_group - Delete members in "struct tomoyo_address_group". | ||
| 342 | * | ||
| 343 | * @element: Pointer to "struct list_head". | ||
| 344 | * | ||
| 345 | * Returns nothing. | ||
| 346 | */ | ||
| 347 | static inline void tomoyo_del_address_group(struct list_head *element) | ||
| 348 | { | ||
| 349 | /* Nothing to do. */ | ||
| 350 | } | ||
| 351 | |||
| 352 | /** | ||
| 425 | * tomoyo_del_number_group - Delete members in "struct tomoyo_number_group". | 353 | * tomoyo_del_number_group - Delete members in "struct tomoyo_number_group". |
| 426 | * | 354 | * |
| 427 | * @element: Pointer to "struct list_head". | 355 | * @element: Pointer to "struct list_head". |
| 428 | * | 356 | * |
| 429 | * Returns nothing. | 357 | * Returns nothing. |
| 430 | */ | 358 | */ |
| 431 | static void tomoyo_del_number_group(struct list_head *element) | 359 | static inline void tomoyo_del_number_group(struct list_head *element) |
| 432 | { | 360 | { |
| 433 | struct tomoyo_number_group *member = | 361 | /* Nothing to do. */ |
| 434 | container_of(element, typeof(*member), head.list); | 362 | } |
| 363 | |||
| 364 | /** | ||
| 365 | * tomoyo_try_to_gc - Try to kfree() an entry. | ||
| 366 | * | ||
| 367 | * @type: One of values in "enum tomoyo_policy_id". | ||
| 368 | * @element: Pointer to "struct list_head". | ||
| 369 | * | ||
| 370 | * Returns nothing. | ||
| 371 | * | ||
| 372 | * Caller holds tomoyo_policy_lock mutex. | ||
| 373 | */ | ||
| 374 | static void tomoyo_try_to_gc(const enum tomoyo_policy_id type, | ||
| 375 | struct list_head *element) | ||
| 376 | { | ||
| 377 | /* | ||
| 378 | * __list_del_entry() guarantees that the list element became no longer | ||
| 379 | * reachable from the list which the element was originally on (e.g. | ||
| 380 | * tomoyo_domain_list). Also, synchronize_srcu() guarantees that the | ||
| 381 | * list element became no longer referenced by syscall users. | ||
| 382 | */ | ||
| 383 | __list_del_entry(element); | ||
| 384 | mutex_unlock(&tomoyo_policy_lock); | ||
| 385 | synchronize_srcu(&tomoyo_ss); | ||
| 386 | /* | ||
| 387 | * However, there are two users which may still be using the list | ||
| 388 | * element. We need to defer until both users forget this element. | ||
| 389 | * | ||
| 390 | * Don't kfree() until "struct tomoyo_io_buffer"->r.{domain,group,acl} | ||
| 391 | * and "struct tomoyo_io_buffer"->w.domain forget this element. | ||
| 392 | */ | ||
| 393 | if (tomoyo_struct_used_by_io_buffer(element)) | ||
| 394 | goto reinject; | ||
| 395 | switch (type) { | ||
| 396 | case TOMOYO_ID_TRANSITION_CONTROL: | ||
| 397 | tomoyo_del_transition_control(element); | ||
| 398 | break; | ||
| 399 | case TOMOYO_ID_MANAGER: | ||
| 400 | tomoyo_del_manager(element); | ||
| 401 | break; | ||
| 402 | case TOMOYO_ID_AGGREGATOR: | ||
| 403 | tomoyo_del_aggregator(element); | ||
| 404 | break; | ||
| 405 | case TOMOYO_ID_GROUP: | ||
| 406 | tomoyo_del_group(element); | ||
| 407 | break; | ||
| 408 | case TOMOYO_ID_PATH_GROUP: | ||
| 409 | tomoyo_del_path_group(element); | ||
| 410 | break; | ||
| 411 | case TOMOYO_ID_ADDRESS_GROUP: | ||
| 412 | tomoyo_del_address_group(element); | ||
| 413 | break; | ||
| 414 | case TOMOYO_ID_NUMBER_GROUP: | ||
| 415 | tomoyo_del_number_group(element); | ||
| 416 | break; | ||
| 417 | case TOMOYO_ID_CONDITION: | ||
| 418 | tomoyo_del_condition(element); | ||
| 419 | break; | ||
| 420 | case TOMOYO_ID_NAME: | ||
| 421 | /* | ||
| 422 | * Don't kfree() until all "struct tomoyo_io_buffer"->r.w[] | ||
| 423 | * forget this element. | ||
| 424 | */ | ||
| 425 | if (tomoyo_name_used_by_io_buffer | ||
| 426 | (container_of(element, typeof(struct tomoyo_name), | ||
| 427 | head.list)->entry.name)) | ||
| 428 | goto reinject; | ||
| 429 | tomoyo_del_name(element); | ||
| 430 | break; | ||
| 431 | case TOMOYO_ID_ACL: | ||
| 432 | tomoyo_del_acl(element); | ||
| 433 | break; | ||
| 434 | case TOMOYO_ID_DOMAIN: | ||
| 435 | /* | ||
| 436 | * Don't kfree() until all "struct cred"->security forget this | ||
| 437 | * element. | ||
| 438 | */ | ||
| 439 | if (atomic_read(&container_of | ||
| 440 | (element, typeof(struct tomoyo_domain_info), | ||
| 441 | list)->users)) | ||
| 442 | goto reinject; | ||
| 443 | break; | ||
| 444 | case TOMOYO_MAX_POLICY: | ||
| 445 | break; | ||
| 446 | } | ||
| 447 | mutex_lock(&tomoyo_policy_lock); | ||
| 448 | if (type == TOMOYO_ID_DOMAIN) | ||
| 449 | tomoyo_del_domain(element); | ||
| 450 | tomoyo_memory_free(element); | ||
| 451 | return; | ||
| 452 | reinject: | ||
| 453 | /* | ||
| 454 | * We can safely reinject this element here bacause | ||
| 455 | * (1) Appending list elements and removing list elements are protected | ||
| 456 | * by tomoyo_policy_lock mutex. | ||
| 457 | * (2) Only this function removes list elements and this function is | ||
| 458 | * exclusively executed by tomoyo_gc_mutex mutex. | ||
| 459 | * are true. | ||
| 460 | */ | ||
| 461 | mutex_lock(&tomoyo_policy_lock); | ||
| 462 | list_add_rcu(element, element->prev); | ||
| 435 | } | 463 | } |
| 436 | 464 | ||
| 437 | /** | 465 | /** |
| @@ -440,19 +468,19 @@ static void tomoyo_del_number_group(struct list_head *element) | |||
| 440 | * @id: One of values in "enum tomoyo_policy_id". | 468 | * @id: One of values in "enum tomoyo_policy_id". |
| 441 | * @member_list: Pointer to "struct list_head". | 469 | * @member_list: Pointer to "struct list_head". |
| 442 | * | 470 | * |
| 443 | * Returns true if some elements are deleted, false otherwise. | 471 | * Returns nothing. |
| 444 | */ | 472 | */ |
| 445 | static bool tomoyo_collect_member(const enum tomoyo_policy_id id, | 473 | static void tomoyo_collect_member(const enum tomoyo_policy_id id, |
| 446 | struct list_head *member_list) | 474 | struct list_head *member_list) |
| 447 | { | 475 | { |
| 448 | struct tomoyo_acl_head *member; | 476 | struct tomoyo_acl_head *member; |
| 449 | list_for_each_entry(member, member_list, list) { | 477 | struct tomoyo_acl_head *tmp; |
| 478 | list_for_each_entry_safe(member, tmp, member_list, list) { | ||
| 450 | if (!member->is_deleted) | 479 | if (!member->is_deleted) |
| 451 | continue; | 480 | continue; |
| 452 | if (!tomoyo_add_to_gc(id, &member->list)) | 481 | member->is_deleted = TOMOYO_GC_IN_PROGRESS; |
| 453 | return false; | 482 | tomoyo_try_to_gc(id, &member->list); |
| 454 | } | 483 | } |
| 455 | return true; | ||
| 456 | } | 484 | } |
| 457 | 485 | ||
| 458 | /** | 486 | /** |
| @@ -460,22 +488,22 @@ static bool tomoyo_collect_member(const enum tomoyo_policy_id id, | |||
| 460 | * | 488 | * |
| 461 | * @list: Pointer to "struct list_head". | 489 | * @list: Pointer to "struct list_head". |
| 462 | * | 490 | * |
| 463 | * Returns true if some elements are deleted, false otherwise. | 491 | * Returns nothing. |
| 464 | */ | 492 | */ |
| 465 | static bool tomoyo_collect_acl(struct list_head *list) | 493 | static void tomoyo_collect_acl(struct list_head *list) |
| 466 | { | 494 | { |
| 467 | struct tomoyo_acl_info *acl; | 495 | struct tomoyo_acl_info *acl; |
| 468 | list_for_each_entry(acl, list, list) { | 496 | struct tomoyo_acl_info *tmp; |
| 497 | list_for_each_entry_safe(acl, tmp, list, list) { | ||
| 469 | if (!acl->is_deleted) | 498 | if (!acl->is_deleted) |
| 470 | continue; | 499 | continue; |
| 471 | if (!tomoyo_add_to_gc(TOMOYO_ID_ACL, &acl->list)) | 500 | acl->is_deleted = TOMOYO_GC_IN_PROGRESS; |
| 472 | return false; | 501 | tomoyo_try_to_gc(TOMOYO_ID_ACL, &acl->list); |
| 473 | } | 502 | } |
| 474 | return true; | ||
| 475 | } | 503 | } |
| 476 | 504 | ||
| 477 | /** | 505 | /** |
| 478 | * tomoyo_collect_entry - Scan lists for deleted elements. | 506 | * tomoyo_collect_entry - Try to kfree() deleted elements. |
| 479 | * | 507 | * |
| 480 | * Returns nothing. | 508 | * Returns nothing. |
| 481 | */ | 509 | */ |
| @@ -484,174 +512,82 @@ static void tomoyo_collect_entry(void) | |||
| 484 | int i; | 512 | int i; |
| 485 | enum tomoyo_policy_id id; | 513 | enum tomoyo_policy_id id; |
| 486 | struct tomoyo_policy_namespace *ns; | 514 | struct tomoyo_policy_namespace *ns; |
| 487 | int idx; | 515 | mutex_lock(&tomoyo_policy_lock); |
| 488 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | ||
| 489 | return; | ||
| 490 | idx = tomoyo_read_lock(); | ||
| 491 | { | 516 | { |
| 492 | struct tomoyo_domain_info *domain; | 517 | struct tomoyo_domain_info *domain; |
| 493 | list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { | 518 | struct tomoyo_domain_info *tmp; |
| 494 | if (!tomoyo_collect_acl(&domain->acl_info_list)) | 519 | list_for_each_entry_safe(domain, tmp, &tomoyo_domain_list, |
| 495 | goto unlock; | 520 | list) { |
| 521 | tomoyo_collect_acl(&domain->acl_info_list); | ||
| 496 | if (!domain->is_deleted || atomic_read(&domain->users)) | 522 | if (!domain->is_deleted || atomic_read(&domain->users)) |
| 497 | continue; | 523 | continue; |
| 498 | /* | 524 | tomoyo_try_to_gc(TOMOYO_ID_DOMAIN, &domain->list); |
| 499 | * Nobody is referring this domain. But somebody may | ||
| 500 | * refer this domain after successful execve(). | ||
| 501 | * We recheck domain->users after SRCU synchronization. | ||
| 502 | */ | ||
| 503 | if (!tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, &domain->list)) | ||
| 504 | goto unlock; | ||
| 505 | } | 525 | } |
| 506 | } | 526 | } |
| 507 | list_for_each_entry_rcu(ns, &tomoyo_namespace_list, namespace_list) { | 527 | list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) { |
| 508 | for (id = 0; id < TOMOYO_MAX_POLICY; id++) | 528 | for (id = 0; id < TOMOYO_MAX_POLICY; id++) |
| 509 | if (!tomoyo_collect_member(id, &ns->policy_list[id])) | 529 | tomoyo_collect_member(id, &ns->policy_list[id]); |
| 510 | goto unlock; | ||
| 511 | for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++) | 530 | for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++) |
| 512 | if (!tomoyo_collect_acl(&ns->acl_group[i])) | 531 | tomoyo_collect_acl(&ns->acl_group[i]); |
| 513 | goto unlock; | 532 | } |
| 533 | { | ||
| 534 | struct tomoyo_shared_acl_head *ptr; | ||
| 535 | struct tomoyo_shared_acl_head *tmp; | ||
| 536 | list_for_each_entry_safe(ptr, tmp, &tomoyo_condition_list, | ||
| 537 | list) { | ||
| 538 | if (atomic_read(&ptr->users) > 0) | ||
| 539 | continue; | ||
| 540 | atomic_set(&ptr->users, TOMOYO_GC_IN_PROGRESS); | ||
| 541 | tomoyo_try_to_gc(TOMOYO_ID_CONDITION, &ptr->list); | ||
| 542 | } | ||
| 543 | } | ||
| 544 | list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) { | ||
| 514 | for (i = 0; i < TOMOYO_MAX_GROUP; i++) { | 545 | for (i = 0; i < TOMOYO_MAX_GROUP; i++) { |
| 515 | struct list_head *list = &ns->group_list[i]; | 546 | struct list_head *list = &ns->group_list[i]; |
| 516 | struct tomoyo_group *group; | 547 | struct tomoyo_group *group; |
| 548 | struct tomoyo_group *tmp; | ||
| 517 | switch (i) { | 549 | switch (i) { |
| 518 | case 0: | 550 | case 0: |
| 519 | id = TOMOYO_ID_PATH_GROUP; | 551 | id = TOMOYO_ID_PATH_GROUP; |
| 520 | break; | 552 | break; |
| 521 | default: | 553 | case 1: |
| 522 | id = TOMOYO_ID_NUMBER_GROUP; | 554 | id = TOMOYO_ID_NUMBER_GROUP; |
| 523 | break; | 555 | break; |
| 556 | default: | ||
| 557 | id = TOMOYO_ID_ADDRESS_GROUP; | ||
| 558 | break; | ||
| 524 | } | 559 | } |
| 525 | list_for_each_entry(group, list, head.list) { | 560 | list_for_each_entry_safe(group, tmp, list, head.list) { |
| 526 | if (!tomoyo_collect_member | 561 | tomoyo_collect_member(id, &group->member_list); |
| 527 | (id, &group->member_list)) | ||
| 528 | goto unlock; | ||
| 529 | if (!list_empty(&group->member_list) || | 562 | if (!list_empty(&group->member_list) || |
| 530 | atomic_read(&group->head.users)) | 563 | atomic_read(&group->head.users) > 0) |
| 531 | continue; | 564 | continue; |
| 532 | if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP, | 565 | atomic_set(&group->head.users, |
| 533 | &group->head.list)) | 566 | TOMOYO_GC_IN_PROGRESS); |
| 534 | goto unlock; | 567 | tomoyo_try_to_gc(TOMOYO_ID_GROUP, |
| 568 | &group->head.list); | ||
| 535 | } | 569 | } |
| 536 | } | 570 | } |
| 537 | } | 571 | } |
| 538 | id = TOMOYO_ID_CONDITION; | 572 | for (i = 0; i < TOMOYO_MAX_HASH; i++) { |
| 539 | for (i = 0; i < TOMOYO_MAX_HASH + 1; i++) { | 573 | struct list_head *list = &tomoyo_name_list[i]; |
| 540 | struct list_head *list = !i ? | ||
| 541 | &tomoyo_condition_list : &tomoyo_name_list[i - 1]; | ||
| 542 | struct tomoyo_shared_acl_head *ptr; | 574 | struct tomoyo_shared_acl_head *ptr; |
| 543 | list_for_each_entry(ptr, list, list) { | 575 | struct tomoyo_shared_acl_head *tmp; |
| 544 | if (atomic_read(&ptr->users)) | 576 | list_for_each_entry_safe(ptr, tmp, list, list) { |
| 577 | if (atomic_read(&ptr->users) > 0) | ||
| 545 | continue; | 578 | continue; |
| 546 | if (!tomoyo_add_to_gc(id, &ptr->list)) | 579 | atomic_set(&ptr->users, TOMOYO_GC_IN_PROGRESS); |
| 547 | goto unlock; | 580 | tomoyo_try_to_gc(TOMOYO_ID_NAME, &ptr->list); |
| 548 | } | 581 | } |
| 549 | id = TOMOYO_ID_NAME; | ||
| 550 | } | 582 | } |
| 551 | unlock: | ||
| 552 | tomoyo_read_unlock(idx); | ||
| 553 | mutex_unlock(&tomoyo_policy_lock); | 583 | mutex_unlock(&tomoyo_policy_lock); |
| 554 | } | 584 | } |
| 555 | 585 | ||
| 556 | /** | 586 | /** |
| 557 | * tomoyo_kfree_entry - Delete entries in tomoyo_gc_list. | ||
| 558 | * | ||
| 559 | * Returns true if some entries were kfree()d, false otherwise. | ||
| 560 | */ | ||
| 561 | static bool tomoyo_kfree_entry(void) | ||
| 562 | { | ||
| 563 | struct tomoyo_gc *p; | ||
| 564 | struct tomoyo_gc *tmp; | ||
| 565 | bool result = false; | ||
| 566 | |||
| 567 | list_for_each_entry_safe(p, tmp, &tomoyo_gc_list, list) { | ||
| 568 | struct list_head *element = p->element; | ||
| 569 | |||
| 570 | /* | ||
| 571 | * list_del_rcu() in tomoyo_add_to_gc() guarantees that the | ||
| 572 | * list element became no longer reachable from the list which | ||
| 573 | * the element was originally on (e.g. tomoyo_domain_list). | ||
| 574 | * Also, synchronize_srcu() in tomoyo_gc_thread() guarantees | ||
| 575 | * that the list element became no longer referenced by syscall | ||
| 576 | * users. | ||
| 577 | * | ||
| 578 | * However, there are three users which may still be using the | ||
| 579 | * list element. We need to defer until all of these users | ||
| 580 | * forget the list element. | ||
| 581 | * | ||
| 582 | * Firstly, defer until "struct tomoyo_io_buffer"->r.{domain, | ||
| 583 | * group,acl} and "struct tomoyo_io_buffer"->w.domain forget | ||
| 584 | * the list element. | ||
| 585 | */ | ||
| 586 | if (tomoyo_struct_used_by_io_buffer(element)) | ||
| 587 | continue; | ||
| 588 | /* | ||
| 589 | * Secondly, defer until all other elements in the | ||
| 590 | * tomoyo_gc_list list forget the list element. | ||
| 591 | */ | ||
| 592 | if (tomoyo_element_linked_by_gc((const u8 *) element, p->size)) | ||
| 593 | continue; | ||
| 594 | switch (p->type) { | ||
| 595 | case TOMOYO_ID_TRANSITION_CONTROL: | ||
| 596 | tomoyo_del_transition_control(element); | ||
| 597 | break; | ||
| 598 | case TOMOYO_ID_AGGREGATOR: | ||
| 599 | tomoyo_del_aggregator(element); | ||
| 600 | break; | ||
| 601 | case TOMOYO_ID_MANAGER: | ||
| 602 | tomoyo_del_manager(element); | ||
| 603 | break; | ||
| 604 | case TOMOYO_ID_CONDITION: | ||
| 605 | tomoyo_del_condition(element); | ||
| 606 | break; | ||
| 607 | case TOMOYO_ID_NAME: | ||
| 608 | /* | ||
| 609 | * Thirdly, defer until all "struct tomoyo_io_buffer" | ||
| 610 | * ->r.w[] forget the list element. | ||
| 611 | */ | ||
| 612 | if (tomoyo_name_used_by_io_buffer( | ||
| 613 | container_of(element, typeof(struct tomoyo_name), | ||
| 614 | head.list)->entry.name, p->size)) | ||
| 615 | continue; | ||
| 616 | tomoyo_del_name(element); | ||
| 617 | break; | ||
| 618 | case TOMOYO_ID_ACL: | ||
| 619 | tomoyo_del_acl(element); | ||
| 620 | break; | ||
| 621 | case TOMOYO_ID_DOMAIN: | ||
| 622 | if (!tomoyo_del_domain(element)) | ||
| 623 | continue; | ||
| 624 | break; | ||
| 625 | case TOMOYO_ID_PATH_GROUP: | ||
| 626 | tomoyo_del_path_group(element); | ||
| 627 | break; | ||
| 628 | case TOMOYO_ID_GROUP: | ||
| 629 | tomoyo_del_group(element); | ||
| 630 | break; | ||
| 631 | case TOMOYO_ID_NUMBER_GROUP: | ||
| 632 | tomoyo_del_number_group(element); | ||
| 633 | break; | ||
| 634 | case TOMOYO_MAX_POLICY: | ||
| 635 | break; | ||
| 636 | } | ||
| 637 | tomoyo_memory_free(element); | ||
| 638 | list_del(&p->list); | ||
| 639 | kfree(p); | ||
| 640 | tomoyo_gc_list_len--; | ||
| 641 | result = true; | ||
| 642 | } | ||
| 643 | return result; | ||
| 644 | } | ||
| 645 | |||
| 646 | /** | ||
| 647 | * tomoyo_gc_thread - Garbage collector thread function. | 587 | * tomoyo_gc_thread - Garbage collector thread function. |
| 648 | * | 588 | * |
| 649 | * @unused: Unused. | 589 | * @unused: Unused. |
| 650 | * | 590 | * |
| 651 | * In case OOM-killer choose this thread for termination, we create this thread | ||
| 652 | * as a short live thread whenever /sys/kernel/security/tomoyo/ interface was | ||
| 653 | * close()d. | ||
| 654 | * | ||
| 655 | * Returns 0. | 591 | * Returns 0. |
| 656 | */ | 592 | */ |
| 657 | static int tomoyo_gc_thread(void *unused) | 593 | static int tomoyo_gc_thread(void *unused) |
| @@ -660,13 +596,7 @@ static int tomoyo_gc_thread(void *unused) | |||
| 660 | static DEFINE_MUTEX(tomoyo_gc_mutex); | 596 | static DEFINE_MUTEX(tomoyo_gc_mutex); |
| 661 | if (!mutex_trylock(&tomoyo_gc_mutex)) | 597 | if (!mutex_trylock(&tomoyo_gc_mutex)) |
| 662 | goto out; | 598 | goto out; |
| 663 | daemonize("GC for TOMOYO"); | 599 | tomoyo_collect_entry(); |
| 664 | do { | ||
| 665 | tomoyo_collect_entry(); | ||
| 666 | if (list_empty(&tomoyo_gc_list)) | ||
| 667 | break; | ||
| 668 | synchronize_srcu(&tomoyo_ss); | ||
| 669 | } while (tomoyo_kfree_entry()); | ||
| 670 | { | 600 | { |
| 671 | struct tomoyo_io_buffer *head; | 601 | struct tomoyo_io_buffer *head; |
| 672 | struct tomoyo_io_buffer *tmp; | 602 | struct tomoyo_io_buffer *tmp; |
diff --git a/security/tomoyo/group.c b/security/tomoyo/group.c index 5fb0e1298400..50092534ec54 100644 --- a/security/tomoyo/group.c +++ b/security/tomoyo/group.c | |||
| @@ -42,7 +42,26 @@ static bool tomoyo_same_number_group(const struct tomoyo_acl_head *a, | |||
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | /** | 44 | /** |
| 45 | * tomoyo_write_group - Write "struct tomoyo_path_group"/"struct tomoyo_number_group" list. | 45 | * tomoyo_same_address_group - Check for duplicated "struct tomoyo_address_group" entry. |
| 46 | * | ||
| 47 | * @a: Pointer to "struct tomoyo_acl_head". | ||
| 48 | * @b: Pointer to "struct tomoyo_acl_head". | ||
| 49 | * | ||
| 50 | * Returns true if @a == @b, false otherwise. | ||
| 51 | */ | ||
| 52 | static bool tomoyo_same_address_group(const struct tomoyo_acl_head *a, | ||
| 53 | const struct tomoyo_acl_head *b) | ||
| 54 | { | ||
| 55 | const struct tomoyo_address_group *p1 = container_of(a, typeof(*p1), | ||
| 56 | head); | ||
| 57 | const struct tomoyo_address_group *p2 = container_of(b, typeof(*p2), | ||
| 58 | head); | ||
| 59 | |||
| 60 | return tomoyo_same_ipaddr_union(&p1->address, &p2->address); | ||
| 61 | } | ||
| 62 | |||
| 63 | /** | ||
| 64 | * tomoyo_write_group - Write "struct tomoyo_path_group"/"struct tomoyo_number_group"/"struct tomoyo_address_group" list. | ||
| 46 | * | 65 | * |
| 47 | * @param: Pointer to "struct tomoyo_acl_param". | 66 | * @param: Pointer to "struct tomoyo_acl_param". |
| 48 | * @type: Type of this group. | 67 | * @type: Type of this group. |
| @@ -77,6 +96,14 @@ int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type) | |||
| 77 | * tomoyo_put_number_union() is not needed because | 96 | * tomoyo_put_number_union() is not needed because |
| 78 | * param->data[0] != '@'. | 97 | * param->data[0] != '@'. |
| 79 | */ | 98 | */ |
| 99 | } else { | ||
| 100 | struct tomoyo_address_group e = { }; | ||
| 101 | |||
| 102 | if (param->data[0] == '@' || | ||
| 103 | !tomoyo_parse_ipaddr_union(param, &e.address)) | ||
| 104 | goto out; | ||
| 105 | error = tomoyo_update_policy(&e.head, sizeof(e), param, | ||
| 106 | tomoyo_same_address_group); | ||
| 80 | } | 107 | } |
| 81 | out: | 108 | out: |
| 82 | tomoyo_put_group(group); | 109 | tomoyo_put_group(group); |
| @@ -137,3 +164,35 @@ bool tomoyo_number_matches_group(const unsigned long min, | |||
| 137 | } | 164 | } |
| 138 | return matched; | 165 | return matched; |
| 139 | } | 166 | } |
| 167 | |||
| 168 | /** | ||
| 169 | * tomoyo_address_matches_group - Check whether the given address matches members of the given address group. | ||
| 170 | * | ||
| 171 | * @is_ipv6: True if @address is an IPv6 address. | ||
| 172 | * @address: An IPv4 or IPv6 address. | ||
| 173 | * @group: Pointer to "struct tomoyo_address_group". | ||
| 174 | * | ||
| 175 | * Returns true if @address matches addresses in @group group, false otherwise. | ||
| 176 | * | ||
| 177 | * Caller holds tomoyo_read_lock(). | ||
| 178 | */ | ||
| 179 | bool tomoyo_address_matches_group(const bool is_ipv6, const __be32 *address, | ||
| 180 | const struct tomoyo_group *group) | ||
| 181 | { | ||
| 182 | struct tomoyo_address_group *member; | ||
| 183 | bool matched = false; | ||
| 184 | const u8 size = is_ipv6 ? 16 : 4; | ||
| 185 | |||
| 186 | list_for_each_entry_rcu(member, &group->member_list, head.list) { | ||
| 187 | if (member->head.is_deleted) | ||
| 188 | continue; | ||
| 189 | if (member->address.is_ipv6 != is_ipv6) | ||
| 190 | continue; | ||
| 191 | if (memcmp(&member->address.ip[0], address, size) > 0 || | ||
| 192 | memcmp(address, &member->address.ip[1], size) > 0) | ||
| 193 | continue; | ||
| 194 | matched = true; | ||
| 195 | break; | ||
| 196 | } | ||
| 197 | return matched; | ||
| 198 | } | ||
diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c index 7a56051146c2..0e995716cc25 100644 --- a/security/tomoyo/memory.c +++ b/security/tomoyo/memory.c | |||
| @@ -27,8 +27,6 @@ void tomoyo_warn_oom(const char *function) | |||
| 27 | panic("MAC Initialization failed.\n"); | 27 | panic("MAC Initialization failed.\n"); |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | /* Lock for protecting tomoyo_memory_used. */ | ||
| 31 | static DEFINE_SPINLOCK(tomoyo_policy_memory_lock); | ||
| 32 | /* Memoy currently used by policy/audit log/query. */ | 30 | /* Memoy currently used by policy/audit log/query. */ |
| 33 | unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT]; | 31 | unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT]; |
| 34 | /* Memory quota for "policy"/"audit log"/"query". */ | 32 | /* Memory quota for "policy"/"audit log"/"query". */ |
| @@ -42,22 +40,19 @@ unsigned int tomoyo_memory_quota[TOMOYO_MAX_MEMORY_STAT]; | |||
| 42 | * Returns true on success, false otherwise. | 40 | * Returns true on success, false otherwise. |
| 43 | * | 41 | * |
| 44 | * Returns true if @ptr is not NULL and quota not exceeded, false otherwise. | 42 | * Returns true if @ptr is not NULL and quota not exceeded, false otherwise. |
| 43 | * | ||
| 44 | * Caller holds tomoyo_policy_lock mutex. | ||
| 45 | */ | 45 | */ |
| 46 | bool tomoyo_memory_ok(void *ptr) | 46 | bool tomoyo_memory_ok(void *ptr) |
| 47 | { | 47 | { |
| 48 | if (ptr) { | 48 | if (ptr) { |
| 49 | const size_t s = ksize(ptr); | 49 | const size_t s = ksize(ptr); |
| 50 | bool result; | ||
| 51 | spin_lock(&tomoyo_policy_memory_lock); | ||
| 52 | tomoyo_memory_used[TOMOYO_MEMORY_POLICY] += s; | 50 | tomoyo_memory_used[TOMOYO_MEMORY_POLICY] += s; |
| 53 | result = !tomoyo_memory_quota[TOMOYO_MEMORY_POLICY] || | 51 | if (!tomoyo_memory_quota[TOMOYO_MEMORY_POLICY] || |
| 54 | tomoyo_memory_used[TOMOYO_MEMORY_POLICY] <= | 52 | tomoyo_memory_used[TOMOYO_MEMORY_POLICY] <= |
| 55 | tomoyo_memory_quota[TOMOYO_MEMORY_POLICY]; | 53 | tomoyo_memory_quota[TOMOYO_MEMORY_POLICY]) |
| 56 | if (!result) | ||
| 57 | tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s; | ||
| 58 | spin_unlock(&tomoyo_policy_memory_lock); | ||
| 59 | if (result) | ||
| 60 | return true; | 54 | return true; |
| 55 | tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s; | ||
| 61 | } | 56 | } |
| 62 | tomoyo_warn_oom(__func__); | 57 | tomoyo_warn_oom(__func__); |
| 63 | return false; | 58 | return false; |
| @@ -71,6 +66,8 @@ bool tomoyo_memory_ok(void *ptr) | |||
| 71 | * | 66 | * |
| 72 | * Returns pointer to allocated memory on success, NULL otherwise. | 67 | * Returns pointer to allocated memory on success, NULL otherwise. |
| 73 | * @data is zero-cleared on success. | 68 | * @data is zero-cleared on success. |
| 69 | * | ||
| 70 | * Caller holds tomoyo_policy_lock mutex. | ||
| 74 | */ | 71 | */ |
| 75 | void *tomoyo_commit_ok(void *data, const unsigned int size) | 72 | void *tomoyo_commit_ok(void *data, const unsigned int size) |
| 76 | { | 73 | { |
| @@ -85,20 +82,6 @@ void *tomoyo_commit_ok(void *data, const unsigned int size) | |||
| 85 | } | 82 | } |
| 86 | 83 | ||
| 87 | /** | 84 | /** |
| 88 | * tomoyo_memory_free - Free memory for elements. | ||
| 89 | * | ||
| 90 | * @ptr: Pointer to allocated memory. | ||
| 91 | */ | ||
| 92 | void tomoyo_memory_free(void *ptr) | ||
| 93 | { | ||
| 94 | size_t s = ksize(ptr); | ||
| 95 | spin_lock(&tomoyo_policy_memory_lock); | ||
| 96 | tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s; | ||
| 97 | spin_unlock(&tomoyo_policy_memory_lock); | ||
| 98 | kfree(ptr); | ||
| 99 | } | ||
| 100 | |||
| 101 | /** | ||
| 102 | * tomoyo_get_group - Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group". | 85 | * tomoyo_get_group - Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group". |
| 103 | * | 86 | * |
| 104 | * @param: Pointer to "struct tomoyo_acl_param". | 87 | * @param: Pointer to "struct tomoyo_acl_param". |
| @@ -123,7 +106,8 @@ struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param, | |||
| 123 | goto out; | 106 | goto out; |
| 124 | list = ¶m->ns->group_list[idx]; | 107 | list = ¶m->ns->group_list[idx]; |
| 125 | list_for_each_entry(group, list, head.list) { | 108 | list_for_each_entry(group, list, head.list) { |
| 126 | if (e.group_name != group->group_name) | 109 | if (e.group_name != group->group_name || |
| 110 | atomic_read(&group->head.users) == TOMOYO_GC_IN_PROGRESS) | ||
| 127 | continue; | 111 | continue; |
| 128 | atomic_inc(&group->head.users); | 112 | atomic_inc(&group->head.users); |
| 129 | found = true; | 113 | found = true; |
| @@ -175,7 +159,8 @@ const struct tomoyo_path_info *tomoyo_get_name(const char *name) | |||
| 175 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 159 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) |
| 176 | return NULL; | 160 | return NULL; |
| 177 | list_for_each_entry(ptr, head, head.list) { | 161 | list_for_each_entry(ptr, head, head.list) { |
| 178 | if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name)) | 162 | if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name) || |
| 163 | atomic_read(&ptr->head.users) == TOMOYO_GC_IN_PROGRESS) | ||
| 179 | continue; | 164 | continue; |
| 180 | atomic_inc(&ptr->head.users); | 165 | atomic_inc(&ptr->head.users); |
| 181 | goto out; | 166 | goto out; |
diff --git a/security/tomoyo/network.c b/security/tomoyo/network.c new file mode 100644 index 000000000000..97527710a72a --- /dev/null +++ b/security/tomoyo/network.c | |||
| @@ -0,0 +1,771 @@ | |||
| 1 | /* | ||
| 2 | * security/tomoyo/network.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005-2011 NTT DATA CORPORATION | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include "common.h" | ||
| 8 | #include <linux/slab.h> | ||
| 9 | |||
| 10 | /* Structure for holding inet domain socket's address. */ | ||
| 11 | struct tomoyo_inet_addr_info { | ||
| 12 | __be16 port; /* In network byte order. */ | ||
| 13 | const __be32 *address; /* In network byte order. */ | ||
| 14 | bool is_ipv6; | ||
| 15 | }; | ||
| 16 | |||
| 17 | /* Structure for holding unix domain socket's address. */ | ||
| 18 | struct tomoyo_unix_addr_info { | ||
| 19 | u8 *addr; /* This may not be '\0' terminated string. */ | ||
| 20 | unsigned int addr_len; | ||
| 21 | }; | ||
| 22 | |||
| 23 | /* Structure for holding socket address. */ | ||
| 24 | struct tomoyo_addr_info { | ||
| 25 | u8 protocol; | ||
| 26 | u8 operation; | ||
| 27 | struct tomoyo_inet_addr_info inet; | ||
| 28 | struct tomoyo_unix_addr_info unix0; | ||
| 29 | }; | ||
| 30 | |||
| 31 | /* String table for socket's protocols. */ | ||
| 32 | const char * const tomoyo_proto_keyword[TOMOYO_SOCK_MAX] = { | ||
| 33 | [SOCK_STREAM] = "stream", | ||
| 34 | [SOCK_DGRAM] = "dgram", | ||
| 35 | [SOCK_RAW] = "raw", | ||
| 36 | [SOCK_SEQPACKET] = "seqpacket", | ||
| 37 | [0] = " ", /* Dummy for avoiding NULL pointer dereference. */ | ||
| 38 | [4] = " ", /* Dummy for avoiding NULL pointer dereference. */ | ||
| 39 | }; | ||
| 40 | |||
| 41 | /** | ||
| 42 | * tomoyo_parse_ipaddr_union - Parse an IP address. | ||
| 43 | * | ||
| 44 | * @param: Pointer to "struct tomoyo_acl_param". | ||
| 45 | * @ptr: Pointer to "struct tomoyo_ipaddr_union". | ||
| 46 | * | ||
| 47 | * Returns true on success, false otherwise. | ||
| 48 | */ | ||
| 49 | bool tomoyo_parse_ipaddr_union(struct tomoyo_acl_param *param, | ||
| 50 | struct tomoyo_ipaddr_union *ptr) | ||
| 51 | { | ||
| 52 | u8 * const min = ptr->ip[0].in6_u.u6_addr8; | ||
| 53 | u8 * const max = ptr->ip[1].in6_u.u6_addr8; | ||
| 54 | char *address = tomoyo_read_token(param); | ||
| 55 | const char *end; | ||
| 56 | |||
| 57 | if (!strchr(address, ':') && | ||
| 58 | in4_pton(address, -1, min, '-', &end) > 0) { | ||
| 59 | ptr->is_ipv6 = false; | ||
| 60 | if (!*end) | ||
| 61 | ptr->ip[1].s6_addr32[0] = ptr->ip[0].s6_addr32[0]; | ||
| 62 | else if (*end++ != '-' || | ||
| 63 | in4_pton(end, -1, max, '\0', &end) <= 0 || *end) | ||
| 64 | return false; | ||
| 65 | return true; | ||
| 66 | } | ||
| 67 | if (in6_pton(address, -1, min, '-', &end) > 0) { | ||
| 68 | ptr->is_ipv6 = true; | ||
| 69 | if (!*end) | ||
| 70 | memmove(max, min, sizeof(u16) * 8); | ||
| 71 | else if (*end++ != '-' || | ||
| 72 | in6_pton(end, -1, max, '\0', &end) <= 0 || *end) | ||
| 73 | return false; | ||
| 74 | return true; | ||
| 75 | } | ||
| 76 | return false; | ||
| 77 | } | ||
| 78 | |||
| 79 | /** | ||
| 80 | * tomoyo_print_ipv4 - Print an IPv4 address. | ||
| 81 | * | ||
| 82 | * @buffer: Buffer to write to. | ||
| 83 | * @buffer_len: Size of @buffer. | ||
| 84 | * @min_ip: Pointer to __be32. | ||
| 85 | * @max_ip: Pointer to __be32. | ||
| 86 | * | ||
| 87 | * Returns nothing. | ||
| 88 | */ | ||
| 89 | static void tomoyo_print_ipv4(char *buffer, const unsigned int buffer_len, | ||
| 90 | const __be32 *min_ip, const __be32 *max_ip) | ||
| 91 | { | ||
| 92 | snprintf(buffer, buffer_len, "%pI4%c%pI4", min_ip, | ||
| 93 | *min_ip == *max_ip ? '\0' : '-', max_ip); | ||
| 94 | } | ||
| 95 | |||
| 96 | /** | ||
| 97 | * tomoyo_print_ipv6 - Print an IPv6 address. | ||
| 98 | * | ||
| 99 | * @buffer: Buffer to write to. | ||
| 100 | * @buffer_len: Size of @buffer. | ||
| 101 | * @min_ip: Pointer to "struct in6_addr". | ||
| 102 | * @max_ip: Pointer to "struct in6_addr". | ||
| 103 | * | ||
| 104 | * Returns nothing. | ||
| 105 | */ | ||
| 106 | static void tomoyo_print_ipv6(char *buffer, const unsigned int buffer_len, | ||
| 107 | const struct in6_addr *min_ip, | ||
| 108 | const struct in6_addr *max_ip) | ||
| 109 | { | ||
| 110 | snprintf(buffer, buffer_len, "%pI6c%c%pI6c", min_ip, | ||
| 111 | !memcmp(min_ip, max_ip, 16) ? '\0' : '-', max_ip); | ||
| 112 | } | ||
| 113 | |||
| 114 | /** | ||
| 115 | * tomoyo_print_ip - Print an IP address. | ||
| 116 | * | ||
| 117 | * @buf: Buffer to write to. | ||
| 118 | * @size: Size of @buf. | ||
| 119 | * @ptr: Pointer to "struct ipaddr_union". | ||
| 120 | * | ||
| 121 | * Returns nothing. | ||
| 122 | */ | ||
| 123 | void tomoyo_print_ip(char *buf, const unsigned int size, | ||
| 124 | const struct tomoyo_ipaddr_union *ptr) | ||
| 125 | { | ||
| 126 | if (ptr->is_ipv6) | ||
| 127 | tomoyo_print_ipv6(buf, size, &ptr->ip[0], &ptr->ip[1]); | ||
| 128 | else | ||
| 129 | tomoyo_print_ipv4(buf, size, &ptr->ip[0].s6_addr32[0], | ||
| 130 | &ptr->ip[1].s6_addr32[0]); | ||
| 131 | } | ||
| 132 | |||
| 133 | /* | ||
| 134 | * Mapping table from "enum tomoyo_network_acl_index" to | ||
| 135 | * "enum tomoyo_mac_index" for inet domain socket. | ||
| 136 | */ | ||
| 137 | static const u8 tomoyo_inet2mac | ||
| 138 | [TOMOYO_SOCK_MAX][TOMOYO_MAX_NETWORK_OPERATION] = { | ||
| 139 | [SOCK_STREAM] = { | ||
| 140 | [TOMOYO_NETWORK_BIND] = TOMOYO_MAC_NETWORK_INET_STREAM_BIND, | ||
| 141 | [TOMOYO_NETWORK_LISTEN] = | ||
| 142 | TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN, | ||
| 143 | [TOMOYO_NETWORK_CONNECT] = | ||
| 144 | TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT, | ||
| 145 | }, | ||
| 146 | [SOCK_DGRAM] = { | ||
| 147 | [TOMOYO_NETWORK_BIND] = TOMOYO_MAC_NETWORK_INET_DGRAM_BIND, | ||
| 148 | [TOMOYO_NETWORK_SEND] = TOMOYO_MAC_NETWORK_INET_DGRAM_SEND, | ||
| 149 | }, | ||
| 150 | [SOCK_RAW] = { | ||
| 151 | [TOMOYO_NETWORK_BIND] = TOMOYO_MAC_NETWORK_INET_RAW_BIND, | ||
| 152 | [TOMOYO_NETWORK_SEND] = TOMOYO_MAC_NETWORK_INET_RAW_SEND, | ||
| 153 | }, | ||
| 154 | }; | ||
| 155 | |||
| 156 | /* | ||
| 157 | * Mapping table from "enum tomoyo_network_acl_index" to | ||
| 158 | * "enum tomoyo_mac_index" for unix domain socket. | ||
| 159 | */ | ||
| 160 | static const u8 tomoyo_unix2mac | ||
| 161 | [TOMOYO_SOCK_MAX][TOMOYO_MAX_NETWORK_OPERATION] = { | ||
| 162 | [SOCK_STREAM] = { | ||
| 163 | [TOMOYO_NETWORK_BIND] = TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND, | ||
| 164 | [TOMOYO_NETWORK_LISTEN] = | ||
| 165 | TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN, | ||
| 166 | [TOMOYO_NETWORK_CONNECT] = | ||
| 167 | TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT, | ||
| 168 | }, | ||
| 169 | [SOCK_DGRAM] = { | ||
| 170 | [TOMOYO_NETWORK_BIND] = TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND, | ||
| 171 | [TOMOYO_NETWORK_SEND] = TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND, | ||
| 172 | }, | ||
| 173 | [SOCK_SEQPACKET] = { | ||
| 174 | [TOMOYO_NETWORK_BIND] = | ||
| 175 | TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND, | ||
| 176 | [TOMOYO_NETWORK_LISTEN] = | ||
| 177 | TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN, | ||
| 178 | [TOMOYO_NETWORK_CONNECT] = | ||
| 179 | TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT, | ||
| 180 | }, | ||
| 181 | }; | ||
| 182 | |||
| 183 | /** | ||
| 184 | * tomoyo_same_inet_acl - Check for duplicated "struct tomoyo_inet_acl" entry. | ||
| 185 | * | ||
| 186 | * @a: Pointer to "struct tomoyo_acl_info". | ||
| 187 | * @b: Pointer to "struct tomoyo_acl_info". | ||
| 188 | * | ||
| 189 | * Returns true if @a == @b except permission bits, false otherwise. | ||
| 190 | */ | ||
| 191 | static bool tomoyo_same_inet_acl(const struct tomoyo_acl_info *a, | ||
| 192 | const struct tomoyo_acl_info *b) | ||
| 193 | { | ||
| 194 | const struct tomoyo_inet_acl *p1 = container_of(a, typeof(*p1), head); | ||
| 195 | const struct tomoyo_inet_acl *p2 = container_of(b, typeof(*p2), head); | ||
| 196 | |||
| 197 | return p1->protocol == p2->protocol && | ||
| 198 | tomoyo_same_ipaddr_union(&p1->address, &p2->address) && | ||
| 199 | tomoyo_same_number_union(&p1->port, &p2->port); | ||
| 200 | } | ||
| 201 | |||
| 202 | /** | ||
| 203 | * tomoyo_same_unix_acl - Check for duplicated "struct tomoyo_unix_acl" entry. | ||
| 204 | * | ||
| 205 | * @a: Pointer to "struct tomoyo_acl_info". | ||
| 206 | * @b: Pointer to "struct tomoyo_acl_info". | ||
| 207 | * | ||
| 208 | * Returns true if @a == @b except permission bits, false otherwise. | ||
| 209 | */ | ||
| 210 | static bool tomoyo_same_unix_acl(const struct tomoyo_acl_info *a, | ||
| 211 | const struct tomoyo_acl_info *b) | ||
| 212 | { | ||
| 213 | const struct tomoyo_unix_acl *p1 = container_of(a, typeof(*p1), head); | ||
| 214 | const struct tomoyo_unix_acl *p2 = container_of(b, typeof(*p2), head); | ||
| 215 | |||
| 216 | return p1->protocol == p2->protocol && | ||
| 217 | tomoyo_same_name_union(&p1->name, &p2->name); | ||
| 218 | } | ||
| 219 | |||
| 220 | /** | ||
| 221 | * tomoyo_merge_inet_acl - Merge duplicated "struct tomoyo_inet_acl" entry. | ||
| 222 | * | ||
| 223 | * @a: Pointer to "struct tomoyo_acl_info". | ||
| 224 | * @b: Pointer to "struct tomoyo_acl_info". | ||
| 225 | * @is_delete: True for @a &= ~@b, false for @a |= @b. | ||
| 226 | * | ||
| 227 | * Returns true if @a is empty, false otherwise. | ||
| 228 | */ | ||
| 229 | static bool tomoyo_merge_inet_acl(struct tomoyo_acl_info *a, | ||
| 230 | struct tomoyo_acl_info *b, | ||
| 231 | const bool is_delete) | ||
| 232 | { | ||
| 233 | u8 * const a_perm = | ||
| 234 | &container_of(a, struct tomoyo_inet_acl, head)->perm; | ||
| 235 | u8 perm = *a_perm; | ||
| 236 | const u8 b_perm = container_of(b, struct tomoyo_inet_acl, head)->perm; | ||
| 237 | |||
| 238 | if (is_delete) | ||
| 239 | perm &= ~b_perm; | ||
| 240 | else | ||
| 241 | perm |= b_perm; | ||
| 242 | *a_perm = perm; | ||
| 243 | return !perm; | ||
| 244 | } | ||
| 245 | |||
| 246 | /** | ||
| 247 | * tomoyo_merge_unix_acl - Merge duplicated "struct tomoyo_unix_acl" entry. | ||
| 248 | * | ||
| 249 | * @a: Pointer to "struct tomoyo_acl_info". | ||
| 250 | * @b: Pointer to "struct tomoyo_acl_info". | ||
| 251 | * @is_delete: True for @a &= ~@b, false for @a |= @b. | ||
| 252 | * | ||
| 253 | * Returns true if @a is empty, false otherwise. | ||
| 254 | */ | ||
| 255 | static bool tomoyo_merge_unix_acl(struct tomoyo_acl_info *a, | ||
| 256 | struct tomoyo_acl_info *b, | ||
| 257 | const bool is_delete) | ||
| 258 | { | ||
| 259 | u8 * const a_perm = | ||
| 260 | &container_of(a, struct tomoyo_unix_acl, head)->perm; | ||
| 261 | u8 perm = *a_perm; | ||
| 262 | const u8 b_perm = container_of(b, struct tomoyo_unix_acl, head)->perm; | ||
| 263 | |||
| 264 | if (is_delete) | ||
| 265 | perm &= ~b_perm; | ||
| 266 | else | ||
| 267 | perm |= b_perm; | ||
| 268 | *a_perm = perm; | ||
| 269 | return !perm; | ||
| 270 | } | ||
| 271 | |||
| 272 | /** | ||
| 273 | * tomoyo_write_inet_network - Write "struct tomoyo_inet_acl" list. | ||
| 274 | * | ||
| 275 | * @param: Pointer to "struct tomoyo_acl_param". | ||
| 276 | * | ||
| 277 | * Returns 0 on success, negative value otherwise. | ||
| 278 | * | ||
| 279 | * Caller holds tomoyo_read_lock(). | ||
| 280 | */ | ||
| 281 | int tomoyo_write_inet_network(struct tomoyo_acl_param *param) | ||
| 282 | { | ||
| 283 | struct tomoyo_inet_acl e = { .head.type = TOMOYO_TYPE_INET_ACL }; | ||
| 284 | int error = -EINVAL; | ||
| 285 | u8 type; | ||
| 286 | const char *protocol = tomoyo_read_token(param); | ||
| 287 | const char *operation = tomoyo_read_token(param); | ||
| 288 | |||
| 289 | for (e.protocol = 0; e.protocol < TOMOYO_SOCK_MAX; e.protocol++) | ||
| 290 | if (!strcmp(protocol, tomoyo_proto_keyword[e.protocol])) | ||
| 291 | break; | ||
| 292 | for (type = 0; type < TOMOYO_MAX_NETWORK_OPERATION; type++) | ||
| 293 | if (tomoyo_permstr(operation, tomoyo_socket_keyword[type])) | ||
| 294 | e.perm |= 1 << type; | ||
| 295 | if (e.protocol == TOMOYO_SOCK_MAX || !e.perm) | ||
| 296 | return -EINVAL; | ||
| 297 | if (param->data[0] == '@') { | ||
| 298 | param->data++; | ||
| 299 | e.address.group = | ||
| 300 | tomoyo_get_group(param, TOMOYO_ADDRESS_GROUP); | ||
| 301 | if (!e.address.group) | ||
| 302 | return -ENOMEM; | ||
| 303 | } else { | ||
| 304 | if (!tomoyo_parse_ipaddr_union(param, &e.address)) | ||
| 305 | goto out; | ||
| 306 | } | ||
| 307 | if (!tomoyo_parse_number_union(param, &e.port) || | ||
| 308 | e.port.values[1] > 65535) | ||
| 309 | goto out; | ||
| 310 | error = tomoyo_update_domain(&e.head, sizeof(e), param, | ||
| 311 | tomoyo_same_inet_acl, | ||
| 312 | tomoyo_merge_inet_acl); | ||
| 313 | out: | ||
| 314 | tomoyo_put_group(e.address.group); | ||
| 315 | tomoyo_put_number_union(&e.port); | ||
| 316 | return error; | ||
| 317 | } | ||
| 318 | |||
| 319 | /** | ||
| 320 | * tomoyo_write_unix_network - Write "struct tomoyo_unix_acl" list. | ||
| 321 | * | ||
| 322 | * @param: Pointer to "struct tomoyo_acl_param". | ||
| 323 | * | ||
| 324 | * Returns 0 on success, negative value otherwise. | ||
| 325 | */ | ||
| 326 | int tomoyo_write_unix_network(struct tomoyo_acl_param *param) | ||
| 327 | { | ||
| 328 | struct tomoyo_unix_acl e = { .head.type = TOMOYO_TYPE_UNIX_ACL }; | ||
| 329 | int error; | ||
| 330 | u8 type; | ||
| 331 | const char *protocol = tomoyo_read_token(param); | ||
| 332 | const char *operation = tomoyo_read_token(param); | ||
| 333 | |||
| 334 | for (e.protocol = 0; e.protocol < TOMOYO_SOCK_MAX; e.protocol++) | ||
| 335 | if (!strcmp(protocol, tomoyo_proto_keyword[e.protocol])) | ||
| 336 | break; | ||
| 337 | for (type = 0; type < TOMOYO_MAX_NETWORK_OPERATION; type++) | ||
| 338 | if (tomoyo_permstr(operation, tomoyo_socket_keyword[type])) | ||
| 339 | e.perm |= 1 << type; | ||
| 340 | if (e.protocol == TOMOYO_SOCK_MAX || !e.perm) | ||
| 341 | return -EINVAL; | ||
| 342 | if (!tomoyo_parse_name_union(param, &e.name)) | ||
| 343 | return -EINVAL; | ||
| 344 | error = tomoyo_update_domain(&e.head, sizeof(e), param, | ||
| 345 | tomoyo_same_unix_acl, | ||
| 346 | tomoyo_merge_unix_acl); | ||
| 347 | tomoyo_put_name_union(&e.name); | ||
| 348 | return error; | ||
| 349 | } | ||
| 350 | |||
| 351 | /** | ||
| 352 | * tomoyo_audit_net_log - Audit network log. | ||
| 353 | * | ||
| 354 | * @r: Pointer to "struct tomoyo_request_info". | ||
| 355 | * @family: Name of socket family ("inet" or "unix"). | ||
| 356 | * @protocol: Name of protocol in @family. | ||
| 357 | * @operation: Name of socket operation. | ||
| 358 | * @address: Name of address. | ||
| 359 | * | ||
| 360 | * Returns 0 on success, negative value otherwise. | ||
| 361 | */ | ||
| 362 | static int tomoyo_audit_net_log(struct tomoyo_request_info *r, | ||
| 363 | const char *family, const u8 protocol, | ||
| 364 | const u8 operation, const char *address) | ||
| 365 | { | ||
| 366 | return tomoyo_supervisor(r, "network %s %s %s %s\n", family, | ||
| 367 | tomoyo_proto_keyword[protocol], | ||
| 368 | tomoyo_socket_keyword[operation], address); | ||
| 369 | } | ||
| 370 | |||
| 371 | /** | ||
| 372 | * tomoyo_audit_inet_log - Audit INET network log. | ||
| 373 | * | ||
| 374 | * @r: Pointer to "struct tomoyo_request_info". | ||
| 375 | * | ||
| 376 | * Returns 0 on success, negative value otherwise. | ||
| 377 | */ | ||
| 378 | static int tomoyo_audit_inet_log(struct tomoyo_request_info *r) | ||
| 379 | { | ||
| 380 | char buf[128]; | ||
| 381 | int len; | ||
| 382 | const __be32 *address = r->param.inet_network.address; | ||
| 383 | |||
| 384 | if (r->param.inet_network.is_ipv6) | ||
| 385 | tomoyo_print_ipv6(buf, sizeof(buf), (const struct in6_addr *) | ||
| 386 | address, (const struct in6_addr *) address); | ||
| 387 | else | ||
| 388 | tomoyo_print_ipv4(buf, sizeof(buf), address, address); | ||
| 389 | len = strlen(buf); | ||
| 390 | snprintf(buf + len, sizeof(buf) - len, " %u", | ||
| 391 | r->param.inet_network.port); | ||
| 392 | return tomoyo_audit_net_log(r, "inet", r->param.inet_network.protocol, | ||
| 393 | r->param.inet_network.operation, buf); | ||
| 394 | } | ||
| 395 | |||
| 396 | /** | ||
| 397 | * tomoyo_audit_unix_log - Audit UNIX network log. | ||
| 398 | * | ||
| 399 | * @r: Pointer to "struct tomoyo_request_info". | ||
| 400 | * | ||
| 401 | * Returns 0 on success, negative value otherwise. | ||
| 402 | */ | ||
| 403 | static int tomoyo_audit_unix_log(struct tomoyo_request_info *r) | ||
| 404 | { | ||
| 405 | return tomoyo_audit_net_log(r, "unix", r->param.unix_network.protocol, | ||
| 406 | r->param.unix_network.operation, | ||
| 407 | r->param.unix_network.address->name); | ||
| 408 | } | ||
| 409 | |||
| 410 | /** | ||
| 411 | * tomoyo_check_inet_acl - Check permission for inet domain socket operation. | ||
| 412 | * | ||
| 413 | * @r: Pointer to "struct tomoyo_request_info". | ||
| 414 | * @ptr: Pointer to "struct tomoyo_acl_info". | ||
| 415 | * | ||
| 416 | * Returns true if granted, false otherwise. | ||
| 417 | */ | ||
| 418 | static bool tomoyo_check_inet_acl(struct tomoyo_request_info *r, | ||
| 419 | const struct tomoyo_acl_info *ptr) | ||
| 420 | { | ||
| 421 | const struct tomoyo_inet_acl *acl = | ||
| 422 | container_of(ptr, typeof(*acl), head); | ||
| 423 | const u8 size = r->param.inet_network.is_ipv6 ? 16 : 4; | ||
| 424 | |||
| 425 | if (!(acl->perm & (1 << r->param.inet_network.operation)) || | ||
| 426 | !tomoyo_compare_number_union(r->param.inet_network.port, | ||
| 427 | &acl->port)) | ||
| 428 | return false; | ||
| 429 | if (acl->address.group) | ||
| 430 | return tomoyo_address_matches_group | ||
| 431 | (r->param.inet_network.is_ipv6, | ||
| 432 | r->param.inet_network.address, acl->address.group); | ||
| 433 | return acl->address.is_ipv6 == r->param.inet_network.is_ipv6 && | ||
| 434 | memcmp(&acl->address.ip[0], | ||
| 435 | r->param.inet_network.address, size) <= 0 && | ||
| 436 | memcmp(r->param.inet_network.address, | ||
| 437 | &acl->address.ip[1], size) <= 0; | ||
| 438 | } | ||
| 439 | |||
| 440 | /** | ||
| 441 | * tomoyo_check_unix_acl - Check permission for unix domain socket operation. | ||
| 442 | * | ||
| 443 | * @r: Pointer to "struct tomoyo_request_info". | ||
| 444 | * @ptr: Pointer to "struct tomoyo_acl_info". | ||
| 445 | * | ||
| 446 | * Returns true if granted, false otherwise. | ||
| 447 | */ | ||
| 448 | static bool tomoyo_check_unix_acl(struct tomoyo_request_info *r, | ||
| 449 | const struct tomoyo_acl_info *ptr) | ||
| 450 | { | ||
| 451 | const struct tomoyo_unix_acl *acl = | ||
| 452 | container_of(ptr, typeof(*acl), head); | ||
| 453 | |||
| 454 | return (acl->perm & (1 << r->param.unix_network.operation)) && | ||
| 455 | tomoyo_compare_name_union(r->param.unix_network.address, | ||
| 456 | &acl->name); | ||
| 457 | } | ||
| 458 | |||
| 459 | /** | ||
| 460 | * tomoyo_inet_entry - Check permission for INET network operation. | ||
| 461 | * | ||
| 462 | * @address: Pointer to "struct tomoyo_addr_info". | ||
| 463 | * | ||
| 464 | * Returns 0 on success, negative value otherwise. | ||
| 465 | */ | ||
| 466 | static int tomoyo_inet_entry(const struct tomoyo_addr_info *address) | ||
| 467 | { | ||
| 468 | const int idx = tomoyo_read_lock(); | ||
| 469 | struct tomoyo_request_info r; | ||
| 470 | int error = 0; | ||
| 471 | const u8 type = tomoyo_inet2mac[address->protocol][address->operation]; | ||
| 472 | |||
| 473 | if (type && tomoyo_init_request_info(&r, NULL, type) | ||
| 474 | != TOMOYO_CONFIG_DISABLED) { | ||
| 475 | r.param_type = TOMOYO_TYPE_INET_ACL; | ||
| 476 | r.param.inet_network.protocol = address->protocol; | ||
| 477 | r.param.inet_network.operation = address->operation; | ||
| 478 | r.param.inet_network.is_ipv6 = address->inet.is_ipv6; | ||
| 479 | r.param.inet_network.address = address->inet.address; | ||
| 480 | r.param.inet_network.port = ntohs(address->inet.port); | ||
| 481 | do { | ||
| 482 | tomoyo_check_acl(&r, tomoyo_check_inet_acl); | ||
| 483 | error = tomoyo_audit_inet_log(&r); | ||
| 484 | } while (error == TOMOYO_RETRY_REQUEST); | ||
| 485 | } | ||
| 486 | tomoyo_read_unlock(idx); | ||
| 487 | return error; | ||
| 488 | } | ||
| 489 | |||
| 490 | /** | ||
| 491 | * tomoyo_check_inet_address - Check permission for inet domain socket's operation. | ||
| 492 | * | ||
| 493 | * @addr: Pointer to "struct sockaddr". | ||
| 494 | * @addr_len: Size of @addr. | ||
| 495 | * @port: Port number. | ||
| 496 | * @address: Pointer to "struct tomoyo_addr_info". | ||
| 497 | * | ||
| 498 | * Returns 0 on success, negative value otherwise. | ||
| 499 | */ | ||
| 500 | static int tomoyo_check_inet_address(const struct sockaddr *addr, | ||
| 501 | const unsigned int addr_len, | ||
| 502 | const u16 port, | ||
| 503 | struct tomoyo_addr_info *address) | ||
| 504 | { | ||
| 505 | struct tomoyo_inet_addr_info *i = &address->inet; | ||
| 506 | |||
| 507 | switch (addr->sa_family) { | ||
| 508 | case AF_INET6: | ||
| 509 | if (addr_len < SIN6_LEN_RFC2133) | ||
| 510 | goto skip; | ||
| 511 | i->is_ipv6 = true; | ||
| 512 | i->address = (__be32 *) | ||
| 513 | ((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr; | ||
| 514 | i->port = ((struct sockaddr_in6 *) addr)->sin6_port; | ||
| 515 | break; | ||
| 516 | case AF_INET: | ||
| 517 | if (addr_len < sizeof(struct sockaddr_in)) | ||
| 518 | goto skip; | ||
| 519 | i->is_ipv6 = false; | ||
| 520 | i->address = (__be32 *) | ||
| 521 | &((struct sockaddr_in *) addr)->sin_addr; | ||
| 522 | i->port = ((struct sockaddr_in *) addr)->sin_port; | ||
| 523 | break; | ||
| 524 | default: | ||
| 525 | goto skip; | ||
| 526 | } | ||
| 527 | if (address->protocol == SOCK_RAW) | ||
| 528 | i->port = htons(port); | ||
| 529 | return tomoyo_inet_entry(address); | ||
| 530 | skip: | ||
| 531 | return 0; | ||
| 532 | } | ||
| 533 | |||
| 534 | /** | ||
| 535 | * tomoyo_unix_entry - Check permission for UNIX network operation. | ||
| 536 | * | ||
| 537 | * @address: Pointer to "struct tomoyo_addr_info". | ||
| 538 | * | ||
| 539 | * Returns 0 on success, negative value otherwise. | ||
| 540 | */ | ||
| 541 | static int tomoyo_unix_entry(const struct tomoyo_addr_info *address) | ||
| 542 | { | ||
| 543 | const int idx = tomoyo_read_lock(); | ||
| 544 | struct tomoyo_request_info r; | ||
| 545 | int error = 0; | ||
| 546 | const u8 type = tomoyo_unix2mac[address->protocol][address->operation]; | ||
| 547 | |||
| 548 | if (type && tomoyo_init_request_info(&r, NULL, type) | ||
| 549 | != TOMOYO_CONFIG_DISABLED) { | ||
| 550 | char *buf = address->unix0.addr; | ||
| 551 | int len = address->unix0.addr_len - sizeof(sa_family_t); | ||
| 552 | |||
| 553 | if (len <= 0) { | ||
| 554 | buf = "anonymous"; | ||
| 555 | len = 9; | ||
| 556 | } else if (buf[0]) { | ||
| 557 | len = strnlen(buf, len); | ||
| 558 | } | ||
| 559 | buf = tomoyo_encode2(buf, len); | ||
| 560 | if (buf) { | ||
| 561 | struct tomoyo_path_info addr; | ||
| 562 | |||
| 563 | addr.name = buf; | ||
| 564 | tomoyo_fill_path_info(&addr); | ||
| 565 | r.param_type = TOMOYO_TYPE_UNIX_ACL; | ||
| 566 | r.param.unix_network.protocol = address->protocol; | ||
| 567 | r.param.unix_network.operation = address->operation; | ||
| 568 | r.param.unix_network.address = &addr; | ||
| 569 | do { | ||
| 570 | tomoyo_check_acl(&r, tomoyo_check_unix_acl); | ||
| 571 | error = tomoyo_audit_unix_log(&r); | ||
| 572 | } while (error == TOMOYO_RETRY_REQUEST); | ||
| 573 | kfree(buf); | ||
| 574 | } else | ||
| 575 | error = -ENOMEM; | ||
| 576 | } | ||
| 577 | tomoyo_read_unlock(idx); | ||
| 578 | return error; | ||
| 579 | } | ||
| 580 | |||
| 581 | /** | ||
| 582 | * tomoyo_check_unix_address - Check permission for unix domain socket's operation. | ||
| 583 | * | ||
| 584 | * @addr: Pointer to "struct sockaddr". | ||
| 585 | * @addr_len: Size of @addr. | ||
| 586 | * @address: Pointer to "struct tomoyo_addr_info". | ||
| 587 | * | ||
| 588 | * Returns 0 on success, negative value otherwise. | ||
| 589 | */ | ||
| 590 | static int tomoyo_check_unix_address(struct sockaddr *addr, | ||
| 591 | const unsigned int addr_len, | ||
| 592 | struct tomoyo_addr_info *address) | ||
| 593 | { | ||
| 594 | struct tomoyo_unix_addr_info *u = &address->unix0; | ||
| 595 | |||
| 596 | if (addr->sa_family != AF_UNIX) | ||
| 597 | return 0; | ||
| 598 | u->addr = ((struct sockaddr_un *) addr)->sun_path; | ||
| 599 | u->addr_len = addr_len; | ||
| 600 | return tomoyo_unix_entry(address); | ||
| 601 | } | ||
| 602 | |||
| 603 | /** | ||
| 604 | * tomoyo_kernel_service - Check whether I'm kernel service or not. | ||
| 605 | * | ||
| 606 | * Returns true if I'm kernel service, false otherwise. | ||
| 607 | */ | ||
| 608 | static bool tomoyo_kernel_service(void) | ||
| 609 | { | ||
| 610 | /* Nothing to do if I am a kernel service. */ | ||
| 611 | return segment_eq(get_fs(), KERNEL_DS); | ||
| 612 | } | ||
| 613 | |||
| 614 | /** | ||
| 615 | * tomoyo_sock_family - Get socket's family. | ||
| 616 | * | ||
| 617 | * @sk: Pointer to "struct sock". | ||
| 618 | * | ||
| 619 | * Returns one of PF_INET, PF_INET6, PF_UNIX or 0. | ||
| 620 | */ | ||
| 621 | static u8 tomoyo_sock_family(struct sock *sk) | ||
| 622 | { | ||
| 623 | u8 family; | ||
| 624 | |||
| 625 | if (tomoyo_kernel_service()) | ||
| 626 | return 0; | ||
| 627 | family = sk->sk_family; | ||
| 628 | switch (family) { | ||
| 629 | case PF_INET: | ||
| 630 | case PF_INET6: | ||
| 631 | case PF_UNIX: | ||
| 632 | return family; | ||
| 633 | default: | ||
| 634 | return 0; | ||
| 635 | } | ||
| 636 | } | ||
| 637 | |||
| 638 | /** | ||
| 639 | * tomoyo_socket_listen_permission - Check permission for listening a socket. | ||
| 640 | * | ||
| 641 | * @sock: Pointer to "struct socket". | ||
| 642 | * | ||
| 643 | * Returns 0 on success, negative value otherwise. | ||
| 644 | */ | ||
| 645 | int tomoyo_socket_listen_permission(struct socket *sock) | ||
| 646 | { | ||
| 647 | struct tomoyo_addr_info address; | ||
| 648 | const u8 family = tomoyo_sock_family(sock->sk); | ||
| 649 | const unsigned int type = sock->type; | ||
| 650 | struct sockaddr_storage addr; | ||
| 651 | int addr_len; | ||
| 652 | |||
| 653 | if (!family || (type != SOCK_STREAM && type != SOCK_SEQPACKET)) | ||
| 654 | return 0; | ||
| 655 | { | ||
| 656 | const int error = sock->ops->getname(sock, (struct sockaddr *) | ||
| 657 | &addr, &addr_len, 0); | ||
| 658 | |||
| 659 | if (error) | ||
| 660 | return error; | ||
| 661 | } | ||
| 662 | address.protocol = type; | ||
| 663 | address.operation = TOMOYO_NETWORK_LISTEN; | ||
| 664 | if (family == PF_UNIX) | ||
| 665 | return tomoyo_check_unix_address((struct sockaddr *) &addr, | ||
| 666 | addr_len, &address); | ||
| 667 | return tomoyo_check_inet_address((struct sockaddr *) &addr, addr_len, | ||
| 668 | 0, &address); | ||
| 669 | } | ||
| 670 | |||
| 671 | /** | ||
| 672 | * tomoyo_socket_connect_permission - Check permission for setting the remote address of a socket. | ||
| 673 | * | ||
| 674 | * @sock: Pointer to "struct socket". | ||
| 675 | * @addr: Pointer to "struct sockaddr". | ||
| 676 | * @addr_len: Size of @addr. | ||
| 677 | * | ||
| 678 | * Returns 0 on success, negative value otherwise. | ||
| 679 | */ | ||
| 680 | int tomoyo_socket_connect_permission(struct socket *sock, | ||
| 681 | struct sockaddr *addr, int addr_len) | ||
| 682 | { | ||
| 683 | struct tomoyo_addr_info address; | ||
| 684 | const u8 family = tomoyo_sock_family(sock->sk); | ||
| 685 | const unsigned int type = sock->type; | ||
| 686 | |||
| 687 | if (!family) | ||
| 688 | return 0; | ||
| 689 | address.protocol = type; | ||
| 690 | switch (type) { | ||
| 691 | case SOCK_DGRAM: | ||
| 692 | case SOCK_RAW: | ||
| 693 | address.operation = TOMOYO_NETWORK_SEND; | ||
| 694 | break; | ||
| 695 | case SOCK_STREAM: | ||
| 696 | case SOCK_SEQPACKET: | ||
| 697 | address.operation = TOMOYO_NETWORK_CONNECT; | ||
| 698 | break; | ||
| 699 | default: | ||
| 700 | return 0; | ||
| 701 | } | ||
| 702 | if (family == PF_UNIX) | ||
| 703 | return tomoyo_check_unix_address(addr, addr_len, &address); | ||
| 704 | return tomoyo_check_inet_address(addr, addr_len, sock->sk->sk_protocol, | ||
| 705 | &address); | ||
| 706 | } | ||
| 707 | |||
| 708 | /** | ||
| 709 | * tomoyo_socket_bind_permission - Check permission for setting the local address of a socket. | ||
| 710 | * | ||
| 711 | * @sock: Pointer to "struct socket". | ||
| 712 | * @addr: Pointer to "struct sockaddr". | ||
| 713 | * @addr_len: Size of @addr. | ||
| 714 | * | ||
| 715 | * Returns 0 on success, negative value otherwise. | ||
| 716 | */ | ||
| 717 | int tomoyo_socket_bind_permission(struct socket *sock, struct sockaddr *addr, | ||
| 718 | int addr_len) | ||
| 719 | { | ||
| 720 | struct tomoyo_addr_info address; | ||
| 721 | const u8 family = tomoyo_sock_family(sock->sk); | ||
| 722 | const unsigned int type = sock->type; | ||
| 723 | |||
| 724 | if (!family) | ||
| 725 | return 0; | ||
| 726 | switch (type) { | ||
| 727 | case SOCK_STREAM: | ||
| 728 | case SOCK_DGRAM: | ||
| 729 | case SOCK_RAW: | ||
| 730 | case SOCK_SEQPACKET: | ||
| 731 | address.protocol = type; | ||
| 732 | address.operation = TOMOYO_NETWORK_BIND; | ||
| 733 | break; | ||
| 734 | default: | ||
| 735 | return 0; | ||
| 736 | } | ||
| 737 | if (family == PF_UNIX) | ||
| 738 | return tomoyo_check_unix_address(addr, addr_len, &address); | ||
| 739 | return tomoyo_check_inet_address(addr, addr_len, sock->sk->sk_protocol, | ||
| 740 | &address); | ||
| 741 | } | ||
| 742 | |||
| 743 | /** | ||
| 744 | * tomoyo_socket_sendmsg_permission - Check permission for sending a datagram. | ||
| 745 | * | ||
| 746 | * @sock: Pointer to "struct socket". | ||
| 747 | * @msg: Pointer to "struct msghdr". | ||
| 748 | * @size: Unused. | ||
| 749 | * | ||
| 750 | * Returns 0 on success, negative value otherwise. | ||
| 751 | */ | ||
| 752 | int tomoyo_socket_sendmsg_permission(struct socket *sock, struct msghdr *msg, | ||
| 753 | int size) | ||
| 754 | { | ||
| 755 | struct tomoyo_addr_info address; | ||
| 756 | const u8 family = tomoyo_sock_family(sock->sk); | ||
| 757 | const unsigned int type = sock->type; | ||
| 758 | |||
| 759 | if (!msg->msg_name || !family || | ||
| 760 | (type != SOCK_DGRAM && type != SOCK_RAW)) | ||
| 761 | return 0; | ||
| 762 | address.protocol = type; | ||
| 763 | address.operation = TOMOYO_NETWORK_SEND; | ||
| 764 | if (family == PF_UNIX) | ||
| 765 | return tomoyo_check_unix_address((struct sockaddr *) | ||
| 766 | msg->msg_name, | ||
| 767 | msg->msg_namelen, &address); | ||
| 768 | return tomoyo_check_inet_address((struct sockaddr *) msg->msg_name, | ||
| 769 | msg->msg_namelen, | ||
| 770 | sock->sk->sk_protocol, &address); | ||
| 771 | } | ||
diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index 6c601bd300f3..738bbdf8d4c7 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c | |||
| @@ -15,17 +15,19 @@ | |||
| 15 | #include "../../fs/internal.h" | 15 | #include "../../fs/internal.h" |
| 16 | 16 | ||
| 17 | /** | 17 | /** |
| 18 | * tomoyo_encode: Convert binary string to ascii string. | 18 | * tomoyo_encode2 - Encode binary string to ascii string. |
| 19 | * | 19 | * |
| 20 | * @str: String in binary format. | 20 | * @str: String in binary format. |
| 21 | * @str_len: Size of @str in byte. | ||
| 21 | * | 22 | * |
| 22 | * Returns pointer to @str in ascii format on success, NULL otherwise. | 23 | * Returns pointer to @str in ascii format on success, NULL otherwise. |
| 23 | * | 24 | * |
| 24 | * This function uses kzalloc(), so caller must kfree() if this function | 25 | * This function uses kzalloc(), so caller must kfree() if this function |
| 25 | * didn't return NULL. | 26 | * didn't return NULL. |
| 26 | */ | 27 | */ |
| 27 | char *tomoyo_encode(const char *str) | 28 | char *tomoyo_encode2(const char *str, int str_len) |
| 28 | { | 29 | { |
| 30 | int i; | ||
| 29 | int len = 0; | 31 | int len = 0; |
| 30 | const char *p = str; | 32 | const char *p = str; |
| 31 | char *cp; | 33 | char *cp; |
| @@ -33,8 +35,9 @@ char *tomoyo_encode(const char *str) | |||
| 33 | 35 | ||
| 34 | if (!p) | 36 | if (!p) |
| 35 | return NULL; | 37 | return NULL; |
| 36 | while (*p) { | 38 | for (i = 0; i < str_len; i++) { |
| 37 | const unsigned char c = *p++; | 39 | const unsigned char c = p[i]; |
| 40 | |||
| 38 | if (c == '\\') | 41 | if (c == '\\') |
| 39 | len += 2; | 42 | len += 2; |
| 40 | else if (c > ' ' && c < 127) | 43 | else if (c > ' ' && c < 127) |
| @@ -49,8 +52,8 @@ char *tomoyo_encode(const char *str) | |||
| 49 | return NULL; | 52 | return NULL; |
| 50 | cp0 = cp; | 53 | cp0 = cp; |
| 51 | p = str; | 54 | p = str; |
| 52 | while (*p) { | 55 | for (i = 0; i < str_len; i++) { |
| 53 | const unsigned char c = *p++; | 56 | const unsigned char c = p[i]; |
| 54 | 57 | ||
| 55 | if (c == '\\') { | 58 | if (c == '\\') { |
| 56 | *cp++ = '\\'; | 59 | *cp++ = '\\'; |
| @@ -68,6 +71,21 @@ char *tomoyo_encode(const char *str) | |||
| 68 | } | 71 | } |
| 69 | 72 | ||
| 70 | /** | 73 | /** |
| 74 | * tomoyo_encode - Encode binary string to ascii string. | ||
| 75 | * | ||
| 76 | * @str: String in binary format. | ||
| 77 | * | ||
| 78 | * Returns pointer to @str in ascii format on success, NULL otherwise. | ||
| 79 | * | ||
| 80 | * This function uses kzalloc(), so caller must kfree() if this function | ||
| 81 | * didn't return NULL. | ||
| 82 | */ | ||
| 83 | char *tomoyo_encode(const char *str) | ||
| 84 | { | ||
| 85 | return str ? tomoyo_encode2(str, strlen(str)) : NULL; | ||
| 86 | } | ||
| 87 | |||
| 88 | /** | ||
| 71 | * tomoyo_get_absolute_path - Get the path of a dentry but ignores chroot'ed root. | 89 | * tomoyo_get_absolute_path - Get the path of a dentry but ignores chroot'ed root. |
| 72 | * | 90 | * |
| 73 | * @path: Pointer to "struct path". | 91 | * @path: Pointer to "struct path". |
diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c index a49c3bfd4dd5..2672ac4f3beb 100644 --- a/security/tomoyo/securityfs_if.c +++ b/security/tomoyo/securityfs_if.c | |||
| @@ -8,6 +8,124 @@ | |||
| 8 | #include "common.h" | 8 | #include "common.h" |
| 9 | 9 | ||
| 10 | /** | 10 | /** |
| 11 | * tomoyo_check_task_acl - Check permission for task operation. | ||
| 12 | * | ||
| 13 | * @r: Pointer to "struct tomoyo_request_info". | ||
| 14 | * @ptr: Pointer to "struct tomoyo_acl_info". | ||
| 15 | * | ||
| 16 | * Returns true if granted, false otherwise. | ||
| 17 | */ | ||
| 18 | static bool tomoyo_check_task_acl(struct tomoyo_request_info *r, | ||
| 19 | const struct tomoyo_acl_info *ptr) | ||
| 20 | { | ||
| 21 | const struct tomoyo_task_acl *acl = container_of(ptr, typeof(*acl), | ||
| 22 | head); | ||
| 23 | return !tomoyo_pathcmp(r->param.task.domainname, acl->domainname); | ||
| 24 | } | ||
| 25 | |||
| 26 | /** | ||
| 27 | * tomoyo_write_self - write() for /sys/kernel/security/tomoyo/self_domain interface. | ||
| 28 | * | ||
| 29 | * @file: Pointer to "struct file". | ||
| 30 | * @buf: Domainname to transit to. | ||
| 31 | * @count: Size of @buf. | ||
| 32 | * @ppos: Unused. | ||
| 33 | * | ||
| 34 | * Returns @count on success, negative value otherwise. | ||
| 35 | * | ||
| 36 | * If domain transition was permitted but the domain transition failed, this | ||
| 37 | * function returns error rather than terminating current thread with SIGKILL. | ||
| 38 | */ | ||
| 39 | static ssize_t tomoyo_write_self(struct file *file, const char __user *buf, | ||
| 40 | size_t count, loff_t *ppos) | ||
| 41 | { | ||
| 42 | char *data; | ||
| 43 | int error; | ||
| 44 | if (!count || count >= TOMOYO_EXEC_TMPSIZE - 10) | ||
| 45 | return -ENOMEM; | ||
| 46 | data = kzalloc(count + 1, GFP_NOFS); | ||
| 47 | if (!data) | ||
| 48 | return -ENOMEM; | ||
| 49 | if (copy_from_user(data, buf, count)) { | ||
| 50 | error = -EFAULT; | ||
| 51 | goto out; | ||
| 52 | } | ||
| 53 | tomoyo_normalize_line(data); | ||
| 54 | if (tomoyo_correct_domain(data)) { | ||
| 55 | const int idx = tomoyo_read_lock(); | ||
| 56 | struct tomoyo_path_info name; | ||
| 57 | struct tomoyo_request_info r; | ||
| 58 | name.name = data; | ||
| 59 | tomoyo_fill_path_info(&name); | ||
| 60 | /* Check "task manual_domain_transition" permission. */ | ||
| 61 | tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE); | ||
| 62 | r.param_type = TOMOYO_TYPE_MANUAL_TASK_ACL; | ||
| 63 | r.param.task.domainname = &name; | ||
| 64 | tomoyo_check_acl(&r, tomoyo_check_task_acl); | ||
| 65 | if (!r.granted) | ||
| 66 | error = -EPERM; | ||
| 67 | else { | ||
| 68 | struct tomoyo_domain_info *new_domain = | ||
| 69 | tomoyo_assign_domain(data, true); | ||
| 70 | if (!new_domain) { | ||
| 71 | error = -ENOENT; | ||
| 72 | } else { | ||
| 73 | struct cred *cred = prepare_creds(); | ||
| 74 | if (!cred) { | ||
| 75 | error = -ENOMEM; | ||
| 76 | } else { | ||
| 77 | struct tomoyo_domain_info *old_domain = | ||
| 78 | cred->security; | ||
| 79 | cred->security = new_domain; | ||
| 80 | atomic_inc(&new_domain->users); | ||
| 81 | atomic_dec(&old_domain->users); | ||
| 82 | commit_creds(cred); | ||
| 83 | error = 0; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | } | ||
| 87 | tomoyo_read_unlock(idx); | ||
| 88 | } else | ||
| 89 | error = -EINVAL; | ||
| 90 | out: | ||
| 91 | kfree(data); | ||
| 92 | return error ? error : count; | ||
| 93 | } | ||
| 94 | |||
| 95 | /** | ||
| 96 | * tomoyo_read_self - read() for /sys/kernel/security/tomoyo/self_domain interface. | ||
| 97 | * | ||
| 98 | * @file: Pointer to "struct file". | ||
| 99 | * @buf: Domainname which current thread belongs to. | ||
| 100 | * @count: Size of @buf. | ||
| 101 | * @ppos: Bytes read by now. | ||
| 102 | * | ||
| 103 | * Returns read size on success, negative value otherwise. | ||
| 104 | */ | ||
| 105 | static ssize_t tomoyo_read_self(struct file *file, char __user *buf, | ||
| 106 | size_t count, loff_t *ppos) | ||
| 107 | { | ||
| 108 | const char *domain = tomoyo_domain()->domainname->name; | ||
| 109 | loff_t len = strlen(domain); | ||
| 110 | loff_t pos = *ppos; | ||
| 111 | if (pos >= len || !count) | ||
| 112 | return 0; | ||
| 113 | len -= pos; | ||
| 114 | if (count < len) | ||
| 115 | len = count; | ||
| 116 | if (copy_to_user(buf, domain + pos, len)) | ||
| 117 | return -EFAULT; | ||
| 118 | *ppos += len; | ||
| 119 | return len; | ||
| 120 | } | ||
| 121 | |||
| 122 | /* Operations for /sys/kernel/security/tomoyo/self_domain interface. */ | ||
| 123 | static const struct file_operations tomoyo_self_operations = { | ||
| 124 | .write = tomoyo_write_self, | ||
| 125 | .read = tomoyo_read_self, | ||
| 126 | }; | ||
| 127 | |||
| 128 | /** | ||
| 11 | * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface. | 129 | * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface. |
| 12 | * | 130 | * |
| 13 | * @inode: Pointer to "struct inode". | 131 | * @inode: Pointer to "struct inode". |
| @@ -135,8 +253,6 @@ static int __init tomoyo_initerface_init(void) | |||
| 135 | TOMOYO_EXCEPTIONPOLICY); | 253 | TOMOYO_EXCEPTIONPOLICY); |
| 136 | tomoyo_create_entry("audit", 0400, tomoyo_dir, | 254 | tomoyo_create_entry("audit", 0400, tomoyo_dir, |
| 137 | TOMOYO_AUDIT); | 255 | TOMOYO_AUDIT); |
| 138 | tomoyo_create_entry("self_domain", 0400, tomoyo_dir, | ||
| 139 | TOMOYO_SELFDOMAIN); | ||
| 140 | tomoyo_create_entry(".process_status", 0600, tomoyo_dir, | 256 | tomoyo_create_entry(".process_status", 0600, tomoyo_dir, |
| 141 | TOMOYO_PROCESS_STATUS); | 257 | TOMOYO_PROCESS_STATUS); |
| 142 | tomoyo_create_entry("stat", 0644, tomoyo_dir, | 258 | tomoyo_create_entry("stat", 0644, tomoyo_dir, |
| @@ -147,6 +263,9 @@ static int __init tomoyo_initerface_init(void) | |||
| 147 | TOMOYO_MANAGER); | 263 | TOMOYO_MANAGER); |
| 148 | tomoyo_create_entry("version", 0400, tomoyo_dir, | 264 | tomoyo_create_entry("version", 0400, tomoyo_dir, |
| 149 | TOMOYO_VERSION); | 265 | TOMOYO_VERSION); |
| 266 | securityfs_create_file("self_domain", 0666, tomoyo_dir, NULL, | ||
| 267 | &tomoyo_self_operations); | ||
| 268 | tomoyo_load_builtin_policy(); | ||
| 150 | return 0; | 269 | return 0; |
| 151 | } | 270 | } |
| 152 | 271 | ||
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index f776400a8f31..4b327b691745 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c | |||
| @@ -442,6 +442,64 @@ static int tomoyo_sb_pivotroot(struct path *old_path, struct path *new_path) | |||
| 442 | return tomoyo_path2_perm(TOMOYO_TYPE_PIVOT_ROOT, new_path, old_path); | 442 | return tomoyo_path2_perm(TOMOYO_TYPE_PIVOT_ROOT, new_path, old_path); |
| 443 | } | 443 | } |
| 444 | 444 | ||
| 445 | /** | ||
| 446 | * tomoyo_socket_listen - Check permission for listen(). | ||
| 447 | * | ||
| 448 | * @sock: Pointer to "struct socket". | ||
| 449 | * @backlog: Backlog parameter. | ||
| 450 | * | ||
| 451 | * Returns 0 on success, negative value otherwise. | ||
| 452 | */ | ||
| 453 | static int tomoyo_socket_listen(struct socket *sock, int backlog) | ||
| 454 | { | ||
| 455 | return tomoyo_socket_listen_permission(sock); | ||
| 456 | } | ||
| 457 | |||
| 458 | /** | ||
| 459 | * tomoyo_socket_connect - Check permission for connect(). | ||
| 460 | * | ||
| 461 | * @sock: Pointer to "struct socket". | ||
| 462 | * @addr: Pointer to "struct sockaddr". | ||
| 463 | * @addr_len: Size of @addr. | ||
| 464 | * | ||
| 465 | * Returns 0 on success, negative value otherwise. | ||
| 466 | */ | ||
| 467 | static int tomoyo_socket_connect(struct socket *sock, struct sockaddr *addr, | ||
| 468 | int addr_len) | ||
| 469 | { | ||
| 470 | return tomoyo_socket_connect_permission(sock, addr, addr_len); | ||
| 471 | } | ||
| 472 | |||
| 473 | /** | ||
| 474 | * tomoyo_socket_bind - Check permission for bind(). | ||
| 475 | * | ||
| 476 | * @sock: Pointer to "struct socket". | ||
| 477 | * @addr: Pointer to "struct sockaddr". | ||
| 478 | * @addr_len: Size of @addr. | ||
| 479 | * | ||
| 480 | * Returns 0 on success, negative value otherwise. | ||
| 481 | */ | ||
| 482 | static int tomoyo_socket_bind(struct socket *sock, struct sockaddr *addr, | ||
| 483 | int addr_len) | ||
| 484 | { | ||
| 485 | return tomoyo_socket_bind_permission(sock, addr, addr_len); | ||
| 486 | } | ||
| 487 | |||
| 488 | /** | ||
| 489 | * tomoyo_socket_sendmsg - Check permission for sendmsg(). | ||
| 490 | * | ||
| 491 | * @sock: Pointer to "struct socket". | ||
| 492 | * @msg: Pointer to "struct msghdr". | ||
| 493 | * @size: Size of message. | ||
| 494 | * | ||
| 495 | * Returns 0 on success, negative value otherwise. | ||
| 496 | */ | ||
| 497 | static int tomoyo_socket_sendmsg(struct socket *sock, struct msghdr *msg, | ||
| 498 | int size) | ||
| 499 | { | ||
| 500 | return tomoyo_socket_sendmsg_permission(sock, msg, size); | ||
| 501 | } | ||
| 502 | |||
| 445 | /* | 503 | /* |
| 446 | * tomoyo_security_ops is a "struct security_operations" which is used for | 504 | * tomoyo_security_ops is a "struct security_operations" which is used for |
| 447 | * registering TOMOYO. | 505 | * registering TOMOYO. |
| @@ -472,6 +530,10 @@ static struct security_operations tomoyo_security_ops = { | |||
| 472 | .sb_mount = tomoyo_sb_mount, | 530 | .sb_mount = tomoyo_sb_mount, |
| 473 | .sb_umount = tomoyo_sb_umount, | 531 | .sb_umount = tomoyo_sb_umount, |
| 474 | .sb_pivotroot = tomoyo_sb_pivotroot, | 532 | .sb_pivotroot = tomoyo_sb_pivotroot, |
| 533 | .socket_bind = tomoyo_socket_bind, | ||
| 534 | .socket_connect = tomoyo_socket_connect, | ||
| 535 | .socket_listen = tomoyo_socket_listen, | ||
| 536 | .socket_sendmsg = tomoyo_socket_sendmsg, | ||
| 475 | }; | 537 | }; |
| 476 | 538 | ||
| 477 | /* Lock for GC. */ | 539 | /* Lock for GC. */ |
diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index c36bd1107fc8..4a9b4b2eb755 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c | |||
| @@ -42,6 +42,39 @@ const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX] = { | |||
| 42 | [TOMOYO_MAC_FILE_MOUNT] = TOMOYO_MAC_CATEGORY_FILE, | 42 | [TOMOYO_MAC_FILE_MOUNT] = TOMOYO_MAC_CATEGORY_FILE, |
| 43 | [TOMOYO_MAC_FILE_UMOUNT] = TOMOYO_MAC_CATEGORY_FILE, | 43 | [TOMOYO_MAC_FILE_UMOUNT] = TOMOYO_MAC_CATEGORY_FILE, |
| 44 | [TOMOYO_MAC_FILE_PIVOT_ROOT] = TOMOYO_MAC_CATEGORY_FILE, | 44 | [TOMOYO_MAC_FILE_PIVOT_ROOT] = TOMOYO_MAC_CATEGORY_FILE, |
| 45 | /* CONFIG::network group */ | ||
| 46 | [TOMOYO_MAC_NETWORK_INET_STREAM_BIND] = | ||
| 47 | TOMOYO_MAC_CATEGORY_NETWORK, | ||
| 48 | [TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN] = | ||
| 49 | TOMOYO_MAC_CATEGORY_NETWORK, | ||
| 50 | [TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT] = | ||
| 51 | TOMOYO_MAC_CATEGORY_NETWORK, | ||
| 52 | [TOMOYO_MAC_NETWORK_INET_DGRAM_BIND] = | ||
| 53 | TOMOYO_MAC_CATEGORY_NETWORK, | ||
| 54 | [TOMOYO_MAC_NETWORK_INET_DGRAM_SEND] = | ||
| 55 | TOMOYO_MAC_CATEGORY_NETWORK, | ||
| 56 | [TOMOYO_MAC_NETWORK_INET_RAW_BIND] = | ||
| 57 | TOMOYO_MAC_CATEGORY_NETWORK, | ||
| 58 | [TOMOYO_MAC_NETWORK_INET_RAW_SEND] = | ||
| 59 | TOMOYO_MAC_CATEGORY_NETWORK, | ||
| 60 | [TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND] = | ||
| 61 | TOMOYO_MAC_CATEGORY_NETWORK, | ||
| 62 | [TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN] = | ||
| 63 | TOMOYO_MAC_CATEGORY_NETWORK, | ||
| 64 | [TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT] = | ||
| 65 | TOMOYO_MAC_CATEGORY_NETWORK, | ||
| 66 | [TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND] = | ||
| 67 | TOMOYO_MAC_CATEGORY_NETWORK, | ||
| 68 | [TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND] = | ||
| 69 | TOMOYO_MAC_CATEGORY_NETWORK, | ||
| 70 | [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND] = | ||
| 71 | TOMOYO_MAC_CATEGORY_NETWORK, | ||
| 72 | [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN] = | ||
| 73 | TOMOYO_MAC_CATEGORY_NETWORK, | ||
| 74 | [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] = | ||
| 75 | TOMOYO_MAC_CATEGORY_NETWORK, | ||
| 76 | /* CONFIG::misc group */ | ||
| 77 | [TOMOYO_MAC_ENVIRON] = TOMOYO_MAC_CATEGORY_MISC, | ||
| 45 | }; | 78 | }; |
| 46 | 79 | ||
| 47 | /** | 80 | /** |
| @@ -126,6 +159,31 @@ char *tomoyo_read_token(struct tomoyo_acl_param *param) | |||
| 126 | } | 159 | } |
| 127 | 160 | ||
| 128 | /** | 161 | /** |
| 162 | * tomoyo_get_domainname - Read a domainname from a line. | ||
| 163 | * | ||
| 164 | * @param: Pointer to "struct tomoyo_acl_param". | ||
| 165 | * | ||
| 166 | * Returns a domainname on success, NULL otherwise. | ||
| 167 | */ | ||
| 168 | const struct tomoyo_path_info *tomoyo_get_domainname | ||
| 169 | (struct tomoyo_acl_param *param) | ||
| 170 | { | ||
| 171 | char *start = param->data; | ||
| 172 | char *pos = start; | ||
| 173 | while (*pos) { | ||
| 174 | if (*pos++ != ' ' || *pos++ == '/') | ||
| 175 | continue; | ||
| 176 | pos -= 2; | ||
| 177 | *pos++ = '\0'; | ||
| 178 | break; | ||
| 179 | } | ||
| 180 | param->data = pos; | ||
| 181 | if (tomoyo_correct_domain(start)) | ||
| 182 | return tomoyo_get_name(start); | ||
| 183 | return NULL; | ||
| 184 | } | ||
| 185 | |||
| 186 | /** | ||
| 129 | * tomoyo_parse_ulong - Parse an "unsigned long" value. | 187 | * tomoyo_parse_ulong - Parse an "unsigned long" value. |
| 130 | * | 188 | * |
| 131 | * @result: Pointer to "unsigned long". | 189 | * @result: Pointer to "unsigned long". |
| @@ -920,14 +978,17 @@ int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile, | |||
| 920 | const u8 index) | 978 | const u8 index) |
| 921 | { | 979 | { |
| 922 | u8 mode; | 980 | u8 mode; |
| 923 | const u8 category = TOMOYO_MAC_CATEGORY_FILE; | 981 | struct tomoyo_profile *p; |
| 982 | |||
| 924 | if (!tomoyo_policy_loaded) | 983 | if (!tomoyo_policy_loaded) |
| 925 | return TOMOYO_CONFIG_DISABLED; | 984 | return TOMOYO_CONFIG_DISABLED; |
| 926 | mode = tomoyo_profile(ns, profile)->config[index]; | 985 | p = tomoyo_profile(ns, profile); |
| 986 | mode = p->config[index]; | ||
| 927 | if (mode == TOMOYO_CONFIG_USE_DEFAULT) | 987 | if (mode == TOMOYO_CONFIG_USE_DEFAULT) |
| 928 | mode = tomoyo_profile(ns, profile)->config[category]; | 988 | mode = p->config[tomoyo_index2category[index] |
| 989 | + TOMOYO_MAX_MAC_INDEX]; | ||
| 929 | if (mode == TOMOYO_CONFIG_USE_DEFAULT) | 990 | if (mode == TOMOYO_CONFIG_USE_DEFAULT) |
| 930 | mode = tomoyo_profile(ns, profile)->default_config; | 991 | mode = p->default_config; |
| 931 | return mode & 3; | 992 | return mode & 3; |
| 932 | } | 993 | } |
| 933 | 994 | ||
| @@ -996,6 +1057,17 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) | |||
| 996 | perm = container_of(ptr, struct tomoyo_mkdev_acl, | 1057 | perm = container_of(ptr, struct tomoyo_mkdev_acl, |
| 997 | head)->perm; | 1058 | head)->perm; |
| 998 | break; | 1059 | break; |
| 1060 | case TOMOYO_TYPE_INET_ACL: | ||
| 1061 | perm = container_of(ptr, struct tomoyo_inet_acl, | ||
| 1062 | head)->perm; | ||
| 1063 | break; | ||
| 1064 | case TOMOYO_TYPE_UNIX_ACL: | ||
| 1065 | perm = container_of(ptr, struct tomoyo_unix_acl, | ||
| 1066 | head)->perm; | ||
| 1067 | break; | ||
| 1068 | case TOMOYO_TYPE_MANUAL_TASK_ACL: | ||
| 1069 | perm = 0; | ||
| 1070 | break; | ||
| 999 | default: | 1071 | default: |
| 1000 | perm = 1; | 1072 | perm = 1; |
| 1001 | } | 1073 | } |
