Selinux梳理总结

前绪

该文档只是将selinux中最基础的知识点进行了整理罗列,其他还有很多高级用法及升华拓展部分,如SElinux域转换、角色转换以及SElinux框架等,并未收录,有需要了解的可以参照参考文献部分中的《SElinux详解》文档,后续会继续补充完善。

一、Selinux基础概述

SELinux则是由美国NSA(国安局)和一些公司(RedHat、Tresys)设计的一个针对Linux的安全加强系统。

NSA最初设计的安全模型叫FLASK,全称为Flux Advanced Security Kernel(由Uta大学和美国国防部开发,后来由NSA将其开源),当时这套模型针对DTOS系统。后来,NSA觉得Linux更具发展和普及前景,所以就在Linux系统上重新实现了FLASK,称之为SELinux。

Linux Kernel中,SELinux通过Linux Security Modules实现。在2.6之前,SElinux通过Patch方式发布。从2.6开始,SELinux正式入驻内核,成为标配。后来,鉴于Selinux的安全性和使用性,Google在Android 4.4上正式推出的一套以SELinux为基础于核心的系统安全机制,且命名为SEandroid,自此,selinux便被"移植"到了Android上了。

 

二、什么是Selinux?

SELinux出现之前,Linux上的安全模型叫DAC,全称是Discretionary Access Control(自主访问控制)。DAC的核心思想很简单,就是:进程理论上所拥有的权限与执行它的用户的权限相同。比如,以root用户启动Browser,那么Browser就有root用户的权限,在Linux系统上能干任何事情。

显然,DAC太过宽松了,所以各路高手想方设法都要在Android系统上搞到root权限。那么SELinux如何解决这个问题呢?原来,它在DAC之外,设计了一个新的安全模型,叫MAC(Mandatory Access Control),翻译为强制访问控制。MAC的处世哲学非常简单:即任何进程想在SELinux系统中干任何事情,都必须先在安全策略配置文件中赋予权限。凡是没有出现在安全策略配置文件中的权限,进程就没有该权限。来看一个SEAndroid中设置权限的例子:

[external/sepolicy/netd.te]

/*

允许(allow )netd域(domain)中的进程写(write)类型为proc的文件

*/

allow netd proc:file write

如果没有在netd.te中使用上例中的权限配置allow语句,则netd就无法往/proc目录下得任何文件中写数据,即使netd具有root权限。

显然,MAC比DAC在权限管理这一块要复杂,要严格,要细致得多。

关于DAC和MAC:

  • Linux系统先做DAC检查。如果没有通过DAC权限检查,则操作直接失败。通过DAC检查之后,再做MAC权限检查。

  • SELinux中也有用户的概念,但它和Linux中原有的user概念不是同一个概念。比如,Linux中的超级用户root在SELinux中可能就是一个没权限,没地位,打打酱油的"路人甲"。当然,这一切都由SELinux安全策略的制定者来决定。

这里只是借用一个例子先对selinux做一个初步的了解,下面将对selinux特有的语法做简单介绍。

三、SELinux Policy语言

在网上看到一种比喻的说法,个人感觉很有道理:Linux中有两种东西,一种死的(Inactive),一种活的(Active)。活的东西就是进程,而死的东西就是资源(eg:文件、套接字等)。他们之间就是一种操作和被操作,或者说使用与被使用的关系。进程能发起动作,例如它能打开文件并操作它。而文件只能被进程操作。SElinux Policy语言就是将死的和活的东西都给打上"标签",通过"标签"将系统内的资源(包括进程)分成不同的角色(比如:用户、客体),进而对整个系统资(包括进程)进行合理安全的管控。

1、安全属性——SContext

在SELinux世界里,每种东西都会被赋予一个安全属性,官方说法叫Security Context,也可以理解为上文所提到的"标签"。通过每个资源的属性,对他们进行归类管理。Security Context(以后用SContext表示)是一个字符串,由四部分组成,被分为两类:进程SContext和资源SContext。根据SELinux规范,完整的SContext字符串为:

user:role:type[:range]

注意,[]中的内容表示可选项。s0属于range中的一部分,表示安全级别(这里不做讨论)。

 

首先,我们通过下面例子看下进程SContext。如SEAndroid中,进程的SContext可通过ps -Z命令查看,如图1所示:

