Android SELinux——工作模式(二)

        通过上一篇文章我们对 SELinux 有一初步的了解,这里我们主要来看一下 SELinux 中的工作模式。

一、模式介绍

        SELinux 提供了三种不同的工作模式,每种模式都有其特定的目的和使用场景。这里我们就来介绍这三种模式的使用场景。

1、Disabled(禁用模式)

        在 Disabled 模式下,SELinux 完全不工作,系统退回到传统的自主访问控制(DAC)机制。此模式下,SELinux 不会对系统中的任何操作进行干预或记录。当系统管理员决定完全禁用 SELinux 时使用此模式,通常是为了简化系统管理或解决特定的兼容性问题。

2、Permissive(宽容模式)

        在 Permissive 模式下,SELinux 被激活,但不会强制执行安全策略。相反,它会记录所有违反安全策略的尝试,并将其记录在日志中。此模式主要用于测试和调试目的,帮助系统管理员了解 SELinux 如何影响系统行为,以及在完全启用 SELinux 之前识别和解决潜在问题。当系统管理员希望监控 SELinux 的行为但又不想立即启用严格的访问控制时,可以使用此模式。

3、Enforcing(强制模式)

        在 Enforcing 模式下,SELinux 完全启用,并且会强制执行所有的安全策略。此模式下,任何违反安全策略的操作都会被阻止,并且系统会记录相应的 AVC 日志。这是 SELinux 的默认模式,适用于生产环境,以确保系统的安全性。在此模式下,系统会严格执行 SELinux 安全策略,防止未经授权的访问。 

        通过这些不同的工作模式,系统管理员可以根据实际需要选择最适合当前环境的 SELinux 设置,既可以保证系统的安全性,又能灵活应对不同的需求。所以我们通常在开发中使用的都是Permissive(宽容模式),记录问题但有未启用访问控制。而正是上线的时候为Enforcing(强制模式)。

二、模式切换

1、ADB命令设置

查看工作模式

进入到 shell:adb shell

查看工作模式:getenforce

        首先进入到 shell 下,使用 getenforce 命令来查看当前 SELinux 的工作模式(或者直接使用 adb shell getenforce 命令查看 SELinux 的工作模式),输出内容:Enforcing 或 Permissive。

切换工作模式

获取 root 权限:adb root

进入到 shell:adb shell

切换到 Permissive 模式:setenforce 0

切换到 Enforcing 模式:setenforce 1

        使用 setenforce [0|1] 来切换 SELinux 的工作模式(0 表示 Permissive,1 表示 Enforcing)。注意这里一定要获取 root 权限,否则会出现如下错误:

setenforce: Couldn't set enforcing status to '0': Permission denied

2、fastboot模式设置      

        除此通过 ADB 命令设置模式之外还可以进入 fastboot 模式对工作模式的 bootargs 参数进行设置。

fastboot setenv

# 设置 androidboot.selinux 参数

$ fastboot setenv bootargs  androidboot.selinux = permissive/enforcing
# 保存参数
$ fastboot flash bootconfig bootconfig 
# 重启系统
$ fastboot reboot

fastboot oem

# 设置 androidboot.selinux 参数

$ fastboot oem setenv "androidboot.selinux = permissive/enforcing
# 保存参数
$ fastboot oem saveenv
# 重启系统
$ fastboot reboot

        对于使用 fastboot 命令更改工作模式需要设备支持,不同的设备和厂商可能有不同的命令格式。这里实际操作均设置失败,fastboot setenv 命令提示 fastboot: usage: unknown command setenv。fastboot oem 命令提示 FAILED (remote: unknown command)。

3、配置文件设置

        其实上面的命令都是在 Permissive 和 Enforcing 模式之间进行切换,无法设置成 Disabled 模式。所以 SELinux 的默认模式也可以通过配置文件进行设置。

