Paranoid Penguin - Linux Filesystem Security, Part II
Last time, we looked at file and directory permissions from the ground up—what users and groups are and how to set and remove read, write and execute permissions on files and directories. In this column, we look at some more advanced types of permissions, explore permission numeric modes and the command umask and see how to delegate root's authority with su and sudo. This article contains more intermediate-level information than last month's, but hopefully it should make sense, even if all you know about permissions is what you read here last time.
Recall last month's long listing of the extreme_casseroles/ directory:
drwxr-x--- 8 biff drummers 288 Mar 25 01:38 extreme_casseroles
Recall also that we set the group permissions on this directory to r-x, that is, group-readable and group-executable, so that our fellow members of the drummers group could enter this directory and enjoy the recipes stored therein.
Suppose that our drummer friend Biff wants to allow his fellow drummers not only to read his recipes but to add their own as well. As we saw last time, all he needs to do is set the group-write bit for this directory, like this:
chmod g+w ./extreme_casseroles
There's only one problem with doing that, however. Write permissions include both the ability to create new files in this directory and also to delete them. What's to stop one of his drummer pals from deleting other people's recipes? The sticky bit, that's what.
In olden times, the sticky bit was used to write a file (program) to memory so it would load more quickly when invoked. On Linux, however, it serves a different function. When you set the sticky bit on a directory, it limits people's ability to delete things in that directory. That is, to delete a given file in the directory you either must own that file or own the directory, even if you belong to the group that owns the directory and group-write permissions are set on it.
To set the sticky bit, issue the command:
chmod +t directory_name
In our example, this would be chmod +t extreme_casseroles. If we now do a long listing of the directory itself, by using ls with the -d option to list the directory's permissions rather than its contents, that is, ls -ld extreme_casseroles, we see:
drwxrwx--T 8 biff drummers 288 Mar 25 01:38 extreme_casseroles
Notice the T at the end of the permissions. We'd normally expect to see either x or - there, depending on whether the directory is other-writable. The T denotes that the directory is not other-executable and has the sticky bit set. A lowercase t would denote that the directory is other-executable and has the sticky bit set.
To illustrate what effect this restriction has, suppose a listing of the contents of extreme_casseroles/ looks like Listing 1.
Listing 1. Contents of extreme_casseroles/
drwxrwxr-T 3 biff drummers 192 2004-08-10 23:39 . drwxr-xr-x 3 biff drummers 4008 2004-08-10 23:39 .. -rw-rw-r-- 1 biff drummers 18 2004-07-08 07:40 chocolate_turkey_casserole.txt -rw-rw-r-- 1 biff drummers 12 2004-08-08 15:10 pineapple_mushroom_surprise.txt drwxr-xr-x 2 biff drummers 80 2004-08-10 23:28 src
Suppose further that the user crash tries to delete the file pineapple_mushroom_surprise.txt, which crash finds offensive. crash expects this to work, because he belongs to the group drummers and the group-write bit is set on this file. Remember, though, that biff set the parent directory's sticky bit. Therefore, crash's attempted deletion fails, as we see in Listing 2.
Listing 2. Attempting Deletion with Sticky Bit Set
crash> rm pineapple_mushroom_surprise.txt rm: cannot remove `pineapple_mushroom_surprise.txt': Operation not permitted
One more note on the sticky bit: it only applies to the directory's first level downward. In Listing 1, you may have noticed that besides the two nasty recipes, extreme_casseroles/ also contains another directory, src. The contents of src will not be affected by extreme_casseroles' sticky bit, although the directory src itself is. If biff wants to protect src's contents from group deletion, he needs to set src's own sticky bit.
Now we come to two of the most dangerous permissions bits in the world of UNIX and Linux, setuid and setgid. If set on an executable binary file, the setuid bit causes that program to run as its owner, no matter who executes it. Similarly, when set on an executable, the setgid bit causes that program to run as a member of the group that owns it, again regardless of who executes it.
When I say run as, I mean the program runs with the same privileges as. For example, suppose biff writes and compiles a C program, killpms, that behaves the same as the command rm /extreme_casseroles/pineapple_mushroom_surprise.txt. Suppose further that biff sets the setuid bit on killpms, with the command chmod +s ./killpms and also makes it group-executable. A long listing of killpms might look like this:
-rwsr-xr-- 1 biff drummers 22 2004-08-11 23:01 killpms
If crash runs this program, he finally can succeed in his quest to delete the Pineapple-Mushroom Surprise recipe: killpms runs as though biff had executed it. When killpms attempts to delete pineapple_mushroom_surprise.txt, it succeeds because the file has user-write permissions and killpms is acting as its user/owner, biff.
setuid and setgid are very dangerous if set on any file owned by root or any other privileged account or group. I'm illustrating setuid and setgid so you understand what they do, not because I think you actually should use them for anything important. The command sudo, described later in this article, is a much better tool for delegating root's authority.
If you want a program to run setuid, that program must be group-executable or other-executable for what I hope are obvious reasons. In addition, the Linux kernel ignores the setuid and setgid bits on shell scripts. These bits work only on binary (compiled) executables.
setgid works the same way but with group permissions. If you set the setgid bit on an executable file with the command chmod g+s filename, and if the file also is other-executable (-r-xr-sr-x), when that program is executed it runs with the group ID of the file rather than of the user who executed it.
In the above example, if we change killpms' other permissions to r-x (chmod o+x killpms) and make it setgid (chmod g+s killpms), no matter who executes killpms, killpms exercises the permissions of the drummers group, because drummers is the group owner of killpms.
What about directories? Well, setuid has no effect on directories, but setgid does, and it's a little non-intuitive. Normally, when you create a file, it's automatically owned by your user ID and your (primary) group ID. For example, if biff creates a file, the file has a user owner of biff and a group owner of drummers, assuming that drummers is biff's primary group, as listed in /etc/passwd.
Setting a directory's setgid bit, however, causes any file created in that directory to inherit the directory's group owner. This is useful if users on your system tend to belong to secondary groups and routinely create files that need to be shared with other members of those groups. For example, if the user animal is listed in /etc/group as being a secondary member of drummers but is listed in /etc/passwd has having a primary group of muppets, then animal has no trouble creating files in the extreme_casseroles/ directory, whose permissions are set to drwxrwx--T. However, by default, animal's files belong to the group muppets, not to drummers, so unless animal manually reassigns his files' group ownership (chgrp drummers newfile) or resets their other permissions (chmod o+rw newfile), other members of drummers cannot read or write animal's recipes.
If, on the other hand, biff or root sets the setgid bit on extreme_casseroles/ (chmod g+s extreme_casseroles), when animal creates a new file therein, the file has a group owner of drummers, exactly like extreme_casseroles/ itself. All other permissions still apply; if the directory in question isn't group-writable to begin with, the setgid bit has no effect, because group members are not able to create files inside it.
Now we've covered all possible permissions: read, write, execute, sticky bit, setuid and setgid. If you understand all six of these, you're probably in the minority of Linux users. But wait, there's more!
So far we've been using mnemonics to represent permissions—r for read, w for write and so on. Needless to say, as with everything else, your system actually uses numbers to represent permissions. The chmod command recognizes both mnemonic permission modifiers (u+rwx,go-w) and numeric modes.
A numeric mode consists of four digits: as you read left to right, these represent special permissions, user permissions, group permissions and other permissions. Recall that other is short for other users not covered by user permissions or group permissions. For example, 0700 translates to no special permissions set, all user permissions set, no group permissions set and no other permissions set.
Each permission has a numeric value, and the permissions in each digit place are additive: the digit represents the sum of all permission bits you want to set. If, for example, user permissions are set to 7, this represents 4 (the value for read) plus 2 (the value for write) plus 1 (the value for execute).
As I just mentioned, the basic numeric values are 4 for read, 2 for write and 1 for execute. (I remember these by mentally repeating the phrase, read-write-execute, 4-2-1.) Why no 3, you might wonder? Because this way, no two combination of permissions have the same sum.
Special permissions are as follows: 4 stands for setuid, 2 stands for setgid and 1 stands for sticky bit. For example, the numeric mode 3000 translates to setgid set, sticky bit set and no other permissions set, which is, actually, a useless set of permissions.
Here's one more example of a numeric mode. If I issue the command chmod 0644 mycoolfile, I am setting the permissions of mycoolfile, as shown in Figure 1.
Figure 1. Permissions for mycoolfile
For a more complete discussion of numeric modes, see the info page for coreutils, node Numeric Modes. That is, enter the command info coreutils numeric.
I want to cover one last command specific to permissions before closing with a couple of other topics. umask is a command built into the bash shell that prints or sets your default permissions mask. To see yours, simply enter the umask command without any arguments; it returns a four-digit number. On my system, it looks like Listing 3.
Listing 3. Checking My Default Permissions Mask
mick@localhost:/home/mick> umask 0022
Mode 0022 means no special permissions, no user-owner permissions, group and other permissions set to write, right? How can that be?
Actually, umask deals in masks, not in modes per se. 0022 is what is subtracted from the number 0777 to determine the numeric mode of files you create: 0777 – 0022 = 0755.
Aha! So, files I create have user-owner permissions set to read-write-execute (7 = 4 + 2 + 1) and group and other permissions set to read-execute (5 = 4 + 1)? Right? Almost. It also happens that umask sets the execute bit automatically only on directories. Even if your permissions mask includes execute permissions, the execute bit does not set automatically on regular files you create. So, if my permissions mask is 0022, resulting in default permissions of 0755, and I create a file named default_file and a directory named default_dir, long listing output for those two items look like Listing 4.
Listing 4. File and Directory with Mask of 0022
-rwxr-xr-x 2 mick users 48 2004-08-13 08:31 default_dir -rw-r--r-- 1 mick users 4 2004-08-13 08:31 default_file
To change your default permissions mask, simply issue the command umask with the new mask as its argument. For example, if I want all my files to have group-read permissions but no other permissions, this translates to a numeric mode of 0740. If I subtract that from 0777 I get a mask of 0037. Therefore, the umask command I enter is umask 0037. This new mask, however, applies only to my current session and any new shells I start from it. To make it persistent, I can add the line umask 0037 to my .bashrc file.
I should say a few words about the reality of users, groups and permissions. The whole problem with UNIX security is that far too often, permissions and authority on a given system boil down to root can do anything, although users can't do much of anything.
Sadly, it's much easier to do a quick su - to become root for a while than it is to create a granular system of group memberships and permissions that allows administrators and sub-administrators to have exactly the permissions they need. Sure, you can use the su command with the -c option, which allows you to specify a single command to run as root rather than an entire shell session (for example, su -c rm somefile.txt), but this requires you to enter the root password. It's never good for more than a small number of people to know root's password.
Another approach to solving the root-takes-all problem is to use role-based access control (RBAC) systems, such as SELinux, which enforce access controls that reduce root's effective authority. However, this makes things even more complicated than setting up effective groups and group permissions. This is not to say that SELinux and the rest aren't good things—I love RBAC.
A better middle ground is to use the sudo command. sudo is short for superuser do, and it allows users to execute single commands as root, without actually needing to know the root password. sudo is now a standard package on most Linux distributions.
sudo is configured with the file /etc/sudoers, but you shouldn't edit this file directly. Rather, use the visudo command, which opens a editor on the file; vi is the editor by default. You can use a different editor by setting the EDITOR environment variable. For example, to use /usr/bin/gedit, do this:
Space doesn't permit me to explain sudoers' syntax in detail; see the sudoers(5), sudo(8) and visudo(8) man pages for complete information. In the space available here, let's run through a quick example.
Remember the user crash's quest to rid the world of Pineapple-Mushroom Surprise? Although in this case it would be overkill—the permissions techniques I've already illustrated are sufficient—you could use sudo to allow crash to realize his goal, assuming you (biff) have root privileges. First, become root (su -). Next, execute the command visudo. You're now in a vi session, editing the file /etc/sudoers; see the vi(1) man page if you're new to vi. Go down to the bottom of the file and add this line:
crash localhost=/bin/rm /home/biff/extreme_casseroles/pineapple_mushroom_surprise.txt
Save and exit the file.
Now, to do his thing, crash enters the command:
sudo rm /home/biff/extreme_casseroles/pineapple_mushroom_surprise.txt
whereupon he is prompted to enter his password. After he enters this correctly, the command:
is executed as root, and the offending file is gone.
Alternately, the line in /etc/sudoers could look like this:
crash localhost=/bin/rm /home/biff/extreme_casseroles/*
That way, crash can delete anything in extreme_casseroles/, regardless of the sticky bit setting.
As handy as it is, sudo is a powerful tool, so use it wisely; root privileges never should be trifled with. It really is better to use user and group permissions judiciously than to hand out root privileges, even with sudo. Better still, use an RBAC-based system such as SELinux if the stakes are high enough.
That's it for now. I hope you've found this tutorial useful. Until next time, be safe!
Mick Bauer, CISSP, is Linux Journal's security editor and an IS security consultant in Minneapolis, Minnesota. He's the author of Building Secure Servers With Linux (O'Reilly & Associates, 2002).