[System|Toolbox] Tools
for the Art
of System
Administration
HOME STAFF FAQ ADVERTISE LEGAL
binaryfreedom.com

Sections
   News
   Reviews
   Commentary

Community Events:
 
Submit an event

System Security Paranoia

Wednesday February 14, 2001 03:46pm PST
Travis takes a look through the benefits and features of several hardcore system security packages for Linux, and finds out that real security starts with the kernel.

As any truly experienced systems administrator will tell you, nothing hurts more than waking up to see your system has been compromised. Good admins put their hearts and souls into the systems they work on, and don't like to see all that hard work trampled on... not to mention the users, web developers, or programmers that may be using the system. With the amount of energy that is put into the average networked box, it more than pays to take security into active consideration, and when I talk security I don't just mean having a firewall and keeping on top of current exploits. Keep in mind your personal information- and in many cases your career- are at risk here. If you're like me, you need just a little bit more to feel safe.

In a Linux distribution, where nothing is mandatory but the kernel itself, it only makes sense to start security model designs at the core of the OS. Being that the Linux kernel source code is readily available under the GPL, and there are patches everywhere that improve upon and harden the Linux core, it is relatively easy to add functionality to an already feature-rich kernel. The three that I will review here are the internationalisation patch, OpenWall, and LIDS. When used together, they can greatly increase the level of security on any box.

OpenWall focuses on fixing bugs in any currently distributed kernel as well as some basic hardening of Linux security. The features of OpenWall depend on which kernel you intend to patch, as it's job is only to fix the problems seen in the current kernel, and add a few features the kernel maintainers would not like to see in the mainstream kernel (feeling that this would foster a false sense of security).

To install OpenWall as a kernel patch, untarball the package, and patch the Linux source tree with the .diff file provided. To do this,cd /usr/src/linux and type:

patch -p1 < /path/to/linux-2.2.18-ow1/linux-2.2.18-ow1.diff

Adding the OpenWall patch adds a menu called Security Options to your menuconfig menus. I will go into detail about each option you would see if you were to patch a 2.2.18 kernel.

Non-executable stack area

This option is an aid in avoiding buffer overflow exploits, and all the little nasties that they bring. Note that this is not a cure-all, and you should not take this as a reason to be overly confident when deciding whether or not a security patch is necessary. This does not make you safe against all buffer overflows, it just makes the stack non-executable. If the stack is non-executable it makes it difficult to exploit buffer overflows which involve putting a return address to arbitrary code on the stack. This patch also changes the default address at which shared libraries are mmap()'d at to make it contain a zero byte, making exploits that deal with ASCII strings difficult by not allowing them to specify any more data.

GCC itself puts CALLs onto the stack to support nested functions. Autodetection of GCC Trampolines is needed to fully support this, and so I suggest you enable the option.

Restricted links in /tmp

This disallows hard links in /tmp to files they don't own unless they have read and write access to the file.

Restricted FIFOs in /tmp

This restricts writes to untrusted FIFOs in /tmp to make data spoofing attacks harder. Users will not be able to write to a FIFO they don't own unless the owner is the same as that of the directory or the FIFO is opened without the O_CREAT flag.

Restricted /proc

This allows users to only see their own processes and nothing about network connections unless they are in a special group (0 by default), or the root user. 'dmesg' is also disabled for users. This does not break programs like top and ps, but there have been problems with w in recent versions of procps (a patch is available).

Special handling of fd 0, 1, and 2

File descriptors 0, 1, and 2 have a special meaning for the C library and lots of programs. Thus, they're often referenced by number. Still, it is normally possible to execute a program with one or more of these fd's closed, and any open(2) calls it might do will happily provide these fd numbers. The program (or the libraries it is linked with) will continue using the fd's for their usual purposes, in reality accessing files the program has just opened. If such a program is installed SUID and/or SGID, then we might have a security problem.

Enforce RLIMIT_NPROC on execve(2)

If a process changes its UID, it might exceed the process limit for its new UID. This is not a security issue.

Destroy shared memory segments not in use

Shared memory segments are allowed to exist without association with a process and may not be counted against resource limits. This kills shared segments when their attach count becomes zero, which may break some programs. Unless you configure resource limits, you don't need this.

The internationalisation patch adds support for strong cryptography into the Linux kernel. It allows other parts of the kernel to access cryptography in a generic way. The applications of such cryptography range from encrypted filesystems to secure virtual private networking. If you wanted to, you could even encrypt ethernet. Hey, if you're truly paranoid (or overly bored) you can encrypt loopback filesystems than are on an already encrypted block device.

To install the international crypto support alone, just pipe gzip to patch after changing into /usr/src/linux with the command:

gzip -cd /path/to/patch-int-2.2.18.3.gz | patch -p1

