FreeBSD 5.0中强制访问控制机制使用与源码

来源:岁月联盟 编辑:zhu 时间:2008-01-21
FreeBSD 5.0中强制访问控制机制使用与源码内容简介:【FreeBSD教程】 本文主要讲述FreeBSD 5.0操作系统中新增的重要安全机制,即强制访问控制机制(MAC)的使用与源代码分析,主要包括强制访问控制框架及多级安全(MLS)策略两部分内容。这一部分讲述要   【FreeBSD教程】本文主要讲述FreeBSD 5.0操作系统中新增的重要安全机制,即强制访问控制机制(MAC)的使用与源代码分析,主要包括强制访问控制框架及多级安全(MLS)策略两部分内容。这一部分讲述要将MAC框架与MLS策略用起来,应该做的一些工作,以及如何有效使用它们的问题。

  强制访问控制(英文缩写MAC)是实现操作系统安全的一个重要的方法,现在几乎所有的安全操作系统都采用强制访问控制作为其核心安全机制之一。强制访问控制是对操作系统的各种客体(如文件、socket、系统FIFO、SCD、IPC等)进行细粒度的访问控制,即当用户或用户程序访问系统的某个客体时,强制访问控制机制对这种访问的安全性进行检查。与自主访问控制不同,强制访问控制对用户及用户程序的行为进行限制,从而达到更高的安全级别。

  强制访问控制是一种机制,它对用户与用户程序对客体的访问进行检查,但什么样的访问是安全的呢?这就需要引入安全策略的概念。安全策略可以认为是一组检查条件,它为每次访问的主体(用户或用户程序)和被访问的客体(如文件等)定义一个安全标记,再根据主体和客体的安全标记来决定这次访问是否安全。目前已经开发出多种安全策略,其中MLS用得最多。MLS是多级安全的意思,它最早用于军事领域。它的基本思想是定义一些安全级,如从低到高分普通、机密、绝密等安全级,要求高安全级别的信息不能泄露给低安全级别的用户,这样就要求低安全级的主体不能读高安全级的客体,同时高安全级的主体不能写低安全级的客体。详情请参见有关资料。

  与Linux一样,FreeBSD系统是开放源代码的操作系统,而且FreeBSD的结构清楚,安全性好,所以使用也很广泛。从5.0RC2版开始,FreeBSD内核开始引入强制访问控制机制。它在内核中实现了一个灵活通用MAC框架,这个框架对FreeBSD内核中几乎所有的核心对象进行了访问控制,并且这个框架设计合理、接口简洁,使得我们可以很方便地开发各种安全策略模块并将之挂接到系统,从而按我们自己的策略对系统进行安全控制。另外,系统还提供了包括MLS策略模块在内的多个策略模块供我们选用。

  本文从使用与源代码的分析这两方面具体讲述FreeBSD系统中的强制访问控制机制,内容主要包括MAC框架和MLS策略,相信会对对此部分内容感爱好的朋友有所启发。

  1: FreeBSD 5.0中强制访问控制机制的使用
  FreeBSD 5.0 RC2版本发布时,内核源代码中已经包含了MAC框架和一些MAC策略模块(如MLS策略模块、用于开发及实验的MAC_NONE策略模块、完整性模块MAC_BIBA等),但是这个版本的内核并没有正式对MAC提供支持,所以缺省情况下,在编译内核的时候没有把实现MAC框架的代码编译进去。另外,我们将以MLS策略为例,说明怎样利用MAC框架加载策略模块。为了让MLS策略真正实用,还需要配置系统的扩展文件属性等,我们将对之进行一定的介绍。最后我们将介绍如通过控制台命令以及在程序中如何使用系统调用接口对文件或进程的MAC标记进行操作。

  1.1 重新编译内核
  如上所述,由于FreeBSD 5.0 RC2版中的MAC框架还处于开发阶段,所以缺省情况下,内核二进制代码中并没有包括对MAC框架的支持。要使内核支持MAC框架,必须配置并重新编译内核,具体方法如下。

  到/usr/src/sys/i386/conf下,把GENERIC文件复制成另一个文件如yxd_kernel(不要改动GENERIC文件),然后在yxd_kernel文件中找到makeoptions这个部分,仿照已有的格式,添加一行(具体信息请参见"FreeBSD Developers' Handbook"中的"TrustedBSD MAC FrameWork"章节):

