Using the Kernel Security Module Interface

Greg shows how to create a simple kernel module that uses the LSM framework.
What Hook to Use

For our example module, we want to be able to stop a new program from being run if our USB device is not present. This can be done by using the bprm_check_security hook. This function is called when the execve system call is made, right before the kernel tries to start up the task. If an error value is returned from this function, the task will not start. Here is our hook function:

static int rootplug_bprm_check_security
    (struct linux_binprm *bprm)
{
    if (bprm->e_gid == 0)
        if (find_usb_device() != 0)
            return -EPERM;
    return 0;
}

This function checks the value of the effective group ID at which the program is to be run. If it is zero, the function find_usb_device() is called. If the USB device is not found in the system, -EPERM is returned, which prevents the task from starting.

Finding a USB Device

The find_usb_device() function simply goes through all of the USB devices in the system and sees if the device specified by the user is present. The USB devices are kept in a tree, starting at the root hub device. The different root hubs are kept in a list of buses. These buses are checked in order in the find_usb_device() function:

static int find_usb_device (void)
{
    struct list_head *buslist;
    struct usb_bus *bus;
    int retval = -ENODEV;
    down (&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)
            goto exit;
    }
exit:
    up (&usb_bus_list_lock);
    return retval;
}

The match_device() function looks at the device passed to it. If it matches the expected device, then it returns success. Otherwise, it looks at the children of this device, calling itself recursively:

static int match_device (struct usb_device *dev)
{
   int retval = -ENODEV;
   int child;
   /* see if this device matches */
   if ((dev->descriptor.idVendor == vendor_id) &&
       (dev->descriptor.idProduct == product_id)) {
       /* we found the device! */
       retval = 0;
       goto exit;
   }
   /* look at all of the children of this device */
   for (child = 0; child < dev->maxchild; ++child) {
       if (dev->children[child]) {
           retval =
               match_device (dev->children[child]);
           if (retval == 0)
               goto exit;
       }
   }
exit:
   return retval;
}

Specifying a USB Device

Because every user has different types of USB devices, specifying the device to look for must be done in a simple manner. All USB devices have a specific vendor and product ID. You can see these values by using the lsusb or usbview program when there are some USB devices plugged in to your system. This information also is shown in the /proc/bus/usb/devices file, in the lines starting with “P:”. See the Documentation/usb/proc_usb_info.txt file for more information on how the data in this file is presented.

The match_device() function looks to see if the value of the specific device matches the vendor_id and product_id variables. These variables are defined in the code as:

static int vendor_id = 0x0557;
static int product_id = 0x2008;
MODULE_PARM(vendor_id, "h");
MODULE_PARM_DESC(vendor_id,
            "USB Vendor ID of device to look for");
MODULE_PARM(product_id, "h");
MODULE_PARM_DESC(product_id,
           "USB Product ID of device to look for");

This allows the module to be loaded with the vendor and product ID specified on the command line. For example, if you want to specify a USB mouse with vendor ID of 0x04b4 and product ID of 0x0001, the module would be loaded with:

modprobe root_plug vendor_id=0x04b4 \
product_id=0x0001
If no vendor or product ID is specified on the module load command line, the code defaults to looking for a generic USB to serial converter with a vendor ID of 0x0557 and a product ID of 0x2008.

Building the Module

Finally, we need to add our module to the kernel build process. This is done by adding the following line to the security/Config.in file:

tristate 'Root Plug Support'
CONFIG_SECURITY_ROOTPLUG

And the following line to the security/Makefile file:

obj-$(CONFIG_SECURITY_ROOTPLUG)    += root_plug.o
These changes allow the user to select this kernel module either to be built into the kernel directly or as a module. Run your favorite *config option to select the “Root Plug Support” (make oldconfig will work nicely here, as only the new option will be asked about if you already have a working .config file set up for your kernel). Then build the kernel as usual.

After your kernel is built and running, load the root_plug module by typing (as root):

modprobe root_plug vendor_id=<YOUR_VENDOR_ID> \
product_id=<YOUR_PRODUCT_ID>

Now try to run a program as root with your specified USB device plugged in to your system, and then try it without. With the module loaded, and the device removed, the following error happens on my machine:

$ sudo ls
sudo: unable to exec
/bin/ls: Operation not permitted
Plug the device back in, and things should work just fine.

______________________

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

modules should support multiple devices

Warren Crossing's picture

I know your magazine and buy/read it occasionally, generally it is a good read. This article provided good information in a clean uncomplicated style. I think LJ has the right to write/solicit articles and charge access for them in print. I think LJ should be committed to maintaining this document and allowing access now that kernel code refer to it.

I also think people shouldn't jump to conclusions and demand software information without any fiscal cost.

We are talking free as in freedom! Money doesn't grow on trees and there is no obligation for module developers to produce/provide their new cool code to the community.

Please be grateful, for SO many have given SO much before you!!

Huh

Anonymous's picture

WTF... i want to read this

very poor that we cannot view this

Anonymous's picture

i came here based on comments in the kernel which referred to this article. i was very disappointed to find that i have to be a financial member in order to read the article.

i will be making a recommendation for your site to be removed from any kernel comments.

Article Referenced in Kernel Docs...

cjsutton's picture

...yet you decide to deny access to it unless I pay you money.

"This still leaves more articles available for public viewing than are reserved for subscribers -- as of last count over 2000!"

Interesting that this isn't one of those.

Does Linus know about this?

Anonymous's picture

This is BS, open up the article.

I wonder how Linus would feel about this non-free scheme, of pay for docs, that are referenced in his kernel.

came here because of referenc

Anonymous's picture

came here because of reference in the kernel docs
looked interesting

Rest assured that, because of

Anonymous's picture

Rest assured that, because of this, I will NEVER subscribe to your magazine. This is complete and utter bull$hit!! Docs shouldn't be proprietary!

Settle down. It's not LJ's f

Anonymous's picture

Settle down. It's not LJ's fault that some kernel dev couldn't be bothered to write real documentation for the rootplug module...

Webinar
One Click, Universal Protection: Implementing Centralized Security Policies on Linux Systems

As Linux continues to play an ever increasing role in corporate data centers and institutions, ensuring the integrity and protection of these systems must be a priority. With 60% of the world's websites and an increasing share of organization's mission-critical workloads running on Linux, failing to stop malware and other advanced threats on Linux can increasingly impact an organization's reputation and bottom line.

Learn More

Sponsored by Bit9

Webinar
Linux Backup and Recovery Webinar

Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.

In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.

Learn More

Sponsored by Storix