The kernel cryptography options are scattered around the kernel menus, but support for the crypto API itself will be in a menu called crypto options. This is where you will choose which algorithms to support and effects all modules that take advantage of the crypto API. The digest algorithms are one-way hashes used by the digest API.

Since it takes a little more effort to enable encrypted filesystems, I will go into the details of doing so. You will need to support the crypto-API and at least one cipher that can be used on loopback encrypted filesystems (I suggest serpent). After selecting support for a cipher, go into the block devices menu and include support for loopback devices and general encryption on loopback devices.

You're now ready to compile your kernel but you still need a losetup, mount, and umount that support encrypted filesystems. You will need the source to util-linux in order to compile these.

To apply the util-linux that kerneli has hidden away in your linux directory tree, just cd to the directory you decompressed the source to and type:

patch -p1 < /usr/src/linux/Documentation/crypto/util-linux-2.10o.int.patch
Seeing as we don't want to install all of util-linux as this would probably break your system's setup, we're going to do a few things by hand. First make sure /usr/include/linux is a symlink to /usr/src/linux/include/linux/ and then type the following to make just the necessary files:
./configure
make -C lib setproctitle.o
cd mount
make losetup mount umount

To install these files, just cp them to /sbin (after backing up the original copies, of course), and you now have working encrypted filesystem support.

Losetup is the program that you will use to prepare block devices and files to be mounted as encrypted filesystems. It decides the cipher and password to be used, and unless the correct cipher and password are specified, you will not be able to access your data. To test out losetup, we first need a file to play with, so lets dd ourself a 10Mb file.

dd if=/dev/urandom of=/crypto-test bs=1M count=10

OK, now that we have a file to work on, lets tell losetup to access it as an encrypted block device.

losetup -e /dev/loop0 /crypto-test

Now that we have a loop device registered to the file, we can make a filesystem on it. Use mkfs.ext2 on /dev/loop0 and not /crypto-test, otherwise the file system won't be encrypted.

mkfs.ext2 /dev/loop0

Now to mount, just mkdir /mnt/crypto then mount -t ext2 /dev/loop0 /mnt/crypto to attach the device to the directory structure. Anything you send to this filesystem will be encrypted using your selected cipher and password. Of course, it's pointless to encrypt a filesystem if you leave it mounted and set up. When finished using it, unmount it and free the loop device. To free the loop device just losetup -d /dev/loop0. Make sure to remember your passwords and ciphers. If you would like to add an encrypted filesystem to /etc/fstab remember to put loop and encryption= in the options field.

LIDS is a patch to the Linux kernel that changes the entire unix security model by making files and attributes that prevent even root from being able to damage the system. It also includes a port scan detector in the kernel itself, and process protection. With LIDS it is possible to do a wide range of security-enhancing changes to your system. If you so desire, you can do anything from making /etc/shadow invisible to all users and un-readable by any process other than login to making only programs spawned by /etc/rc.d/rc.6 able to unmount filesystems and restart the system. Because LIDS is so powerful, it's also very dangerous. This is a major "you can break your system" warning. Subtle changes will have a major effect on the way things work.

