|
Chapter 23 Writing Secure SUID and Network Programs
|
|
If you are writing programs that are
SUID
or
SGID
, you must take
added precautions in your programming.
An
overwhelming
number of
UNIX
security
problems have been caused by SUID/SGID programs.
These
rules should be considered in addition to the previous list
.
Here are some rules for writing (and not writing)
SUID/SGID
programs:
-
"Don't do it. Most
of the time, it's not necessary."[11]
-
Avoid writing
SUID
shell scripts.
-
If you are using
SUID
to access
a special set of files, don't.
Instead, create
a special group for your files and make the program
SGID
to
that group
. If you must use
SUID
,
create a special user for the purpose.
-
If your program needs to perform some functions
as superuser, but generally does not require
SUID
permissions, consider putting the
SUID
part in
a different program, and constructing a carefully controlled and
monitored interface between the two.
-
If you need
SUID
or
SGID
permissions, use them for their intended purpose as early in the
program as possible, and then revoke them by returning the effective,
and real,
UIDS
and
GIDS
to
those of the process that invoked the program.
-
If you have a program that absolutely must run as
SUID
, try to avoid equipping the program with
a general-purpose interface that allows users to specify much in
the way of commands or options.
-
Erase the execution environment, if at all possible,
and start fresh.
Many security problems have been caused because there was a
significant difference between the environment in which the program
was run by an attacker and the environment under which the program was
developed. (See item 5 under
Section 23.2, "Tips on Avoiding Security-related Bugs
"
earlier in this chapter for more information about this
suggestion.)
-
If your program must spawn processes, use only the
execve(
)
,
execv( )
, or
execl(
)
calls, and use them with great care.
Avoid
the
execlp( )
and
execvp( )
calls because they use the
PATH
environment variable
to find an executable, and you might not run what you think you
are running.
-
If you must provide a shell escape, be sure to
setgid(getgid( ))
and
setuid(getuid( ))
before executing the user's
command.
-
In general, use the
setuid()
and
setgid()
functions to bracket the sections of your code which require superuser
privileges. For example:
setuid(0); /* Become superuser to open the master file */
fd = open("/etc/masterfile",O_RDONLY);
setuid(-1); /* Give up superuser for now */
if(fd<0) error_open(); /* Handle errors */
Not all versions of
UNIX
allow you to switch
UIDS
like this; however, most modern versions
do.
-
If you must use pipes or subshells,
be especially careful with the environment variables
PATH
and
IFS
.
If at all possible, erase these variables and set them
to safe values. For example:
putenv("PATH=/bin:/usr/bin:/usr/ucb");
putenv("IFS= \t\n");
Then, examine the
environment to be certain that there is only
one
instance of the
variable: the one you set. An attacker can run your code from another
program that creates multiple instances of an environment variable.
Without an explicit check, you may find the first instance, but
not the others; such a situation could result in problems later
on. In particular, step through the elements of the environment
yourself rather than depending on the library
getenv()
function.
-
Use the full pathname for all files that you open.
Do not make any assumptions about the current directory.
(You can enforce this requirement by doing a
chdir(/tmp/root/)
as one of the first steps in your program, but be sure
to check the return code!)
-
Consider
statically
linking your program, if possible.
If a user can substitute
a different module in a dynamic library, even carefully coded programs
are vulnerable. (We have some serious misgivings about the trend
in commercial systems towards completely shared, dynamic libraries.
See our comments in the section
Chapter 11,
Protecting Against Programmed Threats
.)
-
Consider using
perl -T
or
taintperl
for your
SUID
programs and scripts.
Perl's
tainting features make it more suited to
SUID
programming than C. For example,
taintperl
will
insist that you set the
PATH
environment variable
to a known "safe value" before calling
system().
The program will also require that you "untaint"
any variable that is input from the user before using it (or any
variable dependent on that variable) as an argument for opening
a file.
However, note that you can still get yourself
in a great deal of trouble with
taintperl
if
you circumvent its checks or you are careless in writing code. Also
note that using
taintperl
introduces dependence
on another large body of code working correctly: we'd suggest
you skip using
taintperl
if you believe you can
code at least as well as Larry Wall.[12]
If you are writing a
SUID
root
program,
you can enhance its security by using the
chroot()
system call. The
chroot()
call changes the root directory of a process to a specified subdirectory
within your filesystem. This change essentially gives the calling
process a private world from which it cannot escape.
For
example, if you have a program which only needs to listen to the
network and write into a log file that is stored in the directory
/usr/local/logs
, then you could execute
the following system call to restrict the program to that directory:
chroot("/usr/local/logs");
There are several issues that you must be aware of when using
the
chroot()
system call that are not immediately obvious:
-
If your operating system supports shared
libraries and you are able to statically link your program, you
should be sure that your program is statically linked. On some systems,
static linking is not possible. On these systems, you should make
certain that the necessary shared libraries are available within
the restricted directory (as copies).
-
You should not give users write access to the
chroot()
'ed
directory.
-
If you intend to log with
syslog()
, you
should call the
openlog()
function before executing the
chroot()
system call, or make sure that a
/dev/log
device file exists within the
chroot()
directory.
Note that under some versions of
UNIX
,
a user with a root shell and the ability to copy compiled code into
the
chroot'
d environment may be able to "break
out." Thus, don't put all your faith in this mechanism.
|
|