options MAC

  这个选项将打开MAC编译开关,这样编译时将把相应的MAC框架代码编译进内核。

  另外,为了支持文件及目录的MAC标记的存储,必须让内核支持扩展文件系统(缺省情况下也不支持)。要达到这个目的,必须在yxd_kernel中加入如下两个编译选项:

options UFS_EXTATTR options UFS_EXTATTR_AUTOSTART

  本文稍后会对扩展文件系统有较具体的解释。

  按上述要求配置完成yxd_kernel文件后,使用下面的命令重新编译内核:

cd /usr/src/sys/i386/conf config yxd_kernel

  这时系统会在/usr/src/sys/i386/compile这个目录下面建立一个与你的配置文件同名的目录,本例中为yxd_kernel。进入这个目录,输入下面的命令:

make depend make make install

  至此,编译内核工作完成,整个过程根据你机器的速度可能要花费十几到几十分钟不等。完成上述编译工作并重新启动系统后,我们就可以使用MAC框架了。

  1.2 配置MLS策略
  完成1.1节的工作后,系统只是提供了MAC框架,而并没有为你加载任何安全策略,但有了这个设计非常好的MAC框架以后,加载任何安全策略都会很方便很轻易。下面我们以MLS策略为例,讲述怎样将之加载到系统中以让它对系统进行访问控制。

  MLS策略的源代码在/usr/src/sys/security/mac_mls目录下,为mac_mls.h和mac_mls.c这两个文件,假如对它进行了修改后要编译它,直接进入/usr/src/sys/modules/mac_mls目录,再运行make命令就可以了。make得到的目标文件是mac_mls.ko文件,它是FreeBSD下的KLD(Kernel Linker Dynamic,动态内核链接机制)模块,可以独立于内核进行编译和加载,非常方便我们调试。

  mac_mls.ko这个模块必须在系统初始化之前加载,而不能等到系统启动后再使用kldload命令手工加载,它是通过一个加载标志保证这一点的(详见MAC框架与MLS策略分析章节)。为了达到这个目的,我们必须在/boot/loader.conf文件中加入下面一行并重新启动系统:

mac_mls_load = "YES"

  值得注重的一点是,当我们修改并重新编译了mac_mls.ko这个模块后,必须把它从/usr/src/sys/modules/mac_mls目录拷贝到/boot/kernel目录,因为系统启动的时候是从/boot/kernel目录寻找模块的。

  1.3 配置扩展文件系统
  当我们做完1.1和1.2两步后,MLS模块就已经在系统中起作用了,我们可以使用"getfmac 文件/目录名"来得到文件或目录的MAC标记(形如"文件/目录名: mls/low",具体的介绍请参阅1.4节),但此时的标记是一种伪标记,也就是说,这个标记是MLS策略给每个文件的缺省标记,且这个标记在关闭系统的时候并没有被存储。但是按照我们的需要,我们希望每个文件或目录的MAC标记应该有"非易逝性"的标记,也就是说我们希望这个标记与文件一样被存储在磁盘上。这就需要引入扩展文件系统,使得每个文件或目录的MAC标记存储在对应文件或目录的扩展属性中。为了便于理解,下面对FreeBSD中的扩展文件系统进行一点简要的介绍。

  简单地说,扩展文件系统是对现有文件系统的一种扩充。扩展属性(EA,Extended Attribute)是相对于传统文件系统的inode节点中已存储的文件或目录的标准属性而言的,它可以为inode节点中存储的文件或目录增加额外的信息,这些信息再由系统根据inode结点访问到。每个文件系统的扩展属性(EA)是一组(name,value)对。一个inode节点(文件或目录)可能定义了某种属性,也可能没有定义。假如inode节点定义了某个属性,则它可能包含0个,也可能包含多个字节的这种属性的数据。这与一般shell的环境变量类似。这样,扩展属性机制就为TrustedBSD的各种安全特征(ACL、MAC和能力等),提供了存储访问安全信息的简单方法。一些非安全应用也可以根据需要创建扩展属性,例如文件的校验和、密钥等。扩展属性可以动态地增加新的扩展属性而不必改变文件系统在磁盘上的存储格式。

  要使MAC标记可以被存储在文件的扩展属性里,必须使一个文件系统支持扩展属性。下面我们以/tmp文件系统为例,讲述如何配置使其支持扩展文件属性:

  1. 首先建立/tmp/.attribute/system目录,当mount一个文件系统时,系统将自动从这个目录中寻找扩展文件系统所对应的文件。
  2. 使用下面的命令建立一个名为freebsd.mac的文件,这个文件中将存储/tmp文件系统中所有文件的扩展属性。100这个数表示每个扩展属性的长度为100个字节: cd /tmp/.attribute/system extattrctl initattr -p . 100 freebsd.mac
  3. 使用下面的命令为/tmp文件系统打上标记,告诉系统在mount的时候加载扩展文件系统。其中-l标志表明扩展文件系统是用于MAC标记的,详见man page: cd / umount /tmp runefs -l enable /tmp mount /tmp cd /tmp/.attribute/system
  4. 由于文件系统mount的时候启用的扩展属性名为freebsd.mac,而我们需要的扩展属性名为mac_mls(原因说参见MAC框架分析部分),所以我们必须使用下面的命令激活属性名为mac_mls的扩展属性: extattrctl enable /tmp system mac_mls ./freebsd.mac

  

  至此,/tmp文件系统的扩展属性配置完毕,我们可以尝试使用下面的命令来改变/tmp/test这个文件的MAC标记:

