[Android]开机自启动脚本和selinux权限配置

概述

在前段时间的工作中,需要开发一个开机自动启动的脚本,现把开发过程记录一下

主要框架

编写一个可以开机自动启动的脚本,方法就是通过rc文件,在boot_complete=1时,去启动这个服务,那么,可以先基于以上思路,创建实现脚本所需要的文件。

通常来说,我这个脚本是要放在vendor分区的,因此将脚本放到vendor目录下,参考其他的脚本,创建3个空的文件如下:

multi_tpinsmod/ 		//脚本文件夹
├── Android.bp			//bp文件,用于放置编译参数
├── multi_tpinsmod.cpp	//脚本的代码实现
└── multi_tpinsmod.rc	//rc文件,用于启动脚本

0 directories, 3 files

接下来,我们就来实现如上框架。

脚本编写

脚本的实现方式有很多,包括c/c++,shell等,基于简单高效的原则,shell是比较好的选择,但是对于Android的开机自启动脚本,个人建议是选择c/c++来编写,如果是用于调试的手动执行脚本,可以使用shell来写,主要原因如下:

  1. Android中有许多权限问题,而以shell脚本来说,会有许多的selinux权限限制(并且许多权限还无法绕过),如果是开机自启动的脚本来说,我们并没有adb那样的权限,因此很容易导致运行失败。
  2. 手动执行脚本,我们可以通过su和setenforce 0来临时获取权限,且shell脚本易于修改。

由于本次是需要进行开机启动的,那么这里笔者选择使用了c++来实现脚本,代码主体不多赘述,依个人实现。

Android.bp

脚本的主要代码编写好后,Android.bp的内容如下:

cc_binary {							//表示将脚本编译为bin文件
    name: "multi_tpinsmod",			//模块名字
    init_rc: ["multi_tpinsmod.rc"],	//模块关联的rc文件
    relative_install_path: "hw",	//是否放在hw路径下

    vendor:true,					//表示编译到vendor分区
    cflags: [						//flag,用于忽略一下warning
        "-Wall",
        "-Wextra",
        "-Werror",
    ],
    shared_libs: [					//需要使用的共享库
        "libbase",
        "liblog",
        "libprocessgroup",
        "libcutils",
        "libutils",
    ],
    srcs: [							//源文件
        "multi_tpinsmod.cpp",
    ],
}

rc编写

rc文件的编写则比较简单了,参考如下

on property:sys.boot_completed=1	//在开机启动完成时的动作,这里是启动服务
    start multi_tpinsmod

service multi_tpinsmod /vendor/bin/hw/multi_tpinsmod	//定义了一个服务,和该服务的执行文件路径
    disabled											//表示不随class自动启动,需要手动启动
    user root											//用户
    group shell root									//分属的用户组
    oneshot												//表示服务退出后不重新启动
    capabilities SYS_MODULE

rc文件的参考如上,其余rc,可以参考网上资料自行学习。

编译

以上代码都准备好了以后,就可以进行编译了,编译可以通过mm命令直接在源码路径下执行,也可以将其配置到方案里面编译整个sdk。

一. 调试可以在源码目录下通过mm编译,执行mm需要先在top目录执行过lunch等

二.在sdk里面编译的方法如下:

  1. 在方案目录,找一个地方,增加PACKAGE_PRODUCT +=,参考如下
#support multi_tpinsmod
PRODUCT_PACKAGES += \
    multi_tpinsmod
  1. 编译整个sdk,如果有编译错误,自行解决

  2. 编译完成后,在out目录下,搜索是否有编译出来的产物,如rc文件和bin文件

调试

编译完成后,烧录固件到机器中,然后可以通过手动执行的方式,查看脚本功能是否实现。

如果有权限报错,可以先临时关闭selinux进行调试,临时关闭selinux方式如下:

在adb中执行

setenforce 0

临时关闭selinux权限,然后手动执行脚本,确保脚本功能完成。脚本功能完成后,就可以进行脚本的权限调试了。

权限设置

更改文件的权限类型

