Inexpensive, effective mixed-platform Network Security using Linux-based solutions. 

 
Horizon Network Security™
phone: +1 770-662-8321, email: support@VerySecureLinux.com

 
Our Publications

PROBLEM SOLVER

Permissiveness

by Bob Toxen

File permissions, file ownership, and process ownership are overlooked all too often -- curious in light of the benefits and dangers they represent. Each file has a user and a group associated with it, along with a list of permissions defining who can do what with the file. Who can be limited either to processes with the same effective user identification (UID), processes with the same effective group identification (GID), or all other processes.

Processes that have permission can be restricted either to reading the file's data, writing data to the file (either by writing over existing data or by appending data to the end of the file), executing the file (invoking it as a binary program or process), or some combination of these.

When someone first logs in, a login shell is created with the user id and group id specified for that person's account in the /etc/passwd file. These designate both the real and effective user and group ids. Under normal use, the real and effective ids stay the same. Any child process created with the fork() system call will inherit the user and group ids of its parent.

When the exec() system call is used to start a program, the real user and group ids stay the same but the effective ids may be changed to the user and group of the file executed (instead of that of the process performing the exec()). This can be accomplished by establishing the set-UID or set-GID permissions (bits) on the file prior to execution.

Programs with the set-UID or set-GID bits set in their file entries are called set-UID programs or set-GID programs and are crucial to the day-to-day operations of UNIX. For example, the passwd program is set-UID to root but is executable by everyone. By contrast, the /etc/passwd file, which contains information on people's accounts, is owned by root and its permissions allow only root to write data to it. Thus, anyone can use the passwd program to change their own password, but the program is written in such a way as to prevent people from changing other users' passwords or any other data in the file. It does this by using the getuid() system call to determine a user's real UID.

The su program (which runs set-UID to root) uses a somewhat different technique. It allows one to start a subshell running under someone else's id without first logging out of one's own account, assuming that the password listed for the new account in the /etc/passwd file is known.

After verifying the password, su uses the setgid() and setuid() system calls (in that order) to change both the real and effective group and user ids for the running su process. It then uses the exec() call to start a shell running with the new permissions.

A program running with an effective user id of zero (the id assigned to root) may use the setuid() and setgid() system calls to change its real and effective user and group ids to any value. Of course, after using setuid() to change its UID, the program no longer will have root's unlimited power. This is why su and almost all similar programs invoke setgid before setuid.

A program with an effective UID other than root may use setuid() and setgid() (in any order) to change its effective ids to match its real ids. This capability is quite powerful and will be discussed when rogue is investigated later in this column.

These programs illustrate how an ordinary user can be granted extraordinary permissions (powers) in a controlled manner. The technique is basic to UNIX programming and is used by ps, df, uucp, some printer spoolers, and many databases, among others.

Also central to the UNIX system is the fact that whatever is true about permissions for files is true about directories as well -- but in spades. A directory, such as /usr/people/bob or /bin, may be opened and read like an ordinary file. The ls utility does this when a directory is listed. Directories consist of 16-byte records (4.2BSD has a similar but different format), of which the last 14 bytes specify a null-padded file name of some file "in that directory" and the first two bytes contain a 16-bit integer specifying the file's inode number. An inode number of zero indicates that a record or "slot" is not currently in use.

Not even root can write directly to a directory. This prevents file system corruption and disables an operation that is useless. The create(), link(), unlink(), and mknod() system calls are the mechanisms one uses to write to directories as well as to create or destroy inodes. Thus, these calls require write access to the directory that contains or soon will contain the affected file. Write access to the file itself is not required.

Since it makes no sense to execute a directory as a program, the execute permission bit is interpreted as permission to access a particular file in the directory. Examples of operations that require access are the execution of a program in the directory; the opening of a file within the directory for reading or writing (which will require read or write access to the file); the use of stat() on a file (which requires no access permission for the file itself); the issue of a creat() or unlink() call (which will require write access to the directory); a change into the directory (by the chdir() call) or any references to a subdirectory ... and so forth and so on.

