1

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.

Robidu
  • 576
  • 3
  • 17
  • 1
    duplicate: http://stackoverflow.com/questions/18133498/how-to-use-perl-to-modify-crontab – arcee123 Jun 27 '16 at 20:56
  • [open - perldoc](http://perldoc.perl.org/functions/open.html) – MrCleanX Jun 27 '16 at 21:18
  • Maybe, but since the thread is closed, there's no way of posting any further questions - plus the solution shown there requires the script to have root privileges - which isn't the case here, as I have already stated in the initial post. Piping things into crontab itself furthermore is rather trivial, but the question here is how do I do this in conjunction with sudo? – Robidu Jun 27 '16 at 22:20
  • Even if you get it all working, don't you still have to restart crond as root? – MrCleanX Jun 27 '16 at 23:08
  • First of all, no. Second, this is absolutely no part of the question at hand. – Robidu Jun 28 '16 at 01:03

4 Answers4

1

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");
mob
  • 117,087
  • 18
  • 149
  • 283
0

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...

D Hydar
  • 502
  • 2
  • 8
  • I've already tried this, but to no avail. It yields the same result in either case. Furthermore, the way I take it is that -u isn't required, because root is implied when used without this parameter. – Robidu Jun 27 '16 at 22:13
0

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.

Armali
  • 18,255
  • 14
  • 57
  • 171
0

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...

Robidu
  • 576
  • 3
  • 17