1. 简介
SELinux提供了一种依赖于类型强制(TE)基于角色的访问控制(RBAC),角色用于组域类型和限制域类型与用户之间的关系,SELinux中的用户关联一个或多个角色,使用角色和用户,RBAC特性允许有效地定义和管理最终授予Linux用户的特权。
域类型<---->用户
1.1 SELinux与Linux访问授权的区别
• Linux:将访问权授予用户,或通过用户组或角色的机制进行授权;
• SELinux:访问权不是直接授予用户或角色的,访问权是通过TE allow规则授予类型。
角色扮演是类型强制支持的一个特性,它和用户一起为Linux用户及其允许的程序提供了一种绑定基于类型的访问控制,SELinux中的RBAC通过定义域类型和用户之间的关系对类型强制做了更多限制,以控制Linux用户的特权和访问许可,RBAC没有允许访问权,在SELinux中,所有的允许访问权都是由类型强制提供的。
注意:Linux和SELinux有它们自己的用户标识符,这让人难以理解,为了避免混淆,在本中,如果指的的/etc/passwd中定义的用户,我们就用"Linux用户"来表达,如果没有明确指出是什么用户或用户标识符,那就指的是SELinux策略中在安全上下文中定义的用户标识符了。
1.2 SELinux中RBAC
SELinux中的RBAC特性依赖并支持TE特性,我们通过将域类型和一个或多个角色进行关联,而不是直接将权限授给用户,RBAC通过在安全上下文中控制域类型、角色和用户的关联实现对TE策略更多的约束,也就是说,域转换是受用户的角色约束的,最终约束了用户的总体权限。
其例子如下图所示:
这个例子显示了两类RBAC策略语句:一个用户声明语句(user)和两个角色声明语句(role)。这些语句在策略中创建了用户、角色和类型标识符之间的关系。
上图中的user语句将SELinux用户joe和角色user_r关联起来,这条语句告诉SELinux用户joe和角色user_r在安全上下文中是可以共存的,如果没有这条语句,上图中的用户joe和角色user_r进程安全上下文将是无效的,SELinux将会拒绝创建它们,最后拒绝进行域转换。
那两个role语句将角色user_r和域类型user_t和passwd_t关联起来,与user语句类似,要使进程安全上下文有效,role语句也是必需要有的,如果没有role语句关联类型passwd_t,即使TE策略允许,域转换也会失败。如果我们不想user_r角色运行密码程序,我们只需要将role语句移除,内核也就不会创建对应的安全上下文了,即使TE规则允许进行访问。
1.3 用角色管理用户权限
如上图的例子所示,我们不会直接将域类型和用户进行关联,相反,域类型是与角色进行关联的,然后再将角色与SELinux用户进行关联,这就间接增加了两个效果,首先,他简化了管理所有策略的复杂度,一个系统可能只有三或四个角色,但有成千上万的用户和域类型,直接将域类型与用户进行关联,将会导致管理非常困难,将域类型分配给一个具描述类型(如普通用户域类型)特权集的角色,然后将这些角色指派给用户,这样就更容易管理了。
关键要记住角色仅仅是一套域类型的集合,他可以方便地与用户建立联系,它们不是SELinux中独立的访问控制机制。SELinux中的角色也允许我们限制用户的访问,对于任何进程,同一时间只有一个角色(即进程安全上下文中的角色)是"活动的"。
1.4 客体安全上下文中的用户和角色
在密码策略示例中,没有包括显示的文件客体完整的安全上下文(即可执行文件/etc/bin/passwd和shadow密码文件/etc/shadow),相对主体安全上下文中的用户和角色部分而言,客体安全上下文的重要性要低一些,然而客体必须要有一个完整的安全上下文,用户字段支持审核,而角色没什么用,如果我们在我们的示例系统中检查上图中的客体,我们会发现如下所示的安全上下文:
- # ls --scontext /usr/bin/passwd /etc/shadow
- system_u:object_r:shadow_t /etc/shadow
- system_u:object_r:passwd_exec_t /usr/bin/passwd
正如你所看到的,这两个客体都有特殊的角色object_r,它是所有客体代表性的角色,这个角色是硬编码进SELinux的,它是不需要声明的,对于所有类型都是隐含允许的,你不要尝试声明角色object_r。
客体安全上下文的用户部分通常设置为创建进程安全上下文的用户部分,这个特性有一些潜在的用途,可以跟踪是哪个用户创建的这个客体,但通常没有安全强制效果,在前面的例子中,这个两个客体的用户都是system_u,它是一个特殊的用户,在许多策略中都存在,它代表系统资源和进程。
2. 角色和角色语句
除了object_r外,SELinux没有内置任何其它的角色,与类型一样,角色也需要在策略中进行声明,与角色有关的有4个策略语句:
• 角色声明语句
• 角色allow规则
• 角色转换规则
• 角色控制语句。
2.1 角色声明语句
角色声明语句(role)声明一个角色标识符,例子如下:
role user_r types user_t;
role user_r types passwd_t;
这些语句将域类型user_t和passwd_t与角色user_r关联起来了。
完整的角色声明语法如下:
role 角色名称 types [类型 类型集];
• 角色名称:角色标识符,如果它是在第一条role语句中,这就声明了一个角色,标识符长度任意。
• 类型集:一个或多个类型或标识符属性,使用大括号{}将多个标识符括起来,标识符之间使用空格分开,如{user_t passwd_t},类型也是可以被排除的,只需要在类型名称前加上一个“-”即可,如{exec_type–sbin_t},如果省略type_set(同关键字types一起),角色声明时就不会关联任何类型。
角色声明在单个策略,基础载入模块和非基础载入模块中都是有效的,但在条件语句中就无效了。
2.2 角色转换规则
因为程序在执行过程中角色可能发生变化,某种程度上与类型相似,在策略语言中,我们需要一个方法实现这种转换的自动化,对于类型而言,我们使用了type_transition规则进行自动化处理,对于角色,我们就使用角色转换规则role_transition,这个规则在作用和语法上与type_transition很类似,如:
- role_transition sysadm_r http_exec_t system_r;
这个规则说明,当一个角色为sysadm_r的进程执行一个类型为http_exec_t的文件时,SELinux将会尝试将起转换为system_r角色。
与type_transition规则一样,role_transition规则也不允许(allow)规则执行角色改变,假设这样,要使得角色改变成功,角色allow规则也是必须的,角色转换规则通常用于系统管理员直接执行系统后台进程时改变角色,而不是初始化进程(init),如果这种情形下不使用角色转换规则,后台进程可能有一个不同的角色,这依赖于是谁启动的它,除了这种情况外,我们不希望角色被隐含地改变,相反,我们希望用户在需要的时候明确地改变它们的角色(如newrole命令)。
角色转换规则指定了执行一个文件时默认的角色变化,角色转换规则没有允许访问权,要成功转换角色,角色allow规则也是必须的。
角色转换规则语法如下:
role_transition 角色集 类型集 角色;
• 角色集: 一个或多个角色标识符,多个标识符之间使用空格分隔,并使用大括号将它们括起来,如{staff_r sysadm_r}。
• 类型集:一个或多个类型或属性标识符,多个标识符之间使用空格进行分隔,并使用大括号将它们括起来,如{user_t passwd_t}。可以使用前缀字符“-”来排除类型,如{exec_type –sbin_t}。
• 角色: 角色转换后安全上下文中的新角色。
角色转换规则在单个策略,基础载入模块和非基础载入模块中都有效,但在条件语句中无效。
2.3 角色控制语句
角色控制语句(dominance)按照其他角色声明一个角色,我们可以使用这个语句创建一个角色之间的层次关系,假设这样,"高层角色"可能会自动继承所有与角色关联的类型,例如:
- dominance { role super_r {role sysadm_r; role secadm_r; }}
这个角色dominance语句声明了一个角色super_r,如果它没有声明,并使它控制角色sysadm_r和secadm_r,角色super_r将拥有与角色sysadm_r和secadm_r合并后的类型,如果这些"高层角色"发生了任何变化,合并内容也将为super_r发生变化,注意任何添加到控制语句后的高层角色的类型没有通过dominance语句继承下来,因此,在前面的例子中,如果在dominance语句后面给secadm_r角色添加了一个类型,super_r角色不会继承这个新的类型,角色dominance语句在现有策略中也没有广泛使用。
完整的角色控制语句语法如下:
dominance {role 角色名称 {角色集}}
• 角色集:一个或多个以“role 角色名称”形式指定的角色,多个角色之间使用空格分隔,如{role staff_r;role sysadm_r}。
策略语言不支持更复杂的语法,这里的角色集可以包括嵌套的控制关系定义,使用大括号进行表示,如:
dominance { role a_r { role b_r; role c_r { role d_r; } } }
在这个例子中,角色定义如下:
d_r 只有它自己的类型
c_r 它的类型和d_r的类型
b_r 只有它自己的类型
a_r 它自己的类型和所有b_r,c_r和d_r的类型
2.4 角色allow规则
角色allow规则语法如下:
allow 角色名 角色名;
例子如下:
- allow cashier_r mgr_r;
指角色为mgr_r的进程可以转换到一个角色名为cashier_r的进程。
3. 用户和用户语句
Linux和SELinux用户标识符是不同的,通常也没什么关联,在SELinux中,进程的Linux用户标识符和SELinux用户标识符是不同的(例如:参考稍后讨论的user_u),当初设计SELinux用户标识符时,希望实现一个固定不变的SELinux用户标识符,所以才没有共享Linux的用户标识符,在标准Linux中,用户标识符的改变反应的权限的变化(例如:改变为root账号),在多数情况下,这两个真实有效的用户标识符都可以改变,这使得在跟踪审核、认证登陆的用户变得困难,将Linux用户标识符和SELinux用户标识符分隔开来允许Linux用户标识符按需改变,而不会对SELinux用户标识符产生任何影响。