Selinux 及在Android 的使用详解

版本基于:Andorid16

1. 前言

SELinux ( Security-Enhanced Linux **)**是由美国NSA(国安局)和一些公司(RedHat、Tresys)设计的一个针对Linux的安全加强系统。

NSA 最初设计的安全模型叫 FLASK ,全称为 Flux Advanced Security Kernel (由Uta大学和美国国防部开发,后来由NSA将其开源),当时这套模型针对 DTOS系统。后来,NSA觉得Linux更具发展和普及前景,所以就在Linux系统上重新实现了FLASK,称之为 SELinux。

Linux Kernel中,SELinux通过Linux Security Modules实现。在2.6之前,SElinux通过Patch方式发布。从2.6开始,SELinux正式入驻内核,成为标配。

由于Linux有多种发行版本,所以各家的SELinux表现形式也略有区别。具体到Android平台,Google对其进行了一定得修改,从而得到SEAndroid。

本文将先对 SELinux 相关知识进行介绍,然后看看 Android 是如何实现SELinux的。

2. 基础知识

2.1 访问控制 (DAC & MAC)

Linux内核资源访问控制分为 DAC (Discretionary Access Control ,自主访问控制)和 MAC (Mandatory Access Control,强制访问控制)两类。任何访问控制都要实现真实域向安全域的映射。

  • DAC

SELinux 出现之前,Linux 上的安全模型为 DAC,即自主访问控制。

UGO属于典型的DAC级别访问控制,基于"用户-用户组-其他"的"读、写、执行"的权限检查,进程理论上所拥有的权限与执行它的用户的权限相同,该管理过于宽公,如果获得**root权限**,可以在Linux系统内做任何事情。

  • MAC

SELinux 属于MAC级别访问控制,基于SELinux安全上下文(简称SELinux标签)和安全策略的安全机制,用于补充细化DAC级别的访问控制。访问系统资源时,会先进行DAC级别的访问检查,DAC级别的访问检查通过/后,继续进行MAC级别的访问检查,如果MAC级别的访问检查通过,才允许进行资源访问操作。

复制代码
allow netd proc:file write

这是一条 SELinux 的安全策略,当进程 netd 想要对带有**proc 标签** 的文件进行**写操作**时,首先不仅需要该文件的UGO 拥有 w 权限,更要求开发者指定上面这一安全策略。

2.2 SELinux 模式

SELinux 按照默认拒绝的原则运行:任何未经明确允许的行为都会被拒绝。SELinux 可按两种全局模式运行:

  • Permissive-宽容模式

权限拒绝事件会被记录下来,但不会被强制执行。代表SELinux访问检查运作中,如果访问操作违反了SELinux规则,不会直接限制资源访问行为,只会打印警告信息**(avc:denied)**到日志中。这种模式可用于调试SELinux访问所需权限,查看违反SELinux访问检查的原因。

  • Enforcing强制模式

权限拒绝事件会被记录下来强制执行。代表SELinux访问检查运作中,如果访问操作违反了SELinux规则,那么访问行为将被阻止,并打印警告信息(avc:denied)到日志中。

2.3 SELinux 模式的查看和切换

  • 命令行方式查看 (getenforce)

会直接输出 Permissive 和 Enforcing

  • avc log 查看

在avc log 结尾会有permissive=1/0的标识:

permissive=1,说明是Permissive模式

permissive=0,说明是Enforcing模式

  • 命令行方式切换 (setenforce)
bash 复制代码
adb root ##需要有root权限
adb shell setenforce 0    # 0->permissive,1->enforcing
  • 修改 bootconfig 参数切换

该方式适用于userdebug版本,系统重启修改仍生效

类似修改:

bash 复制代码
#Disable SELinux
ifeq ($(TARGET_BUILD_VARIANT),userdebug)
BOARD_BOOTCONFIG += androidboot.selinux=permissive
endif
  • 修改init 代码切换

该方式适用于user和userdebug版本,系统重启修改仍生效。

修改**system/core/init/selinux.cpp** 文件里的**IsEnforcing()**函数,将该函数直接返回 false值:

