精华内容
下载资源
问答
  • 从4.3开始,Android引入了一套基于SELinux的安全机制,称为SEAndroid,来加强系统安全性。接下来我们就对SEAndroid进行简要介绍和制定学习计划。在介绍SEAndroid安全机制之前,我们要先了解一下Android当前所采用的...
  • 深入理解SEAndroid.pdf

    2018-09-28 17:28:34
    SEAndroid 是 Google 在 Android 4.4 上正式推出的一套以 SELinux 为基础于核心的系统 安全机制。而 SELinux 则是由美国 NSA(国安局)和一些公司(RedHat、Tresys)设计的一 个针对 Linux 的安全加强系统。
  • 为此,NSA针对Android系统,在SELinux基础上开发了SEAndroid。本文就对SEAndroid安全机制框架进行分析,以便后面可以更好地分析其实现细节。SEAndroid安全机制所要保护的对象是系统中的资源,这些资源分布在各个子...
  • SEAndroid概括

    千次阅读 2018-07-11 23:07:29
    引言本文是对SEAndroid方面知识拾人牙慧后的一些总结。SEAndroid是一套以SeLinux为核心的系统安全机制。 SELinux是一种基于域-类型(domain-type)模型的强制访问控制(MAC)安全系统,其原则是任何进程想在SELinux...

    引言

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

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

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

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

    SEAndroid框架

             回到SEAndroidSEAndroid的框架图如下:

     

     


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

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

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

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

    SELinuxLSM和内核中的子系统是如何交互的呢?首先,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 ContextSecurity ServerSEAndroid Policy

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

    Security Server由应用程序安装服务PackageManagerService、应用程序安装守护进程installd、应用程序进程孵化器Zygote进程以及init进程组成。其中,PackageManagerServiceinstalld负责创建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.rcSELinux用户、SELinux角色、类型和安全级别分别为uobject_rrootfss0

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

    LABEL                          USER     PID  PPID  NAME

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

    ......

     

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

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

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

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

    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,并将rattribute domain关联起来

    system/sepolicy/private/users-> 其实是将userroles进行了关联,设置了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项目中。以上配置文件,userroles都比较好理解,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类型有YZ属性句式如下:

    type X,YZ; 

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

    common file

    {

             ioctl

             read

             write

             create

             getattr

             setattr

             lock

             relabelfrom

             relabelto

             append

             map

             unlink

             link

             rename

             execute

             quotaon

             mounton

    }

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

     

    allow zygoteappdomain system_app:process { getpgid setpgid };

     

      允许zygote类型的进程对appdomainsystem_app类型的进程执行getpgidsetpgid操作;

     

      rule_nameallow

     

      soruce_typezygote

     

      target_typeappdomainsystem_app

     

      objectsecurityclassprocess

     

      accessvectorgetpgid  setpgid

     

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

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

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

    allowauditaudit含义就是记录某项操作,默认情况下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

     

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

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

     

     

    了解了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表示进程的SContextu:r:bluetooth:s0,属于bluetooth域;

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

      tclass表示进程要操作的ObjectClassdir表示目录;

      mmcblk0p22userdata分区,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 };

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

    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


    展开全文
  • SELinux System - 2nd Edition + SELinux 详解 SEAndroid基础
  • SEAndroid 在架构和机制上与 SELinux 完全一样,基于移动设备的特点,SEAndroid 的只是所以移植 SELinux 的一个子集。SEAndroid 的安全检查覆盖了包括域转换、类型转换、进程相关操作、内核相关操作、文件目录相关...

    一、SE 概述

    SELinux 是由美国NSA(国安局)和 SCC 开发的 Linux的一个扩张强制访问控制安全模块。原先是在Fluke上开发的,2000年以 GNU GPL 发布。从 fedora core 2开始, 2.6内核的版本都支持SELinux。

     

    1.1、DAC 与 MAC

    在 SELinux 出现之前,Linux 上的使用的安全模型是 DAC( Discretionary Access Control 自主访问控制)。

    DAC 的核心思想很简单:进程理论上所拥有的权限与执行它的用户的权限相同。例如,以 root 用户启动 Browser,那么 Browser 就有 root 用户的权限,在 Linux 系统上能干任何事情。

    显然,DAD 管理太过宽松,只要想办法在系统上获取到 root 权限就可以了。所以后面有了 SELinux,在 DAC 之外,SELinux设计了一种新的安全模型,叫 MAC(Mandatory Access Control 强制访问控制)。

    MAC 的核心思想也很简单:任何进程想在 SELinux 系统上干任何事情,都必须在《安全策略文件》中赋予权限。

    实际使用中,Linux 系统先做 DAC 检查。如果没有通过 DAC 权限检查,则操作直接失败。通过 DAC 检查之后,再做 MAC 权限检查。关于 MAC 的安全策略文件,SELinux 有自己的一套规则来编写,这套规则被称之为 SELinux Policy 语言。

     

    1.2、SEAndroid

    Google在 Android 4.4 上正式添加以 SELinux 为基础的系统安全机制,命名为SEAndroid。SEAndroid 在架构和机制上与 SELinux 完全一样,基于移动设备的特点,SEAndroid 的只是所以移植 SELinux 的一个子集。SEAndroid 的安全检查覆盖了包括域转换、类型转换、进程相关操作、内核相关操作、文件目录相关操作、文件系统相关操作、对设备相关操作、对app相关操作、对网络相关操作、对IPC相关操作等内容。

     

    1.3、SELinux Policy

    Linux 中有两种东西,一种死的(Inactive),一种活的(Active)。死的东西就是文件(Linux哲学,万物皆文件。注意,万不可狭义解释为File);而活的东西就是进程。进程能发起动作,操作文件;而文件只能被进程操作。 根据 SELinux 规范,进程就相当于主体,文件就相当于客体。主体作为操作者,客体作为被操作者。

    在 SELinux 中,每种东西都会被 Policy 赋予一个安全属性,官方说法叫做 Security Context,Security Context 是一个字符串。根据 SELinux 规范,完整的 Secure Context 字符串主要由三个部分组成,如下:

    user:role:type[:range]

    进程的 Security Context 可通过 ps -(A)Z 命令查看:

    # ps -Z
    LABEL                          USER           PID  PPID     VSZ    RSS WCHAN            ADDR S NAME
    u:r:su:s0                      root          4017  3958   10040   2664 SyS_rt_si+          0 S sh
    u:r:su:s0                      root          4113  4017   12184   3244 0                   0 R ps
    u:r:platform_app:s0:c512,c768  u0_a32        3296   588 4144276  83536 SyS_epoll+          0 S com.android.mms

     文件的 Secure Context 可以通过 ls (file) -Z 来查看:

    # ls ./data/ -Z
    u:object_r:adb_data_file:s0           adb    
    u:object_r:aee_exp_data_file:s0       aee_exp
    
    • u :user,SEAndroid 中定义了一个用户
    • r : role,一个 u 可以属于多个 role,不同的 role 具有不同的权限。在SEAndroid中的role有两个,分别为 r 和 object_r
    • object_r:role,文件是死的东西,在 SELinux 中,死的东西都用 object_r 来表示它的 role
    • platform_app :type,表示该进程所属的 type 为 platform_app
    • adb_data_file :type,表示 adb 文件夹所属的 type 为 adb_data_file
    • s0:c512 : sensitivity,category ,是 SELinux 为了满足军用和教育行业而设计的 Multi-Level Security(MLS)机制。MLS 将系统的进程和文件进行了分级,不同级别的资源需要对应级别的进程才能访问

    Policy 是整个安全机制的核心之一,在 SEAndroid 中主要采用 Policy 中实现的两种强制访问方法:

    • TE
    • MLS

     

    1.4、TE(Type Enforcement)

    TE 是 SEAndroid 中的最主要的安全手段。MAC 的管控策略其实也就是 Type Enforcement Access Control(TE 强制访问控制)。所有关于TE的强制访问规则都被定义在了后缀为te的文件中,比如 platform_app 进程拥有什么权限,都通过在 te 文件中添加 allow 语句进行说明。

    SEAndroid 在 te 文件中定义了安全策略中最基本的参量 type,同时将具有共性的 type 归在一起构成一个称为 attribute 的集合,policy 的规则执行就能以 attribute 作为执行对象。

    Android 9.0上,SEAndroid 为所有 type 共定义了71个 attribute,定义文件路径:

    system/sepolicy/public/attributes

    里面对每一种 attribute 都注释说明了,这样我们就知道我们的 type 是属于哪一种 attribute 。

    例举几种常见的:

    domain所有的进程 type
    fs_type所有属于文件系统的 type,例如 sys 目录下创建的节点文件
    file_type所有带标签的文件 type,包括data目录下文件及 bin 文件
    exec_type可执行文件 type
    data_file_type/data 目录下文件 type
    core_data_file_type/data 目录下且不在 /data/vendor 目录下的文件 type
    vendor_file_type/vendor 目录下的文件 type
    proc_type所有procfs文件 type
    sysfs_type所有sysfs文件 type

     

    1.5、MLS(Multi-Level Security)

    MLS (多级别安全)是另一种强制访问控制方法,特别适合于政府机密数据的访问控制,早期对计算机安全的研究大多数都是以在操作系统内实现 MLS 访问控制为目标的。所有 MLS 的使用都是建立在 TE 安全的基础之上。在SELinux中MLS是一个可选访问控制方式,而在SEAndroid中则是被作为安全访问方式的其中之一。 

    1.5.1、MLS中的相关参量

    在 SEAndroid 中 Secure Context 的第四列称为 security level,在安全上下文第四列中可以有一个或者两个security level,第一个表示低安全级别,第二个表示高安全级别。

    每个security level由两个字段组成:

    • sensitivity

    sensitivity有着严格的分级,它反应了一个有序的数据灵敏度模型,如政府分类控制中的绝密,机密和无密级。

    • category

    category是无序的,它反应的是数据划分的需要。

    要访问的数据你同时需要足够的sensivity和正确的category。在SEAndroid中 sensitivity 只有一个级别即s0,category共有1024个,因此最低安全级别就是s0,最高安全级别就是s0:c0.c1023。

    security level之间的三种运算关系:

    • dom

    需要主体sensitiviety大于客体,同时客体的category是主体的一个子集。

    • domby

    与dom完全相反

    • eq

    主体客体的sensitivity和category分别相同。

    高的security level对低的security level拥有dom,低的security level对高的security level关系为domby(与dom相反),同级的security关系是eq,这三种关系运算符是SEAndroid中特有的。

    1.5.2、MLS对进程的约束

    • 限制进程的domain转换

    对于从一个domain转换到另一个domain的操作要求两个domain的高级别security level和低级别security level同时相等才能许可转换,除非是待转换的domain属于对mls无限权限的type。

    • 限制进程读操作

    只有当主体domain的低级别security level对客体domain的低级别security level具有dom关系时,或者主体domian是属于对mls无限权限的type,主体才能对客体具有读操作的许可。

    这一读操作具体是指:

    1. 能获取进程优先权
    2. 能获取另一进程的session id
    3. 能获取到进程的group id
    4. 能获取到系统的capabilities
    5. 能获取文件的属性信息
    6. 能追踪父程序或子程序的执行
    7. 允许通过clone或fork进程实现状态信息共享

    总结一下就是MLS限制了低级别进程向高级别进程进行读的操作,即不能上读。

    • 限制进程写操作

    只有当主体domain的低级别security level对客体domain的低级别security level具有domby关系时,或者主体domain是属于对mls无限权限的type,主体才能对客体具有写操作的许可。

    写操作具体是指:

    1. 能发送SIGKILL信号
    2. 能发送SIGSTOP信号
    3. 能发送一个非SIGKILL、SIGSTOP、SIGCHLD的信号
    4. 能设置进程优先级
    5. 能设置进程group id
    6. 能设置系统的capabilities
    7. 能改变进程hard limits
    8. 能追踪父程序或子程序的执行
    9. 允许通过clone或fork进程实现状态信息共享

    总结一下就是MLS限制了高级别进程对低级别进程写的操作,即不能下写。

    1.5.3、MLS对socket的约束

    • 进程对local socket的访问限制

    只有当主体进程的domain的高级别security level和低级别security level分别与客体local socket的type的security level相同时即满足eq关系时,或者主体客体任何一个具有对mls无限权限的type时,主体进程才对local socket拥有了某些访问权限。

    这些访问权限是指:

    1. 读操作
    2. 写操作
    3. 新建操作
    4. 能获取对象属性信息
    5. 能设置对象的属性信息
    6. 能对对象重新标记安全上下文
    7. 能进行bind操作
    8. 能发起一个连接请求
    9. 能监听连接事件
    10. 能接受一个连接请求
    11. 能获取到socket options
    12. 能关闭socket连接
    • 对socket的datagram发送的限制

    只有当发送方的低级别security level与接受方的低级别security level满足domby关系时,或者主体客体任何一个具有对mls无限权限的type时,发送方才对接受方拥有了发送权限。

    • 对客户端socket和服务端socket建立连接的限制

    只有当客户端的低级别security level与服务端的低级别security level满足eq关系时,或者主体客体任何一个具有对mls无限权限的type时,客户端就能获得连接服务端的权限。

    1.5.4、MLS对文件和目录的约束

    • 对文件的创建和重新标记的限制

    对文件操作时,要求客体的文件安全上下文只有一个security level即没有低级别和高级别security level或者说是这两个级别相同。 当主体domain的低级别security level对客体文件的低级别security level相同时,或者主体具有对mls有无限权限的type时,主体对客体文件拥有创建、和重新标记安全上下文的权限。

    • 对目录的读操作的限制

    只有当主体的低级别security level对客体目录的低级别security level满足dom关系时,或者主体客体任何一个具有对mls无限权限的type时,主体能对目录拥有如下权限:

    1. 能读目录
    2. 能获得目录的属性信息
    3. 能获得某个正在被访问文件的所有上层目录访问权(search权限)

    总结一下就是对目录的访问不能上读。

    • 对文件的读操作的限制

    只有当主体的低级别security level对客体文件的低级别security level满足dom关系时,或者主体客体任何一个具有对mls无限权限的type时,主体能对文件拥有如下权限:

    1. 能读文件
    2. 能获得文件的属性信息
    3. 能执行该文件

    总结一下就是对文件的访问不能上读。

    • 对目录的写操作的限制

    只有当主体的低级别security level对客体目录的低级别security level满足domby关系时,或者主体客体任何一个具有对mls无限权限的type时,主体能对目录拥有如下权限:

    1. 能对目录写操作
    2. 能设置属性信息
    3. 能重命名目录
    4. 能添加一个文件到目录中
    5. 能从目录中删除一个文件
    6. 能改变父目录
    7. 能删除一个空目录

    总结一下就是对目录访问不能下写。

    • 对文件的写操作的限制

    只有当主体的低级别security level对客体文件的低级别security level满足domby关系时,或者主体客体任何一个具有对mls无限权限的type时,主体能对文件拥有如下权限:

    1. 能对文件进行写操作
    2. 能设置文件属性信息
    3. 能对文件内容作append操作
    4. 能对文件创建链接
    5. 能删除一个文件的链接
    6. 能对文件重命名

    总结一下就是对文件访问不能下写。

    1.5.5、MLS对IPC的约束

    • 对IPC创建和销毁的限制

    要求客体的IPC对象只有一个security level。

    只有当主体的低级别security level与客体的低级别security level满足eq关系时,或者主体具有对mls无限权限的type时,主体能对客体IPC拥有创建和销毁的权限。

    • 对IPC读操作的限制

    只有当主体的低级别security level对客体IPC的低级别security level满足dom关系时,或者主体具有对mls无限权限的type时,主体能对客体IPC拥有如下权限:

    1. 获取文件属性信息
    2. 能对IPC文件执行读操作
    3. 能关联一个key
    4. 能执行由IPC操作要求的读操作

    总结一下就是对IPC访问不能上读。

    • 对IPC写操作的限制

    只有当主体的低级别security level对客体IPC的低级别security level满足domby关系时,或者主体具有对mls无限权限的type时,主体能对客体IPC拥有如下权限:

    1. 能对文件执行write和append操作
    2. 能执行由IPC操作要求的write和append操作

    总结一下就是对IPC访问不能下写。

     

    二、新增自定义 type

    Android 对各种 type 在 file_contexts 进行了定义,并在 file.te 中对 type attribute 进行声明和注册。系统自带的 file_contexts 和 file.te 在 system/sepolicy/ 目录下几个文件夹中,包含所有默认的type。

    但在实际使用中,我们可能会遇到需要在 /data 目录下新增一个自定义文件,那么如果使用系统默认的 type ,/data 目录对应 system_data_file ,如果直接对 system_data_file 授 te 权限,可能给的太多了,不符合最小权限安全理念了。这个时候就需要我们再细分一下,例如 /data/adb 文件 type 是 adb_data_file 。

    2.1、file_contexts 中添加 type

    # device/sepolicy/private/file_contexts 
    
    /data/test(/.*)?                                         u:object_r:test_data_file:s0

    再文件中新建 test_data_file type,注意因为是文件(死的),所以 role 是 object_r 。这样就表示 /data/test 目录下文件就都属于 test_data_file 的。

    2.2、file.te 中对新建 type 进行关联说明

    # device/sepolicy/public/file.te
    type test_data_file, file_type, data_file_type, core_data_file_type;

    data 目录下文件,就是对应的 file_type data_file_type core_data_file_type 这三种 attribute ,不知道为啥的同学往上面翻翻。如果少写了也没啥事,编译时系统会去check的,会有 error 提示。然后自己加就行。

    2.3、修改相关 te allow 权限

    一般这一步应该是在看到访问我们新增 data/test/ 目录文件报错时候再根据 logcat 中的 avc 信息去添加。不过我们也可以偷懒,在知道会报啥错情况下,比如 SystemServer 中访问我们新建目录文件,那就去 system_server.te 中添加权限

    allow system_server test_data_file:dir { relabelto search };
    allow system_server test_data_file:file rw_file_perms;

    rw_file_perms 这个宏表示读写所有权限。这样我们就完成一个自定义目录的 te 权限流程。

    需要注意,系统的 file_context 加载会在 init 的时候进行,所以这个目录文件创建一定要在加载之前,等到系统服务起来再创建就已经晚了, SELinux 的 context 会因为找不到目录就过滤新建的规则了。

    那如果必须要在java层去创建呢?我们可以在创建完成后,执行下如下指令,重新加载下 SELinux Context

    restorecon /data/test/test.txt

    参考链接

    SEAndroid策略

    android 8.1 安全机制 — SEAndroid & SELinux

    Selinux小结

    展开全文
  • 这是SEAndroid主要负责人关于SEAndroid的一篇论文,比较系统地描述了整个安全子系统的设计初衷 。
  • SEAndroid

    2021-10-12 16:55:33
    深入理解SELinux SEAndroid(第一部分) 深入理解SELinux SEAndroid之二 深入理解SELinux SEAndroid(最后部分) SELinux/SEAndroid 实例简述(一) 基础概念 SELinux/SEAndroid 实例简述(二) TE语言规则 SELinux/...
    展开全文
  • 在 SELinux 中,每种东西都会被赋予一个安全属性,官方说法叫做 Security Context,Security Context 是一个字符串,主要由三个部分组成,例如 SEAndroid 中,进程的 Security Context 可通过 ps -Z 命令查看: ...

    1. SELinux 背景知识

    详细了解 Android 8.0 SELinux,可以参阅 Google 官方文档

    1.1 DAC 与 MAC

    在 SELinux 出现之前,Linux 上的安全模型叫 DAC,全称是 Discretionary Access Control,翻译为自主访问控制。

    DAC 的核心思想很简单,就是:进程理论上所拥有的权限与执行它的用户的权限相同。比如,以 root 用户启动 Browser,那么 Browser 就有 root 用户的权限,在 Linux 系统上能干任何事情。

    显然,DAD 管理太过宽松,只要想办法在 Android 系统上获取到 root 权限就可以了。那么 SELinux 是怎么解决这个问题呢?在 DAC 之外,它设计了一种新的安全模型,叫 MAC(Mandatory Access Control),翻译为强制访问控制。

    MAC 的理论也很简单,任何进程想在 SELinux 系统上干任何事情,都必须在《安全策略文件》中赋予权限,凡是没有出现在安全策略文件中的权限,就不行。

    关于 DAC 和 MAC,可以总结几个知识点:
    1. Linux 系统先做 DAC 检查。如果没有通过 DAC 权限检查,则操作直接失败。通过 DAC 检查之后,再做 MAC 权限检查
    2. SELinux 有自己的一套规则来编写安全策略文件,这套规则被称之为 SELinux Policy 语言。

    1.2 SEPolicy 语言

    Linux中有两种东西,一种死的(Inactive),一种活的(Active)。死的东西就是文件(Linux哲学,万物皆文件。注意,万不可狭义解释为File),而活的东西就是进程。此处的 死 和 活 是一种比喻,映射到软件层面的意思是:进程能发起动作,例如它能打开文件并操作它。而文件只能被进程操作。

    根据 SELinux 规范,完整的 Secure Context 字符串为:user:role:type[:range]

    1.2.1 进程的 Secure Context

    在 SELinux 中,每种东西都会被赋予一个安全属性,官方说法叫做 Security Context,Security Context 是一个字符串,主要由三个部分组成,例如 SEAndroid 中,进程的 Security Context 可通过 ps -Z 命令查看:

    rk3288:/ $ ps -AZ
    u:r:hal_wifi_supplicant_default:s0 wifi      1816     1   11388   6972 0                   0 S wpa_supplicant
    u:r:platform_app:s0:c512,c768  u0_a14        1388   228 1612844  57396 0                   0 S android.ext.services
    u:r:system_app:s0              system        1531   228 1669680 119364 0                   0 S com.android.gallery3d
    u:r:kernel:s0                  root           582     2       0      0 0                   0 S [kworker/1:2]
    u:r:radio:s0                   radio          594   228 1634876  89296 0                   0 S com.android.phone
    u:r:system_app:s0              system         672   228 1686204 141716 0                   0 S com.android.settings
    u:r:platform_app:s0:c512,c768  u0_a18         522   223 1721656 152116 0                   0 S com.android.systemui
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    上面的最左边的一列就是进程的 Security Context,以第一个进程 wpa_supplicant 为例

    u:r:hal_wifi_supplicant_default:s0
     
    • 1

    其中
    - u 为 user 的意思,SEAndroid 中定义了一个 SELinux 用户,值为 u
    - r 为 role 的意思,role 是角色之意,它是 SELinux 中一个比较高层次,更方便的权限管理思路。简单点说,一个 u 可以属于多个 role,不同的 role 具有不同的权限。
    - hal_wifi_supplicant_default 代表该进程所属的 Domain 为 hal_wifi_supplicant_default。MAC(Mandatory Access Control)强制访问控制 的基础管理思路其实是 Type Enforcement Access Control(简称TEAC,一般用TE表示),对进程来说,Type 就是 Domain,比如 hal_wifi_supplicant_default 需要什么权限,都需要通过 allow 语句在 te 文件中进行说明。
    - s0 是 SELinux 为了满足军用和教育行业而设计的 Multi-Level Security(MLS)机制有关。简单点说,MLS 将系统的进程和文件进行了分级,不同级别的资源需要对应级别的进程才能访问

    1.2.2 文件的 Secure Context

    文件的 Secure Context 可以通过 ls -Z 来查看,如下

    rk3288:/vendor/lib $ ls libOMX_Core.so -Z
    u:object_r:vendor_file:s0 libOMX_Core.so
     
    • 1
    • 2
    • u:同样是 user 之意,它代表创建这个文件的 SELinux user
    • object_r:文件是死的东西,它没法扮演角色,所以在 SELinux 中,死的东西都用 object_r 来表示它的 role
    • vendor_file:type,和进程的 Domain 是一个意思,它表示 libOMX_Core.so 文件所属的 Type 是 vendor_file
    • s0:MLS 的等级

    1.3 TE 介绍

    MAC 基本管理单位是 TEAC(Type Enforcement Accesc Control),然后是高一级别的 Role Based Accesc Control。RBAC 是基于 TE 的,而 TE 也是 SELinux 中最主要的部分。上面说的 allow 语句就是 TE 的范畴。

    根据 SELinux 规范,完整的 SELinux 策略规则语句格式为:

    allow domains types:classes permissions;
    
    - Domain - 一个进程或一组进程的标签。也称为域类型,因为它只是指进程的类型。
    - Type - 一个对象(例如,文件、套接字)或一组对象的标签。
    - Class - 要访问的对象(例如,文件、套接字)的类型。
    - Permission - 要执行的操作(例如,读取、写入)。
    
    = allow : 允许主体对客体进行操作
    = neverallow :拒绝主体对客体进行操作
    = dontaudit : 表示不记录某条违反规则的决策信息
    = auditallow :记录某项决策信息,通常 SElinux 只记录失败的信息,应用这条规则后会记录成功的决策信息。
    
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    使用政策规则时将遵循的结构示例:

    语句:
    allow appdomain app_data_file:file rw_file_perms;
    
    这表示所有应用域都可以读取和写入带有 app_data_file 标签的文件
     
    • 1
    • 2
    • 3
    • 4
    —> 相关实例
    1. SEAndroid 中的安全策略文件 policy.conf
    # 允许 zygote 域中的进程向 init 域中的进程(Object Class 为 process)发送 sigchld 信号
    
    allow zygote init:process sigchld;
    
    2. # 允许 zygote 域中的进程 search 或 getattr 类型为 appdomain 的目录。
       # 注意,多个 perm_set 可用 {} 括起来
       allow zygote appdomain:dir { getattr search };
    
    3. # perm_set 语法比较奇特,前面有一个 ~ 号。
       # 它表示除了{entrypoint relabelto}之外,{chr_file #file}这两个object_class所拥有的其他操作 
      allow unconfineddomain {fs_type dev_type file_type}:{ chr_file file }   \
      ~{entrypoint relabelto};
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    Object Class 类型
    文件路径: system/sepolicy/private/security_classes
    
    # file-related classes
    class filesystem
    class file  #代表普通文件
    class dir   #代表目录
    class fd    #代表文件描述符
    class lnk_file  #代表链接文件
    class chr_file  #代表字符设备文件
    
    # network-related classes
    class socket   #socket
    class tcp_socket
    class udp_socket
    
    ......
    class binder   #Android 平台特有的 binder
    class zygote   #Android 平台特有的 zygote
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    Perm Set 类型

    Perm Set 指得是某种 Object class 所拥有的权限。以 file 这种 Object class 而言,其拥有的 Perm Set 就包括 read、write、open、create、execute等。

    和 Object Class 一样,SELinux 或 SEAndroid 所支持的 Perm set 也需要声明:

    文件路径: system/sepolicy/private/access_vectors
     
    • 1

    2. SELinux 相关设置

    2.1 强制执行等级

    熟悉以下术语,了解如何按不同的强制执行级别实现 SELinux
    - 宽容模式(permissive) - 仅记录但不强制执行 SELinux 安全政策。
    - 强制模式(enforcing) - 强制执行并记录安全政策。如果失败,则显示为 EPERM 错误。

    2.2 关闭 SELinux

    临时关闭

    (1) setenforce

    $ setenforce 0
     
    • 1

    setenforce 命令修改的是 /sys/fs/selinux/enforce 节点的值,是 kernel 意义上的修改 selinux 的策略。断电之后,节点值会复位

    永久关闭

    (2) kernel 关闭 selinux

    SECURITY_SELINUX 设置为 false,重新编译 kernel
     
    • 1

    (3) 设置 ro.boot.selinux=permissive 属性,并且修改在 system/core/init/Android.mk 中设置用于 user 版本下 selinux 模式为 permissive

    ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
    init_options += \
        -DALLOW_LOCAL_PROP_OVERRIDE=1 \
        -DALLOW_PERMISSIVE_SELINUX=1 \                                                                                              
        -DREBOOT_BOOTLOADER_ON_PANIC=1 \
        -DDUMP_ON_UMOUNT_FAILURE=1
    else
    init_options += \
        -DALLOW_LOCAL_PROP_OVERRIDE=0 \
        -DALLOW_PERMISSIVE_SELINUX=1 \ // 修改为1,表示允许 selinux 为 permissive
        -DREBOOT_BOOTLOADER_ON_PANIC=0 \
        -DDUMP_ON_UMOUNT_FAILURE=0
    endif
    
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.3 SELinux 权限不足 avc-denied 问题解决

    目前所有的 SELinux 权限检测失败,在 Kernel Log 或者 Android Log 中都有对应的 avc-denied Log 与之对应。反过来,有 avc-denied Log,并非就会直接失败,还需要确认当时 SELinux 的模式, 是 Enforcing 还是 Permissve。

    如果是 Enforcing 模式,就要检测对应的进程访问权限资源是否正常?是否有必要添加? 如果有必要添加,可以找下面的方式生成需要的 sepolicy 规则并添加到对应 te 文件。

    使用 audit2allow 简化方法
    1. 从 logcat 或串口中提取相应的 avc-denied log,下面的语句为提取所有的 avc- denied log

    $ adb shell "cat /proc/kmsg | grep avc" > avc_log.txt 
     
    • 1
    1. 使用 audit2allow 工具生成对应的 policy 规则
    // audio2allow 使用必须先 source build/envsetup.sh,导入环境变量
    $ audit2allow -i avc_log.txt -p $OUT/vendor/etc/selinux/precompiled_sepolicy
    3. 将对应的policy 添加到 te 文件中
        = 一般添加在 /device/<company>/common/sepolicy 或者 /device/<company>/$DEVICE/sepolicy 目录下
     
    • 1
    • 2
    • 3
    • 4

    BOARD_SEPOLICY_DIRS += device/$SoC/common/sepolicy 通过这个命令添加厂家自定义的 sepolicy 规则

    3. SEAndroid 安全机制框架

    SELinux 系统比起通常的 Linux 系统来,安全性能要高的多,它通过对于用户,进程权限的最小化,即使受到攻击,进程或者用户权限被夺去,也不会对整个系统造成重大影响。

    我们知道,Android 系统是基于 Linux 内核实现,针对 Linux 系统,NSA 开发了一套安全机制 SELinux,用来加强安全性。然而,由于 Android 系统有着独特的用户空间运行时,因此 SELinux 不能完全适用于 Android 系统。为此,NSA 针对 Android 系统,在 SELinux 基础上开发了 SEAndroid。

    SEAndroid 安全机制所要保护的对象是系统中的资源,这些资源分布在各个子系统中,例如我们经常接触的文件就是分布文件子系统中的。实际上,系统中需要保护的资源非常多,除了前面说的文件之外,还有进程、socket 和 IPC 等等。对于 Android 系统来说,由于使用了与传统 Linux 系统不一样的用户空间运行时,即应用程序运行时框架,因此它在用户空间有一些特有的资源是需要特别保护的,例如系统属性的设置等。

    3.1 SEAndroid 框架流程

    SEAndroid 安全机制的整体框架,可以使用下图来概括:

    SEAndroid_frame

    以 SELinux 文件系统接口问边界,SEAndroid 安全机制包含内核空间和用户空间两部分支持。

    这些内核空间模块与用户空间模块空间的作用及交互有:

    1. 内核空间的 SELinux LSM 模块负责内核资源的安全访问控制
    2. 用户空间的 SEAndroid Policy 描述的是资源安全访问策略。
       系统在启动的时候,用户空间的 Security Server 需要将这些安全访问策略加载内核空间的 SELinux LSM 模块中去。
       这是通过SELinux文件系统接口实现的
    3. 用户空间的 Security Context 描述的是资源安全上下文。
       SEAndroid 的安全访问策略就是在资源的安全上下文基础上实现的
    4. 用户空间的 Security Server 一方面需要到用户空间的 Security Context 去检索对象的安全上下文,
       另一方面也需要到内核空间去操作对象的安全上下文
    5. 用户空间的 libselinux 库封装了对 SELinux 文件系统接口的读写操作。
       用户空间的 Security Server 访问内核空间的 SELinux LSM 模块时,都是间接地通过 libselinux进行的。
       这样可以将对 SELinux 文件系统接口的读写操作封装成更有意义的函数调用。
    6. 用户空间的 Security Server 到用户空间的 Security Context 去检索对象的安全上下文时,同样也是通过 selinux 库来进行的
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3.1.1 内核空间

    1. 在内核空间,存在一个 SELinux LSM(Linux Secrity Moudle)模块,(用 MAC 强制访问控制)负责资源的安全访问控制。
    2. LSM 模块中包含一个访问向量缓冲(Access Vector Cache)和一个安全服务(Security Server)。Security Server 负责安全访问控制逻辑,即由它来决定一个主体访问一个客体是否是合法的,这个主体一般是指进程,而客体主要指资源,例如文件。
    3. SELinux、LSM 和内核中的子系统是如何交互的呢?首先,SELinux 会在 LSM 中注册相应的回调函数。其次,LSM 会在相应的内核对象子系统中会加入一些 Hook 代码。例如,我们调用系统接口 read 函数来读取一个文件的时候,就会进入到内核的文件子系统中。在文件子系统中负责读取文件函数 vfs_read 就会调用 LSM 加入的 Hook 代码。这些 Hook 代码就会调用之前 SELinux 注册进来的回调函数,以便后者可以进行安全检查。
    4. SELinux 在进行安全检查的时候,首先是看一下自己的 Access Vector Cache 是否已经有结果。如果有的话,就直接将结果返回给相应的内核子系统就可以了。如果没有的话,就需要到 Security Server 中去进行检查。检查出来的结果在返回给相应的内核子系统的同时,也会保存在自己的 Access Vector Cache 中,以便下次可以快速地得到检查结果

    上面概述的安全访问控制流程,可以使用下图来总结:

    SELinux_check_logic

    1. 一般性错误检查,例如访问的对象是否存在、访问参数是否正确等
    2. DAC 检查,即基于 Linux UID/GID 的安全检查
    3. SELinux 检查,即基于安全上下文和安全策略的安全检查
     
    • 1
    • 2
    • 3

    3.1.2 用户空间

    在用户空间,SEAndorid 主要包含三个模块,分别是安全上下文(Security Context)、安全策略(SEAndroid Policy)和安全服务(Security Server)。

    (1)安全上下文

    前面已经描述过了,SEAndroid 是一种基于安全策略的 MAC 安全机制。这种安全策略又是建立在对象的安全上下文的基础上的,SEAndroid 中的对象分为主体(Subject)和客体(Object),主体通常是指进程,而客体是指进程所要访问的资源,如文件、系统属性等

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

    用 ps -Z 来查看主体的安全上下文,而用 ls -Z 来查看客体的安全上下文。

    安全上下文<类型>说明

    通常将用来标注文件的安全上下文中的类型称为 file_type,用来标注进程的安全上下文的类型称为 domain。

    将两个类型相关联可以通过 type 语句实现,例如用来描述 init 进程安全策略文件 /system/sepolicy/public/init.te 文件中,使用 type 语句将 init 与 domain 相关联(等价)

    type init domain;
    // 这样就可以表明 init 描述的类型是用来描述进程的安全上下文的
     
    • 1
    • 2
    Android 系统中对象的安全上下文定义

    系统中各种类型对象(包含主体和客体)的安全上下文是在 system/sepolicy 工程中定义的,我们讨论四种类型对象的安全上下文,分别是 app 进程、app 数据文件、系统文件和系统属性。这四种类型对象的安全上下文通过四个文件来描述:mac_permissions.xml、seapp_contexts、file_contexts 和 property_contexts。

    mac_permissions 文件给不同签名的 app 分配不同的 seinfo 字符串,例如,在 AOSP 源码环境下编译并且使用平台签名的 App 获得的 seinfo 为 “platform”,使用第三方签名安装的 App 获得的 seinfo 签名为”default”。

    <!-- Platform dev key in AOSP -->
    <signer signature="@PLATFORM" >
      <seinfo value="platform" />
    </signer>
    
    <!-- Media key in AOSP -->
    <signer signature="@MEDIA" >
      <seinfo value="media" />
    </signer>
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这里的 seinfo 描述的并不是安全上下文的对象类型,它用来在 seapp_contexts 中查找对应的对象类型。在 seapp_contexts 对 seinfo 也就是平台签名为”platform”的 app 如下定义:

    user=_app seinfo=platform domain=platform_app type=app_data_file levelFrom=user
    
    也就是说明使用"platform"平台签名的 app 所运行的进程 domain 为"platform_app",
    并且他的数据文件类型为"app_data_file"
     
    • 1
    • 2
    • 3
    • 4

    接下来看一下系统文件的安全上下文是如何定义的:

    ###########################################
    # Root
    /                   u:object_r:rootfs:s0
    
    # Data files
    /adb_keys           u:object_r:adb_keys_file:s0
    /build\.prop        u:object_r:rootfs:s0
    /default\.prop      u:object_r:rootfs:s0
    /fstab\..*          u:object_r:rootfs:s0
    /init\..*           u:object_r:rootfs:s0
    /res(/.*)?          u:object_r:rootfs:s0
    /selinux_version    u:object_r:rootfs:s0
    /ueventd\..*        u:object_r:rootfs:s0
    /verity_key         u:object_r:rootfs:s0
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    可以看到使用正则表达式描述了系统文件的安全上下文。

    在 Android 系统中有一种比较特殊的权限——属性,app 能够读写他们获取相应的系统信息以及控制系统的行为。因此不同与 SELinux,SEAndroid 中对系统属性也进行了保护,这意味着 Android 系统的属性也需要关联安全上下文。这是在 property_contexts 文件中描述的

    ##########################
    # property service keys
    #
    #
    net.rmnet               u:object_r:net_radio_prop:s0
    net.gprs                u:object_r:net_radio_prop:s0
    net.ppp                 u:object_r:net_radio_prop:s0
    net.qmi                 u:object_r:net_radio_prop:s0
    net.lte                 u:object_r:net_radio_prop:s0
    net.cdma                u:object_r:net_radio_prop:s0
    net.dns                 u:object_r:net_dns_prop:s0
    sys.usb.config          u:object_r:system_radio_prop:s0
    ril.                    u:object_r:radio_prop:s0
    ro.ril.                 u:object_r:radio_prop:s0
    gsm.                    u:object_r:radio_prop:s0
    persist.radio           u:object_r:radio_prop:s0
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    这边的 net.rmnet 行类型为 net_radio_prop,意味着只有有权限访问 net_radio_prop 的进程才可以访问这个属性。

    (2)安全策略

    SEAndroid 安全机制中的安全策略是在安全上下文的基础上进行描述的,也就是说,它通过主体和客体的安全上下文,定义主体是否有权限访问客体。

    Type Enforcement

    SEAndroid 安全机制主要是使用对象安全上下文中的类型来定义安全策略,这种安全策略就称 Type Enforcement,简称TE。在 system/sepolicy 目录和其他所有客制化 te 目录(通常在 device//common,用 BOARD_SEPOLICY_DIRS 添加),所有以 .te 为后缀的文件经过编译之后,就会生成一个 sepolicy 文件。这个 sepolicy 文件会打包在ROM中,并且保存在设备上的根目录下。

    一个 Type 所具有的权限是通过allow语句来描述的

    allow unconfineddomain domain:binder { call transfer set_context_mgr };
     
    • 1

    表明 domain 为 unconfineddomain 的进程可以与其它进程进行 binder ipc 通信(call),并且能够向这些进程传递 Binder 对象(transfer),以及将自己设置为 Binder 上下文管理器(set_context_mgr)。

    注意,SEAndroid 使用的是最小权限原则,也就是说,只有通过 allow 语句声明的权限才是允许的,而其它没有通过 allow 语句声明的权限都是禁止,这样就可以最大限度地保护系统中的资源

    前面我们提到,SEAndroid 安全机制的安全策略经过编译之后会得到一个 sepolicy 文件,并且最终保存在设备上的根目录上。这个 sepolicy 文件中的安全策略是不会自动加载的到内核空间的 SELinux LSM 模块中去的,它需要我们在系统启动的时候进行加载。

    具体的源码分析在 4.2 节中。

    (3)Security Server

    Security Server 是一个比较笼统的概念,主要是用来保护用户空间资源的,以及用来操作内核空间对象的安全上下文的,它由应用程序安装服务 PackageManagerService、应用程序安装守护进程 installd、应用程序进程孵化器 Zygote 进程以及 init 进程组成。其中,PackageManagerService 和 installd 负责创建 App 数据目录的安全上下文,Zygote 进程负责创建 App 进程的安全上下文,而 init 进程负责控制系统属性的安全访问。

    PackageManagerService & installed —— app 数据目录的安全上下文

    PackageManagerService 在启动的时候,会找到我们前面分析的 mac_permissions.xml 文件,然后对它进行解析,得到 App 签名或者包名与 seinfo 的对应关系。当 PackageManagerService 安装 App 的时候,它就会根据其签名或者包名查找到对应的 seinfo,并且将这个 seinfo 传递给另外一个守护进程 installed。

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

    Zygote —— 设置进程的安全上下文

    在 Android 系统中,Zygote 进程负责创建应用程序进程。应用程序进程是 SEAndroid 安全机制中的主体,因此它们也需要设置安全上下文,这是由 Zygote 进程来设置的。

    ActivityManagerService 在请求 Zygote 进程创建应用程序进程之前,会到 PackageManagerService 中去查询对应的 seinfo,并且将这个 seinfo 传递到 Zygote 进程。于是,Zygote 进程在 fork 一个应用程序进程之后,就会使用 ActivityManagerService 传递过来的 seinfo,并且调用 libselinux 库提供的 selabel_lookup 函数到前面我们分析的 seapp_contexts 文件中查找到对应的 Domain。有了这个 Domain 之后,Zygote 进程就可以给刚才创建的应用程序进程设置安全上下文了,这是通过调用 libselinux 库提供的 lsetcon 函数来实现的。

    init —— 系统属性的安全上下文

    init 进程在启动的时候会创建一块内存区域来维护系统中的属性,接着还会创建一个 Property 服务系统,这个服务系统通过 socket 提供接口给其他进程访问 android 系统中的属性。

    其他进程通过 socket 和属性系统通信请求访问某项系统属性的值,属性服务系统可以通过 libselinux 库提供的 selabel_lookup 函数到前面我们分析的 property_contexts 中查找要访问的属性的安全上下文了。有了该进程的安全上下文和要访问属性的安全上下文之后,属性系统就能决定是否允许一个进程访问它所指定的服务了。

    4. SEAndroid 源码分析

    4.1 SEAndroid 源码架构

    - externel/selinux:包含编译 sepolicy 策略文件的一些实用构建工具
        - external/selinux/libselinux:提供了帮助用户进程使用 SELinux 的一些函数
        - external/selinux/libsepol:提供了供安全策略文件编译时使用的一个工具 checkcon
    - system/sepolicy:包含 Android SELinux 核心安全策略(te 文件),编译生成 sepolicy 文件
        - file_contexts: 系统中所有文件的安全上下文
        - property_contexts: 系统中所有属性的安全上下文
        - seapp_contexts:定义用户、seinfo和域之间的关系,用于确定用户进程的安全上下文
        - sepolicy:二进制文件,保存系统安全策略,系统初始化时会把它设置到内核中
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    SELinux 虚拟文件系统在 sys/fs/selinux 下,该目录下的文件是 SELinux 内核和用户进程进行通信的接口,libselinux 就是利用这边的接口进行操作

    4.2 init 进程 SEAndroid 启动源码分析

    文件路径:/system/core/init/init.cpp
    
    int main(int argc, char** argv) {
        ...
        if (is_first_stage) {
            ... 
    
            // Set up SELinux, loading the SELinux policy.
            selinux_initialize(true); 
    
            // We're in the kernel domain, so re-exec init to transition to the init domain now
            // that the SELinux policy has been loaded.
            if (selinux_android_restorecon("/init", 0) == -1) {
                PLOG(ERROR) << "restorecon failed";
                security_failure();
            }
            ...
        }
    }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    可以看到 SEAndroid 的启动设置在 init 进程内核态执行(first_stage)过程中,selinux_initialize 函数就是主要的初始化过程,包含加载 sepolicy 策略文件到内核的 LSM 模块中。

    文件路径:/system/core/init/init.cpp
    
    static void selinux_initialize(bool in_kernel_domain) {
        Timer t;
    
        selinux_callback cb;
        cb.func_log = selinux_klog_callback;
        selinux_set_callback(SELINUX_CB_LOG, cb);
        cb.func_audit = audit_callback;
        selinux_set_callback(SELINUX_CB_AUDIT, cb);
    
        // 标识 init 进程的内核态执行和用户态执行
        if (in_kernel_domain) {
            LOG(INFO) << "Loading SELinux policy";
            if (!selinux_load_policy()) {
                panic();
            }
    
            bool kernel_enforcing = (security_getenforce() == 1);
            bool is_enforcing = selinux_is_enforcing();
            if (kernel_enforcing != is_enforcing) {
                if (security_setenforce(is_enforcing)) {
                    PLOG(ERROR) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
                    security_failure();
                }
            }
    
            std::string err;
            if (!WriteFile("/sys/fs/selinux/checkreqprot", "0", &err)) {
                LOG(ERROR) << err;
                security_failure();
            }
    
            // init's first stage can't set properties, so pass the time to the second stage.
            setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
        } else {
            selinux_init_all_handles();
        }
    }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    selinux_set_callback

    selinux_set_callback 用来向 libselinux 设置 SEAndroid 日志和审计回调函数

    selinux_load_policy

    这个函数用来加载 sepolicy 策略文件,并通过 mmap 映射的方式将 sepolicy 的安全策略加载到 SELinux LSM 模块中去。

    下面具体分析一下流程:

    文件路径:system/core/init/init.cpp
    
    static bool selinux_load_policy() {
        return selinux_is_split_policy_device() ? selinux_load_split_policy()
                                                : selinux_load_monolithic_policy();
    }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这里区分了从哪里加载安全策略文件,第一个是从 /vendor/etc/selinux/precompiled_sepolicy 读取,第二个是直接从根目录 /sepolicy
    中读取,最终调用的方法都是 selinux_android_load_policy_from_fd

    int selinux_android_load_policy_from_fd(int fd, const char *description)
    {
        int rc;
        struct stat sb;
        void *map = NULL;
        static int load_successful = 0;
    
        /*
         * Since updating policy at runtime has been abolished
         * we just check whether a policy has been loaded before
         * and return if this is the case.
         * There is no point in reloading policy.
         */
        if (load_successful){
          selinux_log(SELINUX_WARNING, "SELinux: Attempted reload of SELinux policy!/n");
          return 0;
        }
    
        set_selinuxmnt(SELINUXMNT);
        if (fstat(fd, &sb) < 0) {
            selinux_log(SELINUX_ERROR, "SELinux:  Could not stat %s:  %s\n",
                    description, strerror(errno));
            return -1;
        }
        map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
        if (map == MAP_FAILED) {
            selinux_log(SELINUX_ERROR, "SELinux:  Could not map %s:  %s\n",
                    description, strerror(errno));
            return -1;
        }
    
        rc = security_load_policy(map, sb.st_size);
        if (rc < 0) {
            selinux_log(SELINUX_ERROR, "SELinux:  Could not load policy:  %s\n",
                    strerror(errno));
            munmap(map, sb.st_size);
            return -1;
        }
    
        munmap(map, sb.st_size);
        selinux_log(SELINUX_INFO, "SELinux: Loaded policy from %s\n", description);
        load_successful = 1;
        return 0;
    }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    (1)set_selinuxmnt 函数设置内核 SELinux 文件系统路径,这里的值为 /sys/fs/selinux,SELinux 文件系统用来与内核空间 SELinux LSM 模块空间。

    (2)通过 fstat 获取 sepolicy 文件(fd 值)的状态信息,通过 mmap 函数将文件内容映射到内存中,起始地址为 map

    (3)security_load_policy 调用另一个 security_load_policy 函数将已经映射到内存中的 SEAndroid 的安全策略加载到内核空间的 SELinux LSM 模块中去。

    int security_load_policy(void *data, size_t len)
    {
        char path[PATH_MAX];
        int fd, ret;
    
        if (!selinux_mnt) {
            errno = ENOENT;
            return -1;
        }
    
        snprintf(path, sizeof path, "%s/load", selinux_mnt);
        fd = open(path, O_RDWR | O_CLOEXEC);
        if (fd < 0)
            return -1;
    
        ret = write(fd, data, len);
        close(fd);
        if (ret < 0)
            return -1;
        return 0;
    }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    函数 security_load_policy 的实现很简单,它首先打开 /sys/fs/selinux/load 文件,然后将参数 data 所描述的安全策略写入到这个文件中去。由于 /sys/fs/selinux 是由内核空间的 SELinux LSM 模块导出来的文件系统接口,因此当我们将安全策略写入到位于该文件系统中的 load 文件时,就相当于是将安全策略从用户空间加载到 SELinux LSM 模块中去了。

    以后 SELinux LSM 模块中的 Security Server 就可以通过它来进行安全检查

    (4)加载完成,释放 sepolicy 文件占用的内存,并且关闭 sepolicy 文件

    security_setenforce

    前面已经提过,selinux 有两种工作模式:
    - 宽容模式(permissive) - 仅记录但不强制执行 SELinux 安全政策。
    - 强制模式(enforcing) - 强制执行并记录安全政策。如果失败,则显示为 EPERM 错误。

    这个函数用来设置 kernel SELinux 的模式,实际上都是去操作 /sys/fs/selinux/enforce 文件, 0 表示permissive,1 表示 enforcing

    selinux_init_all_handles

    在 init 进程的用户态启动过程中会调用这个函数初始化 file_context、 property_context 相关内容 handler,根据前面的描述,init 进程给一些文件或者系统属性进行安全上下文检查时会使用 libselinux 的 API,查询文件根目录下 file_context、file_context 的安全上下文内容。所以需要先得到这些文件的 handler,以便可以用来查询系统文件和系统属性的安全上下文。

    static void selinux_init_all_handles(void)
    {
        sehandle = selinux_android_file_context_handle();
        selinux_android_set_sehandle(sehandle);
        sehandle_prop = selinux_android_prop_context_handle();
    }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    展开全文
  • seandroid论文(注释版)

    2015-07-17 16:55:19
    该文档是我的学习SEAndroid的时候加上注释之后的论文。
  • SEAndroid-for-share

    2014-10-17 19:58:20
    SEAndroid,成为Android高手进阶路上的必备
  • SEAndroid原理分析

    千次阅读 2016-01-22 23:15:21
    第1章 SEAndroid背景SEAndroid(Security Enhanced Android)是美国国家安全局(NSA)根据Android 以Linux 为内核基础的特性,将SELinux 修改移植到Android 系统上而形成的。SELinux通过提前编写各个进程的安全操作...
  • SEAndroid简介

    千次阅读 2016-03-09 19:17:25
    SEAndroid简介 http://blog.csdn.net/modianwutong/article/details/43114883  SEAndroid是Google在Android4.4上正式推出的一套以SELinux为核心的系统安全机制。而SELinux则是由NSA(美国国安局)在Linux社区的...
  • SEAndroid 浅谈

    千次阅读 2017-03-07 15:56:47
    在引入SEAndroid之前,Android的安全处理机制分为应用程序和内核两个部分。应用程序部分安全机制就是通常所说的permission,相应的权限在AndroidManifest.xml中设置,当应用需要使用系统服务的时候会提示用户来确认。...
  • Android-SEAndroid权限问题指南

    千次阅读 热门讨论 2019-08-02 17:29:10
    Android-SEAndroid权限问题指南 前言 SEAndroid是在Android系统中基于SELinux推出的强制访问控制模型,来完善自主访问模型中只要取得root权限就可以为所欲为的情况。 SELinux是一种基于域-类型(domain-type)...
  • 1.SEAndroid app分类 SELinux(或SEAndroid)将app划分为主要三种类型(根据user不同,也有其他的domain类型): 1)untrusted_app 第三方app,没有Android平台签名,没有system权限 2)platform_app 有Android平台签名,...
  • SEAndroid就是SElinux的增强型版本。 确认问题 先排查是否由SElinux策略导致的问题 串口输入:setenforce 0 串口输入:getenforce 0 即进入了permissive 1. 强制关闭SEAndroid的方法: 1.1 方法一 如果是user...
  • SEAndroid&SELinux

    2019-11-07 15:56:23
    SEAndroid是Google在Android4.4上正式推出的一套以SELinux为核心的系统安全机制。 1)LSM提供了一种通用的安全框架,允许将安全模型以模块方式载入内核, 2)AVC是一个策略缓存,当进程试图访问系统资源的时候...
  • 深入理解Android SEAndroid
  •  在用户空间中,SEAndroid包含有三个主要的模块,分别是安全上下文(Security Context)、安全策略(SEAndroid Policy)和安全服务(Security Server)。接下来我们就分别对它们进行描述。  1. 安全上下文  ...
  • SEAndroid MLS

    千次阅读 2017-03-15 17:09:19
    SEAndroid的安全上下文与SELinux基本一致(除了MLS检测在SEAndroid中被强制执行),共有4个部分组成分别为user、role、type、sensitivity,以u:object_r:system_data_file:s0为例: user:安全上下文的第一列为...
  • SEAndroid 问题解决

    千次阅读 2017-03-30 19:29:32
    注意,SEAndroid使用的是最小权限原则,也就是说,只有通过allow语句声明的权限才是允许的,而其它没有通过allow语句声明的权限都是禁止,这样就可以最大限度地保护系统中的资源。 如果我们继续分析app.te的内容,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,251
精华内容 500
关键字:

seandroid