本文基于 AOSP android-10.0.0_r41 版别解说,内核版别 android-goldfish-4.14-gchips
1. SetupSelinux 初始化 SeLinux
上一节咱们分析了 init 进程的第一阶段—— FirstStageMain 函数的执行过程,FirstStageMain 函数的最终阶段会执行到 SetupSelinux 函数:
// system/core/init/selinux.cpp
// This function initializes SELinux then execs init to run in the init SELinux context.
int SetupSelinux(char** argv) {
//初始化本阶段内核日志
InitKernelLogging(argv);
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
// 初始化 SELinux,加载 SELinux 战略
// Set up SELinux, loading the SELinux policy.
SelinuxSetupKernelLogging(); // selinux 日志重定向到内核
// LoadPolicy 加载 selinux_policy 战略文件
// security_setenforce() 设置默许 selinux 形式
SelinuxInitialize();
// We're in the kernel domain and want to transition to the init domain. File systems that
// store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
// but other file systems do. In particular, this is needed for ramdisks such as the
// recovery image for A/B devices.
// 再次调用 main 函数,并传入 second_stage 进入第二阶段
// 而且此次发动就已经在 SELinux 上下文中运行
// 加载可执行文件 `/system/bin/init` 的安全上下文
if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
}
// 进入下一步
const char* path = "/system/bin/init";
const char* args[] = {path, "second_stage", nullptr};
execv(path, const_cast<char**>(args));
// execv() only returns if an error happened, in which case we
// panic and never return from this function.
PLOG(FATAL) << "execv("" << path << "") failed";
return 1;
}
- 先调用
InitKernelLogging(argv)
初始化内核日志,再调用InstallRebootSignalHandlers()
注册需要处理的信号,这个和第一阶段是一样的 - 接着调用
SelinuxSetupKernelLogging()
将 selinux 日志重定向到内核 - 接着调用
SelinuxInitialize
加载 selinux_policy 战略文件,设置默许 selinux 形式 - 最终调用
selinux_android_restorecon
来设置 init 的安全上下问,接着经过 execv 跳转到第二阶段
接下来逐渐分析
2. SelinuxSetupKernelLogging selinux 日志重定向
SelinuxSetupKernelLogging 将 selinux 日志重定向到内核,其详细实现如下:
// system/core/init/selinux.cpp
//selinux 日志重定向到内核
// This function sets up SELinux logging to be written to kmsg, to match init's logging.
void SelinuxSetupKernelLogging() {
selinux_callback cb;
cb.func_log = SelinuxKlogCallback;
// 设置 selinux 日志的回调
selinux_set_callback(SELINUX_CB_LOG, cb);
}
这儿调用了 selinux_set_callback
函数,第一个参数 SELINUX_CB_LOG
表明要设置 Selinux 日志的回调,第二个参数 cb 的成员 func_log 便是详细的回调函数:
// 将 selinux 日志重定向到 /dev/kmsg
int SelinuxKlogCallback(int type, const char* fmt, ...) {
android::base::LogSeverity severity = android::base::ERROR;
if (type == SELINUX_WARNING) {
severity = android::base::WARNING;
} else if (type == SELINUX_INFO) {
severity = android::base::INFO;
}
char buf[1024];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
return 0;
}
这儿会调用 KernelLogger 将 Selinux 日志写入 /dev/kmsg
中。和上文分析的 InitKernelLogging(argv)
类似。
3. SelinuxInitialize 初始化 Selinux 环境
SelinuxInitialize 用于执行 Selinux 的初始化:
void SelinuxInitialize() {
Timer t;
LOG(INFO) << "Loading SELinux policy";
// 加载 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) {
// 设置默许 selinux 形式
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);
}
核心的调用有两个:
- LoadPolicy:加载 selinux_policy 战略文件
- security_setenforce:设置 selinux 的作业形式
selinux 配置文件的加载过程咱们放到 权限体系
专题给大家解说,这儿了解流程即可
4. 跳转第二阶段
const char* path = "/system/bin/init";
const char* args[] = {path, "second_stage", nullptr};
execv(path, const_cast<char**>(args));
这儿跳转 init 时,带了一个参数 “second_stage”,程序进入第二阶段。