[comp.unix.shell] Beware xargs security holes

D. Allen [CGL]) (10/09/90)

So you like to use this:

    find . -type f -print | xargs rm -f

Interesting things happen here under some versions of xargs if you
have files with odd names that look like command lines, such as:

    echo hi >"foo ; rm -rf /"

Since some versions of xargs (or programs of similar function, such as
"apply") use a shell to execute the indicated command, the shell parses
the file name, splits on the ";", and executes the following part of the
file name as another command, with potentially exciting results:

    rm -f foo ; rm -rf /

Even versions that don't use a shell to run the command often split on
blanks, causing file names to match things you don't intend:

    touch foo bar
    echo hi >"foo bar"
    echo "foo bar" | xargs rm

The above example removes files "foo" and "bar" on Ultrix 3.1C.  File
"foo bar" (which might have been the result of a "find") is not touched.

So be VERY CAREFUL using xargs and friends!
-- 
-IAN! (Ian! D. Allen) idallen@watcgl.uwaterloo.ca idallen@watcgl.waterloo.edu
 [129.97.128.64]  Computer Graphics Lab/University of Waterloo/Ontario/Canada

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (10/09/90)

Yeah. xargs should have a -0 option for taking null-separated filenames
for its input. find should have a -print0 option for producing similar
output.

I wrote a somewhat improved PD ``apply'' clone out of boredom. It can
execute commands itself, pass them to sh, or print them for sh batching.
At least it's reasonably secure; you can feel safe with apply ... "$@".
Copy enclosed. Kids, don't use this awful coding style at home.

---Dan

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  Makefile apply.c getopt.h apply.1
# Wrapped by brnstnd@kramden on Sun Sep  9 05:33:06 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(415 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
XCC=cc
XCCOPTS=-O4 -s -DVFORK
X
XNROFF=nroff
XNROFFOPTS=-man
X
Xall: apply apply.man
X
Xshar: apply.shar
X
Xapply: apply.o Makefile
X	$(CC) $(CCOPTS) -o apply apply.o
X
Xapply.o: apply.c getopt.h Makefile
X	$(CC) $(CCOPTS) -c apply.c
X
Xapply.man: apply.1 Makefile
X	$(NROFF) $(NROFFOPTS) < apply.1 > apply.man
X
Xapply.shar: apply.c getopt.h apply.1 Makefile
X	shar Makefile apply.c getopt.h apply.1 > apply.shar
X	chmod 400 apply.shar
END_OF_FILE
if test 415 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'apply.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'apply.c'\"
else
echo shar: Extracting \"'apply.c'\" \(6256 characters\)
sed "s/^X//" >'apply.c' <<'END_OF_FILE'
X#include <sys/types.h> /* for wait and fork */
X#include <sys/wait.h> /* for wait */
X#include <stdio.h> /* for EOF, fprintf, puts */
X#include <malloc.h> /* for malloc and free */
X#include <strings.h> /* for strlen and strcpy */
X#include "getopt.h" /*XXX: should be <getopt.h> */
X/* XXX: What about system(), exit(), perror()? */
X/* XXX: Shouldn't do repeated malloc-free. Figure out the largest block */
X/*      size needed, allocate it once, and use it repeatedly. */
X/* XXX: Maybe option to quote arguments under flagsystem? */
X/* XXX: Or just allow spaces within arguments inside command? */
X/* XXX: The number of separate command parsers in here is ridiculous. */
X
Xstatic char applyusage[] = "Usage: apply [ -ac ] [ -n ] cmd arg ...\n";
Xstatic char applytoomany[] = "apply: warning: extra arguments\n";
Xstatic char applyforkfail[] = "apply: fatal: cannot fork";
Xstatic char applyexecfail[] = "apply: fatal: cannot exec";
Xstatic char applymalloc[] = "apply: fatal: cannot malloc\n";
X
Xmain(argc,argv)
Xint argc;
Xchar *argv[];
X{
X register char *s;
X register char *t;
X register char **a;
X register int i;
X register int j;
X register char *cmd;
X register int flagsystem = 0;
X register int narg = 1;
X register int nch = 0;
X register int nesc = 0;
X register char escape = '%';
X register int opt;
X
X while ((opt = getopt(argc,argv,"0123456789a:s")) != EOF)
X   switch(opt)
X    {
X     case '?': (void) fprintf(stderr,applyusage);
X	       (void) exit(1);
X     case 'a': escape = *optarg; break; /* can't be null */
X     case 's': flagsystem = 2; break;
X#define OSN(i,j) case i: narg = j; break;
X     OSN('0',0) OSN('1',1) OSN('2',2) OSN('3',3) OSN('4',4)
X     OSN('5',5) OSN('6',6) OSN('7',7) OSN('8',8) OSN('9',9)
X    }
X argv += optind, argc -= optind;
X
X if (!*argv)
X  {
X   (void) fprintf(stderr,applyusage);
X   (void) exit(1);
X  }
X
X /* Note that we accept %0, and do the same with it as with -0. */
X s = *argv;
X while (*s)
X   if (*(s++) != escape)
X     nch++;
X   else
X    {
X     if (!*s)
X      { flagsystem = (!flagsystem ? 1 : flagsystem); break; }
X     switch(*(s++))
X      {
X#define TSN(i,j) case i: narg=(nesc++?(narg>j?narg:j):j); break;
X       TSN('0',0) TSN('1',1) TSN('2',2) TSN('3',3) TSN('4',4)
X       TSN('5',5) TSN('6',6) TSN('7',7) TSN('8',8) TSN('9',9)
X       default: nch++; break;
X      }
X    }
X
X /* Now the number of real characters is nch. */
X /* The number of escape characters is nesc. */
X /* If flagsystem is 1, we should use sh. */
X /* And we should snarf narg args per cmd, plus waste one if !narg. */
X
X if (!nesc)
X  {
X   t = cmd = malloc((unsigned) (nch + 1));
X   s = *argv;
X   while (*s)
X     if (*s != escape)
X       *(t++) = *(s++);
X     else
X      {
X       if (!*++s)
X         break;
X       switch(*s)
X        {
X         case 'e': *(t++) = escape; s++; break;
X	 default: *(t++) = *(s++); break;
X        }
X      }
X   *t = '\0';
X  }
X else
X   cmd = *argv;
X   
X argc--; argv++;
X /* XXX: Should something be done to prevent zombies? */
X
X if (narg && (argc % narg))
X   (void) fprintf(stderr,applytoomany);
X while (argc >= narg + !narg)
X  {
X   if (!nesc)
X    {
X     if (flagsystem)
X      {
X       i = nch + narg + !narg;
X       for (j = 0;j < narg;j++)
X	 i += strlen(argv[j]);
X       t = s = malloc((unsigned) i);
X       if (!t)
X	{
X	 (void) fprintf(stderr,applymalloc);
X	 (void) exit(1);
X	}
X       (void) strcpy(t,cmd); t += strlen(cmd); *(t++) = ' ';
X       for (j = 0;j < narg;j++)
X	{
X         (void) strcpy(t,argv[j]); t += strlen(argv[j]); *(t++) = ' ';
X	}
X       *t = '\0';
X       /* slow? whaddya mean, slow? three passes is nothing! */
X       if (flagsystem == 2)
X         (void) puts(s);
X       else
X         (void) system(s);
X       (void) free(s);
X      }
X     else
X      {
X       s = argv[narg];
X       argv[narg] = (char *) 0;
X       argv[-1] = cmd; /* This always works. Trust me. */
X       /* We haven't done anything with stdio, so no need to flush. */
X#ifdef VFORK
X       switch(vfork())
X#else
X       switch(fork())
X#endif
X        {
X         case -1: perror(applyforkfail);
X		  (void) exit(1);
X         case 0: (void) execvp(cmd,argv - 1);
X	         perror(applyexecfail);
X	         (void) exit(0);
X         default: (void) wait((int *) 0);
X        }
X       argv[narg] = s;
X      }
X    }
X   else /* nesc > 0 */
X    {
X     i = nch + 1;
X     s = cmd;
X     while (*s)
X       if (*(s++) == escape)
X	{
X         if (!*s)
X           break;
X	 switch(*s)
X	  {
X	   case '0': s++; break;
X#define SSN(k,j) case k: i += strlen(argv[j]); s++; break;
X	   /* omit 0 */ SSN('1',0); SSN('2',1); SSN('3',2); SSN('4',3);
X	   SSN('5',4);  SSN('6',5); SSN('7',6); SSN('8',7); SSN('9',8);
X	   default: i += 1; s++; break;
X	  }
X	}
X
X     t = malloc((unsigned) i);
X     if (!t)
X      {
X       (void) fprintf(stderr,applymalloc);
X       (void) exit(1);
X      }
X     s = cmd;
X     while (*s)
X       if (*s != escape)
X	 *(t++) = *(s++);
X       else
X        {
X         if (!*++s)
X           break;
X	 switch(*s)
X	  {
X	   case '0': s++; break;
X#define ISN(i,j) case i:(void)strcpy(t,argv[j]);t+=strlen(argv[j]);s++;break;
X	   /* omit 0 */ ISN('1',0); ISN('2',1); ISN('3',2); ISN('4',3);
X	   ISN('5',4);  ISN('6',5); ISN('7',6); ISN('8',7); ISN('9',8);
X	   default: *(t++) = *(s++); break;
X	  }
X        }
X     *(t++) = '\0';
X     t -= i;
X     switch(flagsystem)
X      {
X       case 0: i = 1;
X	       for (s = t;*s;s++)
X		 i += (*s == ' ');
X	       a = (char **) malloc((unsigned) (sizeof(char *) * i));
X               if (!a)
X        	{
X        	 (void) fprintf(stderr,applymalloc);
X        	 (void) exit(1);
X        	}
X	       for (s = a[i = 0] = t;*s;s++)
X		 if (*s == ' ')
X		   a[a[i] == s ? i : ++i] = s + 1;
X	       a[a[i] == s ? i : ++i] = (char *) 0;
X#ifdef VFORK
X               switch(vfork())
X#else
X               switch(fork())
X#endif
X                {
X                 case -1: perror(applyforkfail);
X        		  (void) exit(1);
X                 case 0: (void) execvp(*a,a);
X        	         perror(applyexecfail);
X        	         (void) exit(0);
X                 default: (void) wait((int *) 0);
X                }
X	       (void) free((char *) a);
X	       break;
X       case 1: (void) system(t); break;
X       case 2: (void) puts(t); break;
X      }
X     (void) free(t);
X    }
X   argv += narg + !narg; argc -= narg + !narg;
X  }
X
X (void) exit(0);
X}
END_OF_FILE
if test 6256 -ne `wc -c <'apply.c'`; then
    echo shar: \"'apply.c'\" unpacked with wrong size!