最左边的那一列就是进程的SContext,以第一个进程/system/bin/logwrapper的SContext为例,其值为u:r:init:s0,其中:

  • u——user,用户,SEAndroid中仅定义了一个SELinux用户,值为u。

  • r——role,role是角色之意,它是SELinux中一种比较高层次,更方便的权限管理思路,即Role Based Access Control(基于角色的访问控制,简称为RBAC)。简单点说,一个u可以属于多个role,不同的role具有不同的权限。RBAC我们到最后再讨论。

  • init——type,代表该进程所属的Domain为init。MAC的基础管理思路其实不是针对上面的RBAC,而是所谓的Type Enforcement Accesc Control(简称TEAC,一般用TE表示)。对进程来说,Type就是Domain。比如init这个Domain有什么权限,都需要通过[例子1]中allow语句来说明。

  • S0——SELinux,为了满足军用和教育行业而设计的Multi-Level Security(MLS)机制有关。简单点说,MLS将系统的进程和文件进行了分级,不同级别的资源需要对应级别的进程才能访问。限于篇幅,这里不做过多探讨。

接着,我们看下文件的SContext,在SEandriod中,文件的SContext可以通过ls -Z查看:

如上图所示,右边数第二列就是所对应的SContext信息。以root目录为例,其对应SContext为:u:object_r:rootfs:s0:

  • u——同样是user之意,它代表创建这个文件的SELinux user。

  • object_r——role,这里指文件,在SELinux中,使用object_r表示它的role。

  • rootfs——死的东西的Type,和进程的Domain其实是一个意思。它表示root目录对应的Type是rootfs。

  • s0——MLS的级别。

2、TE简介

有了SContext就可以对系统内所有资源进行统一管理了。SElinux的安全模型(MAC)有两种管理机制,分别是TEAC(Type Enforcement Accesc Control,后文简称TE)和RBAC(Role Based Accesc Control)。RBAC是更高级的一种管理机制(由于SEandroid涉及不多,这里不予展开谈论),不过也是基于TE的。可见,TE是SElinux的管理核心,下面我们就对TE语法做详细解读。

在没有介绍具体的语法之前,首先需要了解下与TE语句相关的另外几个概念定义:

1). 客体类别和许可:

客体类别及其许可是 SELinux 中访问控制的基础,客体类别代表资源的范畴,如文件和
套接字,许可代表对这些资源的访问权限,如读或发送。一个客体类别代表某个确定类型(如文件或套接字)的所有资源,一个客体类别的实例(如某个特定的文件或套接字)被称为一个客体。客体类别指的是资源(文件)的所有范畴,客体指的是客体类别的某个特定实例(如: /etc/passwd)。

声明定义方法

声明许可有两种方法,第一种叫做通用许可(被多个客体类别所共用),它允许我们创建与客体类别一起作为一个组的许可,通用许可在类似的客体类别(如文件和符号连接)共享一套访问许可时很有用;第二种方法叫做特定类别许可(由某个客体类别所独用),它允许我们单独为客体类别声明特定的许可,正如将会看到的,有一些客体类别只有特定的许可,有一些只有通用许可,还有一些是这两者都有。客体类别和许可定义方法如下:

    声明客体类别:class 类别名字

    通用许可:common 通用名 {许可集}

    联合许可和客体类别:class 类别名 [inherits 通用许可集名] [{许可集}]

此外,既然有客体,肯定还有主体一说,即谁去使用、操作客体。主体和客体只是逻辑上的区别,只有放在安全控制语句中才能看出谁是主体谁是客体。但,既然是控制,主体一般来说肯定是"活的"才有可能去控制"死的"东西,所以,在SEandriod中,主体一般指的就是进程,客体可以是文件等资源,也可以是进程(因为进程一样可以被进程操作)。

2). 访问向量规则:

访问向量规则又称AV规则,属于SElinux策略语言的控制部分。常用的通用AV规则有如下4个:

  • allow:表示允许主体对客体执行允许的操作

  • dontaudit:表示不记录违反规则的决策信息,且违反规则不影响运行(允许操作且不记录)

  • auditallow:表示允许操作并记录访问决策信息(允许操作且记录)

  • neverallow:表示不允许主体对客体执行指定的操作

好了,到此,我们就可以组合成最基本的SElinux策略语句了。一般,完整的基本安全控制语句格式为:

AV规则 主体 客体:客体类别 许可

还拿之前的例子做说明:

allow netd proc:file write

其中:

allow——AV规则

netd——主体

proc——客体

file——客体类别

write——许可

当然,后面还有很多其他策略语句,将在介绍系统文件时,一并概述。

3). AV规则

对于AV规则,前文已经有所提及,比如allow。SElinux中主要的有效规则有allow,auditallow,auditdeny,dontaudit和neverallow,下面逐一介绍。

  • 允许规则allow

    我们使用allow规则指出了所有运行时授予的许可,它们是SELinux策略中允许许可的唯一方法,注意:默认情况下,不允许任何访问,我们指定了两个类型列表(源和目标类型),根据列出的客体类别的许可指定访问权,如:

    

allow user_t bin_t : file { read execute }; 