setfmac mls/high /tmp/test

  1.4 操作MLS标记
  和所有的系统功能一样,MLS标记既可以在控制台由命令来得到或设置,也可以在程序中通过系统调用来得到或进行设置,下面是对这两种方式的简单的介绍。

  控制台命令主要有三个,即getfmac,用于得到文件或目录的MAC标记;setfmac用于设置文件或目录的MAC标记;getpmac,用于得到当前进程的MAC标记。另外的一个命令setpmac在当前系统中支持得不好。

  假如使用的是MLS策略,那么返回的MAC标记为mls/[single_mac]([mac_range]),其中mls为策略名,后面用"/"与标记数据隔开。[single_mac]为单一的MAC标记,可能为"low"或"high"或"equal"。[mac_range]为一个MAC标记范围,往往主体(进程)有一个范围。一个MAC标记的例子为:mls/low(low-high)。

  在应用程序中,可以使用的系统调用请参见/usr/src/sys/sys/mac.h文件中#ifndef _KERNEL这个块中提供的函数原型,值得注重的是,man page中的有些接口参数及返回值类型有误,请以mac.h文件中的类型为准。

  下面是几个最常用的函数的使用方法:

  mac_get_proc,mac_get_file的用法:

struct mac myMac; char ss[60] = "mls"; myMac.m_string = ss; myMac.m_buflen = 60; mac_get_proc(&myMac); mac_get_file("/tmp/test",&myMac);
  

  mac_set_proc,mac_set_file的用法:

struct mac myMac; myMac.m_string = "mls/high"; myMac.m_buflen = strlen(myMac.m_string); mac_set_proc(&myMac); mac_set_file("/tmp/test",&myMac);

  1.5 小结
  由于FreeBSD 5.0RC2版才刚刚开始非正式地支持MAC,所以针对普通用户而言,要将其真正用起来,要做的工作还比较多比较烦琐的。对于MLS策略而言,还有很多值得完善的地方,比如现有的MLS策略不支持对用户设置MAC标记,主体对客体访问的时候限制过多,还有MLS不支持可信进程等等,都是下一个版本需要完善的。

  在使用上,从我个人的使用经验来看,比较不好用。当然,这个问题不能全怪系统,MLS策略导致易用性降低已经是不争的事实,相信经过一段时间的开发,最终会给我们一个满足的答案。

  但是瑕不掩玉,撇开MLS,单看MAC框架,应该说是设计得很完美的:良好的结构、清楚的逻辑、非常简洁的接口,这些优点应该来说可以给每一个对它有爱好的人一个大大的惊喜。即使是MLS策略,由于其作为一个KLD模块来实现,所以十分便于我们对它进行修改和扩充,相信经过一段时间,MLS模块亦会成为我们的惊喜。假如大家有爱好了解MAC框架及MLS策略的实现,请接着往下阅读。

