Bugtraq mailing list archives

Re: ncurses 4.1 security bug


From: deraadt () CVS OPENBSD ORG (Theo de Raadt)
Date: Fri, 10 Jul 1998 14:55:34 -0600


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



Current thread: