一、基本概念

1、TE模型的安全上下文

所有的操作系统访问控制都基于主体、客体,以及与他们相关的访问控制属性。

在selinux中,访问控制属性叫做安全上下文。所有对象(文件、进程间通信通道、套接字、网络主机等)和主体(进程)都有一个与之关联的安全上下文。

一个安全上下文包含三个元素:用户(user)、角色(role)和类型标识符(type identifiers)

安全上下文的形式如下:user:role:type

对进程来说:分别表示用户、角色、类型标识符也被称为域

对客体来说:前两项基本没有实际用途,role通常为object_r,user通常位创建这个对象的进程的user,对访问控制没有影响

显示安全上下文

加上-Z能显示主体、客体的上下文

ls -Z能显示文件系统的安全上下文

ps -Z能显示进程的安全上下文

id -Z能显示shell的安全上下文:joe:usr_r:usr_t

2、TE访问控制

 在SELinux中,默认时没有允许规则的,也没有超级用户。被允许的访问必须由规则给出。

一条规则如下:

allow Source type(s) Target type(s): Object class(es) Permission(s)

比如这样的访问规则:

allow user_t bin_t : file {read execute getattr};

表示允许域为user_t的进程对type为bin_t的文件具有读、执行、得到属性的操作

3、角色的作用

SELinux也提供基于角色的访问控制

通过以下语句指定role的type:

role  user_r  type  passwd_t;

如果没有以上这条语句,则:

安全上下文joe:user_r:passwd_t则不能被创建
exec调用则失败,即便策略允许

 二、架构

1、内核架构

基于LSM(linux security module),为所有的内核的资源提供强制访问控制

LSM提供了一系列的钩子函数

selinux学习-风君雪科技博客

如果访问被DAC拒绝,则会影响审计结果

SELinux的架构如下所示:

selinux学习-风君雪科技博客

策略决定包含在安全服务器中,与具体架构无关,便于移植

对象管理者时各对象的管理者,在LSM架构中,是一系列的LSM钩子,遍布在内核的子系统中。

 2、用户空间的对象管理器

SELinux支持将对象管理器放到用户态,使用内核的对象管理策略服务器来管理用户态的对象

selinux学习-风君雪科技博客

然而,支持用户空间的对象管理器有一些弱点:

对于TE模型,还需要定义class
对于对象管理器的管理策略不再内核之中

策略服务架构如下:
selinux学习-风君雪科技博客

AVC表示各种缓存

 三、class

SELinux的客体除了有type,还有class,进一步对客体的类型进行划分

SELinux中几个常用的客体类别(class),及其权限

file:read、write、execute、getattr、create
dir:read、write、search、rmdir
process:signal、transition(域的转换)、fork、getattr
socket:bind、listen、connect、accept
filesystem:mount、unmount

四、TE策略

在TE模型中,主体通常是正在运行的进程,而不是用户。客体可以是文件、进程、socket等。

TE模型定义了一系列的规则来表示访问是否允许,如果没有规则,则所有的访问都是不被允许的。

TE规则主要分为两大类:access vector(AV)和type rules,AV允许审计,而type rules决定控制策略

 1、类型(type)、属性(attributes)、别名(aliases)

type:在selinux中,将对资源的访问抽象为主体对客体的访问,主体分为多个type,也叫做域,客体也分为多个type,每个type里还能更加细分出class,selinux的访问规则就是基于type建立的规则,所以叫TE模型。

attributes:用来引用一组具有相同标识符的一组类型

alases:对于策略而言,别名标准符和类型标识符是一致的

一个type申明语句的格式如下,中括号中代表的是可选项:

type type_name [alias alias_set] [, attribute_set]

例子:type httpd_user_content_t ,file_type

例子:

举一个例子说明属性的作用

假设现在新建了一个type为backup_t:

type backup_t;

需要对backup_t赋予所有文件的读权限,如果系统中有shadow_t文件和httpd_user_content_t类型的文件,那么需要以下两条的规则来赋予文件读权限

allow backup_t httpd_user_content_t :file read

allow backup_t shadow_t:file read

假设现在多了一个类型的文件,那么就需要重新为backup_t新增一条规则,这样不仅麻烦而且错误率很高

现在有了一个属性file_type

attribute file_type;

然后将所有具有file_type属性的文件的读权限赋予backup_t

allow backup_t file_type:file read;

这样,以后每次有新的文件type出现,只需要将新文件的type域file_type属性关联起来就行,backup_t会自动获得读权限

以上的例子简单说明了属性的使用,一个type可以关联到多个属性,将type和attribute关联起来可以使用typeattribute,格式为:

typeattribute type_name attrib_name

别名用来保证兼容性,使用typealias来关联,格式如下:

typealias type_name alias alias_names;

