• 首发公号:Rand_cs

SELinux 安全模型——TE

通过前面的示例策略,大家对 SELinux 应该有那么点感觉认识了,从这篇开始的三篇文章讲述 SELinux 的三种安全模型,会涉及一些代码,旨在叙述 SELinux 内部的原理

SELinux 提供了 3 种安全模型:

RBAC:Role Based Access Control<基于角色的权限访问控制,它根据用户的角色和职责来管理对系统资源的访问权限。RBAC 将用户分配到不同的角色中,每个角色被赋予一组特定的权限,用户通过被分配到相应的角色来获得相应的权限,从而实现对系统资源的安全访问和管理。

TE:Type Enforcement,SELinux 最主要的安全模型,每一个主体客体都被分配一个类型,且使用白名单策略决定指定类型之间的访问权限。

MLS:Multi-Level Security,用于保护敏感和机密信息。这是 SELinux 对 BLP(Bell-La Padula Model) 模型的实现,编写策略可实现 "no read up, no write down"

本篇文章讲述 SELinux 最重要的安全模型:TE,Type Enforcement,针对类型的一种强制访问控制模式。

两种规则及其数据结构

它是 SELinux 的基石,百分之九十九的规则都是建立在 TE 之上的。TE 这种安全模型主要有两种规则,在之前示例策略种也说过,本文再来复习一遍:

  • Access Vector Rules,简单理解为 allow、neverallow、dontaudit 这些规则属于 AV 规则。其语法为 allow source target : class { perms },表示 source 对 class 类别的 target 的访问权限。
  • Type Rules,这类规则涉及类型转换,总共有 3 个,type_transition、type_change、type_member,后两个先不用管,重点知道 type_transion 就行。其语法规则为 allow a_t b_exec_t : process b_t,表示 a_t 这个类型的进程执行 b_exec_t 这个类型的可执行程序后,类型转换为 b_t。

虽然 TE 分为两大类规则,但是从形式上来讲,它们是统一的,都是 规则名 源类型 目标类型 目标类别 权限/转换后类型,可以看出,只有最后一部分是不一样的。但终归形式统一,所以在内存种这两大类规则对应的数据结构都是一样的。

我们可以将前面部分当作 key,最后的权限/转换后类型当作 value,如此,所有的 TE 规则实际上都以键值对存放在内存当中。

struct avtab_key {
u16 source_type; /* source type */
u16 target_type; /* target type */
u16 target_class; /* target object class */
#define AVTAB_ALLOWED 0x0001
#define AVTAB_AUDITALLOW 0x0002
#define AVTAB_AUDITDENY 0x0004
#define AVTAB_AV (AVTAB_ALLOWED | AVTAB_AUDITALLOW | AVTAB_AUDITDENY)
#define AVTAB_TRANSITION 0x0010
#define AVTAB_MEMBER 0x0020
#define AVTAB_CHANGE 0x0040
#define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE)
/* extended permissions */
#define AVTAB_XPERMS_ALLOWED 0x0100
#define AVTAB_XPERMS_AUDITALLOW 0x0200
#define AVTAB_XPERMS_DONTAUDIT 0x0400
#define AVTAB_XPERMS (AVTAB_XPERMS_ALLOWED | \
AVTAB_XPERMS_AUDITALLOW | \
AVTAB_XPERMS_DONTAUDIT)
#define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */
#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */
u16 specified; /* what field is specified */
};

上述为 key 值定义,可以看出 类型 type,类别 class 在内存中都是一个 16 位的无符号整数,可以看作它们的 ID 值,所以理论上来说最多只能定义 65535 个类型。

specified 指明当前是哪种具体的 TE 规则

struct avtab_datum {
union {
u32 data; /* access vector or type value */
struct avtab_extended_perms *xperms;
} u;
};

key 值对应的数据如上所示,只有一个元素 32 bit 无符号,扩展属性暂时不用了解,基本没人用

对于 AV 规则,data 为一组权限向量,比如说:

#define FILE__IOCTL                               0x00000001UL
#define FILE__READ 0x00000002UL
#define FILE__WRITE 0x00000004UL
#define FILE__CREATE 0x00000008UL
#define FILE__GETATTR 0x00000010UL
#define FILE__SETATTR 0x00000020UL
#define FILE__LOCK 0x00000040UL
.......

