Smack(Simplified Mandatory Access Control Kernel)是Casey Schaufler[15]于2007年在LSM基础上实现的Linux强制访问控制安全模块,它以内核安全补丁的形式存在于Linux操作系统中,其设计思想是利用LSM安全域将Linux内核中所有主体与客体都打上安全标签,并规定安全策略,只有符合安全策略的访问方式才被容许。与SELinux和DTE相比,Smack安全策略要简单得多,但却能实现它们相似的强制访问控制功能,并且Smack对内核性能损耗比较低,因此,Smack也逐渐被业界看好和推广。

2.4.1 Smack基本概念

1. 主体

Smack主体是指Linux内核进程。

2. 客体

Smack客体是指Linux内核客体对象,如文件、消息队列、套接字、共享内存、信号量等,客体也可以是Linux进程或者IPC。

3. 安全标签

Smack安全标签是C语言的字符串,但最多包含24个字符(包括‘/0’),Smack修改了进程task_struct安全域,在进程被初始创建时,其安全标签是“_”。同样,Smack修改了虚拟文件系统的inode和super_block安全域,使得文件系统被创建时所有文件的安全标签是“_”。

4. 访问方式

Smack最初版本的访问方式只有四种,即读(r或R)、写(w或W)、执行(x或X)、盲写(a或A)。其中,在进程之间通信中,一个进程发送消息或者数据包给另一个进程时,这样的操作属于写操作。

5. 安全策略

Smack的安全策略分为Smack内置的安全策略和用户可定制的安全策略。Smack内置的安全策略是指原本就已经被Smack访问控制代码所规定的,包括如下几条:

(1)安全标签是“*”的进程发起的任何形式的访问都被拒绝;

(2)安全标签是“^”的进程发起的读或执行的请求都被容许;

(3)任何进程对安全标签是“_”的客体发起的读或执行的请求都被容许;

(4)任何进程对安全标签是“*”的客体发起的任何形式的请求都被容许;

(5)如果主体和客体的安全标签相同,那么该主体对该客体发起的任何形式的访问都被容许。

除此之外,用户可以根据自己的安全需求,在主、客体安全标签都已经存在的前提下,通过“smackload”工具写入安全策略,例如:

(1) abc    xyz     rwxarW

(2) abc    xyz     rwr

(3) abc    xyz    _

smackload工具会自动配置访问方式的格式,(1)表示主体abc对客体xyz有读、写、执行、盲写的权限,(2)表示主体abc对客体xyz有读、写权限,(3)表示主体abc对客体xyz没有任何权限。当用户连续写入以上三条规则后,Smack会将相同主、客体的规则进行覆盖,最终,abc对xyz没有任何权限。

2.4.2 Smack源码分析

2007版本的smack源码[16]并不庞大,它被组织成了smack.h、smack_access.c、smack_lsm.c和smackfs.c四个源文件。下面本小节就针对这四个文件来分析Smack的实现原理。

1. Smack的结构体

由以上分析可以知道LSM的安全域是“void *security”,它可以指向任意类型的指针,正因为此,Smack定义了几个重要的结构体,使得内核对象的安全域指向它们,下表总结了LSM安全域和Smack结构体之间的对应关系:

内核对象

内核数据结构

安全域

Smack结构体

文件系统

super_block

void *security

superblock_smack

管道、文件等

inode

void *security

inode_smack

进程

task_struct

void *security

task_smack

网络套接字

sock

void *sk_security

sockect_smack

除此之外,结构体smack_rule表示访问规则,它以内核链表的形式存在于内存中,用户通过“smackload”工具每写入一个规则,这个规则就被插入到Smack规则链表中,规则结构体只会被插入或者替换,而不会被删除。结构体smack_known包含了smack安全标签以及与之对应的smack安全级别和cipso结构体。smack_cipso包含了cipso的安全级别和安全分类,smk_netlbladdr包含了主机IP地址以及与之对应的Smack安全标签。

