应用层在开发串口功用时经常需求跟驱动节点打交道,当咱们新增一个硬件设备,一般会供给对应的节点给上层(framework,app)拜访
拜访节点的方式
- java IO
- adb shell cat
以usb节点为例,其途径为/sys/devices/platform/soc/a600000.ssusb/mode
,通常咱们运用java标准的IO流
public static final String NODE_PATH = "/sys/devices/platform/soc/a600000.ssusb/mode";
public void readNode(View view) {
String result = FileUtils.readFileByLines(NODE_PATH);
tvShow.setText(result);
Log.d(TAG, "node data:"+result);
}
public static String readFileByLines(String fileName) {
BufferedReader bufferedReader = null;
FileReader fileReader = null;
try {
File file = new File(fileName);
if (!file.exists()) {
return "文件不存在";
}
fileReader = new FileReader(file);
bufferedReader = new BufferedReader(fileReader);
String string;
StringBuffer sb = new StringBuffer();
while ((string = bufferedReader.readLine()) != null) {
sb.append(string);
}
return sb.toString();
} catch (IOException e) {
e.printStackTrace();
return e.getMessage();
} finally {
if (fileReader != null) {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
或者进入adb shell
,直接cat对应节点称号来拜访节点数据
权限问题
但直接拜访肯定是不可的,会报权限过错
那么实现上层拜访底层节点的核心就在于处理权限问题,首要包含两个方向的权限处理:
- SEAndroid权限
- 节点文件自身的读写权限
首先,app归于application层,驱动节点归于kernel层,由于SEAndroid的约束,application层是无法拜访到kernel层的驱动节点文件的,这个咱们应该很好了解,假如应用层能随随便便拜访到kernel里面的东西,那Android也太不安全了,这个时候需求修改SEAndroid的权限约束,让第三方app也能拜访到kernel节点文件。
SEAndroid介绍
在处理这个SEAndroid权限问题之前,咱们先了解一下什么是SEAndroid,这儿我用通俗易懂的言语简单介绍下:
了解SEAndroid前,需求先了解一下SELinux。SELinux是由美国NSA和 SCC 开发的 Linux的一个扩张强制拜访控制安全模块。而SEAndroid相当于定制版SELinux,专门用于Android体系的安全办理。在Android4.4以前,进程的权限与当前用户的权限共同,也就是说,假如我以root用户启动某个进程,那这个进程就具有了root权限,就能够随心所欲了,这种形式称之为DAC(Discretionary Access Control)。明显,DAC太过于宽松了,所以加入了SEAndroid,任何进程想干任何事情,都必须在《安全策略文件》中赋予权限,这便是MAC(Mandatory Access Control)形式。
实际上现在的Android体系一起采用了DAC和MAC的形式,体系会先做DAC检查,假如不通过,则直接操作失利。通过DAC检查之后再做MAC检查。
关于MAC安全策略文件,SELinux有一套规矩来编写,这套规矩称之为SELinux Policy言语。关于这套言语的基本规矩,可参阅此文章《Android体系SELinux(SEAndroid)》。
权限处理
处理权限问题的思路,一般先封闭SELinux验证,一起将方针节点文件的读写权限设到最大,履行
adb root
adb shell setenforce 0
adb shell chmod 777 /sys/devices/platform/soc/a600000.ssusb/mode
看看能否成功拜访节点,假如成功了,阐明确实是权限的问题,然后分别抓取logcat日志(framework)和dmesg日志(kernel),搜索关键字avc,即可得到跟权限相关的过错日志
履行
# 抓取logcat日志
adb logcat | findstr avc > E://logcat.txt
# 抓取dmesg日志
adb shell dmesg | findstr avc > E://dmesg.txt
抓到日志结果如下
[ 312.961648] type=1400 audit(1684478793.923:22): avc: denied { read } for comm=".jason.nodetest" name="soc" dev="sysfs" ino=10272 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:sysfs:s0 tclass=dir permissive=0
[ 312.962343] type=1400 audit(1684478793.923:23): avc: denied { read } for comm=".jason.nodetest" name="soc" dev="sysfs" ino=10272 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:sysfs:s0 tclass=dir permissive=0
[ 314.145472] type=1400 audit(1684478795.107:24): avc: denied { read } for comm=".jason.nodetest" name="mode" dev="sysfs" ino=29874 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:vendor_sysfs_usb_device:s0 tclass=file permissive=0
[ 314.886338] type=1400 audit(1684478795.847:25): avc: denied { write } for comm=".jason.nodetest" name="mode" dev="sysfs" ino=29874 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:vendor_sysfs_usb_device:s0 tclass=file permissive=0
[ 315.523664] type=1400 audit(1684478796.483:26): avc: denied { write } for comm=".jason.nodetest" name="mode" dev="sysfs" ino=29874 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:vendor_sysfs_usb_device:s0 tclass=file permissive=0
[ 315.945379] type=1400 audit(1684478796.907:27): avc: denied { write } for comm=".jason.nodetest" name="mode" dev="sysfs" ino=29874 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:vendor_sysfs_usb_device:s0 tclass=file permissive=0
[ 316.294456] type=1400 audit(1684478797.255:28): avc: denied { read } for comm=".jason.nodetest" name="mode" dev="sysfs" ino=29874 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:vendor_sysfs_usb_device:s0 tclass=file permissive=0
以其中一条日志来具体剖析
[ 314.145472] type=1400 audit(1684478795.107:24): avc: denied { read } for comm=".jason.nodetest" name="mode" dev="sysfs" ino=29874 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:vendor_sysfs_usb_device:s0 tclass=file permissive=0
-
avc
代表这是SELinux相关的过错 -
denied
{ read } 表明短少读权限 -
comm=".jason.nodetest"
表明是哪个进程在拜访 -
name="mode"
表明方针文件 -
dev="sysfs"
表明.jason.nodetest进程要拜访的方针文件的文件类型 -
scontext=u:r:untrusted_app
表明拜访进程的类型,这儿表明第三方app -
tcontext=u:object_r:vendor_sysfs_usb_device:s0
表明方针文件的空间称号
翻译过来就是第三方app
要拜访vendor_sysfs_usb_device
空间下的mode
文件,需求read
权限
OK,现在报错原因知道了,那咱们就给它增加权限。
第一步:找到方针节点文件的声明,在genfs_contexts
文件中
genfscon sysfs /devices/soc/a800000.ssusb/a800000.dwc3/xhci-hcd.0.auto/usb1 u:object_r:vendor_sysfs_usb_device:s0
genfscon sysfs /devices/soc/a800000.ssusb/a800000.dwc3/xhci-hcd.0.auto/usb2 u:object_r:vendor_sysfs_usb_device:s0
genfscon sysfs /devices/platform/soc/a600000.ssusb/mode u:object_r:vendor_sysfs_usb_device:s0
# 方针节点
genfscon sysfs /devices/platform/soc/a800000.ssusb/mode u:object_r:vendor_sysfs_usb_device:s0
发现确实是界说在vendor_sysfs_usb_device
空间下,而且该空间下还界说了其他节点
第二步:找到vendor_sysfs_usb_device
空间的类型界说,在file.te
文件中
type vendor_sysfs_usb_device, sysfs_type, fs_type;
类型为sysfs
和fs
第三步:在untrusted_app_25.te
文件中增加第三方app拜访vendor_sysfs_usb_device
空间的权限
allow untrusted_app vendor_sysfs_usb_device:file rw_file_perms;
至于这个语法怎样写,之前我在另一篇文章《高通Android 11平台上移植GNSS》中提到过,下面再贴一下
假如你觉得这样不好记,也能够借助linux供给的audit2allow
工具来主动剖析生成
audit2allow -i dmesg.txt
直接将生成的te命令装备到te文件中
装备完后,一定要记住,安全策略不能跟neverallow冲突,咱们检查neverallow规矩发现,SEAndroid规则了第三方app不允许拜访sysfs和fs类型的文件
# Do not allow any write access to files in /sys
# 第三方app不允许拜访sysfs类型文件
neverallow all_untrusted_apps sysfs_type:file { no_w_file_perms no_x_file_perms };
# Apps may never access the default sysfs label.
# 第三方app不允许拜访sysfs类型文件
neverallow all_untrusted_apps sysfs:file no_rw_file_perms;
neverallow { all_untrusted_apps -mediaprovider } {
# 不允许拜访fs类型文件
fs_type
-sdcard_type
file_type
-app_data_file # The apps sandbox itself
-privapp_data_file
-app_exec_data_file # stored within the app sandbox directory
-media_rw_data_file # Internal storage. Known that apps can
# leave artfacts here after uninstall.
-user_profile_data_file # Access to profile files
userdebug_or_eng(`
-method_trace_data_file # only on ro.debuggable=1
-coredump_file # userdebug/eng only
')
}:dir_file_class_set { create unlink };
咱们将这些neverallow去掉,留意system/sepolicy/private/app_neverallows.te
和system/sepolicy/prebuilts/api/30.0/private/app_neverallows.te
都要去掉,这两个文件必须保持共同,不然编译报错。
到这儿,咱们就能够全编验证了,此刻会发现读节点没问题,但往节点写数据还存在权限问题,这是由于节点文件自身只有read权限,所以还需求在体系启动之后给节点文件增加write权限,在system/core/rootdir/init.rc
中的on boot条件下增加
chmod 0777 /sys/devices/platform/soc/a600000.ssusb/mode
这时编译运行,仍会报权限过错,那是由于此节点文件的所有者是root
而init进程的所有者是system
,system
所有者直接修改root
所有者的文件权限是不行的,需求给init进程增加拜访vendor_sysfs_usb_device
权限,在init.te
文件中增加
allow init vendor_sysfs_usb_device:file setattr;
一起还要将vendor_sysfs_usb_device
界说为mlstrustedobject
类型
type vendor_sysfs_usb_device, sysfs_type, fs_type, mlstrustedobject;
此刻再编译验证,第三方app就能够直接读写驱动节点了
读节点
写节点
总结
此文能够让做Android开发的朋友认识到,底层硬件要让上层能够拜访,怎么去处理权限问题,SELinux相关的知识点对于初学者来说十分的笼统和难以了解,但只需处理几个相关的问题就能马上搞清楚是怎样回事了。