admin 管理员组文章数量: 1087139
2024年4月18日发(作者:低代码编程工具)
SELinux源码分析(Federa Core 8)
第一章 SELinux
(Security Enhance Linux,简称SELinux)
简介
1.1 SELinux的起源
SELinux是一个面向政府和行业的产品,由NSA、Network Associates、Tresys以及其他组
织设计和开发。尽管NSA将其作为一个补丁集引入,但从2.6版开始,它就被加入到Linux
内核中。
GUN/Linux非常安全,但它也非常动态:所做的更改会为操作系统带来新的漏洞,这些
漏洞可能被攻击者利用,尽管人们都非常关心阻止授权访问,但是发生入侵后会发生什么
呢?
1.2访问控制
大多数操作系统使用访问控制来判断一个实体(用户或程序)是否能够访问给定资源。基
于 UNIX的系统使用一种自主访问控制(Discretionary Access Control,简称DAC)的形式。此方
法通常根据对象所属的分组来限制对对象的访问。例如,GNU/Linux 中的文件有一个所有者、
一个分组和一个权限集。权限定义谁可以访问给定文件、谁可以读取它、谁可
以向其写入,以及谁可以执行它。这些权限被划分到三个用户集中,分别表示用户(文件所
有者)、分组(一个用户组的所有成员)和其他(既不是文件所有者,又不是该分组的成员的所
有用户)。
很多这样的访问控制都会带来一个问题,因为所利用的程序能够继承用户的访问控制。
这样,该程序就可以在用户的访问层进行操作。与通过这种方式定义约束相比,使用最小特
权原则更安全,程序只能执行完成任务所需的操作。例如,如果一个程序用于响应 socket 请
求,但不需要访问文件系统,那么该程序应该能够监听给定的socket,但是不能访问文件系
统。通过这种方式,如果该程序被攻击者利用,其访问权限显然是最小的。这种控制类型称
为强制访问控制(MAC)。
另一种控制访问的方法是基于角色的访问控制(RBAC)。在 RBAC 中,权限是根据安全系
统所授予的角色来提供的。角色的概念与传统的分组概念不同,因为一个分组代表一个或多
个用户。一个角色可以代表多个用户,但它也代表一个用户集可以执行的权限。
SELinux 将 MAC 和 RBAC 都添加到了 GNU/Linux 操作系统中。下一节将探讨 SELinux
实现,以及如何将安全增强透明地添加到 Linux 内核中。
1.3 Linux安全模块(Linux Security Module,简称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.1e capabilities的一个子集(提供一部分钩子函数)提供支持。
Linux安全模块(LSM)设计的一个需求就是把这个功能移植为一个可选的安全模块。
POSIX.1e capabilities提供了划分传统超级用户特权并赋给特定的进程的功能,即传统超级
用户不允许的权限,可以通过POSIX.1e capabilities授予,大大提高了系统安全的灵活性。
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()。
Hook
int (*task_create) (unsigned long clone_flags);
int (*task_alloc_security) (struct task_struct * p);
void (*task_free_security) (struct task_struct * p);
int (*task_setuid) (uid_t id0, uid_t id1, uid_t id2, int flags);//特权操作的Hook
int (*task_post_setuid) (uid_t old_ruid /* or fsuid */ ,
uid_t old_euid, uid_t old_suid, int flags);
int (*task_setgid) (gid_t id0, gid_t id1, gid_t id2, int flags);
int (*task_setpgid) (struct task_struct * p, pid_t pgid);
int (*task_getpgid) (struct task_struct * p);
int (*task_getsid) (struct task_struct * p);
void (*task_getsecid) (struct task_struct * p, u32 * secid);
int (*task_setgroups) (struct group_info *group_info);
int (*task_setnice) (struct task_struct * p, int nice);//资源管理的Hook
int (*task_setioprio) (struct task_struct * p, int ioprio);
int (*task_getioprio) (struct task_struct * p);
int (*task_setrlimit) (unsigned int resource, struct rlimit * new_rlim);//资源管理的
int (*task_setscheduler) (struct task_struct * p, int policy,
struct sched_param * lp);
int (*task_getscheduler) (struct task_struct * p);
int (*task_movememory) (struct task_struct * p);
int (*task_kill) (struct task_struct * p,
struct siginfo * info, int sig, u32 secid);//控制进程间通信的Hook
int (*task_wait) (struct task_struct * p);
int (*task_prctl) (int option, unsigned long arg2,
unsigned long arg3, unsigned long arg4,
unsigned long arg5);
void (*task_reparent_to_init) (struct task_struct * p);
void (*task_to_inode)(struct task_struct *p, struct inode *inode);
图1.1 任务钩子
其次是程序装载钩子。很多安全模块,包括Linux capabilities,SELinux,DTE都需要
有在一个新程序执行时改变特权的能力。因此Linux安全模块(LSM)提供了一系列程序装
载钩子,用在一个execve()操作执行过程的关键点上。linux_binprm结构中的安全域允许安
全模块维护程序装载过程中的安全信息;提供了钩子用于允许安全模块在装载程序前初始化
安全信息和执行访问控制;还提供了钩子允许模块在新程序成功装载后更新任务的安全信
息;还提供了钩子用来控制程序执行过程中的状态继承,例如确认打开的文件描述符。(不
是可信启动)
int (*bprm_alloc_security) (struct linux_binprm * bprm);
void (*bprm_free_security) (struct linux_binprm * bprm);
void (*bprm_apply_creds) (struct linux_binprm * bprm, int unsafe);
void (*bprm_post_apply_creds) (struct linux_binprm * bprm);
int (*bprm_set_security) (struct linux_binprm * bprm);
int (*bprm_check_security) (struct linux_binprm * bprm);
int (*bprm_secureexec) (struct linux_binprm * bprm);
图1.2 程序装载钩子
再次是进程间通信IPC钩子。安全模块可以使用进程间通信IPC钩子来对System V
IPC的安全信息进行管理,以及执行访问控制。IPC对象数据结构共享一个子结构
kern_ipc_perm,并且这个子结构中只有一个指针传给现存的ipcperms()函数进行权限检查,
因此Linux安全模块(LSM)在这个共享子结构中加入了一个安全域。为了支持单个消息的
安全信息,Linux安全模块(LSM)还在msg_msg结构中加入了一个安全域。Linux安全
模块(LSM)在现存的ipcperms()函数中插入了一个钩子,使得安全模块可以对每个现存的
Linux IPC权限执行检查。由于对于某些安全模块,这样的检查是不足够的,Linux安全模
块(LSM)也在单个的IPC操作中插入了钩子。另外还有钩子支持对通过System V消息队
列发送的单个消息进行细粒度的访问控制。
int (*ipc_permission) (struct kern_ipc_perm * ipcp, short flag);
int (*msg_msg_alloc_security) (struct msg_msg * msg);
void (*msg_msg_free_security) (struct msg_msg * msg);
int (*msg_queue_alloc_security) (struct msg_queue * msq);
void (*msg_queue_free_security) (struct msg_queue * msq);
int (*msg_queue_associate) (struct msg_queue * msq, int msqflg);
int (*msg_queue_msgctl) (struct msg_queue * msq, int cmd);
int (*msg_queue_msgsnd) (struct msg_queue * msq,
struct msg_msg * msg, int msqflg);
int (*msg_queue_msgrcv) (struct msg_queue * msq,
struct msg_msg * msg,
struct task_struct * target,
long type, int mode);
图1.3 进程通信IPC钩子
下面是文件系统钩子。对于文件操作,定义了三种钩子:文件系统钩子,inode结点钩
子,以及文件钩子。Linux安全模块(LSM)在对应的三个内核数据结构中加入了安全域,
分别是:super_block结构,inode结构,file结构。超级块文件系统钩子使得安全模块能够
控制对整个文件系统进行的操作,例如挂载,卸载,还有statfs()。Linux安全模块(LSM)
在permission()函数中插入了钩子,从而保留了这个函数,但是也提供了很多其他inode结
点钩子来对单个inode结点操作进行细粒度访问控制。文件钩子中的一些允许安全模块对
read()和write()这样的文件操作进行额外的检查;还有文件钩子允许安全模块控制通过
socket IPC接收打开文件描述符;其他的文件钩子对像fcntl()和ioctl()这样的操作提供细粒
度访问控制。
int (*sb_alloc_security) (struct super_block * sb);
void (*sb_free_security) (struct super_block * sb);
int (*sb_copy_data)(struct file_system_type *type,
void *orig, void *copy);
int (*sb_kern_mount) (struct super_block *sb, void *data);
int (*sb_statfs) (struct dentry *dentry);
int (*sb_mount) (char *dev_name, struct nameidata * nd,
char *type, unsigned long flags, void *data);
int (*sb_check_sb) (struct vfsmount * mnt, struct nameidata * nd);
int (*sb_umount) (struct vfsmount * mnt, int flags);
void (*sb_umount_close) (struct vfsmount * mnt);
void (*sb_umount_busy) (struct vfsmount * mnt);
void (*sb_post_remount) (struct vfsmount * mnt,
unsigned long flags, void *data);
void (*sb_post_mountroot) (void);
void (*sb_post_addmount) (struct vfsmount * mnt,
struct nameidata * mountpoint_nd);
int (*sb_pivotroot) (struct nameidata * old_nd,
struct nameidata * new_nd);
void (*sb_post_pivotroot) (struct nameidata * old_nd,
struct nameidata * new_nd);
图1.4 super_block文件系统钩子
int (*inode_alloc_security) (struct inode *inode);
void (*inode_free_security) (struct inode *inode);
int (*inode_init_security) (struct inode *inode, struct inode *dir,
char **name, void **value, size_t *len);
int (*inode_create) (struct inode *dir,
struct dentry *dentry, int mode);
int (*inode_link) (struct dentry *old_dentry,
struct inode *dir, struct dentry *new_dentry);
int (*inode_unlink) (struct inode *dir, struct dentry *dentry);
int (*inode_symlink) (struct inode *dir,
struct dentry *dentry, const char *old_name);
int (*inode_mkdir) (struct inode *dir, struct dentry *dentry, int mode);
int (*inode_rmdir) (struct inode *dir, struct dentry *dentry);
int (*inode_mknod) (struct inode *dir, struct dentry *dentry,
int mode, dev_t dev);
int (*inode_rename) (struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry);
int (*inode_readlink) (struct dentry *dentry);
int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd);
int (*inode_permission) (struct inode *inode, int mask, struct nameidata *nd);
int (*inode_setattr) (struct dentry *dentry, struct iattr *attr);
int (*inode_getattr) (struct vfsmount *mnt, struct dentry *dentry);
void (*inode_delete) (struct inode *inode);
int (*inode_setxattr) (struct dentry *dentry, char *name, void *value,
size_t size, int flags);
void (*inode_post_setxattr) (struct dentry *dentry, char *name, void *value,
size_t size, int flags);
int (*inode_getxattr) (struct dentry *dentry, char *name);
int (*inode_listxattr) (struct dentry *dentry);
int (*inode_removexattr) (struct dentry *dentry, char *name);
const char *(*inode_xattr_getsuffix) (void);
int (*inode_getsecurity)(const struct inode *inode, const char *name, void *buffer,
size_t size, int err);
int (*inode_setsecurity)(struct inode *inode, const char *name, const void *value,
size_t size, int flags);
int (*inode_listsecurity)(struct inode *inode, char *buffer, size_t buffer_size);
图1.5 inode节点钩子
int (*file_permission) (struct file * file, int mask);
int (*file_alloc_security) (struct file * file);
void (*file_free_security) (struct file * file);
int (*file_ioctl) (struct file * file, unsigned int cmd,
unsigned long arg);
int (*file_mmap) (struct file * file,
unsigned long reqprot, unsigned long prot,
unsigned long flags, unsigned long addr,
unsigned long addr_only);
int (*file_mprotect) (struct vm_area_struct * vma,
unsigned long reqprot,
unsigned long prot);
int (*file_lock) (struct file * file, unsigned int cmd);
int (*file_fcntl) (struct file * file, unsigned int cmd,
unsigned long arg);
int (*file_set_fowner) (struct file * file);
int (*file_send_sigiotask) (struct task_struct * tsk,
struct fown_struct * fown, int sig);
int (*file_receive) (struct file * file);
图1.6 文件钩子
接下来是网络钩子。对网络的应用层访问使用一系列的socket套接字钩子来进行仲裁,
这些钩子基本覆盖了所有基于socket套接字的协议。由于每个激活的用户socket套接字有
伴随有一个inode结构,所以在socket结构或是更底层的sock结构中都没有加入安全域。
socket套接字钩子对有关进程的网络访问提供了一个通用的仲裁,从而显著扩展了内核的
网络访问控制框架(这在网络层是已经由Linux内核防火墙netfilter进行处理的)。例如
sock_rcv_skb钩子允许在进入内核的包排队到相应的用户空间socket套接字之前,按照其
目的应用来对其进行仲裁。另外Linux安全模块(LSM)也为IPv4,UNIX域,以及Netlink
协议实现了细粒度的钩子,以后还可能实现其他协议的钩子。网络数据以包的形式被封装在
sk_buff结构(socket套接字缓冲区)中游历协议栈,Linux安全模块(LSM)在sk_buff
结构中加入了一个安全域,使得能够在包的层次上对通过网络层的数据的安全信息进行管
理,并提供了一系列的sk_buff钩子用于维护这个安全域的整个生命周期。硬件和软件网络
设备被封装在一个net_device结构中,一个安全域被加到这个结构中,使得能够在设备的
层次上维护安全信息。
int (*socket_create) (int family, int type, int protocol, int kern);
int (*socket_post_create) (struct socket * sock, int family,
int type, int protocol, int kern);
int (*socket_bind) (struct socket * sock,
struct sockaddr * address, int addrlen);
int (*socket_connect) (struct socket * sock,
struct sockaddr * address, int addrlen);
int (*socket_listen) (struct socket * sock, int backlog);
int (*socket_accept) (struct socket * sock, struct socket * newsock);
void (*socket_post_accept) (struct socket * sock,
struct socket * newsock);
int (*socket_sendmsg) (struct socket * sock,
struct msghdr * msg, int size);
int (*socket_recvmsg) (struct socket * sock,
struct msghdr * msg, int size, int flags);
int (*socket_getsockname) (struct socket * sock);
int (*socket_getpeername) (struct socket * sock);
int (*socket_getsockopt) (struct socket * sock, int level, int optname);
int (*socket_setsockopt) (struct socket * sock, int level, int optname);
int (*socket_shutdown) (struct socket * sock, int how);
int (*socket_sock_rcv_skb) (struct sock * sk, struct sk_buff * skb);
int (*socket_getpeersec_stream) (struct socket *sock, char __user *optval, int
__user *optlen, unsigned len);
int (*socket_getpeersec_dgram) (struct socket *sock, struct sk_buff *skb, u32
*secid);
int (*sk_alloc_security) (struct sock *sk, int family, gfp_t priority);
void (*sk_free_security) (struct sock *sk);
void (*sk_clone_security) (const struct sock *sk, struct sock *newsk);
int (*unix_stream_connect) (struct socket * sock,
struct socket * other, struct sock * newsk);
int (*unix_may_send) (struct socket * sock, struct socket * other);
void (*sk_getsecid) (struct sock *sk, u32 *secid);
void (*sock_graft)(struct sock* sk, struct socket *parent);
int (*inet_conn_request)(struct sock *sk, struct sk_buff *skb,
struct request_sock *req);
void (*inet_csk_clone)(struct sock *newsk, const struct request_sock *req);
void (*inet_conn_established)(struct sock *sk, struct sk_buff *skb);
void (*req_classify_flow)(const struct request_sock *req, struct flowi *fl);
图1.7 网络钩子
最后是其他的钩子。Linux安全模块(LSM)提供了两种其他系列的钩子:模块钩子和
顶层的系统钩子。模块钩子用来控制创建,初始化,清除内核模块的内核操作。系统钩子用
来控制系统操作,例如设置主机名,访问I/O端口,以及配置进程记帐。虽然现在的Linux
内核通过使用capability检查对这些系统操作提供了一些支持,但是这些检查对于不同操作
差别很大并且没有提供任何参数信息。
1.5模块说明:给普通用户使用的现成的安全功能
Linux安全模块(LSM)对于普通用户的价值就在于:可以提供各种安全模块,由用户
选择适合自己需要加载到内核,满足特定的安全功能。Linux安全模块(LSM)本身只提供
增强访问控制策略的机制(只提供机制,策略由用户层提供),而由各个安全模块实现具体
特定的安全策略。下面简要介绍一些已经实现的安全模块。
SELinux,这是一个Flask灵活访问控制体系在Linux上的实现,并且提供了类型增强,
基于角色的访问控制,以及可选的多级安全策略。SELinux原来是作为一个内核补丁实现的,
现在已经使用Linux安全模块(LSM)重新实现为一个安全模块。SELinux可以被用来限制
进程为最小特权,保护进程和数据的完整性和机密性,并且支持应用安全需求。(内核补丁
和编译入内核模块,都是需要重新编译内核,是需要对内核进行修改,不是在内核启动后,
采用动态可加载的方式进行处理的)
DTE Linux。这是一个域和类型增强在Linux上的实现。就像SELinux一样,DTE Linux
原来是作为一个内核补丁实现的,现在已经使用Linux安全模块(LSM)重新实现为一个安
全模块。当这个安全模块被加载到内核上时,类型被赋给对象,域被赋给进程。DTE策略
限制域之间和从域到类型的访问。
Openwall 内核补丁的LSM移植。Openwall内核补丁提供了一系列的安全特性集合来
保护系统免受例如缓冲区溢出和临时文件竞争这样的攻击。有安全模块正在被开发出来以支
持Openwall补丁的一个子集。
POSIX.1e capabilities。Linux内核中已经存在有POSIX.1e capabilities逻辑,但是
Linux安全模块(LSM)把这个逻辑划分到了一个安全模块中。这样的修改使得不需要的用
户可以从他们的内核中把这个功能略去;也使得capabilities逻辑的开发可以脱离内核开发
获得更大的独立性。
LIDS。这是中国人谢华刚发起的项目。开始时作为一个入侵检测系统开发,后来逐渐
演变为使用访问控制系统的形式来进行入侵预防,它通过描述一个给定的程序可以访问哪些
文件来进行访问控制。同样的,LIDS原来是作为一个内核补丁实现的并附带了一些管理工
具,现在已经使用Linux安全模块(LSM)重新实现为一个安全模块。(安全模块的方式是
可加载的??)
当然还有缺省的传统超级用户机制。这个安全模块是Linux安全模块(LSM)缺省的,
实现了传统的UNIX超级用户特权机制。
第二章 LSM安全架构启动
2.1 LSM的注册层次
SELinux的出现着实扰乱了文件系统的进度,不过送算慢慢搞清楚了其中的来龙去脉。
下面将通过2.6.20内核中的security代码进行一番简单的分析。
初次接触LSM时,其间复杂的模块加载方式和顺序很是让人头疼。光其中的ops操作
就有security_ops, capability_ops, secondary_ops, selinux_ops, rootplug_ops, selinux_ops,
original_ops,再有init函数security_init, capability_init, rootplug_init, selinux_init中的注册关
系register_secrity反反复复的来回赋值,最后究竟ops操作都是什么,已经成了一团乱麻。
为此,不得不从系统的启动过程开始,寻找其间的因果顺序,事情的经过是这样的:LSM系
统的初始化发生在系统内核初始化阶段,在src/init/main.c的start_kernel()里,其位置如
下所示:
fork_init(num_physpages);
proc_caches_init();
buffer_init();
unnamed_dev_init();
key_init();
security_init();
vfs_caches_init(num_physpages);
radix_tree_init();
signals_init();
图 2.1
在unnamed_dev_init(), key_init()之后,vfs_caches_init之前,其在内核中的位置层次也
基本上如此。
security_init()的具体实现在/src/security/security.c中,
/**
* 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 v" SECURITY_FRAMEWORK_VERSION
" initializedn");
if (verify(&dummy_security_ops)) {
printk(KERN_ERR "%s could not verify "
"dummy_security_ops structure.n", __FUNCTION__);
}
return -EIO;
}
security_ops = &dummy_security_ops;
do_security_initcalls();
return 0;
图 2.2
在此,很有必要了解一下dummy_security_ops的定义,在src/security/dummy.c中,给
出了其定义。
struct security_operations dummy_security_ops;
定义后的dummy_security_ops并没有初始化,也就是说,它是security_operations的一
个结构,该结构里是一系列的指针,每个指针都指向一个函数,而这些函数,就是security
框架所能覆盖的领域,通过修改函数指针,可以达到为原先的系统通过钩子增加一个安全过
滤层的目的。即,系统的这些调用,先通过你设计的函数过滤相关操作,再有你的函数调用
原先的实现,实现增加一层的目的,这和NT中注册操作的回调函数相似。
下面是security_operations的定义,它没有数据成员,所有的成员都是函数指针:
struct security_operations {
int (*ptrace) (struct task_struct * parent, struct task_struct * child);
int (*capget) (struct task_struct * target,
kernel_cap_t * effective,
kernel_cap_t * inheritable, kernel_cap_t * permitted);
int (*capset_check) (struct task_struct * target,
kernel_cap_t * effective,
kernel_cap_t * inheritable,
kernel_cap_t * permitted);
void (*capset_set) (struct task_struct * target,
kernel_cap_t * effective,
kernel_cap_t * inheritable,
kernel_cap_t * permitted);
int (*capable) (struct task_struct * tsk, int cap);
int (*acct) (struct file * file);
int (*sysctl) (struct ctl_table * table, int op);
int (*quotactl) (int cmds, int type, int id, struct super_block * sb);
int (*quota_on) (struct dentry * dentry);
int (*syslog) (int type);
int (*settime) (struct timespec *ts, struct timezone *tz);
int (*vm_enough_memory) (long pages);
int (*bprm_alloc_security) (struct linux_binprm * bprm);
void (*bprm_free_security) (struct linux_binprm * bprm);
void (*bprm_apply_creds) (struct linux_binprm * bprm, int unsafe);
void (*bprm_post_apply_creds) (struct linux_binprm * bprm);
int (*bprm_set_security) (struct linux_binprm * bprm);
int (*bprm_check_security) (struct linux_binprm * bprm);
int (*bprm_secureexec) (struct linux_binprm * bprm);
int (*sb_alloc_security) (struct super_block * sb);
void (*sb_free_security) (struct super_block * sb);
int (*sb_copy_data)(struct file_system_type *type,
void *orig, void *copy);
int (*sb_kern_mount) (struct super_block *sb, void *data);
int (*sb_statfs) (struct dentry *dentry);
int (*sb_mount) (char *dev_name, struct nameidata * nd,
char *type, unsigned long flags, void *data);
int (*sb_check_sb) (struct vfsmount * mnt, struct nameidata * nd);
int (*sb_umount) (struct vfsmount * mnt, int flags);
void (*sb_umount_close) (struct vfsmount * mnt);
void (*sb_umount_busy) (struct vfsmount * mnt);
void (*sb_post_remount) (struct vfsmount * mnt,
unsigned long flags, void *data);
void (*sb_post_mountroot) (void);
void (*sb_post_addmount) (struct vfsmount * mnt,
struct nameidata * mountpoint_nd);
int (*sb_pivotroot) (struct nameidata * old_nd,
struct nameidata * new_nd);
void (*sb_post_pivotroot) (struct nameidata * old_nd,
struct nameidata * new_nd);
int (*inode_alloc_security) (struct inode *inode);
void (*inode_free_security) (struct inode *inode);
int (*inode_init_security) (struct inode *inode, struct inode *dir,
char **name, void **value, size_t *len);
int (*inode_create) (struct inode *dir,
struct dentry *dentry, int mode);
int (*inode_link) (struct dentry *old_dentry,
struct inode *dir, struct dentry *new_dentry);
int (*inode_unlink) (struct inode *dir, struct dentry *dentry);
int (*inode_symlink) (struct inode *dir,
struct dentry *dentry, const char *old_name);
int (*inode_mkdir) (struct inode *dir, struct dentry *dentry, int mode);
int (*inode_rmdir) (struct inode *dir, struct dentry *dentry);
int (*inode_mknod) (struct inode *dir, struct dentry *dentry,
int mode, dev_t dev);
int (*inode_rename) (struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry);
int (*inode_readlink) (struct dentry *dentry);
int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd);
int (*inode_permission) (struct inode *inode, int mask, struct nameidata *nd);
int (*inode_setattr) (struct dentry *dentry, struct iattr *attr);
int (*inode_getattr) (struct vfsmount *mnt, struct dentry *dentry);
void (*inode_delete) (struct inode *inode);
int (*inode_setxattr) (struct dentry *dentry, char *name, void *value,
size_t size, int flags);
void (*inode_post_setxattr) (struct dentry *dentry, char *name, void *value,
size_t size, int flags);
int (*inode_getxattr) (struct dentry *dentry, char *name);
int (*inode_listxattr) (struct dentry *dentry);
int (*inode_removexattr) (struct dentry *dentry, char *name);
const char *(*inode_xattr_getsuffix) (void);
int (*inode_getsecurity)(const struct inode *inode, const char *name, void *buffer, size_t
size, int err);
int (*inode_setsecurity)(struct inode *inode, const char *name, const void *value, size_t
size, int flags);
int (*inode_listsecurity)(struct inode *inode, char *buffer, size_t buffer_size);
int (*file_permission) (struct file * file, int mask);
int (*file_alloc_security) (struct file * file);
void (*file_free_security) (struct file * file);
int (*file_ioctl) (struct file * file, unsigned int cmd,
unsigned long arg);
int (*file_mmap) (struct file * file,
unsigned long reqprot,
unsigned long prot, unsigned long flags);
int (*file_mprotect) (struct vm_area_struct * vma,
unsigned long reqprot,
unsigned long prot);
int (*file_lock) (struct file * file, unsigned int cmd);
int (*file_fcntl) (struct file * file, unsigned int cmd,
unsigned long arg);
int (*file_set_fowner) (struct file * file);
int (*file_send_sigiotask) (struct task_struct * tsk,
struct fown_struct * fown, int sig);
int (*file_receive) (struct file * file);
int (*task_create) (unsigned long clone_flags);
int (*task_alloc_security) (struct task_struct * p);
void (*task_free_security) (struct task_struct * p);
int (*task_setuid) (uid_t id0, uid_t id1, uid_t id2, int flags);
int (*task_post_setuid) (uid_t old_ruid /* or fsuid */ ,
uid_t old_euid, uid_t old_suid, int flags);
int (*task_setgid) (gid_t id0, gid_t id1, gid_t id2, int flags);
int (*task_setpgid) (struct task_struct * p, pid_t pgid);
int (*task_getpgid) (struct task_struct * p);
int (*task_getsid) (struct task_struct * p);
void (*task_getsecid) (struct task_struct * p, u32 * secid);
int (*task_setgroups) (struct group_info *group_info);
int (*task_setnice) (struct task_struct * p, int nice);
int (*task_setioprio) (struct task_struct * p, int ioprio);
int (*task_getioprio) (struct task_struct * p);
int (*task_setrlimit) (unsigned int resource, struct rlimit * new_rlim);
int (*task_setscheduler) (struct task_struct * p, int policy,
struct sched_param * lp);
int (*task_getscheduler) (struct task_struct * p);
int (*task_movememory) (struct task_struct * p);
int (*task_kill) (struct task_struct * p,
struct siginfo * info, int sig, u32 secid);
int (*task_wait) (struct task_struct * p);
int (*task_prctl) (int option, unsigned long arg2,
unsigned long arg3, unsigned long arg4,
unsigned long arg5);
void (*task_reparent_to_init) (struct task_struct * p);
void (*task_to_inode)(struct task_struct *p, struct inode *inode);
int (*ipc_permission) (struct kern_ipc_perm * ipcp, short flag);
int (*msg_msg_alloc_security) (struct msg_msg * msg);
void (*msg_msg_free_security) (struct msg_msg * msg);
int (*msg_queue_alloc_security) (struct msg_queue * msq);
void (*msg_queue_free_security) (struct msg_queue * msq);
int (*msg_queue_associate) (struct msg_queue * msq, int msqf
int (*msg_queue_msgctl) (struct msg_queue * msq, int cmd);
int (*msg_queue_msgsnd) (struct msg_queue * msq,
struct msg_msg * msg, int msqflg);
int (*msg_queue_msgrcv) (struct msg_queue * msq,
struct msg_msg * msg,
struct task_struct * target,
long type, int mode);
int (*shm_alloc_security) (struct shmid_kernel * shp);
void (*shm_free_security) (struct shmid_kernel * shp);
int (*shm_associate) (struct shmid_kernel * shp, int shmflg);
int (*shm_shmctl) (struct shmid_kernel * shp, int cmd);
int (*shm_shmat) (struct shmid_kernel * shp,
char __user *shmaddr, int shmflg);
int (*sem_alloc_security) (struct sem_array * sma);
void (*sem_free_security) (struct sem_array * sma);
int (*sem_associate) (struct sem_array * sma, int semflg);
int (*sem_semctl) (struct sem_array * sma, int cmd);
int (*sem_semop) (struct sem_array * sma,
struct sembuf * sops, unsigned nsops, int alter);
int (*netlink_send) (struct sock * sk, struct sk_buff * skb);
int (*netlink_recv) (struct sk_buff * skb, int cap);
/* allow module stacking */
int (*register_security) (const char *name,
struct security_operations *ops);
int (*unregister_security) (const char *name,
struct security_operations *ops);
void (*d_instantiate) (struct dentry *dentry, struct inode *inode);
int (*getprocattr)(struct task_struct *p, char *name, void *value, size_t size);
int (*setprocattr)(struct task_struct *p, char *name, void *value, size_t size);
int (*secid_to_secctx)(u32 secid, char **secdata, u32 *seclen);
void (*release_secctx)(char *secdata, u32 seclen);
#ifdef CONFIG_SECURITY_NETWORK
int (*unix_stream_connect) (struct socket * sock,
struct socket * other, struct sock * newsk);
int (*unix_may_send) (struct socket * sock, struct socket * other);
int (*socket_create) (int family, int type, int protocol, int kern);
int (*socket_post_create) (struct socket * sock, int family,
int type, int protocol, int kern);
int (*socket_bind) (struct socket * sock,
struct sockaddr * address, int addrlen);
int (*socket_connect) (struct socket * sock,
struct sockaddr * address, int addrlen);
int (*socket_listen) (struct socket * sock, int backlog);
int (*socket_accept) (struct socket * sock, struct socket * newsock);
void (*socket_post_accept) (struct socket * sock,
struct socket * newsock);
int (*socket_sendmsg) (struct socket * sock,
struct msghdr * msg, int size);
int (*socket_recvmsg) (struct socket * sock,
struct msghdr * msg, int size, int flags);
int (*socket_getsockname) (struct socket * sock);
int (*socket_getpeername) (struct socket * sock);
int (*socket_getsockopt) (struct socket * sock, int level, int optname);
int (*socket_setsockopt) (struct socket * sock, int level, int optname);
int (*socket_shutdown) (struct socket * sock, int how);
int (*socket_sock_rcv_skb) (struct sock * sk, struct sk_buff * skb);
int (*socket_getpeersec_stream) (struct socket *sock, char __user *optval, int __user
*optlen, unsigned len);
int (*socket_getpeersec_dgram) (struct socket *sock, struct sk_buff *skb, u32 *secid);
int (*sk_alloc_security) (struct sock *sk, int family, gfp_t priority);
void (*sk_free_security) (struct sock *sk);
void (*sk_clone_security) (const struct sock *sk, struct sock *newsk);
void (*sk_getsecid) (struct sock *sk, u32 *secid);
void (*sock_graft)(struct sock* sk, struct socket *parent);
int (*inet_conn_request)(struct sock *sk, struct sk_buff *skb,
struct request_sock *req);
void (*inet_csk_clone)(struct sock *newsk, const struct request_sock *req);
void (*inet_conn_established)(struct sock *sk, struct sk_buff *skb);
void (*req_classify_flow)(const struct request_sock *req, struct flowi *fl);
#endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_NETWORK_XFRM
int (*xfrm_policy_alloc_security) (struct xfrm_policy *xp,
struct xfrm_user_sec_ctx *sec_ctx);
int (*xfrm_policy_clone_security) (struct xfrm_policy *old, struct xfrm_policy *new);
void (*xfrm_policy_free_security) (struct xfrm_policy *xp);
int (*xfrm_policy_delete_security) (struct xfrm_policy *xp);
int (*xfrm_state_alloc_security) (struct xfrm_state *x,
struct xfrm_user_sec_ctx *sec_ctx,
u32 secid);
void (*xfrm_state_free_security) (struct xfrm_state *x);
int (*xfrm_state_delete_security) (struct xfrm_state *x);
int (*xfrm_policy_lookup)(struct xfrm_policy *xp, u32 fl_secid, u8 dir);
int (*xfrm_state_pol_flow_match)(struct xfrm_state *x,
struct xfrm_policy *xp, struct flowi *fl);
int (*xfrm_decode_session)(struct sk_buff *skb, u32 *secid, int ckall);
#endif /* CONFIG_SECURITY_NETWORK_XFRM */
/* key management security hooks */
#ifdef CONFIG_KEYS
int (*key_alloc)(struct key *key, struct task_struct *tsk, unsigned long flags);
void (*key_free)(struct key *key);
int (*key_permission)(key_ref_t key_ref,
struct task_struct *context,
key_perm_t perm);
#endif /* CONFIG_KEYS */
};
dummy_security_ops的初始化是在上面给出的security_init中
verify(&dummy_security_ops)函数实现的,它将参数XXX_ops中的所有空指针的函数,初始
化为dummy.c中定义的dummy_XXX类型对应函数体,每个函数体只提供一个表示成功的返
回值(即所有的检查都通过,不对原来的执行过程实行任何影响),而不执行任何操作。具
体参见src/security/dummy.c。
dummy_security_ops初始化完成后,通过security_ops=&dummy_security_ops,使得
security_ops指向一个不做任何操作的过滤层。再通过do_security_initcalls()加载具体配置的
安全过滤模块。
do_security_initcalls()函数的实现:
static void __init do_security_initcalls(void)
{
initcall_t *call;
call = __security_initcall_start;
while (call < __security_initcall_end) {
(*call) ();
call++;
}
}
在系统中,security_的函数总共有三个,分别为:capability_init(), rootplug_init()
和selinux_init()。其中,selinux只能第一个注册(即作为security_ops上的第一个过滤层),
否则注册会失败。selinux注册后,original_ops=secondary_ops=dummy_ops;
securuty_ops=selinux_ops。前两者的注册方式完全一致,并且,在当前代码实现中,两者只
能注册一个,另外一个在第一个注册成功后,即使以模块方式加载也会失败。例如,
capability_init()注册后,secondary_ops=capability_ops; original_ops=dummy_ops;
security_ops=selinux_ops。此时如果再调用rootplug_init(),将因为
security_ops!=dummy_security_ops而调用register_security失败,然后因为secondary_ops !=
original_ops而在调用security_ops->register_security即selinux_register_security函数中返回
出错,原因为几经存在了一个第二层模块。这表示do_security_initcalls函数中的while循环
只执行两次。具体代码不在这里列出,有兴趣者可以到src/security/目录下分别查看
capability.c, root_plug.c和selinux/hooks.c。
因此可见,主要得代码实现都是在selinux中实现的。以我所感兴趣的selinux_mount
和selinux_umount为例,其代码如下:
static int selinux_mount(char * dev_name,
struct nameidata *nd,
char * type,
unsigned long flags,
void * data)
{
int rc;
rc = secondary_ops->sb_mount(dev_name, nd, type, flags, data);
if (rc)
return rc;
if (flags & MS_REMOUNT)
return superblock_has_perm(current, nd->mnt->mnt_sb,
FILESYSTEM__REMOUNT, NULL);
else
return dentry_has_perm(current, nd->mnt, nd->dentry,
FILE__MOUNTON);
}
static int selinux_umount(struct vfsmount *mnt, int flags)
{
int rc;
}
rc = secondary_ops->sb_umount(mnt, flags);
if (rc)
return rc;
return superblock_has_perm(current,mnt->mnt_sb,
FILESYSTEM__UNMOUNT,NULL);
由上面的ops赋值关系可知,secondary_ops->sb_mount和secondary_ops->sb_umount
无论capability和root_plug是否加载,因为其在这两者中都没有定义实现,所以,为
dummy_XXX函数,相关的代码如下:
static int dummy_sb_mount (char *dev_name, struct nameidata *nd, char *type,
unsigned long flags, void *data)
{
return 0;
}
static int dummy_sb_check_sb (struct vfsmount *mnt, struct nameidata *nd)
{
return 0;
}
static int dummy_sb_umount (struct vfsmount *mnt, int flags)
{
return 0;
}
static void dummy_sb_umount_close (struct vfsmount *mnt)
{
return;
}
static void dummy_sb_umount_busy (struct vfsmount *mnt)
{
return;
}
static void dummy_sb_post_remount (struct vfsmount *mnt, unsigned long flags,
void *data)
{
return;
}
static void dummy_sb_post_mountroot (void)
{
return;
}
然后给出一定的pemmision的判断。permmision判断通过查找avc的hash表实现,它
是以数据库的形式在内核维护的。在selinux中,ss目录下的文件负责维护security所有安
全检查需要的数据库,对于数据库的详细操作和保存和加载,请参考
src/security/selinux/ss/policydb.c中的相关函数。
2.2 LSM Hooks
上一章已经对LSM安全模块的注册做了很详细的介绍。下面对LSM Hooks的函数功能
做详细的介绍,因为我们的主要工作是在该系统上添加新的功能,必须把该系统的功能分析
清楚。而LSM系统的最重要的部分就是Hook函数。因为要加强整个系统的安全,从可执行
文件的启动,到进程的运行,再到进程对文件等资源的访问,直到进程的结束。
系统调用为用户空间提供了内核的一个抽象的接口,可以通过截获系统调用来实施访问
控制。实际上,2.6版本的内核是不允许内核模块重写系统调用表。截获系统调用表是需要
付出代价的,需要代码复制,可能不能准确的表达上下文所需要的安全策略决策。LSM的策
略执行点刚好在对象被访问之前。如果访问被拒绝,就会强制返回一个错误码。在内核底层
进行截获,可以访问整个内核空间,能够更加细粒度的进行访问控制。具体的访问过程如下
图所示:
图2.3 LSM Hooks访问过程
从上图所示,LSM Hooks的策略检查是在内核的底层,在标准linux的安全检查之后。
2.3 相关的内核数据结构
LSM Hooks的加入,对内核中关键的数据结构都有部分的修改,具体如下图所示,左边
的表示内核中的数据结构,右边是对象,内核中的数据结构和对象都是相对应的。为了能够
更好的了解LSM Hooks的执行原理和功能,我们首先要了解一下内核中的关键数据结构。本
节的主要内容是对LSM Hooks修改的内核数据结构,以及LSM Hooks函数中所使用到的内核
数据结构做一个简要的介绍。
图 2.5
2.3.1 Task_struct(进程结构)
在操作系统中,数据结构是代表不同的实体,为了管理进程,操作系统必须对每个进程
所作的事情进行清楚的描述。在Linux系统中,每个进程都由Task_struct数据结构来定义,
task_struct就是我们通常所说的PCB(Process Control Block,简称PCB)。它包含了进程的
所有信息,在任何时候操作系统都能跟踪这个结构的信息,这个结构是linux内核汇总最重
要的数据结构。
它是对进程控制的唯一手段,也是最有效的手段。当我们调用fork()时,系统会为我们
产生一个task_struct结构。然后从父进程那里继承一些数据,并把新的进程插入到进程树中,
以待进行进程管理。在task_struct中包括以下信息:1.进程状态,记录进程在等待,运行,
或死锁;2.调度信息,由哪个调度函数调度,怎样调度等;3.进程的通信状况;4.进程链接
信息,因为要插入进程树,必须有联系父子兄弟指针;5.时间信息,比如计算好执行的时间,
以便cpu分配;6.标号,决定改变进程归属;7.可以读写打开的一些文件信息;8.进程上下
文和内核上下文;9.处理器上下文;10.内存信息,因为每个PCB都是这样的,只有这些结
构,才能满足一个进程所有要求。
2.3.2 Linux_binprm
在装入二进制是需要用到结构linux_binprm,这个结构保存着一些装入代码时需要的信
息:
详细的定义如下:
struct linux_binprm{
char buf[128];/*读入文件时用的缓冲区*/
unsigned long page[MAX_ARG_PAGES];//页面数组指针 MAX_ARG_PAGES定义为32
unsigned long p; //最大参数个数所占用的空间
int sh_bang; //表示可执行文件的性质,当可执行文件时一个shell脚本是置为1
struct inode * inode;/*映像来自的节点*/即二进制文件的inode节点
int e_uid, e_gid;
int argc, envc; /*参数数目,环境数目*/
char * filename; /* 二进制映像的名字,也就是要执行的文件名 */
unsigned long loader, exec;
int dont_iput; /* binfmt handler has put inode */
};
其它域的含义在后面的do_exec()代码中做进一步解释。
Linux所提供的系统调用名为execve(),可是,C语言的程序库在此系统调用的基础上
向应用程序提供了一整套的库函数,包括execve()、execlp()、execle()、execv()、
execvp(),它们之间的差异仅仅是参数的不同。下面来介绍execve()的实现。
系统调用execve()在内核的入口为sys_execve(),其代码在
arch/i386/kernel/process.c:
2.3.3 Super_block(超级块)
超级块(superblock)中包含的信息描述了文件系统的布局。和引导块一样,超级块的
大小也是固定的1024字节。超级块的主要功能是给出文件系统各个部分的大小。如果给定
块大小和i节点数,我们可以很容易地算出i节点位图和存放i节点所需的块数。例如,假
设块大小为1KB,每个位图块有1024字节(8192位),可以记录8192个i节点的状态(实
际上第一块只能处理8191个i节点,因为第0号i节点并不存在,但我们在位图中也会为它
保留一位)。如果有10000个i节点,则需要用到两个位图块。由于每个i节点要占用64个
字节,因此1KB的块最多能存放16个i节点。如果有64个i节点,则需要4个磁盘块来存
储。
启动文件系统时,根设备中的超级块会被读入内存。同样,当挂装其他的文件系统时,
它们的超级块也会被读入内存。在内存的超级快中,有些字段是磁盘上的超级块所没有的,
如设备的只读标志和字节顺序标志位、位图中第一个空闲位的位置(用于加速位图的访问)
以及该超级块来自于哪一个设备等。
每个文件系统只有一个超级块,它是对文件系统总体信息的描述。
Super_block数据结构:
struct super_block {
struct list_head s_list; /* Keep this first */指向超级块链表的指针
dev_t s_dev; /* search index; _not_ kdev_t */设备标识符
unsigned long s_blocksize;//以字节为单位的块大小
unsigned char s_blocksize_bits;//以位为单位的块大小
unsigned char s_dirt;//修改脏标志
unsigned long long s_maxbytes; /* 文件大小的上限*/
struct file_system_type *s_type;//文件系统的类型
const struct super_operations *s_op;//超级块方法
struct dquot_operations *dq_op;//磁盘限额方法
struct quotactl_ops *s_qcop;//限额控制方法
struct export_operations *s_export_op;//导出方法
unsigned long s_flags;//挂载标志
unsigned long s_magic;//文件系统魔数
struct dentry *s_root;//目录挂载点
struct rw_semaphore s_umount;//卸载信号量
struct mutex s_lock;//超级块信号量
int s_count;//引用计数
int s_syncing;//文件系统同步标志
int s_need_sync_fs;//尚未同步标志
atomic_t s_active;//活动引用计数
#ifdef CONFIG_SECURITY
void *s_security;//super_block的安全域
#endif
struct xattr_handler **s_xattr;//
struct list_head s_inodes; /* 所有的inode */
struct list_head s_dirty; /* 脏节点链表 */
struct list_head s_io; /* 回写链表 */
struct hlist_head s_anon; /* 匿名目录项anonymous dentries for (nfs)
exporting */
struct list_head s_files;//被分配文件链表
struct block_device *s_bdev;//相关设备
struct mtd_info *s_mtd;
struct list_head s_instances;//该类型文件系统
struct quota_info s_dquot; /* 限额相关选项Diskquota specific options */
int s_frozen;
wait_queue_head_t s_wait_unfrozen;
char s_id[32];
void
/* Informational name */
/*文件系统特殊信息 */ *s_fs_info;
/*
* The next field is for VFS *only*. No filesystems have any business
* even looking at it. You had been warned.
*/
struct mutex s_vfs_rename_mutex; /* 重命名信号量Kludge */
/* Granularity of c/m/atime in ns.
Cannot be worse than a second */
u32 s_time_gran;
/*
* Filesystem subtype. If non-empty the filesystem type field
* in /proc/mounts will be "e"
*/
char *s_subtype;//文件系统的子版本号
};
由前面所阐述的内容,可以得出在每个文件系统中,超级块有且只能有一个,大小为1024
个字节。超级块中剩余的空间不能用来做存储。
2.3.4 Inode(索引节点)
文件的i节点的主要功能是给出文件数据块所在的位置,存放关于文件的一般信息。在
打开一个文件时,先找到它的i节点,并把它装入到内存的inode表中,而且它会一直呆在
那儿,直至文件被关闭。在内存中的inode表中有一些字段是磁盘i节点所没有的,如i节
点所在的设备。这样,当该i节点被修改后,文件系统才知道应该把它写会到什么地方。
struct inode {
struct hlist_node i_hash;
struct list_head i_list;
struct list_head i_sb_list;
struct list_head i_dentry;
unsigned long i_ino;
atomic_t i_count;
unsigned int i_nlink;
uid_t i_uid;
gid_t i_gid;
dev_t i_rdev;
unsigned long i_version;
loff_t i_size;
#ifdef __NEED_I_SIZE_ORDERED
seqcount_t i_size_seqcount;
#endif
struct timespec i_atime;
struct timespec i_mtime;
struct timespec i_ctime;
unsigned int i_blkbits;
blkcnt_t i_blocks;
unsigned short i_bytes;
umode_t i_mode;
spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */
struct mutex i_mutex;
struct rw_semaphore i_alloc_sem;
const struct inode_operations *i_op;
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
struct super_block *i_sb;
struct file_lock *i_flock;
struct address_space *i_mapping;
struct address_space i_data;
#ifdef CONFIG_QUOTA
struct dquot *i_dquot[MAXQUOTAS];
#endif
struct list_head i_devices;
union {
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
struct cdev *i_cdev;
};
int i_cindex;
__u32 i_generation;
#ifdef CONFIG_DNOTIFY
unsigned long i_dnotify_mask; /* Directory notify events */
struct dnotify_struct *i_dnotify; /* for directory notifications */
#endif
#ifdef CONFIG_INOTIFY
struct list_head inotify_watches; /* watches on this inode */
struct mutex inotify_mutex; /* protects the watches list */
#endif
unsigned long i_state;
unsigned long dirtied_when; /* jiffies of first dirtying */
unsigned int i_flags;
atomic_t i_writecount;
#ifdef CONFIG_SECURITY
void
#endif
void
};
*i_security;//安全域结构指针
*i_private; /* fs or device private pointer */
2.3.5 File(文件)
文件对象描述进程怎样与一个打开的文件进行交互。文件对象是在文件被打开时创建
的,由一个file结构组成,其中包含的字段如下图所示。注意,文件对象在磁盘上没有对应
的映像,因此file结构中没有设置“脏”字段来表示文件对象是否已被修改。
存放在文件对象中的主要信息是文件指针,即文件中当前的位置,下一个操作将在该位
置发生。由于几个进程可能同时访问同一个文件,因此文件指针必须存放在文件对象而不是
索引节点对象中。
struct file {
union {
struct list_head fu_list;
struct rcu_head fu_rcuhead;
} f_u; //用于通用文件对象链表的指针
struct path f_path;
#define f_dentry f_//与文件相关的目录对象
#define f_vfsmnt f_//含有该文件的已安装文件系统
const struct file_operations *f_op;//指向文件操作表的指针
atomic_t f_count;//文件对象的引用计数器
unsigned int f_flags;//当打开文件时所指定的标志
mode_t f_mode;//进程访问模式
loff_t f_pos;//当前的文件位移
struct fown_struct f_owner;//通过信号进行I/O事件通知的数据
unsigned int f_uid, f_gid;//用户的UID和用户的GID
struct file_ra_state f_ra;//文件预读状态
unsigned long f_version;//版本号,每次使用后自动递增
#ifdef CONFIG_SECURITY
void *f_security;//指向文件对象的安全指针
#endif
void *private_data;//指向特定文件系统或设备驱动程序所需要的数据的
指针
#ifdef CONFIG_EPOLL
struct list_head f_ep_links;//文件的时间轮询等待者链表的头
spinlock_t f_ep_lock;//保护f_ep_links链表的自旋锁
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;//指向文件地址空间对象的指针
};
2.3.6 dentry(目录项)(《深入理解LINUX内核》第474页)
存放目录项(也就是文件的特定名称)与对应文件进行链接的有关信息。每个磁盘文件
系统都以自己特有的方式将该类信息存在磁盘上。《深入理解LINUX内核》第474页。
struct dentry {
atomic_t d_count;//引用计数器
unsigned int d_flags; /* 目录项高速缓存标志*/
spinlock_t d_lock; /* 保护目录项的自旋锁*/
struct inode *d_inode; /* 与文件名关联的索引节点 */
struct hlist_node d_hash; /* lookup hash list */
struct dentry *d_parent; /* 父目录的目录项对象*/
struct qstr d_name;//文件名
struct list_head d_lru; /* 用于未使用目录项链表指针 */
union {
*/
struct list_head d_child; /*对目录而言,用于同一父目录中的目录项链表指针
struct rcu_head d_rcu;//回收目录项对象时,由RCU描述符使用
} d_u;
struct list_head d_subdirs; /* 对目录而言,子目录项链表的头 */
struct list_head d_alias; /*用于与同一索引节点(别名)相关的目录项链表指针 */
unsigned long d_time; /* 由d_revalidate方法使用 */
struct dentry_operations *d_op;//目录项方法
struct super_block *d_sb; /*文件的超级块对象 */
void *d_fsdata; /*依赖于文件系统的数据 */
#ifdef CONFIG_PROFILING
struct dcookie_struct *d_cookie; /* 指向内核配置文件使用的数据结构指针 */
#endif
};
int d_mounted;//对目录而言,用于记录安装该目录项的文件系统数的计数器
unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* 存放短文件名的空间*/
2.3.7 Sk_buff(套接字缓冲区)
套接字缓冲区用结构体struct sk_buff表示,它用于在网络子系统中的各层之间传递数
据,处于一个核心地位,非常之重要。
《Linux设备驱动程序》第三版第522页
2.3.8 Net_device
Net_device结构位于网络驱动程序层的最核心地位。Net_device数据结构的完整描述在
《Linux设备驱动程序》第三版499页。
2.3.9 Kern_ipc_perm(《深入理解LINUX内核》第783页)
消息队列中,重要的数据结构:
struct kern_ipc_perm
{
};
spinlock_t lock;//保护IPC资源描述符的自旋锁
int deleted;//如果资源已被释放,则设置该标志
key_t key;//IPC的关键字
uid_t uid;//属主用户ID
gid_t gid;//属主组ID
uid_t cuid;//创建者用户ID
gid_t cgid;//创建者组ID
mode_t mode; //许可权掩码
unsigned long seq;//位置使用序号
void *security;//安全结构指针
2.3.10 Msg_msg(《深入理解LINUX内核》第791页)
每一个消息都有一个msg_msg结构与之对应
struct msg_msg {
};
struct list_head m_list; //用于消息链表的指针
long m_type; //消息类型
int m_ts; /*消息正文的大小*/
struct msg_msgseg* next;//消息的下一部分
void *security;//安全数据结构指针
2.4 安全结构
在介绍Hooks中介绍。
2.5 Hooks函数
2.5.1 Task Hooks
要详细了解Task Hooks,必须对进程管理有所了解。下面会对进程管理进行简短的介
绍。
进程在操作系统中执行特定的任务。而程序是存储在磁盘上包含可执行机器指令和数据
的静态实体。进程或者任务是处于活动状态的计算机程序。
进程是一个随执行过程不断变化的实体。和程序要包含指令和数据一样,进程也包含程
序计数器和所有CPU寄存器的值,同时它的堆栈中存储着如子程序参数、返回地址以及变
量之类的临时数据。当前的执行程序,或者说进程,包含着当前处理器中的活动状态。Linux
是一个多处理操作系统。进程具有独立的权限与职责。如果系统中某个进程崩溃,它不会影
响到其余的进程。每个进程运行在其各自的虚拟地址空间中,通过核心控制下可靠的通讯机
制,它们之间才能发生联系。
进程在生命期内将使用系统中的资源。它利用系统中的CPU来执行指令,在物理内存
来放置指令和数据。使用文件系统提供的功能打开并使用文件,同时直接或者间接的使用物
理设备。Linux必须跟踪系统中每个进程以及资源,以便在进程间实现资源的公平分配。如
果系统有一个进程独占了大部分物理内存或者CPU的使用时间,这种情况对系统中的其它
进程是不公平的。
系统中最宝贵的资源是CPU,通常系统中只有一个CPU。Linux是一个多处理操作系统,
它最终的目的是:任何时刻系统中的每个CPU上都有任务执行,从而提高CPU的利用率。如
果进程个数多于CPU的个数,则有些进程必须等待到CPU空闲时才可以运行。多处理是的
思路很简单;当进程需要某个系统资源时它将停止执行并等待到资源可用时才继续运行。单
处理系统中,如DOS,此时CPU将处于空等状态,这个时间将被浪费掉。在多处理系统中,
因为可以同时存在多个进程,所以当某个进程开始等待时,操作系统将把CPU控制权拿过
来并交给其它可以运行的进程。调度器负责选择适当的进程来运行,Linux使用一些调度策
略以保证CPU分配的公平性。
在介绍hook函数之前还必须介绍一下task的安全结构:
struct task_security_struct {
struct task_struct *task; /* 指向原task对象 */
};
u32 osid; /* 执行该进程的SID,用于安全执行检查*/
u32 sid; /* 当前进程的SID */
u32 exec_sid; /* exec SID */
u32 create_sid; /* fscreate SID */
u32 keycreate_sid; /* keycreate SID */
u32 sockcreate_sid; /* fscreate SID */
u32 ptrace_sid; /* SID of ptrace parent */
下面就对Task相关的hook函数,它们大部分都是调用task_has_perm进行权限检查的。
static int task_has_perm(struct task_struct *tsk1,struct task_struct *tsk2,u32 perms),第一
个参数是主体进程,第二个参数是客体进程,第三个参数表示申请要检查的权限。返回值为
0时,表示检查通过。否则不同过。下面对具体的函数进行一一介绍::
int (*task_create) (unsigned long clone_flags);
该函数在调用fork时调用。它调用static int task_has_perm(struct task_struct
*tsk1,struct task_struct *tsk2,u32 perms)主体和客体都是当前进程,perms是所要申请检查
的权限。
int (*task_alloc_security) (struct task_struct * p);
void (*task_free_security) (struct task_struct * p);
动态分配或释放安全结构的内存。分配时,除了task成员不同,其它的数据成员的值
都和当前进程(即父进程)一样。
int (*task_setuid) (uid_t id0, uid_t id1, uid_t id2, int flags);//特权操作的Hook
既然setuid只影响当前进程,而selinux访问控制不是基于linux的身份属性的,所以
selinux不需要对该操作进行访问控制。但是,selinux不能控制权能hook使用CAP_SETUID
和CAP_SETGID。因为capability(权能)是和selinux独立的,互相不影响。
int (*task_post_setuid) (uid_t old_ruid /* or fsuid */ ,
uid_t old_euid, uid_t old_suid, int flags);
不作任何操作
int (*task_setgid) (gid_t id0, gid_t id1, gid_t id2, int flags);
同task_setuid都需要,原因也一样。
int (*task_setpgid) (struct task_struct * p, pid_t pgid);
对PROCESS_SETPGID权限进行检查,主体和客体分别为current和p
int (*task_getpgid) (struct task_struct * p);
对PROCESS__GETPGID权限进行检查,主体和客体分别为current和p
int (*task_getsid) (struct task_struct * p);
PROCESS_GETSESSION。
void (*task_getsecid) (struct task_struct * p, u32 * secid);
调用selinux_get_task_sid(p,secid),该函数的功能获取进程的sid。当selinux关
闭时,sid被赋值为0。
int (*task_setgroups) (struct group_info *group_info);
同set_uid。
int (*task_setnice) (struct task_struct * p, int nice);//资源管理的Hook
不作任何操作,只是检查进程的PROCESS__SETSCHED权限。
int (*task_setioprio) (struct task_struct * p, int ioprio);
只检查当前进程对进程p是否有设置io优先级(PROCESS__SETSCHED)权限
int (*task_getioprio) (struct task_struct * p);
只检查当前进程对进程p是否有PROCESS__GETSCHED权限。
int (*task_setrlimit) (unsigned int resource, struct rlimit * new_rlim);//资源管理的Hook
对硬件资源限制的设置进行控制。
int (*task_setscheduler) (struct task_struct * p, int policy,struct sched_param * lp);
设置进程的调度策略,直接对PROCESS_SETSCHED权限进行检查。
int (*task_getscheduler) (struct task_struct * p);
获取进程的调度策略,对PROCESS_GETSCHED权限进行检查。
int (*task_movememory) (struct task_struct * p);
对PROCESS__SETSCHED权限进行检查。
int (*task_kill) (struct task_struct * p, struct siginfo * info, int sig, u32 secid);//控制进程
间通信的Hook
该函数的功能是对杀死进程进行权限检查,如果参数secid不为0,则利用secid作为
主体进行策略检查,否则直接利用当前进程作为主体进行检查。
int (*task_wait) (struct task_struct * p);
对p->exit_signal权限进行检查,主体和客体分别为当前进程current和p。
int (*task_prctl) (int option, unsigned long arg2,
unsigned long arg3, unsigned long arg4,
unsigned long arg5);
对当前进程没有任何影响,故不作任何检查。
void (*task_reparent_to_init) (struct task_struct * p);
将进程p安全域的sid赋值给osid,sid被覆盖为SECINITSID_KERNEL
void (*task_to_inode)(struct task_struct *p, struct inode *inode);
为inode节点设置进程相关的安全属性,将task的sid赋值给inode的sid。并将initialized
成员置为1。
2.5.2 Program Loading Hooks
要详细了解selinux启动的钩子,必须要对linux程序的加载有所了解。下面是对linux
程序加载的简单介绍。
一个进程在内存中主要占用了以下几个部分,分别是代码段、数据段、BSS,栈,堆,
等参数。其中,代码、数据、BSS的内容是可执行文件中对应的内容,加载程序并不是把它
们的内容从可执行程序中填充到内存中,而是将它们的信息(基地址、长度等)更新到进程
控制块(task_struct)中,当CPU第 一次实际寻址执行的时候,就会引起缺页中断,操作
系统再将实际的内容从可执行文件中复制内容到物理内存中。
堆的内容是程序执行中动态分配的,所以加载程序 只是将它的起始地址更新到进程控
制块中,执行过程中遇到动态分配内存的操作的时候再在物理内存分配实际的页。参数区在
新进程加载的时候要存入环境变量和命令行参数列表。栈在程序加载时候存入的内容就是环
境参数列表和命令行参数列表的指针和命令行参数的个数。
关于linux_binprm结构在前面已经介绍,下面就具体介绍一下它所附加的安全结构:
struct bprm_security_struct {
struct linux_binprm *bprm; /*指向bprm对象*/
u32 sid; /* 进程转换SID */
unsigned char set;
char unsafe;//为bprm_post_apply_creds()提供共享失败信息,该信息来自于
bprm_apply_creds()
};
下面是主要hook函数的说明:
int (*bprm_alloc_security) (struct linux_binprm * bprm);//分配安全域
void (*bprm_free_security) (struct linux_binprm * bprm);//释放安全域
上述函数给安全结构动态分配内存,初始化为默认值,sid赋值为
SECINITSID_UNLABELED,set赋值为0,unsafe未被初始化。并将返回的指针赋值给
linux_binprm的void *security,该安全域主要是为了记录安全信息的,以供后面使用。
void (*bprm_apply_creds) (struct linux_binprm * bprm, int unsafe);
该函数的功能是计算并设置进程的转换属性,该属性的转换是根据旧的属性和
set_security所设置的bprm_security值。执行该操作是会调用进程锁,不会返回一个错误结
果。
void (*bprm_post_apply_creds) (struct linux_binprm * bprm);
在bprm_apply_creds操作之后执行,也需要进程锁进行互斥操作,这个hook函数能够执行
进程的状态改变,如当属性改变后,哪些已经打开的文件描述符不能够再访问。
int (*bprm_set_security) (struct linux_binprm * bprm);
该函数的功能是设置安全域中的信息,主要是安全域中sid。它的信息来源于当前进程
的安全信息和二进制文件的inode中的安全信息。Set成员表示该安全域是否被设置。Sid默
认为当前进程的sid。如果当前进程的exec_sid成员为空,则需要通过默认sid转换算出sid,
否则为默认值。
接着就是检验设置的正确性,如果sid为默认值,需要检验是否存在域转换,如果不存
在域转换,成功设置,否则返回错误。如果sid不为默认值,则产生了域转换,需要检验产
生了域转换,否则返回错误。同时还需检验是否具有FILE_ENTRYPOINT权限,如果不具有,
则返回错误值。
int (*bprm_check_security) (struct linux_binprm * bprm);
在调用该函数之前必须调用set_security,在set_security函数已做了权限检查,该函数
没有什么需要处理的。
int (*bprm_secureexec) (struct linux_binprm * bprm);
检查程序是否被安全执行,对那些产生域转换的进程进行检查。该检查发生在进程已成功启
动。
2.5.3 IPC Hooks
按照管理先简单介绍一下进程通信IPC:
Linux下进程间通信的几种主要手段简介:
1.管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有
名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关
系进程间的通信;
2.信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除
了用于进程间通信外,进程还可以发送信号给进程本身;Linux除了支持Unix早期信号语义
函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD
的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal
函数);
报文(Message)队列(消息队列):消息队列是消息的链接表,包括Posix消息队列systemV
消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列
中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小
受限等缺点。
共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其
他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程
间的同步及互斥。
信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。
套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。
起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux
和System V的变种都支持套接字。
上面对IPC的通信原理做了详细的介绍,下面对IPC内核数据结构的安全域进行详细的
分析:
kern_ipc_perm的安全域:
struct ipc_security_struct {
struct kern_ipc_perm *ipc_perm; /*指向所属对象 */
u16 sclass; /* 该主体的安全类型*/
u32 sid; /* IPC资源的sid */
};
msg_msg的安全结构:
struct msg_security_struct {
struct msg_msg *msg; /*指向所属对象*/
u32 sid; /*消息的sid*/
};
从上面看IPC通信所涉及的安全信息比较少。对IPC Hook函数介绍之前还需要对一个重
要的函数进行介绍:static int ipc_has_perm(struct kern_ipc_perm *ipc_perms,u32 perms),它
是调用avc_has_perm对所申请的权限进行检查,该主体为当前的进程,而客体为
ipc_has_perm的第一个参数。第三种对avc_has_perm进行了详细的介绍。
int (*ipc_permission) (struct kern_ipc_perm * ipcp, short flag);
在该函数中,首先对S_IRUGO,S_IWUGO两个信号量的标志,转化为selinux的权
限。接着调用ipc_has_perm进行权限校验。
int (*msg_msg_alloc_security) (struct msg_msg * msg);
void (*msg_msg_free_security) (struct msg_msg * msg);
给msg_msg分配安全结构msg_security_struct,并将其sid初始化为
SECINITSID_UNLABELED。释放函数的功能就是释放该结构所占用的内存空间。
int (*msg_queue_alloc_security) (struct msg_queue * msq);
void (*msg_queue_free_security) (struct msg_queue * msq);
消息队列的安全操作,kern_ipc_perm是消息队列中的一个结构数据成员。该操作是给
消息队列结构,即kern_ipc_perm分配安全域空间。一个消息队列只包含一个
kern_ipc_perm实例。首先是给调用ipc_alloc_security分配ipc_security_struct安全域,其
中sclass成员和ipc_perm成员都是冲过参数传递进去的,而sid成员设置为task的sid。
然后检查当前进程是否有创建消息队列的权限,如果没有释放所申请的空间。
int (*msg_queue_associate) (struct msg_queue * msq, int msqflg);
响应msgget系统调用请求时检查MSGQ_ASSOCIATE权限。该函数只会在返回已存在的
消息队列标识是调用,如果是新的消息队列被创建,则不调用。int (*msg_queue_msgctl)
(struct msg_queue * msq, int cmd);
当对消息队列执行控制操作时,根据cmd检查权限。cmd包括:
IPC_INFO,MSG_INFO,IPC_STAT,MSG_STAT,IPC_SET,IPC_RMID,当cmd为
MSG_INFO,检查当前进程是否有SYSTEM_IPC_INFO权限,客体标签为
SECINITSID_KERNEL.其它都是根据cmd设置相应的权限值,并检查当前进程对消息队列
是否拥有这些权限。
int (*msg_queue_msgsnd) (struct msg_queue * msq, struct msg_msg * msg, int
msqflg);
消息插入队列时进行权限检查。该函数涉及到三个安全域,分别是:当前进程的安全域,
消息队列的安全域,消息的安全域。首先检查消息是否打上标签,如果没有打上标签,则先
通过security_transition_sid计算出消息的sid。该函数会在后面章节详细讲到。接着需要检
查当前进程是否有权限写该队列,如果有写的权限,下一步就需要检查当前进程是否有权限
发送该消息,如果通过检查,最后还需要检查该消息是否能够插入到消息队列中。至此,当
所有的检查都通过后,才会返回成功值,如果其中有一个不通过,就会返回错误值。
int (*msg_queue_msgrcv) (struct msg_queue * msq, struct msg_msg * msg, struct
task_struct * target,long type, int mode);
该函数检查消息队列的接受信息的权限。同上面函数一样,涉及到三个对象的安全域,
它们分别为,当前进程,消息队列,接受的消息。第一步是检查进程是否对消息队列拥有读
的权限。第二步是检查进程是否有对消息结构的权利。
2.5.4 File System Hooks
操作系统中负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统。文
件系统由三部分组成:与文件管理有关的软件、被管理的文件以及实施文件管理所需的数据
结构。从系统角度来看,文件系统是对文件存储器空间进行组织和分配,负责文件的存储并
对存入的文件进行保护和检索的系统。具体地说,它负责为用户建立文件,存入、读出、修
改、转储文件,控制文件的存取,当用户不再使用时撤销文件等。
Linux的第一个版本是基于Minix文件系统的。当 Linux成熟时,引入了扩展文件系统(Ext
FS) ,它包含了几个重要的扩展,但提供的性能不令人满意。在 1994 年引入了第二扩展
文件系统(second Extended Filesystem,Ext2) 其特点为存取文件的性能极好,对于中小型
的文件更显示出优势;它除了包含几个新的特点外,还相当高效和强健,经过逐步改进EXT3
文件系统已成为广泛使用的 Linux文件系统。
Linux文件系统由两大模块组成:虚拟文件系统和实际的文件系统。虚拟文件系统为用
户空间程序提供文件系统接口的调用,也是保证系统内核各种文件系统实现能够共存的抽象
层。
Linux文件系统框架如下:
VFS使得Linux可以支持多个不同的文件系统,每个表示一个VFS 的通用接口。由于软
件将Linux 文件系统的所有细节进行了转换,所以Linux核心的其它部分及系统中运行的程
序将看到统一的文件系统。Linux 的虚拟文件系统允许用户同时能透明地安装许多不同的文
件系统。Linux 2.6 内核中,几乎支持所有现存的文件系统格式,例如ext3, ext2, xia, minix,
umsdos, msdos, vfat, proc, smb, ncp, iso9660, sysv, hpfs, affs, ufs等等。
虚拟文件系统是在内核实现的一个软件层,它既为用户空间程序提供文件系统接口的调
用,也是保证系统内核各种文件系统实现能够共存的抽象层。
VFS系统调用如open(2), stat(2), read(2), write(2), chmod(2)等在进程上下文中被调用。文
件系统锁在文档Documentation/filesystems/Locking中描述。
目录结构缓存(dcache):
VFS实现了open(2), stat(2), chmod(2)以及其它类似的系统调用。VFS利用传递给这些调
用的文件路径参数在目录入口缓存(dcache 或者 目录机构缓存)中进行查找。通过一种快
速的转换机制将文件路径转化为特定的目录入口。目录结构缓存只存在在RAM中而从不写
入磁盘,他们只是为了提高性能而创建的。
目录结构缓存是整个文件空间的视图,但是绝大多数计算机并没有足够的空间将所有的
目录结构同时放在RAM中,因此某些缓存将会丢弃。为了解决目录结构中的路径,VFS也
许需要按照路径结构重新创建该缓存,并加载inode节点。这通过查找inode节点实现。
Inode节点对象:
一个独立的目录结构通常会有一个指向一个inode的指针。Inodes是文件系统的对象,
例如普通文件,目录,FIFO等。他们或者存在于磁盘上(快设备文件系统)或者内存中。
被请求访问的inode从磁盘加载道内存,在修改后再写入磁盘。一个独立的inode可以同时
作为多个目录结构的指针目标(如硬链接时会出现这种情况)。
查找一个inode时,需要通过VFS对inode的父目录inode调用looup()方法。该方法在
inode所属的具体文件系统中实现。一旦VFS请求了目录结构(同时inode),我们就可以
通过open(2)打开文件,或者stat(2)查看inode数据。stat(2)操作很简单,一旦VFS获得了目
录结构,它就察看inode数据,并将其中某些数据传给用户空间。
文件对象:
打开一个文件还需要其它的操作:获取文件结构(内核实现的文件描述表)。最新分配
的文件结构被初始化为一个指向目录结构的指针和一组文件操作函数集合。这些数据从
inode获得。然后调用特定的文件系统实现的open()文件操作执行相关的任务。这是VFS交
换功能的一个实现。文件结构被加入进程的文件表述表。
读、写和关闭文件(以及其它相关的VFS操作)通过用户空间的文件描述表获取对应的
文件结构,然后调用请求的文件结构来实现需要得功能。一旦文件被打开,目录结构将保持
在使用状态,表示VFS inode处于被访问状态。
Super Block Hooks
有关于super_block数据结构,在前面已经详细的描述过,这里就不再重复了。下面就
对super_block的安全结构进行详细介绍:
struct superblock_security_struct {
struct super_block *sb; /* 指向所属super_block结构*/
struct list_head list; /*superblock_security_struct链表 */
u32 sid; /* SID of file system superblock*/
u32 def_sid; /* default SID for labeling */
u32 mntpoint_sid; /*文件系统挂载点的安全上下文 */
unsigned int behavior; /*打标签操作*/
unsigned char initialized; /*初始化标志*/
unsigned char proc; /*proc文件系统*/
struct mutex lock;//信号锁
struct list_head isec_head;//inode安全域链表
spinlock_t isec_lock;//自旋锁
};
介绍完了安全结构,我们开始介绍Super Block Hooks:
int (*sb_alloc_security) (struct super_block * sb);
void (*sb_free_security) (struct super_block * sb);
上述两函数主要功能是为super_block结构动态分配和释放安全数据成员的内存,由于
superblock_security_struct数据成员比较多,其中包括链表和信号量,所以初始化也就比
较复杂,下面截取了部分源码,能够很清楚的表明各个数据成员的默认值,其中sbsec是
新动态分配的安全结构:
mutex_init(&sbsec->lock);
INIT_LIST_HEAD(&sbsec->list);
INIT_LIST_HEAD(&sbsec->isec_head);
spin_lock_init(&sbsec->isec_lock);
sbsec->sb = sb;
sbsec->sid = SECINITSID_UNLABELED;
sbsec->def_sid = SECINITSID_FILE;
sbsec->mntpoint_sid = SECINITSID_UNLABELED;
int (*sb_copy_data)(struct file_system_type *type,void *orig, void *copy);
把数据从orig拷贝到copy中,如果是二进制挂载数据,则直接进行拷贝。(具体这个
还没有弄清楚
int (*sb_kern_mount) (struct super_block *sb, void *data);
首先调用superblock_doinit为data建立sb,这个函数细节比较复杂,会在后面进行详
细介绍。初始化成功后,对挂载操作进行权限检查,主体是当前进程,客体是sb,申请检
查的权限是FILESYSTEM_MOUNT。
int (*sb_statfs) (struct dentry *dentry);
该函数的首先进行审计数据结构进行初始化,审计系统部分,我们这里就忽略不讲,因
为这不是我们任务的重点。这一点在前面我没有提及,文章后半部分也不会提及。主体是当
前进程,客体是dentryd_sb,对FILESYSTEM_GETATTR权限进行检查。
int (*sb_mount) (char *dev_name, struct nameidata * nd,char *type, unsigned long
flags, void *data);
该函数的参数比较多,如果是重新挂载,就直接检查FILESYSTEM_REMOUNT权限,
主体是当前进程,客体是ndmntmnt_sb。否则,对FILE_MOUNTON权限进行检查,
主体是当前进程,客体是nddentry。是调用dentry_has_perm,最终是调用
inode_has_perm,客体的最终是nddentry-->d_inode。
int (*sb_umount) (struct vfsmount * mnt, int flags);
卸载要比挂载简单,只对FILESYSTEM__UNMOUNT权限进程检查就行了。主体是当
前进程,客体是mntmnt_sb。
void (*sb_umount_close) (struct vfsmount * mnt);
void (*sb_umount_busy) (struct vfsmount * mnt);
void (*sb_post_remount) (struct vfsmount * mnt,
unsigned long flags, void *data);
void (*sb_post_mountroot) (void);
void (*sb_post_addmount) (struct vfsmount * mnt,
struct nameidata * mountpoint_nd);
int (*sb_pivotroot) (struct nameidata * old_nd,
struct nameidata * new_nd);
void (*sb_post_pivotroot) (struct nameidata * old_nd,
struct nameidata * new_nd);
上述函数是在security_operations中有,但selinux没有改写,后面如果出现这种函数,
就直接跳过,不再獒述。
Inode Hooks
dentry在文件系统中是一个很重要的数据结构,文件系统的钩子函数中很多都涉及到该
数据结构。为了跟好的理解钩子函数,必须对dentry做简单的介绍。dentry的中文名称是
目录项,是linux文件系统中某个索引节点(inode)的链接。这个索引节点可以是文件,也
可以是目录。
struct dentry {
atomic_t d_count; //目录项对象使用计数器
unsigned int d_flags; //目录项标志
struct inode * d_inode; //与文件名关联的索引节点
struct dentry * d_parent; //父目录的目录项对象
struct list_head d_hash; //散列表表项的指针
struct list_head d_lru; //未使用链表的指针
struct list_head d_child; //父目录中目录项对象的链表的指针
struct list_head d_subdirs;//对目录而言,表示子目录目录项对象的链表
struct list_head d_alias; //相关索引节点(别名)的链表
int d_mounted; //对于安装点而言,表示被安装文件系统根项
struct qstr d_name; //文件名
unsigned long d_time; /* used by d_revalidate */
struct dentry_operations *d_op; //目录项方法
struct super_block * d_sb; //文件的超级块对象
vunsigned long d_vfs_flags;
void * d_fsdata;//与文件系统相关的数据
unsigned char d_iname [DNAME_INLINE_LEN];// 存放短文件名
};
Inode对应于物理磁盘上的具体对象,dentry是一个内存实体,其中的d_inode成员指
向对应的inode节点。也就是说,一个inode可以在运行的时候链接多个dentry,而d_count
记录了这个链接的数量。
Inode(索引节点)
struct inode_security_struct {
struct inode *inode; /* 所属的主体 */
struct list_head list; /*inode_security_struct的链表 */
u32 task_sid; /* 创建该节点的进程(如果刚开始就存在于文件系统中,怎
么办)*/
u32 sid; /* 所属主体的sid */
u16 sclass; /*该主体的安全类*/
unsigned char initialized; /* 初始化标识 */
struct mutex lock;
unsigned char inherit; /*从父目录项继承来的sid*/
};
int (*inode_alloc_security) (struct inode *inode);
void (*inode_free_security) (struct inode *inode);
分配安全结构,并进行初始化。初始化的值如下:
mutex_init(&isec->lock);
INIT_LIST_HEAD(&isec->list);
isec->inode = inode;
isec->sid = SECINITSID_UNLABELED;
isec->sclass = SECCLASS_FILE;
isec->task_sid = tsec->sid;
inode->i_security = isec;
上述代码中isec值是inode_security_struct结构指针。
int (*inode_init_security) (struct inode *inode, struct inode *dir,
char **name, void **value, size_t *len);
该函数涉及到进程、inode、和super_block三个对象,因此它的安全检查也涉及到三
个安全域。它们分别被初始化为tsec = current->security; dsec = dir->i_security; sbsec =
dir->i_sb->s_security; 接着是计算inode的sid,当tsec的create_sid数据成员不为0,且
sbsec的behavior数据成员不为SECURITY_FS_USE_MNTPOINT时,inode的sid为
tseccreate_sid。否则,通过security_transition_sid函数计算出inode的sid。如果sbsec
已经初始化,则直接把上一阶段获得的sid赋值给inode。否则,错误退出。最后通过sid
利用security_sid_to_context给value和len赋值。
int (*inode_create) (struct inode *dir,
struct dentry *dentry, int mode);
调用may_create来判断是否拥有创建新节点的权限。Inode的创建涉及到进程,inode
节点和超级块,安全检查涉及到它们的安全域。新inode的创建,必须给它安全域的数据成
员赋值。首先,需要检查进程对目录的DIR_ADD_NAME和DIR_SEARCH权限。获取新
sid的流程和上面一个函数一样。接着就需要检查进程对新sid的FILE_CREATE权限。最
后,需要检查新sid对超级块,即文件系统的FILESYSTEM_ASSOCIATE权限。这个过程
中需要检查多个主客体之间不同的权限。
int (*inode_link) (struct dentry *old_dentry,struct inode *dir, struct dentry
*new_dentry);
int (*inode_unlink) (struct inode *dir, struct dentry *dentry);
上面两个函数主要是正对文件系统的链接和释放链接操作进行检查,它们都是调用
may_link实现自己功能的。may_link函数的功能是检查一个进程是否拥有链接,删除链接
和删除文件或目录的权限。may_link涉及到三个安全域,当前进程的安全域和连个inode
安全域。根据may_link的第三个参数决定申请检查的权限,主体是进程,客体是inode,
检查的权限为DIR_REMOVE_NAME或DIR_ADD_NAME。该次检查后,接着又另外一次
检查,它的主体是当前进程,客体是dentry的inode,权限时FILE_LINK,FILE_UNLINK,
DIR_RMDIR。该链接是硬链接。
int (*inode_symlink) (struct inode *dir,struct dentry *dentry, const char
*old_name);
该函数是检查符号链接,即软链接的权限。软链接需要创建新的文件,所以调用
may_create函数。May_create函数在前面已经讲到了。
int (*inode_mkdir) (struct inode *dir, struct dentry *dentry, int mode);
创建新的inode节点,所以调用may_create。
int (*inode_rmdir) (struct inode *dir, struct dentry *dentry);
在inode_link函数中已经讲到了。
int (*inode_mknod) (struct inode *dir, struct dentry *dentry,
int mode, dev_t dev);
调用may_create函数。
int (*inode_rename) (struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry);
调用may_rename(old_inode, old_dentry, new_inode, new_dentry),dentry是inode
在内存中的映像,要给inode改名,内存映像也需要改名。首先检查的是进程对原目录的
DIR_REMOVE_NAME和DIR_SEARCH权限,接着是对原dentry的FILE_RENAME权限
进行检查,第三步是对dentry的DIR_REPARENT权限进行检查,第四步是对新目录的
DIR_ADD_NAME,DIR_SEARCH和DIR_REMOVE_NAME等权限进行检查。最后,还
需要检查当前进程对新dentry的删除权限。(这个rename操作怎么这么复杂具体的过程
是什么)
int (*inode_readlink) (struct dentry *dentry);
调用dentry_has_perm来进行FILE_READ权限进行检查。dentry_has_perm最终是
调用inode_has_perm来进行权限检查的,主体是当前进程,客体是dentry的inode对象。
int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd);
检查主体对客体通过符号链接查询路径名权限。符号链接需要二次读取文件,所以也是
对FILE_READ权限进行检查。
int (*inode_permission) (struct inode *inode, int mask, struct nameidata *nd);
主体当前进程,客体是对象,权限为file_mask_to_av(inodei_mode,mask)
int (*inode_setattr) (struct dentry *dentry, struct iattr *attr);
根据iattr的ia_valid数据成员来决定程序的执行流程,如果ia_valid数据成员中
ATTR_FORCE为真,则直接返回0,不作任何权限检查。否则,只要ia_valid中有
ATTR_MODE,ATTR_UID,ATTR_GID,ATTR_ATIME_SET,ATTR_MTIME_SET中的一项,
就需要对FILE_SETATTR权限进行检查。最后还得对 FILE_WRITE权限进行检查。
int (*inode_getattr) (struct vfsmount *mnt, struct dentry *dentry);
只需要检查进程对dentry的inode节点的FILE_GETATTR权限即可。
int (*inode_setxattr) (struct dentry *dentry, char *name, void *value,size_t size, int flags);
该函数的主要功能是对设置扩展属性进行权限检查,由于涉及到进程,超级块,和inode
节点,因此比较复杂。如果我们不能识别所设置的属性值,就简单的对FILE_SETATTR权限做
检查。否则,就需要对检查当前进程对inode的FILE_RELABLELFROM权限进行检查。接着通
过security_context_to_sid查找到value的sid,并检查当前进程对该sid是否具有
FILE_RELABELTO的权限。下一步是需要对当前的类型转换进行验证,看是否合法。最后,还
需要检查新sid对超级块是否具有FILESYSTEM_ASSOCIATE权限。
void (*inode_post_setxattr) (struct dentry *dentry, char *name, void *value,
size_t size, int flags);
成功设置扩展属性后,对inode安全域进行更新。再次调用上一个函数中调用的
security_context_to_sid,查找出新的sid,并把inode安全域的sid设置为新的sid。
int (*inode_getxattr) (struct dentry *dentry, char *name);
int (*inode_listxattr) (struct dentry *dentry);
上述两个函数都是对文件的扩展属性的读权限进行检查,所以都是对FILE_GETATTR权
限进行检查。并不需要做任何其它操作。
int (*inode_removexattr) (struct dentry *dentry, char *name);
首先需要识别扩展属性,如果扩展属性没有定义,就直接对FILE_SETATTR权限进行检查。
(这里貌似有写问题)
const char *(*inode_xattr_getsuffix) (void);
获取inode扩展属性的后缀
int (*inode_getsecurity)(const struct inode *inode, const char *name, void *buffer, size_t
size, int err);
获取inode的安全上下文,并把它放在buffer中。
int (*inode_setsecurity)(struct inode *inode, const char *name, const void *value, size_t size,
int flags);
设置inode的安全上下文,该安全上下文保存在value当中。
int (*inode_listsecurity)(struct inode *inode, char *buffer, size_t buffer_size);
将扩展属性XATTR_NAME_SELINUX输出到buffer当中。
File Hooks
File的安全结构如下:(解决了file结构不确定的问题了)
struct file_security_struct {
struct file *file; /* 指向所属主体 */
u32 sid; /* 打开文件的描述符sid */
u32 fown_sid; /* 文件所有者的sid */
};
int (*file_permission) (struct file * file, int mask);
通过文件的掩码mask,计算出所要检查的权限,再通过file_has_perm进行权限检查。
最后含需要验证socket文件是否已经打上标签,先查看该文件是不是socket文件,如果是
socket文件,先查看文件是否打上标签,如果没有,则根据inode的SID给它打上标签。
int (*file_alloc_security) (struct file * file);
给file的安全结构分配内存,并初始化。Sid和fown_sid数据成员都赋值为当前进程的
sid。
void (*file_free_security) (struct file * file);
和其它释放内存一样。
int (*file_ioctl) (struct file * file, unsigned int cmd, unsigned long arg);
根据cmd参数,检查当前进程对file结构相应的权限。
int (*file_mmap) (struct file * file,unsigned long reqprot, unsigned long prot,unsigned
long flags, unsigned long addr,unsigned long addr_only);
mmap执行文件的内存操作,并将映射放入进程的地址空间。file_mmap函数的功能是
在执行mmap之前进行权限检查。
int (*file_mprotect) (struct vm_area_struct * vma,unsigned long reqprot,unsigned long
prot);
在改变内存访问权限之前进行权限检查。
int (*file_lock) (struct file * file, unsigned int cmd);
调用file_has_perm检查当前进程对file对象的FILE_LOCK权限。
int (*file_fcntl) (struct file * file, unsigned int cmd,unsigned long arg);
根据cmd的值,调用file_has_perm检查当前进程对file对象的特定权限。
int (*file_set_fowner) (struct file * file);
设置file的安全结构的fown_sid数据成员。
int (*file_send_sigiotask) (struct task_struct * tsk, struct fown_struct * fown, int sig);
检查file向进程tsk的发送io信号的权限。
int (*file_receive) (struct file * file);
该钩子函数允许安全模块去控制进程通过socket IPC接收一个打开的文件描述符。
2.5.5 Network Hooks
安全结构:
struct sk_security_struct {
struct sock *sk; /* 指向所属主体对象*/
u32 sid; /*对象 SID */
u32 peer_sid; /* 对等对象的SID*/
#ifdef CONFIG_NETLABEL
u16 sclass; /* sock安全类型*/
enum { /* 网络标签状态*/
NLBL_UNSET = 0,
NLBL_REQUIRE,
NLBL_LABELED,
} nlbl_state;
spinlock_t nlbl_lock; /* 保护网络标签状态 */
#endif
};
int (*unix_stream_connect) (struct socket * sock,struct socket * other, struct sock *
newsk);
在建立UNIX流连接时,检查UNIX_STREAM_SOCKET__CONNECTTO权限,主体
是sock,客体对象是other。并为newsk分配sid。
int (*unix_may_send) (struct socket * sock, struct socket * other);
在连接或发送一个数据报是进行策略检查。主体是sock,客体是other,检查的权限是
SOCKET_SENDTO.
int (*socket_create) (int family, int type, int protocol, int kern);
创建套接字时,对SOCKET_CREATE进行检查,主体是当前进程。客体的sid:newsid
= tsec->sockcreate_sid ? : tsec->sid。
int (*socket_post_create) (struct socket * sock, int family,
int type, int protocol, int kern);
套接字创建成功后,设置套接字的安全结构,由于套接字是一种特殊的文件,所以不仅
要设置inode_security_struct,还需要设置sk_security_struct安全结构。
int (*socket_bind) (struct socket * sock,struct sockaddr * address, int addrlen);
检查SOCKET_BIND权限,主体是当前进程,客体是sock。
int (*socket_connect) (struct socket * sock,struct sockaddr * address, int addrlen);
先调用socket_has_perm检查SOCKET_CONNECT权限,主体是当前进程,客体是
sock。如果是TCP或DCCP套接字,还需检查name_connect的权限。主体是sock的inode,
客体是端口,客体类型是inode的客体类型。权限码是TCP_SOCKET__NAME_CONNECT
或DCCP_SOCKET__NAME_CONNECT。
int (*socket_listen) (struct socket * sock, int backlog);
调用socket_has_perm,检查SOCKET_LISTEN权限,主体是当前进程,客体是sock。
客体对象是sock的客体类型。
int (*socket_accept) (struct socket * sock, struct socket * newsock);
调用socket_has_perm,检查SOCKET_ACCEPT权限,主体是当前进程,客体是sock。
并设置newsock的inode的安全结构。
int (*socket_sendmsg) (struct socket * sock,struct msghdr * msg, int size);
先检查当前进程对sock的SOCKET_WRITE的权限,接着验证套接字是否是NetLabel
标签。实现该功能的是selinux_netlbl_inode_permission(struct inode *inode,int mask),
查找文件的inode,并查看该套接字是否标记为被NetLabel保护,然后验证该套接字是否
已经打上标记,如果没有,就用inode的sid立即给套接字打上标签。
int (*socket_recvmsg) (struct socket * sock,struct msghdr * msg, int size, int flags);
检查当前进程对sock的SOCKET_READ的访问权限。
int (*socket_getsockname) (struct socket * sock);
检查当前进程对sock的SOCKET_GETATTR的权限。
int (*socket_getpeername) (struct socket * sock);
调用socket_has_perm检查当前进程对sock的SOCKET_GETATTR权限。
int (*socket_getsockopt) (struct socket * sock, int level, int optname);
调用socket_has_perm检查当前进程对sock的SOCKET_GETOPT权限。 int
(*socket_setsockopt) (struct socket * sock, int level, int optname);
先调用socket_has_perm检查当前进程对sock的SOCKET_SETOPT权限。接着调
用selinux_netlbl_socket_setsockopt(),不允许用户删除NetLabel。该函数首先检查
setsockopt()函数,如果用户企图替换套接字IP选项和套接字的NetLabel,就阻止该操
作的继续执行。
int (*socket_shutdown) (struct socket * sock, int how);
调用socket_has_perm检查当前进程对sock的SOCKET_SHUTDOWN权限。
int (*socket_sock_rcv_skb) (struct sock * sk, struct sk_buff * skb);
int (*socket_getpeersec_stream) (struct socket *sock, char __user *optval, int __user
*optlen, unsigned len);
int (*socket_getpeersec_dgram) (struct socket *sock, struct sk_buff *skb, u32 *secid);
int (*sk_alloc_security) (struct sock *sk, int family, gfp_t priority);
void (*sk_free_security) (struct sock *sk);
给sock分配安全域或释放安全域,安全域为sk_security_struct
void (*sk_clone_security) (const struct sock *sk, struct sock *newsk);
void (*sk_getsecid) (struct sock *sk, u32 *secid);
void (*sock_graft)(struct sock* sk, struct socket *parent);
int (*inet_conn_request)(struct sock *sk, struct sk_buff *skb,
struct request_sock *req);
void (*inet_csk_clone)(struct sock *newsk, const struct request_sock *req);
void (*inet_conn_established)(struct sock *sk, struct sk_buff *skb);
void (*req_classify_flow)(const struct request_sock *req, struct flowi *fl);
2.5.6 Other Hooks
2.6 SELinux 初始化
第三章 AVC
3.1 设计目标
缓存决策结果,提高查询效率
3.2 数据结构
下面是AVC模块中重要的数据结构:
struct avc_entry {
u32 ssid;
u32 tsid;
u16 tclass;
struct av_decision avd;
atomic_t used; /* used recently */
};
struct avc_node {
struct avc_entry ae;
struct list_head list;
struct rcu_head rhead;
};
struct avc_cache {
struct list_head slots[AVC_CACHE_SLOTS];
spinlock_t slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */
atomic_t lru_hint; /* LRU hint for reclaim scan */
atomic_t active_nodes;
u32 latest_notif; /* latest revocation notification */
};
struct avc_callback_node {
int (*callback) (u32 event, u32 ssid, u32 tsid,
u16 tclass, u32 perms,
u32 *out_retained);
u32 events;
u32 ssid;
u32 tsid;
u16 tclass;
u32 perms;
struct avc_callback_node *next;
};
为了能够更好的了解AVC模块中数据结构之间的关系,我们来详细的分析一下它们的
初始化工作,初始化功能主要在avc_init函数中实现,下面是它的源码:
void __init avc_init(void)
{
int i;
for (i = 0; i < AVC_CACHE_SLOTS; i++) {
INIT_LIST_HEAD(&avc_[i]);
spin_lock_init(&avc__lock[i]);
}
atomic_set(&avc__nodes, 0);//将avc_cache的active_nodes设置为0
atomic_set(&avc__hint, 0);//将avc_cache的lru_hint设置为0,它们必须是
原子操作
avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node),
0, SLAB_PANIC, NULL);//给avc_node非配内存
audit_log(current->audit_context, GFP_KERNEL, AUDIT_KERNEL, "AVC INITIALIZEDn");
}
因为avc_cache中slots数据成员是一个结构数组,所以采取循环方式,对它的数据成员
进行初始化。它的每一项是一个双向链表,调用INIT_LIST_HEAD()便可以对其进行赋值为
空链表。和它一起初始化的还有自旋锁。从上面的函数我们还不能看出avc_node和avc_cache
之间的关系,必须通过它们的操作,如avc_insert,avc_lookup等,才能理解其中联系。它
们是通过list_head数据结构联系在一起,avc_cache中的list_head数组中的中存储的是
avc_node数据成员。这种设计很巧妙,有很多设计都是我们平时不能想象的,如通过数据
成员指针,获取结构的指针。AVC整体的存储架构是Hash表。通过Hash表的思想,就能够
比较容易的理解这段代码。
3.3 重要的操作
AVC的重要操作都在security/selinux/avc.c中。其中大多是和Hash表相关的操作。
3.4 接口
AVC提供了从安全服务器获得的访问策略的缓冲区(cache),提高了安全机制的运行性
能。它提供了hook函数高效检查授权的接口,提供了安全服务器管理cache的接口。与hook
函数的接口定义在include/avc.h中,与服务器的接口定义在include/avc_ss.h中,AVC代码
在avc.c中。(这些接口应该很好找的)
Avc接口函数主要是被Hooks函数调用,下面就看几个例子:
static int selinux_task_create(unsigned long clone_flags)
{
int rc;
rc = secondary_ops->task_create(clone_flags);
if (rc)
return rc;
return task_has_perm(current, current, PROCESS__FORK);
}
static int task_has_perm(struct task_struct *tsk1, struct task_struct *tsk2, u32 perms)
{
struct task_security_struct *tsec1, *tsec2;
tsec1 = tsk1->security;
tsec2 = tsk2->security;
return avc_has_perm(tsec1->sid, tsec2->sid,
SECCLASS_PROCESS, perms, NULL);
}
上面举了几个简单的例子,下面就详细的介绍这些接口的功能。调用接口avc_init()
上面已经介绍了。下面来介绍调用接口:avc_has_perm()该函数非常重要,是selinux Hooks
和AVC的查询接口,下面我就对它的进行详细讲解:
首先我们来看一下源码:
int avc_has_perm(u32 ssid, u32 tsid, u16 tclass,
u32 requested, struct avc_audit_data *auditdata)
{
struct av_decision avd;
int rc;
rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd);
avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
return rc;
}
该函数的功能是检查权限并执行合适的统计检查。参数ssid是主体的安全标识,参数
tsid,客体的安全标识,参数tclass是客体安全类型,参数requested是申请检查的权限,参
数auditdata是附加的审计数据。由源码我们可以看到该函数调用两个函数,一个是
avc_has_perm_noaudit,一个是avc_audit,两个函数把整个功能划分成两部分,一部分是仲
裁权限的,另一部分是负责审计。从源码中还可以看出函数的返回值与后者无关,完全由前
者决定。
为了集中注意力,讲解的重点,审计部分先放在一边。下面是avc_has_perm_audit的源
码:
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
{
u16 tclass, u32 requested,
unsigned flags,
struct av_decision *avd)
struct avc_node *node;
struct avc_entry entry, *p_ae;
int rc = 0;
u32 denied;
rcu_read_lock();
node = avc_lookup(ssid, tsid, tclass, requested);
if (!node) {
rcu_read_unlock();
rc = security_compute_av(ssid,tsid,tclass,requested,&);
if (rc)
goto out;
rcu_read_lock();
node = avc_insert(ssid,tsid,tclass,&entry);
}
p_ae = node ? &node->ae : &entry;
if (avd)
memcpy(avd, &p_ae->avd, sizeof(*avd));
denied = requested & ~(p_ae->d);
if (!requested || denied) {
if (selinux_enforcing || (flags & AVC_STRICT))
rc = -EACCES;
else
if (node)
avc_update_node(AVC_CALLBACK_GRANT,requested,
ssid,tsid,tclass);
}
rcu_read_unlock();
out:
return rc;
}
首先加上读保护锁,接着调用avc_lookup()查询AVC缓冲向量,如果返回值为空,表
示该权限以前没有被查找过,通过调用安全服务器(Security Server)提供的接口
security_compute_av()(如上图红色字体所示),获得策略决策avd。并把查询的结果插入
到AVC缓冲区中。接着,把所获得策略决策保存到参数avd中,并检查权限是否得到允许。
如果请求为空或者denied的参数不为0,且系统处于enforcing模式或者flags为AVC_STRICT
标志是,返回拒绝消息。当系统不是enforcing模式且flags为没有含AVC_STRICT标志时,
在avc node查询命中时,需要对调用avc_update_node进行更新。如果请求不为空或者
denied的参数为0,表示权限获得允许,返回值为0,即表示成功。
最后调用rcu_read_unlock()解除读锁。
avc_init和avc_has_perm是LSM Hooks和AVC最重要的接口,还有其它一些接口,如
avc_audit(),因为是实现审计功能,所以在这里没有介绍。Avc_has_perm_noaudit(),在
介绍avc_has_perm中已经介绍,avc_add_callback(),avc_get_hash_stats(),这两个函数
不是很重要,如果需要了解可以自己阅读源码。
第四章 SELinux安全策略
内核空间的SELinux主要由访问向量缓存(AVC)、安全服务器、安全策略实施机构和
SELinuxfs伪文件系统组成。
SELinux的安全服务器:维护主客体的标识信息,根据安全策略以及主客体标识信息计
算访问向量。
SELinux的访问向量缓存:维护访问向量,接受安全策略实施机构的查询。
SELinux策略库:存放系统安全管理员定义的安全策略。
SELinux安全策略实施机构:获取主客体的安全信息,通过查询访问向量缓存控制主体
对客体的访问。
SELinuxfs:提供用户程序与内核SELinux组件交互的接口。
SELinux的客体管理器:接受用户空间应用程序的策略查询请求。(这个是什么东东)
LYSLinux的安全策略??
XML语言配置
4.1 访问控制简介
4.1.1自主访问控制
4.1.2 Capability
4.1.3强制访问控制
4.1.4基于角色的访问控制
标记决策:不经过avc,直接访问安全服务器。客体创建时调用。
访问决策:需要同过avc。
4.2 相关的数据结构
4.2.1 数据结构基础
哈希表(Hash)简介:
一般的线性表,树中,记录在结构中的相对位置是随机的,即和记录的关键字之间不存
在确定的关系,因此,在结构中查找记录时需进行一系列和关键字的比较。这一类查找方法
建立在“比较“的基础上,查找的效率依赖于查找过程中所进行的比较次数。
理想的情况是能直接找到需要的记录,因此必须在记录的存储位置和它的关键字之间建
立一个确定的对应关系f,使每个关键字和结构中一个唯一的存储位置相对应。
哈希表最常见的例子是以学生学号为关键字的成绩表,1号学生的记录位置在第一条,10
号学生的记录位置在第10条...
有了大概的了解,我们下面会讲到后面会用到的一些基本概念。
若结构中存在关键字和K相等的记录,则必定在f(K)的存储位置上。由此,不需
比较便可直接取得所查记录。称这个对应关系f为散列函数(Hash function),按这个
思想建立的表为散列表。
对不同的关键字可能得到同一散列地址,即key1≠key2,而f(key1)=f(key2),这种现象称
冲突。具有相同函数值的关键字对该散列函数来说称做同义词。综上所述,根据散列函数
H(key)和处理冲突的方法将一组关键字映象到一个有限的连续的地址集(区间)上,并以关
键字在地址集中的“象” 作为记录在表中的存储位置,这种表便称为散列表,这一映象过程
称为散列造表或散列,所得的存储位置称散列地址。
若对于关键字集合中的任一个关键字,经散列函数映象到地址集合中任何一个地址的概
率是相等的,则称此类散列函数为均匀散列函数(Uniform Hash function),这就是使关键字经
过散列函数得到一个“随机的地址”,从而减少冲突。
上面对哈希表进行了简单的介绍,有了上面的知识准备,下面我们对哈希表的结构,及
实现进行详细介绍。
下面的数据结构是典型的哈希表中都会用到的,
struct hashtab_node {
void *key;//关键字
void *datum;//数据
struct hashtab_node *next;//指向下一个节点
};
struct hashtab {
struct hashtab_node **htable; /* 哈希表 */
u32 size; /* 哈希表的桶数,一般为素数 */
u32 nel; /* 哈希表中元素的个数 */
u32 (*hash_value)(struct hashtab *h, const void *key);
/* 哈希函数 */
int (*keycmp)(struct hashtab *h, const void *key1, const void *key2);
/* 关键字比较 */
};
Hash表的存储空间都是动态分配,从hashtab结构中可以看到htable是一个二级指针,
初始化的时候分配了size个hashtab_node指针,组成一个指针数组。数组的大小在初始化
就决定了。把它们用空间图形表示出来,可以很清楚理解他们之间的关系。图形如下图所示:
hashtab
指针数组
hashtab_node
void *key
void *datum
Sidtab_node *next
void *key
void *datum
Sidtab_node *next
void *key
void *datum
Sidtab_node *next
Sidtab_node ** htable
U32 size
U32 nel
Hash_value function
Keycmp function
hashtab_node *
hashtab_node *
hashtab_node *
hashtab_node *
...
hashtab_node *
void *key
void *datum
Sidtab_node *next
void *key
void *datum
Sidtab_node *next
void *key
void *datum
Sidtab_node *next
图 Hash表的结构图
Hash表和其它链表一样,最重要两个操作是插入和查找。
链表:
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是
通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)
组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的
数据域,另一个是存储下一个结点地址的指针域。
相比较顺序结构,链表比较方便插入和删除操作。它包括单链表,循环链表和双
向链表。
线性表的链式存储表示的特点是用一组任意的存储单元存储线性表的数据元素
(这组存储单元可以是连续的,也可以是不连续的)。因此,为了表示每个数据元素 与
其直接后继数据元素 之间的逻辑关系,对数据元素 来说,除了存储其本身的信息之
外,还需存储一个指示其直接后继的信息(即直接后继的存储位置)。由这两部分信
息组成一个"结点"(如下图所示),表示线性表中一个数据元素 。
根据情况,也可以自己设计链表的其它扩展。但是一般不会在边上附加数据,因
为链表的点和边基本上是一一对应的(除了第一个或者最后一个节点,但是也不会产
生特殊情况)。不过有一个特例是如果链表支持在链表的一段中把前和后指针反向,
反向标记加在边上可能会更方便。
对于非线性的链表,可以参见相关的其他数据结构,例如树、图。另外有一种基
于多个线性链表的数据结构:跳表,插入、删除和查找等基本操作的速度可以达到
O(nlogn),和平衡二叉树一样。
其中存储数据元素信息的域称作数据域(设域名为data),存储直接后继存储位
置的域称为指针域(设域名为next)。指针域中存储的信息又称做指针或链。
位图:
#define MAPTYPE u64 /* 每个node中位图的一部分*/
#define MAPSIZE (sizeof(MAPTYPE) * 8) /* 位图中bit位*/
#define MAPBIT 1ULL /*位图中一个bit位 */
struct ebitmap_node {
u32 startbit; /* 所有位图初始位置 */
MAPTYPE map; /*部分位图*/
struct ebitmap_node *next;//指向下一个位图节点
};
struct ebitmap {
struct ebitmap_node *node; /*位图中第一个位图节点*/
u32 highbit; /* 位图中最高的位置*/
};
从结构中可以很明显的看出ebitmap的存储结构是单链表结构,struct ebitmap是链表
头结点,后面所有的节点类型都是struct ebitmap_node结构的节点,作为一种位图存储结构,
除了有链表的一些操作外,还有一些位图特点的操作,这些位图管理函数在
selinux/ss/ebitmap.c文件中。
4.2.2 策略库Policydb
策略库policydb结构是用来存储策略文件,是整个策略服务器存储信息的中心,有关于
策略的操作都是基于该策略库的。作为策略数据的存储结构,它具有了数据存储的所有特点,
数据存储的有序性,高效查找性等。在介绍它的各种操作之前,我们必须了解它所存储的内
容以及各数据成员的含义。
struct policydb {
/* 符号表 */
struct symtab symtab[SYM_NUM];//以哈希表作为存储结构
#define p_commons symtab[SYM_COMMONS]
#define p_classes symtab[SYM_CLASSES]
#define p_roles symtab[SYM_ROLES]
#define p_types symtab[SYM_TYPES]
#define p_users symtab[SYM_USERS]
#define p_bools symtab[SYM_BOOLS]
#define p_levels symtab[SYM_LEVELS]
#define p_cats symtab[SYM_CATS]
/* 符号名字以(value-1)来索引 */
char **sym_val_to_name[SYM_NUM];
#define p_common_val_to_name sym_val_to_name[SYM_COMMONS]
#define p_class_val_to_name sym_val_to_name[SYM_CLASSES]
#define p_role_val_to_name sym_val_to_name[SYM_ROLES]
#define p_type_val_to_name sym_val_to_name[SYM_TYPES]
#define p_user_val_to_name sym_val_to_name[SYM_USERS]
#define p_bool_val_to_name sym_val_to_name[SYM_BOOLS]
#define p_sens_val_to_name sym_val_to_name[SYM_LEVELS]
#define p_cat_val_to_name sym_val_to_name[SYM_CATS]
/* class, role, and user属性,以(value-1)为索引 */
struct class_datum **class_val_to_struct;//数组
struct role_datum **role_val_to_struct;//数组
struct user_datum **user_val_to_struct;//数组
/* 类型裁决访问向量和转换 */
struct avtab te_avtab;//哈希表作为存储
/* 角色转换*/
struct role_trans *role_tr;//链表作为存储结构
/*布尔值,以value-1为索引 */
struct cond_bool_datum **bool_val_to_struct;//数组
/* 类型裁决条件访问向量和转换 */
struct avtab te_cond_avtab;//哈希表
/* 利用conditional链表索引te_cond_avtab */
struct cond_node* cond_list;//链表
/* 角色允许 */
struct role_allow *role_allow;//链表
/* 初始化SID的安全上下文,没标识的文件系统,TCP和UDP端口号,网络接口和节
点安全上下文*/
struct ocontext *ocontexts[OCON_NUM];//数组,每一个数据项是链表作为存储结构
/* 文件系统中的文件安全上下文,不支持采用持久的标签或采取固定标签行为 */
struct genfs *genfs;//链表
/* 域转换 */
struct range_trans *range_tr;//链表
/* 违反规则的type -> attribute */
struct ebitmap *type_attr_map;//位图
unsigned int policyvers;
};
Policydb数据结构庞大,需要对他们进行一一介绍:
(1) symtab(符号表)数组
该符号表的每一项都是以Hash表作为存储结构,关键字key是字符串,数据datum是
对应的数据结构,p_commons是common_datum,p_classes是class_datum,p_roles是
role_datum,p_types是type_datum,p_users是user_datum,p_bools是cond_bool_datum,
p_levels是level_datum,p_cats是cat_datum。这些结构的定义在security/selinux/ss/policydb.h
中。
(2) sym_val_to_name
它是一个数组,它的每个成员都是一个字符串数组,对它的查询是通过,value-1来查
询的。由于很多常量定义都是宏定义,客体类型的定义,每一个宏都代表一个数字,和数组
的下标相关联。由于客体类别是事先都定义好了的,所以可以用数组来存储。它是通过符号
表数组来初始化的,初始化后就不再改变。字符数组中的字符串是symtab中Hash表的key
值。和symtab数组一一对应。
(3) class,role,user,bool属性转换表
struct class_datum **class_val_to_struct,struct role_datum **role_val_to_struct,struct
user_datum **user_val_to_struct,struct cond_bool_datum **bool_val_to_struct,这四个转换
表都是根据p_classes,p_roles,p_users,p_bools,属性转换表中存储的是符号表中的数据
成员。
(4) te强制访问控制策略
te强制访问控制策略,存储在te_avtab结构和te_cond_avtab结构。他们所采取的存储
是一样的,都是采取Hash表作为存储结构。key是avtab_key结构,datum是avtab_datum
结构。这些结构在4.2.6节会详细介绍。
(5) Role_trans角色转换
该数据成员是以单链表作为存储结构。链表结构在4.2.1中已经详细介绍了。
(6) Role_allow
Role_allow策略,采取单链表存储结构。
(7) Cond_list
单链表作为存储结构,条件策略,一个cond节点代表策略中的一个条件块。它包含了
一个条件表达式,表达式当前的状态和两个规则链表。
struct cond_node {
int cur_state;
struct cond_expr *expr;//布尔表达式
struct cond_av_list *true_list;//布尔值为真的节点
struct cond_av_list *false_list;//布尔值为假的节点
struct cond_node *next;
};
和avtab相关联的数据结构:
struct cond_av_list {
struct avtab_node *node;
struct cond_av_list *next;
};
(8) Ocontexts
初始化安全上下文。单链表作为存储结构
(9) Genfs
(10) Range_trans
Range转换表
(11) Type_attr_map
类型属性位图表
由policydb结构可以看出,策略库的很多成员自己也是结构,且比较复杂,如果单纯的
从文字看还不能看出其中的复杂性。为了能够更好的了解policydb的存储结构,作出了
policydb的数据存储图。下图表示policydb中所设计数据结构之间的关系。
symtabcommon_datum
hashtab
struct hashtab_node
**htable
u32 size
u32 nel;
u32
(*hash_value)(struct
hashtab *h, void *key)
int (*keycmp)(struct
hashtab *h, void
*key1, void *key2)
hashtab_node
struct hashtab
*table
u32 nprim
u32 value
struct symtab
permissions
void *key
void *datum
struct
hashtab_node
*next
class_datum
policydb
u32 value
char *comkey
struct
common_datum
*comdatum
struct symtab
permissions
struct
constraint_node
*constraints
struct
constraint_node
*validatetrans
constraint_expr
u32 expr_type
struct symtab
symtab[SYM_NUM]
char
**sym_val_to_name[
SYM_NUM];
struct class_datum
**class_val_to_struct
struct role_datum
**role_val_to_struct
struct user_datum
**user_val_to_struct
struct avtab te_avtab
struct role_trans
*role_tr
struct
cond_bool_datum
**bool_val_to_struct
struct avtab
te_cond_avtab
struct cond_node*
cond_list
struct role_allow
*role_allow
struct ocontext
*ocontexts[OCON_N
UM]
struct genfs *genfs
struct range_trans
*range_tr
struct ebitmap
*type_attr_map
unsigned int
policyvers
constraint_node
u32 attr
u32 permissions
struct
constraint_expr
*expr
struct
constraint_node
*next
u32 op
struct ebitmap
names
struct
constraint_expr
*next
role_trans
u32 role
u32 type
u32 new_role
cond_bool_datum
__u32 value
role_allow
int state
u32 role
u32 new_role
struct role_allow
*next
struct role_trans
*next
role_datum
u32 value
struct ebitmap
dominates
user_datum
u32 value
struct ebitmap
roles
struct mls_range
range
struct mls_level
dfltlevel
struct ebitmap
types
图 Policydb的结构图
4.2.3 Sid table
Sid表,这个很重要,每一个对象都有一个sid,而且每一个sid都关系一个安全上下文,
它是整个强制访问的基础,没有它,整个访问控制便处于无序状态。存储结构是一个
sidtab_node的结构指针数组,具体结构图如下图所示,从存储图的结构,便可知道它的一
些基本操作,这里就不需要详细介绍了。
struct sidtab {
struct sidtab_node **htable;
unsigned int nel; /* 元素个数 */
unsigned int next_sid; /* 下一个待分配SID,为所有sid中最大的一个值加上1*/
unsigned char shutdown;
spinlock_t lock;
};
struct sidtab_node {
u32 sid; /*安全标识*/
struct context context; /*安全上下文*/
struct sidtab_node *next;
};
Sidtab_node ** htable
Sidtab_node *
Sidtab_node *
Sidtab_node *
Sidtab_node *
...
Sidtab_node *
Sidtab_node
Sidtab_node *next
Sidtab_node
Sidtab_node *next
Sidtab_node
Sidtab_node *next
图 sidtab存储结构图
安全上下文结构:
struct context {
u32 user;
u32 role;
u32 type;
struct mls_range range;
};
struct mls_range {
struct mls_level level[2]; /* 两个级别,分为低和高,level[0]表示低级别,level[1]表示高
级别*/
};
struct mls_level {
};
u32 sens; /*保密级别 */
struct ebitmap cat; /*类别集,利用位图存储 */
4.2.4主客体类别class type
由于不同的客体,有各自不同的特点,操作也不同。如果强制的去统一形式,代价非常
大。因此,对客体进行相应的分类,并定义将近30种客体类别,它们的具体定义在Flask.h
文件中。
4.2.5 策略决策avd
策略决策结构avd,主要是作为返回决策信息而设定的结构,它决定了查询操作的目的。
下面详细介绍它的各个数据成员:
struct av_decision {
u32 allowed;//AVTAB_ALLOWED
u32 decided;//
u32 auditallow;//AVTAB_AUDITALLOW
u32 auditdeny;//AVTAB_AUDITDENY
u32 seqno;
};
初始化:当系统中没有策略库时,avd采取默认值,默认值如下:
avd->allowed = 0xffffffff;
avd->decided = 0xffffffff;
avd->auditallow = 0;
avd->auditdeny = 0xffffffff;
avd->seqno = latest_granting;//last_granting初始值为0
seqno当策略改变时才发生改变。具体有什么作用还不清楚。
4.2.6 avtab(访问控制向量表)
Policydb的数据成员te_avtab,存储TE策略,另一个数据成员te_cond_avtab,存储TE
条件访问向量的。TE策略是整个SELinux的核心,其它的策略是对该策略的补充。因此TE
策略的存储效率,是整个系统的性能瓶颈。一个访问控制向量表用来表示类型裁决(TE)表。
struct avtab_key {
u16 source_type; /* 主体标签 */
u16 target_type; /* 客体标签 */
u16 target_class; /* 目标对象的类型*/
u16 specified; /* what field is specified */
};
存放关键数据的结构:
struct avtab_datum {
u32 data; /* 访问控制向量或客体类型值 */
};
节点数据结构:
struct avtab_node {
struct avtab_key key;
struct avtab_datum datum;
struct avtab_node *next;
};
Hash表头:
struct avtab {
struct avtab_node **htable;//Hash表作为存储结构
u32 nel; /* 元素的个数 */
};
Avtab(访问控制向量表)的初始化,调用avtab的初始化函数avtab_init()实现的,因为
它采取Hash表作为存储结构,实际上就是Hash表的初始化,有关于Hash表的结构在前面
已经详细的讲过,这里就不再獒述了。
4.3 策略的加载
4.2节已经详细介绍了policydb等策略库的关键数据结构,下面重点介绍策略的加载和
各种数据结构的初始化。
由于策略结构比较复杂,因此要实现策略的加载,必须对策略二进制文件进行解析,而
解析的过程是一个很复杂和细致的事情。该功能是通过int security_load_policy(void *data,
size_t len)函数实现的,参数1是指向二进制的文件数据指针,参数2是该文件的长度。加
载策略时,需要分两种情况:第一种是没有加载策略,即内核不存在任何其它的安全策略库;
第二种是已经加载了策略,即内核中已经存在安全策略库。如果没有加载策略,第一步先要
对策略缓存进行初始化,调用策略缓存函数avtab_cache_init(),完成策略缓存区之后进行第
二步,第二步是读取文件,初始化policydb数据结构,调用policydb_init(p)函数,由于
policydb结构比较大,涉及到数据结构也是比较复杂,具体的初始化步骤如下:
(1) memset(p, 0, sizeof(*p));
将结构中所有的内存空间都清0,防止数据污染。
(2) symtab_init(&p->symtab[i], symtab_sizes[i]);
哈希表的创建,注意参数1中&不是对p做取地址操作,而是对整个表达式进行取
地址操作。这里涉及到操作符的结合性,单目操作符是右结合性,所以先执行操作符,
再执行&取址操作。
(3) avtab_init(&pte_avtab);
因为avtab也是采取Hash表作为存储结构,因此它的初始化,也就是Hash表的初
始化。
(4) roles_init(p);
插入一个role角色。
(5) cond_policydb_init(p)
te_cond_avtab和te_avtab是同一结构的两个不同数据成员,它调用avtab_init进行
初始化,不同的是初始化的对象。
Policydb数据结构的初始化,只是读取策略库文件的第一步,接下来是对文件数据的读
取,这个才是该函数所做的主要事情,读取文件的过程如下:
(1) 读取文件的magic number和字符串长度,并验证是否为POLICYDB_MAGIC。
(2) 验证字符串长度,如果不符,返回错误代码。该长度是POLICYDB_STRING的长
度。
(3) 读取policydb_str,并验证。
(4) 读取策略库的版本信息,配置,和表大小
(5) 初始化策略库版本policyvers
(6) 检查是否启用多级安全架构
(7) 验证版本信息的正确性
(8) 读取symtab信息,并填充symtab
(9) 接着是调用avtab_read函数,读取te_avtab信息,将所读取的avtab_node数据
插入Hash表中。
(10) 调用cond_read_list读取限制条件链表
(11) 读取role_trans数据
(12) 读取role_allow数据
(13) 调用policydb_index_classes,该函数利用p_commons和p_classes的数据填充
p_common_val_to_name,class_val_to_struct,p_class_val_to_name,它们的存
储结构是Hash表。
(14) 调用policydb_index_others,该函数利用symtab数组中的数据填充
sym_val_to_name中的数据。
(15) 读取文件并填充ocontext[OCON_NUM]数据。
(16) 填充genfs数据结构
(17) 读取MLS相关信息
(18) 填充type_attr_map[]数组,初始化p_types位图
至此policydb_read操作完成,接着第三步调用policydb_load_isid()填充sidtab数据。
该步骤可以分为以下几步:
(1) 调用sidtab_init()函数初始化sidtab
(2) 根据pocontexts[OCON_ISID],即初始化id,对sidtab进行填充操作。
第四步是验证内核定义的类型的正确性。该功能是调用validate_classes实现的。详细的
步骤如下:
(这个涉及的数据比较多)
最后一步是调用下列函数处理加载策略库后的影响:
selinux_complete_init();
avc_ss_reset(seqno);//通知avc模块,进行更新
selnl_notify_policyload(seqno);
selinux_netlbl_cache_invalidate();
selinux_xfrm_notify_policyload();
上面讲了这么还只是讲了整个问题的一半,下面讲如果内核已经存在策略该怎么办。
第一步:policydb_read读取新的策略文件
第二步:sidtab_init(&newsidtab),初始化,没有填充数据
第三步:验证classes正确性
第四步:调用security_preserve_bools()
第五步:利用sidtab_map用newsidtab替换原来的sidtab,在这之前必须先屏蔽掉外部
对sidtab的查询。
第六步:保存原来的policydb和sid表,以备释放空间。
第七步:装在新的policydb和sid表。
第八步:处理其它事情:
policydb_destroy(&oldpolicydb);
sidtab_destroy(&oldsidtab);
avc_ss_reset(seqno);
selnl_notify_policyload(seqno);
selinux_netlbl_cache_invalidate();
selinux_xfrm_notify_policyload();
两种情况的处理方式有所不同,但大部分是相同的。
4.4 策略文件存储的信息
策略文件的信息:类型、类型属性、角色、用户、布尔值、访问向量、转移向量、客体
类、访问模式。
这些都是怎么定义的,怎样形成文件。
4.5 策略文件的组成
SELinux策略存于目录/etc/selinux/下,策略的二进制文件必须存在,默认时策略源代码
文件及其编译工具是不存在的,需要时可从/selinux网址下载。SELinux策略
目录说明如下:
/etc/selinux/
/etc/selinux/
Linux可以有多个策略,运行时只装载一个策略,策略名
strict、webhost和test等。配置文件/etc/selinux/config定义了当前使用的策略,如:
SELINUXTYPE=targeted。
SELinux策略是可配置的,在Fedora Core Linux操作系统中配置的SELinux策略是targeted
策略。在target策略中,除了指定的后台外,每个主体和客体运行在unconfined_t,除了标
准Linux的DAC安全限制外,在unconfined_t域系统上的客体没有任何限制。
与targeted策略相对的是strict策略,strict(严格)策略默认不包含在Linux操作系统
中。在strict策略中,每个主体和客体有一个指定的域,所有的类型交互和转移都单独定义
在策略规则中,这是非常复杂的环境。
/etc/selinux/config文件控制SELinux是激活还是关闭,如果激活,SELinux的运行模式是
许可(permissive)或者强制(enforcing)模式。关闭SELinux将关闭SELinux内核和应用程
序代码,系统在没有任何SELinux保护下运行,许可模式表示激活了SELinux代码,但SELinux
策略不否决操作,只是把违背策略的操作信息审核记录到log系统中。强制策略则对违背策
略的操作进行否决,并记录信息到log系统中。
/bookfiles/574/
4.6 接口
安全服务器所有的接口:
int security_load_policy(void * data, size_t len);
该函数在4.3节已经进行详细描述,在这里就不再描述了。
int security_compute_av(u32 ssid, u32 tsid,u16 tclass, u32 requested,struct av_decision
*avd);
该接口是访问策略的最主要的策略计算函数,主要功能都是由它完成。它的主要调用者
是avc_has_perm,该接口的设计目的是为AVC模块提供服务。下面我们就来详细了解一下
它的设计思路:
int security_compute_av(u32 ssid,
u32 tsid,
u16 tclass,
u32 requested,
struct av_decision *avd)
{
struct context *scontext = NULL, *tcontext = NULL;
int rc = 0;
if (!ss_initialized) {//没有加载策略库
avd->allowed = 0xffffffff;
avd->decided = 0xffffffff;//目前还不知道有什么用
avd->auditallow = 0;
avd->auditdeny = 0xffffffff;
avd->seqno = latest_granting;
return 0;
}
POLICY_RDLOCK;
scontext = sidtab_search(&sidtab, ssid);//将ssid转换成scontext
if (!scontext) {
printk(KERN_ERR "security_compute_av: unrecognized SID %dn",
ssid);
rc = -EINVAL;
goto out;
}
tcontext = sidtab_search(&sidtab, tsid);//将tsid转换成tcontext
if (!tcontext) {
printk(KERN_ERR "security_compute_av: unrecognized SID %dn",
tsid);
rc = -EINVAL;
goto out;
}
rc = context_struct_compute_av(scontext, tcontext, tclass,
requested, avd);
out:
POLICY_RDUNLOCK;
return rc;
}
从上面源码可以看出,如果策略库没有加载,就直接给avd默认值。默认值是所有的权
限都允许。在内核中已经成功加载策略库的情况下,系统已经构建好了SID表和策略库相关
的数据结构。首先通过sidtab_search()函数查询SID表,将安全ID转换成安全上下文,
得到scontext和tcontext。该函数的主体部分知识完成了上述简单的功能,更复杂的功能交
给了context_struct_compute_av()函数完成。为了更加深刻的了解这一过程,需要进一步
的深入分析。下面是context_struct_compute_av()的定义:
static int context_struct_compute_av(struct context *scontext, struct context *tcontext,
u16 tclass, u32 requested, struct av_decision *avd)
{
struct constraint_node *constraint;
struct role_allow *ra;
struct avtab_key avkey;
struct avtab_node *node;
struct class_datum *tclass_datum;
struct ebitmap *sattr, *tattr;
struct ebitmap_node *snode, *tnode;
unsigned int i, j;
/*
* Remap extended Netlink classes for old policy versions.
* Do this here rather than socket_type_to_security_class()
* in case a newer policy version is loaded, allowing sockets
* to remain in the correct class.
*/
if (policydb_loaded_version < POLICYDB_VERSION_NLCLASS)
if (tclass >= SECCLASS_NETLINK_ROUTE_SOCKET &&
tclass <= SECCLASS_NETLINK_DNRT_SOCKET)
tclass = SECCLASS_NETLINK_SOCKET;
if (!tclass || tclass > policydb.p_) {//判断tclass是否超过边界
printk(KERN_ERR "security_compute_av: unrecognized class %dn",
tclass);
return -EINVAL;
}
tclass_datum = _val_to_struct[tclass - 1];//客体类型的权限信息
/*
* 将访问控制向量设置为空值.
*/
avd->allowed = 0;
avd->decided = 0xffffffff;
avd->auditallow = 0;
avd->auditdeny = 0xffffffff;
avd->seqno = latest_granting;
/*
* If a specific type enforcement rule was defined for
* this permission check, then use it.
*/
_class = tclass;//将avkey的target_class数据成员设置为tclass
ied = AVTAB_AV;//对avkey的specified数据成员进行初始化
sattr = &_attr_map[scontext->type - 1];//查找主体的属性信息
tattr = &_attr_map[tcontext->type - 1];//查找客体的属性信息
ebitmap_for_each_bit(sattr, snode, i) {
if (!ebitmap_node_get_bit(snode, i))
continue;
ebitmap_for_each_bit(tattr, tnode, j) {
if (!ebitmap_node_get_bit(tnode, j))
continue;
_type = i + 1;//计算出avkey的source_type和target_type值
_type = j + 1;
//通过avkey查找te策略,并将权限保存在avd的中
for (node = avtab_search_node(&_avtab, &avkey);
node != NULL;
node = avtab_search_node_next(node, ied)) {
if (node->ied == AVTAB_ALLOWED)
avd->allowed |= node->;
else if (node->ied == AVTAB_AUDITALLOW)
avd->auditallow |= node->;
else if (node->ied == AVTAB_AUDITDENY)
avd->auditdeny &= node->;
}
/* Check conditional av table for additional permissions */
cond_compute_av(&_cond_avtab, &avkey, avd);
}
}
}
/*
* 去掉constraint中禁止的权限(包括MLS安全策略)
*/
constraint = tclass_datum->constraints;//该数据记录在客体类型tclass中
while (constraint) {
if ((constraint->permissions & (avd->allowed)) &&
!constraint_expr_eval(scontext, tcontext, NULL,
constraint->expr)) {
avd->allowed = (avd->allowed) & ~(constraint->permissions);
}
constraint = constraint->next;
}
/*
* 检查进程域权限和角色时候转换,如果转换,就检查角色转换表
*/
if (tclass == SECCLASS_PROCESS &&
(avd->allowed & (PROCESS__TRANSITION | PROCESS__DYNTRANSITION)) &&
scontext->role != tcontext->role) {
for (ra = _allow; ra; ra = ra->next) {
if (scontext->role == ra->role &&
tcontext->role == ra->new_role)
break;
}
if (!ra)
avd->allowed = (avd->allowed) & ~(PROCESS__TRANSITION |
PROCESS__DYNTRANSITION);
}
return 0;
该函数首先为老的策略版本,重映射扩展的Netlink类型。第二步是检查tclass是否定
义,如果没有定义就直接返回错误值。如果能够正常识别,则通过tclass在policydb中查找
到相应的tclass_datum数据。
第三步是为了填充avd中的数据,首先对它采用默认值进行初始化,具体的初始化值可
以查看上述源码。初始化完成后,接下来很大一部分工作是完成te策略的获取,他们通过
policydb的type_attr_map数组,查找出主体和客体的属性sattr和tattr,通过该属性在位图
中的位置,初始化avkey的数据成员。avkey在这个过程中扮演了很重要的角色,它的类型
如下:
struct avtab_key {
u16 source_type; /* source type */
u16 target_type; /* target type */
u16 target_class; /* target object class */
u16 specified; /* what field is specified */
};
它只有四个成员,查找sattr和tattr在位图中的位置,分别加1之后初始化avkey的
source_type和target_type。接下来是根据avkey在policydb的te_avtab成员中查找相应的节
点分别对avd结构的allowed,auditallow,auditdeny进行赋值。到此上述avkey的数据成员
的赋值就讲完了。这个数据结构很重要,它是后面所有查询的基础。
进行完te策略的分析后,需要检查conditional av table的附加条件。该功能是通过
cond_compute_av()实现的。有上图中红色标识可以看出,条件向量表是由policydb的
te_cond_avtab结构成员中。
下面是cond_compute_av()的定义:
void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd)
{
struct avtab_node *node;
if(!ctab || !key || !avd)
return;
for(node = avtab_search_node(ctab, key); node != NULL;
node = avtab_search_node_next(node, key->specified)) {
if ( (u16) (AVTAB_ALLOWED|AVTAB_ENABLED) ==
(node->ied & (AVTAB_ALLOWED|AVTAB_ENABLED)))
avd->allowed |= node->;
if ( (u16) (AVTAB_AUDITDENY|AVTAB_ENABLED) ==
(node->ied & (AVTAB_AUDITDENY|AVTAB_ENABLED)))
avd->auditdeny &= node->;
if ( (u16) (AVTAB_AUDITALLOW|AVTAB_ENABLED) ==
(node->ied & (AVTAB_AUDITALLOW|AVTAB_ENABLED)))
avd->auditallow |= node->;
}
return;
}
有上图可以看出,修正的依据是nodeied,而修正的权限数据时保存在node
的datum的data中,它是一个无符号的32位的整数。
继续阅读context_struct_compute_av的源码,你会发现我们下一个需要说明的是禁止策
略。对禁止策略表进行查询,禁止策略信息保存在tclass_datum的结构中。该部分是对
avdallowed规则进行修正。该部分采取的是链表作为存储结构,很容易读懂,没有什么难
理解的地方。
接下来是对进程域转换和角色转换进行处理,这个是对进程的PROCESS__TRANSITION
和PROCESS__DYNTRANSITION权限检查,如果没有该权限,就从avdallowed中删除相应的
标志。
至此,完成了整个策略的计算过程,下面将上面的整个过程整理成一个图,可以更好的
理解其中变量的变化。从下图可以看出,一切都是绕avd为核心的,其中te策略是核心,
其它的策略都是对该策略的修正,包括MLS多级访问控制策略。
Security_comput_avu32 ssidu32 tsidu16 tclassu32 requested
struct av_decision
*avd
sidtab_search(&sidtab, ssid)
sidtab_search(&sidtab, tsid)
context_struct_compute_av
struct context
*scontext
struct context
*tcontext
u16 tclassu32 requested
struct av_decision
*avd
&_attr_map[scont
ext->type - 1]
&_attr_map[tcont
ext->type - 1]
_val_to_
struct[tclass - 1]
struct ebitmap sattrstruct ebitmap tattr
avd->allowed = 0;
avd->decided = 0xffffffff;
avd->auditallow = 0;
avd->auditdeny = 0xffffffff;
avd->seqno = latest_granting;
tclass_datum
查找位图
struct avtab_keyu16 source_typeu16 target_typeu16 target_classu16 specified
node = avtab_search_node(&_avtab, &avkey)
node = avtab_search_node_next(node, ied)
if (node->ied == AVTAB_ALLOWED)
avd->allowed |= node->;
else if (node->ied == AVTAB_AUDITALLOW)
avd->auditallow |= node->;
else if (node->ied == AVTAB_AUDITDENY)
avd->auditdeny &= node->;
struct avtab_node
*node
tclass_datum->constraintsavd
tclass进程切换
cond_compute_av(&_cond_avtab, &avkey, avd)
图 策略决策过程
介绍提供给AVC的重要接口,我们下面来介绍策略库给Hooks函数提供的重要的接口
函数。Security_transition_sid(),它的声明是在security/selinux/include/security.h中,声明
如下所示:
int security_transition_sid(u32 ssid, u32 tsid,u16 tclass, u32 *out_sid);
为了更好的理解其中的过程,我把它的定义在这里也附上了。
int security_transition_sid(u32 ssid, u32 tsid, u16 tclass, u32 *out_sid)
{
return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, out_sid);
}
它是通过调用内部函数security_compute_sid,因为该函数根据有通用性。它的功能是
为一个新的对象分配一个安全标识SID,参数out_sid是新的分配sid。它先计算出该新对象
的安全上下文,对安全上下文结构中的数据成员一一赋值,最后调用sidtab_context_to_sid,
将为新的安全上下文分配新的sid,并把该安全上下文插入到sidtab中,以备下次查询。下
面是它的定义,关键的地方已经做了注释:
static int security_compute_sid(u32 ssid,
u32 tsid,
u16 tclass,
u32 specified,
u32 *out_sid)
{
struct context *scontext = NULL, *tcontext = NULL, newcontext;
struct role_trans *roletr = NULL;
struct avtab_key avkey;
struct avtab_datum *avdatum;
struct avtab_node *node;
int rc = 0;
if (!ss_initialized) {//如果内核中没有安全策略库信息
switch (tclass) {
case SECCLASS_PROCESS:
*out_sid = ssid;
break;
default:
*out_sid = tsid;
break;
}
goto out;
}
//下面是策略库已经初始化的情况
context_init(&newcontext);//初始化安全上下文结构
POLICY_RDLOCK;//读策略库需要加锁
scontext = sidtab_search(&sidtab, ssid);//查找主体的安全上下文
if (!scontext) {
printk(KERN_ERR "security_compute_sid: unrecognized SID %dn",
ssid);
rc = -EINVAL;
goto out_unlock;
}
tcontext = sidtab_search(&sidtab, tsid);//查找客体的安全上下文
if (!tcontext) {
printk(KERN_ERR "security_compute_sid: unrecognized SID %dn",
tsid);
rc = -EINVAL;
goto out_unlock;
}
/* 设置用户的标识 */
switch (specified) {
case AVTAB_TRANSITION:
case AVTAB_CHANGE:
/* Use the process user identity. */
= scontext->user;
break;
case AVTAB_MEMBER:
/* Use the related object owner. */
= tcontext->user;
break;
}
/* 设置用户和客体类型为默认值 */
switch (tclass) {
case SECCLASS_PROCESS:
/* Use the current role and type of process. */
= scontext->role;
= scontext->type;
break;
default:
/* Use the well-defined object role. */
= OBJECT_R_VAL;
/* Use the type of the related object. */
= tcontext->type;
}
/* 查找客体类型转换,成员转换,或转化策略 */
_type = scontext->type;
_type = tcontext->type;
_class = tclass;
ied = specified;
avdatum = avtab_search(&_avtab, &avkey);//在TE策略表中查找
/*如果没有固定的策略,就在条件规则中查找 */
if(!avdatum) {
node = avtab_search_node(&_cond_avtab, &avkey);//
_cond_avtab中查找
for (; node != NULL; node = avtab_search_node_next(node, specified)) {
if (node->ied & AVTAB_ENABLED) {
avdatum = &node->datum;
在
break;
}
}
}
if (avdatum) {
/* 使用类型转换规则 */
= avdatum->data;
}
/* 查找客体类型特定的规则*/
switch (tclass) {
case SECCLASS_PROCESS:
if (specified & AVTAB_TRANSITION) {//只有客体是进程的时候才有
/* Look for a role transition rule. */
for (roletr = _tr; roletr;
roletr = roletr->next) {
if (roletr->role == scontext->role &&
roletr->type == tcontext->type) {
/* Use the role transition rule. */
= roletr->new_role;
break;
}
}
}
break;
default:
break;
}
/* 设置MLS属性 */
rc = mls_compute_sid(scontext, tcontext, tclass, specified, &newcontext);
if (rc)
goto out_unlock;
/* 检查安全上下文的合法性 */
if (!policydb_context_isvalid(&policydb, &newcontext)) {
rc = compute_sid_handle_invalid_context(scontext,
tcontext,
tclass,
&newcontext);
if (rc)
goto out_unlock;
}
/* 为新的context获取sid */
rc = sidtab_context_to_sid(&sidtab, &newcontext, out_sid);
out_unlock:
POLICY_RDUNLOCK;
context_destroy(&newcontext);//释放掉分配安全上下文结构
out:
return rc;
}
为了能够更深入的了解sid的分配过程,就不得不分析sidtab_context_to_sid函数了。
int sidtab_context_to_sid(struct sidtab *s,
struct context *context,
u32 *out_sid)
{
u32 sid;
int ret = 0;
unsigned long flags;
*out_sid = SECSID_NULL;
sid = sidtab_search_context(s, context);//查找该安全上下文是否存在
if (!sid) {
SIDTAB_LOCK(s, flags);
/* 重新扫描需要加锁 */为什么?
sid = sidtab_search_context(s, context);
if (sid)
goto unlock_out;
/*该context不存在,分配一个新的。*/
if (s->next_sid == UINT_MAX || s->shutdown) {
ret = -ENOMEM;
goto unlock_out;
}//检查sid是否被分配完
sid = s->next_sid++;//新的sid为sidtab中的next_sid,并将next_sid加1,为下
一次做准备
ret = sidtab_insert(s, sid, context);
if (ret)
s->next_sid--;
unlock_out:
SIDTAB_UNLOCK(s, flags);
}
if (ret)
return ret;
*out_sid = sid;
return 0;
}
int security_member_sid(u32 ssid, u32 tsid,u16 tclass, u32 *out_sid);
调用security_compute_sid(ssid, tsid, tclass, AVTAB_MEMBER, out_sid),该函数在上面已经
讲过了。
int security_change_sid(u32 ssid, u32 tsid,u16 tclass, u32 *out_sid);
调用security_compute_sid(ssid, tsid, tclass, AVTAB_CHANGE, out_sid),同上。
int security_sid_to_context(u32 sid, char **scontext,u32 *scontext_len);
根据sid,获得context。
int security_context_to_sid(char *scontext, u32 scontext_len,u32 *out_sid);
通过context获取sid。
int security_context_to_sid_default(char *scontext, u32 scontext_len, u32 *out_sid, u32
def_sid);
如果操作失败,就把sid设为def_sid
int security_get_user_sids(u32 callsid, char *username,u32 **sids, u32 *nel);
int security_port_sid(u16 domain, u16 type, u8 protocol, u16 port,u32 *out_sid);
int security_netif_sid(char *name, u32 *if_sid,u32 *msg_sid);
int security_node_sid(u16 domain, void *addr, u32 addrlen,u32 *out_sid);
int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,u16 tclass);
int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid);
int security_get_classes(char ***classes, int *nclasses);
int security_get_permissions(char *class, char ***perms, int *nperms);
int security_fs_use(const char *fstype, unsigned int *behavior,u32 *sid);
int security_genfs_sid(const char *fstype, char *name, u16 sclass,u32 *sid);
int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,u32 base_sid,u32 *sid);
int security_netlbl_sid_to_secattr(u32 sid,struct netlbl_lsm_secattr *secattr);
const char *security_get_initial_sid_context(u32 sid);
第五章 SELinux源码文件
内核selinux的组织结构
Selinux模块由安全服务器、AVC(Access Vector Cache,简称AVC)、网络接口表、netlink
事件(这个是什么?)通知代码、selinuxfs伪文件系统和hook函数应用六个部分组成。安全服
务器为获得安全策略决策提供通用接口,使模块的其余部分保持独立的安全策略。这些接口
定义在include/security.h中,安全服务器的特定应用能在不改变模块其他部分的情况下被改
变。
安全服务器的应用有RBAC(基于角色的访问控制,Role-Base Access Control,简称RABC)、
TE(类型加强,Type Enforcement,简称TE)的生成和可选的MLS(多级安全,Multi-Level
Security,简称MLS)。其中,RBAC和TE策略是高度可配置的,能用于多种不同的安全对象。
安全服务器的源代码在security/selinux/ss目录下。
AVC提供了从安全服务器获得的访问策略的缓冲区(cache),提高了安全机制的运行性
能。它提供了hook函数高效检查授权的接口,提供了安全服务器管理cache的接口。与hook
函数的接口定义在include/avc.h中,与服务器的接口定义在include/avc_ss.h中,AVC代码
在avc.c中。
网络接口表将网络设备映射到安全上下文中。维护一个独立的表是必要的,因为LSM
网络设备没有安全成员。当网络设备被hook函数第一次查找到时该网络设备就被加入到这
个表中,当设备被设置取消或策略重载时移出这个表。网络接口表提供了查找hook函数以
及获得网络设备条目引用或释放引用的接口函数。该接口定义在include/netif.h中,回调函
数在设备配置变化或策略重载时注册。网络接口表的代码在netif.c中。(这个非常特殊,要
特殊处理)
Netlink事件通知让SELinux模块在策略重载以及强制状态改变时通知进程。这些通知
被用户空间的AVC(libselinux的一部分)用来保持与内核一致的状态。用户空间AVC被用
户空间策略强制器(enforces)使用,代码在netlink.c中。
SELinux伪文件系统给进程提供安全服务器的策略API。SELinux提供基于策略API的
调用,它包括进程属性、文件属性和策略API三个部分,并被高层的libselinux API封装起
来,代码在selinuxfs.c中。
Hook函数应用管理与内核对象相关的安全信息以及执行内核每个操作的访问控制。
Hook函数调用安全服务器和AVC得到安全策略并运用这些策略来标识和控制内核对象。
Hook函数还调用文件系统扩展属性代码获得和设备文件上面的安全上下文。Hook函数在
hooks.c中,与内核对象相关的安全信息的数据结构定义在include/objsec.h中。SELinux安
全模块及接口如下图所示。
图 5.1
Linux内核对于程序的运行、文件系统的超级块和节点以及文件操作、任务操作、网络
连接、socket、System V进程间通信等提供了对应的安全操作函数,这些函数指针都放在安
全操作函数结构中,由于结构很大,这里只列出了程序运行的操作函数指针,这些操作函数
指针的前缀bprm是bin program的缩写,其他的操作函数具有类似结构。
Security_operatrions安全操作函数结构在include/linux/security.h中,分析如下:(见前
面)
Security_operations的结构很大,这里只列出了很少的一部分,下面就一些成员函数说
明如下:
Bprm_alloc_security:分配或附加上一个security结构到bprmsecurity成员上,操作成
功返回0.
Bprm_free_security: 释放或删除bprmsecurity field成员上的security结构。
Bprm_set_security: 计算或设置进程的安全属性,这个进程是由execve操作生成的,这
个操作时基于旧属性(当前进程的安全属性currentsecurity)和被set_security函数设置存
在bprmsecurity里的信息。由于set_security函数是void类型,这个函数不能返回错误,
如果属性设置失败,则安全属性值不会改变。
Bprm_apply_creds:该函数在任务锁住的情况下被调用,参数unsafe表示可能不安全地
转换安全状态的各种原因。
Bprm_set_security:将安全信息存放到bprmsecurity成员中。
Bprm_check_security:检查set_security调用时设置的值是否设置到bprmsecurity中,
返回0表示已授予设置的权限。
Bprm_secureexec:返回值是1或0,表示是否安全运行,这个标识传进ELF解释器的初
始堆栈上的补充表里,用来表示libc是否能使用安全模式。
图 5.2
根据图5.1和图5.2,可以把整个SELinux源码文件分成五部分:
5.1 独立的安全模块
Linux/security/capability.c
Linux/security/root_plug.c
5.2 Hook函数
Linux/security/selinux/hooks.c
5.3 访问向量缓存
Linux/security/selinux/avc.c
Linux/security/selinux/include/avc.h
5.4 安全服务器
Linux/security/selinux/ss/
5.5 二进制策略文件
二进制文件解析
第六章 Userspace
6.1 checkpolicy
program to compile policies to binary form
将策略文件编译成二进制文件,加载到内核之前,必须是二进制文件。
6.2 libselinux
library for security-aware applications
Used by SELinux aware applications
Houses user space AVC
Contains functions to
calculate AVCs
get/set/create contexts
query policy engine
6.3 libsemanage
library for policy management
Used to query and configure state of a running system
Provides functions to query/modify
login names
users
network ports/interfaces
file contexts
level translations
roles
etc.
6.4 libsepol
library for binary policy manipulation
6.5 policycoreutils
core set of policy-related utilities
SELinux Management and policy analysis tools
audit2allow
audit2why
load_policy
newrole
restorecon
semanage
semodule
sestatus
setbool
6.6 sepolgen
a python library for parsing and modifying policy source
6.7 SELinuxfs
Interface between userspace and kernel
Used by libselinux and libsemanage to communicate requests with the kernel
Provides a quick and easy interface for humans
Usually not used directly from programs
第七章 附录
Security_operations结构
/**
* struct security_operations - main security structure //security_operations主要的安全结构
*
* Security hooks for program execution operations.//安全Hook,主要是为程序执行过程进行
策略检查
*
* @bprm_alloc_security://bprm是linux_binprm二进制文件结构,linux_binprm定义时,
security数据项是空指针域,该域使用的时候才动态分配大小。Security域在初始化时为NULL,
该函数是为了给security指针域关联一个结构。
* Allocate and attach a security structure to the @bprm->security field.
* The security field is initialized to NULL when the bprm structure is
* allocated.
* @bprm contains the linux_binprm structure to be modified.
* Return 0 if operation was successful.
* @bprm_free_security://释放security指针域
* @bprm contains the linux_binprm structure to be modified.
* Deallocate and clear the @bprm->security field.
* @bprm_apply_creds://计算并设置进程的转换属性,该属性的转换是根据旧的属性和
set_security所设置的bprm_security值。执行该操作是会调用进程锁,不会返回一个错误结
果。
* Compute and set the security attributes of a process being transformed
* by an execve operation based on the old attributes (current->security)
* and the information saved in @bprm->security by the set_security hook.
* Since this hook function (and its caller) are void, this hook can not
* return an error. However, it can leave the security attributes of the
* process unchanged if an access failure occurs at this point.
* bprm_apply_creds is called under task_lock. @unsafe indicates various
* reasons why it may be unsafe to change security state.
* @bprm contains the linux_binprm structure.
* @bprm_post_apply_creds://在bprm_apply_creds操作之后执行,也需要进程锁进行互斥
操作,这个hook函数能够执行进程的状态改变,如当属性改变后,哪些已经打开的文件描
述符不能够再访问。
* Runs after bprm_apply_creds with the task_lock dropped, so that
* functions which cannot be called safely under the task_lock can
* be used. This hook is a good place to perform state changes on
* the process such as closing open file descriptors to which access
* is no longer granted if the attributes were changed.
* Note that a security module might need to save state between
* bprm_apply_creds and bprm_post_apply_creds to store the decision
* on whether the process may proceed.//安全模块可能要保存bprm_apply_creds和
bprm_post_apply_creds操作之间的状态。
* @bprm contains the linux_binprm structure.
* @bprm_set_security://在bprm的security中保存安全信息,该安全信息主要是根据
bprm->file。这个hook函数可以选择的检测。
* Save security information in the bprm->security field, typically based
* on information about the bprm->file, for later use by the apply_creds
* hook. This hook may also optionally check permissions (e.g. for
* transitions between security domains).
* This hook may be called multiple times during a single execve, e.g. for
* interpreters. The hook can tell whether it has already been called by
* checking to see if @bprm->security is non-NULL. If so, then the hook
* may decide either to retain the security information saved earlier or
* to replace it.
* @bprm contains the linux_binprm structure.
* Return 0 if the hook is successful and permission is granted.
* @bprm_check_security://该函数仲裁决定什么时候开始查找二进制处理器。它允许测试
set_security调用中设置的值。调用它之前,必须调用set_security。
* This hook mediates the point when a search for a binary handler will
* begin. It allows a check the @bprm->security value which is set in
* the preceding set_security call. The primary difference from
* set_security is that the argv list and envp list are reliably
* available in @bprm. This hook may be called multiple times
* during a single execve; and in each pass set_security is called
* first.
* @bprm contains the linux_binprm structure.
* Return 0 if the hook is successful and permission is granted.
* @bprm_secureexec://检测是否需要安全执行。
* Return a boolean value (0 or 1) indicating whether a "secure exec"
* is required. The flag is passed in the auxiliary table
* on the initial stack to the ELF interpreter to indicate whether libc
* should enable secure mode.
* @bprm contains the linux_binprm structure.
*
* Security hooks for filesystem operations.//文件系统的安全Hook函数,sb是super_block简
写,s_security是sb的安全域
*
* @sb_alloc_security://给sb->security指针分配或者赋值一个安全的结构
* Allocate and attach a security structure to the sb->s_security field.
* The s_security field is initialized to NULL when the structure is
* allocated.
* @sb contains the super_block structure to be modified.
* Return 0 if operation was successful.
* @sb_free_security://将sb->security置为空,并将所指向的内存区域释放掉
* Deallocate and clear the sb->s_security field.
* @sb contains the super_block structure to be modified.
* @sb_statfs://在获取文件系统mnt的统计数据之前进行许可验证,dentry是文件系统
superblock的句柄。
* Check permission before obtaining filesystem statistics for the @mnt
* mountpoint.
* @dentry is a handle on the superblock for the filesystem.
* Return 0 if permission is granted.
* @sb_mount://在特殊的文件系统挂载之前进行许可验证。
* Check permission before an object specified by @dev_name is mounted on
* the mount point named by @nd. For an ordinary mount, @dev_name
* identifies a device if the file system type requires a device. For a
* remount (@flags & MS_REMOUNT), @dev_name is irrelevant. For a
* loopback/bind mount (@flags & MS_BIND), @dev_name identifies the
* pathname of the object being mounted.
* @dev_name contains the name for object being mounted.
* @nd contains the nameidata structure for mount point object.//挂载点
* @type contains the filesystem type.
* @flags contains the mount flags.//挂载标志
* @data contains the filesystem-specific data.
* Return 0 if permission is granted.
* @sb_copy_data://进行特殊数据的挂载
* Allow mount option data to be copied prior to parsing by the filesystem,
* so that the security module can extract security-specific mount
* options cleanly (a filesystem may modify the with strsep()).
* This also allows the original mount data to be stripped of security-
* specific options to avoid having to make filesystems aware of them.
* @type the type of filesystem being mounted.//文件系统的类型
* @orig the original mount data copied from userspace.
* @copy copied data which will be passed to the security module.//拷贝的数据会传送给安
全模块
* Returns 0 if the copy was successful.
* @sb_check_sb://在nd上挂载mnt->sb设备之前进行检查
* Check permission before the device with superblock @mnt->sb is mounted
* on the mount point named by @nd.
* @mnt contains the vfsmount for device being mounted.
* @nd contains the nameidata object for the mount point.
* Return 0 if permission is granted.
* @sb_umount://卸载文件系统之间进行检查
* Check permission before the @mnt file system is unmounted.
* @mnt contains the mounted file system.
* @flags contains the unmount flags, e.g. MNT_FORCE.
* Return 0 if permission is granted.
* @sb_umount_close://在umount操作之间检查是否有文件被打开,如果有打开的文件,
就将其关闭
* Close any files in the @mnt mounted filesystem that are held open by
* the security module. This hook is called during an umount operation
* prior to checking whether the filesystem is still busy.
* @mnt contains the mounted filesystem.
* @sb_umount_busy://在调用sb_umount_close失败调用,重新打开sb_umount_close关闭
的文件
* Handle a failed umount of the @mnt mounted filesystem, e.g. re-opening
* any files that were closed by umount_close. This hook is called during
* an umount operation if the umount fails after a call to the
* umount_close hook.
* @mnt contains the mounted filesystem.
* @sb_post_remount://当一个文件系统重新挂载时,更新安全模块的状态,该函数只有在
remount调用成功后才会调用。
* Update the security module's state when a filesystem is remounted.
* This hook is only called if the remount was successful.
* @mnt contains the mounted file system.
* @flags contains the new filesystem flags.
* @data contains the filesystem-specific data.
* @sb_post_mountroot://当根目录文件系统被挂载时,更新安全模块的状态,该函数只有
在mount操作成功后才会被调用。
* Update the security module's state when the root filesystem is mounted.
* This hook is only called if the mount was successful.
* @sb_post_addmount://当一个文件系统被挂载时,更新安全模块的状态。
* Update the security module's state when a filesystem is mounted.
* This hook is called any time a mount is successfully grafetd to
* the tree.
* @mnt contains the mounted filesystem.
* @mountpoint_nd contains the nameidata structure for the mount point.
* @sb_pivotroot://在转换根文件系统之前进行检查
* Check permission before pivoting the root filesystem.
* @old_nd contains the nameidata structure for the new location of the current root
(put_old).
* @new_nd contains the nameidata structure for the new root (new_root).
* Return 0 if permission is granted.
* @sb_post_pivotroot://成功pivot之后更新模块的状态
* Update module state after a successful pivot.
* @old_nd contains the nameidata structure for the old root.
* @new_nd contains the nameidata structure for the new root.
*
* Security hooks for inode operations.
*
* @inode_alloc_security://给inode->security指针分配内存,或者赋值
* Allocate and attach a security structure to @inode->i_security. The
*
*
*
*
i_security field is initialized to NULL when the inode structure is
allocated.
@inode contains the inode structure.
Return 0 if operation was successful.
* @inode_free_security://释放inode->security内存
* @inode contains the inode structure.
* Deallocate the inode security structure and set @inode->i_security to
* NULL.
* @inode_init_security://为新的inode设置安全域,该函数在inode创建和创建原子标签的
时候,由文件系统调用。Inode创建时调用
* Obtain the security attribute name suffix and value to set on a newly
* created inode and set up the incore security field for the new inode.
* This hook is called by the fs code as part of the inode creation
* transaction and provides for atomic labeling of the inode, unlike
*
*
*
*
*
*
*
*
*
*
*
*
*
the post_create/mkdir/... hooks called by the VFS. //mkdir由VFS调用 The hook function
is expected to allocate the name and value via kmalloc, with the caller
being responsible for calling kfree after using them.
If the security module does not use security attributes or does
not wish to put a security attribute on this particular inode,
then it should return -EOPNOTSUPP to skip this processing.
@inode contains the inode structure of the newly created inode.//新创建的i节点
@dir contains the inode structure of the parent directory.//i节点的父目录
@name will be set to the allocated name suffix (e.g. selinux).//命名的后缀
@value will be set to the allocated attribute value.//所属性值
@len will be set to the length of the value.//属性值所占的字节数
Returns 0 if @name and @value have been successfully set,
-EOPNOTSUPP if no security attribute is needed, or
* -ENOMEM on memory allocation failure.//内存分配失败
* @inode_create://检查主体创建普通文件的权限
* Check permission to create a regular file.
*
*
构
*
*
@dir contains inode structure of the parent of the new file.//父目录
@dentry contains the dentry structure for the file to be created.//所创建文件的dentry结
@mode contains the file mode of the file to be created.//文件创建的类型
Return 0 if permission is granted.
* @inode_link://检查主体是否有创建硬链接的权限
* Check permission before creating a new hard link to a file.
* @old_dentry contains the dentry structure for an existing link to the file.//原来的dentry结
构
* @dir contains the inode structure of the parent directory of the new link.//父目录
* @new_dentry contains the dentry structure for the new link.//新的dentry结构
* Return 0 if permission is granted.
* @inode_unlink://检查主体是否有解除硬链接的权限
* Check the permission to remove a hard link to a file.
* @dir contains the inode structure of parent directory of the file.
* @dentry contains the dentry structure for file to be unlinked.
* Return 0 if permission is granted.
* @inode_symlink://符号链接文件,即软链接
* Check the permission to create a symbolic link to a file.
* @dir contains the inode structure of parent directory of the symbolic link.
* @dentry contains the dentry structure of the symbolic link.
* @old_name contains the pathname of file.
* Return 0 if permission is granted.
* @inode_mkdir://检查主体是否拥有创建目录的权限
* Check permissions to create a new directory in the existing directory
* associated with inode strcture @dir.
*
*
*
*
@dir containst the inode structure of parent of the directory to be created.//父目录
@dentry contains the dentry structure of new directory.//新目录的dentry结构
@mode contains the mode of new directory.//保护模式
Return 0 if permission is granted.
* @inode_rmdir://检查主体是否拥有删除目录的权限
* Check the permission to remove a directory.
* @dir contains the inode structure of parent of the directory to be removed.//父目录
* @dentry contains the dentry structure of directory to be removed.//被删除的目录项
* Return 0 if permission is granted.
* @inode_mknod://mknod是创建一种特殊的文件,如socket或者fifo文件,检查相关的使
用权限
* Check permissions when creating a special file (or a socket or a fifo
* file created via the mknod system call). Note that if mknod operation
* is being done for a regular file, then the create hook will be called
* and not this hook.
* @dir contains the inode structure of parent of the new file.//父目录
* @dentry contains the dentry structure of the new file.//新的目录项
* @mode contains the mode of the new file.
* @dev contains the the device number.//设备号
* Return 0 if permission is granted.
* @inode_rename://对重命名权限进行检验
* Check for permission to rename a file or directory.
* @old_dir contains the inode structure for parent of the old link.
* @old_dentry contains the dentry structure of the old link.
* @new_dir contains the inode structure for parent of the new link.
* @new_dentry contains the dentry structure of the new link.
* Return 0 if permission is granted.
* @inode_readlink://对主体对客体的读取符号链接的权限进行检验
* Check the permission to read the symbolic link.
* @dentry contains the dentry structure for the file link.//内存中的目录项
* Return 0 if permission is granted.
* @inode_follow_link://检查主体对客体通过符号链接查询路径名
*
*
*
*
Check permission to follow a symbolic link when looking up a pathname.
@dentry contains the dentry structure for the link.
@nd contains the nameidata structure for the parent directory.
Return 0 if permission is granted.
* @inode_permission://访问节点进行权限验证,该钩子函数有存在的linux允许函数调用,
因此安全模块可以用它来存在的linux允许进行检查。如该函数当文件被打开后进行调用,
不管系统是否获得实际的读写权限。
* Check permission before accessing an inode. This hook is called by the
* existing Linux permission function, so a security module can use it to
* provide additional checking for existing Linux permission checks.
* Notice that this hook is called when a file is opened (as well as many
* other operations), whereas the file_security_ops permission hook is
* called when the actual read/write operations are performed.
* @inode contains the inode structure to check.
* @mask contains the permission mask.//权限掩码
* @nd contains the nameidata (may be NULL).
* Return 0 if permission is granted.
* @inode_setattr://检查主体对客体是否拥有设置文件属性的权限
* Check permission before setting file attributes. Note that the kernel
* call to notify_change is performed from several locations, whenever
* file attributes change (such as when a file is truncated, chown/chmod
* operations, transferring disk quotas, etc).
* @dentry contains the dentry structure for the file.
* @attr is the iattr structure containing the new file attributes.
* Return 0 if permission is granted.
* @inode_getattr://主体获取客体的属性之前进行检查
* Check permission before obtaining file attributes.
* @mnt is the vfsmount where the dentry was looked up
* @dentry contains the dentry structure for the file.
* Return 0 if permission is granted.
* @inode_delete://删除inode时进行检查
* @inode contains the inode structure for deleted inode.
* This hook is called when a deleted inode is released (i.e. an inode
* with no hard links has its use count drop to zero). A security module
* can use this hook to release any persistent label associated with the
* inode.
* @inode_setxattr://设置文件的扩展属性进行检查
* Check permission before setting the extended attributes
* @value identified by @name for @dentry.
* Return 0 if permission is granted.
* @inode_post_setxattr://成功设置扩展属性后,更新inode节点的安全上下文
* Update inode security field after successful setxattr operation.
* @value identified by @name for @dentry.
* @inode_getxattr://对获取扩展属性时进行检查
* Check permission before obtaining the extended attributes
* identified by @name for @dentry.
* Return 0 if permission is granted.
* @inode_listxattr://获取扩展属性列表时进行检查
* Check permission before obtaining the list of extended attribute
* names for @dentry.
* Return 0 if permission is granted.
* @inode_removexattr://删除扩展属性时进行检查
* Check permission before removing the extended attribute
* identified by @name for @dentry.//dentry由名字参数进行标识
* Return 0 if permission is granted.
* @inode_getsecurity://拷贝@name安全标签到@buffer
* Copy the extended attribute representation of the security label
* associated with @name for @inode into @buffer. @buffer may be
* NULL to request the size of the buffer required. @size indicates
* the size of @buffer in bytes. Note that @name is the remainder
* of the attribute name after the security. prefix has been removed.
* @err is the return value from the preceding fs getxattr call,
* and can be used by the security module to determine whether it
* should try and canonicalize the attribute value.
* Return number of bytes used/required on success.
* @inode_setsecurity://设置安全标签
* Set the security label associated with @name for @inode from the
* extended attribute value @value. @size indicates the size of the
* @value in bytes. @flags may be XATTR_CREATE, XATTR_REPLACE, or 0.
* Note that @name is the remainder of the attribute name after the
* security. prefix has been removed.
* Return 0 on success.
* @inode_listsecurity://和inode_getsecurity功能相似
* Copy the extended attribute names for the security labels
* associated with @inode into @buffer. The maximum size of @buffer
* is specified by @buffer_size. @buffer may be NULL to request
* the size of the buffer required.
* Returns number of bytes used/required on success.
*
* Security hooks for file operations//文件操作的安全钩子
*
* @file_permission://访问一个已打开的文件时进行检查
* Check file permissions before accessing an open file. This hook is
* called by various operations that read or write files. A security
* module can use this hook to perform additional checking on these
* operations, e.g. to revalidate permissions on use to support privilege
* bracketing or policy changes. Notice that this hook is used when the
* actual read/write operations are performed, whereas the
* inode_security_ops hook is called when a file is opened (as well as
* many other operations).
* Caveat: Although this hook can be used to revalidate permissions for
* various system call operations that read or write files, it does not
* address the revalidation of permissions for memory-mapped files.
* Security modules must handle this separately if they need such
* revalidation.
* @file contains the file structure being accessed.
* @mask contains the requested permissions.
* Return 0 if permission is granted.
//对安全域进行操作
* @file_alloc_security://给文件关联上或者分配一个f_security域
* Allocate and attach a security structure to the file->f_security field.
* The security field is initialized to NULL when the structure is first
* created.
* @file contains the file structure to secure.
* Return 0 if the hook is successful and permission is granted.
* @file_free_security:
* Deallocate and free any security structures stored in file->f_security.
* @file contains the file structure being modified.
//end of 安全域操作
* @file_ioctl://file控制操作,对特定的文件??io控制函数?
* @file contains the file structure.
* @cmd contains the operation to perform.
* @arg contains the operational arguments.
* Check permission for an ioctl operation on @file. Note that @arg can
* sometimes represents a user space pointer; in other cases, it may be a
* simple integer value. When @arg represents a user space pointer, it
* should never be used by the security module.
* Return 0 if permission is granted.
* @file_mmap ://文件映射
* Check permissions for a mmap operation. The @file may be NULL, e.g.
* if mapping anonymous memory.
* @file contains the file structure for file to map (may be NULL).
* @reqprot contains the protection requested by the application.
* @prot contains the protection that will be applied by the kernel.
* @flags contains the operational flags.
* Return 0 if permission is granted.
* @file_mprotect://文件保护,改变内存访问方式
* Check permissions before changing memory access permissions.
* @vma contains the memory region to modify.
* @reqprot contains the protection requested by the application.
* @prot contains the protection that will be applied by the kernel.
* Return 0 if permission is granted.
* @file_lock://文件加锁
* Check permission before performing file locking operations.
* Note: this hook mediates both flock and fcntl style locks.
* @file contains the file structure.
* @cmd contains the posix-translated lock operation to perform
* (e.g. F_RDLCK, F_WRLCK).//读锁还是写所
* Return 0 if permission is granted.
* @file_fcntl://对文件操作进行检查,具有普通控制函数的特点
* Check permission before allowing the file operation specified by @cmd
* from being performed on the file @file. Note that @arg can sometimes
* represents a user space pointer; in other cases, it may be a simple
* integer value. When @arg represents a user space pointer, it should
* never be used by the security module.
* @file contains the file structure.
* @cmd contains the operation to be performed.
* @arg contains the operational arguments.
* Return 0 if permission is granted.
* @file_set_fowner://保存所有者的安全信息,当进行io操作时,切换到内核态时会使用到
* Save owner security information (typically from current->security) in
* file->f_security for later use by the send_sigiotask hook.
* @file contains the file structure to update.
* Return 0 on success.
* @file_send_sigiotask:// fown_struct,该结构的作用是通过信号进行I/O时间通知的数据
* Check permission for the file owner @fown to send SIGIO or SIGURG to the
* process @tsk. Note that this hook is sometimes called from interrupt.
* Note that the fown_struct, @fown, is never outside the context of a
* struct file, so the file structure (and associated security information)
* can always be obtained:
* (struct file *)((long)fown - offsetof(struct file,f_owner));
* @tsk contains the structure of task receiving signal.
* @fown contains the file owner information.
* @sig is the signal that will be sent. When 0, kernel sends SIGIO.
* Return 0 if permission is granted.
* @file_receive://该钩子函数允许安全模块去控制进程通过socket IPC接收一个打开的文件
描述符。
* This hook allows security modules to control the ability of a process
* to receive an open file descriptor via socket IPC.
* @file contains the file structure being received.
* Return 0 if permission is granted.
*
* Security hooks for task operations.//进程操作的安全钩子函数
*
* @task_create://创建一个子进程时进行权限检查
* Check permission before creating a child process. See the clone(2)
* manual page for definitions of the @clone_flags.
* @clone_flags contains the flags indicating what should be shared.
* Return 0 if permission is granted.
* @task_alloc_security://给进程分配一个安全域,task_struct中有一个进程域
* @p contains the task_struct for child process.
* Allocate and attach a security structure to the p->security field. The
* security field is initialized to NULL when the task structure is
* allocated.
* Return 0 if operation was successful.
* @task_free_security://释放进程的安全域
* @p contains the task_struct for process.
* Deallocate and clear the p->security field.
* @task_setuid://给当前进程设置一个或者多个用户标识属性,LSM_SETID??
* Check permission before setting one or more of the user identity
* attributes of the current process. The @flags parameter indicates
* which of the set*uid system calls invoked this hook and how to
* interpret the @id0, @id1, and @id2 parameters. See the LSM_SETID
* definitions at the beginning of this file for the @flags values and
* their meanings.
* @id0 contains a uid.
* @id1 contains a uid.
* @id2 contains a uid.
* @flags contains one of the LSM_SETID_* values.
* Return 0 if permission is granted.
* @task_post_setuid://在上面一个函数操作完后,对模块的状态进行更新
* Update the module's state after setting one or more of the user
* identity attributes of the current process. The @flags parameter
* indicates which of the set*uid system calls invoked this hook. If
* @flags is LSM_SETID_FS, then @old_ruid is the old fs uid and the other
* parameters are not used.
* @old_ruid contains the old real uid (or fs uid if LSM_SETID_FS).
* @old_euid contains the old effective uid (or -1 if LSM_SETID_FS).
* @old_suid contains the old saved uid (or -1 if LSM_SETID_FS).
* @flags contains one of the LSM_SETID_* values.
* Return 0 on success.
* @task_setgid://对当前进程设置一个或多个组id属性
* Check permission before setting one or more of the group identity
* attributes of the current process. The @flags parameter indicates
* which of the set*gid system calls invoked this hook and how to
* interpret the @id0, @id1, and @id2 parameters. See the LSM_SETID
* definitions at the beginning of this file for the @flags values and
* their meanings.
* @id0 contains a gid.
* @id1 contains a gid.
* @id2 contains a gid.
* @flags contains one of the LSM_SETID_* values.
* Return 0 if permission is granted.
* @task_setpgid://设置进程组id
* Check permission before setting the process group identifier of the
* process @p to @pgid.
* @p contains the task_struct for process being modified.
* @pgid contains the new pgid.
* Return 0 if permission is granted.
* @task_getpgid://获取进程组id
* Check permission before getting the process group identifier of the
* process @p.
* @p contains the task_struct for the process.
* Return 0 if permission is granted.
* @task_getsid://获取进程p的会话id
* Check permission before getting the session identifier of the process
* @p.
* @p contains the task_struct for the process.
* Return 0 if permission is granted.
* @task_getsecid://恢复进程p的安全id【】
* Retrieve the security identifier of the process @p.
* @p contains the task_struct for the process and place is into @secid.
* @task_setgroups://设置当前进程附加的组集合
* Check permission before setting the supplementary group set of the
* current process.
* @group_info contains the new group information.
* Return 0 if permission is granted.
* @task_setnice://设置进程p的静态优先级
* Check permission before setting the nice value of @p to @nice.
* @p contains the task_struct of process.
* @nice contains the new nice value.
* Return 0 if permission is granted.
* @task_setioprio://设置io优先级的值
* Check permission before setting the ioprio value of @p to @ioprio.
* @p contains the task_struct of process.
* @ioprio contains the new ioprio value
* Return 0 if permission is granted.
* @task_getioprio://获取io优先级的值
* Check permission before getting the ioprio value of @p.
* @p contains the task_struct of process.
* Return 0 if permission is granted.
* @task_setrlimit://设置当前进程资源限定值
* Check permission before setting the resource limits of the current
* process for @resource to @new_rlim. The old resource limit values can
* be examined by dereferencing (current->signal->rlim + resource).
* @resource contains the resource whose limit is being set.
* @new_rlim contains the new limits for @resource.
* Return 0 if permission is granted.
* @task_setscheduler://设置进程的调度策略或者策略的调度参数
* Check permission before setting scheduling policy and/or parameters of
* process @p based on @policy and @lp.
* @p contains the task_struct for process.
* @policy contains the scheduling policy.
* @lp contains the scheduling parameters.
* Return 0 if permission is granted.
* @task_getscheduler://获取进程p的调度信息
* Check permission before obtaining scheduling information for process
* @p.
* @p contains the task_struct for process.
* Return 0 if permission is granted.
* @task_movememory
* Check permission before moving memory owned by process @p.
* @p contains the task_struct for process.
* Return 0 if permission is granted.
* @task_kill://发送信号给进程P
* Check permission before sending signal @sig to @p. @info can be NULL,
* the constant 1, or a pointer to a siginfo structure. If @info is 1 or
* SI_FROMKERNEL(info) is true, then the signal should be viewed as coming
* from the kernel and should typically be permitted.
* SIGIO signals are handled separately by the send_sigiotask hook in
* file_security_ops.
* @p contains the task_struct for process.
* @info contains the signal information.
* @sig contains the signal value.
* @secid contains the sid of the process where the signal originated
* Return 0 if permission is granted.
* @task_wait://进程睡眠子进程并收集状态信息
* Check permission before allowing a process to reap a child process @p
* and collect its status information.
* @p contains the task_struct for process.
* Return 0 if permission is granted.
* @task_prctl://对当前进程运行进程控制操作
* Check permission before performing a process control operation on the
* current process.
* @option contains the operation.
* @arg2 contains a argument.
* @arg3 contains a argument.
* @arg4 contains a argument.
* @arg5 contains a argument.
* Return 0 if permission is granted.
* @task_reparent_to_init://在重定父级时,设置一个内核线程的@p->security里的安全属性
【】
* Set the security attributes in @p->security for a kernel thread that
* is being reparented to the init task.
* @p contains the task_struct for the kernel thread.
* @task_to_inode://为进程相关的inode设置安全属性【】
* Set the security attributes for an inode based on an associated task's
* security attributes, e.g. for /proc/pid inodes.
* @p contains the task_struct for the task.
* @inode contains the inode structure for the inode.
*
* Security hooks for Netlink messaging.//网络连接安全钩子
*
* @netlink_send://保存网络连接消息的安全信息,在消息处理之前进行权限检查
* Save security information for a netlink message so that permission
* checking can be performed when the message is processed. The security
* information can be saved using the eff_cap field of the
* netlink_skb_parms structure. Also may be used to provide fine
* grained control over message transmission.
* @sk associated sock of task sending the message.,
* @skb contains the sk_buff structure for the netlink message.
* Return 0 if the information was successfully saved and message
* is allowed to be transmitted.
* @netlink_recv://接收网络连接消息
* Check permission before processing the received netlink message in
* @skb.
* @skb contains the sk_buff structure for the netlink message.
* @cap indicates the capability required
* Return 0 if permission is granted.
*
* Security hooks for Unix domain networking.
*
* @unix_stream_connect://建立一个Unix domain流连接
* Check permissions before establishing a Unix domain stream connection
* between @sock and @other.
* @sock contains the socket structure.
* @other contains the peer socket structure.
* Return 0 if permission is granted.
* @unix_may_send://发送消息
* Check permissions before connecting or sending datagrams from @sock to
* @other.
* @sock contains the socket structure.
* @sock contains the peer socket structure.
* Return 0 if permission is granted.
*
* The @unix_stream_connect and @unix_may_send hooks were necessary because
* Linux provides an alternative to the conventional file name space for Unix
* domain sockets. Whereas binding and connecting to sockets in the file name
* space is mediated by the typical file permissions (and caught by the mknod
* and permission hooks in inode_security_ops), binding and connecting to
* sockets in the abstract name space is completely unmediated. Sufficient
* control of Unix domain sockets in the abstract name space isn't possible
* using only the socket layer hooks, since we need to know the actual target
* socket, which is not looked up until we are inside the af_unix code.
*
* Security hooks for socket operations.//socket操作的安全钩子
*
* @socket_create://创建一个新的socket之前进行检查
* Check permissions prior to creating a new socket.
*
*
*
*
*
@family contains the requested protocol family.//协议家族
@type contains the requested communications type.//连接的方式
@protocol contains the requested protocol.//通信协议
@kern set to 1 if a kernel socket.//内核socket还是用户socket
Return 0 if permission is granted.
* @socket_post_create://该函数允许一个模块更新或者分配给每个socket一个安全的结构。
注意安全域不是直接添加到socket结构中,而是存储在相关的inode节点。特别地,inode
节点的alloc_security钩子会给sock->inode->i_security分配并关联上安全信息。
* This hook allows a module to update or allocate a per-socket security
* structure. Note that the security field was not added directly to the
* socket structure, but rather, the socket security information is stored
* in the associated inode. Typically, the inode alloc_security hook will
* allocate and and attach security information to
* sock->inode->i_security. This hook may be used to update the
* sock->inode->i_security field with additional information that wasn't
* available when the inode was allocated.
* @sock contains the newly created socket structure.
* @family contains the requested protocol family.
* @type contains the requested communications type.
* @protocol contains the requested protocol.
* @kern set to 1 if a kernel socket.
* @socket_bind://检查绑定是否合法
* Check permission before socket protocol layer bind operation is
* performed and the socket @sock is bound to the address specified in the
* @address parameter.
* @sock contains the socket structure.
* @address contains the address to bind to.
* @addrlen contains the length of address.
* Return 0 if permission is granted.
* @socket_connect://检查连接操作是否合法
* Check permission before socket protocol layer connect operation
* attempts to connect socket @sock to a remote address, @address.
* @sock contains the socket structure.
* @address contains the address of remote endpoint.
* @addrlen contains the length of address.
* Return 0 if permission is granted.
* @socket_listen://监听
* Check permission before socket protocol layer listen operation.
* @sock contains the socket structure.
* @backlog contains the maximum length for the pending connection queue.
* Return 0 if permission is granted.
* @socket_accept://接受一个新的链接前进行检查
* Check permission before accepting a new connection. Note that the new
* socket, @newsock, has been created and some information copied to it,
* but the accept operation has not actually been performed.
* @sock contains the listening socket structure.
* @newsock contains the newly created server socket for connection.
* Return 0 if permission is granted.
* @socket_post_accept://允许一个安全模块将安全信息拷贝到新创建的inode中
* This hook allows a security module to copy security
* information into the newly created socket's inode.
* @sock contains the listening socket structure.
* @newsock contains the newly created server socket for connection.
* @socket_sendmsg://传送一个信息到另外一个socket
* Check permission before transmitting a message to another socket.
* @sock contains the socket structure.
* @msg contains the message to be transmitted.
* @size contains the size of message.
* Return 0 if permission is granted.
* @socket_recvmsg://从另外一个socket接收信息
* Check permission before receiving a message from a socket.
* @sock contains the socket structure.
* @msg contains the message structure.
* @size contains the size of message structure.
* @flags contains the operational flags.
* Return 0 if permission is granted.
* @socket_getsockname://获取socket对象的本地地址
* Check permission before the local address (name) of the socket object
* @sock is retrieved.
* @sock contains the socket structure.
* Return 0 if permission is granted.
* @socket_getpeername://获取一个socket对象的远程路径名
* Check permission before the remote address (name) of a socket object
* @sock is retrieved.
* @sock contains the socket structure.
* Return 0 if permission is granted.
* @socket_getsockopt://恢复与socket相关的选项
* Check permissions before retrieving the options associated with socket
* @sock.
* @sock contains the socket structure.
* @level contains the protocol level to retrieve option from.
* @optname contains the name of option to retrieve.
* Return 0 if permission is granted.
* @socket_setsockopt://设置与socket相关的选项
* Check permissions before setting the options associated with socket
* @sock.
* @sock contains the socket structure.
* @level contains the protocol level to set options for.
* @optname contains the name of the option to set.
* Return 0 if permission is granted.
* @socket_shutdown://检查socket上所有或部分连接
* Checks permission before all or part of a connection on the socket
* @sock is shut down.
* @sock contains the socket structure.
* @how contains the flag indicating how future sends and receives are handled.
* Return 0 if permission is granted.
* @socket_sock_rcv_skb://检查网络数据包的权限,该hook直接来源于Netfilter's IP 输出
hooks,Netfilter hooks
* Check permissions on incoming network packets. This hook is distinct
* from Netfilter's IP input hooks since it is the first time that the
* incoming sk_buff @skb has been associated with a particular socket, @sk.
* @sk contains the sock (not socket) associated with the incoming sk_buff.
* @skb contains the incoming network data.
* @socket_getpeersec://允许安全模块为用户空间提供对方socket安全状态,这个操作时通
过getsockopt SO_GETPEERSEC实现
* This hook allows the security module to provide peer socket security
* state to userspace via getsockopt SO_GETPEERSEC.
* @sock is the local socket.
* @optval userspace memory where the security state is to be copied.
* @optlen userspace int where the module should copy the actual length
* of the security state.
* @len as input is the maximum length to copy to userspace provided
* by the caller.
* Return 0 if all is well, otherwise, typical getsockopt return
* values.
* @sk_alloc_security://填充sk->sk_security域
* Allocate and attach a security structure to the sk->sk_security field,
* which is used to copy security attributes between local stream sockets.
* @sk_free_security://释放安全域
* Deallocate security structure.
* @sk_getsid://恢复LSM特殊sock的sid,启用网络缓冲授权
* Retrieve the LSM-specific sid for the sock to enable caching of network
* authorizations.
*
* Security hooks for XFRM operations.
*
* @xfrm_policy_alloc_security://为xp->security域分配结构,初始化的时候为NULL【】
* @xp contains the xfrm_policy being added to Security Policy Database
* used by the XFRM system.
* @sec_ctx contains the security context information being provided by
* the user-level policy update program (e.g., setkey).
* Allocate a security structure to the xp->security field.
* The security field is initialized to NULL when the xfrm_policy is
* allocated.
* Return 0 if operation was successful (memory to allocate, legal context)
* @xfrm_policy_clone_security://用旧的安全域填充新的安全域【】
* @old contains an existing xfrm_policy in the SPD.
* @new contains a new xfrm_policy being cloned from old.
* Allocate a security structure to the new->security field
* that contains the information from the old->security field.
* Return 0 if operation was successful (memory to allocate).
* @xfrm_policy_free_security:// 释放xfrm_policy的安全域【】
* @xp contains the xfrm_policy
* Deallocate xp->security.
* @xfrm_policy_delete_security:
* @xp contains the xfrm_policy.
* Authorize deletion of xp->security.
* @xfrm_state_alloc_security://给xfrm_state分配安全域【】
* @x contains the xfrm_state being added to the Security Association
* Database by the XFRM system.
* @sec_ctx contains the security context information being provided by
* the user-level SA generation program (e.g., setkey or racoon).
* Allocate a security structure to the x->security field. The
* security field is initialized to NULL when the xfrm_state is
* allocated.
* Return 0 if operation was successful (memory to allocate, legal context).
* @xfrm_state_free_security://释放xfrm_state的安全域【】
* @x contains the xfrm_state.
* Deallocate x->security.
* @xfrm_state_delete_security://委托删除x->security安全域【】
* @x contains the xfrm_state.
* Authorize deletion of x->security.
* @xfrm_policy_lookup:
* @xp contains the xfrm_policy for which the access control is being
* checked.
* @sk_sid contains the sock security label that is used to authorize
* access to the policy xp.
* @dir contains the direction of the flow (input or output).
* Check permission when a sock selects a xfrm_policy for processing
* XFRMs on a packet. The hook is called when selecting either a
* per-socket policy or a generic xfrm policy.
* Return 0 if permission is granted.
*
* Security hooks affecting all Key Management operations
*
* @key_alloc://允许分配一个Key并分派安全数据,注意在这个时候,Key没有分配序列号
【】
* Permit allocation of a key and assign security data. Note that key does
* not have a serial number assigned at this point.
* @key points to the key.
* @flags is the allocation flags
* Return 0 if permission is granted, -ve error otherwise.
* @key_free://释放安全数据【】
* Notification of destruction; free security data.
* @key points to the key.
* No return value.
* @key_permission://看是否授权给进程一个Key
* See whether a specific operational right is granted to a process on a
* key.
* @key_ref refers to the key (key pointer + possession attribute bit).
* @context points to the process to provide the context against which to
* evaluate the security data on the key.
* @perm describes the combination of permissions required of this key.
* Return 1 if permission granted, 0 if permission denied and -ve it the
* normal permissions model should be effected.
*
* Security hooks affecting all System V IPC operations.//进程通信安全钩子
*
* @ipc_permission://访问IPC
* Check permissions for access to IPC
* @ipcp contains the kernel IPC permission structure
* @flag contains the desired (requested) permission set
* Return 0 if permission is granted.
*
* Security hooks for individual messages held in System V IPC message queues
* @msg_msg_alloc_security://给msg->security域分配空间【】
* Allocate and attach a security structure to the msg->security field.
* The security field is initialized to NULL when the structure is first
* created.
* @msg contains the message structure to be modified.
* Return 0 if operation was successful and permission is granted.
* @msg_msg_free_security://释放【】
* Deallocate the security structure for this message.
* @msg contains the message structure to be modified.
*
* Security hooks for System V IPC Message Queues//进程消息队列
*
* @msg_queue_alloc_security: //分配msq->q_ty安全域【】
* Allocate and attach a security structure to the
* msq->q_ty field. The security field is initialized to
* NULL when the structure is first created.
* @msq contains the message queue structure to be modified.
* Return 0 if operation was successful and permission is granted.
* @msg_queue_free_security://释放消息队列的安全域【】
* Deallocate security structure for this message queue.
* @msq contains the message queue structure to be modified.
* @msg_queue_associate://当一个消息队列响应msgget系统调用请求时检查
* Check permission when a message queue is requested through the
* msgget system call. This hook is only called when returning the
* message queue identifier for an existing message queue, not when a
* new message queue is created.
* @msq contains the message queue to act upon.
* @msqflg contains the operation control flags.
* Return 0 if permission is granted.
* @msg_queue_msgctl://消息队列的控制操作
* Check permission when a message control operation specified by @cmd
* is to be performed on the message queue @msq.
* The @msq may be NULL, e.g. for IPC_INFO or MSG_INFO.
* @msq contains the message queue to act upon. May be NULL.
* @cmd contains the operation to be performed.
* Return 0 if permission is granted.
* @msg_queue_msgsnd://消息插入消息队列时进行检查
* Check permission before a message, @msg, is enqueued on the message
* queue, @msq.
* @msq contains the message queue to send message to.
* @msg contains the message to be enqueued.
* @msqflg contains operational flags.
* Return 0 if permission is granted.
* @msg_queue_msgrcv://从消息队列中接收消息
* Check permission before a message, @msg, is removed from the message
* queue, @msq. The @target task structure contains a pointer to the
* process that will be receiving the message (not equal to the current
* process when inline receives are being performed).
* @msq contains the message queue to retrieve message from.
* @msg contains the message destination.
* @target contains the task structure for recipient process.
* @type contains the type of message requested.
* @mode contains the operational flags.
* Return 0 if permission is granted.
*
* Security hooks for System V Shared Memory Segments
*
* @shm_alloc_security:// 给shp->shm_ty分配安全域【】
* Allocate and attach a security structure to the shp->shm_ty
* field. The security field is initialized to NULL when the structure is
* first created.
* @shp contains the shared memory structure to be modified.
* Return 0 if operation was successful and permission is granted.
* @shm_free_security://释放安全域【】
* Deallocate the security struct for this memory segment.
* @shp contains the shared memory structure to be modified.
* @shm_associate://对已存在的共享内存,响应shmget系统调用进行检查
* Check permission when a shared memory region is requested through the
* shmget system call. This hook is only called when returning the shared
* memory region identifier for an existing region, not when a new shared
* memory region is created.
* @shp contains the shared memory structure to be modified.
* @shmflg contains the operation control flags.
* Return 0 if permission is granted.
* @shm_shmctl://共享内存控制操作进行检查
* Check permission when a shared memory control operation specified by
* @cmd is to be performed on the shared memory region @shp.
* The @shp may be NULL, e.g. for IPC_INFO or SHM_INFO.
* @shp contains shared memory structure to be modified.
* @cmd contains the operation to be performed.
* Return 0 if permission is granted.
* @shm_shmat://关联一个共享内存
* Check permissions prior to allowing the shmat system call to attach the
* shared memory segment @shp to the data segment of the calling process.
* The attaching address is specified by @shmaddr.
* @shp contains the shared memory structure to be modified.
* @shmaddr contains the address to attach memory region to.
* @shmflg contains the operational flags.
* Return 0 if permission is granted.
*
* Security hooks for System V Semaphores//信号量
*
* @sem_alloc_security:// 给sma->sem_ty域分配安全域【】
* Allocate and attach a security structure to the sma->sem_ty
* field. The security field is initialized to NULL when the structure is
* first created.
* @sma contains the semaphore structure
* Return 0 if operation was successful and permission is granted.
* @sem_free_security://释放信号量的安全域【】
* deallocate security struct for this semaphore
* @sma contains the semaphore structure.
* @sem_associate://当信号量响应semget系统调用时进行检查
* Check permission when a semaphore is requested through the semget
* system call. This hook is only called when returning the semaphore
* identifier for an existing semaphore, not when a new one must be
* created.
* @sma contains the semaphore structure.
* @semflg contains the operation control flags.
* Return 0 if permission is granted.
* @sem_semctl://执行信号量控制操作时进行检查
* Check permission when a semaphore operation specified by @cmd is to be
* performed on the semaphore @sma. The @sma may be NULL, e.g. for
* IPC_INFO or SEM_INFO.
* @sma contains the semaphore structure. May be NULL.
* @cmd contains the operation to be performed.
* Return 0 if permission is granted.
* @sem_semop://对一组信号量执行操作进行检查
* Check permissions before performing operations on members of the
* semaphore set @sma. If the @alter flag is nonzero, the semaphore set
* may be modified.
* @sma contains the semaphore structure.
* @sops contains the operations to perform.
* @nsops contains the number of operations to perform.
* @alter contains the flag indicating whether changes are to be made.
* Return 0 if permission is granted.
*
* @ptrace://在允许父进程可以追踪子进程的操作之前进行检查
* Check permission before allowing the @parent process to trace the
* @child process.
* Security modules may also want to perform a process tracing check
* during an execve in the set_security or apply_creds hooks of
* binprm_security_ops if the process is being traced and its security
* attributes would be changed by the execve.
* @parent contains the task_struct structure for parent process.
* @child contains the task_struct structure for child process.
* Return 0 if permission is granted.
* @capget://获取目标进程的权限集【】
* Get the @effective, @inheritable, and @permitted capability sets for
* the @target process. The hook may also perform permission checking to
* determine if the current process is allowed to see the capability sets
* of the @target process.
* @target contains the task_struct structure for target process.
* @effective contains the effective capability set.
* @inheritable contains the inheritable capability set.
* @permitted contains the permitted capability set.
* Return 0 if the capability sets were successfully obtained.
* @capset_check://在设置目标进程权限时进行检查
* Check permission before setting the @effective, @inheritable, and
* @permitted capability sets for the @target process.
* Caveat: @target is also set to current if a set of processes is
* specified (i.e. all processes other than current and init or a
* particular process group). Hence, the capset_set hook may need to
* revalidate permission to the actual target process.
* @target contains the task_struct structure for target process.
* @effective contains the effective capability set.
* @inheritable contains the inheritable capability set.
* @permitted contains the permitted capability set.
* Return 0 if permission is granted.
* @capset_set://设置目标进程的权限集
* Set the @effective, @inheritable, and @permitted capability sets for
* the @target process. Since capset_check cannot always check permission
* to the real @target process, this hook may also perform permission
* checking to determine if the current process is allowed to set the
* capability sets of the @target process. However, this hook has no way
* of returning an error due to the structure of the sys_capset code.
* @target contains the task_struct structure for target process.
* @effective contains the effective capability set.
* @inheritable contains the inheritable capability set.
* @permitted contains the permitted capability set.
* @capable://检查tsk进程是否拥有cap的权限
* Check whether the @tsk process has the @cap capability.
* @tsk contains the task_struct for the process.
* @cap contains the capability
* Return 0 if the capability is granted for @tsk.
* @acct://启用或者屏蔽进程统计时检查相应的权限
* Check permission before enabling or disabling process accounting. If
* accounting is being enabled, then @file refers to the open file used to
* store accounting records. If accounting is being disabled, then @file
* is NULL.
* @file contains the file structure for the accounting file (may be NULL).
* Return 0 if permission is granted.
* @sysctl://在访问@table变量时进行检查
* Check permission before accessing the @table sysctl variable in the
* manner specified by @op.
* @table contains the ctl_table structure for the sysctl variable.
* @op contains the operation (001 = search, 002 = write, 004 = read).
* Return 0 if permission is granted.
* @syslog://访问内核消息环或改变控制台写日志时进行检查
* Check permission before accessing the kernel message ring or changing
* logging to the console.
* See the syslog(2) manual page for an explanation of the @type values.
* @type contains the type of action.
* Return 0 if permission is granted.
* @settime://设置系统时间时进行检查
* Check permission to change the system time.
* struct timespec and timezone are defined in include/linux/time.h
* @ts contains new time
* @tz contains new timezone
* Return 0 if permission is granted.
* @vm_enough_memory://分配新的虚拟映射时进行权限检查
* Check permissions for allocating a new virtual mapping.
* @pages contains the number of pages.
* Return 0 if permission is granted.
*
* @register_security://允许模块注册【】
* allow module stacking.
* @name contains the name of the security module being stacked.
* @ops contains a pointer to the struct security_operations of the module to stack.
* @unregister_security://注销模块【】
* remove a stacked module.
* @name contains the name of the security module being unstacked.
* @ops contains a pointer to the struct security_operations of the module to unstack.
*
* @secid_to_secctx://改变安全上下文的安全ID【】
* Convert secid to security context.
* @secid contains the security ID.
* @secdata contains the pointer that stores the converted security context.
*
* @release_secctx://释放安全上下文【】
* Release the security context.
* @secdata contains the security context.
* @seclen contains the length of the security context.
第八章 参考文献
[1] Linux Security Modules: General Security Support for the Linux Kernel
[2]
[3]
LINUX内核》
LINUX设备驱动程序》第三版
《深入理解
《
版权声明:本文标题:SELinux源码分析_1.31 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://roclinux.cn/p/1713441762a634859.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论