一. 实验目的
在初步熟悉LSM的基础上,仿照SMACK的编码风格,复现Pindown模块(https://github.com/node3/Linux-Security-Module)。
二. 实验环境
本博客用到的linux内核版本为4.19.163,若版本不同则需要对代码进行部分修改。
三. 实验原理和工作流程
实验原理:
该实验将二进制文件的路径与被保护文件的路径相关联。
二进制文件的路径被存储在被保护文件inode的扩展属性xattr中,如果一个进程尝试访问文件,在调用exec()时,会加载已存储的二进制的路径并检查扩展属性xattr(在本项目中为security.pindown),如果文件的扩展属性与进程的路径相匹配,则允许访问,否则访问会被阻止。
1.扩展属性
为了解决inode数据结构中成员无法解决的一些问题,linux设计了扩展属性。在支持扩展属性的文件中,inode和扩展属性是分开存储的,inode中只保留一个关联到扩展属性的索引。扩展属性本身则被实现为一个数组,数组项分为属性名和属性值两部分,属性名是一个字符串(格式为A.B,A从{user、system、trusted、security}中取值,表示用于应用、系统、可信和安全)。
getfattr -n security.pindown *
setfattr -n security.pindown -v /usr/bin/cat demo_lsm.c
setfattr -x security.pindown *
2.int pindown_cred_prepare(struct cred *new, const struct cred *old, gfp_t gfp)
该函数在fork时调用,通过拷贝old指向的cred创建一个新的cred,指针为new。返回值为0表示成功。
3.int bprm_set_creds(struct linux_binprm * bprm)
该函数在一次exec()时可能被调用多次,调用时会将bprm->called_set_creds置1。在本项目中,该函数用于将current->security->bprm_pathname= bprm->pathname。返回值为0表示成功。
4.static int cred_alloc_blank(struct cred *cred, gfp_t gfp)
分配空白的内存给cred用,需要分配可能使用的最大内存。(很奇怪,似乎没调用过)。
5.void cred_free(struct cred *cred)
用于释放cred所占用的空间。
6.void cred_transfer(struct cred *new, const struct cred *old)
在cred_alloc_blank后调用,通过拷贝old cred的值给分配的空内存填充值。
7. int inode_permission(struct inode *inode, int mask)
以文件为客体的控制访问的主体,返回值为0表示允许访问,否则为拒绝。
实际使用中4 5 6作为异步分配/删除cred的机制,不会调用,也可以忽略。
工作流程
1.确定要hook的函数
由于版本变更,这些接口可能会改变,所以先在security_list_options中找到对应的hook函数。
2.阅读原代码,确定其余每一部分的接口和功能
核心代码为inode_permission,实现逻辑为对未设置xattr的文件不做保护,对root用户不做限制,当非root用户访问被保护的文件时,比较进程bprm_pathname的值与文件的security.pindown值,仅相同时允许此次访问。
cred_prepare为fork产生新进程时为新进程拷贝一份pindown_security_t
cred_set_creds为调用exec()时,为进程pindown_security_t 赋值
get_inode_policy获取inode的xattr值
3.修改代码,在linux4.19.163版本的源码上实现相同的功能
4.debug,90%的bug出现在指针相关的问题上,比如:指针初始化,指针置空等
实验效果
对hello_world.c添加扩展属性,发现/usr/bin/cat仍能访问文件,而gedit和tail的访问被拒绝。
切换超级用户执行,tail可以访问该文件。
同时没有扩展属性的文件不受保护。
附上dmesg信息