本文主要讲述FreeBSD 5.0操作系统中新增的重要安全机制,即强制访问控制机制(MAC)的使用与源代码分析,主要包括强制访问控制框架及多级安全(MLS)策略两部分内容。这一部分较系统地对MAC框架及MLS策略的源代码进行分析。

  2 MAC框架与MLS策略源代码分析
与本文相关的源代码文件主要有两个,即 /usr/src/sys/kern/kern_mac.c 和 /usr/src/sys/security/mac_mls/mac_mls.c 。另外还有一些头文件如mac.h、mac_policy.h等。

  2.1 MAC框架整体结构
下面是 MAC 框架的示意性结构图,当用户控制台或用户程序通过系统调用对内核对象进行访问的时候,由于内核代码中相应的位置插入了MAC框架的检查函数,于是内核就会调用MAC框架的相应检查函数来做安全性检查。MAC框架会依次调用每个挂接在MAC框架上的安全策略,以决定访问是否安全。另外,其它可能涉及到安全问题的系统事件,如初始化各种安全标记、初始化各种内核对象等,也会通知MAC框架,由它做出相应的处理。

  从图中我们也可以看到,安全策略作为一个独立的KLD模块,可以独立于内核进行编译,再在使用的时候挂接到MAC框架上。要判定一次访问是否安全,MAC框架会调用所有的安全策略,只有当所有的安全策略均表示同意,MAC框架才会授权这次访问。

  2.2 安全标记
安全标记是由MAC框架和各个安全策略定义的一组数据,用于描述主体或客体的安全信息,安全标记与内核描述主客体的其它数据一起存储在内核中。要实现强制访问控制,首先必须为主客体定义安全标记。不同的策略由于判定的依据不一样,可能定义的标记也不相同。作为MAC框架,当安全策略向它注册的时候,它必须把该策略使用的安全标记附加到各个内核对象上去,这样当需要调用该策略做安全性检查的时候,才能为策略提供它们自己定义和理解的安全标记。我们先给出MAC框架与MLS策略定义的安全标记,再对之作进一步的解释。

  MAC框架中安全标记的定义是这样的:

struct label {    int l_flags;    union {        void    *l_ptr;        long     l_long;    }   l_perpolicy[MAC_MAX_POLICIES];};

  其中l_flags是一个标志,被MAC框架用来判定是否初始化了整个标记数据结构。l_perpolicy数组为每个策略定义了一个联合,这样当策略向MAC框架注册时,它们既可以用一个long类的整数作为它们自己的安全标记,也可以使用联合中的指针指向一个它们自己定义的标记数据结构。MLS策略选择了后者。

  MLS策略定义的安全标记是这们的:

struct mac_mls_element {    u_short mme_type;    u_short mme_level;    u_char  mme_compartments[MAC_MLS_MAX_COMPARTMENTS >> 3];};struct mac_mls {    int         mm_flags;    struct mac_mls_element  mm_single;    struct mac_mls_element  mm_rangelow, mm_rangehigh;};

  在mac_mls结构中,定义了一个单一标记(mm_single)和一个标记范围(mm_rangelow,mm_rangehigh),主客体既可以使用单一标记来标识单一安全级,也可以使用标记范围来标识一个安全级范围,还可以同时使用二者。如第1章中我们使用getfmac得到的输出"mls/high"表明该文件使用的是值为"high"的单一安全标记。再比如我们使用getpmac得到的输出"mls/low(low-high)"表明进程同时使用了单一标记和标记范围。至于究竟使用的是哪种标记,由mm_flags标识。

  mac_mls_element定义一个标记,它定义的标记功能很强大,既支持安全类型(mme_type变量,值为LOW、HIGH、EQUAL和UNDEFINE),也支持多达256个级别的安全级(当mme_type的值为LEVEL时,mme_level变量有效,由它定义安全级),同时还使用mme_compartments数组支持域(field),后续章节将具体讲述MLS策略是怎样使用它所定义的标记的。

  当MLS策略向MAC框架注册时,它会使用MAC标记所定义的l_perpolicy数组中的一项,然后把这一项的l_ptr指针指向自己定义的mac_mls结构,这样就把自己定义的标记挂接到每个内核对象上去了。

  2.3 MAC框架的实现
