开始我们先来跟一下 selinux 的初始化过程
system\core\init\main.cpp
int main(int argc, char** argv) { #if __has_feature(address_sanitizer) __asan_set_error_report_callback(AsanReportCallback); #endif if (!strcmp(basename(argv[0]), "ueventd")) { return ueventd_main(argc, argv); } if (argc > 1) { if (!strcmp(argv[1], "subcontext")) { android::base::InitLogging(argv, &android::base::KernelLogger); const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap(); return SubcontextMain(argc, argv, &function_map); } if (!strcmp(argv[1], "selinux_setup")) { return SetupSelinux(argv); } if (!strcmp(argv[1], "second_stage")) { return SecondStageMain(argc, argv); } } return FirstStageMain(argc, argv); }
调用 SetupSelinux(argv),进入 selinux.cpp 中
system\core\init\selinux.cpp
int SetupSelinux(char** argv) { SetStdioToDevNull(argv); ... // Set up SELinux, loading the SELinux policy. #ifdef MTK_LOG if (GetMTKLOGDISABLERATELIMIT()) SelinuxSetupKernelLogging_split(); else SelinuxSetupKernelLogging(); #else SelinuxSetupKernelLogging(); #endif SelinuxInitialize(); ....
继续调用 SelinuxInitialize()
void SelinuxInitialize() { 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(" << (is_enforcing ? "true" : "false") << ") failed"; } } if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result.ok()) { LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error(); } } bool IsEnforcing() { if (ALLOW_PERMISSIVE_SELINUX) { return StatusFromCmdline() == SELINUX_ENFORCING; } return true; } EnforcingStatus StatusFromCmdline() { EnforcingStatus status = SELINUX_ENFORCING; ImportKernelCmdline([&](const std::string& key, const std::string& value) { if (key == "androidboot.selinux" && value == "permissive") { status = SELINUX_PERMISSIVE; } }); return status; }
security_getenforce() 读取 sys/fs/selinux/enforce 文件值
获取 kernel 层 selinux 默认模式,当定义了 ALLOW_PERMISSIVE_SELINUX 时,会检测是否在
cmdline 写入了 androidboot.selinux=permissive(lk 阶段写入)
kernel_enforcing 和 is_enforcing 不相等时将 is_enforcing 写入 sys/fs/selinux/enforce
在 11 User 版本中,security_setenforce(is_enforcing) 执行失败,开机直接自动进入 fastboot 模式
提示 security_setenforce(false) failed: Invalid argument
[ 4.475989] <2>.(2)[1:init]init: security_setenforce(false) failed: Invalid argument
将下面这句注释能正常开机启动,但无法成功关闭 selinux
PLOG(FATAL) << “security_setenforce(” << (is_enforcing ? “true” : “false”) << “) failed”;
11 eng 版本,默认烧写后 enforce 即为 Permissive,在之前的低版本 eng 中,默认 enforce 为 Enforcing
来看下 security_setenforce() 具体实现, selinux_log() 打印语句是我增加的,需要引入上面的三个头文件,
external\selinux\libselinux\src\setenforce.c
#include "callbacks.h" #include <selinux/android.h> #include <log/log.h> int security_setenforce(int value) { int fd, ret; char path[PATH_MAX]; char buf[20]; selinux_log(SELINUX_INFO, "SELinux: security_setenforce value %d\n", value); selinux_log(SELINUX_INFO, "SELinux: security_setenforce selinux_mnt %s\n", selinux_mnt); if (!selinux_mnt) { errno = ENOENT; return -1; } snprintf(path, sizeof path, "%s/enforce", selinux_mnt); selinux_log(SELINUX_INFO, "SELinux: security_setenforce path %s\n", path); fd = open(path, O_RDWR | O_CLOEXEC); selinux_log(SELINUX_INFO, "SELinux: security_setenforce fd %d\n", fd); if (fd < 0) return -1; snprintf(buf, sizeof buf, "%d", value); ret = write(fd, buf, strlen(buf)); selinux_log(SELINUX_INFO, "SELinux: security_setenforce ret %d\n", ret); close(fd); if (ret < 0) return -1; return 0; } hidden_def(security_setenforce)
selinux_mnt 的定义在 external\selinux\libselinux\src\policy.h 中
/* Preferred selinux mount location */ #define SELINUXMNT "/sys/fs/selinux" #define OLDSELINUXMNT "/selinux" /* selinuxfs mount point */ extern char *selinux_mnt;
selinux_mnt 赋值在 init.c
external\selinux\libselinux\src\init.c void set_selinuxmnt(const char *mnt) { selinux_mnt = strdup(mnt); } external\selinux\libselinux\src\android\android_platform.c int selinux_android_load_policy_from_fd(int fd, const char *description) { ...... set_selinuxmnt(SELINUXMNT);
所以最终的 path = sys/fs/selinux/enforce
snprintf(path, sizeof path, “%s/enforce”, selinux_mnt);
security_setenforce() 和 security_getenforce() 都是读写 path = sys/fs/selinux/enforce
当使用 adb shell setenforce 0 时,调用对应文件位于,
external\toybox\toys\android\setenforce.c
通过上面加的 log,最终还是调用到 setenforce.c
#define FOR_setenforce #include "toys.h" void setenforce_main(void) { char *new = *toys.optargs; int state, ret; if (!is_selinux_enabled()) error_exit("SELinux is disabled"); else if (!strcmp(new, "1") || !strcasecmp(new, "enforcing")) state = 1; else if (!strcmp(new, "0") || !strcasecmp(new, "permissive")) state = 0; else error_exit("Invalid state: %s", new); ret = security_setenforce(state); if (ret == -1) perror_msg("Couldn't set enforcing status to '%s'", new); } adb shell setenforce 0 SELinux: security_setenforce value 0 SELinux: security_setenforce selinux_mnt /sys/fs/selinux SELinux: security_setenforce path /sys/fs/selinux/enforce SELinux: security_setenforce fd 3 SELinux: security_setenforce ret -1 setenforce: Couldn't set enforcing status to '0': Invalid argument
看到 eng 版本 logcat 中打印了
audit: type=1404 audit(1262304022.108:3): enforcing=0 old_enforcing=1 auid=4294967295 ses=4294967295 enabled=1 old-enabled=1 lsm=selinux res=1
搜索找到 kernel 中 kernel-4.19\security\selinux\selinuxfs.c
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP static ssize_t sel_write_enforce(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; struct selinux_state *state = fsi->state; char *page = NULL; ssize_t length; int old_value, new_value; if (count >= PAGE_SIZE) return -ENOMEM; /* No partial writes. */ if (*ppos != 0) return -EINVAL; page = memdup_user_nul(buf, count); if (IS_ERR(page)) return PTR_ERR(page); length = -EINVAL; if (sscanf(page, "%d", &new_value) != 1) goto out; new_value = !!new_value; old_value = enforcing_enabled(state); if (new_value != old_value) { length = avc_has_perm(&selinux_state, current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__SETENFORCE, NULL); if (length) goto out; audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_STATUS, "enforcing=%d old_enforcing=%d auid=%u ses=%u" " enabled=%d old-enabled=%d lsm=selinux res=1", new_value, old_value, from_kuid(&init_user_ns, audit_get_loginuid(current)), audit_get_sessionid(current), selinux_enabled, selinux_enabled); enforcing_set(state, new_value); if (new_value) avc_ss_reset(state->avc, 0); selnl_notify_setenforce(new_value); selinux_status_update_setenforce(state, new_value); if (!new_value) call_lsm_notifier(LSM_POLICY_CHANGE, NULL); } length = count; out: kfree(page); return length; } #else #define sel_write_enforce NULL #endif
可以看到 CONFIG_SECURITY_SELINUX_DEVELOP 宏决定了是否存在 sel_write_enforce 方法
接下来再来看 kernel 中 selinux 初始化设置
kernel-4.19\security\selinux\hooks.c
static __init int selinux_init(void) { if (!security_module_enable("selinux")) { selinux_enabled = 0; return 0; } if (!selinux_enabled) { pr_info("SELinux: Disabled at boot.\n"); return 0; } pr_info("SELinux: Initializing. %d\n", selinux_enforcing_boot); memset(&selinux_state, 0, sizeof(selinux_state)); enforcing_set(&selinux_state, selinux_enforcing_boot); selinux_state.checkreqprot = selinux_checkreqprot_boot; selinux_ss_init(&selinux_state.ss); selinux_avc_init(&selinux_state.avc); .... if (selinux_enforcing_boot) pr_info("SELinux: Starting in enforcing mode\n"); else pr_info("SELinux: Starting in permissive mode\n"); return 0; }
enforcing_set(&selinux_state, selinux_enforcing_boot);
selinux_enforcing_boot 默认值定义由 CONFIG_SECURITY_SELINUX_DEVELOP 决定
全局搜索到 kernel-4.19\kernel\configs\userdebug.config 中定义了 CONFIG_SECURITY_SELINUX_DEVELOP=y
由此可以解释上面说的 eng 版本默认为 permissive 0,user 版本默认为 enforce 1
但是最终搜索发现 eng 版本编译时并没有引入 userdebug.config,这就奇怪了,看 log 可以肯定的是 eng 版本中必须
定义了 CONFIG_SECURITY_SELINUX_DEVELOP,但为何找不到定义的地方???
device\mediateksample\k62v1_64_bsp\vnd_k62v1_64_bsp.mk
ifeq ($(TARGET_BUILD_VARIANT), eng) KERNEL_DEFCONFIG ?= k62v1_64_bsp_debug_defconfig endif ifeq ($(TARGET_BUILD_VARIANT), user) KERNEL_DEFCONFIG ?= k62v1_64_bsp_defconfig endif ifeq ($(TARGET_BUILD_VARIANT), userdebug) KERNEL_DEFCONFIG ?= k62v1_64_bsp_defconfig userdebug.config endif PRELOADER_TARGET_PRODUCT ?= k62v1_64_bsp LK_PROJECT ?= k62v1_64_bsp TRUSTY_PROJECT ?= k62v1_64_bsp
可以看到有且仅有编译 userdebug 版本时才引入 userdebug.config,所以想要 user 版本中默认关闭 selinux,只需编译
时引入 userdebug.config。这样就定义了 CONFIG_SECURITY_SELINUX_DEVELOP
初始化时 enforcing_set(&selinux_state, selinux_enforcing_boot); 直接写入 0
在 kernel-4.19\security\selinux\include\security.h 中才能成功写入
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP static inline bool enforcing_enabled(struct selinux_state *state) { return state->enforcing; } static inline void enforcing_set(struct selinux_state *state, bool value) { state->enforcing = value; } #else static inline bool enforcing_enabled(struct selinux_state *state) { return true; } static inline void enforcing_set(struct selinux_state *state, bool value) { } #endif
最后当你在 user 版本关闭 selinux 后,烧写后重新开机系统会弹出您的设备内部出现了问题。请联系您的设备制造商了解详情 对话框
具体原因可查看这篇进行屏蔽 https://blog.csdn.net/qq_18059855/article/details/108409157
Linux权限详解(chmod、600、644、666、700、711、755、777、4755、6755、7755)
8.0selinux状态获取过程
深入理解SELinux/SEAndroid
玩转Android10源码开发定制(13)修改安卓源码关闭selinux
CONFIG_SECURITY_SELINUX_DEVELOP flag