cpp 复制代码
Bool IsEnforcing() {
    return false;
    ......
}

user版本还需在kernel config中配置:

bash 复制代码
CONFIG_SECURITY_SELINUX_DEVELOP=y

修改完成后重新编译打包,下载软件包到手机即可。

3. 基础语法

3.1 SContext (Security Context)

SELinux中,每种东西都会被赋予一个**安全属性** ,官方说法叫**Security Context**。Security Context(以后用SContext表示)是一个字符串。

例如:

bash 复制代码
$ ps -AZ |grep system_server
u:r:system_server:s0           system        1793  1170   26001560 400624 0                   0 S system_server

$ ps -AZ | grep surface
u:r:surfaceflinger:s0          system        1293     1   12021912  50940 0                   0 S surfaceflinger

$ ls -lZ
-rwxr-xr-x 1 root shell u:object_r:system_file:s0                     320 2009-01-01 08:00 am
-rwxr-xr-x 1 root shell u:object_r:tradeinmode_exec:s0                150 2009-01-01 08:00 tradeinmode

$ getprop -Z
[gsm.sim1.type]: [u:object_r:radio_prop:s0]
[external_storage.casefold.enabled]: [u:object_r:storage_config_prop:s0]

例子中分三部分:进程SContext文件SContext、属性SContext;

这些 SContext 在MAC 的策略中,会被分**scontext** 、tcontext 两种type,即源头上下文、目标上下文,在TE 中的安全策略中scontext 为**发起访问的一方** ,tcontext 为**被访问的一方**。

SContext 格式如下:

bash 复制代码
user:role:type:mls_level
  • user: 指用户,该用户非DAC中的用户,而是在MAC中定义的。目前android只定义了 **"u"**这一个user。
  • role: 表示角色,android目前只定义了一个role即 "r" 。一个user可以属于多个role,每个role有不同的权限。Android中,主体(subject)的role固定为 "r" ,对象(object)的role固定为**"object_r"**。
  • **type:**Source 或者Target 的类型;MAC 的管理都是基于 type 来限制的,即所谓的 Type Enforcement Access Control(简称TEAC,一般用 TE 表示)
  • **mls_level:**MLS级别,即多层安全机制,SELinux为了满足军用和教育行业而设计的机制,目前android基本上都配置的是s0。

说明:

  • **scontext:**主体,发起访问的一方,linux通常以进程为单位,比如 init、StorageManagerService等等。
  • **tcontext:**客体,访问对象,被访问的一方,linux 通常以文件为单位,还包括属性、端口等。

3.2 TE

MAC 基本管理单位是TEACType Enforcement Accesc Control ),然后是高一级别的**Role Based Accesc Control**。RBAC是基于TE的,而TE也是SELinux中最主要的部分。

TE 的文件格式包括:

  • file.te
  • file_contexts
  • genfs_contexts
  • property.te
  • property_contexts
  • service.te
  • services_contexts
  • "scontext".te

当然,contexts 的文件还有很多,例如 hwservice_contexts、port_contexts、seapp_contexts,更多文件类型可以查看:system/sepolicy/private

3.2.1 file.te

制定新增文件SContext 的类型,例如:

bash 复制代码
type    mylog_data_file, file_type, data_file_type;
type    mylog_device, file_type, dev_type;

分两部分:

  • 固定指令;
  • 文件SContex 标签,以及其类型,之间是逗号分隔;

3.2.2 file_contexts

配置文件,用以配置文件的SContext 类型,例如:

bash 复制代码
/data/vendor/shiftlog(/.*)?    u:object_r:mylog_data_file:s0
/dev/mylog_kernel              u:object_r:tranlog_device:s0

分两部分:

  • 前面是文件的完成路径,支持正则表达式
  • 后面是SContext,用以指定这些文件的安全上下文,这些类型可能是新增的,也可能是系统已有的;

当然,文件通常会被当做被访问的一方,也就是 tcontext

更多使用规则可以查看:system/sepolicy/private/file_contexts

**首次加载:**在系统构建阶段(编译固件时)应用标签

运行时加载: 系统启动时,初始化完成后,不会自动加载,必须手动执行 restorecon 命令才会生效,这也是为何 rc 文件中一般都会添加 restorecon 的原因。

