Robidu
Robidu

Reputation: 593

How do I set a crontab from a Perl script?

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

Answers (4)

mob
mob

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

Robidu
Robidu

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

Armali
Armali

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

D Hydar
D Hydar

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

Related Questions