当前位置:Linux教程 - Linux - LIDS精通与进阶--二、保护文件系统

LIDS精通与进阶--二、保护文件系统

冰块


1、保护文件系统是LIDS的重要功能之一。这个功能是在内核的VFS(虚拟文件系统)层实现的,我们可以保护任何种类的文件系统,如EXT2,FAT。 在LIDS,保护的文件按种类分为以下几种:

只读的文件或目录。只读文件意味着它们不被允许改写,如,在目录/usr/bin,/sbin。这些类型的文件大多数都是二进制系统程序或是系统配置文件,除了在升级系统的时候,我们不需要改变它们。
只可增加文件或目录。这些文件是那些只可以增加大小的文件。大多数是系统的日值文件,如在/var/log里的只可增加文件。
额外的文件或目录,这些文件没有被保护。一般来说,你想要保护目录下的所有文件,但是,还需要有一些特殊的文件不要被保护。所以我们可以定义这些文件作为额外的其他的只读文件。
保护挂载或卸载文件系统。当你在启动的时候挂载文件系统的时候,你可以禁止所有人,甚至是root,去卸载文件系统。你也可以禁止任何人在当前文件系统下挂载文件系统来覆盖它。
2、LIDS如何在内核保护文件

在这部分,我们会看到一些内核的代码来理解LIDS是如何保护文件的。

(1)、Linux文件系统数据结构程序

首先,我们必须了解Linux的虚拟文件系统。

在Linux里的每一个文件,不管是什么样子的,都有一个结点inode数,文件系统提供了以下数据结构。

在/usr/src/linux/include/linux/fs.h

struct inode {
struct list_head     i_hash;
struct list_head     i_list;
struct list_head     i_dentry;

unsigned long       i_ino; ----> inode number.
unsigned int       i_count;
kdev_t          i_dev; ----> device number.
umode_t          i_mode;
nlink_t          i_nlink;
uid_t           i_uid;
......
}

注意:用来鉴定一个结点inode。这个意思是你可以用一对来得到一个系统里独一无二的inode。

在/ur/src/linux/cinclude/linux/dcache.h里

struct dentry {
    int d_count;
    unsigned int d_flags;
    struct inode * d_inode;       /* Where the name belongs to - NULL is negative */
    struct dentry * d_parent;     /* parent directory */
    struct dentry * d_mounts;     /* mount information */
    struct dentry * d_covers; struct list_head d_hash;   /* lookup hash list */
    struct list_head d_lru;      /* d_count = 0 LRU list */
    struct list_head d_child;     /* child of parent list */
    struct list_head d_subdirs;    /* our
    ......
}

dentry是一个目录文件的入口。通过这个入口,我们可以很容易的在文件的父目录下移动。

例如,如果你一文件的inode是(struct inode*)file_inode,如果你可以用file_inode->d_entry来得到它的目录入口并且用file_inode->d_entry->d_parent来得到父目录的目录入口。

(2)、LIDS保护数据结构

在分析完linux文件系统后,让我们来看看LIDS是如何容VFS来保护文件和目录的。

在/usr/src/linux/fs/lids.c

struct secure_ino {
    unsigned long int ino;     /* the inode number */
    kdev_t dev;           /* the dev number /*
    int type;            /* the file type */
    };

上面的结构用一对来存储保护文件或目录的结点。""type""是用来标明保护结点文件类型的。

LIDS有4种类型:

在/usr/src/linux/include/linux/fs.h

#define LIDS_APPEND    1    /* APPEND ONLY FILE */
#define LIDS_READONLY   2    /* Read Only File */
#define LIDS_DEVICE    3    /* Protect MBR Writing to device */
#define LIDS_IGNORE    4    /* Ignore the protection */

通过secure_ino结构,我们能很容易的初使化保护的文件或是在内核里执行以下函数。

在/usr/src/linux/fs/lids.c

int lids_add_inode(unsigned long int inode ,kdev_t dev , int type)
{

     if ( last_secure == (LIDS_MAX_INODE-1))
     return 0;

     secure[last_secure].ino = inode;
     secure[last_secure].dev = dev;
     secure[last_secure].type = type;
     secure[++last_secure].ino = 0;

#ifdef VFS_SECURITY_DEBUG
     printk(""lids_add_inode : return %dn"",last_secure);
#endif
     return last_secure;
}

就象你在上面代码上可以看到的,给secure_ino加到一个结点上是非常容易的。被保护的结点会在系统启动的时候初使化。初使化程序在/usr/src/linux/fs/lids.c的init_vfs_security()里。

现在,让我们看看LIDS是如何来检查是否一个结点已经受到保护。

在/usr/src/linux/fs/open.c

int do_truncate(struct dentry *dentry, unsigned long length)
{
     struct inode *inode = dentry->d_inode;
     int error;
     struct iattr newattrs;
     /* Not pretty: ""inode->i_size"" shouldn''t really be ""off_t"". But it is. */
     if ((off_t) length < 0)
     return -EINVAL;

#ifdef CONFIG_LIDS
     if (lids_load && lids_local_load) {
     error =
     lids_check_base(dentry,LIDS_READONLY);
     if (error) {
              lids_security_alert(""Try to truncate a protected file (dev %d %d,inode %ld)"",
              MAJOR(dentry->d_inode->i_dev),
              MINOR(dentry->d_inode->i_dev),
              dentry->d_inode->i_ino);
.....................

这个是LIDS加到内核里做检测的一个例子。你会看到lids_check_base()是LIDS保护方法的一个核心函数。

你可以在LIDS要保护的地方看到很多LIDS保护方法用到lids_check_base()函数,特别是在linux内核的子目录下。

在/usr/src/linux/fs/lids.c

int lids_check_base(struct dentry *base, int flag)
{
..................
inode = base->d_inode;     /* get the inode number */
parent = base->d_parent; /* get the parent diretory */
.................
---->  do {
         if ( inode == parent->d_inode)
         break;
         if ((retval = lids_search_inode(inode))) {
           if ( retval == LIDS_IGNORE || (retval == LIDS_DEVICE && flag != LIDS_DEVICE)) break;
           if ( flag == LIDS_READONLY || ( flag == LIDS_APPEND && retval >flag ) || ( flag == LIDS_DEVICE && flag == retval )) {
                         return -EROFS;
                      } break; }
                      inode = parent->d_inode;
                  } while( ((parent = parent->d_parent ) != NULL) );
            return 0;
     }

lids_check_base()会检查一个给定文件的dentry和它的父目录是否被保护。

注意:如果它的父目录被保护,它下面的文件也会被保护。

例如,如果""/etc/""被保护,""/etc/passwd""也一样被保护。

(3)、在内核保护系统调用

为了保护系统,LIDS会在一些检查临界的系统调用的时候做检查。因此,我们可以保护系统调用和限制文件系统的用户调用。

这些是一些例子:

open(),open是通过禁止一些权利来保护文件的打开。你可以在打开调用open_namei()调用的时候LIDS在检测它。
mknod(),mknod是用来在指定目录下保护mknod。
unlink(),在内核代码检查do_unlink()。