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

Hello monks,

I need some advice with system calls. I have been researching for the past few days and have not been able to find what I'm looking for. I'm running a Perl CGI script from a webpage that needs to make some system calls. I'm running into a problem where no system calls will work at all. Not even a simple one such as "ls". Here's the important part of the code:

#! /usr/bin/perl -T use strict; use warnings; $ENV{"PATH"} = ""; chdir "/home/user/"; chomp($update_location); # $update_location is: # /home/user/test/update_test.pl # it's retrieved from the database my $cmd = sprintf("perl %s", $update_location); system($cmd); if ($? == -1) { logger("failed to execute: $!\n"); # logger is a simple sub that appends whatever is passed # to an error.txt file } elsif ($? & 127) { logger("child died with signal coredump\n"); } else { my $sys_code = $? >> 8; logger("child exited with value $sys_code\n"); }

When I run the code above, my error.txt file says "failed to execute: Permission denied". When I comment out the chdir line, error.txt says "failed to execute: No such file or directory".
Whether or not I comment out the chdir line, if I try a simple system call such as:

system("ls");

it responds with "failed to execute: No such file or directory".
Permissions are
/home/ drwxr-xr-x
/home/user/ drwxr-xr-x
/home/user/test/ drwxr-xr-x
/home/user/test/update_test.pl -rwxr-xr-x
I already ran chmod +x on update_test.pl and am able to run it from a terminal.

Any ideas as to why the system calls won't work? Thank you for any advice you can give me!

Replies are listed 'Best First'.
Re: System calls from CGI script
by Corion (Patriarch) on Nov 04, 2016 at 17:31 UTC

    You empty out $ENV{PATH} at the top of your script. This is good.

    But then, you expect $ENV{"PATH"} to still be available when you call system. That's not how it works.

    Personally, I recommend that you hardcode the path to perl or use $^X for the path to the current interpreter:

    my $perl = $^X; system($perl, $update_location) == 0 or die "Couldn't launch $perl: $!";

      Thank you very much for your advice. Hard-coding the path to perl worked like a charm!

Re: System calls from CGI script
by haukex (Archbishop) on Nov 04, 2016 at 17:35 UTC

    Hi whitdan,

    First of all, the architecture of having a CGI script make system calls is something to be very, very careful with, as it can easily allow attackers access to your system. The very least you need to do is protect access to the server / the script with some kind of authentication mechanism. Even then, you need to be very careful with any input you get and might want to pass along to the system command.

    Having said that, you're clearing the PATH environment variable and then attempting to call perl without the full pathname. Try calling it as /usr/bin/perl (or wherever your perl binary is installed).

    Another thing to remember is that the user the web server and CGI script is being run as often doesn't have permissions to anything, e.g. the nobody user.

    Hope this helps,
    -- Hauke D

      Thanks for the advice. Security is one of the top priorities when working with pages on websites. The user must be logged into our system to access the page and the system calls will only be used to run set values from the database.

      Thank you for your suggestion, it worked perfectly. I needed to add the absolute path to perl.

Re: System calls from CGI script
by shmem (Chancellor) on Nov 04, 2016 at 23:52 UTC
    my $cmd = sprintf("perl %s", $update_location); system($cmd);

    You could avoid shelling out /bin/sh and a new instance of perl via require:

    require $update_location;

    This way your required script will also run with taint checking, which is always a good thing to do for CGI scripts (++ for the OP for that alone). Some points to be aware of:

    • $update_location must be an absolute path
    • it inherits globals from the current process
    • it must return a true value; this can be done by placing a line containing 1; at the end of the script $update_location
    • the invoking script MUST NOT be persistent (e.g. running under mod_perl or fastcgi) since requiring is done only once

    This might fit - or not - your requirements; it is just another way to do it (and a safer one IMHO).

    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'