android6.0 init进程main之selinux_initialize

18 篇文章 1 订阅
8 篇文章 0 订阅

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 为启动的阶段

// /system/core/init/Init.cpp
selinux_initialize(is_first_stage);

selinux_initialize 整体代码如下:

// /system/core/init/Init.cpp
static void selinux_initialize(bool in_kernel_domain) {    // in_kernel_domain 区分内核态和用户态
    Timer t;      //使用 Timer 计时,计算 selinux 初始化耗时

    selinux_callback cb;
    cb.func_log = selinux_klog_callback;              // 用于打印 Log 的回调函数
    selinux_set_callback(SELINUX_CB_LOG, cb);         // 设置 log 的打印回调函数,使用 type 为 SELINUX_CB_LOG
    cb.func_audit = audit_callback;                   // 用于检查权限的回调函数
    selinux_set_callback(SELINUX_CB_AUDIT, cb);       // 设置审查回调函数,使用 type 为 SELINUX_CB_AUDIT

    if (selinux_is_disabled()) {    // 如果 SELinux 没有 enable,退出。
        return;
    }

    if (in_kernel_domain) {        // 内核态处理流程,第一阶段 in_kernel_domain 为 true 
        INFO("Loading SELinux policy...\n");       // 该行 log 打印不出,INFO 级别 (因为设置了打印输出的级别为 5)
        // 用于加载 sepolicy 文件。该函数最终将 sepolicy 文件传递给 kernel,这样 kernel 就有了安全策略配置文件
        if (selinux_android_load_policy() < 0) {
            ERROR("failed to load policy: %s\n", strerror(errno));
            security_failure();    // 将重启进入 recovery mode
        }

        bool is_enforcing = selinux_is_enforcing();          // 命令行中得到的信息
        // 用于设置 selinux 的工作模式。selinux 有两种工作模式:
        // 1、”permissive”,所有的操作都被允许(即没有MAC),但是如果违反权限的话,会记录日志
        // 2、”enforcing”,所有操作都会进行权限检查。在一般的终端中,应该工作于 enforing 模式
        security_setenforce(is_enforcing);        //设置 selinux 的模式,是开还是关

        if (write_file("/sys/fs/selinux/checkreqprot", "0") == -1) {
            security_failure();    // 将重启进入 recovery mode
        }

        NOTICE("(Initializing SELinux %s took %.2fs.)\n",
               is_enforcing ? "enforcing" : "non-enforcing", t.duration());   // 输出 selinux 的模式,与初始化耗时
    } else {
		// selinux_init_all_handles初始化file_context,seapp_context以及property_context相关内容
        selinux_init_all_handles();    // 如果启动第二阶段,调用该函数
    }
}

callback 设置

union selinux_callback {
	// 记录printf样式的格式和参数,类型代码指示消息的类型
	int 
#ifdef __GNUC__
__attribute__ ((format(printf, 2, 3)))
#endif
	(*func_log) (int type, const char *fmt, ...);
	// 将 auditdata 的字符串表示形式(对应于给定的安全类)存储到 msgbuf 中。
	int (*func_audit) (void *auditdata, security_class_t cls,
			   char *msgbuf, size_t msgbufsize);
	// 验证提供的上下文,必要时进行修改
	int (*func_validate) (char **ctx);
	// setenforce 消息的 netlink 回调
	int (*func_setenforce) (int enforcing);
	// policyload 消息的 netlink 回调
	int (*func_policyload) (int seqno);
};

打印 Log 的回调函数,也是往 /dev/kmsg 文件中写入 log 内容:

// /system/core/init/log.cpp
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。

// /external/libselinux/callbacks.c
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 的状态。

// /system/core/init/Init.cpp
static bool selinux_is_disabled(void)
{
    // ALLOW_DISABLE_SELINUX 定义在了 Android.mk
    if (ALLOW_DISABLE_SELINUX) {
        if (access("/sys/fs/selinux", F_OK) != 0) {
            // SELinux 未编译到内核中,或者已通过内核命令行 “SELinux=0” 禁用。
            return true;
        }
		// 没有被禁用的情况时 
        // 通过 androidboot.selinux 判断 SELinux 是否被禁用
        return selinux_status_from_cmdline() == SELINUX_DISABLED;
    }

    return false;
}

其中 ALLOW_DISABLE_SELINUX 定义在了 Android.mk 文件中了:
基本上输出的版本 DALLOW_DISABLE_SELINUX 都是 0 即不允许关闭

// /system/core/init/Android.mk
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 状态信息

// /system/core/init/Init.cpp
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 状态。

// /system/core/init/util.cpp
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;

        /* get rid of trailing newline, it happens */
        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语句中:

