COMMAND

    kernel

SYSTEMS AFFECTED

    FreeBSD 3.x, 4.x and 5.x prior to the correction date

PROBLEM

    Following  is  based  on  a  FreeBSD-SA-00:42 Security Advisory by
    FreeBSD  and  it  was  found   by  Boris  Nikolaus.   FreeBSD   is
    binary-compatible  with  the  Linux  operating  system  through  a
    loadable kernel module/optional kernel component.

    The  linux  binary-compatability  module  implements  a   "shadow"
    filesystem hierarchy rooted  in /compat/linux, which  is overlayed
    against the  regular filesystem  hierarchy so  that Linux binaries
    "see" files  in the  shadow hierarchy  which can  mask the  native
    files.

    Filenames in this shadow hierarchy are treated incorrectly by  the
    linux  kernel  module  under  certain  circumstances, and a kernel
    stack overflow leading to  a system compromise by  an unprivileged
    user may be possible when very  long filenames are used.  This  is
    only  possible  when  the  linux  kernel  module is loaded, or the
    equivalent functionality is  statically compiled into  the kernel.
    It is not enabled by default.

    This vulnerability  was fixed  just after  the release  of FreeBSD
    4.1-RELEASE, and 3.5-RELEASE is also vulnerable.

    Local users may  be able to  obtain root privileges  on the system
    when linux compatability mode is enabled.

SOLUTION

    To  determine  whether  the  linux  compatability  module has been
    loaded,  execute  the  following  command  as  root and look for a
    'linux.ko' entry:

        # kldstat
         Id Refs Address    Size     Name
          1    7 0xc0100000 270be0   kernel
          2    1 0xc0371000 5540     vesa.ko
          3    1 0xc0377000 10094    randomdev.ko
          4    1 0xc0e17000 4e000    nfs.ko
          5    1 0xc0e83000 11000    linux.ko

    If present, unload the  "linux" module by executing  the following
    command as root:

        # kldunload linux

    For safety, remove the /modules/linux.ko file to prevent it  being
    reloaded accidentally,  and add  or change  the following  line in
    /etc/rc.conf:

        linux_enable="NO"       # Linux binary compatibility loaded at startup (or NO).

    If  the   module  is   not  loaded,   to  determine   whether  the
    functionality has been statically compiled into the kernel,  check
    the kernel configuration file for the following line:

        options    COMPAT_LINUX

    If  present,  remove  and  recompile  the  kernel  as described in
    http://www.freebsd.org/handbook/kernelconfig.html  and  reboot the
    system.

    Solution   is  to  upgrade  your  vulnerable  FreeBSD  system   to
    3.5-STABLE,  4.1-STABLE  or   5.0-CURRENT  after  the   respective
    correction  dates  or  apply  the  patch  below and recompile your
    kernel:

        ftp://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-00:42/linux.patch

    Patch:

    Index: linux_misc.c
    ===================================================================
    RCS file: /home/ncvs/src/sys/i386/linux/linux_misc.c,v
    retrieving revision 1.77.2.3
    retrieving revision 1.77.2.4
    diff -u -r1.77.2.3 -r1.77.2.4
    --- linux_misc.c	2000/07/20 05:31:56	1.77.2.3
    +++ linux_misc.c	2000/07/30 05:36:11	1.77.2.4
    @@ -954,6 +954,8 @@
 	    tv[1].tv_usec = 0;
 	    /* so that utimes can copyin */
 	    tvp = (struct timeval *)stackgap_alloc(&sg, sizeof(tv));
    +	if (tvp == NULL)
    +		return (ENAMETOOLONG);
 	    if ((error = copyout(tv, tvp, sizeof(tv))))
 	        return error;
 	    bsdutimes.tptr = tvp;
    Index: linux_util.c
    ===================================================================
    RCS file: /home/ncvs/src/sys/i386/linux/linux_util.c,v
    retrieving revision 1.9.2.1
    retrieving revision 1.9.2.2
    diff -u -r1.9.2.1 -r1.9.2.2
    --- linux_util.c	2000/07/07 01:23:45	1.9.2.1
    +++ linux_util.c	2000/07/30 05:36:11	1.9.2.2
    @@ -162,7 +162,10 @@
 	    else {
 		    sz = &ptr[len] - buf;
 		    *pbuf = stackgap_alloc(sgp, sz + 1);
    -		error = copyout(buf, *pbuf, sz);
    +		if (*pbuf != NULL)
    +			error = copyout(buf, *pbuf, sz);
    +		else
    +			error = ENAMETOOLONG;
 		    free(buf, M_TEMP);
 	    }
    
    Index: linux_util.h
    ===================================================================
    RCS file: /home/ncvs/src/sys/i386/linux/linux_util.h,v
    retrieving revision 1.10
    retrieving revision 1.10.2.1
    diff -u -r1.10 -r1.10.2.1
    --- linux_util.h	1999/12/04 11:10:22	1.10
    +++ linux_util.h	2000/07/30 05:36:11	1.10.2.1
    @@ -56,29 +56,27 @@
     static __inline caddr_t stackgap_init(void);
     static __inline void *stackgap_alloc(caddr_t *, size_t);
    
    +#define szsigcode (*(curproc->p_sysent->sv_szsigcode))
    +
     static __inline caddr_t
     stackgap_init()
     {
    -#define szsigcode (*(curproc->p_sysent->sv_szsigcode))
 	    return (caddr_t)(PS_STRINGS - szsigcode - SPARE_USRSPACE);
     }
    
    -
     static __inline void *
     stackgap_alloc(sgp, sz)
 	    caddr_t	*sgp;
 	    size_t   sz;
     {
    -	void	*p = (void *) *sgp;
    -	*sgp += ALIGN(sz);
    +	void *p = (void *) *sgp;
    +
    +	sz = ALIGN(sz);
    +	if (*sgp + sz > (caddr_t)(PS_STRINGS - szsigcode))
    +		return NULL;
    +	*sgp += sz;
 	    return p;
     }
    -
    -#ifdef DEBUG_LINUX
    -#define DPRINTF(a)      printf a;
    -#else
    -#define DPRINTF(a)
    -#endif
    
     extern const char linux_emul_path[];