Smack核心机制浅析
- 2014/7/8
- 关注( 141)
- 评论( 0)
声明:此内容仅代表网友个人经验或观点,不代表本网站立场和观点。
<iframe id="cproIframe_u1617689" width="640" height="60" src="http://pos.baidu.com/acom?adn=3&at=6&aurl=&cad=1&ccd=24&cec=UTF-8&cfv=11&ch=0&col=zh-CN&conOP=0&cpa=1&dai=2&dis=0&ltr=http%3A%2F%2Fwww.baidu.com%2Fs%3Fie%3Dutf-8%26f%3D8%26rsv_bp%3D1%26tn%3D93233997_hao_pg%26wd%3Dsmack%2520domain%2520linux%26rsv_spt%3D3%26rsv_pq%3D8f2081190009aa68%26rsv_t%3D9964CtdcNq9jEXPqGLTRaSK%252F%252BN18UTted7XCu80ZxDFOefGWT9RbRWVnQtlstluyTfGbM0ZX%26rsv_enter%3D1%26rsv_sug3%3D7%26rsv_sug4%3D190%26rsv_sug2%3D0%26inputT%3D1604&ltu=http%3A%2F%2Fwww.shangxueba.com%2Fjingyan%2F1849665.html&lunum=6&n=csai_cpr&pcs=1572x904&pis=10000x10000&ps=407x327&psr=1920x1080&pss=1572x407&qn=e77fa1a4a202993f&rad=&rsi0=640&rsi1=60&rsi5=4&rss0=%23FFFFFF&rss1=%23FFFFFF&rss2=%230000FF&rss3=%23444444&rss4=%23008000&rss5=&rss6=%23e10900&rss7=&scale=&skin=&td_id=1617689&tn=text_default_640_60&tpr=1417915931596&ts=1&xuanting=0&dtm=BAIDU_DUP2_SETJSONADSLOT&dc=2&di=u1617689" align="center,center" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" allowtransparency="true" style="margin: 0px; padding: 0px;"></iframe>在以上几篇博客中,我已经阐述了有关Smack技术的一些知识和要点,但是没有把Smack最精华的部分展示出来,由于本人学疏才浅,在阐述的过程中不免有这样那样的问题和诟病,但本人还是尽力把问题说清楚,在这篇文章中,我将尽我所能把Smack的核心机制进行阐述,如果涉及的内容有错误,还请梁老师和各位批判指正。
一 Smack是如何为主体和客体打上标签,又是如何获取它们的标签?
Smack的主体是任务,Smack称之为task,客体是文件,套接字,消息,IPC等,Smack为主体和客体打上和获取标签可以分为Smack内核空间和Smack用户空间两部分。
1. Smack内核空间:
为task打上标签: 我们知道Smack利用了LSM机制,LSM是要改造内核对象,为其添加安全域,对于任务而言,它的结构体task_struct里有一个成员是cred型,而cred结构体中有一项是void *security,这正是task的安全域,它指向了task_smack这个结构体,这个结构体里的成员就是父进程和子进程的安全标签,smack_lsm.c中的代码static struct task_smack *new_task_smack(char *task, char *forked, gfp_t gfp)是用来设置task_smack的父子进程smack标签,参数task指向父进程smack标签,forked指向子进程的smack标签,task_struct是以Linux内核链表形式组织起来,当设置完进程的安全标签后,该函数会初始化task_smack链表。
获取task的安全标签:在smack_lsm.c中,有一个很重要的宏--task_security(task),它的作用是获取进程的安全域,即void *security所指向的task_smack结构体,而在smack.h中有一个静态函数smk_of_current(),它用来获取当前进程的smack标签。
为inode打上标签:inode结构体中安全域是指向了inode_smack结构体,它不仅包含了文件对象的smack标签,而且包含了创建该inode的进程的smack标签,在smack_lsm.c中,钩子函数smack_inode_post_setxattr为inode打上标签,但是要注意只有以超级用户运行程序,才能在用户空间将文件打上Smack标签,因为像setxattr(设置安全标签)或者xattr这样的函数或命令都被安插了Smack的钩子smack_inode_setxattr,而此钩子会检查当前运行的程序有没有超级用户的权限。
获取inode标签:smk_fetch, smk_of_inode, smack_inode_getsecurity用来获取inode的smack标签。注意,Smack标签形式是键--值,键是XATTR_NAME_SMACK,XATTR_NAME_SMACKEXEC(进程标签),XATTR_NAME_SMACKIPIN(接收包的标签),XATTR_NAME_SMACKIPOUT(外发包的标签)它们在Linux的源码xattr.h中作为宏定义,而这里的值是Smack标签的值,即Smack的字符串,钩子函数一般是根据键(name)来获取值(value)。
2. Smack用户空间:
我们可以在/ect/accesses中写下一行访问规则--主体标签 客体标签 访问方式,运行smackload工具,这样这行规则便会被写入Smack的虚拟文件/smack/load中,这样smackfs.c中代码smk_write_load函数会设置相应的主体,客体标签以及访问控制链表smack_rule。
通过工具getsmack和setsmack可以分别设置和获取当前进程的Smack标签,必须注意两点:第一,设置进程Smack标签的程序必须具备超级用户的权限,因为Smack的钩子setprocattr会对系统调用进行检查,第二,我们只能对当前运行的进程设置和获取标签,也就是说当前运行的进程只能获取或者改变自己的标签,这是由setprocattr和getprocattr两个钩子检查的。在Linux的/proc目录下有个self的子目录,它指向当前正在运行进程,而self/attr/current里面保存着当前进程的Smack标签,默认是"_",getsmack和setsmack就是对此进行操作。
所以,对于用户来说,Smack给我们提供了很方便的接口,设置主客体安全标签由Smack在内核空间帮我们完成。
二 Smack是基于LSM机制,那么Smack是如何实现LSM?
像Smack,SELinux这样的安全模块都是基于LSM做的,这也是Linux的鼻祖Torvalds所要求的,Smack要想实现访问控制,必须做到:
第一,重写Smack的钩子函数,实现对进程,文件,套接字,管道,消息,IPC等控制;
第二,实例化结构体security_operations,让LSM的钩子指针指向Smack的钩子函数;
第三,向LSM注册Smack的安全模块,让安全模块发挥效能;
第四,初始化安全调用。
三 Smack的访问控制的实现
在前面的例子中,我们以普通用户运行setxattr函数对文件设置Smack标签,结果是操作无法进行,这其实是因为setxattr函数是一个系统调用,而此系统调用的源代码又被安插了LSM钩子,而LSM钩子指针指向了Smack的钩子函数,那么Smack的钩子函数就可以对上层的调用进行访问控制。Smack的访问控制可以分为几类:
第一类,关于进程跟踪ptrace
Smack钩子函数要求当上层应用执行ptrace等进程跟踪函数或命令时,当前进程对被跟踪进程有读和写的权限,如smack_ptrace_access_check和smack_ptrace_me;
第二类,关于超块superblock
当上层应用执行获取文件状态statfs函数时,Smack钩子函数smack_sb_statfs要求当前进程对文件有读权限,当上层应用执行mount或unmount进行文件系统挂载或卸载操作时,Smack钩子函数smack_sb_mount和smack_sb_unmount要求当前进程对文件有写权限;
第三类,关于可执行程序linux_bprm
此类钩子函数主要是为linux_bprm设置和提交凭证cred,如smack_bprm_set_creds和smack_bprm_committing_creds;
第四类, 关于索引结点inode
当上层应用要执行添加目录,删除目录,更改文件名,设置和移除文件扩展属性等操作时,Smack钩子函数要求当前进程对被操作的inode有写权限,具体钩子函数是smack_inode_link,smack_inode_unlink,smack_inode_rmdir,smack_inode_rename,smack_inode_secattr,smack_inode_setxattr等,当上层应用执行获取文件扩展属性的操作时,Smack的钩子函数要求当前进程对该inode有读权限,具体钩子函数是smack_inode_getxattr;
第五类,关于文件file
Linux的文件结构体file中也有安全域void *f_security,Smack的钩子函数smack_file_alloc_security使得此安全域指向当前进程的Smack的标签,也就是说如果该进程创建了文件,那么文件的安全域就是该进程的Smack标签,除此之外还有获取文件状态,为文件加锁等钩子函数。
第六类, 关于进程task
当上层应用执行设置进程组识别码setpgid,设置进程nice值setnice,设置进程调度值setscheduler等操作时,Smack钩子函数要求当前进程对被设置的进程或者说事目标进程有写权限,而当上层应用执行getpgid,getsid等操作时,Smack钩子函数要求当前进程对目标进程有读权限;
第七类,关于消息
Linux消息结构体msg_msg中有安全域void *security,它指向了当前进程的安全标签,这通过钩子函数smack_msg_msg_alloc_security来设置;
第八类,关于IPC
Linux结构体shmid_kernel有一个成员是kern_ipc_perm类型,而此类型的安全域void *security由Smack的钩子函数smack_shm_alloc_security设置为空,当进程利用共享内存进行通信时,Smack钩子函数smk_curacc_shm来进行权限检查;
第九类,关于信号量数组sem_array
Linux结构体sem_arrayl有一个成员是kern_ipc_perm类型,Smack钩子函数smk_curacc_sem来进行权限检查;
第十类,关于套接字socket
一 从Android内核编译说起
在将Smack编译到Android内核中,我曾经发现Smack的Kconfig文件里明确要求了NETLABEL,如果内核不进行NETLABEL配置,那么Android内核无法编译Smack,这就是说NETLABEL在Smack技术中占有举足轻重的作用,那么什么是NETLABEL?
NETLABEL翻译成网络标签,它是一项类似于LSM的工程,可以使得Linux内核具备向IP包打上安全标签CIPSO的能力,而这里的安全标签CIPSO(Commerical IP Security Option)是附加在IP包的包头的一个信息体,由域名解释DOI(domain interpretation),安全级别Level,安全分类Category组成,其中DOI负责对IP包的安全标签进行解释,能够让在网络上收发双方决定对方有没有权限发送包,发送方要想将IP包发送给网络的接收方,那么发送方必须对接收方有写权限。
那么Linux内核通过什么方式或者说借助什么能够在IP包上打上CIPSO标签? 内核是通过LSM提供的钩子函数可以设置或者获取IP包的CIPSO标签,在Smack技术中,Smack钩子函数smack_sk_alloc_security可以使得套接字sock的安全域指向sockect_smack结构体,而sockect_smack结构体两项smk_in和smk_out分别代表了从此套接字接收包的标签和从此套接字发送出去包的标签。
二 LSM和NETLABEL交互的结构体netlbl_lsm_secattr
LSM对套接字设置CIPSO标签是借助了netlbl_lsm_secattr这个结构体,这个结构体保存了套接字的安全属性,域,类型等信息。Smack的钩子函数static void smack_to_secattr(char *smack, struct netlbl_lsm_secattr *nlsp)可以将smack标签填充到nlsp中,而Smack钩子函数static int smack_netlabel(struct sock *sk, int labeled)就是借助netlbl_lsm_secattr来设置套接字sock的CIPSO标签。
三 两个重要的结构体smack_known和smk_netlbladdr
struct smack_known {
struct list_head list;
char smk_known[SMK_LABELLEN];
u32 smk_secid;
struct smack_cipso *smk_cipso;
spinlock_t smk_cipsolock; /* for changing cipso map */
struct list_head smk_rules; /* access rules */
struct mutex smk_rules_lock; /* lock for the rules */
};
这个结构体以Linux内核链表形式组织起来,其中smk_known表示smack的安全标签,每当上层应用执行设置文件的smack标签操作时,这个链表会被Smack函数smack_import和smack_import_entry检查,如果设置的smack标签在smack_known链表中查不到,则新的smack标签会被导入到此链表,在链表中的结点不会被删除,新结点只会被加入。而这里的smack_cipso正是CIPSO标签,通过此结构体一个smk_known可以映射到一个smack_cipso。
在Smack的用户空间我们可以通过工具smackcipso向Smack虚拟文件/smack/cipso导入规则: smack标签 安全级别(整数) 安全分类(整数),导入到cipso文件中的形式是 smack标签 安全级别/安全分类1,安全分类2,...。
在smack_lsm.c文件中,有很多函数涉及到smack_known的操作,其中大部分涉及套接字钩子,如:
static char *smack_from_secattr(struct netlbl_lsm_secattr *sap, struct socket_smack *ssp),这个钩子函数就将netlbl_lsm_secattr的相关信息设置到套接字的安全域ssp中。
struct smk_netlbladdr {
struct list_head list;
struct sockaddr_in smk_host; /* network address */
struct in_addr smk_mask; /* network mask */
char *smk_label; /* label */
};
此结构体也是以Linux内核链表组织起来的,它是关于主机的网络地址和主机的Smack标签,它在smack_lsm.c中也多次用到,如Smack的钩子函数:static char *smack_host_label(struct sockaddr_in *sip)就是根据主机地址sip来从smk_netlbladdr链表中获取主机的Smack标签。
四 Smack对网络的访问控制
Smack对网络控制很像IPC机制,Smack的LSM机制将从套接字发送或者接收的IP包都打上了CIPSO标签,如果在网络中传输的包没有被打上CIPSO标签,那么网络环境的CIPSO标签将被赋予该IP包。网络的发送方要想把IP包发送给接收方,必须保证发送方对接收方有Smack的写权限。如果我们想要容许一方One和另一方Other进行网络通信,我们可以在Smack规则写下:One Other wW Other One wW。
Smack的钩子函数static int smack_unix_may_send(struct socket *sock, struct socket *other),检查了套接字sock对套接字other有没有发送包的权限,这其实就是在访问控制链表smack_rule中检查sock的安全域socket_smack的成员smk_out对other的安全域socket_smack的成员smk_in有没有写权限。类似的钩子函数还有smack_unix_stream_connect,smack_netlabel_send,smack_socket_sendmsg等。