From: Todd C. Miller Date: Tue, 5 Oct 2004 17:46:22 +0000 (+0000) Subject: Add environment updating via STRIOCINJECT (if available). X-Git-Tag: SUDO_1_7_0~896 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=dc368b59fb176dc5bdf16562b74d444515ca3949;p=sudo Add environment updating via STRIOCINJECT (if available). --- diff --git a/mon_systrace.c b/mon_systrace.c index 5c9117419..61dc2fa5f 100644 --- a/mon_systrace.c +++ b/mon_systrace.c @@ -96,7 +96,8 @@ sigusr1(signo) * 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) @@ -229,11 +230,13 @@ 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; @@ -512,6 +515,227 @@ find_handler(pid, code) 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. @@ -559,7 +783,6 @@ decode_args(fd, pid, askp) cp += len; *cp++ = ' '; /* replace NUL with a space */ } - /* XXX - detect cp >= ep */ return(0); } @@ -567,9 +790,10 @@ decode_args(fd, pid, askp) * 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; @@ -631,6 +855,32 @@ check_exec(fd, pid, askp, cookie, policyp, errorp) 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 */ diff --git a/mon_systrace.h b/mon_systrace.h index 7bd5fda8d..f1063aa0b 100644 --- a/mon_systrace.h +++ b/mon_systrace.h @@ -17,7 +17,7 @@ #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; @@ -25,20 +25,27 @@ 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 */ @@ -65,7 +72,7 @@ struct syscallaction { 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 */ @@ -74,7 +81,7 @@ static struct syscallaction syscalls_openbsd[] = { 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} @@ -82,7 +89,7 @@ static struct syscallaction syscalls_bsdos[] = { 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 */ @@ -91,30 +98,29 @@ static struct syscallaction syscalls_freebsd[] = { 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 */ @@ -128,47 +134,47 @@ static struct syscallaction syscalls_linux[] = { 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}