Linux内核安全模块深入剖析【2.3】

发布时间:2026/5/22 9:14:28

Linux内核安全模块深入剖析【2.3】 第 9 章 Tomoyo9.1 简介Tomoyo 是另一个 Linux 内核安全模块。 SELinux 和 SMACK 的名字都来自英文单词缩写 Tomoyo 则不同它是一个日本女性的名字写作汉语是“友子”或“知子” ㊀ 。 Tomoyo 的开发者是日本的 NTT Data 公司开发起始于 2003 年 3 月此时距离 SELinux 被 Linux 主线接受只有五个月。Tomoyo 的开发者不会不知道成熟的 SELinux 已经占尽先机。那么这些日本的 Linux内核安全人员为什么要自己开发一个新的内核安全模块而不是使用已有的 SELinux 呢客观地说 Tomoyo 的确有独到之处。9.1.1 基于路径的安全SMACK 鼓吹的是简单。 Tomoyo 推出的则是另一个卖点而这又涉及一场至今没有结果的争论基于 inode 的安全与基于路径的安全哪一个更安全自 UNIX 诞生之日起文件就是一个非常重要的概念。文件是什么文件首先包含一堆数据这堆数据就是文件的内容。除了内容之外文件还有一些被称为元数据的关联信息需要存储比如文件的拥有者、文件的创建日期、文件的长度等。在 UNIX/Linux 文件系统中元数据存储在文件的 inode 中 文件本身存储的是内容数据。 用户态进程访问文件并不是通过 inode内核根本没有提供通过 inode 号来查找文件的系统调用。进程访问文件需要通过目录将各级目录和文件名串联起来就是一条文件路径。要为文件引入安全属性很自然地想到安全属性不是文件内容而是属于元数据应该与inode 关联。于是很多文件系统引入了扩展属性一些内核安全模块将安全属性存储在文件的扩展属性中这种方式就是基于 inode 的安全。基于 inode 的安全的优点主要有两个1文件的安全属性与文件路径无关。文件可以在不同目录间移动不管它怎么移动它的安全属性都没有变化。2同一个文件可以有多个链接从不同链接访问文件其安全属性总是一样的。基于 inode 的安全的缺点是1文件系统必须支持扩展属性并且挂载文件系统时必须使用扩展属性。现在这个问题已经基本得到解决了。 目前 Linux 上大多数文件系统已经支持扩展属性 并且挂载时缺省使用扩展属性。2删除文件时文件的安全属性会随之消失。再在原先的路径处创建同名文件并不能保证新文件和老文件的安全属性相同。3安装软件和升级软件需要保证系统中新的文件具有正确的安全属性。新文件来自软件包新的安全属性自然也应该来自软件包。于是有了下一个要求众多软件包格式也需要支持㊀ http://en.wikipedia.org/wiki/Tomoyo文件的扩展属性比如 tar、 cpio 等。下面说说基于路径的安全。从用户角度看用户通过路径访问文件用户态进程无法用 inode 号来访问文件。即使是基于inode 的安全用户读写安全属性也要先通过路径找到文件然后才能访问文件的安全属性。那么能否将文件的安全属性简单地与文件路径对应起来呢比如/bin/bash 的安全属性是“system-shell” /usr/local/bin/bash 的安全属性是“local-shell”。不把这些安全属性存储在文件的扩展属性中而是存储在系统内部的一张表里这就是基于路径的安全的实现原理。这样做的优点是1不需要文件系统有额外支持。2不怕文件更新对打包格式也没有额外要求。用户甚至可以为还不存在的文件定义安全属性。基于路径的安全的缺点是同一个文件可能有多个安全属性简单地创建链接就可能让文件拥有另一个安全属性。在 Linux 四个主要的安全模块中 SELinux 和 SMACK 是基于 inode 的安全㊀ Tomoyo 和AppArmor 是基于路径的安全。与基于 inode 的安全相比基于路径的安全的最大优点是容易使用。9.1.2 粒度管理SELinux 和 SMACK 都是将系统中所有的进程作为一个整体要么所有的进程都受控制要么所有的进程都不受控制。 Tomoyo 可以做到让系统中部分进程受到控制部分进程不受控制。更进一步 Tomoyo 还可以做到让进程的某些操作受到控制某些操作不受控制。9.1.3 用户态工具在 Linux 内核的 5 个安全模块中 Tomoyo 的用户态工具是最易于使用的。 Tomoyo 声称不仅能做访问控制还能探测系统行为。在 Tomoyo 的学习模式下 Tomoyo 的用户态工具会将所有进程的操作记录下来。简单的 SMACK 没有这个功能 SELinux 有一个类似的称作 permissive的模式但不如 Tomoyo 的学习模式易用。第一它只会记录第一次违反策略的行为以后的违反策略一律放行并且不会留下记录 第二 SELinux 是基于 inode 的 所以它不会记录文件名第三 SELinux 的处理方式烦琐内核的 SELinux 通过内核中的 Audit 子系统输出消息用户态 Audit 守护进程接收消息存储到日志文件 SELinux 用户态工具分析日志文件。而 Tomoyo 则是用户态工具通过内核伪文件接口直接采集来自内核的 Tomoyo 数据呈现给用户。9.1.4 三个分支Tomoyo 与其他内核安全模块的一个区别是它公开发布三个代码分支。第一个是功能最全的 1.x 系列这是开发的主线其功能与代码都是最新的但是这个分支的代码是以内核 patch的形式发布的使用者需要修改内核代码编译内核之后才能使用。第二个分支是 2.x 系列这个分支已被 Linux 主线接受所以不需要修改内核而且许多 Linux 发行版已将其编入所发行内核。但是这个分支的代码最为陈旧功能也最少。第三个分支叫 AKARI以模块形式发布功能和代码更新程度处于 1.x 和 2.x 之间。本章以下部分以 Tomoyo 2.5 为研究对象。9.2 机制作为在 Linux 内核中的一个强制访问控制子系统 Tomoyo 的理论基础是类型增强。类型的另一种称呼是域本章以下内容主要用域这个名词。基于域的访问控制本质上包含两个核心问题域内的操作许可和域间的转换规则。9.2.1 操作许可相对来说 Tomoyo 可以控制的客体类型并不多只有文件、网络和环境等几类。网络实际上指的是套接字网络之下又可分为 INET 和 UNIX 两小类。 Tomoyo 中用 ENVIRON 来代表环境。“环境”是 Tomoyo 独有的一种客体类型 其他强制访问控制模块——SELinux、 SMACK、 AppArmor都没有这种客体类型。“环境”指的是进程的环境变量。在进程执行 execve 时起作用用来控制执行 execve 后进程可以得到的环境变量。举个例子如果在策略文件中规定了执行 execve 前后进程可以读取的环境变量包括 PATH、 PWD、 RUNLEVEL 等但是不包括 LD_PRELOAD那么通过环境变量 LD_PRELOAD 使得进程加载恶意共享库的攻击手段就无效了。在 Tomoyo 中不同的客体类型有不同的操作许可。客体类型隐含在操作许可之中。以下列出 Tomoyo 2.5 中的操作许可前缀包含“FILE”的都是文件类型的操作许可前缀包含NETWORK 的都是网络类型的操作许可。9.2.2 类型和域Tomoyo 的强制访问控制的理论基础是类型增强。“增强”指的是 9.2.1 节提到的操作许可通过这些操作许可规范进程行为规定进程在当前的域中可以做什么。理论上主体和客体都应该有类型。但是 Tomoyo 对类型进行了简化只有主体即进程有类型。类型增强的一条典型的策略控制语句是类型 A 的主体进程可以对类型 x 的客体进行 m 操作文件 f 的类型为 x。在 Tomoyo 中这条语句就变为域 A 的进程可以对 f 文件进行 m 操作。那么 Tomoyo 的域又是什么呢 Tomoyo 的域就是进程的执行历史。比如某人在登录系统通过字符终端登录后运行“ls”命令这个运行“ls”命令的进程的域就是kernel /sbin/getty /bin/login /bin/bash /bin/ls从这里我们可以看到 kernel 启动之后运行/sbin/getty㊀ 然后运行了/bin/login当某人输入了正确的用户名和口令之后 /bin/login 运行/bin/bash最后在这个 shell 中运行了某人输入的命令“ls”。Tomoyo 域转换工作主要是在内核的 execve 系统调用实现中进行的缺省操作就是将被执行文件的全路径名附加在当前域的最后。所以 Tomoyo 的域是由全部执行历史决定的。比如某人运行“sudo ls”产生的进程的域就是kernel /sbin/getty /bin/login /bin/bash /usr/sbin/sudo /bin/lsTomoyo 的这种设计是简单而有效的。简单是指系统管理员不必为域的定义费心。对比一下 SELinux SELinux 的域是由策略定义的策略的制定者要通过策略语句来分出不同的域。有效是指 Tomoyo 的域不是由进程当前所执行的文件唯一决定的。在本地终端登录的 shell 和远程登录的 shell 处于不同的域管理员可以配置策略让这两个 shell 的行为有所区别。9.3 策略Tomoyo 也遵循机制和策略分离的原则。 9.2 节讲述机制本节讲述策略。Tomoyo 是内核的一个子系统它使用的策略本质上是存储于内核内存中的数据表。用户接触的是策略文件用户制定策略就是编辑策略文件用户查看策略就是查看策略文件。需要注意的是内核中的 Tomoyo 子系统并不会访问策略文件。用户要想让自己编辑的策略生效需要将策略文件加载进内核的策略数据表。这个过程在 Tomoyo 中是通过安全文件系统securityfs的伪文件接口来实现的。无论是 SELinux 还是 SMACK内核内存中的策略数据表都不是一个 Tomoyo 也是如此。但是 Tomoyo 为不同的策略数据表设计了不同的伪文件接口。9.3.1 域策略域策略的作用是规定系统中每个域的操作许可。域策略的策略文件的一个可能的位置在/etc/tomoyo/之下。之所以说“可能”是因为文件位置并不重要它完全由 Tomoyo 的用户态工具决定。安全文件系统通常的挂载点在/sys/kernel/security。域策略的伪文件接口在/sys/ kernel/security/tomoyo/domain_policy。读这个文件就可以看到当前系统中所有域的操作许可rootubuntu-desktop:/sys/kernel/security/tomoyo# cat domain_policy … kernel /sbin/init /sbin/getty /bin/login /bin/bash use_profile 1 use_group 0 file execute /bin/ls file write /dev/null … kernel /sbin/init /usr/sbin/sshd …domain_policy 文件的内容就是系统中所有域的操作许可。每个域的格式是第一行是域的标识基本可以等价于进程的执行历史第二行是域使用的轮廓第三行是域使用的异常组第四行是空行第五行以下是域的操作许可最后是一个空行。9.3.2 异常前面的例子里有个问题像/dev/null 这样的文件谁都可以写在每一个域里面专门写出一条允许策略语句是很浪费资源的。此外假设进程处在下面这个域里kernel /sbin/init /sbin/getty /bin/login /bin/bash进程执行/bin/bash进程的新域是kernel /sbin/init /sbin/getty /bin/login /bin/bash /bin/bash如果再次执行/bin/bash进程的新域变为kernel /sbin/init /sbin/getty /bin/login /bin/bash /bin/bash /bin/bash有必要区分上面这三个域吗显然没有必要。Tomoyo 通过异常来规范策略语句减少资源浪费。异常相关的策略语句的伪文件接口是/sys/kernel/security/tomoyo/exception_policy。异常exception这个词有些词不达意但作者又找不到一个更合适的词来表达。下面看一下具体的异常策略语句。1.域定义相关的异常1域保持域保持就是让进程的域保持原样不会因为执行文件而变化。举个例子keep_domain any from kernel /usr/sbin/sshd /bin/bash这意味着通过 ssh 登录而产生的 shell 进程固定在域“kernel /usr/sbin/sshd /bin/bash”中再执行文件进程的域不再发生变化。在此基础上策略语句的语法可以变化为keep_domain any from /bin/bash上面语句没有列出一个完整域名只有域名中最后一级所执行文件的全路径名意思是在执行了/bin/bash 的进程中再执行任何文件都不会发生域转换了。keep_domain /bin/bash from /bin/bash上面语句的意思是在/bin/bash 进程中执行/bin/bash 不会发生域转换执行别的文件仍然可以引起域的变化。2域初始化根据前面的介绍进程的域就是进程执行文件的历史。当然有些历史是进程从祖先进程处继承来的。有时候进程的域中包含太多的历史信息实在没有必要域初始化语句就是用来删除不必要的历史信息的。举个例子initialize_domain /usr/sbin/sshd from any这条语句导致执行/usr/sbin/sshd 将会转换到这个域中kernel /usr/sbin/sshd除了最后一个执行文件的记录外还有一个用“”和“”包裹的“kernel”。用“”和“”包裹的是 Tomoyo 的名字空间下文会有讲述。any 也可以替换为域名或路径名initialize_domain /usr/sbin/sshd from kernel /etc/init.d/sshd这条语句导致在系统启动过程启动的 ssh 服务进程处在初始化的域中而其他方式启动的ssh 进程则不是。3创建名字空间首先看一下什么是 Tomoyo 的名字空间namespace。名字空间的形式是用“”和“”包裹的一个字符串。一个名字空间会关联一组策略、异常以及 9.3.3 节要提到的轮廓。 Tomoyo缺省的名字空间是“kernel”。创建新名字空间的语句是“reset”语句reset_domain /usr/sbin/sshd from any上 述 语 句 的 含 义 是 在 任 意 域 中 执 行 /usr/sbin/sshd 导 致 域 转 换 到 新 的 名 字 空 间“/usr/sbin/sshd”也就是说 sshd 进程的新域是“/usr/sbin/sshd”。假设 sshd 进程创建了子进程并在子进程中执行/bin/bash那么子进程的域就是“/usr/sbin/sshd /bin/bash”。4异常的异常为了增加灵活性 Tomoyo 又提供了 3 个 no 开头的异常策略语句 no_keep_domain、no_initialize_domain、 no_reset_domain来抵消 keep_domain、 initialize_domain、 reset_domain的作用。举几个例子keep_domain any from kernel /usr/sbin/sshd /bin/bashno_keep_domain /bin/cat from kernel /usr/sbin/sshd /bin/bash上述异常策略语句表示在 ssh 登录产生的 shell 中如果 shell 进程创建的子进程执行了/bin/cat那么 cat 进程所在的域是“kernel /usr/sbin/sshd /bin/bash /bin/cat”。 shell 进程执行其他文件时其所在的域是“kernel /usr/sbin/sshd /bin/bash”。initialize_domain /usr/sbin/sendmail from anyno_initialize_domain /usr/sbin/sendmail from /bin/mail述异常策略语句表示如果一个进程的域的最后一个字段是“/bin/mail”也就是说该进程所执行的文件是/bin/mail那么当该进程执行/usr/sbin/sendmail 时新的域是“…/bin/mail/usr/bin/sendmail”。反之如果进程的域的最后一个字段不是“ /bin/mail”那么进程执行/usr/sbin/sendmail 后的新域就是“kernel /usr/sbin/sendmail”。reset_domain /usr/sbin/sendmail from anyno_reset_domain /usr/sbin/sendmail from /bin/mail上述异常策略语句表示如果一个进程的域的最后一个字段是“ /bin/mail”那么当该进程执行/usr/sbin/sendmail 时新的域是“…/bin/mail /usr/sbin/sendmail”。反之如果进程的域的最后一个字段不是“/bin/mail” 那么进程执行/usr/sbin/sendmail 后的新域就是“/usr/sbin/sendmail”。2.其他异常异常策略语句的另一个作用是提供归类功能减少策略语句数量节约内核内存节省策略制定者的时间和精力。1聚合Tomoyo 是基于路径的一条典型的访问控制策略是file execute /bin/less这条语句的意思是允许执行/bin/less。 less 和 more 功能相同能不能对两者一视同仁呢于是产生了聚合语句aggregator /bin/more /bin/less上述异常策略语句表示在策略文件中“/bin/less”和“/bin/more”同义凡是对“/bin/less”的操作许可默认也存在于“/bin/more”之上。2路径组、数字组和地址组1路径组总是写全路径名太麻烦了引入正则表达式会方便些。于是有了路径组path_group HOME-DIR-FILE /home/\*/\*在策略中使用 path_group 所定义的“HOME-DIR-FILE”需要前缀“”file read HOME-DIR-FILE2数字组同理数字组的出现是为了避免在策略文件中反复输入同一个数字number_group CREATE-MODES 0644使用的策略file create /tmp/file CREATE-MODES3地址组再看为网络地址而设的地址组:address_group LOCAL-ADDRESS 10.0.0.0-10.255.255.255address_group LOCAL-ADDRESS 172.16.0.0-172.31.255.255address_group LOCAL-ADDRESS 192.168.0.0-192.168.255.255使用的策略network inet stream accept LOCAL-ADDRESS 1024-655353访问控制组前面提到有些操作许可在每个域都应该具备比如“file write /dev/null”。如果每个域都为这种通用的操作许可写一条策略语句那就很浪费资源了。访问控制组就用来定义通用的操作许可。下面看一个例子acl_group 0 file read /dev/nullacl_group 0 file read /etc/localtime策略会在每一个域下面声明这个域使用的访问控制组下面看一个例子

相关新闻