本文的文档连接在以下位置,如有需要可以下载:
https://download.csdn.net/download/fanx9339/12542410
- SELinux是什么?
SELinux是一套完整的安全策略,最开始是美国国家安全局和一些公司联合设计为了针对Linux系统的安全隐患而产生的一套系统,它为每一个进程,每一个文件,每一个属性都定义了标签,用来控制进程对文件的操作的权限控制!
在安卓里面,SELinux有三种状态:
enforce模式:强制模式,必须有配置权限才能执行相应的访问/操作
permissive模式:宽容模式,打印记录出现的越权行为,但是不禁止该访问/操作
disable:关闭模式,关闭SELinux,不受SELinux权限控制,此模式会导致不安全风险
SELinux默认是打开的。我们可以在窗口命令行下输入命令getenforce来查看我们的设备的SELinux状态。我们可以在命令行下输入命令:setenforce 0暂时性的将SELinux关闭,如果需要永久关闭,需要我们编译软件之前,在uboot和kernel代码中将androidboot.selinux属性设置为disable。
- 安全上下文标签
上面有提到SELinux系统中,每一个对象(进程和文件或者设备)都有自己的安全标签。在串口下,通过ps -Z命令可以查看进程的安全上下文标签,可以通过ls -Z查看文件的安全上下文标签,例如:
- LABEL USER PID PPID NAME
- u:r:init:s0 root 1 0 /init
- u:r:kernel:s0 root 2 0 kthreadd
- ...
- u:r:kernel:s0 root 258 2 irq/322-HPH_R O
- u:r:logd:s0 logd 259 1 /system/bin/logd
- u:r:healthd:s0 root 260 1 /sbin/healthd
- u:r:lmkd:s0 root 261 1 /system/bin/lmkd
- u:r:servicemanager:s0 system 262 1 /system/bin/servicemanager
- u:r:vold:s0 root 263 1 /system/bin/vold
- u:r:surfaceflinger:s0 system 264 1 /system/bin/surfaceflinger
看到一个完整的安全上下文它由四部分组成:u,r,init,s0
u:表示用户,Android系统的SELinux中只定义了一个用户,所有的进程都属于同一个安全用户,叫做:u
r:表示角色,表示这是一个进程的安全上下文,和文件的安全上下文(object_r),属性上下文和其他上下文彼此区分
init:这个字段对于进程或者服务来说,是表示进程运行的域,也叫:domain(后面配置规则的时候会遇到),而对于文件或者其他对象来说,可以理解为类型type。根据不同的对象(有的是文件,有的是设备,有的是进程),Android定义了100多个不同的关键字来区别这些进程的域和文件和设备的类型。我们可以根据需求自定义,例如本文中后面定义的preinstall和preinstall_exec
s0:安全级别,第四列专为多级安全功能(扩展 MLS)而设计,MLS 是一种访问机制,可增加安全上下文和格式敏感度 [: 类别列表] [-敏感度 [: 类别列表]],例如 s0 - s15: c0 - c1023,而在当前Android 版本中不需要类别。敏感度和类别组合表示当前的安全级别,数字根据最低和最高级别的安全功能进行确定。刺裂参数被用于查看MLS限制,其中“15”和“1023”表示最大敏感度和类别,此参数范围可以在Android.mk中进行确定,Android中默认配置为“1”和“1024”,代表敏感度只定义了s0,类别列表定义了c0-c1023。
在安卓系统中,我们只需要重点关注第二和第三个字段,就是角色和域/类型。
- 如何配置SELinux的策略
SEAndroid 策略源位于 SDK system/sepolicy 目录。该策略包括用于生成 SELinux 核心策略文件的源文件:file_contexts 配置、property_contexts 配置、seapp_contexts 配置和 mac_permissions.xml 配置。
* file_contexts 配置用于在构建(例如,系统分区)和运行时(例如,设备节点、服务套接字文件和由 init.rc 创建的/数据目录等)标记文件。
* property_contexts 被用于指定 Android 属性的安全上下文,供查看权限。
* seapp_contexts 配置被用于标记应用进程和应用程序包目录。
* mac_permissions.xml 配置是中间件 MAC 策略
与设备相关的策略文件位于 device/xxxxxxx/common/sepolicy 目录中。
在配置规则之前,我们首先要知道什么是规则?
规则就是针对domain对象的,上面我们说过,每一个进程都属于一个domain(域),规则就是设置哪个domain(域)的对象(就是进程)能够对哪些type(类型)的目标对象(文件或者属性或者设备)具有哪有操作(删除啊,访问啊等等),这就是规则!
配置规则我们一般写在device/xxxxxxx/common/sepolicy目录下的te文件中。可以看到这个目录下有很多的te文件。
那么具体应该如何来编写呢?这里我们举一个例子来说明:假如我们要在init.rc文件中启动一个service,他的功能是在开机的时候安装一些APP,我们已经编译好了这个service的可执行文件:preinstall。
刚刚上面讲了,每一个对象不管是文件还是属性还是设备,在SELinux下都有一个安全上下文,我们首先要定义我们的可执行文件preinstall的安全上下文,这个上面有介绍是在device/xxxxxxx/common/sepolicy/file_contexts文件中设置的:按照上面介绍的安全上下文格式,4个部分,我们定义为:
/system/bin/preinstall u:object_r:preinstall_exec:s0
可以看到,我们给preinstall的类型取名叫:preinstall_exec。为啥叫这个名字?我们稍后介绍!
上面说了规则的主体是domain,那么这里,我们的domain是什么呢?我们的preinstall是写在init.rc中,是fork自init进程的,init进程的domain系统已经配置了,它的domain就叫做“init”。在SELinux的系统中,子进程fork父进程,他们的domain也是默认继承自父进程,所以我们的preinstall所在的子进程的域默认也是“init”。那我们是不是可以开始针对“init”这个domain开始编写规则了呢?答案是不能!因为init进程域是系统的,负责很多基础的工作,而我们的preinstall进程和init进程没有什么关系和联系,是彼此独立的两个功能,在SELinux的原则上,应该将preinstall进程域从init进程域中单独出来,仅针对preinstall子进程域配置该有的权限,而不是直接在init进程域的基础上新增缺少的权限,这样也是为了符合SELinux的规范!那么问题来了,怎么创建preinstall自己的的domain呢?
我们在device/xxxxxxx/common/sepolicy/目录下新建一个文件,取名就叫preinstall.te:
//这句话是声明一个新的domain,名字叫:preinstall
type preinstall domain
//这句话是声明一个新的type,名字叫:preinstall_exec,我们之前在file_contexts中引用了这个type的。
//后面的exec_type就是说把我们新的preinstall_exec和系统定义好的exec_type关联起来,可以类比于java的继承,子类继承父类的属性的概念。因为我们的preinstall是个可执行文件,所以我们继承了系统的exec_type,而不是device_type很好理解吧,也可以同时关联多个type,例如:type preinstall_exec exec_type vendor_type,就是说我的preinstall_type同时和exec_type和vendor_type具有相同的类型。
type preinstall_exec exec_type
//这下面这句话的方法实现在源码中的te_macros文件中,意思我大概翻译一下就是:
当init进程中执行了“preinstall_exec” type的文件时,将新的子进程的domain从默认的init转换到preinstall这个domain
init_daemon_trans(preinstall)
这样写完了之后,我们就了自己的domain,可以开始给自己的domain主体设置权限了,安装APP,我们需要从/data目录下读取文件,那么这个data目录的访问权限如何写呢?
首先我们可以在命令行下通过ls -Z查看一下data目录的安全上下文:
ls -Z
data u:object_r:vendor_file:s0
其中的vendor_file就是我们的type了。写权限命令之前,我们把标准公式拿出来:
[section_rule] [section_domain] [section_target] [section_action]
section_rule:规则名称,常用的有:allow允许,dontaudit不记录,neverallow不允许。
section_domain:domain域,对哪一个domain进行授权,就写哪个名字。
section_target:操作的对象type信息,由安全上下文的中type,后面加冒号,加一个class组成。class有file,dir,fd等等,具体可以在security_classes文件中可以查看。
section_action:具体的动作,读read,写write,创建create等,具体可以在access_vectors文件中查看!
那么很简单了,我们就能写出我们的完整的te文件了:
type preinstall domain
type preinstall_exec exec_type
init_daemon_trans(preinstall)
allow preinstall vendor_file:file read
还有一些我们没想到的权限,就需要抓取打印dmesg | grep avc查看违反了那些规则:可以使用 linux 工具 allow2audit 可以将违反规则的 avc 记录生成放行规则 (适合于dmesg 所有输出,初期的开发阶段)。也可以手动来写规则,例如打印如下:
type=1400audit(1386760471.880:7):avc: denied {entrypoint}for pid=1227comm=``init''path=``/
sbin/healthd''dev=``rootfs''ino=4396scontext=u:r:healthd:s0tcontext=u:object_r:rootfs:s0tclass=file
我们从scontext中获取到domain=healthd,从tcontext中获取到type=rootfs,tclass=file
所以我们可以写出rule如下:
allow healthd rootfs:file entrypoint;
将以上rule语句写进名字为“healthd.te”的文件中,重新编译即可!然后重新测试,在抓取打印看看是否还有缺失的打印,反复几次就OK了!
四、App应用的安全上下文标签
上面介绍了我们的二进制可执行文件一般都是通过domain迁移来确定自己的domain,而APP应用的安全上下文却不一样,这里我们单独介绍。
seinfo
我们都知道APP都会被签名,一般有这四种签名:platform,media,test,shared。而SELinux系统就是根据APP被签名的不同,定义了不同的seinfo(安全信息)概念。在system/sepolicy/private/mac_permissions.xml文件中定义了根据不同的签名来确定不同的seinfo:
<!-- Platform dev key in AOSP -->
<signer signature="@PLATFORM" >
<seinfo value="platform" />
</signer>
<!-- Media key in AOSP -->
<signer signature="@MEDIA" >
<seinfo value="media" />
</signer>
<signer signature="@NETWORK_STACK" >
<seinfo value="network_stack" />
</signer>
可以看到默认这里只定义了三个seinfo,如果我们APP的签名不在这几个里面,那么seinfo会取值为“default”。
确定了seinfo后,再根据system/sepolicy/private/seapp_contexts文件确认doamain和type:
neverallow isEphemeralApp=true domain=((?!ephemeral_app).)*
isSystemServer=true domain=system_server_startup
user=_app seinfo=platform name=com.android.traceur domain=traceur_app type=app_data_file levelFrom=all
user=system seinfo=platform domain=system_app type=system_app_data_file
user=bluetooth seinfo=platform domain=bluetooth type=bluetooth_data_file
user=network_stack seinfo=network_stack domain=network_stack levelFrom=all type=radio_data_file
user=nfc seinfo=platform domain=nfc type=nfc_data_file
user=secure_element seinfo=platform domain=secure_element levelFrom=all
user=radio seinfo=platform domain=radio type=radio_data_file
user=shared_relro domain=shared_relro
user=shell seinfo=platform domain=shell name=com.android.shell type=shell_data_file
user=webview_zygote seinfo=webview_zygote domain=webview_zygote
user=_isolated domain=isolated_app levelFrom=all
user=_app seinfo=app_zygote domain=app_zygote levelFrom=all
user=_app seinfo=media domain=mediaprovider name=android.process.media type=app_data_file levelFrom=user
user=_app seinfo=platform domain=platform_app type=app_data_file levelFrom=user
user=_app isEphemeralApp=true domain=ephemeral_app type=app_data_file levelFrom=all
user=_app isPrivApp=true domain=priv_app type=privapp_data_file levelFrom=user
user=_app minTargetSdkVersion=29 domain=untrusted_app type=app_data_file levelFrom=all
user=_app minTargetSdkVersion=28 domain=untrusted_app_27 type=app_data_file levelFrom=all
user=_app minTargetSdkVersion=26 domain=untrusted_app_27 type=app_data_file levelFrom=user
user=_app domain=untrusted_app_25 type=app_data_file levelFrom=user
user=_app minTargetSdkVersion=28 fromRunAs=true domain=runas_app levelFrom=all
user=_app fromRunAs=true domain=runas_app levelFrom=user
以上文中红色字这行为例:当user是system时,seinfo是platform,那么它的domain就是system_app,它的文件type就是system_app_data_file。
这里的user是指的APP的uid,系统会为每一个应用分配一个uid,定义在system/core/include/private/android_filesystem_config.h文件中,android系统默认保留了1000-9999之内的uid,例如1000就是表示system:
那如果我们的APP是第三方APP,安装后他的user一般是这种形式的:u0_xxx,那么这些APP在seapp_contexts文件中怎么表示呢?Android系统对于所有的不是系统预定义的user的APP,全部表示为:"_app",通过上文中的蓝色字体可以看到,这些APP基本上被认定为domain:untrusted_app, untrusted_app_27, untrusted_app_25 。
#define AID_ROOT 0 /* traditional unix root user */
/* The following are for LTP and should only be used for testing */
#define AID_DAEMON 1 /* traditional unix daemon owner */
#define AID_BIN 2 /* traditional unix binaries owner */
#define AID_SYSTEM 1000 /* system server */
#define AID_RADIO 1001 /* telephony subsystem, RIL */
#define AID_BLUETOOTH 1002 /* bluetooth subsystem */
#define AID_GRAPHICS 1003 /* graphics devices */
#define AID_INPUT 1004 /* input devices */
#define AID_AUDIO 1005 /* audio devices */
#define AID_CAMERA 1006 /* camera devices */
再从上述绿色的行可以看到,还可以用APP的进程name来匹配。
如果我们需要针对我们的某一个预装在system目录下使用platform签名的APP单独一个安全上下文,我们只需要在我们自己的SELinux目录device/xxxxxxx/common/sepolicy/private目录新增seapp_contexts文件(直接修改system/sepolicy目录下会导致无法过CTS认证),新增一条:
user=system seinfo=platform domain=mytest_app name=com.mytest.app type=mytest_app_data_file
前提是mytest_app这个domain和mytest_app_data_file这个type也要手动的定义。
有了单独的domain,我们可以针对这个APP单独配置权限了。
在自己的自定义的目录下新增te文件,要带上:
BOARD_SEPOLICY_DIRS += paxdroid/device/a100/sepolicy
把目录编译进去!