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 * 1081adace9bSMimi Zohar * Return 0 to measure. For matching a DONT_MEASURE policy, no policy, 1091adace9bSMimi Zohar * or other error, return an error code. 1103323eec9SMimi Zohar */ 1111adace9bSMimi Zohar int ima_must_measure(struct inode *inode, int mask, int function) 1123323eec9SMimi Zohar { 1133323eec9SMimi Zohar int must_measure; 1143323eec9SMimi Zohar 1153323eec9SMimi Zohar must_measure = ima_match_policy(inode, function, mask); 1163323eec9SMimi Zohar return must_measure ? 0 : -EACCES; 1173323eec9SMimi Zohar } 1183323eec9SMimi Zohar 1193323eec9SMimi Zohar /* 1203323eec9SMimi Zohar * ima_collect_measurement - collect file measurement 1213323eec9SMimi Zohar * 1223323eec9SMimi Zohar * Calculate the file hash, if it doesn't already exist, 1233323eec9SMimi Zohar * storing the measurement and i_version in the iint. 1243323eec9SMimi Zohar * 1253323eec9SMimi Zohar * Must be called with iint->mutex held. 1263323eec9SMimi Zohar * 1273323eec9SMimi Zohar * Return 0 on success, error code otherwise 1283323eec9SMimi Zohar */ 1293323eec9SMimi Zohar int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file) 1303323eec9SMimi Zohar { 1313323eec9SMimi Zohar int result = -EEXIST; 1323323eec9SMimi Zohar 1333323eec9SMimi Zohar if (!(iint->flags & IMA_MEASURED)) { 1343323eec9SMimi Zohar u64 i_version = file->f_dentry->d_inode->i_version; 1353323eec9SMimi Zohar 1363323eec9SMimi Zohar memset(iint->digest, 0, IMA_DIGEST_SIZE); 1373323eec9SMimi Zohar result = ima_calc_hash(file, iint->digest); 1383323eec9SMimi Zohar if (!result) 1393323eec9SMimi Zohar iint->version = i_version; 1403323eec9SMimi Zohar } 1413323eec9SMimi Zohar return result; 1423323eec9SMimi Zohar } 1433323eec9SMimi Zohar 1443323eec9SMimi Zohar /* 1453323eec9SMimi Zohar * ima_store_measurement - store file measurement 1463323eec9SMimi Zohar * 1473323eec9SMimi Zohar * Create an "ima" template and then store the template by calling 1483323eec9SMimi Zohar * ima_store_template. 1493323eec9SMimi Zohar * 1503323eec9SMimi Zohar * We only get here if the inode has not already been measured, 1513323eec9SMimi Zohar * but the measurement could already exist: 1523323eec9SMimi Zohar * - multiple copies of the same file on either the same or 1533323eec9SMimi Zohar * different filesystems. 1543323eec9SMimi Zohar * - the inode was previously flushed as well as the iint info, 1553323eec9SMimi Zohar * containing the hashing info. 1563323eec9SMimi Zohar * 1573323eec9SMimi Zohar * Must be called with iint->mutex held. 1583323eec9SMimi Zohar */ 1593323eec9SMimi Zohar void ima_store_measurement(struct ima_iint_cache *iint, struct file *file, 1603323eec9SMimi Zohar const unsigned char *filename) 1613323eec9SMimi Zohar { 1623323eec9SMimi Zohar const char *op = "add_template_measure"; 1633323eec9SMimi Zohar const char *audit_cause = "ENOMEM"; 1643323eec9SMimi Zohar int result = -ENOMEM; 1653323eec9SMimi Zohar struct inode *inode = file->f_dentry->d_inode; 1663323eec9SMimi Zohar struct ima_template_entry *entry; 1673323eec9SMimi Zohar int violation = 0; 1683323eec9SMimi Zohar 1693323eec9SMimi Zohar entry = kmalloc(sizeof(*entry), GFP_KERNEL); 1703323eec9SMimi Zohar if (!entry) { 1713323eec9SMimi Zohar integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, 1723323eec9SMimi Zohar op, audit_cause, result, 0); 1733323eec9SMimi Zohar return; 1743323eec9SMimi Zohar } 1753323eec9SMimi Zohar memset(&entry->template, 0, sizeof(entry->template)); 1763323eec9SMimi Zohar memcpy(entry->template.digest, iint->digest, IMA_DIGEST_SIZE); 1773323eec9SMimi Zohar strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX); 1783323eec9SMimi Zohar 1793323eec9SMimi Zohar result = ima_store_template(entry, violation, inode); 1803323eec9SMimi Zohar if (!result) 1813323eec9SMimi Zohar iint->flags |= IMA_MEASURED; 1823323eec9SMimi Zohar else 1833323eec9SMimi Zohar kfree(entry); 1843323eec9SMimi Zohar } 185