Getting to Know UNIX from a Security Perspective

To have a meaningful discussion of UNIX security it is important that you first understand how the operating system works and that you understand its built-in security mechanisms. The best view to have of UNIX for purposes of security also happens to be the simplest view. This chapter describes UNIX in the most fundamental terms and begins to build your understanding of how UNIX functions (with occasional detours to discuss relevant security issues). It then goes on to provide a high-level description of all the system elements of concern to security administrators. In a structured organization, this chapter alone might very well provide the degree of knowledge required by managers, and the following chapters might provide the degree of detail required by technicians.

This chapter does not cover the individual UNIX commands but focuses on communicating the fundamental concepts. The UNIX commands associated with the topics of this chapter, and in many cases the topics themselves, are described in greater detail in subsequent chapters. In many ways this chapter is designed to be an "executive summary" of the inner workings of UNIX, with an emphasis on issues related to security.

The goal of this chapter is to characterize UNIX as simply a set of processes that operates on files, and to familiarize you with the general attributes of processes and files. It describes the relationships that exist between processes (for example, "parent" and "child" processes) and the various ways processes are initiated under UNIX.

Files, Directories, and Processes in UNIX

The three fundamental entities in a UNIX system are files, directories, and processes. Once you understand the attributes of these entities and how they interact with one another, you'll see that understanding all the complexities of using UNIX becomes an exercise in understanding how the various software subsystems work. If you are familiar with the concept of files and directories under DOS, you have a high-level view of how files are organized into directories under UNIX. Files are collections of information stored on the disk, and directories are areas where related files may be grouped together. Processes are simply programs that run on the system and generally read, write, create, or delete files according to their programming. Processes may also interact with the outside world (for example, accepting input from and directing output to terminals). However, as you will see, even the devices, such as terminals, are accessed through special files maintained under the UNIX file system.

All information permanently stored in UNIX is stored in the form of files on a disk. Computer files contain a wide variety of information. Some files may be simple word processing documents that users are editing at a terminal. Other files may be programs that the computer can run. Still others may contain information about the computer's configuration, and are used internally by the operating system. When a program is executed under UNIX it is loaded from the appropriate disk file into memory, where it runs.

Because UNIX is a complex, multiuser system, files also contain certain standard information that enables user access to files to be controlled. Each file has an owner and a group, and each file has read, write, and execute permissions stored for the owner, the group, and everyone else.

In UNIX, file systems are devices that can store files. Most often a file system is a partition of a hard disk. When a file system is created, a data block is allocated for each file that can possibly exist on the device. (Needless to say, there can be thousands and tens of thousands of these blocks on a single disk partition.) This data block is called an inode, and among other things it contains the ID of the owner of the file, the user group assigned to the file, and file access permissions. The inode also contains information indicating the type of file to which it refers. In UNIX, files may be regular files, directories, device files, or symbolic links. Regular files contain stored information that is read and written by processes on the system. Directories are special files that assign filenames to inode numbers. Device files contain instructions for opening, closing, reading, and writing to system devices such as tape drives and disk partitions. Symbolic links are files that contain the names of other files or directories, and when they are accessed for input/output (I/O) they redirect the access to the named file.

All the normal UNIX commands interact with directories. Directories are most easily thought of as tables that map filenames to inode numbers. (See Figure 1.1.) By referencing directories, you can refer to files with names, rather than being forced to remember and specify inode numbers.

