This chapter presents specific techniques for designing trusted programs.
SUID (set user ID) and SGID (set group ID) programs change the effective UID or GID of a process to the UID or GID of the program. They are a solution to the problem of providing controlled access to system-level files and directories, because they give a process the access rights of the files' owner.
The potential for security abuse is higher for
programs in which the user ID is set to
root
or the group ID is set to any group that provides write access to
system-level files.
Do not write a program that sets the user ID to
root
unless there is no other way to accomplish the task.
The
chown
system call automatically removes any SUID or SGID bits on a file, unless the
RUID of the executing process is set to zero.
This prevents the accidental creation of SUID or SGID programs owned by
the
root
account. For more information, see
chown(2).
The following list provides suggestions for creating more secure SUID and SGID programs:
access
system call.
When possible, create SGID programs rather than SUID programs. One reason is that file access is generally more restrictive for a group than for a user. If your SGID program is compromised, the restrictive file access reduces the range of actions available to the attacker.
Another reason is that it is easier to access files owned by the user executing the SGID program. When a user executes an SUID program, the original effective UID is no longer available for use for file access. However, when a user executes an SGID program, the user's primary GID is still available as part of the group access list. Therefore, the SGID process still has group access to the files that the user could access.
Most system calls and library routines return an integer return code,
which indicates the success or failure of the call.
Always check the return code to make sure that a routine succeeded. If
the call fails, test the global variable
errno
to find out why it failed.
The
errno
variable is set when an error occurs in a system call.
You can use
this value to obtain a more detailed description of the error
condition. This information can help the program decide how to
respond, or produce a more helpful diagnostic message.
This error code
corresponds to an error name in
<errno.h>.
For more information, see
errno(2).
The following
errno
values indicate a possible security breach:
If your program makes a privileged system call but the resulting executable program does not have superuser privilege, it will fail when it tries to execute the privileged system call. If the security administrator has set up the audit system to log failed attempts to execute privileged system calls, the failure will be audited.
If your program detects a possible security breach, do not have it
display a diagnostic message that could help an attacker defeat the program.
For instance, do not display a message that indicates the
program is about to exit because the attacker's real user ID (UID) did not match
a UID in an access file, or even worse, provide the name of
the access file.
Restrict this information by using the
audgen()
routine for SUID root programs and using
syslog
for other programs.
In addition, you could program a small delay before
issuing a message to prevent programmed attempts to penetrate your
program by systematically trying various inputs.
If your program uses any permanent files (for example, a database), make sure these files have restrictive permissions and that your program provides controlled access. These precautions also apply to shared memory segments, semaphores, and interprocess communication mechanisms; set restrictive permissions on all of these objects.
Programs sometimes create temporary files to store data while the program is running. Follow these precautions when you use temporary files:
umask()
system call at the beginning of the program.
/tmp.
The
/tmp,
directory has the sticky bit set
(mode 1777), so that
files in it can be deleted only by the file owner, the owner of the
directory, or the superuser.
A common practice is to create a temporary file, then unlink the file while it is still open. This limits access to any processes that had the file open before the unlink; when the processes exit, the inode is released.
Note that this use of
unlink
on an NFS-mounted
file system takes a slightly different action. The client
kernel renames the file and the unlink
is sent to NFS only when the process exits. You cannot guarantee that
the file will be inaccessible to someone else,
but you can be
reasonably sure that the file will be inaccessible when the process exits.
In any case, always explicitly ensure that no
temporary files remain after the process exits.
If you use the
popen,
system,
or
exec*p
routines, which execute
/bin/sh
or
/sbin/sh,
be careful when specifying a pathname or defining the shell
PATH
variable.
The
PATH
variable is a security-sensitive variable because it
specifies the search path for executing commands and scripts on your system.
For more information, see
environ(7),
popen(3),
and
system(3).
The following list describes how to create a secure search path:
PATH
variable.
exec*p
routines search the current working directory last.
To avoid having various
shells interpret this trailing colon in different ways, use the
decimal point rather than a null entry to reference the
current working directory.
You might want to use the
execve
system call rather than any of the
exec*p
routines because
execve
requires that you specify the pathname.
For more information, see
execve(2).
The Digital UNIX operating system generates signals in response to certain events. The event could be initiated by a user at a terminal (such as quit, interrupt, or stop), by a program error (such as a bus error), or by another program (such as kill).
By default, most signals terminate the receiving process; however, some signals only stop the receiving process. Many signals, such as SIGQUIT or SIGTRAP, write the core image to a file for debugging purposes. A core image file might contain sensitive information, such as passwords.
To protect sensitive information in core image files and protect programs from being interrupted by input from the keyboard, write programs that capture signals such as SIGQUIT, SIGTRAP, or SIGTSTP.
Use the
signal
routine to cause your process to change its response
to a signal.
This routine enables a process to ignore a signal or call a subroutine
when the signal is delivered.
(The SIGKILL and SIGSTOP signals cannot be caught, ignored, or blocked.
They are always passed to the receiving process.)
For more information, see
signal(3)
and
sigvec(2).
Also, be aware that child processes inherit the signal
mask that the parent process sets before calling
fork.
The
execve
system call resets all caught signals to the default action; ignored
signals remain ignored.
Therefore, be sure that processes handle signals appropriately before you
call
fork
or
execve.
For more information, see the
fork(2)
and
execve(2)
reference pages.
A child process can inherit all the open file descriptors of its parent process and therefore can have the same type of access to files. This relationship creates a security concern.
For example, suppose you write a set user ID (SUID) program that does the following:
Because the parent SUID process opens a file for writing, the child (or any user running the child process) can write to that sensitive file.
To protect sensitive, privileged files from users of a child process,
close all file descriptors that are not needed by the
child process before the child is created.
An efficient way to close file descriptors before creating a child
process is to use the
fcntl
system call. You can use this call to set the
close-on-exec
flag on the file after you open it. File descriptors that have this
flag set are automatically closed when the process
starts a new program with the
exec
system call.
For more information, see the
fcntl(2)
reference page.
The following sections discuss several ways to increase security in a DECwindows programming environment:
Users logged into hosts listed in the access control list can call the
XGrabKeyboard
function to take control of the keyboard.
When a client has called this function, the X server directs all keyboard
events only to that client.
Using this call, an attacker could grab the input stream from a
window and direct it to another window. The attacker could return
simulated keystrokes to the window to fool the user running the
window. Thus, the user might not realize that anything was wrong.
The ability of an attacker to capture a user's keystrokes threatens the confidentiality of the data stored on the workstation.
DECterm windows provide a secure keyboard mode that directs everything a user types at the workstation keyboard to a single, secure window. Users can set this mode by selecting the Secure Keyboard item from the Commands menu in a DECterm window.
Include a secure keyboard mode in programs that deal with sensitive data. This precaution is especially important if your program prompts a user for a password.
Some guidelines for implementing secure keyboard mode follow:
XGrabKeyboard
call to the
Xlib
library.
XUngrabKeyboard
function to release the keyboard grab when the user reduces the window
to an icon. Releasing the keyboard frees the user to direct
keystrokes to another window.
Hosts listed in the access control list can send events to any window
if they know its ID. The
XSendEvent
call enables the calling application to send keyboard or mouse events
to the specified window. An attacker could use this call to send
potentially destructive data to a window. For example, this data
could execute the
rm -rf *
command or use a text editor to change the
contents of a sensitive file. If the terminal was idle, a user might not
notice these commands being executed.
The ability of an attacker to send potentially destructive data to a workstation window threatens the integrity of the data stored on the workstation.
DECterm windows block keyboard and mouse events sent from another
client if the
allowSendEvents
resource is set to
False
in the
.Xdefaults
file.
You can write programs that block events sent from other clients.
The
XSendEvent
call sends an event to the specified window and sets
the
send_event
flag in the event structure to
True.
Test this flag for each keyboard and mouse event that your
program accepts.
If the flag is set to
False,
the event was initiated by the
keyboard and is safe to accept.
Device-related events, such as keyboard and mouse events, propagate upward from the source window to ancestor windows until one of the following conditions is met:
do-not-propagate
mask
You can use the
XReparentWindow
function to change the parent of a window. This call changes a window's
parent to another window on the same screen. All you need to know to
change a window's parent is the window ID. With the window ID of the child,
you can discover the window ID of its parent.
The misuse of the
XReparentWindow
call can threaten security in a windowing system.
The new parent window can select any event that the child
window does not select.
Take these precautions to protect against this type of abuse:
StructureNotify
or
SubstructureNotify
bit in the child window's event mask. For information on setting these
event masks, see the
X Window System: The Complete Reference to Xlib, X Protocol, ICCCM, XLFD.
When you write a shell script that handles sensitive data,
set and export the
PATH
variable
before writing the body of the script.
Do not make shell scripts SUID or SGID.