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