18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Integrity Measurement Architecture 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2005,2006,2007,2008 IBM Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Authors: 88c2ecf20Sopenharmony_ci * Reiner Sailer <sailer@watson.ibm.com> 98c2ecf20Sopenharmony_ci * Serge Hallyn <serue@us.ibm.com> 108c2ecf20Sopenharmony_ci * Kylene Hall <kylene@us.ibm.com> 118c2ecf20Sopenharmony_ci * Mimi Zohar <zohar@us.ibm.com> 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * File: ima_main.c 148c2ecf20Sopenharmony_ci * implements the IMA hooks: ima_bprm_check, ima_file_mmap, 158c2ecf20Sopenharmony_ci * and ima_file_check. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/file.h> 208c2ecf20Sopenharmony_ci#include <linux/binfmts.h> 218c2ecf20Sopenharmony_ci#include <linux/kernel_read_file.h> 228c2ecf20Sopenharmony_ci#include <linux/mount.h> 238c2ecf20Sopenharmony_ci#include <linux/mman.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <linux/xattr.h> 268c2ecf20Sopenharmony_ci#include <linux/ima.h> 278c2ecf20Sopenharmony_ci#include <linux/iversion.h> 288c2ecf20Sopenharmony_ci#include <linux/fs.h> 298c2ecf20Sopenharmony_ci#include <linux/iversion.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include "ima.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#ifdef CONFIG_IMA_APPRAISE 348c2ecf20Sopenharmony_ciint ima_appraise = IMA_APPRAISE_ENFORCE; 358c2ecf20Sopenharmony_ci#else 368c2ecf20Sopenharmony_ciint ima_appraise; 378c2ecf20Sopenharmony_ci#endif 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ciint ima_hash_algo = HASH_ALGO_SHA1; 408c2ecf20Sopenharmony_cistatic int hash_setup_done; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic struct notifier_block ima_lsm_policy_notifier = { 438c2ecf20Sopenharmony_ci .notifier_call = ima_lsm_policy_change, 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic int __init hash_setup(char *str) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct ima_template_desc *template_desc = ima_template_desc_current(); 498c2ecf20Sopenharmony_ci int i; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (hash_setup_done) 528c2ecf20Sopenharmony_ci return 1; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0) { 558c2ecf20Sopenharmony_ci if (strncmp(str, "sha1", 4) == 0) { 568c2ecf20Sopenharmony_ci ima_hash_algo = HASH_ALGO_SHA1; 578c2ecf20Sopenharmony_ci } else if (strncmp(str, "md5", 3) == 0) { 588c2ecf20Sopenharmony_ci ima_hash_algo = HASH_ALGO_MD5; 598c2ecf20Sopenharmony_ci } else { 608c2ecf20Sopenharmony_ci pr_err("invalid hash algorithm \"%s\" for template \"%s\"", 618c2ecf20Sopenharmony_ci str, IMA_TEMPLATE_IMA_NAME); 628c2ecf20Sopenharmony_ci return 1; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci goto out; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci i = match_string(hash_algo_name, HASH_ALGO__LAST, str); 688c2ecf20Sopenharmony_ci if (i < 0) { 698c2ecf20Sopenharmony_ci pr_err("invalid hash algorithm \"%s\"", str); 708c2ecf20Sopenharmony_ci return 1; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci ima_hash_algo = i; 748c2ecf20Sopenharmony_ciout: 758c2ecf20Sopenharmony_ci hash_setup_done = 1; 768c2ecf20Sopenharmony_ci return 1; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci__setup("ima_hash=", hash_setup); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* Prevent mmap'ing a file execute that is already mmap'ed write */ 818c2ecf20Sopenharmony_cistatic int mmap_violation_check(enum ima_hooks func, struct file *file, 828c2ecf20Sopenharmony_ci char **pathbuf, const char **pathname, 838c2ecf20Sopenharmony_ci char *filename) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct inode *inode; 868c2ecf20Sopenharmony_ci int rc = 0; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if ((func == MMAP_CHECK) && mapping_writably_mapped(file->f_mapping)) { 898c2ecf20Sopenharmony_ci rc = -ETXTBSY; 908c2ecf20Sopenharmony_ci inode = file_inode(file); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (!*pathbuf) /* ima_rdwr_violation possibly pre-fetched */ 938c2ecf20Sopenharmony_ci *pathname = ima_d_path(&file->f_path, pathbuf, 948c2ecf20Sopenharmony_ci filename); 958c2ecf20Sopenharmony_ci integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, *pathname, 968c2ecf20Sopenharmony_ci "mmap_file", "mmapped_writers", rc, 0); 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci return rc; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* 1028c2ecf20Sopenharmony_ci * ima_rdwr_violation_check 1038c2ecf20Sopenharmony_ci * 1048c2ecf20Sopenharmony_ci * Only invalidate the PCR for measured files: 1058c2ecf20Sopenharmony_ci * - Opening a file for write when already open for read, 1068c2ecf20Sopenharmony_ci * results in a time of measure, time of use (ToMToU) error. 1078c2ecf20Sopenharmony_ci * - Opening a file for read when already open for write, 1088c2ecf20Sopenharmony_ci * could result in a file measurement error. 1098c2ecf20Sopenharmony_ci * 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_cistatic void ima_rdwr_violation_check(struct file *file, 1128c2ecf20Sopenharmony_ci struct integrity_iint_cache *iint, 1138c2ecf20Sopenharmony_ci int must_measure, 1148c2ecf20Sopenharmony_ci char **pathbuf, 1158c2ecf20Sopenharmony_ci const char **pathname, 1168c2ecf20Sopenharmony_ci char *filename) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct inode *inode = file_inode(file); 1198c2ecf20Sopenharmony_ci fmode_t mode = file->f_mode; 1208c2ecf20Sopenharmony_ci bool send_tomtou = false, send_writers = false; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (mode & FMODE_WRITE) { 1238c2ecf20Sopenharmony_ci if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) { 1248c2ecf20Sopenharmony_ci if (!iint) 1258c2ecf20Sopenharmony_ci iint = integrity_iint_find(inode); 1268c2ecf20Sopenharmony_ci /* IMA_MEASURE is set from reader side */ 1278c2ecf20Sopenharmony_ci if (iint && test_bit(IMA_MUST_MEASURE, 1288c2ecf20Sopenharmony_ci &iint->atomic_flags)) 1298c2ecf20Sopenharmony_ci send_tomtou = true; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci } else { 1328c2ecf20Sopenharmony_ci if (must_measure) 1338c2ecf20Sopenharmony_ci set_bit(IMA_MUST_MEASURE, &iint->atomic_flags); 1348c2ecf20Sopenharmony_ci if (inode_is_open_for_write(inode) && must_measure) 1358c2ecf20Sopenharmony_ci send_writers = true; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (!send_tomtou && !send_writers) 1398c2ecf20Sopenharmony_ci return; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci *pathname = ima_d_path(&file->f_path, pathbuf, filename); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (send_tomtou) 1448c2ecf20Sopenharmony_ci ima_add_violation(file, *pathname, iint, 1458c2ecf20Sopenharmony_ci "invalid_pcr", "ToMToU"); 1468c2ecf20Sopenharmony_ci if (send_writers) 1478c2ecf20Sopenharmony_ci ima_add_violation(file, *pathname, iint, 1488c2ecf20Sopenharmony_ci "invalid_pcr", "open_writers"); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic void ima_check_last_writer(struct integrity_iint_cache *iint, 1528c2ecf20Sopenharmony_ci struct inode *inode, struct file *file) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci fmode_t mode = file->f_mode; 1558c2ecf20Sopenharmony_ci bool update; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (!(mode & FMODE_WRITE)) 1588c2ecf20Sopenharmony_ci return; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci mutex_lock(&iint->mutex); 1618c2ecf20Sopenharmony_ci if (atomic_read(&inode->i_writecount) == 1) { 1628c2ecf20Sopenharmony_ci update = test_and_clear_bit(IMA_UPDATE_XATTR, 1638c2ecf20Sopenharmony_ci &iint->atomic_flags); 1648c2ecf20Sopenharmony_ci if (!IS_I_VERSION(inode) || 1658c2ecf20Sopenharmony_ci !inode_eq_iversion(inode, iint->version) || 1668c2ecf20Sopenharmony_ci (iint->flags & IMA_NEW_FILE)) { 1678c2ecf20Sopenharmony_ci iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE); 1688c2ecf20Sopenharmony_ci iint->measured_pcrs = 0; 1698c2ecf20Sopenharmony_ci if (update) 1708c2ecf20Sopenharmony_ci ima_update_xattr(iint, file); 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci mutex_unlock(&iint->mutex); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci/** 1778c2ecf20Sopenharmony_ci * ima_file_free - called on __fput() 1788c2ecf20Sopenharmony_ci * @file: pointer to file structure being freed 1798c2ecf20Sopenharmony_ci * 1808c2ecf20Sopenharmony_ci * Flag files that changed, based on i_version 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_civoid ima_file_free(struct file *file) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct inode *inode = file_inode(file); 1858c2ecf20Sopenharmony_ci struct integrity_iint_cache *iint; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (!ima_policy_flag || !S_ISREG(inode->i_mode)) 1888c2ecf20Sopenharmony_ci return; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci iint = integrity_iint_find(inode); 1918c2ecf20Sopenharmony_ci if (!iint) 1928c2ecf20Sopenharmony_ci return; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci ima_check_last_writer(iint, inode, file); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int process_measurement(struct file *file, const struct cred *cred, 1988c2ecf20Sopenharmony_ci u32 secid, char *buf, loff_t size, int mask, 1998c2ecf20Sopenharmony_ci enum ima_hooks func) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct inode *backing_inode, *inode = file_inode(file); 2028c2ecf20Sopenharmony_ci struct integrity_iint_cache *iint = NULL; 2038c2ecf20Sopenharmony_ci struct ima_template_desc *template_desc = NULL; 2048c2ecf20Sopenharmony_ci char *pathbuf = NULL; 2058c2ecf20Sopenharmony_ci char filename[NAME_MAX]; 2068c2ecf20Sopenharmony_ci const char *pathname = NULL; 2078c2ecf20Sopenharmony_ci int rc = 0, action, must_appraise = 0; 2088c2ecf20Sopenharmony_ci int pcr = CONFIG_IMA_MEASURE_PCR_IDX; 2098c2ecf20Sopenharmony_ci struct evm_ima_xattr_data *xattr_value = NULL; 2108c2ecf20Sopenharmony_ci struct modsig *modsig = NULL; 2118c2ecf20Sopenharmony_ci int xattr_len = 0; 2128c2ecf20Sopenharmony_ci bool violation_check; 2138c2ecf20Sopenharmony_ci enum hash_algo hash_algo; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (!ima_policy_flag || !S_ISREG(inode->i_mode)) 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* Return an IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT action 2198c2ecf20Sopenharmony_ci * bitmask based on the appraise/audit/measurement policy. 2208c2ecf20Sopenharmony_ci * Included is the appraise submask. 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_ci action = ima_get_action(inode, cred, secid, mask, func, &pcr, 2238c2ecf20Sopenharmony_ci &template_desc, NULL); 2248c2ecf20Sopenharmony_ci violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) && 2258c2ecf20Sopenharmony_ci (ima_policy_flag & IMA_MEASURE)); 2268c2ecf20Sopenharmony_ci if (!action && !violation_check) 2278c2ecf20Sopenharmony_ci return 0; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci must_appraise = action & IMA_APPRAISE; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* Is the appraise rule hook specific? */ 2328c2ecf20Sopenharmony_ci if (action & IMA_FILE_APPRAISE) 2338c2ecf20Sopenharmony_ci func = FILE_CHECK; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci inode_lock(inode); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (action) { 2388c2ecf20Sopenharmony_ci iint = integrity_inode_get(inode); 2398c2ecf20Sopenharmony_ci if (!iint) 2408c2ecf20Sopenharmony_ci rc = -ENOMEM; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (!rc && violation_check) 2448c2ecf20Sopenharmony_ci ima_rdwr_violation_check(file, iint, action & IMA_MEASURE, 2458c2ecf20Sopenharmony_ci &pathbuf, &pathname, filename); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci inode_unlock(inode); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (rc) 2508c2ecf20Sopenharmony_ci goto out; 2518c2ecf20Sopenharmony_ci if (!action) 2528c2ecf20Sopenharmony_ci goto out; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci mutex_lock(&iint->mutex); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (test_and_clear_bit(IMA_CHANGE_ATTR, &iint->atomic_flags)) 2578c2ecf20Sopenharmony_ci /* reset appraisal flags if ima_inode_post_setattr was called */ 2588c2ecf20Sopenharmony_ci iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED | 2598c2ecf20Sopenharmony_ci IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK | 2608c2ecf20Sopenharmony_ci IMA_ACTION_FLAGS); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* 2638c2ecf20Sopenharmony_ci * Re-evaulate the file if either the xattr has changed or the 2648c2ecf20Sopenharmony_ci * kernel has no way of detecting file change on the filesystem. 2658c2ecf20Sopenharmony_ci * (Limited to privileged mounted filesystems.) 2668c2ecf20Sopenharmony_ci */ 2678c2ecf20Sopenharmony_ci if (test_and_clear_bit(IMA_CHANGE_XATTR, &iint->atomic_flags) || 2688c2ecf20Sopenharmony_ci ((inode->i_sb->s_iflags & SB_I_IMA_UNVERIFIABLE_SIGNATURE) && 2698c2ecf20Sopenharmony_ci !(inode->i_sb->s_iflags & SB_I_UNTRUSTED_MOUNTER) && 2708c2ecf20Sopenharmony_ci !(action & IMA_FAIL_UNVERIFIABLE_SIGS))) { 2718c2ecf20Sopenharmony_ci iint->flags &= ~IMA_DONE_MASK; 2728c2ecf20Sopenharmony_ci iint->measured_pcrs = 0; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* Detect and re-evaluate changes made to the backing file. */ 2768c2ecf20Sopenharmony_ci backing_inode = d_real_inode(file_dentry(file)); 2778c2ecf20Sopenharmony_ci if (backing_inode != inode && 2788c2ecf20Sopenharmony_ci (action & IMA_DO_MASK) && (iint->flags & IMA_DONE_MASK)) { 2798c2ecf20Sopenharmony_ci if (!IS_I_VERSION(backing_inode) || 2808c2ecf20Sopenharmony_ci backing_inode->i_sb->s_dev != iint->real_dev || 2818c2ecf20Sopenharmony_ci backing_inode->i_ino != iint->real_ino || 2828c2ecf20Sopenharmony_ci !inode_eq_iversion(backing_inode, iint->version)) { 2838c2ecf20Sopenharmony_ci iint->flags &= ~IMA_DONE_MASK; 2848c2ecf20Sopenharmony_ci iint->measured_pcrs = 0; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* Determine if already appraised/measured based on bitmask 2898c2ecf20Sopenharmony_ci * (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED, 2908c2ecf20Sopenharmony_ci * IMA_AUDIT, IMA_AUDITED) 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_ci iint->flags |= action; 2938c2ecf20Sopenharmony_ci action &= IMA_DO_MASK; 2948c2ecf20Sopenharmony_ci action &= ~((iint->flags & (IMA_DONE_MASK ^ IMA_MEASURED)) >> 1); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* If target pcr is already measured, unset IMA_MEASURE action */ 2978c2ecf20Sopenharmony_ci if ((action & IMA_MEASURE) && (iint->measured_pcrs & (0x1 << pcr))) 2988c2ecf20Sopenharmony_ci action ^= IMA_MEASURE; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* HASH sets the digital signature and update flags, nothing else */ 3018c2ecf20Sopenharmony_ci if ((action & IMA_HASH) && 3028c2ecf20Sopenharmony_ci !(test_bit(IMA_DIGSIG, &iint->atomic_flags))) { 3038c2ecf20Sopenharmony_ci xattr_len = ima_read_xattr(file_dentry(file), &xattr_value); 3048c2ecf20Sopenharmony_ci if ((xattr_value && xattr_len > 2) && 3058c2ecf20Sopenharmony_ci (xattr_value->type == EVM_IMA_XATTR_DIGSIG)) 3068c2ecf20Sopenharmony_ci set_bit(IMA_DIGSIG, &iint->atomic_flags); 3078c2ecf20Sopenharmony_ci iint->flags |= IMA_HASHED; 3088c2ecf20Sopenharmony_ci action ^= IMA_HASH; 3098c2ecf20Sopenharmony_ci set_bit(IMA_UPDATE_XATTR, &iint->atomic_flags); 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* Nothing to do, just return existing appraised status */ 3138c2ecf20Sopenharmony_ci if (!action) { 3148c2ecf20Sopenharmony_ci if (must_appraise) { 3158c2ecf20Sopenharmony_ci rc = mmap_violation_check(func, file, &pathbuf, 3168c2ecf20Sopenharmony_ci &pathname, filename); 3178c2ecf20Sopenharmony_ci if (!rc) 3188c2ecf20Sopenharmony_ci rc = ima_get_cache_status(iint, func); 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci goto out_locked; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if ((action & IMA_APPRAISE_SUBMASK) || 3248c2ecf20Sopenharmony_ci strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0) { 3258c2ecf20Sopenharmony_ci /* read 'security.ima' */ 3268c2ecf20Sopenharmony_ci xattr_len = ima_read_xattr(file_dentry(file), &xattr_value); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci /* 3298c2ecf20Sopenharmony_ci * Read the appended modsig if allowed by the policy, and allow 3308c2ecf20Sopenharmony_ci * an additional measurement list entry, if needed, based on the 3318c2ecf20Sopenharmony_ci * template format and whether the file was already measured. 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_ci if (iint->flags & IMA_MODSIG_ALLOWED) { 3348c2ecf20Sopenharmony_ci rc = ima_read_modsig(func, buf, size, &modsig); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (!rc && ima_template_has_modsig(template_desc) && 3378c2ecf20Sopenharmony_ci iint->flags & IMA_MEASURED) 3388c2ecf20Sopenharmony_ci action |= IMA_MEASURE; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci hash_algo = ima_get_hash_algo(xattr_value, xattr_len); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci rc = ima_collect_measurement(iint, file, buf, size, hash_algo, modsig); 3458c2ecf20Sopenharmony_ci if (rc != 0 && rc != -EBADF && rc != -EINVAL) 3468c2ecf20Sopenharmony_ci goto out_locked; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (!pathbuf) /* ima_rdwr_violation possibly pre-fetched */ 3498c2ecf20Sopenharmony_ci pathname = ima_d_path(&file->f_path, &pathbuf, filename); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (action & IMA_MEASURE) 3528c2ecf20Sopenharmony_ci ima_store_measurement(iint, file, pathname, 3538c2ecf20Sopenharmony_ci xattr_value, xattr_len, modsig, pcr, 3548c2ecf20Sopenharmony_ci template_desc); 3558c2ecf20Sopenharmony_ci if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) { 3568c2ecf20Sopenharmony_ci rc = ima_check_blacklist(iint, modsig, pcr); 3578c2ecf20Sopenharmony_ci if (rc != -EPERM) { 3588c2ecf20Sopenharmony_ci inode_lock(inode); 3598c2ecf20Sopenharmony_ci rc = ima_appraise_measurement(func, iint, file, 3608c2ecf20Sopenharmony_ci pathname, xattr_value, 3618c2ecf20Sopenharmony_ci xattr_len, modsig); 3628c2ecf20Sopenharmony_ci inode_unlock(inode); 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci if (!rc) 3658c2ecf20Sopenharmony_ci rc = mmap_violation_check(func, file, &pathbuf, 3668c2ecf20Sopenharmony_ci &pathname, filename); 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci if (action & IMA_AUDIT) 3698c2ecf20Sopenharmony_ci ima_audit_measurement(iint, pathname); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if ((file->f_flags & O_DIRECT) && (iint->flags & IMA_PERMIT_DIRECTIO)) 3728c2ecf20Sopenharmony_ci rc = 0; 3738c2ecf20Sopenharmony_ciout_locked: 3748c2ecf20Sopenharmony_ci if ((mask & MAY_WRITE) && test_bit(IMA_DIGSIG, &iint->atomic_flags) && 3758c2ecf20Sopenharmony_ci !(iint->flags & IMA_NEW_FILE)) 3768c2ecf20Sopenharmony_ci rc = -EACCES; 3778c2ecf20Sopenharmony_ci mutex_unlock(&iint->mutex); 3788c2ecf20Sopenharmony_ci kfree(xattr_value); 3798c2ecf20Sopenharmony_ci ima_free_modsig(modsig); 3808c2ecf20Sopenharmony_ciout: 3818c2ecf20Sopenharmony_ci if (pathbuf) 3828c2ecf20Sopenharmony_ci __putname(pathbuf); 3838c2ecf20Sopenharmony_ci if (must_appraise) { 3848c2ecf20Sopenharmony_ci if (rc && (ima_appraise & IMA_APPRAISE_ENFORCE)) 3858c2ecf20Sopenharmony_ci return -EACCES; 3868c2ecf20Sopenharmony_ci if (file->f_mode & FMODE_WRITE) 3878c2ecf20Sopenharmony_ci set_bit(IMA_UPDATE_XATTR, &iint->atomic_flags); 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci return 0; 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci/** 3938c2ecf20Sopenharmony_ci * ima_file_mmap - based on policy, collect/store measurement. 3948c2ecf20Sopenharmony_ci * @file: pointer to the file to be measured (May be NULL) 3958c2ecf20Sopenharmony_ci * @reqprot: protection requested by the application 3968c2ecf20Sopenharmony_ci * @prot: protection that will be applied by the kernel 3978c2ecf20Sopenharmony_ci * @flags: operational flags 3988c2ecf20Sopenharmony_ci * 3998c2ecf20Sopenharmony_ci * Measure files being mmapped executable based on the ima_must_measure() 4008c2ecf20Sopenharmony_ci * policy decision. 4018c2ecf20Sopenharmony_ci * 4028c2ecf20Sopenharmony_ci * On success return 0. On integrity appraisal error, assuming the file 4038c2ecf20Sopenharmony_ci * is in policy and IMA-appraisal is in enforcing mode, return -EACCES. 4048c2ecf20Sopenharmony_ci */ 4058c2ecf20Sopenharmony_ciint ima_file_mmap(struct file *file, unsigned long reqprot, 4068c2ecf20Sopenharmony_ci unsigned long prot, unsigned long flags) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci u32 secid; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (file && (prot & PROT_EXEC)) { 4118c2ecf20Sopenharmony_ci security_task_getsecid(current, &secid); 4128c2ecf20Sopenharmony_ci return process_measurement(file, current_cred(), secid, NULL, 4138c2ecf20Sopenharmony_ci 0, MAY_EXEC, MMAP_CHECK); 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci return 0; 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci/** 4208c2ecf20Sopenharmony_ci * ima_file_mprotect - based on policy, limit mprotect change 4218c2ecf20Sopenharmony_ci * @prot: contains the protection that will be applied by the kernel. 4228c2ecf20Sopenharmony_ci * 4238c2ecf20Sopenharmony_ci * Files can be mmap'ed read/write and later changed to execute to circumvent 4248c2ecf20Sopenharmony_ci * IMA's mmap appraisal policy rules. Due to locking issues (mmap semaphore 4258c2ecf20Sopenharmony_ci * would be taken before i_mutex), files can not be measured or appraised at 4268c2ecf20Sopenharmony_ci * this point. Eliminate this integrity gap by denying the mprotect 4278c2ecf20Sopenharmony_ci * PROT_EXECUTE change, if an mmap appraise policy rule exists. 4288c2ecf20Sopenharmony_ci * 4298c2ecf20Sopenharmony_ci * On mprotect change success, return 0. On failure, return -EACESS. 4308c2ecf20Sopenharmony_ci */ 4318c2ecf20Sopenharmony_ciint ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct ima_template_desc *template; 4348c2ecf20Sopenharmony_ci struct file *file = vma->vm_file; 4358c2ecf20Sopenharmony_ci char filename[NAME_MAX]; 4368c2ecf20Sopenharmony_ci char *pathbuf = NULL; 4378c2ecf20Sopenharmony_ci const char *pathname = NULL; 4388c2ecf20Sopenharmony_ci struct inode *inode; 4398c2ecf20Sopenharmony_ci int result = 0; 4408c2ecf20Sopenharmony_ci int action; 4418c2ecf20Sopenharmony_ci u32 secid; 4428c2ecf20Sopenharmony_ci int pcr; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* Is mprotect making an mmap'ed file executable? */ 4458c2ecf20Sopenharmony_ci if (!(ima_policy_flag & IMA_APPRAISE) || !vma->vm_file || 4468c2ecf20Sopenharmony_ci !(prot & PROT_EXEC) || (vma->vm_flags & VM_EXEC)) 4478c2ecf20Sopenharmony_ci return 0; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci security_task_getsecid(current, &secid); 4508c2ecf20Sopenharmony_ci inode = file_inode(vma->vm_file); 4518c2ecf20Sopenharmony_ci action = ima_get_action(inode, current_cred(), secid, MAY_EXEC, 4528c2ecf20Sopenharmony_ci MMAP_CHECK, &pcr, &template, 0); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci /* Is the mmap'ed file in policy? */ 4558c2ecf20Sopenharmony_ci if (!(action & (IMA_MEASURE | IMA_APPRAISE_SUBMASK))) 4568c2ecf20Sopenharmony_ci return 0; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (action & IMA_APPRAISE_SUBMASK) 4598c2ecf20Sopenharmony_ci result = -EPERM; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci file = vma->vm_file; 4628c2ecf20Sopenharmony_ci pathname = ima_d_path(&file->f_path, &pathbuf, filename); 4638c2ecf20Sopenharmony_ci integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, pathname, 4648c2ecf20Sopenharmony_ci "collect_data", "failed-mprotect", result, 0); 4658c2ecf20Sopenharmony_ci if (pathbuf) 4668c2ecf20Sopenharmony_ci __putname(pathbuf); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci return result; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci/** 4728c2ecf20Sopenharmony_ci * ima_bprm_check - based on policy, collect/store measurement. 4738c2ecf20Sopenharmony_ci * @bprm: contains the linux_binprm structure 4748c2ecf20Sopenharmony_ci * 4758c2ecf20Sopenharmony_ci * The OS protects against an executable file, already open for write, 4768c2ecf20Sopenharmony_ci * from being executed in deny_write_access() and an executable file, 4778c2ecf20Sopenharmony_ci * already open for execute, from being modified in get_write_access(). 4788c2ecf20Sopenharmony_ci * So we can be certain that what we verify and measure here is actually 4798c2ecf20Sopenharmony_ci * what is being executed. 4808c2ecf20Sopenharmony_ci * 4818c2ecf20Sopenharmony_ci * On success return 0. On integrity appraisal error, assuming the file 4828c2ecf20Sopenharmony_ci * is in policy and IMA-appraisal is in enforcing mode, return -EACCES. 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_ciint ima_bprm_check(struct linux_binprm *bprm) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci int ret; 4878c2ecf20Sopenharmony_ci u32 secid; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci security_task_getsecid(current, &secid); 4908c2ecf20Sopenharmony_ci ret = process_measurement(bprm->file, current_cred(), secid, NULL, 0, 4918c2ecf20Sopenharmony_ci MAY_EXEC, BPRM_CHECK); 4928c2ecf20Sopenharmony_ci if (ret) 4938c2ecf20Sopenharmony_ci return ret; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci security_cred_getsecid(bprm->cred, &secid); 4968c2ecf20Sopenharmony_ci return process_measurement(bprm->file, bprm->cred, secid, NULL, 0, 4978c2ecf20Sopenharmony_ci MAY_EXEC, CREDS_CHECK); 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci/** 5018c2ecf20Sopenharmony_ci * ima_path_check - based on policy, collect/store measurement. 5028c2ecf20Sopenharmony_ci * @file: pointer to the file to be measured 5038c2ecf20Sopenharmony_ci * @mask: contains MAY_READ, MAY_WRITE, MAY_EXEC or MAY_APPEND 5048c2ecf20Sopenharmony_ci * 5058c2ecf20Sopenharmony_ci * Measure files based on the ima_must_measure() policy decision. 5068c2ecf20Sopenharmony_ci * 5078c2ecf20Sopenharmony_ci * On success return 0. On integrity appraisal error, assuming the file 5088c2ecf20Sopenharmony_ci * is in policy and IMA-appraisal is in enforcing mode, return -EACCES. 5098c2ecf20Sopenharmony_ci */ 5108c2ecf20Sopenharmony_ciint ima_file_check(struct file *file, int mask) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci u32 secid; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci security_task_getsecid(current, &secid); 5158c2ecf20Sopenharmony_ci return process_measurement(file, current_cred(), secid, NULL, 0, 5168c2ecf20Sopenharmony_ci mask & (MAY_READ | MAY_WRITE | MAY_EXEC | 5178c2ecf20Sopenharmony_ci MAY_APPEND), FILE_CHECK); 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ima_file_check); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci/** 5228c2ecf20Sopenharmony_ci * ima_file_hash - return the stored measurement if a file has been hashed and 5238c2ecf20Sopenharmony_ci * is in the iint cache. 5248c2ecf20Sopenharmony_ci * @file: pointer to the file 5258c2ecf20Sopenharmony_ci * @buf: buffer in which to store the hash 5268c2ecf20Sopenharmony_ci * @buf_size: length of the buffer 5278c2ecf20Sopenharmony_ci * 5288c2ecf20Sopenharmony_ci * On success, return the hash algorithm (as defined in the enum hash_algo). 5298c2ecf20Sopenharmony_ci * If buf is not NULL, this function also outputs the hash into buf. 5308c2ecf20Sopenharmony_ci * If the hash is larger than buf_size, then only buf_size bytes will be copied. 5318c2ecf20Sopenharmony_ci * It generally just makes sense to pass a buffer capable of holding the largest 5328c2ecf20Sopenharmony_ci * possible hash: IMA_MAX_DIGEST_SIZE. 5338c2ecf20Sopenharmony_ci * The file hash returned is based on the entire file, including the appended 5348c2ecf20Sopenharmony_ci * signature. 5358c2ecf20Sopenharmony_ci * 5368c2ecf20Sopenharmony_ci * If IMA is disabled or if no measurement is available, return -EOPNOTSUPP. 5378c2ecf20Sopenharmony_ci * If the parameters are incorrect, return -EINVAL. 5388c2ecf20Sopenharmony_ci */ 5398c2ecf20Sopenharmony_ciint ima_file_hash(struct file *file, char *buf, size_t buf_size) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci struct inode *inode; 5428c2ecf20Sopenharmony_ci struct integrity_iint_cache *iint; 5438c2ecf20Sopenharmony_ci int hash_algo; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci if (!file) 5468c2ecf20Sopenharmony_ci return -EINVAL; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci if (!ima_policy_flag) 5498c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci inode = file_inode(file); 5528c2ecf20Sopenharmony_ci iint = integrity_iint_find(inode); 5538c2ecf20Sopenharmony_ci if (!iint) 5548c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci mutex_lock(&iint->mutex); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci /* 5598c2ecf20Sopenharmony_ci * ima_file_hash can be called when ima_collect_measurement has still 5608c2ecf20Sopenharmony_ci * not been called, we might not always have a hash. 5618c2ecf20Sopenharmony_ci */ 5628c2ecf20Sopenharmony_ci if (!iint->ima_hash) { 5638c2ecf20Sopenharmony_ci mutex_unlock(&iint->mutex); 5648c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci if (buf) { 5688c2ecf20Sopenharmony_ci size_t copied_size; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci copied_size = min_t(size_t, iint->ima_hash->length, buf_size); 5718c2ecf20Sopenharmony_ci memcpy(buf, iint->ima_hash->digest, copied_size); 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci hash_algo = iint->ima_hash->algo; 5748c2ecf20Sopenharmony_ci mutex_unlock(&iint->mutex); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci return hash_algo; 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ima_file_hash); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci/** 5818c2ecf20Sopenharmony_ci * ima_post_create_tmpfile - mark newly created tmpfile as new 5828c2ecf20Sopenharmony_ci * @file : newly created tmpfile 5838c2ecf20Sopenharmony_ci * 5848c2ecf20Sopenharmony_ci * No measuring, appraising or auditing of newly created tmpfiles is needed. 5858c2ecf20Sopenharmony_ci * Skip calling process_measurement(), but indicate which newly, created 5868c2ecf20Sopenharmony_ci * tmpfiles are in policy. 5878c2ecf20Sopenharmony_ci */ 5888c2ecf20Sopenharmony_civoid ima_post_create_tmpfile(struct inode *inode) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci struct integrity_iint_cache *iint; 5918c2ecf20Sopenharmony_ci int must_appraise; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci must_appraise = ima_must_appraise(inode, MAY_ACCESS, FILE_CHECK); 5948c2ecf20Sopenharmony_ci if (!must_appraise) 5958c2ecf20Sopenharmony_ci return; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* Nothing to do if we can't allocate memory */ 5988c2ecf20Sopenharmony_ci iint = integrity_inode_get(inode); 5998c2ecf20Sopenharmony_ci if (!iint) 6008c2ecf20Sopenharmony_ci return; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci /* needed for writing the security xattrs */ 6038c2ecf20Sopenharmony_ci set_bit(IMA_UPDATE_XATTR, &iint->atomic_flags); 6048c2ecf20Sopenharmony_ci iint->ima_file_status = INTEGRITY_PASS; 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci/** 6088c2ecf20Sopenharmony_ci * ima_post_path_mknod - mark as a new inode 6098c2ecf20Sopenharmony_ci * @dentry: newly created dentry 6108c2ecf20Sopenharmony_ci * 6118c2ecf20Sopenharmony_ci * Mark files created via the mknodat syscall as new, so that the 6128c2ecf20Sopenharmony_ci * file data can be written later. 6138c2ecf20Sopenharmony_ci */ 6148c2ecf20Sopenharmony_civoid ima_post_path_mknod(struct dentry *dentry) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci struct integrity_iint_cache *iint; 6178c2ecf20Sopenharmony_ci struct inode *inode = dentry->d_inode; 6188c2ecf20Sopenharmony_ci int must_appraise; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci must_appraise = ima_must_appraise(inode, MAY_ACCESS, FILE_CHECK); 6218c2ecf20Sopenharmony_ci if (!must_appraise) 6228c2ecf20Sopenharmony_ci return; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci /* Nothing to do if we can't allocate memory */ 6258c2ecf20Sopenharmony_ci iint = integrity_inode_get(inode); 6268c2ecf20Sopenharmony_ci if (!iint) 6278c2ecf20Sopenharmony_ci return; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* needed for re-opening empty files */ 6308c2ecf20Sopenharmony_ci iint->flags |= IMA_NEW_FILE; 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci/** 6348c2ecf20Sopenharmony_ci * ima_read_file - pre-measure/appraise hook decision based on policy 6358c2ecf20Sopenharmony_ci * @file: pointer to the file to be measured/appraised/audit 6368c2ecf20Sopenharmony_ci * @read_id: caller identifier 6378c2ecf20Sopenharmony_ci * @contents: whether a subsequent call will be made to ima_post_read_file() 6388c2ecf20Sopenharmony_ci * 6398c2ecf20Sopenharmony_ci * Permit reading a file based on policy. The policy rules are written 6408c2ecf20Sopenharmony_ci * in terms of the policy identifier. Appraising the integrity of 6418c2ecf20Sopenharmony_ci * a file requires a file descriptor. 6428c2ecf20Sopenharmony_ci * 6438c2ecf20Sopenharmony_ci * For permission return 0, otherwise return -EACCES. 6448c2ecf20Sopenharmony_ci */ 6458c2ecf20Sopenharmony_ciint ima_read_file(struct file *file, enum kernel_read_file_id read_id, 6468c2ecf20Sopenharmony_ci bool contents) 6478c2ecf20Sopenharmony_ci{ 6488c2ecf20Sopenharmony_ci enum ima_hooks func; 6498c2ecf20Sopenharmony_ci u32 secid; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci /* 6528c2ecf20Sopenharmony_ci * Do devices using pre-allocated memory run the risk of the 6538c2ecf20Sopenharmony_ci * firmware being accessible to the device prior to the completion 6548c2ecf20Sopenharmony_ci * of IMA's signature verification any more than when using two 6558c2ecf20Sopenharmony_ci * buffers? It may be desirable to include the buffer address 6568c2ecf20Sopenharmony_ci * in this API and walk all the dma_map_single() mappings to check. 6578c2ecf20Sopenharmony_ci */ 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci /* 6608c2ecf20Sopenharmony_ci * There will be a call made to ima_post_read_file() with 6618c2ecf20Sopenharmony_ci * a filled buffer, so we don't need to perform an extra 6628c2ecf20Sopenharmony_ci * read early here. 6638c2ecf20Sopenharmony_ci */ 6648c2ecf20Sopenharmony_ci if (contents) 6658c2ecf20Sopenharmony_ci return 0; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci /* Read entire file for all partial reads. */ 6688c2ecf20Sopenharmony_ci func = read_idmap[read_id] ?: FILE_CHECK; 6698c2ecf20Sopenharmony_ci security_task_getsecid(current, &secid); 6708c2ecf20Sopenharmony_ci return process_measurement(file, current_cred(), secid, NULL, 6718c2ecf20Sopenharmony_ci 0, MAY_READ, func); 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ciconst int read_idmap[READING_MAX_ID] = { 6758c2ecf20Sopenharmony_ci [READING_FIRMWARE] = FIRMWARE_CHECK, 6768c2ecf20Sopenharmony_ci [READING_MODULE] = MODULE_CHECK, 6778c2ecf20Sopenharmony_ci [READING_KEXEC_IMAGE] = KEXEC_KERNEL_CHECK, 6788c2ecf20Sopenharmony_ci [READING_KEXEC_INITRAMFS] = KEXEC_INITRAMFS_CHECK, 6798c2ecf20Sopenharmony_ci [READING_POLICY] = POLICY_CHECK 6808c2ecf20Sopenharmony_ci}; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci/** 6838c2ecf20Sopenharmony_ci * ima_post_read_file - in memory collect/appraise/audit measurement 6848c2ecf20Sopenharmony_ci * @file: pointer to the file to be measured/appraised/audit 6858c2ecf20Sopenharmony_ci * @buf: pointer to in memory file contents 6868c2ecf20Sopenharmony_ci * @size: size of in memory file contents 6878c2ecf20Sopenharmony_ci * @read_id: caller identifier 6888c2ecf20Sopenharmony_ci * 6898c2ecf20Sopenharmony_ci * Measure/appraise/audit in memory file based on policy. Policy rules 6908c2ecf20Sopenharmony_ci * are written in terms of a policy identifier. 6918c2ecf20Sopenharmony_ci * 6928c2ecf20Sopenharmony_ci * On success return 0. On integrity appraisal error, assuming the file 6938c2ecf20Sopenharmony_ci * is in policy and IMA-appraisal is in enforcing mode, return -EACCES. 6948c2ecf20Sopenharmony_ci */ 6958c2ecf20Sopenharmony_ciint ima_post_read_file(struct file *file, void *buf, loff_t size, 6968c2ecf20Sopenharmony_ci enum kernel_read_file_id read_id) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci enum ima_hooks func; 6998c2ecf20Sopenharmony_ci u32 secid; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci /* permit signed certs */ 7028c2ecf20Sopenharmony_ci if (!file && read_id == READING_X509_CERTIFICATE) 7038c2ecf20Sopenharmony_ci return 0; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci if (!file || !buf || size == 0) { /* should never happen */ 7068c2ecf20Sopenharmony_ci if (ima_appraise & IMA_APPRAISE_ENFORCE) 7078c2ecf20Sopenharmony_ci return -EACCES; 7088c2ecf20Sopenharmony_ci return 0; 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci func = read_idmap[read_id] ?: FILE_CHECK; 7128c2ecf20Sopenharmony_ci security_task_getsecid(current, &secid); 7138c2ecf20Sopenharmony_ci return process_measurement(file, current_cred(), secid, buf, size, 7148c2ecf20Sopenharmony_ci MAY_READ, func); 7158c2ecf20Sopenharmony_ci} 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci/** 7188c2ecf20Sopenharmony_ci * ima_load_data - appraise decision based on policy 7198c2ecf20Sopenharmony_ci * @id: kernel load data caller identifier 7208c2ecf20Sopenharmony_ci * @contents: whether the full contents will be available in a later 7218c2ecf20Sopenharmony_ci * call to ima_post_load_data(). 7228c2ecf20Sopenharmony_ci * 7238c2ecf20Sopenharmony_ci * Callers of this LSM hook can not measure, appraise, or audit the 7248c2ecf20Sopenharmony_ci * data provided by userspace. Enforce policy rules requring a file 7258c2ecf20Sopenharmony_ci * signature (eg. kexec'ed kernel image). 7268c2ecf20Sopenharmony_ci * 7278c2ecf20Sopenharmony_ci * For permission return 0, otherwise return -EACCES. 7288c2ecf20Sopenharmony_ci */ 7298c2ecf20Sopenharmony_ciint ima_load_data(enum kernel_load_data_id id, bool contents) 7308c2ecf20Sopenharmony_ci{ 7318c2ecf20Sopenharmony_ci bool ima_enforce, sig_enforce; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci ima_enforce = 7348c2ecf20Sopenharmony_ci (ima_appraise & IMA_APPRAISE_ENFORCE) == IMA_APPRAISE_ENFORCE; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci switch (id) { 7378c2ecf20Sopenharmony_ci case LOADING_KEXEC_IMAGE: 7388c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_KEXEC_SIG) 7398c2ecf20Sopenharmony_ci && arch_ima_get_secureboot()) { 7408c2ecf20Sopenharmony_ci pr_err("impossible to appraise a kernel image without a file descriptor; try using kexec_file_load syscall.\n"); 7418c2ecf20Sopenharmony_ci return -EACCES; 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (ima_enforce && (ima_appraise & IMA_APPRAISE_KEXEC)) { 7458c2ecf20Sopenharmony_ci pr_err("impossible to appraise a kernel image without a file descriptor; try using kexec_file_load syscall.\n"); 7468c2ecf20Sopenharmony_ci return -EACCES; /* INTEGRITY_UNKNOWN */ 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci break; 7498c2ecf20Sopenharmony_ci case LOADING_FIRMWARE: 7508c2ecf20Sopenharmony_ci if (ima_enforce && (ima_appraise & IMA_APPRAISE_FIRMWARE) && !contents) { 7518c2ecf20Sopenharmony_ci pr_err("Prevent firmware sysfs fallback loading.\n"); 7528c2ecf20Sopenharmony_ci return -EACCES; /* INTEGRITY_UNKNOWN */ 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci break; 7558c2ecf20Sopenharmony_ci case LOADING_MODULE: 7568c2ecf20Sopenharmony_ci sig_enforce = is_module_sig_enforced(); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci if (ima_enforce && (!sig_enforce 7598c2ecf20Sopenharmony_ci && (ima_appraise & IMA_APPRAISE_MODULES))) { 7608c2ecf20Sopenharmony_ci pr_err("impossible to appraise a module without a file descriptor. sig_enforce kernel parameter might help\n"); 7618c2ecf20Sopenharmony_ci return -EACCES; /* INTEGRITY_UNKNOWN */ 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci break; 7648c2ecf20Sopenharmony_ci default: 7658c2ecf20Sopenharmony_ci break; 7668c2ecf20Sopenharmony_ci } 7678c2ecf20Sopenharmony_ci return 0; 7688c2ecf20Sopenharmony_ci} 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci/** 7718c2ecf20Sopenharmony_ci * ima_post_load_data - appraise decision based on policy 7728c2ecf20Sopenharmony_ci * @buf: pointer to in memory file contents 7738c2ecf20Sopenharmony_ci * @size: size of in memory file contents 7748c2ecf20Sopenharmony_ci * @id: kernel load data caller identifier 7758c2ecf20Sopenharmony_ci * @description: @id-specific description of contents 7768c2ecf20Sopenharmony_ci * 7778c2ecf20Sopenharmony_ci * Measure/appraise/audit in memory buffer based on policy. Policy rules 7788c2ecf20Sopenharmony_ci * are written in terms of a policy identifier. 7798c2ecf20Sopenharmony_ci * 7808c2ecf20Sopenharmony_ci * On success return 0. On integrity appraisal error, assuming the file 7818c2ecf20Sopenharmony_ci * is in policy and IMA-appraisal is in enforcing mode, return -EACCES. 7828c2ecf20Sopenharmony_ci */ 7838c2ecf20Sopenharmony_ciint ima_post_load_data(char *buf, loff_t size, 7848c2ecf20Sopenharmony_ci enum kernel_load_data_id load_id, 7858c2ecf20Sopenharmony_ci char *description) 7868c2ecf20Sopenharmony_ci{ 7878c2ecf20Sopenharmony_ci if (load_id == LOADING_FIRMWARE) { 7888c2ecf20Sopenharmony_ci if ((ima_appraise & IMA_APPRAISE_FIRMWARE) && 7898c2ecf20Sopenharmony_ci (ima_appraise & IMA_APPRAISE_ENFORCE)) { 7908c2ecf20Sopenharmony_ci pr_err("Prevent firmware loading_store.\n"); 7918c2ecf20Sopenharmony_ci return -EACCES; /* INTEGRITY_UNKNOWN */ 7928c2ecf20Sopenharmony_ci } 7938c2ecf20Sopenharmony_ci return 0; 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci return 0; 7978c2ecf20Sopenharmony_ci} 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci/* 8008c2ecf20Sopenharmony_ci * process_buffer_measurement - Measure the buffer to ima log. 8018c2ecf20Sopenharmony_ci * @inode: inode associated with the object being measured (NULL for KEY_CHECK) 8028c2ecf20Sopenharmony_ci * @buf: pointer to the buffer that needs to be added to the log. 8038c2ecf20Sopenharmony_ci * @size: size of buffer(in bytes). 8048c2ecf20Sopenharmony_ci * @eventname: event name to be used for the buffer entry. 8058c2ecf20Sopenharmony_ci * @func: IMA hook 8068c2ecf20Sopenharmony_ci * @pcr: pcr to extend the measurement 8078c2ecf20Sopenharmony_ci * @keyring: keyring name to determine the action to be performed 8088c2ecf20Sopenharmony_ci * 8098c2ecf20Sopenharmony_ci * Based on policy, the buffer is measured into the ima log. 8108c2ecf20Sopenharmony_ci */ 8118c2ecf20Sopenharmony_civoid process_buffer_measurement(struct inode *inode, const void *buf, int size, 8128c2ecf20Sopenharmony_ci const char *eventname, enum ima_hooks func, 8138c2ecf20Sopenharmony_ci int pcr, const char *keyring) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci int ret = 0; 8168c2ecf20Sopenharmony_ci const char *audit_cause = "ENOMEM"; 8178c2ecf20Sopenharmony_ci struct ima_template_entry *entry = NULL; 8188c2ecf20Sopenharmony_ci struct integrity_iint_cache iint = {}; 8198c2ecf20Sopenharmony_ci struct ima_event_data event_data = {.iint = &iint, 8208c2ecf20Sopenharmony_ci .filename = eventname, 8218c2ecf20Sopenharmony_ci .buf = buf, 8228c2ecf20Sopenharmony_ci .buf_len = size}; 8238c2ecf20Sopenharmony_ci struct ima_template_desc *template = NULL; 8248c2ecf20Sopenharmony_ci struct { 8258c2ecf20Sopenharmony_ci struct ima_digest_data hdr; 8268c2ecf20Sopenharmony_ci char digest[IMA_MAX_DIGEST_SIZE]; 8278c2ecf20Sopenharmony_ci } hash = {}; 8288c2ecf20Sopenharmony_ci int violation = 0; 8298c2ecf20Sopenharmony_ci int action = 0; 8308c2ecf20Sopenharmony_ci u32 secid; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci if (!ima_policy_flag) 8338c2ecf20Sopenharmony_ci return; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci /* 8368c2ecf20Sopenharmony_ci * Both LSM hooks and auxilary based buffer measurements are 8378c2ecf20Sopenharmony_ci * based on policy. To avoid code duplication, differentiate 8388c2ecf20Sopenharmony_ci * between the LSM hooks and auxilary buffer measurements, 8398c2ecf20Sopenharmony_ci * retrieving the policy rule information only for the LSM hook 8408c2ecf20Sopenharmony_ci * buffer measurements. 8418c2ecf20Sopenharmony_ci */ 8428c2ecf20Sopenharmony_ci if (func) { 8438c2ecf20Sopenharmony_ci security_task_getsecid(current, &secid); 8448c2ecf20Sopenharmony_ci action = ima_get_action(inode, current_cred(), secid, 0, func, 8458c2ecf20Sopenharmony_ci &pcr, &template, keyring); 8468c2ecf20Sopenharmony_ci if (!(action & IMA_MEASURE)) 8478c2ecf20Sopenharmony_ci return; 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci if (!pcr) 8518c2ecf20Sopenharmony_ci pcr = CONFIG_IMA_MEASURE_PCR_IDX; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if (!template) { 8548c2ecf20Sopenharmony_ci template = lookup_template_desc("ima-buf"); 8558c2ecf20Sopenharmony_ci ret = template_desc_init_fields(template->fmt, 8568c2ecf20Sopenharmony_ci &(template->fields), 8578c2ecf20Sopenharmony_ci &(template->num_fields)); 8588c2ecf20Sopenharmony_ci if (ret < 0) { 8598c2ecf20Sopenharmony_ci pr_err("template %s init failed, result: %d\n", 8608c2ecf20Sopenharmony_ci (strlen(template->name) ? 8618c2ecf20Sopenharmony_ci template->name : template->fmt), ret); 8628c2ecf20Sopenharmony_ci return; 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci iint.ima_hash = &hash.hdr; 8678c2ecf20Sopenharmony_ci iint.ima_hash->algo = ima_hash_algo; 8688c2ecf20Sopenharmony_ci iint.ima_hash->length = hash_digest_size[ima_hash_algo]; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci ret = ima_calc_buffer_hash(buf, size, iint.ima_hash); 8718c2ecf20Sopenharmony_ci if (ret < 0) { 8728c2ecf20Sopenharmony_ci audit_cause = "hashing_error"; 8738c2ecf20Sopenharmony_ci goto out; 8748c2ecf20Sopenharmony_ci } 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci ret = ima_alloc_init_template(&event_data, &entry, template); 8778c2ecf20Sopenharmony_ci if (ret < 0) { 8788c2ecf20Sopenharmony_ci audit_cause = "alloc_entry"; 8798c2ecf20Sopenharmony_ci goto out; 8808c2ecf20Sopenharmony_ci } 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci ret = ima_store_template(entry, violation, NULL, buf, pcr); 8838c2ecf20Sopenharmony_ci if (ret < 0) { 8848c2ecf20Sopenharmony_ci audit_cause = "store_entry"; 8858c2ecf20Sopenharmony_ci ima_free_template_entry(entry); 8868c2ecf20Sopenharmony_ci } 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ciout: 8898c2ecf20Sopenharmony_ci if (ret < 0) 8908c2ecf20Sopenharmony_ci integrity_audit_message(AUDIT_INTEGRITY_PCR, NULL, eventname, 8918c2ecf20Sopenharmony_ci func_measure_str(func), 8928c2ecf20Sopenharmony_ci audit_cause, ret, 0, ret); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci return; 8958c2ecf20Sopenharmony_ci} 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci/** 8988c2ecf20Sopenharmony_ci * ima_kexec_cmdline - measure kexec cmdline boot args 8998c2ecf20Sopenharmony_ci * @kernel_fd: file descriptor of the kexec kernel being loaded 9008c2ecf20Sopenharmony_ci * @buf: pointer to buffer 9018c2ecf20Sopenharmony_ci * @size: size of buffer 9028c2ecf20Sopenharmony_ci * 9038c2ecf20Sopenharmony_ci * Buffers can only be measured, not appraised. 9048c2ecf20Sopenharmony_ci */ 9058c2ecf20Sopenharmony_civoid ima_kexec_cmdline(int kernel_fd, const void *buf, int size) 9068c2ecf20Sopenharmony_ci{ 9078c2ecf20Sopenharmony_ci struct fd f; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci if (!buf || !size) 9108c2ecf20Sopenharmony_ci return; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci f = fdget(kernel_fd); 9138c2ecf20Sopenharmony_ci if (!f.file) 9148c2ecf20Sopenharmony_ci return; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci process_buffer_measurement(file_inode(f.file), buf, size, 9178c2ecf20Sopenharmony_ci "kexec-cmdline", KEXEC_CMDLINE, 0, NULL); 9188c2ecf20Sopenharmony_ci fdput(f); 9198c2ecf20Sopenharmony_ci} 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_cistatic int __init init_ima(void) 9228c2ecf20Sopenharmony_ci{ 9238c2ecf20Sopenharmony_ci int error; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci ima_init_template_list(); 9268c2ecf20Sopenharmony_ci hash_setup(CONFIG_IMA_DEFAULT_HASH); 9278c2ecf20Sopenharmony_ci error = ima_init(); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci if (error && strcmp(hash_algo_name[ima_hash_algo], 9308c2ecf20Sopenharmony_ci CONFIG_IMA_DEFAULT_HASH) != 0) { 9318c2ecf20Sopenharmony_ci pr_info("Allocating %s failed, going to use default hash algorithm %s\n", 9328c2ecf20Sopenharmony_ci hash_algo_name[ima_hash_algo], CONFIG_IMA_DEFAULT_HASH); 9338c2ecf20Sopenharmony_ci hash_setup_done = 0; 9348c2ecf20Sopenharmony_ci hash_setup(CONFIG_IMA_DEFAULT_HASH); 9358c2ecf20Sopenharmony_ci error = ima_init(); 9368c2ecf20Sopenharmony_ci } 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci if (error) 9398c2ecf20Sopenharmony_ci return error; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci error = register_blocking_lsm_notifier(&ima_lsm_policy_notifier); 9428c2ecf20Sopenharmony_ci if (error) 9438c2ecf20Sopenharmony_ci pr_warn("Couldn't register LSM notifier, error %d\n", error); 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci if (!error) 9468c2ecf20Sopenharmony_ci ima_update_policy_flag(); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci return error; 9498c2ecf20Sopenharmony_ci} 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_cilate_initcall(init_ima); /* Start IMA after the TPM is available */ 952