SEAndroid 项目实战
1、介绍
首先什么是 SEAndroid,我们都知道 Android 是基于 linux系统搭建的,而 SELinux(Security-EnhancedLinux) 是美国国家安全局(NSA)对于强
制访问控制的实现,是 Linux 历史上最杰出的新安全子系统,SEAndroid 也是基于 SELinux 从 Android 4.4平台之后加入到 Android 系统的,
SEAndroid在SELinux的基础上添加了新的访问控制,比如跨进程的binder调用、property的读 写等。
2、如何配置
这篇文章可能不太适合初次接触 SEAndroid 的人,主要是讲解如何在android 平台上配置定制Android系统的 sepolicy。我做的是车机端的开发,所以以下均以基于android 的车机开发为例子。
2.1、sepolicy的来源
一个完整的android 车机开发,sepolicy的来源主要有四个部分:
1、 system/sepolicy : 这一部分是android原生的所有进程和资源的访问管控
2、 device/xxx/sepolicy :这一部分是硬件设备提供厂商点亮屏幕自己增加的权限
3、 ivi/xxx/sepolicy: 这一部分是也是我们开发主要配置的权限,是在原生android系统进行定制的时候产生的权限。
4、 third-party/app/sepolicy: 这一部分是车机端集成第三方app时,第三方app访问系统资源产生的权限(一般这一部分是不会专门
进行配置的,因为原生android系统已经把第三方 app所需要的权限已经配置了,如果集成的第三方app还需要特定权限,需要谨慎配置)
2.2 资源划分
我们可以根据label的关联,把所有资源划分为以下几部分:
system/sepolicy/public/file.te
1、资源label 需要关联fs_type label的,这一部分资源包括 /proc、/sys 目录下的资源。
2、资源label 需要关联file_type label的, 这一部分资源包括/data、/vendor 、/system 还有厂商自己添加的分区比如 /update、/map、/log等
system/sepolicy/public/device.te
3、资源label 需要关联 dev_type label, 这一部分资源包括/dev 目录下的设备节点
system/sepolicy/public/property.te
4、资源label需要关联 property_type label,这一部分资源包括所有property
system/sepolicy/public/hwservice.te
5、资源label需要关联hwservice_manager_type label, 这一部分资源包括所有的hidl服务
system/sepolicy/public/service.te
6、 资源label需要关联service_manager_type label,这一部分资源包括所有的aidl服务
还有一部分是socket,这一部分基本都是关联file_type 但是它的创建和使用还是和普通的文件有区别的。
我之所以把上面的资源进行划分是因为在开发中,我遇到的需要添加的资源和进程需要访问的资源就这些,从普通文件到虚拟文件、device、socket、property、hidl服务、aidl服务。
3、进程创建资源
3.1 对执行文件打label
我们新加了一个进程,执行文件放在 vendor/bin/newprocess, 那我首先要给这个执行文件打label做法如下:
在file_contexts里面增加如下
vendor/bin/newProcess u:object_r:newProcess_exec:s0
在newprocess.te(这个是自己新加的) 里面增加如下
type newProcess, domain;
type newProcess_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(newProcess)
3.2 新增进程创建普通文件、虚拟文件、device
1、比如 newProcess进程要在/vendor/etc/目录下创建一个test_file.txt 文件,则需要配置权限
allow newProcess vendor_configs_file:file create_file_perms
2、比如 newProcess进程要在/vendor/etc/目录下创建一个test_dir 目录,则需要配置权限
allow newProcess vendor_configs_file:file create_dir_perms
解释:/vendor/etc/ 的 label 就是 vendor_configs_file,可是在实机中ls -lZ 查看。也可以在file_context里面查看。
对于新建的目录或者文件,如果我们不重新配置label,则会继承上一级的目录label。
如果需要重新赋label则需要以下操作:
在file_context里面追加
/vendor/etc/test_file\.txt u:object_r:my_test_file:s0
在file.te 里面追加
type my_test_file, vendor_file_type, file_type;
3、比如 newProcess进程要在/sysfs/class/net目录下创建一个test_file.txt 文件,则需要配置权限
allow newProcess sysfs_net:file create_file_perms
解释:/sysfs/class/net 的 label 就是 sysfs_net,可是在实机中ls -lZ 查看。也可以在genfs_contexts里面查看。
对于新建的目录或者文件,如果我们不重新配置label,则会继承上一级的目录label。
如果需要重新赋label则需要以下操作:
在genfs_contexts里面追加
genfscon sysfs /class/net u:object_r:sysfs_net_test_file:s0
在file.te 里面追加
type sysfs_net_test_file, fs_type, sysfs_type;
4、比如 newProcess进程要在/dev/ 目录下创建一个kmsg 设备节点,则需要配置权限
allow newProcess device:file create_file_perms;
allow newProcess self:capability {mknod };
解释:/dev/ 的 label 就是 device,可是在实机中ls -lZ 查看。也可以在file_contexts里面查看。
一般在 /dev 目录下直接创建设备节点需要比较大的权限,还需要 mknod 能力。所以一般要在/dev 下创建设备节点都是init uevent vold等权限
比较高的进程
对于新建的设备节点,如果我们不重新配置label,则会继承上一级的目录label。
如果需要重新赋label则需要以下操作:
在file_contexts里面追加
/dev/kmsg u:object_r:kmsg_device:s0
在device.te 里面追加
type kmsg_device, dev_type;
3.3 新增hidl aidl 服务
1、我们这里根据系统原有的一个hidl服务,列举它是怎么配置权限的
/vendor/bin/hw/android.hardware.automotive.audiocontrol@1.0-service 这也是一个执行文件,是一个hidl的服务
我们要给他配置label在file_contexts里面追加
/(vendor|system/vendor)/bin/hw/android\.hardware\.automotive\.audiocontrol@1\.0-service u:object_r:hal_audiocontrol_default_exec:s0
解释:/(vendor|system/vendor) 就是/vendor 或者 system/vendor
2、之后和上面3.1 一样对执行文件打一个label, 前面三个权限是任何一个hidl执行文件都要配置的
type hal_audiocontrol_default, domain;
type hal_audiocontrol_default_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(hal_audiocontrol_default)
hal_server_domain(hal_audiocontrol_default, hal_audiocontrol)
3、在hwservice_contexts 里面对这个hidl服务提供出去的接口类打label
android.hardware.automotive.audiocontrol::IAudioControl u:object_r:hal_audiocontrol_hwservice:s0
这样一个hidl服务基本就可以在SEAndroid打开的情况下跑起来了
4、这里先声明一下,自从android8 treble之后,厂商的aidl不允许再注册到servicemanager中了,需要另外注册到vndservicemanager中去。
这里我们在一个aidl文件里面定义了一个aidl 服务的接口,之后将它的子类new 出来注册到vndservicemanager中去。
比如 vndservicemanager.add("test_srevice", new TestService() );
那现在就有一个服务名为test_service的aidl服务了。
首先要给这个服务打label,如下
在vndservicemanager_contexts里面追加
test_service u:object_r:test_service_type:s0
在 vndservice.te中追加
test_service_type vndservice_manager_type;
允许test_service 注册到 vndservice_manager中去
allow test_service_type test_service_type:vndservice_manager add;
3.4 新增property
1、增加property
这里面厂商自己增加的property也需要根据android8 的treble 以避免和android 原生的property冲突
比如增加一个property ro.product.automotive.socversion
需要在property_contexts里面增加
ro.product.automotive.socversion u:object_r:automotive_sys_prop:s0
然后在property.te里面增加
type automotive_sys_prop, property_type;
3.5 创建 socket
1、创建socket
在newprocess.te 里面增加
file_type_auto_trans(newprocess, socket_device, newprocess_socket)
解释:file_type_auto_trans是一个宏定义在system/sepolicy/public/te_macros 里面
/dev/socket(/.*)? u:object_r:socket_device:s0 , socket_device 就是 目录 /dev/socket/的label我们创建的socket也就是在这个目录下面
总体的一句话就是newprocess 这个进程在 /dev/socket/ 目录下创建了一个socket 文件label由 socket_device 切换到 newprocess_socket。
3.6 给非root权限赋值capability
1、给进程赋值 capability。
我们知道SEAndroid SELinux 的出现最终都是要显示root 进程的权限,而root进程除了可以不受DAC控制之外,另一个能力就是它具备30多个特殊的capability,比如我们要创建一个节点就是需要能力mknod,如下配置
在newprocess.te 里面增加
allow newprocess self:capability mkond;
这里假设的是newprocess不具备root权限
以上大体就是一个进程创建某些资源时权限的配置 和创建资源后资源 label 的配置。大家可以先做一个参考。
4、进程访问资源
这里我们还是按照上面的创建资源的顺序来配置访问权限
4.1、访问普通文件 临时文件
假设一个新的进程oldprocess 他的初步权限已经配置好了,接着要读写/vendor/etc/test_file.txt , 而这个文件的label为 my_test_file,则需要配置权限
allow oldprocess my_test_file:file rw_file_perms
rw_file_perms是一个宏定义在 system/sepolicy/public/global_macros
4.2 访问设备节点
这里要区别这个设备节点是什么访问方式,可以在实机中使用 ls -l查看,如下,在最前面可以看到c b 等,c即使char型字符设备、b就是block 块设备,那么一下的配置就不一样。
比如 kmsg_device 是一个字符设备节点的label,则oldprocess 进程要对其读写,应配置为
allow oldprocess kmsg_device:chr_file rw_file_perms
比如 kmsg_device 是一个块设备节点的label,则oldprocess 进程要对其读写,应配置为
allow oldprocess kmsg_device:blk_file rw_file_perms
4.3 访问一个hidl服务
比如说要访问android.hardware.automotive.audiocontrol@1.0-service ,它的label为 hal_audiocontrol_default,则需要配置
allow oldprocess hal_audiocontrol_hwservice:hwservice_manager {find}; //意思为允许oldprocess 在hwservucemanager中查找 label为 hal_audiocontrol_hwservice的接口类
allow oldprocess hwservicemanager:binder { call transfer };
binder_call(oldprocess , hal_audiocontrol_default) //允许oldprocess binder call android.hardware.automotive.audiocontrol@1.0-service。这个binder_call()也是一个宏在 system/sepolicy/public/te_macros里面。
4.4 访问一个aidl服务
就访问我们自己追加的aidl服务
allow oldprocess test_service_type:vndservice_manager find; //允许oldprocess 在 vndservicemanager中find label为test_service_type的aidl服务
allow oldprocess vndservicemanager:binder { call transfer }; // 允许oldprocess 对 vndservicemanager 进行binder的call transfer
4.5 读写一个 property
就访问上面创建的property。
set_prop(oldporcess, automotive_sys_prop) //这是一个宏意思就是 oldporcess进程可以对 label为automotive_sys_prop的property进行set,一般用了set这个宏,里面其实就把get也给了。
get_prop(oldporcess, automotive_sys_prop) // 这就是单给读的权限。
4.6 上面举例创建了一个本地stream 流 socket,这里创建一个网络tcp socket带访问的。
allow oldporcess self:tcp_socket { create_socket_perms listen accept};
allow oldporcess port:tcp_socket name_bind;
allow oldporcess node:tcp_socket node_bind;
下面是访问上面创建的stream socket
allow oldprocess newprocess_socket:sock_file write;
allow oldprocess newprocess:unix_stream_socket connectto;
呀,以上进程访问资源的权限基本就这些,到此配置算结束了
5 总结
其实这个配置还是比较繁琐的,内容比较复杂的。如果在实际开发中让每个进程的开发者都自己去配置自己的权限,那就要都要学习一遍 SEAndroid,这个是不可能的,那如何在不让模块担当不需要熟悉SEAndroid,而专门的SEAndroid 配置人员又能知道这些模块需要哪些权限呢?这就需要SEAndroid的担当用一个可视化的表格,让担当将需要访问系统的资源都列出来,这也就是我一开始把资源划分的目的,然后由专门的SEAndroid配置人员,根据具体进程,具体访问和申请的资源来配置,具体的权限。
当然这个可视化的表格和生成权限的脚本我们已经实现了,哈哈。
6 遗憾
遗憾点就是没有能力跟到很深的位置去一探究竟 , selinux这一套MAC是如何将进程访问任何资源都做了控制的,哎!