本文将详细介绍在 Android 12+ 开发过程中,涉及对串口设备(如
/dev/ttyS1
)的读写操作时,可能会遇到 SELinux 权限不足的问题,包括涉及的 SELinux 策略修改和其他相关问题的解决方案。
问题描述
在尝试对串口设备进行读写操作时,遇到了如下权限拒绝问题:
07-26 15:46:23.048 5964 5964 W com.sysout.app: type=1400 audit(0.0:854): avc: denied { write } for name="ttyS1" dev="tmpfs" ino=57 scontext=u:r:untrusted_app:s0:c68,c256,c512,c768 tcontext=u:object_r:ttyS_device:s0 tclass=chr_file permissive=0 app=com.xxx.featuretest
初步解决方案
为了允许 untrusted_app
对 ttyS_device
进行读写操作,需要修改 SELinux 策略,添加相应的权限。修改以下文件:
- device/mediatek/sepolicy/basic/non_plat/untrusted_app.te
- device/mediatek/sepolicy/basic/non_plat/untrusted_app_25.te
- device/mediatek/sepolicy/basic/non_plat/shell.te
- device/mediatek/sepolicy/basic/non_plat/system.te
在这些文件中添加如下权限:
allow untrusted_app ttyS_device:chr_file { getattr read write open ioctl };
allow untrusted_app proc_tty_drivers:file { getattr read write open };
权限解释
allow untrusted_app ttyS_device:chr_file { getattr read write open ioctl };
# or
allow untrusted_app ttyS_device:chr_file rw_file_perms;
这条规则允许 untrusted_app
对 ttyS_device
类型的字符设备文件执行 getattr
、read
、write
、open
和 ioctl
操作。
allow untrusted_app proc_tty_drivers:file { getattr read write open };
这条规则允许 untrusted_app
对 proc_tty_drivers
类型的文件执行 getattr
、read
、write
和 open
操作。
rw_file_perms
详细说明
在 SELinux 中,rw_file_perms
是一个预定义的权限集,通常包括对文件执行读写操作的所有必要权限。具体来说,它包括以下权限:
define(`x_file_perms', `{ getattr execute execute_no_trans map }')
define(`r_file_perms', `{ getattr open read ioctl lock map watch watch_reads }')
define(`w_file_perms', `{ open append write lock map }')
define(`rx_file_perms', `{ r_file_perms x_file_perms }')
define(`ra_file_perms', `{ r_file_perms append }')
define(`rw_file_perms', `{ r_file_perms w_file_perms }')
define(`rwx_file_perms', `{ rw_file_perms x_file_perms }')
define(`create_file_perms', `{ create rename setattr unlink rw_file_perms }')
这些权限定义在 system/sepolicy/public/global_macros
文件中,用于方便地组合常用的权限集。例如,rw_file_perms
包含读取、写入和获取属性的权限。
调试过程
检查和确认设备文件的权限
- 检查设备文件的权限:
使用以下命令查看设备文件的权限和 SELinux 上下文:
ls -l /dev/ttyS*
ls -lZ /dev/ttyS*
示例输出:
ls -l /dev/ttyS*
crwxrwxrwx 1 root root 4, 64 2024-07-30 16:04 /dev/ttyS0
crwxrwxrwx 1 root root 4, 65 2024-07-30 16:04 /dev/ttyS1
crwxrwxrwx 1 root root 4, 66 2024-07-30 16:04 /dev/ttyS2
ls -lZ /dev/ttyS*
crwxrwxrwx 1 root root u:object_r:ttyS_device:s0 4, 64 2024-07-30 16:04 /dev/ttyS0
crwxrwxrwx 1 root root u:object_r:ttyS_device:s0 4, 65 2024-07-30 16:04 /dev/ttyS1
crwxrwxrwx 1 root root u:object_r:ttyS_device:s0 4, 66 2024-07-30 16:04 /dev/ttyS2
- 确认当前系统的应用类型:
根据 AVC 日志确认当前应用类型,例如:
scontext=u:r:untrusted_app:s0:c68,c256,c512,c768
这表明当前应用类型为 untrusted_app
。
确认权限问题
通过 AVC 日志,可以确定当前应用类型及报什么权限问题:
- 设备节点权限问题:确认设备文件的权限是否正确。
- SELinux 权限问题:确认 SELinux 策略是否正确。
解决 write 不生效问题
在 .te
文件中添加权限规则后,串口写操作仍不生效,且没有报 avc
错误时,可以尝试以下方法:
使用 mlstrustedobject
解决问题
如果上述修改仍然无法解决问题,可以尝试将 untrusted_app
类型设置为 mlstrustedobject
。修改 device/mediatek/sepolicy/basic/non_plat/untrusted_app.te 文件,添加以下内容:
typeattribute untrusted_app mlstrustedobject;
这将使 untrusted_app
类型具有更多权限,能够跨越一些安全级别限制。
解决 untrusted_app
访问 ttyS_device
权限问题
基于之前的分析,主要问题可能是 MLS(多级安全性)约束。为了确保 untrusted_app
能够正确访问 ttyS_device
,需要确认和修改以下几个方面:
1. 确保 untrusted_app
类型具有必要的权限
确保在 untrusted_app.te
文件中添加以下规则:
allow untrusted_app ttyS_device:chr_file { getattr read write open };
2. 确认和设置 MLS 相关属性
需要确认并设置 untrusted_app
和 ttyS_device
的 MLS 属性。
设置 untrusted_app
为 mlstrustedsubject
:
在 untrusted_app.te
文件中添加以下行:
typeattribute untrusted_app mlstrustedsubject;
确保 ttyS_device
为 mlstrustedobject
:
在相应的策略文件中添加以下行:
typeattribute ttyS_device mlstrustedobject;
解决 read 不生效问题
如果串口读操作不生效,可以检查并添加以下权限:
# 目前读还是有问题,增加了权限无用,也没报错, 有空查一下,后续解决后补充到本篇
allow untrusted_app ttyS_device:chr_file { getattr read open };
不同应用的权限差异
不同类型的应用(普通应用、系统应用、签名应用)具有不同的 SELinux 策略。这些策略定义了应用可以执行的操作和访问的资源。具体来说:
- 普通应用:通常受到严格限制,只能访问特定的资源。
- 系统应用:具有更多权限,可以访问系统资源。
- 签名应用:由系统签名,通常具有更高的权限。
这些策略在不同的 .te
文件中定义,以确保每种类型的应用都遵循相应的安全策略。
为什么 typeattribute ttyS_device mlstrustedobject
有效
将 ttyS_device
类型设置为 mlstrustedobject
后,该设备文件的访问权限将不再受限于 MLS(多级安全性)约束。mlstrustedobject
类型允许跨越不同安全级别的访问,从而使串口写操作生效。
验证与调试
- 检查
stty
设置:确保stty
设置正确。使用stty -F /dev/ttyS1
和stty -a -F /dev/ttyS1
查看详细设置,确认没有冲突。
stty -F /dev/ttyS1
speed 9600 baud; line = 0;
hupcl clocal
-brkint ixon -imaxbel
- 使用调试工具:使用其他串口调试工具(如
stty
)来进一步检查通信:
stty -F /dev/ttyS1 115200
-
手动测试:通过手动发送和接收来验证数据传输。
-
读取数据:打开一个终端,运行以下命令读取数据:
cat /dev/ttyS1
-
发送数据:打开另一个终端,运行以下命令发送数据:
echo "1" > /dev/ttyS1
-
验证数据接收:检查另外的设备或终端是否显示 “1”。
-
在 Android 系统上的调试方法
由于 Android 系统环境的特殊性,可以使用以下方法进行调试:
-
通过 ADB shell 访问设备
进入 shell 环境:
adb shell
-
检查串口设备
确认串口设备是否存在并查看其权限:
ls -l /dev/ttyS1 crwxrwxrwx 1 root root 4, 65 2024-08-02 08:57 /dev/ttyS1
-
设置串口参数
使用
stty
设置串口参数:xxx:/ $ stty -F /dev/ttyS1 115200 xxx:/ $ stty -F /dev/ttyS1 speed 115200 baud; line = 0; hupcl clocal -brkint ixon -imaxbel
-
读取串口数据
使用
cat
命令读取串口数据:cat /dev/ttyS1
-
发送串口数据
使用
echo
命令发送数据到串口:echo "1" > /dev/ttyS1
-
应用层调试
在应用层面,可以编写测试应用,通过标准的Google Android API 进行串口读写操作,并通过日志输出进行调试。
结论
通过以上步骤和修改,可以解决 Android 12+ 开发过程中遇到的串口权限问题。