LSM内核源代码分析与测试(一)
来源:互联网 发布:任天堂vb用电池的吗 编辑:程序博客网 时间:2024/06/05 07:17
- LSM初始化
- 全局变量security_ops的初始化
- do_security_initcalls
- 自定义钩子
- 测试
- LSM初始化
LSM初始化
LSM系统初始化发生在内核初始化阶段,内核的启动函数为init/main.c::start_kernel()
:
asmlinkage void __init start_kernel(void){ char * command_line; extern const struct kernel_param __start___param[], __stop___param[]; ... buffer_init(); key_init(); security_init(); dbg_late_init(); vfs_caches_init(totalram_pages); signals_init(); ... rest_init();}
该函数调用了security/security.c::security_init()
,用来初始化LSM框架
/** * security_init - initializes the security framework * * This should be called early in the kernel initialization sequence. */int __init security_init(void){ printk(KERN_INFO "Security Framework initialized\n"); security_fixup_ops(&default_security_ops); security_ops = &default_security_ops; do_security_initcalls(); return 0;}
全局变量security_ops的初始化
函数的前部分利用default_security_ops来初始化security_ops结构体,两个结构体的定义为:
static struct security_operations *security_ops;static struct security_operations default_security_ops = { .name = "default",};
default_security_ops
在定义时只有name字段,security/capability.c::security_fixup_ops()
函数的作用在于将ptrace_access_check
等等函数指针指向cap_##function
:
#define set_to_cap_if_null(ops, function) \ do { \ if (!ops->function) { \ ops->function = cap_##function; \ pr_debug("Had to override the " #function \ " security operation with the default.\n");\ } \ } while (0)void __init security_fixup_ops(struct security_operations *ops){ set_to_cap_if_null(ops, ptrace_access_check); set_to_cap_if_null(ops, ptrace_traceme); ...
在函数式宏定义中,#运算符用于创建字符串,而##则将运算符把前后两个预处理Token连接成一个预处理Token。
比如在执行set_to_cap_if_null(ops, inode_create);
时,security_ops
的inode_create
函数指针将指向函数cap_inode_create
(该函数也在security/capability.c,并默认不做操作)
static int cap_inode_create(struct inode *inode, struct dentry *dentry, umode_t mask){ return 0;}
do_security_initcalls
在初始化security_ops
后,security_init
调用do_security_initcalls()
static void __init do_security_initcalls(void){ initcall_t *call; call = __security_initcall_start; while (call < __security_initcall_end) { (*call) (); call++; }}
initcall_t *
的定义在include/linux/init.h
typedef int (*initcall_t)(void);typedef void (*exitcall_t)(void);extern initcall_t __security_initcall_start[], __security_initcall_end[];
我们知道typedef能够对类型取别名,如typedef int MyInt;
typedef int(init_fnc_t) (void)
是对一个 int (void)类型的函数类型进行取别名init_fnc_ttypedef int (*init_fnc_t_p)(void)
则是取一个int (void)类型的函数指针
如:
#include <stdio.h> int GetData(void) { return 101; } int main() { typedef int (init_fnc_t)(void); typedef int (*init_fnc_t_p)(void); init_fnc_t *MyFunction; init_fnc_t_p MyFunctionP; MyFunction = GetData; MyFunctionP = GetData; printf("(init_fnc_t)(void) = %d \n", MyFunction()); printf("(*init_fnc_t_p)(void) = %d \n", MyFunctionP()); return 0; }
打印出来的结果都是101。
回到函数do_security_initcalls
,可以发现其作用为依次调用__security_initcall_start
到__security_initcall_end
之间的函数
那么到底具体调用了哪些函数呢?
查阅资料发现include/asm-generic/vmlinux.lds.h
定义了一些宏用于辅助写连接脚本,从其中可以看到最终会出现在连接脚本中的各个内存section以及它们的相对位置。在这个文件里我们可以找到security_initcall
所在的内存section的相关代码:
#define SECURITY_INIT \ .security_initcall.init : AT(ADDR(.security_initcall.init) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__security_initcall_start) = .; \ *(.security_initcall.init) \ VMLINUX_SYMBOL(__security_initcall_end) = .; \ }
从上述内存段的定义可以看出,该宏定义的内存区域起始于__security_initcall_start
,结束于__security_initcall_end
,也就是do_security_initcalls
要调用的函数了
注意include/linux/init.h
中还有这么一个宏。这里对于形参fn,将定义一个__initcall_##fn
函数,并且指定使用内存段.security_initcall.init
#define security_initcall(fn) \ static initcall_t __initcall_##fn \ __used __section(.security_initcall.init) = fn
因此,调用这个宏的函数就是do_security_initcalls
要调用的函数
用xargs grep查找下整个内核代码:
root@BlockIMATest:/usr/src/octa-blockIMA/octa-blockIMA# find . -type f | xargs grep "security_initcall("./include/linux/init.h:#define security_initcall(fn) \./include/linux/init.h:#define security_initcall(fn) module_init(fn)./security/yama/yama_lsm.c:security_initcall(yama_init);./security/selinux/hooks.c:security_initcall(selinux_init);./security/apparmor/lsm.c:security_initcall(apparmor_init);./security/integrity/iint.c:security_initcall(integrity_iintcache_init);./security/tomoyo/tomoyo.c:security_initcall(tomoyo_init);./security/smack/smack_lsm.c:security_initcall(smack_init);root@BlockIMATest:/usr/src/octa-blockIMA/octa-blockIMA#
那么do_security_initcalls
调用了(如果模块enable的话,而且感觉应该最多只有一个security_ops
会生效[待分析]??):
yama_init
selinux_init
apparmor_init
integrity_iintcache_init
tomoyo_init
smack_init
自定义钩子
假设机器enable了SELinux,则security_init
将会调用selinux_init
,该函数位于security/selinux/hooks.c
static __init int selinux_init(void){ if (!security_module_enable(&selinux_ops)) { selinux_enabled = 0; return 0; } ... if (register_security(&selinux_ops)) panic("SELinux: Unable to register with kernel.\n"); ... return 0;}
我们知道LSM的具体做法是在内核关键代码处打上了安全钩子,并且允许安全模块来定义这些安全钩子的具体操作,如访问控制等。使用安全模块自定义钩子的函数为register_security
,该函数是在security/security.c
中
int __init register_security(struct security_operations *ops){ if (verify(ops)) { printk(KERN_DEBUG "%s could not verify " "security_operations structure.\n", __func__); return -EINVAL; } if (security_ops != &default_security_ops) return -EAGAIN; security_ops = ops; return 0;}
verify函数只是简单地调用了security_fixup_ops
,这个在初始化过程中已经介绍了。register_security
实际上就是用传入的形参赋值给全局的security_ops
。对于SELinux来说,则是让内核使用selinux_ops
。而且这也意味着只能有一个register_security
生效
static struct security_operations selinux_ops = { .name = "selinux", .ptrace_access_check = selinux_ptrace_access_check, .ptrace_traceme = selinux_ptrace_traceme, .capget = selinux_capget, .capset = selinux_capset, .capable = selinux_capable, ...};
测试
修改内核代码来测试LSM见下一篇文章http://blog.csdn.net/lwyeluo/article/details/55215792..
- LSM内核源代码分析与测试(一)
- LSM内核源代码分析与测试(二)
- WebKit内核源代码分析(一)
- 内核源代码分析(3)
- 《Linux内核完全注释》与《Linux内核源代码情景分析》(上下册)
- 基于MIPS(LOOGSON)架构LINUX内核启动流程源代码分析(一)--kernel_entry
- Linux 内核源代码情景分析 chap 2 存储管理(一)
- WebKit内核源代码分析(三)
- WebKit内核源代码分析(五)
- WebKit内核源代码分析(二)
- WebKit内核源代码分析(三)
- WebKit内核源代码分析(五)
- WebKit内核源代码分析(二)
- WebKit内核源代码分析(三)
- WebKit内核源代码分析(二)
- WebKit内核源代码分析(三)
- WebKit内核源代码分析(二)
- WebKit内核源代码分析(三)
- MFC 对文件以及文件夹操作
- 生化危机6终章中英双字
- Auxiliary Set----DFS思维题
- Git(1)/Git简介
- Ubuntu16.04下启动eclips
- LSM内核源代码分析与测试(一)
- BZOJ 1024, 生日快乐
- Android studio使用JNI实例
- Glide-图片的压缩
- 用gdb调试游戏服务端
- AngularJs服务-$location(暴露当前地址栏的URL)
- [cpp]C++中的宏定义
- shiro使用注解式和jsp标签的方式进行开发
- 快速幂 及 快速幂取模