Skip to content

Public Private Policy

追踪 Android 17 SELinux public/private 策略从目录收集、CIL 导出、版本映射到 vendor 合并和编译失败的完整边界。

基于android-17.0.0_r1
AndroidSELinuxpublic policyprivate policyTrebleCIL源码阅读

Public Private Policy ​

本文面向已经读过 类型与属性、M4预处理、版本化策略 和 Treble与sepolicy 的读者。这里的 public 与 private 不是 Linux 目录权限,也不是源码是否公开,而是平台 SELinux policy 对非平台分区暴露的接口边界。

本文解决一个工程问题:新增 type、attribute 或 allow 规则时,应该进入哪个目录、哪一个 policy.conf/CIL,vendor 是否可以引用,平台升级后旧 vendor 如何继续编译。文章不重复完整 CIL 语法,只追踪 Android 17 的目录收集器、Soong policy graph、version_policy 和兼容性测试。

1. 边界模型 ​

1.1 三个集合 ​

集合输入主要消费者vendor能否依赖
platform privatesystem/sepolicy/privateplatform完整策略否
platform publicsystem/sepolicy/publicplatform、system_ext、product、vendor是
vendor policysystem/sepolicy/vendor、设备目录vendor/odm完整策略属于实现方

public 导出的是声明,不是可直接加载的最小策略。pub_policy.conf 还需要 reqd_mask 提供 checkpolicy 所需骨架,转成 CIL 后再 filter_out;plat_sepolicy.conf 则把 public 和 private 合并成平台策略。

1.2 放置决策 ​

新增声明前先问:

  1. 非平台 policy 是否必须引用这个名字?不需要时默认放 private。
  2. 这个名字是否成为跨分区 ABI?需要时放 public,并为 board API 建立 mapping/compat。

allow、neverallow、dontaudit 和 type_transition 是实现规则,应放 private 或 vendor。public 只保留 vendor 需要看到的 type、attribute 和必要 typeattribute。

2. 目录收集 ​

2.1 se_build_files ​

源码文件:system/sepolicy/build/soong/build_files.go

go
// se_build_files gathers policy files and exposes partition/scope tags.
func (b *buildFiles) GenerateAndroidBuildActions(ctx android.ModuleContext) {
    b.srcs = make(map[string]android.Paths)
    b.srcs[".reqd_mask"] = b.findSrcsInDirs(ctx,
        filepath.Join("system", "sepolicy", "reqd_mask"))
    b.srcs[".plat_public"] = b.findSrcsInDirs(ctx,
        filepath.Join("system", "sepolicy", "public"))
    b.srcs[".plat_private"] = b.findSrcsInDirs(ctx,
        filepath.Join("system", "sepolicy", "private"))
    b.srcs[".plat_vendor"] = b.findSrcsInDirs(ctx,
        filepath.Join("system", "sepolicy", "vendor"))
    b.srcs[".vendor"] = b.findSrcsInDirs(ctx,
        ctx.DeviceConfig().VendorSepolicyDirs()...)
    b.srcs[".odm"] = b.findSrcsInDirs(ctx,
        ctx.DeviceConfig().OdmSepolicyDirs()...)
    b.setOutputFiles(ctx)
}

源码文件:system/sepolicy/Android.bp

make
reqd_mask_policy = [":se_build_files{.reqd_mask}"]
plat_public_policy = [":se_build_files{.plat_public}"]
plat_private_policy = [":se_build_files{.plat_private}"]
system_ext_public_policy = [":se_build_files{.system_ext_public}"]
system_ext_private_policy = [":se_build_files{.system_ext_private}"]
product_public_policy = [":se_build_files{.product_public}"]
product_private_policy = [":se_build_files{.product_private}"]
vendor_policy = [
    ":se_build_files{.plat_vendor}",
    ":se_build_files{.vendor}",
    ":ashmem_build_file",
]

se_build_files 只是 filegroup。目录和 scope 在这个阶段已经决定可见性,但尚未执行 M4、checkpolicy 或 secilc。

2.2 声明和实现 ​

源码文件:system/sepolicy/public/keystore.te、system/sepolicy/private/keystore.te

text
type keystore, domain, keystore2_key_type;
type keystore_exec, system_file_type, exec_type, file_type;

下面切换到同一对象的 private 实现规则;两段代码的目录边界正是本文要建立的接口/实现分层。

text
typeattribute keystore coredomain;
init_daemon_domain(keystore)
hal_client_domain(keystore, hal_keymint)
allow keystore keystore2_key_contexts_file:file r_file_perms;
selinux_check_access(keystore)

public 声明使 vendor 能引用 keystore 这个 type;private 关联 coredomain、进程启动、文件读取和 SELinux 检查。将第二段放 public 会把平台实现权限变成跨分区接口。

3. Public CIL ​

3.1 reqd mask ​

源码文件:system/sepolicy/Android.bp

