温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Android selinux策略文件怎么编译与加载

发布时间:2023-03-08 11:30:44 来源:亿速云 阅读:112 作者:iii 栏目:开发技术

本文小编为大家详细介绍“Android selinux策略文件怎么编译与加载”,内容详细,步骤清晰,细节处理妥当,希望这篇“Android selinux策略文件怎么编译与加载”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

    编译

    用法1:

    编译selinux

    make sepolicy -j48 或 make selinux_policy -j48

    把生成的文件 \out\target\product\XXXX\obj\ETC\vendor_sepolicy.cil_intermediates\vendor_sepolicy.cil push(out\target\product\XXXX\vendor\etc\selinux\vendor_sepolicy.cil)到 /vendor/etc/selinux 目录下

    注:当前以规则文件 vendor_sepolicy.cil 为例。

    • 备份后删除/odm/etc/selinux/precompiled_sepolicy文件

    • 重启手机后生效

    用法2(推荐):

    编译selinux

    make selinux_policy -j48

    make编译大约 3~17分钟 如果make编译过了,可使用ninja编译,不到一分钟就可以编译完成

    time prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-XXXX.ninja -j48 selinux_policy

    time prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-XXXX.ninja -j48 sepolicy.recovery

    • 备份/odm/etc/selinux/precompiled_sepolicy文件

    • 把生成的文件out\target\product\klein\odm\etc\selinux\precompiled_sepolicy,push到/odm/etc/selinux/目录下

    • 重启手机后生效

    注:App上下文seapp_contexts,文件上下文 file_contexts ,属性上下文 property_contexts 的修改生效方法,见文末

    原理简介:

    手机启动后,进入加载selinux政策流程,会使用/odm/etc/selinux/目录下的两个sha256文件中的值分别同/system/etc/selinux,/product/etc/selinux 目录的sha256文件中的值对比,如果都相等,则加载/odm/etc/selinux/目录下的/odm/etc/selinux/precompiled_sepolicy 预编译的selinux二进制政策文件。如果不同,则使用/system/etc/selinux 和 /vendor/etc/selinux 等目录下的文件重新编译 selinux二进制政策文件,然后加载新的 sepolicy 文件。

    1、selinux政策加载流程(加载sepolicy 二进制文件流程)

    函数调用流程:

    system/core/init/main.cpp

    ---> selinux.cpp ---> int SetupSelinux(char** argv)

    ---> SelinuxInitialize();

    ---> LoadPolicy()

    ---> LoadSplitPolicy()

    ---> FindPrecompiledSplitPolicy(std::string* file) // 关键函数

    函数调用流程讲解:

    开机启动时会加载selinux政策。main.cpp 中调用 selinux.cpp 中的 int SetupSelinux(char** argv) 函数,SetupSelinux 调用SelinuxInitialize(),SelinuxInitialize() 调用 LoadPolicy() 函数

    int SetupSelinux(char** argv) {
    	···
        // Set up SELinux, loading the SELinux policy.
        SelinuxSetupKernelLogging();
        SelinuxInitialize();
    	···
        return 1;
    }
    void SelinuxInitialize() {
        ···
        LOG(INFO) << "Loading SELinux policy";
        if (!LoadPolicy()) {
            LOG(FATAL) << "Unable to load SELinux policy";
        }
    	···
    }

    LoadPolicy 方法中会判断 /system/etc/selinux/plat_sepolicy.cil 文件是否存在,存在调用LoadSplitPolicy,不存在调用LoadMonolithicPolicy方法从根目录/sepolicy(此路径在 /external/selinux/libselinux/src/android/android_platform.c sepolicy_file变量写死)下加载selinux政策(这是以前的版本)

    【根据此处代码逻辑,make sepolicy 后,把生成的文件out\target\product\klein\root\sepolicy,push到根目录下,顺便删除/system/etc/selinux/plat_sepolicy.cil 文件

    应该也可以使新的selinux政策生效(经测试无法push到根目录:Read-only file system)】

    bool LoadPolicy() {
        return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy();
    }
    constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";
    bool IsSplitPolicyDevice() {
        return access(plat_policy_cil_file, R_OK) != -1;
    }
    bool LoadMonolithicPolicy() {
        LOG(VERBOSE) << "Loading SELinux policy from monolithic file";
        if (selinux_android_load_policy() < 0) {
            PLOG(ERROR) << "Failed to load monolithic SELinux policy";
            return false;
        }
        return true;
    }

    /external/selinux/libselinux/src/android/android_platform.c

    static const char *const sepolicy_file = "/sepolicy";
    int selinux_android_load_policy()
    {
    	int fd = -1;
    	fd = open(sepolicy_file, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
    	if (fd < 0) {
    		selinux_log(SELINUX_ERROR, "SELinux:  Could not open %s:  %s\n",
    				sepolicy_file, strerror(errno));
    		return -1;
    	}
    	int ret = selinux_android_load_policy_from_fd(fd, sepolicy_file);
    	close(fd);
    	return ret;
    }

    LoadSplitPolicy() 方法

    use_userdebug_policy 由环境变量 INIT_FORCE_DEBUGGABLE 决定,使用adb shell命令 echo $INIT_FORCE_DEBUGGABLE 查看此变量为空,所以 use_userdebug_policy == false

    代码进入 FindPrecompiledSplitPolicy方法.

    bool LoadSplitPolicy() {
        // IMPLEMENTATION NOTE: Split policy consists of three CIL files:
        // * platform -- policy needed due to logic contained in the system image,
        // * non-platform -- policy needed due to logic contained in the vendor image,
        // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
        //   with newer versions of platform policy.
        //
        // secilc is invoked to compile the above three policy files into a single monolithic policy
        // file. This file is then loaded into the kernel.
        // See if we need to load userdebug_plat_sepolicy.cil instead of plat_sepolicy.cil.
        const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
        // 可使用adb shell命令 echo $INIT_FORCE_DEBUGGABLE 查看此环境变量的值
        // 此变量为空,所以 use_userdebug_policy == false
        bool use_userdebug_policy =
                ((force_debuggable_env && "true"s == force_debuggable_env) &&
                 AvbHandle::IsDeviceUnlocked() && access(kDebugRamdiskSEPolicy, F_OK) == 0);
        if (use_userdebug_policy) {
            LOG(WARNING) << "Using userdebug system sepolicy";
        }
        // Load precompiled policy from vendor image, if a matching policy is found there. The policy
        // must match the platform policy on the system image.
        std::string precompiled_sepolicy_file;
        // use_userdebug_policy requires compiling sepolicy with userdebug_plat_sepolicy.cil.
        // Thus it cannot use the precompiled policy from vendor image.
        // 核心代码 !use_userdebug_policy == true ,进入 FindPrecompiledSplitPolicy 函数
        if (!use_userdebug_policy && FindPrecompiledSplitPolicy(&precompiled_sepolicy_file)) {
            unique_fd fd(open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
            if (fd != -1) {
                if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {
                    LOG(ERROR) << "Failed to load SELinux policy from " << precompiled_sepolicy_file;
                    return false;
                }
                return true;
            }
        }
        // No suitable precompiled policy could be loaded
        LOG(INFO) << "Compiling SELinux policy";
        // We store the output of the compilation on /dev because this is the most convenient tmpfs
        // storage mount available this early in the boot sequence.
        char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
        unique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));//创建临时文件
        if (compiled_sepolicy_fd < 0) {
            PLOG(ERROR) << "Failed to create temporary file " << compiled_sepolicy;
            return false;
        }
    	···
        unlink(compiled_sepolicy);// 临时文件如果不再被使用后,文件会被自动删除
        LOG(INFO) << "Loading compiled SELinux policy";
        // 编译完成,加载新的selinux文件
        if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) {
            LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy;
            return false;
        }
        return true;
    }

    FindPrecompiledSplitPolicy 此方法主要工作:

    /odm/etc/selinux/precompiled_sepolicy.plat_sepolicy_and_mapping.sha256

    /system/etc/selinux/plat_sepolicy_and_mapping.sha256

    对比两个文件中的sha值

    /odm/etc/selinux/precompiled_sepolicy.product_sepolicy_and_mapping.sha256

    /product/etc/selinux/product_sepolicy_and_mapping.sha256

    并且对比这两个文件中的sha值

    如果这两对文件中的值一致,FindPrecompiledSplitPolicy返回true,不一致返回false。

    如果/odm/etc/selinux/目录下没有precompiled_sepolicy文件,

    则会去/vendor/etc/selinux/目录下找相关的mapping.sha256去和system product 中的文件对比,

    (我们的手机没有/vendor/etc/selinux/precompiled_sepolicy文件)

    如果/vendor/etc/selinux/目录下也没有precompiled_sepolicy文件,

    则FindPrecompiledSplitPolicy返回 false。

    如果FindPrecompiledSplitPolicy返回true,加载预编译的政策

    FindPrecompiledSplitPolicy返回false,则重新编译selinux政策,完成后加载新政策

    【由此,可推测,修改/odm/etc/selinux/precompiled_sepolicy.plat_sepolicy_and_mapping.sha256文件中的值,

    或,修改/odm/etc/selinux/precompiled_sepolicy.product_sepolicy_and_mapping.sha256的值

    或,修改/system/etc/selinux/plat_sepolicy_and_mapping.sha256的值

    或,修改/product/etc/selinux/product_sepolicy_and_mapping.sha256的值

    或,删除/odm/etc/selinux/precompiled_sepolicy文件

    都能引起重启后,重新编译新的sepolicy文件,使新的selinux政策生效】

    bool FindPrecompiledSplitPolicy(std::string* file) {
        file->clear();
        // If there is an odm partition, precompiled_sepolicy will be in
        // odm/etc/selinux. Otherwise it will be in vendor/etc/selinux.
        static constexpr const char vendor_precompiled_sepolicy[] =
            "/vendor/etc/selinux/precompiled_sepolicy";
        static constexpr const char odm_precompiled_sepolicy[] =
            "/odm/etc/selinux/precompiled_sepolicy";
        if (access(odm_precompiled_sepolicy, R_OK) == 0) {
            *file = odm_precompiled_sepolicy;
        } else if (access(vendor_precompiled_sepolicy, R_OK) == 0) {
            *file = vendor_precompiled_sepolicy;
        } else {
            PLOG(INFO) << "No precompiled sepolicy";
            return false;
        }
        std::string actual_plat_id;
        if (!ReadFirstLine("/system/etc/selinux/plat_sepolicy_and_mapping.sha256", &actual_plat_id)) {
            PLOG(INFO) << "Failed to read "
                          "/system/etc/selinux/plat_sepolicy_and_mapping.sha256";
            return false;
        }
        std::string actual_product_id;
        if (!ReadFirstLine("/product/etc/selinux/product_sepolicy_and_mapping.sha256",
                           &actual_product_id)) {
            PLOG(INFO) << "Failed to read "
                          "/product/etc/selinux/product_sepolicy_and_mapping.sha256";
            return false;
        }
        std::string precompiled_plat_id;
        std::string precompiled_plat_sha256 = *file + ".plat_sepolicy_and_mapping.sha256";
        if (!ReadFirstLine(precompiled_plat_sha256.c_str(), &precompiled_plat_id)) {
            PLOG(INFO) << "Failed to read " << precompiled_plat_sha256;
            file->clear();
            return false;
        }
        std::string precompiled_product_id;
        std::string precompiled_product_sha256 = *file + ".product_sepolicy_and_mapping.sha256";
        if (!ReadFirstLine(precompiled_product_sha256.c_str(), &precompiled_product_id)) {
            PLOG(INFO) << "Failed to read " << precompiled_product_sha256;
            file->clear();
            return false;
        }
        // 核心代码
        if (actual_plat_id.empty() || actual_plat_id != precompiled_plat_id ||
            actual_product_id.empty() || actual_product_id != precompiled_product_id) {
            file->clear();
            return false;
        }
        return true;
    }

    2、综上使新的selinux政策生效的方法有:

    • 删除/system/etc/selinux/plat_sepolicy.cil 文件,make sepolicy 后,把生成的文件out\target\product\klein\root\sepolicy,push到根目录下 【无法push:Read-only file system】

    • make selinux_policy 后,把生成的文件out\target\product\klein\odm\etc\selinux\precompiled_sepolicy,push到/odm/etc/selinux/目录下 【已验证】

    • make selinux_policy 后,把生成的文件out\target\product\klein\odm\etc\selinux\precompiled_sepolicy,push到/vendor/etc/selinux/目录下,复制/odm/etc/selinux/目录下的两个sha文件到/vendor/etc/selinux/目录 【未验证】

    • 删除或修改/odm/etc/selinux/precompiled_sepolicy.plat_sepolicy_and_mapping.sha256 【已验证 删除方式】

    • 删除或修改/odm/etc/selinux/precompiled_sepolicy.product_sepolicy_and_mapping.sha256 【未验证】

    • 删除或修改**/system/etc/selinux/plat_sepolicy_and_mapping.sha256** 【已验证 修改方式】

    • 删除或修改/product/etc/selinux/product_sepolicy_and_mapping.sha256 【已验证 删除方式】

    • 删除/odm/etc/selinux/precompiled_sepolicy文件 【已验证】

    3、关于开机编译sepolicy文件

    开机时重新编译的sepolicy文件,会编译一个临时文件/dev/sepolicy.XXXXXX,新的selinux生效后,此文件会被删除。

    当前测试发现:开机时编译sepolicy文件会导致开机时间变长,并且每次开机都编译一次。

    有没有其他副作用?暂时未发现。

    4、开机 SELinux 相关 log

    1970-01-01 11:56:20.738 0-0/? I/SELinux: Initializing. 1970-01-01 11:56:31.265 0-0/? I/init: Loading SELinux policy 1970-01-01 11:56:31.271 0-0/? I/init: Compiling SELinux policy // log中出现此日志表明在编译新的selinux,selinux将会生效 1970-01-01 11:56:32.034 0-0/? I/init: Loading compiled SELinux policy1970-01-01 11:56:32.255 0-0/? I/SELinux: policy capability network_peer_controls=1 1970-01-01 11:56:32.255 0-0/? I/SELinux: policy capability open_perms=1 1970-01-01 11:56:32.255 0-0/? I/SELinux: policy capability extended_socket_class=1 1970-01-01 11:56:32.255 0-0/? I/SELinux: policy capability always_check_network=0 1970-01-01 11:56:32.255 0-0/? I/SELinux: policy capability cgroup_seclabel=0 1970-01-01 11:56:32.255 0-0/? I/SELinux: policy capability nnp_nosuid_transition=1 1970-01-01 11:56:32.461 0-0/? I/selinux: SELinux: Loaded policy from /dev/sepolicy.ys2KNm // 新的selinux生效,如果没有编译新的selinux,此处加载的是/odm/etc/selinux/precompiled_sepolicy 1970-01-01 11:56:32.467 0-0/? W/selinux: SELinux: Skipping /product/etc/selinux/product_file_contexts: empty file 1970-01-01 11:56:32.467 0-0/? I/selinux: SELinux: Loaded file_contexts 1970-01-01 11:56:32.524 0-0/? W/selinux: SELinux: Skipping /product/etc/selinux/product_file_contexts: empty file 1970-01-01 11:56:32.524 0-0/? I/selinux: SELinux: Loaded file_contexts

    其他技巧

    • App的上下文 seapp_contexts 文件,可以直接修改,push手机上。重启后生效。

    • 属性的上下文 property_contexts 文件, 可以直接修改,push手机上。重启后生效。

    • 服务的上下文 service_contexts 文件,可以直接修改,push手机上。重启后生效。

    • 虚拟文件上下文 genfs_contexts 文件,通过chcon命令进行修改

    • 讲下文件上下文 file_contexts 文件的修改与测试。restorecon 命令可使文件上下文selinux政策生效。 restorecon -R

    chcon : 随意修改某个文件(夹)的selinux lable。Ex: chcon u:object_r:system_data_file:s0 /data/app

    restorecon : 依照sepolicy Rule中定义的规则,重新relable指定的文件(夹)。

    修改 /system/bin/toybox 上下文示例:把junkserver的上下文修改为 shell_exec

    • 首先修改 /system/etc/selinux/plat_file_contexts 文件内容

    • 把 /system/bin/toybox u:object_r:toolbox_exec:s0

    • 修改为: /system/bin/toybox u:object_r:shell_exec:s0

    进入手机shell 执行以下命令

    # restorecon 命令需要跟参数,无法执行单个命令
    mobius:/ # restorecon system/bin/toybox -v
    SELinux:  Skipping /product/etc/selinux/product_file_contexts:  empty file
    SELinux: Loaded file_contexts
    SELinux:  Relabeling /system/bin/toybox from u:object_r:toolbox_exec:s0 to u:object_r:shell_exec:s0.
    • 可以看到文件上下文生效了

    mobius:/ # ls system/bin/toybox -lZ
    -rwxrwxrwx 1 root shell u:object_r:shell_exec:s0 432976 2009-01-01 08:00 system/bin/toybox

    chcon

    # android
    chcon  <安全上下文> 文件
    chcon -R  <安全上下文> 目录
    # 示例
    klein:/ # chcon -v u:object_r:junkserverd_d_file:s0  /data/junk-server/junk.txt
    chcon '/data/junk-server/junk.txt' to u:object_r:junkserverd_d_file:s0
    # 
    chcon -R -v u:object_r:system_data_file:s0 ./0
    # linux:
    chcon -t <安全上下文> 文件
    chcon -R -t <安全上下文> 目录

    读到这里,这篇“Android selinux策略文件怎么编译与加载”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注亿速云行业资讯频道。

    向AI问一下细节

    免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

    AI