Android SELinux

转自:https://blog.csdn.net/ch853199769/article/details/82501078

https://blog.csdn.net/su749520/article/details/82800050

 

引言

本文是对SEAndroid方面知识拾人牙慧后的一些总结。

SEAndroid是一套以SeLinux为核心的系统安全机制。 

 SELinux是一种基于域-类型(domain-type)模型的强制访问控制(MAC)安全系统,其原则是任何进程想在SELinux系统中干任何事,都必须先在安全策略的配置文件中赋予权限。凡是没有在安全策略中配置的权限,进程就没有该项操作的权限。在SELinux出现之前,Linux的安全模型是DAC(DiscretionaryAccess Control),译为自主访问控制。其核心思想是进程理论上所拥有的权限与运行它的用户权限相同。比如,以root用户启动shell,那么shell就有root用户的权限,在Linux系统上能干任何事。这种管理显然比较松散。

         在SELinux中,如果需要访问资源,系统会先进行DAC检查,不通过则访问失败,然后再进行MAC权限检查。

SEAndroid框架

         回到SEAndroid,SEAndroid的框架图如下:

 

 

 

主要分为两部分:用户空间和内核空间,两者以SELinux文件系统的接口为界。libselinux中封装了访问Security Context、加载资源安全策略和访问SELinux内核文件的接口。

先来看内核空间,在内核空间中,存在一个SELinux LSM模块,这个模块包含有一个访问向量缓冲(Access Vector Cache)和一个安全服务(Security Server)。Security Server负责安全访问控制逻辑,即由它来决定一个主体访问一个客体是否是合法的。这里说的主体一般就是指进程,而客体就是主体要访问的资源,例如文件。

在实际系统中,以/sys/fs/selinux为安装点,安装一个类型为selinuxfs的文件系统,也就是SELinux文件系统,用来与内核空间的SELinux LSM模块通信。

LSM,全称是Linux Security Model。LSM可以说是为了SELinux而设计的,但是它是一个通用的安全模块,SELinux可以使用,其它的模块也同样可以使用。这体现了Linux内核模块的一个重要设计思想,只提供机制实现而不提供策略实现。在我们这个例子中,LSM实现的就是MAC机制,而SELinux就是在这套机制下的一个策略实现。也就是说,你也可以通过LSM来实现自己的一套MAC安全机制。

SELinux、LSM和内核中的子系统是如何交互的呢?首先,SELinux会在LSM中注册相应的回调函数。其次,LSM会在相应的内核对象子系统中会加入一些Hook代码。例如,我们调用系统接口read函数来读取一个文件的时候,就会进入到内核的文件子系统中。在文件子系统中负责读取文件函数vfs_read就会调用LSM加入的Hook代码。这些Hook代码就会调用之前SELinux注册进来的回调函数,以便后者可以进行安全检查。

SELinux在进行安全检查的时候,首先是看一下自己的Access Vector Cache是否已经有缓存。如果有的话,就直接将结果返回给相应的内核子系统就可以了。如果没有的话,就需要到Security Server中去进行检查。检查出来的结果在返回给相应的内核子系统的同时,也会保存在自己的Access Vector Cache中,以便下次可以快速地得到检查结果。

流程图如下:

 

允许访

 

 

 

 

 

 

 

 

 

从图中可以看到,内核中的资源在访问的过程中,一般需要获得三次检查通过:

      1. 一般性错误检查,例如访问的对象是否存在、访问参数是否正确等。

     2. DAC检查,即基于Linux UID/GID的安全检查。

      3. SELinux检查,即基于安全上下文和安全策略的安全检查。

 

再来看用户空间,分三部分:Security Context、Security Server、SEAndroid Policy。

Security Context里保存着资源的安全上下文,整套SEAndroid系统就是基于这些安全上下文实现的。

Security Server由应用程序安装服务PackageManagerService、应用程序安装守护进程installd、应用程序进程孵化器Zygote进程以及init进程组成。其中,PackageManagerService和installd负责创建App数据目录的安全上下文,Zygote进程负责创建App进程的安全上下文,而init进程负责控制系统属性的安全访问。它有三个任务:1、在开机时将资源安全访问策略SEAndroid Policy加载进内核空间;2、去Security Context中查找安全上下文;3、获取内核空间中安全上下文对应的资源访问权限。

守护进程installd负责创建App数据目录。在创建App数据目录的时候,需要给它设置安全上下文,使得SEAndroid安全机制可以对它进行安全访问控制。Installd根据PackageManagerService传递过来的seinfo,并且调用libselinux库提供的selabel_lookup函数到前面我们分析的seapp_contexts文件中查找到对应的Type。有了这个Type之后,installd就可以给正在安装的App的数据目录设置安全上下文了,这是通过调用libselinux库提供的lsetfilecon函数来实现的。

