【文献翻译】 Reference Policy for Security Enhanced Linux

原文 Reference Policy for Security Enhanced Linux

1. Motivations

在用 SELinux 中的策略表述对应用程序的安全目的时,有一个强大的基础是十分重要的。由原来 NSA 策略发展而来的策略都有难以理解、开发和维护的问题,除非对 SELinux 的机制和策略都十分的熟悉。在示例策略中,源文件结构松散且策略模块非常藕合。为了创建一个新的应用程序基本策略必须了解全部的策略模块或其绝大部分。创建一个第三方模块也是非常困难的,因为其需要了解 type, role, user, attributes 等等的定义进行深入的了解。但是当下使用最多的策略依然是基于 NSA 策略的。对 SELinux 来说,编写策略过于困难和复杂依然是被抱怨最多的问题。

Tresys 根据社区在发展 NSA 策略获得经验开始了 Reference Policy 项目。它展现出了很多现代软件工程的很多优势和特性,因此这也使得其更易于维护,检验和使用。

2. Reference Policy Goals

Reference Policy 有两个主要的目标:

  • 安全:为了能够达到 SELinux 和 应用程序的安全目的提供一个更好的基础。

  • 功能性:提供一个更好的基于软件工程方法的结构来组织策略源文件,使其更易于维护,添加策略更加简单并且可以构建高层的应用工具,例如基于图形的策略编辑器。

2.1 Security Goals

为了安全是使用 SELinux 的主要原因,因此这也是 Reference Policy 的主要目的。通常,查看一个系统的安全性时,只有安全和不安全两种状态。但实际上这远远不够,不同的系统有不同的用处,同样就应该有着不同的安全评判方法。SELinux 的一个重要特性就是使用 type 并作为其强制访问控制机制,该机制允许策略编写者指定所需的安全属性。

Reference Policy 有四个安全目标,通常这些也是大多数 SELinux 的目标。

  • 自我保护:保护系统自身,如 SELinux 策略和 每一个应用程序(不受其他程序威胁);
  • 保证:在整个策略开发生命周期中,信任所有的 SELinux 策略模块的正确性和完整性。包括广泛使用最小特权和限制错误传播,以及增强可理解性;
  • 安全可扩展性:当维护其他安全目标和保证时,允许指定的程序和服务被添加进 SELinux 策略。
  • 改进的角色分离:通过最小权限和详细的角色定义来改进当前 SELinux 策略的缺点。

到目前为止,我们已经在这些所有的安全目标上取得了重大进展,除了最后一个改进的角色分离,这是未来的工作计划。

2.2 Functional Goals

在开发 SELinux 策略中会面对一些难以处理的问题,而这些是 Reference Policy 准备处理的,包括:

  • 管理复杂性:减少编写策略时必须管理详细信息的数量,简化创建新策略模块的过程,并且无需深入了解底层策略的实现;
  • 可加载模块支持:支持已存在的整体策略和兼容可加载策略模块的基础的策略,它们可以构建于同一份源文件。
  • 对工具的更友好的支持:对策略进行结构化,使复杂的策略开发和分析工具能够更容易地与策略源一起工作;
  • 第三方支持:允许第三方使用稳定的、定义良好的接口创建策略模块;
  • 便于理解:允许策略编写人员通过积极的添加文档使得策略更易于理解,能够做出与安全性相关的决策。
  • 策略配置:使用 MLS 和最近添加的 MCS 配置作为构建选项可以支持严格且有针对性的策略,不需要对策略或多个策略源树进行破坏性更改。

3. Concept,Rules,and Conventions

为了达到目的,Reference Policy 介绍并形式化了一些的新的概念。还为策略模块的构造使用强制规则,以帮助确保这些概念得到执行。

3.1 Design Concepts

通过借鉴现代软件工程,这里定义了一些列的概念用来指导 Reference Policy项目。

3.1.1 Layering

Reference Policy 中的 Layering(分层)是一个相当弱的原则,主要用于帮助组织模块和简化用户理解。一个模块属于哪一层的决定因素有时是模糊的。一般来说,模块在系统中是按功能分组的。较底层的模块通常被包含在大多数系统策略中,例如,保护内核资源或启动/关闭系统。较高层次的模块更趋向于可选的,按需使用的。例如对于用户应用程序或基于网络的服务。当前定义的层次有:kernel、system、administrator、services,和 application。

3.1.2 Modularity

模块是 Reference Policy 中最小的组件,将于对特定域及其资源的相关策略语句合为一组。划分组主要是基于功能的相似性。每一个模块都包含策略资源声明(例如:type,attribute),相关的策略规则,和具体的对象标签需求。这种形式的模块构成了封装和抽象策略语句的基础,我们将在下面进一步讨论这些策略语句。每一个模块都与 Red Hat 系统的 RPMs 一样,可是选择是否安装来减轻系统配置。

现有的示例策略已经发展成一种有助于管理复杂性的模块化形式。但是一般来说,模块仍然是紧密耦合的。在 Reference Policy 中模块的组织和定义上将更加的严格,并将一些强制规则作为惯例。