MAC 框架首先必须要维护一个链表,以记录所有挂接在它上面的安全策略,这个链表由下列代码定义,其意义不再具体解释,请参见kern_mac.c: static LIST_HEAD(, mac_policy_conf) mac_policy_list;

  上面的代码定义一个mac_policy_conf类型的链表mac_policy_list,其类型定义如下:

    struct mac_policy_conf {    char                *mpc_name;  /* policy name */    char                *mpc_fullname;  /* policy full name */    struct mac_policy_ops       *mpc_ops;   /* policy operations */    int              mpc_loadtime_flags;    /* flags */    int             *mpc_field_off; /* security field */    int              mpc_runtime_flags; /* flags */    LIST_ENTRY(mac_policy_conf)  mpc_list;  /* global list */};

  其中mpc_list成员变量是一组指针,用于形成链表。mac_policy_conf结构中最重要的一个成员是mpc_ops,我们讲过,每当有访问安全性检查时,MAC框架会把所有的检查事件如创建inode、访问inode等传递给安全策略作检查,这就是通过mpc_ops这个成员变量来进行的。也就是说,mpc_ops这个结构中记录了每个策略对各种MAC事件的处理函数。例如下面是一个来自kern_mac.c的函数:

int  mac_check_vnode_open(struct ucred *cred, struct vnode *vp, int acc_mode){    int error;    ASSERT_VOP_LOCKED(vp, "mac_check_vnode_open");    if (!mac_enforce_fs)        return (0);    MAC_CHECK(check_vnode_open, cred, vp, &vp->v_label, acc_mode);    return (error);}

  当用户试图打开一个文件时,内核就会试图打开一个vnode,在打开之前会调用这个函数,用于检查主体是否有权限打开这个vnode。MAC_CHECK宏定义如下:

#define MAC_CHECK(check, args...) do {                      struct mac_policy_conf *mpc;                                                            error = 0;                              MAC_POLICY_LIST_BUSY();                         LIST_FOREACH(mpc, &mac_policy_list, mpc_list) {                 if (mpc->mpc_ops->mpo_ ## check != NULL)                    error = error_select(                               mpc->mpc_ops->mpo_ ## check (args),                     error);                     }                                   MAC_POLICY_LIST_UNBUSY();                   } while (0)

  我们可以看到,代码中使用LIST_FOREACH宏来遍历安全策略链表中的每个策略,然后调用其在mpc_ops中注册的检查函数mpo_##check。mac_policy_ops这个结构中定义了很多事件处理函数,进行细粒度的访问控制,限于篇幅,在此不一一列出。这些函数共分四类:第一类是策略本身的初始化和销毁函数;第二类是对安全标记的操作函数,包括各种内核对象的安全标记的初始化与销毁函数,以及获取与设置安全标记的接口函数;第三类是对文件系统、网络系统及进程对象的标记进行操作的事件处理函数;第四类是检查对各个内核对象的访问是否安全的检查函数。详见mac_policy.h。

  2.4 安全策略的注册
分析MLS策略,我们可以发现,安全策略向MAC框架注册是一件很简单的事情。mac_mls.c文件中花了大量篇幅定义了所有的事件处理函数,如前所述,这些函数是安全策略的核心,用于判定访问的安全性等。定义完这些函数后,在源代码的最后,把这些函数的地址填入mac_policy_ops结构中,再用一个MAC_POLICY_SET宏把mac_policy_ops结构挂接到上面提到的mac_policy_list链表中去就可以了,如下:

MAC_POLICY_SET(&mac_mls_ops, trustedbsd_mac_mls, "TrustedBSD MAC/MLS",MPC_LOADTIME_FLAG_NOTLATE, &mac_mls_slot);

  其中MPC_LOADTIME_FLAG_NOTLATE标志告诉系统这个KLD模块只能在系统初始化之前加载,这是因为MLS必须在初始化系统内核对象的时候为每个对象附加上自己定义的安全标记。具体地,系统初始化所有的内核对象之前,都会调用mac_policy_ops结构中定义的mpo_init_XXX_label函数,MLS就是在这个函数中在该内核对象的标记里附加上自己的标记的。下面我们分析一下MAC_POLICY_SET宏。

