开始我们先来跟一下 selinux 的初始化过程
system\core\init\main.cpp
java
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
java
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()
java
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
java
#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 中
java
/* Preferred selinux mount location */
#define SELINUXMNT "/sys/fs/selinux"
#define OLDSELINUXMNT "/selinux"
/* selinuxfs mount point */
extern char *selinux_mnt;
selinux_mnt 赋值在 init.c
java
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
java
#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
java
#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
java
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
java
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 中才能成功写入
java
#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 后,烧写后重新开机系统会弹出您的设备内部出现了问题。请联系您的设备制造商了解详情 对话框
具体原因可查看这篇进行屏蔽 blog.csdn.net/qq_18059855...
Linux权限详解(chmod、600、644、666、700、711、755、777、4755、6755、7755)