make
se_policy_conf {
    name: "reqd_policy_mask.conf",
    defaults: ["se_policy_conf_flags_defaults"],
    srcs: reqd_mask_policy,
    installable: false,
}
se_policy_cil {
    name: "reqd_policy_mask.cil",
    src: ":reqd_policy_mask.conf",
    secilc_check: false,
    installable: false,
}

源码文件:system/sepolicy/reqd_mask/reqd_mask.te

text
typeattribute domain mlstrustedsubject;
typeattribute file_type mlstrustedobject;

reqd mask 只帮助 public 声明通过语法编译,随后从导出的 CIL 过滤掉;它不是设备额外加载的授权策略。

3.2 public与platform ​

源码文件:system/sepolicy/Android.bp

make
se_policy_conf {
    name: "pub_policy.conf",
    defaults: ["se_policy_conf_flags_defaults"],
    srcs: plat_public_policy +
        system_ext_public_policy +
        product_public_policy +
        reqd_mask_policy,
    vendor: true,
    installable: false,
}
se_policy_cil {
    name: "pub_policy.cil",
    src: ":pub_policy.conf",
    filter_out: [":reqd_policy_mask.cil"],
    secilc_check: false,
    vendor: true,
    installable: false,
}
se_policy_conf {
    name: "plat_sepolicy.conf",
    defaults: ["se_policy_conf_flags_defaults"],
    srcs: plat_public_policy + plat_private_policy,
    installable: false,
}

pub_policy.conf 是非平台 policy 的联合 public 视图;plat_sepolicy.conf 是 platform 完整输入的一部分。两者的消费者和可见内容不同。

4. Vendor合并 ​

4.1 输入集合 ​

源码文件:system/sepolicy/Android.bp

make
se_policy_conf {
    name: "vendor_sepolicy.conf",
    defaults: ["se_policy_conf_flags_defaults"],
    srcs: plat_public_policy +
        system_ext_public_policy +
        product_public_policy +
        reqd_mask_policy +
        vendor_policy,
    vendor: true,
    installable: false,
}
se_policy_cil {
    name: "vendor_sepolicy.unversioned.cil",
    src: ":vendor_sepolicy.conf",
    filter_out: [":reqd_policy_mask.cil"],
    secilc_check: false,
    vendor: true,
    installable: false,
}

vendor policy 接收 platform/system_ext/product 的 public 和自身实现,不接收 platform private。vendor 依赖的 type 必须在 public 集合中出现。

4.2 versioned policy ​

源码文件:system/sepolicy/Android.bp

make
se_versioned_policy {
    name: "vendor_sepolicy.cil",
    base: ":pub_policy.cil",
    target_policy: ":vendor_sepolicy.unversioned.cil",
    version: "vendor",
    dependent_cils: [
        ":plat_sepolicy.cil",
        ":system_ext_sepolicy.cil",
        ":product_sepolicy.cil",
        ":plat_pub_versioned.cil",
        ":plat_mapping_file",
    ],
    filter_out: [":plat_pub_versioned.cil"],
    vendor: true,
}

源码文件:system/sepolicy/build/soong/versioned_policy.go

go
if proptools.Bool(m.properties.Mapping) {
    // Mapping mode exports the public base as a versioned interface.
    rule.Command().BuiltTool("version_policy").
        FlagWithInput("-b ", android.PathForModuleSrc(ctx, *m.properties.Base)).
        FlagWithArg("-n ", version).
        FlagWithOutput("-o ", out).
        Flag("-m")
} else if target := proptools.String(m.properties.Target_policy); target != "" {
    mapping := pathForModuleOut(ctx, stem+".mapping.cil")
    // Target mode attributes the target policy against the public base.
    rule.Command().BuiltTool("version_policy").
        FlagWithInput("-b ", android.PathForModuleSrc(ctx, *m.properties.Base)).
        FlagWithArg("-n ", version).
        FlagWithOutput("-o ", mapping).
        Flag("-m")
}

mapping=true 与 target_policy 互斥;前者生成 public mapping,后者把 target policy 映射到 base version。模块两者都没有设置也会失败,说明 versioned policy 不是普通复制规则。

4.3 mapping安装 ​

源码文件:system/sepolicy/Android.bp

make
se_versioned_policy {
    name: "plat_mapping_file",
    base: ":plat_pub_policy.cil",
    mapping: true,
    version: "current",
    relative_install_path: "mapping",
    dist: { targets: ["sepolicy_finalize"] },
}
se_versioned_policy {
    name: "plat_pub_versioned.cil",
    base: ":pub_policy.cil",
    target_policy: ":pub_policy.cil",
    version: "vendor",
    vendor: true,
}

system 分区的 mapping 供平台和兼容检查使用;vendor 分区的 plat_pub_versioned.cil 供非平台 policy 按 board version 引用。private type 不在 mapping API 中。

5. 规则位置 ​

5.1 public声明 ​

源码文件:system/sepolicy/public/attributes、system/sepolicy/public/file.te

text
attribute domain;
attribute file_type;
attribute service_manager_type;
attribute halclientdomain;

type system_file, fs_type, file_type;
type vendor_file, fs_type, vendor_file_type, file_type;
type activity_service, service_manager_type;
type wifi_service, service_manager_type;