2、AV(access rules)规则

AV规则有四类

allow:允许两个类型之间的具体访问

dontaudit:指定不记录拒绝访问的信息

auditallow:允许事件被记录

neverallow:指定规则永远不会被赋予访问权限

alow规则

格式:

allow source_type target_type :objectclass{permission}

例子:

allow user_d bin_t : file{read、execute、getattr}

允许user_d域的进程对bin_t类型的普通文件进行读、写、取属性操作

AV规则通用语法

所有的AV规则的语法都同于上述的allow规则

在AV规则中使用属性

以allow的例子为例,定义了file_type的属性之后,可以将规则写为:

allow user_d file_type: file{read、execute、getattr}

如果定义了主体的的type,则规则可以写为:

allow domain file_type: file{read、execute、getattr}

多个type和attribute

如果有多个type和attribute存在,则可以用括号表示多个,并且type的attribute可以混用

allow {domain user_t} {file_type bin_t}: file{read、execute、getattr}

self

规则中可以出现self字样,以下两条规则等价

allow user_t user_t : process signal;

allow user_t self: process signal;

类型否定

类型否定用来在一系列的type中减去某个type,比如以下规则从exec_type中减去sbin_t

allow domain (exec_type -sbin_t): file{read、execute、getattr}

class的权限

allow user_t bin_t : {file dir} {read getattr}

等价于:

allow user_t bin_t : file {read getattr}

allow user_t bin_t : dir {read getattr}

通配符

allow user_t bin_t : dir *

表示赋予所有权限

取反操作符

allow user_t bin_t : file ~{read getattr}

表示除了read和getattr之外的权限全部赋予

审计规则

默认情况下,允许的访问时不被记录的,而不允许的访问会被记录下,比如以下的两个指令

dontaudit:指定不记录拒绝访问的信息

auditallow:允许事件被记录

从不允许

neverallow:指定规则永远不会被赋予访问权限

3、策略规则

策略规则中有两类

type_transition:

type_change:

通用规则语义

rule_name  source_type  target_type  object_class  defult_type

例子:

type_transition  user_t   passwd_exec_t : process passwd_t;

以上规则表示,当一个user_t域的进程执行类型为passwd_exec_t的文件时,进程的type默认转移到passwd_t

注意:在以上的语法中,处了default_type不能使用集合,其他都能使用集合

type转移

进程在fork的时候继承父进程的type,而文件在在创建时继承自容器的type,例如目录

假设有如下规则:

type_transition  user_t   passwd_exec_t : process passwd_t;‘

在上述例子中,defult_type就是默认要转移的type

但是需要完成一个完整的转移不仅仅需要上述的一条规则,总共需要3条。’

例子:

type为init_t的进程fork一个子进程,然后执行type为appache_exec_t的文件,同时进程的域需要转移到apache_t

selinux学习-风君雪科技博客

一共需要3条规则

1、原域必须对目标类型有执行权限(init_t对appache_exec_t有执行权限)

2、原域必须对目标域有转移权限(init_t对apache_t有转移权限)

3、目标域必须对目标文件类型有entrypoint权限(apache_t对appache_exec_t有entrypoint权限)

object转移

type_transition passwd_t tmp_t : file passwd_tmp_t;

以上例子表示当一个passwd_t进程在tmp_t 目录下创建一个文件时,文件的类型为passwd_tmp_t

需要的规则包括

1、tmp_t的增加名字,写,搜索权限

2、passwd_tmp_t的写和创建权限

type改变,type_change

type改变时用来指明重新执行时的默认类型,像type_transition一样,指明默认但是不允许,

type_change   sysadm_t  tty_device_t : chr_file   sysadm_tty_device_t;

这个type_change规则声明,当代表sysadm_t重新标记tty_device_t类型的字符文件时,应该使用sysadm_tty_device_t类型。

 这个规则是type_change规则最常用的一个例子,即在用户登录时重新标记终端设备。

登录程序将通过SELinux模块的内核接口查询策略,传入类型sysadm_t和tty_device_t,并接收类型sysadm_tty_device_t作为用于relabel更改的类型。

这种机制允许登录过程在新的登录会话期间代表用户对tty设备进行标记,同时将类型的细节封装在策略中,而不是硬编码在应用程序中。

五、用户和角色

SELinux中的角色和用户是其RBAC特性的基础。大多数其他主流操作系统的安全特性主要集中于授予用户访问权限,或者直接授予用户访问权限,或者通过某种形式的组或角色机制授予用户访问权限。在SELinux中,通过TE allow规则将访问权限授予类型。

角色充当类型强制的支持特性,并与用户一起提供一种方法,将基于类型的访问控制与Linux用户及其允许运行的程序绑定在一起。SELinux中的RBAC通过定义域类型和用户之间的关系来控制Linux用户的特权和访问权限,从而进一步限制了类型强制。

