cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ima_api.c (12760B)


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