[7228] in bugtraq
Re: ncurses 4.1 security bug
daemon@ATHENA.MIT.EDU (Theo de Raadt)
Sat Jul 11 17:40:11 1998
Date: Fri, 10 Jul 1998 14:55:34 -0600
Reply-To: Theo de Raadt <deraadt@CVS.OPENBSD.ORG>
From: Theo de Raadt <deraadt@CVS.OPENBSD.ORG>
X-To: mrg@ETERNA.COM.AU
To: BUGTRAQ@NETSPACE.ORG
In-Reply-To: Your message of "Fri, 10 Jul 1998 19:35:50 +1000."
<21653.900063350@eterna.com.au>
> how do you fix this? how does a _library_ know this? openbsd has defined an
> issetugid() syscall (or something) that libraries could use to ignore the
> things like $TAPE and $TERMCAP, etc., but that isn't correct.
You don't think it is correct? Almost two years ago we completely
solved the problem this entire thread is talking about. And you don't
think that is correct?
Is it better to leave it unfixed?
> how does it
> know what the real userid _really_ is, to perform the necessary checks on
> whether a file will be used or not -- or do you simple say that priviledged
> programs don't get this functionality?
Well, first off, it is obvious that you completely misunderstand how
this interface is intended to be used.
issetugid() tells you that the process is "tainted", because at some
point in the past it or it's parent(s) were started as a setuid
process. This kind of means it has or did have two uids or two gids,
but more generally it just means it was setuid or setuid back a while
ago. It's parent might have and might have revoked the privs before
fork()'ing the child, but in such a case the child still is "tainted".
(Who knows. it might have some nasty descriptor inside it, like a
SOCK_RAW, or a writeable descriptor to /etc/master.passwd).
The issetugid() status remains attached to a process and it's children
until execve() -- at which point it is assumed that either (1) the
environment was cleaned, or (2) the elevated uid/gid permissions were
completely revoked -- hence execve() is a safe place to remove the
status. If either of these two assumptions is not met, you've got a
hell of a lot worse problem, and nothing could save you.
Since the program is tainted, there are a couple of resources you should
not trust.
One of those is the environment.
Or at least, processes with this state should be careful with how MUCH
they trust the environment.
In a program itself, it is easy to detect this issue because you have
access to your uid and gid information. Deep in a library, it is
impossible to reliably get access to the uid and gid information, so
issetugid() tells you when you should not trust completely.
Does this mean $TZ can't work at all when when issetugid() returns 1?
No -- $TZ works..... It just doesn't work completely and fully.
TZ=Canada/Mountain works.
TZ=/dev/rst0 doesn't work (ie. it doesn't rewind the tape which your
backup software was carefully writing a series of files onto using
/dev/nrst0)
Here's the code snippet in tzload() that matters:
if (name != NULL && issetugid() != 0)
if ((name[0] == ':' && name[1] == '/') ||
name[0] == '/' || strchr(name, '.'))
name = NULL;
This is obviously the right thing to do. (We could improve it a bit
perhaps, but for now it is completely safe).
Similar things are done in other places in the libraries (not just
libc). Some variables from the environment are completely ignored;
others have various restrictions placed upon them.
We've solved this problem. We had to add a system call, which is
unfortunate, but noone can deny that we solved this problem. We
looked at crt0 based solutions as well, but we decided to do it this
way.
I've been told that vmailer calls issetugid() for similar reasons (if
it exists, which means OpenBSD or FreeBSD, though the FreeBSD
semantics are a tiny little bit different). (Wietse helped me clean
up the man page).
> i've had fixing this in problem in my TODO liist for over 2 years but
> without a total solution i've left it as is for now. these are the
> variables listed that NetBSD uses that i've determined are affected:
>
> - TZ
> - TERMCAP
> - HOSTALIASES
So for two years you've done absolutely nothing, and yet you think it
is your place to denounce the actions of others who fixed this
problem? In this community, "I knew about that security problem two
years ago" doesn't count for much.
And what about the following, from your library source tree?
PROFDIR
LOCALDOMAIN
NLSPATH
TMPDIR
RCMD_CMD
RES_OPTIONS
DEBUGTTY
HOME
PATH_LOCALE
----------------------------------------
Here is our man page:
ISSETUGID(2) OpenBSD Programmer's Manual ISSETUGID(2)
NAME
issetugid - is current executable running setuid or setgid
SYNOPSIS
#include <unistd.h>
int
issetugid(void);
DESCRIPTION
The issetugid() function returns 1 if the process was made setuid or set-
gid as the result of the last or other previous execve() system calls.
Otherwise it returns 0.
This system call exists so that library routines (inside libtermlib,
libc, or other libraries) can gaurantee safe behavior when used inside
setuid or setgid programs. Some library routines may be passed insuffi-
cient information and hence not know whether the current program was
started setuid or setgid because higher level calling code may have made
changes to the uid, euid, gid, or egid. Hence these low-level library
routines are unable to determine if they are being run with elevated or
normal priveledges.
In particular, it is wise to use this call to determine if a pathname re-
turned from a getenv() call may safely be used to open() the specified
file. Quite often this is not wise because the status of the effective
uid is not known.
The issetugid() system call's result is unaffected by calls to setuid(),
setgid(), or other such calls. In case of a fork(), the child process
inherits the same status.
The status of issetugid() is only affected by execve(). If a child pro-
cess executes a new executable file, a new issetuid status will be deter-
mined. This status is based on the existing process' uid, euid, gid, and
egid permissions and on the modes of the executable file. If the new ex-
ecutable file modes are setuid or setgid, or if the existing process is
executing the new image with uid != euid or gid != egid, the new process
will be considered issetugid.
ERRORS
The issetugid() function is always successful, and no return value is re-
served to indicate an error.
SEE ALSO
execve(2), setuid(2), seteuid(2), setgid(2), setegid(2)
HISTORY
The issetugid() function call first appeared in OpenBSD 2.0.
OpenBSD 2.0 August, 25 1996 1