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]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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 }