NetBSD Problem Report #7060

Received: (qmail 28070 invoked from network); 28 Feb 1999 06:42:18 -0000
Message-Id: <199902280642.XAA22222@rupert.honors.montana.edu>
Date: Sat, 27 Feb 1999 23:42:17 -0700 (MST)
From: Chris Jones <cjones@rupert.honors.montana.edu>
Reply-To: cjones@netbsd.org
To: gnats-bugs@gnats.netbsd.org
Subject: find and rm
X-Send-Pr-Version: 3.95

>Number:         7060
>Category:       security
>Synopsis:       find and rm
>Confidential:   no
>Severity:       serious
>Priority:       low
>Responsible:    security-officer
>State:          closed
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Sat Feb 27 22:50:00 +0000 1999
>Closed-Date:    Tue Feb 06 13:26:01 +0000 2007
>Last-Modified:  Wed Feb 07 06:20:01 +0000 2007
>Originator:     Chris Jones
>Release:        Feb 23, 1999
>Organization:
-----------------------------------------------------cjones@math.montana.edu
Chris Jones                                          cjones@honors.montana.edu
           Mad scientist at large                    cjones@nervana.montana.edu
"Is this going to be a stand-up programming session, sir, or another bug hunt?"
>Environment:

System: NetBSD rupert.honors.montana.edu 1.3.2 NetBSD 1.3.2 (RUPERT) #2: Thu Jun 4 11:36:25 MDT 1998 tnkrbell@rupert.honors.montana.edu:/usr/src/sys/arch/i386/compile/RUPERT i386


>Description:
NetBSD has had a problem with find and rm in the daily script for a
while.  As I understand it, the issue is a race condition:  Find is
trying to remove files that match certain criteria, but there is a
race condition between the time when it runs stat on a file and when
it (or rm) unlinks the file.

>How-To-Repeat:
In order to exploit this, a user can create a directory under /tmp
which contains a file that would be targeted by find.  After the file
is targeted, but before rm is run, the user renames the directory and
puts a symlink in its place.  Then rm will be fooled into deleting the
target file in whatever directory the symlink points to.

As far as I know, the above is the only way to reproduce this problem.
Please correct me if my understanding is false.

>Fix:
One possibility would be a system call which takes a stat buffer and a
pathname as arguments.  It does a stat on the file indicated by
pathname, and compares the result to the stat buffer that gets passed
in to it.  If they're the same, it deletes the file.  Doesn't seem
like a very clean solution, though.

A preferred (IHO) solution is to have an unlink(2) call which refuses
to traverse symlinks.  So, the attacker can substitute a different
directory for the intended one, but it still has to be a directory
owned by the attacker, or the parent directory has to be owned by the
attacker.  (If I own /tmp/cjones, and find wants to delete
/tmp/cjones/foo/bar, then I can rename some other directory
/tmp/cjones/joe to /tmp/cjones/foo, and a file named bar will be
deleted in that directory.)  I don't see this as a problem, but maybe
someone else will see a security hole that it opens.

There should be an accompanying system call that does the same thing
for rmdir(2).  In addition, it might make sense to give the same
treatment to a few calls like chmod(2) and chown(2).  There should be
a naming scheme for the "non-symlink" versions of these calls.  The
best thing I came up with was unlinkd and rmdird, with the "d"
indicating that it traverses directories only.

In fact, I now realize that there will still be a race condition open,
though the time window will be much smaller:  Between the time when
find identifies a directory to recurse into, and the time when it
issues an open(2) on that directory, the directory could be replaced
with a symlink.  In order to fix this, there should be a opend(2) call
which refuses to traverse symlinks, including a symlink in the last
component of the pathname.

Here are patches to implement the unlinkd and rmdird system calls.
Hopefully I haven't missed anything else really obvious, and these
will actually work.

*** sys/kern/syscalls.master.orig	Sat Feb 27 09:32:35 1999
--- sys/kern/syscalls.master	Sat Feb 27 20:07:24 1999
***************
*** 1,4 ****
! 	$NetBSD: syscalls.master,v 1.88 1999/02/10 18:02:28 kleink Exp $

  ;	@(#)syscalls.master	8.2 (Berkeley) 1/13/94

--- 1,4 ----
! 	$NetBSD: syscalls.master,v 1.88 1999/02/10 18:02:28 kleink Exp  $

  ;	@(#)syscalls.master	8.2 (Berkeley) 1/13/94

***************
*** 564,566 ****
--- 564,568 ----
  			    sigset_t *oset); }
  294	STD		{ int sys___sigsuspend14(const sigset_t *set); }
  295	STD		{ int sys___sigreturn14(struct sigcontext *sigcntxp); }
+ 296	STD		{ int sys_unlinkd(const char *path); }
+ 297	STD		{ int sys_rmdird(const char *path); }
*** sys/kern/vfs_syscalls.c.orig	Sat Feb 27 09:32:43 1999
--- sys/kern/vfs_syscalls.c	Sat Feb 27 20:07:24 1999
***************
*** 1,4 ****
! /*	$NetBSD: vfs_syscalls.c,v 1.127 1998/12/10 15:09:19 christos Exp $	*/

  /*
   * Copyright (c) 1989, 1993
--- 1,4 ----
! /*	$NetBSD: vfs_syscalls.c,v 1.127 1998/12/10 15:09:19 christos Exp  $	*/

  /*
   * Copyright (c) 1989, 1993
***************
*** 1195,1200 ****
--- 1195,1261 ----
  }

  /*
+  * Delete a name from the filesystem, refusing to follow symlinks.
+  */
+ /* ARGSUSED */
+ int
+ sys_unlinkd(p, v, retval)
+ 	struct proc *p;
+ 	void *v;
+ 	register_t *retval;
+ {
+ 	struct sys_unlinkd_args /* {
+ 		syscallarg(const char *) path;
+ 	} */ *uap = v;
+ 	register struct vnode *vp;
+ 	int error;
+ 	struct nameidata nd;
+ 
+ 	NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE,
+ 	    SCARG(uap, path), p);
+ 	if ((error = namei(&nd)) != 0)
+ 		return (error);
+ 	vp = nd.ni_vp;
+ 
+ 	if(nd.ni_loopcnt > 0) {
+ 		VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ 		if(nd.ni_dvp == vp)
+ 			vrele(nd.ni_dvp);
+ 		else
+ 			vput(nd.ni_dvp);
+ 		vput(vp);
+ 		error = ELOOP;
+ 		goto out;
+ 	}
+ 
+ 	/*
+ 	 * The root of a mounted filesystem cannot be deleted.
+ 	 */
+ 	if (vp->v_flag & VROOT) {
+ 		VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ 		if (nd.ni_dvp == vp)
+ 			vrele(nd.ni_dvp);
+ 		else
+ 			vput(nd.ni_dvp);
+ 		vput(vp);
+ 		error = EBUSY;
+ 		goto out;
+ 	}
+ 
+ #if defined(UVM)
+ 	(void)uvm_vnp_uncache(vp);
+ #else
+ 	(void)vnode_pager_uncache(vp);
+ #endif
+ 
+ 	VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+ 	VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE);
+ 	error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
+ out:
+ 	return (error);
+ }
+ 
+ /*
   * Reposition read/write file offset.
   */
  int
