This source file includes following definitions.
- do_command
- child_process
- safe_p
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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) {
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
56
57
58
59
60
61
62 switch (fork()) {
63 case -1:
64 log_it("CRON", pid, "CAN'T FORK", "do_command", errno);
65 break;
66 case 0:
67
68 acquire_daemonlock(1);
69
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
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) {
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
97
98 memset(&sa, 0, sizeof(sa));
99 sa.sa_handler = SIG_IGN;
100 sigaction(SIGPIPE, &sa, NULL);
101
102
103
104
105
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
114
115
116 {
117 char *pch;
118
119 for (pch = ProgramName; *pch; pch++)
120 *pch = (char)MkUpper(*pch);
121 }
122 #endif
123
124
125
126 usernm = e->pwd->pw_name;
127 mailto = env_get("MAILTO", jobenv);
128 mailfrom = env_get("MAILFROM", e->envp);
129
130
131
132 if (pipe(stdin_pipe) == -1) {
133 log_it("CRON", pid, "PIPE() FAILED", "stdin_pipe", errno);
134 return ERROR_EXIT;
135 }
136
137 if (pipe(stdout_pipe) == -1) {
138 log_it("CRON", pid, "PIPE() FAILED", "stdout_pipe", errno);
139 return ERROR_EXIT;
140 }
141
142
143
144
145
146
147
148
149
150
151 {
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
180
181 switch (fork()) {
182 case -1:
183 log_it("CRON", pid, "CAN'T FORK", "child_process", errno);
184 return ERROR_EXIT;
185
186 case 0:
187 Debug(DPROC, ("[%ld] grandchild process fork()'ed\n", (long) getpid()));
188
189
190
191
192
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
205
206 (void) setsid();
207
208
209
210
211 sa.sa_handler = SIG_DFL;
212 sigaction(SIGPIPE, &sa, NULL);
213
214
215
216
217
218
219
220 close(stdin_pipe[WRITE_PIPE]);
221 close(stdout_pipe[READ_PIPE]);
222
223
224
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
238
239 {
240 char *shell = env_get("SHELL", jobenv);
241 int fd, fdmax = getdtablesize();
242
243
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
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
264 break;
265 }
266
267 children++;
268
269
270
271
272
273 Debug(DPROC, ("[%ld] child continues, closing pipes\n", (long) getpid()));
274
275
276
277
278 close(stdin_pipe[READ_PIPE]);
279 close(stdout_pipe[WRITE_PIPE]);
280
281
282
283
284
285
286
287
288
289
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
302
303
304 sa.sa_handler = SIG_DFL;
305 sigaction(SIGPIPE, &sa, NULL);
306
307
308
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
315
316
317
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
340
341
342 fclose(out);
343
344 Debug(DPROC, ("[%ld] child2 done sending to grandchild\n",
345 (long) getpid()));
346 _exit(0);
347 }
348
349
350
351
352 close(stdin_pipe[WRITE_PIPE]);
353
354 children++;
355
356
357
358
359
360
361
362
363 Debug(DPROC, ("[%ld] child reading output from grandchild\n",
364 (long) getpid()));
365
366 {
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
388
389
390 if (mailto) {
391
392
393 if (!*mailto) {
394
395
396 mailto = NULL;
397 }
398 }
399 else {
400
401
402 mailto = usernm;
403 }
404
405
406
407
408 if (!mailfrom || !*mailfrom || !safe_p(usernm, mailfrom)) {
409 mailfrom = e->pwd->pw_name;
410 }
411
412
413
414
415
416
417
418 if (mailto && safe_p(usernm, mailto)
419 && strncmp(MailCmd,"off",3) && !SyslogOutput) {
420 char **env;
421 char mailcmd[MAX_COMMAND+1];
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
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 {
458
459
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
483
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
493
494 putc(ch, mail);
495 }
496
497
498
499
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
521
522
523
524 if (mail) {
525 Debug(DPROC, ("[%ld] closing pipe to mail\n", (long) getpid()));
526
527
528
529
530
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
544
545
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 }
557
558 Debug(DPROC, ("[%ld] got EOF from grandchild\n", (long) getpid()));
559
560 fclose(in);
561 }
562
563
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) {
588 static const char safe_delim[] = "@!:%-.,_+";
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 }