Readers: Thank you for your excellent feedback during this
past month. I received many letters in particular about the "Fastest
OS for Network Applications" article featured on the July Web
site. The letters are too numerous to reprint here, but many contained
similar points. Specifically, the FreeBSD users among you expressed
concern over the lack of kernel tuning done for the test. You made
your case so strongly that the authors agreed to retune and retest.
Their fresh results, and the readers' recommendations for proper
tuning appear on the Sys Admin
I also received a most useful letter providing additional tips
and outlining inaccuracies in the "Dangers of SUID Shell Scripts"
article (June issue). It appears below.
Thanks again for your support of the magazine. Write to me
with your comments at: firstname.lastname@example.org.
Subject: Dangers of SUID Shell Scripts
I read the article "Danger of SUID Shell Scripts" in
the June issue of Sys Admin. While there are many pitfalls
in writing SUID shell scripts, some of which you pointed out, the
article contains several inaccuracies and makes it seems as if it
is impossible to write SUID shell scripts that are secure. Writing
SUID scripts in any language requires a great deal of understanding
and should not be done without a complete understanding of the issues.
As far as csh is concerned, I agree with you. It is not an appropriate
shell for writing any script, let alone, SUID scripts.
First, let me describe the inaccuracies. You state that if you
invoke the script as
change-pass "user;cp /bin/sh /tmp/sh;chown root /tmp/sh;chmod 4755 /tmp/sh"
that the shell will run the cp command and create a SUID shell
program. No shell that I am aware of would do this. Shell syntax is
checked before parameter expansion, and the results of expansions
are not automatically re-evaluated. Perhaps, you are thinking of the
effects of eval. Your warning about the effects of special
characters would make sense if you were describing to C programmers
the danger of calling system() from a SUID program. The system("string")
call invoked sh -c "string" and special characters in string
would be processed by the shell grammar.
A second error relates to the warnings about IFS. ksh, and POSIX-conforming
shells, will not split /usr/bin/passwd into usr bin passwd
like older Bourne Shells did. Only characters generated as the result
of substitutions are split with IFS. Moreover, ksh ignores IFS settings
from the environment and resets IFS to the default value at the
start of each script. Better advice would be to set IFS=''
if you do not require word splitting, and to put set -o noglob
if you do not require file name generation. Your current example
does not put double quotes around $user, so that a user could
run from a directory that contains only a file named root,
and /usr/bin/passwd would be invoked with the argument root
by passing * as the name.
The dangers of /tmp files have also been eliminated with
recent versions of ksh. The /tmp file names are not guessable,
and they are created with an exclusive mode that prevents their
use in any way.
The timing problem you mention in the article depends on the system
that you are running on. On some systems, when the system encounters
#!, it opens this file and then passes down /dev/fd/n,
where n is the open file descriptor number, rather than the
name of the script. The shell than reads the script from this file
descriptor. On these systems there is no race condition. Moreover,
the script does not need to be readable in order to run. On systems
that don't have this feature, if the SUID script has been invoked
by ksh, the environment variable "_" will contain the
pathname of the invoking script when the script is invoked. The
script could check this against the expected name to avoid allowing
a symlinked version to be invoked so that this race condition could
not be exploited.
Finally, you left out additional advice on writing secure SUID
shell scripts. For example, if a user creates a symbolic link named
-i to the SUID script, and you use #! /bin/ksh in
line 1, then ksh will be invoked as ksh -i, which would specify
an interactive shell. (Actually, ksh is paranoid about running interactive
SUID shells, so it will fail). Adding a -, or --
after #! /bin/ksh will prevent this. Additionally, you do
not warn about the dangers of using eval, or the danger of
not quoting parameter and command substitution.
I have included a list of advice that I would give to users that
wish to create SUID shell scripts.
- Put #! /bin/ksh - as the first line, rather than just
- Manually set your PATH using only absolute names.
- If you do not require file name expansion, use >set -o
- If you do not need word splitting, use IFS=''. Otherwise,
use double quotes around every expansion. You might still need
to use double quotes to handle the case in which the expansion
yields the null string.
- Invoke unset on each variable before you use it.
- Check each argument to see whether it meets the required specifications.
- Do not use eval in your script.
- Avoid creating file unless absolutely necessary. Be careful
about any files that you create. Use set -o noclobber before
creating files so that they will be created with exclusive use.
- Before you create any file, add a trap on EXIT to remove
that file so that if your script aborts or exits, the file will
Author of ksh