阅读:9203回复:3

Android SELinux详解

楼主#
更多 发布于:2020-04-30 19:48
一、SELinux是什么?
    SELinux是一套完整的安全策略,最开始是美国国家安全局和一些公司联合设计为了针对Linux系统的安全隐患而产生的一套系统,它为每一个进程,每一个文件,每一个属性都定义了标签,用来控制进程对文件的操作的权限控制!
在安卓里面,SELinux有三种状态:
enforce模式:强制模式,必须有配置权限才能访问/操作
permissive模式:宽容模式,打印记录出现的越权行为,但是不禁止该访问/操作
disable:关闭模式,关闭SELinux,可以自由访问

SELinux默认是打开的。我们可以在窗口命令行下输入命令getenforce来查看我们的设备的SELinux状态。我们可以在命令行下输入命令:setenforce 0暂时性的将SELinux关闭,如果需要永久关闭,需要我们编译软件之前,在uboot和kernel代码中将androidboot.selinux属性设置为disable

二、安全上下文标签
    上面有提到SELinux系统中,每一个对象(进程和文件或者设备)都有自己的安全标签。在串口下,通过ps -Z命令可以查看进程的安全上下文标签,可以通过ls -Z查看文件的安全上下文标签,例如:
  1. LABEL                          USER     PID   PPID  NAME
  2. u:r:init:s0                    root 1 0 /init
  3. u:r:kernel:s0                  root 2 0 kthreadd
  4. ...
  5. u:r:kernel:s0                  root 258 2 irq/322-HPH_R O
  6. u:r:logd:s0                    logd 259 1 /system/bin/logd
  7. u:r:healthd:s0                 root 260 1 /sbin/healthd
  8. u:r:lmkd:s0                    root 261 1 /system/bin/lmkd
  9. u:r:servicemanager:s0          system 262 1 /system/bin/servicemanager
  10. u:r:vold:s0                    root 263 1 /system/bin/vold
  11. 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多个不同的关键字来区别这些进程的域和文件和设备的类型。
s0:安全级别,第四列专为多级安全功能(扩展 MLS)而设计,MLS 是一种访问机制,可增加安全上下文和格式敏感度 [: 类别列表] [-敏感度 [: 类别列表]],例如 s0 - s15: c0 - c1023,而在当前Android 版本中不需要类别。
在安卓系统中,我们只需要重点关注第二和第三个字段,就是角色和域/类型。

三、如何配置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。
1.首先刚刚上面讲了,每一个对象不管是文件还是属性还是设备,在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:
#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是第三方APP,安装后他的user一般是这种形式的:u0_xxx,那么这些APP在seapp_contexts文件中怎么表示呢?Android系统对于所有的不是系统预定义的user的其他,全部表示为:"_app",通过上文中的蓝色字体可以看到,这些APP基本上被认定为domain:untrusted_app, untrusted_app_27, untrusted_app_25

再从上述绿色的行可以看到,还可以用APP的进程name来匹配。
如果我们需要针对我们的某一个预装在system目录下使用platform签名的APP单独一个安全上下文,我们只需要在我们自己的SELinux目录device/xxxxxxx/common/sepolicy/private目录新增seapp_contexts文件,新增一条:
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单独配置权限了。

If you have nothing to lose, then you can do anything.
沙发#
发布于:2020-05-01 07:18
先赞一个
[url]http://190.lsal.cn/195/1329.gif?0728100424873[/url]
板凳#
发布于:2020-05-12 11:07
再暂一个
地板#
发布于:2021-03-15 17:27
mark
签个名
游客

返回顶部