xref: /openbmc/linux/security/integrity/ima/ima_api.c (revision 5a0e3ad6)
13323eec9SMimi Zohar /*
23323eec9SMimi Zohar  * Copyright (C) 2008 IBM Corporation
33323eec9SMimi Zohar  *
43323eec9SMimi Zohar  * Author: Mimi Zohar <zohar@us.ibm.com>
53323eec9SMimi Zohar  *
63323eec9SMimi Zohar  * This program is free software; you can redistribute it and/or
73323eec9SMimi Zohar  * modify it under the terms of the GNU General Public License as
83323eec9SMimi Zohar  * published by the Free Software Foundation, version 2 of the
93323eec9SMimi Zohar  * License.
103323eec9SMimi Zohar  *
113323eec9SMimi Zohar  * File: ima_api.c
123323eec9SMimi Zohar  *	Implements must_measure, collect_measurement, store_measurement,
133323eec9SMimi Zohar  *	and store_template.
143323eec9SMimi Zohar  */
153323eec9SMimi Zohar #include <linux/module.h>
165a0e3ad6STejun Heo #include <linux/slab.h>
173323eec9SMimi Zohar 
183323eec9SMimi Zohar #include "ima.h"
19523979adSMimi Zohar static const char *IMA_TEMPLATE_NAME = "ima";
203323eec9SMimi Zohar 
213323eec9SMimi Zohar /*
223323eec9SMimi Zohar  * ima_store_template - store ima template measurements
233323eec9SMimi Zohar  *
243323eec9SMimi Zohar  * Calculate the hash of a template entry, add the template entry
253323eec9SMimi Zohar  * to an ordered list of measurement entries maintained inside the kernel,
263323eec9SMimi Zohar  * and also update the aggregate integrity value (maintained inside the
273323eec9SMimi Zohar  * configured TPM PCR) over the hashes of the current list of measurement
283323eec9SMimi Zohar  * entries.
293323eec9SMimi Zohar  *
303323eec9SMimi Zohar  * Applications retrieve the current kernel-held measurement list through
313323eec9SMimi Zohar  * the securityfs entries in /sys/kernel/security/ima. The signed aggregate
323323eec9SMimi Zohar  * TPM PCR (called quote) can be retrieved using a TPM user space library
333323eec9SMimi Zohar  * and is used to validate the measurement list.
343323eec9SMimi Zohar  *
353323eec9SMimi Zohar  * Returns 0 on success, error code otherwise
363323eec9SMimi Zohar  */
373323eec9SMimi Zohar int ima_store_template(struct ima_template_entry *entry,
383323eec9SMimi Zohar 		       int violation, struct inode *inode)
393323eec9SMimi Zohar {
403323eec9SMimi Zohar 	const char *op = "add_template_measure";
413323eec9SMimi Zohar 	const char *audit_cause = "hashing_error";
423323eec9SMimi Zohar 	int result;
433323eec9SMimi Zohar 
443323eec9SMimi Zohar 	memset(entry->digest, 0, sizeof(entry->digest));
453323eec9SMimi Zohar 	entry->template_name = IMA_TEMPLATE_NAME;
463323eec9SMimi Zohar 	entry->template_len = sizeof(entry->template);
473323eec9SMimi Zohar 
483323eec9SMimi Zohar 	if (!violation) {
493323eec9SMimi Zohar 		result = ima_calc_template_hash(entry->template_len,
503323eec9SMimi Zohar 						&entry->template,
513323eec9SMimi Zohar 						entry->digest);
523323eec9SMimi Zohar 		if (result < 0) {
533323eec9SMimi Zohar 			integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
543323eec9SMimi Zohar 					    entry->template_name, op,
553323eec9SMimi Zohar 					    audit_cause, result, 0);
563323eec9SMimi Zohar 			return result;
573323eec9SMimi Zohar 		}
583323eec9SMimi Zohar 	}
593323eec9SMimi Zohar 	result = ima_add_template_entry(entry, violation, op, inode);
603323eec9SMimi Zohar 	return result;
613323eec9SMimi Zohar }
623323eec9SMimi Zohar 
633323eec9SMimi Zohar /*
643323eec9SMimi Zohar  * ima_add_violation - add violation to measurement list.
653323eec9SMimi Zohar  *
663323eec9SMimi Zohar  * Violations are flagged in the measurement list with zero hash values.
673323eec9SMimi Zohar  * By extending the PCR with 0xFF's instead of with zeroes, the PCR
683323eec9SMimi Zohar  * value is invalidated.
693323eec9SMimi Zohar  */
703323eec9SMimi Zohar void ima_add_violation(struct inode *inode, const unsigned char *filename,
713323eec9SMimi Zohar 		       const char *op, const char *cause)
723323eec9SMimi Zohar {
733323eec9SMimi Zohar 	struct ima_template_entry *entry;
743323eec9SMimi Zohar 	int violation = 1;
753323eec9SMimi Zohar 	int result;
763323eec9SMimi Zohar 
773323eec9SMimi Zohar 	/* can overflow, only indicator */
783323eec9SMimi Zohar 	atomic_long_inc(&ima_htable.violations);
793323eec9SMimi Zohar 
803323eec9SMimi Zohar 	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
813323eec9SMimi Zohar 	if (!entry) {
823323eec9SMimi Zohar 		result = -ENOMEM;
833323eec9SMimi Zohar 		goto err_out;
843323eec9SMimi Zohar 	}
853323eec9SMimi Zohar 	memset(&entry->template, 0, sizeof(entry->template));
863323eec9SMimi Zohar 	strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX);
873323eec9SMimi Zohar 	result = ima_store_template(entry, violation, inode);
883323eec9SMimi Zohar 	if (result < 0)
893323eec9SMimi Zohar 		kfree(entry);
903323eec9SMimi Zohar err_out:
913323eec9SMimi Zohar 	integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
923323eec9SMimi Zohar 			    op, cause, result, 0);
933323eec9SMimi Zohar }
943323eec9SMimi Zohar 
953323eec9SMimi Zohar /**
963323eec9SMimi Zohar  * ima_must_measure - measure decision based on policy.
973323eec9SMimi Zohar  * @inode: pointer to inode to measure
983323eec9SMimi Zohar  * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE)
991e93d005SMimi Zohar  * @function: calling function (FILE_CHECK, BPRM_CHECK, FILE_MMAP)
1003323eec9SMimi Zohar  *
1013323eec9SMimi Zohar  * The policy is defined in terms of keypairs:
1023323eec9SMimi Zohar  * 		subj=, obj=, type=, func=, mask=, fsmagic=
1033323eec9SMimi Zohar  *	subj,obj, and type: are LSM specific.
1041e93d005SMimi Zohar  * 	func: FILE_CHECK | BPRM_CHECK | FILE_MMAP
1053323eec9SMimi Zohar  * 	mask: contains the permission mask
1063323eec9SMimi Zohar  *	fsmagic: hex value
1073323eec9SMimi Zohar  *
1083323eec9SMimi Zohar  * Must be called with iint->mutex held.
1093323eec9SMimi Zohar  *
1103323eec9SMimi Zohar  * Return 0 to measure. Return 1 if already measured.
1113323eec9SMimi Zohar  * For matching a DONT_MEASURE policy, no policy, or other
1123323eec9SMimi Zohar  * error, return an error code.
1133323eec9SMimi Zohar */
1143323eec9SMimi Zohar int ima_must_measure(struct ima_iint_cache *iint, struct inode *inode,
1153323eec9SMimi Zohar 		     int mask, int function)
1163323eec9SMimi Zohar {
1173323eec9SMimi Zohar 	int must_measure;
1183323eec9SMimi Zohar 
1193323eec9SMimi Zohar 	if (iint->flags & IMA_MEASURED)
1203323eec9SMimi Zohar 		return 1;
1213323eec9SMimi Zohar 
1223323eec9SMimi Zohar 	must_measure = ima_match_policy(inode, function, mask);
1233323eec9SMimi Zohar 	return must_measure ? 0 : -EACCES;
1243323eec9SMimi Zohar }
1253323eec9SMimi Zohar 
1263323eec9SMimi Zohar /*
1273323eec9SMimi Zohar  * ima_collect_measurement - collect file measurement
1283323eec9SMimi Zohar  *
1293323eec9SMimi Zohar  * Calculate the file hash, if it doesn't already exist,
1303323eec9SMimi Zohar  * storing the measurement and i_version in the iint.
1313323eec9SMimi Zohar  *
1323323eec9SMimi Zohar  * Must be called with iint->mutex held.
1333323eec9SMimi Zohar  *
1343323eec9SMimi Zohar  * Return 0 on success, error code otherwise
1353323eec9SMimi Zohar  */
1363323eec9SMimi Zohar int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file)
1373323eec9SMimi Zohar {
1383323eec9SMimi Zohar 	int result = -EEXIST;
1393323eec9SMimi Zohar 
1403323eec9SMimi Zohar 	if (!(iint->flags & IMA_MEASURED)) {
1413323eec9SMimi Zohar 		u64 i_version = file->f_dentry->d_inode->i_version;
1423323eec9SMimi Zohar 
1433323eec9SMimi Zohar 		memset(iint->digest, 0, IMA_DIGEST_SIZE);
1443323eec9SMimi Zohar 		result = ima_calc_hash(file, iint->digest);
1453323eec9SMimi Zohar 		if (!result)
1463323eec9SMimi Zohar 			iint->version = i_version;
1473323eec9SMimi Zohar 	}
1483323eec9SMimi Zohar 	return result;
1493323eec9SMimi Zohar }
1503323eec9SMimi Zohar 
1513323eec9SMimi Zohar /*
1523323eec9SMimi Zohar  * ima_store_measurement - store file measurement
1533323eec9SMimi Zohar  *
1543323eec9SMimi Zohar  * Create an "ima" template and then store the template by calling
1553323eec9SMimi Zohar  * ima_store_template.
1563323eec9SMimi Zohar  *
1573323eec9SMimi Zohar  * We only get here if the inode has not already been measured,
1583323eec9SMimi Zohar  * but the measurement could already exist:
1593323eec9SMimi Zohar  * 	- multiple copies of the same file on either the same or
1603323eec9SMimi Zohar  *	  different filesystems.
1613323eec9SMimi Zohar  *	- the inode was previously flushed as well as the iint info,
1623323eec9SMimi Zohar  *	  containing the hashing info.
1633323eec9SMimi Zohar  *
1643323eec9SMimi Zohar  * Must be called with iint->mutex held.
1653323eec9SMimi Zohar  */
1663323eec9SMimi Zohar void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
1673323eec9SMimi Zohar 			   const unsigned char *filename)
1683323eec9SMimi Zohar {
1693323eec9SMimi Zohar 	const char *op = "add_template_measure";
1703323eec9SMimi Zohar 	const char *audit_cause = "ENOMEM";
1713323eec9SMimi Zohar 	int result = -ENOMEM;
1723323eec9SMimi Zohar 	struct inode *inode = file->f_dentry->d_inode;
1733323eec9SMimi Zohar 	struct ima_template_entry *entry;
1743323eec9SMimi Zohar 	int violation = 0;
1753323eec9SMimi Zohar 
1763323eec9SMimi Zohar 	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
1773323eec9SMimi Zohar 	if (!entry) {
1783323eec9SMimi Zohar 		integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
1793323eec9SMimi Zohar 				    op, audit_cause, result, 0);
1803323eec9SMimi Zohar 		return;
1813323eec9SMimi Zohar 	}
1823323eec9SMimi Zohar 	memset(&entry->template, 0, sizeof(entry->template));
1833323eec9SMimi Zohar 	memcpy(entry->template.digest, iint->digest, IMA_DIGEST_SIZE);
1843323eec9SMimi Zohar 	strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX);
1853323eec9SMimi Zohar 
1863323eec9SMimi Zohar 	result = ima_store_template(entry, violation, inode);
1873323eec9SMimi Zohar 	if (!result)
1883323eec9SMimi Zohar 		iint->flags |= IMA_MEASURED;
1893323eec9SMimi Zohar 	else
1903323eec9SMimi Zohar 		kfree(entry);
1913323eec9SMimi Zohar }
192