Android - 深入浅出理解SeLinux

1. 概述

官方文档:

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。

2. 概念

什么是标签(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,这些都不重要, 在实际语法中并没有相关关键词,只要各种资料里出现这些词的时候知道其所指就可以。

2.1 规则Policy Rule(或叫Access Vector Rule)

策略规则的语法为:

allow source target:class permissions;
  • 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具体要做什么操作?比如对被访问者是文件来说,是要读、写它还是其他操作?)

具体例子如下:

allow sample_app app_data_file:file { read write };

这个例子是说,允许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这些具体关键字。

2.2 规则里的关键字说明

以下面这个规则举例

allow sample_app app_data_file:file { read write };

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是厂商或供应商可以自定义的

Attribute

将多个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了:

allow dog dog_chow:food eat;

此时如果还有一只狗小白,那么可以将小白映射到dog这个type,这样小白也可以有权限吃狗粮A。

2.3 Context

上面所说的“映射”,即将一个进程关联到一个Type,或者将一个文件关联到一个Type,是通过context来完成的。

进程Context(seapp_contexts)

一个进程的Context条目可能如下:

user=_app isPrivApp=true name=com.android.vzwomatrigger domain=vzwomatrigger_app type=privapp_data_file levelFrom=all

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下相应目录添加

文件Context

一个文件的Context可能这样:

/system/bin/bcc                 u:object_r:rs_exec:s0

/system/bin/bcc 指的是具体文件

u:object_r:rs_exec:s0是一个security context。其中的rs_exec为type。这样文件和type就进行了关联

Security Context

其格式为:

user:role:type:sensitivity[:categories]

在Android中,

user是固定的,永远为u

role是固定的,访问者(称subject或source)永远为r;被访问者(称object或target)永远为object_r。一般情况下,进程一方就是subject,文件一方就是object,所以一般进程的role

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映射domainmac_permissions.xml定义seinfo根据app所属signature分配一个seinfo

比如

platform/system/sepolicy/private/mac_permissions.xml

<!-- Platform dev key in AOSP -->
    <signer signature="@PLATFORM" >
      <seinfo value="platform" />
    </signer>

    <!-- Sdk Sandbox key -->
    <signer signature="@SDK_SANDBOX" >
      <seinfo value="sdk_sandbox" />
    </signer>

    <!-- Bluetooth key in AOSP -->
    <signer signature="@BLUETOOTH" >
      <seinfo value="bluetooth" />
    </signer>

    <!-- Media key in AOSP -->
    <signer signature="@MEDIA" >
      <seinfo value="media" />
    </signer>

    <signer signature="@NETWORK_STACK" >
      <seinfo value="network_stack" />
    </signer>

也就是所有拥有PLATFORM signatureapp拥有platform这个seinfoseapp_context可以如下配置

user=radio seinfo=platform domain=radio type=radio_data_file

所有拥有platform签名并且配置具体contextapp将遵循其seinfo platform规则

untrusted_app

配置seinfo配置seapp_contextapp默认untrusted_app各级seapp_context也有untrusted_app权限配置

查看当前Context

要看进程的Context,使用ps -Z

emu64xa:/ $ ps -AZ | grep google
u:r:hal_camera_default:s0      system         362     1   10891800   3272 0                   0 S android.hardware.camera.provider@2.7-service-google
u:r:permissioncontroller_app:s0:c179,c256,c512,c768 u0_a179 976 354 13918328 83660 0          0 S com.google.android.permissioncontroller
u:r:bluetooth:s0               bluetooth     1033   354   14050488  73628 0                   0 S com.google.android.bluetooth
u:r:priv_app:s0:c512,c768      u0_a167       1090   354   14016372 119796 0                   0 S com.google.android.apps.nexuslauncher
u:r:priv_app:s0:c512,c768      u0_a170       1157   354   13873728  68724 0                   0 S com.google.android.ext.services
u:r:untrusted_app_32:s0:c142,c256,c512,c768 u0_a142 1393 354 14070168 104520 0                0 S com.google.android.inputmethod.latin

查看文件的Context,使用

emu64xa:/ $ ls -Z
u:object_r:cgroup:s0                acct         u:object_r:tmpfs:s0                 debug_ramdisk    u:object_r:vendor_file:s0           odm                     u:object_r:sysfs:s0                 sys
u:object_r:apex_mnt_dir:s0          apex         u:object_r:device:s0                dev              u:object_r:vendor_file:s0           odm_dlkm                u:object_r:system_file:s0           system
u:object_r:rootfs:s0                bin          u:object_r:rootfs:s0                etc              u:object_r:oemfs:s0                 oem                     ?                                  
...

3. SeLinux的配置

参见https://android.googlesource.com/platform/system/sepolicy/

3.1 SeLinux文件体系

之前提到Android架构中大致包含AOSP厂商Vendor部分Android 8以上系统AOSP厂商供应商部分是独立配置方便OTA更新

这种架构SeLinux的Policy文件体系包含以下目录

system/sepolicy/public部分AOSP公开vender使用作为基础api比如声明domainattribute就在下面platform/system/sepolicy/public/attributes这部分Policy需要兼容处理因为Vender引用这里policy如果OTA单独升级AOSP需要向后兼容处理。详见https://source.android.com/docs/security/features/selinux/compatibility

system/sepolicy/privateAOSP内部使用system image内部policyvender不应该感知到这部分policy

system/sepolicy/vendorvender部分policy

device/manufacturer/device-name/sepolicy特定设备policy比如三星设备device/samsung/tuna/sepolicy

