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 122fe5d6deSMimi Zohar * Implements must_appraise_or_measure, collect_measurement, 132fe5d6deSMimi Zohar * appraise_measurement, store_measurement and store_template. 143323eec9SMimi Zohar */ 153323eec9SMimi Zohar #include <linux/module.h> 165a0e3ad6STejun Heo #include <linux/slab.h> 172fe5d6deSMimi Zohar #include <linux/file.h> 182fe5d6deSMimi Zohar #include <linux/fs.h> 192fe5d6deSMimi Zohar #include <linux/xattr.h> 202fe5d6deSMimi Zohar #include <linux/evm.h> 211525b06dSDmitry Kasatkin 223323eec9SMimi Zohar #include "ima.h" 232fe5d6deSMimi Zohar 243323eec9SMimi Zohar /* 25a7ed7c60SRoberto Sassu * ima_free_template_entry - free an existing template entry 26a7ed7c60SRoberto Sassu */ 27a7ed7c60SRoberto Sassu void ima_free_template_entry(struct ima_template_entry *entry) 28a7ed7c60SRoberto Sassu { 29a7ed7c60SRoberto Sassu int i; 30a7ed7c60SRoberto Sassu 31a7ed7c60SRoberto Sassu for (i = 0; i < entry->template_desc->num_fields; i++) 32a7ed7c60SRoberto Sassu kfree(entry->template_data[i].data); 33a7ed7c60SRoberto Sassu 34a7ed7c60SRoberto Sassu kfree(entry); 35a7ed7c60SRoberto Sassu } 36a7ed7c60SRoberto Sassu 37a7ed7c60SRoberto Sassu /* 387bc5f447SRoberto Sassu * ima_alloc_init_template - create and initialize a new template entry 397bc5f447SRoberto Sassu */ 4023b57419SRoberto Sassu int ima_alloc_init_template(struct ima_event_data *event_data, 4123b57419SRoberto Sassu struct ima_template_entry **entry) 427bc5f447SRoberto Sassu { 43a71dc65dSRoberto Sassu struct ima_template_desc *template_desc = ima_template_desc_current(); 44a71dc65dSRoberto Sassu int i, result = 0; 457bc5f447SRoberto Sassu 46a71dc65dSRoberto Sassu *entry = kzalloc(sizeof(**entry) + template_desc->num_fields * 47a71dc65dSRoberto Sassu sizeof(struct ima_field_data), GFP_NOFS); 48a71dc65dSRoberto Sassu if (!*entry) 497bc5f447SRoberto Sassu return -ENOMEM; 507bc5f447SRoberto Sassu 51a7ed7c60SRoberto Sassu (*entry)->template_desc = template_desc; 52a71dc65dSRoberto Sassu for (i = 0; i < template_desc->num_fields; i++) { 53a71dc65dSRoberto Sassu struct ima_template_field *field = template_desc->fields[i]; 54a71dc65dSRoberto Sassu u32 len; 55a71dc65dSRoberto Sassu 5623b57419SRoberto Sassu result = field->field_init(event_data, 57a71dc65dSRoberto Sassu &((*entry)->template_data[i])); 58a71dc65dSRoberto Sassu if (result != 0) 597bc5f447SRoberto Sassu goto out; 607bc5f447SRoberto Sassu 61a71dc65dSRoberto Sassu len = (*entry)->template_data[i].len; 62a71dc65dSRoberto Sassu (*entry)->template_data_len += sizeof(len); 63a71dc65dSRoberto Sassu (*entry)->template_data_len += len; 647bc5f447SRoberto Sassu } 657bc5f447SRoberto Sassu return 0; 66a71dc65dSRoberto Sassu out: 67a7ed7c60SRoberto Sassu ima_free_template_entry(*entry); 68a71dc65dSRoberto Sassu *entry = NULL; 697bc5f447SRoberto Sassu return result; 707bc5f447SRoberto Sassu } 717bc5f447SRoberto Sassu 727bc5f447SRoberto Sassu /* 733323eec9SMimi Zohar * ima_store_template - store ima template measurements 743323eec9SMimi Zohar * 753323eec9SMimi Zohar * Calculate the hash of a template entry, add the template entry 763323eec9SMimi Zohar * to an ordered list of measurement entries maintained inside the kernel, 773323eec9SMimi Zohar * and also update the aggregate integrity value (maintained inside the 783323eec9SMimi Zohar * configured TPM PCR) over the hashes of the current list of measurement 793323eec9SMimi Zohar * entries. 803323eec9SMimi Zohar * 813323eec9SMimi Zohar * Applications retrieve the current kernel-held measurement list through 823323eec9SMimi Zohar * the securityfs entries in /sys/kernel/security/ima. The signed aggregate 833323eec9SMimi Zohar * TPM PCR (called quote) can be retrieved using a TPM user space library 843323eec9SMimi Zohar * and is used to validate the measurement list. 853323eec9SMimi Zohar * 863323eec9SMimi Zohar * Returns 0 on success, error code otherwise 873323eec9SMimi Zohar */ 883323eec9SMimi Zohar int ima_store_template(struct ima_template_entry *entry, 899803d413SRoberto Sassu int violation, struct inode *inode, 9014b1da85SEric Richter const unsigned char *filename, int pcr) 913323eec9SMimi Zohar { 9252a13284SMimi Zohar static const char op[] = "add_template_measure"; 9352a13284SMimi Zohar static const char audit_cause[] = "hashing_error"; 94a71dc65dSRoberto Sassu char *template_name = entry->template_desc->name; 953323eec9SMimi Zohar int result; 96a35c3fb6SDmitry Kasatkin struct { 97a35c3fb6SDmitry Kasatkin struct ima_digest_data hdr; 98140d8022SMimi Zohar char digest[TPM_DIGEST_SIZE]; 99a35c3fb6SDmitry Kasatkin } hash; 1003323eec9SMimi Zohar 1013323eec9SMimi Zohar if (!violation) { 102a71dc65dSRoberto Sassu int num_fields = entry->template_desc->num_fields; 103a71dc65dSRoberto Sassu 104ea593993SDmitry Kasatkin /* this function uses default algo */ 105ea593993SDmitry Kasatkin hash.hdr.algo = HASH_ALGO_SHA1; 106a71dc65dSRoberto Sassu result = ima_calc_field_array_hash(&entry->template_data[0], 107b6f8f16fSRoberto Sassu entry->template_desc, 108a71dc65dSRoberto Sassu num_fields, &hash.hdr); 1093323eec9SMimi Zohar if (result < 0) { 1103323eec9SMimi Zohar integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, 111a71dc65dSRoberto Sassu template_name, op, 1123323eec9SMimi Zohar audit_cause, result, 0); 1133323eec9SMimi Zohar return result; 1143323eec9SMimi Zohar } 115a35c3fb6SDmitry Kasatkin memcpy(entry->digest, hash.hdr.digest, hash.hdr.length); 1163323eec9SMimi Zohar } 11714b1da85SEric Richter entry->pcr = pcr; 1189803d413SRoberto Sassu result = ima_add_template_entry(entry, violation, op, inode, filename); 1193323eec9SMimi Zohar return result; 1203323eec9SMimi Zohar } 1213323eec9SMimi Zohar 1223323eec9SMimi Zohar /* 1233323eec9SMimi Zohar * ima_add_violation - add violation to measurement list. 1243323eec9SMimi Zohar * 1253323eec9SMimi Zohar * Violations are flagged in the measurement list with zero hash values. 1263323eec9SMimi Zohar * By extending the PCR with 0xFF's instead of with zeroes, the PCR 1273323eec9SMimi Zohar * value is invalidated. 1283323eec9SMimi Zohar */ 1297d802a22SRoberto Sassu void ima_add_violation(struct file *file, const unsigned char *filename, 1308d94eb9bSRoberto Sassu struct integrity_iint_cache *iint, 1313323eec9SMimi Zohar const char *op, const char *cause) 1323323eec9SMimi Zohar { 1333323eec9SMimi Zohar struct ima_template_entry *entry; 13431d4b761SLibo Chen struct inode *inode = file_inode(file); 1358d94eb9bSRoberto Sassu struct ima_event_data event_data = {iint, file, filename, NULL, 0, 1368d94eb9bSRoberto Sassu cause}; 1373323eec9SMimi Zohar int violation = 1; 1383323eec9SMimi Zohar int result; 1393323eec9SMimi Zohar 1403323eec9SMimi Zohar /* can overflow, only indicator */ 1413323eec9SMimi Zohar atomic_long_inc(&ima_htable.violations); 1423323eec9SMimi Zohar 14323b57419SRoberto Sassu result = ima_alloc_init_template(&event_data, &entry); 1447bc5f447SRoberto Sassu if (result < 0) { 1453323eec9SMimi Zohar result = -ENOMEM; 1463323eec9SMimi Zohar goto err_out; 1473323eec9SMimi Zohar } 14814b1da85SEric Richter result = ima_store_template(entry, violation, inode, 14914b1da85SEric Richter filename, CONFIG_IMA_MEASURE_PCR_IDX); 1503323eec9SMimi Zohar if (result < 0) 151a7ed7c60SRoberto Sassu ima_free_template_entry(entry); 1523323eec9SMimi Zohar err_out: 1533323eec9SMimi Zohar integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, 1543323eec9SMimi Zohar op, cause, result, 0); 1553323eec9SMimi Zohar } 1563323eec9SMimi Zohar 1573323eec9SMimi Zohar /** 158d9d300cdSDmitry Kasatkin * ima_get_action - appraise & measure decision based on policy. 1593323eec9SMimi Zohar * @inode: pointer to inode to measure 16020f482abSLans Zhang * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXEC, 16120f482abSLans Zhang * MAY_APPEND) 1624ad87a3dSMimi Zohar * @func: caller identifier 163725de7faSEric Richter * @pcr: pointer filled in if matched measure policy sets pcr= 1643323eec9SMimi Zohar * 1653323eec9SMimi Zohar * The policy is defined in terms of keypairs: 1663323eec9SMimi Zohar * subj=, obj=, type=, func=, mask=, fsmagic= 1673323eec9SMimi Zohar * subj,obj, and type: are LSM specific. 16816cac49fSMimi Zohar * func: FILE_CHECK | BPRM_CHECK | MMAP_CHECK | MODULE_CHECK 1693323eec9SMimi Zohar * mask: contains the permission mask 1703323eec9SMimi Zohar * fsmagic: hex value 1713323eec9SMimi Zohar * 1722fe5d6deSMimi Zohar * Returns IMA_MEASURE, IMA_APPRAISE mask. 1732fe5d6deSMimi Zohar * 1743323eec9SMimi Zohar */ 175725de7faSEric Richter int ima_get_action(struct inode *inode, int mask, enum ima_hooks func, int *pcr) 1762fe5d6deSMimi Zohar { 177e7c568e0SPeter Moody int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE; 1782fe5d6deSMimi Zohar 179fd5f4e90SDmitry Kasatkin flags &= ima_policy_flag; 1802fe5d6deSMimi Zohar 181725de7faSEric Richter return ima_match_policy(inode, func, mask, flags, pcr); 1822fe5d6deSMimi Zohar } 1832fe5d6deSMimi Zohar 1843323eec9SMimi Zohar /* 1853323eec9SMimi Zohar * ima_collect_measurement - collect file measurement 1863323eec9SMimi Zohar * 1873323eec9SMimi Zohar * Calculate the file hash, if it doesn't already exist, 1883323eec9SMimi Zohar * storing the measurement and i_version in the iint. 1893323eec9SMimi Zohar * 1903323eec9SMimi Zohar * Must be called with iint->mutex held. 1913323eec9SMimi Zohar * 1923323eec9SMimi Zohar * Return 0 on success, error code otherwise 1933323eec9SMimi Zohar */ 194f381c272SMimi Zohar int ima_collect_measurement(struct integrity_iint_cache *iint, 195cf222217SMimi Zohar struct file *file, void *buf, loff_t size, 196cf222217SMimi Zohar enum hash_algo algo) 1973323eec9SMimi Zohar { 198f9b2a735SMimi Zohar const char *audit_cause = "failed"; 199496ad9aaSAl Viro struct inode *inode = file_inode(file); 200b583043eSAl Viro const char *filename = file->f_path.dentry->d_name.name; 2012fe5d6deSMimi Zohar int result = 0; 202f3cc6b25SMimi Zohar int length; 203f3cc6b25SMimi Zohar void *tmpbuf; 204f3cc6b25SMimi Zohar u64 i_version; 205a35c3fb6SDmitry Kasatkin struct { 206a35c3fb6SDmitry Kasatkin struct ima_digest_data hdr; 207a35c3fb6SDmitry Kasatkin char digest[IMA_MAX_DIGEST_SIZE]; 208a35c3fb6SDmitry Kasatkin } hash; 2093323eec9SMimi Zohar 210f3cc6b25SMimi Zohar if (iint->flags & IMA_COLLECTED) 211f3cc6b25SMimi Zohar goto out; 2123323eec9SMimi Zohar 213f3cc6b25SMimi Zohar /* 214f3cc6b25SMimi Zohar * Dectecting file change is based on i_version. On filesystems 215f3cc6b25SMimi Zohar * which do not support i_version, support is limited to an initial 216f3cc6b25SMimi Zohar * measurement/appraisal/audit. 217f3cc6b25SMimi Zohar */ 218f3cc6b25SMimi Zohar i_version = file_inode(file)->i_version; 219f3cc6b25SMimi Zohar hash.hdr.algo = algo; 220f3cc6b25SMimi Zohar 221f3cc6b25SMimi Zohar /* Initialize hash digest to 0's in case of failure */ 222f3cc6b25SMimi Zohar memset(&hash.digest, 0, sizeof(hash.digest)); 223f3cc6b25SMimi Zohar 224f3cc6b25SMimi Zohar if (buf) 225f3cc6b25SMimi Zohar result = ima_calc_buffer_hash(buf, size, &hash.hdr); 226f3cc6b25SMimi Zohar else 227f3cc6b25SMimi Zohar result = ima_calc_file_hash(file, &hash.hdr); 228f3cc6b25SMimi Zohar 229f3cc6b25SMimi Zohar if (result && result != -EBADF && result != -EINVAL) 230f3cc6b25SMimi Zohar goto out; 231f3cc6b25SMimi Zohar 232f3cc6b25SMimi Zohar length = sizeof(hash.hdr) + hash.hdr.length; 233f3cc6b25SMimi Zohar tmpbuf = krealloc(iint->ima_hash, length, GFP_NOFS); 234f3cc6b25SMimi Zohar if (!tmpbuf) { 235f3cc6b25SMimi Zohar result = -ENOMEM; 236f9b2a735SMimi Zohar goto out; 237f9b2a735SMimi Zohar } 238f9b2a735SMimi Zohar 239a35c3fb6SDmitry Kasatkin iint->ima_hash = tmpbuf; 240a35c3fb6SDmitry Kasatkin memcpy(iint->ima_hash, &hash, length); 2413323eec9SMimi Zohar iint->version = i_version; 242f3cc6b25SMimi Zohar 243f3cc6b25SMimi Zohar /* Possibly temporary failure due to type of read (eg. O_DIRECT) */ 244f3cc6b25SMimi Zohar if (!result) 2452fe5d6deSMimi Zohar iint->flags |= IMA_COLLECTED; 246f9b2a735SMimi Zohar out: 247f3cc6b25SMimi Zohar if (result) { 248f3cc6b25SMimi Zohar if (file->f_flags & O_DIRECT) 249f3cc6b25SMimi Zohar audit_cause = "failed(directio)"; 250f3cc6b25SMimi Zohar 2512fe5d6deSMimi Zohar integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, 252f9b2a735SMimi Zohar filename, "collect_data", audit_cause, 2532fe5d6deSMimi Zohar result, 0); 254f3cc6b25SMimi Zohar } 2553323eec9SMimi Zohar return result; 2563323eec9SMimi Zohar } 2573323eec9SMimi Zohar 2583323eec9SMimi Zohar /* 2593323eec9SMimi Zohar * ima_store_measurement - store file measurement 2603323eec9SMimi Zohar * 2613323eec9SMimi Zohar * Create an "ima" template and then store the template by calling 2623323eec9SMimi Zohar * ima_store_template. 2633323eec9SMimi Zohar * 2643323eec9SMimi Zohar * We only get here if the inode has not already been measured, 2653323eec9SMimi Zohar * but the measurement could already exist: 2663323eec9SMimi Zohar * - multiple copies of the same file on either the same or 2673323eec9SMimi Zohar * different filesystems. 2683323eec9SMimi Zohar * - the inode was previously flushed as well as the iint info, 2693323eec9SMimi Zohar * containing the hashing info. 2703323eec9SMimi Zohar * 2713323eec9SMimi Zohar * Must be called with iint->mutex held. 2723323eec9SMimi Zohar */ 273f381c272SMimi Zohar void ima_store_measurement(struct integrity_iint_cache *iint, 274bcbc9b0cSMimi Zohar struct file *file, const unsigned char *filename, 275bcbc9b0cSMimi Zohar struct evm_ima_xattr_data *xattr_value, 27614b1da85SEric Richter int xattr_len, int pcr) 2773323eec9SMimi Zohar { 27852a13284SMimi Zohar static const char op[] = "add_template_measure"; 27952a13284SMimi Zohar static const char audit_cause[] = "ENOMEM"; 2803323eec9SMimi Zohar int result = -ENOMEM; 281496ad9aaSAl Viro struct inode *inode = file_inode(file); 2823323eec9SMimi Zohar struct ima_template_entry *entry; 2838d94eb9bSRoberto Sassu struct ima_event_data event_data = {iint, file, filename, xattr_value, 2848d94eb9bSRoberto Sassu xattr_len, NULL}; 2853323eec9SMimi Zohar int violation = 0; 2863323eec9SMimi Zohar 287a422638dSEric Richter if (iint->measured_pcrs & (0x1 << pcr)) 2882fe5d6deSMimi Zohar return; 2892fe5d6deSMimi Zohar 29023b57419SRoberto Sassu result = ima_alloc_init_template(&event_data, &entry); 2917bc5f447SRoberto Sassu if (result < 0) { 2923323eec9SMimi Zohar integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, 2933323eec9SMimi Zohar op, audit_cause, result, 0); 2943323eec9SMimi Zohar return; 2953323eec9SMimi Zohar } 2963323eec9SMimi Zohar 29714b1da85SEric Richter result = ima_store_template(entry, violation, inode, filename, pcr); 298f3cc6b25SMimi Zohar if ((!result || result == -EEXIST) && !(file->f_flags & O_DIRECT)) { 2993323eec9SMimi Zohar iint->flags |= IMA_MEASURED; 300a422638dSEric Richter iint->measured_pcrs |= (0x1 << pcr); 301a422638dSEric Richter } 30245fae749SRoberto Sassu if (result < 0) 303a7ed7c60SRoberto Sassu ima_free_template_entry(entry); 3043323eec9SMimi Zohar } 305e7c568e0SPeter Moody 306e7c568e0SPeter Moody void ima_audit_measurement(struct integrity_iint_cache *iint, 307e7c568e0SPeter Moody const unsigned char *filename) 308e7c568e0SPeter Moody { 309e7c568e0SPeter Moody struct audit_buffer *ab; 310a35c3fb6SDmitry Kasatkin char hash[(iint->ima_hash->length * 2) + 1]; 3115278aa52SMimi Zohar const char *algo_name = hash_algo_name[iint->ima_hash->algo]; 3125278aa52SMimi Zohar char algo_hash[sizeof(hash) + strlen(algo_name) + 2]; 313e7c568e0SPeter Moody int i; 314e7c568e0SPeter Moody 315e7c568e0SPeter Moody if (iint->flags & IMA_AUDITED) 316e7c568e0SPeter Moody return; 317e7c568e0SPeter Moody 318a35c3fb6SDmitry Kasatkin for (i = 0; i < iint->ima_hash->length; i++) 319a35c3fb6SDmitry Kasatkin hex_byte_pack(hash + (i * 2), iint->ima_hash->digest[i]); 320e7c568e0SPeter Moody hash[i * 2] = '\0'; 321e7c568e0SPeter Moody 322e7c568e0SPeter Moody ab = audit_log_start(current->audit_context, GFP_KERNEL, 323e7c568e0SPeter Moody AUDIT_INTEGRITY_RULE); 324e7c568e0SPeter Moody if (!ab) 325e7c568e0SPeter Moody return; 326e7c568e0SPeter Moody 327e7c568e0SPeter Moody audit_log_format(ab, "file="); 328e7c568e0SPeter Moody audit_log_untrustedstring(ab, filename); 329e7c568e0SPeter Moody audit_log_format(ab, " hash="); 3305278aa52SMimi Zohar snprintf(algo_hash, sizeof(algo_hash), "%s:%s", algo_name, hash); 3315278aa52SMimi Zohar audit_log_untrustedstring(ab, algo_hash); 332e7c568e0SPeter Moody 333e7c568e0SPeter Moody audit_log_task_info(ab, current); 334e7c568e0SPeter Moody audit_log_end(ab); 335e7c568e0SPeter Moody 336e7c568e0SPeter Moody iint->flags |= IMA_AUDITED; 337e7c568e0SPeter Moody } 338ea1046d4SDmitry Kasatkin 339bc15ed66SMimi Zohar /* 340bc15ed66SMimi Zohar * ima_d_path - return a pointer to the full pathname 341bc15ed66SMimi Zohar * 342bc15ed66SMimi Zohar * Attempt to return a pointer to the full pathname for use in the 343bc15ed66SMimi Zohar * IMA measurement list, IMA audit records, and auditing logs. 344bc15ed66SMimi Zohar * 345bc15ed66SMimi Zohar * On failure, return a pointer to a copy of the filename, not dname. 346bc15ed66SMimi Zohar * Returning a pointer to dname, could result in using the pointer 347bc15ed66SMimi Zohar * after the memory has been freed. 348bc15ed66SMimi Zohar */ 349bc15ed66SMimi Zohar const char *ima_d_path(const struct path *path, char **pathbuf, char *namebuf) 350ea1046d4SDmitry Kasatkin { 351ea1046d4SDmitry Kasatkin char *pathname = NULL; 352ea1046d4SDmitry Kasatkin 353456f5fd3SDmitry Kasatkin *pathbuf = __getname(); 354ea1046d4SDmitry Kasatkin if (*pathbuf) { 35517f4bad3SDmitry Kasatkin pathname = d_absolute_path(path, *pathbuf, PATH_MAX); 356ea1046d4SDmitry Kasatkin if (IS_ERR(pathname)) { 357456f5fd3SDmitry Kasatkin __putname(*pathbuf); 358ea1046d4SDmitry Kasatkin *pathbuf = NULL; 359ea1046d4SDmitry Kasatkin pathname = NULL; 360ea1046d4SDmitry Kasatkin } 361ea1046d4SDmitry Kasatkin } 362bc15ed66SMimi Zohar 363bc15ed66SMimi Zohar if (!pathname) { 364bc15ed66SMimi Zohar strlcpy(namebuf, path->dentry->d_name.name, NAME_MAX); 365bc15ed66SMimi Zohar pathname = namebuf; 366bc15ed66SMimi Zohar } 367bc15ed66SMimi Zohar 368bc15ed66SMimi Zohar return pathname; 369ea1046d4SDmitry Kasatkin } 370