#define MAC_POLICY_SET(mpops, mpname, mpfullname, mpflags, privdata_wanted)     static struct mac_policy_conf mpname##_mac_policy_conf = {          #mpname,                                mpfullname,                             mpops,                                  mpflags,                                privdata_wanted,                            0,                              };                                  static moduledata_t mpname##_mod = {                        #mpname,                                mac_policy_modevent,                            &mpname##_mac_policy_conf                   };                                  MODULE_DEPEND(mpname, kernel_mac_support, 1, 1, 1);         DECLARE_MODULE(mpname, mpname##_mod, SI_SUB_MAC_POLICY,             SI_ORDER_MIDDLE)

  逻辑也相当简单,我们来走一遍。首先定义一个mac_policy_conf结构,用于描述MLS策略,后续的操作会把这个结构加入到MAC框架所定义的mac_policy_list列表中的。由于MLS策略本身是一个KLD模块,所以必须定义一个moduledata_t结构用于向系统注册模块,在这个结构中指明了由mac_policy_modevent函数处理模块事件,当有模块事件产生的时候将把上面定义的mac_policy_conf结构的地址传给处理函数。

  mac_policy_modevent函数的核心代码如下:

case MOD_LOAD:        if (mpc->mpc_loadtime_flags & MPC_LOADTIME_FLAG_NOTLATE &&            mac_late) {            printf("mac_policy_modevent: can't load %s policy "                "after booting/n", mpc->mpc_name);            error = EBUSY;            break;        }        error = mac_policy_register(mpc);        break;    case MOD_UNLOAD:        /* Don't unregister the module if it was never registered. */        if ((mpc->mpc_runtime_flags & MPC_RUNTIME_FLAG_REGISTERED)            != 0)            error = mac_policy_unregister(mpc);        else            error = 0;        break;

  当模块被LOAD的时候,会调用 mac_policy_register函数,它首先检查该模块是否已注册,假如没有,就会执行下面的代码: LIST_INSERT_HEAD(&mac_policy_list, mpc, mpc_list);

  其中mpc为我们上面提到的mac_policy_conf结构的地址,这样到此为止,就把MLS策略添加到MAC框架的策略列表中去了。

  2.5 MLS策略源代码分析
根据MLS策略,高安全级别的主体不能写低安全级别的客体,而低安全级别的主体则不能读高安全级别的客体,只有主客体的安全级别相同,才能既读又写。MLS定义了三种标记比较函数:

  1. mac_mls_dominate_single(a,b):用于检查a的单一标记的安全级是否高于b的单一标记的安全级。这个函数主要用于对内核变量的读、写、查询等事件进行安全性检查。
  2. mac_mls_single_in_range(a,b):用于检查a的单一标记的安全级是否界于b的标记范围的安全级范围之内。这个函数主要用于对改变内核对象的标记进行安全性检查,当主体改变一个客体的安全标记时,要求新旧标记的安全级别处于主体的安全级别范围之内。
  3. mac_mls_range_in_range(a,b):用于检查a的安全级范围是否落于b的安全级范围之内。这个函数主要用于改变主体安全级时,要求主体新的安全级必须落于主体当前安全级范围之内。
  4. mac_mls_equal_single(a,b):用于检查a、b的安全级是否相同。这个函数主要用于网络和设备操作。

  

  以上函数都是调用mac_mls_dominate_element()函数来具体比较的,下面我们就分析一下这个函数,其源代码如下:

