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了

相关推荐
一只特立独行的Yang2 小时前
Android中的系统级共享库
android
两个人的幸福online2 小时前
php开发者 需要 协程吗
android·开发语言·php
修炼者4 小时前
WindowManager(WMS)构建全局悬浮窗
android
xiaoshiquan12064 小时前
Android Studio里,SDK Manager显示不全问题
android·ide·android studio
Lstone73645 小时前
Bitmap深入分析(一)
android
一起搞IT吧5 小时前
Android功耗系列专题理论之十四:Sensor功耗问题分析方法
android·c++·智能手机·性能优化
ByNotD0g6 小时前
Doris 学习笔记
android·笔记·学习
修炼者6 小时前
【Android进阶】 RenderEffect的底层实现
android