2. Smack访问控制函数

Smack访问控制函数smk_access定义如下所示:

int smk_access(char *subject_label, char*object_label, int request);

smk_access首先根据Smack默认的五条访问规则进行判断,然后在Smack内核规则链表中去查找和参数主客体标签一致的结点,判断此结点的权限是否包含请求的权限request。访问控制函数smk_curacc用于判断当前进程对目标客体是否有访问权限,smk_curacc_on_task用来判断当前进程对目标进程是否有访问权限,它们最终是调用smk_access来进行访问控制。需要指出的是,smk_curacc可以被特权进程绕过,正因为此,Smack不能阻止超级用户或特权进程的一切行为。

3.Smack的LSM内核

Smack是在LSM的基础上实现钩子函数以达到强制访问控制的目的,下表总结了Smack访问控制对象及其相应的钩子函数:

主体

客体

控制行为

钩子函数

要求权限

当前进程

目标进程

setpgid

smack_task_setpgid

当前进程

目标进程

getpgid

smack_task_getpgid

当前进程

目标进程

getsid

smack_task_getsid

当前进程

目标进程

setnice

smack_task_setnice

当前进程

目标进程

setioprio

smack_task_setioprio

当前进程

目标进程

getioprio

smack_task_getioprio

当前进程

目标进程

setscheduler

smack_task_setscheduler

当前进程

目标进程

getscheduler

smack_task_getscheduler

当前进程

目标进程

movememory

smack_task_movememory

当前进程

目标进程

kill

smack_task_kill

当前进程

目标进程

wait

smack_task_wait

主体

客体

控制行为

钩子函数

要求权限

当前进程

目标文件

ioctl

smack_file_ioctl

根据请求方式

当前进程

目标文件

lock

smack_file_lock

当前进程

目标文件

fcntl

smack_file_fcntl

根据请求方式

当前文件

目标进程

send_sigiotask

smack_file_send_sigiotask

当前进程

目标文件

file_receive

smack_file_receive

根据请求方式

源套接字

目标套接字

netlabel_send

smack_netlabel_send

源套接字

目标套接字

socket_connect

smack_socket_connect

当前进程

共享内存

shm_associate

smack_shm_associate

根据请求方式

当前进程

共享内存

shm_shmctl

smack_shm_shmctl

根据请求方式

当前进程

共享内存

shm_shmat

smack_shm_shmat

根据请求方式

当前进程

信号量

sem_associate

smack_sem_associate

根据请求方式

当前进程

信号量

sem_semctl

smack_sem_semctl

根据请求方式

当前进程

信号量

sem_semop

smack_sem_semop

读和写

当前进程

消息队列

msgctl

smack_msg_queue_msgctl

根据请求方式

当前进程

消息队列

msgsnd

smack_msg_queue_msgsnd

根据请求方式

当前进程

IPC

ipc_permission

smack_ipc_permission

根据请求方式

源套接字

目标套接字

unix_stream_connect

smack_unix_stream_connect

读和写

源套接字

目标套接字

unix_may_send

smack_unix_may_send

源套接字

目标套接字

socket_sendmsg

smack_socket_sendmsg

目标套接字

源套接字

sock_rcv_skb

smack_socket_sock_rcv_skb

正如上表所示,Smack是在LSM基础上实现了对进程、文件、套接字、共享内存、消息队列、信号量等的控制。Smack的访问控制非常简单,就是检查主体对客体有没有Smack访问方式,除此之外,Smack使用了钩子函数为进程和文件设置安全标签。当把Smack编译进Linux内核后,Linux操作系统上所有文件和进程默认的安全标签都是“_”。只有特权进程才能改变自身的安全标签,只有特权进程才能设置文件的安全标签。一个进程无法改变另一个进程的安全标签。当父进程创建子进程时,子进程的安全标签就是父进程的安全标签。当一个进程创建文件时,这些文件的安全标签就是进程的安全标签。当一个进程创建套接字时,套接字的安全标签就是该进程的安全标签。当两个进程要通过网络进行通信时,发送和接收IP包的进程必须对彼此都有写权限。

