May 18, 2012

Writing Linux Security Module

Linux Security Modules (LSM) is a framework allowing Linux to support various security models. LSM has been a part of the kernel starting with Linux v. 2.6. Currently, the official kernel hosts such security modules as SELinux, AppArmor, Tomoyo, and Smack.

The modules run simultaneously with the native Linux security model Discretionary Access Control (DAC). LSM checks are triggered by the actions allowed by DAC.

The LSM mechanism can be implemented in various ways. Generally, it is adding mandatory access control (as, for example, in SELinux case). Besides you can invent your own security model and implement it as a module using the framework. As an example let's consider implementation of a module that will grant privileges on system actions if a specific USB device is connected.

Let's take a look at the diagram and try to understand the way the LSM hook works (using the system call open as an example).


Nothing extraordinary. LSM’s main purpose is to provide security modules with a mechanism to control access to kernel objects (hooks are inserted into the kernel code right before object calls). Before the kernel addresses an internal object, a check function provided by LSM is called.

In other words, LSM allows the modules to understand whether subject S is allowed to perform action OP over kernel's internal object OBJ.
This is really great.

It is reasonable to write a security module skeleton at first. It will be very modest and will always agree with DAC. The source codes we require are located in the security directory among the kernel source codes.

Digging the sources


Go to include/linux/security.h (my kernel version is 2.6.39.4). The most important thing here is the huge structure security_ops.
Here is its fragment:
struct security_operations 
{
char name[SECURITY_NAME_MAX + 1];

int (*ptrace_access_check) (struct task_struct *child, unsigned int mode);
         int (*ptrace_traceme) (struct task_struct *parent);
int (*capget) (struct task_struct *target,
            kernel_cap_t *effective,
            kernel_cap_t *inheritable, kernel_cap_t *permitted);};
It is a list of predefined and documented callback functions available for security module checks. By default, these functions usually return 0, thus allowing any actions. However, some of them use the POSIX security module. These are the Common Capabilities functions, which you can  review in the security/commoncap.c file.

In this case we are interested in the following function from include/linux/security.c:

/**
 * register_security – registers the security module with the kernel.
 * @ops: a pointer to the structure security_options that will be used. 
 *
 * This function allows a security module to register itself with the
 * kernel security subsystem.  Some rudimentary checking is done on the @ops
 * value passed to this function. You'll need to check first if your LSM
 * is allowed to register its @ops by calling security_module_enable(@ops).
 *
 * If the security module has been already registered with the kernel, an error
 * will be returned. In case of success, 0 will be returned
 */
int __init register_security(struct security_operations *ops) 
{
    if (verify(ops)) 
        {
        printk(KERN_DEBUG "%s could not verify "
               "security_operations structure.\n", __func__);
        return -EINVAL;
    }

    if (security_ops != &default_security_ops)
        return -EAGAIN;

    security_ops = ops;

    return 0;
}

Writing the skeleton


I have BackTrack 5 R1 (kernel version 2.6.39.4) at hand. Let's consider a ready security module, for example, SELinux (the /security/selinux/ directory). Its main mechanism is described in the hooks.c file. Basing on this file, I have created the skeleton of a new security module (a little bit later we'll make something interesting out of it).

We fill the monstrous structure of security_ops with pointers to our functions. You only have to replace SELinux with the name of your module (in my example it is PtLSM) for all the functions. Then edit the bodies of all the functions: those that return void should be empty and those that return int should return 0. As a result we have LSM that does nothing and allows everything that the native security mechanism allows (the module source code: pastebin.com/Cst0VVQh).

Here is a little and sad digression. For security reasons, the kernel ceased to export characters necessary for writing security modules in the form of loadable kernel modules (Linux Kernel Module, LKM) starting with version 2.6.24. For instance, the function register_security, which allows registering a module and its hooks, was removed from export. That is why we are going to compile the kernel with our own module.

Create a directory with the name of the module PtLSM: /usr/src/linux-2.6.39.4/security/ptlsm/.
To build the module, perform the following actions:

1. Create Makefile:

obj-$(CONFIG_SECURITY_PTLSM) += ptlsm.o

2. Create Kconfig:

config SECURITY_PTLSM
bool «Positive Protection»
default n
help
This module does nothing in a positive kind of way.

If you are unsure how to answer this question, answer N.

3. Edit /security/Makefile and /security/Kconfig to make the new module known all over the world. Add strings as in other modules.

My files with added PtLSM:
1) Makefile — pastebin.com/k7amsnQK
2) Kconfig — pastebin.com/YDsPBGAz

Then make menuconfig in the directory with the kernel source codes, choose PtLSM among the Security Options.



Now make, make modules_install, and make install. The module is placed in the kernel, and using the dmesg utility you can check what the module records to the log.

Writing a super cool module


It is time to make our module incredibly cool! Let the module disable any actions on the computer, if a USB device with a defined Vendor ID and Product ID is not connected to it (I use IDs of Galaxy S II as an example).


I have changed the body of the function ptlsm_inode_create, which checks whether this or that process can create files. If the function finds the device of ‘the supreme power,’ then it will allow execution. Similar checks can be performed for any other actions.
static int ptlsm_inode_create(struct inode *dir, struct dentry *dentry, int mask)
static int ptlsm_inode_create(struct inode *dir, struct dentry *dentry, int mask)
{
    if (find_usb_device() != 0)
    {
        printk(KERN_ALERT "You shall not pass!\n");
        return -EACCES;
    }
    else {
        printk(KERN_ALERT "Found supreme USB device\n");
    }

    return 0;
}
Now would be useful to write the find_usb_device function. It will analyze all USB devices in the system and choose the one with the necessary ID. Information about USB devices is stored in the form of trees, the roots of which are called root hub devices. The list of all the roots is in usb_bus_list.
static int find_usb_device(void)
{
    struct list_head* buslist;
    struct usb_bus* bus;
    int retval = -ENODEV;

    mutex_lock(&usb_bus_list_lock);

    for (buslist = usb_bus_list.next; buslist != &usb_bus_list; buslist = buslist->next) 
    {
        bus = container_of(buslist, struct usb_bus, bus_list);
        retval = match_device(bus->root_hub);
        if (retval == 0)
        {
            break;
        }
    }    

    mutex_unlock(&usb_bus_list_lock);
    return retval;
}
And finally let’s consider the function match_device which checks Vendor ID and Product ID.

static int match_device(struct usb_device* dev)
{
    int retval = -ENODEV;
    int child;

    if ((dev->descriptor.idVendor == vendor_id) &&
        (dev->descriptor.idProduct == product_id)) 
    {
        return 0;
    }

    for (child = 0; child < dev->maxchild; ++child) 
    {
        if (dev->children[child]) 
        {
            retval = match_device(dev->children[child]);
            if (retval == 0)
            {
                return retval;
            }
        }
    }

    return retval;
}

Let's add a few headings to work with USB.

#include <linux/usb.h>
#include <linux/usb/hcd.h>

Repeat the actions to insert the module. And buy a cool cell phone, to use your computer.


Author: Dmitry Sadovnikov, Positive Research.

14 comments:

  1. This comment has been removed by a blog administrator.

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. This comment has been removed by a blog administrator.

    ReplyDelete
  4. This comment has been removed by a blog administrator.

    ReplyDelete
  5. This comment has been removed by a blog administrator.

    ReplyDelete
  6. This comment has been removed by a blog administrator.

    ReplyDelete
  7. This comment has been removed by a blog administrator.

    ReplyDelete
  8. This comment has been removed by a blog administrator.

    ReplyDelete
  9. This comment has been removed by a blog administrator.

    ReplyDelete
  10. This comment has been removed by a blog administrator.

    ReplyDelete
  11. This comment has been removed by a blog administrator.

    ReplyDelete
  12. This comment has been removed by a blog administrator.

    ReplyDelete
  13. This comment has been removed by a blog administrator.

    ReplyDelete
  14. This comment has been removed by a blog administrator.

    ReplyDelete