5.2 private实现 ​

源码文件:system/sepolicy/private/system_server.te、system/sepolicy/private/domain.te

text
allow system_server activity_service:service_manager add;
allow system_server wifi_service:service_manager find;
allow system_server hal_audio_hwservice:hwservice_manager find;

neverallow { domain -system_server } *:keystore2_key use_dev_id;
neverallow * default_android_vndservice:service_manager *;

public 声明让 vendor 识别 target type;private allow/neverallow 决定 platform 的实际授权和约束。

5.3 vendor实现 ​

源码文件:system/sepolicy/vendor/hal_wifi_supplicant_default.te

text
type hal_wifi_supplicant_default, domain;
type hal_wifi_supplicant_default_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(hal_wifi_supplicant_default)

vendor 自己的 domain 不会自动成为 platform API。只有明确导出 public type/attribute 并经过 mapping/compat,才形成跨分区依赖。

6. 兼容与失败 ​

6.1 类型变更 ​

public type 一旦被 vendor 使用,就属于 board sepolicy API。新增类型必须在目标版本 mapping 中出现;删除或重命名也必须留下兼容关系。private type 没有同等承诺,但平台内部所有消费者必须同步修改。

源码文件:system/sepolicy/tests/treble_sepolicy_tests.py

python
def TestNoUnmappedNewTypes(base_pub_policy, old_pub_policy, mapping):
    ret = ""
    for n in base_pub_policy.types:
        if n not in old_pub_policy.types and mapping.rTypeattributesets.get(n) is None:
            # Newly exported types need a mapping entry.
            ret += "unmapped public type: %s\n" % n
    return ret

def TestNoUnmappedRmTypes(base_pub_policy, old_pub_policy, mapping):
    ret = ""
    for o in old_pub_policy.types:
        if o in mapping.pubtypes and o not in mapping.types:
            # Removed public types also need an explicit compatibility entry.
            ret += "removed public type without mapping: %s\n" % o
    return ret

输入是当前/旧 public policy 和 mapping;断言覆盖新增与删除类型,不覆盖 allow 最小权限或设备运行时 AVC。

6.2 典型失败 ​

现象真实边界首先检查
vendor 报 undefined typeprivate 声明未进入 vendor 输入public/vendor 文件组
mapping test 失败public API 新增/删除未登记versioned mapping/compat
secilc merge 失败dependent CIL 类型关系不一致base、target、mapping
运行时 AVCpolicy 已编译但 allow 不足最终加载策略和 audit 日志

vendor 引用 private type 时,复制 private 文件不是正确修复;应判断该名字是否真是跨分区契约。若不是,改用已有 public type;若是,新增 public 声明并建立兼容映射。

6.3 平台加载条件 ​

platform、system_ext 和 product 的预编译策略还要与对应 mapping 的 sha256 匹配;hash 不匹配时 init 不应直接使用旧的 precompiled policy,而会走重新加载/编译路径。这个条件属于加载阶段,不是 public/private 目录解析阶段。

7. 验证方法 ​

7.1 构建产物 ​

sh
# Locate generated policy inputs, not the source directories.
find out/soong/.intermediates/system/sepolicy \
  -name '*pub_policy*.conf' -o -name 'vendor_sepolicy*.cil'

# Check whether a symbol is visible in public and vendor inputs.
rg -n '\b<symbol>\b' system/sepolicy/public system/sepolicy/vendor \
  out/soong/.intermediates/system/sepolicy

第一条命令确认 filegroup 和 policy.conf 的实际输出;第二条把源码声明与生成 CIL 对照。它不能证明设备已经加载该策略。

7.2 兼容测试 ​

sh
# Run the Treble mapping checks for the selected policy version.
atest treble_sepolicy_tests

# Inspect version_policy invocation in the generated build graph.
rg -n 'version_policy|plat_mapping_file|vendor_sepolicy.cil' \
  out/soong/.intermediates/system/sepolicy

7.3 运行时分界 ​

设备上出现 AVC 时,先判断失败发生在编译前还是运行时:undefined type、mapping 缺失和 secilc 错误属于构建;scontext/tcontext/tclass/permission denial 属于已加载策略后的访问控制。两类问题不能用同一条 allow 规则修复。

8. 源码导航 ​

  1. system/sepolicy/build/soong/build_files.go:目录和 scope tag。
  2. system/sepolicy/Android.bp:policy.conf、CIL、vendor 合并和 mapping module。
  3. system/sepolicy/build/soong/versioned_policy.go:version_policy、filter_out、secilc 依赖。
  4. system/sepolicy/tools/version_policy.c:mapping 与 attributization。
  5. system/sepolicy/tests/treble_sepolicy_tests.py:新增/删除 public type 的反向断言。
  6. system/sepolicy/public、private、vendor:对照声明、实现和消费者。

用 keystore 做练习:public 查找 type 声明,private 查找 coredomain/allow,vendor policy 查找引用点,再判断新增能力应该是导出声明还是平台内部规则。