在Android系统中,Zygote进程负责创建应用程序进程。应用程序进程是SEAndroid安全机制中的主体,因此它们也需要设置安全上下文,这是由Zygote进程来设置的。组件管理服务ActivityManagerService在请求Zygote进程创建应用程序进程之前,会到PackageManagerService中去查询对应的seinfo,并且将这个seinfo传递到Zygote进程。于是,Zygote进程在fork一个应用程序进程之后,就会使用ActivityManagerService传递过来的seinfo,并且调用libselinux库提供的selabel_lookup函数到前面我们分析的seapp_contexts文件中查找到对应的Domain。有了这个Domain之后,Zygote进程就可以给刚才创建的应用程序进程设置安全上下文了,这是通过调用libselinux库提供的lsetcon函数来实现的。

    在Android系统中,属性也是一项需要保护的资源。Init进程在启动的时候,会创建一块内存区域来维护系统中的属性,接着还会创建一个Property服务。这个Property服务通过socket提供接口给其它进程访问Android系统中的属性。其它进程通过socket来和Property服务通信时,Property服务可以获得它的安全上下文。有了这个安全上下文之后,Property服务就可以通过libselinux库提供的selabel_lookup函数到前面我们分析的property_contexts去查找要访问的属性的安全上下文了。有了这两个安全上下文之后,Property服务就可以决定是否允许一个进程访问它所指定的属性了。

SEAndroid Policy就是SEAndroid的安全策略,实际是在系统编译时生成的一个sepolicy文件,在init进程中被加载到SELinux内核中。

 

 

机制实现

要想理解SEAndroid,就先要了解它的基础——对象。主体通常是进程,是访问者,客体就是指进程被访问的资源,例如文件、系统属性等。

安全上下文实际上是一个附加在对象上的标签(Tag)。这个标签实际上就是一个字符串,它由四部分内容组成,分别是SELinux用户、SELinux角色、类型、安全级别,,每一个部分都通过一个冒号来分隔,格式为“user:role:type:sensitivity”。

例如,在开启了SEAndroid安全机制的设备上执行带-Z和-ef选项的ls命令,就可以看到一个文件的安全上下文:

-rwxr-x---root     root     u:object_r:rootfs:s0 init.rc

 

上面的命令列出文件/init.rc的安全上下文为“u:object_r:rootfs:s0”,这表明文件/init.rc的SELinux用户、SELinux角色、类型和安全级别分别为u、object_r、rootfs和s0。

又如,在开启了SEAndroid安全机制的设备上执行带-Z选项的ps命令,就可以看到一个进程的安全上下文:

LABEL                          USER     PID  PPID  NAME

u:r:init:s0                    root      1    0     /init

......

 

上面的命令列出进程init的安全上下文为“u:r:init:s0”,这表明进程init的SELinux用户、SELinux角色、类型和安全级别分别为u、r、init和s0。

         在SEAndroid中,用户、角色和安全级别都分别只有一个,故安全上下文中最重要的是类型这一属性。

         对于进程来说,SELinux用户和SELinux角色只是用来限制进程可以标注的类型。而对于文件来说,SELinux用户和SELinux角色就可以完全忽略不计。为了完整地描述一个文件的安全上下文,通常将它的SELinux角色固定为object_r,而将它的SELinux用户设置为创建它的进程的SELinux用户。

在SEAndroid中,只定义了一个SELinux用户u,因此我们通过ps -Z和ls -Z命令看到的所有的进程和文件的安全上下文中的SELinux用户都为u。同时,SEAndroid也只定义了一个SELinux角色r,因此,我们通过ps -Z命令看到的所有进程的安全上下文中的SELinux角色都为r。SELinux的配置文件有:

device/ mediate/common/BoardConfig.mk->TE环境的一些配置,如te文件的路径

system/sepolicy/public/attributes-> 所有定义的attributes都在这个文件

system/sepolicy/private/access_vectors-> 对应了每一个class可以被允许执行的命令

system/sepolicy/public/roles-> Android中只定义了一个role,名字就是r,并将r和attribute domain关联起来

system/sepolicy/private/users-> 其实是将user与roles进行了关联,设置了user的安全级别,s0为最低级是默认的级别,s15是最高的级别

system/sepolicy/private/security_classes->这个class的内容是指在android运行过程中,程序或者系统可能用到的操作的资源类型,它们在*.te文件中会用到。

system/sepolicy/public/te_macros-> 系统定义的宏全在te_macros文件

system/sepolicy/下public目录和private目录、device/sepolicy/mediate/,以及device/sepolicy/mediate/[platformcode]中的te文件 -> 一些配置的文件,包含了各种运行的规则

以上文件路径均是在MTK平台的Android 8.0项目中。以上配置文件,user和roles都比较好理解,BoardConfig.mk配置了te文件的路径:

