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...

White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState