Init进程的main()函数有一段代码用来初始化SELinux,如下所示:
int main
(......) {
......
//
设置SELinux的回调函数
union selinux_callback cb;
cb.func_log = klog_write;
selinux_set_callback(SELINUX_CB_LOG,
cb);
cb.func_audit = audit_callback;
selinux_set_callback(SELINUX_CB_AUDIT,
cb);
// 初始化SELinux
selinux_initialize();
// 恢复下面目录的安全上下文为系统原始设置
restorecon("/dev");
restorecon("/dev/socket");
restorecon("/dev/__properties__");
restorecon_recursive("/sys");
......
}
这里调用的函数selinux_set_callback()的代码位于目录external/libselinux下。我们先看看函数selinux_set_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_set_callback()的作用是对一些全局的函数指针赋值,前面Init进程的main()代码中调用函数selinux_set_callback()的结果是将selinux_log的值设置成klog_write,将selinux_audit的值设置成audit_callback,这两个回调函数并没有太多实际的作用。下面再看看selinux_initialize()函数的代码:
static
void selinux_initialize(void)
{
if (selinux_is_disabled()) {
return;
// 如果SELinux没有enable,退出。
}
if
(selinux_android_load_policy() < 0) { //
装载并向内核设置Policy
// 如果装载SELinux的policy失败,重启Android
android_reboot(ANDROID_RB_RESTART2, 0,
"recovery");
while (1) { pause(); }
}
selinux_init_all_handles();
// 装载文件和属性的安全上下文
bool is_enforcing =
selinux_is_enforcing();
security_setenforce(is_enforcing);
}
selinux_initialize()函数的主要作用是从文件中读取SELinux的配置文件,然后把它设置到内核中,这样SELinux才能开始工作。我们先看看selinux_is_disabled()函数是如何工作的:
static
bool selinux_is_disabled(void)
{
char tmp[PROP_VALUE_MAX];
if (access("/sys/fs/selinux", F_OK) != 0)
{
return true;
// 如果目录/sys/fs/selinux不可以访问,说明SeLinux被disable了。
}
if((property_get("ro.boot.selinux", tmp) !=
0)&&(strcmp(tmp, "disabled")== 0))
{
return true; //
如果ro.boot.selinux等于disabled,说明配置中disable了SELinux
}
return false;
}
selinux_is_disabled()函数先判断目录/sys/fs/selinux是否可以访问,因为这个目录是SELinux的虚拟文件系统所在的目录,不能返回说明SELinux被disable了。同时还要判断属性ro.boot.selinux是否等于“disabled”,这个属性值表示SELinux的状态。
selinux_android_load_policy()函数代码如下:
int
selinux_android_load_policy(void)
{
char
*mnt = SELINUXMNT;
// mnt设置为/sys/fs/selinux
int
rc;
// 挂载文件系统selinuxfs到目录/sys/fs/selinux
rc =
mount(SELINUXFS, mnt, SELINUXFS, 0, NULL);
if
(rc < 0) {
if (errno == ENODEV) {
return -1;
// 如果mount返回的错误是没有设备,返回。
}
if (errno == ENOENT) {
// 如果mount返回的错误表示目录不存在
mnt = OLDSELINUXMNT;
// 则讲mnt设置为
/selinux
rc = mkdir(mnt, 0755);
// 创建目录
...... // 错误处理
// 挂载文件系统selinuxfs到目录/selinux
rc = mount(SELINUXFS, mnt, SELINUXFS, 0, NULL);
}
}
...... // 错误处理
set_selinuxmnt(mnt);
// 将mnt的值赋给selinux_mnt
return selinux_android_reload_policy(); //
装载SELinux的策略
}
selinux_android_load_policy()函数首先把SELinux的虚拟文件系统selinuxfs挂载到目录/sys/fs/selinux,如果不成功,再尝试挂载到目录/selinux。挂载成功后,调用selinux_android_reload_policy()函数来装载策略,函数代码如下:
int
selinux_android_reload_policy(void)
{
int
fd = -1, rc;
struct stat sb;
void
*map = NULL;
int
i = 0;
// 打开一个policy文件
while (fd < 0 && sepolicy_file[i])
{
fd = open(sepolicy_file[i], O_RDONLY |
O_NOFOLLOW);
i++;
}
......
// 错误处理
if
(fstat(fd, &sb) < 0) {
......
// 错误处理
}
map
= mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);//
mmap文件到内存中
......
// 错误处理
rc =
security_load_policy(map, sb.st_size); //
装载policy
......
// 错误处理
munmap(map, sb.st_size);
close(fd);
return 0;
}
selinux_android_reload_policy()函数首先打开policy文件,然后把它mmap进内存,最后调用函数security_load_policy()把policy设置到内核中。policy文件的文件名保存在数组const
sepolicy_file中,定义如下:
static
const char *const sepolicy_file[] = {
"/data/security/current/sepolicy",
"/sepolicy",
0 };
这两个文件找到一个就可以了,通常是根目录下的sepolicy文件。这个文件就是从源码的external/sepolicy目录下的规则文件编译得到的。
最后我们看看security_load_policy()函数的实现:
int
security_load_policy(void *data, size_t
len)
{
char
path[PATH_MAX];
int
fd, ret;
...... // 检查参数
snprintf(path, sizeof path, "%s/load", selinux_mnt);
fd =
open(path, O_RDWR);
...... // 错误检查
ret
= write(fd, data, len); //
写入文件
close(fd);
if
(ret < 0) return -1;
return 0;
}
security_load_policy()函数很简单,打开SELinux虚拟目录(通常是/sys/fs/selinux)下的“load”文件,然后把策略数据写到文件中。
本文详细介绍了SELinux在Linux系统中的启动过程,包括初始化、设置回调函数、加载策略文件等关键步骤,并解释了如何判断SELinux是否被禁用。
194

被折叠的 条评论
为什么被折叠?



