Linux内核安全模块深入剖析【2.1】
7.4 伪文件系统的含义UNIX/Linux 系统中的文件本来是用来存储数据的文件本来都对应着一块存储区域。后来随着系统开发的深入 UNIX/Linux 内核开发人员引入了一种特殊的文件这种文件和内核中的某个或某些变量相关联。内核为这种文件提供特殊的读写函数。用户态进程读这种文件时内核向进程返回内核中变量的值用户态进程写这种文件时内核会根据进程的输入改变内核中变量的值。这种文件的存在不是为了存储数据 而是为了提供一个内核和用户态进程之间的便捷接口。有些书称这种文件为虚拟文件称这种文件所在的文件系统为虚拟文件系统。虚拟文件系统的英文是“Virtual File System”它和 Linux 文件系统中一个现有的术语冲突因此本书使用伪文件和伪文件系统这两个术语。伪文件系统的例子有 proc 文件系统和 sys 文件系统。7.5 SELinux 相关的伪文件系统SELinux 选择伪文件接口作为内核和用户态进程的接口。 SELinux 使用了两种伪文件系统一种是 SELinux 创造的 selinuxfs另一种是 proc 文件系统。7.5.1 selinuxfs在 Linux 系统中创造一种新的文件系统是一件相对容易的事。而类似 proc 和 sys 这样的伪文件系统的基本作用就是作为内核和用户态进程的接口由于它们的存在 Linux 的系统调用数量没有呈现爆炸式的增长。 SELinux 创造的伪文件系统 selinuxfs 的作用是让用户态进程管理内核中的 SELinux。普通开发人员一般不用关心 selinuxfs因为 SELinux 提供的库函数封装了对 selinuxfs 的使用。下面简单看看 selinuxfs 中的内容和作用。当用户态进程打开系统调用 open selinuxfs 之上的文件时 SELinux 会判断进程对文件客体是否有读/写操作许可。当进程读系统调用 read或写系统调用 write文件时如果这个文件和 SELinux 安全客体有关联的话 SELinux 会判断进程是否有安全客体上的相关操作许可。selinuxfs 中包含的文件见表 7-3。值得仔细分析的是 create、 member 和 relabel 这三个文件。这三个文件的用法相同都是写入源安全上下文、目的安全上下文、客体类别读回新的安全上下文。实际上它们和 7.3.3 节讲述的转换策略有关。 create 文件对应的是 type_transition 策略规则 member 对应的是 type_member策略规则 relabel 对应的是 type_change 策略规则。create 文件的一种用法是用来参与制定进程的安全上下文的。进程先利用 create 文件得到新的安全上下文再利用 7.5.2 节将讲述的/proc 文件接口修改进程的某个安全上下文。member 文件的用法比较罕见。在管理用户登录的 pam 软件中可能会为不同的用户分配不同的/tmp 目录这些/tmp 目录存在于不同的文件系统命名空间中因为它们为不同的用户服务所以也会有不同的安全上下文。看起来所有这些/tmp 目录都是一类实际又有所不同。 pam软件会利用 selinuxfs 的 member 文件去查询某个/tmp 的安全上下文。relabel 文件的使用例子是 tty用户登录后系统会改变用户所使用的 tty 的安全上下文。此外 selinuxfs 中还有几个目录见表 7-4。7.5.2 procSELinux 为每个进程在 proc 文件系统下增加了一个目录“attr”。它的主要使用者仍然是SELinux 在用户态空间的库函数。目录中的文件见表 7-5。除了文件 prev 之外这些文件都是可读可写的。当然写文件操作需要 SELinux 策略的允许。7.6 总结提到 SELinux作者想到的是两个词权威和复杂。 SELinux 的理想是让 SELinux 的安全机制可以覆盖 Linux 系统的方方面面这个理想实现了。 SELinux 之后的 4 个安全模块都没有做到全系统覆盖。SELinux 的第二个理想是让 Linux 系统中所有的开发人员、管理人员和使用人员都自觉地学习 SELinux使用 SELinux。这个理想没有实现。因为大家都在抱怨 SELinux 太复杂了。SELinux 的开发者认为自己努力地开发了一个完美的安全系统但是发现大家不用而且随着时间的推移使用率并没有增加。 SELinux 的一个开发者 Dan Walsh 为此制作了一个网站http://stopdisablingselinux.com/鼓励人们使用 SELinux。作者认为 SELinux 的复杂正来源于它的理想。 SELinux 太想从根本上解决 Linux 的安全问题它引入了一套机制但这套机制和 UNIX/Linux 原有的概念和机制完全不同。这无疑增加了 SELinux 的学习难度。SELinux 的设计理念似乎是大而全它的机制包含基于角色的访问控制、类型增强、多级安全。而 UNIX/Linux 的理念是一个模块只做一件事。像 SELinux 这样的设计在UNIX/Linux 中是不多的。7.7 参考资料读者可参考以下资料。Richard Haines. The SELinux Notebook-The Foundations, 2014. http://freecomputerbooks.com/ books/The_SELinux_Notebook-4th_Edition.pdfhttp://selinuxproject.org/page/Main_Page习题1. selinuxfs 中的 disable 文件似乎没有对应安全相关的操作。 SELinux 对于关闭自身这个操作没有做额外的保护2.查看代码列出写文件/proc/[pid]/attr/exec 所需的所有条件。提示包括自主访问控制的约束比如进程的 euid 为 0也包括本章讲述的 SELinux 的约束。3.使用 SELinux 策略语言表达允许类型为 A 的进程执行文件 b执行后进程的类型变为 B需要至少几条策略语句4. SELinux 现有机制包括基于角色的访问控制、类型增强和多级安全。如果去掉基于角色的访问控制和多级安全只保留类型增强那么可以删除哪些类策略语言如果只保留基于角色的访问控制那么又该删除哪些类策略语言调整哪些类策略语言5. SELinux 为了控制进程的能力引入了两个客体类别 capability 和 capability2。这么做的原因是什么如果要改进可以怎么改第 8 章 SMACK8.1 历史SMACK 是“ Simplified Mandatory Access Control Kernel”的缩写。它的作者是 Casey Schaufler目前在 Intel 从事手机操作系统 Tizen 的安全开发工作。 SMACK 于 2008 年 4 月 16日进入了 Linux 2.6.25是继 SELinux 之后第二个进入 Linux 内核主线的安全模块。SMACK 的出现在 Linux 内核社区引发了很大的争论。争论的背景是 SELinux 在 2003 年进入了 Linux 内核主线之后并未能如预想的那样为广大系统管理员和应用开发者理解和接受。管理员在发现系统因 SELinux 策略配置问题而不能正常运行时 往往是简单地关闭 SELinux 功能而不是去调试和修改 SELinux 策略。应用开发者的开发工作只包括开发应用的功能不包括开发应用相关的 SELinux 策略。 Linux 发行版比如 RedHat面对众多的应用只能为一部分核心应用开发 SELinux 策略。这就造成了 SELinux 安全策略滞后于应用用户在运行应用时 SELinux 常常阻碍应用的正常运行。面对复杂的系统系统管理员没有能力为系统中各个应用调整 SELinux 策略。几乎没有管理员能够完全理解 SELinux 系统的各方面。应用开发者没有精力也没有意愿开发应用相关的 SELinux 策略。他们通常的抱怨是 SELinux 太复杂了无法掌握。普通用户呢普通用户面对 SELinux 引起的“故障”通常是束手无策。指望普通用户读懂 SELinux 的错误日志是不现实的。怎么办一种解决办法是强制推行在内核中彻底去除 LSM 机制让 SELinux 机制无法回避地成为内核中不可删除的一部分。让所有的 Linux 内核开发人员、 Linux 应用开发人员、 Linux 系统管理员以及 Linux 用户都必须去适应 SELinux学习它掌握它。 Linux 内核安全子系统负责人 James Morris 的观点很有代表性㊀ 。他认为内核安全开发人员要想成为内核开发队伍的一等公民就要消灭 LSM 机制让 SELinux 不可替代让内核安全代码不能被忽视。第二种解决方法是既然 LSM 机制的目的是允许多个安全模块并存既然 SELinux 的主要问题是复杂难用至少表面上看起来是那就设计出一种简单的安全模块来作为 SELinux 的替代品。 SMACK 就是循着这个思路而产生的。SMACK 的出现让 SELinux 的开发者和拥护者备受打击。它不仅动摇了 SELinux 的地位㊀ http://lwn.net/Articles/252562/而且让 SELinux 开发团队的理想更加难以实现。有了 SMACK内核安全开发人员还要继续作Linux内核开发社区的二等公民 Linux应用的开发者会继续忽视SELinux 而不是学习SELinux。面对 SMACK SELinux 开发者的自然反应就是阻挠 SMACK 进入 Linux 内核主线。而 SMACK的开发者 Casey Schaufler 偏偏是一个不屈不挠的人他一次又一次地提交自己的作品。这时两个重量级人物站出来支持 SMACK第一个是 Andrew Morton他说“我不是很懂安全。在读过你提交的代码后我的观点是代码本身的质量很好但是似乎SELinux 可以有相同的功能。将上面那个‘但是’作为不接受 SMACK 的理由对我而言有些困难。我更倾向于接受 SMACK然后看大家是否用它。”Andrew Morton 虽然表示 SMACK 在功能上并没有超越 SELinux但是明确表示倾向于将SMACK 纳入 Linux 内核主线。这还不够。第二个重量级人物站了出来这个人是 Linux 的“仁慈的独裁者”——Linus Torvalds。这次的邮件要“刺激”得多㊀ 。邮件大意是调度算法是“硬科学”而安全不是“硬科学” 因为安全无法定量地度量。 尽管 Stephen Smalley 争辩说 SMACK 能做的事只是 SELinux的一个子集。但是 Linus Torvalds 关心的根本不是具体的安全功能他要用 SMACK 的进入主线来保留 LSM 机制改变 SELinux 一家独大的局面。大佬一锤定音 SMACK 进入 Linux 内核主线8.2 概述SMACK 的强制访问控制机制是类型增强Type Enforcement。与 SELinux 不同 SMACK的工作机制只有类型增强没有基于角色的访问控制和多级安全。因此它更简单。类型在 SMACK 中的体现是标签。 SMACK 的策略语句形式是subjectlabel objectlabel access这条语句规定主体可以对客体进行什么样的操作。主体指的是进程主体标签就在内核管理进程的数据结构 task_struct 中include/linux/sched.hstruct task_struct {…/* process credentials */const struct cred __rcu *real_cred; /* objective and real subjective task* credentials (COW) */const struct cred __rcu *cred; /* effective (overridable) subjective task* credentials (COW) */…}include/linux/cred.hstruct cred {…#ifdef CONFIG_SECURITYvoid *security; /* subjective LSM security */#endif…}结构 cred 中的 security 指针会指向 SMACK 定义的结构 task_smack 的实例。在 SMACK 中客体包含进程、文件、进程间通信、套接字、密钥等。进程的标签、进程间通信的标签和密钥的标签都来自创建它们的进程的标签。文件的标签和套接字的标签来自扩展属性。SMACK 的操作种类比 SELinux 少很多。 SMACK 的操作不区分客体类型所有客体的操作都一样只有 6 种读r、写w、执行x、追加a、变形t、锁l。这 6 种访问方式的原始语义来自文件读、写、执行、锁都很好理解。变形transmute是 SMACK 的创造它的来源是由一个问题引来的创建一个新文件时新文件的安全标签来自哪里有三个选择一、由安全策略规定二、来自创建文件的进程三、来自新文件所在的目录。 SMACK的做法是混合这三个选择。当新文件被创建时新文件的安全标签是1如果新文件/目录所在目录的扩展属性 SMACK64TRANSMUTE 的值为“TRUE”并且有策略允许当前进程对目录的“w”和“t”操作那么新文件/目录的安全标签的值是目录的安全标签。如果创建的是目录那么此新目录的 SMACK64TRANSMUTE 值也为 TRUE。 2 如 果 新 文 件 /目 录 所 在 目 录 没 有 扩 展 属 性 SMACK64TRANSMUTE 或 者SMACK64TRANSMUTE 值不为“TRUE”那么新文件/目录的安全标签的值是当前进程的安全标签。3如果没有策略允许当前进程对目录的“w”和“t”操作那么新文件/目录的安全标签的值是当前进程的安全标签。对于非文件客体读和写操作大多很容易映射。但也有不那么直观的比如网络通信通信双方交换数据包是双向的谁是读谁是写呢 SMACK 的解决方法是对于 TCP在客户端链接时检查客户端是否有对服务器端的写操作许可对于 UDP在客户端每次发包时判断客户端是否有对服务器端的写操作许可。8.3 工作机制“类型增强”的核心问题有两个一个是类型内的操作许可在 SMACK 中就是允许带有 X标签的主体对带有 Y 标签的客体进行 Z 操作另一个是类型转换在 SMACK 中就是标签的初始值是什么在什么情况下可以改变成什么值。8.3.1 操作许可前面提到 SMACK 的操作许可很简单只有 6 种。它们对应的文件操作很直观对于其他客体操作也是简单映射这里不多赘述。8.3.2 类型转换前面说过类型体现在安全标签上所以类型转换就是安全标签的赋值操作。先说主体即进程的安全标签。进程的安全标签的值有三个来源1进程复制时子进程获得父进程的安全标签值。2 进程执行系统调用 execve()时 如果被执行文件有扩展属性“security.SMACK64EXEC”则执行 execve()后进程安全标签为被执行文件的扩展属性“security.SMACK64EXEC”的值。3通过伪文件接口/proc/[pid]/attr/current 改变进程的安全标签不过有三个条件 1只能改变进程自己的安全标签。2进程具备 CAP_MAC_ADMIN 能力。3进程的安全标签等于 SMACK 内部变量 smack_onlycap或者 smack_onlycap 为空。再说客体 SMACK 中文件的安全标签来自创建它的进程或文件所在的目录存储在文件的扩展属性“security.SMACK64”中。SMACK 中 套接字的安全标签来自创建它的进程进程可以通过假的扩展属性“security.SMACK64IPIN”和“security.SMACK64IPOUT”来修改。套接字本身没有扩展属性用户态进程针对套接字调用设置和查看扩展属性的系统调用本来是不会成功的。但是 SMACK提供了相关的函数让这些系统调用可以成功。在用户态代码中len strlen(〝Zhi” ); rc fsetxattr(fd,〝security.SMACK64IPOUT” ,〝Zhi” , len, 0); 在内核代码中 security/smack/smack_lsm.c static int smack_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags) { … skp smk_import_entry(value, size); if (skp NULL) return -EINVAL; if (strcmp(name, XATTR_SMACK_SUFFIX) 0) { nsp-smk_inode skp-smk_known; nsp-smk_flags | SMK_INODE_INSTANT; return 0; } /* * The rest of the Smack xattrs are only on sockets. */ if (inode-i_sb-s_magic ! SOCKFS_MAGIC) return -EOPNOTSUPP; sock SOCKET_I(inode); if (sock NULL || sock-sk NULL) return -EOPNOTSUPP; ssp sock-sk-sk_security; if (strcmp(name, XATTR_SMACK_IPIN) 0) ssp-smk_in skp-smk_known; else if (strcmp(name, XATTR_SMACK_IPOUT) 0) { ssp-smk_out skp; … } else return -EOPNOTSUPP; … return 0; }