Author: Thibaut Sautereau

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:

C

1

2

3

4

5

6

7

8

9

10

#ifdef CONFIG_SECURITY_YAMA

externvoid__init yama_add_hooks(void);

#else

staticinlinevoid__init yama_add_hooks(void){}

#endif

#ifdef CONFIG_SECURITY_LOADPIN

void__init loadpin_add_hooks(void);

#else

staticinlinevoidloadpin_add_hooks(void){};

#endif

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:

C

1

2

3

4

5

6

7

8

9

10

11

12

config SECURITY_YAMA

bool"Yama support"

depends on SECURITY

defaultn

help

Thisselects Yama,which extendsDAC support with additional

system-wide security settings beyond regular Linux discretionary

access controls.Currently available isptrace scope restriction.

Like capabilities,thissecurity module stacks with other LSMs.

Further information can be found inDocumentation/security/Yama.txt.

Ifyou are unsure how toanswer thisquestion,answerN.

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:

C

1

2

3

obj-$(CONFIG_SECURITY_YAMA):=yama.o

yama-y:=yama_lsm.o

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

1

2

3

4

5

subdir-$(CONFIG_SECURITY_YAMA)+=yama

[...]

obj-$(CONFIG_SECURITY_YAMA)+=yama/

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:

C

1

2

3

4

5

6

void__init yama_add_hooks(void)

{

pr_info("Yama: becoming mindful.\n");

security_add_hooks(yama_hooks,ARRAY_SIZE(yama_hooks),"yama");

yama_init_sysctl();

}

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:

C

1

2

3

4

5

6

staticstructsecurity_hook_list yama_hooks[]__lsm_ro_after_init={

LSM_HOOK_INIT(ptrace_access_check,yama_ptrace_access_check),

LSM_HOOK_INIT(ptrace_traceme,yama_ptrace_traceme),

LSM_HOOK_INIT(task_prctl,yama_task_prctl),

LSM_HOOK_INIT(task_free,yama_task_free),

};

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:

C

1

2

3

#define LSM_HOOK_INIT(HEAD, HOOK) \

{.head=&security_hook_heads.HEAD,\

.hook={.HEAD=HOOK}}

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:

C

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

/**

* ptrace_traceme -- helper for PTRACE_TRACEME

*

* Performs checks and sets PT_PTRACED.

* Should be used by all ptrace implementations for PTRACE_TRACEME.

*/

staticintptrace_traceme(void)

{

intret=-EPERM;

write_lock_irq(&tasklist_lock);

/* Are we already being traced? */

if(!current->ptrace){

ret=security_ptrace_traceme(current->parent);

/*

* Check PF_EXITING to ensure ->real_parent has not passed

* exit_ptrace(). Otherwise we don't report the error but

* pretend ->real_parent untraces us right after return.

*/

if(!ret&&!(current->real_parent->flags&PF_EXITING)){

current->ptrace=PT_PTRACED;

__ptrace_link(current,current->real_parent);

}

}

write_unlock_irq(&tasklist_lock);

returnret;

}

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

C

1

2

3

4

intsecurity_ptrace_traceme(structtask_struct*parent)

{

returncall_int_hook(ptrace_traceme,0,parent);

}

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:

C

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

/*

* Hook list operation macros.

*

* call_void_hook:

* This is a hook that does not return a value.

*

* call_int_hook:

* This is a hook that returns a value.

*/

#define call_void_hook(FUNC, ...) \

do{\

structsecurity_hook_list*P;\

\

list_for_each_entry(P,&security_hook_heads.FUNC,list)\

P->hook.FUNC(__VA_ARGS__);\

}while(0)

#define call_int_hook(FUNC, IRC, ...) ({ \

intRC=IRC;\

do{\

structsecurity_hook_list*P;\

\

list_for_each_entry(P,&security_hook_heads.FUNC,list){\

RC=P->hook.FUNC(__VA_ARGS__);\

if(RC!=0)\

break;\

}\

}while(0);\

RC;\

})

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 😉