IMA源码分析——度量事件记录与显示
来源:互联网 发布:黑暗之魂1萝莉捏脸数据 编辑:程序博客网 时间:2024/06/02 05:49
- 初始化
- 处理度量事件
- ima_add_digest_entry
- 显示度量事件
- 初始化
- ascii_runtime_measurements
- start
- next
- show
- stop
IMA(Integrity Measurement Architecture)是内核中一个用来度量所有二进制加载、模块加载、动态链接库加载的模块,以用来记录平台的完整性证据
本文对IMA度量事件的记录与显示部分进行源码分析,本文环境为ubuntu14.04.4,利用apt-get install linux-source后编译进入的内核版本为:
root@vtpm:/sys/kernel/security/ima# uname -r3.13.11-ckt39root@vtpm:/sys/kernel/security/ima#
初始化
内核中维护度量的双向链表ima_measurements
在security/integrity/ima/ima.h
进行了extern引用:
extern struct list_head ima_measurements; /* list of all measurements */
真正的定义在security/integrity/ima/ima_queue.c
:
LIST_HEAD(ima_measurements); /* list of all measurements */
LIST_HEAD
在于定义双向链表结构体,list_head
结构体有next与prev两个指针,而LIST_HEAD_INIT
则是将两个指针全部指向自身
struct list_head { struct list_head *next, *prev;};#define LIST_HEAD_INIT(name) { &(name), &(name) }#define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name)
处理度量事件
当产生度量事件后,内核将一步步地调用到ima_queue.c::ima_add_template_entry()
。该函数首先判断digest是否已经存在,若已经存在则表明是未被修改的程序等再次加载,因而可以忽略。另外,关于为度量事件建立hash的代码,我们之后再来分析。。。
if (!violation) { memcpy(digest, entry->digest, sizeof digest); if (ima_lookup_digest_entry(digest)) { audit_cause = "hash_exists"; result = -EEXIST; goto out; }}result = ima_add_digest_entry(entry);if (result < 0) { audit_cause = "ENOMEM"; audit_info = 0; goto out;}
entry的类型是struct ima_template_entry *,实际上记录的度量事件的信息(如度量值等等),其定义如下:
/* IMA template descriptor definition */struct ima_template_desc { char *name; char *fmt; int num_fields; struct ima_template_field **fields;};struct ima_template_entry { u8 digest[TPM_DIGEST_SIZE]; /* sha1 or md5 measurement hash */ struct ima_template_desc *template_desc; /* template descriptor */ u32 template_data_len; struct ima_field_data template_data[0]; /* template related data */};struct ima_queue_entry { struct hlist_node hnext; /* place in hash collision list */ struct list_head later; /* place in ima_measurements list */ struct ima_template_entry *entry;};
ima_add_digest_entry
再来看该函数ima_add_digest_entry
函数,该函数输入参数为度量事件(entry),函数执行时首先将entry放入ima_queue_entry
结构体中:
struct ima_queue_entry *qe;unsigned int key;qe = kmalloc(sizeof(*qe), GFP_KERNEL);if (qe == NULL) { pr_err("IMA: OUT OF MEMORY ERROR creating queue entry.\n"); return -ENOMEM;}qe->entry = entry;
再借助ima_queue_entry
结构体的第二个参数struct list_head later
与记录所有度量事件的双向链表ima_measurements
建立联系:
INIT_LIST_HEAD(&qe->later);list_add_tail_rcu(&qe->later, &ima_measurements);
INIT_LIST_HEAD
在include/linux/list.h
中,用来将list的两个指针全部指向自身:
static inline void INIT_LIST_HEAD(struct list_head *list){ list->next = list; list->prev = list;}
list_add_tail_rcu
类似于include/linux/list.h
中的list_add_tail
:
static inline void list_add_tail(struct list_head *new, struct list_head *head){ __list_add(new, head->prev, head);}static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next){ next->prev = new; new->next = next; new->prev = prev; prev->next = new;}
目的是将new插入到双向链表head之前,由于是双向链表,因此,对于函数ima_add_digest_entry
来说,实际上就是将qe->later
(也就是当前的度量事件entry
)加入到ima_measurements
的最后。从而形成了一个队列
显示度量事件
在用户空间IMA的的文件在/sys/kernel/security/ima
,其中ascii_runtime_measurements即是以ascii显示的所有度量日志
root@vtpm:/sys/kernel/security/ima# lsascii_runtime_measurements binary_runtime_measurements policy runtime_measurements_count violationsroot@vtpm:/sys/kernel/security/ima#
初始化
回到内核的IMA源码,ima的初始化函数在security/ima/ima_init.c::ima_init()
函数中
int __init ima_init(void){ ... return ima_fs_init();}
函数最后调用了ima_fs_init(),该函数在ima_fs.c中,在这个函数中创建了用户空间的/sys/kernel/security/ima
目录以及其下的所有文件
int __init ima_fs_init(void){ ima_dir = securityfs_create_dir("ima", NULL); binary_runtime_measurements = securityfs_create_file("binary_runtime_measurements", S_IRUSR | S_IRGRP, ima_dir, NULL, &ima_measurements_ops); ascii_runtime_measurements = securityfs_create_file("ascii_runtime_measurements", S_IRUSR | S_IRGRP, ima_dir, NULL, &ima_ascii_measurements_ops); ... ima_policy = securityfs_create_file("policy", S_IWUSR, ima_dir, NULL, &ima_measure_policy_ops);
我们具体来看当在用户空间cat ascii_runtime_measurements
时,内核对应的代码
ascii_runtime_measurements
在ima_fs_init中,我们知道文件ascii_runtime_measurements
对应的file_operations
结构体为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,};
struct file_operations
是一个字符设备把驱动的操作和设备号联系在一起的纽带,是一系列指针的集合,每个被打开的文件都对应于一系列的操作,用来执行一系列的系统调用
对于文件ascii_runtime_measurements
,四个操作里只有open被指向到其他函数(ima_ascii_measurements_open
),其他操作均采用默认的seq操作
ima_ascii_measurements_open将对该文件的open定向到ima_ascii_measurements_seqops
,这个seq_operations
定义了open操作时,的start、next、stop、show操作
static const struct seq_operations ima_ascii_measurements_seqops = { .start = ima_measurements_start, .next = ima_measurements_next, .stop = ima_measurements_stop, .show = ima_ascii_measurements_show};static int ima_ascii_measurements_open(struct inode *inode, struct file *file){ return seq_open(file, &ima_ascii_measurements_seqops);}
seq_operations
针对的是序列文件(seq_file),使用它能够将Linux内核里面常用的数据结构通过文件(主要关注proc文件)导出到用户空间。使用Seq_file,用户必须抽象出一个链接对象,然后可以依次遍历这个链接对象。这个链接对象可以是链表,数组,哈希表等等。Seq_file必须实现四个操作函数:start(), next(), show(), stop()。
start():
主要实现初始化工作,在遍历一个链接对象开始时,调用。返回一个链接对象的偏移或者SEQ_START_TOKEN(表征这是所有循环的开始)。出错返回ERR_PTR。该函数返回值就是show()、next()函数第二个参数。stop():
当所有链接对象遍历结束时调用。主要完成一些清理工作。next():
用来在遍历中寻找下一个链接对象。返回下一个链接对象或者NULL(遍历结束)。show():
对遍历对象进行操作的函数。主要是调用seq_printf(), seq_puts()之类的函数,打印出这个对象节点的信息。
用户态调用一次读操作,seq_file流程为:该函数调用struct seq_operations结构提顺序为:start->show->next->show…->next->show->next->stop->start->stop来读取文件
start
我们首先来看seq_operarion中的start函数:
static void *ima_measurements_start(struct seq_file *m, loff_t *pos){ loff_t l = *pos; struct ima_queue_entry *qe; /* we need a lock since pos could point beyond last element */ rcu_read_lock(); list_for_each_entry_rcu(qe, &ima_measurements, later) { if (!l--) { rcu_read_unlock(); return qe; } } rcu_read_unlock(); return NULL;}
list_for_each_entry_rcu
类似于list_for_each_entry
。以下是与之相关的宏定义:
#define container_of(ptr, type, member) ({ \ const typeof(((type *)0)->member)*__mptr = (ptr); \ (type *)((char *)__mptr - offsetof(type, member)); })#define list_entry(ptr, type, member) \ container_of(ptr, type, member)#define list_next_entry(pos, member) \ list_entry((pos)->member.next, typeof(*(pos)), member)#define list_first_entry(ptr, type, member) \ list_entry((ptr)->next, type, member)#define list_for_each_entry(pos, head, member) \ for (pos = list_first_entry(head, typeof(*pos), member); \ &pos->member != (head); \ pos = list_next_entry(pos, member))
list_for_each_entry,传入的形参中,member为later,head为&ima_measurements, pos为qe
调用list_first_entry时,传入的形参中,member为later,ptr指向ima_measurement件,type为typeof(pos),也就是struct ima_queue_entry
调用list_entry时,传入的形参中,member为later,ptr指向ima_measurement的的第一个度量事件,type为struct ima_queue_entry*
container_of的目的在于通过传入的ima_measurement的的第一个度量事件,找到这个事件对应的ima_queue_entry指针,并且返回
依次返回后,list_for_each_entry_rcu(qe, &ima_measurements, later) {
相当于返回一个for循环语句,对ima_measurement进行遍历,而循环值则是对应的每个ima_queue_entry指针,并用qe指向这个指针
这样start函数相当于返回了ima_measurement保存的第*pos个度量事件的ima_queue_entry结构体
next
第二个参数为目前正在show的度量事件,next调用list_entry_rcu
,并将形参定义为qe->later.next
找到接下来的度量事件
若找到的度量事件已经达到ima_measurements的头,则返回空
static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos){ struct ima_queue_entry *qe = v; /* lock protects when reading beyond last element * against concurrent list-extension */ rcu_read_lock(); qe = list_entry_rcu(qe->later.next, struct ima_queue_entry, later); rcu_read_unlock(); (*pos)++; return (&qe->later == &ima_measurements) ? NULL : qe;}
show
show是将每个度量事件打印到用户空间,实际上是将start或者是next传送给它的qe按照一定格式打印
static int ima_measurements_show(struct seq_file *m, void *v){ /* the list never shrinks, so we don't need a lock here */ struct ima_queue_entry *qe = v; struct ima_template_entry *e; int namelen; u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX; bool is_ima_template = false; int i; /* get entry */ e = qe->entry; if (e == NULL) return -1; /* * 1st: PCRIndex * PCR used is always the same (config option) in * little-endian format */ ima_putc(m, &pcr, sizeof pcr); /* 2nd: template digest */ ima_putc(m, e->digest, TPM_DIGEST_SIZE); /* 3rd: template name size */ namelen = strlen(e->template_desc->name); ima_putc(m, &namelen, sizeof namelen);
stop
stop函数函数体为空,不需要做任何操作
至此,IMA用来记录与显示给用户空间的双向链表的代码已经分析完毕~~
- IMA源码分析——度量事件记录与显示
- 完整性度量架构(IMA)介绍与分析
- 完整性度量架构(IMA)引见与分析
- 第二章 过程域——度量与分析
- ExtJs源码分析与学习—ExtJs事件机制(六)
- 测试数据度量与控制(第一步:度量分析)
- 互信息——事件相关性度量
- 【Flume】【源码分析】flume中http监控类型的源码分析,度量信息分析,以及flume的事件总线
- Nginx源码分析—定时器事件
- 如何度量与分析幸福
- 如何度量与分析幸福
- 分析模式 - 度量与测绘
- ima
- 应用框架的设计与实现——.NET平台(7.事件通知服务.源码分析)
- ExtJs源码分析与学习—ExtJs事件机制(一)
- ExtJs源码分析与学习—ExtJs事件机制(二)
- ExtJs源码分析与学习—ExtJs事件机制(三)
- ExtJs源码分析与学习—ExtJs事件机制(四)
- 招投标异议与政府采购质疑二者差异大比拼,你必须掌握
- Log4j配置详解
- 根据下表,去JSONArray对应的值
- LeetCode题解
- MongoDB - 连接
- IMA源码分析——度量事件记录与显示
- Linux中创建Android Studio的快捷方式
- Redis学习笔记(九)redis实现时时直播列表缓存,支持分页[热点数据存储]
- 百度地图——判断用户是否在配送范围内解决方案
- QuickSort快速排序源码
- 创建基于jsp的商品浏览器的四个步骤
- 机器学习--Logistic回归计算过程的推导
- parse_ini_file() 函数的使用
- POJ-2356 Find a multiple(鸽巢原理)题目数据太垃圾了!!