Linux Security Modules (Part 2)

This is the second part of an article about Linux Security Modules. You may want to read part one first.

Integration of a LSM to the Linux kernel

First of all, one should take a look at the  include/linux/lsm_hooks.h file. You can see the following lines at the end of the file:

This defines the <LSM-name>_add_hooks() functions if the related Kconfig options are enabled. Otherwise, those functions are defined as empty ones and the LSM hooks are not loaded.

These Kconfig options are defined in security/<LSM-name>/Kconfig. For instance, the Kconfig file for Yama looks like the following:

The keyword default allows for defining the default value for the configuration option (which is of type boolean). The Yama LSM is thus disabled by default and has to be enabled before compiling the kernel. The keyword depends on makes the configuration option being available only if some other kernel configuration options are enabled. There is also the select keyword which allows for automatically enabling other configuration options which are needed by the LSM. All LSMs need the “basic security options” and most of them also require for instance filesystem and network options to be able to do their work. This file is finally sourced in the “above” file (in the directory tree) security/Kconfig.

Obviously, a Makefile is also required. For a simple minor LSM like Yama, it can look like that:

Just as Kconfig files, Makefiles are organized as a tree and the following lines need to be added in the security/Makefile file:

These two lines integrate the security/yama directory into the kernel compilation process (if the related Kconfig option is enabled, of course).

Documentation should be written in Documentation/security/<LSM-name>.txt.

The last file that needs to be edited to integrate a LSM to the Linux kernel is security/security.c, that we have already mentioned in the first article. In the body of the function security_init(), the function <LSM-name>_add_hooks() is called. We have also already mentioned this function and we will see its definition later. The call is located after the calls to the equivalent functions for other minor LSMs.

The remaining of the important source code is located in the security module source code itself (for instance, in security/yama/yama_lsm.c). Such files need to include the include/lsm_hooks.h header containing the prototypes of all LSM hooks, as well as the definitions of several needed structures and macros. This file also contains the definition of the security_add_hooks()  function. This function is called from the <LSM-name>_add_hooks() functions. For Yama:

The arguments passed to this function are the <LSM-name>_hooks array and its size, obtained with the ARRAY_SIZE() macro. This array is an array of security_hook_list structures (which was described in the first part of this article) and is defined in the same file, i.e. security/yama/yama_lsm.c for Yama:

These security_hook_list structures are obtained with the LSM_HOOK_INIT() macro defined in include/linux/lsm_hooks.h. Understanding how this macro works will make you understand the stacking of the different LSMs and their hooks:

We see that passing the name of a LSM hook as the first parameter initializes the head field of the structure with a linked list corresponding to this hook, via the security_hook_heads structure defined in the file security/security.c. This structure is composed of as many linked lists as there are hooks defined by the LSM framework. Those linked lists are initialized with the traditional LIST_HEAD_INIT() macro provided by the Linux kernel.

The second parameter passed to the macro corresponds to the hook that is defined by the security module, for instance in security/yama/yama_lsm.c. By convention, these functions start with a prefix corresponding to the name of the LSM, i.e. “yama_” for Yama. In broad outline, the remaining of a LSM source code is only a succession of hook functions definitions and their helpers. For a major LSM using auditing functionalities and a pseudo-filesystem, this is of course much more complex and beyond the scope of this blog post.

This mechanism is used for every single hook defined by every single LSM integrated to the Linux kernel and enabled. The security_add_hooks() function is then expected to correctly “chain” all those security_hook_list structures. This way, we obtain one linked list per LSM and one linked list per hook provided by the LSM framework, depending on how we walk through those lists. In a word, it is these few structures, functions and macros which implement the stacking of the LSM hooks, which is itself a consequence of the stacking of the security modules that define them. Also (brace yourselves for the incoming long sentence), the order of the hooks in each linked list being directly related to the order of the calls to the <LSM-name>_add_hooks() functions of each minor LSM in the security_init()functions of the security/security.c source file, the stacking order is respected.

At last, the kernel functions which have LSM hooks call the related functions defined in security/security.c, which in turn make use of the call_int_hook() or call_void_hook() macros. For instance, if we take one Yama hook like  yama_ptrace_traceme(), which corresponds to the  ptrace_traceme() kernel function, which is defined like the following in kernel/ptrace.c:

We see the call to security_ptrace_traceme(), defined like the following in security/security.c:

These call_int_hook() and call_void_hook() macros are defined in the same file and simply iterate through the linked list corresponding to the hook, in order to call the LSM hooks defined by the security modules which are enabled on the running system:

One should notice that in the case of a hook returning an integer value, the iteration is interrupted as soon as one hook function returns a value different from 0, thus satisfying the “cannot override a denial” rule.

And that’s pretty much all I wanted to share with you. I hope this can help someone trying to play with Linux Security Modules. Don’t hesitate to e-mail me il you have any question 😉

2 thoughts on “Linux Security Modules (Part 2)”

  1. Thank you for the great articles! I’ve been struggling with finding documentation for writing an lsm for linux >= 4.2, everything out there seems to be stuck at linux 2.6.x
    It seems odd to me that the Yama developers chose to include their initialization functions in security.c and lsm_hooks.h, it looks like this can be achieved using security_initcall(module_init_fn) and then checking if the module has been enabled in the boot parameters using security_module_enable(module). Quickly looking at the source code of the remaining security modules included in the kernel code suggests that all of them are using this one. What do you think? Am I missing something here?

    1. Hello jds and thank you for your interest in my articles.

      If you take a look at commit 730daa164e7c7 from Kees Cook, you can see that Yama was once using a security initcall exactly the way you mentioned. But you can also see that the yama_init() function was not initializing hooks (i.e. not calling security_add_hooks() through yama_add_hooks()). This function was indeed only called when CONFIG_SECURITY_YAMA_STACKED was enabled, from the same place as today in security/security.c.

      The thing is LSMs could not be stacked at that time. Actually, it is still a bit complicated today. Only _minor_ LSMs such as Yama and Loadpin can be stacked with LSMs like SELinux and AppArmor. Such mainline LSMs , called _major_ LSMs, cannot be stacked with other LSMs, partly due to their use of “security blobs”. Therefore, if you look at the implementation of the security_module_enable() function, you will see that only one LSM can be chosen this way. You can take a look at that LWN article from 2015 to start seeing some of the issues with security modules stacking:

      However, Yama could be “forced” to explicitly stack with other LSMs in case another major LSM was to be chosen. And then directly calling *_add_hooks() kind of became the default way of integrating a new minor LSM to the kernel sources (another minor LSM, Loadpin, was added later and is initialized the same way) after the security_operations structure was replaced with the lists of hooks I described in my articles. See also this commit from Casey Schaufler for more details on this change: b1d9e6b0646d0e5ee5d9050bd236b6c65d66faef.

Leave a Reply

Your email address will not be published. Required fields are marked *