Filenames actually contain three distinct sections: the directory, the body of the filename, and the filename extensions. You might have a file named todo.lis in your directory. (The directory is much like a directory under DOS--it's simply a logical "place" where you can keep files.) In this case the filename is todo and the extension is lis. UNIX filenames are separated from extensions with the . character. A file may have several extensions separated by the . character or it may have no extension at all. The following are valid UNIX filenames:

todo
todo.lis
todo.lis.new

Unlike some computer operating systems (most notably DOS), UNIX is case-sensitive regarding filenames as well as the names of commands. As a result, UNIX treats the filename phone.lis as being completely different from the file name PHONE.LIS, or even Phone.lis. Similarly, the command runprog is entirely different from the commands RUNPROG and RunProg.
Although many variations of UNIX support very long filenames, it is generally a good idea to keep filenames shorter than 14 characters. This is because some pretty commonly used UNIX tools still assume a 14-character filename limit and may either produce errors when handling filenames that are longer or assume that two filenames are the same if they contain the same first 14 characters. One example of this problem is the ar command, which manages archive files and often truncates to 14 characters the names of files it manages. Most vendors of modern UNIX systems allow up to 64 characters in a filename, with Hewlett Packard and SUN allowing up to 128 characters. The maximum length of a complete filename (including its directory path) is defined by the MAXPATH kernel parameter. Kernel parameters are adjustable numeric values that govern the limits within which UNIX must operate (for example, the maximum number of processes that can run on the system or the maximum number of processes that can be running for a single user).

Some file extensions are recognized universally; for example, the .c extension indicates that a file contains the source code to a C program (C is a common language resident on virtually all UNIX systems). You may, however, assign file extensions to the files you create as you see fit. It is very common to see file extensions of three characters; for example, files with the extension .txt may contain general text information, .doc files may contain documents, and .tmp files may contain temporary information that is subject to deletion at any time. File extensions are not limited to just 3 characters. Generally filenames should be limited to 14 characters, because filenames that agree up to the first 14 characters are taken to be identical by some UNIX facilities.

The following are common file extension that you may encounter on UNIX systems:

.doc
Text files that contain documentation
.txt
Miscellaneous text files
.tmp
Temporary files
.sh
UNIX Bourne shell programs
.ksh
UNIX Korn shell programs
.csh
UNIX C shell programs
.c
Source code to C programs
.h
Files that support C programs
.lis
Source code listings produced by compilers
.sed
Stream editor command scripts
.awk
awk language programs
.dat
Data files
.o
Linkable object files produced by compilers
.a
Libraries of linkable object files
Some tools will modify file extensions to indicate that a file has gone through some sort of processing or transformation. One example of this is the compress command, which compresses a file and appends a .Z to the filename to indicate that the file has been compressed. If you ran compress on the file todo.lis, the file produced would be todo.lis.Z.

If the thousands or tens of thousands of files on a typical UNIX system could not somehow be segregated into groups of related files and be viewed and treated as groups, UNIX would certainly be an unwieldy working environment. Fortunately UNIX supports the notion of separate directories in which files may be stored, managed, and manipulated. In fact, directories can and often do contain their own subdirectories, which may then contain subdirectories of their own, nested to virtually any level. Such a directory arrangement is often termed the "inverted tree" structure, because a graphic diagram of directory relationships shows innumerable directories spreading out from one main "root" directory, much like the trunk and branches extending from the root system of a tree. If you are comfortable with the inverted tree directory organization of DOS, you've got a good picture of the organization of files under UNIX. The primary difference in the organization of the typical UNIX directory structure is the large number of directories that tend to come into existence on a multitasking, multiuser system. Figure 1.2 shows a portion of a typical UNIX directory structure.

If files are contained in directories, to specify a file you must specify not only the name of the file but the directory path to get to the file. UNIX directories in a directory path are separated by the / character, with a / character at the beginning of the filename indicating the highest-level (or root) directory. For example, suppose the file usernames.txt is located in a directory russell, which is itself contained in the higher-level directory users. The full name of this file, then, would be /users/russell/usernames.txt. Of course it would be cumbersome to specify a full path each time you wanted to work with any file on the system, and the UNIX shells (the programs that interact with the user to process user commands) provide a number of shortcuts that greatly reduce the need for you to specify file paths when referencing files and directories from the UNIX command line. One common example of such a technique is to first use the cd command to move to a particular directory, and then to refer to all files contained in that directory by leaving out the path specification altogether (in such cases it's understood that the path is the current directory).

There are some standard directories that are common to most UNIX systems. Here are some of these common directories:

/dev
Contains the special device files through which all input and output are controlled
/users
Contains the home directories" user accounts (the directories into which users are deposited after successfully logging in)
/etc
Contains system configuration programs and files
/tmp
A directory reserved for various temporary files, including temporary files created by users
/usr
A directory under which third-party software is often installed
Note that these directories may themselves contain subdirectories that enable you to further organize files. For example, the /users directory would contain either a subdirectory for each user account (which would be the home directory for that user) or subdirectories for identifiable groups of users that would, in turn, contain the home directories for those users. The /dev directory might contain individual device files, subdirectories for device types that would contain individual special device files, or both. Under the /usr directory you will often find the following standard subdirectories:
/usr/include
Contains system header files referenced by C programs as they are being compiled
/usr/local
Contains subdirectories that host programs and UNIX shell scripts that are of general interest to everyone on the system
/usr/contrib
A directory that contains subdirectories that are usually made available to system users to add programs that may be of general interest to everyone on the system
Other directories spring up at the discretion of users with appropriate privileges. (Anyone can create a directory, as long as he or she has write permissions for the parent directory.) Often the addition of new disk devices to a system will precipitate the creation of new directories branching below the root directory (for example, /usr2, /usr3, /usr4, and so on).

Files as Inodes

It is important to understand that UNIX files are ultimately not known by their names, but rather by their unique inode numbers, which are assigned to them by the operating system. In some UNIX text you'll see the terms "file" and "inode" used interchangeably. A directory is simply a special type of UNIX file that cross-references filenames with inode numbers. (See Figure 1.2.) When you open a file by name, the file is first looked up in the directory, the inode number is extracted, and the file is internally referenced by that number. Generally all this takes place in the background, but it is important for you to understand because it is the key to understanding the concept of file links in UNIX.

Inode Links

If a directory associates a filename with an inode number, it is conceivable that UNIX might allow multiple filenames to be associated with the same inode number, thereby allowing the same physical information to be accessed and manipulated through two or more different physical filenames (possibly in different directories). UNIX supports this through the creation of links via the ln command. One use for this might be a situation in which data entry personnel are updating their files in their own home directories, and you would like these files to exist also in a central directory to facilitate nightly backups (the entire directory could be backed up each night, rather than having a collection of files distributed throughout the file system). Another use might be to create a link to a file in some remote area of the file system so that you could enter a brief filename to access the file rather than having to repeatedly specify a lengthy filename (including the path).

Each inode tracks how many links have been established to it so that only when all directory entries referencing the inode have been deleted is the actual inode data deleted.

Note that system information about a file is stored as part of the inode data and not within the directory. The directory only associates a name to an inode.

Symbolic Links

With System V, Release 4, UNIX began support for a special type of file known as a symbolic link. A symbolic link is a special file that actually contains the name of another file or directory anywhere on the system (even on a different disk device). When the symbolic link was created, a file attribute was set, telling the system that when the file is opened it should actually be opened only to read the name of another file or directory, and that this file or directory is actually being opened for work. Symbolic links are particularly handy for enabling files and entire branches of a directory structure to be transplanted to new disk drives, and yet enabling them to be accessed by programs just as if they had never been relocated. The use of symbolic links carries with it the additional cost of having to go through this redirection process before a file can actually be opened for work. Most often this additional overhead is a negligible percentage of the time spent processing the file.

Suppose that your system has recently had a second fixed disk installed, and the original fixed disk is reaching capacity. Of course you could use various methods to copy an entire directory from the old disk to the new disk, but that directory would then be located within a new base directory, and all programs that referenced the old directory would suddenly require adjustments that would cost a significant amount of time (and entail a certain amount of risk, because you may not be aware of all references to the original directory). Symbolic links address this problem. After the new directory has been created and copied on the new file system, the old directory can be deleted and a symbolic link with the name of the old directory can be created to point to the new directory on the new device.

One approach to performing this operation is to use cpio to copy the current directory structure to a new device, and then to delete the entire directory structure and replace it with a symbolic link that points to the new home for the file set. Suppose, for example, that you used this technique to move an installed product (maintained under the directory /usr/product) to the directory /newdir, which is resident on a different disk device. The UNIX command sequence to perform this is the following:

$ cd /usr
$ find product -print | cpio -pmd /newdir
$ rm -r -f product
$ ln -s /newdir/product product
Line 2 uses the cpio command (described in Chapter 7, "Backup and Recovery") to copy the product directory to an identical directory under the /newdir directory. The command-line switches -pmd, supplied to the cpio command, tell cpio to copy from one file system to another (-p), to preserve the modification times of files as they were before they were backed up (-m), and to create new directories as needed (-d). Line 3 removes all files and directories in the product subdirectory and below (including the product subdirectory itself). The ln -s command creates a new symbolic link, product, to the newly created /newdir/product directory. Now you've moved an entire directory tree to a new disk, and applications continue to run unaffected.

UNIX Device Files

As discussed earlier, computer programs are stored as files in the UNIX file system. A program is simply a file that contains instructions to be executed by the central processing unit (CPU). Device files are special types of program files that are utilized to work with I/O devices on the system. These files actually contain no conventional data, but rather maintain in their inode entries numeric data (a device major number and a minor number) that identifies to the kernel the type of device represented and how it may be accessed. One set of instructions opens and initializes a device for work, the second set performs data input from the device, the third sends output to the device, and the fourth closes the device and makes it available for others to use.

By their very nature, some devices communicate best with blocks of data and other devices operate better on one character at a time. For example, if a terminal did not communicate one character at a time, you could not see each keystroke echoed to the screen as you type, and you could not produce applications that perform operations based on single-user keystrokes. At the same time, disk drives tend to perform much better when they process blocks of data. For this reason some special device files may operate in character mode and some may operate in block mode.

Under UNIX you will often find multiple device files set up to support a single device on the system, depending on how the device is being utilized. For example, a terminal device file may configure a hardware port for I/O to a terminal as part of the device "open" logic (establish baud rate, parity, and so on), whereas an entirely separate device file may contain open logic that not only initializes the port but assumes that a modem is connected to the port and sends out a modem initialization command. The "close" logic for such a device file might send the command string out to the modem to ensure that the communications connection has been broken.

Another example of multiple device files for a single device is device files used to communicate with a tape drive. One device file may put the tape drive into compressed mode as part of opening the device for output, whereas another might not; or perhaps one command would cause the tape to rewind as part of the open logic, whereas another device file associated with the tape drive might simply begin recording at the current head position on the tape. You will find that virtually all special device files under UNIX appear immediately under the /dev directory or in a subdirectory thereof. You will find that the name of the special device file often reflects the options it implements, as well as whether it operates in character mode or in block mode (consult the vendor's documentation for details, because these conventions vary).

How Files Are Represented by UNIX

The UNIX commands for generating directory listings of files are sensitive to the file types, and can reflect in their output whether each file listed is a regular file, a directory, a symbolic link, a character-mode special file, or a block-mode special file. If a file listed by one of these commands has multiple links (not symbolic links), the number of links may also be printed. Note that each file has an owner and a group, as well as a set of permission bits used to govern who can access the file and in what manner. These file attributes are covered in the following sections of this chapter and in greater detail in Chapter 3, "UNIX File System Security." Listing 1.1 is a directory listing of a file maintained under a System V, Release 4 implementation of UNIX.

Listing 1.1. A sample UNIX directory listing.


$ls -alF
lrwxrwx--x 1 root sys 23 Apr 5 14:18 bin -> /usr3/bin drwxrwxrwx 2 root sys 24 Feb 7 14:03 cfg -rw-rw-rw- 1 john adm 530 Dec 17 17:35 cmbu0101.tmpl -rwxr-xr-x 1 john users 33412 Oct 21 13:39 login.sql -rw-rw--" 1 mike users 400 Apr 6 14:26 serial.dat -rw-rw-rw- 1 allen users 27912 Jan 5 17:58 sqlnet.log -rwxr-xr-x 1 john users 918 Aug 3 1993 sscurq03.sql drwxrwxrwx 2 root sys 1024 Apr 6 10:58 com lrwxrwx--x 1 root sys 23 Apr 5 14:19 etc -> /usr3/etc drwxrwxrwx 2 root sys 24 Feb 7 14:03 exp lrwxrwx--x 1 root sys 23 Apr 5 14:19 fil -> /usr3/fil drwxrwxrwx 2 root sys 24 Feb 7 14:03 log drwxrwxrwx 2 root sys 24 Feb 7 14:03 msc crw-r--" 1 root sys 69 0x000000 Jun 22 1993 config crw--w--w- 1 root sys 0 0x000000 Apr 6 16:22 console brw----" 1 root root 7 0x000102 Jun 22 1993 src_device $
Notice that the first field of each line looks something like drwxrw-rw-. The first character of this field indicates the type of file being listed. A - indicates a regular file (login.sql), a d indicates a directory (cfg), an l indicates a symbolic link (etc -> /usr3/etc), a c indicates a character-mode device file, a b indicates a block- mode device file, and a p indicates a special pipe file sometimes used to store data communications between processes. The remaining nine characters of this field indicate the read, write, and execute permissions granted to the owner, the group, and all others, respectively. (A - indicates that the permission is not allowed.) The second and third fields give the username and group name, respectively (although if the user and group are undefined, the user and group numbers are printed). The remainder of each line indicates the time and date the file was created and the name of the file. Note that the file pointed to by symbolic links is indicated with the -> notation.

Users and Groups in UNIX

UNIX is a multiuser operating system, which means that a number of users can be doing work on a UNIX system simultaneously. UNIX also allows users to be assigned to one or more user groups on the system. When a user is working under UNIX, the operating system determines how the user may access a particular file through the user's ID; the user's group ID; the read, write, and execute access permissions that have been assigned to the file; and the read and execute permissions assigned to each directory in the file's full path. This is one of the fundamental cornerstones of UNIX security.

As you might suspect, UNIX tracks certain information about each user who has been set up to log in to the system. Critical user information is kept in the file /etc/passwd. This is a normal text file that can be edited, viewed, or printed by anyone with the proper access permissions (generally only the root user has the ability to actually modify this file). UNIX maintains one line per user, each containing several items of user information, separated by the : character. Information kept in this file includes the user's name, the user's password, the user's ID number (internally, UNIX likes to track everything, including users, by number rather than by name), the user's default group ID number, the name of the directory into which the user initially logs in to the system, and the name of the UNIX shell that the user runs when logging in to the system. (As described later in this chapter, the shell is the program that allows the user to execute commands from a system prompt.) Provisions are also made to track other miscellaneous user information. Because this file is often readable by everyone on the system, the password is encrypted. See Listing 1.2 for a depiction of a typical /etc/passwd file.

Listing 1.2. A sample /etc/passwd file.


$cat /etc/passwd
root:fRBdpqobjKT1Y:0:3::/:/bin/ksh
daemon:*:1:5::/:/bin/ksh
bin:*:2:2::/bin:/bin/ksh
adm:*:4:4::/usr/adm:/bin/ksh
uucp:*:5:3::/usr/spool/uucppublic:/usr/lib/uucp/uucico
lp:*:9:7::/usr/spool/lp:/bin/ksh
james:i5QiQ1K9pNml6:201:20:John James:/users/james:/bin/ksh
rich:cE78tWjuM9ER.:202:20:Richard Styles:/users/rich:/bin/ksh
watts:ZETejgJiU:203:20:Tim Watts,x2311:/users/watts:/bin/ksh
hanson:1yzkc2tvo:204:20:Mickie Hanson:/users/hanson:/bin/ksh
oswald:5uzfMDRwUw:205:20:Donald Oswald:/users/oswald:/bin/ksh
$
The /etc/passwd file must be made readable for everyone on the system for various common UNIX commands to execute properly. For example, the ls command that produces directory listings reads this file to translate user IDs of files into the corresponding usernames. The su command must be able to find the user ID number based on the name of the user, which also requires /etc/passwd to be readable. Because this file also contains the encrypted passwords for all users, you might correctly suspect that the availability of this file represents a security risk. A persistent password cracker might encrypt a large number of common passwords and compare them to the passwords in /etc/passwd. In fact, there are a few programs that are designed to perform this very act, and they invariably decipher poorly chosen passwords. As of System V, Release 2, this problem has been addressed by placing encrypted passwords in a separate file named /etc/shadow. This file is protected such that it can be accessed only by the root user, or by the login process to verify users" passwords as they log in. Under such systems you generally see the field of the /etc/passwd file that previously contained the encrypted password set to a single placeholder character (such as *).


The /etc/shadow file is required to upgrade the standard UNIX operating system from a C1 classification, as described by the Orange Book, to a higher classification, C2. (See Appendix E, "The Orange Book Guidelines for Computer Security.") Most UNIX systems do not install by default with the /etc/shadow file in place, but rather require that you execute a command that explicitly (and often irreversibly) upgrades the system. This upgrade not only creates the /etc/shadow file, but replaces numerous system programs (such as the login program) with programs that have been modified to reference the /etc/shadow file when checking login passwords.
Some implementations of UNIX make password shadowing an optional security feature. Occasionally the shadow password file will be maintained in some file other than /etc/shadow. One interesting thing to note about the encrypted password is that this field may be set to certain values that are never the result of the password encryption process, such as the * character. Because this character will never appear in an encrypted password, placing the * in an encrypted password field (in either /etc/passwd or /etc/shadow) is a quick way to deactivate an account without actually deleting it. No one will ever be able to log in while the * character is in place. Another security-related feature of the encrypted password field is that it can be modified to implement automatic password aging (which is discussed in detail in Chapter 2, "Controlling Access to UNIX"). EM>Password aging is the process by which passwords automatically expire after a specified period.

Listing 1.3 is a listing of a typical /etc/shadow file. The first field in this file is the user's name. The second field is the encrypted password. The third field is the day the password was last changed (stored as the number of days since January 1, 1970). The fourth field is the minimum number of days required to elapse before the password may be changed (to prevent a user from changing a password to a new value and immediately changing it back to the old password to defeat the password-aging protection). The fifth field is the maximum number of days that may elapse before the user is required to change the password. The sixth field is the number of days during which the user will be warned to change the password before the password expires. The seventh field is the number of days of account inactivity allowed before the account is automatically disabled. The eighth field specifies the date when the account may no longer be used (expressed as the number of days since January 1, 1970). The ninth field of the /etc/shadow file is currently unused. Leaving any field of this file empty disables the corresponding security check.

Listing 1.3. A sample listing of the /etc/shadow file.


$cat /etc/shadow
root:fRBdpqobjKT1Y:8002:1:30:3:10:::
daemon:*:8049:::::::
bin:*:8027:::::::
adm:*:8030:::::::
uucp:*:8012:::::::
lp:*:8023::::::
james:i5QiQ1K9pNml6:8023:1:30:3:10:8200:
rich:cE78tWjuM9ER.:8017:1:30:3:10:8200:
wyatt:ZETejgJiU:8010;1:30:3:10:8200:
benson:1yzkc2tvo:8019:1:30:3:10:8200:
riswald:5uzfMDRwUw:8015:1:30:3:10:8200:


UNIX also maintains an /etc/group file that identifies all the user groups defined for the system and lists all the users associated with each group. A user can be associated with any number of groups, although the group number appearing in the /etc/passwd file will be the user's group ID when the user initially logs in to the system. As in the /etc/passwd file, the fields in this file also are separated by the : character. This file contains the name of the group, an optional encrypted password for the group, the group ID number, and a list of the UNIX usernames of those users who can be associated with the group. See Listing 1.4 for a depiction of a typical /etc/group file.

Listing 1.4. An example of an /etc/group file.

$cat /etc/group
root::0:root
other::1:root,hpdb
bin::2:root,bin
sys::3:root,uucp
adm::4:root,adm,rbb,rjemgr
daemon::5:root,daemon
mail::6:root
lp::7:root,lp
users::20:root,james,rich,watts,hanson,oswald
nogroup:*:-2:
$

It would be wise to modify the file access permissions on the /etc/passwd and /etc/group files carefully, because some third-party software uses the information contained in these files for internal purposes. For example, modifying the /etc/passwd file has been known to cause some database engines to hang up or simply fail to process data requests.
You can execute the newgrp command to switch from your current group to any other group with which you are associated in the /etc/group file. If an encrypted password exists for that group, you are prompted for the proper password before your group ID is changed. In the vast majority of UNIX systems a user is assigned to a single group when added to the system and never deviates from that group. Furthermore, it is not uncommon to find all the users on a system assigned to the same group. This implies that UNIX security policies are often broad enough to be worthy of concern, because the group mechanism for file system control is seldom exploited. As machines begin to commonly host more than a single application, the security features associated with user groups should be increasingly utilized. For example, if your UNIX system serves as a host for the projects on billing and inventory, you might define the groups billng and invntry in your /etc/group file. Now, rather than assigning all users to the generic group users, you might assign them to one of these new groups. Ownerships and access permissions for files associated with these distinct groups could then be set accordingly.

Every file created by a user logged in to UNIX is assigned that user's numeric user and group IDs as the owner and group IDs of the file itself. It is also assigned a default set of access permission flags. These default permissions are generally established as system defaults when the user logs in, but may be changed by the user at any time through execution of the standard UNIX umask command. (See Chapter 5, "User Account Security.")

On virtually all UNIX systems the standard file permission flags form the heart and soul of processing control security. (Keep in mind that access control is used in this book to mean the set of mechanisms put in place to limit who can access the system, and process control is used in this book to mean the set of mechanisms put into place to limit user activity when access has been attained.) Each file has read, write, and execute permissions that are granted separately to the user who created the file, the group under which the file was created, and others. Only the owner of a file can change the file permission flags as well as the user and the group associated with the file.

The three file access permissions, which may be granted or revoked by the owner of the file, are read permission, write permission, and execute permission. Other file attribute flags also associated with UNIX files are covered later. Broadly stated, when a user attempts to perform an operation on a file, the user's user ID and group ID are compared to the ownership and permissions associated with the file, and the operating system either accepts or denies the operation. Because I/O devices are also controlled through device special files, and because directories are also simply a special type of UNIX file, these permission flags can be used to control a user's total access to the system. By appropriately setting the permission flags for a directory, you can protect every file beneath that directory.

Modern security standards defined by the Department of Defense in the manuscript "Department of Defense Trusted Computer System Evaluation Criteria" (known popularly as the "Orange Book") describe security requirements that are more strict than the capabilities provided by the file access permissions described in this chapter. See Appendix E for a full summary of the requirements outlined in the Orange Book. To achieve higher levels of security classification under this standard, major UNIX vendors have augmented the standard UNIX security with the implementation of access control lists (ACLs), which have been utilized successfully in other computing environments.

The mechanics of the standard UNIX file permissions and ACLs, as well as the UNIX commands that manipulate them, are described in detail in Chapter 3.

More on UNIX Processes

As mentioned earlier, processes are simply programs that are running under UNIX. Programs exist as executable files in the file system, and through various means are loaded into memory and executed. This is not to say that these programs are simply brought up to run with total command of the system; rather, UNIX initializes an internal data structure for each process that begins to run, allots execution time to processes, and generally regulates and monitors them from start to finish. As a process is started it is assigned a process number. UNIX maintains an internal counter that starts at 1 (always assigned to the init process that starts on reboot), increments as high as an implementation-specific limit (generally 32767) as new numbers are assigned to new processes, and recycles back to 1. UNIX is smart enough to guarantee that the same process number never gets assigned to more than one running process.

The init process can be considered the ancestor of all processes that run under UNIX. This is because, directly or indirectly, all processes can trace their roots to a process started by init. When this program (resident in the file /etc/init) runs, it scans a special file on the system, named /etc/inittab. Each line of this file contains the command to be executed, how it is to be handled, and in which run state the command is valid (run states are discussed in greater detail later in this chapter). For example, all the system getty processes (the processes that allow a user to log in over a serial line or via modem) are specified to run in the /etc/inittab file. All processes started by init are run with an owner ID of root. Hence, there are some very significant security considerations associated with the init process. Only the root user should have write permission for the /etc/inittab file, and all processes run directly or indirectly from the inittab file should also be protected. Listing 1.5 shows the contents of a typical inittab file.

Listing 1.5. A sample /etc/inittab file.


$cat /etc/inittab
i2:2:initdefault:
io:s:sysinit:/etc/ioinit -i >/dev/console 2>&1
br::wait:/etc/rc.single >/dev/console 2>&1
rc:2:wait:/etc/rc </dev/console >/dev/console 2>&1
po::wait:/etc/powerfail >/dev/console 2>&1
co::respawn:/etc/getty console console
a0:2:respawn:/etc/getty -h ttyd0p1 2400
$


Each line of the inittab file contains several fields, separated by : characters (a convention you'll find to be common with UNIX system files). The first field is a one- to two-character, unique identifier for the line. (Some UNIX vendors allow this field to be larger.)

The second field indicates the run states for which this process is allowed to exist. Valid run states are 0, 1, 2, 3, 4, 5, 6, and s, with state 0 often being reserved by the manufacturer, states 1 and s being single-user states in which only the minimum number of processes required to run the file system are started and logins are often allowed only from the system console, state 2 being the standard multiuser state, and other states being defined to support alternate system configurations. (For example, it's not uncommon to find state 3 defined as the networked multiuser state.) If this second field is left empty, then the process is defined for all run states.

The third field tells UNIX how to handle the action being performed. The following standard contents are recognized for field three of the /etc/inittab file:

initdefault
This indicates that the run level of field two is to be the system default run level entered whenever the system reboots.
sysinit
This indicates that the process is to be executed before anything is written to the system console.
respawn
This indicates that the process is to be started if it does not already exist, and that it should be restarted if it ever terminates.
wait
This indicates that the initialization process is to halt during initialization and await the completion of the process specified in field four of this /etc/inittab line.
off
This indicates that whenever the system goes into the designated run level(s), the specified process must always be killed.
As mentioned previously, the fourth field is the actual command line to be executed by init.

In Listing 1.5 the two shell scripts /etc/rc.single and /etc/rc are executed as part of the system initialization process. In this case, /etc/rc.single would execute as part of an init into single-user state and /etc/rc would execute as part of an init into multiuser state. It is not uncommon to find a number of rc files in the /etc directory that are responsible for starting various software subsystems. Sometimes these rc files are referenced in the /etc/inittab file and are run directly by root, and sometimes these files are evoked from a shell program referenced in inittab. These files are all very important from a security perspective because they are all run as root by the init process. Hence, the ownership and file access permissions of these files are critical to UNIX system security.


Some UNIX vendors speak in terms of run levels rather than run states. These two terms have the same meaning.

Often when you perform delicate tasks such as major software or hardware installations you'll find it desirable and sometimes necessary to perform the work on a "quiet" system (a system in which no user processes are running, users are prohibited from logging in to the system, and no unnecessary processes are allowed to execute). To bring the system into single-user state the root user can execute the init s command, which terminates all processing and brings up only those processes designated to run in single-user state. After the work is complete the init 2 command brings the system back into normal multiuser state.
UNIX maintains the following information for each process running on the system: Processes generally have the real and effective user and group IDs of the user who initiated the process. Under certain circumstances, however, the effective user and group IDs of a process can be changed to those of the file being executed. This enables you to write a program that has all your privileges while it executes, which provides a means of providing controlled access to critical data. This is discussed in greater detail later in this chapter.

The UNIX system call kill enables processes to send various types of simple signals to one another. A signal is a message sent to the process to inform it that some external event has taken place. This is just one way processes can communicate with one another in the UNIX environment. A standard UNIX command, kill, is also defined. It provides the user with a means of sending signals to processes from the UNIX command line.

Along with the process ID that is maintained for each running process, the process ID of the parent process (the process that initiated the process) is also maintained by UNIX. Whenever a process terminates it sends a signal to its parent, saying that it has terminated. Another item of information stored for each process is the process group to which it belongs. In the case of some major signals (for example, signals telling a process to terminate unconditionally) the signal is sent to every process in that process's group. In this fashion, the termination of a user's main process (for example, the login session) can cause all associated processes to terminate also. Note that a process group is not related to user groups. A process group is a set of processes associated with a terminal through a common login session.

The internal process table also tracks the UNIX user ID and group ID of the owner of the process. This is extremely important because UNIX uses this information, coupled with the effective owner ID, group ID, and protections of a file, to determine what types of file operations to allow a process to perform on a file. Therefore, one user may execute a program that prints out the contents of a file, while some other user may execute the same program in the same manner, but fail due to file permission restrictions.

As you might guess from this discussion of processes having parent processes, there is a hierarchic relationship for all processes running under UNIX, and ultimately all processes may be tracked back to a common ancestor, the init process.

The analogy of process relationships to family relationships extends beyond the notion that processes have parent and child processes to the inheritance of the process environment. The process environment consists of a set of environment variables, maintained for each process, which provide a means for the user to tailor the behavior of various commands and programs. These environment variables contain text strings that are referenced by commands and programs. A good example of a UNIX environment variable is the HOME environment variable, which always contains the name of the directory that a user logs in to. If a program has been written that always prints the contents of the file userlist in the directory pointed to by the HOME environment variable, the behavior of that program depends on the user executing it. Proper use of UNIX environment variables is critical for programs that need to execute in a variety of contexts, such as on different machines that have unknown directory structures or on a single machine that must maintain multiple runtime environments. A process always gets a copy of its parent's environment, which it is then free to modify and expand in accordance with its own programming. With the Bourne and Korn shells you can utilize the export command to define environment variables. Under the C shell the setenv command serves the same purpose.

The following are some common UNIX environment variables:

TERM
Definition of the type of terminal you are logged in to for programs that require this information to manage the screen properly
SHELL
The shell program you are currently using
LOGNAME
Your UNIX username
HOME
The directory you log in to
PATH
A series of directories, separated by semicolons, to be searched for by commands that you issue from the command line
TZ
Systemwide time zone information

UNIX Daemon Processes

As mentioned earlier, a number of processes are automatically started whenever the system changes init states (such as during the system boot process), and are continuously running in the background. These programs are referred to as daemon processes and are designed to provide various services that don't require user interaction. Other daemons may be started at any time (those that start at boot time are of most concern because they run as the root user). From a security standpoint it is important to be aware of the existence of daemon processes, because it is through these processes that users can gain indirect access to your system(s).

Examples of UNIX daemons include the ftp daemon, which allows files to be transferred between UNIX systems; the mail daemon, which routes electronic messages between UNIX systems; the cron process, which runs user programs according to a programmed schedule; the spooling process, which services system printers; database engines, which are continuously monitoring for data requests; and other daemons that are required to support third-party software as well as user-written applications.

Daemon processes, like any other processes, have real and effective user and group IDs, and like any other process they are subject to the user and group ownership and permission access restrictions of UNIX. Because many daemons are started by the init process, or from one of the rc files executed by init, they have a user ID of root, giving them unlimited access to the system. Note, however, that daemons are programmed to provide only a certain group of behaviors, and so are often limited in the impact they may have on the file system.

How Users Are Logged In to UNIX

As mentioned earlier, the init process is always the first process to execute as a UNIX system reboots. This process scans the /etc/inittab file and starts all necessary system processes and daemons. Among these processes are the getty (or get tty) processes that allow you to log in to terminals.

One getty process is started for each communication port attached to a terminal (or at least for those terminals you wish to make available for login). Additionally, the init process executes the /etc/rc general-purpose startup script, which starts up all network daemons that support logins across the network. getty processes support only physical communication ports and are not used extensively in heavily networked systems, although the getty process is still required for console and modem connections.

After you enter your username and password, the login process encrypts the password and compares it to your encrypted password in the /etc/passwd file (or your /etc/shadow file if C2 security has been established for the system). If the password test succeeds, the system transfers execution to the UNIX shell program specified in the /etc/passwd file and you are deposited in the login directory also specified in the /etc/passwd file. After a successful login, the shell program that is executed on behalf of the user will be owned by the user, with a group ID set to the user's default group ID. Your UNIX shell executes its own initialization sequence before it presents you with a prompt. One major part of this process is executing startup scripts (for the Korn shell, this involves processing the shell script /etc/profile, followed by the .profile script contained in the user's home directory). At this point you may utilize the full capabilities of your shell to do work (subject to the constraints of your user ID with regard to file access permissions established systemwide).

The UNIX Shells

The UNIX shells are the programs through which most users interact with the operating system. These shell programs accept user commands and execute them. Because these shell programs understand commands for looping and branching, and because shell commands can be executed from files as well as from the command line, the shell command set is actually a programming language.

You may generally choose to use one of the common UNIX shells. The full file path to your default shell is specified in your account line in the /etc/passwd file. As the shells begin to execute they generally process one or more startup scripts that define aliases for UNIX commands and establish environment variables for the session. These startup scripts serve much the same purpose as the AUTOEXEC.BAT file that runs when you turn on a PC, in that they perform initialization for a user when a login session is started.

In production environments it is not uncommon to establish users in captive accounts, in which a particular program is run from within the shell's initialization script and the user is logged out after exiting the program. In such situations the user never actually has access to the shell prompt to enter UNIX commands. In other situations, you may wish to tailor the user's environment to a particular specification whenever that user logs in, and you may do so by customizing the login script and setting the ownership and file permission attributes of the script to prohibit unauthorized personnel from modifying or deleting the file.

The most common shell programs in the UNIX world are the Bourne shell (sh), the C shell (csh), the Korn shell (ksh), and the restricted shell (rsh). The original UNIX shell was the Bourne shell. Early on it was recognized that this shell could be improved substantially, and the C shell was developed. This shell added some powerful capabilities to re-execute previously executed commands, as well as some more powerful programming capabilities that resembled those of the C language. Later, the Korn shell was developed. The Korn shell is a superset of the Bourne shell and includes many of the capabilities that had made the C shell desirable. The Korn shell carries the added benefit of being compliant with the generic UNIX standard known as POSIX. Many organizations have since adopted the Korn shell as a standard.

The restricted shell provides the system administrator with the means to provide users with a strictly limited environment. Users cannot execute commands that are not found in their PATH environment variable, users are restricted from changing their PATH environment variable, and no directory other than the user's login directory can appear in any command entered. The ultimate effect of all these restrictions is that the user can execute only those UNIX commands explicitly authorized by the system administrator. This is covered in greater detail in Chapter 5.

The configuration of the shell environment, and the specific security characteristics of the various UNIX shells, is also discussed in detail in Chapter 5.

Program Scheduling

Like many operating systems, UNIX provides facilities for scheduling programs that are to be run at a particular time of day. UNIX provides cron for executing programs repeatedly in accordance with a fixed schedule and the at command to cause a command to be executed one time only, at some specified time. The cron scheduler is started by the init process at system boot times, and each minute it comes up, scans its schedule, and executes all tasks scheduled for that time.

Program scheduling is a system security issues for several reasons. One reason is that CPU power is an important system resource, and the execution of unexpected programs may drain CPU power during times when it is anticipated to be most available. Another reason is that some programs are interdependent; for example, a database application might be dependent on the database engine daemon being up and running. At the same time a system backup may require a database engine daemon to be down to guarantee data integrity. Additionally, the cron and at commands could provide a disruptive user with a mechanism for setting off a time bomb, which is a destructive program scheduled to run if not stopped before a specific time. Hence, access to UNIX scheduling facilities must be controlled.

The mechanisms for controlling user access to the at and cron commands are detailed in Chapter 4, "Monitoring and Controlling Processes."

Summary

This chapter provides an overview of UNIX, paying particular attention to the characteristics and features of UNIX that would be of interest to anyone involved in UNIX security issues.

Files, directories, and processes are the three fundamental parts of UNIX. Files can be described as collections of related information stored by UNIX as a single, named entity. This chapter covers user and group ownership of files, as well as the read, write, and execute permissions that are assigned to files. It also describes the organization of the UNIX file system into an inverted tree structure of directories that themselves contain subdirectories (which may then contain their own subdirectories, ad infinitum). This chapter covers the distinction between inodes and files, along with the allocation of file attributes at the inode level rather than at the directory level.

Full filenames consist of a directory path, a filename, and an optional extension. In this chapter you have learned about the concept of hard links and symbolic links, and you have seen examples of scenarios in which these mechanisms might be employed.

You have learned about processes and process inheritance, including the function of the init process and the form and function of the /etc/inittab file. You know about system run states, particularly regarding the contents of the /etc/inittab file. You can use the init process to evoke the /etc/rc file to perform system initialization as well as to start getty processes to allow user logins.

You have learned about users and groups, as well as the login process, the /etc/passwd file, and the shadow password file for systems that implement C2 security. You can use UNIX shell programs to interact with the UNIX file system through a command-line interface.

This chapter describes UNIX processes and how they relate to users, groups, and files. You can use cron to schedule processes for periodic execution.