Linux和SELinux有不同的用户标识符,有时是相关的。Linux用户是指/etc/passwd中定义的用户帐户SELinux用户是指安全上下文中在SELinux策略中定义的用户标识符。

通过将域类型与一个或多个角色关联,我们间接地向用户授予特权。RBAC策略语句不授予访问权限。相反,RBAC通过控制安全上下文中域类型、角色和用户的关联来进一步约束TE策略SELinux 没有直接给“用户”授权。SELinux的安全策略定义了一个用户可以跟哪些角色关联一个角色可以跟哪些域类型关联

最终定义了一个用户可以跟哪些域类型关联,用户关联的域类型才可以执行该域类型的程序,才具有该域类型的权限。一个用户要执行一个可执行程序时,会产生域切换;SELinux控制一个角色可以切换到哪些角色;SELinux控制角色与域类型的关联进而控制角色可以可以运行的程序角色的切换和角色与域类型的关联控制了用户的权限。

selinux学习-风君雪科技博客

 域从域类型为user_t的bash shell进程转换为运行域类型为passwd_t的密码程序的进程。我们为流程安全上下文添加了用户和角色部分的安全上下文joe: user_r: user_t joe: user_r: passwd_t

selinux学习-风君雪科技博客

selinux学习-风君雪科技博客

selinux学习-风君雪科技博客

这个例子演示了两种RBAC策略语句:一个用户声明语句(user)和两个角色声明语句(role)。用户语句“user joe roles {user_r}”将SELinux用户joe与角色user_r关联起来。该语句告诉SELinux,允许用户joe和角色user_r在安全上下文中共存。如果没有这条语句,图中的user joe和role user_r进程安全上下文将无效,SELinux将拒绝创建它们,从而拒绝域转换尝试。

这两个角色语句将角色user_r与域类型user_t和passwd_t关联起来。要使流程安全上下文有效,角色语句是必需的。如果没有关联类型passwd_t的role语句,即使TE策略允许,这个域转换也会失败。如果我们不希望user_r角色运行密码程序,那么只需删除这个角色语句,即使TE规则允许访问,内核也永远不会创建安全上下文。

1、role语句

除了object_r之外,SELinux没有任何内置角色。与类型一样,角色也是在策略中声明的,并通过一致的使用赋予其意义。

role有四个相关联的语句:

1、role声明语句

2、role允许语句

3、role转移语句

4、role支配语句

角色声明语句

格式为:

role   role_name   [types  type_set];

角色声明语句声明角色标识符并将类型与角色关联起来。类型必须与角色关联,以便与角色在安全上下文中共存。给定角色标识符的第一个角色语句除了关联列出的类型外,还将声明该角色。所有后续角色语句都关联其他类型。单个角色的多个角色语句通常用于将角色语句放置在与其关联的类型的声明附近

角色允许语句

格式为:

allow    role_set    role_set;

SELinux提供了一种通过execve()系统调用在程序执行期间更改角色的方法。这个特性在本质上类似于域转换,这会导致域类型的更改。角色允许规则(allow)通过指定允许哪些角色更改为其他角色来控制在程序执行时可能发生的角色更改。

角色转移规则

因为角色可以在程序执行时以类似于类型的方式进行更改,所以我们需要一种方法在策略语言中自动完成这种转换。对于类型,我们使用type_transition规则来指定自动的默认类型转换。对于角色,我们有角色转换规则(role_transition)。这个规则在目的和语法上与type_transition规则类似,只是它指定了在执行文件时发生的默认角色更改。

格式为:

role_transition      role_set    type_set     role;

例子:

role_transition   sysadm_r   http_exec_t  system_r;

此规则声明,除非另有要求,否则当角色为sysadm_r的进程执行类型为http_exec_t的文件时,SELinux应该尝试将角色更改为system_r。

角色支配语句

格式为:
dominance { role role_name { role_set} }

角色优势语句指定角色之间的层次关系。角色继承它们所控制的角色的所有类型关联。

表单角色“role_name”中指定的一个或多个角色;使用空格分隔的列表指定多个角色(例如,{role staff_r;sysadm_r作用;})

例子:

dominance { role a_r { role b_r; role c_r { role d_r; } } }

d_r关联的角色只有它自己的类型

c_r它的类型和d_r的类型

d_r关联的角色只有它自己的类型

a_r它自己的类型以及b_r、c_r和d_r中的所有类型

2、用户和用户语句

