diff options
author | Mimi Zohar <zohar@linux.vnet.ibm.com> | 2012-02-13 10:15:05 -0500 |
---|---|---|
committer | Mimi Zohar <zohar@linux.vnet.ibm.com> | 2012-09-07 14:57:44 -0400 |
commit | 2fe5d6def1672ae6635dd71867bf36dcfaa7434b (patch) | |
tree | f83878d309605440b5bc2d2d43a16ccece64c645 /security/integrity | |
parent | 4199d35cbc90c15db447d115bd96ffa5f1d60d3a (diff) |
ima: integrity appraisal extension
IMA currently maintains an integrity measurement list used to assert the
integrity of the running system to a third party. The IMA-appraisal
extension adds local integrity validation and enforcement of the
measurement against a "good" value stored as an extended attribute
'security.ima'. The initial methods for validating 'security.ima' are
hashed based, which provides file data integrity, and digital signature
based, which in addition to providing file data integrity, provides
authenticity.
This patch creates and maintains the 'security.ima' xattr, containing
the file data hash measurement. Protection of the xattr is provided by
EVM, if enabled and configured.
Based on policy, IMA calls evm_verifyxattr() to verify a file's metadata
integrity and, assuming success, compares the file's current hash value
with the one stored as an extended attribute in 'security.ima'.
Changelov v4:
- changed iint cache flags to hex values
Changelog v3:
- change appraisal default for filesystems without xattr support to fail
Changelog v2:
- fix audit msg 'res' value
- removed unused 'ima_appraise=' values
Changelog v1:
- removed unused iint mutex (Dmitry Kasatkin)
- setattr hook must not reset appraised (Dmitry Kasatkin)
- evm_verifyxattr() now differentiates between no 'security.evm' xattr
(INTEGRITY_NOLABEL) and no EVM 'protected' xattrs included in the
'security.evm' (INTEGRITY_NOXATTRS).
- replace hash_status with ima_status (Dmitry Kasatkin)
- re-initialize slab element ima_status on free (Dmitry Kasatkin)
- include 'security.ima' in EVM if CONFIG_IMA_APPRAISE, not CONFIG_IMA
- merged half "ima: ima_must_appraise_or_measure API change" (Dmitry Kasatkin)
- removed unnecessary error variable in process_measurement() (Dmitry Kasatkin)
- use ima_inode_post_setattr() stub function, if IMA_APPRAISE not configured
(moved ima_inode_post_setattr() to ima_appraise.c)
- make sure ima_collect_measurement() can read file
Changelog:
- add 'iint' to evm_verifyxattr() call (Dimitry Kasatkin)
- fix the race condition between chmod, which takes the i_mutex and then
iint->mutex, and ima_file_free() and process_measurement(), which take
the locks in the reverse order, by eliminating iint->mutex. (Dmitry Kasatkin)
- cleanup of ima_appraise_measurement() (Dmitry Kasatkin)
- changes as a result of the iint not allocated for all regular files, but
only for those measured/appraised.
- don't try to appraise new/empty files
- expanded ima_appraisal description in ima/Kconfig
- IMA appraise definitions required even if IMA_APPRAISE not enabled
- add return value to ima_must_appraise() stub
- unconditionally set status = INTEGRITY_PASS *after* testing status,
not before. (Found by Joe Perches)
Signed-off-by: Mimi Zohar <zohar@us.ibm.com>
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
Diffstat (limited to 'security/integrity')
-rw-r--r-- | security/integrity/evm/evm_main.c | 3 | ||||
-rw-r--r-- | security/integrity/iint.c | 3 | ||||
-rw-r--r-- | security/integrity/ima/Kconfig | 15 | ||||
-rw-r--r-- | security/integrity/ima/Makefile | 1 | ||||
-rw-r--r-- | security/integrity/ima/ima.h | 37 | ||||
-rw-r--r-- | security/integrity/ima/ima_api.c | 50 | ||||
-rw-r--r-- | security/integrity/ima/ima_appraise.c | 168 | ||||
-rw-r--r-- | security/integrity/ima/ima_crypto.c | 8 | ||||
-rw-r--r-- | security/integrity/ima/ima_main.c | 79 | ||||
-rw-r--r-- | security/integrity/ima/ima_policy.c | 32 | ||||
-rw-r--r-- | security/integrity/integrity.h | 8 |
11 files changed, 351 insertions, 53 deletions
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 8901501425f4..eb5484504f50 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c | |||
@@ -34,6 +34,9 @@ char *evm_config_xattrnames[] = { | |||
34 | #ifdef CONFIG_SECURITY_SMACK | 34 | #ifdef CONFIG_SECURITY_SMACK |
35 | XATTR_NAME_SMACK, | 35 | XATTR_NAME_SMACK, |
36 | #endif | 36 | #endif |
37 | #ifdef CONFIG_IMA_APPRAISE | ||
38 | XATTR_NAME_IMA, | ||
39 | #endif | ||
37 | XATTR_NAME_CAPS, | 40 | XATTR_NAME_CAPS, |
38 | NULL | 41 | NULL |
39 | }; | 42 | }; |
diff --git a/security/integrity/iint.c b/security/integrity/iint.c index 399641c3e846..e600986aa49f 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c | |||
@@ -74,6 +74,7 @@ static void iint_free(struct integrity_iint_cache *iint) | |||
74 | { | 74 | { |
75 | iint->version = 0; | 75 | iint->version = 0; |
76 | iint->flags = 0UL; | 76 | iint->flags = 0UL; |
77 | iint->ima_status = INTEGRITY_UNKNOWN; | ||
77 | iint->evm_status = INTEGRITY_UNKNOWN; | 78 | iint->evm_status = INTEGRITY_UNKNOWN; |
78 | kmem_cache_free(iint_cache, iint); | 79 | kmem_cache_free(iint_cache, iint); |
79 | } | 80 | } |
@@ -157,7 +158,7 @@ static void init_once(void *foo) | |||
157 | memset(iint, 0, sizeof *iint); | 158 | memset(iint, 0, sizeof *iint); |
158 | iint->version = 0; | 159 | iint->version = 0; |
159 | iint->flags = 0UL; | 160 | iint->flags = 0UL; |
160 | mutex_init(&iint->mutex); | 161 | iint->ima_status = INTEGRITY_UNKNOWN; |
161 | iint->evm_status = INTEGRITY_UNKNOWN; | 162 | iint->evm_status = INTEGRITY_UNKNOWN; |
162 | } | 163 | } |
163 | 164 | ||
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 809ccf19d09c..d232c73647ae 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig | |||
@@ -56,3 +56,18 @@ config IMA_LSM_RULES | |||
56 | default y | 56 | default y |
57 | help | 57 | help |
58 | Disabling this option will disregard LSM based policy rules. | 58 | Disabling this option will disregard LSM based policy rules. |
59 | |||
60 | config IMA_APPRAISE | ||
61 | bool "Appraise integrity measurements" | ||
62 | depends on IMA | ||
63 | default n | ||
64 | help | ||
65 | This option enables local measurement integrity appraisal. | ||
66 | It requires the system to be labeled with a security extended | ||
67 | attribute containing the file hash measurement. To protect | ||
68 | the security extended attributes from offline attack, enable | ||
69 | and configure EVM. | ||
70 | |||
71 | For more information on integrity appraisal refer to: | ||
72 | <http://linux-ima.sourceforge.net> | ||
73 | If unsure, say N. | ||
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index 5f740f6971e1..3f2ca6bdc384 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile | |||
@@ -8,3 +8,4 @@ obj-$(CONFIG_IMA) += ima.o | |||
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 | 9 | ima_policy.o |
10 | ima-$(CONFIG_IMA_AUDIT) += ima_audit.o | 10 | ima-$(CONFIG_IMA_AUDIT) += ima_audit.o |
11 | ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o | ||
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index e7c99fd0d223..069a4aa63e95 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h | |||
@@ -40,6 +40,7 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 }; | |||
40 | extern int ima_initialized; | 40 | extern int ima_initialized; |
41 | extern int ima_used_chip; | 41 | extern int ima_used_chip; |
42 | extern char *ima_hash; | 42 | extern char *ima_hash; |
43 | extern int ima_appraise; | ||
43 | 44 | ||
44 | /* IMA inode template definition */ | 45 | /* IMA inode template definition */ |
45 | struct ima_template_data { | 46 | struct ima_template_data { |
@@ -107,6 +108,7 @@ static inline unsigned long ima_hash_key(u8 *digest) | |||
107 | } | 108 | } |
108 | 109 | ||
109 | /* LIM API function definitions */ | 110 | /* LIM API function definitions */ |
111 | int ima_must_appraise_or_measure(struct inode *inode, int mask, int function); | ||
110 | int ima_must_measure(struct inode *inode, int mask, int function); | 112 | int ima_must_measure(struct inode *inode, int mask, int function); |
111 | int ima_collect_measurement(struct integrity_iint_cache *iint, | 113 | int ima_collect_measurement(struct integrity_iint_cache *iint, |
112 | struct file *file); | 114 | struct file *file); |
@@ -123,14 +125,45 @@ struct integrity_iint_cache *integrity_iint_insert(struct inode *inode); | |||
123 | struct integrity_iint_cache *integrity_iint_find(struct inode *inode); | 125 | struct integrity_iint_cache *integrity_iint_find(struct inode *inode); |
124 | 126 | ||
125 | /* IMA policy related functions */ | 127 | /* IMA policy related functions */ |
126 | enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK }; | 128 | enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK, POST_SETATTR }; |
127 | 129 | ||
128 | int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask); | 130 | int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, |
131 | int flags); | ||
129 | void ima_init_policy(void); | 132 | void ima_init_policy(void); |
130 | void ima_update_policy(void); | 133 | void ima_update_policy(void); |
131 | ssize_t ima_parse_add_rule(char *); | 134 | ssize_t ima_parse_add_rule(char *); |
132 | void ima_delete_rules(void); | 135 | void ima_delete_rules(void); |
133 | 136 | ||
137 | /* Appraise integrity measurements */ | ||
138 | #define IMA_APPRAISE_ENFORCE 0x01 | ||
139 | #define IMA_APPRAISE_FIX 0x02 | ||
140 | |||
141 | #ifdef CONFIG_IMA_APPRAISE | ||
142 | int ima_appraise_measurement(struct integrity_iint_cache *iint, | ||
143 | struct file *file, const unsigned char *filename); | ||
144 | int ima_must_appraise(struct inode *inode, enum ima_hooks func, int mask); | ||
145 | void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file); | ||
146 | |||
147 | #else | ||
148 | static inline int ima_appraise_measurement(struct integrity_iint_cache *iint, | ||
149 | struct file *file, | ||
150 | const unsigned char *filename) | ||
151 | { | ||
152 | return INTEGRITY_UNKNOWN; | ||
153 | } | ||
154 | |||
155 | static inline int ima_must_appraise(struct inode *inode, | ||
156 | enum ima_hooks func, int mask) | ||
157 | { | ||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | static inline void ima_update_xattr(struct integrity_iint_cache *iint, | ||
162 | struct file *file) | ||
163 | { | ||
164 | } | ||
165 | #endif | ||
166 | |||
134 | /* LSM based policy rules require audit */ | 167 | /* LSM based policy rules require audit */ |
135 | #ifdef CONFIG_IMA_LSM_RULES | 168 | #ifdef CONFIG_IMA_LSM_RULES |
136 | 169 | ||
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 032ff03ad907..41cce84416c5 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c | |||
@@ -9,13 +9,17 @@ | |||
9 | * License. | 9 | * License. |
10 | * | 10 | * |
11 | * File: ima_api.c | 11 | * File: ima_api.c |
12 | * Implements must_measure, collect_measurement, store_measurement, | 12 | * Implements must_appraise_or_measure, collect_measurement, |
13 | * and store_template. | 13 | * appraise_measurement, store_measurement and store_template. |
14 | */ | 14 | */ |
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/slab.h> | 16 | #include <linux/slab.h> |
17 | 17 | #include <linux/file.h> | |
18 | #include <linux/fs.h> | ||
19 | #include <linux/xattr.h> | ||
20 | #include <linux/evm.h> | ||
18 | #include "ima.h" | 21 | #include "ima.h" |
22 | |||
19 | static const char *IMA_TEMPLATE_NAME = "ima"; | 23 | static const char *IMA_TEMPLATE_NAME = "ima"; |
20 | 24 | ||
21 | /* | 25 | /* |
@@ -93,7 +97,7 @@ err_out: | |||
93 | } | 97 | } |
94 | 98 | ||
95 | /** | 99 | /** |
96 | * ima_must_measure - measure decision based on policy. | 100 | * ima_must_appraise_or_measure - appraise & measure decision based on policy. |
97 | * @inode: pointer to inode to measure | 101 | * @inode: pointer to inode to measure |
98 | * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE) | 102 | * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE) |
99 | * @function: calling function (FILE_CHECK, BPRM_CHECK, FILE_MMAP) | 103 | * @function: calling function (FILE_CHECK, BPRM_CHECK, FILE_MMAP) |
@@ -105,15 +109,22 @@ err_out: | |||
105 | * mask: contains the permission mask | 109 | * mask: contains the permission mask |
106 | * fsmagic: hex value | 110 | * fsmagic: hex value |
107 | * | 111 | * |
108 | * Return 0 to measure. For matching a DONT_MEASURE policy, no policy, | 112 | * Returns IMA_MEASURE, IMA_APPRAISE mask. |
109 | * or other error, return an error code. | 113 | * |
110 | */ | 114 | */ |
111 | int ima_must_measure(struct inode *inode, int mask, int function) | 115 | int ima_must_appraise_or_measure(struct inode *inode, int mask, int function) |
112 | { | 116 | { |
113 | int must_measure; | 117 | int flags = IMA_MEASURE | IMA_APPRAISE; |
118 | |||
119 | if (!ima_appraise) | ||
120 | flags &= ~IMA_APPRAISE; | ||
121 | |||
122 | return ima_match_policy(inode, function, mask, flags); | ||
123 | } | ||
114 | 124 | ||
115 | must_measure = ima_match_policy(inode, function, mask); | 125 | int ima_must_measure(struct inode *inode, int mask, int function) |
116 | return must_measure ? 0 : -EACCES; | 126 | { |
127 | return ima_match_policy(inode, function, mask, IMA_MEASURE); | ||
117 | } | 128 | } |
118 | 129 | ||
119 | /* | 130 | /* |
@@ -129,16 +140,24 @@ int ima_must_measure(struct inode *inode, int mask, int function) | |||
129 | int ima_collect_measurement(struct integrity_iint_cache *iint, | 140 | int ima_collect_measurement(struct integrity_iint_cache *iint, |
130 | struct file *file) | 141 | struct file *file) |
131 | { | 142 | { |
132 | int result = -EEXIST; | 143 | struct inode *inode = file->f_dentry->d_inode; |
144 | const char *filename = file->f_dentry->d_name.name; | ||
145 | int result = 0; | ||
133 | 146 | ||
134 | if (!(iint->flags & IMA_MEASURED)) { | 147 | if (!(iint->flags & IMA_COLLECTED)) { |
135 | u64 i_version = file->f_dentry->d_inode->i_version; | 148 | u64 i_version = file->f_dentry->d_inode->i_version; |
136 | 149 | ||
137 | memset(iint->digest, 0, IMA_DIGEST_SIZE); | 150 | memset(iint->digest, 0, IMA_DIGEST_SIZE); |
138 | result = ima_calc_hash(file, iint->digest); | 151 | result = ima_calc_hash(file, iint->digest); |
139 | if (!result) | 152 | if (!result) { |
140 | iint->version = i_version; | 153 | iint->version = i_version; |
154 | iint->flags |= IMA_COLLECTED; | ||
155 | } | ||
141 | } | 156 | } |
157 | if (result) | ||
158 | integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, | ||
159 | filename, "collect_data", "failed", | ||
160 | result, 0); | ||
142 | return result; | 161 | return result; |
143 | } | 162 | } |
144 | 163 | ||
@@ -167,6 +186,9 @@ void ima_store_measurement(struct integrity_iint_cache *iint, | |||
167 | struct ima_template_entry *entry; | 186 | struct ima_template_entry *entry; |
168 | int violation = 0; | 187 | int violation = 0; |
169 | 188 | ||
189 | if (iint->flags & IMA_MEASURED) | ||
190 | return; | ||
191 | |||
170 | entry = kmalloc(sizeof(*entry), GFP_KERNEL); | 192 | entry = kmalloc(sizeof(*entry), GFP_KERNEL); |
171 | if (!entry) { | 193 | if (!entry) { |
172 | integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, | 194 | integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, |
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c new file mode 100644 index 000000000000..4865f61f9044 --- /dev/null +++ b/security/integrity/ima/ima_appraise.c | |||
@@ -0,0 +1,168 @@ | |||
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 | #include <linux/module.h> | ||
12 | #include <linux/file.h> | ||
13 | #include <linux/fs.h> | ||
14 | #include <linux/xattr.h> | ||
15 | #include <linux/magic.h> | ||
16 | #include <linux/ima.h> | ||
17 | #include <linux/evm.h> | ||
18 | |||
19 | #include "ima.h" | ||
20 | |||
21 | static int __init default_appraise_setup(char *str) | ||
22 | { | ||
23 | if (strncmp(str, "off", 3) == 0) | ||
24 | ima_appraise = 0; | ||
25 | else if (strncmp(str, "fix", 3) == 0) | ||
26 | ima_appraise = IMA_APPRAISE_FIX; | ||
27 | return 1; | ||
28 | } | ||
29 | |||
30 | __setup("ima_appraise=", default_appraise_setup); | ||
31 | |||
32 | /* | ||
33 | * ima_must_appraise - set appraise flag | ||
34 | * | ||
35 | * Return 1 to appraise | ||
36 | */ | ||
37 | int ima_must_appraise(struct inode *inode, enum ima_hooks func, int mask) | ||
38 | { | ||
39 | return 0; | ||
40 | } | ||
41 | |||
42 | static void ima_fix_xattr(struct dentry *dentry, | ||
43 | struct integrity_iint_cache *iint) | ||
44 | { | ||
45 | iint->digest[0] = IMA_XATTR_DIGEST; | ||
46 | __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, | ||
47 | iint->digest, IMA_DIGEST_SIZE + 1, 0); | ||
48 | } | ||
49 | |||
50 | /* | ||
51 | * ima_appraise_measurement - appraise file measurement | ||
52 | * | ||
53 | * Call evm_verifyxattr() to verify the integrity of 'security.ima'. | ||
54 | * Assuming success, compare the xattr hash with the collected measurement. | ||
55 | * | ||
56 | * Return 0 on success, error code otherwise | ||
57 | */ | ||
58 | int ima_appraise_measurement(struct integrity_iint_cache *iint, | ||
59 | struct file *file, const unsigned char *filename) | ||
60 | { | ||
61 | struct dentry *dentry = file->f_dentry; | ||
62 | struct inode *inode = dentry->d_inode; | ||
63 | u8 xattr_value[IMA_DIGEST_SIZE]; | ||
64 | enum integrity_status status = INTEGRITY_UNKNOWN; | ||
65 | const char *op = "appraise_data"; | ||
66 | char *cause = "unknown"; | ||
67 | int rc; | ||
68 | |||
69 | if (!ima_appraise) | ||
70 | return 0; | ||
71 | if (!inode->i_op->getxattr) | ||
72 | return INTEGRITY_UNKNOWN; | ||
73 | |||
74 | if (iint->flags & IMA_APPRAISED) | ||
75 | return iint->ima_status; | ||
76 | |||
77 | rc = inode->i_op->getxattr(dentry, XATTR_NAME_IMA, xattr_value, | ||
78 | IMA_DIGEST_SIZE); | ||
79 | if (rc <= 0) { | ||
80 | if (rc && rc != -ENODATA) | ||
81 | goto out; | ||
82 | |||
83 | cause = "missing-hash"; | ||
84 | status = | ||
85 | (inode->i_size == 0) ? INTEGRITY_PASS : INTEGRITY_NOLABEL; | ||
86 | goto out; | ||
87 | } | ||
88 | |||
89 | status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint); | ||
90 | if ((status != INTEGRITY_PASS) && (status != INTEGRITY_UNKNOWN)) { | ||
91 | if ((status == INTEGRITY_NOLABEL) | ||
92 | || (status == INTEGRITY_NOXATTRS)) | ||
93 | cause = "missing-HMAC"; | ||
94 | else if (status == INTEGRITY_FAIL) | ||
95 | cause = "invalid-HMAC"; | ||
96 | goto out; | ||
97 | } | ||
98 | |||
99 | rc = memcmp(xattr_value, iint->digest, IMA_DIGEST_SIZE); | ||
100 | if (rc) { | ||
101 | status = INTEGRITY_FAIL; | ||
102 | cause = "invalid-hash"; | ||
103 | print_hex_dump_bytes("security.ima: ", DUMP_PREFIX_NONE, | ||
104 | xattr_value, IMA_DIGEST_SIZE); | ||
105 | print_hex_dump_bytes("collected: ", DUMP_PREFIX_NONE, | ||
106 | iint->digest, IMA_DIGEST_SIZE); | ||
107 | goto out; | ||
108 | } | ||
109 | status = INTEGRITY_PASS; | ||
110 | iint->flags |= IMA_APPRAISED; | ||
111 | out: | ||
112 | if (status != INTEGRITY_PASS) { | ||
113 | if (ima_appraise & IMA_APPRAISE_FIX) { | ||
114 | ima_fix_xattr(dentry, iint); | ||
115 | status = INTEGRITY_PASS; | ||
116 | } | ||
117 | integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename, | ||
118 | op, cause, rc, 0); | ||
119 | } | ||
120 | iint->ima_status = status; | ||
121 | return status; | ||
122 | } | ||
123 | |||
124 | /* | ||
125 | * ima_update_xattr - update 'security.ima' hash value | ||
126 | */ | ||
127 | void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file) | ||
128 | { | ||
129 | struct dentry *dentry = file->f_dentry; | ||
130 | int rc = 0; | ||
131 | |||
132 | rc = ima_collect_measurement(iint, file); | ||
133 | if (rc < 0) | ||
134 | return; | ||
135 | ima_fix_xattr(dentry, iint); | ||
136 | } | ||
137 | |||
138 | /** | ||
139 | * ima_inode_post_setattr - reflect file metadata changes | ||
140 | * @dentry: pointer to the affected dentry | ||
141 | * | ||
142 | * Changes to a dentry's metadata might result in needing to appraise. | ||
143 | * | ||
144 | * This function is called from notify_change(), which expects the caller | ||
145 | * to lock the inode's i_mutex. | ||
146 | */ | ||
147 | void ima_inode_post_setattr(struct dentry *dentry) | ||
148 | { | ||
149 | struct inode *inode = dentry->d_inode; | ||
150 | struct integrity_iint_cache *iint; | ||
151 | int must_appraise, rc; | ||
152 | |||
153 | if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode) | ||
154 | || !inode->i_op->removexattr) | ||
155 | return; | ||
156 | |||
157 | must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR); | ||
158 | iint = integrity_iint_find(inode); | ||
159 | if (iint) { | ||
160 | if (must_appraise) | ||
161 | iint->flags |= IMA_APPRAISE; | ||
162 | else | ||
163 | iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED); | ||
164 | } | ||
165 | if (!must_appraise) | ||
166 | rc = inode->i_op->removexattr(dentry, XATTR_NAME_IMA); | ||
167 | return; | ||
168 | } | ||
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index 9b3ade7468b2..b21ee5b5495a 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c | |||
@@ -48,7 +48,7 @@ int ima_calc_hash(struct file *file, char *digest) | |||
48 | struct scatterlist sg[1]; | 48 | struct scatterlist sg[1]; |
49 | loff_t i_size, offset = 0; | 49 | loff_t i_size, offset = 0; |
50 | char *rbuf; | 50 | char *rbuf; |
51 | int rc; | 51 | int rc, read = 0; |
52 | 52 | ||
53 | rc = init_desc(&desc); | 53 | rc = init_desc(&desc); |
54 | if (rc != 0) | 54 | if (rc != 0) |
@@ -59,6 +59,10 @@ int ima_calc_hash(struct file *file, char *digest) | |||
59 | rc = -ENOMEM; | 59 | rc = -ENOMEM; |
60 | goto out; | 60 | goto out; |
61 | } | 61 | } |
62 | if (!(file->f_mode & FMODE_READ)) { | ||
63 | file->f_mode |= FMODE_READ; | ||
64 | read = 1; | ||
65 | } | ||
62 | i_size = i_size_read(file->f_dentry->d_inode); | 66 | i_size = i_size_read(file->f_dentry->d_inode); |
63 | while (offset < i_size) { | 67 | while (offset < i_size) { |
64 | int rbuf_len; | 68 | int rbuf_len; |
@@ -80,6 +84,8 @@ int ima_calc_hash(struct file *file, char *digest) | |||
80 | kfree(rbuf); | 84 | kfree(rbuf); |
81 | if (!rc) | 85 | if (!rc) |
82 | rc = crypto_hash_final(&desc, digest); | 86 | rc = crypto_hash_final(&desc, digest); |
87 | if (read) | ||
88 | file->f_mode &= ~FMODE_READ; | ||
83 | out: | 89 | out: |
84 | crypto_free_hash(desc.tfm); | 90 | crypto_free_hash(desc.tfm); |
85 | return rc; | 91 | return rc; |
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index be8294915cf7..6eb28d47e74b 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c | |||
@@ -22,12 +22,19 @@ | |||
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/xattr.h> | ||
25 | #include <linux/ima.h> | 26 | #include <linux/ima.h> |
26 | 27 | ||
27 | #include "ima.h" | 28 | #include "ima.h" |
28 | 29 | ||
29 | int ima_initialized; | 30 | int ima_initialized; |
30 | 31 | ||
32 | #ifdef CONFIG_IMA_APPRAISE | ||
33 | int ima_appraise = IMA_APPRAISE_ENFORCE; | ||
34 | #else | ||
35 | int ima_appraise; | ||
36 | #endif | ||
37 | |||
31 | char *ima_hash = "sha1"; | 38 | char *ima_hash = "sha1"; |
32 | static int __init hash_setup(char *str) | 39 | static int __init hash_setup(char *str) |
33 | { | 40 | { |
@@ -52,7 +59,7 @@ static void ima_rdwr_violation_check(struct file *file) | |||
52 | struct dentry *dentry = file->f_path.dentry; | 59 | struct dentry *dentry = file->f_path.dentry; |
53 | struct inode *inode = dentry->d_inode; | 60 | struct inode *inode = dentry->d_inode; |
54 | fmode_t mode = file->f_mode; | 61 | fmode_t mode = file->f_mode; |
55 | int rc; | 62 | int must_measure; |
56 | bool send_tomtou = false, send_writers = false; | 63 | bool send_tomtou = false, send_writers = false; |
57 | unsigned char *pathname = NULL, *pathbuf = NULL; | 64 | unsigned char *pathname = NULL, *pathbuf = NULL; |
58 | 65 | ||
@@ -67,8 +74,8 @@ static void ima_rdwr_violation_check(struct file *file) | |||
67 | goto out; | 74 | goto out; |
68 | } | 75 | } |
69 | 76 | ||
70 | rc = ima_must_measure(inode, MAY_READ, FILE_CHECK); | 77 | must_measure = ima_must_measure(inode, MAY_READ, FILE_CHECK); |
71 | if (rc < 0) | 78 | if (!must_measure) |
72 | goto out; | 79 | goto out; |
73 | 80 | ||
74 | if (atomic_read(&inode->i_writecount) > 0) | 81 | if (atomic_read(&inode->i_writecount) > 0) |
@@ -100,17 +107,21 @@ out: | |||
100 | } | 107 | } |
101 | 108 | ||
102 | static void ima_check_last_writer(struct integrity_iint_cache *iint, | 109 | static void ima_check_last_writer(struct integrity_iint_cache *iint, |
103 | struct inode *inode, | 110 | struct inode *inode, struct file *file) |
104 | struct file *file) | ||
105 | { | 111 | { |
106 | fmode_t mode = file->f_mode; | 112 | fmode_t mode = file->f_mode; |
107 | 113 | ||
108 | mutex_lock(&iint->mutex); | 114 | if (!(mode & FMODE_WRITE)) |
109 | if (mode & FMODE_WRITE && | 115 | return; |
110 | atomic_read(&inode->i_writecount) == 1 && | 116 | |
111 | iint->version != inode->i_version) | 117 | mutex_lock(&inode->i_mutex); |
112 | iint->flags &= ~IMA_MEASURED; | 118 | if (atomic_read(&inode->i_writecount) == 1 && |
113 | mutex_unlock(&iint->mutex); | 119 | iint->version != inode->i_version) { |
120 | iint->flags &= ~(IMA_COLLECTED | IMA_APPRAISED | IMA_MEASURED); | ||
121 | if (iint->flags & IMA_APPRAISE) | ||
122 | ima_update_xattr(iint, file); | ||
123 | } | ||
124 | mutex_unlock(&inode->i_mutex); | ||
114 | } | 125 | } |
115 | 126 | ||
116 | /** | 127 | /** |
@@ -140,14 +151,17 @@ static int process_measurement(struct file *file, const unsigned char *filename, | |||
140 | struct inode *inode = file->f_dentry->d_inode; | 151 | struct inode *inode = file->f_dentry->d_inode; |
141 | struct integrity_iint_cache *iint; | 152 | struct integrity_iint_cache *iint; |
142 | unsigned char *pathname = NULL, *pathbuf = NULL; | 153 | unsigned char *pathname = NULL, *pathbuf = NULL; |
143 | int rc = 0; | 154 | int rc = -ENOMEM, action, must_appraise; |
144 | 155 | ||
145 | if (!ima_initialized || !S_ISREG(inode->i_mode)) | 156 | if (!ima_initialized || !S_ISREG(inode->i_mode)) |
146 | return 0; | 157 | return 0; |
147 | 158 | ||
148 | rc = ima_must_measure(inode, mask, function); | 159 | /* Determine if in appraise/measurement policy, |
149 | if (rc != 0) | 160 | * returns IMA_MEASURE, IMA_APPRAISE bitmask. */ |
150 | return rc; | 161 | action = ima_must_appraise_or_measure(inode, mask, function); |
162 | if (!action) | ||
163 | return 0; | ||
164 | |||
151 | retry: | 165 | retry: |
152 | iint = integrity_iint_find(inode); | 166 | iint = integrity_iint_find(inode); |
153 | if (!iint) { | 167 | if (!iint) { |
@@ -157,11 +171,21 @@ retry: | |||
157 | return rc; | 171 | return rc; |
158 | } | 172 | } |
159 | 173 | ||
160 | mutex_lock(&iint->mutex); | 174 | must_appraise = action & IMA_APPRAISE; |
161 | 175 | ||
162 | rc = iint->flags & IMA_MEASURED ? 1 : 0; | 176 | mutex_lock(&inode->i_mutex); |
163 | if (rc != 0) | 177 | |
178 | /* Determine if already appraised/measured based on bitmask | ||
179 | * (IMA_MEASURE, IMA_MEASURED, IMA_APPRAISE, IMA_APPRAISED) */ | ||
180 | iint->flags |= action; | ||
181 | action &= ~((iint->flags & (IMA_MEASURED | IMA_APPRAISED)) >> 1); | ||
182 | |||
183 | /* Nothing to do, just return existing appraised status */ | ||
184 | if (!action) { | ||
185 | if (iint->flags & IMA_APPRAISED) | ||
186 | rc = iint->ima_status; | ||
164 | goto out; | 187 | goto out; |
188 | } | ||
165 | 189 | ||
166 | rc = ima_collect_measurement(iint, file); | 190 | rc = ima_collect_measurement(iint, file); |
167 | if (rc != 0) | 191 | if (rc != 0) |
@@ -177,11 +201,16 @@ retry: | |||
177 | pathname = NULL; | 201 | pathname = NULL; |
178 | } | 202 | } |
179 | } | 203 | } |
180 | ima_store_measurement(iint, file, !pathname ? filename : pathname); | 204 | if (action & IMA_MEASURE) |
205 | ima_store_measurement(iint, file, | ||
206 | !pathname ? filename : pathname); | ||
207 | if (action & IMA_APPRAISE) | ||
208 | rc = ima_appraise_measurement(iint, file, | ||
209 | !pathname ? filename : pathname); | ||
181 | kfree(pathbuf); | 210 | kfree(pathbuf); |
182 | out: | 211 | out: |
183 | mutex_unlock(&iint->mutex); | 212 | mutex_unlock(&inode->i_mutex); |
184 | return rc; | 213 | return (rc && must_appraise) ? -EACCES : 0; |
185 | } | 214 | } |
186 | 215 | ||
187 | /** | 216 | /** |
@@ -197,14 +226,14 @@ out: | |||
197 | */ | 226 | */ |
198 | int ima_file_mmap(struct file *file, unsigned long prot) | 227 | int ima_file_mmap(struct file *file, unsigned long prot) |
199 | { | 228 | { |
200 | int rc; | 229 | int rc = 0; |
201 | 230 | ||
202 | if (!file) | 231 | if (!file) |
203 | return 0; | 232 | return 0; |
204 | if (prot & PROT_EXEC) | 233 | if (prot & PROT_EXEC) |
205 | rc = process_measurement(file, file->f_dentry->d_name.name, | 234 | rc = process_measurement(file, file->f_dentry->d_name.name, |
206 | MAY_EXEC, FILE_MMAP); | 235 | MAY_EXEC, FILE_MMAP); |
207 | return 0; | 236 | return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0; |
208 | } | 237 | } |
209 | 238 | ||
210 | /** | 239 | /** |
@@ -228,7 +257,7 @@ int ima_bprm_check(struct linux_binprm *bprm) | |||
228 | (strcmp(bprm->filename, bprm->interp) == 0) ? | 257 | (strcmp(bprm->filename, bprm->interp) == 0) ? |
229 | bprm->filename : bprm->interp, | 258 | bprm->filename : bprm->interp, |
230 | MAY_EXEC, BPRM_CHECK); | 259 | MAY_EXEC, BPRM_CHECK); |
231 | return 0; | 260 | return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0; |
232 | } | 261 | } |
233 | 262 | ||
234 | /** | 263 | /** |
@@ -249,7 +278,7 @@ int ima_file_check(struct file *file, int mask) | |||
249 | rc = process_measurement(file, file->f_dentry->d_name.name, | 278 | rc = process_measurement(file, file->f_dentry->d_name.name, |
250 | mask & (MAY_READ | MAY_WRITE | MAY_EXEC), | 279 | mask & (MAY_READ | MAY_WRITE | MAY_EXEC), |
251 | FILE_CHECK); | 280 | FILE_CHECK); |
252 | return 0; | 281 | return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0; |
253 | } | 282 | } |
254 | EXPORT_SYMBOL_GPL(ima_file_check); | 283 | EXPORT_SYMBOL_GPL(ima_file_check); |
255 | 284 | ||
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 1a9583008aae..3e22e17da295 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c | |||
@@ -25,7 +25,13 @@ | |||
25 | #define IMA_FSMAGIC 0x0004 | 25 | #define IMA_FSMAGIC 0x0004 |
26 | #define IMA_UID 0x0008 | 26 | #define IMA_UID 0x0008 |
27 | 27 | ||
28 | enum ima_action { UNKNOWN = -1, DONT_MEASURE = 0, MEASURE }; | 28 | #define UNKNOWN 0 |
29 | #define MEASURE 1 /* same as IMA_MEASURE */ | ||
30 | #define DONT_MEASURE 2 | ||
31 | #define MEASURE_MASK 3 | ||
32 | #define APPRAISE 4 /* same as IMA_APPRAISE */ | ||
33 | #define DONT_APPRAISE 8 | ||
34 | #define APPRAISE_MASK 12 | ||
29 | 35 | ||
30 | #define MAX_LSM_RULES 6 | 36 | #define MAX_LSM_RULES 6 |
31 | enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, | 37 | enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, |
@@ -34,7 +40,7 @@ enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, | |||
34 | 40 | ||
35 | struct ima_measure_rule_entry { | 41 | struct ima_measure_rule_entry { |
36 | struct list_head list; | 42 | struct list_head list; |
37 | enum ima_action action; | 43 | int action; |
38 | unsigned int flags; | 44 | unsigned int flags; |
39 | enum ima_hooks func; | 45 | enum ima_hooks func; |
40 | int mask; | 46 | int mask; |
@@ -163,18 +169,28 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule, | |||
163 | * as elements in the list are never deleted, nor does the list | 169 | * as elements in the list are never deleted, nor does the list |
164 | * change.) | 170 | * change.) |
165 | */ | 171 | */ |
166 | int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask) | 172 | int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, |
173 | int flags) | ||
167 | { | 174 | { |
168 | struct ima_measure_rule_entry *entry; | 175 | struct ima_measure_rule_entry *entry; |
176 | int action = 0, actmask = flags | (flags << 1); | ||
169 | 177 | ||
170 | list_for_each_entry(entry, ima_measure, list) { | 178 | list_for_each_entry(entry, ima_measure, list) { |
171 | bool rc; | ||
172 | 179 | ||
173 | rc = ima_match_rules(entry, inode, func, mask); | 180 | if (!(entry->action & actmask)) |
174 | if (rc) | 181 | continue; |
175 | return entry->action; | 182 | |
183 | if (!ima_match_rules(entry, inode, func, mask)) | ||
184 | continue; | ||
185 | |||
186 | action |= (entry->action & (IMA_APPRAISE | IMA_MEASURE)); | ||
187 | actmask &= (entry->action & APPRAISE_MASK) ? | ||
188 | ~APPRAISE_MASK : ~MEASURE_MASK; | ||
189 | if (!actmask) | ||
190 | break; | ||
176 | } | 191 | } |
177 | return 0; | 192 | |
193 | return action; | ||
178 | } | 194 | } |
179 | 195 | ||
180 | /** | 196 | /** |
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 7a25ecec5aaa..dac6b68e945a 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h | |||
@@ -16,7 +16,11 @@ | |||
16 | #include <crypto/sha.h> | 16 | #include <crypto/sha.h> |
17 | 17 | ||
18 | /* iint cache flags */ | 18 | /* iint cache flags */ |
19 | #define IMA_MEASURED 0x01 | 19 | #define IMA_MEASURE 0x01 |
20 | #define IMA_MEASURED 0x02 | ||
21 | #define IMA_APPRAISE 0x04 | ||
22 | #define IMA_APPRAISED 0x08 | ||
23 | #define IMA_COLLECTED 0x10 | ||
20 | 24 | ||
21 | enum evm_ima_xattr_type { | 25 | enum evm_ima_xattr_type { |
22 | IMA_XATTR_DIGEST = 0x01, | 26 | IMA_XATTR_DIGEST = 0x01, |
@@ -36,7 +40,7 @@ struct integrity_iint_cache { | |||
36 | u64 version; /* track inode changes */ | 40 | u64 version; /* track inode changes */ |
37 | unsigned char flags; | 41 | unsigned char flags; |
38 | u8 digest[SHA1_DIGEST_SIZE]; | 42 | u8 digest[SHA1_DIGEST_SIZE]; |
39 | struct mutex mutex; /* protects: version, flags, digest */ | 43 | enum integrity_status ima_status; |
40 | enum integrity_status evm_status; | 44 | enum integrity_status evm_status; |
41 | }; | 45 | }; |
42 | 46 | ||