Cover V13, i09

Article
Figure 1

sep2004.tar

Using the BSD Secure Levels LSM

Michael A. Halcrow

BSD Secure Levels functionality used to be hard-coded into the 2.0 Linux kernel. Under the BSD Secure Levels security model, sets of policies are associated with levels. Levels range from -1 to 2 -- -1 being the weakest and 2 being the strongest. These security policies are enforced at the kernel level, so not even the superuser is able to disable or circumvent them. This hardens the machine against attackers who gain root access to the system.

To use BSD Secure Levels, you first bring the machine into an operational state by mounting partitions, loading modules, and performing other privileged operations. You can then raise the secure level of the running system. At that point, it becomes impossible for the secure level to be reduced (optionally, without a password) while the currently running kernel is in control of the machine.

One of the strongest protections afforded by BSD Secure Levels is the inability for an attacker to clear the IMMUTABLE flag on a file. When the IMMUTABLE flag is set, a file is impervious to deletion or modification, even by the root user. By making it impossible to clear the IMMUTABLE flag (among other protections), BSD Secure Levels can render a file "invincible".

Models for Linux Security

As the kernel matured and gained wider adoption, some users demanded different security functionality than BSD Secure Levels were designed to provide. In response, during the 2.1 branch, kernel developers replaced BSD Secure Levels code with code to implement capabilities, as detailed in the POSIX.1003.1e draft. This provided higher granularity for implementing security policies to meet more diverse security requirements.

These capabilities could emulate the functionality of BSD Secure Levels; however, an experimental kernel patch was required for the emulation to be comprehensive. Many administrators who were accustomed to a BSD distribution's way of implementing Secure Levels found Linux capabilities to be an unattractive method of getting equivalent functionality.

Linux Security Modules

Security in the Linux kernel remained a concern to many, and the capabilities model fell short in many regards. New languages and constructs to implement more robust and flexible security policies simply could not be expressed within the context of capabilities. The NSA's Security-Enhanced Linux (SE Linux) system embodied one significant solution to this problem.

After the NSA introduced SE Linux to the community, kernel developers formed the Linux Security Modules (LSM) Project. This project resulted in a framework involving kernel hooks that various modules could use to implement their own security models. The BSD Secure Levels LSM is one such module.

LSM Basics

LSM hooks are calls to function pointers that are defined and set by Linux Security Modules. One example of an LSM hook within the kernel is in the vfs_unlink() function defined in fs/namei.c:

error = security_inode_unlink(dir, dentry);
if (!error)
        error = dir->i_op->unlink(dir, dentry);
The security_inode_unlink() function winds up calling a function pointer, *inode_unlink(), that is contained in the security_ops struct. An LSM can define this function in any arbitrary manner. If that function determines that the action is not authorized, it returns a non-zero error code.

When you load a security module that implements the inode_unlink() function, that module instantiates a security_ops struct and sets the inode_unlink() function pointer, along with any others for functions that the module defines in that struct. The module then registers the struct with the LSM framework. LSM hooks will use that registered struct to find function pointers to call when authenticating various actions within the kernel.

BSD Secure Levels Policies

The BSD Secure Levels security model contains four levels of security. For any given level between 0 and 2, all the policies of the levels from 0 to that level also apply. See Figure 1 for a detailed list of the secure levels and their associated policies.

Basic Usage

Once the machine is in a running state, with all the necessary modules loaded and all the filesystems mounted, you can load the seclvl.ko module:

# insmod seclvl.ko
The module defaults to secure level 1 (when compiled directly into the kernel, it defaults to secure level 0). To raise the secure level to 2, the administrator writes "2" to the seclvl/seclvl file under the sysfs mount point (in these examples, I assume that the sysfs mount point is /sys). For example:

# echo -n "2" > /sys/seclvl/seclvl
Alternatively, you can initialize the module at secure level 2 with the initlvl module parameter:

# insmod seclvl.ko initlvl=2
Breaking Out

Disallowing module and raw I/O operations would make it impossible for most popular GNU/Linux distributions to perform normal shutdown tasks in their default state. So, I provided for a password-triggered reduction in the secure level that could be performed by a systems administrator. This password can be set in the module at load time by passing in either plain text or a SHA1 hash (I recommend the latter). Password-induced reduction of the secure level requires that the SHA1 crypto module be present in the kernel.