上述是 file 这个类别的权限位定义,在内核里面搜索会发现并没有上述定义,这些宏是内核编译的时候自动生成的,生成脚本对应着 linux/scripts/selinux/genheaders

上述可以看出,每个权限都是 32 bit 中的某一位, data 中某一比特位为 1 表示拥有该权限,为 0 表示没有该权限权限。举个例子,如果存在规则 allow a_t b_file_t : file read; 那么当查询 a_t 类型的进程 对 b_file_t 类型的文件有什么访问权限时,返回结果 data 值中对应 FILE__READ 那个比特位应该为 1

对于 Type 规则,也就是类型转换类的规则,data 表示转换后类型的 ID 值

AVC

TE 规则当中又数 AV 规则使用的最频繁,为了加快查找速度,内核设计了 AVC,Access Vector Cache。

struct avc_entry {
u32 ssid;
u32 tsid;
u16 tclass;
struct av_decision avd;
struct avc_xperms_node *xp_node;
}; struct avc_node {
struct avc_entry ae;
struct hlist_node list; /* anchored in avc_cache->slots[i] */
struct rcu_head rhead;
}; struct avc_cache {
struct hlist_head slots[AVC_CACHE_SLOTS]; /* head for avc_node->list */
spinlock_t slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */
atomic_t lru_hint; /* LRU hint for reclaim scan */
atomic_t active_nodes;
u32 latest_notif; /* latest revocation notification */
};

整个 cache 就是一个哈希表,由 avc_node 组成,每个 avc_node 又由 key 值(ssid, tsid, tclass)和 value(avd) 组成。

这里的 key 值有三个,ssid、tsid、tclass,我们对哈希表增删查改需要的 hash 值也是由这三个值算出来。sid,全称 security id,对于 sid 后面会详细讲述,这里先简单说一说。之前有提到过,在 SELinux 中,每个主体和客体都有一个安全上下文(标签),由 4 部分组成(user:role:type:mls),内核中由 struct context 来表示,而 sid 则是与 context 对应的一个 id 值,context 和 sid 一一对应

value 值为 av_decision,其结构体表示如下:

struct av_decision {
u32 allowed;
u32 auditallow;
u32 auditdeny;
u32 seqno;
u32 flags;
};

AV 规则有 4 种语句,allow,auditallow,dontaudit,neverallow,前三个与上述的定义对应,最后一个 neverallow 语句是在编译期间起作用,所以内核没有相关定义

对于每一种 AV 规则,内核都定义了一组权限向量,但其实只有 allowed 对应的向量才表示权限授予与否,其他的都是指示当前访问是否应该被日志记录。

比如说对于 FILE__WRITE,如果在 allowed 中对应的比特位为 0,表示没有权限写;如果在 auditdeny 中对应的比特位为 1,即使在 allowed 中表示没有权限写,但是也不会记录在日志中。

上述就是对 AVC 涉及的数据结构进行介绍,其他一些函数大多是哈希表常见的增删改查函数,这里不做详细说明,可以自己阅读相关内核代码,比较简单。这里主要说明权限检查函数,avc_has_perm。

int avc_has_perm(u32 ssid, u32 tsid, u16 tclass,
u32 requested, struct common_audit_data *auditdata)
{
//将存放权限查询结果
struct av_decision avd;
int rc, rc2; //调用此函数来进行真正的权限查询,查询结果存放在 avd 中
rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0,
&avd); //日志记录相关
rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc,
auditdata);
if (rc2)
return rc2;
return rc;
} inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
unsigned int flags,
struct av_decision *avd)
{
u32 denied;
struct avc_node *node; if (WARN_ON(!requested))
return -EACCES; rcu_read_lock();
// 根据键值在 avb 中查询 avd_node
node = avc_lookup(ssid, tsid, tclass);
// 如果 node 不存在,则会去查询 security server,然后分配 node,填充node,插入哈希表等操作
if (unlikely(!node)) {
rcu_read_unlock();
return avc_perm_nonode(ssid, tsid, tclass, requested,
flags, avd);
}
// ae.avd.allowed 记录着allowed权限向量,request 表示要查询权限对应的比特位
// 一通位操作下来,denied 为 1 表示没有该权限,反之有该权限
denied = requested & ~node->ae.avd.allowed;
memcpy(avd, &node->ae.avd, sizeof(*avd));
rcu_read_unlock(); // 一般不太可能为 denied,这么想,如果一个系统的 denied 很多,多半策略有问题,而且系统也不能正常运行
// 如果为 denied=1,则 SELinux 还有其他配置让它变为 allowed,比如说如果当前开启了 permissive 模式
// 所以这里还需要调用 avc_denied 再次判断当前模式、策略下是否真的没有该权限
if (unlikely(denied))
return avc_denied(ssid, tsid, tclass, requested, 0, 0,
flags, avd); // 返回 0 表示有权限
return 0;
}