Linux和SELinux用户标识符是不同的,通常是不相关的。在SELinux中,Linux用户标识符和给定进程的SELinux用户标识符可能不同。SELinux使用不同的用户标识符(而不是共享Linux的标识符)的设计决策的动机是希望创建不可变的SELinux用户标识符。在标准Linux中,用户标识符的变化反映权限的变化(例如,更改为root)。在许多情况下,实际的和有效的用户标识符都会发生变化。这使得跟踪哪个用户登录进行审计、身份验证和其他使用变得困难。分离Linux和SELinux用户标识符允许Linux用户标识符在不影响SELinux的情况下根据需要进行更改。

user声明语句(user)在策略中声明一个用户标识符,并将其与一个或多个角色关联。user语句是与SELinux用户相关的惟一策略语句。用户语句必须在所有类型和角色语句之后以及约束之前

格式如下:

user user_name roles role_set;

假设有如下规则:

user joe roles { user_r };

如果策略中尚未声明用户joe,则该语句声明该用户joe,并将角色user_r与该用户关联起来。

将SELinux的用户和linux用户关联

登录程序(例如,login, sshd)负责将Linux用户映射到SELinux用户。登录时,如果有一个与Linux用户标识符完全相同的SELinux用户标识符,则匹配的SELinux用户标识符将成为初始shell进程的安全上下文中的用户标识符。

在许多情况下,不希望在策略中定义每个普通用户。对于SELinux(即user_r角色和user_t初始用户域类型),普通用户通常具有相同的特权。为了解决这个问题,SELinux有一个特殊的用户标识user_u,称为泛型用户。如果策略中定义了通用用户user_u,那么如果策略中没有匹配的SELinux用户,那么所有Linux用户都将映射到它。

对于语句:

user   user_u   roles   { user_r };

这个语句定义了通用用户user_u,并将其授权给角色user_r,就像我们前面为joe所做的那样。

如果在策略中定义user_u,则将策略中未显式定义的所有Linux用户映射到user_u。例如,如果jane是一个Linux用户标识符,但是SELinux策略中没有定义用户jane,那么当Linux用户jane登录时,初始shell进程安全上下文中的用户标识符将是user_u。因为joe是在策略中定义的,所以该用户的初始SELinux用户标识符将是joe,即使user_u也是在策略中定义的。

如果在策略中没有定义泛型用户user_u,那么在SELinux策略中没有显式定义的任何Linux用户标识符都将无法登录,即使在许可模式下也是如此。

登录时,初始shell进程必须具有有效的安全上下文,包括用户标识符。如果策略中既没有定义user_u,也没有定义Linux用户标识符,则登录过程无法创建有效的安全上下文(因为没有用户标识符可供使用)。因此,如果您的策略中没有包含user_u(这对于许多配置是有意义的),那么您必须显式地将所有Linux用户添加到SELinux策略中。

SELinux还有第二个特殊用户,系统用户system_u,它通常用于init等所有系统进程,以及由init启动的守护进程。从技术上讲,用户system_u没有特殊含义,在策略语言中没有以任何方式进行异常处理。但是,大多数现有的策略都包含这个用户,并且系统通常在配置时希望这个SELinux用户用于系统资源。通常,在策略中始终包含system_u是一个好主意

永远不要使用标识符system_u创建Linux用户帐户。如果这样做,该Linux用户将能够使用系统用户标识符登录,系统用户标识符通常具有很高的特权(尽管仍然比普通Linux系统上的root权限小得多)。

六、约束

SELinux提供了一种约束机制来进一步限制策略允许的访问,而不管策略允许规则是什么。

为了理解约束的目的,让我们重新看看SELinux Linux安全模块(LSM)。

selinux学习-风君雪科技博客

我们希望进一步了解访问决策逻辑在安全服务器中的工作方式。访问向量缓存(AVC)由源安全标识符(SID)、目标SID和对象类三部分组成。SID是安全上下文的内部参考

1、安全上下文和SID

SELinux实现了Flask安全体系结构,该体系结构提供了实现增强访问控制的框架,但仍然保持安全策略中立。这意味着AVC及其与LSM挂钩的接口没有专门绑定到类型强制(TE)和SELinux实现的其他安全策略。就AVC而言,安全标识符是对一组安全凭据的不透明惟一引用。AVC缓存由源和目标sid以及对象类标识符索引决定。

SELinux安全服务器在内部将安全上下文与SIDs联系起来,因此,SELinux可以使用sid查找类型、用户和角色标识符,而AVC和LSM钩子接口可以忽略这些细节。当SELinux LSM钩子请求访问决策时,它们提供主题(源)和对象(目标)以及对象类的sid。AVC使用SID-SID-class三元组查找被允许的访问,该访问被存储为位掩码。当缓存丢失发生时,AVC调用安全服务器函数security_compute_av()来确定允许的访问。

该函数的访问决策逻辑有两个基本步骤:

1)为type-type-class三元组创建一个表示根据TE allow规则允许的对象权限的掩码;

