| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) 2008 IBM Corporation |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Author: Mimi Zohar <zohar@us.ibm.com> |
|---|
| 5 | 6 | * |
|---|
| 6 | | - * This program is free software; you can redistribute it and/or |
|---|
| 7 | | - * modify it under the terms of the GNU General Public License as |
|---|
| 8 | | - * published by the Free Software Foundation, version 2 of the |
|---|
| 9 | | - * License. |
|---|
| 10 | | - * |
|---|
| 11 | 7 | * File: ima_api.c |
|---|
| 12 | 8 | * Implements must_appraise_or_measure, collect_measurement, |
|---|
| 13 | 9 | * appraise_measurement, store_measurement and store_template. |
|---|
| 14 | 10 | */ |
|---|
| 15 | | -#include <linux/module.h> |
|---|
| 16 | 11 | #include <linux/slab.h> |
|---|
| 17 | 12 | #include <linux/file.h> |
|---|
| 18 | 13 | #include <linux/fs.h> |
|---|
| .. | .. |
|---|
| 32 | 27 | for (i = 0; i < entry->template_desc->num_fields; i++) |
|---|
| 33 | 28 | kfree(entry->template_data[i].data); |
|---|
| 34 | 29 | |
|---|
| 30 | + kfree(entry->digests); |
|---|
| 35 | 31 | kfree(entry); |
|---|
| 36 | 32 | } |
|---|
| 37 | 33 | |
|---|
| .. | .. |
|---|
| 39 | 35 | * ima_alloc_init_template - create and initialize a new template entry |
|---|
| 40 | 36 | */ |
|---|
| 41 | 37 | int ima_alloc_init_template(struct ima_event_data *event_data, |
|---|
| 42 | | - struct ima_template_entry **entry) |
|---|
| 38 | + struct ima_template_entry **entry, |
|---|
| 39 | + struct ima_template_desc *desc) |
|---|
| 43 | 40 | { |
|---|
| 44 | | - struct ima_template_desc *template_desc = ima_template_desc_current(); |
|---|
| 41 | + struct ima_template_desc *template_desc; |
|---|
| 42 | + struct tpm_digest *digests; |
|---|
| 45 | 43 | int i, result = 0; |
|---|
| 46 | 44 | |
|---|
| 47 | | - *entry = kzalloc(sizeof(**entry) + template_desc->num_fields * |
|---|
| 48 | | - sizeof(struct ima_field_data), GFP_NOFS); |
|---|
| 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); |
|---|
| 49 | 52 | if (!*entry) |
|---|
| 50 | 53 | return -ENOMEM; |
|---|
| 51 | 54 | |
|---|
| 55 | + digests = kcalloc(NR_BANKS(ima_tpm_chip) + ima_extra_slots, |
|---|
| 56 | + sizeof(*digests), GFP_NOFS); |
|---|
| 57 | + if (!digests) { |
|---|
| 58 | + kfree(*entry); |
|---|
| 59 | + *entry = NULL; |
|---|
| 60 | + return -ENOMEM; |
|---|
| 61 | + } |
|---|
| 62 | + |
|---|
| 63 | + (*entry)->digests = digests; |
|---|
| 52 | 64 | (*entry)->template_desc = template_desc; |
|---|
| 53 | 65 | for (i = 0; i < template_desc->num_fields; i++) { |
|---|
| 54 | | - struct ima_template_field *field = template_desc->fields[i]; |
|---|
| 66 | + const struct ima_template_field *field = |
|---|
| 67 | + template_desc->fields[i]; |
|---|
| 55 | 68 | u32 len; |
|---|
| 56 | 69 | |
|---|
| 57 | 70 | result = field->field_init(event_data, |
|---|
| .. | .. |
|---|
| 94 | 107 | static const char audit_cause[] = "hashing_error"; |
|---|
| 95 | 108 | char *template_name = entry->template_desc->name; |
|---|
| 96 | 109 | int result; |
|---|
| 97 | | - struct { |
|---|
| 98 | | - struct ima_digest_data hdr; |
|---|
| 99 | | - char digest[TPM_DIGEST_SIZE]; |
|---|
| 100 | | - } hash; |
|---|
| 101 | 110 | |
|---|
| 102 | 111 | if (!violation) { |
|---|
| 103 | | - int num_fields = entry->template_desc->num_fields; |
|---|
| 104 | | - |
|---|
| 105 | | - /* this function uses default algo */ |
|---|
| 106 | | - hash.hdr.algo = HASH_ALGO_SHA1; |
|---|
| 107 | 112 | result = ima_calc_field_array_hash(&entry->template_data[0], |
|---|
| 108 | | - entry->template_desc, |
|---|
| 109 | | - num_fields, &hash.hdr); |
|---|
| 113 | + entry); |
|---|
| 110 | 114 | if (result < 0) { |
|---|
| 111 | 115 | integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, |
|---|
| 112 | 116 | template_name, op, |
|---|
| 113 | 117 | audit_cause, result, 0); |
|---|
| 114 | 118 | return result; |
|---|
| 115 | 119 | } |
|---|
| 116 | | - memcpy(entry->digest, hash.hdr.digest, hash.hdr.length); |
|---|
| 117 | 120 | } |
|---|
| 118 | 121 | entry->pcr = pcr; |
|---|
| 119 | 122 | result = ima_add_template_entry(entry, violation, op, inode, filename); |
|---|
| .. | .. |
|---|
| 133 | 136 | { |
|---|
| 134 | 137 | struct ima_template_entry *entry; |
|---|
| 135 | 138 | struct inode *inode = file_inode(file); |
|---|
| 136 | | - struct ima_event_data event_data = {iint, file, filename, NULL, 0, |
|---|
| 137 | | - cause}; |
|---|
| 139 | + struct ima_event_data event_data = { .iint = iint, |
|---|
| 140 | + .file = file, |
|---|
| 141 | + .filename = filename, |
|---|
| 142 | + .violation = cause }; |
|---|
| 138 | 143 | int violation = 1; |
|---|
| 139 | 144 | int result; |
|---|
| 140 | 145 | |
|---|
| 141 | 146 | /* can overflow, only indicator */ |
|---|
| 142 | 147 | atomic_long_inc(&ima_htable.violations); |
|---|
| 143 | 148 | |
|---|
| 144 | | - result = ima_alloc_init_template(&event_data, &entry); |
|---|
| 149 | + result = ima_alloc_init_template(&event_data, &entry, NULL); |
|---|
| 145 | 150 | if (result < 0) { |
|---|
| 146 | 151 | result = -ENOMEM; |
|---|
| 147 | 152 | goto err_out; |
|---|
| .. | .. |
|---|
| 157 | 162 | |
|---|
| 158 | 163 | /** |
|---|
| 159 | 164 | * ima_get_action - appraise & measure decision based on policy. |
|---|
| 160 | | - * @inode: pointer to inode to measure |
|---|
| 165 | + * @inode: pointer to the inode associated with the object being validated |
|---|
| 161 | 166 | * @cred: pointer to credentials structure to validate |
|---|
| 162 | 167 | * @secid: secid of the task being validated |
|---|
| 163 | 168 | * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXEC, |
|---|
| 164 | 169 | * MAY_APPEND) |
|---|
| 165 | 170 | * @func: caller identifier |
|---|
| 166 | 171 | * @pcr: pointer filled in if matched measure policy sets pcr= |
|---|
| 172 | + * @template_desc: pointer filled in if matched measure policy sets template= |
|---|
| 173 | + * @keyring: keyring name used to determine the action |
|---|
| 167 | 174 | * |
|---|
| 168 | 175 | * The policy is defined in terms of keypairs: |
|---|
| 169 | 176 | * subj=, obj=, type=, func=, mask=, fsmagic= |
|---|
| 170 | 177 | * subj,obj, and type: are LSM specific. |
|---|
| 171 | 178 | * func: FILE_CHECK | BPRM_CHECK | CREDS_CHECK | MMAP_CHECK | MODULE_CHECK |
|---|
| 179 | + * | KEXEC_CMDLINE | KEY_CHECK |
|---|
| 172 | 180 | * mask: contains the permission mask |
|---|
| 173 | 181 | * fsmagic: hex value |
|---|
| 174 | 182 | * |
|---|
| .. | .. |
|---|
| 176 | 184 | * |
|---|
| 177 | 185 | */ |
|---|
| 178 | 186 | int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid, |
|---|
| 179 | | - int mask, enum ima_hooks func, int *pcr) |
|---|
| 187 | + int mask, enum ima_hooks func, int *pcr, |
|---|
| 188 | + struct ima_template_desc **template_desc, |
|---|
| 189 | + const char *keyring) |
|---|
| 180 | 190 | { |
|---|
| 181 | 191 | int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH; |
|---|
| 182 | 192 | |
|---|
| 183 | 193 | flags &= ima_policy_flag; |
|---|
| 184 | 194 | |
|---|
| 185 | | - return ima_match_policy(inode, cred, secid, func, mask, flags, pcr); |
|---|
| 195 | + return ima_match_policy(inode, cred, secid, func, mask, flags, pcr, |
|---|
| 196 | + template_desc, keyring); |
|---|
| 186 | 197 | } |
|---|
| 187 | 198 | |
|---|
| 188 | 199 | /* |
|---|
| .. | .. |
|---|
| 197 | 208 | */ |
|---|
| 198 | 209 | int ima_collect_measurement(struct integrity_iint_cache *iint, |
|---|
| 199 | 210 | struct file *file, void *buf, loff_t size, |
|---|
| 200 | | - enum hash_algo algo) |
|---|
| 211 | + enum hash_algo algo, struct modsig *modsig) |
|---|
| 201 | 212 | { |
|---|
| 202 | 213 | const char *audit_cause = "failed"; |
|---|
| 203 | 214 | struct inode *inode = file_inode(file); |
|---|
| .. | .. |
|---|
| 210 | 221 | struct ima_digest_data hdr; |
|---|
| 211 | 222 | char digest[IMA_MAX_DIGEST_SIZE]; |
|---|
| 212 | 223 | } hash; |
|---|
| 224 | + |
|---|
| 225 | + /* |
|---|
| 226 | + * Always collect the modsig, because IMA might have already collected |
|---|
| 227 | + * the file digest without collecting the modsig in a previous |
|---|
| 228 | + * measurement rule. |
|---|
| 229 | + */ |
|---|
| 230 | + if (modsig) |
|---|
| 231 | + ima_collect_modsig(modsig, buf, size); |
|---|
| 213 | 232 | |
|---|
| 214 | 233 | if (iint->flags & IMA_COLLECTED) |
|---|
| 215 | 234 | goto out; |
|---|
| .. | .. |
|---|
| 277 | 296 | void ima_store_measurement(struct integrity_iint_cache *iint, |
|---|
| 278 | 297 | struct file *file, const unsigned char *filename, |
|---|
| 279 | 298 | struct evm_ima_xattr_data *xattr_value, |
|---|
| 280 | | - int xattr_len, int pcr) |
|---|
| 299 | + int xattr_len, const struct modsig *modsig, int pcr, |
|---|
| 300 | + struct ima_template_desc *template_desc) |
|---|
| 281 | 301 | { |
|---|
| 282 | 302 | static const char op[] = "add_template_measure"; |
|---|
| 283 | 303 | static const char audit_cause[] = "ENOMEM"; |
|---|
| 284 | 304 | int result = -ENOMEM; |
|---|
| 285 | 305 | struct inode *inode = file_inode(file); |
|---|
| 286 | 306 | struct ima_template_entry *entry; |
|---|
| 287 | | - struct ima_event_data event_data = {iint, file, filename, xattr_value, |
|---|
| 288 | | - xattr_len, NULL}; |
|---|
| 307 | + struct ima_event_data event_data = { .iint = iint, |
|---|
| 308 | + .file = file, |
|---|
| 309 | + .filename = filename, |
|---|
| 310 | + .xattr_value = xattr_value, |
|---|
| 311 | + .xattr_len = xattr_len, |
|---|
| 312 | + .modsig = modsig }; |
|---|
| 289 | 313 | int violation = 0; |
|---|
| 290 | 314 | |
|---|
| 291 | | - if (iint->measured_pcrs & (0x1 << pcr)) |
|---|
| 315 | + /* |
|---|
| 316 | + * We still need to store the measurement in the case of MODSIG because |
|---|
| 317 | + * we only have its contents to put in the list at the time of |
|---|
| 318 | + * appraisal, but a file measurement from earlier might already exist in |
|---|
| 319 | + * the measurement list. |
|---|
| 320 | + */ |
|---|
| 321 | + if (iint->measured_pcrs & (0x1 << pcr) && !modsig) |
|---|
| 292 | 322 | return; |
|---|
| 293 | 323 | |
|---|
| 294 | | - result = ima_alloc_init_template(&event_data, &entry); |
|---|
| 324 | + result = ima_alloc_init_template(&event_data, &entry, template_desc); |
|---|
| 295 | 325 | if (result < 0) { |
|---|
| 296 | 326 | integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, |
|---|
| 297 | 327 | op, audit_cause, result, 0); |
|---|
| .. | .. |
|---|
| 335 | 365 | audit_log_untrustedstring(ab, filename); |
|---|
| 336 | 366 | audit_log_format(ab, " hash=\"%s:%s\"", algo_name, hash); |
|---|
| 337 | 367 | |
|---|
| 338 | | - audit_log_task_info(ab, current); |
|---|
| 368 | + audit_log_task_info(ab); |
|---|
| 339 | 369 | audit_log_end(ab); |
|---|
| 340 | 370 | |
|---|
| 341 | 371 | iint->flags |= IMA_AUDITED; |
|---|