IMA/EVM完整性检测
IMA(Integrity Measurement Architecture)是一个内核安全子系统,用于检测文件或数据的完整性和安全性。IMA的hook机制指的是内核接口钩子(kernel interface hooks),用于向IMA注册和实现一系列回调函数,以便在文件系统操作过程中拦截和记录检测信息。
1.0 IMA的hook机制
IMA的hook机制包括以下几个主要步骤:
1.1 定义回调函数
在IMA hook机制中,需要定义一系列回调函数,包括measure、appraise、policy、inode_free和post_parse等函数。measure函数用于在文件读取之前计算文件的哈希值;appraise函数用于评估文件完整性;policy函数用于读取并更新IMA策略;inode_free函数用于在删除文件时更新操作系统的IMA状态信息;post_parse函数用于在解析之后更新文件系统缓存和错误日志。
int security_kernel_post_read_file(struct file *file, char *buf, loff_t size,
enum kernel_read_file_id id)
{
int ret;
ret = call_int_hook(kernel_post_read_file, 0, file, buf, size, id);
if (ret)
return ret;
return ima_post_read_file(file, buf, size, id);
}
EXPORT_SYMBOL_GPL(security_kernel_post_read_file);
其中ima_post_read_file为ima的重要文件测量函数,可以看到,imahook独立与call_int_hook, 也就是lsm的审计框架体系。
其中还包括
ima_bprm_check
ima_inode_setxattr
ima_inode_removexattr
ima_file_mmap
ima_read_file
ima_post_read_file
ima_load_data
1.2 ima_bprm_check
ima_bprm_check指的是Linux内核中一个函数,全名是“int ima_bprm_check(struct linux_binprm *bprm)”。该函数用于在执行新的可执行文件之前检查其完整性和安全性。
当一个新的可执行文件被加载到内存中时,内核会调用ima_bprm_check函数。该函数通过访问内存中的内容和计算哈希值等方式对可执行文件的完整性进行检查。在哈希值被计算之后,该函数还会对哈希值进行数字签名验证。
IMA(Integrity Measurement Architecture)一旦启用后,在执行新的可执行文件时,IMA_bprm_check函数将被调用进行文件安全性检查。如果IMA检测到可执行文件被篡改或存在安全隐患,则会拒绝继续执行该文件。
1.3 ima_inode_setxattr
ima_inode_setxattr是Linux内核中的一个函数,在文件属性被设置时执行完整性测量和策略检查。该函数允许文件的附加属性进行修改,并对修改后的属性进行完整性测量,以确保文件的完整性和安全性。
1.4 ima_file_mmap
ima_file_mmap是Linux内核中的一个函数,用于在可执行文件的内存映射过程中进行完整性保护和测量。当进程请求将文件映射到内存中时,Linux内核将调用ima_file_mmap函数以确保文件的完整性和安全性。
1.5 ima_post_read_file
其中process_measurement会根据配置启动项来开启对应的策略
if (action & IMA_MEASURE)
ima_store_measurement(iint, file, pathname,
xattr_value, xattr_len, modsig, pcr,
template_desc);
if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) {
inode_lock(inode);
rc = ima_appraise_measurement(func, iint, file, pathname,
xattr_value, xattr_len, modsig);
inode_unlock(inode);
if (!rc)
rc = mmap_violation_check(func, file, &pathbuf,
&pathname, filename);
}
if (action & IMA_AUDIT)
ima_audit_measurement(iint, pathname);
ima_appraise_measurement - 评价文件度量值,最终调用evm_verifyxattr 校验security.ima属性的完整性。
这里会与收集到的度量信息进行比较。
2.0 ima的管理系统
static struct dentry *ima_dir;
static struct dentry *ima_symlink;
static struct dentry *binary_runtime_measurements;
static struct dentry *ascii_runtime_measurements;
static struct dentry *runtime_measurements_count;
static struct dentry *violations;
static struct dentry *ima_policy;
ascii_runtime_measurements =
securityfs_create_file("ascii_runtime_measurements",
S_IRUSR | S_IRGRP, ima_dir, NULL,
&ima_ascii_measurements_ops);
通过以上结构和函数创建虚拟文件系统,
static const struct file_operations ima_ascii_measurements_ops = {
.open = ima_ascii_measurements_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
并通过seq系列函数实现管理文件。struct seq_file *m = file->private_data,核心数据位于struct file private_data属性,并通过struct seq_operations结构实现数据管理。
2.1 struct seq_operations
在 Linux 内核中,struct seq_operations 是一种数据类型,用于通知 seq 文件序列化系统如何遍历复杂的数据结构并生成序列化文件内容。数据结构的遍历与序列化是通过回调函数实现的,这些回调函数在 struct seq_operations 中定义。
struct seq_operations 包含以下回调函数:
start:开始序列化操作时将调用此函数。该函数应返回遍历开始的位置,通常是数据结构的头部。
next:此函数根据当前的位置,返回下一个数据结构。这个函数必须返回下一个要序列化的数据结构或者 NULL 表示序列化过程已经结束。
stop:序列化过程结束时将调用此函数。此函数负责回收任何用于跟踪遍历状态的数据结构,并释放所有持有的资源。
show:此函数负责接受序列化请求并将表示当前条目所需的数据写入到 buffer 中。
stop:序列化过程结束时将调用此函数。此函数负责回收任何用于跟踪遍历状态的数据结构,并释放所有持有的资源。
seq 文件序列化系统是一种相对简单的内核数据序列化框架。它的灵活性和易用性共同促成了它在 Linux 内核中的流行。seq 文件的常见用途是在 proc 文件系统中为驱动程序、系统状态和信息提供统一、标准和轻巧的用户接口。
总之,struct seq_operations 是用于实现 Linux 内核中序列化复杂数据结构的一种数据类型,它定义了用于实现序列化过程的回调函数。
下面是一个简单的例子,展示如何使用 struct seq_operations 执行序列化操作。假设我们有一个以单链表形式组织的数据结构,其中每个节点包含一个整数,现在需要将这些节点序列化并输出到一个 seq 文件中。
定义 seq 文件的操作函数:
static int list_show(struct seq_file *s, void *v) {
struct node *n = v;
seq_printf(s, "%d\n", n->data);
return 0;
}
static void *list_start(struct seq_file *s, loff_t *pos) {
if (*pos >= list_size)
return NULL;
return get_node(*pos);
}
static void *list_next(struct seq_file *s, void *v, loff_t *pos) {
if (++(*pos) >= list_size)
return NULL;
return get_node(*pos);
}
static void list_stop(struct seq_file *s, void *v) {
// 释放数据结构
}
其中,list_show 函数用于将节点中的整数序列化到 seq 文件中,list_start 函数用于返回遍历链表的起始位置,list_next 函数根据当前位置返回下一个节点,list_stop 函数用于在序列化结束后释放所有资源。
创建一个 seq_operations 结构体:
static struct seq_operations list_seq_ops = {
.start = list_start,
.next = list_next,
.stop = list_stop,
.show = list_show
};
通过 seq_open 系列函数创建 seq 文件:
static int list_open(struct inode *inode, struct file *file) {
return seq_open(file, &list_seq_ops);
}
static struct file_operations list_fops = {
.owner = THIS_MODULE,
.open = list_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release
};
其中,open 函数用于打开 list,读取函数调用 seq_read,llseek 函数调用 seq_lseek,release 函数调用 seq_release。
此时,我们可以像操作其他 proc 或 sys 文件一样,读取并操作 list 文件,通过 list_show 函数将链表的每个节点上的整数依次输出。
2.2 ima measurement的存储
IMA (Integrity Measurement Architecture) 模块在 Linux 内核中用于测量和验证系统的完整性和安全性。在 IMA 中,PCR (Platform Configuration Register) 是一个用于存储测量值的寄存器,可用于验证系统和文件的完整性。在 Linux 内核中,IMA 模块使用 TPM (Trusted Platform Module) 来存储 PCR 中的测量值,并使用哈希算法来计算新测量值的哈希值,并将其与旧哈希值合并。
在 Linux 内核中,IMA 模块和 PCR 的实现主要在以下文件中:
include/linux/ima.h: 提供IMA模块的头文件,与PCR和相关结构体和函数定义有关。
security/integrity/ima/ima.h: 提供IMA模块的实现,包括enforce、bprm、crypto等检查。
security/integrity/ima/ima_main.c: 实现IMA模块的主要功能,包括处理系统测量事件、计算哈希值、更新PCR等流程。
security/integrity/ima/ima_policy.c: 实现IMA策略文件的解析和更新逻辑。
security/integrity/ima/ima_template.c: 提供IMA日志文件格式的转换函数,将测量信息转换为符合规范的日志格式。
drivers/char/tpm/tpm_vtpm_proxy.c: 提供TPM虚拟代理驱动程序,可在不使用TPM硬件的情况下测试IMA模块和PCR的运行情况。
总之,IMA 模块和 PCR 在 Linux 内核中的实现涵盖多个文件,包括IMA模块文件、IMA策略文件、日志格式转换函数等。管理员可以根据需求查看这些文件,以了解Linux内核中IMA模块和PCR的具体实现
evm_inode_init_security
evm_inode_setxattr
evm_inode_post_setxattr
evm_inode_removexattr