2)从允许掩码中删除任何约束不允许的权限。

第二个步骤允许将约束作为限制策略所允许的权限的一种方法。因此,正如您所看到的,约束进一步限制了SELinux策略中允许的访问。
SELinux有两种类型的约束。
constraint语句是最常见的约束,允许您根据源和目标安全上下文的用户、角色和/或类型进一步限制访问。validatetrans语句是SELinux中最新添加的,它使您能够基于旧的、新的和流程安全上下文进一步限制对安全上下文更改事件的访问。

2、约束语句

约束语句有三个元素:约束应用到的对象类的集合、被约束类的权限集和约束的布尔表达式。约束由对象类组织并存储在策略中。constraint语句允许您根据源和目标安全上下文之间的关系定义约束,从而限制指定对象类的指定权限。约束语句的完整语法如下所示:

constrain   class_set   perm_set    expression ;

class_set一个或多个对象类。

perm_set一个或多个权限。所有权限必须对

class_set中的所有对象类有效。表达式约束的布尔表达式。

布尔表达式语法支持以下关键字:

t1, r1, u1 Source type, role, and user
t2, r2, u2 Target type, role, and user

约束表达式语法还支持以下操作符:

= =集合成员的或等价的

=设置不属于或不等于

eq (Role关键字)的等价

dom (Role关键字)支配由

incomp (Role关键字)支配的

domby (Role关键字)不可比较

例子:

constrain process transition (u1 == u2) ;

首先,注意它只应用于流程对象类,并且只限制流程的转换权限。回想一下,允许域转换需要转换权限;实际上,这个约束进一步限制了域转换。现在让我们看看约束表达式(u1 == u2)。我们看到,它要求所有域转换的源用户标识符和目标用户标识符保持相同。allow rule 限制了域类型的切换; constrain 进一步限制 process类型在域切换时:安全上下文中的用户不能改变。

回想一下前面对访问算法的描述。当流程请求转换权限时,AVC调用安全服务器以确定允许对三重源目标类的访问时,前面的约束将变得有效(对于流程对象类),并将检查源和目标安全上下文中的用户标识符。如果用户标识符不相同,则在将授予的访问掩码返回AVC之前,掩码中表示转换权限的位将被删除。constraint语句允许您对安全上下文的三个元素(用户、角色和类型)的任意组合表示约束。约束表达式将源(主题)流程和目标(对象)的上下文相互比较,并/或使用显式名称(例如类型或角色标识符)进行比较。

例子:

constrain process transition (r1 == r2) ;

这个约束类似于前面的语句,只是它约束的是角色标识符而不是用户标识符。关键字r1和r2分别表示源角色标识符和目标角色标识符。这个约束要求角色标识符不能在域转换上更改。

因为这两个约束与相同的对象类和权限相关,约束表达式语法允许我们将它们组合成一个布尔表达式:

constrain process transition (u1 == u2 and r1 == r2) ;

让我们进一步看看我们的例子。在某些情况下,我们希望允许在域转换上更改用户和/或角色标识符。例如,登录过程需要将用户和角色标识符更改为登录用户的标识符。登录的SC:system_u:system_r:local_login_t当前SC:user_u:user_r:user_t。另一个例子是允许您更改角色的程序,该程序必须能够在域转换期间更改角色标识符。一般来说,这些程序是受信任的进程,我们需要一种方法来允许它们更改用户/角色标识符,同时确保约束对所有其他程序都是活动的。为了实现这个目标,让我们首先定义一种方法来识别那些受信任可以更改用户和角色标识符的域类型。特别地,让我们假设策略中定义了两个属性:privuser和privrole。前者与允许更改用户标识符的所有类型相关联,后者与允许更改角色标识符的类型相关联。通过这些属性,我们可以改变约束如下:

constrain process transition (u1 == u2 or t1 == privuser) ;
constrain process transition (r1 == r2 or t1 == privrole) ;

在这两个语句中,t1都表示源类型。第一个语句只允许在源类型具有privuser属性的情况下在域转换中更改用户标识符。同样,如果源类型具有privrole属性,则可以更改角色

对于约束表达式,所有操作符的左侧必须是允许的关键字之一(例如,u1或u2),并且可能永远不是类型、属性、角色或用户标识符(或标识符列表)。运算符的右侧可以是一个关键字或一个或多个标识符名称。

selinux学习-风君雪科技博客

selinux学习-风君雪科技博客

selinux学习-风君雪科技博客

3、标签转换约束

SELinux支持第二条约束语句validatetrans。该语句是作为修改后的多级安全特性的一部分添加的,我们将在下一章中进行讨论。通过validatetrans语句,我们可以进一步控制更改受支持对象的安全上下文的能力。

