root/src/do_command.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. do_command
  2. child_process
  3. safe_p

   1 /* Copyright 1988,1990,1993,1994 by Paul Vixie
   2  * All rights reserved
   3  */
   4 
   5 /*
   6  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
   7  * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
   8  *
   9  * Permission to use, copy, modify, and distribute this software for any
  10  * purpose with or without fee is hereby granted, provided that the above
  11  * copyright notice and this permission notice appear in all copies.
  12  *
  13  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
  14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  15  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
  16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
  19  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  20  */
  21 
  22 #include "config.h"
  23 
  24 #include <ctype.h>
  25 #include <errno.h>
  26 #include <pwd.h>
  27 #include <signal.h>
  28 #include <stdio.h>
  29 #include <stdlib.h>
  30 #include <string.h>
  31 #include <sys/wait.h>
  32 #include <unistd.h>
  33 
  34 #include "externs.h"
  35 #include "funcs.h"
  36 #include "globals.h"
  37 #include "structs.h"
  38 
  39 #ifndef isascii
  40 # define isascii(c)     ((unsigned)(c)<=0177)
  41 #endif
  42 
  43 static int child_process(entry *, char **);
  44 static int safe_p(const char *, const char *);
  45 
  46 void do_command(entry * e, user * u) {
     /* [previous][next][first][last][top][bottom][index][help]  */
  47         pid_t pid = getpid();
  48         int ev;
  49         char **jobenv = 0L;
  50 
  51         Debug(DPROC, ("[%ld] do_command(%s, (%s,%ld,%ld))\n",
  52                         (long) pid, e->cmd, u->name,
  53                         (long) e->pwd->pw_uid, (long) e->pwd->pw_gid));
  54 
  55                 /* fork to become asynchronous -- parent process is done immediately,
  56                  * and continues to run the normal cron code, which means return to
  57                  * tick().  the child and grandchild don't leave this function, alive.
  58                  *
  59                  * vfork() is unsuitable, since we have much to do, and the parent
  60                  * needs to be able to run off and fork other processes.
  61                  */
  62                 switch (fork()) {
  63         case -1:
  64                 log_it("CRON", pid, "CAN'T FORK", "do_command", errno);
  65                 break;
  66         case 0:
  67                 /* child process */
  68                 acquire_daemonlock(1);
  69                 /* Set up the Red Hat security context for both mail/minder and job processes:
  70                  */
  71                 if (cron_set_job_security_context(e, u, &jobenv) != 0) {
  72                         _exit(ERROR_EXIT);
  73                 }
  74                 ev = child_process(e, jobenv);
  75 #ifdef WITH_PAM
  76                 cron_close_pam();
  77 #endif
  78                 env_free(jobenv);
  79                 Debug(DPROC, ("[%ld] child process done, exiting\n", (long) getpid()));
  80                 _exit(ev);
  81                 break;
  82         default:
  83                 /* parent process */
  84                 break;
  85         }
  86         Debug(DPROC, ("[%ld] main process returning to work\n", (long) pid));
  87 }
  88 
  89 static int child_process(entry * e, char **jobenv) {
     /* [previous][next][first][last][top][bottom][index][help]  */
  90         int stdin_pipe[2], stdout_pipe[2];
  91         char *input_data, *usernm, *mailto, *mailfrom;
  92         int children = 0;
  93         pid_t pid = getpid();
  94         struct sigaction sa;
  95 
  96         /* Ignore SIGPIPE as we will be writing to pipes and do not want to terminate
  97            prematurely */
  98         memset(&sa, 0, sizeof(sa));
  99         sa.sa_handler = SIG_IGN;
 100         sigaction(SIGPIPE, &sa, NULL);
 101 
 102         /* our parent is watching for our death by catching SIGCHLD.  we
 103          * do not care to watch for our children's deaths this way -- we
 104          * use wait() explicitly.  so we have to reset the signal (which
 105          * was inherited from the parent).
 106          */
 107         sa.sa_handler = SIG_DFL;
 108         sigaction(SIGCHLD, &sa, NULL);
 109 
 110 
 111         Debug(DPROC, ("[%ld] child_process('%s')\n", (long) getpid(), e->cmd));
 112 #ifdef CAPITALIZE_FOR_PS
 113         /* mark ourselves as different to PS command watchers by upshifting
 114          * our program name.  This has no effect on some kernels.
 115          */
 116         /*local */  {
 117                 char *pch;
 118 
 119                 for (pch = ProgramName; *pch; pch++)
 120                         *pch = (char)MkUpper(*pch);
 121         }
 122 #endif /* CAPITALIZE_FOR_PS */
 123 
 124         /* discover some useful and important environment settings
 125          */
 126         usernm = e->pwd->pw_name;
 127         mailto = env_get("MAILTO", jobenv);
 128         mailfrom = env_get("MAILFROM", e->envp);
 129 
 130         /* create some pipes to talk to our future child
 131          */
 132         if (pipe(stdin_pipe) == -1) {   /* child's stdin */
 133                 log_it("CRON", pid, "PIPE() FAILED", "stdin_pipe", errno);
 134                 return ERROR_EXIT;
 135         }
 136 
 137         if (pipe(stdout_pipe) == -1) {  /* child's stdout */
 138                 log_it("CRON", pid, "PIPE() FAILED", "stdout_pipe", errno);
 139                 return ERROR_EXIT;
 140         }
 141 
 142         /* since we are a forked process, we can diddle the command string
 143          * we were passed -- nobody else is going to use it again, right?
 144          *
 145          * if a % is present in the command, previous characters are the
 146          * command, and subsequent characters are the additional input to
 147          * the command.  An escaped % will have the escape character stripped
 148          * from it.  Subsequent %'s will be transformed into newlines,
 149          * but that happens later.
 150          */
 151         /*local */  {
 152                 int escaped = FALSE;
 153                 int ch;
 154                 char *p;
 155 
 156                 for (input_data = p = e->cmd;
 157                         (ch = *input_data) != '\0'; input_data++, p++) {
 158                         if (p != input_data)
 159                                 *p = (char)ch;
 160                         if (escaped) {
 161                                 if (ch == '%')
 162                                         *--p = (char)ch;
 163                                 escaped = FALSE;
 164                                 continue;
 165                         }
 166                         if (ch == '\\') {
 167                                 escaped = TRUE;
 168                                 continue;
 169                         }
 170                         if (ch == '%') {
 171                                 *input_data++ = '\0';
 172                                 break;
 173                         }
 174                 }
 175                 *p = '\0';
 176         }
 177 
 178 
 179         /* fork again, this time so we can exec the user's command.
 180          */
 181         switch (fork()) {
 182         case -1:
 183                 log_it("CRON", pid, "CAN'T FORK", "child_process", errno);
 184                 return ERROR_EXIT;
 185                 /*NOTREACHED*/
 186         case 0:
 187                 Debug(DPROC, ("[%ld] grandchild process fork()'ed\n", (long) getpid()));
 188 
 189                 /* write a log message.  we've waited this long to do it
 190                  * because it was not until now that we knew the PID that
 191                  * the actual user command shell was going to get and the
 192                  * PID is part of the log message.
 193                  */
 194                 if ((e->flags & DONT_LOG) == 0) {
 195                         char *x = mkprints((u_char *) e->cmd, strlen(e->cmd));
 196 
 197                         log_it(usernm, getpid(), "CMD", x, 0);
 198                         free(x);
 199                 }
 200 
 201                 if (cron_change_user_permanently(e->pwd, env_get("HOME", jobenv)) < 0)
 202                         _exit(ERROR_EXIT);
 203 
 204                 /* get new pgrp, void tty, etc.
 205                  */
 206                 (void) setsid();
 207 
 208                 /* reset the SIGPIPE back to default so the child will terminate
 209                  * if it tries to write to a closed pipe
 210                  */
 211                 sa.sa_handler = SIG_DFL;
 212                 sigaction(SIGPIPE, &sa, NULL);
 213 
 214                 /* close the pipe ends that we won't use.  this doesn't affect
 215                  * the parent, who has to read and write them; it keeps the
 216                  * kernel from recording us as a potential client TWICE --
 217                  * which would keep it from sending SIGPIPE in otherwise
 218                  * appropriate circumstances.
 219                  */
 220                 close(stdin_pipe[WRITE_PIPE]);
 221                 close(stdout_pipe[READ_PIPE]);
 222 
 223                 /* grandchild process.  make std{in,out} be the ends of
 224                  * pipes opened by our daddy; make stderr go to stdout.
 225                  */
 226                 if (stdin_pipe[READ_PIPE] != STDIN) {
 227                         dup2(stdin_pipe[READ_PIPE], STDIN);
 228                         close(stdin_pipe[READ_PIPE]);
 229                 }
 230                 if (stdout_pipe[WRITE_PIPE] != STDOUT) {
 231                         dup2(stdout_pipe[WRITE_PIPE], STDOUT);
 232                         close(stdout_pipe[WRITE_PIPE]);
 233                 }
 234                 dup2(STDOUT, STDERR);
 235 
 236                 /*
 237                  * Exec the command.
 238                  */
 239                 {
 240                         char *shell = env_get("SHELL", jobenv);
 241                         int fd, fdmax = getdtablesize();
 242 
 243                         /* close all unwanted open file descriptors */
 244                         for(fd = STDERR + 1; fd < fdmax; fd++) {
 245                                 close(fd);
 246                         }
 247 
 248 #if DEBUGGING
 249                         if (DebugFlags & DTEST) {
 250                                 fprintf(stderr, "debug DTEST is on, not exec'ing command.\n");
 251                                 fprintf(stderr, "\tcmd='%s' shell='%s'\n", e->cmd, shell);
 252                                 _exit(OK_EXIT);
 253                         }
 254 #endif           /*DEBUGGING*/
 255                                 execle(shell, shell, "-c", e->cmd, (char *) 0, jobenv);
 256                         fprintf(stderr, "execl: couldn't exec `%s'\n", shell);
 257                         perror("execl");
 258                         _exit(ERROR_EXIT);
 259                 }
 260                 break;
 261         default:
 262                 cron_restore_default_security_context();
 263                 /* parent process */
 264                 break;
 265         }
 266 
 267         children++;
 268 
 269         /* middle process, child of original cron, parent of process running
 270          * the user's command.
 271          */
 272 
 273         Debug(DPROC, ("[%ld] child continues, closing pipes\n", (long) getpid()));
 274 
 275         /* close the ends of the pipe that will only be referenced in the
 276          * grandchild process...
 277          */
 278         close(stdin_pipe[READ_PIPE]);
 279         close(stdout_pipe[WRITE_PIPE]);
 280 
 281         /*
 282          * write, to the pipe connected to child's stdin, any input specified
 283          * after a % in the crontab entry.  while we copy, convert any
 284          * additional %'s to newlines.  when done, if some characters were
 285          * written and the last one wasn't a newline, write a newline.
 286          *
 287          * Note that if the input data won't fit into one pipe buffer (2K
 288          * or 4K on most BSD systems), and the child doesn't read its stdin,
 289          * we would block here.  thus we must fork again.
 290          */
 291 
 292         if (*input_data && fork() == 0) {
 293                 FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w");
 294                 int need_newline = FALSE;
 295                 int escaped = FALSE;
 296                 int ch;
 297 
 298                 Debug(DPROC, ("[%ld] child2 sending data to grandchild\n",
 299                                 (long) getpid()));
 300 
 301                 /* reset the SIGPIPE back to default so the child will terminate
 302                  * if it tries to write to a closed pipe
 303                  */
 304                 sa.sa_handler = SIG_DFL;
 305                 sigaction(SIGPIPE, &sa, NULL);
 306 
 307                 /* close the pipe we don't use, since we inherited it and
 308                  * are part of its reference count now.
 309                  */
 310                 close(stdout_pipe[READ_PIPE]);
 311 
 312                 if (cron_change_user_permanently(e->pwd, env_get("HOME", jobenv)) < 0)
 313                         _exit(ERROR_EXIT);
 314                 /* translation:
 315                  *  \% -> %
 316                  *  %  -> \n
 317                  *  \x -> \x    for all x != %
 318                  */
 319                 while ((ch = *input_data++) != '\0') {
 320                         if (escaped) {
 321                                 if (ch != '%')
 322                                         putc('\\', out);
 323                         }
 324                         else {
 325                                 if (ch == '%')
 326                                         ch = '\n';
 327                         }
 328 
 329                         if (!(escaped = (ch == '\\'))) {
 330                                 putc(ch, out);
 331                                 need_newline = (ch != '\n');
 332                         }
 333                 }
 334                 if (escaped)
 335                         putc('\\', out);
 336                 if (need_newline)
 337                         putc('\n', out);
 338 
 339                 /* close the pipe, causing an EOF condition.  fclose causes
 340                  * stdin_pipe[WRITE_PIPE] to be closed, too.
 341                  */
 342                 fclose(out);
 343 
 344                 Debug(DPROC, ("[%ld] child2 done sending to grandchild\n",
 345                                 (long) getpid()));
 346                 _exit(0);
 347         }
 348 
 349         /* close the pipe to the grandkiddie's stdin, since its wicked uncle
 350          * ernie back there has it open and will close it when he's done.
 351          */
 352         close(stdin_pipe[WRITE_PIPE]);
 353 
 354         children++;
 355 
 356         /*
 357          * read output from the grandchild.  it's stderr has been redirected to
 358          * it's stdout, which has been redirected to our pipe.  if there is any
 359          * output, we'll be mailing it to the user whose crontab this is...
 360          * when the grandchild exits, we'll get EOF.
 361          */
 362 
 363         Debug(DPROC, ("[%ld] child reading output from grandchild\n",
 364                         (long) getpid()));
 365 
 366         /*local */  {
 367                 FILE *in = fdopen(stdout_pipe[READ_PIPE], "r");
 368                 int ch = getc(in);
 369 
 370                 if (ch != EOF) {
 371                         FILE *mail = NULL;
 372                         int bytes = 1;
 373                         int status = 0;
 374 #if defined(SYSLOG)
 375                         char logbuf[1024];
 376                         int bufidx = 0;
 377                         if (SyslogOutput) {
 378                                 if (ch != '\n')
 379                                         logbuf[bufidx++] = (char)ch;
 380                         }
 381 #endif
 382 
 383                         Debug(DPROC | DEXT,
 384                                 ("[%ld] got data (%x:%c) from grandchild\n",
 385                                         (long) getpid(), ch, ch));
 386 
 387                                 /* get name of recipient.  this is MAILTO if set to a
 388                                  * valid local username; USER otherwise.
 389                                  */
 390                                 if (mailto) {
 391                                 /* MAILTO was present in the environment
 392                                  */
 393                                 if (!*mailto) {
 394                                         /* ... but it's empty. set to NULL
 395                                          */
 396                                         mailto = NULL;
 397                                 }
 398                         }
 399                         else {
 400                                 /* MAILTO not present, set to USER.
 401                                  */
 402                                 mailto = usernm;
 403                         }
 404 
 405                         /* get sender address.  this is MAILFROM if set (and safe),
 406                          * the user account name otherwise.
 407                          */
 408                         if (!mailfrom || !*mailfrom || !safe_p(usernm, mailfrom)) {
 409                                 mailfrom = e->pwd->pw_name;
 410                         }
 411 
 412                         /* if we are supposed to be mailing, MAILTO will
 413                          * be non-NULL.  only in this case should we set
 414                          * up the mail command and subjects and stuff...
 415                          */
 416 
 417                         /* Also skip it if MailCmd is set to "off" */
 418                         if (mailto && safe_p(usernm, mailto)
 419                                 && strncmp(MailCmd,"off",3) && !SyslogOutput) {
 420                                 char **env;
 421                                 char mailcmd[MAX_COMMAND+1]; /* +1 for terminator */
 422                                 char hostname[MAXHOSTNAMELEN];
 423                                 char *content_type = env_get("CONTENT_TYPE", jobenv),
 424                                         *content_transfer_encoding =
 425                                         env_get("CONTENT_TRANSFER_ENCODING", jobenv);
 426 
 427                                 gethostname(hostname, MAXHOSTNAMELEN);
 428 
 429                                 if (MailCmd[0] == '\0') {
 430                                         if (snprintf(mailcmd, sizeof mailcmd, MAILFMT, MAILARG, mailfrom)
 431                                                 >= sizeof mailcmd) {
 432                                                 fprintf(stderr, "mailcmd too long\n");
 433                                                 (void) _exit(ERROR_EXIT);
 434                                         }
 435                                 }
 436                                 else {
 437                                         strncpy(mailcmd, MailCmd, MAX_COMMAND+1);
 438                                 }
 439                                 if (!(mail = cron_popen(mailcmd, "w", e->pwd, jobenv))) {
 440                                         perror(mailcmd);
 441                                         (void) _exit(ERROR_EXIT);
 442                                 }
 443 
 444                                 fprintf(mail, "From: \"(Cron Daemon)\" <%s>\n", mailfrom);
 445                                 fprintf(mail, "To: %s\n", mailto);
 446                                 fprintf(mail, "Subject: Cron <%s@%s> %s\n",
 447                                         usernm, first_word(hostname, "."), e->cmd);
 448 
 449 #ifdef MAIL_DATE
 450                                 fprintf(mail, "Date: %s\n", arpadate(&StartTime));
 451 #endif /*MAIL_DATE */
 452                                 fprintf(mail, "MIME-Version: 1.0\n");
 453                                 if (content_type == 0L) {
 454                                         fprintf(mail, "Content-Type: text/plain; charset=%s\n",
 455                                                 cron_default_mail_charset);
 456                                 }
 457                                 else {  /* user specified Content-Type header. 
 458                                                  * disallow new-lines for security reasons 
 459                                                  * (else users could specify arbitrary mail headers!)
 460                                                  */
 461                                         char *nl = content_type;
 462                                         size_t ctlen = strlen(content_type);
 463                                         while ((*nl != '\0')
 464                                                 && ((nl = strchr(nl, '\n')) != 0L)
 465                                                 && (nl < (content_type + ctlen))
 466                                                 )
 467                                                 *nl = ' ';
 468                                         fprintf(mail, "Content-Type: %s\n", content_type);
 469                                 }
 470                                 if (content_transfer_encoding != 0L) {
 471                                         char *nl = content_transfer_encoding;
 472                                         size_t ctlen = strlen(content_transfer_encoding);
 473                                         while ((*nl != '\0')
 474                                                 && ((nl = strchr(nl, '\n')) != 0L)
 475                                                 && (nl < (content_transfer_encoding + ctlen))
 476                                                 )
 477                                                 *nl = ' ';
 478                                         fprintf(mail, "Content-Transfer-Encoding: %s\n",
 479                                                 content_transfer_encoding);
 480                                 }
 481 
 482                                 /* The Auto-Submitted header is
 483                                  * defined (and suggested by) RFC3834.
 484                                  */
 485                                 fprintf(mail, "Auto-Submitted: auto-generated\n");
 486                                 fprintf(mail, "Precedence: bulk\n");
 487 
 488                                 for (env = jobenv; *env; env++)
 489                                         fprintf(mail, "X-Cron-Env: <%s>\n", *env);
 490                                 fprintf(mail, "\n");
 491 
 492                                 /* this was the first char from the pipe
 493                                  */
 494                                 putc(ch, mail);
 495                         }
 496 
 497                         /* we have to read the input pipe no matter whether
 498                          * we mail or not, but obviously we only write to
 499                          * mail pipe if we ARE mailing.
 500                          */
 501 
 502                         while (EOF != (ch = getc(in))) {
 503                                 bytes++;
 504                                 if (mail)
 505                                         putc(ch, mail);
 506 #if defined(SYSLOG)
 507                                 if (SyslogOutput) {
 508                                         logbuf[bufidx++] = (char)ch;
 509                                         if ((ch == '\n') || (bufidx == sizeof(logbuf)-1)) {
 510                                                 if (ch == '\n')
 511                                                         logbuf[bufidx-1] = '\0';
 512                                                 else
 513                                                         logbuf[bufidx] = '\0';
 514                                                 log_it(usernm, getpid(), "CMDOUT", logbuf, 0);
 515                                                 bufidx = 0;
 516                                         }
 517                                 }
 518 #endif
 519                         }
 520                         /* only close pipe if we opened it -- i.e., we're
 521                          * mailing...
 522                          */
 523 
 524                         if (mail) {
 525                                 Debug(DPROC, ("[%ld] closing pipe to mail\n", (long) getpid()));
 526                                         /* Note: the pclose will probably see
 527                                          * the termination of the grandchild
 528                                          * in addition to the mail process, since
 529                                          * it (the grandchild) is likely to exit
 530                                          * after closing its stdout.
 531                                          */
 532                                         status = cron_pclose(mail);
 533                         }
 534 #if defined(SYSLOG)
 535                         if (SyslogOutput) {
 536                                 if (bufidx) {
 537                                         logbuf[bufidx] = '\0';
 538                                         log_it(usernm, getpid(), "CMDOUT", logbuf, 0);
 539                                 }
 540                         }
 541 #endif
 542 
 543                         /* if there was output and we could not mail it,
 544                          * log the facts so the poor user can figure out
 545                          * what's going on.
 546                          */
 547                         if (mail && status && !SyslogOutput) {
 548                                 char buf[MAX_TEMPSTR];
 549 
 550                                 sprintf(buf,
 551                                         "mailed %d byte%s of output but got status 0x%04x\n",
 552                                         bytes, (bytes == 1) ? "" : "s", status);
 553                                 log_it(usernm, getpid(), "MAIL", buf, 0);
 554                         }
 555 
 556                 }       /*if data from grandchild */
 557 
 558                 Debug(DPROC, ("[%ld] got EOF from grandchild\n", (long) getpid()));
 559 
 560                 fclose(in);     /* also closes stdout_pipe[READ_PIPE] */
 561         }
 562 
 563         /* wait for children to die.
 564          */
 565         for (; children > 0; children--) {
 566                 WAIT_T waiter;
 567                 PID_T child;
 568 
 569                 Debug(DPROC, ("[%ld] waiting for grandchild #%d to finish\n",
 570                                 (long) getpid(), children));
 571                 while ((child = wait(&waiter)) < OK && errno == EINTR) ;
 572                 if (child < OK) {
 573                         Debug(DPROC,
 574                                 ("[%ld] no more grandchildren--mail written?\n",
 575                                         (long) getpid()));
 576                         break;
 577                 }
 578                 Debug(DPROC, ("[%ld] grandchild #%ld finished, status=%04x",
 579                                 (long) getpid(), (long) child, WEXITSTATUS(waiter)));
 580                         if (WIFSIGNALED(waiter) && WCOREDUMP(waiter))
 581                                 Debug(DPROC, (", dumped core"));
 582                         Debug(DPROC, ("\n"));
 583         }
 584         return OK_EXIT;
 585 }
 586 
 587 static int safe_p(const char *usernm, const char *s) {
     /* [previous][next][first][last][top][bottom][index][help]  */
 588         static const char safe_delim[] = "@!:%-.,_+";   /* conservative! */
 589         const char *t;
 590         int ch, first;
 591 
 592         for (t = s, first = 1; (ch = *t++) != '\0'; first = 0) {
 593                 if (isascii(ch) && isprint(ch) &&
 594                         (isalnum(ch) || (!first && strchr(safe_delim, ch))))
 595                         continue;
 596                 log_it(usernm, getpid(), "UNSAFE", s, 0);
 597                 return (FALSE);
 598         }
 599         return (TRUE);
 600 }

/* [previous][next][first][last][top][bottom][index][help]  */