It is possible to write shutdown scripts to accommodate for the fact that certain operations will be disallowed, thus eliminating the need to reduce the secure level in the first place. In general, vendors and administrators using the BSD Secure Levels LSM should attempt to eliminate the need to ever reduce the secure level. In the meantime, currently existing shutdown scripts that would break under an elevated secure level will require that the secure level be reduced before the scripts are run.

A Complete Example

The password can be supplied to the module at loading time via the sha1_passwd module parameter, which expects the 40-character hexadecimal representation of the SHA1 hash of a password. You can generate this hash with the OpenSSL utility:

# echo -n "secret" | openssl sha1
e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4
Here we're using the OpenSSL utility to generate a SHA1 hash of the string "secret" (without a newline character).

It should go without saying that you must choose a secure password in place of "secret" and be mindful of the shell history; it is probably best to generate the SHA1 hash on a separate machine. You should select this password with the same level of paranoia under which you would choose your root password. You can include this hash value in the startup script that performs the insmod on the seclvl.ko module.

To prevent an adversary from putting his own password into a script or performing an attack in a reduced secure level, you can set the IMMUTABLE extended attribute on certain scripts. An attacker will be unable to modify those scripts, and the BSD Secure Levels LSM will prevent the attacker from removing the IMMUTABLE flag. Once all the startup activities are complete and the machine is in its running state, you can load the SHA1 crypto module and the BSD Secure Levels module:

#!/bin/sh
insmod sha1.ko
insmod seclvl.ko
       sha1_passwd=e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4
At this point, the BSD Secure Levels module is in level 1; certain actions are now disallowed, even for the superuser. I advise paranoid (a.k.a. "security-conscious") administrators to wait until after this point before connecting their machines to an untrusted network. To reduce the secure level back to 0, the administrator writes the password to /sys/seclvl/passwd:

# echo -n "secret" > /sys/seclvl/passwd
The action of giving the password to the seclvl module in this manner is equivalent to a separate authentication step on the part of the administrator, above and beyond just gaining root access. For this reason, when you need to take action that is forbidden by the seclvl module, you must take the manual step of writing the password to /sys/seclvl/passwd. Under no circumstances should this password be contained in plain text in any files accessible from the host, such as boot scripts in the /etc/init.d directory.

In any event, at this point the BSD Secure Levels module is in level 0; you can now remove kernel modules, unmount filesystems, etc. Note that, depending on the system's security requirements, additional measures may be necessary to protect the system when the secure level is reduced. For example, an adversary could have run a Trojan process that polls to discover when the secure level is reduced, at which point it attacks (a statically linked /bin/ps with the IMMUTABLE flag set may help mitigate this particular risk). It really is best never to reduce the secure level, if at all possible, so you do not have to worry about the myriad potential risks.

Conclusion

Many administrators welcome the ability to plug in a simple and lightweight module that will give them BSD Secure Levels protection out of the box. The BSD Secure Levels LSM is a viable option for those who wish to easily increase their systems' security resilience in the event of a root compromise.

Legal Statement

This work represents the view of the author and does not necessarily represent the view of IBM. IBM is a registered trademark of International Business Machines Corporation in the United States, other countries, or both. Other company, product, and service names may be trademarks or service marks of others.

Resources

The BSD Secure Levels LSM requires two separate patches against the 2.6 kernel, found at:

http://oss.software.ibm.com/linux/patches/?patch_id=1453
The first patch, settime.diff, modifies the kernel by adding a new hook that is necessary to detect when the user tries to set the clock back. The second patch, seclvl.diff, adds the module into the kernel source tree. You can enable the compilation of the module by enabling SECURITY_SECLVL (Security options->BSD Secure Levels).

To read an attempt at a formal definition of capabilities, refer to the (withdrawn) POSIX 1003.1e draft:

http://wt.xpilot.org/publications/posix.1e/download/Posix_1003.1e-990310.ps.bz2
For more information about SE Linux, see:

http://www.nsa.gov/selinux/
For more information on the kernel LSM framework, see:

http://lsm.immunix.org/
Michael A. Halcrow (mike@halcrow.us) is a security software engineer at the IBM Linux Technology Center. His interests in computer and network security are wide and varied. Recent endeavors have included Common Criteria certification of SUSE Linux Enterprise Server and VFS/MM-layer Linux filesystem encryption. He is also fluent in Italian, and on occasion, he can be found meandering the streets of Rome and Venice.