作为 Android 安全模型的一部分,Android 使用安全增强型 Linux (SELinux) 对所有进程强制执行强制访问控制 (MAC),甚至包括以 Root/超级用户权限运行的进程(Linux 功能)。借助 SELinux,Android 可以更好地保护和限制系统服务、控制对应用数据和系统日志的访问、降低恶意软件的影响,并保护用户免遭移动设备上的代码可能存在的缺陷的影响。
SELinux 按照默认拒绝的原则运行:任何未经明确允许的行为都会被拒绝。SELinux 可按两种全局模式运行:
Android 中包含 SELinux(处于强制模式)和默认适用于整个 AOSP 的相应安全政策。在强制模式下,非法操作会被阻止,并且尝试进行的所有违规行为都会被内核记录到 dmesg 和 logcat。开发时,您应该先利用这些错误信息对软件和 SELinux 政策进行优化,再对它们进行强制执行。
此外,SELinux 还支持基于域的宽容模式。在这种模式下,可将特定域(进程)设为宽容模式,同时使系统的其余部分处于全局强制模式。简单来说,域是安全政策中用于标识一个进程或一组进程的标签,安全政策会以相同的方式处理所有具有相同域标签的进程。借助基于域的宽容模式,可逐步将 SELinux 应用于系统中越来越多的部分,还可以为新服务制定政策(同时确保系统的其余部分处于强制模式)。
Android 安全模型部分基于应用沙盒的概念。每个应用都在自己的沙盒内运行。在 Android 4.3 之前的版本中,这些沙盒是通过为每个应用创建独一无二的 Linux UID(在应用安装时创建)来定义的。Android 4.3 及更高版本使用 SELinux 进一步定义 Android 应用沙盒的边界。
基于 Android 4.3(宽容模式)和 Android 4.4(部分强制模式),在 Android 5.0 及更高版本中,已全面强制执行 SELinux。通过此项变更,Android 已从对有限的一组关键域(installd、netd、vold 和 zygote)强制执行 SELinux 转为对所有域(超过 60 个)强制执行 SELinux。具体而言:
Android 6.0 通过降低我们政策的宽容度强化了系统安全,从而实现更好的用户隔离和 IOCTL 过滤、降低可从设备/系统之外访问的服务面临的威胁、进一步强化 SELinux 域,以及高度限制对 /proc 的访问。
Android 7.0 更新了 SELinux 配置,以进一步锁定应用沙盒并缩小受攻击面。此版本还将单片式 mediaserver 堆栈拆分为较小的进程,以缩小其权限范围。
Android 8.0 更新了 SELinux 以便与 Treble 配合使用,后者可将较低级别的供应商代码与 Android 系统框架分离开来。此版本更新了 SELinux 政策以允许设备制造商和 SOC 供应商更新自己的政策部分、构建自己的映像(vendor.img、boot.img 等),然后更新这些映像而不受平台影响,反之亦然。
虽然可以在设备上运行更高/更新版本的平台(框架),但反之并不成立;供应商映像 (vendor.img/odm.img) 的版本不能高于平台 (system.img) 的版本。因此,较新版平台可能会带来 SELinux 兼容性问题,因为平台 SELinux 政策的版本要比该政策的供应商 SELinux 部分更新。Android 8.0 模型提供了一种保持兼容性的方法,以免进行不必要的同时 OTA。
为什么需要关闭:
由于应用层访问设备节点的时候,因为selinux权限问题而访问不了,所以就先关闭selinux。
查看当前样机的SELinux状态值:
adb shell getenforce
Enforcing:表示SELinux处于开启状态;
Permissive:表示SELinux处于关闭状态。
临时关闭SELinux操作比较简单,但是前提是样机能被root,setenforce 命令修改的是 /sys/fs/selinux/enforce
节点的值,是 kernel 意义上的修改 selinux 的策略。
adb root
adb shell setenforce 0 // 开SELinux:设置成模式permissive
adb shell setenforce 1 // 关SELinux:设置成模式enforce
XXX:/ # getenforce // 获取当前SELinux状态
Enforcing
XXX:/ # setenforce 0 // 临时关闭SELinux状态
XXX:/ # getenforce // 获取SELinux状态
Permissive
XXX:/ # setenforce 1 // 永久开启SELinux状态
XXX:/ # getenforce
Enforcing
XXX:/ #
注意点:
userdebug
版本,系统重启修改会失效root
模式下运行,且必须是终端没有对setenforce进行限制,否则会报如下的错误:setenforce: Couldn't set enforcing status to '0': Permission denied //非root情况
init进程是Android内核启动的第一个用户级进程,其中的SELinux也是在init进程中启动的,代码位置在system/core/init/init.cpp
中:
main()方法中调用SelinuxInitialize();
方法对selinux进行初始化,该方法的实现位于system/core/init/selinux.cpp
中:
int main(int argc, char** argv) {
...
SelinuxInitialize();
...
}
init会通过security_getenforce()和selinux_is_enforcing()的值是否一致来判断是否开启SELinux,当不一致的时候会设置security_setenforce的值为is_enforcing。
void SelinuxInitialize() {
Timer t;
LOG(INFO) << "Loading SELinux policy";
if (!LoadPolicy()) {
LOG(FATAL) << "Unable to load SELinux policy";
}
bool kernel_enforcing = (security_getenforce() == 1);
bool is_enforcing = IsEnforcing();
if (kernel_enforcing != is_enforcing) {
if (security_setenforce(is_enforcing)) {
PLOG(FATAL) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
}
}
if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result) {
LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();
}
// init's first stage can't set properties, so pass the time to the second stage.
setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
}
在/selinux.cpp
文件中IsEnforcing()
方法中直接返回false就是关闭selinux权限了。
bool IsEnforcing() {
//if (ALLOW_PERMISSIVE_SELINUX) {
// return StatusFromCmdline() == SELINUX_ENFORCING;
//}
//return true;
return false;
}
参考:
https://source.android.google.cn/security/selinux?hl=zh-cn
https://blog.csdn.net/wq892373445/article/details/120823080
https://blog.csdn.net/tkwxty/article/details/103938287?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-2.pc_relevant_paycolumn_v3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-2.pc_relevant_paycolumn_v3&utm_relevant_index=5
https://www.jianshu.com/p/f1a6f248c607
https://blog.csdn.net/u012932409/article/details/114632885