与constraint语句不同,validatetrans语句允许您将对象的新旧安全上下文相互关联,并/或与第三个安全上下文(试图重新标记对象的进程的安全上下文)关联起来。因此,为该语句添加了新的关键字,特别是t3、r3和u3,分别表示流程安全上下文的类型、角色和用户。*1关键字表示旧的安全上下文,*2关键字表示新的安全上下文

注意不要混淆约束和validatetrans语句之间的关键字关联。对于约束语句,t1表示源(或调用流程)类型,t2表示目标(对象)类型。然而,在validatetrans语句中,t3现在是源流程类型,t1是“旧”类型,t2是“新”类型。

validatetrans语句通过与新旧安全上下文和流程的安全上下文定义基于约束的关系来限制更改指定受支持对象的安全上下文的能力。validatetrans语句的完整语法如下:

validatetrans    class_set   expression ;

布尔表达式语法支持以下关键字:

t1, r1, u1 Old type, role, and user
t2, r2, u2 New type, role, and user
t3, r3, u3 Process type, role, and user

约束表达式语法还支持以下操作符:

==集合成员的或等价的

=设置不属于或不等于

eq(仅限Role关键字)的等价

dom(仅限Role关键字)支配

domby(仅限Role关键字)不受支配

incomp(仅限Role关键字)不可比较

假设我们有一个user_tmp_t类型,在我们的策略中,我们将其用作普通不受信任用户程序的临时文件的类型。我们可能希望确保具有更改所有文件标签权限的域不会意外地将user_tmp_t作为其类型的文件重新标记为某些非常关键的类型(输入shadow_t类型,这是/etc/shadow文件的类型)。这是我们的约束条件它提供了这样的约束:

 validatetrans {file lnk_file} ( t2 != shadow_t or t1 != user_tmp_t );

注意这个约束的几个特性。我们同时包含普通文件和符号链接(lnk_file),因为我们不希望有人使用链接代替文件。该约束表示,要允许文件和符号链接对象更改安全上下文,只有在旧类型不是user_tmp_t的情况下,新类型才可能是shadow_t。如果旧类型是user_tmp_t,则新类型可能不是shadow_t。换句话说,任何域类型都不能被授权将用户临时文件重新标记为影子密码文件的类型。

要扩展这个示例,假设有一个域类型子集,我们希望允许它将user_tmp_t重新标记为shadow_t。现在,我们创建一个属性relabel_any,并将其分配给我们希望授予此特权的那些域类型。

validatetrans {file lnk_file}
(
  ( t3 == relabel_any) or
  ( t2 != shadow_t or t1 != user_tmp_t )
);

selinux学习-风君雪科技博客

selinux学习-风君雪科技博客

selinux学习-风君雪科技博客

七、多级安全

在最近对SELinux的增强中,约束特性得到了扩展,以实现可选的多级安全(MLS)策略。MLS是另一种形式的强制访问控制,它构建在类型强制(TE)之上。在本章中,我们将探讨可选的MLS策略特性。MLS是另一种强制访问控制形式,适用于一些安全问题,特别是与政府机密数据控制相关的安全问题。在SELinux中,MLS是类型强制的可选扩展;没有它,MLS特性就无法实现。

7.1 安全上下文和多级安全

启用MLS时,将使用两个附加字段扩展安全上下文:低安全级别和高安全级别。安全级别本身有两个字段:敏感(sensitivity)性和一组类别(categories)。敏感性是严格分层的,反映了一个有序的数据敏感性模型,如政府分类控制中的绝密、机密和非机密。类别是无序的,反映了数据划分的需要。基本思想是,您既需要足够高的灵敏度间隙,又需要正确的类别来访问数据。

不要混淆安全级别和敏感性。安全级别是单个敏感性和一组(零或多个)类别的组合。安全级别不是分层的,使用优势关系(dom、domby、eq、incomp)进行比较

7.2 定义安全等级

使用灵敏度语句定义灵敏度,如下所示:

sensitivity s0;
sensitivity s1;
sensitivity s2;
sensitivity s3;

这些名称是SELinux中典型的通用敏感命名约定。

敏感性语句还支持将附加别名与敏感性关联的能力,敏感性将被视为与核心敏感性名称相同的名称。例如:

sensitivity s1 alias unclassified;

这些名称是SELinux中典型的通用敏感命名约定。

由于敏感性必须是层次性相关的,我们必须在政策中使用支配性语句指定敏感性的层次性,如下所示:

dominance { s0 s1 s2 s3 } 

优势语句按照从低到高的顺序列出敏感性名称。因此,在我们的例子中,s0小于s1, s1小于s2,以此类推。

类别的定义类似于使用类别语句定义敏感性。类别也可以有别名。下列语句是类别语句的例子:

category c0 alias blue;
category c1 alias red;
category c2 alias green;
category c3 alias orange;
category c4 alias white;

