Ahah! I think I have confirmed that IPC::Open2 does not use the shell. And my original post to the list specifying the quotes around "Program Files" appears to not really be necessary after all... I don't know what my problem was the other day, and I'm sorry if I've caused anybody grief or alarm!
The gory details follow.
This test script:
use IPC::Open2;
# using the shell, chokes on space
$pid = open2($rdrfh, $wtrfh, 'C:\Program Files\SWISH-E\swish-e.exe -w foo');
print scalar <$rdrfh>, '-'x50, "\n";
# using the shell, properly escaped
$pid = open2($rdrfh, $wtrfh, 'C:\"Program Files"\SWISH-E\swish-e.exe -w foo');
print scalar <$rdrfh>, '-'x50, "\n";
# without using the shell
$pid = open2($rdrfh, $wtrfh, 'C:\Program Files\SWISH-E\swish-e.exe', "-w", "foo");
print scalar <$rdrfh>, '-'x50, "\n";
produces this output:
'C:\Program' is not recognized as an internal or external command,
operable program or batch file.
--------------------------------------------------
# SWISH format: 2.2.3
--------------------------------------------------
# SWISH format: 2.2.3
--------------------------------------------------
So, swish.cgi using open2 should be avoiding the shell. I must have had some other problem the other day, and I finally got the swish binary to run by hand from DOS by quoting "Program Files" so I tried that in the swish.cgi config and it worked.
At least, system(1, @array) is supposed to not use the shell according to perldoc perlfunc and perlport, and therefore open2($rfh, $wfh, @array) is supposed to not use it too, and this example shows that it doesn't choke on the space like it would if it was using the shell.
SO: a pretty good solution for all the other system calls would be something like this subroutine (with code I used to test it):
#print safer_backticks('C:\Program Files\SWISH-E\swish-e.exe -w foo'); # see if the erroneous usage thing works
#print "\n", '-'x50, "\n";
print safer_backticks('C:\Program Files\SWISH-E\swish-e.exe', '-w', 'foo>junk');
print "\n", '-'x50, "\n";
print safer_backticks('C:\Program Files\SWISH-E\swish-e.exe', '-w', 'foo');
print "\n", '-'x50, "\n";
print safer_backticks('catdoc', "-a", 'N:\IT\Procedures\testdoc.doc') ;
# safer_backticks('catdoc', 'blah', 'blah')
# a safer alternative to `catdoc blah blah`
sub safer_backticks {
die "this won't be safe unless you pass the args as an array, smarty!" if $#_ == 0 && $_[0] =~ /[ ;]/;
warn "oop, now now you're not trying none of that fancy pipe/file redirection shell business are you?" if join(' ',@_) =~ /[\|\>\<]/;
use IPC::Open2;
my ( $rdrfh, $wtrfh );
my $pid = IPC::Open2::open2($rdrfh, $wtrfh, @_ );
return <$rdrfh>;
}
It's not as good as the forking you're doing in case swish runs for a long time, but it's better than backticks and about as easy to use.
-n
>>> <moseley@hank.org> 06/10/03 09:20PM >>>
On Tue, Jun 10, 2003 at 05:24:01PM -0700, Nathan Vonnahme wrote:
> > Well, without fork/exec on Windows it's hard. I'm sure there's some
> > Win32 specific functions to do that, but I have never looked into it. I
> > spent a *year* posting to Win32 CGI lists asking how to securely run an
> > external program (like swish-e) from a CGI script and never got any
> > response.
>
> I was thinking that that's what you're doing with IPC::Open2, right?
>
> At least, its documentation claims:
> $pid = open2(\*RDRFH, \*WTRFH, 'some cmd and args');
> # or without using the shell
> $pid = open2(\*RDRFH, \*WTRFH, 'some', 'cmd', 'and', 'args');
> so
> since you're passing an array it shouldn't use the shell (and as far
> as I can tell from reading IPC::Open* and IO::Pipe, it doesn't), but
> apparently on Windows it still barfs on that unescaped space, so it
> must be interacting with DOS somehow. I guess if you use
> system(@array) it's not supposed to use the shell, but if the first
> arg has a space it probably thinks you do need to use the shell, or
> something like that?
Most of that documentation was written before porting Perl to Windows.
Anyway, that's what I hoped when I switched form a piped open to
IPC::Open. Someone recommended it to me at one point as a solution to
the shell problem. I had Windows at the time and I do remember a few
choice words I had after switching to IPC and seeing that the shell was
still used. At least that's what I remember happening.
> The escaping and quoting method above should be find for everything,
> but I think it would be possible to just write a backtick() type
> subroutine that uses IPC::Open2 or 3, then I think you would be
> forever free from shell metacharacter exploits, though you'd still
> need to do the escaping/quoting thing on Windows at least. It might
> not be worth it though... just escaping and quoting should be pretty
> foolproof.
IPC::Open2 uses IPC::Open3, and when running under windows I think it
finally uses a system(). Let me look. (tap..tap...tap...) yes, open3
calls _open3 which checks $do_spawn which is true on Win32 and then
calls spawn_with_handles() which does the system call:
$pid = eval { system 1, @_ }; # 1 == P_NOWAIT
I'm not very clear on that style of system() call, but it is described
somewhere in perldoc perlport. IIRC, that call does a spawn() on
Windows -- not that I know what that does.
That's all just a quick look, though.
I don't have Windows, but it would be really interesting if you could
try IPC::Open3 and throw in some metachars and see it it croaks. Maybe
mess with COMSPEC or whatever Windows uses to define the shell. It
would be interesting to see if I was mistaken about IPC::Open* running
through the shell. But, the requirement to place the swish-e binary
path in quotes makes me think it does, though.
On other systems fork/exec is the way to go. On Windows maybe
Win32::Process would be the way to go. Someone else will need to go
down that road.
Thanks,
--
Bill Moseley
moseley@hank.org
Received on Thu Jun 12 01:21:34 2003