diff options
Diffstat (limited to 'security/integrity/ima/ima_api.c')
-rw-r--r-- | security/integrity/ima/ima_api.c | 154 |
1 files changed, 117 insertions, 37 deletions
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 1c03e8f1e0e1..c38bbce8c6a6 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c | |||
@@ -18,9 +18,59 @@ | |||
18 | #include <linux/fs.h> | 18 | #include <linux/fs.h> |
19 | #include <linux/xattr.h> | 19 | #include <linux/xattr.h> |
20 | #include <linux/evm.h> | 20 | #include <linux/evm.h> |
21 | #include <crypto/hash_info.h> | ||
21 | #include "ima.h" | 22 | #include "ima.h" |
22 | 23 | ||
23 | static const char *IMA_TEMPLATE_NAME = "ima"; | 24 | /* |
25 | * ima_free_template_entry - free an existing template entry | ||
26 | */ | ||
27 | void ima_free_template_entry(struct ima_template_entry *entry) | ||
28 | { | ||
29 | int i; | ||
30 | |||
31 | for (i = 0; i < entry->template_desc->num_fields; i++) | ||
32 | kfree(entry->template_data[i].data); | ||
33 | |||
34 | kfree(entry); | ||
35 | } | ||
36 | |||
37 | /* | ||
38 | * ima_alloc_init_template - create and initialize a new template entry | ||
39 | */ | ||
40 | int ima_alloc_init_template(struct integrity_iint_cache *iint, | ||
41 | struct file *file, const unsigned char *filename, | ||
42 | struct evm_ima_xattr_data *xattr_value, | ||
43 | int xattr_len, struct ima_template_entry **entry) | ||
44 | { | ||
45 | struct ima_template_desc *template_desc = ima_template_desc_current(); | ||
46 | int i, result = 0; | ||
47 | |||
48 | *entry = kzalloc(sizeof(**entry) + template_desc->num_fields * | ||
49 | sizeof(struct ima_field_data), GFP_NOFS); | ||
50 | if (!*entry) | ||
51 | return -ENOMEM; | ||
52 | |||
53 | (*entry)->template_desc = template_desc; | ||
54 | for (i = 0; i < template_desc->num_fields; i++) { | ||
55 | struct ima_template_field *field = template_desc->fields[i]; | ||
56 | u32 len; | ||
57 | |||
58 | result = field->field_init(iint, file, filename, | ||
59 | xattr_value, xattr_len, | ||
60 | &((*entry)->template_data[i])); | ||
61 | if (result != 0) | ||
62 | goto out; | ||
63 | |||
64 | len = (*entry)->template_data[i].len; | ||
65 | (*entry)->template_data_len += sizeof(len); | ||
66 | (*entry)->template_data_len += len; | ||
67 | } | ||
68 | return 0; | ||
69 | out: | ||
70 | ima_free_template_entry(*entry); | ||
71 | *entry = NULL; | ||
72 | return result; | ||
73 | } | ||
24 | 74 | ||
25 | /* | 75 | /* |
26 | * ima_store_template - store ima template measurements | 76 | * ima_store_template - store ima template measurements |
@@ -39,28 +89,35 @@ static const char *IMA_TEMPLATE_NAME = "ima"; | |||
39 | * Returns 0 on success, error code otherwise | 89 | * Returns 0 on success, error code otherwise |
40 | */ | 90 | */ |
41 | int ima_store_template(struct ima_template_entry *entry, | 91 | int ima_store_template(struct ima_template_entry *entry, |
42 | int violation, struct inode *inode) | 92 | int violation, struct inode *inode, |
93 | const unsigned char *filename) | ||
43 | { | 94 | { |
44 | const char *op = "add_template_measure"; | 95 | const char *op = "add_template_measure"; |
45 | const char *audit_cause = "hashing_error"; | 96 | const char *audit_cause = "hashing_error"; |
97 | char *template_name = entry->template_desc->name; | ||
46 | int result; | 98 | int result; |
47 | 99 | struct { | |
48 | memset(entry->digest, 0, sizeof(entry->digest)); | 100 | struct ima_digest_data hdr; |
49 | entry->template_name = IMA_TEMPLATE_NAME; | 101 | char digest[TPM_DIGEST_SIZE]; |
50 | entry->template_len = sizeof(entry->template); | 102 | } hash; |
51 | 103 | ||
52 | if (!violation) { | 104 | if (!violation) { |
53 | result = ima_calc_buffer_hash(&entry->template, | 105 | int num_fields = entry->template_desc->num_fields; |
54 | entry->template_len, | 106 | |
55 | entry->digest); | 107 | /* this function uses default algo */ |
108 | hash.hdr.algo = HASH_ALGO_SHA1; | ||
109 | result = ima_calc_field_array_hash(&entry->template_data[0], | ||
110 | entry->template_desc, | ||
111 | num_fields, &hash.hdr); | ||
56 | if (result < 0) { | 112 | if (result < 0) { |
57 | integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, | 113 | integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, |
58 | entry->template_name, op, | 114 | template_name, op, |
59 | audit_cause, result, 0); | 115 | audit_cause, result, 0); |
60 | return result; | 116 | return result; |
61 | } | 117 | } |
118 | memcpy(entry->digest, hash.hdr.digest, hash.hdr.length); | ||
62 | } | 119 | } |
63 | result = ima_add_template_entry(entry, violation, op, inode); | 120 | result = ima_add_template_entry(entry, violation, op, inode, filename); |
64 | return result; | 121 | return result; |
65 | } | 122 | } |
66 | 123 | ||
@@ -71,26 +128,26 @@ int ima_store_template(struct ima_template_entry *entry, | |||
71 | * By extending the PCR with 0xFF's instead of with zeroes, the PCR | 128 | * By extending the PCR with 0xFF's instead of with zeroes, the PCR |
72 | * value is invalidated. | 129 | * value is invalidated. |
73 | */ | 130 | */ |
74 | void ima_add_violation(struct inode *inode, const unsigned char *filename, | 131 | void ima_add_violation(struct file *file, const unsigned char *filename, |
75 | const char *op, const char *cause) | 132 | const char *op, const char *cause) |
76 | { | 133 | { |
77 | struct ima_template_entry *entry; | 134 | struct ima_template_entry *entry; |
135 | struct inode *inode = file->f_dentry->d_inode; | ||
78 | int violation = 1; | 136 | int violation = 1; |
79 | int result; | 137 | int result; |
80 | 138 | ||
81 | /* can overflow, only indicator */ | 139 | /* can overflow, only indicator */ |
82 | atomic_long_inc(&ima_htable.violations); | 140 | atomic_long_inc(&ima_htable.violations); |
83 | 141 | ||
84 | entry = kmalloc(sizeof(*entry), GFP_KERNEL); | 142 | result = ima_alloc_init_template(NULL, file, filename, |
85 | if (!entry) { | 143 | NULL, 0, &entry); |
144 | if (result < 0) { | ||
86 | result = -ENOMEM; | 145 | result = -ENOMEM; |
87 | goto err_out; | 146 | goto err_out; |
88 | } | 147 | } |
89 | memset(&entry->template, 0, sizeof(entry->template)); | 148 | result = ima_store_template(entry, violation, inode, filename); |
90 | strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX); | ||
91 | result = ima_store_template(entry, violation, inode); | ||
92 | if (result < 0) | 149 | if (result < 0) |
93 | kfree(entry); | 150 | ima_free_template_entry(entry); |
94 | err_out: | 151 | err_out: |
95 | integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, | 152 | integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, |
96 | op, cause, result, 0); | 153 | op, cause, result, 0); |
@@ -138,20 +195,42 @@ int ima_must_measure(struct inode *inode, int mask, int function) | |||
138 | * Return 0 on success, error code otherwise | 195 | * Return 0 on success, error code otherwise |
139 | */ | 196 | */ |
140 | int ima_collect_measurement(struct integrity_iint_cache *iint, | 197 | int ima_collect_measurement(struct integrity_iint_cache *iint, |
141 | struct file *file) | 198 | struct file *file, |
199 | struct evm_ima_xattr_data **xattr_value, | ||
200 | int *xattr_len) | ||
142 | { | 201 | { |
143 | struct inode *inode = file_inode(file); | 202 | struct inode *inode = file_inode(file); |
144 | const char *filename = file->f_dentry->d_name.name; | 203 | const char *filename = file->f_dentry->d_name.name; |
145 | int result = 0; | 204 | int result = 0; |
205 | struct { | ||
206 | struct ima_digest_data hdr; | ||
207 | char digest[IMA_MAX_DIGEST_SIZE]; | ||
208 | } hash; | ||
209 | |||
210 | if (xattr_value) | ||
211 | *xattr_len = ima_read_xattr(file->f_dentry, xattr_value); | ||
146 | 212 | ||
147 | if (!(iint->flags & IMA_COLLECTED)) { | 213 | if (!(iint->flags & IMA_COLLECTED)) { |
148 | u64 i_version = file_inode(file)->i_version; | 214 | u64 i_version = file_inode(file)->i_version; |
149 | 215 | ||
150 | iint->ima_xattr.type = IMA_XATTR_DIGEST; | 216 | /* use default hash algorithm */ |
151 | result = ima_calc_file_hash(file, iint->ima_xattr.digest); | 217 | hash.hdr.algo = ima_hash_algo; |
218 | |||
219 | if (xattr_value) | ||
220 | ima_get_hash_algo(*xattr_value, *xattr_len, &hash.hdr); | ||
221 | |||
222 | result = ima_calc_file_hash(file, &hash.hdr); | ||
152 | if (!result) { | 223 | if (!result) { |
153 | iint->version = i_version; | 224 | int length = sizeof(hash.hdr) + hash.hdr.length; |
154 | iint->flags |= IMA_COLLECTED; | 225 | void *tmpbuf = krealloc(iint->ima_hash, length, |
226 | GFP_NOFS); | ||
227 | if (tmpbuf) { | ||
228 | iint->ima_hash = tmpbuf; | ||
229 | memcpy(iint->ima_hash, &hash, length); | ||
230 | iint->version = i_version; | ||
231 | iint->flags |= IMA_COLLECTED; | ||
232 | } else | ||
233 | result = -ENOMEM; | ||
155 | } | 234 | } |
156 | } | 235 | } |
157 | if (result) | 236 | if (result) |
@@ -177,7 +256,9 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, | |||
177 | * Must be called with iint->mutex held. | 256 | * Must be called with iint->mutex held. |
178 | */ | 257 | */ |
179 | void ima_store_measurement(struct integrity_iint_cache *iint, | 258 | void ima_store_measurement(struct integrity_iint_cache *iint, |
180 | struct file *file, const unsigned char *filename) | 259 | struct file *file, const unsigned char *filename, |
260 | struct evm_ima_xattr_data *xattr_value, | ||
261 | int xattr_len) | ||
181 | { | 262 | { |
182 | const char *op = "add_template_measure"; | 263 | const char *op = "add_template_measure"; |
183 | const char *audit_cause = "ENOMEM"; | 264 | const char *audit_cause = "ENOMEM"; |
@@ -189,37 +270,35 @@ void ima_store_measurement(struct integrity_iint_cache *iint, | |||
189 | if (iint->flags & IMA_MEASURED) | 270 | if (iint->flags & IMA_MEASURED) |
190 | return; | 271 | return; |
191 | 272 | ||
192 | entry = kmalloc(sizeof(*entry), GFP_KERNEL); | 273 | result = ima_alloc_init_template(iint, file, filename, |
193 | if (!entry) { | 274 | xattr_value, xattr_len, &entry); |
275 | if (result < 0) { | ||
194 | integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, | 276 | integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, |
195 | op, audit_cause, result, 0); | 277 | op, audit_cause, result, 0); |
196 | return; | 278 | return; |
197 | } | 279 | } |
198 | memset(&entry->template, 0, sizeof(entry->template)); | ||
199 | memcpy(entry->template.digest, iint->ima_xattr.digest, IMA_DIGEST_SIZE); | ||
200 | strcpy(entry->template.file_name, | ||
201 | (strlen(filename) > IMA_EVENT_NAME_LEN_MAX) ? | ||
202 | file->f_dentry->d_name.name : filename); | ||
203 | 280 | ||
204 | result = ima_store_template(entry, violation, inode); | 281 | result = ima_store_template(entry, violation, inode, filename); |
205 | if (!result || result == -EEXIST) | 282 | if (!result || result == -EEXIST) |
206 | iint->flags |= IMA_MEASURED; | 283 | iint->flags |= IMA_MEASURED; |
207 | if (result < 0) | 284 | if (result < 0) |
208 | kfree(entry); | 285 | ima_free_template_entry(entry); |
209 | } | 286 | } |
210 | 287 | ||
211 | void ima_audit_measurement(struct integrity_iint_cache *iint, | 288 | void ima_audit_measurement(struct integrity_iint_cache *iint, |
212 | const unsigned char *filename) | 289 | const unsigned char *filename) |
213 | { | 290 | { |
214 | struct audit_buffer *ab; | 291 | struct audit_buffer *ab; |
215 | char hash[(IMA_DIGEST_SIZE * 2) + 1]; | 292 | char hash[(iint->ima_hash->length * 2) + 1]; |
293 | const char *algo_name = hash_algo_name[iint->ima_hash->algo]; | ||
294 | char algo_hash[sizeof(hash) + strlen(algo_name) + 2]; | ||
216 | int i; | 295 | int i; |
217 | 296 | ||
218 | if (iint->flags & IMA_AUDITED) | 297 | if (iint->flags & IMA_AUDITED) |
219 | return; | 298 | return; |
220 | 299 | ||
221 | for (i = 0; i < IMA_DIGEST_SIZE; i++) | 300 | for (i = 0; i < iint->ima_hash->length; i++) |
222 | hex_byte_pack(hash + (i * 2), iint->ima_xattr.digest[i]); | 301 | hex_byte_pack(hash + (i * 2), iint->ima_hash->digest[i]); |
223 | hash[i * 2] = '\0'; | 302 | hash[i * 2] = '\0'; |
224 | 303 | ||
225 | ab = audit_log_start(current->audit_context, GFP_KERNEL, | 304 | ab = audit_log_start(current->audit_context, GFP_KERNEL, |
@@ -230,7 +309,8 @@ void ima_audit_measurement(struct integrity_iint_cache *iint, | |||
230 | audit_log_format(ab, "file="); | 309 | audit_log_format(ab, "file="); |
231 | audit_log_untrustedstring(ab, filename); | 310 | audit_log_untrustedstring(ab, filename); |
232 | audit_log_format(ab, " hash="); | 311 | audit_log_format(ab, " hash="); |
233 | audit_log_untrustedstring(ab, hash); | 312 | snprintf(algo_hash, sizeof(algo_hash), "%s:%s", algo_name, hash); |
313 | audit_log_untrustedstring(ab, algo_hash); | ||
234 | 314 | ||
235 | audit_log_task_info(ab, current); | 315 | audit_log_task_info(ab, current); |
236 | audit_log_end(ab); | 316 | audit_log_end(ab); |