***************
*** 2437,2442 ****
--- 2498,2561 ----
  	 */
  	if (nd.ni_dvp == vp) {
  		error = EINVAL;
+ 		goto out;
+ 	}
+ 	/*
+ 	 * The root of a mounted filesystem cannot be deleted.
+ 	 */
+ 	if (vp->v_flag & VROOT)
+ 		error = EBUSY;
+ out:
+ 	if (!error) {
+ 		VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+ 		VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE);
+ 		error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
+ 	} else {
+ 		VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ 		if (nd.ni_dvp == vp)
+ 			vrele(nd.ni_dvp);
+ 		else
+ 			vput(nd.ni_dvp);
+ 		vput(vp);
+ 	}
+ 	return (error);
+ }
+ 
+ /*
+  * Remove a directory file, refusing to follow symlinks.
+  */
+ /* ARGSUSED */
+ int
+ sys_rmdird(p, v, retval)
+ 	struct proc *p;
+ 	void *v;
+ 	register_t *retval;
+ {
+ 	struct sys_rmdird_args /* {
+ 		syscallarg(const char *) path;
+ 	} */ *uap = v;
+ 	register struct vnode *vp;
+ 	int error;
+ 	struct nameidata nd;
+ 
+ 	NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE,
+ 	    SCARG(uap, path), p);
+ 	if ((error = namei(&nd)) != 0)
+ 		return (error);
+ 	vp = nd.ni_vp;
+ 	if (vp->v_type != VDIR) {
+ 		error = ENOTDIR;
+ 		goto out;
+ 	}
+ 	/*
+ 	 * No rmdir "." please.
+ 	 */
+ 	if (nd.ni_dvp == vp) {
+ 		error = EINVAL;
+ 		goto out;
+ 	}
+ 	if (nd.ni_loopcnt > 0) {
+ 		error = ELOOP;
  		goto out;
  	}
  	/*
*** lib/libc/sys/Makefile.inc.orig	Sat Feb 27 19:45:24 1999
--- lib/libc/sys/Makefile.inc	Sat Feb 27 20:07:24 1999
***************
*** 1,4 ****
! #	$NetBSD: Makefile.inc,v 1.89 1999/02/11 20:42:49 carrel Exp $
  #	@(#)Makefile.inc	8.3 (Berkeley) 10/24/94

  # sys sources
--- 1,4 ----
! #	$NetBSD: Makefile.inc,v 1.89 1999/02/11 20:42:49 carrel Exp  $
  #	@(#)Makefile.inc	8.3 (Berkeley) 10/24/94

  # sys sources
***************
*** 47,60 ****
  	nfssvc.o ntp_adjtime.o ntp_gettime.o open.o pathconf.o \
  	poll.o profil.o quotactl.o read.o \
  	readlink.o readv.o reboot.o recvfrom.o recvmsg.o rename.o revoke.o \
! 	rmdir.o select.o semconfig.o semget.o semop.o sendmsg.o sendto.o \
! 	setegid.o seteuid.o setgid.o setgroups.o setitimer.o \
  	setpgid.o setpriority.o setregid.o setreuid.o setrlimit.o \
  	setsid.o setsockopt.o settimeofday.o setuid.o shmat.o \
  	shmctl.o shmdt.o shmget.o shutdown.o __sigaction14.o \
  	__sigaltstack14.o __sigpending14.o __sigprocmask14.o __sigsuspend14.o \
  	socket.o socketpair.o __stat13.o statfs.o \
! 	swapctl.o symlink.o sysarch.o umask.o undelete.o unlink.o \
  	unmount.o utimes.o vadvise.o wait4.o write.o writev.o \
  	__semctl.o __syscall.o __sysctl.o \
  	__posix_chown.o __posix_fchown.o __posix_lchown.o __posix_rename.o
--- 47,60 ----
  	nfssvc.o ntp_adjtime.o ntp_gettime.o open.o pathconf.o \
  	poll.o profil.o quotactl.o read.o \
  	readlink.o readv.o reboot.o recvfrom.o recvmsg.o rename.o revoke.o \
! 	rmdir.o rmdird.o select.o semconfig.o semget.o semop.o sendmsg.o \
! 	sendto.o setegid.o seteuid.o setgid.o setgroups.o setitimer.o \
  	setpgid.o setpriority.o setregid.o setreuid.o setrlimit.o \
  	setsid.o setsockopt.o settimeofday.o setuid.o shmat.o \
  	shmctl.o shmdt.o shmget.o shutdown.o __sigaction14.o \
  	__sigaltstack14.o __sigpending14.o __sigprocmask14.o __sigsuspend14.o \
  	socket.o socketpair.o __stat13.o statfs.o \
! 	swapctl.o symlink.o sysarch.o umask.o undelete.o unlink.o unlinkd.o \
  	unmount.o utimes.o vadvise.o wait4.o write.o writev.o \
  	__semctl.o __syscall.o __sysctl.o \
  	__posix_chown.o __posix_fchown.o __posix_lchown.o __posix_rename.o
***************
*** 216,221 ****
--- 216,222 ----
  MLINKS+=pathconf.2 fpathconf.2
  MLINKS+=read.2 readv.2 read.2 pread.2 read.2 preadv.2
  MLINKS+=recv.2 recvfrom.2 recv.2 recvmsg.2
+ MLINKS+=rmdir.2 rmdird.2
  MLINKS+=send.2 sendmsg.2 send.2 sendto.2
  MLINKS+=setpgid.2 setpgrp.2
  MLINKS+=setuid.2 setegid.2 setuid.2 seteuid.2 setuid.2 setgid.2
***************
*** 224,229 ****
--- 225,231 ----
  MLINKS+=statfs.2 fstatfs.2
  MLINKS+=syscall.2 __syscall.2
  MLINKS+=truncate.2 ftruncate.2
+ MLINKS+=unlink.2 unlinkd.2
  MLINKS+=utimes.2 futimes.2 utimes.2 lutimes.2
  MLINKS+=wait.2 wait3.2 wait.2 wait4.2 wait.2 waitpid.2
  MLINKS+=write.2 writev.2 write.2 pwrite.2 write.2 pwritev.2
*** include/unistd.h.orig	Sat Feb 27 19:14:51 1999
--- include/unistd.h	Sat Feb 27 20:07:25 1999
***************
*** 1,4 ****
! /*	$NetBSD: unistd.h,v 1.69 1998/11/30 20:36:27 thorpej Exp $	*/

  /*-
   * Copyright (c) 1998 The NetBSD Foundation, Inc.
--- 1,4 ----
! /*	$NetBSD: unistd.h,v 1.69 1998/11/30 20:36:27 thorpej Exp  $	*/

  /*-
   * Copyright (c) 1998 The NetBSD Foundation, Inc.
***************
*** 132,137 ****
--- 132,138 ----
  int	 pipe __P((int *));
  ssize_t	 read __P((int, void *, size_t));
  int	 rmdir __P((const char *));
+ int	 rmdird __P((const char *));
  int	 setgid __P((gid_t));
  int	 setpgid __P((pid_t, pid_t));
  pid_t	 setsid __P((void));
***************
*** 142,147 ****
--- 143,149 ----
  int	 tcsetpgrp __P((int, pid_t));
  __aconst char *ttyname __P((int));
  int	 unlink __P((const char *));
+ int	 unlinkd __P((const char *));
  ssize_t	 write __P((int, const void *, size_t));


*** lib/libc/sys/rmdir.2.orig	Sat Feb 27 19:29:29 1999
--- lib/libc/sys/rmdir.2	Sat Feb 27 20:07:25 1999
***************
*** 1,4 ****
! .\"	$NetBSD: rmdir.2,v 1.10 1998/08/29 08:32:41 lukem Exp $
  .\"
  .\" Copyright (c) 1983, 1991, 1993
  .\"	The Regents of the University of California.  All rights reserved.
--- 1,4 ----
! .\"	$NetBSD: rmdir.2,v 1.10 1998/08/29 08:32:41 lukem Exp  $
  .\"
  .\" Copyright (c) 1983, 1991, 1993
  .\"	The Regents of the University of California.  All rights reserved.
***************
*** 37,48 ****
  .Dt RMDIR 2
  .Os BSD 4.2
  .Sh NAME
! .Nm rmdir
  .Nd remove a directory file
  .Sh SYNOPSIS
  .Fd #include <unistd.h>
  .Ft int
  .Fn rmdir "const char *path"
  .Sh DESCRIPTION
  .Fn rmdir
  removes a directory file
--- 37,51 ----
  .Dt RMDIR 2
  .Os BSD 4.2
  .Sh NAME
! .Nm rmdir ,
! .Nm rmdird
  .Nd remove a directory file
  .Sh SYNOPSIS
  .Fd #include <unistd.h>
  .Ft int
  .Fn rmdir "const char *path"
+ .Ft int
+ .Fn rmdird "const char *path"
  .Sh DESCRIPTION
  .Fn rmdir
  removes a directory file
***************
*** 53,58 ****
--- 56,68 ----
  .Ql \&.
  and
  .Ql \&.. .
+ .Pp
+ .Fn rmdird
+ is similar, except the file will not be removed if any component of
+ .Fa path
+ is a symlink.  Useful to avoid a race condition between the time
+ .Fn stat
+ is run on a file and the time it is removed.
  .Sh RETURN VALUES
  A 0 is returned if the remove succeeds; otherwise a -1 is
  returned and an error code is stored in the global location
***************
*** 72,77 ****
--- 82,91 ----
  The named directory does not exist.
  .It Bq Er ELOOP
  Too many symbolic links were encountered in translating the pathname.
+ .It Bq Er ELOOP
+ .Fn rmdird
+ encountered a symbolic link in
+ .Fa path .
  .It Bq Er ENOTEMPTY
  The named directory contains files other than
  .Ql \&.
*** lib/libc/sys/unlink.2.orig	Sat Feb 27 19:29:34 1999
--- lib/libc/sys/unlink.2	Sat Feb 27 20:07:25 1999
***************
*** 1,4 ****
! .\"	$NetBSD: unlink.2,v 1.11 1998/08/29 08:32:43 lukem Exp $
  .\"
  .\" Copyright (c) 1980, 1991, 1993
  .\"	The Regents of the University of California.  All rights reserved.
--- 1,4 ----
! .\"	$NetBSD: unlink.2,v 1.11 1998/08/29 08:32:43 lukem Exp  $
  .\"
  .\" Copyright (c) 1980, 1991, 1993
  .\"	The Regents of the University of California.  All rights reserved.
***************
*** 37,48 ****
  .Dt UNLINK 2
  .Os BSD 4
  .Sh NAME
! .Nm unlink
  .Nd remove directory entry
  .Sh SYNOPSIS
  .Fd #include <unistd.h>
  .Ft int
  .Fn unlink "const char *path"
  .Sh DESCRIPTION
  The
  .Fn unlink
--- 37,51 ----
  .Dt UNLINK 2
  .Os BSD 4
  .Sh NAME
! .Nm unlink ,
! .Nm unlinkd
  .Nd remove directory entry
  .Sh SYNOPSIS
  .Fd #include <unistd.h>
  .Ft int
  .Fn unlink "const char *path"
+ .Ft int
+ .Fn unlinkd "const char *path"
  .Sh DESCRIPTION
  The
  .Fn unlink
***************
*** 58,63 ****
--- 61,75 ----
  If one or more process have the file open when the last link is removed,
  the link is removed, but the removal of the file is delayed until
  all references to it have been closed.
+ .Pp
+ .Fn unlinkd
+ behaves similarly, but will also refuse to remove the file if any
+ component of
+ .Fa path ,
+ other than the last (filename) portion, is a symbolic link.  Useful
+ to avoid a race condition between the time when
+ .Fn stat
+ is run on a file and the time when it is removed.
  .Sh RETURN VALUES
  Upon successful completion, a value of 0 is returned.
  Otherwise, a value of -1 is returned and
***************
*** 85,90 ****
--- 97,105 ----
  to be removed.
  .It Bq Er ELOOP
  Too many symbolic links were encountered in translating the pathname.
+ .It Bq Er ELOOP
+ .Fn unlinkd
+ encountered a symbolic link.
  .It Bq Er EPERM
  The named file is a directory and the effective user ID
  of the process is not the super-user, or the file system
*** bin/rm/rm.c.orig	Sat Feb 27 19:19:18 1999
--- bin/rm/rm.c	Sat Feb 27 20:07:25 1999
***************
*** 1,4 ****
! /*	$NetBSD: rm.c,v 1.24 1998/07/28 11:41:51 mycroft Exp $	*/

  /*-
   * Copyright (c) 1990, 1993, 1994
--- 1,4 ----
! /*	$NetBSD: rm.c,v 1.24 1998/07/28 11:41:51 mycroft Exp  $	*/

  /*-
   * Copyright (c) 1990, 1993, 1994
***************
*** 62,68 ****
  #include <pwd.h>
  #include <grp.h>

! int dflag, eval, fflag, iflag, Pflag, Wflag, stdin_ok;

  int	check __P((char *, char *, struct stat *));
  void	checkdot __P((char **));
--- 62,68 ----
  #include <pwd.h>
  #include <grp.h>

! int dflag, eval, fflag, iflag, Pflag, sflag, Wflag, stdin_ok;

  int	check __P((char *, char *, struct stat *));
  void	checkdot __P((char **));
***************
*** 80,85 ****
--- 80,90 ----
  #define NONEXISTENT(x) \
      ((x) == ENOENT || (x) == ENAMETOOLONG || (x) == ENOTDIR)

+ #define RMDIR(x) \
+      (sflag ? rmdird(x) : rmdir(x))
+ #define UNLINK(x) \
+      (sflag ? unlinkd(x) : unlink(x))
+ 
  /*
   * rm --
   *	This rm is different from historic rm's, but is expected to match
***************
*** 97,103 ****
  	(void)setlocale(LC_ALL, "");

  	Pflag = rflag = 0;
! 	while ((ch = getopt(argc, argv, "dfiPRrW")) != -1)
  		switch(ch) {
  		case 'd':
  			dflag = 1;
--- 102,108 ----
  	(void)setlocale(LC_ALL, "");

  	Pflag = rflag = 0;
! 	while ((ch = getopt(argc, argv, "dfiPRrsW")) != -1)
  		switch(ch) {
  		case 'd':
  			dflag = 1;
***************
*** 117,122 ****
--- 122,130 ----
  		case 'r':			/* Compatibility. */
  			rflag = 1;
  			break;
