162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2008 IBM Corporation 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: 662306a36Sopenharmony_ci * Mimi Zohar <zohar@us.ibm.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * File: integrity_iint.c 962306a36Sopenharmony_ci * - implements the integrity hooks: integrity_inode_alloc, 1062306a36Sopenharmony_ci * integrity_inode_free 1162306a36Sopenharmony_ci * - cache integrity information associated with an inode 1262306a36Sopenharmony_ci * using a rbtree tree. 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/spinlock.h> 1762306a36Sopenharmony_ci#include <linux/rbtree.h> 1862306a36Sopenharmony_ci#include <linux/file.h> 1962306a36Sopenharmony_ci#include <linux/uaccess.h> 2062306a36Sopenharmony_ci#include <linux/security.h> 2162306a36Sopenharmony_ci#include <linux/lsm_hooks.h> 2262306a36Sopenharmony_ci#include "integrity.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic struct rb_root integrity_iint_tree = RB_ROOT; 2562306a36Sopenharmony_cistatic DEFINE_RWLOCK(integrity_iint_lock); 2662306a36Sopenharmony_cistatic struct kmem_cache *iint_cache __read_mostly; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistruct dentry *integrity_dir; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* 3162306a36Sopenharmony_ci * __integrity_iint_find - return the iint associated with an inode 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_cistatic struct integrity_iint_cache *__integrity_iint_find(struct inode *inode) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct integrity_iint_cache *iint; 3662306a36Sopenharmony_ci struct rb_node *n = integrity_iint_tree.rb_node; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci while (n) { 3962306a36Sopenharmony_ci iint = rb_entry(n, struct integrity_iint_cache, rb_node); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci if (inode < iint->inode) 4262306a36Sopenharmony_ci n = n->rb_left; 4362306a36Sopenharmony_ci else if (inode > iint->inode) 4462306a36Sopenharmony_ci n = n->rb_right; 4562306a36Sopenharmony_ci else 4662306a36Sopenharmony_ci return iint; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return NULL; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* 5362306a36Sopenharmony_ci * integrity_iint_find - return the iint associated with an inode 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_cistruct integrity_iint_cache *integrity_iint_find(struct inode *inode) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci struct integrity_iint_cache *iint; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (!IS_IMA(inode)) 6062306a36Sopenharmony_ci return NULL; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci read_lock(&integrity_iint_lock); 6362306a36Sopenharmony_ci iint = __integrity_iint_find(inode); 6462306a36Sopenharmony_ci read_unlock(&integrity_iint_lock); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return iint; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define IMA_MAX_NESTING (FILESYSTEM_MAX_STACK_DEPTH+1) 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* 7262306a36Sopenharmony_ci * It is not clear that IMA should be nested at all, but as long is it measures 7362306a36Sopenharmony_ci * files both on overlayfs and on underlying fs, we need to annotate the iint 7462306a36Sopenharmony_ci * mutex to avoid lockdep false positives related to IMA + overlayfs. 7562306a36Sopenharmony_ci * See ovl_lockdep_annotate_inode_mutex_key() for more details. 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_cistatic inline void iint_lockdep_annotate(struct integrity_iint_cache *iint, 7862306a36Sopenharmony_ci struct inode *inode) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci#ifdef CONFIG_LOCKDEP 8162306a36Sopenharmony_ci static struct lock_class_key iint_mutex_key[IMA_MAX_NESTING]; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci int depth = inode->i_sb->s_stack_depth; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (WARN_ON_ONCE(depth < 0 || depth >= IMA_MAX_NESTING)) 8662306a36Sopenharmony_ci depth = 0; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci lockdep_set_class(&iint->mutex, &iint_mutex_key[depth]); 8962306a36Sopenharmony_ci#endif 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic void iint_init_always(struct integrity_iint_cache *iint, 9362306a36Sopenharmony_ci struct inode *inode) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci iint->ima_hash = NULL; 9662306a36Sopenharmony_ci iint->version = 0; 9762306a36Sopenharmony_ci iint->flags = 0UL; 9862306a36Sopenharmony_ci iint->atomic_flags = 0UL; 9962306a36Sopenharmony_ci iint->ima_file_status = INTEGRITY_UNKNOWN; 10062306a36Sopenharmony_ci iint->ima_mmap_status = INTEGRITY_UNKNOWN; 10162306a36Sopenharmony_ci iint->ima_bprm_status = INTEGRITY_UNKNOWN; 10262306a36Sopenharmony_ci iint->ima_read_status = INTEGRITY_UNKNOWN; 10362306a36Sopenharmony_ci iint->ima_creds_status = INTEGRITY_UNKNOWN; 10462306a36Sopenharmony_ci iint->evm_status = INTEGRITY_UNKNOWN; 10562306a36Sopenharmony_ci iint->measured_pcrs = 0; 10662306a36Sopenharmony_ci mutex_init(&iint->mutex); 10762306a36Sopenharmony_ci iint_lockdep_annotate(iint, inode); 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic void iint_free(struct integrity_iint_cache *iint) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci kfree(iint->ima_hash); 11362306a36Sopenharmony_ci mutex_destroy(&iint->mutex); 11462306a36Sopenharmony_ci kmem_cache_free(iint_cache, iint); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/** 11862306a36Sopenharmony_ci * integrity_inode_get - find or allocate an iint associated with an inode 11962306a36Sopenharmony_ci * @inode: pointer to the inode 12062306a36Sopenharmony_ci * @return: allocated iint 12162306a36Sopenharmony_ci * 12262306a36Sopenharmony_ci * Caller must lock i_mutex 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_cistruct integrity_iint_cache *integrity_inode_get(struct inode *inode) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct rb_node **p; 12762306a36Sopenharmony_ci struct rb_node *node, *parent = NULL; 12862306a36Sopenharmony_ci struct integrity_iint_cache *iint, *test_iint; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci iint = integrity_iint_find(inode); 13162306a36Sopenharmony_ci if (iint) 13262306a36Sopenharmony_ci return iint; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci iint = kmem_cache_alloc(iint_cache, GFP_NOFS); 13562306a36Sopenharmony_ci if (!iint) 13662306a36Sopenharmony_ci return NULL; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci iint_init_always(iint, inode); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci write_lock(&integrity_iint_lock); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci p = &integrity_iint_tree.rb_node; 14362306a36Sopenharmony_ci while (*p) { 14462306a36Sopenharmony_ci parent = *p; 14562306a36Sopenharmony_ci test_iint = rb_entry(parent, struct integrity_iint_cache, 14662306a36Sopenharmony_ci rb_node); 14762306a36Sopenharmony_ci if (inode < test_iint->inode) { 14862306a36Sopenharmony_ci p = &(*p)->rb_left; 14962306a36Sopenharmony_ci } else if (inode > test_iint->inode) { 15062306a36Sopenharmony_ci p = &(*p)->rb_right; 15162306a36Sopenharmony_ci } else { 15262306a36Sopenharmony_ci write_unlock(&integrity_iint_lock); 15362306a36Sopenharmony_ci kmem_cache_free(iint_cache, iint); 15462306a36Sopenharmony_ci return test_iint; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci iint->inode = inode; 15962306a36Sopenharmony_ci node = &iint->rb_node; 16062306a36Sopenharmony_ci inode->i_flags |= S_IMA; 16162306a36Sopenharmony_ci rb_link_node(node, parent, p); 16262306a36Sopenharmony_ci rb_insert_color(node, &integrity_iint_tree); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci write_unlock(&integrity_iint_lock); 16562306a36Sopenharmony_ci return iint; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/** 16962306a36Sopenharmony_ci * integrity_inode_free - called on security_inode_free 17062306a36Sopenharmony_ci * @inode: pointer to the inode 17162306a36Sopenharmony_ci * 17262306a36Sopenharmony_ci * Free the integrity information(iint) associated with an inode. 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_civoid integrity_inode_free(struct inode *inode) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct integrity_iint_cache *iint; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (!IS_IMA(inode)) 17962306a36Sopenharmony_ci return; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci write_lock(&integrity_iint_lock); 18262306a36Sopenharmony_ci iint = __integrity_iint_find(inode); 18362306a36Sopenharmony_ci rb_erase(&iint->rb_node, &integrity_iint_tree); 18462306a36Sopenharmony_ci write_unlock(&integrity_iint_lock); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci iint_free(iint); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic void iint_init_once(void *foo) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct integrity_iint_cache *iint = (struct integrity_iint_cache *) foo; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci memset(iint, 0, sizeof(*iint)); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic int __init integrity_iintcache_init(void) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci iint_cache = 19962306a36Sopenharmony_ci kmem_cache_create("iint_cache", sizeof(struct integrity_iint_cache), 20062306a36Sopenharmony_ci 0, SLAB_PANIC, iint_init_once); 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ciDEFINE_LSM(integrity) = { 20462306a36Sopenharmony_ci .name = "integrity", 20562306a36Sopenharmony_ci .init = integrity_iintcache_init, 20662306a36Sopenharmony_ci .order = LSM_ORDER_LAST, 20762306a36Sopenharmony_ci}; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci/* 21162306a36Sopenharmony_ci * integrity_kernel_read - read data from the file 21262306a36Sopenharmony_ci * 21362306a36Sopenharmony_ci * This is a function for reading file content instead of kernel_read(). 21462306a36Sopenharmony_ci * It does not perform locking checks to ensure it cannot be blocked. 21562306a36Sopenharmony_ci * It does not perform security checks because it is irrelevant for IMA. 21662306a36Sopenharmony_ci * 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_ciint integrity_kernel_read(struct file *file, loff_t offset, 21962306a36Sopenharmony_ci void *addr, unsigned long count) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci return __kernel_read(file, addr, count, &offset); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci/* 22562306a36Sopenharmony_ci * integrity_load_keys - load integrity keys hook 22662306a36Sopenharmony_ci * 22762306a36Sopenharmony_ci * Hooks is called from init/main.c:kernel_init_freeable() 22862306a36Sopenharmony_ci * when rootfs is ready 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_civoid __init integrity_load_keys(void) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci ima_load_x509(); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_IMA_LOAD_X509)) 23562306a36Sopenharmony_ci evm_load_x509(); 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic int __init integrity_fs_init(void) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci integrity_dir = securityfs_create_dir("integrity", NULL); 24162306a36Sopenharmony_ci if (IS_ERR(integrity_dir)) { 24262306a36Sopenharmony_ci int ret = PTR_ERR(integrity_dir); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (ret != -ENODEV) 24562306a36Sopenharmony_ci pr_err("Unable to create integrity sysfs dir: %d\n", 24662306a36Sopenharmony_ci ret); 24762306a36Sopenharmony_ci integrity_dir = NULL; 24862306a36Sopenharmony_ci return ret; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci return 0; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cilate_initcall(integrity_fs_init) 255