The most important job LIDS do is to protect the file system. It is impletementated in the VFS( virtual File System) layer in the kernel.For that reason, we can protect any kind of filesystem, such as EXT2, FAT.
In the LIDS, the protected files are devided into catelog shown below,
usr
sbin/, sbin
.
This kind of files mostly are binary system program or system
configuration files, we do not need to change them unless system upgrade.
In this section, we will view some kernel source let you understand how LIDS protect files.
Firstly, we must understand the virtual filesytem in linux.
Every file in the linux, whatever the filesytem he reside on, has an inode number, the files system provide the following data struct.
in the /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;
......
}
Note: <i_ino ,i_dev> used to be a identification of an inode.That means that you can use the pair <i_ino,i_dev> to get a unique inode from the system.
in the /usr/src/linux/include/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
......
}
The dentry is a entry of a file in diretory. Using the dentry, we can easily trave through the file's parent diretory.
For example, if a file's inode is (struct inode *)file_inode, you can use
file_inode->d_entry
to get its diretory entry and then use
file_inode->d_entry->d_parent
to get its parent diretory's entry.
After the above analyzing of the linux file system, let's have a look at how LIDS uses the VFS to protect files and diretories.
/* in /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 */
};
The above struct is used to store the protected file or directorie's inode with the pair <ino,dev>."type" is used to indentify which type the protected inode(file).
LIDS has 4 type,
/* in /usr/src/linux/incluce/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 */
With the secure_ino struct, we can easily initial the protected files or diretories into the kernel with the flollowing functions,
/* in /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 %d\n",last_secure);
#endif
return last_secure;
}
As you can see from the above code, it is very easy to add a given inode
into the secure_ino.
Protected inodes are initialized during the
system boot. The initial routine is init_vfs_security()
in
/usr/src/linux/fs/lids.c.
And now, let's have a look at how the LIDS check whether an inode is being protected,
/* /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);
.....................
This is an example that LIDS add checking in kernel. You can see that the
function lids_check_base()
is one of core functions for the lids
protection method.
You can see lids_check_base()
in many place where LIDS want to protect,
especially in fs subdiretory of linux kernel.
/* in /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()
check if the given dentry of a file and it's
parent diretories have been protected.
Note: If the its parent diretory is protected, the file is also protected.
For example, if "/etc/" has been protected, the "/etc/passwd" is also protected.
In order to protect the filesystem, LIDS insert a checking in the begin of some critical system call. Therefore, we can protect the system call and restrict user using the filesystem.
Here we list some example,