Annotation of embedaddon/sudo/src/exec.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (c) 2009-2011 Todd C. Miller <Todd.Miller@courtesan.com>
! 3: *
! 4: * Permission to use, copy, modify, and distribute this software for any
! 5: * purpose with or without fee is hereby granted, provided that the above
! 6: * copyright notice and this permission notice appear in all copies.
! 7: *
! 8: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 9: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 10: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 11: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 12: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 13: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 14: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 15: */
! 16:
! 17: #include <config.h>
! 18:
! 19: #include <sys/types.h>
! 20: #include <sys/param.h>
! 21: #ifdef HAVE_SYS_SYSMACROS_H
! 22: # include <sys/sysmacros.h>
! 23: #endif
! 24: #include <sys/socket.h>
! 25: #include <sys/stat.h>
! 26: #include <sys/time.h>
! 27: #include <sys/wait.h>
! 28: #include <sys/ioctl.h>
! 29: #ifdef HAVE_SYS_SELECT_H
! 30: # include <sys/select.h>
! 31: #endif /* HAVE_SYS_SELECT_H */
! 32: #include <stdio.h>
! 33: #ifdef STDC_HEADERS
! 34: # include <stdlib.h>
! 35: # include <stddef.h>
! 36: #else
! 37: # ifdef HAVE_STDLIB_H
! 38: # include <stdlib.h>
! 39: # endif
! 40: #endif /* STDC_HEADERS */
! 41: #ifdef HAVE_STRING_H
! 42: # include <string.h>
! 43: #endif /* HAVE_STRING_H */
! 44: #ifdef HAVE_STRINGS_H
! 45: # include <strings.h>
! 46: #endif /* HAVE_STRINGS_H */
! 47: #ifdef HAVE_UNISTD_H
! 48: # include <unistd.h>
! 49: #endif /* HAVE_UNISTD_H */
! 50: #if TIME_WITH_SYS_TIME
! 51: # include <time.h>
! 52: #endif
! 53: #ifdef HAVE_SETLOCALE
! 54: # include <locale.h>
! 55: #endif
! 56: #include <errno.h>
! 57: #include <fcntl.h>
! 58: #include <signal.h>
! 59: #include <termios.h>
! 60:
! 61: #include "sudo.h"
! 62: #include "sudo_exec.h"
! 63: #include "sudo_plugin.h"
! 64: #include "sudo_plugin_int.h"
! 65:
! 66: /* Shared with exec_pty.c for use with handler(). */
! 67: int signal_pipe[2];
! 68:
! 69: /* We keep a tailq of signals to forward to child. */
! 70: struct sigforward {
! 71: struct sigforward *prev, *next;
! 72: int signo;
! 73: };
! 74: TQ_DECLARE(sigforward)
! 75: static struct sigforward_list sigfwd_list;
! 76:
! 77: static int handle_signals(int fd, pid_t child, int log_io,
! 78: struct command_status *cstat);
! 79: static void forward_signals(int fd);
! 80: static void schedule_signal(int signo);
! 81:
! 82: /*
! 83: * Like execve(2) but falls back to running through /bin/sh
! 84: * ala execvp(3) if we get ENOEXEC.
! 85: */
! 86: int
! 87: my_execve(const char *path, char *const argv[], char *const envp[])
! 88: {
! 89: execve(path, argv, envp);
! 90: if (errno == ENOEXEC) {
! 91: int argc;
! 92: char **nargv;
! 93:
! 94: for (argc = 0; argv[argc] != NULL; argc++)
! 95: continue;
! 96: nargv = emalloc2(argc + 2, sizeof(char *));
! 97: nargv[0] = "sh";
! 98: nargv[1] = (char *)path;
! 99: memcpy(nargv + 2, argv + 1, argc * sizeof(char *));
! 100: execve(_PATH_BSHELL, nargv, envp);
! 101: efree(nargv);
! 102: }
! 103: return -1;
! 104: }
! 105:
! 106: /*
! 107: * Fork and execute a command, returns the child's pid.
! 108: * Sends errno back on sv[1] if execve() fails.
! 109: */
! 110: static int fork_cmnd(struct command_details *details, int sv[2])
! 111: {
! 112: struct command_status cstat;
! 113: sigaction_t sa;
! 114: pid_t child;
! 115:
! 116: zero_bytes(&sa, sizeof(sa));
! 117: sigemptyset(&sa.sa_mask);
! 118: sa.sa_flags = SA_INTERRUPT; /* do not restart syscalls */
! 119: sa.sa_handler = handler;
! 120: sigaction(SIGCONT, &sa, NULL);
! 121:
! 122: child = fork();
! 123: switch (child) {
! 124: case -1:
! 125: error(1, _("unable to fork"));
! 126: break;
! 127: case 0:
! 128: /* child */
! 129: close(sv[0]);
! 130: close(signal_pipe[0]);
! 131: close(signal_pipe[1]);
! 132: fcntl(sv[1], F_SETFD, FD_CLOEXEC);
! 133: restore_signals();
! 134: if (exec_setup(details, NULL, -1) == TRUE) {
! 135: /* headed for execve() */
! 136: if (details->closefrom >= 0)
! 137: closefrom(details->closefrom);
! 138: #ifdef HAVE_SELINUX
! 139: if (ISSET(details->flags, CD_RBAC_ENABLED))
! 140: selinux_execve(details->command, details->argv, details->envp);
! 141: else
! 142: #endif
! 143: my_execve(details->command, details->argv, details->envp);
! 144: }
! 145: cstat.type = CMD_ERRNO;
! 146: cstat.val = errno;
! 147: send(sv[1], &cstat, sizeof(cstat), 0);
! 148: _exit(1);
! 149: }
! 150: return child;
! 151: }
! 152:
! 153: static struct signal_state {
! 154: int signo;
! 155: sigaction_t sa;
! 156: } saved_signals[] = {
! 157: { SIGALRM },
! 158: { SIGCHLD },
! 159: { SIGCONT },
! 160: { SIGHUP },
! 161: { SIGINT },
! 162: { SIGPIPE },
! 163: { SIGQUIT },
! 164: { SIGTERM },
! 165: { SIGTSTP },
! 166: { SIGTTIN },
! 167: { SIGTTOU },
! 168: { SIGUSR1 },
! 169: { SIGUSR2 },
! 170: { -1 }
! 171: };
! 172:
! 173: /*
! 174: * Save signal handler state so it can be restored before exec.
! 175: */
! 176: void
! 177: save_signals(void)
! 178: {
! 179: struct signal_state *ss;
! 180:
! 181: for (ss = saved_signals; ss->signo != -1; ss++)
! 182: sigaction(ss->signo, NULL, &ss->sa);
! 183: }
! 184:
! 185: /*
! 186: * Restore signal handlers to initial state.
! 187: */
! 188: void
! 189: restore_signals(void)
! 190: {
! 191: struct signal_state *ss;
! 192:
! 193: for (ss = saved_signals; ss->signo != -1; ss++)
! 194: sigaction(ss->signo, &ss->sa, NULL);
! 195: }
! 196:
! 197: /*
! 198: * Execute a command, potentially in a pty with I/O loggging.
! 199: * This is a little bit tricky due to how POSIX job control works and
! 200: * we fact that we have two different controlling terminals to deal with.
! 201: */
! 202: int
! 203: sudo_execve(struct command_details *details, struct command_status *cstat)
! 204: {
! 205: int maxfd, n, nready, sv[2], log_io = FALSE;
! 206: const char *utmp_user = NULL;
! 207: fd_set *fdsr, *fdsw;
! 208: sigaction_t sa;
! 209: pid_t child;
! 210:
! 211: /* If running in background mode, fork and exit. */
! 212: if (ISSET(details->flags, CD_BACKGROUND)) {
! 213: switch (fork()) {
! 214: case -1:
! 215: cstat->type = CMD_ERRNO;
! 216: cstat->val = errno;
! 217: return -1;
! 218: case 0:
! 219: /* child continues without controlling terminal */
! 220: (void)setpgid(0, 0);
! 221: break;
! 222: default:
! 223: /* parent exits (but does not flush buffers) */
! 224: _exit(0);
! 225: }
! 226: }
! 227:
! 228: /*
! 229: * If we have an I/O plugin or the policy plugin has requested one, we
! 230: * need to allocate a pty. It is OK to set log_io in the pty-only case
! 231: * as the io plugin tailqueue will be empty and no I/O logging will occur.
! 232: */
! 233: if (!tq_empty(&io_plugins) || ISSET(details->flags, CD_USE_PTY)) {
! 234: log_io = TRUE;
! 235: if (ISSET(details->flags, CD_SET_UTMP))
! 236: utmp_user = details->utmp_user ? details->utmp_user : user_details.username;
! 237: sudo_debug(8, "allocate pty for I/O logging");
! 238: pty_setup(details->euid, user_details.tty, utmp_user);
! 239: }
! 240:
! 241: /*
! 242: * We communicate with the child over a bi-directional pair of sockets.
! 243: * Parent sends signal info to child and child sends back wait status.
! 244: */
! 245: if (socketpair(PF_UNIX, SOCK_DGRAM, 0, sv) == -1)
! 246: error(1, _("unable to create sockets"));
! 247:
! 248: /*
! 249: * We use a pipe to atomically handle signal notification within
! 250: * the select() loop.
! 251: */
! 252: if (pipe_nonblock(signal_pipe) != 0)
! 253: error(1, _("unable to create pipe"));
! 254:
! 255: zero_bytes(&sa, sizeof(sa));
! 256: sigemptyset(&sa.sa_mask);
! 257:
! 258: /*
! 259: * Signals for forward to the child process (excluding SIGALRM and SIGCHLD).
! 260: * Note: HP-UX select() will not be interrupted if SA_RESTART set.
! 261: */
! 262: sa.sa_flags = SA_INTERRUPT; /* do not restart syscalls */
! 263: sa.sa_handler = handler;
! 264: sigaction(SIGALRM, &sa, NULL);
! 265: sigaction(SIGCHLD, &sa, NULL);
! 266: sigaction(SIGHUP, &sa, NULL);
! 267: sigaction(SIGINT, &sa, NULL);
! 268: sigaction(SIGPIPE, &sa, NULL);
! 269: sigaction(SIGQUIT, &sa, NULL);
! 270: sigaction(SIGTERM, &sa, NULL);
! 271: sigaction(SIGUSR1, &sa, NULL);
! 272: sigaction(SIGUSR2, &sa, NULL);
! 273:
! 274: /* Max fd we will be selecting on. */
! 275: maxfd = MAX(sv[0], signal_pipe[0]);
! 276:
! 277: /*
! 278: * Child will run the command in the pty, parent will pass data
! 279: * to and from pty. Adjusts maxfd as needed.
! 280: */
! 281: if (log_io)
! 282: child = fork_pty(details, sv, &maxfd);
! 283: else
! 284: child = fork_cmnd(details, sv);
! 285: close(sv[1]);
! 286:
! 287: /* Set command timeout if specified. */
! 288: if (ISSET(details->flags, CD_SET_TIMEOUT))
! 289: alarm(details->timeout);
! 290:
! 291: #ifdef HAVE_SETLOCALE
! 292: /*
! 293: * I/O logging must be in the C locale for floating point numbers
! 294: * to be logged consistently.
! 295: */
! 296: setlocale(LC_ALL, "C");
! 297: #endif
! 298:
! 299: /*
! 300: * In the event loop we pass input from user tty to master
! 301: * and pass output from master to stdout and IO plugin.
! 302: */
! 303: fdsr = (fd_set *)emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
! 304: fdsw = (fd_set *)emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
! 305: for (;;) {
! 306: zero_bytes(fdsw, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
! 307: zero_bytes(fdsr, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
! 308:
! 309: FD_SET(signal_pipe[0], fdsr);
! 310: FD_SET(sv[0], fdsr);
! 311: if (!tq_empty(&sigfwd_list))
! 312: FD_SET(sv[0], fdsw);
! 313: if (log_io)
! 314: fd_set_iobs(fdsr, fdsw); /* XXX - better name */
! 315: nready = select(maxfd + 1, fdsr, fdsw, NULL, NULL);
! 316: if (nready == -1) {
! 317: if (errno == EINTR)
! 318: continue;
! 319: error(1, _("select failed"));
! 320: }
! 321: if (FD_ISSET(sv[0], fdsw)) {
! 322: forward_signals(sv[0]);
! 323: }
! 324: if (FD_ISSET(signal_pipe[0], fdsr)) {
! 325: n = handle_signals(signal_pipe[0], child, log_io, cstat);
! 326: if (n == 0) {
! 327: /* Child has exited, cstat is set, we are done. */
! 328: goto done;
! 329: }
! 330: if (n == -1) {
! 331: /* Error reading signal_pipe[0], should not happen. */
! 332: break;
! 333: }
! 334: /* Restart event loop so signals get sent to child immediately. */
! 335: continue;
! 336: }
! 337: if (FD_ISSET(sv[0], fdsr)) {
! 338: /* read child status */
! 339: n = recv(sv[0], cstat, sizeof(*cstat), 0);
! 340: if (n == -1) {
! 341: if (errno == EINTR)
! 342: continue;
! 343: /*
! 344: * If not logging I/O we will receive ECONNRESET when
! 345: * the command is executed. It is safe to ignore this.
! 346: */
! 347: if (log_io && errno != EAGAIN) {
! 348: cstat->type = CMD_ERRNO;
! 349: cstat->val = errno;
! 350: break;
! 351: }
! 352: }
! 353: if (cstat->type == CMD_WSTATUS) {
! 354: if (WIFSTOPPED(cstat->val)) {
! 355: /* Suspend parent and tell child how to resume on return. */
! 356: sudo_debug(8, "child stopped, suspending parent");
! 357: n = suspend_parent(WSTOPSIG(cstat->val));
! 358: schedule_signal(n);
! 359: continue;
! 360: } else {
! 361: /* Child exited or was killed, either way we are done. */
! 362: break;
! 363: }
! 364: } else if (cstat->type == CMD_ERRNO) {
! 365: /* Child was unable to execute command or broken pipe. */
! 366: break;
! 367: }
! 368: }
! 369:
! 370: if (perform_io(fdsr, fdsw, cstat) != 0) {
! 371: /* I/O error, kill child if still alive and finish. */
! 372: schedule_signal(SIGKILL);
! 373: forward_signals(sv[0]);
! 374: break;
! 375: }
! 376: }
! 377:
! 378: if (log_io) {
! 379: /* Flush any remaining output and free pty-related memory. */
! 380: pty_close(cstat);
! 381: }
! 382:
! 383: #ifdef HAVE_SELINUX
! 384: if (ISSET(details->flags, CD_RBAC_ENABLED)) {
! 385: /* This is probably not needed in log_io mode. */
! 386: if (selinux_restore_tty() != 0)
! 387: warningx(_("unable to restore tty label"));
! 388: }
! 389: #endif
! 390:
! 391: done:
! 392: efree(fdsr);
! 393: efree(fdsw);
! 394: while (!tq_empty(&sigfwd_list)) {
! 395: struct sigforward *sigfwd = tq_first(&sigfwd_list);
! 396: tq_remove(&sigfwd_list, sigfwd);
! 397: efree(sigfwd);
! 398: }
! 399:
! 400: return cstat->type == CMD_ERRNO ? -1 : 0;
! 401: }
! 402:
! 403: /*
! 404: * Read signals on fd written to by handler().
! 405: * Returns -1 on error, 0 on child exit, else 1.
! 406: */
! 407: static int
! 408: handle_signals(int fd, pid_t child, int log_io, struct command_status *cstat)
! 409: {
! 410: unsigned char signo;
! 411: ssize_t nread;
! 412: int status;
! 413: pid_t pid;
! 414:
! 415: for (;;) {
! 416: /* read signal pipe */
! 417: nread = read(signal_pipe[0], &signo, sizeof(signo));
! 418: if (nread <= 0) {
! 419: /* It should not be possible to get EOF but just in case. */
! 420: if (nread == 0)
! 421: errno = ECONNRESET;
! 422: /* Restart if interrupted by signal so the pipe doesn't fill. */
! 423: if (errno == EINTR)
! 424: continue;
! 425: /* If pipe is empty, we are done. */
! 426: if (errno == EAGAIN)
! 427: break;
! 428: sudo_debug(9, "error reading signal pipe %s", strerror(errno));
! 429: cstat->type = CMD_ERRNO;
! 430: cstat->val = errno;
! 431: return -1;
! 432: }
! 433: sudo_debug(9, "received signal %d", signo);
! 434: if (signo == SIGCHLD) {
! 435: /*
! 436: * If logging I/O, child is the intermediate process,
! 437: * otherwise it is the command itself.
! 438: */
! 439: do {
! 440: pid = waitpid(child, &status, WUNTRACED|WNOHANG);
! 441: } while (pid == -1 && errno == EINTR);
! 442: if (pid == child) {
! 443: /* If not logging I/O and child has exited we are done. */
! 444: if (!log_io) {
! 445: if (WIFSTOPPED(status)) {
! 446: /*
! 447: * Save the controlling terminal's process group
! 448: * so we can restore it after we resume.
! 449: */
! 450: pid_t saved_pgrp = (pid_t)-1;
! 451: int fd = open(_PATH_TTY, O_RDWR|O_NOCTTY, 0);
! 452: if (fd != -1)
! 453: saved_pgrp = tcgetpgrp(fd);
! 454: if (kill(getpid(), WSTOPSIG(status)) != 0) {
! 455: warning("kill(%d, %d)", (int)getpid(),
! 456: WSTOPSIG(status));
! 457: }
! 458: if (fd != -1) {
! 459: if (saved_pgrp != (pid_t)-1)
! 460: (void)tcsetpgrp(fd, saved_pgrp);
! 461: close(fd);
! 462: }
! 463: } else {
! 464: /* Child has exited, we are done. */
! 465: cstat->type = CMD_WSTATUS;
! 466: cstat->val = status;
! 467: return 0;
! 468: }
! 469: }
! 470: /* Else we get ECONNRESET on sv[0] if child dies. */
! 471: }
! 472: } else {
! 473: if (log_io) {
! 474: /* Schedule signo to be forwared to the child. */
! 475: schedule_signal(signo);
! 476: } else {
! 477: /* Nothing listening on sv[0], send directly. */
! 478: if (signo == SIGALRM)
! 479: terminate_child(child, FALSE);
! 480: else if (kill(child, signo) != 0)
! 481: warning("kill(%d, %d)", (int)child, signo);
! 482: }
! 483: }
! 484: }
! 485: return 1;
! 486: }
! 487:
! 488: /*
! 489: * Forward signals in sigfwd_list to child listening on fd.
! 490: */
! 491: static void
! 492: forward_signals(int sock)
! 493: {
! 494: struct sigforward *sigfwd;
! 495: struct command_status cstat;
! 496: ssize_t nsent;
! 497:
! 498: while (!tq_empty(&sigfwd_list)) {
! 499: sigfwd = tq_first(&sigfwd_list);
! 500: sudo_debug(9, "sending signal %d to child over backchannel",
! 501: sigfwd->signo);
! 502: cstat.type = CMD_SIGNO;
! 503: cstat.val = sigfwd->signo;
! 504: do {
! 505: nsent = send(sock, &cstat, sizeof(cstat), 0);
! 506: } while (nsent == -1 && errno == EINTR);
! 507: tq_remove(&sigfwd_list, sigfwd);
! 508: efree(sigfwd);
! 509: if (nsent != sizeof(cstat)) {
! 510: if (errno == EPIPE) {
! 511: /* Other end of socket gone, empty out sigfwd_list. */
! 512: while (!tq_empty(&sigfwd_list)) {
! 513: sigfwd = tq_first(&sigfwd_list);
! 514: tq_remove(&sigfwd_list, sigfwd);
! 515: efree(sigfwd);
! 516: }
! 517: }
! 518: break;
! 519: }
! 520: }
! 521: }
! 522:
! 523: /*
! 524: * Schedule a signal to be forwared.
! 525: */
! 526: static void
! 527: schedule_signal(int signo)
! 528: {
! 529: struct sigforward *sigfwd;
! 530:
! 531: sigfwd = emalloc(sizeof(*sigfwd));
! 532: sigfwd->prev = sigfwd;
! 533: sigfwd->next = NULL;
! 534: sigfwd->signo = signo;
! 535: tq_append(&sigfwd_list, sigfwd);
! 536: }
! 537:
! 538: /*
! 539: * Generic handler for signals passed from parent -> child.
! 540: * The other end of signal_pipe is checked in the main event loop.
! 541: */
! 542: void
! 543: handler(int s)
! 544: {
! 545: unsigned char signo = (unsigned char)s;
! 546:
! 547: /*
! 548: * The pipe is non-blocking, if we overflow the kernel's pipe
! 549: * buffer we drop the signal. This is not a problem in practice.
! 550: */
! 551: if (write(signal_pipe[1], &signo, sizeof(signo)) == -1)
! 552: /* shut up glibc */;
! 553: }
! 554:
! 555: /*
! 556: * Open a pipe and make both ends non-blocking.
! 557: * Returns 0 on success and -1 on error.
! 558: */
! 559: int
! 560: pipe_nonblock(int fds[2])
! 561: {
! 562: int flags, rval;
! 563:
! 564: rval = pipe(fds);
! 565: if (rval != -1) {
! 566: flags = fcntl(fds[0], F_GETFL, 0);
! 567: if (flags != -1 && !ISSET(flags, O_NONBLOCK))
! 568: rval = fcntl(fds[0], F_SETFL, flags | O_NONBLOCK);
! 569: if (rval != -1) {
! 570: flags = fcntl(fds[1], F_GETFL, 0);
! 571: if (flags != -1 && !ISSET(flags, O_NONBLOCK))
! 572: rval = fcntl(fds[1], F_SETFL, flags | O_NONBLOCK);
! 573: }
! 574: if (rval == -1) {
! 575: close(fds[0]);
! 576: close(fds[1]);
! 577: }
! 578: }
! 579:
! 580: return rval;
! 581: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>