4. Smack的虚拟文件系统

smack_access.c和smack_lsm.c构成了Smack强制访问控制机制,但强制访问控制机制是需要安全策略数据库的支持,为此,Smack采取了虚拟文件系统作为其安全策略存储系统,这样Smack决策时间会很短,但这也就意味着,一旦计算机系统重新启动,需求人为得再次设定安全策略。Smack文件系统位于“/smack”目录下,包含了load、cipso、doi、direct、ambient、netlabel、onlycap和logging这几个虚拟文件,它们被组织成Linux内核链表形式,其中最常用到的是load、cipso和netlabel。load文件存放了Smack的安全策略,cipso存放cipso值,包括安全级别和安全分类,netlabel存放了主机的IP地址和其相关的Smack标签,cipso和netlabel用于Smack网络通信控制。Smack重写文件系统操作函数,并使用注册文件系统、挂载文件系统和初始化系统调用等内核API来实现虚拟文件系统。

2.4.3 Android内核的Smack编译

Smack内核代码除了上面介绍的四个文件外,还有makefile和Kconfig两个文件。makefile文件设定了Smack内核编译规则,而Kconfig文件指定了Smack的配置信息,其中就有一项是“bool simplified mandatoryaccess control kernel depend on NETLABEL & SECURITY_NETWORK”,从这条信息中可以看出,要想编译Smack,Android内核必须配置“NETLABEL”和“SECURITY_NETWORK”两项。

本节以panda内核为例演示编译Smack内核的过程,首先从Google官网或pandaboard官网上下载panda内核,解压后会有panda/kernel-ics-chipsee-panda文件夹。

1. 打开“shell”,输入指令“cdpanda/kernel-ics-chipsee-panda,cd arch/arm/configs”,找到目录下的缺省配置文件panda_chinese_def;

2. 在终端上输入指令“vim panda_chinese_def”,打开此配置文件,经过查找,可以发现这个文件里没有配置“NETLABEL”和“SECURITY_NETWORK”两项,这也是Smack没有被编译进panda内核的原因;

3. 用vim添加配置信息,如下所示:

[置顶] 强制访问控制内核模块Smack-冯金伟博客园

4. 保存退出后,输入如下指令:

[置顶] 强制访问控制内核模块Smack-冯金伟博客园

以上指令也同样适用于模拟器内核编译,需要强调的是,在编译Smack之前,要确保panda内核或模拟器内核已经配置了ext2或ext4或者支持扩展属性的yaffs2文件系统,否则Smack无法正常运行。为了验证Smack确实在Android系统上发挥了效果,本小节给出一个实验进行验证。

1. 实验环境:

软件环境为Ubuntu10.04,编译好的Android4.0源码和goldfish内核。

2. 实验过程:

(1)编写getsmk.c源码,getsmk.c输入是命令行参数,输出是文件的安全标签;

(2)在Android源码目录下新建getsmk文件夹,并将getsmk.c拷贝到给文件夹下,编写Android.mk文件,让getsmk能够被交叉编译;

(3)使用如下指令来启动模拟器:

“emulator –sdcard/host/android4.0/sdcard.img –kernel/host/android4.0/kernel/goldfish/arch/arm/boot/zImage”

(4)等待模拟器运行正常后,打开终端shell,使用“adb push”指令将可执行文件getsmk拷贝到“/data”目录下,随机选择文件,进行测试,测试结果如下:

[置顶] 强制访问控制内核模块Smack-冯金伟博客园

3. 实验结论:

Smack会在系统初始化时使用钩子函数smack_d_instantiate将Linux系统所有文件和进程的安全标签都设置为“_”,因此,通过以上实验,smack确实在Android系统上生效了。