在策略语言中定义安全级别的最后一步是使用level语句定义允许的安全级别组合。level语句规定了如何将类别与敏感性联系起来。请记住,单个敏感性和一组类别的组合构成一个安全级别。下面是level语句的一些例子:

level s0:c0.c4;
level s1:c0.c4;
level s2:c0.c4;
level s3:c0.c4;

例子:
level s0:c0.c2;
level s1:c0.c2,c4;

在本例中,s0可能只与类别c0、c1和c2相关联;s1有c0 c1 c2 c4(但不是c3)现在,您应该已经注意到,点(.)表示类别的一个包含范围,逗号(,)表示类别的一个非连续列表。level语句定义了哪些敏感性和类别的组合构成SELinux策略中MLS部分可接受的安全级别

仅仅因为类别的范围是使用范围运算符(.)指定的,这并不意味着类别是分层相关的。相反,range操作符只是一种方便地引用一组类别的方法。范围操作符的类别顺序只是声明它们的顺序,与它们的名称隐含的任何内在顺序无关。

7.3 安全上下文的MLS拓展

格式如下:

user:role:type:sensitivity[:category,…][ – sensitivity[:category,…]]

对于MLS SELinux系统,安全上下文被扩展为包括两个安全级别:低级别或当前安全级别和高级别或清除安全级别。通常,低级别反映了流程的当前安全级别或对象中包含的数据的敏感性。高级别反映上下文中用户标识符的清除级别(从而确定任何安全上下文的当前级别所允许的最高安全级别)或某些所谓的多级别对象所允许的最大数据范围。

要使安全上下文有效,高级别必须始终控制低级别。此外,与敏感性相关的类别必须根据策略中的级别声明有效。

例子:

level s0:c0.c2;
level s1:c0.c2,c4;

user_u、user_r和user_t是有效的用户、角色和类型标识符,以下安全上下文无效:

user_u:user_r:user_t:s0 -s0:c2,c4 (c4 is invalid for s0)
user_u:user_r:user_t:s0:c0 -s0:c2 (high does not dominate the low)

八、条件策略

这里,我们将探讨通过策略语句创建的条件策略,这些策略允许我们根据环境定义启用或禁用的规则。

8.1 概述

条件策略语句使我们能够定义仅在条件表达式定义的情况下启用的策略规则集,条件表达式是使用定义的变量和逻辑运算符构造的逻辑表达式。让我们看一个虚构的例子。

假设我们有一台移动计算机,并且希望定义策略规则,使其能够访问特定程序的域类型(例如,myprog_t),以便在计算机停靠时仅访问有线以太网网络接口,在计算机未停靠时仅访问无线网络接口。

为了达到这个目的,我们可以写一个条件句,如:

bool docked true;
if (docked) {
  # rules to allow my_prog_t access to wired Ethernet device
} else {
  # rules to allow my_prog_t access to wireless device
}

我们所要做的就是在停靠/取消停靠设备时更改布尔值(例如,正在运行的服务可能会监视此状态并相应地设置布尔值),以启用适当的策略规则集。

8.2 布尔变量

编写条件策略的第一步是创建布尔变量。例如,假设我们想要配置策略,以便普通用户使用ping程序的能力能够被打开和关闭。我们需要定义一个布尔变量user_ping,我们将在条件表达式中使用它。

bool  user_ping  false;

我们使用bool语句来定义布尔变量。bool语句有两个参数,布尔值(user_ping)的名称及其默认值,可以为真,也可以为假。在这种情况下,默认值(false)意味着普通用户在默认情况下不能使用ping,bool语句定义了条件布尔值及其默认值。

bool    bool_name    default_value;

内核通过selinux伪文件系统公开布尔值。这个伪文件系统是用户空间和内核中的SELinux Linux安全模块(LSM)之间的主要接口。文件系统通常安装在/selinux/上。当前策略中定义的所有布尔变量将显示为这个伪文件系统的布尔目录中的文件。例如,您可以将上面定义的布尔值看作一个路径名为/selinux/ boolies /user_ping的文件。

在运行的系统中更改布尔变量值的能力使我们能够更改条件表达式的值,从而提供条件策略。因此,SELinux内核必须使布尔变量可用于运行更改的进程。这与策略的任何其他组件不同,后者一旦加载到内核中,在加载新的整个策略之前都是静态的。布尔值在运行的系统上可以单独访问和更改。

我们使用selinux文件系统中的布尔文件来查询和设置布尔变量的当前值。如果您查看布尔文件的内容,您总是会看到一对数字(0或1表示false或true),如下所示:

# cat /selinux/booleans/user_ping
1 1