一般来说,文件的类型在vendor目录下,是vendor_file,这种类型是没有执行权限,也就是无法通过rc来启动的,查看文件类型可以通过ls -lZ命令查看:

[Android]开机自启动脚本和selinux权限配置_第1张图片

如图所示,通过ls -lZ查看文件的类型,如果看到类型为vendor_file,则表示需要配置文件的类型,以获取执行权限。

修改步骤如下:

  1. 找到配置selinux权限位置,一般来说,可以通过在Android目录下执行如下命令来获取selinux的配置目录,
get_build_var BOARD_SEPOLICY_DIRS
  1. 找到目录后,如笔者的配置位置在
device/softwinner/common/sepolicy/vendor
  1. 在目录下,先新建一个与脚本名字相同的te文件,如笔者的脚本名为A,那么可以新建一个文件A_default.te的文件
  2. 在新建的te文件中,编写如下:

[Android]开机自启动脚本和selinux权限配置_第2张图片

表示新建一个名字为multi_tpinsmod的domian域

  1. 在同目录下的file_contexts中,增加如下

[Android]开机自启动脚本和selinux权限配置_第3张图片

其中,前面为bin文件的路径,后面为固件格式,中间的为刚才新建的multi_tpinsmod_defalut.te中的执行类型。

  1. 完成后,重新编译固件,烧录固件,再次查看bin文件的类型,查看类型是否已经从vendor_file变成了自己所定义的multi_tpinsmod_exec类型。
  2. 临时关闭selinux权限,然后手动执行bin文件,查看串口(或者dmesg),查看是否有类似的报错信息
type=1400 audit(1629099780.612:406): avc: denied { write } for comm="tp_module_insmo" name="property_service" dev="tmpfs" ino=11726 scontext=u:r:tp_module_insmod:s0 tcontext=u:object_r:property_socket:s0 tclass=sock_file permissive=1
type=1400 audit(1629099780.612:407): avc: denied { connectto } for comm="tp_module_insmo" path="/dev/socket/property_service" scontext=u:r:tp_module_insmod:s0 tcontext=u:r:init:s0 tclass=unix_stream_socket permissive=1

type=1107 audit(1629099780.616:408): uid=0 auid=4294967295 ses=4294967295 subj=u:r:init:s0 msg='avc: denied { set } for property=persist.vendor.tp.name pid=1056 uid=0 gid=2000 scontext=u:r:tp_module_insmod:s0 tcontext=u:object_r:vendor_default_prop:s0 tclass=property_service permissive=1'

type=1400 audit(1629099780.632:409): avc: denied { sys_module } for comm="tp_module_insmo" capability=16 scontext=u:r:tp_module_insmod:s0 tcontext=u:r:tp_module_insmod:s0 tclass=capability permissive=1

type=1400 audit(1629099780.632:410): avc: denied { module_load } for comm="tp_module_insmo" path="/vendor_dlkm/lib/modules/gslX680new.ko" dev="overlay" ino=48232 scontext=u:r:tp_module_insmod:s0 tcontext=u:object_r:vendor_file:s0 tclass=system permissive=1

type=1400 audit(1629099780.832:411): avc: denied { read } for comm="tp_module_insmo" name="input" dev="sysfs" ino=8803 scontext=u:r:tp_module_insmod:s0 tcontext=u:object_r:sysfs:s0 tclass=dir permissive=1

tp_module_insmo (1056) used greatest stack depth: 9936 bytes left
type=1400 audit(1629099780.840:412): avc: denied { open } for comm="tp_module_insmo" path="/sys/class/input" dev="sysfs" ino=8803 scontext=u:r:tp_module_insmod:s0 tcontext=u:object_r:sysfs:s0 tclass=dir permissive=1

type=1400 audit(1629099780.840:413): avc: denied { read } for comm="tp_module_insmo" name="name" dev="sysfs" ino=39538 scontext=u:r:tp_module_insmod:s0 tcontext=u:object_r:sysfs:s0 tclass=file permissive=1
type=1400 audit(1629099780.840:414): avc: denied { open } for comm="tp_module_insmo" path="/sys/devices/platform/soc@2900000/5002000.twi/i2c-0/0-0040/input/input7/name" dev="sysfs" ino=39538 scontext=u:r:tp_module_insmod:s0 tcontext=u:object_r:sysfs:s0 tclass=file permissive=1

