Re: ncurses 4.1 security bug

From: Theo de Raadt (deraadtat_private)
Date: Fri Jul 10 1998 - 13:55:34 PDT

  • Next message: Toomas Soome: "Re: Forwared to me"

    > 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
    



    This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 14:03:12 PDT