BoardConfig.mk makefile

AOSPSeLinux Policy文件一般不需要更改少量更改厂商主要修改定制Policy/device/manufacturer/device-name//device/manufacturer/device-name/BoardConfig.mk用于指定Policy文件具体路径通过BOARD_VENDOR_SEPOLICY_DIRS比如

device/samsung/tuna/BoardConfig.mk

BOARD_VENDOR_SEPOLICY_DIRS += device/samsung/tuna/sepolicy
system_ext  product分区

Android 11及以上系统system_ext  product分区独立单独policy并且区分publicprivatepublic部分同样vender引用system_extproduct版本兼容处理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分区

3.2 Policy文件

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系统propertiestype关联起来

service_contexts用于service进程type关联

seapp_contexts用于apptype关联

mac_permissions.xml根据appsignature包名app分配一个seinfoseinfo用于app没有明确关联一个type归属一个默认type

keystore2_key_contexts Keystore 2.0 namespaces分配一个label

Attribute

用来声明Attribute

security_classes

用来声明Class

3.3 TE(Type Enforcement)文件

所有的规则配置,或称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

type dhcp, domain;
permissive dhcp;
type dhcp_exec, exec_type, file_type;
type dhcp_data_file, file_type, data_file_type;

init_daemon_domain(dhcp)
net_domain(dhcp)

allow dhcp self:capability { setgid setuid net_admin net_raw net_bind_service
};
allow dhcp self:packet_socket create_socket_perms;
allow dhcp self:netlink_route_socket { create_socket_perms nlmsg_write };
allow dhcp shell_exec:file rx_file_perms;
allow dhcp system_file:file rx_file_perms;
# For /proc/sys/net/ipv4/conf/*/promote_secondaries
allow dhcp proc_net:file write;
allow dhcp system_prop:property_service set ;
unix_socket_connect(dhcp, property, init)

type_transition dhcp system_data_file:{ dir file } dhcp_data_file;
allow dhcp dhcp_data_file:dir create_dir_perms;
allow dhcp dhcp_data_file:file create_file_perms;

allow dhcp netd:fd use;
allow dhcp netd:fifo_file rw_file_perms;
allow dhcp netd:{ dgram_socket_class_set unix_stream_socket } { read write };
allow dhcp netd:{ netlink_kobject_uevent_socket netlink_route_socket
netlink_nflog_socket } { read write };

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}; -- 这个就是具体规则描述dhcpsource selftargetcapability一个Class可以操作这句允许dhcp这个进程自己setgid操作self关键用来表示source可以自己什么操作

除了上面例子出现关键字有时我们看到typeattribute这个关键字代表之前已经声明Type关联一个之前声明Attribute

更多语法TypeStatements - SELinux Wiki

​​​​​​​4. SeLinux的编译

4.1 编译SeLinux Policy

Android 8以上编译过程/system  /vendorpolicy合并这个逻辑体现/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)

编译过程

  1. Policy转化成CIL( Common Intermediate Language )文件包括
    1. system + system_ext + productpublic部分policy
    2. 合并private + public policy
    3. 合并public + vendor and BOARD_SEPOLICY_DIRS policy
  2. public部分policy进行整理成对应版本policy作为vendor policy一部分然后publicpolicy合并public + vendor and BOARD_SEPOLICY_DIRS policy告知其哪部分可以连接到platformattribute
  3. 创建一个mapping文件连接platformvendor部分
  4. 合并所有policy文件整合mappingplatformvenderpolicy最终输出一个二进制的名为policy文件一般这个文件的位置在/sys/fs/sepolicy/policy。这个policy最终会被kernel加载。这个工作init进程初始化进行也就是运行时进行

4.2 预编译

SeLinux正式开启之前init进程收集所有cil文件将其编译policy这个过程需要花费1-2时间为了更快完成此过程会将CIL编一阶段预编译放在/vendor/etc/selinux/precompiled_sepolicy/odm/etc/selinux/precompiled_sepolicy并且附有sha256 摘要运行时检查sha256是否变化如果没有变化直接使用预编译文件

相关推荐

  1. AndroidSELinux详解

    2024-03-23 12:16:01       9 阅读
  2. <span style='color:red;'>selinux</span>

    selinux

    2024-03-23 12:16:01      21 阅读
  3. <span style='color:red;'>SELinux</span>

    SELinux

    2024-03-23 12:16:01      19 阅读
  4. Android Selinux详解[一]---整体介绍

    2024-03-23 12:16:01       20 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-23 12:16:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-23 12:16:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-23 12:16:01       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-23 12:16:01       20 阅读

热门阅读

  1. Day 29 回溯05

    2024-03-23 12:16:01       18 阅读
  2. 探索MySQL中的SQL_MODE数据模式

    2024-03-23 12:16:01       18 阅读
  3. vue中如何用一个数组减去另一个数组

    2024-03-23 12:16:01       17 阅读
  4. node和npm yarn包管理工具

    2024-03-23 12:16:01       21 阅读
  5. npm 常用命令详解

    2024-03-23 12:16:01       21 阅读
  6. SAP-FI配置与业务解析之代发(客户)销售作业

    2024-03-23 12:16:01       19 阅读
  7. 将uint8_t数组转成uint32_t

    2024-03-23 12:16:01       23 阅读
  8. C++继承

    2024-03-23 12:16:01       16 阅读
  9. html5&css&js代码 038 列表

    2024-03-23 12:16:01       20 阅读