这个规则允许任何安全上下文中类型具有user_t的进程对任何安全上下文中具有类型为bin_t的普通文件所有read和execute访问权。allow规则共享了通用AV规则的的所有语法,并且也没有增加任何额外的语法了。

  • 审核规则auditallow/auditdeny/dontaudit

    SELinux有大量的工具记录日志信息,或审核、访问尝试被策略允许或拒绝的信息。审核消息通常叫做"AVC消息",它提供了详细了关于访问尝试的信息,包括是允许还是拒绝,源和目标的安全上下文,以及其它一些访问尝试涉及到资源信息。AVC消息与其它内核消息类似,都是存储在/var/log目录下的日志文件中,它是策略开发、系统管理和系统监视不可缺少的工具。在此,我们检查是哪一个访问尝试产生了审核消息。

    默认情况下,SELinux不会记录任何允许的访问检查,只会记录被拒绝的访问检查。这并没什么奇怪的,在大多数系统上,每秒会允许成千上万的访问,只有很少的一部分会被拒绝,允许的访问通常是在预料之中的,通常不需要审核,被拒绝的访问通常是(但不总是)非预期的访问,对它们进行审核便于管理员发现策略的bug和可能的入侵尝试。策略语言允许我们取消这些默认的预料之中的拒绝审核消息,改为记录允许的访问尝试审核消息。

    SELinux提供两个AV规则允许我们控制审核哪一种访问尝试:dontaudit和auditallow。使用这两条规则我们就可以改变默认的审核类型了,最常用的是dontaudit规则,它指出哪一个访问尝试被拒绝时不审核,这样就覆盖了SELinux默认的审核所有拒绝的访问尝试的行为。

   

 dontaudit httpd_t etc_t : dir search;  

注意,审核(audit)规则让我们覆盖了默认的审核设置,allow规则指出了什么访问是允许的,auditallow规则不允许访问,它只审核允许的许可。

 

  • 禁止规则neverallow

最后一个AV规则是neverallow规则,我们使用这个规则来指定永远不会被allow规则执行的访问,你可能会疑惑,为什么会有这个规则?因为默认情况下所有的访问都是被拒绝的,设计这个规则的主要目的是为了帮助编写策略时,可以明确地指出不想要的访问许可,因此可以预防意外发生,回想一下,在一个SELinux策略中可能包含成千上万条规则,可能不小心加入了我们本不想授予的访问权,此时,neverallow规则就可以帮助预防这种情况发生,如:

    

neverallow user_t shadow_t : file write;  

这条neverallow规则可以有效地阻止我们在策略中添加一条允许user_t对类型为shadow_t的文件进行写操作的规则,如果添加了这样的规则在编译时就会报错,这条规则不会移除访问权,它只是会产生编译错误。我们在编写策略时,neverallow规则往往放在allow规则前面,首先声明哪些访问是明确地被拒绝的,然后再声明哪些访问是可以接受的,这样就可以预防我们人为出错了。

 neverallow规则支持一些特殊的其它AV规则不支持的语法,在neverallow规则中的源和目标类型列表中可以使用通配符(*)和求补算操作符(~),如:

    

neverallow * domain : dir ~{ read getattr };  

这条规则指出没有哪条allow可以授予任何类型对具有domain属性的类型的目录有任何访问权,除了read和getattr访问权外(即读访问权),这条规则的中通配符意味着所有的类型,在真实的策略中,类似这样的规则很常见,它们用来阻止对/proc/目录适当的访问。

我们从前面这个例子中看出,在源类型列表中需要使用通配符,因为我们想要指出任何类型或所有类型,包括那些还没有创建的类型,使用通配符可以预防我们未来犯错。

另一个常见的neverallow规则是:

    

neverallow domain ~domain : process transition;  

这条neverallow规则增强了domain属性,它指出了进程不能转换到无domain属性的类型,这就使得要为一个类型无doamin属性的进程创建一个有效的策略是不可能的。

四、SElinux策略文件

SElinux的策略文件主要集中在external\sepolicy下,这里也是我们添加修改安全策略的主要地方。这里所有的安全策略在编译后最终都会汇集到out/target/product/generic/obj/ETC/sepolicy_intermediates/policy.conf文件中,然后再经过转化load到linux kernel。下面我们就对external\sepolicy目录下的文件进行逐一解析说明。

打开external\sepolicy目录,我们会发现里面有很多.te文件,这些te文件就是我们定义的策略文件,除了策略文件之外,还有很多没有后缀的,这些没有后缀的文件就是专门定义策略语句元素的。由于所有的te文件中的规则语句都是基于这些语句元素组成的,这里我们先来看下这些定义性文件:

1、定义性文件

users

该文件中定义了SElinux中唯一的用户u:

user u roles { r } level s0 range s0 - mls_systemhigh;

roles

该文件定义了SElinux中唯一的角色r:

