10.4. System Security Features
Unix security is a problem of legendary notoriety. Just about every aspect of a Unix system has some security issue associated with it, and it's usually the system administrator's job to worry about this issue.
NOTE: This is not a textbook on Unix system security. Be aware that this section merely touches the tip of the iceberg and that there are myriad other aspects to Unix system security besides how the shell is set up. See the end of the chapter for one book that we recommend.
We first present a list of "tips" for writing shell scripts that have a better chance of avoiding security problems. Next we cover the restricted shell, which attempts to put a straitjacket around the user's environment. Then we present the idea of a "trojan horse," and why such things should be avoided. Finally we discuss the Korn shell's privileged mode, which is used with shell scripts that run as if the user were root.
10.4.1. Tips for Secure Shell Scripts
Here are some tips for writing more secure shell scripts, courtesy of Professor Eugene (Gene) Spafford, the director of Purdue University's Center for Education and Research in Information Assurance and Security:
Chet Ramey, the maintainer of bash, offers the following prologue for use in shell scripts that need to be more secure:
# reset IFS, even though ksh doesn't import IFS from the environment, # $ENV could set it IFS=$' \t\n' # make sure unalias is not a function, since it's a regular builtin # unset is a special builtin, so it will be found before functions unset -f unalias # unset all aliases # quote unalias so it's not alias-expanded \unalias -a # make sure command is not a function, since it's a regular builtin # unset is a special builtin, so it will be found before functions unset -f command # get a reliable path prefix, handling case where getconf is not # available (not too necessary, since getconf is a ksh93 built-in) SYSPATH="$(command -p getconf PATH 2>/dev/null)" if [[ -z "$SYSPATH" ]]; then SYSPATH="/usr/bin:/bin" # pick your poison fi PATH="$SYSPATH:$PATH"
10.4.2. Restricted Shell
The restricted shell is designed to put the user into an environment where his or her ability to move around and write files is severely limited. It's usually used for guest accounts. When invoked as rksh (or with the -r option), ksh acts as a restricted shell. You can make a user's login shell restricted by putting the full pathname to rksh in the user's /etc/passwd entry. The ksh executable file must have a link to it named rksh for this to work.
The specific constraints imposed by the restricted shell disallow the user from doing the following:
These restrictions go into effect after the user's .profile and environment files are run. This means that the restricted shell user's entire environment is set up in .profile. This lets the system administrator configure the environment as she sees fit.
To keep the user from overwriting ~/.profile, it is not enough to make the file read-only by the user. Either the home directory should not be writable by the user, or the commands in ~/.profile should cd to a different directory.
Two common ways of setting up such environments are to set up a directory of "safe" commands and have that directory be the only one in PATH, and to set up a command menu from which the user can't escape without exiting the shell. In any case, make sure that there is no other shell in any directory listed in $PATH; otherwise the user can just run that shell and avoid the restrictions listed earlier.
10.4.3. Trojan Horses
The concept of a trojan horse was introduced briefly in Chapter 3. A trojan horse is something that looks harmless, or even useful, but which contains a hidden danger.
Consider the following scenario. User John Q. Programmer (login name jprog) is an excellent programmer and has quite a collection of personal programs in ~jprog/bin. This directory occurs first in the PATH variable in ~jprog/.profile. Since he is such a good programmer, management recently promoted him to system administrator.
This is a whole new field of endeavor, and John -- not knowing any better -- has unfortunately left his bin directory writable. Along comes W. M. Badguy, who creates the following shell script, named grep, in John's bin directory:
/bin/grep "$@" case $(whoami) in Check effective user ID name root) nasty stuff here Danger Will Robinson, danger! rm ~/jprog/bin/grep Hide the evidence ;; esac
In and of itself, this script can do no damage when jprog is working as himself. The problem comes when jprog uses the su(1) command. This command allows a regular user to "switch user" to a different user. By default, it allows a regular user to become root (as long as that user knows the password, of course). The problem is that normally, su uses whatever PATH it inherits. In this case, $PATH includes ~jprog/bin. Now, when jprog, working as root, runs grep, he actually executes the trojan horse version in his bin. This version runs the real grep, so jprog gets the results he expects. But it also silently executes the nasty stuff here part, as root. This means that Unix will let the script do anything it wants to. Anything. And to make things worse, by removing the trojan horse when it's done, there's no longer any evidence.
Writable bin directories open one door for trojan horses, as does having dot in PATH. Having writable shell scripts in any bin directory is another door. Just as you close and lock the doors of your house at night, you should make sure that you close any doors on your system!
10.4.4. Setuid and Privileged Mode
Many problems with Unix security hinge on a Unix file attribute called the setuid (set user ID) bit. This is like a permission bit (see the earlier discussion of umask): when an executable file has it turned on, the file runs with an effective user ID equal to the owner of the file. The effective user ID is distinct from the real user ID of the process, and Unix applies its permission tests to the process's effective user ID.
For example, suppose you've written a really nifty game program that keeps a private score file showing the top 15 players on your system. You don't want to make the score file world-writable, because anyone could just come along and edit the file to make themselves the high scorer. By making your game setuid to your user ID, the game program can update the file, which you own, but no one else can update it. (The game program can determine who ran it by looking at its real user ID and using that to determine the login name.)
The setuid facility is a nice feature for games and score files, but it becomes much more dangerous when used for root. Making programs setuid root lets administrators write programs that do certain things that require root privilege (e.g., configure printers) in a controlled way. To set a file's setuid bit, the superuser can type chmod 4755 filename; the 4 is the setuid bit.
A similar facility exists at the group level, known (not surprisingly) as setgid (set group ID). Use chmod 2755 filename to turn on setgid permissions. When you do an ls -l on a setuid or setgid file, the x in the permission mode is replaced with an s; for example, -rws--s--x for a file that is readable and writable by the owner, executable by everyone, and has both the setuid and setgid bits set (octal mode 6711).
Modern system administration wisdom says that creating setuid and setgid shell scripts is a very, very bad idea. This has been especially true under the C shell, because its .cshrc environment file introduces numerous opportunities for break-ins. In particular, there are multiple ways of tricking a setuid shell script into becoming an interactive shell with an effective user ID of root. This is about the best thing a cracker could hope for: the ability to run any command as root.
NOTE: There is an important difference between a setuid shell script and a setuid shell. The latter is a copy of the shell executable, which has been made to belong to root and had the setuid bit applied. In the previous section on trojan horses, suppose that the nasty stuff here was this code:cp /bin/ksh ~badguy/bin/myls chown root ~badguy/bin/myls chmod 4755 ~badguy/bin/myls
Privileged mode was designed to protect against setuid shell scripts. This is a set -o option (set -o privileged or set -p), but the shell enters it automatically whenever it executes a script whose setuid bit is set, i.e., when the effective user ID is different from the real user ID.
In privileged mode, when a setuid Korn shell script is invoked, the shell runs the file /etc/suid_profile. This file should be written so as to restrict setuid shell scripts in much the same way as the restricted shell does. At a minimum, it should make PATH read-only (typeset -r PATH or readonly PATH) and set it to one or more "safe" directories. Once again, this prevents any decoys from being invoked.
Since privileged mode is an option, it is possible to turn it off with the command set +o privileged (or set +p). But this doesn't help the potential system cracker: the shell automatically changes its effective user ID to be the same as the real user ID -- i.e., if you turn off privileged mode, you also turn off setuid.
In addition to privileged mode, ksh provides a special "agent" program, /etc/suid_exec, that runs setuid shell scripts (or shell scripts that are executable but not readable).
For this to work, the script should not start with #! /bin/ksh. When the program is invoked, ksh attempts to run the program as a regular binary executable. When the operating system fails to run the script (because it isn't binary, and because it doesn't have the name of an interpreter specified with #!), ksh realizes that it's a script and invokes /etc/suid_exec with the name of the script and its arguments. It also arranges to pass an authentication "token" to /etc/suid_exec indicating the real and effective user and group IDs of the script. /etc/suid_exec verifies that it is safe to run the script and then arranges to invoke ksh with the proper real and effective user and group IDs on the script.
Although the combination of privileged mode and /etc/suid_exec allows you to avoid many of the attacks on setuid scripts, actually writing scripts that can safely be run setuid is a difficult art, requiring a fair amount of knowledge and experience. It should be done very carefully.
In fact, the dangers of setuid and setgid shell scripts (at least for shells besides ksh) are so great that modern Unix systems, meaning both commercial Unix systems and freeware clones (4.4-BSD-derived and GNU/Linux), disable the setuid and setgid bits on shell scripts. Even if you apply the bits to the file, the operating system does not honor them.
Although setuid shell scripts don't work on modern systems, there are occasions where privileged mode is still useful. In particular, there is a widely used third party program named sudo, which, to quote the web page, "allows a system administrator to give certain users (or groups of users) the ability to run some (or all) commands as root or another user while logging the commands and arguments." The home page for sudo is http://www.courtesan.com/sudo/. A system administrator could easily execute sudo /bin/ksh -p in order to get a known environment for performing administrative tasks.
Copyright © 2003 O'Reilly & Associates. All rights reserved.