策略语法工作台
本文是策略语言组的收束篇,面向已经读过 TE规则语法、Type与Attribute、Type Transition、TE宏库、Global Macros、M4预处理、Neverallow、Constrain约束 和 Boolean条件策略 的读者。
这不是九篇文章的缩略目录。本文把知识重新按开发任务组织:需求改变的是 type membership、operation allow、对象转换、名称映射、构建输入还是运行时 Context;一行宏展开后由哪个内核 hook 或用户空间 object manager 消费;为什么规则存在仍可能被 label、neverallow、MLS、xperm 或条件构建挡住;最后如何用源码搜索、m4、binary policy 与设备标签建立闭环。
1. 先定问题层
1.1 六类变化
写策略前先确定要改变哪类状态。不同语句解决的是不同问题,不能用更多 allow 代替缺失的 type、transition 或 Context 映射。
| 需求 | 状态 owner | 首选构造 | 运行消费者 |
|---|---|---|---|
| 定义新主体/客体 | policy symbol table | type、attribute、typeattribute | policydb type/attribute bitmap |
| 允许已有对象操作 | access vector | allow、permission set | AVC、LSM hook、object manager |
| 选择新进程/对象标签 | SID transition | type_transition、transition macro | exec/create hook、security_compute_sid |
| 把名字映射到type | contexts 数据 | file/service/property/hwservice contexts | label lookup、ServiceManager、Property Service |
| 按产品裁剪规则 | build input | m4/Soong flag | m4 输出的 policy.conf |
| 对候选allow再限缩 | 构建或运行约束 | neverallow、mlsconstrain、xperm | checkpolicy/secilc、security server |
1.2 选择入口
1.3 生效顺序
.te 文件中规则的前后顺序通常不表示运行时先后。真正顺序由 operation 决定:exec hook 先算 new SID 再检查 transition/entrypoint;文件创建先查父目录、再算标签、再查 create/associate;ServiceManager 先按名字找 target Context,再查 add/find;Property Service 先解析 property Context,再查 set 和 value type。
2. 规则生命周期
2.1 构建输入
Soong 将宏、class、attributes 与 .te 按固定顺序交给 m4。宏调用在 policy.conf 中变成普通规则。
源码文件:system/sepolicy/build/soong/policy.go
var policyConfOrder = []string{
"flagging_macros",
"security_classes",
"initial_sids",
"access_vectors",
"global_macros",
"neverallow_macros",
"mls_macros",
"mls_decl",
"mls",
"policy_capabilities",
"te_macros",
"ioctl_defines",
"ioctl_macros",
"nlmsg_defines",
"nlmsg_macros",
"attributes|*.te",
"roles_decl",
"roles",
"users",
"initial_sid_contexts",
"fs_use",
"genfs_contexts",
"port_contexts",
}2.2 编译与加载
M4 输出交给 checkpolicy 生成 CIL,再由 secilc 合并为 binary policy。Kernel load 后建立 type maps、avtabs、constraints、sidtab 和 class mapping。
2.3 Access决策
运行时 security server 先查询普通和 conditional TE rules,再清除不满足 constraint、role 或 type bounds 的 permission。
源码文件:kernel/common/security/selinux/ss/services.c
/* Attribute expansion and avkey setup omitted. */
for (node = avtab_search_node(&policydb->te_avtab, &avkey);
node;
node = avtab_search_node_next(node, avkey.specified)) {
if (node->key.specified == AVTAB_ALLOWED)
avd->allowed |= node->datum.u.data;
else if (node->key.specified == AVTAB_AUDITALLOW)
avd->auditallow |= node->datum.u.data;
else if (node->key.specified == AVTAB_AUDITDENY)
avd->auditdeny &= node->datum.u.data;
}
cond_compute_av(&policydb->te_cond_avtab, &avkey, avd, xperms);
/* Unrelated decision stages omitted. */
constraint = tclass_datum->constraints;
while (constraint) {
if ((constraint->permissions & avd->allowed) &&
!constraint_expr_eval(policydb, scontext, tcontext, NULL,
constraint->expr))
avd->allowed &= ~constraint->permissions;
constraint = constraint->next;
}Android 当前没有 policy boolean,但 conditional compatibility 仍在内核;MLS constraints 则真实参与每次相关 AV 计算。
3. MediaTuner闭环
media.tuner 是理解策略语言分层的好案例:业务层通过 property 启用 service,SELinux policy 始终静态存在;init 执行带 _exec 标签的 binary 触发 domain transition;daemon 注册 Binder 服务、查找其他服务、使用 HAL 和读取 properties;neverallow 再限制执行与 socket ioctl 边界。
3.1 业务启用
Service 默认 disabled,tuner.server.enable=true 时 init 只改变服务生命周期,不修改 SELinux policy。
源码文件:frameworks/av/services/tuner/mediatuner.rc
# media.tuner service is not started by default unless tuner.server.enable is set
service media.tuner /system/bin/mediatuner
class main
group media
user root
ioprio rt 4
task_profiles ProcessCapacityHigh HighPerformance
interface aidl media.tuner
oneshot
disabled
on property:tuner.server.enable=true
enable media.tunerProperty trigger 是 init 业务条件。它不会像 SELinux boolean 那样启停 conditional avtab,也不会生成 allow。
3.2 Executable标签
文件路径被映射到 mediatuner_exec,为 exec transition 提供 target type。
源码文件:system/sepolicy/private/file_contexts
/system/bin/mediatuner u:object_r:mediatuner_exec:s0源码文件:system/sepolicy/private/mediatuner.te
type mediatuner, domain;
type mediatuner_exec, system_file_type, exec_type, file_type;
typeattribute mediatuner coredomain;
init_daemon_domain(mediatuner)domain 表示进程 domain,exec_type/file_type/system_file_type 描述 executable。缺少 file contexts 时 binary 可能落到普通 system file type,宏存在也不会命中 transition key。
3.3 Transition展开
init_daemon_domain(mediatuner) 递归展开为执行权限、process transition、entrypoint 和自动 type transition。
源码文件:system/sepolicy/public/te_macros
define(`init_daemon_domain', `
domain_auto_trans(init, $1_exec, $1)
')
define(`domain_auto_trans', `
domain_trans($1,$2,$3)
type_transition $1 $2:process $3;
')对应的 exec hook 先计算 new SID,SID 变化时再检查 source→new process transition 与 new domain→executable entrypoint。
源码文件:kernel/common/security/selinux/hooks.c
rc = security_transition_sid(old_crsec->sid,
isec->sid, SECCLASS_PROCESS, NULL,
&new_crsec->sid);
if (rc)
return rc;
/* NNP/nosuid fallback and audit-data setup omitted. */
if (new_crsec->sid == old_crsec->sid) {
rc = avc_has_perm(old_crsec->sid, isec->sid, isec->sclass,
FILE__EXECUTE_NO_TRANS, &ad);
} else {
rc = avc_has_perm(old_crsec->sid, new_crsec->sid,
SECCLASS_PROCESS, PROCESS__TRANSITION, &ad);
if (!rc)
rc = avc_has_perm(new_crsec->sid, isec->sid,
isec->sclass, FILE__ENTRYPOINT, &ad);
}3.4 Service名称
Binder service name media.tuner 映射到 mediatuner_service。这是 ServiceManager add/find 的 target type,不是 daemon process type。
源码文件:system/sepolicy/private/service_contexts
media.tuner u:object_r:mediatuner_service:s0源码文件:system/sepolicy/private/mediatuner.te
binder_use(mediatuner)
binder_call(mediatuner, appdomain)
binder_service(mediatuner)
add_service(mediatuner, mediatuner_service)
allow mediatuner tv_tuner_resource_mgr_service:service_manager find;
allow mediatuner package_native_service:service_manager find;
binder_call(mediatuner, system_server)add_service 决定谁能注册该 service type,binder_call 决定 transaction/transfer/fd,二者不能替代。
3.5 名称检查
ServiceManager 用调用 SID 与 service name 查 target Context,再执行 service_manager add/find。
源码文件:frameworks/native/cmds/servicemanager/Access.cpp
bool Access::canFind(const CallingContext& ctx,const std::string& name) {
return actionAllowedFromLookup(ctx, name, "find");
}
bool Access::canAdd(const CallingContext& ctx, const std::string& name) {
return actionAllowedFromLookup(ctx, name, "add");
}
bool Access::actionAllowedFromLookup(const CallingContext& sctx,
const std::string& name,
const char *perm) {
#ifdef __ANDROID__
char *tctx = nullptr;
if (selabel_lookup(getSehandle(), &tctx, name.c_str(),
SELABEL_CTX_ANDROID_SERVICE) != 0) {
LOG(ERROR) << "SELinux: No match for " << name
<< " in service_contexts.\n";
return false;
}
bool allowed = actionAllowed(sctx, tctx, perm, name);
freecon(tctx);
return allowed;
#else
(void)sctx;
(void)name;
(void)perm;
(void)kIsVendor;
return true;
#endif
}名字没有映射时,新增 add allow 也不会让注册成功。
3.6 Property读取
Daemon 读取两类 property,宏只展开 property file 的 getattr/open/read/map。
源码文件:system/sepolicy/private/mediatuner.te
get_prop(mediatuner, tuner_config_prop)
get_prop(mediatuner, tuner_server_ctl_prop)源码文件:system/sepolicy/public/te_macros
define(`get_prop', `
allow $1 $2:file { getattr open read map };
')读取 property 与设置 tuner.server.enable 是不同主体和 operation;这里没有 property_service set。
3.7 HAL角色
hal_client_domain(mediatuner, hal_tv_tuner) 只建立 client attribute 与 memfd 规则,具体 service find 来自 HAL 专属策略。
源码文件:system/sepolicy/private/mediatuner.te
hal_client_domain(mediatuner, hal_tv_tuner)不能从宏名推导“自动获得所有 tuner HAL 权限”;应继续搜索 hal_tv_tuner_client 的规则消费者。
3.8 负向边界
Daemon 禁止执行任何 executable 而不发生 transition,并禁止 privileged socket ioctl commands。
源码文件:system/sepolicy/private/mediatuner.te
neverallow mediatuner { file_type fs_type }:file execute_no_trans;
neverallowxperm mediatuner domain:{ rawip_socket tcp_socket udp_socket }
ioctl priv_sock_ioctls;这些规则说明“能启动 daemon”并不等于 daemon 可以继续 exec 任意工具或使用任意 socket ioctl。
4. 文件操作表
4.1 读取已有文件
最少需要沿路径目录的 search,以及目标 file 的实际 operation。r_file_perms 是常用集合,但包含 ioctl、lock、map、watch,并不总是最小授权。
源码文件:system/sepolicy/public/global_macros
define(`r_file_perms', `{ getattr open read ioctl lock map watch watch_reads }')
define(`r_dir_perms', `{ open getattr read search ioctl lock watch watch_reads }')| 真实需求 | 常见规则 | 还要检查 |
|---|---|---|
| 已知路径读取 | parent dir search + file open/read/getattr | 每级目录 label |
| mmap 配置 | file map/read | shared write/execute protection |
| 使用别人传入的 fd | file object permission + fd use | 不一定需要 path open |
| 设备 ioctl | ioctl + allowxperm | command allowlist |
4.2 创建文件
创建需要父目录、new object 和 filesystem 三层检查。create_file_perms 只针对 new object,不能替代父目录 search/add_name。
源码文件:kernel/common/security/selinux/hooks.c
rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR,
DIR__ADD_NAME | DIR__SEARCH, &ad);
if (rc)
return rc;
rc = selinux_determine_inode_label(crsec, dir, &dentry->d_name,
tclass, &newsid);
if (rc)
return rc;
rc = avc_has_perm(sid, newsid, tclass, FILE__CREATE, &ad);
if (rc)
return rc;
return avc_has_perm(newsid, sbsec->sid,
SECCLASS_FILESYSTEM,
FILESYSTEM__ASSOCIATE, &ad);Android 策略通常把目录和文件规则成对书写。
源码文件:system/sepolicy/private/system_server.te
allow system_server system_data_file:dir create_dir_perms;
allow system_server system_data_file:notdevfile_class_set create_file_perms;
allow system_server packages_list_file:file create_file_perms;4.3 创建后的标签
如果结果 type 与预期不符,先检查 type_transition source parent:class new_type [name],再检查父目录实际 SID 与 object class。仅添加 create allow 可能让错误标签对象创建成功。
5. 进程启动表
5.1 三个对象
进程启动至少涉及 old domain、executable file type 和 new domain。domain_auto_trans(old, exec_type, new) 同时补齐规则,但 file context 仍负责把路径标成 exec type。
| 输入 | 示例 | Owner |
|---|---|---|
| old domain | init | 当前 task credentials |
| executable type | mediatuner_exec | inode security/xattr |
| new domain | mediatuner | transition SID 结果 |
5.2 两种执行结果
SID 未变化时需要 execute_no_trans;SID 变化时需要 process transition 与 executable entrypoint。不要同时随意授予两条路径。
5.3 启动失败顺序
- 路径不存在或 init service 未启用;
- Executable label 错误,transition key 不命中;
- Source 无 execute;
- New domain 缺 process transition;
- New domain 对 executable 缺 entrypoint;
- MLS/role/NNP/nosuid 等后续条件拒绝。
6. Binder与服务
6.1 三个权限层
| 层次 | 规则 | 消费者 |
|---|---|---|
| 名字发现/注册 | service_manager find/add | ServiceManager Access |
| Binder transaction | binder call/transfer | SELinux Binder hooks |
| FD传递 | fd use + inode operation | Binder file-transfer hook |
binder_call(client, server) 只生成后两层,不包含 service find。
源码文件:system/sepolicy/public/te_macros
define(`binder_call', `
allow $1 $2:binder { call transfer };
allow $2 $1:binder transfer;
allow $1 $2:fd use;
')6.2 Binder Hook
源码文件:kernel/common/security/selinux/hooks.c
static int selinux_binder_transaction(const struct cred *from,
const struct cred *to)
{
u32 mysid = current_sid();
u32 fromsid = cred_sid(from);
u32 tosid = cred_sid(to);
int rc;
if (mysid != fromsid) {
rc = avc_has_perm(mysid, fromsid, SECCLASS_BINDER,
BINDER__IMPERSONATE, NULL);
if (rc)
return rc;
}
return avc_has_perm(fromsid, tosid,
SECCLASS_BINDER, BINDER__CALL, NULL);
}6.3 服务独占
add_service 同时生成 allow 与 owner neverallow。换 owner 不是增加第二条 add allow,而是重新设计 service type 所有权。
7. Property路径
7.1 Read与Set
get_prop 读取共享 property area 对应 file;set_prop 额外连接 Property Service 并请求 property_service set。
源码文件:system/sepolicy/public/te_macros
define(`set_prop', `
unix_socket_connect($1, property, init)
allow $1 $2:property_service set;
get_prop($1, $2)
')
define(`get_prop', `
allow $1 $2:file { getattr open read map };
')7.2 用户空间消费者
Property Service 根据 property name lookup target Context,再检查 property_service set;MAC 通过后还要验证 value type。
源码文件:system/core/init/property_service.cpp
const char* target_context = nullptr;
const char* type = nullptr;
property_info_area->GetPropertyInfo(name.c_str(),
&target_context, &type);
if (!CheckMacPerms(name, target_context,
source_context.c_str(), cr)) {
*error = StringPrintf(
"SELinux permission check failed "
"(source_context=%s, target_context=%s)",
source_context.c_str(), target_context ?: "(null)");
return PROP_ERROR_PERMISSION_DENIED;
}
if (!CheckType(type, value)) {
*error = StringPrintf(
"Property type check failed, value doesn't match expected type '%s'",
(type ?: "(null)"));
return PROP_ERROR_INVALID_VALUE;
}7.3 真实组合
Fingerprint HAL 的 property type 在 public policy 中声明,vendor daemon 调用 set macro。
源码文件:system/sepolicy/public/property.te
system_public_prop(virtual_fingerprint_prop)源码文件:system/sepolicy/vendor/hal_fingerprint_default.te
set_prop(hal_fingerprint_default, virtual_fingerprint_prop)缺 property contexts name mapping、value schema 不匹配或 socket 路径失败,都不能靠重复 property_service set 修复。
8. HAL组合
8.1 三种身份
HAL policy 通常分为基础 attribute、client attribute 与 server attribute。具体 daemon 通过 hal_server_domain 加入 server,调用方通过 hal_client_domain 加入 client。
源码文件:system/sepolicy/vendor/hal_audio_default.te
type hal_audio_default, domain;
hal_server_domain(hal_audio_default, hal_audio)
type hal_audio_default_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(hal_audio_default)
hal_client_domain(hal_audio_default, hal_allocator)
hal_client_domain(hal_audio_default, hal_bluetooth)8.2 IPC与名称
Audio HAL 公共策略分别建立 Binder direction、HwService/AIDL service ownership 和 ServiceManager transaction。
源码文件:system/sepolicy/private/hal_audio.te
binder_call(hal_audio_client, hal_audio_server)
binder_call(hal_audio_server, hal_audio_client)
hal_attribute_hwservice(hal_audio, hal_audio_hwservice)
hal_attribute_service(hal_audio, hal_audio_service)
binder_call(hal_audio_server, servicemanager)8.3 名称映射
服务实例名还必须映射到 HAL service type。
源码文件:system/sepolicy/private/service_contexts
android.hardware.audio.core.IConfig/default u:object_r:hal_audio_service:s0
android.hardware.audio.core.IModule/default u:object_r:hal_audio_service:s0
android.hardware.audio.effect.IFactory/default u:object_r:hal_audio_service:s08.4 硬件边界
HAL client/server 标记不自动授予设备节点。Audio HAL 单独获得 audio device 权限,并通过 neverallow 保证其他 HAL server 不能直接访问。
源码文件:system/sepolicy/private/hal_audio.te
allow hal_audio_server audio_device:dir r_dir_perms;
allow hal_audio_server audio_device:chr_file rw_file_perms;
neverallow { halserverdomain -hal_audio_server -hal_omx_server }
audio_device:chr_file *;9. 不能互换
9.1 语义矩阵
| 误用 | 为什么错误 | 正确方向 |
|---|---|---|
| 用 allow 修复错误 label | Rule target 与运行对象不匹配 | 修 contexts/transition |
| 用 type_transition 当授权 | 只计算 new type | 补父目录/create/associate 等 allow |
| 用 binder_call 修 find denial | 不含 service_manager find | 查 service type 与 find rule |
| 用 get_prop 修 set denial | 不含 socket 与 property_service set | 使用 set_prop 并检查 mapping/schema |
| 用 dontaudit 修功能失败 | 只抑制 audit,不授予 | 找真正缺失 permission 或标签 |
| 用 permissive 绕 neverallow | Neverallow 在构建期 | 修 allow/attribute/架构边界 |
| 用 trusted attribute 修 MLS | 豁免范围过大 | 修 level 或使用 fd 路径 |
| 用业务 property 当policy boolean | Property 不改 loaded AV table | 构建条件或固定策略设计 |
9.2 条件层次
10. Denial定位
10.1 四个字段
从 AVC 先提取 scontext、tcontext、tclass 与 denied permission。它们直接对应 TE query key,不要先运行 audit2allow 猜宏。
10.2 按Class分流
| tclass/permission | 第一检查点 | 第二检查点 |
|---|---|---|
file open/read/map | 文件实际 label、目录 search | permission set是否过宽/过窄 |
dir add_name/search | 父目录 label | 创建流程中的 new type |
process transition | old/new domain | executable type与entrypoint |
binder call | client/server process SID | service find 是否先通过 |
service_manager find/add | service name mapping | client/owner attribute |
property_service set | property name mapping | set_prop、value schema |
unix_stream_socket connectto | server socket/process SID | sock_file write |
filesystem associate | new object type | filesystem SID |
10.3 约束判断
Binary policy 已有 allow 但仍 denied 时,再看:
- source/target MLS level 是否满足
private/mls; - permission 是否被 type bounds 或 role transition 清除;
- ioctl command 是否还需 allowxperm;
- 规则是否只存在于另一 build variant;
- source/target 是否通过 attribute 展开落入 neverallow。
11. 验证阶梯
11.1 跨文件搜索
先用真实对象名建立 owner map。MediaTuner 的搜索应同时命中 rc、file context、service context 与 TE policy。
# 从AOSP根目录执行。
rg -n 'mediatuner|media\.tuner' \
system/sepolicy/private/mediatuner.te \
system/sepolicy/private/file_contexts \
system/sepolicy/private/service_contexts \
frameworks/av/services/tuner/mediatuner.rc输入是四个已知 ownership 文件。断言是能找到 service lifecycle、executable label、Binder service label 和 domain policy。它不能证明宏展开和最终 product policy。
11.2 M4展开
# 展开MediaTuner策略中的高层宏。
case "$(uname -s)-$(uname -m)" in
Darwin-*) M4=prebuilts/build-tools/darwin-x86/bin/m4 ;;
Linux-x86_64) M4=prebuilts/build-tools/linux-x86/bin/m4 ;;
Linux-aarch64) M4=prebuilts/build-tools/linux-arm64/bin/m4 ;;
*) printf '%s\n' 'unsupported host'; exit 1 ;;
esac
"$M4" --fatal-warnings \
-D target_build_variant=user \
-D target_full_treble=true \
-D target_exclude_build_test=false \
system/sepolicy/public/global_macros \
system/sepolicy/public/neverallow_macros \
system/sepolicy/public/te_macros \
system/sepolicy/private/mediatuner.te | \
sed '/^#/d;/^[[:space:]]*$/d'关键断言是输出包含 type_transition init mediatuner_exec:process mediatuner、Binder call/transfer/fd、service add/find、property file read 和 owner neverallow。M4 成功仍不验证 type/class symbols 与 neverallow 能通过完整编译。
11.3 Binary查询
# 将PRODUCT替换为实际产品名。
ANDROID_SEPOLICY='out/target/product/PRODUCT/root/sepolicy'
# Process transition与entrypoint。
sesearch -T -s init -t mediatuner_exec -c process \
"$ANDROID_SEPOLICY"
sesearch -A -s mediatuner -t mediatuner_exec -c file -p entrypoint \
"$ANDROID_SEPOLICY"
# Service注册、Binder与property读取。
sesearch -A -s mediatuner -t mediatuner_service -c service_manager \
"$ANDROID_SEPOLICY"
sesearch -A -s mediatuner -t system_server -c binder -p call \
"$ANDROID_SEPOLICY"
sesearch -A -s mediatuner -t tuner_config_prop -c file -p read \
"$ANDROID_SEPOLICY"11.4 设备标签
# 服务启用后查看进程domain与executable label。
adb shell ps -AZ | grep mediatuner
adb shell ls -Z /system/bin/mediatuner
# 查看服务是否注册;命令可用性取决于产品工具集。
adb shell service list | grep 'media.tuner'进程不存在可能是业务 property 未启用,不等于 SELinux transition 失败。应同时检查 init 状态和日志。
11.5 测试消费者
Android policy tests 同时遍历普通与 conditional avtab,说明最终查询应针对 binary rules,而不是假设所有 rule 都来自单个 .te。
源码文件:system/sepolicy/tests/policy.py
def __InitTERules(self):
avtabIterP = self.__libsepolwrap.init_avtab(self.__policydbP)
if (avtabIterP == None):
sys.exit("Failed to initialize avtab")
self.__GetTERules(self.__policydbP, avtabIterP, self.__Rules)
self.__libsepolwrap.destroy_avtab(avtabIterP)
avtabIterP = self.__libsepolwrap.init_cond_avtab(self.__policydbP)
if (avtabIterP == None):
sys.exit("Failed to initialize conditional avtab")
self.__GetTERules(self.__policydbP, avtabIterP, self.__Rules)
self.__libsepolwrap.destroy_avtab(avtabIterP)测试输入是 binary policy,断言范围是普通和 conditional allow 都进入查询集合;它不检查运行对象是否带正确 label。
12. 提交审视
12.1 权限面积
提交前展开每个新增宏,确认它是否附带 ioctl、map、watch、fd use、callback、neverallow 或非 Treble 分支。宏名表达意图,但审查对象是展开结果。
12.2 Attribute传播
给 type 增加 attribute 前搜索所有消费者:
# 同时查正向allow、neverallow、transition和宏参数。
rg -n '\b目标attribute\b' system/sepolicy \
-g '*.te' -g 'attributes' -g 'te_macros'Attribute membership 可能一次改变数百条规则,并让 type 进入新的 neverallow source/target set。
12.3 结束路径
策略资源没有传统的“释放函数”,但修改仍有结束路径:
- 构建失败:修语法、symbol、neverallow 或条件输入;
- 服务未启动:回到 init/property/lifecycle;
- 运行 denial:回到 label、AV、constraint;
- 功能成功但权限过宽:用 neverallow、xperm 或更窄 type 收紧;
- OTA/policy reload:确认 mapping、public API 与旧 vendor compatibility。
12.4 五个反例
- 为了消除 denial 给 daemon 加
mlstrustedsubject; - 对设备节点使用
rw_file_perms却不审查 ioctl commands; - 服务 find 失败时只增加
binder_call; - 文件创建失败时只增加 new file
create,忽略父目录和 associate; - 源码里看到 userdebug rule,就假定 user 产品也包含。
13. 源码导航
| 要解决的问题 | 先看哪里 | 再追哪里 |
|---|---|---|
| TE规则语义 | system/sepolicy/**/*.te | kernel/common/security/selinux/ss/services.c |
| Type/attribute成员 | public/attributes、type声明 | policydb type maps、neverallow消费者 |
| Exec transition | executable contexts、transition macro | selinux_bprm_creds_for_exec |
| File create/label | parent type、type_transition | may_create、security_compute_sid |
| Binder transaction | binder_call | Binder LSM hooks |
| Service add/find | service_contexts、service type | ServiceManager Access.cpp |
| Property set/get | property type、macro | property_service.cpp |
| HAL client/server | HAL attributes、service/hwservice type | Binder/HwBinder service mapping |
| 构建条件 | te_macros、flagging macros | Soong policy.go |
| Neverallow冲突 | 展开后的 conf、attribute members | checkpolicy、sepolicy-analyze |
| MLS denial | source/target levels、private/mls | constraint evaluator |
| Boolean疑问 | final bool table | selinuxfs/conditional kernel code |
面对一个新的 Android SELinux 修改,最短可靠路径是:先找到运行入口和对象 owner,确认实际 Context;再选择对应的声明、mapping、transition 或 allow;展开宏并构建最终策略;用 binary query 验证 rule;最后由设备上的进程、文件、服务或 property 行为确认消费者。任何一步缺失,策略都可能“源码看起来正确,运行仍然失败”。
media.tuner 的完整链正好覆盖这套方法:Property 只启用 init service;file contexts 提供 executable type;transition macro 产生 mediatuner domain;service contexts 提供 Binder service type;add_service、binder_call、HAL client 和 get_prop 分别服务不同 operation;neverallow/xperm 定义不可越过的边界。能够独立从这条链替换成另一个真实 daemon,并逐层找到相同角色的源码文件,就已经具备继续进入 Android contexts 与具体 domain 专题的基础。