USAGE

It is clear that inattention to permissions could potentially invite others to execute programs, read documents, and write over or remove files. What can be done to defend against this? First, you must decide who should have access to your files, and what level of access they should have. This can often be a complex question. Typically, most files are not confidential and so may be left readable by anyone. Others, such as company planning documents, customer lists, or personnel reports, should be held in strict confidence.

The chmod program can be used to change the permissions of any file you own to the modes you desire. The first argument (parameter) to chmod specifies the modes to which files identified in subsequent arguments should be set. This first argument consists of an octal number, in which the rightmost digit specifies the permissions for others, the next digit to the left specifies permissions for users in the same group, and the following digit to the left specifies permissions for the file's owner. Each digit is either zero or the sum of some combination of four, two, and one. The digit 4 signifies read permission, 2 stands for write permission, 1 indicates execute permission, and 0 means that no permissions whatsoever have been granted.

An optional fourth digit, located on the extreme left, may be used to have a program be set-UID or set-GID. If this digit is a 4, then the program will be set-UID. If the digit is a 2, the program will be set-GID, and if the digit is a 6, the program will be set-UID and set-GID. [Letters can also be used to designate permissions under System V and 4.2BSD implementations of chmod. The argument g+w, for instance, indicates that group members should enjoy write permission for the specified file. Likewise, the argument o-w indicates that write permission should be denied to others.]

Thus, if you have a document called group_plan that you personally want to be able to read and write, that members of your group should be able to read, and that other users at large should not be able to access at all, you would want to issue the command:


  % chmod 640 group_plan

If you create lots of files, this process can become tiresome and it likely will be overlooked from time to time. Fortunately, a command available in both the regular Bourne shell and the C shell can do this automatically. Called umask, it takes a single argument in the same format as chmod but the digits represent permissions that should be turned off, rather than on (as with chmod). Thus, if the command:


  % umask 037
is issued before the group_plan file is created, the file automatically will be generated with full permissions for you, read permissions for your group, and no permissions whatsoever for other users. It is common practice to include an invocation of the umask command in one's .profile or .login file so that the command will be executed automatically each time you log in (see the July, 1984 Problem Solver for more details).

Another way to make things easier is to group similar files in subdirectories and structure the directory permissions so as to limit access to the files they contain. For example, one can create a directory called group_notes and set its permissions to, say, 770. This would allow anyone in the group to search the directory, create or delete files, and read and write those files that offer permission to do so, while keeping others out of the directory altogether.

Execute permission for a directory is required in order to access the files it contains. Note that in order to access a file, one must also have permissions for the file itself (except when creating or removing it or using the stat() system call).

GROUP PERMISSIONS

A user's account should be assigned to a group containing other users with whom data will likely be shared. Access to that user's account then will be denied to those outside of the group.

The group that a person's account is in (GID) is specified in the /etc/passwd file in the colon-separated field following the UID field. Once that field is modified, the new GID (and UID) automatically will be used by all programs that the person invokes, starting with the next time the person logs in. (The set-UID and set-GID programs, of course, are exceptions to this rule.)

In order to associate a numerical GID with a named group (in much the same way that the /etc/passwd file associates UIDs with named accounts), an entry must be made in the /etc/group file. This is a text file with one entry per line. Each entry consists of several comma-separated fields. The first field contains the group's name. The second contains a group password that can be used in conjunction with newgrp. If you don't know what this password is, I recommend putting an "x" or "*" in the field for security. The third field, which should be terminated by a colon, contains the GID.

The optional fourth field of /etc/group usually contains a comma-separated list of accounts associated with the group (to help administrators track these things). If someone from a different group (according to the /etc/passwd file) is listed here, she can use the newgrp command (in some implementations) to switch her GID to the new group for all subsequent processes in the login session.