举个栗子

这一小节用两个例子说明内核里面到底是如何进行 SELinux 权限检查和类型转换的

假如我们正在执行某个 exec 调用,需要检查当前进程是否对该文件有执行权限。内核里面是如何做检查的呢?首先是 DAC 检查,也就是检查是该文件是否有 x 权限位。

当我们访问文件的时候,内核里面经常会调用 inode_permission(idmap, inode, mask) 检查权限。比如说这里想要检查是否有 exec 权限,便会调用inode_permission(idmap, nd->inode, MAY_EXEC);之后会存在如下的调用路径:

inode_permission
security_inode_permission
selinux_inode_permission
//将想要查询的权限用 SELinux 中的向量表示
file_mask_to_av
// 这里的mode就是inode中的mode元素,作用之一就是指示当前文件类型
if (!S_ISDIR(mode)){
// mask 可以看作上层想要查询的权限,之后转换为SELinux中对应的权限
if (mask & MAY_EXEC)
av |= FILE__EXECUTE;
} else {
//如果访问的是目录,想要检查是否有 exec 权限,那么实际上是想要检查是否有搜索权限
if (mask & MAY_EXEC)
av |= DIR__SEARCH;
return av
// 调用 avc_has_perm 来查询权限,查询结果存放在 &avd 结构中
avc_has_perm_noaudit(sid, isec->sid, isec->sclass, perms, 0, &avd);

在 SELinux 中,对于目录和文件的执行权限有不同解释,对于普通文件,那就是真的检查是否可执行。但是对于目录文件来说检查是否能够执行其实指的是能否对该目录进行搜索。

其实在 DAC 中,对于目录和普通文件的 x 权限位解释也是不一样的,一个文件如果有 x,说明该文件可以被执行,如果一个目录有 x,指的是可以进入这个目录, 通俗来讲可以 cd 进去,就需要该目录有 x 权限。

我们上层的种种操作,其背后都需要各种权限,在 SELinux 安全检查的时候都会进行 SELinux 权限检查。

对于类型转换的流程,例子先不说了,这玩意儿还有点点复杂,后面单独来一篇文章说明,好了本文就先到这里,有什么问题欢迎来交流讨论

  • 首发公号:Rand_cs

SELinux 安全模型——TE的更多相关文章

  1. Android中SELinux的TE简介【转】

    转自:https://blog.csdn.net/murphykwu/article/details/52457667 selinux的概念如上一篇链接所示: http://www.cnblogs.c ...

  2. CnetOS6.7编译安装MariaDB

    --安装所需软件包 [root@localhost mariadb-10.1.14]# yum install bison bison-devel ncurses libxml2 libxml2-de ...

  3. SELinux/SEAndroid 实例简述(二) TE语言规则【转】

    本文转载自:https://blog.csdn.net/shell812/article/details/54930246 版权声明:本文为博主原创文章,未经博主允许不得转载.    https:// ...

  4. SElinux 读懂.te 定义自己的 .te【转】

    本文转载自:https://blog.csdn.net/kongbaidepao/article/details/61417291 一. .te 文件定义中的一些宏 1.1 unix_socket_c ...

  5. SELinux深入理解

    ps:今天在远程给服务器配置https的时候,一直乱码,以前做系统的系统第一件事情,就是关闭selinx,今天忘记了,然后就悲剧了... 弄了半天才弄好,镇定思痛,好好的来看下selinux 1. 简 ...

  6. 使用 SELinux 和 Smack 增强轻量级容器

    http://www.bitscn.com/os/linux/200904/158771.html 安全 Linux 容器实现指南 轻量级容器 又称作 Virtual Private Servers ...

  7. 深入理解SELinux

      目录(?)[+]   1. 简介 SELinux带给Linux的主要价值是:提供了一个灵活的,可配置的MAC机制. Security-Enhanced Linux (SELinux)由以下两部分组 ...

  8. Selinux是什么?

    在新的基于RHEL一般都自带了selinux,多数情况下我们把selinux禁用了,事实上既然RHEL要集成它,必然有他的优点和长处,我们通过下文来了解selinux,也许你会喜欢用上它. 英文原文来 ...

  9. Selinux安全机制

    1.Selinux安全机制简介 Selinux是Google在Android 4.4上正式推出的一套以SELinux为基础于核心的系统安全机制.而SELinux则是由美国NSA(国安局)和一些公司(R ...

  10. SElinux解决web网站无法访问

    SElinux解决web网站无法访问工具/原料centos 6.5系统httpd web服务器 SELinux 设置为enforcing:强制模式,代表 SELinux 运作中 方法/步骤1. 1se ...

随机推荐

  1. 使用JSZip实现在浏览器中操作文件与文件夹

    1. 引言 浏览器中如何创建文件夹.写入文件呢? 答曰:可以借助JSZip这个库来实现在浏览器内存中创建文件与文件夹,最后只需下载这个.zip文件,就是最终得结果 类似的使用场景如下: 在线下载很多图 ...

  2. 力扣196(MySQL)-删除重复的电子邮箱(简单)

    题目: 表: Person 编写一个 SQL 删除语句来 删除 所有重复的电子邮件,只保留一个id最小的唯一电子邮件. 以 任意顺序 返回结果表. (注意: 仅需要写删除语句,将自动对剩余结果进行查询 ...

  3. 3.CSS三种基本选择器

    三种选择器的优先级: id选择器 > class选择器 > 标签选择器 1.标签选择器:会选择到页面上所有的该类标签的元素 格式: 标签{} 1 <!DOCTYPE html> ...

  4. 深度 | 从DevOps到BizDevOps, 研发效能提升的系统方法

    ​简介:研发效能提升不知从何下手.一头雾水?阿里资深技术专家一文为你揭秘研发效能提升的系统方法. ​ 注:本文是对云栖大会何勉分享内容的整理 这几年"研发效能"一直是热词,很多组织 ...

  5. 技术干货|基于Apache Hudi 的CDC数据入湖「内附干货PPT下载渠道」

    ​简介: 阿里云技术专家李少锋(风泽)在Apache Hudi 与 Apache Pulsar 联合 Meetup 杭州站上的演讲整理稿件,本议题将介绍典型 CDC 入湖场景,以及如何使用 Pulsa ...

  6. [FAQ] Git远程仓库想把目录大写改为小写,windows本地不识别的的处理

      通过四步操作: 1. 先把忽略大小写设为false,即区分大小写git config core.ignorecase false 2. 拷贝出来备份那几个大写的目录,随后分支上操作删除,提交到远程 ...

  7. [ML] Tensorflow2 保存完整模型以及使用 HDF5

    将模型保存为完整的 HDF5 文件,后面可以直接加载使用: # cnblogs.com/farwish import tenforflow as tf model = tf.keras.models. ...

  8. Pod入门知识(4)

    一.Pod是什么? 官方文档:https://kubernetes.io/docs/concepts/workloads/pods/ Pod 是 Kubernetes 中的最小调度单元,k8s 是通过 ...

  9. JAVA基础-流程控制、字符串

    一.java基础 1.java主类结构 package com.study.again001; 包名public class helloword { 类名 static String s1 = &qu ...

  10. CentOS加载yum源时报错 [Errno 14] HTTP Error 404 - Not Found Trying other mirror.

    报错内容: [root@192 yum.repos.d]# yum repolist Loaded plugins: fastestmirror, product-id, search-disable ...