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