Under 4.2BSD, different rules apply. When a file is created, its group will be that of the directory in which it's created. Additionally, an account automatically belongs to all of the groups in which it is listed in /etc/group.

MOVING UPWARD

The use of permissions can be truly innovative. For example, suppose that a system contains a printer or modem that belongs to a particular department. To prevent it from being used by others, the device's file in the /dev directory can be set up such that its group contains the accounts of the people in the department, with its mode set to 660. This will prevent others from using it. The same technique can be used for modems, tape drives, file systems, or any other device. In one installation, I applied this technique to the device file for a serial tty line that was connected to a remote computer containing sensitive company source code. This prevented unauthorized access to the remote computer -- an important consideration since the local computer was part of the more-or-less public UUCP network. (UUCP has its own set of security rules, but that is a column unto itself.)

The game rogue offers an interesting study in permissions problems. As many people know, rogue is a fantasy game program similar in concept to adventure. Permissions become an issue because the game maintains a scoreboard that ranks scorers (according to who finds the most gold). To prevent cheating, ordinary users should not have unlimited access to this file. The obvious approach is to emulate the relationship between the passwd program and the /etc/passwd file. Thus, the score file would be owned by the games account and set to mode 600 while rogue would run set-UID to games

This presents a problem, however, because rogue has another feature that allows one to suspend a game by saving its state in the file of the user's choice, making it possible to continue a game at a later time, even if that time comes after the system has been rebooted. See if you can figure out the problem.

It should be rogue rather than the user that worries about how to keep the score file away from the clutches of dastardly data diddlers. Users, though, may be interested in preserving security in their own directories.

Consider the situation where the user's current working directory is her login directory (the usual situation), where the permissions have been set to 700 to prevent anyone else from accessing files or creating new ones. What will happen if the user starts playing rogue and then decides to save the game in a file called hotstuff? Rogue will not be able to create this file in her current directory because it will be running set-UID to games, which is not her account. One solution would be to have rogue use the setuid() system call to change its effective user id back to its real user id. It can then create the hotstuff file and exit.

Alas! Life is not so simple. Rogue was created as a game of risk. If one could save the game whenever danger arose, it would be tempting to start the game repeatedly from that point, trying different strategies until one succeeded -- and where would the risk be in that? Happily, rogue prevents this by automatically removing the saved file whenever play is resumed. (It also checks to ensure that no copies or links have been made to the file.)

Consider the situation where the directory containing the hotstuff file is set to mode 700. What do you suppose happens when rogue tries to remove it? Right, rogue won't be able to. It actually could use the setuid() system call to set its effective user id back to its real user id so that it could remove the hotstuff file and resume the game, but it would no longer be able to change the score file since it would no longer be set-UID to games. What rogue really needs is a split personality allowing it to be both set-UID and not set-UID. Although there are a number of proposals for allowing the setuid() system call to flip the effective user back and forth between the real user id and the original effective user id, 4.2BSD is to my knowledge the only major UNIX version to have implemented the concept.

Fortunately, the fork() system call is just the thing for creating split personalities. One of the processes that a fork can create could do a setuid() back to the real user id while its sibling could stay set-UID to games (or whatever). Synchronization between these two processes could be done with exit() and wait(), pipes (named or unnamed), semaphores, or by other means.

When I fixed rogue to handle this situation, I had the child process do a setuid() back to the real user id, remove the file, and exit. The parent would wait for the child to exit and then get on with the game. This isn't the most elegant solution but the second or so consumed by the fork is trivial compared to the hours spent by the average user rogueing.

Leave it to a game to show just how much the right amount of permissiveness -- combined with a dash of creativity and insight -- can accomplish.


Bob Toxen is the Senior Software Engineer at Stratus Computer, Inc. He has gained a reputation as a leading expert on UUCP communications, file system repair, and UNIX utilities. He has also done ports of System III and System V to systems based on the Zilog 8000 and Motorola 68010 chips.
Copyright © 1985, 2007, 2014, 2020 Robert M. Toxen. All rights reserved.
Back