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