5.【SELinux学习笔记】类型增强
selinux
common selinux implementation
项目地址:https://gitcode.com/gh_mirrors/sel/selinux
免费下载资源
·
这一篇对我来说是非常重要的,我们可以从本篇博文中了解到SELinux策略语言
一、类型增强
SELinux策略大部分是一套声明和规则一起定义的类型增强(TE)策略。每个进程对每个资源的访问尝试都必须要有一条允许的TE访问规则。所有规则都属于两类范畴:访问向量(AV,即权限)和类型规则。
1.类型、属性、别名
SELinux主要使用类型来确定什么访问是被允许的,而别名是我们为类型定义的另外一个名字。
1.1类型声明
区别于客体类别,这里所说的类型指的是域类型和客体类型。使用类型前必须使用type语句明确声明一个类型标示符,SELinux没有预定类型,需要我们自己声明。例:我们如果要声明一个类型(httpd_t),并打算将其作为Web服务器的域类型,而另一个类型(http_user_content_t)作为用户数据文件,那么声明语句如下:
type httpd_t;
type http_user_content_t;
那么类型声明过之后,我们就可以在安全上下文、TE和其他策略语句中使用它了。
注!类型声明语法:
type 类型名称 [alias 别名集] [,属性集];
别名集 如果有多个别名,可在一对大括号中用空
格将各个别名区别开来,如:alias {aliasa_t aliasb_t}。
属性集 如果同时指定多个属性标识符,属性之间使用逗号进行分隔,如:type
bin_t,file_type,exec_type;。
1.2类型和属性
规则默认情况下是拒绝所有访问的,每一个访问都需要明确声明,那么一个系统上的策略将会非常的复杂和冗长,那么此时属性的存在将会是这种情况得到很大的缓解。
如果我们想让一个备份程序可以访问所有的文件,首先我们需要创建一个对应的域类型(backup_t),并允许它访问任何类型的文件。
type backup_t;//声明域类型backup_t;
allow backup_t httpd_user_content_t : file read;//允许该域类型具有对httpd_user_content_t类型的读权限。
allow backup_t shadow_t : file read;//允许该域类型对客体类型为shadow_t的文件具有读权限。
既然域类型backup_t需要多所有文件都具有访问权限,那么我们编写大量的allow规则才能满足这个需求,这是一个非常大的工作量。那么有了属性,这一切都只是浮云。通过给所有文件即客体类型都定义一个属性,然后授予对具有该属性的文件对应的访问权限即可。如下:
attribute file_type;//使用attribute语句来声明属性file_type。
注!类型和属性共享相同的命名空间,即类型和属性命名不能雷同。
那么如果每个文件都具有了file_type属性,而如果有一条规则指定backup_t可以对属性为file_tpye的文件具有read权限,那么就满足了上述需求,如下:
allow backup_t file_type :file read;
注!属性生命语法:
attribute 属性名称;
我们知道在域类型和属性共享命名空间i,在对类型进行声明的时候,通常在最后已"_t"结尾,这是通俗的约定写法。
1.3关联类型和属性
我们在定义客体类型的同时可以指定关联相应的属性。如下:
type http_user_content_t,file_type;
一个文件也可以指定多个属性;
type httpd_user_content_t, file_type, httpdcontent;
type shadow_t, file_type;
allow backup_t file_type : file read;//backup_t可以访问所有具有 file_type 属性的文件;
allow httpd_t httpdcontent : file read;//httpd_t可以访问所有具有 httpcontent 属性的文件;
typeattribute语法:
typeattribute 类型名 属性名;
除了使用type语句关联类型和属性外,我们还可以typeattribute语句。我们在声明类型时可以单独关联属性。首先我们先拆分之前的示例语句:
type httpd_user_content_t, file_type, httpdcontent;
拆分之后:
#下面是两条语句//在策略编译器中#号表示注释的意思。
type httpd_user_content_t;
typeattribute httpd_user_content_t file_type,
httpdcontent;//这里的两条语句等于之前的一条语句。
注!将一条语句分成两条语句来定义可以实现策略的模块化,即我们可以在一个文件中定义类型,一个文件中关联属性。
1.4别名
别名的设计通常是为了策略更新时新旧类型的兼容性。例如,一个就策略可能引入了netscape_t,那么更新后的策略可能改名为了mozilla_t,那么为了保证新旧策略模块的兼容性可以为mozilla_t定义一个新的别名netscape_t。
声明别名的方式有两种,首先第一种声明语句中使用alias为类型定义一个别名,如下:
type mozilla_t alias netscape_t, domain;//这里需要注意的是别名的声明是在属性定义的前面的。
第二种:使用typealias语句独立声明,如下:
# 这两条语句等同于
type mozilla_t, domain;
typealias mozilla_t alias netscape_t;
#下面这一条语句
type mozilla_t alias netscape_t, domain;
typealias语句语法:
typealias 类型名称 alias 别名名称
本系列博文中关于“域类型”、“文件类型”、“目录类型”等,在类型前面加上形容词目的是为了描述类型的用途,只是一种称呼。例如“域类型”我们知道通常指的是主体类型-进程,而文件类型我们可以理解为客体类型-文件在安全上下文中的类型。虽然我们可以定义一个类型同时用在进程和文件上,但是为了使策略更加清晰,我们尽量不要把域类型用在文件上下文之中。
2.访问向量规则( Access Vector Rules,即AV规则)
AV规则是按照对客体类别的访问许可指定具体含义的规则,SELinux策略语言支持4类AV规则:
allow 表示主体对客体执行允许的操作。
dontaudit 表示不记录违反规则的决策信息,且违反规则不影响运行。
auditallow 表示允许操作并记录访问决策信息。
neverallow 表示不允许主体对客体执行指定的操作。
2.1通用AV规则语法
四类AV规则用途虽然不同,但基本语法中每个规则都要包含以下五个元素:
-
-
- 规则名称 allow、dontaudit、auditallow和neverallow。
- 源类型 授予访问的类型,通常指域类型-进程。
- 目标类型 客体的类型
- 客体类别 客体的类别,如file。
- 许可 表示主体访问客体时所指定操作类型,即许可。
-
举例如下:
allow(规则类型) user_t(域类型) bin_t(客体类型) : file(客体类别) execute(访问向量);
2.2AV规则的键
AV规则通常包含源类型、目标类型、客体类别和许可进行标识,那么前面三个元素组合在一起称之为键,许可或访问向量我们可以称之为值。当一个进程产生一个访问请求时,SELinxu LSM模块被要求允许基于这个键进行访问;那么如果多个规则使用同一个键,或者一个键对应多个值会产生什么影响呢?如下:(同样翻译自SELinux by Example Using Security Enhanced Linux的SELinux详解一书这里翻译的是错误的)
allow
user_t
bin_t :
file execute;
allow
user_t bin_t :
file read;
上面的两个规则一个允许域类型为user_t的进程对类型bin_t的文件具有可执行权限,一个允许其具有读权限,那么在实际运行的机制中到底哪个有效呢?
当然,答案是两者皆可。为什么呢?编译后的策略会将具有相同键的规则通过checkpolicy进行整合为一条规则。那么整合后的这条规则会同时具有read和execute权限。
2.3在AV规则中使用属性
如果我面临着一个域类型需要对多个客体类型具有某个访问权限(一对多),或者多个域类型同时对某个客体类型具有某个访问权限(多对一),再或者是多个域类型同时对多个客体类型具有某个访问权限(多对多)的情形时,一个一个的定义AV规则虽然能够实现前面的场景,但是未免有些冗杂,是不是有一种一劳永逸的实现方式呢?
属性。
举个多对多的例子;如果我们现在为域类型user_t和staff_t定义属性domain,为客体类型bin_t,local_bin_t和sbin_t定义属性exec_type,那么下面两种实现方式的效果是相同的。
allow domain exec_type : file execute; |
allow user_t bin_t : file execute;
allow user_t local_bin_t : file execute;
allow user_t sbin_t : file execute;
allow staff_t bin_t : file execute;
allow staff_t local_bin_t : file execute;
allow staff_t sbin_t : file execute;
|
2.4AV规则中的多类型和属性
我们在AV规则中可以使用多个类型和属性,也就是说我们在定义AV规则是可以定义多个域类型、客体类型和属性。当我们在AV规则中定义多个类型或属性时,需要用大括号括起来,而且它们之间使用空格隔开。如下:
allow user_t { bin_t sbin_t } : file execute;或allow {user_t domain} {bin_t file_type sbin_t} : file execute ;
2.5特殊类型self
self是策略语言中的一个关键字,通常用于AV规则中的客体类型中,如下两条语句是等同的:
allow user_t user_t : process signal; | allow user_t self : process signal; |
那通过上面的语句我们大概了解了,self通常用于主体类型与客体类型相同的场景。
接着再看一个例子:
allow {user_t staff_t} self : process signal;① |
# 这两条规则②
allow user_t user_t : process signal;
allow staff_t staff_t : process signal;
|
那么实际上,上面表格中的规则实际上是前面例子的一个扩展,也就说规则①与规则②也是等同的。但这并不意味着user_t可以访问staff_t,也就是说下面这条语句是不被允许的。如下:
allow user_t staff_t:process signal;
注!这里需要注意的是self关键字只能用于客体类型,而不能指代主体类型或属性等。
self存在的意义并不止步于此,从上面的案例我们似乎感觉self只是达到了属性的效力,其实不是。首先来看个案例:
allow domain domain : process signal;
//属性为domain的进程可以向属性为domain的进程发送信号
上面的av规则几乎是一场安全灾难,这是很可怕的。怎么理解呢?上面的AV规则允许每个域类型除了可以向自己发送信号外也可以向其他进程属性为domain的进程发送信号。这并不是我们设计此AV规则的初衷,那么此时self可以改变这个场景,如下:
allow domain self: process signal;
//属性为domain的进程可以向属性为domain的进程发送信号,但仅限于进程自身。
2.6特殊操作符“非"
如果我们遇到这样一个场景,即如果我们用某个属性来指代一组域域类型,而该组域类型中我们不希望某个域类型针对客体类型具有相关访问许可,那么此时使用”非“操作符可以帮我们解决这个问题,当然”非“操作符也可用于客体类型,先看个例子:
allow domain { exec_type -sbin_t } : file execute;//属性为domain的域类型可以对属性为exec_type的客体类型具有可执行权限,但客体类型中并不包括-sbin_t.
实际的AV规则中对”非“操作符的顺序并没有要求,下面语句与上面的语句具有同等效力的。
allow domain { -sbin_t exec_type } : file execute;
2.7在AV规则中指定多个客体类别和类型
这里的写法与之前学习在AV规则中使用多个主、客体类型相似,不在赘述,直接看例子:
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 };
|
右表是左表的展开,从左表我们可以看到,展开之后,AV规则中的两个键(客体类别不同)同时拥有许可列表中声明的所有权限。那是不是可以”同理“下面的这条AV规则也成立呢?
# 无效的规则,因为 search 对于客体类别 file 是无效的
allow user_t bin_t : { file dir } { read getattr search }
read和getattr权限对file和dir客体类别来说属于通用许可的范畴,但search只对dir客体类别有效,那么此时的AV规则中为file类别提供了一个无效的许可(search)。那么当checkpolicy时不能为其创建键,编译的时候会报错。那么此时我们需要创建两条规则来解决,如下:
#当许可对两个客体类别不是都有效时,需要两条规则
allow user_t bin_t : file { read getattr };
allow user_t bin_t : file { read getattr };
allow user_t bin_t : dir { read getattr search } ;
2.8AV规则中的特殊许可操作符
AV规则语法为我们定义了两个特殊的许可操作符,分别是通配符(*)和补集(~)。
2.8.1通配符的使用
allow user_t bin_t : { file dir } *;//域类型user_t具有对客体类型为bint_t非客体类别file和dir具有所有许可。
那么这里并没有考虑其中一个许可对某个客体类别是否有效的问题,这样是不是同样会造成编译策略时失败的情况呢?
答案是当然不会,因为使用通配符时不需要考虑这个场景,通配符所指代的许可只针对那些有效的客体类别有效。那上面这个例子来说dir客体类别的search对客体类别file并无任何影响。
2.8.2补集操作符的使用
补集操作符的作用即使除指定许可之外的许可。如下案例:
allow user_t bin_t : file ~{ write setattr ioctl };//允许域类型user_t对客体类型bin_t具有除write、setattr、ioctl之外的任何访问许可。
3.allow规则
通常情况下,与我们打交道最多,也是最常见的AV规则就是allow规则。它是SELinux策略中允许许可的唯一方法。
allow user_t bin_t : file { read execute };//允许域类型为user_t的进程对客体类型为bin_t的文件具有读和可执行权限。
AV规则具有累加性,如下案例:
# 这两条规则.
allow user_t bin_t : file read;
allow user_t bin_t : file write;
|
# 等同于这一条规则
allow user_t bin_t : file { read write );
|
4.Audit规则
SELinxu有大量的工具用于记录日志信息、审核信息和那些被策略允许或禁止的访问尝试信息。其中审核信息被称之为“AVC 信息”,在AVC信息中我们可以看到非常详细的信息,包括访问被允许还是被禁止,安全上下文的域类型、客体类型等。与kernel信息一样,这些信息被保存在/var/log下的日志文件中。
默认情况下SELinux不会记录任何允许的访问检查,只会记录被拒绝的访问审核。因为在系统中,每一秒钟都有数以万计的访问,而只有很少一部分访问才会被拒绝。因此允许的访问通常不需要审核。当然策略语言也允许我们对这些进行修改,我们可以根据需要修改为记录允许的访问尝试审核信息。
那么,关于对于访问尝试的控制,SELinux提供了两个AV规则来选择,使用哪一种审核规则:dontaudit和auditallow。我们可以选择其中的一种来定义默认的审核机制。
举例:
dontaudit httpd_t etc_t : dir search;//域类型为httpd_t的进程对客体类型为etc_t的目录进行search访问被拒绝时,这个拒绝不会被审核。简单来理解就是对权限检查失败的操作不做记录。
dontaudit规则的作用显然是用来避免不必要的授权,而且也会为日志文件省去大量的审核信息。
auditallow domain shadow_t : file write;//
域类型为
domain
的进程对客体类型为
shadow_t
的文件进行
write
访问得到允许时进行审核
5.neverallow规则
neverallow规则用来禁止执行某些访问,但SELinxu默认情况下没有指定的访问是不被允许的,那么neverallow规则会不会显得多余呢?
neverallow的设计主要是为了帮助我们避免非法的授权,如下面这个例子:
neverallow user_t shadow_t : file write;//禁止域类型为user_t的进程执行对客体类型为shadow_t的文件写权限
上面规则的意义在于如果我们在策略中误添加了一条允许域类型为user_t的进程对客体类型为shadow_t的文件具有写权限时,在编译策略时就会报错。可以预防我们认为出错。
二、类型规则
类型规则在创建客体或者运行过程重新标记时指定默认类型。策略语言为我们定义了两种类型规则:
type_transition 域转换过程重新标记时或创建客体时,指定默认的类型
type_change 使用SELinux的应用程序执行标记时指定默认的类型。
1.通用类型规则语法
类型规则语法包含五个元素:
规则名称 | type_transition或type_change |
源类型(域类型,如进程) | The type(s) of the creating or owning process(创建或拥有进程的类型) |
目标类型(客体类型,如某个具体的文件或目录等) | 新的或重新标记的客体的客体类型 |
客体类别(如文件file、目录dir等) | 新的或重新标记的客体的类别 |
默认类型 | 新创建或重新标记的单一默认类型(这个本人理解的比较费解) |
通用类型规则语法:
规则名称 类型集 类型集:类别集 默认类型;
看下面一个例子:
type_transition user_t passwd_exec_t : process passwd_t;//规则名称 域类型 客体类型:客体类别 默认类型。
那本例的意思就是当一个域类型为user_t的进程执行一个客体类型为passwd_exec_t的文件时,进程类型若没有其他请求将会默认转换为passwd_t。
此外和AV规则类似,我们可以同时指定多个域类型,并用大括号括起来,用空格分隔开,如下。
type_transition { user_t sysadm_t } passwd_exec_t : process passwd_t;//这条type_transition规则在域列表中包括两个类型:user_t和sysadm_t。拆分来看与下面两条语句具有同等意义。
# 这两条规则.
type_transition user_t passwd_exec_t : process passwd_t;
type_transition sysadm_t passwd_exec_t : process passwd_t;
|
# 等于下面这一条规则.
type_transition { user_t sysadm_t } passwd_exec_t : process passwd_t;
|
在类型规则中同样可以使用属性的概念,但由于默认类型只能指定一个,属性和上面的多个类型不能用于默认类型。同样我们不能指定两条独立的规则同时拥有相同的规则名称、域类型、客体类型、客体类别。如果我们把类型规则中的规则名称、域类型、客体类型、客体类别作为类型规则的键,而默认类型作为类型规则的值,一个键不能存在多个不同的值。
类似于下面这的定义,是不成立的,编译策略时会报错而无法编过:
# 这两条规则将会冲突,编译时会报错
type_transition user_t passwd_exec_t : process passwd_t;
type_transition user_t passwd_exec_t : process passwd_t;
type_transition user_t passwd_exec_t : process user_passwd_t;
2.类型转换规则
我们使用type_transition规则指定默认类型,而type_transition规则分为两种格式,第一种支持默认域转换事件;第二种支持客体类型转换,它允许我们指定默认的客体标记。
默认情况下,在SELinxu中新创建的客体接收包含它们的客体的类型如目录,进程也会继承父进程的类型。而type_tranisition规则允许我们覆盖这种默认指定的类型。
type_transition规则没有allow访问权,仅是提供一个默认类型标记,要成功进行类型转换,还需要一套相关联的allow规则,才能允许进程创建客体或标记客体。
2.1默认域转换
首先来看下面的规则:
type_transition init_t apache_exec_t : process apache_t;//类型为 init_t 的进程执行一个类型为 apache_exec_t 的文件时,进程类型将会转换到 apache_t.
如上,域转换只是改变进程现有的类型,而不是创建一个新的进程,因为在SELinux上之用调用fork()函数才能创建一个新的进程。而通过execve()系统调用执行一个新的程序时,发生域转换更安全些。下图显示一个域转换的流程:
图1
正如前面所说,type_transition规则没有allow访问权,仅是提供一个默认类型标记,要成功进行类型转换,还需要一套相关联的allow规则。SELinxu策略要去必须允许下面三个访问权:
-
-
- execute 域类型 ( init_t )必须对客体类型( apache_exec_t )具有该权限。
- transition 域类型 ( init_t ) 必须对默认类型( apache_t )具有该许可.
- entrypoint 默认类型 ( apache_t ) 目标类型 ( apache_exec_t )具有该许可.
-
因此,上图中的规则若想成立必须满足下面三条规则:
allow init_t apache_exec_t : file execute;
allow init_t apache_t : process transition;
allow init_t apache_t : process transition;
allow apache_t apache_exec_t : file entrypoint;
2.2默认客体转换
客体转换规则为新创建的客体指定一个默认的类型,通常在与文件系统有关的客体(如 file,dir,lnk_file 等)上使用此规则,首先来看案例:
type_transition passwd_t tmp_t : file passwd_tmp_t;//域类型为passwd_t 的进程在一个客体类型为 tmp_t 的目录下创建一个普通文件(file 客体类别)时,默认情况下,如果策略允许的话,新创建的文件类型默认为 passwd_tmp_t,而不是客体类型tmp_t。
上例中的规则与域转换规则一样策略必须允许对默认标记的访问,对于上面的例子,对类型为 tmp_t 的目录的访问权需要包括 add_name, write 和 search, 对类型为 passwd_tmp_t的文件要有 read 和 write 访问权。
GitHub 加速计划 / sel / selinux
150
64
下载
common selinux implementation
最近提交(Master分支:2 个月前 )
1b71cb46
pwalk, pwalkdir: fix walk vs remove race 1 年前
40a1afee
Misc nitpicks 1 年前
更多推荐
已为社区贡献6条内容
所有评论(0)