说明脚本已经基本完成,只剩余selinux权限问题,下一步就是配置权限问题。

配置selinux权限

手动配置权限

回到我们刚才创建的multi_tpinsmod_defalut.te,权限的配置则需要在该文件来完成。

解析selinux信息

我们拿一条selinux权限来看,例如:

type=1400 audit(1629099780.832:411): avc: denied { read } for comm="tp_module_insmo" name="input" dev="sysfs" ino=8803 scontext=u:r:tp_module_insmod:s0 tcontext=u:object_r:sysfs:s0 tclass=dir permissive=1

那么该语句表示需要配置的权限如下:

  1. scontext:谁需要权限,这里表示为tp_module_insmod
  2. tcontext:需要的是谁的权限,这里表示需要获取sysfs的权限
  3. tclass:需要获取的tcontext中表示的具体的类别,这里为idr
  4. avc: denied { read }:表示需要获取的是read权限

那么组合起来就是:tp_module_insmod需要获取sysfs中dir的read权限,那么,权限配置语句则为如下:

allow tp_module_insmod sysfs:dir read

以上语句就表示运行tp_module_insmod去读sysfs中的文件夹权限。

其他的可以自行完成。

使用audit2allow来快速配置权限

除了通过手动的方式配置权限,也可以通过使用在Android源码中的audit2allow工具来快速完成权限的配置。工具的使用步骤如下:

  1. 将所有相关的selinux的报错复制到一个文本中,此操作最好在selinux关闭的情况下,去完成收集,这样收集的比较全面
  2. 将log中的时间打印去除,语句以type=1400开头。
  3. 将selinux报错文件放在android的top目录中,然后执行audit2allow -i a.txt,执行结果如下:

[Android]开机自启动脚本和selinux权限配置_第4张图片

可以看到,selinux语句被解析了出来,将其复制到对应的te文件下,重新编译,看是否会有报错(audit2allow仅进行解析,部分权限是无法获取的)。

  1. 重新编译,可能会有如下报错提示:nerverallow···

nerverallow_selinux

可以看到,编译提示,不允许vendor去获取属性的设置权限,这时可以先将报错的语句给注释掉,验证其他的权限是否还存在问题。

nerverallow处理

一般来说,出现了nerverallow是比较头疼的事情,出现的原因主要是使用了google不允许的权限导致的,那么解决办法一般有2个:

  1. 在出现报错的地方,强行注释掉google不允许的权限,这样可能会造成google的gms测试无法通过
  2. 通过其他办法绕过该权限,如vendor访问system是没办法实现的,那就可以将需要访问的资源再编译一份放到vendor

prop的设置

在上面出现的vendor_default_prop是可以通过其他办法进行绕过的,一般来说google不允许一个服务区获取所有的vendor的属性设置权限,但是允许获取特定的属性组的权限,以上述的属性设置为例,设置方法如下:

  1. 获取具体需要进行设置的属性:需要在代码中查看:
persist.vendor.tp.name
persist.vendor.tp.path

可以找到是对这2个属性进行了设置和读取。

  1. 在te文件的目录下,存在一个property.te文件,在此处创建一个属性类型
+type vendor_tp_prop, property_type, vendor_property_type;
  1. 在property_contexts中,将具体的2个属性给到我们创建的vendor_tp_prop,参考如下

[Android]开机自启动脚本和selinux权限配置_第5张图片

  1. 在我们自己的te文件中,赋予属性权限,语句如下:
set_prop(multi_tpinsmod, vendor_tp_prop)
  1. 实现后,再次编译,没问题,通过。

selinux注意事项

  1. 不要跨分区调用,如system调用vendor里的文件
  2. 部分无法执行的方法可以通过属性的方式让更高权限的init来执行:如在服务里设置某个属性,然后由init在init.rc中触发相应的动作,以此来绕过权限的限制

你可能感兴趣的:(android,android,selinux)