COMMAND

    libc

SYSTEMS AFFECTED

    NetBSD, OpenBSD, FreeBSD

PROBLEM

    Charles M. Hannum posted following.  The OpenBSD version of  fts.c
    is still susceptible to  at least two bugs  that can cause a  core
    dump when FTS_NOCHDIR is used (e.g. by pax(1)).  Beware if you  do
    backups with pax(1)!

    The fts  library functions  had a  buffer overflow  in them  where
    which would  lead to  a core  dump when  periodic ran the security
    checking scripts (or other  scripts which traverse trees  that can
    be controlled by  users).  periodic(3)  should limit core  size to
    zero to  disable core  dumps while  it is  executing commands, but
    does  not  do  so.   In  addition,  the  kernel  should not follow
    symbolic links.   All three of  these problems caused  a situation
    where it was  possible for an  attacker could create  or overwrite
    an arbitrary file on the system with a moderate degree of controll
    of its contents to cause  a problem (FreeBSD).  Local  users could
    gain root access.

SOLUTION

    These  problems,  and  others,  are  fixed  in  the current NetBSD
    version:

        revision 1.20
        date: 1999/08/27 18:01:35;  author: mycroft;  state: Exp;  lines:  +16 -10
        Fix multiple problems in the FTS_NOCHDIR case:
        * There was an off-by-one error that caused the addition of  a
          NUL or slash in fts_build() to overwrite other memory.
        * After fts_palloc(), we need to reset `cp' so that it  points
          to the new path name  buffer; otherwise the addition of  the
          file name before calling fts_stat() could lose.

    One  can  workaround  this  problem  by  preventing core dumps for
    periodic  (FreeBSD).   This  solution  is  less  than   completely
    satisfying, since it only plugs the known exploit hole.  None  the
    less, this may provide a  short term stopgap solution until  a new
    kernel and userland can be installed.

        # mv /usr/sbin/periodic /usr/sbin/periodic.bin
        # cat > /usr/sbin/periodic
        #!/bin/sh
        ulimit -c 0
        /usr/sbin/periodic.bin $*
        ^D
        # chmod 555 /usr/sbin/periodic

    Apply the following patches to libc  and do a make world.   Please
    also see the  companion advisory FreeBSD-SA-99:04.core.asc  in the
    BSD section:

        http://oliver.efri.hr/crv/security/bugs/BSD/kernel91.html

    for details on the kernel portions of this fix.

    Index: lib/libc/gen/fts.c
    RCS file: /home/imp/FreeBSD/CVS/src/lib/libc/gen/fts.c,v
    retrieving revision 1.10
    retrieving revision 1.11
    diff -u -r1.10 -r1.11
    --- fts.c	1999/08/15 19:21:29	1.10
    +++ fts.c	1999/09/02 07:45:07	1.11
    @@ -963,6 +963,24 @@
        return (sp->fts_path == NULL);
     }
    
    +static void
    +ADJUST(p, addr)
    +	FTSENT *p;
    +	void *addr;
    +{
    +	if ((p)->fts_accpath >= (p)->fts_path &&
    +	    (p)->fts_accpath < (p)->fts_path + (p)->fts_pathlen) {
    +		if (p->fts_accpath != p->fts_path)
    +			errx(1, "fts ADJUST: accpath %p path %p",
    +			    p->fts_accpath, p->fts_path);
    +		if (p->fts_level != 0)
    +			errx(1, "fts ADJUST: level %d not 0", p->fts_level);
    +		(p)->fts_accpath =
    +		    (char *)addr + ((p)->fts_accpath - (p)->fts_path);
    +	}
    +	(p)->fts_path = addr;
    +}
    +
     /*
      * When the path is realloc'd, have to fix all of the pointers in structures
      * already returned.
    @@ -974,18 +992,18 @@
     {
        FTSENT *p;
    
    -#define	ADJUST(p) {							\
    -	(p)->fts_accpath =						\
    -	    (char *)addr + ((p)->fts_accpath - (p)->fts_path);		\
    +#define	ADJUST1(p) {							\
    +	if ((p)->fts_accpath == (p)->fts_path)				\
    +		(p)->fts_accpath = (addr);				\
        (p)->fts_path = addr;						\
     }
        /* Adjust the current set of children. */
        for (p = sp->fts_child; p; p = p->fts_link)
    -		ADJUST(p);
    +		ADJUST(p, addr);
    
        /* Adjust the rest of the tree. */
        for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
    -		ADJUST(p);
    +		ADJUST(p, addr);
	        p = p->fts_link ? p->fts_link : p->fts_parent;
        }
     }