3.2.3 genfs_contexts

sysfs、debugfs、tracefs等和procfs一样,都属于虚拟文件系统,给这类文件配置权限需要注意,既可以使用 file_contexts,也可以使用 genfs_contexts。

bash 复制代码
genfscon proc /mm_collect              u:object_r:my_mm_collect:s0
genfscon proc /pages_prefiltered       u:object_r:my_mm_collect:s0

genfscon sysfs /class/leds/vibrator/gain u:object_r:sysfs_vibrator:s0

genfscon usbfs / system_u:object_r:usbfs_t:s0

分三部分:

  • **genfscon:**固定指令;
  • **fs type:**文件系统类型,包括:proc、sysfs、debugfs、tracefs、selinuxfs等等;
  • **SContxt:**文件所属的安全上下文;

更多使用规则可以查看:system/sepolicy/private/genfs_contexts

Android原生不会使用 file_contexts 给虚拟文件配置上下文,建议使用 genfs_contexts。

genfs_contexts 配置的安全上下文比 file_contexts 加载得更早,如果文件/目录的创建在启动早期使用,请务必使用genfs_contexts;

genfs_contexts无法使用正则表达式,file_contexts支持正则表达式,如果文件/目录的创建在启动晚期使用,又有使用正则表达式的需求,可以使用file_contexts;

**加载时机:**在内核挂载虚拟文件系统时自动加载

触发条件: 系统启动过程中挂载 /proc, /sys 等虚拟文件系统时;动态设备创建时(如热插拔);

**自动生效:**无需任何命令;内核直接读取策略并应用标签;

3.2.4 property.te

同file.te,这里是制定新增属性SContext 的类型,例如:

bash 复制代码
system_vendor_config_prop(wifi_config_prop)

type tr_trace_prop, property_type, system_property_type;

typeattribute vendor_mm_state_prop       vendor_property_type;

3.2.5 property_contexts

配置文件,用以为prop 指定其 SContext 类型,例如:

bash 复制代码
vendor.media.wfd.debug.       u:object_r:system_video_framework_debug_log_prop:s0
init.svc.odm.                 u:object_r:vendor_default_prop:s0

分两部分:

  • 前面是prop 的名称,注意最后的点号 ".",表示以这个prop 为前缀的属性;
  • 后面是property 的SContext,用以指定该prop 的安全上下文,这些类型可能是新增的,也可能是系统已有的;

更多的使用规则可以查看:system/sepolicy/private/property_contexts

由于系统属性资源可以划分为 System 域和 vendor 域,部分属性是可以被跨域使用的,部分属性不可以。因此在定义系统属性type时需要明确它的 attribute,是否私有,是否可写,可使用如下辅助宏进行配置:

宏说明
system_internal_prop 声明type:仅在 /system 中使用的属性
system_restricted_prop 声明type:/system 外无法写入的属性
system_public_prop 声明type:/system 没有限制的属性
system_vendor_config_prop 声明type:只能被vendor_init写入的属性
vendor_internal_prop 声明type:仅在 /vendor 中使用的属性
vendor_restricted_prop 声明type:/vendor 外无法写入的属性
vendor_public_prop 声明type:/vendor 没有限制的属性

3.2.6 service.te

同file.te,这里是制定新增服务SContext 的类型,例如:

bash 复制代码
type logcat_service,       system_server_service, service_manager_type;

type stats_service,        service_manager_type;

type app_binding_service,  system_server_service, service_manager_type;

通常这里定义的都是 service_manager_type,用以在Android Binder 中注册的binder service。

3.2.7 service_contexts

配置文件,为service manager 类型的的service 指定SContext 类型,例如:

bash 复制代码
logcat                                    u:object_r:logcat_service:s0
powerstats                                u:object_r:powerstats_service:s0
app_binding                               u:object_r:app_binding_service:s0

3.2.8 "scontext".te

通常像domain、service 等SContext 类型都会作为访问者,当其访问被访问者 (例如file、prop) tcontext 时都需要取得 tcontext 制定的 SContext 类型的权限,从而形成 scontext te 策略。

