官方文档:
https://source.android.com/docs/security/features/selinux
https://source.android.com/docs/security/features/selinux/images/SELinux_Treble.pdf
Your visual how-to guide for SELinux policy enforcement | Opensource.com
SeLinux(Security-Enhanced Linux)是一个标签系统(labeling system)。每个进程都有一个label(称为process label),每个文件系统所涵盖的文件/目录、网络端口、设备等对象也有一个lable(称为Object label)。SeLinux通过编写规则来控制一个process label对一个Object label的访问,这个规则称之为策略(Policy)。SeLinux的这种安全机制称为Mandatory Access Control (MAC),是在内核层实现的。
在标准的Linux上,也就是未实施SeLinux的Linux上,传统的访问控制是通过owner/group + permission flags(比如rwx)实现的,称之为Discretionary Access Control (DAC).
SeLinux和传统的DAC不是替代的关系,而是并行的关系。两者同时在起作用。之所以出现SeLinux,是因为传统DAC的安全机制过于粗粝,而Selinux提供了更为细致和安全的访问控制。简言之,传统DAC机制下,一旦你获得了root权限,将无所不能,但在SeLinux的机制下,即使获得了root权限,也仍然需要遵循已经设置好的访问策略,只有指定的进程才可以访问指定的文件。
SeLinux有两种运行模式:
- Permissive mode:当访问并未授权时,不会阻止访问,但会打印log
- Enforcing mode:当访问未被授权时,会阻止访问,并且会打印log
log将出现在dmesg和logcat。
除了可以对整体进行模式设置,也可以针对某个进程单独设置某个模式,除此之外的进程使用全局模式。
SeLinux在Android 4~7和Android 8以后采用了不同的设计
Android 4~7上,SeLinux的策略是作为一个整体来编译和更新的;
Android 8及以后,SeLinux采用了模块化、动态化设计,Platform(可以理解为AOSP的部分)、Non-Platform(vendor、odm、oem的部分,这里总体称为vendor部分)的SeLinux策略分别独立编译、升级。
附一张Android设备的架构图:
编译后会生成对应的img文件
● system.img. Contains mainly Android framework.
● boot.img. (kernel/ramdisk) Contains Linux kernel + Android patches.
● vendor.img. Contains SoC-specific code and configurations.
● odm.img. Contains device-specific code and configurations.
● oem.img. Contains OEM/carrier-related configurations and customizations.
● bootloader. Brings up the kernel (vendor-proprietary).
● radio. Modem (proprietary).
Android 8以后,SeLinux的策略文件可以伴随相应的img独立编译或者OTA。
什么是标签(Label)?怎么基于Label对访问进行控制?
先抛开Label这个概念不说。所谓SeLinux里的访问控制,就是判定一个Source有没有权限去访问Target。这里的Source一般就是进程,Target最长见的就是文件系统(比如文件、目录、socket、设备等等),当然还有其他类型的Target。换句话说,SeLinux的机制就是通过读取一个“规则”,来控制一个进程有没有权限去访问一个文件(或其他类型)。
上面说的“规则”,在SeLinux里的术语叫做Policy(策略)或叫Access Vector Rule。是可以由AOSP和厂商、供应商来编写的。
上面的Source,还叫做Subject(主体)
上面的Target,还叫做Object(对象或客体)
Label、Source、Target、Subject、Object,这些都不重要, 在实际语法中并没有相关关键词,只要各种资料里出现这些词的时候知道其所指就可以。
策略规则的语法为:
- Source - The type (or attribute) of the subject of the rule. Who is requesting the access?(是谁在请求访问一个资源)
- Target - The type (or attribute) of the object. To what is the access requested?(被访问的对象)
- Class - The kind of object (e.g. file, socket) being accessed.(被访问者的类型)
- Permissions - The operation (or set of operations) (e.g. read, write) being performed.(对Target具体要做什么操作?比如对被访问者是文件来说,是要读、写它还是其他操作?)
具体例子如下:
这个例子是说,允许sample_app这个进程去访问app_data_file(它是一个file类型,也就是文件),允许的操作是read和write。
而其实这里的sample_app并不是一个真正的具体的进程名,而是在系统编译阶段就定义好的一个标签(Label),一些真正的进程被映射到sample_app这个标签上,那么在执行上面规则的时候,其实生效的、有权限访问app_data_file的是所有映射了sample_app标签的那些进程。同样的,app_data_file也不是一个具体的文件名。它也是一个提前声明了的标签,一些真正的文件被映射到这个标签上,sample_app有权访问的是所映射的这些文件。
从这里看出来,有别于传统DAC的Owner、Group、Permissions 的控制方式,所谓的“基于标签系统”的SeLinux,就是这种通过声明标签的方式来表述访问规则的。
标签只是一种概念性的东西,具体体现在策略文件里,则是抽象成了Type、Attribute、Class、Permissions这些具体关键字。
以下面这个规则举例
Rule Name
上面规则示例中,allow就是Rule Name的一种。SeLinux有多种RuleName:
- allow:表示允许Source对Target执行许可的操作
- neverallow:表示不允许Source对Target执行制定的操作
- auditallow:表示允许操作并记录访问决策信息
- dontaudit:表示不记录违反规则的决策信息,切违反规则不影响运行。
其中allow是用的最多的。
Type
上面的示例中,sample_app、app_data_file都是一个Type。简单理解就是将一个或几个进程声明为Type A, 将一个或多个文件声明为Type B。那么在控制这个进程有没有权限访问这个文件的时候,只用A和B来表示就可以了。
这样做有什么好处?“一类进程”总比具体的“一个进程”要灵活的多。把多个进程声明为同一个Type,那么在写规则的时候只要描述这个Type,那么这个Type对应的所有进程都会生效。文件或其他对象也是同样的。
也就是说,在规则中描述Source能不能访问Target,是通过Type来表述的。
Type是厂商或供应商可以自定义的
将多个Type归为一组,就是一个Attribute。
通俗的说,把一些进程声明为Type,但是多个Type有某种共通的特性,就可以把这些Type声明为同一个Attribute。在描述规则的时候,可以将Source或者Target指定为一个Attribute而不是Type,这样所有属于这个Attribute的Type都生效。
AOSP本身内置了一些Attribute,而这些Attribute很多都是约定俗称的固定含义。比如:
domain
所有进程的type必须归属于domain。domain因此成了进程type的常见表述。
file_type
所有文件type都归属于file_type
...
AOSP内置的Attribute见
platform/system/sepolicy/public/attributes
platform/system/sepolicy/private/attributes
platform/system/sepolicy/prebuilts/api/[version]/public/attributes
platform/system/sepolicy/prebuilts/api/[version]/private/attributes
Class
上面示例中,“:file”就是一个Class。简单说就是将某些被访问对象归为一种Class,比如说被访问的是文件,Class一般就是file,如果是目录,Class就是dir,如果是socket,Class就是socket等等。Class是AOSP内定义好的,一般不需要自定义
具体有那些Class,可见源码platform/system/sepolicy/private/security_classes
Class是用来做什么的?其实是与Permissions相关的。
Permissions
即示例中的 { read write }。表示具体可以做什么操作。不同的Class有不同的Permissions集合。罗列一些Class对应的Permission(非完整):
Class
Permission
file
ioctl read write create getattr setattr lock relabelfrom relabelto append unlink link rename execute swapon quotaon mounton
directory
add_name remove_name reparent search rmdir open audit_access execmod
socket
ioctl read write create getattr setattr lock relabelfrom relabelto append bind connect listen accept getopt setopt shutdown recvfrom sendto recv_msg send_msg name_bind
filesystem
mount remount unmount getattr relabelfrom relabelto transition associate quotamod quotaget
process
fork transition sigchld sigkill sigstop signull signal ptrace getsched setsched getsession getpgid setpgid getcap setcap share getattr setexec setfscreate noatsecure siginh setrlimit rlimitinh dyntransition setcurrent execmem execstack execheap setkeycreate setsockcreate
security
compute_av compute_create compute_member check_context load_policy compute_relabel compute_user setenforce setbool setsecparam setcheckreqprot read_policy
capability
chown dac_override dac_read_search fowner fsetid kill setgid setuid setpcap linux_immutable net_bind_service net_broadcast net_admin net_raw ipc_lock ipc_owner sys_module sys_rawio sys_chroot sys_ptrace sys_pacct sys_admin sys_boot sys_nice sys_resource sys_time sys_tty_config mknod lease audit_write audit_control setfcap
MORE
AND MORE
可以从对应的Class中选取任意个Permission
Class与Perimssion的完整映射具体见源码:kernel/arm64/security/selinux/include/classmap.h
示例
以控制狗是否有权吃猫粮狗粮为例。
有一个狗叫小黄, 一种狗粮叫“狗粮A”。
现在声明一个Type叫dog,一个Type叫dog_chow,系统本身内置了Class叫food,food对应的permissions里包含了eat这个权限。
随后将小黄映射到dog这个type,将狗粮A映射到dog_chow这个type
这样,在添加了这样一条规则后,小黄就有权限去吃狗粮A了:
此时如果还有一只狗小白,那么可以将小白映射到dog这个type,这样小白也可以有权限吃狗粮A。
上面所说的“映射”,即将一个进程关联到一个Type,或者将一个文件关联到一个Type,是通过context来完成的。
进程Context(seapp_contexts)
一个进程的Context条目可能如下:
user=_app代表这是一个常规的app;
isPrivApp=true代表这是一个预置app;
name=com.android.vzwomatrigger 指定了进程名
domain=vzwomatrigger_app 将进程名关联到一个type
type=privapp_data_file 这是一个文件type,指定了app的data directory目录所属的type
levelFrom=all MLS/MCS的level相关
AOSP内置进程Context见platform/system/sepolicy/private/seapp_contexts
供应商或厂商要定义自己的seapp_context,将在/vender下或<root>/device/manufacturer/device-name/sepolicy下相应目录添加
文件Context
一个文件的Context可能这样:
/system/bin/bcc 指的是具体文件
u:object_r:rs_exec:s0是一个security context。其中的rs_exec为type。这样文件和type就进行了关联
Security Context
其格式为:
在Android中,
user是固定的,永远为u
role是固定的,访问者(称subject或source)永远为r;被访问者(称object或target)永远为object_r。一般情况下,进程一方就是subject,文件一方就是object,所以一般进程的role为r,文件的role为object_r。
type为与文件关联的type
sensitivity是固定的,永远为s0
categories是Multi-Level Security (MLS) 协议,用来隔离一个app的data,防止被另一个app访问,或者隔离不同用户间对同一个app data的访问。
AOSP内置的文件Context见platform/system/sepolicy/private/file_contexts
seinfo
seapp_context里除了可以将一个具体应用映射到一个domain,也可以将seinf映射到domain。mac_permissions.xml里定义了seinfo。其会根据app所属的signature来为其分配一个seinfo。
比如:
platform/system/sepolicy/private/mac_permissions.xml
也就是说所有拥有PLATFORM signature的app,将拥有platform这个seinfo。在seapp_context中可以如下配置:
所有拥有platform签名的,并且未配置具体context的app,将遵循其seinfo platform的规则。
untrusted_app
既为配置seinfo,又未配置seapp_context的app,将默认为untrusted_app。各级seapp_context中也有对untrusted_app的权限配置。
查看当前Context
要看进程的Context,使用ps -Z
查看文件的Context,使用
参见https://android.googlesource.com/platform/system/sepolicy/
之前提到Android架构中大致包含AOSP、厂商、Vendor等部分。在Android 8以上的系统中,AOSP和厂商、供应商的部分是独立配置的,方便OTA更新。
针对这种架构,SeLinux的Policy文件体系包含以下目录:
system/sepolicy/public:这部分是AOSP公开给vender使用的,作为其基础api。比如声明了domain的attribute就在这下面:platform/system/sepolicy/public/attributes。这部分Policy需要做兼容处理,因为Vender引用了这里policy,如果OTA单独升级了AOSP,需要做向后兼容处理。详见https://source.android.com/docs/security/features/selinux/compatibility
system/sepolicy/private:AOSP内部使用的,即system image内部用的policy。vender不应该感知到这部分policy
system/sepolicy/vendor:vender部分的policy
device/manufacturer/device-name/sepolicy:特定设备的policy,比如三星设备:device/samsung/tuna/sepolicy
BoardConfig.mk makefile
AOSP的SeLinux Policy文件一般不需要更改,或少量更改。厂商侧主要修改和定制的Policy在/device/manufacturer/device-name/下,/device/manufacturer/device-name/BoardConfig.mk用于指定Policy文件的具体路径,通过BOARD_VENDOR_SEPOLICY_DIRS,比如:
device/samsung/tuna/BoardConfig.mk
system_ext 和 product分区
在Android 11及以上系统中,system_ext 和 product分区也独立出单独的policy,并且区分了public和private。public部分同样共vender引用。system_ext和product的版本兼容处理由各partner自己负责,AOSP不负责。
SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS:指定system_ext的public的目录,安装到system_ext分区。
SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS:指定system_ext的private目录。system_ext内部使用。安装到system_ext分区
PRODUCT_PUBLIC_SEPOLICY_DIRS:指定product分区的public目录。安装到product分区
PRODUCT_PRIVATE_SEPOLICY_DIRS:指定product的private目录。安装到product分区。
把SeLinux策略配置相关的文件统称为Policy文件
Policy文件一般包含:
TE文件
就是一堆.te文件。TE里定义了Type、访问规则等。对进程和文件Type的定义、访问规则的定制就只在TE里完成的。一般每个进程有一个独立的te文件,而所有文件的te统一声明在一个file.te文件中。详见3.3节
Context files
file_contexts:前面说过,file_contexts里面定义了文件的context,用于将文件目录和文件的type关联起来。
genfs_contexts:用于将文件系统(比如/proc和vfat)与type关联起来。
property_contexts:用于将Android系统properties与type关联起来
service_contexts:用于将service进程与type关联
seapp_contexts:用于将app与type关联
mac_permissions.xml:根据app的signature或包名,为app分配一个seinfo。seinfo用于在app没有明确关联一个type时归属一个默认type。
keystore2_key_contexts :为Keystore 2.0 namespaces分配一个label。
用来声明Attribute
security_classes
用来声明Class
所有的规则配置,或称Policy,都写在.te文件中。编写完TE文件后,将TE文件放在对应目录下,Android系统编译后.te文件将被编译成.cil文件。在init进程启动阶段,会将.cil文件汇总统一编译成一个命名为policy的文件。cil文件是可读的,policy文件是二进制的。在系统运行时最终加载使用的是policy文件。
policy文件一般在/sys/fs/selinux/policy
一般一个进程单独声明一个TE文件。比如一个dhcp进程单独有一个te文件叫dhcp.te。而文件的te统一整合在file.te(比如platform/system/sepolicy/public/file.te)中。
针对Platform(AOSP的部分)、Non-Platform(厂商、供应商的部分),TE会有不同的放置目录。
下面是TE文件的一个例子:
platform/system/sepolicy/public/dhcp.te
TE文件语法解析
type dhcp, domain; -- 声明一个type名为dhcp,其继承domain这个Attribute,也就是说在声明时便继承了domain所拥有的权限。domain属性是进程专用的,很显然这是一个进程的te文件。
permissive dhcp; -- 将dhcp标识为permissive模式。之前说到,SeLinux开启模式可以全局设置,也可以针对进程单独设置。这里是单独设置该进程的模式。
type dhcp_exec, exec_type, file_type; -- 声明一个type名为dhcp_exec, 从属于exec_type和file_type两个Attribute。也就是说同时继承两个Attribute所代表的文件。exec_type用于代表可执行文件,也就是进程的可执行文件入口;file_type代表通用文件。从这里我们知道dhcp_exec代表dhcp可执行文件的入口。
init_daemon_domain(dhcp) -- 这是一个宏,代表这是一个从init启动的进程,并且可以同init通信。宏的源码见platform/system/sepolicy/public/te_macros
net_domain -- 也是一个宏,可以进行通用的网络操作,比如读写TCP包,操作socket等。
allow dhcp self:capability { setgid setuid net_admin net_raw net_bind_service}; -- 这个就是具体的规则描述了。dhcp是source方, self是target方,capability是一个Class名,其后是可以做的操作。这句话是说允许dhcp这个进程对自己做setgid等操作。self关键字用来表示source可以对自己做什么操作。
除了上面例子里出现的关键字,有时我们还会看到typeattribute这个关键字:它代表将之前已经声明过的Type关联到一个之前声明过的Attribute。
更多语法见TypeStatements - SELinux Wiki
Android 8以上编译过程中将把/system 和 /vendor下的policy合并。这个逻辑体现在/platform/system/sepolicy/Android.mk。
具体policy位置:
Location
Contains
system/sepolicy/public
The platform's sepolicy API
system/sepolicy/private
Platform implementation details (vendors can ignore)
system/sepolicy/vendor
Policy and context files that vendors can use (vendors can ignore if desired)
BOARD_SEPOLICY_DIRS
Vendor sepolicy
BOARD_ODM_SEPOLICY_DIRS (Android 9 and higher)
Odm sepolicy
SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS (Android 11 and higher)
System_ext's sepolicy API
SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS (Android 11 and higher)
System_ext implementation details (vendors can ignore)
PRODUCT_PUBLIC_SEPOLICY_DIRS (Android 11 and higher)
Product's sepolicy API
PRODUCT_PRIVATE_SEPOLICY_DIRS (Android 11 and higher)
Product implementation details (vendors can ignore)
编译过程:
- 将Policy转化成CIL( Common Intermediate Language )文件,包括
- system + system_ext + product的public部分的policy
- 合并private + public policy
- 合并public + vendor and BOARD_SEPOLICY_DIRS policy
- 将public部分的policy进行整理成对应版本policy,作为vendor policy的一部分。然后将public的policy传给合并后的public + vendor and BOARD_SEPOLICY_DIRS policy,告知其哪部分可以连接到platform的attribute
- 创建一个mapping文件,连接platform和vendor的部分。
- 合并所有的policy文件,整合mapping、platform和vender的policy,最终输出一个二进制的名为policy的文件。一般这个文件的位置在/sys/fs/sepolicy/policy。这个policy最终会被kernel加载。这个工作是在init进程初始化时进行的。也就是说是在运行时进行的。
在SeLinux正式开启之前,init进程会收集所有的cil文件,将其编译成policy,这个过程需要花费1-2秒时间。为了更快完成此过程,会将CIL在编一阶段预编译,放在/vendor/etc/selinux/precompiled_sepolicy和/odm/etc/selinux/precompiled_sepolicy下,并且附有sha256 摘要。运行时会检查sha256是否有变化,如果没有变化就直接使用预编译的文件。
编译kernel时,指定CONFIG_SECURITY_SELINUX=y。
比如在kernel/arm64/arch/arm64/configs/defconfig中添加此配置。
在kernel/arm64/include/linux/selinux.h会对该配置进行判断:
在厂商目录的BoardConfig.mk中(比如/device/google/trout/trout_arm64/BoardConfig.mk)添加如下配置,可改变SeLinux模式
或
上面两个配置只用于开发阶段。正式版本需要移除,否则无法通过CTS。即正式版本上,SeLinux是强制enforcing模式。
非正式的修改方法:
ALLOW_PERMISSIVE_SELINUX宏定义了是否启用permissivve模式。
在core/init/Android.bp中有对该宏设置:
实际的生效位置:
厂商或供应上在添加了一些进程、访问行为、策略等之后,需要确认是否符合SeLinux规则。
如果有任何访问限制出现,则会打印log:
精简下log,只需avc:后面的即可
各字段含义:
{ find }:被组织的行为。
scontext(可以理解为source context的缩写):该行为的执行者的context;
tcontext(可以理解为target context的缩写:)被访问者的context;
tclass:被访问者的class,详见本文之前的解释。
使用auditallow生成TE
ubuntu中,通过apt-get audit2allow安装audit2allow。
获取policy文件:
/sys/fs/selinux/policy
将上面的avc log保存在test.avc文件中,然后执行:
以上就是本篇文章【Android - 深入浅出理解SeLinux】的全部内容了,欢迎阅览 ! 文章地址:http://dfvalve.xrbh.cn/news/7347.html 资讯 企业新闻 行情 企业黄页 同类资讯 首页 网站地图 返回首页 迅博思语资讯移动站 http://keant.xrbh.cn/ , 查看更多