1 /* Copyright 1988,1990,1993,1994 by Paul Vixie 2 * All rights reserved 3 * 4 * Distribute freely, except: don't remove my name from the source or 5 * documentation (don't take credit for my work), mark your changes (don't 6 * get me blamed for your possible bugs), don't alter or remove this 7 * notice. May be sold if buildable source is provided to buyer. No 8 * warrantee of any kind, express or implied, is included with this 9 * software; use at your own risk, responsibility for damages (if any) to 10 * anyone resulting from the use of this software rests entirely with the 11 * user. 12 * 13 * Send bug reports, bug fixes, enhancements, requests, flames, etc., and 14 * I'll try to keep a version up to date. I can be reached as follows: 15 * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul 16 */ 17 18 #if !defined(lint) && !defined(LINT) 19 static const char rcsid[] = 20 "$FreeBSD$"; 21 #endif 22 23 24 #include "cron.h" 25 #include <sys/signal.h> 26 #if defined(sequent) 27 # include <sys/universe.h> 28 #endif 29 #if defined(SYSLOG) 30 # include <syslog.h> 31 #endif 32 #if defined(LOGIN_CAP) 33 # include <login_cap.h> 34 #endif 35 #ifdef PAM 36 # include <security/pam_appl.h> 37 # include <security/openpam.h> 38 #endif 39 40 41 static void child_process(entry *, user *); 42 43 static WAIT_T wait_on_child(PID_T, const char *); 44 45 extern char *environ; 46 47 void 48 do_command(entry *e, user *u) 49 { 50 pid_t pid; 51 52 Debug(DPROC, ("[%d] do_command(%s, (%s,%d,%d))\n", 53 getpid(), e->cmd, u->name, e->uid, e->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 switch ((pid = fork())) { 60 case -1: 61 log_it("CRON",getpid(),"error","can't fork"); 62 if (e->flags & INTERVAL) 63 e->lastexit = time(NULL); 64 break; 65 case 0: 66 /* child process */ 67 pidfile_close(pfh); 68 child_process(e, u); 69 Debug(DPROC, ("[%d] child process done, exiting\n", getpid())) 70 _exit(OK_EXIT); 71 break; 72 default: 73 /* parent process */ 74 Debug(DPROC, ("[%d] main process forked child #%d, " 75 "returning to work\n", getpid(), pid)) 76 if (e->flags & INTERVAL) { 77 e->lastexit = 0; 78 e->child = pid; 79 } 80 break; 81 } 82 Debug(DPROC, ("[%d] main process returning to work\n", getpid())) 83 } 84 85 86 static void 87 child_process(entry *e, user *u) 88 { 89 int stdin_pipe[2], stdout_pipe[2]; 90 register char *input_data; 91 char *usernm, *mailto, *mailfrom; 92 PID_T jobpid, stdinjob, mailpid; 93 register FILE *mail; 94 register int bytes = 1; 95 int status = 0; 96 const char *homedir = NULL; 97 # if defined(LOGIN_CAP) 98 struct passwd *pwd; 99 login_cap_t *lc; 100 # endif 101 102 Debug(DPROC, ("[%d] child_process('%s')\n", getpid(), e->cmd)) 103 104 /* mark ourselves as different to PS command watchers by upshifting 105 * our program name. This has no effect on some kernels. 106 */ 107 setproctitle("running job"); 108 109 /* discover some useful and important environment settings 110 */ 111 usernm = env_get("LOGNAME", e->envp); 112 mailto = env_get("MAILTO", e->envp); 113 mailfrom = env_get("MAILFROM", e->envp); 114 115 #ifdef PAM 116 /* use PAM to see if the user's account is available, 117 * i.e., not locked or expired or whatever. skip this 118 * for system tasks from /etc/crontab -- they can run 119 * as any user. 120 */ 121 if (strcmp(u->name, SYS_NAME)) { /* not equal */ 122 pam_handle_t *pamh = NULL; 123 int pam_err; 124 struct pam_conv pamc = { 125 .conv = openpam_nullconv, 126 .appdata_ptr = NULL 127 }; 128 129 Debug(DPROC, ("[%d] checking account with PAM\n", getpid())) 130 131 /* u->name keeps crontab owner name while LOGNAME is the name 132 * of user to run command on behalf of. they should be the 133 * same for a task from a per-user crontab. 134 */ 135 if (strcmp(u->name, usernm)) { 136 log_it(usernm, getpid(), "username ambiguity", u->name); 137 exit(ERROR_EXIT); 138 } 139 140 pam_err = pam_start("cron", usernm, &pamc, &pamh); 141 if (pam_err != PAM_SUCCESS) { 142 log_it("CRON", getpid(), "error", "can't start PAM"); 143 exit(ERROR_EXIT); 144 } 145 146 pam_err = pam_acct_mgmt(pamh, PAM_SILENT); 147 /* Expired password shouldn't prevent the job from running. */ 148 if (pam_err != PAM_SUCCESS && pam_err != PAM_NEW_AUTHTOK_REQD) { 149 log_it(usernm, getpid(), "USER", "account unavailable"); 150 exit(ERROR_EXIT); 151 } 152 153 pam_end(pamh, pam_err); 154 } 155 #endif 156 157 #ifdef USE_SIGCHLD 158 /* our parent is watching for our death by catching SIGCHLD. we 159 * do not care to watch for our children's deaths this way -- we 160 * use wait() explicitly. so we have to disable the signal (which 161 * was inherited from the parent). 162 */ 163 (void) signal(SIGCHLD, SIG_DFL); 164 #else 165 /* on system-V systems, we are ignoring SIGCLD. we have to stop 166 * ignoring it now or the wait() in cron_pclose() won't work. 167 * because of this, we have to wait() for our children here, as well. 168 */ 169 (void) signal(SIGCLD, SIG_DFL); 170 #endif /*BSD*/ 171 172 /* create some pipes to talk to our future child 173 */ 174 if (pipe(stdin_pipe) != 0 || pipe(stdout_pipe) != 0) { 175 log_it("CRON", getpid(), "error", "can't pipe"); 176 exit(ERROR_EXIT); 177 } 178 179 /* since we are a forked process, we can diddle the command string 180 * we were passed -- nobody else is going to use it again, right? 181 * 182 * if a % is present in the command, previous characters are the 183 * command, and subsequent characters are the additional input to 184 * the command. Subsequent %'s will be transformed into newlines, 185 * but that happens later. 186 * 187 * If there are escaped %'s, remove the escape character. 188 */ 189 /*local*/{ 190 register int escaped = FALSE; 191 register int ch; 192 register char *p; 193 194 for (input_data = p = e->cmd; (ch = *input_data); 195 input_data++, p++) { 196 if (p != input_data) 197 *p = ch; 198 if (escaped) { 199 if (ch == '%' || ch == '\\') 200 *--p = ch; 201 escaped = FALSE; 202 continue; 203 } 204 if (ch == '\\') { 205 escaped = TRUE; 206 continue; 207 } 208 if (ch == '%') { 209 *input_data++ = '\0'; 210 break; 211 } 212 } 213 *p = '\0'; 214 } 215 216 /* fork again, this time so we can exec the user's command. 217 */ 218 switch (jobpid = fork()) { 219 case -1: 220 log_it("CRON",getpid(),"error","can't fork"); 221 exit(ERROR_EXIT); 222 /*NOTREACHED*/ 223 case 0: 224 Debug(DPROC, ("[%d] grandchild process fork()'ed\n", 225 getpid())) 226 227 if (e->uid == ROOT_UID) 228 Jitter = RootJitter; 229 if (Jitter != 0) { 230 srandom(getpid()); 231 sleep(random() % Jitter); 232 } 233 234 /* write a log message. we've waited this long to do it 235 * because it was not until now that we knew the PID that 236 * the actual user command shell was going to get and the 237 * PID is part of the log message. 238 */ 239 if ((e->flags & DONT_LOG) == 0) { 240 char *x = mkprints((u_char *)e->cmd, strlen(e->cmd)); 241 242 log_it(usernm, getpid(), "CMD", x); 243 free(x); 244 } 245 246 /* that's the last thing we'll log. close the log files. 247 */ 248 #ifdef SYSLOG 249 closelog(); 250 #endif 251 252 /* get new pgrp, void tty, etc. 253 */ 254 (void) setsid(); 255 256 /* close the pipe ends that we won't use. this doesn't affect 257 * the parent, who has to read and write them; it keeps the 258 * kernel from recording us as a potential client TWICE -- 259 * which would keep it from sending SIGPIPE in otherwise 260 * appropriate circumstances. 261 */ 262 close(stdin_pipe[WRITE_PIPE]); 263 close(stdout_pipe[READ_PIPE]); 264 265 /* grandchild process. make std{in,out} be the ends of 266 * pipes opened by our daddy; make stderr go to stdout. 267 */ 268 close(STDIN); dup2(stdin_pipe[READ_PIPE], STDIN); 269 close(STDOUT); dup2(stdout_pipe[WRITE_PIPE], STDOUT); 270 close(STDERR); dup2(STDOUT, STDERR); 271 272 /* close the pipes we just dup'ed. The resources will remain. 273 */ 274 close(stdin_pipe[READ_PIPE]); 275 close(stdout_pipe[WRITE_PIPE]); 276 277 environ = NULL; 278 279 # if defined(LOGIN_CAP) 280 /* Set user's entire context, but note that PATH will 281 * be overridden later 282 */ 283 if ((pwd = getpwnam(usernm)) == NULL) 284 pwd = getpwuid(e->uid); 285 lc = NULL; 286 if (pwd != NULL) { 287 if (pwd->pw_dir != NULL 288 && pwd->pw_dir[0] != '\0') { 289 homedir = strdup(pwd->pw_dir); 290 if (homedir == NULL) { 291 warn("strdup"); 292 _exit(ERROR_EXIT); 293 } 294 } 295 pwd->pw_gid = e->gid; 296 if (e->class != NULL) 297 lc = login_getclass(e->class); 298 } 299 if (pwd && 300 setusercontext(lc, pwd, e->uid, 301 LOGIN_SETALL) == 0) 302 (void) endpwent(); 303 else { 304 /* fall back to the old method */ 305 (void) endpwent(); 306 # endif 307 /* set our directory, uid and gid. Set gid first, 308 * since once we set uid, we've lost root privileges. 309 */ 310 if (setgid(e->gid) != 0) { 311 log_it(usernm, getpid(), 312 "error", "setgid failed"); 313 _exit(ERROR_EXIT); 314 } 315 # if defined(BSD) 316 if (initgroups(usernm, e->gid) != 0) { 317 log_it(usernm, getpid(), 318 "error", "initgroups failed"); 319 _exit(ERROR_EXIT); 320 } 321 # endif 322 if (setlogin(usernm) != 0) { 323 log_it(usernm, getpid(), 324 "error", "setlogin failed"); 325 _exit(ERROR_EXIT); 326 } 327 if (setuid(e->uid) != 0) { 328 log_it(usernm, getpid(), 329 "error", "setuid failed"); 330 _exit(ERROR_EXIT); 331 } 332 /* we aren't root after this..*/ 333 #if defined(LOGIN_CAP) 334 } 335 if (lc != NULL) 336 login_close(lc); 337 #endif 338 339 /* For compatibility, we chdir to the value of HOME if it was 340 * specified explicitly in the crontab file, but not if it was 341 * set in the environment by some other mechanism. We chdir to 342 * the homedir given by the pw entry otherwise. 343 * 344 * If !LOGIN_CAP, then HOME is always set in e->envp. 345 * 346 * XXX: probably should also consult PAM. 347 */ 348 { 349 char *new_home = env_get("HOME", e->envp); 350 if (new_home != NULL && new_home[0] != '\0') 351 chdir(new_home); 352 else if (homedir != NULL) 353 chdir(homedir); 354 else 355 chdir("/"); 356 } 357 358 /* exec the command. Note that SHELL is not respected from 359 * either login.conf or pw_shell, only an explicit setting 360 * in the crontab. (default of _PATH_BSHELL is supplied when 361 * setting up the entry) 362 */ 363 { 364 char *shell = env_get("SHELL", e->envp); 365 char **p; 366 367 /* Apply the environment from the entry, overriding 368 * existing values (this will always set LOGNAME and 369 * SHELL). putenv should not fail unless malloc does. 370 */ 371 for (p = e->envp; *p; ++p) { 372 if (putenv(*p) != 0) { 373 warn("putenv"); 374 _exit(ERROR_EXIT); 375 } 376 } 377 378 /* HOME in login.conf overrides pw, and HOME in the 379 * crontab overrides both. So set pw's value only if 380 * nothing was already set (overwrite==0). 381 */ 382 if (homedir != NULL 383 && setenv("HOME", homedir, 0) < 0) { 384 warn("setenv(HOME)"); 385 _exit(ERROR_EXIT); 386 } 387 388 /* PATH in login.conf is respected, but the crontab 389 * overrides; set a default value only if nothing 390 * already set. 391 */ 392 if (setenv("PATH", _PATH_DEFPATH, 0) < 0) { 393 warn("setenv(PATH)"); 394 _exit(ERROR_EXIT); 395 } 396 397 # if DEBUGGING 398 if (DebugFlags & DTEST) { 399 fprintf(stderr, 400 "debug DTEST is on, not exec'ing command.\n"); 401 fprintf(stderr, 402 "\tcmd='%s' shell='%s'\n", e->cmd, shell); 403 _exit(OK_EXIT); 404 } 405 # endif /*DEBUGGING*/ 406 execl(shell, shell, "-c", e->cmd, (char *)NULL); 407 warn("execl: couldn't exec `%s'", shell); 408 _exit(ERROR_EXIT); 409 } 410 break; 411 default: 412 /* parent process */ 413 break; 414 } 415 416 /* middle process, child of original cron, parent of process running 417 * the user's command. 418 */ 419 420 Debug(DPROC, ("[%d] child continues, closing pipes\n", getpid())) 421 422 /* close the ends of the pipe that will only be referenced in the 423 * grandchild process... 424 */ 425 close(stdin_pipe[READ_PIPE]); 426 close(stdout_pipe[WRITE_PIPE]); 427 428 /* 429 * write, to the pipe connected to child's stdin, any input specified 430 * after a % in the crontab entry. while we copy, convert any 431 * additional %'s to newlines. when done, if some characters were 432 * written and the last one wasn't a newline, write a newline. 433 * 434 * Note that if the input data won't fit into one pipe buffer (2K 435 * or 4K on most BSD systems), and the child doesn't read its stdin, 436 * we would block here. thus we must fork again. 437 */ 438 439 if (*input_data && (stdinjob = fork()) == 0) { 440 register FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w"); 441 register int need_newline = FALSE; 442 register int escaped = FALSE; 443 register int ch; 444 445 if (out == NULL) { 446 warn("fdopen failed in child2"); 447 _exit(ERROR_EXIT); 448 } 449 450 Debug(DPROC, ("[%d] child2 sending data to grandchild\n", getpid())) 451 452 /* close the pipe we don't use, since we inherited it and 453 * are part of its reference count now. 454 */ 455 close(stdout_pipe[READ_PIPE]); 456 457 /* translation: 458 * \% -> % 459 * % -> \n 460 * \x -> \x for all x != % 461 */ 462 while ((ch = *input_data++)) { 463 if (escaped) { 464 if (ch != '%') 465 putc('\\', out); 466 } else { 467 if (ch == '%') 468 ch = '\n'; 469 } 470 471 if (!(escaped = (ch == '\\'))) { 472 putc(ch, out); 473 need_newline = (ch != '\n'); 474 } 475 } 476 if (escaped) 477 putc('\\', out); 478 if (need_newline) 479 putc('\n', out); 480 481 /* close the pipe, causing an EOF condition. fclose causes 482 * stdin_pipe[WRITE_PIPE] to be closed, too. 483 */ 484 fclose(out); 485 486 Debug(DPROC, ("[%d] child2 done sending to grandchild\n", getpid())) 487 exit(0); 488 } 489 490 /* close the pipe to the grandkiddie's stdin, since its wicked uncle 491 * ernie back there has it open and will close it when he's done. 492 */ 493 close(stdin_pipe[WRITE_PIPE]); 494 495 /* 496 * read output from the grandchild. it's stderr has been redirected to 497 * it's stdout, which has been redirected to our pipe. if there is any 498 * output, we'll be mailing it to the user whose crontab this is... 499 * when the grandchild exits, we'll get EOF. 500 */ 501 502 Debug(DPROC, ("[%d] child reading output from grandchild\n", getpid())) 503 504 /*local*/{ 505 register FILE *in = fdopen(stdout_pipe[READ_PIPE], "r"); 506 register int ch; 507 508 if (in == NULL) { 509 warn("fdopen failed in child"); 510 _exit(ERROR_EXIT); 511 } 512 513 mail = NULL; 514 515 ch = getc(in); 516 if (ch != EOF) { 517 Debug(DPROC|DEXT, 518 ("[%d] got data (%x:%c) from grandchild\n", 519 getpid(), ch, ch)) 520 521 /* get name of recipient. this is MAILTO if set to a 522 * valid local username; USER otherwise. 523 */ 524 if (mailto == NULL) { 525 /* MAILTO not present, set to USER, 526 * unless globally overridden. 527 */ 528 if (defmailto) 529 mailto = defmailto; 530 else 531 mailto = usernm; 532 } 533 if (mailto && *mailto == '\0') 534 mailto = NULL; 535 536 /* if we are supposed to be mailing, MAILTO will 537 * be non-NULL. only in this case should we set 538 * up the mail command and subjects and stuff... 539 */ 540 541 if (mailto) { 542 register char **env; 543 auto char mailcmd[MAX_COMMAND]; 544 auto char hostname[MAXHOSTNAMELEN]; 545 546 if (gethostname(hostname, MAXHOSTNAMELEN) == -1) 547 hostname[0] = '\0'; 548 hostname[sizeof(hostname) - 1] = '\0'; 549 (void) snprintf(mailcmd, sizeof(mailcmd), 550 MAILARGS, MAILCMD); 551 if (!(mail = cron_popen(mailcmd, "w", e, &mailpid))) { 552 warn("%s", MAILCMD); 553 (void) _exit(ERROR_EXIT); 554 } 555 if (mailfrom == NULL || *mailfrom == '\0') 556 fprintf(mail, "From: Cron Daemon <%s@%s>\n", 557 usernm, hostname); 558 else 559 fprintf(mail, "From: Cron Daemon <%s>\n", 560 mailfrom); 561 fprintf(mail, "To: %s\n", mailto); 562 fprintf(mail, "Subject: Cron <%s@%s> %s\n", 563 usernm, first_word(hostname, "."), 564 e->cmd); 565 # if defined(MAIL_DATE) 566 fprintf(mail, "Date: %s\n", 567 arpadate(&TargetTime)); 568 # endif /* MAIL_DATE */ 569 for (env = e->envp; *env; env++) 570 fprintf(mail, "X-Cron-Env: <%s>\n", 571 *env); 572 fprintf(mail, "\n"); 573 574 /* this was the first char from the pipe 575 */ 576 putc(ch, mail); 577 } 578 579 /* we have to read the input pipe no matter whether 580 * we mail or not, but obviously we only write to 581 * mail pipe if we ARE mailing. 582 */ 583 584 while (EOF != (ch = getc(in))) { 585 bytes++; 586 if (mail) 587 putc(ch, mail); 588 } 589 } 590 /*if data from grandchild*/ 591 592 Debug(DPROC, ("[%d] got EOF from grandchild\n", getpid())) 593 594 /* also closes stdout_pipe[READ_PIPE] */ 595 fclose(in); 596 } 597 598 /* wait for children to die. 599 */ 600 if (jobpid > 0) { 601 WAIT_T waiter; 602 603 waiter = wait_on_child(jobpid, "grandchild command job"); 604 605 /* If everything went well, and -n was set, _and_ we have mail, 606 * we won't be mailing... so shoot the messenger! 607 */ 608 if (WIFEXITED(waiter) && WEXITSTATUS(waiter) == 0 609 && (e->flags & MAIL_WHEN_ERR) == MAIL_WHEN_ERR 610 && mail) { 611 Debug(DPROC, ("[%d] %s executed successfully, mail suppressed\n", 612 getpid(), "grandchild command job")) 613 kill(mailpid, SIGKILL); 614 (void)fclose(mail); 615 mail = NULL; 616 } 617 618 619 /* only close pipe if we opened it -- i.e., we're 620 * mailing... 621 */ 622 623 if (mail) { 624 Debug(DPROC, ("[%d] closing pipe to mail\n", 625 getpid())) 626 /* Note: the pclose will probably see 627 * the termination of the grandchild 628 * in addition to the mail process, since 629 * it (the grandchild) is likely to exit 630 * after closing its stdout. 631 */ 632 status = cron_pclose(mail); 633 634 /* if there was output and we could not mail it, 635 * log the facts so the poor user can figure out 636 * what's going on. 637 */ 638 if (status) { 639 char buf[MAX_TEMPSTR]; 640 641 snprintf(buf, sizeof(buf), 642 "mailed %d byte%s of output but got status 0x%04x\n", 643 bytes, (bytes==1)?"":"s", 644 status); 645 log_it(usernm, getpid(), "MAIL", buf); 646 } 647 } 648 } 649 650 if (*input_data && stdinjob > 0) 651 wait_on_child(stdinjob, "grandchild stdinjob"); 652 } 653 654 static WAIT_T 655 wait_on_child(PID_T childpid, const char *name) { 656 WAIT_T waiter; 657 PID_T pid; 658 659 Debug(DPROC, ("[%d] waiting for %s (%d) to finish\n", 660 getpid(), name, childpid)) 661 662 #ifdef POSIX 663 while ((pid = waitpid(childpid, &waiter, 0)) < 0 && errno == EINTR) 664 #else 665 while ((pid = wait4(childpid, &waiter, 0, NULL)) < 0 && errno == EINTR) 666 #endif 667 ; 668 669 if (pid < OK) 670 return waiter; 671 672 Debug(DPROC, ("[%d] %s (%d) finished, status=%04x", 673 getpid(), name, pid, WEXITSTATUS(waiter))) 674 if (WIFSIGNALED(waiter) && WCOREDUMP(waiter)) 675 Debug(DPROC, (", dumped core")) 676 Debug(DPROC, ("\n")) 677 678 return waiter; 679 } 680