COMMAND

    kernel

SYSTEMS AFFECTED

    FreeBSD  3.2  and  earlier,  FreeBSD-current before the correction
    date,  FreeBSD  3.2-stable  before  the  correction  date, FreeBSD
    2.2.8-stable before the correction date.

PROBLEM

    Following is based on FreeBSD Security Advisory.  As a  diagnostic
    aid to help  programmers find bugs  in their programs,  the system
    creates  core  files  when  an  illegal instruction or other fatal
    error happens.  A flaw in the kernel allowed it to follow symbolic
    links when creating core files.   The fts library functions had  a
    flaw 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.  Local users could gain root access.

SOLUTION

    Corrected in:

        FreeBSD-3.3 RELEASE
        FreeBSD-current as of 1999/08/26
        FreeBSD-3.2-stable as of 1999/08/26
        FreeBSD-2.2.8-stable as of 1999/08/29
        The FreeBSD-3.3-RC series of releases are not affected.

    One  can  workaround  this  problem  by  preventing core dumps for
    periodic.   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/or 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

    Another  alternative  would  be  to  update  the fts routines to a
    version  newer  than  1999/09/02  (for  -current or 3.3-stable) or
    1999/09/04 (for  2.2.8-stable).   However, this  requires that you
    rebuild via "make world" to take effect.  Please note: there is  a
    separate advisory describing the fts problem and solution.  Please
    see:

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

    for additional information about the fts patch.

    Apply the following  patches to your  kernel.  They  will disallow
    following symbolic links when creating core files.  This will stop
    this attack, and all similar  such attacks.  Here's the  patch for
    freebsd-current:

    *** kern/imgact_elf.c	1999/07/09 19:10:14	1.61
    --- kern/imgact_elf.c	1999/08/26 17:32:48	1.62
    ***************
    *** 722,729 ****
        if (name == NULL)
	        return (EFAULT);	/* XXX -- not the best error */

    ! 	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, name, p);
    ! 	error = vn_open(&nd, O_CREAT | FWRITE, S_IRUSR | S_IWUSR);
        free(name, M_TEMP);
        if (error)
	        return (error);
    --- 722,729 ----
        if (name == NULL)
	        return (EFAULT);	/* XXX -- not the best error */

    ! 	NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, name, p);
    ! 	error = vn_open(&nd, O_CREAT | FWRITE | O_NOFOLLOW, S_IRUSR | S_IWUSR);
        free(name, M_TEMP);
        if (error)
	        return (error);
    *** kern/imgact_aout.c	1999/05/17 00:53:36	1.52
    --- kern/imgact_aout.c	1999/08/26 17:32:48	1.53
    ***************
    *** 264,271 ****
        name = expand_name(p->p_comm, p->p_ucred->cr_uid, p->p_pid);
        if (name == NULL)
	        return (EFAULT);	/* XXX -- not the best error */
    ! 	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, name, p);
    ! 	error = vn_open(&nd, O_CREAT | FWRITE, S_IRUSR | S_IWUSR);
        free(name, M_TEMP);
        if (error)
	        return (error);
    --- 264,271 ----
        name = expand_name(p->p_comm, p->p_ucred->cr_uid, p->p_pid);
        if (name == NULL)
	        return (EFAULT);	/* XXX -- not the best error */
    ! 	NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, name, p);
    ! 	error = vn_open(&nd, O_CREAT | FWRITE | O_NOFOLLOW, S_IRUSR | S_IWUSR);
        free(name, M_TEMP);
        if (error)
	        return (error);

    Here's the patch for freebsd-3.2-stable:

    *** kern/imgact_elf.c	1999/07/15 13:01:54	1.44.2.4
    --- kern/imgact_elf.c	1999/08/26 17:35:03	1.44.2.5
    ***************
    *** 699,706 ****
        if (name == NULL)
	        return (EFAULT);	/* XXX -- not the best error */

    ! 	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, name, p);
    ! 	error = vn_open(&nd, O_CREAT | FWRITE, S_IRUSR | S_IWUSR);
        free(name, M_TEMP);
        if (error)
	        return (error);
    --- 699,706 ----
        if (name == NULL)
	        return (EFAULT);	/* XXX -- not the best error */

    ! 	NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, name, p);
    ! 	error = vn_open(&nd, O_CREAT | FWRITE | O_NOFOLLOW, S_IRUSR | S_IWUSR);
        free(name, M_TEMP);
        if (error)
	        return (error);
    *** kern/imgact_aout.c	1999/04/14 04:55:22	1.44.2.1
    --- kern/imgact_aout.c	1999/08/26 17:35:02	1.44.2.2
    ***************
    *** 259,266 ****
        name = expand_name(p->p_comm, p->p_ucred->cr_uid, p->p_pid);
        if (name == NULL)
	        return (EFAULT);	/* XXX -- not the best error */
    ! 	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, name, p);
    ! 	error = vn_open(&nd, O_CREAT | FWRITE, S_IRUSR | S_IWUSR);
        free(name, M_TEMP);
        if (error)
	        return (error);
    --- 259,266 ----
        name = expand_name(p->p_comm, p->p_ucred->cr_uid, p->p_pid);
        if (name == NULL)
	        return (EFAULT);	/* XXX -- not the best error */
    ! 	NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, name, p);
    ! 	error = vn_open(&nd, O_CREAT | FWRITE | O_NOFOLLOW, S_IRUSR | S_IWUSR);
        free(name, M_TEMP);
        if (error)
	        return (error);

    Here's the patch for FreeBSD-2.2.8-stable

    *** sys/LINK/fcntl.h	Wed Dec 18 05:08:08 1996
    --- sys/fcntl.h	Fri Aug 27 14:39:26 1999
    ***************
    *** 84,89 ****
    --- 84,90 ----
      #define	O_EXLOCK	0x0020		/* open with exclusive file lock */
      #define	O_ASYNC		0x0040		/* signal pgrp when data ready */
      #define	O_FSYNC		0x0080		/* synchronous writes */
    + #define	O_NOFOLLOW	0x0100		/* don't follow symlinks */
      #endif
      #define	O_CREAT		0x0200		/* create if nonexistent */
      #define	O_TRUNC		0x0400		/* truncate to zero length */
    *** kern/LINK/kern_sig.c	Sat Dec 21 10:57:24 1996
    --- kern/kern_sig.c	Fri Aug 27 14:38:25 1999
    ***************
    *** 1241,1249 ****
	    p->p_rlimit[RLIMIT_CORE].rlim_cur)
	        return (EFAULT);
        sprintf(name, "%s.core", p->p_comm);
    ! 	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, name, p);
        if ((error = vn_open(&nd,
    ! 	    O_CREAT | FWRITE, S_IRUSR | S_IWUSR)))
	        return (error);
        vp = nd.ni_vp;

    --- 1241,1249 ----
	    p->p_rlimit[RLIMIT_CORE].rlim_cur)
	        return (EFAULT);
        sprintf(name, "%s.core", p->p_comm);
    ! 	NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, name, p);
        if ((error = vn_open(&nd,
    ! 	    O_CREAT | FWRITE | O_NOFOLLOW, S_IRUSR | S_IWUSR)))
	        return (error);
        vp = nd.ni_vp;

    *** kern/LINK/vfs_vnops.c	Sat Mar  8 07:16:18 1997
    --- kern/vfs_vnops.c	Fri Aug 27 14:37:01 1999
    ***************
    *** 87,93 ****
        if (fmode & O_CREAT) {
	        ndp->ni_cnd.cn_nameiop = CREATE;
	        ndp->ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
    ! 		if ((fmode & O_EXCL) == 0)
		        ndp->ni_cnd.cn_flags |= FOLLOW;
	        error = namei(ndp);
	        if (error)
    --- 87,93 ----
        if (fmode & O_CREAT) {
	        ndp->ni_cnd.cn_nameiop = CREATE;
	        ndp->ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
    ! 		if ((fmode & O_EXCL) == 0 && (fmode & O_NOFOLLOW) == 0)
		        ndp->ni_cnd.cn_flags |= FOLLOW;
	        error = namei(ndp);
	        if (error)
    ***************
    *** 119,125 ****
	        }
        } else {
	        ndp->ni_cnd.cn_nameiop = LOOKUP;
    ! 		ndp->ni_cnd.cn_flags = FOLLOW | LOCKLEAF;
	        error = namei(ndp);
	        if (error)
		        return (error);
    --- 119,126 ----
	        }
        } else {
	        ndp->ni_cnd.cn_nameiop = LOOKUP;
    ! 		ndp->ni_cnd.cn_flags =
    ! 		    ((fmode & O_NOFOLLOW) ? NOFOLLOW : FOLLOW) | LOCKLEAF;
	        error = namei(ndp);
	        if (error)
		        return (error);
    *** kern/LINK/vfs_syscalls.c	Wed Aug  4 12:44:30 1999
    --- kern/vfs_syscalls.c	Sat Aug 28 10:48:51 1999
    ***************
    *** 694,699 ****
    --- 694,701 ----
        flags = FFLAGS(uap->flags);
        if ((flags & FREAD + FWRITE) == 0)
	        return (EINVAL);
    + 	if (flags & O_NOFOLLOW)
    + 		flags &= ~O_NOFOLLOW;
        error = falloc(p, &nfp, &indx);
        if (error)
	        return (error);