* Fork a process that traces the command to be run and its descendents.
*
* TODO:
- * set SUDO_* env variables for sub-execs
+ * should the tracing process catch signals and detach?
+ * (right now "sudo reboot" fails due to tracing)
*/
void
systrace_attach(pid)
* once after. We only want to log attempts when our
* answer is accepted; otherwise we can get dupes.
*/
- cookie = handler(fd, msg.msg_pid, &msg.msg_data.msg_ask, -1,
- &ans.stra_policy, &ans.stra_error);
+ cookie = handler(fd, msg.msg_pid, msg.msg_seqnr,
+ &msg.msg_data.msg_ask, -1, &ans.stra_policy,
+ &ans.stra_error);
if (ioctl(fd, STRIOCANSWER, &ans) == 0)
- handler(fd, msg.msg_pid, &msg.msg_data.msg_ask,
- cookie, &ans.stra_policy, &ans.stra_error);
+ handler(fd, msg.msg_pid, msg.msg_seqnr,
+ &msg.msg_data.msg_ask, cookie,
+ &ans.stra_policy, &ans.stra_error);
} else
(void) ioctl(fd, STRIOCANSWER, &ans);
break;
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 int
+systrace_write(fd, pid, addr, buf, len)
+ int fd;
+ pid_t pid;
+ void *addr;
+ void *buf;
+ size_t len;
+{
+ struct systrace_io io;
+
+ 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;
+ return(ioctl(fd, STRIOCIO, &io));
+}
+
+/*
+ * 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, *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)) != 0) {
+ warn("STRIOCIO");
+ return(-1);
+ }
+ if ((*envp = ap) == NULL)
+ break;
+ 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] = 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) == 0)
+ replace[SUDO_USER] = NULL;
+ }
+ } else if (strncmp(buf, "SUDO_COMMAND=", 13) == 0) {
+ offsets[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) == 0)
+ replace[SUDO_COMMAND] = NULL;
+ }
+ } else if (strncmp(buf, "SUDO_UID=", 9) == 0) {
+ offsets[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) == 0)
+ replace[SUDO_UID] = NULL;
+ }
+ } else if (strncmp(buf, "SUDO_GID=", 9) == 0) {
+ offsets[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) == 0)
+ 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) {
+ warnx("STRIOCINJECT");
+ return(-1);
+ }
+ /*
+ * XXX - if no missing variables we don't really need a new envp
+ * can just systrace_write the new addr.
+ */
+ /*
+ * Update the addresses in our copy of envp, making them relative
+ * to inject.stri_addr.
+ */
+ for (envp = envbuf; *envp != NULL; envp++) {
+ if (replace[SUDO_USER] != NULL && *envp == offsets[SUDO_USER])
+ *envp = inject.stri_addr + (replace[SUDO_USER] - buf);
+ else if (replace[SUDO_COMMAND] != NULL && *envp == offsets[SUDO_COMMAND])
+ *envp = inject.stri_addr + (replace[SUDO_COMMAND] - buf);
+ else if (replace[SUDO_UID] != NULL && *envp == offsets[SUDO_UID])
+ *envp = inject.stri_addr + (replace[SUDO_UID] - buf);
+ else if (replace[SUDO_GID] != NULL && *envp == offsets[SUDO_GID])
+ *envp = inject.stri_addr + (replace[SUDO_GID] - buf);
+ }
+ /* Add any missing variables to our new envp. */
+ if (envp + (offsets[SUDO_USER] == NULL) +
+ (offsets[SUDO_COMMAND] == NULL) + (offsets[SUDO_UID] == NULL) +
+ (offsets[SUDO_GID] == NULL) >= envep)
+ return(-1);
+ if (offsets[SUDO_USER] == NULL)
+ *envp++ = replace[SUDO_USER];
+ if (offsets[SUDO_COMMAND] == NULL)
+ *envp++ = replace[SUDO_COMMAND];
+ if (offsets[SUDO_UID] == NULL)
+ *envp++ = replace[SUDO_UID];
+ if (offsets[SUDO_GID] == NULL)
+ *envp++ = replace[SUDO_GID];
+ *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) {
+ warnx("STRIOCREPLACE");
+ return(-1);
+ }
+ }
+ return(0);
+}
+#endif
+
/*
* Decode path and argv from systrace and fill in user_cmnd,
* user_base and user_args.
cp += len;
*cp++ = ' '; /* replace NUL with a space */
}
- /* XXX - detect cp >= ep */
return(0);
}
* Decode the args to exec and check the command in sudoers.
*/
static int
-check_exec(fd, pid, askp, cookie, policyp, errorp)
+check_execv(fd, pid, seqnr, askp, cookie, policyp, errorp)
int fd;
pid_t pid;
+ u_int16_t seqnr;
struct str_msg_ask *askp;
int cookie;
int *policyp;
return(validated);
}
+/*
+ * Call check_execv() and, if the command it permitted, set
+ * the SUDO_* environment variables.
+ */
+static int
+check_execve(fd, pid, seqnr, askp, cookie, policyp, errorp)
+ int fd;
+ u_int16_t seqnr;
+ pid_t pid;
+ struct str_msg_ask *askp;
+ int cookie;
+ int *policyp;
+ int *errorp;
+{
+ int rval;
+
+ rval = check_execv(fd, pid, seqnr, askp, cookie, policyp, errorp);
+#ifdef STRIOCINJECT
+ if (rval > 0 && *policyp == SYSTR_POLICY_PERMIT) {
+ /* read environment into buf, munge, and bung it back */
+ update_env(fd, pid, seqnr, askp);
+ }
+#endif
+ return(rval);
+}
+
/*
* Kill all pids in the list
*/
#define SYSTRACE_MAXENTS 1024
typedef int (*schandler_t)
- __P((int, pid_t, struct str_msg_ask *, int, int *, int *));
+ __P((int, pid_t, u_int16_t, struct str_msg_ask *, int, int *, int *));
struct childinfo;
struct listhead;
extern struct passwd *sudo_pwdup __P((const struct passwd *, int));
extern struct passwd *sudo_getpwuid __P((uid_t));
-static int check_exec __P((int, pid_t, struct str_msg_ask *, int,
- int *, int *));
-static schandler_t find_handler __P((pid_t, int));
+static int check_execv __P((int, pid_t, u_int16_t,
+ struct str_msg_ask *, int, int *, int *));
+static int check_execve __P((int, pid_t, u_int16_t,
+ struct str_msg_ask *, int, int *, int *));
static int decode_args __P((int, pid_t, struct str_msg_ask *));
static int set_policy __P((int, struct childinfo *));
+static int switch_emulation __P((int, struct str_message *));
static int systrace_open __P((void));
static int systrace_read __P((int, pid_t, void *, void *, size_t));
-static int switch_emulation __P((int, struct str_message *));
+#ifdef STRIOCINJECT
+static int systrace_write __P((int, pid_t, void *, void *, size_t));
+static int update_env __P((int, pid_t, u_int16_t, struct str_msg_ask *));
+#endif
+static schandler_t find_handler __P((pid_t, int));
static ssize_t read_string __P((int, pid_t, void *, char *, size_t));
+static struct childinfo *find_child __P((pid_t));
+static void killall __P((struct listhead *, int));
static void new_child __P((pid_t, pid_t));
static void rm_child __P((pid_t));
static void update_child __P((pid_t, uid_t));
-static struct childinfo *find_child __P((pid_t));
-static void killall __P((struct listhead *, int));
+
static struct listhead children; /* list of children being traced */
static int initialized; /* set to true when we are inited */
static struct syscallaction syscalls_openbsd[] = {
{ 23, SYSTR_POLICY_ASK, NULL}, /* OPENBSD_SYS_setuid */
- { 59, SYSTR_POLICY_ASK, check_exec}, /* OPENBSD_SYS_execve */
+ { 59, SYSTR_POLICY_ASK, check_execve}, /* OPENBSD_SYS_execve */
{ 126, SYSTR_POLICY_ASK, NULL}, /* OPENBSD_SYS_setreuid */
{ 183, SYSTR_POLICY_ASK, NULL}, /* OPENBSD_SYS_seteuid */
{ 282, SYSTR_POLICY_ASK, NULL}, /* OPENBSD_SYS_setresuid */
static struct syscallaction syscalls_bsdos[] = {
{ 23, SYSTR_POLICY_ASK, NULL}, /* BSDOS_SYS_setuid */
- { 59, SYSTR_POLICY_ASK, check_exec}, /* BSDOS_SYS_execve */
+ { 59, SYSTR_POLICY_ASK, check_execve}, /* BSDOS_SYS_execve */
{ 126, SYSTR_POLICY_ASK, NULL}, /* BSDOS_SYS_setreuid */
{ 183, SYSTR_POLICY_ASK, NULL}, /* BSDOS_SYS_seteuid */
{ -1, -1, NULL}
static struct syscallaction syscalls_freebsd[] = {
{ 23, SYSTR_POLICY_ASK, NULL}, /* FREEBSD_SYS_setuid */
- { 59, SYSTR_POLICY_ASK, check_exec}, /* FREEBSD_SYS_execve */
+ { 59, SYSTR_POLICY_ASK, check_execve}, /* FREEBSD_SYS_execve */
{ 126, SYSTR_POLICY_ASK, NULL}, /* FREEBSD_SYS_setreuid */
{ 183, SYSTR_POLICY_ASK, NULL}, /* FREEBSD_SYS_seteuid */
{ 311, SYSTR_POLICY_ASK, NULL}, /* FREEBSD_SYS_setresuid */
static struct syscallaction syscalls_netbsd[] = {
{ 23, SYSTR_POLICY_ASK, NULL}, /* NETBSD_SYS_setuid */
- { 59, SYSTR_POLICY_ASK, check_exec}, /* NETBSD_SYS_execve */
+ { 59, SYSTR_POLICY_ASK, check_execve}, /* NETBSD_SYS_execve */
{ 126, SYSTR_POLICY_ASK, NULL}, /* NETBSD_SYS_setreuid */
{ 183, SYSTR_POLICY_ASK, NULL}, /* NETBSD_SYS_seteuid */
{ -1, -1, NULL}
};
static struct syscallaction syscalls_hpux[] = {
- { 11, SYSTR_POLICY_ASK, check_exec}, /* HPUX_SYS_execv */
+ { 11, SYSTR_POLICY_ASK, check_execv}, /* HPUX_SYS_execv */
{ 23, SYSTR_POLICY_ASK, NULL}, /* HPUX_SYS_setuid */
- { 59, SYSTR_POLICY_ASK, check_exec}, /* HPUX_SYS_execve */
+ { 59, SYSTR_POLICY_ASK, check_execve}, /* HPUX_SYS_execve */
{ 126, SYSTR_POLICY_ASK, NULL}, /* HPUX_SYS_setresuid */
{ -1, -1, NULL}
};
static struct syscallaction syscalls_ibsc2[] = {
- { 11, SYSTR_POLICY_ASK, check_exec}, /* ISCS2_SYS_execv */
+ { 11, SYSTR_POLICY_ASK, check_execv}, /* ISCS2_SYS_execv */
{ 23, SYSTR_POLICY_ASK, NULL}, /* ISCS2_SYS_setuid */
- { 59, SYSTR_POLICY_ASK, check_exec}, /* ISCS2_SYS_execve */
+ { 59, SYSTR_POLICY_ASK, check_execve}, /* ISCS2_SYS_execve */
{ -1, -1, NULL}
};
-/* XXX - deny fexecve */
static struct syscallaction syscalls_linux[] = {
- { 11, SYSTR_POLICY_ASK, check_exec}, /* LINUX_SYS_execve */
+ { 11, SYSTR_POLICY_ASK, check_execve}, /* LINUX_SYS_execve */
{ 23, SYSTR_POLICY_ASK, NULL}, /* LINUX_SYS_setuid16 */
{ 70, SYSTR_POLICY_ASK, NULL}, /* LINUX_SYS_setreuid16 */
{ 138, SYSTR_POLICY_ASK, NULL}, /* LINUX_SYS_setfsuid16 */
static struct syscallaction syscalls_osf1[] = {
{ 23, SYSTR_POLICY_ASK, NULL}, /* OSF1_SYS_setuid */
- { 59, SYSTR_POLICY_ASK, check_exec}, /* OSF1_SYS_execve */
+ { 59, SYSTR_POLICY_ASK, check_execve}, /* OSF1_SYS_execve */
{ 126, SYSTR_POLICY_ASK, NULL}, /* OSF1_SYS_setreuid */
{ -1, -1, NULL}
};
static struct syscallaction syscalls_sunos[] = {
- { 11, SYSTR_POLICY_ASK, check_exec}, /* SUNOS_SYS_execv */
+ { 11, SYSTR_POLICY_ASK, check_execv}, /* SUNOS_SYS_execv */
{ 23, SYSTR_POLICY_ASK, NULL}, /* SUNOS_SYS_setuid */
- { 59, SYSTR_POLICY_ASK, check_exec}, /* SUNOS_SYS_execve */
+ { 59, SYSTR_POLICY_ASK, check_execve}, /* SUNOS_SYS_execve */
{ 126, SYSTR_POLICY_ASK, NULL}, /* SUNOS_SYS_setreuid */
{ -1, -1, NULL}
};
static struct syscallaction syscalls_svr4[] = {
- { 11, SYSTR_POLICY_ASK, check_exec}, /* SVR4_SYS_execv */
+ { 11, SYSTR_POLICY_ASK, check_execv}, /* SVR4_SYS_execv */
{ 23, SYSTR_POLICY_ASK, NULL}, /* SVR4_SYS_setuid */
- { 59, SYSTR_POLICY_ASK, check_exec}, /* SVR4_SYS_execve */
+ { 59, SYSTR_POLICY_ASK, check_execve}, /* SVR4_SYS_execve */
{ 141, SYSTR_POLICY_ASK, NULL}, /* SVR4_SYS_seteuid */
{ 202, SYSTR_POLICY_ASK, NULL}, /* SVR4_SYS_setreuid */
{ -1, -1, NULL}
};
static struct syscallaction syscalls_ultrix[] = {
- { 11, SYSTR_POLICY_ASK, check_exec}, /* ULTRIX_SYS_execv */
+ { 11, SYSTR_POLICY_ASK, check_execv}, /* ULTRIX_SYS_execv */
{ 23, SYSTR_POLICY_ASK, NULL}, /* ULTRIX_SYS_setuid */
- { 59, SYSTR_POLICY_ASK, check_exec}, /* ULTRIX_SYS_execve */
+ { 59, SYSTR_POLICY_ASK, check_execve}, /* ULTRIX_SYS_execve */
{ 126, SYSTR_POLICY_ASK, NULL}, /* ULTRIX_SYS_setreuid */
{ -1, -1, NULL}
};
static struct syscallaction syscalls_irix[] = {
- { 11, SYSTR_POLICY_ASK, check_exec}, /* IRIX_SYS_execv */
+ { 11, SYSTR_POLICY_ASK, check_execv}, /* IRIX_SYS_execv */
{ 23, SYSTR_POLICY_ASK, NULL}, /* IRIX_SYS_setuid */
- { 59, SYSTR_POLICY_ASK, check_exec}, /* IRIX_SYS_execve */
+ { 59, SYSTR_POLICY_ASK, check_execve}, /* IRIX_SYS_execve */
{ 124, SYSTR_POLICY_ASK, NULL}, /* IRIX_SYS_setreuid */
{ -1, -1, NULL}
};
static struct syscallaction syscalls_darwin[] = {
{ 23, SYSTR_POLICY_ASK, NULL}, /* DARWIN_SYS_setuid */
- { 59, SYSTR_POLICY_ASK, check_exec}, /* DARWIN_SYS_execve */
+ { 59, SYSTR_POLICY_ASK, check_execve}, /* DARWIN_SYS_execve */
{ 126, SYSTR_POLICY_ASK, NULL}, /* DARWIN_SYS_setreuid */
{ 183, SYSTR_POLICY_ASK, NULL}, /* DARWIN_SYS_seteuid */
{ -1, -1, NULL}