config.xml

        修改 /etc/selinux/config.xml 文件中的 SELINUX 选项来设定,可以选择 Disabled、Permissive 或 Enforcing。编辑配置文件 /etc/selinux/config.xml,把SELINUX=disabled,然后重启系统,SELinux 就被禁用了。

        某些 Android 设备可能会有特定的 SELinux 配置文件,这些文件可能位于不同的路径,例如 /vendor/etc/selinux、/system/etc/selinux、/vendor/etc/security 或 /system/etc/security 。当然也有不使用 config.xml 配置文件的情况,这些都需要根据项目实际情况设定。

系统属性

        如果项目中并没有找到上面的 config.xml 配置文件,可以通过如下命令确认是否使用了系统属性设置了 SELinux 的工作模式。

进入 shell 模式:adb shell

查找相关属性:getprop | grep "selinux"

        这里在 shell 模式下,通过 getprop 查找 "selinux" 关键字的属性内容,可以看到如下输出内容:

[androidboot.selinux]: [enforcing]

        可以看到这个属性其实就是上面通过 fastboot 修改的对应属性值,androidboot.selinux 属性是一个特殊的内核启动参数,它在设备启动时被内核读取,并不是普通的系统属性,所以也不能通过 /system/build.prop 文件进行修改。

  • androidboot.selinux 名称由厂商自定义设置,可能与这里的名称并不相同。
  • /system/build.prop 文件主要用于定义构建信息和其他非启动时的系统属性。它可以用来设置诸如设备型号、制造商、构建版本等信息。

4、代码修改

源码位置:/system/core/init/selinux.cpp

SelinuxInitialize

void SelinuxInitialize() {
    LOG(INFO) << "Loading SELinux policy";
    // 加载 SELinux 策略
    if (!LoadPolicy()) {
        LOG(FATAL) << "Unable to load SELinux policy";
    }
  
    // 获取内核当前的 SELinux 模式
    bool kernel_enforcing = (security_getenforce() == 1);
    // 获取期望的 SELinux 模式
    bool is_enforcing = IsEnforcing();
    // 如果两者不一致,则尝试设置 SELinux 模式
    if (kernel_enforcing != is_enforcing) {
        if (security_setenforce(is_enforcing)) {
            PLOG(FATAL) << "security_setenforce(" << (is_enforcing ? "true" : "false") << ") failed";
        }
    }
  
    // 将 checkreqprot 设置为 0,关闭 SELinux 请求保护功能
    if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result.ok()) {
        LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();
    }
}

        函数用于初始化 SELinux 并确保其工作模式符合预期。这里我们是不是直接修改期望的 SELinux 模式就可以实现工作模式的修改。在修改之前,我们先来看一下这里为什么要关闭 SELinux 请求保护功能(checkreqprot)。

checkreqprot

        SELinux 请求保护功能(checkreqprot)主要用于记录 SELinux 策略违规(avc denials)。当 SELinux 处于 enforcing 模式时,如果某个进程请求了不允许的操作,SELinux 会拒绝该操作并记录一条违规日志。

默认情况下:checkreqprot 的值为 1,表示启用请求保护功能。
启用时:SELinux 会记录所有违规行为,并将这些违规行为记录到日志中。
禁用时:SELinux 不会记录违规行为,只会在 enforcing 模式下直接拒绝违规操作。

        关闭 checkreqprot 的原因:

  • 性能考虑:当 checkreqprot 为 1 时,SELinux 会记录大量的违规日志,这可能会导致较高的系统开销。关闭 checkreqprot 可以减少系统日志记录的开销,提高系统性能。
  • 安全性考虑:在生产环境中,通常希望 SELinux 严格控制违规行为,而不记录过多的日志。
  • 在开发和调试阶段,可能会开启 checkreqprot 以便更好地了解违规情况,但在生产环境中通常不需要这些详细的日志记录。
  • 系统稳定性:大量的日志记录可能会导致日志文件迅速增长,占用大量磁盘空间。关闭 checkreqprot 可以避免这种情况,保持系统的稳定性和高效性。

        关闭 checkreqprot 功能的原因主要是为了提高系统性能和稳定性。在生产环境中,通常不需要记录大量的 SELinux 违规日志,因此关闭 checkreqprot 是一个常见的做法。 

