Android CTS中neverallow规则生成过程
CTS里面SELinux相关测试中neverallow测试项占绝大多数,Android系统开发者都应该知道,在修改sepolicy时,需要确保不能违反这些neverallow规则,不然会过不了CTS。CTS中nerverallow测试都是在SELinuxNeverallowRulesTest.java文件中,并且从AOSP代码中发现该文件不是人工提交的,而是通过python脚本生成的,为了以后更好的修改sepolicy,就需要了解下SELinuxNeverallowRulesTest.java是如何生成的。
Makefile
首先看下SELinuxNeverallowRulesTest.java的生成的Makefile.
selinux_general_policy := $(call intermediates-dir-for,ETC,general_sepolicy.conf)/general_sepolicy.conf
selinux_neverallow_gen := cts/tools/selinux/SELinuxNeverallowTestGen.py
selinux_neverallow_gen_data := cts/tools/selinux/SELinuxNeverallowTestFrame.py
LOCAL_ADDITIONAL_DEPENDENCIES := $(COMPATIBILITY_TESTCASES_OUT_cts)/sepolicy-analyze
LOCAL_GENERATED_SOURCES := $(call local-generated-sources-dir)/android/cts/security/SELinuxNeverallowRulesTest.java # 目标文件
$(LOCAL_GENERATED_SOURCES) : PRIVATE_SELINUX_GENERAL_POLICY := $(selinux_general_policy)
$(LOCAL_GENERATED_SOURCES) : $(selinux_neverallow_gen) $(selinux_general_policy) $(selinux_neverallow_gen_data)
mkdir -p $(dir $@)
$< $(PRIVATE_SELINUX_GENERAL_POLICY) $@
# $< 为:右边依赖的第一个元素, 即 $(selinux_neverallow_gen) = cts/tools/selinux/SELinuxNeverallowTestGen.py
# $@ 为:左边目标,即要生成的目标文件SELinuxNeverallowRulesTest.java
# 这条命令相当于 cts/tools/selinux/SELinuxNeverallowTestGen.py $(call intermediates-dirfor,ETC,general_sepolicy.conf)/general_sepolicy.conf SELinuxNeverallowRulesTest.java
include $(BUILD_CTS_HOST_JAVA_LIBRARY)
从上面可以看到,执行SELinuxNeverallowTestGen.py general_sepolicy.conf SELinuxNeverallowRulesTest.java会生成SELinuxNeverallowRulesTest.java文件。
general_sepolicy.conf 生成
该文件的生成Makfile
# SELinux policy embedded into CTS.
# CTS checks neverallow rules of this policy against the policy of the device under test.
##################################
include $(CLEAR_VARS)
LOCAL_MODULE := general_sepolicy.conf # 目标文件
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_TAGS := tests
include $(BUILD_SYSTEM)/base_rules.mk
$(LOCAL_BUILT_MODULE): PRIVATE_MLS_SENS := $(MLS_SENS)
$(LOCAL_BUILT_MODULE): PRIVATE_MLS_CATS := $(MLS_CATS)
$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_BUILD_VARIANT := user
$(LOCAL_BUILT_MODULE): PRIVATE_TGT_ARCH := $(my_target_arch)
$(LOCAL_BUILT_MODULE): PRIVATE_WITH_ASAN := false
$(LOCAL_BUILT_MODULE): PRIVATE_SEPOLICY_SPLIT := cts
$(LOCAL_BUILT_MODULE): PRIVATE_COMPATIBLE_PROPERTY := cts
$(LOCAL_BUILT_MODULE): $(call build_policy, $(sepolicy_build_files), \
$(PLAT_PUBLIC_POLICY) $(PLAT_PRIVATE_POLICY)) # PLAT_PUBLIC_POLICY = syetem/sepolicy/public PLAT_PRIVATE_POLICY = system/sepolicy/private
$(transform-policy-to-conf) # 这里是使用m4将te规则文件都处理合成为目标文件$@,即general_sepolicy.conf
$(hide) sed '/dontaudit/d' $@ > $@.dontaudit
##################################
可以看到,general_sepolicy.conf 文件是将system/sepolicy/public和system/sepolicy/private规则文件整合在一起,而这些目录包含的是AOSP sepolicy大多数配置信息。
SELinuxNeverallowTestGen.py 脚本逻辑
生成的逻辑都是在该脚本中,下面脚本我调整了顺序,方便说明执行的逻辑,脚本代码
#!/usr/bin/env python
import re
import sys
import SELinuxNeverallowTestFrame
usage = "Usage: ./SELinuxNeverallowTestGen.py <input policy file> <output cts java source>"
if __name__ == "__main__":
# check usage
if len(sys.argv) != 3:
print usage
exit(1)
input_file = sys.argv[1]
output_file = sys.argv[2]
# 这三个变量是同目录下SELinuxNeverallowTestFrame.py文件中的内容,是生成java文件的模版
src_header = SELinuxNeverallowTestFrame.src_header
src_body = SELinuxNeverallowTestFrame.src_body
src_footer = SELinuxNeverallowTestFrame.src_footer
# grab the neverallow rules from the policy file and transform into tests
neverallow_rules = extract_neverallow_rules(input_file) # 提取neverallow规则从general_sepolicy.conf中
i = 0
for rule in neverallow_rules:
src_body += neverallow_rule_to_test(rule, i)
i += 1
# 然后将neverallow规则写入到SELinuxNeverallowRulesTest.java文件中
with open(output_file, 'w') as out_file:
out_file.write(src_header)
out_file.write(src_body)
out_file.write(src_footer)
# extract_neverallow_rules - takes an intermediate policy file and pulls out the
# neverallow rules by taking all of the non-commented text between the 'neverallow'
# keyword and a terminating ';'
# returns: a list of rules
def extract_neverallow_rules(policy_file):
with open(policy_file, 'r') as in_file:
policy_str = in_file.read()
# full-Treble only tests are inside sections delimited by BEGIN_TREBLE_ONLY
# and END_TREBLE_ONLY comments.
# uncomment TREBLE_ONLY section delimiter lines
remaining = re.sub(
r'^\s*#\s*(BEGIN_TREBLE_ONLY|END_TREBLE_ONLY|BEGIN_COMPATIBLE_PROPERTY_ONLY|END_COMPATIBLE_PROPERTY_ONLY)',
r'\1', # group 引用
policy_str,
flags = re.M) # 该方法是将 #开头的注释行任意空格后跟着BEGIN_TREBLE_ONLY、END_TREBLE_ONLY、BEGIN_COMPATIBLE_PROPERTY_ONLY和END_COMPATIBLE_PROPERTY_ONLY时,替换为这些关键字,即去掉注释
# remove comments
remaining = re.sub(r'#.+?$', r'', remaining, flags = re.M) # 将文件中的 # 开头注释行去掉
# match neverallow rules
lines = re.findall(
r'^\s*(neverallow\s.+?;|BEGIN_TREBLE_ONLY|END_TREBLE_ONLY|BEGIN_COMPATIBLE_PROPERTY_ONLY|END_COMPATIBLE_PROPERTY_ONLY)',
remaining,
flags = re.M |re.S) # 将neverallow和以这几个关键字开头的行取出来
# extract neverallow rules from the remaining lines
# 这些关键字会修饰里面的neverallowrules,若treble_only_depth > 1 说明是适用于treble系统, 若compatible_property_only_depth > 1,说明适用于 compatible_property 系统
rules = list()
treble_only_depth = 0
compatible_property_only_depth = 0
for line in lines:
if line.startswith("BEGIN_TREBLE_ONLY"):
treble_only_depth += 1
continue
elif line.startswith("END_TREBLE_ONLY"):
if treble_only_depth < 1:
exit("ERROR: END_TREBLE_ONLY outside of TREBLE_ONLY section")
treble_only_depth -= 1
continue
elif line.startswith("BEGIN_COMPATIBLE_PROPERTY_ONLY"):
compatible_property_only_depth += 1
continue
elif line.startswith("END_COMPATIBLE_PROPERTY_ONLY"):
if compatible_property_only_depth < 1:
exit("ERROR: END_COMPATIBLE_PROPERTY_ONLY outside of COMPATIBLE_PROPERTY_ONLY section")
compatible_property_only_depth -= 1
continue
rule = NeverallowRule(line)
rule.treble_only = (treble_only_depth > 0)
rule.compatible_property_only = (compatible_property_only_depth > 0)
rules.append(rule)
if treble_only_depth != 0:
exit("ERROR: end of input while inside TREBLE_ONLY section")
if compatible_property_only_depth != 0:
exit("ERROR: end of input while inside COMPATIBLE_PROPERTY_ONLY section")
return rules
# neverallow_rule_to_test - takes a neverallow statement and transforms it into
# the output necessary to form a cts unit test in a java source file.
# returns: a string representing a generic test method based on this rule.
# 将neverallowrules 替换到java模版中
def neverallow_rule_to_test(rule, test_num):
squashed_neverallow = rule.statement.replace("\n", " ")
method = SELinuxNeverallowTestFrame.src_method
method = method.replace("testNeverallowRules()",
"testNeverallowRules" + str(test_num) + "()")
method = method.replace("$NEVERALLOW_RULE_HERE$", squashed_neverallow)
method = method.replace(
"$FULL_TREBLE_ONLY_BOOL_HERE$",
"true" if rule.treble_only else "false")
method = method.replace(
"$COMPATIBLE_PROPERTY_ONLY_BOOL_HERE$",
"true" if rule.compatible_property_only else "false")
return method
总结下脚本功能
将BEGIN_TREBLE_ONLY|END_TREBLE_ONLY|BEGIN_COMPATIBLE_PROPERTY_ONLY|
END_COMPATIBLE_PROPERTY_ONLY这几个关键字前面的注释去掉,以便后面解析时使用;删除冗余的注释行;
取neverallow和上面四个关键字的部分进行解析,并根据下面情况对treble_only和compatible_property_only进行设置;
- neverallow 包含在BEGIN_TREBLE_ONLY和END_TREBLE_ONLY之间,treble_only被设置为true;
- neverallow 包含在BEGIN_COMPATIBLE_PROPERTY_ONLY和END_COMPATIBLE_PROPERTY_ONLY之间,compatible_property_only被设置为true;
- neverallow 不在任何BEGIN_TREBLE_ONLY/END_TREBLE_ONLY和BEGIN_COMPATIBLE_PROPERTY_ONLY/END_COMPATIBLE_PROPERTY_ONLY之间,则treble_only和compatible_property_only都被设置为false。
然后用neverallow部分、treble_only和compatible_property_only值对下面方法模板中的$NEVERALLOW_RULE_HERE$、$FULL_TREBLE_ONLY_BOOL_HERE$和$COMPATIBLE_PROPERTY_ONLY_BOOL_HERE$分别替换。
src_method = """
@RestrictedBuildTest
public void testNeverallowRules() throws Exception {
String neverallowRule = "$NEVERALLOW_RULE_HERE$";
boolean fullTrebleOnly = $FULL_TREBLE_ONLY_BOOL_HERE$;
boolean compatiblePropertyOnly = $COMPATIBLE_PROPERTY_ONLY_BOOL_HERE$;
if ((fullTrebleOnly) && (!isFullTrebleDevice())) {
// This test applies only to Treble devices but this device isn't one
return;
}
if ((compatiblePropertyOnly) && (!isCompatiblePropertyEnforcedDevice())) {
// This test applies only to devices on which compatible property is enforced but this
// device isn't one
return;
}
// If sepolicy is split and vendor sepolicy version is behind platform's,
// only test against platform policy.
File policyFile =
(isSepolicySplit() && mVendorSepolicyVersion < P_SEPOLICY_VERSION) ?
deviceSystemPolicyFile :
devicePolicyFile;
/* run sepolicy-analyze neverallow check on policy file using given neverallow rules */
ProcessBuilder pb = new ProcessBuilder(sepolicyAnalyze.getAbsolutePath(),
policyFile.getAbsolutePath(), "neverallow", "-w", "-n",
neverallowRule);
pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
pb.redirectErrorStream(true);
Process p = pb.start();
p.waitFor();
BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
StringBuilder errorString = new StringBuilder();
while ((line = result.readLine()) != null) {
errorString.append(line);
errorString.append("\\n");
}
assertTrue("The following errors were encountered when validating the SELinux"
+ "neverallow rule:\\n" + neverallowRule + "\\n" + errorString,
errorString.length() == 0);
}
本地生成 SELinuxNeverallowRulesTest.java 文件
在修改SELinux后,想确定下是否满足neverallow规则,虽然编译过程中会进行neverallow检查,但由于打包时间比较耗时,如果在本地生成的话,那速度会更快。
本地生成 SELinuxNeverallowRulesTest.java 命令
默认是在源码的根目录
make general_sepolicy.conf
cts/tools/selinux/SELinuxNeverallowTestGen.py out/target/product/cepheus/obj/ETC/general_sepolicy.conf_intermediates/general_sepolicy.conf SELinuxNeverallowRulesTest.java
由于某些规则是使用attribute,可能不是很明显,还需要结合其他方法来确定。
总结
从生成代码中可以看到,neverallow规则都属于AOSP system/sepolicy/private和system/sepolicy/public中的neverallow,所以在添加规则时不能修改neverallow,也不能违背。
附件
cts_neverallow.zip,中包含有:
SELinuxNeverallowTestGen.py 脚本
general_sepolicy.conf
SELinuxNeverallowTestFrame.py Java测试代码模板
first 为SELinuxNeverallowTestGen.py第一步执行的结果
second 为SELinuxNeverallowTestGen.py第二步执行的结果
SELinuxNeverallowRulesTest.java 为生成的文件
后面三个文件是前三个文件所生成,执行命令为:
SELinuxNeverallowTestGen.py general_sepolicy.conf SELinuxNeverallowRulesTest.java
链接
https://liwugang.github.io/2019/12/29/CTS-neverallow.html
Android CTS中neverallow规则生成过程的更多相关文章
- Android N和O中使用adb shell dpm set-device-owner 'com.android.cts.verifier/com.android.cts.verifier.managedprovisioning.DeviceAdminTestReceiver' setup Device Owner失败
PC端出现如下log: D:\workspace\AndroidO\CTS\CTS_Verifier>adb shell dpm set-device-owner 'com.android.ct ...
- 在Android Studio 中正确使用adil ”绝对经典“
今天调用远程服务中遇到了一个问题,哎,调了2个小时,后来终于解决,总结来看还是对新的Android Studio 不够熟悉.那么....就可以睡觉啦!!! 在Android Studio中使用进程通信 ...
- 监听Android CTS测试项解决方案(二)
二,监听当前测试项是否是Accelerometer Measurement Test测试项 通过第一种方式介绍的,我们可以得到当前处于活动状态的Activity类似监听CTS测试当前的测试项.但是由于 ...
- 监听Android CTS测试项解决方案(一)
前言: 首先这里需要详细叙述一下标题中"监听Android CTS测试项解决方案"的需求.这里的需求是指我们需要精确的监听到当前CTS测试正在测试的测试项. 因为我们知道CTS认证 ...
- android项目中如何加载已有so库 <转>
1,在项目根目录下建立文件夹libs/armeabi文件夹 2,将so库放入 libs/armeabi文件夹 注意事项: 1,如果采用静态注册的方式请注意C文件中严格按照命名规则 Java_packa ...
- 在Android应用中使用Clean架构
自从开始开发安卓应用,我一直感觉我可以做得更好.我看过不少烂代码,其中当然有我写的.安卓系统的复杂性加上烂代码势必酿成灾祸,所以从错误中成长就很重要.我Google了如何更好地开发应用,发现了这个叫做 ...
- 转:Android开发中的MVP架构(最后链接资源不错)
Android开发中的MVP架构 最近越来越多的人开始谈论架构.我周围的同事和工程师也是如此.尽管我还不是特别深入理解MVP和DDD,但是我们的新项目还是决定通过MVP来构建. 这篇文章是我通过研究和 ...
- 转: Android开发中的MVP架构详解(附加链接比较不错)
转: http://www.codeceo.com/article/android-mvp-artch.html 最近越来越多的人开始谈论架构.我周围的同事和工程师也是如此.尽管我还不是特别深入理解M ...
- Android Studio中使用Gradle打包
首先要注意一点,Android Studio中把proguard.txt已经命名为proguard-rules.pro,由此可见,採用Gradle打包,混淆规则文件的名称是不重要的.能够自己随便命名. ...
随机推荐
- Java练习 SDUT - 2669_2-2 Time类的定义
2-2 Time类的定义 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 通过本题目的练习可以掌握类与对象的定义: 设计 ...
- UVA_694:The Collatz Sequence
Language: C++ 4.8.2 #include<stdio.h> int main(void) { long long int m, n, copy_m; ; ; ) { sca ...
- 【NS2】cygwin+NS2.29安装之道 (转载)
最近接了一个需要进行网络模拟的项目.对与网络模拟一窍不通的本人算是一个挑战.于是乎开始整日上网搜索相关的资料,没想到这个世界的资源真是丰富多彩.最后发现了一个网络模拟的流行软件NS2,在经过多次尝试后 ...
- hdu 2807 The Shortest Path(矩阵+floyd)
The Shortest Path Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others ...
- oralce 分离表和索引
总是将你的表和索引建立在不同的表空间内(TABLESPACES). 决不要将不属于ORACLE内部系统的对象存放到SYSTEM表空间里. 同时,确保数据表空间和索引表空间置于不同的硬盘上. “同时 ...
- React事件用法
一.事件处理函数的使用 1.1 绑定事件处理函数 1.2 使用 <HelloWorld onClick={this.handleClick}/> 大括号中指定的是this对象即当前组件引用 ...
- 冒泡排序&直接插入排序&快速排序
一.冒泡排序 0 1 2 3 4 5 假设有一个6个数的数组,0,1,2,3,4,5是索引,冒泡排序就是相邻两个对比,比如5和4比,如果满足条件就互 ...
- 防止SyntaxHighlighter.js的闪烁闪一下的方法
SyntaxHighlighter.js是一个代码高亮的JS插件,使用也很简单,但是由于是浏览器段执行JS代码来着色,会出现视觉上闪一下的效果.比如你的20行代码网页打开显示高度为100px,但是Sy ...
- 创建JAVASCRIPT对象3种方法
创建JAVASCRIPT对象3种方法 方法一:直接定义并创建对象实例 var obj = new Object(); //创建对象实例 //添加属性obj.num = 5; //添加属性 o ...
- supersockets单个 listener
在下面的配置中,你可以配置服务器的监听 ip/port: <superSocket> <servers> <server name="TelnetServer& ...