static int  mac_mls_dominate_element(struct mac_mls_element *a,    struct mac_mls_element *b){    int bit;    switch (a->mme_type) {    case MAC_MLS_TYPE_EQUAL:    case MAC_MLS_TYPE_HIGH:        return (1);    case MAC_MLS_TYPE_LOW:        switch (b->mme_type) {        case MAC_MLS_TYPE_LEVEL:        case MAC_MLS_TYPE_HIGH:            return (0);        case MAC_MLS_TYPE_EQUAL:        case MAC_MLS_TYPE_LOW:            return (1);        default:            panic("mac_mls_dominate_element: b->mme_type invalid");        }    case MAC_MLS_TYPE_LEVEL:        switch (b->mme_type) {        case MAC_MLS_TYPE_EQUAL:        case MAC_MLS_TYPE_LOW:            return (1);        case MAC_MLS_TYPE_HIGH:            return (0);        case MAC_MLS_TYPE_LEVEL:            for (bit = 1; bit <= MAC_MLS_MAX_COMPARTMENTS; bit  )                if (!MAC_MLS_BIT_TEST(bit,                    a->mme_compartments) &&                    MAC_MLS_BIT_TEST(bit, b->mme_compartments))                    return (0);            return (a->mme_level >= b->mme_level);        default:            panic("mac_mls_dominate_element: b->mme_type invalid");        }    default:        panic("mac_mls_dominate_element: a->mme_type invalid");    }    return (0);}

  我们只解释一下最后一个case,即当a、b的类型均为MAC_MLS_TYPE_LEVEL时,也就是它们都使用一个数(0到254)标识它们的安全级时,判定是如何进行的。首先:

  #define MAC_MLS_BIT_TEST(b, w) ((w)[(((b) - 1) >> 3)] & (1 << (((b) - 1) & 7)))

  我们可以看到,在比较二者安全级之前,对它们的mme_compartments进行了位测试。mme_compartments是一个256bit的数据(由uchar[32]数组定义),每个bit都可以设置为1来表示该对象属于某一个安全域,当然也可以设置多个bit表示这个对象属于多个安全域。MLS要求假如a可以访问b的话,b所属的安全域必须要是a的子集,否则访问被拒绝。对mme_compartments进行测试的目的也即是要判定b中设置了1的那些bit在a中有否设置。

  2.6 对MAC框架及MLS策略的扩充
最后我们对MAC框架作一点改进。前面提过,MAC框架目前并不支持对用户的安全级进行设置,也没有提供存储用户安全级信息的地方,我们想对之做改进,以使其更实用。我们改进的思想是,拦截系统的setuid系统调用,在系统作setuid的时候(用户登录、运行su命令等),读入用户安全级别信息并作相应设置。我们做了如下工作:

  1. 在mac_policy.h文件中的mac_policy_ops结构中增加一个新函数mpo_get_cred_from_file(),其参数和返回值与mpo_create_cred()完全一样。
  2. 在mac_mls.c文件中增加一个新函数mac_mls_get_cred_from_file(),其参数和返回值与mac_mls_create_cred()完全一样。在这个函数里,增加在内核中读写用户安全级配置文件的语句,从而把用户的安全级在这里读到标记中。然后在对mac_policy_ops结构的设置语句中,增加一条语句:.mpo_get_cred_from_file = mac_mls_get_cred_from_file。
  3. 在kern_prot.c文件中的setuid()函数里crcopy()函数替换为我们自己的mac_crcopy()函数,并在调用这个函数之前加一条语句:newcred->cr_uid = uid;。
  4. mac_crcopy()函数的代码如下:
    void mac_crcopy(struct ucred *dest,struct ucred *src){    uid_t uid = dest->cr_uid;    KASSERT(crshared(dest) == 0,("crcopy of shared ucred"));    bcopy(&src->cr_startcopy,&dest->cr_startcopy,        (unsigned)((caddr_t)&src->cr_endcopy -            (caddr_t)&src->cr_startcopy));    uihold(dest->cr_uidinfo);    uihold(dest->cr_ruidinfo);    if(jailed(dest))        prison_hold(dest->cr_prison);#ifdef MAC    dest->cr_uid = uid;    mac_get_cred(src,dest);    dest->cr_uid = src->cr_uid;#endif}
  5. 修改相应的头文件,并重新编译内核。

  

  2.7 小结
上面分析了MAC框架与MLS策略的主要内容,限于篇幅,我们只给出主要逻辑,假如您想要了解更为具体的技术细节,请阅读其源代码或联系我。另外,MAC框架和MLS策略还用到了一些很重要的技术,如系统控制(sysctl)机制、锁等,我们也没有提到,如有爱好的读者可以继续钻研。

图片内容