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 private | system/sepolicy/private | platform完整策略 | 否 |
| platform public | system/sepolicy/public | platform、system_ext、product、vendor | 是 |
| vendor policy | system/sepolicy/vendor、设备目录 | vendor/odm完整策略 | 属于实现方 |
public 导出的是声明,不是可直接加载的最小策略。pub_policy.conf 还需要 reqd_mask 提供 checkpolicy 所需骨架,转成 CIL 后再 filter_out;plat_sepolicy.conf 则把 public 和 private 合并成平台策略。
1.2 放置决策
新增声明前先问:
- 非平台 policy 是否必须引用这个名字?不需要时默认放 private。
- 这个名字是否成为跨分区 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
// 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
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
type keystore, domain, keystore2_key_type;
type keystore_exec, system_file_type, exec_type, file_type;下面切换到同一对象的 private 实现规则;两段代码的目录边界正是本文要建立的接口/实现分层。
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
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
typeattribute domain mlstrustedsubject;
typeattribute file_type mlstrustedobject;reqd mask 只帮助 public 声明通过语法编译,随后从导出的 CIL 过滤掉;它不是设备额外加载的授权策略。
3.2 public与platform
源码文件:system/sepolicy/Android.bp
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
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
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
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
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
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
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
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
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 type | private 声明未进入 vendor 输入 | public/vendor 文件组 |
| mapping test 失败 | public API 新增/删除未登记 | versioned mapping/compat |
| secilc merge 失败 | dependent CIL 类型关系不一致 | base、target、mapping |
| 运行时 AVC | policy 已编译但 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 构建产物
# 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 兼容测试
# 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/sepolicy7.3 运行时分界
设备上出现 AVC 时,先判断失败发生在编译前还是运行时:undefined type、mapping 缺失和 secilc 错误属于构建;scontext/tcontext/tclass/permission denial 属于已加载策略后的访问控制。两类问题不能用同一条 allow 规则修复。
8. 源码导航
system/sepolicy/build/soong/build_files.go:目录和 scope tag。system/sepolicy/Android.bp:policy.conf、CIL、vendor 合并和 mapping module。system/sepolicy/build/soong/versioned_policy.go:version_policy、filter_out、secilc 依赖。system/sepolicy/tools/version_policy.c:mapping 与 attributization。system/sepolicy/tests/treble_sepolicy_tests.py:新增/删除 public type 的反向断言。system/sepolicy/public、private、vendor:对照声明、实现和消费者。
用 keystore 做练习:public 查找 type 声明,private 查找 coredomain/allow,vendor policy 查找引用点,再判断新增能力应该是导出声明还是平台内部规则。
