gryf has asked for the wisdom of the Perl Monks concerning the following question:

I've got a CGI script where I am trying to run a shell command and report the results. For some reason it works as expected from the command line but not via the web server.
#!/usr/bin/perl -w use Data::Dumper; use CGI qw/:standard/; use CGI::Carp 'fatalsToBrowser'; use strict; $ENV{'PATH'} = '/bin:/usr/bin'; delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; my $cmd = qq(rsh case3 /home/dncms/bin/testForwardLookup.pl case3 &|); my $result = ""; print header; print "running {$cmd}\n"; open(FOO,$cmd) || die "Failed to run $cmd: $!"; while (<FOO>) { print "feh: "; $result .= "$_\n"; } close(FOO); print ( $result );
Here's the shell response:
dncms@alien 531 > perl foo.pl Content-Type: text/html; charset=ISO-8859-1 running {rsh case3 /home/dncms/bin/testForwardLookup.pl case3 &|} feh: 130.42.0.117
Here's the sum total of the web response:
running {rsh case3 /home/dncms/bin/testForwardLookup.pl case3 &|}
It doesn't seem to matter whether I use setguid or not. Does anyone have an idea of what I'm doing wrong? Thanks!

Replies are listed 'Best First'.
Re: shell redirect CGI failure
by ikegami (Patriarch) on Sep 10, 2007 at 17:00 UTC

    It could be reaching the die statement. die sends its message to STDERR, so it wouldn't appear in the web reponse. Did you check your error logs?

    Also, you don't check if the child returned an error. (See Using open() for IPC.)

      > die sends its message to STDERR, so it wouldn't appear in the web reponse.

      Yeah but. He put use CGI::Carp 'fatalsToBrowser'; Shouldn't that take care of it?

        Yup, forgot to check for that. It's still a good idea to check the logs.

Re: shell redirect CGI failure
by sauoq (Abbot) on Sep 10, 2007 at 17:56 UTC
    It doesn't seem to matter whether I use setguid or not. Does anyone have an idea of what I'm doing wrong?

    You're probably looking in the right place. It probably is a permissions issue. First thing I'd do is change the command you run to /usr/bin/id and try it.

    -sauoq
    "My two cents aren't worth a dime.";
Re: shell redirect CGI failure
by ikegami (Patriarch) on Sep 10, 2007 at 18:06 UTC
    I'm very confused by the &. Is that supposed to be there?
      I've discovered that I'm actually bouncing into a Perl security issue. I got past the last problem by calling perl with -TUw, this way the script should run but error on tainted data. Here's the error:
      Running {/usr/bin/rsh case3 /usr/sbin/ping n0s30243.dnilab.cs.boeing.c +om 5 &|} [Tue Sep 11 09:01:13 2007] 7: Insecure dependency in piped open while +running setgid at /dev/fd/7 line 195.
      I'm trying to make sure the data is untainted:
      #--------------------- untaint the shell command ----------------- +------------------------# # --- untaint the $nnm argument if ($nnm !~ /([a-z]+ms1.[nkth][sxev].(cs.)?boeing.com)/ && $nnm !~ + /(^case3.*)/) { print h3("NNM tainted: $nnm\n"); die; } $nnm = $1; # --- untaint the $command argument unless ($command =~ /^[\w\s.\-\/]+$/ ) ##/^([-\@\w.]+)$/) ### #= +~ m#^([\w\.\-/]+)$#) { print h3("command tainted: $command"); print li($1); print li($2); print li($3); print li("$4\n"); die; } $command = $1; # --- untaint the $debug argument unless ($debug =~ m#^([\w\.\-/]+)$#) { die h3("debug tainted: $debug\n"); } $debug = $1; #--------------------------------------------------------------- +--------------------------# open(RSH, $cmd ) || die "Failed to run {$cmd}: $!"; while (<RSH>) { $result .= "$_\n"; push( @lines, $_ ); } close(RSH); if ( $debug ) { print hr; print br; print i("Result: {" . $result +. "}"); } print br( "Lines returned: (" . @lines . ")" ); print Dumper( @lines ); print br,"------------", p;
      I've also added logic that should secure the environment for perl:
      # redirect stderror to screen BEGIN { use CGI::Carp qw(carpout); carpout(\*STDOUT); } # Turn off output buffering $|=1; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; #delete $ENV{'PATH'},$ENV{'IFS'}, $ENV{'CDPATH'}, $ENV{'BASH_ENV'}; $ENV{'PATH'} = '/usr/bin;/usr/sbin'; # Set real UID to effective UID (dncms instead of oracle) so that rsh +works $< = $>; # Verify script is setuid by checking that dncnms is executing my $uid = getpwnam('dncms'); if ($< != $uid) { die "Error - $0 must be run as dncms $uid $<\n"; }
      As for the '&', that's for the shell to run the command in the background and the '|' should return the result to the perl script.

        As for the '&', that's for the shell to run the command in the background

        Again, why? That shell is never going to run anything but rsh, so why run rsh "in the background"? At best, it does nothing. At worse, the shell exits before rsh does and you can't collect the info from rsh and the remote process.

        In fact, I think a shell won't even get loaded without the & because the command contains no shell meta characters. So at best, & slows down the program and hides the PID of rsh.