1/* $OpenBSD: do_command.c,v 1.63 2022/05/21 01:21:29 deraadt Exp $ */ 2 3/* Copyright 1988,1990,1993,1994 by Paul Vixie 4 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 5 * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 6 * Copyright (c) 2018 Job Snijders <job@openbsd.org> 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 18 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 */ 20 21#include <sys/types.h> 22#include <sys/wait.h> 23 24#include <bitstring.h> /* for structs.h */ 25#include <bsd_auth.h> 26#include <ctype.h> 27#include <err.h> 28#include <errno.h> 29#include <fcntl.h> 30#include <limits.h> 31#include <login_cap.h> 32#include <pwd.h> 33#include <signal.h> 34#include <stdio.h> 35#include <stdlib.h> 36#include <string.h> 37#include <syslog.h> 38#include <time.h> /* for structs.h */ 39#include <unistd.h> 40#include <vis.h> 41 42#include "config.h" 43#include "pathnames.h" 44#include "macros.h" 45#include "structs.h" 46#include "funcs.h" 47 48static void child_process(entry *, user *); 49 50pid_t 51do_command(entry *e, user *u) 52{ 53 pid_t pid; 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 ((pid = fork())) { 63 case -1: 64 syslog(LOG_ERR, "(CRON) CAN'T FORK (%m)"); 65 break; 66 case 0: 67 /* child process */ 68 child_process(e, u); 69 _exit(EXIT_SUCCESS); 70 break; 71 default: 72 /* parent process */ 73 if ((e->flags & SINGLE_JOB) == 0) 74 pid = -1; 75 break; 76 } 77 78 /* only return pid if a singleton */ 79 return (pid); 80} 81 82static void 83child_process(entry *e, user *u) 84{ 85 FILE *in; 86 int stdin_pipe[2], stdout_pipe[2]; 87 char **p, *input_data, *usernm; 88 auth_session_t *as; 89 login_cap_t *lc; 90 extern char **environ; 91 92 /* mark ourselves as different to PS command watchers */ 93 setproctitle("running job"); 94 95 /* close sockets from parent (i.e. cronSock) */ 96 closefrom(3); 97 98 /* discover some useful and important environment settings 99 */ 100 usernm = e->pwd->pw_name; 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 (void) signal(SIGCHLD, SIG_DFL); 108 109 /* create some pipes to talk to our future child 110 */ 111 if (pipe(stdin_pipe) != 0 || pipe(stdout_pipe) != 0) { 112 syslog(LOG_ERR, "(CRON) PIPE (%m)"); 113 _exit(EXIT_FAILURE); 114 } 115 116 /* since we are a forked process, we can diddle the command string 117 * we were passed -- nobody else is going to use it again, right? 118 * 119 * if a % is present in the command, previous characters are the 120 * command, and subsequent characters are the additional input to 121 * the command. An escaped % will have the escape character stripped 122 * from it. Subsequent %'s will be transformed into newlines, 123 * but that happens later. 124 */ 125 /*local*/{ 126 int escaped = FALSE; 127 int ch; 128 char *p; 129 130 for (input_data = p = e->cmd; 131 (ch = *input_data) != '\0'; 132 input_data++, p++) { 133 if (p != input_data) 134 *p = ch; 135 if (escaped) { 136 if (ch == '%') 137 *--p = ch; 138 escaped = FALSE; 139 continue; 140 } 141 if (ch == '\\') { 142 escaped = TRUE; 143 continue; 144 } 145 if (ch == '%') { 146 *input_data++ = '\0'; 147 break; 148 } 149 } 150 *p = '\0'; 151 } 152 153 /* fork again, this time so we can exec the user's command. 154 */ 155 156 pid_t jobpid; 157 switch (jobpid = fork()) { 158 case -1: 159 syslog(LOG_ERR, "(CRON) CAN'T FORK (%m)"); 160 _exit(EXIT_FAILURE); 161 /*NOTREACHED*/ 162 case 0: 163 /* write a log message. we've waited this long to do it 164 * because it was not until now that we knew the PID that 165 * the actual user command shell was going to get and the 166 * PID is part of the log message. 167 */ 168 if ((e->flags & DONT_LOG) == 0) { 169 char *x; 170 if (stravis(&x, e->cmd, 0) != -1) { 171 syslog(LOG_INFO, "(%s) CMD (%s)", usernm, x); 172 free(x); 173 } 174 } 175 176 /* get new pgrp, void tty, etc. 177 */ 178 (void) setsid(); 179 180 /* close the pipe ends that we won't use. this doesn't affect 181 * the parent, who has to read and write them; it keeps the 182 * kernel from recording us as a potential client TWICE -- 183 * which would keep it from sending SIGPIPE in otherwise 184 * appropriate circumstances. 185 */ 186 close(stdin_pipe[WRITE_PIPE]); 187 close(stdout_pipe[READ_PIPE]); 188 189 /* grandchild process. make std{in,out} be the ends of 190 * pipes opened by our daddy; make stderr go to stdout. 191 */ 192 if (stdin_pipe[READ_PIPE] != STDIN_FILENO) { 193 dup2(stdin_pipe[READ_PIPE], STDIN_FILENO); 194 close(stdin_pipe[READ_PIPE]); 195 } 196 if (stdout_pipe[WRITE_PIPE] != STDOUT_FILENO) { 197 dup2(stdout_pipe[WRITE_PIPE], STDOUT_FILENO); 198 close(stdout_pipe[WRITE_PIPE]); 199 } 200 dup2(STDOUT_FILENO, STDERR_FILENO); 201 202 /* 203 * From this point on, anything written to stderr will be 204 * mailed to the user as output. 205 */ 206 207 /* XXX - should just pass in a login_cap_t * */ 208 if ((lc = login_getclass(e->pwd->pw_class)) == NULL) { 209 warnx("unable to get login class for %s", 210 e->pwd->pw_name); 211 syslog(LOG_ERR, "(CRON) CAN'T GET LOGIN CLASS (%s)", 212 e->pwd->pw_name); 213 _exit(EXIT_FAILURE); 214 } 215 if (setusercontext(lc, e->pwd, e->pwd->pw_uid, LOGIN_SETALL) == -1) { 216 warn("setusercontext failed for %s", e->pwd->pw_name); 217 syslog(LOG_ERR, "(%s) SETUSERCONTEXT FAILED (%m)", 218 e->pwd->pw_name); 219 _exit(EXIT_FAILURE); 220 } 221 as = auth_open(); 222 if (as == NULL || auth_setpwd(as, e->pwd) != 0) { 223 warn("auth_setpwd"); 224 syslog(LOG_ERR, "(%s) AUTH_SETPWD FAILED (%m)", 225 e->pwd->pw_name); 226 _exit(EXIT_FAILURE); 227 } 228 if (auth_approval(as, lc, usernm, "cron") <= 0) { 229 warnx("approval failed for %s", e->pwd->pw_name); 230 syslog(LOG_ERR, "(%s) APPROVAL FAILED (cron)", 231 e->pwd->pw_name); 232 _exit(EXIT_FAILURE); 233 } 234 auth_close(as); 235 login_close(lc); 236 237 /* If no PATH specified in crontab file but 238 * we just added one via login.conf, add it to 239 * the crontab environment. 240 */ 241 if (env_get("PATH", e->envp) == NULL && environ != NULL) { 242 for (p = environ; *p; p++) { 243 if (strncmp(*p, "PATH=", 5) == 0) { 244 e->envp = env_set(e->envp, *p); 245 break; 246 } 247 } 248 } 249 chdir(env_get("HOME", e->envp)); 250 251 (void) signal(SIGPIPE, SIG_DFL); 252 253 /* 254 * Exec the command. 255 */ 256 { 257 char *shell = env_get("SHELL", e->envp); 258 259 execle(shell, shell, "-c", e->cmd, (char *)NULL, e->envp); 260 warn("unable to execute %s", shell); 261 syslog(LOG_ERR, "(%s) CAN'T EXEC (%s: %m)", 262 e->pwd->pw_name, shell); 263 _exit(EXIT_FAILURE); 264 } 265 break; 266 default: 267 /* parent process */ 268 break; 269 } 270 271 /* middle process, child of original cron, parent of process running 272 * the user's command. 273 */ 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 pid_t stdinjob; 293 if (*input_data && (stdinjob = fork()) == 0) { 294 FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w"); 295 int need_newline = FALSE; 296 int escaped = FALSE; 297 int ch; 298 299 /* close the pipe we don't use, since we inherited it and 300 * are part of its reference count now. 301 */ 302 close(stdout_pipe[READ_PIPE]); 303 304 /* translation: 305 * \% -> % 306 * % -> \n 307 * \x -> \x for all x != % 308 */ 309 while ((ch = *input_data++) != '\0') { 310 if (escaped) { 311 if (ch != '%') 312 putc('\\', out); 313 } else { 314 if (ch == '%') 315 ch = '\n'; 316 } 317 318 if (!(escaped = (ch == '\\'))) { 319 putc(ch, out); 320 need_newline = (ch != '\n'); 321 } 322 } 323 if (escaped) 324 putc('\\', out); 325 if (need_newline) 326 putc('\n', out); 327 328 /* close the pipe, causing an EOF condition. fclose causes 329 * stdin_pipe[WRITE_PIPE] to be closed, too. 330 */ 331 fclose(out); 332 333 _exit(EXIT_SUCCESS); 334 } 335 336 /* close the pipe to the grandkiddie's stdin, since its wicked uncle 337 * ernie back there has it open and will close it when he's done. 338 */ 339 close(stdin_pipe[WRITE_PIPE]); 340 341 /* 342 * read output from the grandchild. Its stderr has been redirected to 343 * its stdout, which has been redirected to our pipe. if there is any 344 * output, we'll be mailing it to the user whose crontab this is... 345 * when the grandchild exits, we'll get EOF. 346 */ 347 348 (void) signal(SIGPIPE, SIG_IGN); 349 in = fdopen(stdout_pipe[READ_PIPE], "r"); 350 351 char *mailto; 352 FILE *mail = NULL; 353 int status = 0; 354 pid_t mailpid; 355 size_t bytes = 1; 356 357 if (in != NULL) { 358 int ch = getc(in); 359 360 if (ch != EOF) { 361 362 /* get name of recipient. this is MAILTO if set to a 363 * valid local username; USER otherwise. 364 */ 365 mailto = env_get("MAILTO", e->envp); 366 if (!mailto) { 367 /* MAILTO not present, set to USER. 368 */ 369 mailto = usernm; 370 } else if (!*mailto || !safe_p(usernm, mailto)) { 371 mailto = NULL; 372 } 373 374 /* if we are supposed to be mailing, MAILTO will 375 * be non-NULL. only in this case should we set 376 * up the mail command and subjects and stuff... 377 */ 378 379 if (mailto) { 380 char **env; 381 char mailcmd[MAX_COMMAND]; 382 char hostname[HOST_NAME_MAX + 1]; 383 384 gethostname(hostname, sizeof(hostname)); 385 if (snprintf(mailcmd, sizeof mailcmd, MAILFMT, 386 MAILARG) >= sizeof mailcmd) { 387 syslog(LOG_ERR, 388 "(%s) ERROR (mailcmd too long)", 389 e->pwd->pw_name); 390 (void) _exit(EXIT_FAILURE); 391 } 392 if (!(mail = cron_popen(mailcmd, "w", e->pwd, 393 &mailpid))) { 394 syslog(LOG_ERR, "(%s) POPEN (%s)", 395 e->pwd->pw_name, mailcmd); 396 (void) _exit(EXIT_FAILURE); 397 } 398 fprintf(mail, "From: root (Cron Daemon)\n"); 399 fprintf(mail, "To: %s\n", mailto); 400 fprintf(mail, "Subject: Cron <%s@%s> %s\n", 401 usernm, first_word(hostname, "."), 402 e->cmd); 403 fprintf(mail, "Auto-Submitted: auto-generated\n"); 404 for (env = e->envp; *env; env++) 405 fprintf(mail, "X-Cron-Env: <%s>\n", 406 *env); 407 fprintf(mail, "\n"); 408 409 /* this was the first char from the pipe 410 */ 411 fputc(ch, mail); 412 } 413 414 /* we have to read the input pipe no matter whether 415 * we mail or not, but obviously we only write to 416 * mail pipe if we ARE mailing. 417 */ 418 419 while ((ch = getc(in)) != EOF) { 420 bytes++; 421 if (mail) 422 fputc(ch, mail); 423 } 424 425 } /*if data from grandchild*/ 426 427 fclose(in); /* also closes stdout_pipe[READ_PIPE] */ 428 } 429 430 /* wait for children to die. 431 */ 432 int waiter; 433 if (jobpid > 0) { 434 while (waitpid(jobpid, &waiter, 0) == -1 && errno == EINTR) 435 ; 436 437 /* If everything went well, and -n was set, _and_ we have mail, 438 * we won't be mailing... so shoot the messenger! 439 */ 440 if (WIFEXITED(waiter) && WEXITSTATUS(waiter) == 0 441 && (e->flags & MAIL_WHEN_ERR) == MAIL_WHEN_ERR 442 && mail) { 443 kill(mailpid, SIGKILL); 444 (void)fclose(mail); 445 mail = NULL; 446 } 447 448 /* only close pipe if we opened it -- i.e., we're mailing... */ 449 if (mail) { 450 /* 451 * Note: the pclose will probably see the termination 452 * of the grandchild in addition to the mail process, 453 * since it (the grandchild) is likely to exit after 454 * closing its stdout. 455 */ 456 status = cron_pclose(mail, mailpid); 457 } 458 459 /* if there was output and we could not mail it, 460 * log the facts so the poor user can figure out 461 * what's going on. 462 */ 463 if (mail && status) { 464 syslog(LOG_NOTICE, "(%s) MAIL (mailed %zu byte" 465 "%s of output but got status 0x%04x)", usernm, 466 bytes, (bytes == 1) ? "" : "s", status); 467 } 468 } 469 470 if (stdinjob > 0) 471 while (waitpid(stdinjob, &waiter, 0) == -1 && errno == EINTR) 472 ; 473} 474 475int 476safe_p(const char *usernm, const char *s) 477{ 478 static const char safe_delim[] = "@!:%+-.,"; /* conservative! */ 479 const char *t; 480 int ch, first; 481 482 for (t = s, first = 1; (ch = (unsigned char)*t++) != '\0'; first = 0) { 483 if (isascii(ch) && isprint(ch) && 484 (isalnum(ch) || ch == '_' || 485 (!first && strchr(safe_delim, ch)))) 486 continue; 487 syslog(LOG_WARNING, "(%s) UNSAFE (%s)", usernm, s); 488 return (FALSE); 489 } 490 return (TRUE); 491} 492