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