role r;

role r types domain;

security_classes

该文件定义了所有通用客体类别:

# FLASK



#

# Define the security object classes

#



# Classes marked as userspace are classes

# for userspace object managers



class security

class process

class system

class capability

....

attributes

该文件定义了所有SContext中的通用type:

######################################

# Attribute declarations

#



# All types used for devices.

attribute dev_type;



# All types used for processes.

attribute domain;



# All types used for filesystems.

attribute fs_type;

....

access_vectors

该文件定义了资源的所有通用许可:

#

# Define common prefixes for access vectors

#

# common common_name { permission_name ... }





#

# Define a common prefix for file access vectors.

#



common file

{

ioctl

read

write

create

getattr

setattr

lock

relabelfrom

relabelto

append

unlink

link

rename

execute

swapon

quotaon

mounton

}

.....

file.te

该文件自定义了file_contexts中的所有文件类型的type:

# Filesystem types

type labeledfs, fs_type;

type pipefs, fs_type;

type sockfs, fs_type;

type rootfs, fs_type;

type proc, fs_type;

....

device.te

该文件自定义了file_contexts中的所有设备节点的type:

# Device types

type device, dev_type, fs_type;

type alarm_device, dev_type, mlstrustedobject;

type adb_device, dev_type;

file_contexts

....

该文件定义了所有文件资源的SContext:

#/##########################################

# Root

/ u:object_r:rootfs:s0



# Data files

/adb_keys u:object_r:adb_keys_file: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

/ueventd\..* u:object_r:rootfs:s0

....

genfs_contexts

定义了虚拟文件的SContext:

# Label inodes with the fs label.

genfscon rootfs / u:object_r:rootfs:s0

# proc labeling can be further refined (longest matching prefix).

genfscon proc / u:object_r:proc:s0

genfscon proc /net u:object_r:proc_net:s0

....

fs_use

定义了所有文件系统类型的SContext:

# Label inodes via getxattr.

fs_use_xattr yaffs2 u:object_r:labeledfs:s0;

fs_use_xattr jffs2 u:object_r:labeledfs:s0;

fs_use_xattr ext2 u:object_r:labeledfs:s0;

....

service.te

定义了所有service_contexts中SContext的type:

type bluetooth_service, service_manager_type;

type default_android_service, service_manager_type;

type drmserver_service, service_manager_type;

....

service_contexts

定义了所有所有系统service的SContext:

accessibility u:object_r:accessibility_service:s0

account u:object_r:account_service:s0

activity u:object_r:activity_service:s0

alarm u:object_r:alarm_service:s0

....

mac_permissions.xml

与seapp_contexts一起负责为接下来安装的不同类型的app添加SContext。

<?xml version="1.0" encoding="utf-8"?>

<policy>

....

<!-- Platform dev key in AOSP -->

<signer signature="@PLATFORM" >

<seinfo value="platform" />

</signer>



<!-- All other keys -->

<default>

<seinfo value="default" />

</default>

</policy>

seapp_contexts

与mac_permissions.xml一起负责为接下来安装的不同类型的app添加SContext。

isSystemServer=true domain=system_server

user=system seinfo=platform domain=system_app type=system_app_data_file

user=bluetooth seinfo=platform domain=bluetooth type=bluetooth_data_file

user=nfc seinfo=platform domain=nfc type=nfc_data_file

....

2、其他te文件

剩余的.te文件中主要是各个进程的具体策略控制。这里我们拿recovery.te来举例说明。

recovery.te

# recovery console (used in recovery init.rc for /sbin/recovery)



# Declare the domain unconditionally so we can always reference it

# in neverallow rules.

type recovery, domain;



# But the allow rules are only included in the recovery policy.

# Otherwise recovery is only allowed the domain rules.

