/* * Copyright (c) 2004-2005 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_DEV_SYSTRACE_H # include #else # ifdef HAVE_SYS_SYSTRACE_H # include # else # ifdef HAVE_LINUX_SYSTRACE_H # include # else # include # endif # endif #endif #include "sudo.h" #include "mon_systrace.h" #ifndef lint __unused static const char rcsid[] = "$Sudo$"; #endif /* lint */ /* * Open the systrace device and return the fd or -1 on failure. */ static int systrace_open() { int serrno, fd; fd = open(_PATH_DEV_SYSTRACE, O_RDONLY, 0644); if (fd == -1) return(-1); serrno = errno; #ifdef SYSTR_CLONE { int tfd; if (ioctl(fd, STRIOCCLONE, &tfd) == -1) goto bad; close(fd); fd = tfd; } #endif if (fcntl(fd, F_SETFD, 1) == -1) /* really needed? */ goto bad; return(fd); bad: close(fd); errno = serrno; return(-1); } static void catchsig(signo) int signo; { dodetach = signo; return; } /* * Fork a process that monitors the command to be run and its descendents. * The monitoring process will detach upon receipt of SIGHUP, SIGINT or SIGTERM. */ void systrace_attach(pid) pid_t pid; { struct syscallhandler *handler; struct systrace_answer ans; struct str_message msg; sigaction_t sa, osa; sigset_t set, oset; ssize_t nread; int fd, status; if ((fd = systrace_open()) == -1) error(1, "unable to open systrace"); fflush(stdout); /* * Do signal setup early so there is no race between when the tracer * kill()s the tracee and when the tracee calls sigsuspend(). */ sigfillset(&set); if (sigprocmask(SIG_BLOCK, &set, &oset) != 0) error(1, "sigprocmask"); sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = catchsig; if (sigaction(SIGUSR1, &sa, &osa) != 0) error(1, "sigaction"); switch (fork()) { case -1: error(1, "can't fork"); case 0: /* tracer, fork again to completely disassociate */ switch (fork()) { case -1: warning("can't fork"); kill(pid, SIGKILL); _exit(1); case 0: break; default: /* the main sudo process will wait for us */ _exit(0); } break; default: /* tracee, sleep until the tracer process wakes us up. */ close(fd); sigdelset(&set, SIGUSR1); (void) sigsuspend(&set); if (sigprocmask(SIG_SETMASK, &oset, NULL) != 0) { warning("sigprocmask"); exit(1); } return; } /* set signal state for tracer */ dodetach = 0; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = catchsig; if (sigaction(SIGUSR1, &osa, NULL) != 0 || sigaction(SIGHUP, &sa, NULL) != 0 || sigaction(SIGINT, &sa, NULL) != 0 || sigaction(SIGTERM, &sa, NULL) != 0 || sigprocmask(SIG_SETMASK, &oset, NULL) != 0) { warning("unable to setup signals for %s", user_cmnd); goto fail; } /* become a daemon */ set_perms(PERM_FULL_ROOT); if (setsid() == -1) { warning("setsid"); kill(pid, SIGKILL); _exit(1); } (void) chdir("/"); #ifdef HAVE_SETPROCTITLE setproctitle("monitor %s%s%s", user_base, user_args ? " " : "", user_args ? user_args : ""); #endif if (ioctl(fd, STRIOCATTACH, &pid) == -1) { if (errno == EBUSY) { /* already being traced, nothing to do */ (void) kill(pid, SIGUSR1); _exit(0); } warning("unable to systrace %s", user_cmnd); goto fail; } new_child(-1, pid); if (set_policy(fd, children.first) != 0) { warning("failed to set policy for %s", user_cmnd); goto fail; } if (kill(pid, SIGUSR1) != 0) { warning("unable to wake up sleeping child"); _exit(1); } /* handle systrace events until the child finishes */ for (;;) { if ((nread = read(fd, &msg, sizeof(msg))) != sizeof(msg)) { if (dodetach) { detachall(fd); _exit(0); } if (nread == -1 && (errno == EINTR || errno == EAGAIN)) continue; killall(SIGKILL); _exit(nread != 0); /* shouldn't happen */ } switch (msg.msg_type) { case SYSTR_MSG_CHILD: /* either a fork or an exit */ if (msg.msg_data.msg_child.new_pid != -1) { new_child(msg.msg_pid, msg.msg_data.msg_child.new_pid); } else { rm_child(msg.msg_pid); if (children.first == NULL) _exit(0); } break; case SYSTR_MSG_UGID: /* uid/gid change */ memset(&ans, 0, sizeof(ans)); ans.stra_pid = msg.msg_pid; ans.stra_seqnr = msg.msg_seqnr; ans.stra_policy = SYSTR_POLICY_PERMIT; if ((ioctl(fd, STRIOCANSWER, &ans)) == 0) update_child(msg.msg_pid, msg.msg_data.msg_ugid.uid); break; case SYSTR_MSG_ASK: memset(&ans, 0, sizeof(ans)); ans.stra_pid = msg.msg_pid; ans.stra_seqnr = msg.msg_seqnr; ans.stra_policy = SYSTR_POLICY_PERMIT; handler = find_handler(msg.msg_pid, msg.msg_data.msg_ask.code); if (handler != NULL && handler->checker != NULL) { status = handler->checker(fd, msg.msg_pid, msg.msg_seqnr, &msg.msg_data.msg_ask, &ans.stra_policy, &ans.stra_error); if (status >= 0 && ioctl(fd, STRIOCANSWER, &ans) == 0) { if (handler->logger != NULL) handler->logger(status); } } else if (ioctl(fd, STRIOCANSWER, &ans) == -1) warning("STRIOCANSWER"); break; case SYSTR_MSG_EMUL: /* Change in emulation. */ memset(&ans, 0, sizeof(ans)); ans.stra_pid = msg.msg_pid; ans.stra_seqnr = msg.msg_seqnr; if (switch_emulation(fd, &msg) == 0) ans.stra_policy = SYSTR_POLICY_PERMIT; else { warningx("unsupported emulation \"%s\"", msg.msg_data.msg_emul.emul); ans.stra_policy = SYSTR_POLICY_NEVER; } if (ioctl(fd, STRIOCANSWER, &ans) == -1) warning("STRIOCANSWER"); break; #ifdef SYSTR_MSG_POLICYFREE case SYSTR_MSG_POLICYFREE: break; #endif default: #ifdef SUDO_DEVEL warningx("unexpected message type %d", msg.msg_type); #endif memset(&ans, 0, sizeof(ans)); ans.stra_pid = msg.msg_pid; ans.stra_seqnr = msg.msg_seqnr; ans.stra_policy = SYSTR_POLICY_PERMIT; if (ioctl(fd, STRIOCANSWER, &ans) == -1) warning("STRIOCANSWER"); break; } } fail: killall(SIGKILL); _exit(1); } /* * Push a new child to the head of the list, inheriting the struct pw * of its parent. */ static void new_child(ppid, pid) pid_t ppid; pid_t pid; { struct childinfo *entry; struct passwd *pw; struct syscallaction *action; struct emulation *emul; if (ppid != -1 && (entry = find_child(ppid)) != NULL) { pw = entry->pw; action = entry->action; } else { pw = runas_pw; for (emul = emulations; emul != NULL; emul++) if (strcmp(emul->name, "native") == 0) { action = emul->action; break; } if (emul == NULL) errorx(1, "unable to find native emulation!"); } entry = (struct childinfo *) emalloc(sizeof(*entry)); entry->pid = pid; entry->pw = pw; entry->action = action; entry->next = children.first; children.first = entry; } static int switch_emulation(fd, msgp) int fd; struct str_message *msgp; { struct childinfo *entry; struct emulation *emul; if ((entry = find_child(msgp->msg_pid)) == NULL) return(-1); for (emul = emulations; emul != NULL; emul++) if (strcmp(emul->name, msgp->msg_data.msg_emul.emul) == 0) { entry->action = emul->action; return(set_policy(fd, entry)); } return(-1); } /* * Remove the named pid from the list. */ static void rm_child(pid) pid_t pid; { struct childinfo *cur, *prev; for (prev = NULL, cur = children.first; cur != NULL; cur = cur->next) { if (cur->pid == pid) { if (prev != NULL) prev->next = cur->next; else children.first = cur->next; efree(cur); break; } prev = cur; } } /* * Find a child by pid. */ static struct childinfo * find_child(pid) pid_t pid; { struct childinfo *cur; for (cur = children.first; cur != NULL; cur = cur->next) { if (cur->pid == pid) return(cur); } return(NULL); } /* * Update the uid associated with a pid. */ static void update_child(pid, uid) pid_t pid; uid_t uid; { struct childinfo *child; if ((child = find_child(pid)) == NULL) return; /* cannot happen */ if (child->pw->pw_uid != uid) { /* look up uid in passwd db, using a stub on failure */ if ((child->pw = sudo_getpwuid(uid)) == NULL) child->pw = sudo_fakepwuid(uid); } } /* * Create a policy that intercepts execve and lets all others go free. */ static int set_policy(fd, child) int fd; struct childinfo *child; { struct syscallaction *sca; struct systrace_policy pol; int i; pol.strp_op = SYSTR_POLICY_NEW; pol.strp_num = -1; pol.strp_maxents = SYSTRACE_MAXENTS; if (ioctl(fd, STRIOCPOLICY, &pol) == -1) return(-1); pol.strp_op = SYSTR_POLICY_ASSIGN; pol.strp_pid = child->pid; if (ioctl(fd, STRIOCPOLICY, &pol) == -1) return(-1); for (i = 0; i < SYSTRACE_MAXENTS; i++) { pol.strp_op = SYSTR_POLICY_MODIFY; pol.strp_policy = SYSTR_POLICY_PERMIT; pol.strp_code = i; for (sca = child->action; sca->code != -1; sca++) { if (sca->code == i) { pol.strp_policy = sca->policy; break; } } if (ioctl(fd, STRIOCPOLICY, &pol) == -1) return(-1); } return(0); } /* * Read from an address and store in buf. * XXX - should deal with EBUSY from STRIOCIO */ static ssize_t systrace_read(fd, pid, addr, buf, bufsiz) int fd; pid_t pid; void *addr; void *buf; size_t bufsiz; { struct systrace_io io; int rval; memset(&io, 0, sizeof(io)); io.strio_pid = pid; io.strio_addr = buf; io.strio_len = bufsiz; io.strio_offs = addr; io.strio_op = SYSTR_READ; if ((rval = ioctl(fd, STRIOCIO, &io)) != 0) warning("systrace_read: STRIOCIO"); return(rval ? -1 : (ssize_t)io.strio_len); } /* * Read up to bufsiz bytes from addr into buf, stopping when we hit * a NUL byte. Reads are done in chunks since STRIOCIO cannot * handle a strio_len > the actual kernel buffer. It might be nice * to pass a starting chunksize though. */ static ssize_t read_string(fd, pid, addr, buf, bufsiz) int fd; pid_t pid; void *addr; char *buf; size_t bufsiz; { size_t chunksiz = 32; ssize_t nread; char *cp = buf, *ep; while (bufsiz >= chunksiz) { if ((nread = systrace_read(fd, pid, addr, cp, chunksiz)) != -1) { if ((ep = memchr(cp, '\0', nread)) != NULL) { cp = ep; /* found NUL byte in chunk, done */ break; } cp += nread; addr += nread; bufsiz -= nread; } else { if (errno != EINVAL || chunksiz == 1) return(-1); chunksiz >>= 1; /* chunksiz too big, halve it */ } } #ifdef SUDO_DEVEL if (cp == buf) warningx("read empty string, chunksize == %d", chunksiz); /* XXX, should not happen but does */ #endif return(bufsiz >= chunksiz ? cp - buf : -1); } static struct syscallhandler * find_handler(pid, code) pid_t pid; int code; { struct syscallaction *sca; struct childinfo *child; if ((child = find_child(pid)) == NULL) { warningx("unable to find child with pid %d", pid); return(NULL); } for (sca = child->action; sca->code != -1; sca++) { if (sca->code == code) return(&sca->handler); } return(NULL); } #define SUDO_USER 0 #define SUDO_COMMAND 1 #define SUDO_UID 2 #define SUDO_GID 3 #ifdef STRIOCINJECT /* * Write buf to a kernel address. * XXX - should deal with EBUSY from STRIOCIO */ static ssize_t systrace_write(fd, pid, addr, buf, len) int fd; pid_t pid; void *addr; void *buf; size_t len; { struct systrace_io io; int rval; memset(&io, 0, sizeof(io)); io.strio_pid = pid; io.strio_addr = buf; io.strio_len = len; io.strio_offs = addr; io.strio_op = SYSTR_WRITE; if ((rval = ioctl(fd, STRIOCIO, &io)) != 0) warning("systrace_write: STRIOCIO"); return(rval ? -1 : (ssize_t)io.strio_len); } /* * Update SUDO_* variables in the process's environment. */ static int update_env(fd, pid, seqnr, askp) int fd; pid_t pid; u_int16_t seqnr; struct str_msg_ask *askp; { struct systrace_replace repl; ssize_t len; char *envbuf[ARG_MAX / sizeof(char *)], **envp, **envep; char buf[ARG_MAX], *ap, *cp, *off, *envptrs[4], *offsets[4], *replace[4]; int n; /* * Iterate through the environment, copying the data pointers and * attempting to update the SUDO_* variables (space permitting). */ memset(offsets, 0, sizeof(offsets)); memset(replace, 1, sizeof(replace)); off = (char *)askp->args[2]; envep = envbuf + (sizeof(envbuf) / sizeof(char *)); for (envp = envbuf; envp < envep; envp++, off += sizeof(char *)) { if (systrace_read(fd, pid, off, &ap, sizeof(ap)) == -1) return(-1); if ((*envp = ap) == NULL) break; memset(buf, 0, sizeof(buf)); if ((len = read_string(fd, pid, ap, buf, sizeof(buf))) == -1) return(-1); if (buf[0] == 'S') { if (strncmp(buf, "SUDO_USER=", 10) == 0) { offsets[SUDO_USER] = off; envptrs[SUDO_USER] = ap; if (strcmp(&buf[10], user_name) == 0) replace[SUDO_USER] = NULL; else { len = strlen(buf); n = snprintf(buf, len + 1, "SUDO_USER=%s", user_name); if (n > 0 && n <= len && systrace_write(fd, pid, ap, buf, len + 1) != -1) replace[SUDO_USER] = NULL; } } else if (strncmp(buf, "SUDO_COMMAND=", 13) == 0) { offsets[SUDO_COMMAND] = off; envptrs[SUDO_COMMAND] = ap; len = strlen(user_cmnd); if (strncmp(&buf[13], user_cmnd, len) == 0) { if (user_args == NULL) { if (buf[13 + len] == '\0') replace[SUDO_COMMAND] = NULL; } else if (buf[13 + len] == ' ') { if (strcmp(&buf[14 + len], user_args) == 0) replace[SUDO_COMMAND] = NULL; } } if (replace[SUDO_COMMAND] != NULL) { len = strlen(buf); n = snprintf(buf, len + 1, "SUDO_COMMAND=%s%s%s", user_cmnd, user_args ? " " : "", user_args ? user_args : ""); if (n > 0 && n <= len && systrace_write(fd, pid, ap, buf, len + 1) != -1) replace[SUDO_COMMAND] = NULL; } } else if (strncmp(buf, "SUDO_UID=", 9) == 0) { offsets[SUDO_UID] = off; envptrs[SUDO_UID] = ap; if ((uid_t) atoi(&buf[9]) == user_uid) replace[SUDO_UID] = NULL; else { len = strlen(buf); n = snprintf(buf, len + 1, "SUDO_UID=%lu", (unsigned long) user_uid); if (n > 0 && n <= len && systrace_write(fd, pid, ap, buf, len + 1) != -1) replace[SUDO_UID] = NULL; } } else if (strncmp(buf, "SUDO_GID=", 9) == 0) { offsets[SUDO_GID] = off; envptrs[SUDO_GID] = ap; if ((gid_t) atoi(&buf[9]) == user_gid) replace[SUDO_GID] = NULL; else { len = strlen(buf); n = snprintf(buf, len + 1, "SUDO_GID=%lu", (unsigned long) user_gid); if (n > 0 && n <= len && systrace_write(fd, pid, ap, buf, len + 1) != -1) replace[SUDO_GID] = NULL; } } } } /* * Allocate space for any SUDO_* variables we didn't have room for * or that weren't present. */ cp = buf; if (replace[SUDO_USER]) { n = snprintf(cp, sizeof(buf) - (cp - buf), "SUDO_USER=%s", user_name); if (n < 0 || n >= sizeof(buf) - (cp - buf)) return(-1); replace[SUDO_USER] = cp; cp += n + 1; } if (replace[SUDO_COMMAND]) { n = snprintf(cp, sizeof(buf) - (cp - buf), "SUDO_COMMAND=%s%s%s", user_cmnd, user_args ? " " : "", user_args ? user_args : ""); if (n < 0 || n >= sizeof(buf) - (cp - buf)) return(-1); replace[SUDO_COMMAND] = cp; cp += n + 1; } if (replace[SUDO_UID]) { n = snprintf(cp, sizeof(buf) - (cp - buf), "SUDO_UID=%lu", (unsigned long) user_uid); if (n < 0 || n >= sizeof(buf) - (cp - buf)) return(-1); replace[SUDO_UID] = cp; cp += n + 1; } if (replace[SUDO_GID]) { n = snprintf(cp, sizeof(buf) - (cp - buf), "SUDO_GID=%lu", (unsigned long) user_gid); if (n < 0 || n >= sizeof(buf) - (cp - buf)) return(-1); replace[SUDO_GID] = cp; cp += n + 1; } if (cp != buf) { struct systrace_inject inject; memset(&inject, 0, sizeof(inject)); inject.stri_pid = pid; inject.stri_addr = buf; inject.stri_len = cp - buf; if (ioctl(fd, STRIOCINJECT, &inject) != 0) return(-1); n = (offsets[SUDO_USER] == NULL) + (offsets[SUDO_COMMAND] == NULL) + (offsets[SUDO_UID] == NULL) + (offsets[SUDO_GID] == NULL); /* * If there were SUDO_* variables missing in the environment we * need to add them to our copy of envp and replace the envp in * the user process. If no, we just update the addresses * of the modified environment variables in the process's envp. */ if (n == 0) { /* No missing variables, just update addresses in user process. */ for (n = 0; n < 4; n++) { if (replace[n] == NULL) continue; ap = inject.stri_addr + (replace[n] - buf); if (systrace_write(fd, pid, offsets[n], &ap, sizeof(ap)) == -1) return(-1); } } else { /* * There were missing variables; make existing variables * relative to inject.stri_addr and add missing one. */ for (envp = envbuf; *envp != NULL; envp++) { if (replace[SUDO_USER] != NULL && *envp == envptrs[SUDO_USER]) *envp = inject.stri_addr + (replace[SUDO_USER] - buf); else if (replace[SUDO_COMMAND] != NULL && *envp == envptrs[SUDO_COMMAND]) *envp = inject.stri_addr + (replace[SUDO_COMMAND] - buf); else if (replace[SUDO_UID] != NULL && *envp == envptrs[SUDO_UID]) *envp = inject.stri_addr + (replace[SUDO_UID] - buf); else if (replace[SUDO_GID] != NULL && *envp == envptrs[SUDO_GID]) *envp = inject.stri_addr + (replace[SUDO_GID] - buf); } if (envp + n >= envep) return(-1); if (offsets[SUDO_USER] == NULL) *envp++ = inject.stri_addr + (replace[SUDO_USER] - buf); if (offsets[SUDO_COMMAND] == NULL) *envp++ = inject.stri_addr + (replace[SUDO_COMMAND] - buf); if (offsets[SUDO_UID] == NULL) *envp++ = inject.stri_addr + (replace[SUDO_UID] - buf); if (offsets[SUDO_GID] == NULL) *envp++ = inject.stri_addr + (replace[SUDO_GID] - buf); *envp++ = NULL; /* Replace existing envp with our new one. */ memset(&repl, 0, sizeof(repl)); repl.strr_pid = pid; repl.strr_seqnr = seqnr; repl.strr_nrepl = 1; repl.strr_base = (char *)envbuf; repl.strr_len = (char *)envp - (char *)envbuf; repl.strr_argind[0] = 2; repl.strr_off[0] = 0; repl.strr_offlen[0] = (char *)envp - (char *)envbuf; if (ioctl(fd, STRIOCREPLACE, &repl) != 0) return(-1); } } return(0); } #endif /* STRIOCINJECT */ /* * Decode path and argv from systrace and fill in user_cmnd, * user_base and user_args. */ static int decode_args(fd, pid, askp) int fd; pid_t pid; struct str_msg_ask *askp; { ssize_t len; int i, argc, argc_max; char *off, *ap, *cp, *ep, **argv; static char pbuf[PATH_MAX], abuf[ARG_MAX]; /* * Fill in user_cmnd and user_base from the 1st arg to execve(). * Note that this is the path the kernel will execute, which * may be different from argv[0]. */ memset(pbuf, 0, sizeof(pbuf)); if (read_string(fd, pid, (void *)askp->args[0], pbuf, sizeof(pbuf)) == -1) return(-1); if ((user_base = strrchr(user_cmnd = pbuf, '/')) != NULL) user_base++; else user_base = user_cmnd; user_args = NULL; /* XXX - write exec path back to stack gap */ /* * Make a local copy of argv, looping until we hit the * terminating NULL pointer. */ argc = 0; argc_max = 16; argv = emalloc2(argc_max, sizeof(char *)); memset(abuf, 0, sizeof(abuf)); off = (char *)askp->args[1]; for (cp = abuf, ep = abuf + sizeof(abuf); cp < ep; off += sizeof(char *)) { if (systrace_read(fd, pid, off, &ap, sizeof(ap)) == -1) return(-1); if (ap == NULL) break; /* end of args */ if (argc + 1 >= argc_max) { argc_max *= 2; argv = erealloc3(argv, argc_max, sizeof(char *)); } if ((len = read_string(fd, pid, ap, cp, ep - cp)) == -1) return(-1); argv[argc++] = cp; cp += len; } ep = cp; argv[argc] = NULL; /* XXX - now write argv back into stack gap. */ /* * Collapse argv into user_args, skipping argv[0]. * Since argv strings are contiguous (in abuf) we can * just replace the previous char in each string with a * space. */ if (argc > 1) { user_args = argv[1]; for (i = 2; i < argc; i++) argv[i][-1] = ' '; /* replace NUL with a space */ } efree(argv); return(0); } static void log_exec(status) int status; { if (status > 0) log_auth(status, TRUE); } /* * Decode the args to exec and check the command in sudoers. */ static int check_execv(fd, pid, seqnr, askp, policyp, errorp) int fd; pid_t pid; u_int16_t seqnr; struct str_msg_ask *askp; int *policyp; int *errorp; { int rval, validated; struct childinfo *info; #ifdef HAVE_LDAP void *ld; #endif /* We're not really initialized until the first exec finishes. */ if (initialized == 0) { initialized = 1; *policyp = SYSTR_POLICY_PERMIT; return(0); } /* Failure should not be possible. */ if ((info = find_child(pid)) == NULL) { *policyp = SYSTR_POLICY_NEVER; *errorp = ECHILD; return(0); } /* Fill in user_cmnd, user_base, user_args and user_stat. */ if (decode_args(fd, pid, askp) != 0) { if (errno == EBUSY) return(-1); *policyp = SYSTR_POLICY_NEVER; *errorp = errno; return(0); } /* Get process cwd. */ rval = ioctl(fd, STRIOCGETCWD, &pid); if (rval == -1 || getcwd(user_cwd, sizeof(user_cwd)) == NULL) { if (rval == -1 && errno == EBUSY) return(-1); warningx("cannot get working directory"); (void) strlcpy(user_cwd, "unknown", sizeof(user_cwd)); } /* * Stat user_cmnd and restore cwd */ if (sudo_goodpath(user_cmnd, user_stat) == NULL) { if (rval != -1 && ioctl(fd, STRIOCRESCWD, 0) != 0) warning("can't restore cwd"); *policyp = SYSTR_POLICY_NEVER; *errorp = EACCES; return(0); } if (rval != -1 && ioctl(fd, STRIOCRESCWD, 0) != 0) warning("can't restore cwd"); /* Check sudoers and log the result. */ init_defaults(); def_authenticate = FALSE; runas_pw = info->pw; user_runas = &info->pw->pw_name; validated = VALIDATE_NOT_OK; #ifdef HAVE_LDAP if ((ld = sudo_ldap_open()) != NULL) { sudo_ldap_update_defaults(ld); validated = sudo_ldap_check(ld, 0); sudo_ldap_close(ld); } if (!def_ignore_local_sudoers && !ISSET(validated, VALIDATE_OK)) #endif { (void) update_defaults(SET_ALL); validated = sudoers_lookup(0); } if (ISSET(validated, VALIDATE_OK)) { *policyp = SYSTR_POLICY_PERMIT; } else { *policyp = SYSTR_POLICY_NEVER; *errorp = EACCES; } return(validated); } /* * Call check_execv() and, if the command it permitted, set * the SUDO_* environment variables. */ static int check_execve(fd, pid, seqnr, askp, policyp, errorp) int fd; u_int16_t seqnr; pid_t pid; struct str_msg_ask *askp; int *policyp; int *errorp; { int rval; rval = check_execv(fd, pid, seqnr, askp, policyp, errorp); #ifdef STRIOCINJECT if (rval > 0 && *policyp == SYSTR_POLICY_PERMIT) { /* read environment into buf, munge, and bung it back */ if (update_env(fd, pid, seqnr, askp) != 0) rval = -1; } #endif return(rval); } /* * Kill all pids in the list */ static void killall(sig) int sig; { struct childinfo *child; for (child = children.first; child != NULL; child = child->next) (void) kill(child->pid, sig); } /* * Detach all traced processes. */ static void detachall(fd) int fd; { struct childinfo *child; for (child = children.first; child != NULL; child = child->next) (void) ioctl(fd, STRIOCDETACH, &child->pid); }