BOARD_SEPOLICY_DIRS:= \

        device/mediatek/sepolicy/basic/non_plat\

        device/mediatek/sepolicy/bsp/non_plat \

        device/mediatek/sepolicy/full/non_plat

BOARD_PLAT_PUBLIC_SEPOLICY_DIR:= \

       device/mediatek/sepolicy/basic/plat_public \

       device/mediatek/sepolicy/bsp/plat_public \

        device/mediatek/sepolicy/full/plat_public

BOARD_PLAT_PRIVATE_SEPOLICY_DIR:= \

       device/mediatek/sepolicy/basic/plat_private \

       device/mediatek/sepolicy/bsp/plat_private \

       device/mediatek/sepolicy/full/plat_private

BOARD_PREBUILTS_FULL_PUBLIC_PLAT_DIRS:= \

       device/mediatek/sepolicy/basic/prebuilts/api/26.0/plat_public \

       device/mediatek/sepolicy/bsp/prebuilts/api/26.0/plat_public \

       device/mediatek/sepolicy/full/prebuilts/api/26.0/plat_public

BOARD_PREBUILTS_FULL_PRIVATE_PLAT_DIRS:= \

       device/mediatek/sepolicy/basic/prebuilts/api/26.0/plat_private \

       device/mediatek/sepolicy/bsp/prebuilts/api/26.0/plat_private \

       device/mediatek/sepolicy/full/prebuilts/api/26.0/plat_private

BOARD_COMPAT_MAPPING_CIL_DIRS:= \

       device/mediatek/sepolicy/full/private/compat/26.0/26.0.cil

BOARD_COMPAT_MAPPING_IGNORE_CIL_DIRS:= \

       device/mediatek/sepolicy/full/private/compat/26.0/26.0.ignore.cil

BOARD_26.0_NONPLAT_FILE:= \

        device/mediatek/sepolicy/full/prebuilts/api/26.0/nonplat_sepolicy.cil

对应路径下就是各种te文件,当然,BoardConfig.mk文件不止配置了SEAndroid。经试验,系统服务的安全上下文声明要放在BOARD_PLAT_PRIVATE_SEPOLICY_DIR中的目录下,否则不生效,SEAndroid编译生成的文件在手机系统中的system/etc/selinux目录下。

在SEAndroid中,所有的东西都被抽象成类型。进程,抽象成类型;资源,抽象成类型。属性,是类型的集合。所以,TE规则中的最小单位就是类型。一般来说,资源的类型,都定义在了security_classes文件中,句式如下:

class XXX;

type则可由开发者自己定义,可定义在一些te文件中,句式如下:

type xxx

类型又可集合成属性,属性的出现简化了TE规则的配置,例如,如果要配置m个进程对同一组资源(n个)的访问权限,没有属性的话需要一个一个去设置,要m*n行代码,而设置这n个资源为同一个属性的话,只需要m+1行代码,声明X类型有Y和Z属性句式如下:

type X,,Y,Z; 

在主体对客体,又有不同的操作类型,如读、写和创建等,这些操作类型定义在access_vectors中,如:

common file

{

         ioctl

         read

         write

         create

         getattr

         setattr

         lock

         relabelfrom

         relabelto

         append

         map

         unlink

         link

         rename

         execute

         quotaon

         mounton

}

以上的配置文件都是为了服务于te文件中的规则。te文件中除了声明类型,并关联属性之外,主要就是声明一些类型的安全权限。举个例子:

 

allow zygote{appdomain system_app}:process { getpgid setpgid };

 

  允许zygote类型的进程对appdomain和system_app类型的进程执行getpgid和setpgid操作;

 

  rule_name:allow;

 

  soruce_type:zygote;

 

  target_type:appdomain,system_app;

 

  object(security)class:process;

 

  accessvector:getpgid  setpgid;

 

“{}”可以用以表示一组type或操作集,简化了te语句的书写。除了“{}”还有其他的语法,如“~getpgid”表示process相关操作除了getpgid的操作集;“file_type -system_file”表示拥有file_type属性中除了system_file的类型集;“*”表示所有内容。

之前的例子中还要说明一下rule_name,它不止有一个allow(赋予权限),还有:

neverallow:检查安全策略文件中是否有违反该项操作的allow语句,用来阻止某些操作,allow和neverallowe不可冲突,否则会编译报错,且不可随意更改,否则可能导致CTS测试失败;

allowaudit:audit含义就是记录某项操作,默认情况下SELinux只记录那些权限检查失败的操作。allowaudit则使得权限检查成功的操作也被记录。注意:allowaudit只是允许记录,它和赋予权限没有关系。赋予权限必须且只能使用allow语句。

dontaudit:对那些权限检查失败的操作不做记录;

 