+ 		case 's':
+ 			sflag = 1;
+ 			break;
  		case 'W':
  			Wflag = 1;
  			break;
***************
*** 226,232 ****
  		switch (p->fts_info) {
  		case FTS_DP:
  		case FTS_DNR:
! 			if (!rmdir(p->fts_accpath) ||
  			    (fflag && errno == ENOENT))
  				continue;
  			break;
--- 234,240 ----
  		switch (p->fts_info) {
  		case FTS_DP:
  		case FTS_DNR:
! 			if (!RMDIR(p->fts_accpath) ||
  			    (fflag && errno == ENOENT))
  				continue;
  			break;
***************
*** 240,246 ****
  		default:
  			if (Pflag)
  				rm_overwrite(p->fts_accpath, NULL);
! 			if (!unlink(p->fts_accpath) ||
  			    (fflag && NONEXISTENT(errno)))
  				continue;
  		}
--- 248,254 ----
  		default:
  			if (Pflag)
  				rm_overwrite(p->fts_accpath, NULL);
! 			if (!UNLINK(p->fts_accpath) ||
  			    (fflag && NONEXISTENT(errno)))
  				continue;
  		}
***************
*** 291,301 ****
  		if (S_ISWHT(sb.st_mode))
  			rval = undelete(f);
  		else if (S_ISDIR(sb.st_mode))
