android6.0 init进程main之selinux_initialize
对应代码android6.0_r72,kernel对应linux3.18
前言
selinux 的初始化 selinux_initialize
涉及文件
/system/core/init/Init.cpp /external/libselinux/callbacks.c /system/core/init/log.cpp /system/core/init/Android.mk /external/libselinux/src/android.c /external/libselinux/src/init.c /external/libselinux/src/load_policy.c /system/core/init/setenforce.c /system/core/libcutils/android_reboot.c
selinux_initialize
init.cpp 文件 main() 函数中调用 selinux_initialize 参数 is_first_stage 为启动的阶段
selinux_initialize(is_first_stage);
selinux_initialize 整体代码如下:
static void selinux_initialize(bool in_kernel_domain) {
Timer t;
selinux_callback cb;
cb.func_log = selinux_klog_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
cb.func_audit = audit_callback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
if (selinux_is_disabled()) {
return;
}
if (in_kernel_domain) {
INFO("Loading SELinux policy...\n");
if (selinux_android_load_policy() < 0) {
ERROR("failed to load policy: %s\n", strerror(errno));
security_failure();
}
bool is_enforcing = selinux_is_enforcing();
security_setenforce(is_enforcing);
if (write_file("/sys/fs/selinux/checkreqprot", "0") == -1) {
security_failure();
}
NOTICE("(Initializing SELinux %s took %.2fs.)\n",
is_enforcing ? "enforcing" : "non-enforcing", t.duration());
} else {
selinux_init_all_handles();
}
}
callback 设置
union selinux_callback {
int
#ifdef __GNUC__
__attribute__ ((format(printf, 2, 3)))
#endif
(*func_log) (int type, const char *fmt, ...);
int (*func_audit) (void *auditdata, security_class_t cls,
char *msgbuf, size_t msgbufsize);
int (*func_validate) (char **ctx);
int (*func_setenforce) (int enforcing);
int (*func_policyload) (int seqno);
};
打印 Log 的回调函数,也是往 /dev/kmsg 文件中写入 log 内容:
int selinux_klog_callback(int type, const char *fmt, ...) {
int level = KLOG_ERROR_LEVEL;
if (type == SELINUX_WARNING) {
level = KLOG_WARNING_LEVEL;
} else if (type == SELINUX_INFO) {
level = KLOG_INFO_LEVEL;
}
va_list ap;
va_start(ap, fmt);
init_klog_vwrite(level, fmt, ap);
va_end(ap);
return 0;
}
selinux_set_callback() 的作用是对一些全局的函数指针赋值,selinux_initialize 调用函数 selinux_set_callback() 的结果是将 selinux_log 的值设置成 klog_write, 将 selinux_audit 的值设置成 audit_callback。
void selinux_set_callback(int type, union selinux_callback cb)
{
switch (type) {
case SELINUX_CB_LOG:
selinux_log = cb.func_log;
break;
case SELINUX_CB_AUDIT:
selinux_audit = cb.func_audit;
break;
case SELINUX_CB_VALIDATE:
selinux_validate = cb.func_validate;
break;
case SELINUX_CB_SETENFORCE:
selinux_netlink_setenforce = cb.func_setenforce;
break;
case SELINUX_CB_POLICYLOAD:
selinux_netlink_policyload = cb.func_policyload;
break;
}
}
判断 selinux 是否关闭
selinux_is_disabled 判断 selinux 是否关闭,如果关闭就直接退出,没必要再初始化了。 selinux_is_disabled() 当设置允许关闭时,函数判断目录 /sys/fs/selinux 是否可以访问,因为这个目录是 SELinux 的虚拟文件系统所在的目录,不能返回说明 SELinux 被 disable 了。同时还要判断属性 ro.boot.selinux 是否等于 “disabled”, 这个属性值表示 SELinux 的状态。
static bool selinux_is_disabled(void)
{
if (ALLOW_DISABLE_SELINUX) {
if (access("/sys/fs/selinux", F_OK) != 0) {
return true;
}
return selinux_status_from_cmdline() == SELINUX_DISABLED;
}
return false;
}
其中 ALLOW_DISABLE_SELINUX 定义在了 Android.mk 文件中了: 基本上输出的版本 DALLOW_DISABLE_SELINUX 都是 0 即不允许关闭
ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
init_options += -DALLOW_LOCAL_PROP_OVERRIDE=1 -DALLOW_DISABLE_SELINUX=1
else
init_options += -DALLOW_LOCAL_PROP_OVERRIDE=0 -DALLOW_DISABLE_SELINUX=0
endif
获取 selinux 状态信息
static selinux_enforcing_status selinux_status_from_cmdline() {
selinux_enforcing_status status = SELINUX_ENFORCING;
std::function<void(char*,bool)> fn = [&](char* name, bool in_qemu) {
char *value = strchr(name, '=');
if (value == nullptr) { return; }
*value++ = '\0';
if (strcmp(name, "androidboot.selinux") == 0) {
if (strcmp(value, "disabled") == 0) {
status = SELINUX_DISABLED;
} else if (strcmp(value, "permissive") == 0) {
status = SELINUX_PERMISSIVE;
}
}
};
import_kernel_cmdline(false, fn);
return status;
}
import_kernel_cmdline 将从 /proc/cmdline 读取到的 cmdline 进行分割,回调 import_kernel_nv() 进行判断;返回 selinux 状态。
void import_kernel_cmdline(bool in_qemu, std::function<void(char*,bool)> import_kernel_nv)
{
char cmdline[2048];
char *ptr;
int fd;
fd = open("/proc/cmdline", O_RDONLY | O_CLOEXEC);
if (fd >= 0) {
int n = read(fd, cmdline, sizeof(cmdline) - 1);
if (n < 0) n = 0;
if (n > 0 && cmdline[n-1] == '\n') n--;
cmdline[n] = 0;
close(fd);
} else {
cmdline[0] = 0;
}
ptr = cmdline;
while (ptr && *ptr) {
char *x = strchr(ptr, ' ');
if (x != 0) *x++ = 0;
import_kernel_nv(ptr, in_qemu);
ptr = x;
}
}
启动第一阶段的 selinux 初始化
经过上述判断过程后,满足条件则进入启动第一阶段的 selinux 初始化 即进入如下if语句中:
if (in_kernel_domain) {
INFO("Loading SELinux policy...\n");
if (selinux_android_load_policy() < 0) {
ERROR("failed to load policy: %s\n", strerror(errno));
security_failure();
}
bool is_enforcing = selinux_is_enforcing();
security_setenforce(is_enforcing);
if (write_file("/sys/fs/selinux/checkreqprot", "0") == -1) {
security_failure();
}
NOTICE("(Initializing SELinux %s took %.2fs.)\n",
is_enforcing ? "enforcing" : "non-enforcing", t.duration());
}
selinux_android_load_policy()
用于加载 sepolicy 文件。该函数最终将 sepolicy 文件传递给 kernel,这样 kernel 就有了安全策略配置文件
int selinux_android_load_policy(void)
{
const char *mnt = SELINUXMNT;
int rc;
rc = mount(SELINUXFS, mnt, SELINUXFS, 0, NULL);
if (rc < 0) {
if (errno == ENODEV) {
return -1;
}
if (errno == ENOENT) {
mnt = OLDSELINUXMNT;
rc = mkdir(mnt, 0755);
if (rc == -1 && errno != EEXIST) {
selinux_log(SELINUX_ERROR,"SELinux: Could not mkdir: %s\n",
strerror(errno));
return -1;
}
rc = mount(SELINUXFS, mnt, SELINUXFS, 0, NULL);
}
}
if (rc < 0) {
selinux_log(SELINUX_ERROR,"SELinux: Could not mount selinuxfs: %s\n",
strerror(errno));
return -1;
}
set_selinuxmnt(mnt);
return selinux_android_load_policy_helper(false);
}
其中 set_selinuxmnt 如下:
void set_selinuxmnt(const char *mnt)
{
selinux_mnt = strdup(mnt);
}
selinux_android_load_policy_helper 函数实体:
static int selinux_android_load_policy_helper(bool reload)
{
int fd = -1, rc;
struct stat sb;
void *map = NULL;
int old_policy_index = policy_index;
set_policy_index();
if (reload && !policy_index && !old_policy_index)
return 0;
fd = open(sepolicy_file[policy_index], O_RDONLY | O_NOFOLLOW);
if (fd < 0) {
selinux_log(SELINUX_ERROR, "SELinux: Could not open sepolicy: %s\n",
strerror(errno));
return -1;
}
if (fstat(fd, &sb) < 0) {
selinux_log(SELINUX_ERROR, "SELinux: Could not stat %s: %s\n",
sepolicy_file[policy_index], strerror(errno));
close(fd);
return -1;
}
map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (map == MAP_FAILED) {
selinux_log(SELINUX_ERROR, "SELinux: Could not map %s: %s\n",
sepolicy_file[policy_index], strerror(errno));
close(fd);
return -1;
}
rc = security_load_policy(map, sb.st_size);
if (rc < 0) {
selinux_log(SELINUX_ERROR, "SELinux: Could not load policy: %s\n",
strerror(errno));
munmap(map, sb.st_size);
close(fd);
return -1;
}
munmap(map, sb.st_size);
close(fd);
selinux_log(SELINUX_INFO, "SELinux: Loaded policy from %s\n", sepolicy_file[policy_index]);
return 0;
}
init 通过 mmap 的方式,将 sepolicy 文件传递给 kernel。init 使用了 libselinux 提供的 API 完成相关操作。而 libselinux 则是通过 /sys/fs/selinux 下的文件来完成和 Kernel 中 SELinux 模块的交互。
set_policy_index 和 security_load_policy 如下:
static void set_policy_index(void)
{
int fd_base = -1, fd_override = -1;
struct stat sb_base;
struct stat sb_override;
void *map_base, *map_override;
policy_index = 0;
fd_base = open(POLICY_BASE_VERSION, O_RDONLY | O_NOFOLLOW);
if (fd_base < 0)
return;
if (fstat(fd_base, &sb_base) < 0)
goto close_base;
fd_override = open(POLICY_OVERRIDE_VERSION, O_RDONLY | O_NOFOLLOW);
if (fd_override < 0)
goto close_base;
if (fstat(fd_override, &sb_override) < 0)
goto close_override;
if (sb_base.st_size != sb_override.st_size)
goto close_override;
map_base = mmap(NULL, sb_base.st_size, PROT_READ, MAP_PRIVATE, fd_base, 0);
if (map_base == MAP_FAILED)
goto close_override;
map_override = mmap(NULL, sb_override.st_size, PROT_READ, MAP_PRIVATE, fd_override, 0);
if (map_override == MAP_FAILED)
goto unmap_base;
if (memcmp(map_base, map_override, sb_base.st_size) != 0)
goto unmap_override;
if (access(sepolicy_file[1], R_OK) != 0)
goto unmap_override;
if (access(seopts[1].value, R_OK) != 0)
goto unmap_override;
if (access(seopts_prop[1].value, R_OK) != 0)
goto unmap_override;
if (access(seopts_service[1].value, R_OK) != 0)
goto unmap_override;
if (access(seapp_contexts_file[1], R_OK) != 0)
goto unmap_override;
policy_index = 1;
unmap_override:
munmap(map_override, sb_override.st_size);
unmap_base:
munmap(map_base, sb_base.st_size);
close_override:
close(fd_override);
close_base:
close(fd_base);
return;
}
将策略内容写入 /sys/fs/selinux/load
int security_load_policy(void *data, size_t len)
{
char path[PATH_MAX];
int fd, ret;
if (!selinux_mnt) {
errno = ENOENT;
return -1;
}
snprintf(path, sizeof path, "%s/load", selinux_mnt);
fd = open(path, O_RDWR);
if (fd < 0)
return -1;
ret = write(fd, data, len);
close(fd);
if (ret < 0)
return -1;
return 0;
}
selinux_is_enforcing()
static bool selinux_is_enforcing(void)
{
if (ALLOW_DISABLE_SELINUX) {
return selinux_status_from_cmdline() == SELINUX_ENFORCING;
}
return true;
}
这里调用的 selinux_status_from_cmdline() 文章 selinux_is_disabled 部分已经分析,这里略过了。 设置 selinux 的模式: security_setenforce 用于设置 selinux 的工作模式。selinux 有两种工作模式: 1、”permissive”,所有的操作都被允许(即没有MAC),但是如果违反权限的话,会记录日志 2、”enforcing”,所有操作都会进行权限检查。在一般的终端中,应该工作于 enforing 模式
int security_setenforce(int value)
{
int fd, ret;
char path[PATH_MAX];
char buf[20];
if (!selinux_mnt) {
errno = ENOENT;
return -1;
}
snprintf(path, sizeof path, "%s/enforce", selinux_mnt);
fd = open(path, O_RDWR);
if (fd < 0)
return -1;
snprintf(buf, sizeof buf, "%d", value);
ret = write(fd, buf, strlen(buf));
close(fd);
if (ret < 0)
return -1;
return 0;
}
导入和设置失败时
当 selinux_android_load_policy() 和 security_setenforce(is_enforcing) 失败时,会调用 security_failure() 重启 Android 进行 recovery
static void security_failure() {
ERROR("Security failure; rebooting into recovery mode...\n");
android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
while (true) { pause(); }
}
android_reboot 代码如下:
int android_reboot(int cmd, int flags UNUSED, const char *arg)
{
int ret;
sync();
remount_ro();
switch (cmd) {
case ANDROID_RB_RESTART:
ret = reboot(RB_AUTOBOOT);
break;
case ANDROID_RB_POWEROFF:
ret = reboot(RB_POWER_OFF);
break;
case ANDROID_RB_RESTART2:
ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART2, arg);
break;
default:
ret = -1;
}
return ret;
}
启动第二阶段的 selinux 初始化
第二阶段的启动 selinux 初始化执行 selinux_init_all_handles() 函数,创建并设置文件 handler,创建 prophandler。
static void selinux_init_all_handles(void)
{
sehandle = selinux_android_file_context_handle();
selinux_android_set_sehandle(sehandle);
sehandle_prop = selinux_android_prop_context_handle();
}
创建文件 handler
struct selabel_handle* selinux_android_file_context_handle(void)
{
struct selabel_handle *sehandle;
set_policy_index();
sehandle = selabel_open(SELABEL_CTX_FILE, &seopts[policy_index], 1);
if (!sehandle) {
selinux_log(SELINUX_ERROR, "%s: Error getting file context handle (%s)\n",
__FUNCTION__, strerror(errno));
return NULL;
}
if (!compute_contexts_hash(seopts, fc_digest)) {
selabel_close(sehandle);
return NULL;
}
selinux_log(SELINUX_INFO, "SELinux: Loaded file_contexts contexts from %s.\n",
seopts[policy_index].value);
return sehandle;
}
设置文件 handler
void selinux_android_set_sehandle(const struct selabel_handle *hndl)
{
fc_sehandle = (struct selabel_handle *) hndl;
}
创建 prophandler。
struct selabel_handle* selinux_android_prop_context_handle(void)
{
struct selabel_handle* sehandle;
set_policy_index();
sehandle = selabel_open(SELABEL_CTX_ANDROID_PROP,
&seopts_prop[policy_index], 1);
if (!sehandle) {
selinux_log(SELINUX_ERROR, "%s: Error getting property context handle (%s)\n",
__FUNCTION__, strerror(errno));
return NULL;
}
selinux_log(SELINUX_INFO, "SELinux: Loaded property_contexts from %s.\n",
seopts_prop[policy_index].value);
return sehandle;
}
至此,selinux_initialize 分析完毕。
|