看到这里,思考一下,绝大多数文件总会归属于某一个目录,即使文件数量庞大,只有声明了目录的权限即可,那进程呢?Android系统中的重要固有进程应该都已经设置了权限,那么应用呢?原生的系统并不知道会有什么其他应用会运行在系统中,那怎么定义这些应用进程的权限呢?

答案是根据它们的特点归类。这个归类方案定义在seapp_context中:

isSystemServer=truedomain=system_server

user=systemseinfo=platform domain=system_app type=system_app_data_file

user=bluetoothseinfo=platform domain=bluetooth type=bluetooth_data_file

user=nfcseinfo=platform domain=nfc type=nfc_data_file

user=radioseinfo=platform domain=radio type=radio_data_file

user=shared_relrodomain=shared_relro

user=shellseinfo=platform domain=shell type=shell_data_file

user=_isolateddomain=isolated_app levelFrom=user

user=_appseinfo=media domain=mediaprovider name=android.process.media type=app_data_filelevelFrom=user

user=_appseinfo=platform domain=platform_app type=app_data_file levelFrom=user

user=_appisV2App=true isEphemeralApp=true domain=ephemeral_app type=app_data_filelevelFrom=user

user=_appisPrivApp=true domain=priv_app type=app_data_file levelFrom=user

user=_appminTargetSdkVersion=26 domain=untrusted_app type=app_data_file levelFrom=user

user=_appdomain=untrusted_app_25 type=app_data_file levelFrom=user

 

如这一行:

user=_appseinfo=platform domain=platform_app type=app_data_file levelFrom=user

 

意思是seinfo为platform的app的属性为platform_app,其进程的权限与platform_app相同(权限定义在platform_app.te中),产生的文件类型为app_data_file,其安全权限与app_data_file相同。Seinfo的定义在mac_permissions.xml中,主要是根据app签名区分。

还有一些语法规则如type_transition(声明主体新建某些进程或文件时,这些进程或文件会转变为另一个类型,不与主体相同)、type_change、alias等,使用不多。

 

 

了解了SEAnroid的基本原理,接下来看一下如何定制符合项目需求的安全策略,想象一下该情景,某个进程需要访问某个目录,进行操作,发现操作失败,输出log如下:

type=1400audit(1882976.149:5): avc: denied { write } for pid=3194comm="BluetoothAdapte"name="aplog" dev="mmcblk0p22" ino=88 scontext=u:r:bluetooth:s0tcontext=u:object_r:system_data_file:s0tclass=dir

这句log是违反SEAndroid  MAC访问策略的一个访问记录。scontext表示进程的SContext,u:r:bluetooth:s0,属于bluetooth域;

  tcontext表示目标的SContext,u:r:system_data_file:s0,属于system_data_file类型;

  tclass表示进程要操作的ObjectClass,dir表示目录;

  mmcblk0p22是userdata分区,write表示写操作。

  连起来就是bluetooth域的进程(BluetoothAdapte),对system_data_file类型的dir执行write操作失败。明确了失败原因,我们就可以在安全策略配置文件中定制我们自己的策略了:

  在bluetooth.te文件中

  allow bluetooth system_data_file:dirw_dir_perms;

  w_dir_perms是一个宏,其定义在global_macros中,包含了write相关操作:

  define(`w_dir_perms', `{ open search write add_nameremove_name }')

 

 

 

在项目开发中,我们在/dev目录下建立了一个新的设备文件tfa98xx,这是一个音频相关的设备文件,但是在集成framework层的代码后,总是出现下面的访问错误,应该如何处理呢?

  type=1400 audit(3635791.670:21): avc: denied{ read write } for pid=273 comm="mediaserver"name="tfa98xx" dev="tmpfs" ino=9770 scontext=u:r:mediaserver:s0tcontext=u:object_r:device:s0 tclass=chr_file

  首先,我们先看一下访问失败的原因:从log看,应该是mediaserver域的进程没有权限读写device类型的字符设备文件。那么我们能不能在mediaserver.te中加入访问权限呢?

  在domain.te中有如下定义:

  [external/sepolicy/domain.te]

  neverallow { domain -unconfineddomain -ueventd} device:chr_file { open read write };

  也就是说除了unconfineddomain和uevented域外,所有在domain域中的进程都不能对device类型的字符设备文件执行open,read,write操作。

mediaserver也属于domain域,所以肯定不能通过添加策略来设置访问权限,怎么办呢?

  在mediaserver.te中,我们发现mediaserver域是可以对audio_device类型的字符设备执行读写的:

  allow mediaserver audio_device:chr_filerw_file_perms;

  那么,能不能通过打标签的方法,把/dev/tfa98xx设置为audio_device类型呢?答案是肯定的。

  在file_context文件中设置/dev/tfa98xx的安全属性,问题解决了:

  /dev/tfa9890      u:object_r:audio_device:s0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值