添加selinux较好的文章,可以通过adb shell dmesg > kenel.log 获取kmesg 可以看到 avc:dined 相关内容
首先说说环境:
基于Android L的aosp修改(修改对于此文基本无影响)过的sorce code.
本来的思路是通过setenforcing permissive来开启宽容模式,可是宽容模式就是selinux的debug模式就相当于关掉了seliux毫无安全可言,而下面说到的,是以我的读取系统logcat并写入文件为例,来讲在selinux中申请我们所需的权限。
系统服务:
在init.rc里面声明service,这个service并不是android的service,而是进程,用我启动logcat的服务为例:
首先为了格式,找到声明service的地方。然后添加:
service start_logcat /system/bin/start_logcat.sh
oneshot
disabled
on property:service.start_logcat=1
start start_logcat
看看代码。
第一段:
第一行:service代表声明,start_logcat是service的名字 /system/bin/start_logcat.sh是指的这个service指向的(要执行的脚本的路径) 注意了,这个路径指的是安卓系统也就是build之后的路径,而我放在system/bin下是因为这里的start_logcat.sh被我赋予了可执行的权限(后面会说怎么赋予),我第一次做的时候创建了脚本用chmod 777给的权限,实际是毫无意义的。
第二行代码的oneshot指的是只执行一次。
第三行代码的disabled指的是开机不启动。
第二段:
第一行代码的on跟java代码的if同意 后面用property关键字声明了一个key为 service.start_logcat的property。整行的意思就是 声明一个key是service.start_logcat的property并且在其value是1的时候执行下一行代码。
第二行代码显然易见启动 start_logcat service。
也就是说 我们在安卓程序里 只要调用( 其中SystemProperties可能需要用到反射来调用set方法):
SystemProperties.set("service.start_logcat", "1");
系统监听到key为service.start_logcat的property的value变为1的时候就会去启动这个service去执行脚本.
还有一种启动方式 是不需要上面的第二段代码只声明一个service 然后在调用的时候通过这种方式:
SystemProperties.set("ctl.start", "start_logcat");
这种方式也会启动我们的系统服务去执行指向的脚本。
好,接下来说重点,重点是,我这个脚本执行(别忘记将脚本在.mk文件中cp到system/bin下)的内容是:
logcat -v time -f /sdcard/logcat -r 600000-n 1
也就是在sdcard下创建名为logcat的文件,大小为600M。一共2个文件交替着去记录logcat 超过600M就交替。
上篇文章说到了Android L之后selinux相关的权限限制,有兴趣的可以去看看。
接下来就说说遇到的问题。
首先,会报一个叫service:start_logcat selinux needs a domian 是这个错,大致是这么写的。
然后下面会报avc:dined的错误(待会会详细说这个错误怎么解决)。
后面会着重讲解avc:dined的错误,先看第一个错。第一个错的意思呢就是我们的service在执行脚本的时候selinux约束必须有domain
具体做法就是去device/xx/common/sepolicy 里面添加 一个.te文件(同级目录下有一个system_app.te,如果在这儿添加是不需要用system service的方式的,但是会开放对所有systemapp的权限,请谨慎处理),以logcat为例,创建start_logcat.te 并且在 file_contexts 下添加:
/system/bin/start_logcat.sh u:object_r:start_logcat_exec:s0
这个就是上面说到的让 /system/bin/start_logcat.sh具备可以执行的权限。
接下来看看.te文件,同样以logcat为例:
#Additional rules for shell
type start_logcat, domain;
type start_logcat_exec, exec_type, file_type;
init_daemon_domain(start_logcat)
# allow shell to run dmesg
allow start_logcat kernel:system syslog_read;
allow start_logcat toolbox_exec:file { read getattr open execute execute_no_trans };
allow start_logcat logcat_exec:file { read getattr open execute execute_no_trans };
allow start_logcat serial_device:chr_file rw_file_perms;
allow start_logcat shell_exec:file read;
allow start_logcat self:capability dac_override;
allow start_logcat system_data_file:dir { write add_name };
allow start_logcat system_data_file:file { create open append };
allow start_logcat logd:unix_stream_socket {connectto};
allow start_logcat vfat:dir { search write add_name create };
allow start_logcat logdr_socket:sock_file {create write};
allow start_logcat vfat:file create_file_perms;
allow start_logcat system_file:file execute_no_trans;
将一个类型设置为另一个类型的属性可以通过type语句实现,type start_logcat, domain; 表示将类型 domain设置为start_logcat的属性。这样就可以表明start_logcat描述的类型是用来描述进程的安全上下文的。
对于type start_logcat_exec, exec_type, file_type; 同理。
在# allow shell to run dmesg 这行注释下面的代码都是为start_logcat这个服务在selinux申请的权限。当然logcat可能不需要这么多权限, 我是考别的文件 然后在基础上添加需要的。
接下来问题来了,我们怎么知道都需要哪些权限。
有趣的是,我并不知道我需要什么权限,wtf。
能想到的唯一答案就是通过avc:dined的报错来分析我需要什么样的权限。
那么首先就要会看avc:dined的错误。
还是以logcat为例:
avc: denied { connectto } for pid=1712 comm="logcat" path="/dev/socket/logdr" scontext=u:r:start_logcat:s0 tcontext=u:r:logd:s0 tclass=unix_stream_socket permissive=0
这个呢 就是selinux在enforcing模式下的安全策略的限制报出的错。
接下来看看这个log我们应该怎么看:
denied {} 代表缺少什么 connectto
scontext 代表谁缺少 start_logcat
tcontext 代表对哪个文件 logd
tclass 代表文件类型 unix_stream_socket
现在再来看看上面的报错。 也就是说 我们的start_logcat的服务 对unix_stream_socket类型的logd文件缺少connectto的权限。
那么我们自然知道.te的规则文件怎么写了,在start_logcat.te中添加 :
allow start_logcat logd:unix_stream_socket {connectto};
允许start_logcat对unix_stream_socket类型的logd文件有connectto的操作。
酱紫(继续build,然后如果报错,按上所述添加需要的权限),我们就完成了安卓向selinux申请所需权限并执行所需脚本从0到有的整个过程。
ps:如果有更好的可以知道做什么需要什么权限的办法 ,欢迎告知,学习为主。
顺便附上查看kernellog的命令:
adb shell cat /proc/kmsg
这个是以我自己的logcat为例来讲述的整个selinux申请所需权限的过程,如果有不对,欢迎指出,共同进步。