[comp.lang.perl] Beware xargs security holes

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

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

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         |