185 lines · 5.5 KB · C
RawDownload
/* * Copyright (C) 2008 IBM Corporation * * Author: Mimi Zohar <zohar@us.ibm.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, version 2 of the * License. * * File: ima_api.c *	Implements must_measure, collect_measurement, store_measurement, *	and store_template. */#include <linux/module.h>#include <linux/slab.h> #include "ima.h"static const char *IMA_TEMPLATE_NAME = "ima"; /* * ima_store_template - store ima template measurements * * Calculate the hash of a template entry, add the template entry * to an ordered list of measurement entries maintained inside the kernel, * and also update the aggregate integrity value (maintained inside the * configured TPM PCR) over the hashes of the current list of measurement * entries. * * Applications retrieve the current kernel-held measurement list through * the securityfs entries in /sys/kernel/security/ima. The signed aggregate * TPM PCR (called quote) can be retrieved using a TPM user space library * and is used to validate the measurement list. * * Returns 0 on success, error code otherwise */int ima_store_template(struct ima_template_entry *entry,		       int violation, struct inode *inode){	const char *op = "add_template_measure";	const char *audit_cause = "hashing_error";	int result; 	memset(entry->digest, 0, sizeof(entry->digest));	entry->template_name = IMA_TEMPLATE_NAME;	entry->template_len = sizeof(entry->template); 	if (!violation) {		result = ima_calc_template_hash(entry->template_len,						&entry->template,						entry->digest);		if (result < 0) {			integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,					    entry->template_name, op,					    audit_cause, result, 0);			return result;		}	}	result = ima_add_template_entry(entry, violation, op, inode);	return result;} /* * ima_add_violation - add violation to measurement list. * * Violations are flagged in the measurement list with zero hash values. * By extending the PCR with 0xFF's instead of with zeroes, the PCR * value is invalidated. */void ima_add_violation(struct inode *inode, const unsigned char *filename,		       const char *op, const char *cause){	struct ima_template_entry *entry;	int violation = 1;	int result; 	/* can overflow, only indicator */	atomic_long_inc(&ima_htable.violations); 	entry = kmalloc(sizeof(*entry), GFP_KERNEL);	if (!entry) {		result = -ENOMEM;		goto err_out;	}	memset(&entry->template, 0, sizeof(entry->template));	strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX);	result = ima_store_template(entry, violation, inode);	if (result < 0)		kfree(entry);err_out:	integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,			    op, cause, result, 0);} /** * ima_must_measure - measure decision based on policy. * @inode: pointer to inode to measure * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE) * @function: calling function (FILE_CHECK, BPRM_CHECK, FILE_MMAP) * * The policy is defined in terms of keypairs: * 		subj=, obj=, type=, func=, mask=, fsmagic= *	subj,obj, and type: are LSM specific. * 	func: FILE_CHECK | BPRM_CHECK | FILE_MMAP * 	mask: contains the permission mask *	fsmagic: hex value * * Return 0 to measure. For matching a DONT_MEASURE policy, no policy, * or other error, return an error code.*/int ima_must_measure(struct inode *inode, int mask, int function){	int must_measure; 	must_measure = ima_match_policy(inode, function, mask);	return must_measure ? 0 : -EACCES;} /* * ima_collect_measurement - collect file measurement * * Calculate the file hash, if it doesn't already exist, * storing the measurement and i_version in the iint. * * Must be called with iint->mutex held. * * Return 0 on success, error code otherwise */int ima_collect_measurement(struct integrity_iint_cache *iint,			    struct file *file){	int result = -EEXIST; 	if (!(iint->flags & IMA_MEASURED)) {		u64 i_version = file->f_dentry->d_inode->i_version; 		memset(iint->digest, 0, IMA_DIGEST_SIZE);		result = ima_calc_hash(file, iint->digest);		if (!result)			iint->version = i_version;	}	return result;} /* * ima_store_measurement - store file measurement * * Create an "ima" template and then store the template by calling * ima_store_template. * * We only get here if the inode has not already been measured, * but the measurement could already exist: * 	- multiple copies of the same file on either the same or *	  different filesystems. *	- the inode was previously flushed as well as the iint info, *	  containing the hashing info. * * Must be called with iint->mutex held. */void ima_store_measurement(struct integrity_iint_cache *iint,			   struct file *file, const unsigned char *filename){	const char *op = "add_template_measure";	const char *audit_cause = "ENOMEM";	int result = -ENOMEM;	struct inode *inode = file->f_dentry->d_inode;	struct ima_template_entry *entry;	int violation = 0; 	entry = kmalloc(sizeof(*entry), GFP_KERNEL);	if (!entry) {		integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,				    op, audit_cause, result, 0);		return;	}	memset(&entry->template, 0, sizeof(entry->template));	memcpy(entry->template.digest, iint->digest, IMA_DIGEST_SIZE);	strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX); 	result = ima_store_template(entry, violation, inode);	if (!result || result == -EEXIST)		iint->flags |= IMA_MEASURED;	if (result < 0)		kfree(entry);}