SELinux源码分析-1.31Word文件下载.doc
《SELinux源码分析-1.31Word文件下载.doc》由会员分享,可在线阅读,更多相关《SELinux源码分析-1.31Word文件下载.doc(96页珍藏版)》请在冰点文库上搜索。
![SELinux源码分析-1.31Word文件下载.doc](https://file1.bingdoc.com/fileroot1/2023-4/30/2c1001f6-9729-4f7f-a87c-8c266a7e03b8/2c1001f6-9729-4f7f-a87c-8c266a7e03b81.gif)
另一种控制访问的方法是基于角色的访问控制(RBAC)。
在RBAC中,权限是根据安全系统所授予的角色来提供的。
角色的概念与传统的分组概念不同,因为一个分组代表一个或多个用户。
一个角色可以代表多个用户,但它也代表一个用户集可以执行的权限。
SELinux将MAC和RBAC都添加到了GNU/Linux操作系统中。
下一节将探讨SELinux实现,以及如何将安全增强透明地添加到Linux内核中。
1.3Linux安全模块(LinuxSecurityModule,简称LSM)
Linux安全模块(LSM)提供了两类对安全钩子函数的调用:
一类管理内核对象的安全域,另一类仲裁对这些内核对象的访问。
对安全钩子函数的调用通过钩子来实现,钩子是全局表security_ops中的函数指针,这个全局表的类型是security_operations结构,这个结构定义在include/linux/security.h这个头文件中,这个结构中包含了按照内核对象或内核子系统分组的钩子组成的子结构,以及一些用于系统操作的顶层钩子。
在内核源代码中很容易找到对钩子函数的调用:
其前缀是security_ops->
。
对钩子函数(Hooks)的详细说明留到后面。
在内核引导的过程中,Linux安全模块(LSM)框架被初始化为一系列的虚拟钩子函数,以实现传统的UNIX超级用户机制。
当加载一个安全模块时,必须使用register_security()(在linux/security.h中声明,security.c中定义)函数向Linux安全模块(LSM)框架注册这个安全模块:
这个函数将设置全局表security_ops,使其指向这个安全模块的钩子函数指针,从而使内核向这个安全模块询问访问控制决策。
一旦一个安全模块被加载,就成为系统的安全策略决策中心,而不会被后面的register_security()函数覆盖,直到这个安全模块被使用unregister_security()函数向框架注销:
这简单的将钩子函数替换为缺省值,即Dummy.c中的Dummy函数,系统回到UNIX超级用户机制。
另外,Linux安全模块(LSM)框架还提供了函数mod_reg_security()和函数mod_unreg_security(),使其后的安全模块可以向已经第一个注册的主模块注册和注销,但其策略实现由主模块决定:
是提供某种策略来实现模块堆栈从而支持模块功能合成,还是简单的返回错误值以忽略其后的安全模块。
(需要主模块进行调用)这些函数都提供在内核源代码文件security/security.c中。
这种方式的设计主要为了实现模块的叠加,实现多种模块对系统的安全进行检查。
目前该功能主要是实现,capability和SELinux的叠加。
Linux内核现在对POSIX.1ecapabilities的一个子集(提供一部分钩子函数)提供支持。
Linux安全模块(LSM)设计的一个需求就是把这个功能移植为一个可选的安全模块。
POSIX.1ecapabilities提供了划分传统超级用户特权并赋给特定的进程的功能,即传统超级用户不允许的权限,可以通过POSIX.1ecapabilities授予,大大提高了系统安全的灵活性。
Linux安全模块(LSM)保留了用来在内核中执行capability检查的现存的capable()接口,但把capable()函数简化为一个Linux安全模块(LSM)钩子函数的包装(secondary_ops),从而允许在安全模块中实现任何需要的逻辑。
Linux安全模块(LSM)还保留了task_struck结构中的进程capability集(一个简单的位向量),而并没有把它移到安全域中去。
Linux内核对capabilities的支持还包括两个系统调用:
capset()和capget()。
Linux安全模块(LSM)同样保留了这些系统调用但将其替换为对钩子函数的调用,使其基本上可以通过security()系统调用来重新实现。
Linux安全模块(LSM)已经开发并且移植了相当部分的capabilities逻辑到一个capabilities安全模块中,但内核中仍然保留了很多原有capabilities的残余。
这些实现方法都最大程度的减少了对Linux内核的修改影响,并且最大程度保留了对原有使用capabilities的应用程序的支持,同时满足了设计的功能需求。
以后要使capabilities模块完全独立,剩下要做的主要步骤是:
把位向量移到task_struct结构中合适的安全域中,以及重新定位系统调用接口。
目前的操作系统中还没有在这方面做改进,为了减少LSM架构对原内核的影响,这部分暂且不懂。
1.4接口说明:
给内核开发人员和安全研究人员使用的钩子
Linux安全模块(LSM)对于内核开发人员和安全研究人员的价值就在于:
可以使用其提供的接口将现存的安全增强系统移植到这一框架上,从而能够以可加载内核模块的形式提供给用户使用;
或者甚至可以直接编写适合自己需要的安全模块。
Linux安全模块(LSM)提供的接口就是钩子,其初始化时所指向的虚拟函数实现了缺省的传统UNIX超级用户机制,模块编写者必须重新实现这些钩子函数来满足自己的安全策略。
下面简要介绍Linux安全模块(LSM)提供的钩子,详细情况请参考源代码,特别是include/linux/security.h头文件中security_operations结构的定义。
至于具体如何根据自己需要的安全策略编写安全模块,可以参考SELinux,DTE,LIDS等系统的安全模块实现。
首先是任务钩子,Linux安全模块(LSM)提供了一系列的任务钩子使得安全模块可以管理进程的安全信息并且控制进程的操作。
模块可以使用task_struct结构中的安全域来维护进程安全信息;
任务钩子提供了控制进程间通信的钩子,例如kill();
还提供了控制对当前进程进行特权操作的钩子,例如setuid();
还提供了对资源管理操作进行细粒度控制的钩子,例如setrlimit()和nice()。
int(*task_create)(unsignedlongclone_flags);
int(*task_alloc_security)(structtask_struct*p);
void(*task_free_security)(structtask_struct*p);
int(*task_setuid)(uid_tid0,uid_tid1,uid_tid2,intflags);
//特权操作的Hook
int(*task_post_setuid)(uid_told_ruid/*orfsuid*/,
uid_told_euid,uid_told_suid,intflags);
int(*task_setgid)(gid_tid0,gid_tid1,gid_tid2,intflags);
int(*task_setpgid)(structtask_struct*p,pid_tpgid);
int(*task_getpgid)(structtask_struct*p);
int(*task_getsid)(structtask_struct*p);
void(*task_getsecid)(structtask_struct*p,u32*secid);
int(*task_setgroups)(structgroup_info*group_info);
int(*task_setnice)(structtask_struct*p,intnice);
//资源管理的Hook
int(*task_setioprio)(structtask_struct*p,intioprio);
int(*task_getioprio)(structtask_struct*p);
int(*task_setrlimit)(unsignedintresource,structrlimit*new_rlim);
int(*task_setscheduler)(structtask_struct*p,intpolicy,
structsched_param*lp);
int(*task_getscheduler)(structtask_struct*p);
int(*task_movememory)(structtask_struct*p);
int(*task_kill)(structtask_struct*p,
structsiginfo*info,intsig,u32secid);
//控制进程间通信的Hook
int(*task_wait)(structtask_struct*p);
int(*task_prctl)(intoption,unsignedlongarg2,
unsignedlongarg3,unsignedlongarg4,
unsignedlongarg5);
void(*task_reparent_to_init)(structtask_struct*p);
void(*task_to_inode)(structtask_struct*p,structinode*inode);
图1.1任务钩子
其次是程序装载钩子。
很多安全模块,包括Linuxcapabilities,SELinux,DTE都需要有在一个新程序执行时改变特权的能力。
因此Linux安全模块(LSM)提供了一系列程序装载钩子,用在一个execve()操作执行过程的关键点上。
linux_binprm结构中的安全域允许安全模块维护程序装载过程中的安全信息;
提供了钩子用于允许安全模块在装载程序前初始化安全信息和执行访问控制;
还提供了钩子允许模块在新程序成功装载后更新任务的安全信息;
还提供了钩子用来控制程序执行过程中的状态继承,例如确认打开的文件描述符。
(不是可信启动)
int(*bprm_alloc_security)(structlinux_binprm*bprm);
void(*bprm_free_security)(structlinux_binprm*bprm);
void(*bprm_apply_creds)(structlinux_binprm*bprm,intunsafe);
void(*bprm_post_apply_creds)(structlinux_binprm*bprm);
int(*bprm_set_security)(structlinux_binprm*bprm);
int(*bprm_check_security)(structlinux_binprm*bprm);
int(*bprm_secureexec)(structlinux_binprm*bprm);
图1.2程序装载钩子
再次是进程间通信IPC钩子。
安全模块可以使用进程间通信IPC钩子来对SystemVIPC的安全信息进行管理,以及执行访问控制。
IPC对象数据结构共享一个子结构kern_ipc_perm,并且这个子结构中只有一个指针传给现存的ipcperms()函数进行权限检查,因此Linux安全模块(LSM)在这个共享子结构中加入了一个安全域。
为了支持单个消息的安全信息,Linux安全模块(LSM)还在msg_msg结构中加入了一个安全域。
Linux安全模块(LSM)在现存的ipcperms()函数中插入了一个钩子,使得安全模块可以对每个现存的LinuxIPC权限执行检查。
由于对于某些安全模块,这样的检查是不足够的,Linux安全模块(LSM)也在单个的IPC操作中插入了钩子。
另外还有钩子支持对通过SystemV消息队列发送的单个消息进行细粒度的访问控制。
int(*ipc_permission)(structkern_ipc_perm*ipcp,shortflag);
int(*msg_msg_alloc_security)(structmsg_msg*msg);
void(*msg_msg_free_security)(structmsg_msg*msg);
int(*msg_queue_alloc_security)(structmsg_queue*msq);
void(*msg_queue_free_security)(structmsg_queue*msq);
int(*msg_queue_associate)(structmsg_queue*msq,intmsqflg);
int(*msg_queue_msgctl)(structmsg_queue*msq,intcmd);
int(*msg_queue_msgsnd)(structmsg_queue*msq,
structmsg_msg*msg,intmsqflg);
int(*msg_queue_msgrcv)(structmsg_queue*msq,
structmsg_msg*msg,
structtask_struct*target,
longtype,intmode);
图1.3进程通信IPC钩子
下面是文件系统钩子。
对于文件操作,定义了三种钩子:
文件系统钩子,inode结点钩子,以及文件钩子。
Linux安全模块(LSM)在对应的三个内核数据结构中加入了安全域,分别是:
super_block结构,inode结构,file结构。
超级块文件系统钩子使得安全模块能够控制对整个文件系统进行的操作,例如挂载,卸载,还有statfs()。
Linux安全模块(LSM)在permission()函数中插入了钩子,从而保留了这个函数,但是也提供了很多其他inode结点钩子来对单个inode结点操作进行细粒度访问控制。
文件钩子中的一些允许安全模块对read()和write()这样的文件操作进行额外的检查;
还有文件钩子允许安全模块控制通过socketIPC接收打开文件描述符;
其他的文件钩子对像fcntl()和ioctl()这样的操作提供细粒度访问控制。
int(*sb_alloc_security)(structsuper_block*sb);
void(*sb_free_security)(structsuper_block*sb);
int(*sb_copy_data)(structfile_system_type*type,
void*orig,void*copy);
int(*sb_kern_mount)(structsuper_block*sb,void*data);
int(*sb_statfs)(structdentry*dentry);
int(*sb_mount)(char*dev_name,structnameidata*nd,
char*type,unsignedlongflags,void*data);
int(*sb_check_sb)(structvfsmount*mnt,structnameidata*nd);
int(*sb_umount)(structvfsmount*mnt,intflags);
void(*sb_umount_close)(structvfsmount*mnt);
void(*sb_umount_busy)(structvfsmount*mnt);
void(*sb_post_remount)(structvfsmount*mnt,
unsignedlongflags,void*data);
void(*sb_post_mountroot)(void);
void(*sb_post_addmount)(structvfsmount*mnt,
structnameidata*mountpoint_nd);
int(*sb_pivotroot)(structnameidata*old_nd,
structnameidata*new_nd);
void(*sb_post_pivotroot)(structnameidata*old_nd,
structnameidata*new_nd);
图1.4super_block文件系统钩子
int(*inode_alloc_security)(structinode*inode);
void(*inode_free_security)(structinode*inode);
int(*inode_init_security)(structinode*inode,structinode*dir,
char**name,void**value,size_t*len);
int(*inode_create)(structinode*dir,
structdentry*dentry,intmode);
int(*inode_link)(structdentry*old_dentry,
structinode*dir,structdentry*new_dentry);
int(*inode_unlink)(structinode*dir,structdentry*dentry);
int(*inode_symlink)(structinode*dir,
structdentry*dentry,constchar*old_name);
int(*inode_mkdir)(structinode*dir,structdentry*dentry,intmode);
int(*inode_rmdir)(structinode*dir,structdentry*dentry);
int(*inode_mknod)(structinode*dir,structdentry*dentry,
intmode,dev_tdev);
int(*inode_rename)(structinode*old_dir,structdentry*old_dentry,
structinode*new_dir,structdentry*new_dentry);
int(*inode_readlink)(structdentry*dentry);
int(*inode_follow_link)(structdentry*dentry,structnameidata*nd);
int(*inode_permission)(structinode*inode,intmask,structnameidata*nd);
int(*inode_setattr) (structdentry*dentry,structiattr*attr);
int(*inode_getattr)(structvfsmount*mnt,structdentry*dentry);
void(*inode_delete)(structinode*inode);
int(*inode_setxattr)(structdentry*dentry,char*name,void*value,
size_tsize,intflags);
void(*inode_post_setxattr)(structdentry*dentry,char*name,void*value,
size_tsize,intflags);
int(*inode_getxattr)(structdentry*dentry,char*name);
int(*inode_listxattr)(structdentry*dentry);
int(*inode_removexattr)(structdentry*dentry,char*name);
constchar*(*inode_xattr_getsuffix)(void);
int(*inod