|
本文主要讲述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)
|
[1] [2] [3] 下一页
|