例如 lmkd.te:

bash 复制代码
typeattribute lmkd coredomain;
init_daemon_domain(lmkd)

allow lmkd fs_bpf:file read;
allow lmkd cgroup_v2:dir { remove_name rmdir };
allow lmkd cgroup_v2:file r_file_perms;

lmkd 被定义为一个 coredomain 的SContext,作为domain 想要访问 cgroup_v2 标签的文件或目录时必须指定 MAC 的策略,取得 删除、读取的权限。

3.3 SELinux 宏的使用

Android在SELinux定义了很多宏给开发使用,以提高权限配置的效率,同时增加可读性。在权限配置,type定义时都可以使用对应宏来代替冗杂的语句。

宏定义主要在代码仓库的**system/sepolicy/public**下,可以在如下文件中查看具体使用。

例如,global_macros、te_macros、ioctl_macros 等。

3.3.1 global_macros 文件

该文件中定义全局的权限集合,例如:r_file_perms、w_file_perms、rw_file_perms、rw_dir_perms 等。

bash 复制代码
define(`x_file_perms', `{ getattr execute execute_no_trans map }')
define(`r_file_perms', `{ getattr open read ioctl lock map watch watch_reads }')
define(`w_file_perms', `{ open append write lock map }')
define(`rx_file_perms', `{ r_file_perms x_file_perms }')
define(`ra_file_perms', `{ r_file_perms append }')
define(`rw_file_perms', `{ r_file_perms w_file_perms }')
define(`rwx_file_perms', `{ rw_file_perms x_file_perms }')
define(`create_file_perms', `{ create rename setattr unlink rw_file_perms }')

define(`r_dir_perms', `{ open getattr read search ioctl lock watch watch_reads }')
define(`w_dir_perms', `{ open search write add_name remove_name lock }')
define(`ra_dir_perms', `{ r_dir_perms add_name write }')
define(`rw_dir_perms', `{ r_dir_perms w_dir_perms }')
define(`create_dir_perms', `{ create reparent rename rmdir setattr rw_dir_perms }'

3.3.2 te_macros 文件

bash 复制代码
define(`r_dir_file', `
allow $1 $2:dir r_dir_perms;
allow $1 $2:{ file lnk_file } r_file_perms;
')

define(`set_prop', `
unix_socket_connect($1, property, init)
allow $1 $2:property_service set;
get_prop($1, $2)
')

define(`get_prop', `
allow $1 $2:file { getattr open read map };
')

3.4 SELinux SContext 的调试

3.4.1 chcon (修改安全上下文)

SELinux安全上下文的修改可以使用 chcon 命令来实现,用于文件(可以批量),格式如下:

bash 复制代码
$ chcon [选项] files

选项:

  • -R:递归,当前目录和目录下所有的子文件同时恢复
  • -h:更改符号链接,而不是它们指向的内容
  • -v:为处理的所有文件显示诊断信息

3.4.1 restorecon (恢复安全上下文)

SELinux安全上下文的恢复可以使用 restorecon 命令来实现,用于文件(可以批量),格式如下:

bash 复制代码
$ restorecon [选项] [SContext] files

选项:

  • -R:递归,当前目录和目录下所有的子文件同时恢复
  • -F:强制恢复
  • -n:不做任何修改,与 -v 一起使用查看加载文件的上下文有哪些

4. SELinux 策略分布

**Public公共策略:**谷歌原生策略中的public部分以及开发人员自行添加策略中的public部分,vendor域和system域共用。

**Private私有策略:**谷歌原生策略中的private部分以及开发人员自行添加策略中的private部分,仅system域可用。

**Vendor供应商通用组件策略:**谷歌原生策略中的vendor部分以及开发人员自行添加策略中的vendor,仅vendor域可用。

|------------|---------------------------|-----------------------------------|
| Google原生策略 | system/sepolicy | Google原生策略,不要在此路径下进行修改 |
| Google原生策略 | system/sepolicy/private | Google原生system分区 private sepolicy |
| Google原生策略 | system/sepolicy/public | Google原生system分区 public sepolicy |
| Google原生策略 | system/sepolicy/vendor | Google原生vendor分区sepolicy |
| Google原生策略 | system/sepolicy/prebuilts | 版本兼容sepolicy |

vendor 供应商也可以按照 Google 这种分布方式分private、public、vendor 管理。

由于SELinux策略分为Vendor域和System域,在标签定义和策略声明时须满足如下要求:

  • 定义的标签既在system域使用,又在vendor域使用:该标签应当定义在system_ext/public/路径下
  • 定义的标签只在system域使用:该标签应当定义在system_ext/private/路径下
  • 定义的标签只在vendor域使用:该标签应当定义在vendor/或odm/路径下
  • system_ext/public/路径下定义的标签对system_ext/private/、system_ext/public/、vendor/、odm/下的allow均可见
  • system_ext/private/路径下定义的标签只对system_ext/private/路径下的allow可见
  • vendor/路径下定义的标签只对vendor/路径下的allow可见

因此:

  • SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS 指定的权限跟随system编译,可仅配置在system侧
  • BOARD_VENDOR_SEPOLICY_DIRS 指定的权限跟随vendor编译,可仅配置在vendor侧
  • SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS 指定的权限需要跟随system编译,但其类型/属性会被vendor的编译所依赖,大多数情况下两侧都需要添加

5. 使用举例

5.1 init.rc 中添加内核节点的写操作

bash 复制代码
on property:sys.boot_completed=1
    write /proc/pages_pre/inodes_protect_enabled 1

需要在initrc 中对节点写操作,需要满足下面几点:

  • file.te 中指定一个SContext 类型名;(当然可以使用已有的)
  • file_contexts 中将该节点对应的 SContext 添加上;(建议使用genfscon 添加到genfs_contexts)
  • init.te 中增加写权限申请;
bash 复制代码
allow init proc_my_temp:file rw_file_perms;

目前该 init.te 添加到 vendor 目录也是可以实现的。

遇到问题:

平台 /vendor/etc/selinux/vendor_sepolicy.cil 文件中已经在看该权限成功添加了,但user 版本会打印 avc 提示没有write 权限:

bash 复制代码
03-12 17:57:48.872     1     1 W init    : type=1400 audit(0.0:340): avc:  denied  { write } for  name="inodes_protect_enabled" dev="proc" ino=4026534030 scontext=u:r:init:s0 tcontext=u:object_r:proc_my_temp:s0 tclass=file permissive=0

非常奇怪的是user_root 版本又是可行的。

目前解决办法:

修改除了编译system、system_ext、vext以外,还需要编译 odm分区,可以解决该问题。

只能怀疑是否和 initrc执行的操作时 主体的type有变化,是不是切成root了

相关推荐
sunwenjian88614 小时前
MySQL加减间隔时间函数DATE_ADD和DATE_SUB的详解
android·数据库·mysql
ictI CABL14 小时前
MySQL Workbench菜单汉化为中文
android·数据库·mysql
放学以后Nicetry16 小时前
Android SELinux 指南:从基本概念到实战修复
android
CCIE-Yasuo16 小时前
《永恒战士2-无双战神》无限金币版(提供apk下载)安卓Android逆向记录学习-Deepseek-AI辅助
android·java·学习·游戏
jzlhll12317 小时前
kotlin flow去重distinctUntilChanged vs distinctUntilChangedBy
android·开发语言·kotlin
渡我白衣17 小时前
【MySQL基础】(3):MySQL库与表的操作
android·数据库·人工智能·深度学习·神经网络·mysql·adb
huwuhang18 小时前
植物大战僵尸版本所有版本合集下载含杂交版 融合版 火影版 二战版 无双版 抽卡版 β版等等
android·游戏·电脑·游戏机
尤老师FPGA1 天前
petalinux修改设备树添加vdma生成linux系统
android·linux·运维
月山知了1 天前
linux kernel component子系统:基于rk3588 Android 14 kernel-6.1 display-subsystem代码分析
android·linux·运维
leo_messi941 天前
多线程(五) -- 并发工具(二) -- J.U.C并发包(八) -- CompletableFuture组合式异步编程
android·java·c语言