Linux Security Module的注册层次

 

===================================================

作者:ietf AT doit.com.cn

所有源文件来自于linux kernel 2.6.20

请在GNU Library General Public License下参考。

引用请注明出处。

===================================================

selinux的出现着实扰乱了文件系统的进度,不过送算慢慢搞清楚了其中的来龙去脉。下面将通过2.6.20内核中的security代码进行一番简单的分析。该版本的security系统在capabilityrootplug之间还存在一些问题,有很多新的补丁程序,具体可以参考下列邮件列表。

 

http://lists.jammed.com/linux-security-module/2005/08/

 

初次接触LSM时,其间复杂的模块加载方式和顺序很是让人头疼。光其中的ops操作就有security_ops, capability_ops, secondary_ops, selinux_ops, rootplug_ops, selinux_ops, original_ops,再有init函数security_init, capability_init, rootplug_init, selinux_init中的注册关系register_secrity反反复复的来回赋值,最后究竟ops操作都是什么,已经成了一团乱麻。为此,不得不从系统的启动过程开始,寻找其间的因果顺序,事情的经过是这样的:

 

一、LSM的初始化

 

LSM系统的初始化发生在系统内核初始化阶段,在src/init/main.cstart_kernel()里,其位置如下所示:

      fork_init(num_physpages);

      proc_caches_init();

      buffer_init();

      unnamed_dev_init();

      key_init();

      security_init();

      vfs_caches_init(num_physpages);

      radix_tree_init();

      signals_init();

unnamed_dev_init(), key_init()之后,vfs_caches_init之前,其在内核中的位置层次也基本上如此。

 

security_init()的具体实现在/src/security/security.c中,

/**

 * security_init - initializes the security framework

 *

 * This should be called early in the kernel initialization sequence.

 */

int __init security_init(void)

{

      printk(KERN_INFO "Security Framework v" SECURITY_FRAMEWORK_VERSION

            " initialized\n");

 

      if (verify(&dummy_security_ops)) {

             printk(KERN_ERR "%s could not verify "

                   "dummy_security_ops structure.\n", __FUNCTION__);

             return -EIO;

      }

 

      security_ops = &dummy_security_ops;

      do_security_initcalls();

 

      return 0;

}

在此,很有必要了解一下dummy_security_ops的定义,在src/security/dummy.c中,给出了其定义

 

struct security_operations dummy_security_ops;

 

定义后的dummy_security_ops并没有初始化,也就是说,它是security_operations的一个结构,该结构里是一系列的指针,每个指针都指向一个函数,而这些函数,就是security框架所能覆盖的领域,通过修改函数指针,可以达到为原先的系统通过钩子增加一个安全过滤层的目的。即,系统的这些调用,先通过你设计的函数过滤相关操作,再有你的函数调用原先的实现,实现增加一层的目的,这和NT中注册操作的回调函数相似。

 

struct security_operations {.......

dummy_security_ops的初始化是在上面给出的security_initverify(&dummy_security_ops)函数实现的,它将参数XXX_ops中的所有空指针的函数,初始化为dummy.c中定义的dummy_XXX类型对应函数体,每个函数体只提供一个表示成功的返回值,而不执行任何操作。具体参见src/security/dummy.c

 

dummy_security_ops初始化完成后,通过security_ops=&dummy_security_ops,使得security_ops指向一个不做任何操作的过滤层。再通过do_security_initcalls()加载具体配置的安全过滤模块。

 

在来看看do_security_initcalls()函数是怎么实现的:

 

static void __init do_security_initcalls(void)

{

      initcall_t *call;

      call = __security_initcall_start;

      while (call < __security_initcall_end) {

             (*call) ();

             call++;

      }

}

 

在系统中,security_initcall.init的函数总共有三个,分别为:capability_init(), rootplug_init()selinux_init()。其中,selinux只能第一个注册(即作为security_ops上的第一个过滤层),否则注册会失败。selinux注册后,original_ops=secondary_ops=dummy_ops; securuty_ops=selinux_ops。前两者的注册方式完全一致,并且,在当前代码实现中,两者只能注册一个,另外一个在第一个注册成功后,即使以模块方式加载也会失败。例如,capability_init()注册后,secondary_ops=capability_ops; original_ops=dummy_ops; security_ops=selinux_ops。此时如果再调用rootplug_init(),将因为security_ops!=dummy_security_ops而调用register_security失败,然后因为secondary_ops != original_ops而在调用security_ops->register_securityselinux_register_security函数中返回出错,原因为几经存在了一个第二层模块。

 

具体代码不在这里列出,有兴趣者可以到src/security/目录下分别查看capability.c, root_plug.cselinux/hooks.c

 

因此可见,主要得代码实现都是在selinux中实现的。以我所感兴趣的selinux_mountselinux_umount为例,其代码如下:

 

static int selinux_mount(char * dev_name,

                        struct nameidata *nd,

                        char * type,

                        unsigned long flags,

                        void * data)

{

      int rc;

 

      rc = secondary_ops->sb_mount(dev_name, nd, type, flags, data);

      if (rc)

             return rc;

 

      if (flags & MS_REMOUNT)

             return superblock_has_perm(current, nd->mnt->mnt_sb,

                                       FILESYSTEM__REMOUNT, NULL);

      else

             return dentry_has_perm(current, nd->mnt, nd->dentry,

                                   FILE__MOUNTON);

}

 

static int selinux_umount(struct vfsmount *mnt, int flags)

{

      int rc;

 

      rc = secondary_ops->sb_umount(mnt, flags);

      if (rc)

             return rc;

 

      return superblock_has_perm(current,mnt->mnt_sb,

                                FILESYSTEM__UNMOUNT,NULL);

}

 

由上面的ops赋值关系可知,secondary_ops->sb_mountsecondary_ops->sb_umount无论capabilityroot_plug是否加载,因为其在这两者中都没有定义实现,所以,为dummy_XXX函数,相关的代码如下:

 

static int dummy_sb_mount (char *dev_name, struct nameidata *nd, char *type,

                      unsigned long flags, void *data)

{

      return 0;

}

 

static int dummy_sb_check_sb (struct vfsmount *mnt, struct nameidata *nd)

{

      return 0;

}

 

static int dummy_sb_umount (struct vfsmount *mnt, int flags)

{

      return 0;

}

 

static void dummy_sb_umount_close (struct vfsmount *mnt)

{

      return;

}

 

static void dummy_sb_umount_busy (struct vfsmount *mnt)

{

      return;

}

 

static void dummy_sb_post_remount (struct vfsmount *mnt, unsigned long flags,

                             void *data)

{

      return;

}

 

 

static void dummy_sb_post_mountroot (void)

{

      return;

}

 

然后给出一定的pemmision的判断。permmision判断通过查找avchash表实现,它是以数据库的形式在内核维护的。在selinux中,ss目录下的文件负责维护security所有安全检查需要的数据库,对于数据库的详细操作和保存和加载,请参考src/security/selinux/ss/policydb.c中的相关函数。

 


 posted on 2011-09-21 15:50  chingliuyu  阅读(750)  评论(0编辑  收藏  举报