大家好,又见面了,我是你们的朋友全栈君。
Android SELinux开发多场景实战指南目录:
Android SELinux开发入门指南之SELinux基础知识 Android SEAndroid权限问题指南 Android SELinux开发入门指南之如何增加Java Binder Service权限 Android SELinux开发入门指南之权限解决万能规则 Android SELinux开发入门指南之如何增加Native Binder Service权限 Android SELinux开发入门指南之正确姿势解决访问data目录权限问题 正确姿势临时和永久关闭Android的SELinux
自从Android 4.4强制开启SELinux以后,在开发中我们经常会遇到avc denied的问题,为了方便开发调试我们会将SELinux关闭,那么本章将带领读者怎么临时和永久关闭Android的SELinux。
注意:这里的源码是以Android 8.0为基准的。
这个操作比较简单,但是前提条件是机器能被root,且固件里面没有限制setenforce命令的执行。下面让我们看看怎么执行:
XXX:/ # getenforce //获取当前SELinux状态
Enforcing
XXX:/ # setenforce 0 //临时关闭SELinux状态
XXX:/ # getenforce //获取SELinux状态
Permissive
XXX:/ # setenforce 1 //永久开启SELinux状态
XXX:/ # getenforce
Enforcing
XXX:/ #
注意点:
setenforce: Could not set enforcing status: Permission denied //非root情况
setenforce: Couldn't set enforcing status to '0': Invalid argument //Root情况,但是Room做了定制不允许setenforce
init进程是Android内核启动的第一个用户级进程,其中的SELinux也是在init进程中启动的,代码位置在system/core/init/init.cpp中。
int main(int argc, char** argv) {
...
selinux_initialize(true);
...
}
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 (in_kernel_domain) {
LOG(INFO) << "Loading SELinux policy";
if (!selinux_load_policy()) {
panic();
}
bool kernel_enforcing = (security_getenforce() == 1);//判断条件1
bool is_enforcing = selinux_is_enforcing();//判断条件2
if (kernel_enforcing != is_enforcing) {
if (security_setenforce(is_enforcing)) {
PLOG(ERROR) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
security_failure();
}
}
std::string err;
if (!WriteFile("/sys/fs/selinux/checkreqprot", "0", &err)) {
LOG(ERROR) << err;
security_failure();
}
// 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);
} else {
selinux_init_all_handles();
}
}
通过代码我们可以看出,init会通过security_getenforce()和selinux_is_enforcing()的值是否一致来判断是否开启SELinux,当不一致的时候会设置security_setenforce的值为is_enforcing。
下面让我么看看security_getenforce()的实现,通过全局搜索我们发现,该代码的位置在在external/selinux/libselinux/src/getenforce.c目录下:
int security_getenforce(void)
{
int fd, ret, enforce = 0;
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_RDONLY | O_CLOEXEC);
if (fd < 0)
return -1;
memset(buf, 0, sizeof buf);
ret = read(fd, buf, sizeof buf - 1);
close(fd);
if (ret < 0)
return -1;
if (sscanf(buf, "%d", &enforce) != 1)
return -1;
return !!enforce;
}
这里有个要注意点,sizeof path,这个不是错误的,因为这里的sizeof是一个运算符,这里的代码所以首先获取一个节点数值,Android改节点路为/sys/fs/selinux/enforce,这个值表示是否开启selinux,让我们手动cat一下该节点,然后查看一下值:
XXX:/ # cat /sys/fs/selinux/enforce
1
这里另一个判定条件是selinux_is_enforcing()函数。其定义如下:
enum selinux_enforcing_status {
SELINUX_PERMISSIVE, SELINUX_ENFORCING };
static selinux_enforcing_status selinux_status_from_cmdline() {
selinux_enforcing_status status = SELINUX_ENFORCING;
import_kernel_cmdline(false, [&](const std::string& key, const std::string& value, bool in_qemu) {
if (key == "androidboot.selinux" && value == "permissive") {
status = SELINUX_PERMISSIVE;
}
});
return status;
}
static bool selinux_is_enforcing(void)
{
if (ALLOW_PERMISSIVE_SELINUX) {
return selinux_status_from_cmdline() == SELINUX_ENFORCING;
}
return true;
}
此处从cmdline中获取androidboot.selinux的值,如果是permissive,则返回SELINUX_PERMISSIVE,即0,否则返回SELINUX_ENFORCING。如果enforce节点和cmdline设置不一致,则调用security_setenforce重新设置selinux的enforce节点值。 因此想要关闭selinux,可以直接将selinux_is_enforcing返回false,网上也有人从驱动返回,这个驱动这边不是很熟悉,所以就在这里直接注释掉从驱动获取的值直接返回false即可。
static bool selinux_is_enforcing(void)
{
return false;
}
最后重新编译boot.img,烧入固件这样重新启。启动完成后调用getenforce命令可以看到已经特地关闭SELinux了。
关于从驱动层返回修改的,可以参考下面的这篇博文: https://blog.csdn.net/zhangdaxia2/article/details/98243665
关于SELinxu都是些偏向实战类型的博客,所以也没有过多好说的,跟着规则实际操作几把就基本OK了。好了今天的博客正确姿势临时和永久开启关闭Android的SELinux到这里就结束了,各位青山不改绿水长流,江湖见!
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/153550.html原文链接:https://javaforall.cn