该系列文章总纲链接:专题分纲目录 android 开机启动流程分析
本章关键点总结 & 说明:
说明:思维导图是基于之前文章不断迭代的,本章内容我们关注➕android SELinux 部分即可
init中SELinux的源码分析,Android平台中,SEAndroid的初始化由进程的祖先init的main函数完成,相关代码如下所示:
int main(int argc, char **argv)
{
...
//说明:init中对SElinux的控制主要依赖于使用了libselinux库(android的SEAndroid库)
//向SELinux设置两个回调函数,主要是打印log
union selinux_callback cb;
cb.func_log = log_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
cb.func_audit = audit_callback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
selinux_initialize(); //SELinux初始化
//restore context:就是根据XXX_contexts中的内容给一些目录做labeling{打SELinux标签}。
restorecon("/dev");
restorecon("/dev/socket");
restorecon("/dev/__properties__");
restorecon_recursive("/sys");
...
}
1 selinux_initialize实现如下:
static void selinux_initialize(void)
{
/**判断selinux功能是否启用。方法是:
/sys/fs/selinux 是否存在。或者ro.boot.selinux 属性不为disabled
*/
if (selinux_is_disabled()) {
return;
}
if (selinux_android_load_policy() < 0) {//加载sepolicy文件
ERROR("SELinux: Failed to load policy; rebooting into recovery mode\n");
android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
while (1) { pause(); } // never reached
}
//初始化file_context,seapp_context及property_context相关内容
selinux_init_all_handles();
/**
selinux有两种工作模式,
“permissive”:所有操作都被允许(即没有MAC),但是如果有违反权限的话,会记录日志
“enforcing”:所有操作都会进行权限检查
*/
bool is_enforcing = selinux_is_enforcing();
//设置SELinux的模式,向"/sys/fs/selinux"/enforce文件中写入is_enforcing值
security_setenforce(is_enforcing);
}
这里关注核心的2个方法,selinux_android_load_policy和selinux_init_all_handles
1.1 selinux_android_load_policy的实现
int selinux_android_load_policy(void)
{
const char *mnt = SELINUXMNT;
int rc;
//挂载/sys/fs/selinux,SELINUXFS值为"selinuxfs"
rc = mount(SELINUXFS, mnt, SELINUXFS, 0, NULL);
...//rc<0容错处理
/**
说明:/sys/fs/selinux为userpace和kernel中的SELinux模块交互的通道
*/
set_selinuxmnt(mnt);
return selinux_android_load_policy_helper(false);
}
继续分析selinux_android_load_policy_helper的实现,如下:
static int selinux_android_load_policy_helper(bool reload)
{
int fd = -1, rc;
struct stat sb;
void *map = NULL;
...//根据文件存在与否确定policy_index的值
//根据policy_index的不同0/1来确定,注意sepolicy文件的位置:
/data/security/current/sepolicy:自定义的策略文件一般放在这里,init优先使用它。
/sepolicy:root根目录下的sepolicy:如果data目录下没有sepolicy,则使用系统默认的sepolicy。
fd = open(sepolicy_file[policy_index], O_RDONLY | O_NOFOLLOW);
...
fstat(fd, &sb);
...
map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); //这里通过mmap的方式,将sepolicy文件映射到map中
...
//该函数由selinux.h定义,它将此map、即sepolicy文件加载到内核中
rc = security_load_policy(map, sb.st_size);
...
munmap(map, sb.st_size);
close(fd);
return 0;
}
特殊说明:
libselinux库通过操作/sys/fs/selinux下的文件来完成和Kernel中SELinux模块的交互。
同时将sepolicy文件传递给Kernel,这样Kernel就有了安全策略配置文件,后续的MAC才能开展起来。
1.2 selinux_init_all_handles的实现如下:
/**
创建两个handler,主要为后续做labeling{打SELinux的系统标签}控制
通过调用libselinux的函数来打开前面分析file_contexts和property_contexts文件
以便可以用来查询系统文件和系统属性的安全上下文。
*/
void selinux_init_all_handles(void)
{
sehandle = selinux_android_file_context_handle();//文件上下文handle
selinux_android_set_sehandle(sehandle);
sehandle_prop = selinux_android_prop_context_handle();//属性上下文handle
}
restorecon分析如下:
static int selinux_android_restorecon_common(...){
if (is_selinux_enabled() <= 0)
return 0;
...
return restorecon_sb(pathname, &sb, nochange, verbose, seinfo, uid);
//->最后会调用lsetfilecon->核心就是调用lsetxattr来设置文件系统属性
}
}
2 property权限检查的分析
/**
在init的handle_property_set_fd中获取property_set发送的消息
处理时会执行check_perms或check_control_mac_perms,2个方法最后都会调用check_mac_perms
因此属性服务设置的SELinux权限检测是在init中,方法check_mac_perms的实现如下:
*/
static int check_mac_perms(const char *name, char *sctx)
{
if (is_selinux_enabled() <= 0)
return 1;
...//容错处理
//检查property_context中是否定义了目标SContext,即tctx。
if (selabel_lookup(sehandle_prop, &tctx, name, 1) != 0)
goto err;
/**
将源SContext和目标SContext进行比较,判断是否有相关权限。
name是属性的名字
源SContext是调用setprop进程的SContext。调用setprop的进程的SContext
目标SContext是property_context文件中定义的SContext。不同的属性有不同的SContext
操作名称(perm,由access vector定义。对Property这种Object class而言,其唯一需要做权限检查的操作就是set。
要检查的Object class(系统所支持的类在external/sepolicy/security_classes文件中定义,
参考external/sepolicy/access_vectors这个文件)。
具体的哪一个属性(name参数指定,就是具体指明哪一文件)。
*/
if (selinux_check_access(sctx, tctx, class, perm, (void*) name) == 0)
result = 1;
freecon(tctx);
err:
return result;
}
说明:这里仅仅是从init中的代码来分析SELinux的机制,实际上SElinux也要专门开一个专题来讲才会清楚。目前这里仅作简介