recovery_only(`

allow recovery self:capability { chown dac_override fowner fsetid setfcap setuid setgid sys_admin sys_tty_config };



# Set security contexts on files that are not known to the loaded policy.

allow recovery self:capability2 mac_admin;



# Run helpers from / or /system without changing domain.

allow recovery rootfs:file execute_no_trans;

allow recovery system_file:file execute_no_trans;

.....

allow recovery kernel:process setsched;

')

....

neverallow recovery data_file_type:file { no_w_file_perms no_x_file_perms };

neverallow recovery data_file_type:dir no_w_dir_perms;

这里定义了有关recovery的所有策略控制。

首先,文件开头定义了进程域名recovery,继承自domain,说明这里新定义的recovery域为进程域。接下来在recovery_only中制定了很多策略控制。

如:allow recovery system_file:file execute_no_trans;表示允许SContext标志为recovery的进程不可传递域地去执行SContext为system_file的文件。

最后还有neverallow检查语句,

如:neverallow recovery data_file_type:dir no_w_dir_perms;表示不允许SContext标志为recovery的进程去no_w_dir_perms SContext为data_file_type的目录。

五、如何配置SElinux

在支持SElinux的Android版本中,有关selinux策略文件有两处,一个是google原生的,另一个是产品平台配置的,他们分别对应如下目录:

google原生目录:s912/external/sepolicy

平台配置目录:s912/device/amlogic/common/sepolicy

在编译时, 系统会以合并的方式,将平台配置目录下的policy 附加到Google 原生的policy 上,而非替换。一般情况下,不建议修改google原生目录下的策略控制。接下来看下如何配置平台的selinux。

找到平台下的BoardConfig.mk,其相关变量配置就是BOARD_SEPOLICY_DIRS,目的就是指定平台sepolicy的目录。

BoardConfig.mk中直接包含了sepolicy的mkfile文件:

include device/amlogic/common/sepolicy.mk

打开该sepolicy.mk文件可以看到BOARD_SEPOLICY_DIRS变量的配置:

BOARD_SEPOLICY_DIRS := \

device/amlogic/common/sepolicy

 

六、SElinux调试方法

1、selinux的编译

首先我们关注下selinux的编译,大家知道编译整个Android工程是个很耗时的工作。为了避免耗时,在我们修改或者添加了selinux策略之后,建议先独自对sepolicy模块进行一次编译,如果通过了,然后再进行整个Android的编译。具体指令如下:

如:mmm external/sepolicy

2、如何快速确认是selinux引起?

目前所有的SELinux check 失败,在kernel log 或者android log(L版本后)中都有对应的" avc: denied"的LOG 与之对应。举例如下:

<5>[ 17.285600].(0)[503:idmap]type=1400 audit(1356999072.320:202): avc:  denied  { create } for  pid=503 comm="idmap" name="overlays.list" scontext=u:r:zygote:s0 tcontext=u:object_r:resource_cache_data_file:s0 tclass=file

即表明idmap 这个process, 使用zygote 的source context, 访问/data/resource_cache 目录,并创建文件时,被SELinux 拒绝访问。

但反过来,有此LOG,并非就会直接失败,还需要确认当时SELinux 的模式, 是enforcing mode 还是 permissve mode.这里需要提到的是selinux有3中模式:permissive、enforcing和disabled。

  • enforcing:强制模式,代表 SELinux 运作中,且已经正确的开始限制 domain/type 了;

  • permissive:宽容模式:代表 SELinux 运作中,不过仅会有警告讯息并不会实际限制 domain/type 的存取。这种模式可以运来作为 SELinux 的 debug 之用;

  • disabled:关闭,SELinux 并没有实际运作。

如果问题容易复现,我们可以先将SELinux 模式调整到Permissive mode,然后再测试确认是否与SELinux 约束相关.

指令如下:

查看selinux当前模式:

[root@python bin]# getenforce

Permissive

更改当前的SELINUX值 ,后面可以跟 enforcing,permissive 或者 1, 0。

[root@python bin]# setenforce permissive

[root@python bin]# setenforce enforcing

3、修改/添加selinux策略

首先, 务必确认对应进程访问系统资源是否正常, 是否有必要?如果本身是异常非法访问,那么就要自行消除访问。

其次,如果确认访问是必要,并且正常的,那么就要对对应的process/domain 增加新的policy.

具体步骤大致如下:

1).简化方法

1.1 提取所有的avc LOG. 如 adb shell "cat /proc/kmsg | grep avc" > avc_log.txt

1.2 使用 audit2allow tool 直接生成policy. audit2allow -i avc_log.txt 即可自动输出生成的policy

1.3 将对应的policy 添加到selinux policy 规则中,对应MTK Solution, 您可以将它们添加在

KK: mediatek/custom/common/sepolicy, L: device/mediatek/common/sepolicy 下面,如

allow zygote resource_cache_data_file:dir rw_dir_perms;

allow zygote resource_cache_data_file:file create_file_perms;

===> mediatek/custom/common/sepolicy/zygote.te (KK)

===> device/mediatek/common/sepolicy/zygote.te (L)

注意audit2allow 它自动机械的帮您将LOG 转换成policy, 而无法知道你操作的真实意图,有可能出现权限放大问题。

2).按需确认方法

此方法需要工程人员对SELinux 基本原理,以及SELinux Policy Language 有了解.

2.1 确认是哪个进程访问哪个资源,具体需要哪些访问权限,read ? write ? exec ? create ? search ?

2.2 当前进程是否已经创建了policy 文件? 通常是process 的执行档.te,如果没有,并且它的父进程即source context 无须访问对应的资源,则创建新的te 文件.

在L 版本上, Google 要求维护关键 security context 的唯一性, 比如严禁zygote, netd, installd, vold, ueventd 等关键process 与其它process 共享同一个security context.

2.3 创建文件后,关联它的执行档,在file_contexts 中, 关联相关的执行档.

比如 /system/bin/idmap 则是 /system/bin/idmap u:object_r:idmap_exec:s0

2.4 填写policy 到相关的te 文件中

如果沿用原来父进程的te 文件,则直接添加.

如果是新的文件,那么首先:

#==============================================

# Type Declaration

#==============================================

type idmap, domain;

type idmap_exec, exec_type, file_type;



#==============================================

# Android Policy Rule

#==============================================

#permissive idmap;

domain_auto_trans(zygote, idmap_exec, idmap);

然后添加新的policy

# new policy

allow idmap resource_cache_data_file:dir rw_dir_perms;

allow idmap resource_cache_data_file:file create_file_perms;

 

3).权限放大情况处理

如果直接按照avc: denied 的LOG 转换出SELinux Policy, 往往会产生权限放大问题. 比如因为要访问某个device, 在这个device 没有细化SELinux Label 的情况下, 可能出现:

<7>[11281.586780] avc: denied { read write } for pid=1217 comm="mediaserver" name="tfa9897" dev="tmpfs" ino=4385 scontext=u:r:mediaserver:s0 tcontext=u:object_r:device:s0 tclass=chr_file permissive=0

如果直接按照此LOG 转换出SELinux Policy: allow mediaserver device:chr_file {read write}; 那么就会放开mediaserver 读写所有device 的权限. 而Google 为了防止这样的情况, 使用了neverallow 语句来约束, 这样你编译sepolicy 时就无法编译通过.

为了规避这种权限放大情况, 我们需要细化访问目标(Object) 的SELinux Label, 做到按需申请. 通常会由三步构成

3.1 定义相关的SELinux type.

比如上述案例, 在 device/mediatek/common/sepolicy/device.te 添加

type tfa9897_device, dev_type;

3.2 绑定文件与SELinux type.

比如上述案例, 在 device/mediatek/common/sepolicy/file_contexts 添加

/dev/tfa9897(/.*)? u:object_r:tfa9897_device:s0

3.3 添加对应process/domain 的访问权限.

比如上述案例, 在 device/mediatek/common/sepolicy/mediaserver.te 添加

allow mediaserver tfa9897_device:chr_file { open read write };

 

那么,哪些访问对象通常会遇到此类呢?(以L 版本为例)

*device类型

-- 类型定义: external/sepolicy/device.te;device/mediatek/common/sepolicy/device.te

-- 类型绑定: external/sepolicy/file_contexts;device/mediatek/common/sepolicy/file_contexts

 

*File类型:

-- 类型定义: external/sepolicy/file.te;device/mediatek/common/sepolicy/file.te

-- 绑定类型: external/sepolicy/file_contexts;device/mediatek/common/sepolicy/file_contexts

 

*虚拟File类型:

-- 类型定义: external/sepolicy/file.te;device/mediatek/common/sepolicy/file.te

-- 绑定类型: external/sepolicy/genfs_contexts;device/mediatek/common/sepolicy/genfs_contexts

 

*Service类型:

-- 类型定义: external/sepolicy/service.te; device/mediatek/common/sepolicy/service.te

-- 绑定类型:external/sepolicyservice_contexts;device/mediatek/common/sepolicy/service_contexts

 

*Property类型:

-- 类型定义: external/sepolicy/property.te;device/mediatek/common/sepolicy/property.te

-- 绑定类型: external/sepolicy/property_contexts;device/mediatek/common/sepolicy/property_contexts;

七、案例分析

案例一、OTA升级过程中出现/system/etc/ppp/ip-up-vpn无法添加scontext

【问题现象】

制作好的差分包在升级过程中出现如下错误而导致升级失败,且重启后一直处于recovery模式下:

Symlinks and permissions...

ApplyParsedPerms: lsetfilecon of /system/etc/ppp/ip-up-vpn to u:ppp_system_file:s0 failed:Permission denied

set_metadata:some changes failed

E:Error in /udisk/update.zip

(Status 7)

该问题与问题一的提示信息基本一致,为何还是将本问题摘出来?这是因为他们的改动方案有所不同。

【问题分析】

出现上述错误后,根据提示信息可以确定升级包校验以及系统校验已经完成。这里是由于recovery中涉及selinux方面在向/system/etc/ppp/ip-up-vpn文件添加"u: ppp_system_file:s0"上下文(scontext)时因权限问题被拒绝了。

【解决方案】

方案1、3同问题一。

方案2、将META-INF\com\google\android\updater-script中的下列条目删除:

set_metadata_recursive("/system/etc/ppp ", "uid", 0, "gid", 0, "dmode", 0755, "fmode", 0555, "capabilities", 0x0, "selabel", "u:object_r:ppp_system_file:s0");

方案4、根据方案2中所删除的额条目中的function可以看出,该条目目的是要在system/etc/ppp目录下进行递归加scontext,但不知为何没有生效。由于system/etc/ppp目录下只有ip-up-vpn一个文件,该方案仿照问题一中方案4按照直接操作ip-up-vpn文件的思路进行添加修改,具体修改如下:

a、ip-up-vpn也是可执行文件,首先定义出该文件的selinux权限规则:

在device/amlogic/common/sepolicy目录下创建ip-up-vpn.te文件,并添加如下内容:

type ip-up-vpn, domain;

type ip-up-vpn_exec, exec_type, file_type;



init_daemon_domain(ip-up-vpn)



# Third line is important

allow ip-up-vpn input_device:chr_file rw_file_perms;

allow ip-up-vpn ppp_exec:file { execute_no_trans execute getattr read open };

allow ip-up-vpn ip-up-vpn_exec:file { entrypoint read setattr execute };

并更改其传统linux权限为777:

chmod 777 ip-up-vpn.te

b、在file_contexts中定义ip-up-vpn上下文属性(scontext):

/system/etc/ppp/ip-up-vpn u:object_r:ip-up-vpn_exec:s0

c、在recovery.te中添加操控ip-up-vpn的权限:

allow recovery ip-up-vpn_exec:file {create_file_perms relabelfrom relabelto create write setattr};

 

【结果确认】updater-script中该条目通过selinux操作,ota差分升级成功。

 

案例二、recovery删除data目录下的文件

 

在recover升级的时候,我们有个需要从服务器上下载升级包,然后放在data/download,这部分工作是放在一个systemapp中完成的。

然后重启进入recoveyr模式,在recovery升级完之后,我们需要在recovery中删除这个文件,这部分是在recovery中完成。

功能很简单,但是在android6.0上碰到selinux的问题。

我们先来看recovery.te中是不允许recovery操作data下面的目录,也就是下面这个neverallow原则

# Recovery should never touch /data.

#

# In particular, if /data is encrypted, it is not accessible

# to recovery anyway.

#

# For now, we only enforce write/execute restrictions, as domain.te

# contains a number of read-only rules that apply to all

# domains, including recovery.

#

# TODO: tighten this up further.

neverallow recovery data_file_type:file { no_w_file_perms no_x_file_perms };

neverallow recovery data_file_type:dir no_w_dir_perms;

我们再来看下system_data_file在file_contexts.te中的定义

/data(/.*)? u:object_r:system_data_file:s0

而在file.te定义了system_data_file文件属于data_file_type类型,因此recovery也是不能操作data下面的文件

[html] view plain copy 在CODE上查看代码片派生到我的代码片

./file.te:56:type system_data_file, file_type, data_file_type;

在我们可以自己定义自己的file类型,我们可以定义data/download属于download_data_file类型

/data/download(/.*)? u:object_r:download_data_file:s0

然后在file.te中定义download_data_file类型, 注意是只属于file_type类型的。

type download_data_file, file_type;

然后在recovery.te中增加对download_data_file的权限

allow recovery download_data_file:dir { write search remove_name };

allow recovery download_data_file:file { read getattr unlink };

另外我们还需在一个systemapp中先下载升级包,所以需要在system_app.te中增加对如下权限:

allow system_app download_data_file:dir { search write add_name getattr remove_name };

allow system_app download_data_file:file { create read write open getattr unlink };

但是最后还是失败了,为什么呢,因为一开始没有data/download这个目录,而是我们再app中自己下载的时候创建的目录,所以是system_data_file类型的。那么我们就需要在一开始就有data/download目录,所以在init.rc中添加如下代码:

mkdir /data/download 0771 system system

最后我们还需要在uncrypt.te中增加如下,因为data目录有可能需要进行加密处理。

allow uncrypt download_data_file:dir { search getattr };

allow uncrypt download_data_file:file { getattr read open };

 

八、常用指令

SELinux 是个经过安全强化的Linux操作系统,实际上,基本上原来的运用软件没有必要修改就能在它上面运行。真正做了特别修改的RPM包只要50多个。像文件系统EXT3都是经过了扩展。对于一些原有的命令也进行了扩展,另外还增加了一些新的命令,接下来我们就来看看这些命令。

1、文件操作

1)ls命令

在命令后加个 -Z 或者加 –context

[root@python azureus]# ls -Z

-rwxr-xr-x fu fu user_u:object_r:user_home_t azureus

-rw-r--r-- fu fu user_u:object_r:user_home_t Azureus2.jar+

-rw-r--r-- fu fu user_u:object_r:user_home_t Azureus.png

2)chcon

更改文件的标签

[root@python tmp]# ls --context test.txt

-rw-r--r-- root root root:object_r:staff_tmp_t test.txt

[root@python tmp]# chcon -t etc_t test.txt

[root@python tmp]# ls -lZ test.txt

-rw-r--r-- root root root:object_r:etc_t test.txt

3)restorecon

当这个文件在策略里有定义是,可以恢复原来的 文件标签。

4)setfiles

跟chcon一样可以更改一部分文件的标签,不需要对整个文件系统重新设定标签。

5)fixfiles

一般是对整个文件系统的, 后面一般跟 relabel,对整个系统 relabel后,一般我们都重新启动。如果,在根目录下有.autorelabel空文件的话,每次重新启动时都调用 fixfiles relabel

6)star

就是tar在SELinux下的互换命令,能把文件的标签也一起备份起来。

7)cp

可以跟 -Z, --context=CONTEXT 在拷贝的时候指定目的地文件的security context

8)find

可以跟 –context 查特定的type的文件。

例子:

find /home/fu/ --context fu:fu_r:amule_t -exec ls -Z {} \:

9)run_init

在sysadm_t里手动启动一些如Apache之类的程序,也可以让它正常进行,domain迁移。

2、进程domain的确认

程序现在在那个domain里运行,我们可以在ps 命令后加 -Z

[root@python /]# ps -eZ

LABEL PID TTY TIME CMD

system_u:system_r:init_t 1 ? 00:00:00 init

system_u:system_r:kernel_t 2 ? 00:00:00 ksoftirqd/0

system_u:system_r:kernel_t 3 ? 00:00:00 watchdog/0

3、ROLE的确认和变更

命令id能用来确认自己的 security context

[root@python ~]# id

uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) context=root:staff_r:staff_t

这里,虽然是ROOT用户,但也只是在一般的ROLE和staff_t里运行,如果在enforcing模式下,这时的ROOT对于系统管理工作来说什么也干不了。

[root@python ~]# newrole -r sysadm_r

Authenticating root.

口令:

[root@python ~]# id

uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) context=root:sysadm_r:sysadm_t

4、模式切换

1)getenforce

得到当前的SELINUX值

[root@python bin]# getenforce

Permissive

2)setenforce

更改当前的SELINUX值 ,后面可以跟 enforcing,permissive 或者 1, 0。

[root@python bin]# setenforce permissive

3)sestatus

显示当前的 SELinux的信息

[root@python bin]# sestatus -v

SELinux status: enabled

SELinuxfs mount: /selinux

Current mode: permissive

Mode from config file: permissive

Policy version: 20

Policy from config file: refpolicy

Process contexts:

Current context: user_u:user_r:user_t

Init context: system_u:system_r:init_t

/sbin/mingetty system_u:system_r:getty_t

/usr/sbin/sshd system_u:system_r:sshd_t

File contexts:

Controlling term: user_u:object_r:user_devpts_t

/etc/passwd system_u:object_r:etc_t

/etc/shadow system_u:object_r:shadow_t

/bin/bash system_u:object_r:shell_exec_t

/bin/login system_u:object_r:login_exec_t

/bin/sh system_u:object_r:bin_t -> system_u:object_r:shell_exec_t

/sbin/agetty system_u:object_r:getty_exec_t

/sbin/init system_u:object_r:init_exec_t

/sbin/mingetty system_u:object_r:getty_exec_t

5、其他重要命令

1)Audit2allow

很重要的一个以python写的命令,主要用来处理日志,把日志中的违反策略的动作的记录,转换成 access vector,对开发安全策略非常有用。在refpolicy里,它的功能比以前有了很大的扩展。

[root@python log]# cat dmesg | audit2allow -m local > local.te

2)checkmodule

编译模块

[root@python log]# checkmodule -m -o local.mod local.te

checkmodule: loading policy configuration from local.te

checkmodule: policy configuration loaded

checkmodule: writing binary representation (version 5) to local.mod

3)semodule_package

创建新的模块

[root@python log]# semodule_package -o local.pp -m local.mod

4)semodule

可以显示,加载,删除 模块

加载的例子:

[root@python log]# semodule -i local.pp

5)semanage

这是一个功能强大的策略管理工具,有了它即使没有策略的源代码,也是可以管理安全策略的。因为我主要是介绍用源代码来修改策略的,详细用法大家可以参考它的man页。

 

 

九、参考文献

 

1. Security-Enhanced Linux in Android

https://source.android.com/security/selinux/index.html

2. 深入理解SELinux SEAndroid

http://blog.csdn.net/innost/article/details/19299937

3.SElinux详解.pdf

4.类型强制(TE编写规则)

http://blog.csdn.net/myarrow/article/details/10105961

5.http://c.biancheng.net/view/1147.html

Logo

更多推荐