fi
# end of 'apply.c'
fi
if test -f 'getopt.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'getopt.h'\"
else
echo shar: Extracting \"'getopt.h'\" \(61 characters\)
sed "s/^X//" >'getopt.h' <<'END_OF_FILE'
Xextern int optind;
Xextern char *optarg;
Xextern int getopt();
END_OF_FILE
if test 61 -ne `wc -c <'getopt.h'`; then
    echo shar: \"'getopt.h'\" unpacked with wrong size!
fi
# end of 'getopt.h'
fi
if test -f 'apply.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'apply.1'\"
else
echo shar: Extracting \"'apply.1'\" \(2909 characters\)
sed "s/^X//" >'apply.1' <<'END_OF_FILE'
X.TH apply 1
X.SH NAME
Xapply \- apply a command to a set of arguments
X.SH SYNOPSIS
X.B apply
X[
X.B \-a
X.I escape-char
X] [
X.B \-s
X] [
X.I \-n
X]
X.I command
X[
X.I arg ...
X]
X.SH DESCRIPTION
X.B apply
Xapplies
X.I command
Xto each
X.I arg
Xin sequence.
X.PP
X.B apply
Xnormally runs
X.I command
Xwith one argument at a time.
XUnder
X.I \-2,
Xit will pass two arguments at once to
X.I command.
XSimilarly for all of
X.I \-1
Xthrough
X.I \-9.
XUnder
X.I \-0,
X.B apply
Xwill run
X.I command
Xwithout arguments, once for each
X.I arg.
X.PP
X.B apply
Xlooks for escape characters,
Xdefault %,
Xin
X.I command.
XIf
X.I %n
Xappears for any
X.I n
Xfrom 0 through 9,
X.B apply
Xwill place arguments
Xinside
X.I command
Xrather than after it:
Xthe first argument replacing
X.I %1,
Xetc.
XIt will also ignore any
X.I -n
Xoption,
Xinstead taking as many arguments per
X.I command
Xas the highest-numbered
X.I %n.
XIn this case
X.B apply
Xwill split
X.I command
Xon spaces,
Xalong with each
X.I arg;
Xthis is the only effect of
X.I %0,
Xwhich is deleted before
X.I command
Xis executed.
X.PP
X%% is passed through as a single %.
X%e is also passed through as a single %.
X% followed by any other character is passed through
Xas that character, though this behavior may not be true
Xin future versions.
XThe escape character may be changed with
X.B \-a.
X.PP
XNormally
X.I command
Xis executed directly
Xfrom 
X.B apply.
XIf
X.I command
Xends with a single %,
Xit is passed through
Xto
X.B sh(1)
Xvia
X.B system(3).
XThis lets
X.I command
Xuse output redirection and other shell features.
X.PP
XUnder
X.B \-s,
X.B apply
Xprints each
X.I command
Xrather than executing it.
XThis can be saved or piped through
X.B sh(1).
XIt is useful for debugging
Xuses of
X.B apply.
X.PP
X.B apply
Xwill ignore any extra arguments
Xand print a warning message.
X.SH EXAMPLES
XCompare the examples in
X.B xargs(1).
X.IP
X.B apply echo \(**
X.LP
XSimilar to
X.B ls(1).
X.IP
X\fBapply "mv $1/%1 $2/%1" `ls`\fR
X.LP
XMove all files from directory
X.B $1
Xto directory
X.B $2.
X.IP
X\fBapply \-s "mv $1/%1 $2/%1" \(** | sh \-v\fR
X.LP
XMove all files from directory
X.B $1
Xto directory
X.B $2,
Xprinting each command before it is executed.
X.IP
X.B apply \-2 diff $\(**
X.LP
XShow differences between successive pairs of files,
Xpassed to the shell as arguments.
X.IP
X\fBapply \-0 who 1 2 3 4 5\fR
X.LP
XRun
X.B who(1)
Xfive times.
X.IP
X.B apply \-2 %2 \(**
X.LP
XShow every other file.
X.IP
X.B apply \-2 %2 x \(**
X.LP
XShow the other every other file.
X.SH RESTRICTIONS
X.B apply
Xwill keep going blindly if
X.I command
Xisn't executable.
XOf course, this behavior is necessary
Xin commands like
X.IP
X.B
Xapply '%1 foo' rm touch 'chmod 755'
X.LP
XSometimes it is much faster to use
X.I sh,
Xas when
X.I command
Xis a shell builtin whose normal version is slow.
X.B apply
Xdoes not know to invoke
X.I sh
Xin this case.
X(This can happen when, for example,
X.I command
Xis dynamically linked
Xwhile
X.I sh
Xis not, on a system supporting dynamic linking.)
X.SH BUGS
XNone known.
X.SH "SEE ALSO"
X.B repeat(1), sh(1), xargs(1)
END_OF_FILE
if test 2909 -ne `wc -c <'apply.1'`; then
    echo shar: \"'apply.1'\" unpacked with wrong size!
fi
# end of 'apply.1'
fi
echo shar: End of shell archive.
exit 0

sahayman@iuvax.cs.indiana.edu (Steve Hayman) (10/09/90)

>Yeah. xargs should have a -0 option for taking null-separated filenames
>for its input. find should have a -print0 option for producing similar
>output.

So long as you have to modify "find" anyway to solve this problem, why
not just add "-xargs" option to "find", that would be like the "-exec"
option only using as many file names as possible at once.

	find .... -xargs rm {} \;

Seems simpler than modifying two programs.

Steve
-- 
Steve Hayman    Workstation Manager    Computer Science Department   Indiana U.
sahayman@iuvax.cs.indiana.edu                                    (812) 855-6984
NeXT Mail: sahayman@spurge.bloomington.in.us

lml@cbnews.att.com (L. Mark Larsen) (10/10/90)

In article <63404@iuvax.cs.indiana.edu>, sahayman@iuvax.cs.indiana.edu (Steve Hayman) writes:
# >Yeah. xargs should have a -0 option for taking null-separated filenames
# >for its input. find should have a -print0 option for producing similar
# >output.
# 
# So long as you have to modify "find" anyway to solve this problem, why
# not just add "-xargs" option to "find", that would be like the "-exec"
# option only using as many file names as possible at once.
# 
# 	find .... -xargs rm {} \;
# 
# Seems simpler than modifying two programs.
# 

Actually, this is what was done when find was rewritten for SVR4 (among
other things).  The way to use it is to terminate the command with "+"
instead of ";" (e.g., find ... -exec rm {} +)

I never much cared for xargs since it limits you to an argument list of
only 470 bytes.  Since all of the System V UNIX's I know of support argument
lists of at least 5120 bytes (12288 in UTS), this has always seemed like
too small of an upper-limit to me - though it's still better than one exec
per file...

L. Mark Larsen
lml@atlas.att.com

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (10/10/90)

In article <63404@iuvax.cs.indiana.edu> sahayman@iuvax.cs.indiana.edu (Steve Hayman) writes:
: >Yeah. xargs should have a -0 option for taking null-separated filenames
: >for its input. find should have a -print0 option for producing similar
: >output.
: 
: So long as you have to modify "find" anyway to solve this problem, why
: not just add "-xargs" option to "find", that would be like the "-exec"
: option only using as many file names as possible at once.
: 
: 	find .... -xargs rm {} \;
: 
: Seems simpler than modifying two programs.

What makes you think xargs is the only program that would like to use -print0
output?  Think toolbox, man!  (That's how Randal would say it.)

    find ... -print0 | perl -e '$/ = "\0"; while (<>) {chop; unlink;}'

That doesn't suffer any of the security holes of xargs.

Larry Wall
lwall@jpl-devvax.jpl.nasa.gov

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (10/10/90)

In article <63404@iuvax.cs.indiana.edu> sahayman@iuvax.cs.indiana.edu (Steve Hayman) writes:
> >Yeah. xargs should have a -0 option for taking null-separated filenames
> >for its input. find should have a -print0 option for producing similar
> >output.
> So long as you have to modify "find" anyway to solve this problem, why
> not just add "-xargs" option to "find", that would be like the "-exec"
> option only using as many file names as possible at once.
> 	find .... -xargs rm {} \;
> Seems simpler than modifying two programs.

On the general grounds of ``modularity'' I'll claim that the separate
commands are more useful. I imagine commands other than xargs being able
to take that 0-terminated list and do something with it.

Modularity aside, it's much, much simpler to add -0 to xargs (a few
lines added) and -print0 to find (just change a \n to \0) than to add
all of the argument handling of xargs into find.

---Dan

lvc@cbnews.att.com (Lawrence V. Cipriani) (10/10/90)

In article <9365:Oct910:43:4590@kramden.acf.nyu.edu>, brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:
> Yeah. xargs should have a -0 option for taking null-separated filenames
> for its input. find should have a -print0 option for producing similar
> output.

It does, sort of:

	find . -print | xargs -i your_command '{}'

but, since the -i option executes your_command once per file it will be
very slow.
-- 
Larry Cipriani, att!cbvox!lvc or lvc@cbvox.att.com

peter@ficc.ferranti.com (Peter da Silva) (10/10/90)

In article <1990Oct9.172621.13484@cbnews.att.com> lml@cbnews.att.com (L. Mark Larsen) writes:
> I never much cared for xargs since it limits you to an argument list of
> only 470 bytes.

Surely this is an implementation detail. After all, xargs is almost as much
of a no-brainer command as echo. If you don't like it fix it... it's just
an efficiency consideration anyway.
-- 
Peter da Silva.   `-_-'
+1 713 274 5180.   'U`
peter@ferranti.com

chip@tct.uucp (Chip Salzenberg) (10/13/90)

According to lml@cbnews.att.com (L. Mark Larsen):
>I never much cared for xargs since it limits you to an argument list of
>only 470 bytes.

For the most common use of xargs -- "find ... | xargs command" -- the
script below, called "many", does a good job.  Since it doesn't spawn
a subshell, it isn't prone to metacharacter-caused security problems.
And you can configure the max arg size to whatever you like.

Enjoy.

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  many
# Wrapped by chip@tct on Fri Oct 12 19:36:53 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'many' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'many'\"
else
echo shar: Extracting \"'many'\" \(2681 characters\)
sed "s/^X//" >'many' <<'END_OF_FILE'
Xeval 'exec /bin/perl -S $0 ${1+"$@"}'
X	if 0;
X
X# $Id: many,v 1.1 90/10/11 13:51:17 chip Exp $
X#
X# Execute the given command with many arguments, one per line.
X# This is like xargs, but it's safer, more flexible and free of
X# the stupid 470-byte argument limit.
X#
X
Xrequire 'signal.ph';
Xrequire 'errno.ph';
X
X$NCARGS = 5120;		## I've never seen it less than this
X
X($ME = $0) =~ s#^.*/##;
X
X$opt_chdir = 0;
X$opt_trace = 0;
X$opt_argfile = undef;
Xwhile (@ARGV) {
X	$_ = $ARGV[0];
X	last unless s/^-//;
X	shift;
X	last if $_ eq "-";
X	while ($_ ne "") {
X		if (s/^d//)	{ $opt_chdir = 1; }
X		elsif (s/^t//)	{ $opt_trace = 1; }
X		elsif (s/^f//)	{ $opt_argfile = length($_) ? $_ : shift; }
X		else		{ &USAGE; }
X	}
X}
X
Xsub USAGE {
X	print STDERR <<EOF;
X
Xusage: $ME [-d][-t][-f argfile] command
X
XCollect arguments, one per line, up to maximum argument length,
Xexecuting the given command with those arguments as many times
Xas required until arguments are exhausted.
X
XOptions:
X	-d	Change to files' directory before spawning command.
X	-t	Trace mode: print directories and commands before acting.
X	-f file Get arguments from specified file instead of standard input.
X
XEOF
X	exit 1;
X}
X
X&USAGE unless @ARGV;
X@cmd = @ARGV;
X$cmd_len = 0;
Xgrep($cmd_len += length($_) + 1, @cmd);
X
Xif (defined($opt_argfile)) {
X	die "$ME: can't open $opt_argfile: $!\n"
X	    unless open(ARG, "< $opt_argfile");
X}
Xelse {
X	open(ARG, "<&STDIN");
X	close(STDIN);
X	open(STDIN, "/dev/null") || die "$ME: can't open /dev/null: $!\n";
X}
X
X@args = ();
X$args_len = 0;
Xwhile (<ARG>) {
X	chop($a = $_) if /\n$/;
X	$a_len = length($a) + 1;
X	$doit = ($cmd_len + $args_len + $a_len > $NCARGS);
X	if ($opt_chdir) {
X		if ($a =~ m#^(.*)/+([^/]+/*)$#) {
X			$a_dir = $1;
X			$a = $2;
X		}
X		else {
X			$a_dir = ".";
X		}
X		$spawn_dir = $a_dir unless defined($spawn_dir);
X		$doit = 1 if $spawn_dir ne $a_dir;
X	}
X	if ($doit) {
X		&SPAWN(@cmd, @args);
X		@args = ();
X		$args_len = 0;
X		if ($opt_chdir) {
X			$spawn_dir = $a_dir;
X		}
X	}
X	if ($cmd_len + $a_len > $NCARGS) {
X		print STDERR "$ME: line $.: argument too long\n";
X		next;
X	}
X	push(@args, $a);
X	$args_len += $a_len;
X}
X
X&SPAWN(@cmd, @args) if @args;
X
Xexit;
X
Xsub SPAWN {
X	local(@av) = @_;
X
X	$parent_pid = $$;
X	die "$ME: can't fork" unless defined($pid = fork);
X	if ($pid == 0) {
X		$failed = 0;
X		if ($opt_chdir) {
X			print STDERR "++ chdir $spawn_dir\n" if $opt_trace;
X			unless (chdir $spawn_dir) {
X				print STDERR "$ME: $spawn_dir: $!\n";
X				$failed = 1;
X			}
X		}
X		unless ($failed) {
X			print STDERR join(" ", "+", @cmd, @args), "\n"
X			    if $opt_trace;
X			exec @cmd, @args;
X			print STDERR "$ME: $cmd[0]: $!\n";
X		}
X		kill &SIGTERM, $parent_pid;
X		exit 1;
X	}
X	0 while wait == -1 && $! == &EINTR;
X	1;
X}
END_OF_FILE
if test 2681 -ne `wc -c <'many'`; then
    echo shar: \"'many'\" unpacked with wrong size!
fi
chmod +x 'many'
# end of 'many'
fi
echo shar: End of shell archive.
exit 0
-- 
Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>
    "I've been cranky ever since my comp.unix.wizards was removed
         by that evil Chip Salzenberg."   -- John F. Haugh II

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (10/16/90)

In article <271653D6.1CE8@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
> According to lml@cbnews.att.com (L. Mark Larsen):
> >I never much cared for xargs since it limits you to an argument list of
> >only 470 bytes.
> For the most common use of xargs -- "find ... | xargs command" -- the
> script below, called "many", does a good job.  Since it doesn't spawn
> a subshell, it isn't prone to metacharacter-caused security problems.

But it's still susceptible to filenames with carriage returns, and will
be until find has a -print0 option. Please, please, please don't claim
that your xargs is by any means secure when a standard command like

  find / -name '#*' -atime +7 -print | xargs rm

lets a malicious user remove every file on the system. Maybe it's
unreasonable of me to want others to live up to my standard of security,
but in my eyes no \n-parsing xargs qualifies as ``a good job.'' Sorry.

---Dan

tif@doorstop.austin.ibm.com (Paul Chamberlain) (10/16/90)

In article <4062:Oct1518:22:1290@kramden.acf.nyu.edu> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:
>  find / -name '#*' -atime +7 -print | xargs rm
>lets a malicious user remove every file on the system.

If I understand, to do this a user would have to create a file
with a '/' in its name.  Is this possible on some systems?
The most malicious thing I can do with the above command is
remove a file that doesn't start with '#' that's in a
writable directory.

Twice (I think), you have asserted grave danger with find
and xargs.  I still don't see it.

Paul Chamberlain | I do NOT represent IBM.     tif@doorstop, sc30661 at ausvm6
512/838-7008     | ...!cs.utexas.edu!ibmaus!auschs!doorstop.austin.ibm.com!tif

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (10/17/90)

In article <3876@awdprime.UUCP> tif@doorstop.austin.ibm.com (Paul Chamberlain) writes:
> In article <4062:Oct1518:22:1290@kramden.acf.nyu.edu> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:
> >  find / -name '#*' -atime +7 -print | xargs rm
> >lets a malicious user remove every file on the system.
> If I understand, to do this a user would have to create a file
> with a '/' in its name.

Incorrect. find prints full pathnames, not just filenames.

> The most malicious thing I can do with the above command is
> remove a file that doesn't start with '#' that's in a
> writable directory.

Incorrect. If that command is run daily from cron, as it is on many
systems, then any user can remove any file on the system.

---Dan

tim@ggumby.cs.caltech.edu (Timothy L. Kay) (10/17/90)

tif@doorstop.austin.ibm.com (Paul Chamberlain) writes:
>In article brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:
>>  find / -name '#*' -atime +7 -print | xargs rm
>>lets a malicious user remove every file on the system.

>If I understand, to do this a user would have to create a file
>with a '/' in its name.  Is this possible on some systems?
>The most malicious thing I can do with the above command is
>remove a file that doesn't start with '#' that's in a
>writable directory.

Let me see.  If I create a directory named

	directory\n

and create a file in there called vmunix, then find will spit out
a file

	.../directory\n/vmunix\n

which xargs will see as

	.../directory

and

	/vmunix


Tim

pfalstad@stone.Princeton.EDU (Paul John Falstad) (10/17/90)

In article <13569:Oct1617:00:0590@kramden.acf.nyu.edu> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:
>In article <3876@awdprime.UUCP> tif@doorstop.austin.ibm.com (Paul Chamberlain) writes:
>> In article <4062:Oct1518:22:1290@kramden.acf.nyu.edu> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:
>> >  find / -name '#*' -atime +7 -print | xargs rm
>> The most malicious thing I can do with the above command is
>> remove a file that doesn't start with '#' that's in a
>> writable directory.
>Incorrect. If that command is run daily from cron, as it is on many
>systems, then any user can remove any file on the system.

Oh, I see.  You could do something like this:

$ echo >'#
vmunix'

And then cron would delete /vmunix.  That's assuming cron starts up xargs
with / as its current directory.

And to delete other files (not necessarily in /), you could do:

$ mkdir '#
'
$ cd '#
'
$ mkdir u; mkdir u/subbarao
$ mkdir u/subbarao/.plan'
'
$ date >u/subbarao/.plan'
/#foo'

If you do a find . -name '#*' -print | xargs echo in this directory, you get:

./# ./# /u/subbarao/.plan /#foo ./# vmunix

Very nasty.  Wonder if it works on my system...

--
Paul Falstad, pfalstad@phoenix.princeton.edu PLink:HYPNOS GEnie:P.FALSTAD
"And she's always on about men following her.  I don't know what she
thinks they're going to do to her.  Vomit on her, Basil, says."-Flowery Twats

peter@ficc.ferranti.com (Peter da Silva) (10/18/90)

In article <13569:Oct1617:00:0590@kramden.acf.nyu.edu> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:
>  find / -name '#*' -atime +7 -print | xargs rm

> Incorrect. If that command is run daily from cron, as it is on many
> systems, then any user can remove any file on the system.

OK, enlighten me. Why is this command run daily on cron on many systems? And
if it is, what systems are they so I can avoid them?
-- 
Peter da Silva.   `-_-'
+1 713 274 5180.   'U`
peter@ferranti.com

subbarao@phoenix.Princeton.EDU (Kartik Subbarao) (10/18/90)

In article <3369@idunno.Princeton.EDU> pfalstad@stone.Princeton.EDU (Paul John Falstad) writes:
>In article <13569:Oct1617:00:0590@kramden.acf.nyu.edu> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:
>>In article <3876@awdprime.UUCP> tif@doorstop.austin.ibm.com (Paul Chamberlain) writes:
>>> In article <4062:Oct1518:22:1290@kramden.acf.nyu.edu> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:
>>> >  find / -name '#*' -atime +7 -print | xargs rm
>>> The most malicious thing I can do with the above command is
>>> remove a file that doesn't start with '#' that's in a
>>> writable directory.
>>Incorrect. If that command is run daily from cron, as it is on many
>>systems, then any user can remove any file on the system.
>
>Oh, I see.  You could do something like this:

>And then cron would delete /vmunix.  That's assuming cron starts up xargs
>with / as its current directory.

>And to delete other files (not necessarily in /), you could do:
>
>$ mkdir '#
>'
>$ cd '#
>'
>$ mkdir u; mkdir u/subbarao
>$ mkdir u/subbarao/.plan'
>'
>$ date >u/subbarao/.plan'
>/#foo'
>

Yow! nah, we'd never wan't to do that, now would we? Then I'd have no goal in life?! :-)


>If you do a find . -name '#*' -print | xargs echo in this directory, you get:
>
>./# ./# /u/subbarao/.plan /#foo ./# vmunix
>
>Very nasty.  Wonder if it works on my system...

No, good thing it doesn't. Especially after I'd want to put a pipe as my .plan to execute a command
;-). Gee, it's also a good thing there are no shell escapes in rm. I can just see the thread now:

"Beware : Re: how to prevent shell escapes from rm".

And then Dan would give his wonderful pty solution (not that pty isn't wonderful!),
Larry Wall and Randall Schwartz would probably find a nice one line perl hack,
some other sysadmin would complain about the openness of the discussion, another would cry
"security through obscurity", and the wizards would go back and forth about this.

comp.unix.* can be so funny at times :-)


>Paul Falstad, pfalstad@phoenix.princeton.edu PLink:HYPNOS GEnie:P.FALSTAD
>"And she's always on about men following her.  I don't know what she
>thinks they're going to do to her.  Vomit on her, Basil, says."-Flowery Twats


Good thing Paul only removed my .plan, so I can say:




(I need a new .signature -- any suggestions?)
subbarao@{phoenix or gauguin}.Princeton.EDU -|Internet
kartik@silvertone.Princeton.EDU (NeXT mail)       -|	
SUBBARAO@PUCC.BITNET			          - Bitnet

tif@doorstop.austin.ibm.com (Paul Chamberlain) (10/19/90)

In article <tim.656101080@ggumby> tim@ggumby.cs.caltech.edu (Timothy L. Kay) writes:
>tif@doorstop.austin.ibm.com (Paul Chamberlain) writes:
>>In article brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:
>>>  find / -name '#*' -atime +7 -print | xargs rm
>>>lets a malicious user remove every file on the system.
>>
>>The most malicious thing I can do with the above command is
>>remove a file that doesn't start with '#' that's in a
>>writable directory.
>
>Let me see.  If I create a directory [and file] named
>	.../directory\n/vmunix

Okay, I've had this explained to me and I admit that this
could be a problem.  But I think it is contrived because
the "find" command to pass this filename to xargs either
doesn't check the name or allows "vmunix" to match it.

I suppose I could create the above and then convince the
administrator to do "find /u/tif -print | xargs chown tif".
But, then again, wouldn't "ln /etc/passwd /u/tif/my_file"
be easier.

In any case, I've yet to see how "a malicious user [could]
remove every file on the system."

Paul Chamberlain | I do NOT represent IBM.     tif@doorstop, sc30661 at ausvm6
512/838-7008     | ...!cs.utexas.edu!ibmaus!auschs!doorstop.austin.ibm.com!tif

dkeisen@Gang-of-Four.Stanford.EDU (Dave Eisen) (10/20/90)

In article <3940@awdprime.UUCP> tif@doorstop.austin.ibm.com (Paul Chamberlain) writes:
>In article <tim.656101080@ggumby> tim@ggumby.cs.caltech.edu (Timothy L. Kay) writes:
>>tif@doorstop.austin.ibm.com (Paul Chamberlain) writes:
>>>In article brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:
>>>>  find / -name '#*' -atime +7 -print | xargs rm
>>>>lets a malicious user remove every file on the system.
>>>
>
>In any case, I've yet to see how "a malicious user [could]
>remove every file on the system."
>

If xargs is implemented using system (as it is on this machine),
an old file named #<newline>-rf * will remove every file on the machine if
the rm -rf * happens to be the start of the buffer passed to system.




--
Dave Eisen                      	    Home: (415) 323-9757
dkeisen@Gang-of-Four.Stanford.EDU           Office: (415) 967-5644
1447 N. Shoreline Blvd.
Mountain View, CA 94043

res@cbnews.att.com (Robert E. Stampfli) (10/20/90)

> In any case, I've yet to see how "a malicious user [could]
> remove every file on the system."

No user, malicious or not, can remove "every file on the system."
In fact, I contend it is impossible to remove the last instance of
an executable that contains the unlink() system call.
-- 
Rob Stampfli	/ att.com!stampfli (uucp@work) / kd8wk@w8cqk (packet radio)
614-864-9377	/ osu-cis.cis.ohio-state.edu!kd8wk!res (uucp@home)

wcs) (10/21/90)

In article <SGH6K6D@xds13.ferranti.com>, peter@ficc.ferranti.com (Peter da Silva) writes:
> >  find / -name '#*' -atime +7 -print | xargs rm
> OK, enlighten me. Why is this command run daily on cron on many systems? And
> if it is, what systems are they so I can avoid them?

Many systems, especially universities, have a convention that
filenames beginning with '#' are "marked for death".
Instead of a "real" rm command, most users use a file deletion
command that renames foo as #foo, which can be recovered by an undelete
command, if the user is lucky after having been stupid, and the ls
command may also be hacked not to show them.
Other places use similar conventions.

But even if you DO avoid systems like this, LOTS of places do
	find /tmp /usr/tmp -atime +7 -mtime +7 -print |xargs rm
to clean old stuff out of /tmp, which IS morally acceptable behaviour :-)
And it can be hosed just as easily;
	mkdir /tmp/foo'
	' 
	cd "/tmp/foo?" ; echo gotcha > vmunix
-- 
					Thanks; Bill
# Bill Stewart 908-949-0705 erebus.att.com!wcs AT&T Bell Labs 4M-312 Holmdel NJ
Government is like an elephant on drugs: It's very confused, makes lots of noise,
can't do anything well, stomps on anyone in its way, and it sure eats a lot.

davidsen@sixhub.UUCP (Wm E. Davidsen Jr) (10/21/90)

  It *appears* that xenix quotes its arguments in xargs, since I did a
small and cautious test and it worked all right. How about testing your
version of xargs and posting the results here? I will do Sun, Ultrix and
(if I get the files reloaded) V.4.
-- 
bill davidsen - davidsen@sixhub.uucp (uunet!crdgw1!sixhub!davidsen)
    sysop *IX BBS and Public Access UNIX
    moderator of comp.binaries.ibm.pc and 80386 mailing list
"Stupidity, like virtue, is its own reward" -me

rouben@math9.math.umbc.edu (Rouben Rostamian) (10/21/90)

In article <2113@sixhub.UUCP> davidsen@sixhub.UUCP (bill davidsen) writes:
>  It *appears* that xenix quotes its arguments in xargs, since I did a
>small and cautious test and it worked all right. How about testing your
>version of xargs and posting the results here? I will do Sun, Ultrix and
>(if I get the files reloaded) V.4.

Why "small and cautious"?  To test whether xargs quotes its arguments, 
in an empty directory do:

touch "This is a test"
find . -print | xargs rm

If the file "This is a test" goes away, then xargs is quoting its arguments.
Otherwise, xargs is feeding the file name as four separate arguments to rm 
and you will get complains from rm for not finding the files.

I ran this test on Ultrix V4.0 and on a Stardent 3000 (a hybrid SysV/bsd 
beast.)  In neither test the file was removed.  So no quoting from xargs
in these cases.

--
Rouben Rostamian                            Telephone: (301) 455-2458
Department of Mathematics and Statistics    e-mail:
University of Maryland Baltimore County     bitnet: rostamian@umbc
Baltimore, MD 21228,  U.S.A.                internet: rostamian@umbc3.umbc.edu

pfalstad@fish.Princeton.EDU (Paul John Falstad) (10/21/90)

>In article <2113@sixhub.UUCP> davidsen@sixhub.UUCP (bill davidsen) writes:
>>  It *appears* that xenix quotes its arguments in xargs, since I did a
>>small and cautious test and it worked all right. How about testing your

Though as Dan said earlier, even if xargs quotes its arguments, you can
still get in trouble, since find and xargs use a newline as a delimiter for
filenames, and filenames can have newlines in them.

--
Paul Falstad, pfalstad@phoenix.princeton.edu PLink:HYPNOS GEnie:P.FALSTAD
And Dinsdale said, "You've been a naughty boy, Clement," and splits me nostrils
open, and saws me leg off, and pulls me liver out.  And I said, "My name's not
Clement."  And then he loses his temper.  And he nails me head to the floor.

les@chinet.chi.il.us (Leslie Mikesell) (10/22/90)

In article <3484@idunno.Princeton.EDU> pfalstad@fish.Princeton.EDU (Paul John Falstad) writes:

>Though as Dan said earlier, even if xargs quotes its arguments, you can
>still get in trouble, since find and xargs use a newline as a delimiter for
>filenames, and filenames can have newlines in them.

Actually the problem of allowing characters that are valid in filenames
to have special meanings on the command line runs rampant throughout
unix.  Even if you eliminate part of the problem by using a '\0' delimiter
to syncronize find and xargs, you can still get into trouble with a
file named "-r" appearing at the front of an argument list that might
also mention directories.

Les Mikesell
  les@chinet.chi.il.us

ag@cbmvax.commodore.com (Keith Gabryelski) (10/22/90)

In article <1990Oct21.223729.10521@chinet.chi.il.us> les@chinet.chi.il.us
(Leslie Mikesell) writes:
>[...] you can still get into trouble with a file named "-r" appearing
>at the front of an argument list that might also mention directories.

But this is easily taken care of by suppling `--' as the last argument
to rm (ie, find . -name '*.dvi' -print | xargs rm --)

Pax, Keith

emv@math.lsa.umich.edu (Edward Vielmetti) (10/22/90)

In article <4203@umbc3.UMBC.EDU> rouben@math9.math.umbc.edu (Rouben Rostamian) writes:

   I ran this test on Ultrix V4.0 and on a Stardent 3000 (a hybrid SysV/bsd 
   beast.)  In neither test the file was removed.  So no quoting from xargs
   in these cases.

SunOS 4.0.3 also does not remove the file.  Instead it says 

urania /tmp/foo % find . -print | xargs rm
rm: cannot remove `.' or `..'

--Ed

Edward Vielmetti, U of Michigan math dept <emv@math.lsa.umich.edu>
moderator, comp.archives

pfalstad@phoenix.Princeton.EDU (Paul John Falstad) (10/22/90)

In article <2113@sixhub.UUCP> davidsen@sixhub.UUCP (bill davidsen) writes:
>  It *appears* that xenix quotes its arguments in xargs, since I did a
>small and cautious test and it worked all right. How about testing your

Even if the arguments are quoted, xargs still presents a security
problem if it calls system.  Just about any program that runs a shell is
unsecure.  If your system's xargs calls system, then someone could just create
a file with the quote character in it.  The only really safe way is to do an
execve.

--
Paul Falstad, pfalstad@phoenix.princeton.edu PLink:HYPNOS GEnie:P.FALSTAD
And Dinsdale said, "You've been a naughty boy, Clement," and splits me nostrils
open, and saws me leg off, and pulls me liver out.  And I said, "My name's not
Clement."  And then he loses his temper.  And he nails me head to the floor.

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (10/23/90)

In article <1990Oct21.223729.10521@chinet.chi.il.us> les@chinet.chi.il.us (Leslie Mikesell) writes:
> Even if you eliminate part of the problem by using a '\0' delimiter
> to syncronize find and xargs, you can still get into trouble with a
> file named "-r" appearing at the front of an argument list that might
> also mention directories.

No, you can't. As long as find is invoked upon a fixed directory name
(like . or / or /tmp), every name it prints will start with that. So

  find / -name core -atime +7 -print0 | xargs -0 rm

will be safe. You can always use rm --, but there's no need to.

---Dan

davidsen@sixhub.UUCP (Wm E. Davidsen Jr) (10/23/90)

In article <4203@umbc3.UMBC.EDU> rouben@math9.math.umbc.edu.UUCP (Rouben Rostamian) writes:

| Why "small and cautious"?  To test whether xargs quotes its arguments, 
| in an empty directory do:
| 
| touch "This is a test"
| find . -print | xargs rm

  That's what I mean by small and cautions. 

  Actually I tried creating a file called "abc;date" to see if the date
command would be executed, and abc#x to see if the comment character
was okay. These worked, but embedded blanks caused problems. Obviously
either (a) a shell is not being called to process this, or (b) the
shell is run with IFS redefined.

  Verdict: xenix xargs is better than some, not perfect.

-- 
bill davidsen - davidsen@sixhub.uucp (uunet!crdgw1!sixhub!davidsen)
    sysop *IX BBS and Public Access UNIX
    moderator of comp.binaries.ibm.pc and 80386 mailing list
"Stupidity, like virtue, is its own reward" -me

jeff@onion.pdx.com (Jeff Beadles) (10/23/90)

Well, under the 4.2 BSD based system I have here (Tektronix 4.1 UTek)
it appears that xargs IS quoting it's arguements.  Here's the test that I did,
that's fairly safe to run.

If xargs was not quoting it's arguements, then all files starting with 'a'
would have been deleted.

Script started on Mon Oct 22 22:29:31 1990
jo:jeff> cd /usr/tmp
jo:tmp> ls -al
total 2
drwxrwxrwx  2 root          512 Oct 22 22:29 .
drwxr-xr-x 21 sys           512 Oct 19 23:18 ..
jo:tmp> touch 'a' 'a*' 'aa' 'aaa' 'aaaa'
jo:tmp> ls -al
total 2
drwxrwxrwx  2 root          512 Oct 22 22:29 .
drwxr-xr-x 21 sys           512 Oct 19 23:18 ..
-rw-r--r--  1 jeff            0 Oct 22 22:29 a
-rw-r--r--  1 jeff            0 Oct 22 22:29 a*
-rw-r--r--  1 jeff            0 Oct 22 22:29 aa
-rw-r--r--  1 jeff            0 Oct 22 22:29 aaa
-rw-r--r--  1 jeff            0 Oct 22 22:29 aaaa
jo:tmp> find . -name 'a?' -print | xargs -t rm -f
rm -f ./a* ./aa 
jo:tmp> ls -al
total 2
drwxrwxrwx  2 root          512 Oct 22 22:30 .
drwxr-xr-x 21 sys           512 Oct 19 23:18 ..
-rw-r--r--  1 jeff            0 Oct 22 22:29 a
-rw-r--r--  1 jeff            0 Oct 22 22:29 aaa
-rw-r--r--  1 jeff            0 Oct 22 22:29 aaaa
jo:tmp> exit
script done on Mon Oct 22 22:30:30 1990


A sigh of relief... :-)

	-Jeff
-- 
Jeff Beadles		jeff@onion.pdx.com

boyd@necisa.ho.necisa.oz (Boyd Roberts) (10/23/90)

In article <3940@awdprime.UUCP> tif@doorstop.austin.ibm.com (Paul Chamberlain) writes:
>
>In any case, I've yet to see how "a malicious user [could]
>remove every file on the system."
>

Nor can I.  Since when did xargs(1) use system(3)?

A malicious user may be able to embed newlines in filenames,
but that's not going to trash the _whole_ file-system.

If someone did change xargs(1) to use system(3) it's obviously been broken.
Using system(3) raises all sorts of revolting shell quoting problems -- not
to mention the security holes.



Boyd Roberts			boyd@necisa.ho.necisa.oz.au

``When the going gets wierd, the weird turn pro...''

cpcahil@virtech.uucp (Conor P. Cahill) (10/23/90)

In article <1990Oct20.032718.1874@cbnews.att.com> res@cbnews.att.com (Robert E. Stampfli) writes:
>No user, malicious or not, can remove "every file on the system."
>In fact, I contend it is impossible to remove the last instance of
>an executable that contains the unlink() system call.

This is only true for system V.  BSD allows you to unlink a running executable.


-- 
Conor P. Cahill            (703)430-9247        Virtual Technologies, Inc.,
uunet!virtech!cpcahil                           46030 Manekin Plaza, Suite 160
                                                Sterling, VA 22170 

nomann@rimfaxe.diku.dk (Ole Nomann Thomsen) (10/23/90)

davidsen@sixhub.UUCP (Wm E. Davidsen Jr) writes:


>  It *appears* that xenix quotes its arguments in xargs, since I did a
>small and cautious test and it worked all right. ...

No:
touch "foo bar"
find . -type f -print | xargs ls -l
# Produces:
./foo not found
bar not found
...
# ["ls -l"s deleted]
# on Xenix 2.3.2 .


- Ole. (nomann@diku.dk).

"Information is not knowledge" - Frank Zappa. 

emcguire@ccad.uiowa.edu (Ed McGuire) (10/23/90)

In article <1990Oct21.013603.25168@cbnewsh.att.com> wcs@cbnewsh.att.com (Bill Stewart 201-949-0705 erebus.att.com!wcs) writes:

> Many systems, especially universities, have a convention that
> filenames beginning with '#' are "marked for death".

Among other things, `#' begins the name of a file created in /lost+found.
-- 
peace.  -- Ed
"Vote.  Because it's the Right Thing."

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (10/24/90)

In article <1890@necisa.ho.necisa.oz> boyd@necisa.ho.necisa.oz (Boyd Roberts) writes:
> In article <3940@awdprime.UUCP> tif@doorstop.austin.ibm.com (Paul Chamberlain) writes:
> >In any case, I've yet to see how "a malicious user [could]
> >remove every file on the system."
> A malicious user may be able to embed newlines in filenames,
> but that's not going to trash the _whole_ file-system.

Oh? Each filename he sets up can remove dozens of other files. There are
only so many files in the entire system.

> If someone did change xargs(1) to use system(3) it's obviously been broken.

Agreed.

---Dan

ag@cbmvax.commodore.com (Keith Gabryelski) (10/24/90)

In article <1990Oct20.032718.1874@cbnews.att.com> res@cbnews.att.com (Robert
E. Stampfli) writes:
>No user, malicious or not, can remove "every file on the system."
>In fact, I contend it is impossible to remove the last instance of
>an executable that contains the unlink() system call.

This was, infact, impossible on System V Release 3.x, but in SVR4 it
is possible.  BSD and SunOS varients get this `right', also.

Pax, Keith

cpcahil@virtech.uucp (Conor P. Cahill) (10/24/90)

In article <1890@necisa.ho.necisa.oz> boyd@necisa.ho.necisa.oz (Boyd Roberts) writes:
>Nor can I.  Since when did xargs(1) use system(3)?

It dosn't matter what xargs uses to run the command.  The problem is how
it parses it's input.  If the input is newline separated and a user can
add newlines to a filename, then the user can create a file that will
cause xargs to incorrectly parse it's input.

-- 
Conor P. Cahill            (703)430-9247        Virtual Technologies, Inc.,
uunet!virtech!cpcahil                           46030 Manekin Plaza, Suite 160
                                                Sterling, VA 22170 

greywolf@unisoft.UUCP (The Grey Wolf) (10/25/90)

In article <4062:Oct1518:22:1290@kramden.acf.nyu.edu> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:
>In article <271653D6.1CE8@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>> According to lml@cbnews.att.com (L. Mark Larsen):
>> >I never much cared for xargs since it limits you to an argument list of
>> >only 470 bytes.
>> For the most common use of xargs -- "find ... | xargs command" -- the
>> script below, called "many", does a good job.  Since it doesn't spawn
>> a subshell, it isn't prone to metacharacter-caused security problems.
>
>But it's still susceptible to filenames with carriage returns, and will
>be until find has a -print0 option. Please, please, please don't claim
>that your xargs is by any means secure when a standard command like
>
>  find / -name '#*' -atime +7 -print | xargs rm
>
>lets a malicious user remove every file on the system. Maybe it's
>unreasonable of me to want others to live up to my standard of security,
>but in my eyes no \n-parsing xargs qualifies as ``a good job.'' Sorry.
>
>---Dan

Having looked at the output of find and the input of xargs, there
is definitely a hole ("NO SHIT, SHERLOCK!") (yeah, I saw the other posts).
Just the same, xargs kind of loses functionality if you take away its
ability to handle newlines.  "find $fs -conditions ... -print | xargs cmd"
certainly has more advantages to it than the hole can really offset.
If you're worried about the above command running as root, then don't.
Or don't use "xargs" in that case, use "-exec rm {} \;".
It's that simple.  Don't cripple an otherwise very useful command.
-- 
"This is *not* going to work!"
				"Well, why didn't you say so before?"
"I *did* say so before!"
...!{ucbvax,acad,uunet,amdahl,pyramid}!unisoft!greywolf

john@sco.COM (John R. MacMillan) (10/26/90)

|It dosn't matter what xargs uses to run the command.  The problem is how
|it parses it's input.  If the input is newline separated and a user can
|add newlines to a filename, then the user can create a file that will
|cause xargs to incorrectly parse it's input.

So check that xargs did indeed parse its input correctly.  Instead of

find / -name '#*' -print | xargs rm -f

use

find / -name '#*' -print | xargs patok '.*/#[^/]*' rm -f --

where patok elimnates arguments that don't match the pattern.
Shouldn't be tough to write.  It wouldn't remove files with newlines,
but if patok complains about non-matches, you could find them later and
go slap the file owner's wrist.

Sound reasonable?
-- 
John R. MacMillan       | I'm in love with a McDonald's girl
SCO Canada, Inc.        | She's an angel in a polyester uniform.
john@sco.COM            |      -- barenaked ladies

david@cs.uow.edu.au (David E A Wilson) (10/26/90)

At least with SunOS 4.1 the manual page describes exactly what characters
will cause problems.

     Arguments read in from the standard input are defined to  be
     contiguous  strings  of characters delimited by white space.
     Empty lines are always discarded.  Blanks and  tabs  may  be
     embedded  as  part  of  an  argument  if they are escaped or
     quoted.  Characters enclosed in quotes  (single  or  double)
     are  taken literally, and the delimiting quotes are removed.
     Outside of quoted strings, a `\' (backslash) will escape the
     character it precedes.

Newlines cannot be part of an argument and spaces & quotes would cause so many
problems that you would have to preceed each character with a \ to be sure.
-- 
David Wilson	Dept Comp Sci, Uni of Wollongong	david@cs.uow.edu.au

karl@ficc.ferranti.com (Karl Lehenbauer) (10/27/90)

In article <1990Oct20.032718.1874@cbnews.att.com> res@cbnews.att.com (Robert E. Stampfli) writes:
>No user, malicious or not, can remove "every file on the system."
>In fact, I contend it is impossible to remove the last instance of
>an executable that contains the unlink() system call.

Oh, then I guess it's not a problem...
-- 
-- uunet!sugar!ficc!karl (wk), uunet!sugar!karl (hm)
"The computer programmer is a creator of universes for which he alone is 
 responsible.  Universes of virtually unlimited complexity can be created in 
 the form of computer programs."  -- Joseph Weizenbaum

bhoughto@cmdnfs.intel.com (Blair P. Houghton) (10/27/90)

In article <john.656921455@troch> john@sco.COM (John R. MacMillan) writes:
>
>find / -name '#*' -print | xargs patok '.*/#[^/]*' rm -f --
>
>where patok elimnates arguments that don't match the pattern.
>[patok] shouldn't be too hard to write.

find / -name '#*' -print | egrep -v '.*/#[^/]*' | xargs rm -f --

				--Blair
				  "Took longer to type."

bhoughto@cmdnfs.intel.com (Blair P. Houghton) (10/27/90)

In article <649@inews.intel.com> bhoughto@cmdnfs.intel.com (Blair P. Houghton) writes:
>In article <john.656921455@troch> john@sco.COM (John R. MacMillan) writes:
>>
>>find / -name '#*' -print | xargs patok '.*/#[^/]*' rm -f --
>>
>>where patok elimnates arguments that don't match the pattern.
>>[patok] shouldn't be too hard to write.
>
>find / -name '#*' -print | egrep -v '.*/#[^/]*' | xargs rm -f --

Lose the -v, dimwit.

>				  "Took longer to type."

				--Blair
				  "Egotistical, overreaching,
				   pretentious, typographically
				   unsound geek!"

john@sco.COM (John R. MacMillan) (10/29/90)

|>
|>find / -name '#*' -print | xargs patok '.*/#[^/]*' rm -f --
|>
|>where patok elimnates arguments that don't match the pattern.
|>[patok] shouldn't be too hard to write.
|
|find / -name '#*' -print | egrep -v '.*/#[^/]*' | xargs rm -f --

Of course by the time I'd realized this, Blair had already pointed it
out.  It's so much more painless when you catch your own mistakes.

Speaking of which, you probably don't want the -v to egrep unless you
want to remove only the files the nasty guy wanted you to, and the
pattern should probably have been '/#[^/]*$'.

So now that we can all safely and automatically remove files I think
we should kill this thread before I embarass myself further.
-- 
John R. MacMillan       | I'm in love with a McDonald's girl
SCO Canada, Inc.        | She's an angel in a polyester uniform.
john@sco.COM            |      -- barenaked ladies

levy@mtcchi.uucp (2656-Daniel R. Levy(0000000)0000) (10/29/90)

>>But it's still susceptible to filenames with carriage returns, and will
>>be until find has a -print0 option. Please, please, please don't claim
>>that your xargs is by any means secure when a standard command like
>>
>>  find / -name '#*' -atime +7 -print | xargs rm
>>
>>lets a malicious user remove every file on the system. Maybe it's
>>unreasonable of me to want others to live up to my standard of security,
>>but in my eyes no \n-parsing xargs qualifies as ``a good job.'' Sorry.

For a brief moment there I thought one could do an end run by telling
find not to match files whose names contain newline (e.g. ! -name '*^J*'
where ^J is an actual newline character).  But alas, that does not exclude
files within directories whose names (the directories' that is) contain
newlines.  Oh well.
-- 
* Daniel R. Levy *  uunet!tellab5!mtcchi!levy *                        |
* These views are live; they are not Memorex' *                      --+--
"Because we love something else more than this world we love even      |
this world better than those who know no other" -- C. S. Lewis         |

staff@cadlab.sublink.ORG (Alex Martelli) (10/29/90)

bhoughto@cmdnfs.intel.com (Blair P. Houghton) writes:
	...
>>find / -name '#*' -print | egrep -v '.*/#[^/]*' | xargs rm -f --
>Lose the -v, dimwit.

Won't "egrep '/#'" pass EXACTLY the same lines as "egrep '.*/#[^/]*'"???
After all, it's not as if the RE had to match ALL of the incoming line!
Yours for simpler, more regular expression of Regular Expressions...
-- 
Alex Martelli - CAD.LAB s.p.a., v. Stalingrado 45, Bologna, Italia
Email: (work:) staff@cadlab.sublink.org, (home:) alex@am.sublink.org
Phone: (work:) ++39 (51) 371099, (home:) ++39 (51) 250434; 
Fax: ++39 (51) 366964 (work only), Fidonet: 332/401.3 (home only).

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (10/30/90)

In article <john.657158675@troch> john@sco.COM (John R. MacMillan) writes:
> |find / -name '#*' -print | egrep -v '.*/#[^/]*' | xargs rm -f --

No! Think about links.

> So now that we can all safely and automatically remove files I think
> we should kill this thread before I embarass myself further.

Perhaps people should think about their supposed solutions before
posting them. Not to flame, but you're the eighth person to post an
insecure pipeline in this thread.

Folks, just don't use find | xargs as root until it has -0 to parse
nulls and find has -print0 to match. You'll sleep better at night using
-exec rm than somebody's supposedly secure pipeline from hell.

---Dan

engbert@cs.vu.nl (=Engbert Gerrit IJff) (10/30/90)

In article <303@cadlab.sublink.ORG>,
	staff@cadlab.sublink.ORG (Alex Martelli) writes:
) bhoughto@cmdnfs.intel.com (Blair P. Houghton) writes:
) 	...
) >>find / -name '#*' -print | egrep -v '.*/#[^/]*' | xargs rm -f --
) >Lose the -v, dimwit.
) 
) Won't "egrep '/#'" pass EXACTLY the same lines as "egrep '.*/#[^/]*'"???
) After all, it's not as if the RE had to match ALL of the incoming line!
) Yours for simpler, more regular expression of Regular Expressions...

Yes, you're right. However, if I am right, the original intention
was to delete files whose names started with the # character.
Shouldn't it be "egrep '/#[^/]*$'" then, to check on the basename?
							   ^
Bert IJff

kimcm@diku.dk (Kim Christian Madsen) (11/01/90)

Just my $0.02 to the ongoing discussion of:

	find .... | xargs rm

If you're really worried about *NASTY* users and embedded newlines in files,
why use xargs at the receiving end of find, and not just settle with:

	find .... -exec /bin/rm {} \;

Yes I know that it spawns a lot of processes compared to the xargs solution,
but it is also a secure way of removal of files with embedded newlines.

					Kim Chr. Madsen

iand@labtam.labtam.oz (Ian Donaldson) (11/23/90)

karl@ficc.ferranti.com (Karl Lehenbauer) writes:

>In article <1990Oct20.032718.1874@cbnews.att.com> res@cbnews.att.com (Robert E. Stampfli) writes:
>>No user, malicious or not, can remove "every file on the system."
>>In fact, I contend it is impossible to remove the last instance of
>>an executable that contains the unlink() system call.

If the executable is built with non-shared text (ie unpure) then its
possible on most UNIXen.  On some UNIXen its even possible with shared
text binaries (eg: SVR4, SUNOS4.1)

Ian D