第一个数字表示布尔变量的当前值;在这种情况下,1为真。第二个数字表示布尔变量的挂起值。当前值是内核用于布尔值和确定条件表达式值的实际值。挂起是布尔值的当前值在提交布尔值更改时将被更改的值。我们通过更改布尔的挂起值,然后将更改提交给内核,来更改布尔的当前值。

我们通过向布尔文件写入1或0来更改挂起的值,如下所示:

# cat /selinux/booleans/user_ping
1 1
# echo 0 > /selinux/booleans/user_ping
# cat /selinux/booleans/user_ping
1 0

挂起的值现在变为0,当前值保持不变。这意味着,即使将布尔user_ping的挂起值更改为false(0),它的值仍然为true(1)。

原因是更改布尔值需要两个步骤的提交过程。首先,更改想要更改的布尔值的挂起值,然后将挂起值提交到当前值。这允许您更改多个布尔值,然后在一步中提交所有更改。文件/selinux/commit_pending_bools是将所有布尔值的挂起值提交为当前值的接口。通过向该文件写入1,可以实现提交,如下所示:

# echo 1 > /selinux/commit_pending_bools
# commit all pending values
# cat /selinux/booleans/user_ping
0 0

SELinux为查询和更改布尔值提供了方便的命令,而无需记住它们的文件位置。getsebool命令将布尔值的状态显示为活动(TRue)或非活动(false)。例如:

# getsebool -a
docked > inactive
user_ping > active

我们还可以使用setsebool命令更改布尔值

# getsebool user_ping (* show current state *)
user_ping > active
# setsebool user_ping false (* change and commit current state*)
# getsebool user_ping (* show changed stated *)
user_ping > inactive

我们还可以使用setsebool命令,使用参数的另一种格式在一个事务中更改多个布尔值,如下所示:

# getsebool user_ping docked
user_ping > active
docked > inactive
# setsebool user_ping=0 docked=1
# getsebool user_ping docked
user_ping > inactive
docked > active

在策略文件中定义了布尔变量及其默认状态。在SELinux策略语言中包含布尔值之后,出现了一个问题,即如何在不重新创建策略的情况下更改布尔值的默认状态。因此引入了持久值的概念。SELinux实用程序使用的标准库提供了一种方法,通过使用布尔值来维护文件,从而对布尔值进行持久更改。init进程使用此文件在系统初始化期间覆盖策略默认值。通过这种方式,我们可以更改在重新引导期间持续存在的布尔值的当前值,而不必修改静态SELinux策略。

8.3 条件语句

if (cond_expression)

{true_list}

[else {false_list}]

cond_expression

是一个条件表达式,由一个或多个布尔变量和逻辑运算符组成。表9-1列出了受支持的逻辑运算符。布尔变量必须使用bool语句定义。根据条件表达式的值有条件地启用或禁用的规则列表。当条件列表为真时,将启用规则的真列表(禁用false)。反之亦然。false列表是可选的。

selinux学习-风君雪科技博客

九、客体标识

要使SELinux策略发挥作用,所有对象实例都必须使用安全上下文进行标记。在本节中,我们将讨论将安全上下文应用于对象实例的各种方法,包括在创建对象时如何分配安全上下文,以及稍后如何修改这些标签(称为重标号)。

9.1 基本含义

SELinux中的所有对象从创建到销毁都有一个相关的安全上下文。此属性对于SELinux执行访问控制的能力至关重要。让我们看看文件/etc/shadow的安全上下文

# ls -Z /etc/shadow
-r——- root root system_u:object_r:shadow_t shadow

这个例子演示了为文件/etc/shadow显示安全上下文的程序ls。与对象关联的安全上下文(在本例中是system_u:object_r:shadow_t)是SELinux在访问控制决策中使用的唯一属性。从根本上说,这一点以及为所有对象分配正确的安全上下文非常重要。

SELinux策略语言包含一些特性,这些特性使标记决策自动化并在很大程度上透明。文件和域转换的类型转换规则,有时,标签成为我们需要关注的问题。在系统管理、策略开发、系统安装期间。作为政策制定者,我们必须仔细制定标签政策声明,以便在运行时简化标签管理。

对象在SELinux系统上的标记有四种基本方式:

1、策略语句:SELinux策略语言包含一些特性,比如type_transition规则,它们为对象标记指定行为

2、硬编码的默认:大多数对象类都在对象管理器中编码了某种类型的默认标记行为。例如,默认情况下,当进程创建新套接字时,新套接字具有与其创建进程相同的安全上下文。

3、程序需要的标记:对于一些对象类,SELinux提供了各种应用程序编程接口(api),允许程序显式地请求标签,包括新对象实例和现有对象实例

4、出事SID:SELinux有一组初始安全标识符(初始sid),用于标记一些对象,并在对象缺少或无效标签时用作故障安全标签

未完待续。。。