o Made children global so check_exec() can lookup a child.
o Replaced uid in struct childinfo with struct passwd * (for runas) o new_child() now takes a parent pid so the runas info can be inherited o Added find_child() to lookup a child by its pid o update_child() now fills in a struct passwd o Converted the big if/else mess in set_policy to a switch o Syscalls that change uid are now "ask" so we get SYSTR_MSG_UGID events
This commit is contained in:
168
mon_systrace.c
168
mon_systrace.c
@@ -54,7 +54,7 @@ struct listhead {
|
|||||||
};
|
};
|
||||||
struct childinfo {
|
struct childinfo {
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
uid_t uid;
|
struct passwd *pw;
|
||||||
struct childinfo *next;
|
struct childinfo *next;
|
||||||
};
|
};
|
||||||
struct syscallhandler {
|
struct syscallhandler {
|
||||||
@@ -73,14 +73,19 @@ int systrace_open __P((void));
|
|||||||
int systrace_read __P((int, pid_t, void *, void *, size_t));
|
int systrace_read __P((int, pid_t, void *, void *, size_t));
|
||||||
int systrace_run __P((char *, char **, int));
|
int systrace_run __P((char *, char **, int));
|
||||||
ssize_t read_string __P((int, pid_t, void *, char *, size_t));
|
ssize_t read_string __P((int, pid_t, void *, char *, size_t));
|
||||||
void new_child __P((struct listhead *, pid_t, uid_t));
|
void new_child __P((pid_t, pid_t));
|
||||||
void new_handler __P((struct listhead *, int,
|
void new_handler __P((struct listhead *, int,
|
||||||
void (*)(int, struct str_msg_ask *,
|
void (*)(int, struct str_msg_ask *,
|
||||||
struct systrace_answer *)));
|
struct systrace_answer *)));
|
||||||
void rm_child __P((struct listhead *, pid_t));
|
void rm_child __P((pid_t));
|
||||||
void update_child __P((struct listhead *, pid_t, uid_t));
|
void update_child __P((pid_t, uid_t));
|
||||||
|
struct childinfo *find_child __P((pid_t));
|
||||||
|
|
||||||
static int initialized;
|
extern struct passwd *sudo_pwdup __P((const struct passwd *, int));
|
||||||
|
extern struct passwd *sudo_getpwuid __P((uid_t));
|
||||||
|
|
||||||
|
static struct listhead children; /* list of children being traced */
|
||||||
|
static int initialized; /* set to true when we are inited */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open the systrace device and return the fd or -1 on failure.
|
* Open the systrace device and return the fd or -1 on failure.
|
||||||
@@ -135,7 +140,7 @@ systrace_attach(pid)
|
|||||||
{
|
{
|
||||||
struct systrace_answer ans;
|
struct systrace_answer ans;
|
||||||
struct str_message msg;
|
struct str_message msg;
|
||||||
struct listhead children, handlers;
|
struct listhead handlers;
|
||||||
sigaction_t sa, osa;
|
sigaction_t sa, osa;
|
||||||
sigset_t set, oset;
|
sigset_t set, oset;
|
||||||
ssize_t nread;
|
ssize_t nread;
|
||||||
@@ -205,8 +210,7 @@ systrace_attach(pid)
|
|||||||
if (set_policy(fd, pid, &handlers) != 0)
|
if (set_policy(fd, pid, &handlers) != 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
children.first = NULL;
|
new_child(-1, pid);
|
||||||
new_child(&children, pid, runas_pw->pw_uid);
|
|
||||||
if (kill(pid, SIGUSR1) != 0) {
|
if (kill(pid, SIGUSR1) != 0) {
|
||||||
warn("unable to wake up sleeping child");
|
warn("unable to wake up sleeping child");
|
||||||
_exit(1);
|
_exit(1);
|
||||||
@@ -226,11 +230,9 @@ systrace_attach(pid)
|
|||||||
case SYSTR_MSG_CHILD:
|
case SYSTR_MSG_CHILD:
|
||||||
/* either a fork or an exit */
|
/* either a fork or an exit */
|
||||||
if (msg.msg_data.msg_child.new_pid != -1) {
|
if (msg.msg_data.msg_child.new_pid != -1) {
|
||||||
/* XXX - runas_pw->pw_uid may be wrong */
|
new_child(msg.msg_pid, msg.msg_data.msg_child.new_pid);
|
||||||
new_child(&children, msg.msg_data.msg_child.new_pid,
|
|
||||||
runas_pw->pw_uid);
|
|
||||||
} else {
|
} else {
|
||||||
rm_child(&children, msg.msg_pid);
|
rm_child(msg.msg_pid);
|
||||||
if (children.first == NULL)
|
if (children.first == NULL)
|
||||||
_exit(0);
|
_exit(0);
|
||||||
}
|
}
|
||||||
@@ -238,8 +240,10 @@ systrace_attach(pid)
|
|||||||
|
|
||||||
case SYSTR_MSG_UGID:
|
case SYSTR_MSG_UGID:
|
||||||
/* uid/gid change */
|
/* uid/gid change */
|
||||||
warn("new uid %d", msg.msg_data.msg_ugid.uid);
|
#ifdef DEBUG
|
||||||
update_child(&children, msg.msg_pid, msg.msg_data.msg_ugid.uid);
|
warnx("new uid %d for %d", msg.msg_data.msg_ugid.uid, msg.msg_pid);
|
||||||
|
#endif
|
||||||
|
update_child(msg.msg_pid, msg.msg_data.msg_ugid.uid);
|
||||||
memset(&ans, 0, sizeof(ans));
|
memset(&ans, 0, sizeof(ans));
|
||||||
ans.stra_pid = msg.msg_pid;
|
ans.stra_pid = msg.msg_pid;
|
||||||
ans.stra_seqnr = msg.msg_seqnr;
|
ans.stra_seqnr = msg.msg_seqnr;
|
||||||
@@ -271,7 +275,6 @@ systrace_attach(pid)
|
|||||||
if ((ioctl(fd, STRIOCANSWER, &ans)) == -1)
|
if ((ioctl(fd, STRIOCANSWER, &ans)) == -1)
|
||||||
goto fail;
|
goto fail;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SYSTR_MSG_POLICYFREE:
|
case SYSTR_MSG_POLICYFREE:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -314,39 +317,44 @@ new_handler(head, num, handler)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Push a new child to the head of the list.
|
* Push a new child to the head of the list, inheriting the struct pw
|
||||||
|
* of its parent. XXX - do ref counting on pw instead of copying.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
new_child(head, pid, uid)
|
new_child(ppid, pid)
|
||||||
struct listhead *head;
|
pid_t ppid;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
uid_t uid;
|
|
||||||
{
|
{
|
||||||
struct childinfo *entry;
|
struct childinfo *entry;
|
||||||
|
struct passwd *pw;
|
||||||
|
|
||||||
|
if (ppid != -1 && (entry = find_child(ppid)) != NULL)
|
||||||
|
pw = entry->pw;
|
||||||
|
else
|
||||||
|
pw = runas_pw;
|
||||||
entry = (struct childinfo *) emalloc(sizeof(*entry));
|
entry = (struct childinfo *) emalloc(sizeof(*entry));
|
||||||
entry->pid = pid;
|
entry->pid = pid;
|
||||||
entry->uid = uid;
|
entry->pw = sudo_pwdup(pw, 0);
|
||||||
entry->next = head->first;
|
entry->next = children.first;
|
||||||
head->first = entry;
|
children.first = entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Remove the named pid from the list.
|
* Remove the named pid from the list.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
rm_child(head, pid)
|
rm_child(pid)
|
||||||
struct listhead *head;
|
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
{
|
{
|
||||||
struct childinfo *cur, *prev;
|
struct childinfo *cur, *prev;
|
||||||
|
|
||||||
for (prev = NULL, cur = head->first; cur != NULL; cur = cur->next) {
|
for (prev = NULL, cur = children.first; cur != NULL; cur = cur->next) {
|
||||||
if (cur->pid == pid) {
|
if (cur->pid == pid) {
|
||||||
if (prev != NULL)
|
if (prev != NULL)
|
||||||
prev->next = cur->next;
|
prev->next = cur->next;
|
||||||
else
|
else
|
||||||
head->first = cur->next;
|
children.first = cur->next;
|
||||||
|
free(cur->pw);
|
||||||
free(cur);
|
free(cur);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -355,20 +363,44 @@ rm_child(head, pid)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Update the uid associated with a pid.
|
* Find a child by pid.
|
||||||
*/
|
*/
|
||||||
void
|
struct childinfo *
|
||||||
update_child(head, pid, uid)
|
find_child(pid)
|
||||||
struct listhead *head;
|
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
uid_t uid;
|
|
||||||
{
|
{
|
||||||
struct childinfo *cur;
|
struct childinfo *cur;
|
||||||
|
|
||||||
for (cur = head->first; cur != NULL; cur = cur->next) {
|
for (cur = children.first; cur != NULL; cur = cur->next) {
|
||||||
if (cur->pid == pid) {
|
if (cur->pid == pid)
|
||||||
cur->uid = uid;
|
return(cur);
|
||||||
break;
|
}
|
||||||
|
return(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the uid associated with a pid.
|
||||||
|
*/
|
||||||
|
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) {
|
||||||
|
free(child->pw);
|
||||||
|
/* lookup uid in passwd db, using a stub on failure */
|
||||||
|
if ((child->pw = sudo_getpwuid(uid)) == NULL) {
|
||||||
|
child->pw = emalloc(sizeof(struct passwd) + MAX_UID_T_LEN + 1);
|
||||||
|
memset(child->pw, 0, sizeof(struct passwd));
|
||||||
|
child->pw->pw_uid = uid;
|
||||||
|
child->pw->pw_name = (char *)child->pw + sizeof(struct passwd);
|
||||||
|
(void) snprintf(child->pw->pw_name, MAX_UID_T_LEN + 1, "%lu",
|
||||||
|
(unsigned long) uid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -391,6 +423,7 @@ set_policy(fd, pid, handlers)
|
|||||||
if (ioctl(fd, STRIOCPOLICY, &pol) == -1)
|
if (ioctl(fd, STRIOCPOLICY, &pol) == -1)
|
||||||
return(-1);
|
return(-1);
|
||||||
|
|
||||||
|
handlers->first = NULL;
|
||||||
for (i = 0; i < SYS_MAXSYSCALL; i++) {
|
for (i = 0; i < SYS_MAXSYSCALL; i++) {
|
||||||
pol.strp_op = SYSTR_POLICY_ASSIGN;
|
pol.strp_op = SYSTR_POLICY_ASSIGN;
|
||||||
pol.strp_pid = pid;
|
pol.strp_pid = pid;
|
||||||
@@ -398,31 +431,48 @@ set_policy(fd, pid, handlers)
|
|||||||
return(-1);
|
return(-1);
|
||||||
|
|
||||||
pol.strp_op = SYSTR_POLICY_MODIFY;
|
pol.strp_op = SYSTR_POLICY_MODIFY;
|
||||||
pol.strp_code = i;
|
switch (pol.strp_code = i) {
|
||||||
#ifdef SYS_exec
|
#ifdef SYS_exec
|
||||||
if (i == SYS_exec) {
|
case SYS_exec:
|
||||||
pol.strp_policy = SYSTR_POLICY_ASK;
|
pol.strp_policy = SYSTR_POLICY_ASK;
|
||||||
new_handler(handlers, i, check_exec);
|
new_handler(handlers, i, check_exec);
|
||||||
} else
|
break;
|
||||||
#endif
|
#endif
|
||||||
#ifdef SYS_execv
|
#ifdef SYS_execv
|
||||||
if (i == SYS_execv) {
|
case SYS_execv:
|
||||||
pol.strp_policy = SYSTR_POLICY_ASK;
|
pol.strp_policy = SYSTR_POLICY_ASK;
|
||||||
new_handler(handlers, i, check_exec);
|
new_handler(handlers, i, check_exec);
|
||||||
} else
|
break;
|
||||||
#endif
|
#endif
|
||||||
#ifdef SYS_execve
|
#ifdef SYS_execve
|
||||||
if (i == SYS_execve) {
|
case SYS_execve:
|
||||||
pol.strp_policy = SYSTR_POLICY_ASK;
|
pol.strp_policy = SYSTR_POLICY_ASK;
|
||||||
new_handler(handlers, i, check_exec);
|
new_handler(handlers, i, check_exec);
|
||||||
} else
|
break;
|
||||||
#endif
|
#endif
|
||||||
#ifdef SYS_fexecve
|
#ifdef SYS_fexecve
|
||||||
if (i == SYS_fexecve)
|
case SYS_fexecve:
|
||||||
pol.strp_policy = SYSTR_POLICY_NEVER; /* not checkable */
|
pol.strp_policy = SYSTR_POLICY_NEVER; /* not checkable */
|
||||||
else
|
break;
|
||||||
#endif
|
#endif
|
||||||
pol.strp_policy = SYSTR_POLICY_PERMIT;
|
#ifdef SYS_setuid
|
||||||
|
case SYS_setuid:
|
||||||
|
#endif
|
||||||
|
#ifdef SYS_seteuid
|
||||||
|
case SYS_seteuid:
|
||||||
|
#endif
|
||||||
|
#ifdef SYS_setreuid
|
||||||
|
case SYS_setreuid:
|
||||||
|
#endif
|
||||||
|
#ifdef SYS_setresuid
|
||||||
|
case SYS_setresuid:
|
||||||
|
#endif
|
||||||
|
pol.strp_policy = SYSTR_POLICY_ASK;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pol.strp_policy = SYSTR_POLICY_PERMIT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (ioctl(fd, STRIOCPOLICY, &pol) == -1)
|
if (ioctl(fd, STRIOCPOLICY, &pol) == -1)
|
||||||
return(-1);
|
return(-1);
|
||||||
}
|
}
|
||||||
@@ -517,7 +567,7 @@ decode_args(fd, pid, askp)
|
|||||||
pid_t pid;
|
pid_t pid;
|
||||||
struct str_msg_ask *askp;
|
struct str_msg_ask *askp;
|
||||||
{
|
{
|
||||||
size_t len;
|
ssize_t len;
|
||||||
char *off, *ap, *cp, *ep;
|
char *off, *ap, *cp, *ep;
|
||||||
static char pbuf[PATH_MAX], abuf[ARG_MAX];
|
static char pbuf[PATH_MAX], abuf[ARG_MAX];
|
||||||
|
|
||||||
@@ -570,6 +620,7 @@ check_exec(fd, askp, ansp)
|
|||||||
struct systrace_answer *ansp;
|
struct systrace_answer *ansp;
|
||||||
{
|
{
|
||||||
int validated;
|
int validated;
|
||||||
|
struct childinfo *info;
|
||||||
|
|
||||||
/* We're not really initialized until the first exec finishes. */
|
/* We're not really initialized until the first exec finishes. */
|
||||||
if (initialized == 0) {
|
if (initialized == 0) {
|
||||||
@@ -578,6 +629,13 @@ check_exec(fd, askp, ansp)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Failure should not be possible. */
|
||||||
|
if ((info = find_child(ansp->stra_pid)) == NULL) {
|
||||||
|
ansp->stra_policy = SYSTR_POLICY_NEVER;
|
||||||
|
ansp->stra_error = ECHILD;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Fill in user_cmnd, user_base, user_args and user_stat. */
|
/* Fill in user_cmnd, user_base, user_args and user_stat. */
|
||||||
decode_args(fd, ansp->stra_pid, askp);
|
decode_args(fd, ansp->stra_pid, askp);
|
||||||
if (user_cmnd[0] != '/' || !sudo_goodpath(user_cmnd, user_stat)) {
|
if (user_cmnd[0] != '/' || !sudo_goodpath(user_cmnd, user_stat)) {
|
||||||
@@ -594,11 +652,11 @@ check_exec(fd, askp, ansp)
|
|||||||
} else
|
} else
|
||||||
(void) ioctl(fd, STRIOCRESCWD, 0);
|
(void) ioctl(fd, STRIOCRESCWD, 0);
|
||||||
|
|
||||||
/* XXX - should update user_runas and _runas_pw too! */
|
|
||||||
|
|
||||||
/* Check sudoers and log the result. */
|
/* Check sudoers and log the result. */
|
||||||
init_defaults();
|
init_defaults();
|
||||||
def_authenticate = FALSE;
|
def_authenticate = FALSE;
|
||||||
|
runas_pw = info->pw;
|
||||||
|
user_runas = &info->pw->pw_name;
|
||||||
validated = sudoers_lookup(0);
|
validated = sudoers_lookup(0);
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
warnx("intercepted: %s %s in %s -> 0x%x", user_cmnd, user_args, user_cwd, validated);
|
warnx("intercepted: %s %s in %s -> 0x%x", user_cmnd, user_args, user_cwd, validated);
|
||||||
|
Reference in New Issue
Block a user