Reputation: 593
I'm currently writing a CGI script in Perl that is supposed to read in a crontab (works), allows for easy manipulation in a web browser (TODO) and should finally set the crontab when editing it is done (fails). To preemtively answer any questions on security: The script is reachable only within a VPN tunnel and so is invisible on the 'Net...
Anyway, here's the code in question:
if($p_action eq 'set')
{
my $l_i = 1;
my $l_param;
if(!open(CRON, "| /usr/bin/sudo /usr/bin/crontab"))
{
print CRON "CRON_TZ=$l_param\n" if(defined ($l_param = $cgi->param("cron_tz")));
print CRON "MAILTO=$l_param\n" if(defined ($l_param = $cgi->param("mailto")));
print CRON "SHELL=$l_param\n" if(($l_param = $cgi->param("shell")) ne 'unset');
print CRON "\n" if(defined $cgi->param("cron_tz") || defined $cgi->param("mailto") || $cgi->param('shell') ne 'unset');
while(defined $cgi->param("line$l_i-min"))
{
my $l_min = $cgi->param("line$l_i-min");
my $l_hour = $cgi->param("line$l_i-hour");
my $l_day = $cgi->param("line$l_i-day");
my $l_month = $cgi->param("line$l_i-month");
my $l_wday = $cgi->param("line$l_i-wday");
my $l_cmd = $cgi->param("line$l_i-cmd");
print CRON "- " if($cgi->param("line$l_i-nolog") eq 'Nolog');
print CRON $l_min.' ';
print CRON $l_hour.' ';
print CRON $l_day.' ';
print CRON $l_month.' ';
print CRON $l_wday.' ';
print CRON $l_cmd."\n";
$l_i++;
}
close(CRON);
}
}
I already have permitted both /usr/bin/crontab -l and /usr/bin/crontab in the sudoers file for execution by wwwrun.
Although crontab is actually executed when opening the pipe, nothing, however, is passed to crontab and so resulting in an empty crontab. It appears that I'm missing something crucial here, but I don't possibly see what it could be. Any clues on how to get this thing unstuck?
P.S.: I want to confine root privileges to as few lines of code as possible so running the script as root is not an option.
Upvotes: 1
Views: 295
Reputation: 118595
sudo
consumes standard input in case a password is supplied, so it is not possible to pipe input to a sudo'ed command.
To install a crontab, crontab
supports the syntax
crontab FILE
So as a workaround you could write the crontab contents to a file and call
system("/usr/bin/sudo /usr/bin/crontab $FILE");
Upvotes: 1
Reputation: 593
After letting this thing rest and then doing some reading (man crontab
actually gave me the all-important hint) I just dropped the pipe in the open
statement and instead wrote the crontab to a temporary file.
With that done, all I needed to do was invoking sudo /usr/bin/crontab /tmp/ without having to add a redirection to install the new crontab.
Problem solved...
Upvotes: 0
Reputation: 19375
What MrCleanX probably intended to hint at with his reference open - perldoc is that
Open returns nonzero on success, the undefined value otherwise.
So, by writing
if(!open(CRON, "| /usr/bin/sudo /usr/bin/crontab"))
you are skipping the following block if the open
succeeded instead of if it failed.
Upvotes: 0
Reputation: 501
I think you're missing a "-" argument on the crontab. Without that, contab doesn't know that you want it to read from stdin. (Also, not using "-u" with sudo can be a bit dicey.)
See "man crontab" for details...
Upvotes: 0