1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2008 IBM Corporation |
4 | * |
5 | * Author: Mimi Zohar <zohar@us.ibm.com> |
6 | * |
7 | * File: ima_api.c |
8 | * Implements must_appraise_or_measure, collect_measurement, |
9 | * appraise_measurement, store_measurement and store_template. |
10 | */ |
11 | #include <linux/slab.h> |
12 | #include <linux/file.h> |
13 | #include <linux/fs.h> |
14 | #include <linux/xattr.h> |
15 | #include <linux/evm.h> |
16 | #include <linux/fsverity.h> |
17 | |
18 | #include "ima.h" |
19 | |
20 | /* |
21 | * ima_free_template_entry - free an existing template entry |
22 | */ |
23 | void ima_free_template_entry(struct ima_template_entry *entry) |
24 | { |
25 | int i; |
26 | |
27 | for (i = 0; i < entry->template_desc->num_fields; i++) |
28 | kfree(objp: entry->template_data[i].data); |
29 | |
30 | kfree(objp: entry->digests); |
31 | kfree(objp: entry); |
32 | } |
33 | |
34 | /* |
35 | * ima_alloc_init_template - create and initialize a new template entry |
36 | */ |
37 | int ima_alloc_init_template(struct ima_event_data *event_data, |
38 | struct ima_template_entry **entry, |
39 | struct ima_template_desc *desc) |
40 | { |
41 | struct ima_template_desc *template_desc; |
42 | struct tpm_digest *digests; |
43 | int i, result = 0; |
44 | |
45 | if (desc) |
46 | template_desc = desc; |
47 | else |
48 | template_desc = ima_template_desc_current(); |
49 | |
50 | *entry = kzalloc(struct_size(*entry, template_data, |
51 | template_desc->num_fields), GFP_NOFS); |
52 | if (!*entry) |
53 | return -ENOMEM; |
54 | |
55 | digests = kcalloc(NR_BANKS(ima_tpm_chip) + ima_extra_slots, |
56 | size: sizeof(*digests), GFP_NOFS); |
57 | if (!digests) { |
58 | kfree(objp: *entry); |
59 | *entry = NULL; |
60 | return -ENOMEM; |
61 | } |
62 | |
63 | (*entry)->digests = digests; |
64 | (*entry)->template_desc = template_desc; |
65 | for (i = 0; i < template_desc->num_fields; i++) { |
66 | const struct ima_template_field *field = |
67 | template_desc->fields[i]; |
68 | u32 len; |
69 | |
70 | result = field->field_init(event_data, |
71 | &((*entry)->template_data[i])); |
72 | if (result != 0) |
73 | goto out; |
74 | |
75 | len = (*entry)->template_data[i].len; |
76 | (*entry)->template_data_len += sizeof(len); |
77 | (*entry)->template_data_len += len; |
78 | } |
79 | return 0; |
80 | out: |
81 | ima_free_template_entry(entry: *entry); |
82 | *entry = NULL; |
83 | return result; |
84 | } |
85 | |
86 | /* |
87 | * ima_store_template - store ima template measurements |
88 | * |
89 | * Calculate the hash of a template entry, add the template entry |
90 | * to an ordered list of measurement entries maintained inside the kernel, |
91 | * and also update the aggregate integrity value (maintained inside the |
92 | * configured TPM PCR) over the hashes of the current list of measurement |
93 | * entries. |
94 | * |
95 | * Applications retrieve the current kernel-held measurement list through |
96 | * the securityfs entries in /sys/kernel/security/ima. The signed aggregate |
97 | * TPM PCR (called quote) can be retrieved using a TPM user space library |
98 | * and is used to validate the measurement list. |
99 | * |
100 | * Returns 0 on success, error code otherwise |
101 | */ |
102 | int ima_store_template(struct ima_template_entry *entry, |
103 | int violation, struct inode *inode, |
104 | const unsigned char *filename, int pcr) |
105 | { |
106 | static const char op[] = "add_template_measure" ; |
107 | static const char audit_cause[] = "hashing_error" ; |
108 | char *template_name = entry->template_desc->name; |
109 | int result; |
110 | |
111 | if (!violation) { |
112 | result = ima_calc_field_array_hash(field_data: &entry->template_data[0], |
113 | entry); |
114 | if (result < 0) { |
115 | integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, |
116 | fname: template_name, op, |
117 | cause: audit_cause, result, info: 0); |
118 | return result; |
119 | } |
120 | } |
121 | entry->pcr = pcr; |
122 | result = ima_add_template_entry(entry, violation, op, inode, filename); |
123 | return result; |
124 | } |
125 | |
126 | /* |
127 | * ima_add_violation - add violation to measurement list. |
128 | * |
129 | * Violations are flagged in the measurement list with zero hash values. |
130 | * By extending the PCR with 0xFF's instead of with zeroes, the PCR |
131 | * value is invalidated. |
132 | */ |
133 | void ima_add_violation(struct file *file, const unsigned char *filename, |
134 | struct ima_iint_cache *iint, const char *op, |
135 | const char *cause) |
136 | { |
137 | struct ima_template_entry *entry; |
138 | struct inode *inode = file_inode(f: file); |
139 | struct ima_event_data event_data = { .iint = iint, |
140 | .file = file, |
141 | .filename = filename, |
142 | .violation = cause }; |
143 | int violation = 1; |
144 | int result; |
145 | |
146 | /* can overflow, only indicator */ |
147 | atomic_long_inc(v: &ima_htable.violations); |
148 | |
149 | result = ima_alloc_init_template(event_data: &event_data, entry: &entry, NULL); |
150 | if (result < 0) { |
151 | result = -ENOMEM; |
152 | goto err_out; |
153 | } |
154 | result = ima_store_template(entry, violation, inode, |
155 | filename, CONFIG_IMA_MEASURE_PCR_IDX); |
156 | if (result < 0) |
157 | ima_free_template_entry(entry); |
158 | err_out: |
159 | integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, fname: filename, |
160 | op, cause, result, info: 0); |
161 | } |
162 | |
163 | /** |
164 | * ima_get_action - appraise & measure decision based on policy. |
165 | * @idmap: idmap of the mount the inode was found from |
166 | * @inode: pointer to the inode associated with the object being validated |
167 | * @cred: pointer to credentials structure to validate |
168 | * @secid: secid of the task being validated |
169 | * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXEC, |
170 | * MAY_APPEND) |
171 | * @func: caller identifier |
172 | * @pcr: pointer filled in if matched measure policy sets pcr= |
173 | * @template_desc: pointer filled in if matched measure policy sets template= |
174 | * @func_data: func specific data, may be NULL |
175 | * @allowed_algos: allowlist of hash algorithms for the IMA xattr |
176 | * |
177 | * The policy is defined in terms of keypairs: |
178 | * subj=, obj=, type=, func=, mask=, fsmagic= |
179 | * subj,obj, and type: are LSM specific. |
180 | * func: FILE_CHECK | BPRM_CHECK | CREDS_CHECK | MMAP_CHECK | MODULE_CHECK |
181 | * | KEXEC_CMDLINE | KEY_CHECK | CRITICAL_DATA | SETXATTR_CHECK |
182 | * | MMAP_CHECK_REQPROT |
183 | * mask: contains the permission mask |
184 | * fsmagic: hex value |
185 | * |
186 | * Returns IMA_MEASURE, IMA_APPRAISE mask. |
187 | * |
188 | */ |
189 | int ima_get_action(struct mnt_idmap *idmap, struct inode *inode, |
190 | const struct cred *cred, u32 secid, int mask, |
191 | enum ima_hooks func, int *pcr, |
192 | struct ima_template_desc **template_desc, |
193 | const char *func_data, unsigned int *allowed_algos) |
194 | { |
195 | int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH; |
196 | |
197 | flags &= ima_policy_flag; |
198 | |
199 | return ima_match_policy(idmap, inode, cred, secid, func, mask, |
200 | flags, pcr, template_desc, func_data, |
201 | allowed_algos); |
202 | } |
203 | |
204 | static bool ima_get_verity_digest(struct ima_iint_cache *iint, |
205 | struct inode *inode, |
206 | struct ima_max_digest_data *hash) |
207 | { |
208 | enum hash_algo alg; |
209 | int digest_len; |
210 | |
211 | /* |
212 | * On failure, 'measure' policy rules will result in a file data |
213 | * hash containing 0's. |
214 | */ |
215 | digest_len = fsverity_get_digest(inode, raw_digest: hash->digest, NULL, halg: &alg); |
216 | if (digest_len == 0) |
217 | return false; |
218 | |
219 | /* |
220 | * Unlike in the case of actually calculating the file hash, in |
221 | * the fsverity case regardless of the hash algorithm, return |
222 | * the verity digest to be included in the measurement list. A |
223 | * mismatch between the verity algorithm and the xattr signature |
224 | * algorithm, if one exists, will be detected later. |
225 | */ |
226 | hash->hdr.algo = alg; |
227 | hash->hdr.length = digest_len; |
228 | return true; |
229 | } |
230 | |
231 | /* |
232 | * ima_collect_measurement - collect file measurement |
233 | * |
234 | * Calculate the file hash, if it doesn't already exist, |
235 | * storing the measurement and i_version in the iint. |
236 | * |
237 | * Must be called with iint->mutex held. |
238 | * |
239 | * Return 0 on success, error code otherwise |
240 | */ |
241 | int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file, |
242 | void *buf, loff_t size, enum hash_algo algo, |
243 | struct modsig *modsig) |
244 | { |
245 | const char *audit_cause = "failed" ; |
246 | struct inode *inode = file_inode(f: file); |
247 | struct inode *real_inode = d_real_inode(dentry: file_dentry(file)); |
248 | const char *filename = file->f_path.dentry->d_name.name; |
249 | struct ima_max_digest_data hash; |
250 | struct kstat stat; |
251 | int result = 0; |
252 | int length; |
253 | void *tmpbuf; |
254 | u64 i_version = 0; |
255 | |
256 | /* |
257 | * Always collect the modsig, because IMA might have already collected |
258 | * the file digest without collecting the modsig in a previous |
259 | * measurement rule. |
260 | */ |
261 | if (modsig) |
262 | ima_collect_modsig(modsig, buf, size); |
263 | |
264 | if (iint->flags & IMA_COLLECTED) |
265 | goto out; |
266 | |
267 | /* |
268 | * Detecting file change is based on i_version. On filesystems |
269 | * which do not support i_version, support was originally limited |
270 | * to an initial measurement/appraisal/audit, but was modified to |
271 | * assume the file changed. |
272 | */ |
273 | result = vfs_getattr_nosec(&file->f_path, &stat, STATX_CHANGE_COOKIE, |
274 | AT_STATX_SYNC_AS_STAT); |
275 | if (!result && (stat.result_mask & STATX_CHANGE_COOKIE)) |
276 | i_version = stat.change_cookie; |
277 | hash.hdr.algo = algo; |
278 | hash.hdr.length = hash_digest_size[algo]; |
279 | |
280 | /* Initialize hash digest to 0's in case of failure */ |
281 | memset(&hash.digest, 0, sizeof(hash.digest)); |
282 | |
283 | if (iint->flags & IMA_VERITY_REQUIRED) { |
284 | if (!ima_get_verity_digest(iint, inode, hash: &hash)) { |
285 | audit_cause = "no-verity-digest" ; |
286 | result = -ENODATA; |
287 | } |
288 | } else if (buf) { |
289 | result = ima_calc_buffer_hash(buf, len: size, hash: &hash.hdr); |
290 | } else { |
291 | result = ima_calc_file_hash(file, hash: &hash.hdr); |
292 | } |
293 | |
294 | if (result && result != -EBADF && result != -EINVAL) |
295 | goto out; |
296 | |
297 | length = sizeof(hash.hdr) + hash.hdr.length; |
298 | tmpbuf = krealloc(objp: iint->ima_hash, new_size: length, GFP_NOFS); |
299 | if (!tmpbuf) { |
300 | result = -ENOMEM; |
301 | goto out; |
302 | } |
303 | |
304 | iint->ima_hash = tmpbuf; |
305 | memcpy(iint->ima_hash, &hash, length); |
306 | iint->version = i_version; |
307 | if (real_inode != inode) { |
308 | iint->real_ino = real_inode->i_ino; |
309 | iint->real_dev = real_inode->i_sb->s_dev; |
310 | } |
311 | |
312 | /* Possibly temporary failure due to type of read (eg. O_DIRECT) */ |
313 | if (!result) |
314 | iint->flags |= IMA_COLLECTED; |
315 | out: |
316 | if (result) { |
317 | if (file->f_flags & O_DIRECT) |
318 | audit_cause = "failed(directio)" ; |
319 | |
320 | integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, |
321 | fname: filename, op: "collect_data" , cause: audit_cause, |
322 | result, info: 0); |
323 | } |
324 | return result; |
325 | } |
326 | |
327 | /* |
328 | * ima_store_measurement - store file measurement |
329 | * |
330 | * Create an "ima" template and then store the template by calling |
331 | * ima_store_template. |
332 | * |
333 | * We only get here if the inode has not already been measured, |
334 | * but the measurement could already exist: |
335 | * - multiple copies of the same file on either the same or |
336 | * different filesystems. |
337 | * - the inode was previously flushed as well as the iint info, |
338 | * containing the hashing info. |
339 | * |
340 | * Must be called with iint->mutex held. |
341 | */ |
342 | void ima_store_measurement(struct ima_iint_cache *iint, struct file *file, |
343 | const unsigned char *filename, |
344 | struct evm_ima_xattr_data *xattr_value, |
345 | int xattr_len, const struct modsig *modsig, int pcr, |
346 | struct ima_template_desc *template_desc) |
347 | { |
348 | static const char op[] = "add_template_measure" ; |
349 | static const char audit_cause[] = "ENOMEM" ; |
350 | int result = -ENOMEM; |
351 | struct inode *inode = file_inode(f: file); |
352 | struct ima_template_entry *entry; |
353 | struct ima_event_data event_data = { .iint = iint, |
354 | .file = file, |
355 | .filename = filename, |
356 | .xattr_value = xattr_value, |
357 | .xattr_len = xattr_len, |
358 | .modsig = modsig }; |
359 | int violation = 0; |
360 | |
361 | /* |
362 | * We still need to store the measurement in the case of MODSIG because |
363 | * we only have its contents to put in the list at the time of |
364 | * appraisal, but a file measurement from earlier might already exist in |
365 | * the measurement list. |
366 | */ |
367 | if (iint->measured_pcrs & (0x1 << pcr) && !modsig) |
368 | return; |
369 | |
370 | result = ima_alloc_init_template(event_data: &event_data, entry: &entry, desc: template_desc); |
371 | if (result < 0) { |
372 | integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, fname: filename, |
373 | op, cause: audit_cause, result, info: 0); |
374 | return; |
375 | } |
376 | |
377 | result = ima_store_template(entry, violation, inode, filename, pcr); |
378 | if ((!result || result == -EEXIST) && !(file->f_flags & O_DIRECT)) { |
379 | iint->flags |= IMA_MEASURED; |
380 | iint->measured_pcrs |= (0x1 << pcr); |
381 | } |
382 | if (result < 0) |
383 | ima_free_template_entry(entry); |
384 | } |
385 | |
386 | void ima_audit_measurement(struct ima_iint_cache *iint, |
387 | const unsigned char *filename) |
388 | { |
389 | struct audit_buffer *ab; |
390 | char *hash; |
391 | const char *algo_name = hash_algo_name[iint->ima_hash->algo]; |
392 | int i; |
393 | |
394 | if (iint->flags & IMA_AUDITED) |
395 | return; |
396 | |
397 | hash = kzalloc(size: (iint->ima_hash->length * 2) + 1, GFP_KERNEL); |
398 | if (!hash) |
399 | return; |
400 | |
401 | for (i = 0; i < iint->ima_hash->length; i++) |
402 | hex_byte_pack(buf: hash + (i * 2), byte: iint->ima_hash->digest[i]); |
403 | hash[i * 2] = '\0'; |
404 | |
405 | ab = audit_log_start(ctx: audit_context(), GFP_KERNEL, |
406 | AUDIT_INTEGRITY_RULE); |
407 | if (!ab) |
408 | goto out; |
409 | |
410 | audit_log_format(ab, fmt: "file=" ); |
411 | audit_log_untrustedstring(ab, string: filename); |
412 | audit_log_format(ab, fmt: " hash=\"%s:%s\"" , algo_name, hash); |
413 | |
414 | audit_log_task_info(ab); |
415 | audit_log_end(ab); |
416 | |
417 | iint->flags |= IMA_AUDITED; |
418 | out: |
419 | kfree(objp: hash); |
420 | return; |
421 | } |
422 | |
423 | /* |
424 | * ima_d_path - return a pointer to the full pathname |
425 | * |
426 | * Attempt to return a pointer to the full pathname for use in the |
427 | * IMA measurement list, IMA audit records, and auditing logs. |
428 | * |
429 | * On failure, return a pointer to a copy of the filename, not dname. |
430 | * Returning a pointer to dname, could result in using the pointer |
431 | * after the memory has been freed. |
432 | */ |
433 | const char *ima_d_path(const struct path *path, char **pathbuf, char *namebuf) |
434 | { |
435 | char *pathname = NULL; |
436 | |
437 | *pathbuf = __getname(); |
438 | if (*pathbuf) { |
439 | pathname = d_absolute_path(path, *pathbuf, PATH_MAX); |
440 | if (IS_ERR(ptr: pathname)) { |
441 | __putname(*pathbuf); |
442 | *pathbuf = NULL; |
443 | pathname = NULL; |
444 | } |
445 | } |
446 | |
447 | if (!pathname) { |
448 | strscpy(namebuf, path->dentry->d_name.name, NAME_MAX); |
449 | pathname = namebuf; |
450 | } |
451 | |
452 | return pathname; |
453 | } |
454 | |