! 			rval = rmdir(f);
  		else {
  			if (Pflag)
  				rm_overwrite(f, &sb);
! 			rval = unlink(f);
  		}
  		if (rval && (!fflag || !NONEXISTENT(errno))) {
  			warn("%s", f);
--- 299,309 ----
  		if (S_ISWHT(sb.st_mode))
  			rval = undelete(f);
  		else if (S_ISDIR(sb.st_mode))
! 			rval = RMDIR(f);
  		else {
  			if (Pflag)
  				rm_overwrite(f, &sb);
! 			rval = UNLINK(f);
  		}
  		if (rval && (!fflag || !NONEXISTENT(errno))) {
  			warn("%s", f);
***************
*** 438,444 ****
  usage()
  {

! 	(void)fprintf(stderr, "usage: rm [-dfiPRrW] file ...\n");
  	exit(1);
  	/* NOTREACHED */
  }
--- 446,452 ----
  usage()
  {

! 	(void)fprintf(stderr, "usage: rm [-dfiPRrsW] file ...\n");
  	exit(1);
  	/* NOTREACHED */
  }
*** bin/rm/rm.1.orig	Sat Feb 27 19:25:23 1999
--- bin/rm/rm.1	Sat Feb 27 20:07:25 1999
***************
*** 45,51 ****
  .Sh SYNOPSIS
  .Nm
  .Op Fl f | Fl i
! .Op Fl dPRrW
  .Ar file ...
  .Sh DESCRIPTION
  The
--- 45,51 ----
  .Sh SYNOPSIS
  .Nm
  .Op Fl f | Fl i
! .Op Fl dPRrsW
  .Ar file ...
  .Sh DESCRIPTION
  The
***************
*** 101,106 ****
--- 101,110 ----
  .It Fl r
  Equivalent to
  .Fl R .
+ .It Fl s
+ Do not unlink the file if any pathname component (except the filename
+ part) is a symlink.  This avoids certain race condition security
+ problems, such as when rm is used with find.
  .It Fl W
  Attempts to undelete the named files.
  Currently, this option can only be used to recover
*** bin/rmdir/rmdir.c.orig	Sat Feb 27 19:55:16 1999
--- bin/rmdir/rmdir.c	Sat Feb 27 20:07:25 1999
***************
*** 1,4 ****
! /*	$NetBSD: rmdir.c,v 1.16 1998/07/28 05:31:27 mycroft Exp $	*/

  /*-
   * Copyright (c) 1992, 1993, 1994
--- 1,4 ----
! /*	$NetBSD: rmdir.c,v 1.16 1998/07/28 05:31:27 mycroft Exp  $	*/

  /*-
   * Copyright (c) 1992, 1993, 1994
***************
*** 55,60 ****
--- 55,65 ----
  #include <locale.h>
  #include <unistd.h>

+ #define RMDIR(x) \
+         (sflag ? rmdird(x) : rmdir(x))
+ 
+ int sflag;
+ 
  int rm_path __P((char *));
  void usage __P((void));
  int main __P((int, char *[]));
***************
*** 69,80 ****

  	(void)setlocale(LC_ALL, "");

! 	pflag = 0;
! 	while ((ch = getopt(argc, argv, "p")) != -1)
  		switch(ch) {
  		case 'p':
  			pflag = 1;
  			break;
  		case '?':
  		default:
  			usage();
--- 74,88 ----

  	(void)setlocale(LC_ALL, "");

! 	pflag = sflag = 0;
! 	while ((ch = getopt(argc, argv, "ps")) != -1)
  		switch(ch) {
  		case 'p':
  			pflag = 1;
  			break;
+ 		case 's':
+ 			sflag = 1;
+ 			break;
  		case '?':
  		default:
  			usage();
***************
*** 94,100 ****
  			;
  		*++p = '\0';

! 		if (rmdir(*argv) < 0) {
  			warn("%s", *argv);
  			errors = 1;
  		} else if (pflag)
--- 102,108 ----
  			;
  		*++p = '\0';

! 		if (RMDIR(*argv) < 0) {
  			warn("%s", *argv);
  			errors = 1;
  		} else if (pflag)
***************
*** 117,123 ****
  			;
  		*++p = '\0';

! 		if (rmdir(path) < 0) {
  			warn("%s", path);
  			return (1);
  		}
--- 125,131 ----
  			;
  		*++p = '\0';

! 		if (RMDIR(path) < 0) {
  			warn("%s", path);
  			return (1);
  		}
***************
*** 130,136 ****
  usage()
  {

! 	(void)fprintf(stderr, "usage: rmdir [-p] directory ...\n");
  	exit(1);
  	/* NOTREACHED */
  }
--- 138,144 ----
  usage()
  {

! 	(void)fprintf(stderr, "usage: rmdir [-ps] directory ...\n");
  	exit(1);
  	/* NOTREACHED */
  }
*** bin/rmdir/rmdir.1.orig	Sat Feb 27 19:55:25 1999
--- bin/rmdir/rmdir.1	Sat Feb 27 20:07:26 1999
***************
*** 1,4 ****
! .\"	$NetBSD: rmdir.1,v 1.11 1997/10/20 08:53:22 enami Exp $
  .\"
  .\" Copyright (c) 1990, 1993
  .\"	The Regents of the University of California.  All rights reserved.
--- 1,4 ----
! .\"	$NetBSD: rmdir.1,v 1.11 1997/10/20 08:53:22 enami Exp  $
  .\"
  .\" Copyright (c) 1990, 1993
  .\"	The Regents of the University of California.  All rights reserved.
***************
*** 44,50 ****
  .Nd remove directories
  .Sh SYNOPSIS
  .Nm
! .Op Fl p
  .Ar directory ...
  .Sh DESCRIPTION
  The rmdir utility removes the directory entry specified by
--- 44,50 ----
  .Nd remove directories
  .Sh SYNOPSIS
  .Nm
! .Op Fl ps
  .Ar directory ...
  .Sh DESCRIPTION
  The rmdir utility removes the directory entry specified by
***************
*** 60,66 ****
  .Nm
  tries to remove it.
  .Pp
! The following option is available:
  .Bl -tag -width Ds
  .It Fl p
  Each
--- 60,66 ----
  .Nm
  tries to remove it.
  .Pp
! The following options are available:
  .Bl -tag -width Ds
  .It Fl p
  Each
***************
*** 71,76 ****
--- 71,83 ----
  (See
  .Xr rm 1
  for fully non-discriminant recursive removal.)
+ .It Fl s
+ Do not remove the file if any component of the specified pathname is a
+ symbolic link.  This can avoid certain race conditions caused by
+ running
+ .Nm
+ with
+ .Xr find 1 .
  .El
  .Pp
  The
***************
*** 85,91 ****
  An error occurred.
  .El
  .Sh SEE ALSO
! .Xr rm 1
  .Sh STANDARDS
  The
  .Nm
--- 92,99 ----
  An error occurred.
  .El
  .Sh SEE ALSO
! .Xr rm 1 ,
! .Xr rmdir 2
  .Sh STANDARDS
  The
  .Nm
*** etc/daily.orig	Sat Feb 27 19:48:01 1999
--- etc/daily	Sat Feb 27 20:07:26 1999
***************
*** 1,6 ****
  #!/bin/sh -
  #
! #	$NetBSD: daily,v 1.29 1999/01/06 03:24:06 abs Exp $
  #	@(#)daily	8.2 (Berkeley) 1/25/94
  #

--- 1,6 ----
  #!/bin/sh -
  #
! #	$NetBSD: daily,v 1.29 1999/01/06 03:24:06 abs Exp  $
  #	@(#)daily	8.2 (Berkeley) 1/25/94
  #

***************
*** 32,68 ****
  echo ""
  echo "Uptime: " `uptime`

! # Uncommenting any of the finds below would open up a race condition attack
! # based on symlinks, potentially allowing removal of any file on the system.
! #
! #echo ""
! #echo "Removing scratch and junk files:"
! #if [ -d /tmp -a ! -h /tmp ]; then
! #	cd /tmp && {
! #	find . -type f -atime +3 -exec rm -f -- {} \;
! #	find . ! -name . -type d -mtime +1 -exec rmdir -- {} \; \
! #	    >/dev/null 2>&1; }
! #fi

! #if [ -d /var/tmp -a ! -h /var/tmp ]; then
! #	cd /var/tmp && {
! #	find . ! -name . -atime +7 -exec rm -f -- {} \;
! #	find . ! -name . -type d -mtime +1 -exec rmdir -- {} \; \
! #	    >/dev/null 2>&1; }
! #fi

  # Additional junk directory cleanup would go like this:
  #if [ -d /scratch -a ! -h /scratch ]; then
  #	cd /scratch && {
! #	find . ! -name . -atime +1 -exec rm -f -- {} \;
! #	find . ! -name . -type d -mtime +1 -exec rmdir -- {} \; \
  #	    >/dev/null 2>&1; }
  #fi

! #if [ -d /var/rwho -a ! -h /var/rwho ] ; then
! #	cd /var/rwho && {
! #	find . ! -name . -mtime +7 -exec rm -f -- {} \; ; }
! #fi

  TMPDIR=/tmp/_daily.$$

--- 32,65 ----
  echo ""
  echo "Uptime: " `uptime`

! echo ""
! echo "Removing scratch and junk files:"
! if [ -d /tmp -a ! -h /tmp ]; then
! 	cd /tmp && {
! 	find . -type f -atime +3 -exec rm -fs -- {} \;
! 	find . ! -name . -type d -mtime +1 -exec rmdir -s -- {} \; \
! 	    >/dev/null 2>&1; }
! fi

! if [ -d /var/tmp -a ! -h /var/tmp ]; then
! 	cd /var/tmp && {
! 	find . ! -name . -atime +7 -exec rm -fs -- {} \;
! 	find . ! -name . -type d -mtime +1 -exec rmdir -s -- {} \; \
! 	    >/dev/null 2>&1; }
! fi

  # Additional junk directory cleanup would go like this:
  #if [ -d /scratch -a ! -h /scratch ]; then
  #	cd /scratch && {
! #	find . ! -name . -atime +1 -exec rm -fs -- {} \;
! #	find . ! -name . -type d -mtime +1 -exec rmdir -s -- {} \; \
  #	    >/dev/null 2>&1; }
  #fi

! if [ -d /var/rwho -a ! -h /var/rwho ] ; then
! 	cd /var/rwho && {
! 	find . ! -name . -mtime +7 -exec rm -fs -- {} \; ; }
! fi

  TMPDIR=/tmp/_daily.$$

>Release-Note:
>Audit-Trail:

From: "Erik E. Fair" <fair@clock.org>
To: cjones@rupert.honors.montana.edu
Cc: gnats-bugs@gnats.netbsd.org
Subject: Re: security/7060: find and rm
Date: Sun, 28 Feb 1999 01:54:21 -0800

 I don't think that a new system call for this is a good idea.

 How big is the window that the attacker has to hit?

 Can an attacker deterministically hit the window?

 I'd like to see more evidence that this is a real problem.

 	Erik <fair@clock.org>

From: Richard Earnshaw <rearnsha@arm.com>
To: cjones@rupert.honors.montana.edu
Cc: gnats-bugs@gnats.netbsd.org, current-users@netbsd.org
Subject: Re: security/7060: find and rm 
Date: Mon, 01 Mar 1999 10:50:05 +0000

 cjones@rupert.honors.montana.edu said:
 > >Description: NetBSD has had a problem with find and rm in the daily
 > script for a while.  As I understand it, the issue is a race
 > condition:  Find is trying to remove files that match certain
 > criteria, but there is a race condition between the time when it runs
 > stat on a file and when it (or rm) unlinks the file. 

 Have I missed something?  I can't see the problem here.  rm will remove 
 the symlink not the file pointed to (unless some magic options are being 
 used with rm).



From: Jaromir Dolecek <dolecek@ics.muni.cz>
To: richard.earnshaw@arm.com
Cc: cjones@rupert.honors.montana.edu, gnats-bugs@gnats.netbsd.org
Subject: Re: security/7060: find and rm
Date: Mon, 1 Mar 1999 13:10:39 +0100 (MET)

 Richard Earnshaw wrote:
 > 
 > cjones@rupert.honors.montana.edu said:
 > > >Description: NetBSD has had a problem with find and rm in the daily
 > > script for a while.  As I understand it, the issue is a race
 > > condition:  Find is trying to remove files that match certain
 > > criteria, but there is a race condition between the time when it runs
 > > stat on a file and when it (or rm) unlinks the file. 
 > 
 > Have I missed something?  I can't see the problem here.  rm will remove 
 > the symlink not the file pointed to (unless some magic options are being 
 > used with rm).

 Problem:
 1. mkdir /tmp/foo
 2. touch /tmp/foo/passwd
 3. let daily scan fs
 4. in right time do:
 	rm -r /tmp/foo
 	ln -s /etc /tmp/foo
 5. guess what happens when daily would issue ``rm /tmp/foo/passwd''

 -- 
 Jaromir Dolecek <dolecek@ics.muni.cz>      http://www.ics.muni.cz/~dolecek/
 "The only way how to get rid temptation is to yield to it." -- Oscar Wilde

From: Manuel Bouyer <bouyer@antioche.lip6.fr>
To: richard.earnshaw@arm.com
Cc: cjones@rupert.honors.montana.edu, gnats-bugs@gnats.netbsd.org,
        current-users@netbsd.org
Subject: Re: security/7060: find and rm
Date: Mon, 1 Mar 1999 13:47:08 +0100

 On Mar 1, Richard Earnshaw wrote
 > Have I missed something?  I can't see the problem here.  rm will remove 
 > the symlink not the file pointed to (unless some magic options are being 
 > used with rm).
 > 

 The problem is if you remplace a directory with a symlink to another directory.
 rm may then remove entries from the target directory, before removing
 the symlink.

 --
 Manuel Bouyer, LIP6, Universite Paris VI.           Manuel.Bouyer@lip6.fr
 --

From: Chris Jones <cjones@rupert.honors.montana.edu>
To: "Erik E. Fair" <fair@clock.org>
Cc: gnats-bugs@gnats.netbsd.org
Subject: Re: security/7060: find and rm
Date: 01 Mar 1999 09:10:19 -0700

 >>>>> "Erik" == Erik E Fair <fair@clock.org> writes:

 Erik> I don't think that a new system call for this is a good idea.

 I'm not convinced it is, either.  But it's the only decent solution I
 could come up with, and nobody else had come up with anything in the
 year or so that this problem has been around.

 Erik> How big is the window that the attacker has to hit?

 I guess it would depend on the VM activity of the system.  If it's
 busy, the window's going to be larger.  Practically speaking, it's
 enough time to fork, do a path search, exec, load a new binary, and
 then run unlink.

 Erik> Can an attacker deterministically hit the window?

 I don't know.

 Erik> I'd like to see more evidence that this is a real problem.

 Apparently somebody thinks it is, because the find | rm thing has
 been disabled in /etc/daily for quite some time.  And I'd like to go
 with a deterministic solution rather than a probabilistic one, based
 on the idea that, "It's unlikely that anybody'll exploit this bug."

 Chris

 -- 
 -----------------------------------------------------cjones@math.montana.edu
 Chris Jones                                          cjones@honors.montana.edu
            Mad scientist at large                    cjones@nervana.montana.edu
 "Is this going to be a stand-up programming session, sir, or another bug hunt?"
State-Changed-From-To: open->analyzed 
State-Changed-By: david 
State-Changed-When: Sat Aug 10 20:22:21 PDT 2002 
State-Changed-Why:  
Added some discussion of how to implement a fix. 
Responsible-Changed-From-To: security-officer->elad
Responsible-Changed-By: elad@netbsd.org
Responsible-Changed-When: Sun, 18 Sep 2005 20:42:19 +0000
Responsible-Changed-Why:
I'll handle it.


Responsible-Changed-From-To: elad->security-officer
Responsible-Changed-By: elad@netbsd.org
Responsible-Changed-When: Sun, 22 Jan 2006 19:55:23 +0000
Responsible-Changed-Why:
sink


From: Elad Efrat <elad@NetBSD.org>
To: gnats-bugs@NetBSD.org
Cc: netbsd-bugs@netbsd.org
Subject: re: security/7060
Date: Mon, 05 Feb 2007 22:59:14 +0200

 I recommend to close this pr now that it is possible to use per-user
 /tmp directories.

 -e.

From: SODA Noriyuki <soda@sra.co.jp>
To: gnats-bugs@NetBSD.org
Cc: 
Subject: Re: security/7060 (find and rm)
Date: Tue, 6 Feb 2007 10:53:20 +0900

 David wrote:
 > One solution would be adding an option to find, which could then
 > perform the unlink,

 FreeBSD already has -delete option in find(1).
 How about importing their change?
 -- 
 soda

State-Changed-From-To: analyzed->closed
State-Changed-By: elad@netbsd.org
State-Changed-When: Tue, 06 Feb 2007 13:26:01 +0000
State-Changed-Why:
as suggested by david@ & soda@, I imported a -delete for our find(1) from freebsd. personally I think perusertmp can be used in this case, but we'll let the admin decide. :)


From: matthew green <mrg@eterna.com.au>
To: gnats-bugs@NetBSD.org
Cc: security-officer@netbsd.org, security-alert@netbsd.org,
	gnats-admin@netbsd.org, elad@netbsd.org, cjones@netbsd.org
Subject: re: security/7060 (find and rm) 
Date: Wed, 07 Feb 2007 09:08:57 +1100

 cool.  find ... -delete will save me typing. :-)


 however, i don't understand how per user /tmp solves this problem.
 i also don't see how the implemented "-delete" option solves the
 problem, merely shortens the race.  it probably could do so more
 correctly if it chdir()'d to the right directly and checked that
 the file in that dir was the same stat() as the one i wants to
 delete...


 i also wonder about the automatic stripping of append/immutable
 bits by find's "-delete" option.  to me, that seems counter
 intuitive.  i would expect it to fail by default, and require
 either manual changing or an explicit option.


 .mrg.