IsEnforcing

bool IsEnforcing() {
    // 是否允许 SELinux 处于 permissive 模式
    if (ALLOW_PERMISSIVE_SELINUX) {
        return StatusFromCmdline() == SELINUX_ENFORCING;
    }
    return true;
}

        该函数的主要作用是根据配置和命令行参数来决定 SELinux 是否应该处于 enforcing 模式。这种设计使得 SELinux 的模式可以根据不同的配置和命令行参数灵活调整,适用于不同的场景。 

enum EnforcingStatus { SELINUX_PERMISSIVE, SELINUX_ENFORCING };

EnforcingStatus StatusFromCmdline() {
    EnforcingStatus status = SELINUX_ENFORCING;

    ImportKernelCmdline([&](const std::string& key, const std::string& value) {
        if (key == "androidboot.selinux" && value == "permissive") {
            status = SELINUX_PERMISSIVE;
        }
    });

    return status;
}

        该函数通常用于解析内核命令行参数,并执行相应的处理。可以看到这里还是根据上面的 androidboot.selinux 属性进行 SELinux 的工作模式初始化,如果我们在这里修改,可以直接修改 IsEnforcing() 函数的返回值即可。

bool IsEnforcing() {
    // 关闭SELinux
    return false;
}

        这里通过直接修改 IsEnforcing() 函数的返回值来修改 SELinux 的工作模式。接下来我们回过头来看一下 ALLOW_PERMISSIVE_SELINUX 参数的设置。

ALLOW_PERMISSIVE_SELINUX

源码位置:/system/core/init/Android.bp

cc_defaults {
	cflags: [
		"-DALLOW_PERMISSIVE_SELINUX=0",
        ……
	],
	product_variables: {
		debuggable: {
			cppflags: [
				"-UALLOW_PERMISSIVE_SELINUX",
				"-DALLOW_PERMISSIVE_SELINUX=1",
                ……
			],
            ……
		},
        ……
	},
    ……
}

        首先在 C 和 C++ 中,预处理器宏定义通常使用 -D 开头。这里的 -D 是一个编译器选项,用于定义预处理器宏。

        在 Android.bp 文件中,cc_defaults 用于设置默认的编译选项,这里 cc_defaults 通过 cflags 和 product_variables 来定义不同的编译配置。具体来说,-DALLOW_PERMISSIVE_SELINUX=0 和 product_variables 中的条件定义使得编译行为可以根据不同的产品变量进行调整。

  • 默认编译选项 (cflags):"-DALLOW_PERMISSIVE_SELINUX=0":默认情况下,定义 ALLOW_PERMISSIVE_SELINUX 为 0。
  • 产品变量 (product_variables):debuggable 变量。

         1) "-UALLOW_PERMISSIVE_SELINUX":取消定义 ALLOW_PERMISSIVE_SELINUX。
         2)"-DALLOW_PERMISSIVE_SELINUX=1":重新定义 ALLOW_PERMISSIVE_SELINUX 为 1。

        这样非调试版本 (debuggable=false) 时,只有默认的编译选项生效,因此,编译时 ALLOW_PERMISSIVE_SELINUX 的值为 0。调试版本 (debuggable=true),在这种情况下,product_variables 中的配置生效,首先,-UALLOW_PERMISSIVE_SELINUX 取消定义 ALLOW_PERMISSIVE_SELINUX,然后 "-DALLOW_PERMISSIVE_SELINUX=1" 重新定义 ALLOW_PERMISSIVE_SELINUX 为 1。

        这种方式使得编译行为可以根据不同的产品变量进行灵活调整,从而适应不同的开发和部署环境。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

c小旭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值