每一个模块都有三个源文件:

  • 一个私有模块策略:包含模块本地的声明和规则;
  • 一组外部接口:为了能能够被其他模块使用,提供了访问私有模块策略的类型和属性的抽象。这些接口应该被设计成直观的,反映了对某些系统功能的访问。策略开发人员应该能够使用接口,了解其用途,而不必了解如何在模块中实现该用途。
  • 一个文件标签策略;文件标签策略文件由与模块关联的文件上下文语句(如果有的话)组成。

3.1.3 Encapsulation and Abstraction

模块的封装规则确保模块的所有实现细节对模块本身是私有的。允许在不影响其他策略模块的情况下更改模块的实现,从而减少了策略模块的耦合。

Reference Policy 有几个重要的策略书写规则,用以维持封装和抽象的重要概念。类型和属性应当是模块的私有数据。因此,在声明类型和属性的模块之外,类型和属性可能不会直接由名称引用。这也意味着不存在全局类型或属性

抽象用于创建更高级别的访问概念。这使得策略编写者能够做出与安全相关的决策,而不是被理解所有实现细节所阻碍。M4 宏用于创建抽象,不过通常我们对所允许的宏类型执行严格的约定,而不像当前的实践允许M4支持的任何形式的宏。每个宏只包含一个概念,并且规则不是为了方便而添加的。

Reference Policy 支持三种类型的宏:

  1. interface

Interface 宏是那些访问对模块内部类型和转换类型的宏。例如,使一个调用者的类型为“domain”。Interface 是 Reference Policy 主要的创新之一,提供大部分模块实现的解耦。

  1. template

模板宏是一种特殊形式的 Interface,它代表调用者创建和操作派生类型。虽然派生类型在概念上对调用者是私有的,但是它们的实现细节完全包含在创建 template 宏的模块中。例如,使用这些宏创建 ssh 域的类型和规则以及每个角色的键。

  1. support

support 宏与 NSA 示例策略中的许多“核心宏”处于并行,提供公共策略模式。示例包括域转换和对象类权限集。支持宏本质上是“辅助函数”,仅用于帮助简化模块的私有内部实现。

3.1.4 Module Interfaces

当前结构最根本的改变时在模块外部获取定义在模块内部的访问权限类型。Interface 提供了访问模块内部策略资源的能力。所有的域需要获取特定的权限都将使用同样的接口。因此策略规则要求接的访问要在所有用户中一致。因此,策略的访问类型更改只需要在一个地方进行更改,而不像样例策略中常见的那样需要更改使用该类型的所有模块。

将来,向基础语言中添加接口和模板宏语义可能是有意义的。作为宏,任何时候修改接口,使用该接口的所有模块都必须重新编译。通过语言中的接口,模块将在可加载模块级别和源级别上解耦。

更进一步说明,接口需要遵循清晰的命名约定。特别是模块名,或其是接口名前缀的简称。这允许策略编写人员查看策略,并轻松地查看所有接口调用的位置。此外,需要用一致动词用于描述访问,如读、写和删除。

每个接口包含两个部分,依赖项和访问。依赖项包含在 gen_require() 宏中。此宏包含将放置在可加载模块的 require 块中的语句。它列出接口使用的所有类型和属性。如果使用用户空间对象管理器的对象类,如 DBUS 或 NSCD,则还必须列出对象类和所需的权限。

3.1.5 Module Template Interface

模板宏是唯一可以将类型和属性声明为接口的一部分的宏,所谓的派生类型。定义模板宏的模块是惟一可以定义对派生类型的显式访问的地方。接口和模板宏之间唯一真正的区别是模板宏代表了调用者来的使用和创建派生类型。这两个宏都是定义模块的“接口”,可以访问模块的私有类型。

4. Module Example

下面是来自 BIND DNS 服务器的 Reference Policy 的摘录。

4.1 bind.te

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
policy_module(bind,1.1.0)  

type named_t;
type named_exec_t;
init_daemon_domain(named_t,named_exec_t)

type named_cache_t;
files_type(named_cache_t)

type named_conf_t;
files_type(named_conf_t)

type named_zone_t;
files_type(named_zone_t)

allow named_t named_cache_t:file manage_file_perms;
allow named_t named_conf_t:file 08| r_file_perms;
allow named_t named_zone_t:file r_file_perms;

kernel_read_system_state(named_t)
kernel_read_network_state(named_t)

corenet_non_ipsec_sendrecv(named_t)
corenet_udp_sendrecv_all_if(named_t)
corenet_udp_sendrecv_all_nodes(named_t)
corenet_udp_sendrecv_all_ports(named_t)
corenet_udp_bind_all_nodes(named_t)
corenet_udp_bind_dns_port(named_t)
logging_send_syslog_msg(named_t)

bind.te 显示了完整的名为 domain 的完整私有策略。 policy_module() 宏包含了模块名称和版本的信息,这些信息用于可载入的策略模块。接下来的 9 行包含 type 声明和变换宏。domain 的 type 和它的入口函数通过 init_daemon_domain() 宏声明和转换,这个宏是该模块的初始化接口。剩下的私有策略包含对于 BIND Server 的规则。