From: Elad Efrat <elad@NetBSD.org>
To: matthew green <mrg@eterna.com.au>
Cc: gnats-bugs@NetBSD.org
Subject: Re: security/7060 (find and rm)
Date: Wed, 07 Feb 2007 03:48:19 +0200

 matthew green wrote:

 > however, i don't understand how per user /tmp solves this problem.

 	">How-To-Repeat:
 	 In order to exploit this, a user can create a directory under
 	 /tmp which contains a file that would be targeted by find."

 > i also don't see how the implemented "-delete" option solves the
 > problem, merely shortens the race. 

 I have mentioned that already. I was asked to implement freebsd's
 -delete because it made sense to those who asked me to. given it
 can be used to save typing :) I figured why not.

 > it probably could do so more
 > correctly if it chdir()'d to the right directly and checked that
 > the file in that dir was the same stat() as the one i wants to
 > delete...

 comparing stat() is incorrect and not needed. (not to say ugly :)

 > i also wonder about the automatic stripping of append/immutable
 > bits by find's "-delete" option.  to me, that seems counter
 > intuitive.  i would expect it to fail by default, and require
 > either manual changing or an explicit option.

 I merely imported the same behavior from freebsd. you may change
 this function as you see fit, given others don't mind, of course...

 -e.

From: matthew green <mrg@eterna.com.au>
To: Elad Efrat <elad@NetBSD.org>
Cc: gnats-bugs@NetBSD.org
Subject: re: security/7060 (find and rm) 
Date: Wed, 07 Feb 2007 14:50:31 +1100


    > however, i don't understand how per user /tmp solves this problem.

    	">How-To-Repeat:
    	 In order to exploit this, a user can create a directory under
    	 /tmp which contains a file that would be targeted by find."


 so when the daily find runs as root over the /private/*/tmp how
 does this PR get solved now?  AFAICT, the is no safe way to do
 this without changing find "-delete" to do the chdir combo i
 suggested previously.  if you run find over writeable-by-users
 directories, this problem exists.

 per-user-tmp solves a bunch of problems, but not this one.


 .mrg.

From: Elad Efrat <elad@NetBSD.org>
To: matthew green <mrg@eterna.com.au>
Cc: gnats-bugs@NetBSD.org
Subject: Re: security/7060 (find and rm)
Date: Wed, 07 Feb 2007 06:10:28 +0200

 matthew green wrote:
 >    
 >    > however, i don't understand how per user /tmp solves this problem.
 >    
 >    	">How-To-Repeat:
 >    	 In order to exploit this, a user can create a directory under
 >    	 /tmp which contains a file that would be targeted by find."
 > 
 > 
 > so when the daily find runs as root over the /private/*/tmp how
 > does this PR get solved now?  AFAICT, the is no safe way to do
 > this without changing find "-delete" to do the chdir combo i
 > suggested previously.  if you run find over writeable-by-users
 > directories, this problem exists.
 > 
 > per-user-tmp solves a bunch of problems, but not this one.

 okay. I still think that the current race happens because there is
 only one /tmp dir. given a per-user tmp dir, and quotas, a daily
 check don't even need to care about cleaning scartch files for
 the various users on the system.

 about what you suggested (comparing struct stat), iirc it was
 shown that in some cases an attacker can achieve the same data.

 personally, as the commands in question are commented out and not
 even mentioned outside the /etc/daily file (or so I'm guessing),
 and given my opinion above, this issue is not something I'm worried
 about.

 -e.

From: matthew green <mrg@eterna.com.au>
To: Elad Efrat <elad@NetBSD.org>
Cc: gnats-bugs@NetBSD.org
Subject: re: security/7060 (find and rm) 
Date: Wed, 07 Feb 2007 15:29:23 +1100

    matthew green wrote:
    >    
    >    > however, i don't understand how per user /tmp solves this problem.
    >    
    >    	">How-To-Repeat:
    >    	 In order to exploit this, a user can create a directory under
    >    	 /tmp which contains a file that would be targeted by find."
    > 
    > 
    > so when the daily find runs as root over the /private/*/tmp how
    > does this PR get solved now?  AFAICT, the is no safe way to do
    > this without changing find "-delete" to do the chdir combo i
    > suggested previously.  if you run find over writeable-by-users
    > directories, this problem exists.
    > 
    > per-user-tmp solves a bunch of problems, but not this one.

    okay. I still think that the current race happens because there is
    only one /tmp dir. given a per-user tmp dir, and quotas, a daily
    check don't even need to care about cleaning scartch files for
    the various users on the system.

 i'm not so sure.  i'd rather fix the tools to work properly than
 have them only work securely with a lot of setup.

    about what you suggested (comparing struct stat), iirc it was
    shown that in some cases an attacker can achieve the same data.

 do you have a pointer for this?  thanks.

    personally, as the commands in question are commented out and not
    even mentioned outside the /etc/daily file (or so I'm guessing),
    and given my opinion above, this issue is not something I'm worried
    about.

 i just wanted it to be clear that this doesn't solve the problem
 that this PR was logged for, given that i expect that cleaning
 these private tmp's will be wanted.


 .mrg.

From: Elad Efrat <elad@NetBSD.org>
To: matthew green <mrg@eterna.com.au>
Cc: gnats-bugs@NetBSD.org
Subject: Re: security/7060 (find and rm)
Date: Wed, 07 Feb 2007 06:43:33 +0200

 matthew green wrote:

 > i'm not so sure.  i'd rather fix the tools to work properly than
 > have them only work securely with a lot of setup.

 how is it a lot of setup? security(8) states:

 	# /etc/rc.d/perusertmp start

 >    about what you suggested (comparing struct stat), iirc it was
 >    shown that in some cases an attacker can achieve the same data.
 > 
 > do you have a pointer for this?  thanks.

 I would have to search the lists (not even sure it was on a netbsd
 list) and as I already mentioned, afaic, this is a non-issue.

 > i just wanted it to be clear that this doesn't solve the problem
 > that this PR was logged for, given that i expect that cleaning
 > these private tmp's will be wanted.

 well, let's see:

   - the relevant commands (again, iiuc) were commented out in 1996
     (rev 1.17 of daily) and a comment was added as to why they are
     commented out in 1999 (rev 1.29 of daily).

   - the pr was debated since its arrival in feb 1999 up to march
     of the same year. in 2002 it seems the audit trail was appended.

   - in sep 2005 I took it over to handle it, but I had other netbsd
     work, I withdrew "ownership".

   - few days ago (feb 2007) we implemented per-user-/tmp, and I
     commented that I think this pr can be closed.

   - someone disagreed, and suggested to import freebsd's -delete
     option to find(1). I mentioned it doesn't solve the problem,
     and that the issue is really a subset of a much larger problem
     that if the admin cares about he'll have to take further measures
     that can also mitigate this one.

     I imported it anyway, though, to make people happy. :)

   - now we have per-user-/tmp and find -delete. the commands seem to
     be commented out since 1996. to me, this is a non-issue.

 you may do whatever it is you like with this pr, but do not cc me
 on further mails on this issue, please. if anyone considered it a
 real problem, than other people than myself would have done something
 in the course of 8 (11?) years.

 -e.

From: matthew green <mrg@eterna.com.au>
To: Elad Efrat <elad@NetBSD.org>
Cc: gnats-bugs@NetBSD.org
Subject: re: security/7060 (find and rm) 
Date: Wed, 07 Feb 2007 17:07:56 +1100


    >    about what you suggested (comparing struct stat), iirc it was
    >    shown that in some cases an attacker can achieve the same data.
    > 
    > do you have a pointer for this?  thanks.

    I would have to search the lists (not even sure it was on a netbsd
    list) and as I already mentioned, afaic, this is a non-issue.


 i can't find anything.  i'm quite curious what this attack vector
 looks like.


 .mrg.

From: Elad Efrat <elad@NetBSD.org>
To: matthew green <mrg@eterna.com.au>
Cc: gnats-bugs@NetBSD.org
Subject: Re: security/7060 (find and rm)
Date: Wed, 07 Feb 2007 08:17:23 +0200

 matthew green wrote:
 >    
 >    >    about what you suggested (comparing struct stat), iirc it was
 >    >    shown that in some cases an attacker can achieve the same data.
 >    > 
 >    > do you have a pointer for this?  thanks.
 >    
 >    I would have to search the lists (not even sure it was on a netbsd
 >    list) and as I already mentioned, afaic, this is a non-issue.
 > 
 > 
 > i can't find anything.  i'm quite curious what this attack vector
 > looks like.

 http://ilja.netric.org/files/Unusual%20bugs%2023c3.pdf

 slide 46:

 	"File race conditions

 	 Reopening in a file in /tmp in a (un)safe manner.

 	 Suggested solutions: ``lstat/open/fstat dance''

 	 –Compare both devices and inodes
 	 –If they match it must be the same file

 	 Not true
 	 Still a race

 	 –File could be deleted
 	 –Create new file with the same inode nr"

 slide 47:

 	"``But if you compare more from the stat struct it's safe''

 	 Not true"

 and it continues with an analysis of struct stat. granted, netbsd has
 a member that may help in this ugly form of verification (the "birth"
 date of the inode) -- but as I have already mentioned numerous times,
 this issue does not interest me.

 -e.

>Unformatted:

 This certainly could be exploitable. This isn't a problem that is
 specific to /etc/daily, though it is an issue there. One solution would
 be adding an option to find, which could then perform the unlink,
 instead of execing rm for the job. Find could then take the proper
 precausions to make sure the unlinked file was the file which was stated.

 I'm marking this analyzed now, and moving the priority to 'low', since the
 /etc/daily issue was addresses years ago but disabling those finds.

 							David

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.39 2013/11/01 18:47:49 spz Exp $
$NetBSD: gnats_config.sh,v 1.8 2006/05/07 09:23:38 tsutsui Exp $
Copyright © 1994-2007 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.