To install the LIDS patch alone, untarball the package, cd /usr/src/linux/, and then patch -p1 < /path/to/lids-0.9.11-2.2.18/lids-0.9.11-2.2.18.patch. To install lidsadm, the utility used to control LIDS and set up LIDS rule-sets, just cd /path/to/lids-0.9.11-2.2.18/lidsadm-0.9.11/ and type make VIEW=1 ; make install (make sure /usr/include/linux is a symlink to /usr/src/linux/include/linux or lidsadm won't compile).

It is important that you have the LIDS password set and the LIDS rule-sets lax before booting into a LIDS-enabled kernel, so let's do that before even compiling LIDS support. First lets set ourselves a password by typing lidsadm -P. LIDS comes with an initial configuration file /etc/lids/lids.conf. This file contains rules we didn't purposefully create ourselves, and so we don't need nor want it, as it may cause trouble in the future. mv it to another location and then touch /etc/lids/lids.conf to create an empty file in it's place.

The LIDS kernel options have all been cleanly placed into a submenu of menuconfig called Linux Intrusion Detection System. To use LIDS we will want to at least enable LIDS itself and the ability to allow switching of LIDS protection, so that once we have a LIDS kernel going, we won't have to boot into a non-LIDS kernel to make massive changes to the system. Some other very important options are:

Do not execute unprotected programs before sealing LIDS

This disallows the execution of untrusted binaries during boot-time, meaning only files protected by the current lids configuration can be run, as those are the only files that can be guaranteed to have not been tampered with since last boot. Once you have a working LIDS configuration, it is highly suggested you enable this option... but for now it would be wise to leave it disabled.

Try not to flood logs

Until you get everything set up smoothly, you definitely want to enable this one, and probably afterward as well. This will prevent LIDS from logging the same message repeatedly in a short period of time.

Allow remote users to switch LIDS protections

If you have console access to the machine in question, don't enable this, as it decreases security. It's a better idea to have only users with physical access to the machine able to compromise that machine's security (I mean, if they have physical access, they can bypass lids by booting into single-user mode anyways, and, barring that, can even remove the hard drive and bring it home). However, if you're protecting a box you will need to administer remotely, like a co-located web server, or even the gateway a few rooms over, it's a better idea to enable this, otherwise you will have a system so secure, not even you can change anything.

Allow any program to switch LIDS protections

Don't enable this. It has no logical use. What it does is allow programs other than lidsadm to feed /proc/sys/lids/locks. Don't do it, you don't need it.

Allow reloading of config file

Generally a good idea... disabling this option doesn't increase security. If a user can access the config files to change them, he/she can bypass LIDS anyways. Besides, it makes life a LOT easier.

Send security alerts through network

This will allow the sending of messages across a network directly from the kernel, without the possible security risk of running outside executables.

Once compiled, install the new bzImage as your primary kernel, making sure to have a backup in your lilo.conf or a bootdisk handy just in case the kernel doesn't boot. You may notice upon first boot that LIDS complains about running unprotected binaries before the sealing of the kernel. This means that you are running programs during boot time that LIDS has not yet been set up to make sure are secure, with either a rule to make them read-only or invisible. Once you seal your kernel (by adding /sbin/lidsadm -I to rc.local or any script run at the end of your boot sequence), all LIDS features are effectively activated, and the loading of new kernel modules is prohibited. It is OK to see these messages, as we have yet to protect anything, but for future reference, you should protect all files run at boot time.

The first thing we will do after logging in as root will be creating a LIDS free session. This disables LIDS protection for a singular terminal login and will ONLY disable protection for a singular terminal login. This ability is available so you can administer LIDS on a system without rebooting into a non-LIDS kernel. To create a LIDS free session type in /sbin/lidsadm -S -- -LIDS and enter your LIDS password.

To get LIDS to stop complaining during boot time, let us protect all binaries that may be executed during boot time.

lidsadm -A -o /sbin -j READ
lidsadm -A -o /bin -j READ
lidsadm -A -o /usr/sbin -j READ
lidsadm -A -o /usr/sbin -j READ

The -A argument to lidsadm tells it we're adding a rule to lids.conf, the -o specifies the object the rule applies to, and the -j specifies the rule. You can also use the -s option to tell lidsadm the subject that the rule applies to, like allowing login to read /etc/shadow after setting /etc/shadow to deny for all other applications. The READ rule makes files read-only, even to root, and when applied to directories, it protects the directory and all files in it with the rule but doesn't travel across filesystems. The DENY rule makes files and directories invisible, even to root. If you so desired, you COULD make /sbin default to the DENY rule, but then you would have to make sure all your boot scripts had read access to /sbin and any other system-critical scripts. Are you really that bored? (Just nod your head yes with me, and we'll be fine...)

While we're protecting binaries, we might as well protect their libraries. I mean... all non-static binaries use code from the libs. It wouldn't make sense not to protect them.

lidsadm -A -o /lib -j READ
lidsadm -A -s /sbin/depmod -o /lib/modules/2.2.18/modules.dep -j WRITE

The second rule is necessary, otherwise your init scripts won't be able to build module dependencies at boot time. Of course, it isn't really necessary for this to be done, but it's the default way things are done, so we might as well set it up that way. Note that the LIDS documentation says to just remove depmod from your init scripts, but I personally view that as a backwards way of doing things. Either way works, and security isn't lessened either way, so do whatever makes you happiest.

Seeing as we have our modules protected, why not protect the kernel?

lidsadm -A -o /boot -j READ

The protection of important configuration files would be considered a good idea. Note that LIDS protects /etc/lids/ under all situations, unless you are in a LIDS free session, anyway.

lidsadm -A -o /etc -j READ

Doing this increases security, but causes all sorts of problems for programs that need to write to files in /etc, so we're going to have to set a few binaries up to bypass the default rules.

lidsadm -A -s /bin/mount -o /etc/mtab -j WRITE
lidsadm -A -s /bin/umount -o /etc/mtab -j WRITE

Mount and umount need to update this file. Another hack-around involves making /etc/mtab a symbolic link to /proc/mounts, and then editing your init scripts to call mount and umount with the -n option. That is a pain in the butt, and I'd rather do it with LIDS rules.

lidsadm -A -s /usr/bin/passwd -o /etc/shadow -j IGNORE
lidsadm -A -s /usr/bin/passwd -o /etc/passwd -j IGNORE

This allows any users on the system to change their passwords. It is probably a good idea to have this in your lids configuration.

lidsadm -A -s /sbin/hwclock -o /etc/adjtime -j WRITE

This helps keep your system time accurate.

lidsadm -A -o /etc/issue -j WRITE
lidsadm -A -o /etc/issue.net -j WRITE
lidsadm -A -o /etc/motd -j WRITE

These files don't really need to be protected and boot scripts like to write to them. Make said scripts put something nice in them too... I personally like to see ASCII art when I log in to my system. lidsadm -A -s /sbin/init -o /etc -j WRITE

This stops init from complaining that it cant write the file /etc/initrunlvl, which cant be done any other way. Init recreates this file each time you boot and thus has a different inode number each time you boot. This makes it impossible to give permissions to specifically under LIDS. You don't need this file, and so you can and probably should ignore this fix.

Lets set up /etc/shadow as DENY so that even processes with root privileges cant read it. This increases security.

lidsadm -o /etc/shadow -j DENY
lidsadm -s /bin/login -o /etc/shadow -j READ
lidsadm -s /bin/su -o /etc/shadow -j READ
lidsadm -s /bin/su -t -o CAP_SETUID -j NO_INHERIT
lidsadm -s /bin/su -t -o CAP_SETGID -j NO_INHERIT
lidsadm -A -s /usr/sbin/sshd -o /etc/shadow -j READ
lidsadm -A -s /usr/local/sbin/sshd -t -o CAP_SETUID -j NO_INHERIT
lidsadm -A -s /usr/local/sbin/sshd -t -o CAP_SETGID -j NO_INHERIT
lidsadm -A -s /usr/local/sbin/sshd -t -o CAP_NET_BIND_SERVICE -j NO_INHERIT

This set of rules set up /etc/shadow to be invisible and yet still allows authentication to take place. This is a good thing if you want users to be able to log into the system. The -t option to lidsadm tells it that the object is going to be a system capability and not a file or directory. System capabilities allow everything from setuid binaries (CAP_SETUID) to the binding of privileged ports (CAP_NET_BIND_SERVICE) to setting programs as append-only or immutable (CAP_LINUX_IMMUTABLE). The list of default system capabilities is kept in /etc/lids/lids.cap and can be edited to change the initial capabilities of your system. Just make sure that you have in your LIDS ruleset rules that give necessary binaries the capabilities they need to function properly. The INHERIT and NO_INHERIT options are available only when setting rules that change capabilities and decide whether or not child processes inherit this ability.

Protect the lilo configuration file so that it can be updated by root, but can only be changed during a LIDS free session.

lidsadm -A -o /etc/lids.conf -j DENY
lidsadm -A -s /sbin/lilo -o /etc/lids.conf -j READ

Lets make sure we have the proper capabilities to shut down our system.

lidsadm -A -s /sbin/init -t -o CAP_INIT_KILL -j NO_INHERIT
lidsadm -A -s /sbin/init -t -o CAP_KILL -j NO_INHERIT
lidsadm -A -s /etc/rc.d/rc.6 -t -o CAP_SYS_ADMIN -j INHERIT

Replace /etc/rc.d/rc.6 with whatever script reboots your system and unmounts filesystems.

Your /etc/lids/lids.cap file configures the state of the default capabilities on your system. It would be wise not to make changes to this file without knowing what these changes do. The results of a misconfiguration can result in everything from not being able to log in, to not being able to reboot.

Open /etc/lids/lids.cap with your favorite text editor and take a look at it. This is a list of all capabilities LIDS will be able to change in your kernel. Capabilities are activated or deactivated by default by placing either a + or - sign at the beginning of the line. Lets disable CAP_SYS_RAWIO, as raw input and output can be considered a security risk. Now in order for some programs to work, they may need access to raw input/output, like X. To get X to work after disabling this capability, add the following rule to your LIDS configuration:

lidsadm -A -s /path/to/your/X_server -t -o CAP_SYS_RAWIO -j NO_INHERIT

To reload your LIDS configuration, type in lidsadm -S -- +RELOAD_CONF if you have it enabled, otherwise you will need to reboot to put your new configuration into effect. If you ever make a change to your LIDS configuration that prevents you from booting correctly you can always specify security=0 at the lilo command line to disable LIDS, and then fix it.

I wouldn't be able to give examples of all LIDS abilities and configurations in less than the space provided in a book, but I do believe you get the point on how powerful LIDS is and how useful it can be as a tool for system security. If you need more information, be sure to visit the LIDS website. If you would like to use all of the mentioned patches at the same time, they have been combined for you here.

Comment? - Or do you think this article blows chunks and you could write a better one in your sleep? Then do it!
View Comment Page

Copyright © 2004, The Binary Freedom Project, LLC.