// /system/core/init/Init.cpp(static void selinux_initialize(bool in_kernel_domain)函数中)
    if (in_kernel_domain) {        // 内核态处理流程,第一阶段 in_kernel_domain 为 true 
        INFO("Loading SELinux policy...\n");       // 该行 log 打印不出,INFO 级别 (因为设置了打印输出的级别为 5)
        // 用于加载 sepolicy 文件。该函数最终将 sepolicy 文件传递给 kernel,这样 kernel 就有了安全策略配置文件
        if (selinux_android_load_policy() < 0) {
            ERROR("failed to load policy: %s\n", strerror(errno));
            security_failure();    // 将重启进入 recovery mode
        }

        bool is_enforcing = selinux_is_enforcing();          // 命令行中得到的信息
        // 用于设置 selinux 的工作模式。selinux 有两种工作模式:
        // 1、”permissive”,所有的操作都被允许(即没有MAC),但是如果违反权限的话,会记录日志
        // 2、”enforcing”,所有操作都会进行权限检查。在一般的终端中,应该工作于 enforing 模式
        security_setenforce(is_enforcing);        //设置 selinux 的模式,是开还是关

        if (write_file("/sys/fs/selinux/checkreqprot", "0") == -1) {
            security_failure();    // 将重启进入 recovery mode
        }

        NOTICE("(Initializing SELinux %s took %.2fs.)\n",
               is_enforcing ? "enforcing" : "non-enforcing", t.duration());   // 输出 selinux 的模式,与初始化耗时
    }

selinux_android_load_policy()

用于加载 sepolicy 文件。该函数最终将 sepolicy 文件传递给 kernel,这样 kernel 就有了安全策略配置文件

// /external/libselinux/src/android.c
int selinux_android_load_policy(void)
{
    // /external/libselinux/src/policy.h:#define SELINUXMNT "/sys/fs/selinux"
	const char *mnt = SELINUXMNT;
	int rc;
	// /external/libselinux/src/policy.h:20:#define SELINUXFS "selinuxfs"
	rc = mount(SELINUXFS, mnt, SELINUXFS, 0, NULL);
	if (rc < 0) {
		if (errno == ENODEV) {
			/* SELinux not enabled in kernel */
			return -1;
		}
		if (errno == ENOENT) {
			/* Fall back to legacy mountpoint. */
			// /external/libselinux/src/policy.h:#define OLDSELINUXMNT "/selinux"
			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;
	}
	// sys/fs/selinux 为 userspace 和 kernel 的 SELinux 模块交互通道
	// 用户空间进程可以读写 /sys/fs/selinux 的各个文件或其中的子目录来通知 kernel 中的 SELinux 完成相关的操作。
	set_selinuxmnt(mnt);

    // //加载 selinux policy。
    return selinux_android_load_policy_helper(false);
}

其中 set_selinuxmnt 如下:

// /external/libselinux/src/init.c
void set_selinuxmnt(const char *mnt)
{
	// selinux_mnt 为写入策略时的文件
	// strdup 将字符串拷贝到新建的位置处,返回一个指针,指向为复制字符串分配的空间;如果分配空间失败,则返回NULL值。
	selinux_mnt = strdup(mnt);
}

selinux_android_load_policy_helper 函数实体:

// /external/libselinux/src/android.c
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;


    // 如果重新加载策略时没有/data策略,或者/data策略的版本错误,并且之前的加载来自/policy,那么只需返回即可。
	set_policy_index();
	if (reload && !policy_index && !old_policy_index)
		return 0;


    // sepolicy_file 指向的是 sepolicy 文件的路径,即 root 目录下的 sepolicy 文件。
	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;
	}
    // 将 sepolicy 文件加载到内核中, map 需要导入的 data 内容
	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 如下:

// /external/libselinux/src/android.c
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

// /external/libselinux/src/load_policy.c
int security_load_policy(void *data, size_t len)
{
	char path[PATH_MAX];
	int fd, ret;

	if (!selinux_mnt) {
		errno = ENOENT;
		return -1;
	}
    // selinux_mnt 被设置为了 /sys/fs/selinux
	snprintf(path, sizeof path, "%s/load", selinux_mnt);
	// fd 对应的是 /sys/fs/selinux/load
	fd = open(path, O_RDWR);
	if (fd < 0)
		return -1;
    // 传入参数 data 为策略文件读取到的内容; fd 为 /sys/fs/selinux/load 文件句柄
	// 即 将策略内容写入 /sys/fs/selinux/load
	ret = write(fd, data, len);
	close(fd);
	if (ret < 0)
		return -1;
	return 0;
}

selinux_is_enforcing()

// /system/core/init/Init.cpp
// 命令行中得到的信息
static bool selinux_is_enforcing(void)
{
    // ALLOW_DISABLE_SELINUX 定义在了 Android.mk
    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 模式

// /system/core/init/setenforce.c
int security_setenforce(int value)
{
	int fd, ret;
	char path[PATH_MAX];
	char buf[20];

	if (!selinux_mnt) {
		errno = ENOENT;
		return -1;
	}
    // selinux_mnt 被设置为了 /sys/fs/selinux
	snprintf(path, sizeof path, "%s/enforce", selinux_mnt);
	// fd 对应的是 /sys/fs/selinux/enforce
	fd = open(path, O_RDWR);
	if (fd < 0)
		return -1;

	snprintf(buf, sizeof buf, "%d", value);
	// 即 将策略内容 value 值写入 /sys/fs/selinux
	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

// /system/core/init/init.cpp
static void security_failure() {
    ERROR("Security failure; rebooting into recovery mode...\n");
    android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
    while (true) { pause(); }  // never reached
}

android_reboot 代码如下:

// /system/core/libcutils/android_reboot.c
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:
            // 走这条 case 进行 recovery
            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。

// /system/core/init/init.cpp
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

// /external/libselinux/src/android.c
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


// /external/libselinux/src/android.c
void selinux_android_set_sehandle(const struct selabel_handle *hndl)
{
    fc_sehandle = (struct selabel_handle *) hndl;
}

创建 prophandler。

// /external/libselinux/src/android.c
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 分析完毕。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值