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> 21ea593993SDmitry Kasatkin #include <crypto/hash_info.h> 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 */ 407bc5f447SRoberto Sassu int ima_alloc_init_template(struct integrity_iint_cache *iint, 417bc5f447SRoberto Sassu struct file *file, const unsigned char *filename, 42bcbc9b0cSMimi Zohar struct evm_ima_xattr_data *xattr_value, 43bcbc9b0cSMimi Zohar int xattr_len, struct ima_template_entry **entry) 447bc5f447SRoberto Sassu { 45a71dc65dSRoberto Sassu struct ima_template_desc *template_desc = ima_template_desc_current(); 46a71dc65dSRoberto Sassu int i, result = 0; 477bc5f447SRoberto Sassu 48a71dc65dSRoberto Sassu *entry = kzalloc(sizeof(**entry) + template_desc->num_fields * 49a71dc65dSRoberto Sassu sizeof(struct ima_field_data), GFP_NOFS); 50a71dc65dSRoberto Sassu if (!*entry) 517bc5f447SRoberto Sassu return -ENOMEM; 527bc5f447SRoberto Sassu 53a7ed7c60SRoberto Sassu (*entry)->template_desc = template_desc; 54a71dc65dSRoberto Sassu for (i = 0; i < template_desc->num_fields; i++) { 55a71dc65dSRoberto Sassu struct ima_template_field *field = template_desc->fields[i]; 56a71dc65dSRoberto Sassu u32 len; 57a71dc65dSRoberto Sassu 58a71dc65dSRoberto Sassu result = field->field_init(iint, file, filename, 59bcbc9b0cSMimi Zohar xattr_value, xattr_len, 60a71dc65dSRoberto Sassu &((*entry)->template_data[i])); 61a71dc65dSRoberto Sassu if (result != 0) 627bc5f447SRoberto Sassu goto out; 637bc5f447SRoberto Sassu 64a71dc65dSRoberto Sassu len = (*entry)->template_data[i].len; 65a71dc65dSRoberto Sassu (*entry)->template_data_len += sizeof(len); 66a71dc65dSRoberto Sassu (*entry)->template_data_len += len; 677bc5f447SRoberto Sassu } 687bc5f447SRoberto Sassu return 0; 69a71dc65dSRoberto Sassu out: 70a7ed7c60SRoberto Sassu ima_free_template_entry(*entry); 71a71dc65dSRoberto Sassu *entry = NULL; 727bc5f447SRoberto Sassu return result; 737bc5f447SRoberto Sassu } 747bc5f447SRoberto Sassu 757bc5f447SRoberto Sassu /* 763323eec9SMimi Zohar * ima_store_template - store ima template measurements 773323eec9SMimi Zohar * 783323eec9SMimi Zohar * Calculate the hash of a template entry, add the template entry 793323eec9SMimi Zohar * to an ordered list of measurement entries maintained inside the kernel, 803323eec9SMimi Zohar * and also update the aggregate integrity value (maintained inside the 813323eec9SMimi Zohar * configured TPM PCR) over the hashes of the current list of measurement 823323eec9SMimi Zohar * entries. 833323eec9SMimi Zohar * 843323eec9SMimi Zohar * Applications retrieve the current kernel-held measurement list through 853323eec9SMimi Zohar * the securityfs entries in /sys/kernel/security/ima. The signed aggregate 863323eec9SMimi Zohar * TPM PCR (called quote) can be retrieved using a TPM user space library 873323eec9SMimi Zohar * and is used to validate the measurement list. 883323eec9SMimi Zohar * 893323eec9SMimi Zohar * Returns 0 on success, error code otherwise 903323eec9SMimi Zohar */ 913323eec9SMimi Zohar int ima_store_template(struct ima_template_entry *entry, 929803d413SRoberto Sassu int violation, struct inode *inode, 939803d413SRoberto Sassu const unsigned char *filename) 943323eec9SMimi Zohar { 9552a13284SMimi Zohar static const char op[] = "add_template_measure"; 9652a13284SMimi Zohar static const char audit_cause[] = "hashing_error"; 97a71dc65dSRoberto Sassu char *template_name = entry->template_desc->name; 983323eec9SMimi Zohar int result; 99a35c3fb6SDmitry Kasatkin struct { 100a35c3fb6SDmitry Kasatkin struct ima_digest_data hdr; 101140d8022SMimi Zohar char digest[TPM_DIGEST_SIZE]; 102a35c3fb6SDmitry Kasatkin } hash; 1033323eec9SMimi Zohar 1043323eec9SMimi Zohar if (!violation) { 105a71dc65dSRoberto Sassu int num_fields = entry->template_desc->num_fields; 106a71dc65dSRoberto Sassu 107ea593993SDmitry Kasatkin /* this function uses default algo */ 108ea593993SDmitry Kasatkin hash.hdr.algo = HASH_ALGO_SHA1; 109a71dc65dSRoberto Sassu result = ima_calc_field_array_hash(&entry->template_data[0], 110b6f8f16fSRoberto Sassu entry->template_desc, 111a71dc65dSRoberto Sassu num_fields, &hash.hdr); 1123323eec9SMimi Zohar if (result < 0) { 1133323eec9SMimi Zohar integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, 114a71dc65dSRoberto Sassu template_name, op, 1153323eec9SMimi Zohar audit_cause, result, 0); 1163323eec9SMimi Zohar return result; 1173323eec9SMimi Zohar } 118a35c3fb6SDmitry Kasatkin memcpy(entry->digest, hash.hdr.digest, hash.hdr.length); 1193323eec9SMimi Zohar } 1209803d413SRoberto Sassu result = ima_add_template_entry(entry, violation, op, inode, filename); 1213323eec9SMimi Zohar return result; 1223323eec9SMimi Zohar } 1233323eec9SMimi Zohar 1243323eec9SMimi Zohar /* 1253323eec9SMimi Zohar * ima_add_violation - add violation to measurement list. 1263323eec9SMimi Zohar * 1273323eec9SMimi Zohar * Violations are flagged in the measurement list with zero hash values. 1283323eec9SMimi Zohar * By extending the PCR with 0xFF's instead of with zeroes, the PCR 1293323eec9SMimi Zohar * value is invalidated. 1303323eec9SMimi Zohar */ 1317d802a22SRoberto Sassu void ima_add_violation(struct file *file, const unsigned char *filename, 1323323eec9SMimi Zohar const char *op, const char *cause) 1333323eec9SMimi Zohar { 1343323eec9SMimi Zohar struct ima_template_entry *entry; 13531d4b761SLibo Chen struct inode *inode = file_inode(file); 1363323eec9SMimi Zohar int violation = 1; 1373323eec9SMimi Zohar int result; 1383323eec9SMimi Zohar 1393323eec9SMimi Zohar /* can overflow, only indicator */ 1403323eec9SMimi Zohar atomic_long_inc(&ima_htable.violations); 1413323eec9SMimi Zohar 142bcbc9b0cSMimi Zohar result = ima_alloc_init_template(NULL, file, filename, 143bcbc9b0cSMimi Zohar NULL, 0, &entry); 1447bc5f447SRoberto Sassu if (result < 0) { 1453323eec9SMimi Zohar result = -ENOMEM; 1463323eec9SMimi Zohar goto err_out; 1473323eec9SMimi Zohar } 1489803d413SRoberto Sassu result = ima_store_template(entry, violation, inode, filename); 1493323eec9SMimi Zohar if (result < 0) 150a7ed7c60SRoberto Sassu ima_free_template_entry(entry); 1513323eec9SMimi Zohar err_out: 1523323eec9SMimi Zohar integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, 1533323eec9SMimi Zohar op, cause, result, 0); 1543323eec9SMimi Zohar } 1553323eec9SMimi Zohar 1563323eec9SMimi Zohar /** 157d9d300cdSDmitry Kasatkin * ima_get_action - appraise & measure decision based on policy. 1583323eec9SMimi Zohar * @inode: pointer to inode to measure 1593323eec9SMimi Zohar * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE) 16016cac49fSMimi Zohar * @function: calling function (FILE_CHECK, BPRM_CHECK, MMAP_CHECK, MODULE_CHECK) 1613323eec9SMimi Zohar * 1623323eec9SMimi Zohar * The policy is defined in terms of keypairs: 1633323eec9SMimi Zohar * subj=, obj=, type=, func=, mask=, fsmagic= 1643323eec9SMimi Zohar * subj,obj, and type: are LSM specific. 16516cac49fSMimi Zohar * func: FILE_CHECK | BPRM_CHECK | MMAP_CHECK | MODULE_CHECK 1663323eec9SMimi Zohar * mask: contains the permission mask 1673323eec9SMimi Zohar * fsmagic: hex value 1683323eec9SMimi Zohar * 1692fe5d6deSMimi Zohar * Returns IMA_MEASURE, IMA_APPRAISE mask. 1702fe5d6deSMimi Zohar * 1713323eec9SMimi Zohar */ 172d9d300cdSDmitry Kasatkin int ima_get_action(struct inode *inode, int mask, int function) 1732fe5d6deSMimi Zohar { 174e7c568e0SPeter Moody int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE; 1752fe5d6deSMimi Zohar 1762fe5d6deSMimi Zohar if (!ima_appraise) 1772fe5d6deSMimi Zohar flags &= ~IMA_APPRAISE; 1782fe5d6deSMimi Zohar 1792fe5d6deSMimi Zohar return ima_match_policy(inode, function, mask, flags); 1802fe5d6deSMimi Zohar } 1812fe5d6deSMimi Zohar 1821adace9bSMimi Zohar int ima_must_measure(struct inode *inode, int mask, int function) 1833323eec9SMimi Zohar { 1842fe5d6deSMimi Zohar return ima_match_policy(inode, function, mask, IMA_MEASURE); 1853323eec9SMimi Zohar } 1863323eec9SMimi Zohar 1873323eec9SMimi Zohar /* 1883323eec9SMimi Zohar * ima_collect_measurement - collect file measurement 1893323eec9SMimi Zohar * 1903323eec9SMimi Zohar * Calculate the file hash, if it doesn't already exist, 1913323eec9SMimi Zohar * storing the measurement and i_version in the iint. 1923323eec9SMimi Zohar * 1933323eec9SMimi Zohar * Must be called with iint->mutex held. 1943323eec9SMimi Zohar * 1953323eec9SMimi Zohar * Return 0 on success, error code otherwise 1963323eec9SMimi Zohar */ 197f381c272SMimi Zohar int ima_collect_measurement(struct integrity_iint_cache *iint, 198d3634d0fSDmitry Kasatkin struct file *file, 199d3634d0fSDmitry Kasatkin struct evm_ima_xattr_data **xattr_value, 200d3634d0fSDmitry Kasatkin int *xattr_len) 2013323eec9SMimi Zohar { 202f9b2a735SMimi Zohar const char *audit_cause = "failed"; 203496ad9aaSAl Viro struct inode *inode = file_inode(file); 2042fe5d6deSMimi Zohar const char *filename = file->f_dentry->d_name.name; 2052fe5d6deSMimi Zohar int result = 0; 206a35c3fb6SDmitry Kasatkin struct { 207a35c3fb6SDmitry Kasatkin struct ima_digest_data hdr; 208a35c3fb6SDmitry Kasatkin char digest[IMA_MAX_DIGEST_SIZE]; 209a35c3fb6SDmitry Kasatkin } hash; 2103323eec9SMimi Zohar 211d3634d0fSDmitry Kasatkin if (xattr_value) 212d3634d0fSDmitry Kasatkin *xattr_len = ima_read_xattr(file->f_dentry, xattr_value); 213d3634d0fSDmitry Kasatkin 2142fe5d6deSMimi Zohar if (!(iint->flags & IMA_COLLECTED)) { 215496ad9aaSAl Viro u64 i_version = file_inode(file)->i_version; 2163323eec9SMimi Zohar 217f9b2a735SMimi Zohar if (file->f_flags & O_DIRECT) { 218f9b2a735SMimi Zohar audit_cause = "failed(directio)"; 219f9b2a735SMimi Zohar result = -EACCES; 220f9b2a735SMimi Zohar goto out; 221f9b2a735SMimi Zohar } 222f9b2a735SMimi Zohar 223c7c8bb23SDmitry Kasatkin /* use default hash algorithm */ 224a35c3fb6SDmitry Kasatkin hash.hdr.algo = ima_hash_algo; 225d3634d0fSDmitry Kasatkin 226d3634d0fSDmitry Kasatkin if (xattr_value) 227a35c3fb6SDmitry Kasatkin ima_get_hash_algo(*xattr_value, *xattr_len, &hash.hdr); 228d3634d0fSDmitry Kasatkin 229a35c3fb6SDmitry Kasatkin result = ima_calc_file_hash(file, &hash.hdr); 2302fe5d6deSMimi Zohar if (!result) { 231a35c3fb6SDmitry Kasatkin int length = sizeof(hash.hdr) + hash.hdr.length; 232a35c3fb6SDmitry Kasatkin void *tmpbuf = krealloc(iint->ima_hash, length, 233a35c3fb6SDmitry Kasatkin GFP_NOFS); 234a35c3fb6SDmitry Kasatkin if (tmpbuf) { 235a35c3fb6SDmitry Kasatkin iint->ima_hash = tmpbuf; 236a35c3fb6SDmitry Kasatkin memcpy(iint->ima_hash, &hash, length); 2373323eec9SMimi Zohar iint->version = i_version; 2382fe5d6deSMimi Zohar iint->flags |= IMA_COLLECTED; 239a35c3fb6SDmitry Kasatkin } else 240a35c3fb6SDmitry Kasatkin result = -ENOMEM; 2413323eec9SMimi Zohar } 2422fe5d6deSMimi Zohar } 243f9b2a735SMimi Zohar out: 2442fe5d6deSMimi Zohar if (result) 2452fe5d6deSMimi Zohar integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, 246f9b2a735SMimi Zohar filename, "collect_data", audit_cause, 2472fe5d6deSMimi Zohar result, 0); 2483323eec9SMimi Zohar return result; 2493323eec9SMimi Zohar } 2503323eec9SMimi Zohar 2513323eec9SMimi Zohar /* 2523323eec9SMimi Zohar * ima_store_measurement - store file measurement 2533323eec9SMimi Zohar * 2543323eec9SMimi Zohar * Create an "ima" template and then store the template by calling 2553323eec9SMimi Zohar * ima_store_template. 2563323eec9SMimi Zohar * 2573323eec9SMimi Zohar * We only get here if the inode has not already been measured, 2583323eec9SMimi Zohar * but the measurement could already exist: 2593323eec9SMimi Zohar * - multiple copies of the same file on either the same or 2603323eec9SMimi Zohar * different filesystems. 2613323eec9SMimi Zohar * - the inode was previously flushed as well as the iint info, 2623323eec9SMimi Zohar * containing the hashing info. 2633323eec9SMimi Zohar * 2643323eec9SMimi Zohar * Must be called with iint->mutex held. 2653323eec9SMimi Zohar */ 266f381c272SMimi Zohar void ima_store_measurement(struct integrity_iint_cache *iint, 267bcbc9b0cSMimi Zohar struct file *file, const unsigned char *filename, 268bcbc9b0cSMimi Zohar struct evm_ima_xattr_data *xattr_value, 269bcbc9b0cSMimi Zohar int xattr_len) 2703323eec9SMimi Zohar { 27152a13284SMimi Zohar static const char op[] = "add_template_measure"; 27252a13284SMimi Zohar static const char audit_cause[] = "ENOMEM"; 2733323eec9SMimi Zohar int result = -ENOMEM; 274496ad9aaSAl Viro struct inode *inode = file_inode(file); 2753323eec9SMimi Zohar struct ima_template_entry *entry; 2763323eec9SMimi Zohar int violation = 0; 2773323eec9SMimi Zohar 2782fe5d6deSMimi Zohar if (iint->flags & IMA_MEASURED) 2792fe5d6deSMimi Zohar return; 2802fe5d6deSMimi Zohar 281bcbc9b0cSMimi Zohar result = ima_alloc_init_template(iint, file, filename, 282bcbc9b0cSMimi Zohar xattr_value, xattr_len, &entry); 2837bc5f447SRoberto Sassu if (result < 0) { 2843323eec9SMimi Zohar integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, 2853323eec9SMimi Zohar op, audit_cause, result, 0); 2863323eec9SMimi Zohar return; 2873323eec9SMimi Zohar } 2883323eec9SMimi Zohar 2899803d413SRoberto Sassu result = ima_store_template(entry, violation, inode, filename); 29045fae749SRoberto Sassu if (!result || result == -EEXIST) 2913323eec9SMimi Zohar iint->flags |= IMA_MEASURED; 29245fae749SRoberto Sassu if (result < 0) 293a7ed7c60SRoberto Sassu ima_free_template_entry(entry); 2943323eec9SMimi Zohar } 295e7c568e0SPeter Moody 296e7c568e0SPeter Moody void ima_audit_measurement(struct integrity_iint_cache *iint, 297e7c568e0SPeter Moody const unsigned char *filename) 298e7c568e0SPeter Moody { 299e7c568e0SPeter Moody struct audit_buffer *ab; 300a35c3fb6SDmitry Kasatkin char hash[(iint->ima_hash->length * 2) + 1]; 3015278aa52SMimi Zohar const char *algo_name = hash_algo_name[iint->ima_hash->algo]; 3025278aa52SMimi Zohar char algo_hash[sizeof(hash) + strlen(algo_name) + 2]; 303e7c568e0SPeter Moody int i; 304e7c568e0SPeter Moody 305e7c568e0SPeter Moody if (iint->flags & IMA_AUDITED) 306e7c568e0SPeter Moody return; 307e7c568e0SPeter Moody 308a35c3fb6SDmitry Kasatkin for (i = 0; i < iint->ima_hash->length; i++) 309a35c3fb6SDmitry Kasatkin hex_byte_pack(hash + (i * 2), iint->ima_hash->digest[i]); 310e7c568e0SPeter Moody hash[i * 2] = '\0'; 311e7c568e0SPeter Moody 312e7c568e0SPeter Moody ab = audit_log_start(current->audit_context, GFP_KERNEL, 313e7c568e0SPeter Moody AUDIT_INTEGRITY_RULE); 314e7c568e0SPeter Moody if (!ab) 315e7c568e0SPeter Moody return; 316e7c568e0SPeter Moody 317e7c568e0SPeter Moody audit_log_format(ab, "file="); 318e7c568e0SPeter Moody audit_log_untrustedstring(ab, filename); 319e7c568e0SPeter Moody audit_log_format(ab, " hash="); 3205278aa52SMimi Zohar snprintf(algo_hash, sizeof(algo_hash), "%s:%s", algo_name, hash); 3215278aa52SMimi Zohar audit_log_untrustedstring(ab, algo_hash); 322e7c568e0SPeter Moody 323e7c568e0SPeter Moody audit_log_task_info(ab, current); 324e7c568e0SPeter Moody audit_log_end(ab); 325e7c568e0SPeter Moody 326e7c568e0SPeter Moody iint->flags |= IMA_AUDITED; 327e7c568e0SPeter Moody } 328ea1046d4SDmitry Kasatkin 329ea1046d4SDmitry Kasatkin const char *ima_d_path(struct path *path, char **pathbuf) 330ea1046d4SDmitry Kasatkin { 331ea1046d4SDmitry Kasatkin char *pathname = NULL; 332ea1046d4SDmitry Kasatkin 333ea1046d4SDmitry Kasatkin /* We will allow 11 spaces for ' (deleted)' to be appended */ 334ea1046d4SDmitry Kasatkin *pathbuf = kmalloc(PATH_MAX + 11, GFP_KERNEL); 335ea1046d4SDmitry Kasatkin if (*pathbuf) { 336ea1046d4SDmitry Kasatkin pathname = d_path(path, *pathbuf, PATH_MAX + 11); 337ea1046d4SDmitry Kasatkin if (IS_ERR(pathname)) { 338ea1046d4SDmitry Kasatkin kfree(*pathbuf); 339ea1046d4SDmitry Kasatkin *pathbuf = NULL; 340ea1046d4SDmitry Kasatkin pathname = NULL; 341ea1046d4SDmitry Kasatkin } 342ea1046d4SDmitry Kasatkin } 34361997c43SDmitry Kasatkin return pathname ?: (const char *)path->dentry->d_name.name; 344ea1046d4SDmitry Kasatkin } 345