162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2008 IBM Corporation 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Mimi Zohar <zohar@us.ibm.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * File: ima_api.c 862306a36Sopenharmony_ci * Implements must_appraise_or_measure, collect_measurement, 962306a36Sopenharmony_ci * appraise_measurement, store_measurement and store_template. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/file.h> 1362306a36Sopenharmony_ci#include <linux/fs.h> 1462306a36Sopenharmony_ci#include <linux/xattr.h> 1562306a36Sopenharmony_ci#include <linux/evm.h> 1662306a36Sopenharmony_ci#include <linux/fsverity.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "ima.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * ima_free_template_entry - free an existing template entry 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_civoid ima_free_template_entry(struct ima_template_entry *entry) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci int i; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci for (i = 0; i < entry->template_desc->num_fields; i++) 2862306a36Sopenharmony_ci kfree(entry->template_data[i].data); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci kfree(entry->digests); 3162306a36Sopenharmony_ci kfree(entry); 3262306a36Sopenharmony_ci} 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * ima_alloc_init_template - create and initialize a new template entry 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_ciint ima_alloc_init_template(struct ima_event_data *event_data, 3862306a36Sopenharmony_ci struct ima_template_entry **entry, 3962306a36Sopenharmony_ci struct ima_template_desc *desc) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct ima_template_desc *template_desc; 4262306a36Sopenharmony_ci struct tpm_digest *digests; 4362306a36Sopenharmony_ci int i, result = 0; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (desc) 4662306a36Sopenharmony_ci template_desc = desc; 4762306a36Sopenharmony_ci else 4862306a36Sopenharmony_ci template_desc = ima_template_desc_current(); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci *entry = kzalloc(struct_size(*entry, template_data, 5162306a36Sopenharmony_ci template_desc->num_fields), GFP_NOFS); 5262306a36Sopenharmony_ci if (!*entry) 5362306a36Sopenharmony_ci return -ENOMEM; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci digests = kcalloc(NR_BANKS(ima_tpm_chip) + ima_extra_slots, 5662306a36Sopenharmony_ci sizeof(*digests), GFP_NOFS); 5762306a36Sopenharmony_ci if (!digests) { 5862306a36Sopenharmony_ci kfree(*entry); 5962306a36Sopenharmony_ci *entry = NULL; 6062306a36Sopenharmony_ci return -ENOMEM; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci (*entry)->digests = digests; 6462306a36Sopenharmony_ci (*entry)->template_desc = template_desc; 6562306a36Sopenharmony_ci for (i = 0; i < template_desc->num_fields; i++) { 6662306a36Sopenharmony_ci const struct ima_template_field *field = 6762306a36Sopenharmony_ci template_desc->fields[i]; 6862306a36Sopenharmony_ci u32 len; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci result = field->field_init(event_data, 7162306a36Sopenharmony_ci &((*entry)->template_data[i])); 7262306a36Sopenharmony_ci if (result != 0) 7362306a36Sopenharmony_ci goto out; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci len = (*entry)->template_data[i].len; 7662306a36Sopenharmony_ci (*entry)->template_data_len += sizeof(len); 7762306a36Sopenharmony_ci (*entry)->template_data_len += len; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ciout: 8162306a36Sopenharmony_ci ima_free_template_entry(*entry); 8262306a36Sopenharmony_ci *entry = NULL; 8362306a36Sopenharmony_ci return result; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* 8762306a36Sopenharmony_ci * ima_store_template - store ima template measurements 8862306a36Sopenharmony_ci * 8962306a36Sopenharmony_ci * Calculate the hash of a template entry, add the template entry 9062306a36Sopenharmony_ci * to an ordered list of measurement entries maintained inside the kernel, 9162306a36Sopenharmony_ci * and also update the aggregate integrity value (maintained inside the 9262306a36Sopenharmony_ci * configured TPM PCR) over the hashes of the current list of measurement 9362306a36Sopenharmony_ci * entries. 9462306a36Sopenharmony_ci * 9562306a36Sopenharmony_ci * Applications retrieve the current kernel-held measurement list through 9662306a36Sopenharmony_ci * the securityfs entries in /sys/kernel/security/ima. The signed aggregate 9762306a36Sopenharmony_ci * TPM PCR (called quote) can be retrieved using a TPM user space library 9862306a36Sopenharmony_ci * and is used to validate the measurement list. 9962306a36Sopenharmony_ci * 10062306a36Sopenharmony_ci * Returns 0 on success, error code otherwise 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_ciint ima_store_template(struct ima_template_entry *entry, 10362306a36Sopenharmony_ci int violation, struct inode *inode, 10462306a36Sopenharmony_ci const unsigned char *filename, int pcr) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci static const char op[] = "add_template_measure"; 10762306a36Sopenharmony_ci static const char audit_cause[] = "hashing_error"; 10862306a36Sopenharmony_ci char *template_name = entry->template_desc->name; 10962306a36Sopenharmony_ci int result; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (!violation) { 11262306a36Sopenharmony_ci result = ima_calc_field_array_hash(&entry->template_data[0], 11362306a36Sopenharmony_ci entry); 11462306a36Sopenharmony_ci if (result < 0) { 11562306a36Sopenharmony_ci integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, 11662306a36Sopenharmony_ci template_name, op, 11762306a36Sopenharmony_ci audit_cause, result, 0); 11862306a36Sopenharmony_ci return result; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci entry->pcr = pcr; 12262306a36Sopenharmony_ci result = ima_add_template_entry(entry, violation, op, inode, filename); 12362306a36Sopenharmony_ci return result; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* 12762306a36Sopenharmony_ci * ima_add_violation - add violation to measurement list. 12862306a36Sopenharmony_ci * 12962306a36Sopenharmony_ci * Violations are flagged in the measurement list with zero hash values. 13062306a36Sopenharmony_ci * By extending the PCR with 0xFF's instead of with zeroes, the PCR 13162306a36Sopenharmony_ci * value is invalidated. 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_civoid ima_add_violation(struct file *file, const unsigned char *filename, 13462306a36Sopenharmony_ci struct integrity_iint_cache *iint, 13562306a36Sopenharmony_ci const char *op, const char *cause) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct ima_template_entry *entry; 13862306a36Sopenharmony_ci struct inode *inode = file_inode(file); 13962306a36Sopenharmony_ci struct ima_event_data event_data = { .iint = iint, 14062306a36Sopenharmony_ci .file = file, 14162306a36Sopenharmony_ci .filename = filename, 14262306a36Sopenharmony_ci .violation = cause }; 14362306a36Sopenharmony_ci int violation = 1; 14462306a36Sopenharmony_ci int result; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* can overflow, only indicator */ 14762306a36Sopenharmony_ci atomic_long_inc(&ima_htable.violations); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci result = ima_alloc_init_template(&event_data, &entry, NULL); 15062306a36Sopenharmony_ci if (result < 0) { 15162306a36Sopenharmony_ci result = -ENOMEM; 15262306a36Sopenharmony_ci goto err_out; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci result = ima_store_template(entry, violation, inode, 15562306a36Sopenharmony_ci filename, CONFIG_IMA_MEASURE_PCR_IDX); 15662306a36Sopenharmony_ci if (result < 0) 15762306a36Sopenharmony_ci ima_free_template_entry(entry); 15862306a36Sopenharmony_cierr_out: 15962306a36Sopenharmony_ci integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, 16062306a36Sopenharmony_ci op, cause, result, 0); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/** 16462306a36Sopenharmony_ci * ima_get_action - appraise & measure decision based on policy. 16562306a36Sopenharmony_ci * @idmap: idmap of the mount the inode was found from 16662306a36Sopenharmony_ci * @inode: pointer to the inode associated with the object being validated 16762306a36Sopenharmony_ci * @cred: pointer to credentials structure to validate 16862306a36Sopenharmony_ci * @secid: secid of the task being validated 16962306a36Sopenharmony_ci * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXEC, 17062306a36Sopenharmony_ci * MAY_APPEND) 17162306a36Sopenharmony_ci * @func: caller identifier 17262306a36Sopenharmony_ci * @pcr: pointer filled in if matched measure policy sets pcr= 17362306a36Sopenharmony_ci * @template_desc: pointer filled in if matched measure policy sets template= 17462306a36Sopenharmony_ci * @func_data: func specific data, may be NULL 17562306a36Sopenharmony_ci * @allowed_algos: allowlist of hash algorithms for the IMA xattr 17662306a36Sopenharmony_ci * 17762306a36Sopenharmony_ci * The policy is defined in terms of keypairs: 17862306a36Sopenharmony_ci * subj=, obj=, type=, func=, mask=, fsmagic= 17962306a36Sopenharmony_ci * subj,obj, and type: are LSM specific. 18062306a36Sopenharmony_ci * func: FILE_CHECK | BPRM_CHECK | CREDS_CHECK | MMAP_CHECK | MODULE_CHECK 18162306a36Sopenharmony_ci * | KEXEC_CMDLINE | KEY_CHECK | CRITICAL_DATA | SETXATTR_CHECK 18262306a36Sopenharmony_ci * | MMAP_CHECK_REQPROT 18362306a36Sopenharmony_ci * mask: contains the permission mask 18462306a36Sopenharmony_ci * fsmagic: hex value 18562306a36Sopenharmony_ci * 18662306a36Sopenharmony_ci * Returns IMA_MEASURE, IMA_APPRAISE mask. 18762306a36Sopenharmony_ci * 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_ciint ima_get_action(struct mnt_idmap *idmap, struct inode *inode, 19062306a36Sopenharmony_ci const struct cred *cred, u32 secid, int mask, 19162306a36Sopenharmony_ci enum ima_hooks func, int *pcr, 19262306a36Sopenharmony_ci struct ima_template_desc **template_desc, 19362306a36Sopenharmony_ci const char *func_data, unsigned int *allowed_algos) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci flags &= ima_policy_flag; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return ima_match_policy(idmap, inode, cred, secid, func, mask, 20062306a36Sopenharmony_ci flags, pcr, template_desc, func_data, 20162306a36Sopenharmony_ci allowed_algos); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic bool ima_get_verity_digest(struct integrity_iint_cache *iint, 20562306a36Sopenharmony_ci struct ima_max_digest_data *hash) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci enum hash_algo alg; 20862306a36Sopenharmony_ci int digest_len; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* 21162306a36Sopenharmony_ci * On failure, 'measure' policy rules will result in a file data 21262306a36Sopenharmony_ci * hash containing 0's. 21362306a36Sopenharmony_ci */ 21462306a36Sopenharmony_ci digest_len = fsverity_get_digest(iint->inode, hash->digest, NULL, &alg); 21562306a36Sopenharmony_ci if (digest_len == 0) 21662306a36Sopenharmony_ci return false; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* 21962306a36Sopenharmony_ci * Unlike in the case of actually calculating the file hash, in 22062306a36Sopenharmony_ci * the fsverity case regardless of the hash algorithm, return 22162306a36Sopenharmony_ci * the verity digest to be included in the measurement list. A 22262306a36Sopenharmony_ci * mismatch between the verity algorithm and the xattr signature 22362306a36Sopenharmony_ci * algorithm, if one exists, will be detected later. 22462306a36Sopenharmony_ci */ 22562306a36Sopenharmony_ci hash->hdr.algo = alg; 22662306a36Sopenharmony_ci hash->hdr.length = digest_len; 22762306a36Sopenharmony_ci return true; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci/* 23162306a36Sopenharmony_ci * ima_collect_measurement - collect file measurement 23262306a36Sopenharmony_ci * 23362306a36Sopenharmony_ci * Calculate the file hash, if it doesn't already exist, 23462306a36Sopenharmony_ci * storing the measurement and i_version in the iint. 23562306a36Sopenharmony_ci * 23662306a36Sopenharmony_ci * Must be called with iint->mutex held. 23762306a36Sopenharmony_ci * 23862306a36Sopenharmony_ci * Return 0 on success, error code otherwise 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_ciint ima_collect_measurement(struct integrity_iint_cache *iint, 24162306a36Sopenharmony_ci struct file *file, void *buf, loff_t size, 24262306a36Sopenharmony_ci enum hash_algo algo, struct modsig *modsig) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci const char *audit_cause = "failed"; 24562306a36Sopenharmony_ci struct inode *inode = file_inode(file); 24662306a36Sopenharmony_ci struct inode *real_inode = d_real_inode(file_dentry(file)); 24762306a36Sopenharmony_ci const char *filename = file->f_path.dentry->d_name.name; 24862306a36Sopenharmony_ci struct ima_max_digest_data hash; 24962306a36Sopenharmony_ci struct kstat stat; 25062306a36Sopenharmony_ci int result = 0; 25162306a36Sopenharmony_ci int length; 25262306a36Sopenharmony_ci void *tmpbuf; 25362306a36Sopenharmony_ci u64 i_version = 0; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* 25662306a36Sopenharmony_ci * Always collect the modsig, because IMA might have already collected 25762306a36Sopenharmony_ci * the file digest without collecting the modsig in a previous 25862306a36Sopenharmony_ci * measurement rule. 25962306a36Sopenharmony_ci */ 26062306a36Sopenharmony_ci if (modsig) 26162306a36Sopenharmony_ci ima_collect_modsig(modsig, buf, size); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (iint->flags & IMA_COLLECTED) 26462306a36Sopenharmony_ci goto out; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* 26762306a36Sopenharmony_ci * Detecting file change is based on i_version. On filesystems 26862306a36Sopenharmony_ci * which do not support i_version, support was originally limited 26962306a36Sopenharmony_ci * to an initial measurement/appraisal/audit, but was modified to 27062306a36Sopenharmony_ci * assume the file changed. 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ci result = vfs_getattr_nosec(&file->f_path, &stat, STATX_CHANGE_COOKIE, 27362306a36Sopenharmony_ci AT_STATX_SYNC_AS_STAT); 27462306a36Sopenharmony_ci if (!result && (stat.result_mask & STATX_CHANGE_COOKIE)) 27562306a36Sopenharmony_ci i_version = stat.change_cookie; 27662306a36Sopenharmony_ci hash.hdr.algo = algo; 27762306a36Sopenharmony_ci hash.hdr.length = hash_digest_size[algo]; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* Initialize hash digest to 0's in case of failure */ 28062306a36Sopenharmony_ci memset(&hash.digest, 0, sizeof(hash.digest)); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (iint->flags & IMA_VERITY_REQUIRED) { 28362306a36Sopenharmony_ci if (!ima_get_verity_digest(iint, &hash)) { 28462306a36Sopenharmony_ci audit_cause = "no-verity-digest"; 28562306a36Sopenharmony_ci result = -ENODATA; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci } else if (buf) { 28862306a36Sopenharmony_ci result = ima_calc_buffer_hash(buf, size, &hash.hdr); 28962306a36Sopenharmony_ci } else { 29062306a36Sopenharmony_ci result = ima_calc_file_hash(file, &hash.hdr); 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (result && result != -EBADF && result != -EINVAL) 29462306a36Sopenharmony_ci goto out; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci length = sizeof(hash.hdr) + hash.hdr.length; 29762306a36Sopenharmony_ci tmpbuf = krealloc(iint->ima_hash, length, GFP_NOFS); 29862306a36Sopenharmony_ci if (!tmpbuf) { 29962306a36Sopenharmony_ci result = -ENOMEM; 30062306a36Sopenharmony_ci goto out; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci iint->ima_hash = tmpbuf; 30462306a36Sopenharmony_ci memcpy(iint->ima_hash, &hash, length); 30562306a36Sopenharmony_ci iint->version = i_version; 30662306a36Sopenharmony_ci if (real_inode != inode) { 30762306a36Sopenharmony_ci iint->real_ino = real_inode->i_ino; 30862306a36Sopenharmony_ci iint->real_dev = real_inode->i_sb->s_dev; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* Possibly temporary failure due to type of read (eg. O_DIRECT) */ 31262306a36Sopenharmony_ci if (!result) 31362306a36Sopenharmony_ci iint->flags |= IMA_COLLECTED; 31462306a36Sopenharmony_ciout: 31562306a36Sopenharmony_ci if (result) { 31662306a36Sopenharmony_ci if (file->f_flags & O_DIRECT) 31762306a36Sopenharmony_ci audit_cause = "failed(directio)"; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, 32062306a36Sopenharmony_ci filename, "collect_data", audit_cause, 32162306a36Sopenharmony_ci result, 0); 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci return result; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci/* 32762306a36Sopenharmony_ci * ima_store_measurement - store file measurement 32862306a36Sopenharmony_ci * 32962306a36Sopenharmony_ci * Create an "ima" template and then store the template by calling 33062306a36Sopenharmony_ci * ima_store_template. 33162306a36Sopenharmony_ci * 33262306a36Sopenharmony_ci * We only get here if the inode has not already been measured, 33362306a36Sopenharmony_ci * but the measurement could already exist: 33462306a36Sopenharmony_ci * - multiple copies of the same file on either the same or 33562306a36Sopenharmony_ci * different filesystems. 33662306a36Sopenharmony_ci * - the inode was previously flushed as well as the iint info, 33762306a36Sopenharmony_ci * containing the hashing info. 33862306a36Sopenharmony_ci * 33962306a36Sopenharmony_ci * Must be called with iint->mutex held. 34062306a36Sopenharmony_ci */ 34162306a36Sopenharmony_civoid ima_store_measurement(struct integrity_iint_cache *iint, 34262306a36Sopenharmony_ci struct file *file, const unsigned char *filename, 34362306a36Sopenharmony_ci struct evm_ima_xattr_data *xattr_value, 34462306a36Sopenharmony_ci int xattr_len, const struct modsig *modsig, int pcr, 34562306a36Sopenharmony_ci struct ima_template_desc *template_desc) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci static const char op[] = "add_template_measure"; 34862306a36Sopenharmony_ci static const char audit_cause[] = "ENOMEM"; 34962306a36Sopenharmony_ci int result = -ENOMEM; 35062306a36Sopenharmony_ci struct inode *inode = file_inode(file); 35162306a36Sopenharmony_ci struct ima_template_entry *entry; 35262306a36Sopenharmony_ci struct ima_event_data event_data = { .iint = iint, 35362306a36Sopenharmony_ci .file = file, 35462306a36Sopenharmony_ci .filename = filename, 35562306a36Sopenharmony_ci .xattr_value = xattr_value, 35662306a36Sopenharmony_ci .xattr_len = xattr_len, 35762306a36Sopenharmony_ci .modsig = modsig }; 35862306a36Sopenharmony_ci int violation = 0; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* 36162306a36Sopenharmony_ci * We still need to store the measurement in the case of MODSIG because 36262306a36Sopenharmony_ci * we only have its contents to put in the list at the time of 36362306a36Sopenharmony_ci * appraisal, but a file measurement from earlier might already exist in 36462306a36Sopenharmony_ci * the measurement list. 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_ci if (iint->measured_pcrs & (0x1 << pcr) && !modsig) 36762306a36Sopenharmony_ci return; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci result = ima_alloc_init_template(&event_data, &entry, template_desc); 37062306a36Sopenharmony_ci if (result < 0) { 37162306a36Sopenharmony_ci integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, 37262306a36Sopenharmony_ci op, audit_cause, result, 0); 37362306a36Sopenharmony_ci return; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci result = ima_store_template(entry, violation, inode, filename, pcr); 37762306a36Sopenharmony_ci if ((!result || result == -EEXIST) && !(file->f_flags & O_DIRECT)) { 37862306a36Sopenharmony_ci iint->flags |= IMA_MEASURED; 37962306a36Sopenharmony_ci iint->measured_pcrs |= (0x1 << pcr); 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci if (result < 0) 38262306a36Sopenharmony_ci ima_free_template_entry(entry); 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_civoid ima_audit_measurement(struct integrity_iint_cache *iint, 38662306a36Sopenharmony_ci const unsigned char *filename) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct audit_buffer *ab; 38962306a36Sopenharmony_ci char *hash; 39062306a36Sopenharmony_ci const char *algo_name = hash_algo_name[iint->ima_hash->algo]; 39162306a36Sopenharmony_ci int i; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (iint->flags & IMA_AUDITED) 39462306a36Sopenharmony_ci return; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci hash = kzalloc((iint->ima_hash->length * 2) + 1, GFP_KERNEL); 39762306a36Sopenharmony_ci if (!hash) 39862306a36Sopenharmony_ci return; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci for (i = 0; i < iint->ima_hash->length; i++) 40162306a36Sopenharmony_ci hex_byte_pack(hash + (i * 2), iint->ima_hash->digest[i]); 40262306a36Sopenharmony_ci hash[i * 2] = '\0'; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci ab = audit_log_start(audit_context(), GFP_KERNEL, 40562306a36Sopenharmony_ci AUDIT_INTEGRITY_RULE); 40662306a36Sopenharmony_ci if (!ab) 40762306a36Sopenharmony_ci goto out; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci audit_log_format(ab, "file="); 41062306a36Sopenharmony_ci audit_log_untrustedstring(ab, filename); 41162306a36Sopenharmony_ci audit_log_format(ab, " hash=\"%s:%s\"", algo_name, hash); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci audit_log_task_info(ab); 41462306a36Sopenharmony_ci audit_log_end(ab); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci iint->flags |= IMA_AUDITED; 41762306a36Sopenharmony_ciout: 41862306a36Sopenharmony_ci kfree(hash); 41962306a36Sopenharmony_ci return; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci/* 42362306a36Sopenharmony_ci * ima_d_path - return a pointer to the full pathname 42462306a36Sopenharmony_ci * 42562306a36Sopenharmony_ci * Attempt to return a pointer to the full pathname for use in the 42662306a36Sopenharmony_ci * IMA measurement list, IMA audit records, and auditing logs. 42762306a36Sopenharmony_ci * 42862306a36Sopenharmony_ci * On failure, return a pointer to a copy of the filename, not dname. 42962306a36Sopenharmony_ci * Returning a pointer to dname, could result in using the pointer 43062306a36Sopenharmony_ci * after the memory has been freed. 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_ciconst char *ima_d_path(const struct path *path, char **pathbuf, char *namebuf) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci char *pathname = NULL; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci *pathbuf = __getname(); 43762306a36Sopenharmony_ci if (*pathbuf) { 43862306a36Sopenharmony_ci pathname = d_absolute_path(path, *pathbuf, PATH_MAX); 43962306a36Sopenharmony_ci if (IS_ERR(pathname)) { 44062306a36Sopenharmony_ci __putname(*pathbuf); 44162306a36Sopenharmony_ci *pathbuf = NULL; 44262306a36Sopenharmony_ci pathname = NULL; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (!pathname) { 44762306a36Sopenharmony_ci strscpy(namebuf, path->dentry->d_name.name, NAME_MAX); 44862306a36Sopenharmony_ci pathname = namebuf; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci return pathname; 45262306a36Sopenharmony_ci} 453