University of Minnesota
Development of Secure Software Systems
index.php

CSci 4271 Lab 6

Today's lab will give you hands on experience with Unix file permissions, continuing the abstract discussion from lecture.

For the first part of the lab, we'd like you to spend some time trying out Unix commands related to checking and changing the permissions on files. In the description below we'll first introduce some permissions-related commands; then we'll give some suggestions of things you can try out.

Since you're doing the lab with other students, you should take advantage of being able to ask another student to test an operation for you. One of the limitations of trying out file permissions yourself is that you can only simulate one user's accesses, but someone else can check how permissions work for a non-owner.

Your CSE Labs home directories are stored on a networked file systems where some rarer aspects of permissions are unsupported or work differently. You also might not want to change the permissions on your home directory to let another student access it, even temporarily. So what we recommend you use instead is creating files and directories inside the directory named /tmp that is explicitly for temporary files. Note however that the /tmp directory is not shared between machines, so multiple people doing testing should all SSH into the same machine to run experiments in its /tmp. Also, the top-level directory /tmp has some special rules, so create a subdirectory of /tmp (for instance, named after your user name) for testing.

There are three commands we recommend you try out for looking at the permissions on Unix files (all of these commands also have more detailed documentation in their man pages):

  • The most commonly used program is ls with the -l (lowercase ell for long) option that causes it to print a line of information about each file or directory. The permissions information is in the 2nd through 10th columns, after the very first column which gives the file type. These columns mostly correspond to the nine basic permissions bits, where they are either a hyphen to represent a bit being 0, or an r, w, or x to represent a one bit based on its position. There is an exception that an x bit will turn into an s, S, t, or T in some special circumstances called "setuid", "setgid", or "sticky". Later in the line you'll see the names of the user and group owners.

  • The stat command-line program is like a more detailed version of ls -l that prints all of the metadata returned by the stat system call, across several lines. The line of its output that has the headings Access:, Uid:, and Gid: has the permissions information. The most useful extra feature for us is that it prints the octal version of the permissions bits.

  • The command getfacl provides the most expanded version of the permissions information; as the letters ACL in its name indicates, the multiple lines of its output have the structure of a general access-control list. You may find its output the easiest to read; it also become important once you try out more general ACLs (described later).

Then there are two commands you can try out to change permissions on files:

  • The command chgrp has a limited purpose: it changes the group of a file. Only the owner of a file can change its group, and they can only change it to another group they are a member of. You can use the command groups to see what groups you're a member of. There is also an analogous program named chown to change the owner of a file, but it is useless to you on CSE Labs machines because only root can change the ownership of files. (Actually, chown can change just the group; then it's equivalent to chgrp.)

  • The command chmod changes permissions. The most basic way to use it, which is convenient if you know exactly the permissions you want in octal, is to use the new permissions in octal as the first argument, and any remaining arguments are the names of files or directories you want to have that permission set. If you want to change some of the permissions and leave others the same, which is most important if your changing multiple files at once, the first argument also has a letter-based form that you can read about in the manual page. And if you want to change the permissions on a whole tree of files, the option -R causes chmod to work recursively on a directory and all of the files and directories inside it.

Take some time to try out these programs on different files, and then trying to do different operations on the files, to see whether the permissions work the way you would expect. For checking whether you can execute a file, you may want to work with a file that's a copy of a simple executable from a system directory, for instance /bin/uname. Once the basics seem to make sense, here are some further things you can try:

  • Try creating a directory where you have execute but not read permissions. For instance if you can access a file in that directory if you know its name, but ls and tab completion won't work.
  • Conversely, what happens if you try to run ls or ls -l on a directory for which you have read but not execute permission? Why?
  • If you have write permission but not execute permission on a directory, are there any operations you can do to it?
  • Suppose there is a file that you want to modify, and you don't have write permissions on the file, but you do have write permissions on the directory where the file is stored. What can you do?
  • Similar to the previous question, but what if the file is inside several levels of directories you don't have write access to, but high above it there is a directory you can modify?

More general ACLs

The restriction of file ACLs having only one specified user and one specified group, which is traditional in Unix, has been lifted in many derived systems. The extension that's most reliably available for local filesystems on Linux is referred to as POSIX ACLs (though it never made it to an official part of the POSIX standard). You can set these with the command setfacl which is the modifying counterpart of the reading program getfacl mentioned above. The full mechanism has a number of wrinkles, but as something simple you might try giving read access to just one other person, something that in the traditional Unix model would require creating a new group.

Giving a program permissions with setuid

The main way Unix permissions work is that all programs run by a user run with the user's permissions: anything you could do with one program, you could also do with a different program, so the exact set of operations that any given program allows doesn't affect security. But sometimes you want to allow users to do an operation that requires privilege, but with some extra checks. One way Unix makes this possible is by making a program "setuid", which means that when the program runs, it uses the UID of the owner of the executable file rather than the user who executed the program. You can thing of this as embedding some of the owner's privilege in the program, so that it can be run by users to do operations they wouldn't otherwise be allowed to do. But you have to be more careful with the design of a setuid program from a security standpoint, because if the program is subverted, it can allow an attacker to misuse the privileges.

One classic example of a service that needs to be provided by a privileged service is (local) email. To deliver a message, we would like to append it to the end of a user's mailbox file, which requires write permission on the mailbox file, but if everyone who could send email had write permissions on the recipient's mailbox, though could do undesirable things like modify previously-delivered messages. So one way of implementing this is to have mail delivery performed by a setuid process, which carries the permission to write to the mailbox file but will only do so in a safe way. For this lab, you can try out this idea in a simplified way by setting up a setuid program that delivers short messages (more like chat messages than emails) just for a single user.

To see the power of setuid in action, one student will need to set up the delivery program and another student will need to run it. To get you started we've written a short C program that has the basic needed functionality, which you can copy with a command like:

cp /web/classes/Spring-2023/csci4271/labs/06/append-to-messages.c .

The location of the messages file is hardcoded into the program as the constant variable messages_file. You'll want to update the line in the code that defines the variable to point to a file controlled by you if you're going to be receiving messages: this file doesn't need to have read or write permissions for anyone else, but it should have write permissions for you. Then you can compile the program in the usual way, such as:

gcc -Wall -g append-to-messages.c -o append-to-messages

The put the compiled program in a location where the other user who is going to send you a message can access it, and make sure they have execute permissions on it, and make the program setuid to you with the flag u+s to chmod. If everything is working correctly, the sending user shouldn't be able to access the messages file directly, but they should be able to run the setuid program with a message as a command-line argument, and the recipient should see the message appended to their messages file.

Combinatorics of Unix permissions

Here's one question you might think about as a bit of a brainteaser; it's related to Unix permissions bits but is more of a math (or CSci 2011) question. If we put no restrictions on the different bits in a permissions set, there are a total of 512 different possible permissions sets, but most of these are not very useful. One restriction you might imagine imposing to get a more limited but useful set of permissions (and which is true of most permissions in practice) is to require that the permissions never go down as you go to a more specific level. In other words, require that every permission that other users have, group users have, and every permission that group users have, the owner also has. For instance the octal permissions sets 000, 700, 777, 755, and 664 all obey this restriction, but 070 and 321 do not. How many permission sets remain after imposing this restriction? The best answer would be a formula for counting the permissions that generalizes to different numbers of permission bits (say b=3 for Unix's r, w, and x) or different numbers of levels (say l=3 for Unix's user, group, and other), and an explanation of where the formula comes from. If you're not feeling mathematically inspired, you could also try writing a program to compute the result by brute force for different parameters and looking for patterns in the results.