From 36207a330ccf1cae7cf91b22b4e4b123030ab8be Mon Sep 17 00:00:00 2001 From: Nikolay Sturm Date: Sun, 2 Jul 2006 12:34:16 +0000 Subject: sync with systrace 1.6d, keeping local changes tests and feedback by a few --- bin/systrace/Makefile | 4 +- bin/systrace/cradle.c | 12 +- bin/systrace/filter.c | 16 +- bin/systrace/intercept.c | 36 +++- bin/systrace/intercept.h | 14 +- bin/systrace/lex.l | 6 +- bin/systrace/linux-translate.c | 384 ++++++++++++++++++++++++++++++++++++++ bin/systrace/linux-translate.h | 43 +++++ bin/systrace/linux_socketcall.h | 196 +++++++++++++++++++ bin/systrace/openbsd-syscalls.c | 4 +- bin/systrace/policy.c | 205 +++++++++++++++++--- bin/systrace/register.c | 20 +- bin/systrace/systrace-translate.c | 59 ++++-- bin/systrace/systrace.1 | 14 +- bin/systrace/systrace.c | 234 +++++++++++++++-------- bin/systrace/systrace.h | 25 ++- 16 files changed, 1119 insertions(+), 153 deletions(-) create mode 100644 bin/systrace/linux-translate.c create mode 100644 bin/systrace/linux-translate.h create mode 100644 bin/systrace/linux_socketcall.h diff --git a/bin/systrace/Makefile b/bin/systrace/Makefile index 1450f01d385..fe8c1dc0a21 100644 --- a/bin/systrace/Makefile +++ b/bin/systrace/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.14 2006/06/30 19:03:43 otto Exp $ +# $OpenBSD: Makefile,v 1.15 2006/07/02 12:34:15 sturm Exp $ PROG= systrace CFLAGS+=-I. -I${.CURDIR} -I${.CURDIR}/../../sys @@ -9,7 +9,7 @@ DPADD+= ${LIBEVENT} LDADD+= -levent SRCS= cradle.c filter.c intercept-translate.c intercept.c \ - openbsd-syscalls.c util.c \ + linux-translate.c openbsd-syscalls.c util.c \ policy.c systrace-errno.h systrace-error.c \ systrace-translate.c systrace.c alias.c register.c \ parse.y lex.l diff --git a/bin/systrace/cradle.c b/bin/systrace/cradle.c index 821c6ade20a..336f77994cc 100644 --- a/bin/systrace/cradle.c +++ b/bin/systrace/cradle.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cradle.c,v 1.3 2006/04/26 20:19:25 sturm Exp $ */ +/* $OpenBSD: cradle.c,v 1.4 2006/07/02 12:34:15 sturm Exp $ */ /* * Copyright (c) 2003 Marius Aamodt Eriksen @@ -61,11 +61,11 @@ extern char dirpath[]; static struct event listen_ev; static struct event uilisten_ev; -static int cradle_server(char *path, char *uipath, char *guipath); -static void listen_cb(int, short, void *); -static void msg_cb(int, short, void *); -static void ui_cb(int, short, void *); -static void gensig_cb(int, short, void *); +static int cradle_server(char *, char *, char *); +static void listen_cb(int, short, void *); +static void msg_cb(int, short, void *); +static void ui_cb(int, short, void *); +static void gensig_cb(int, short, void *); static FILE *ui_fl = NULL; static struct event ui_ev, sigterm_ev, sigint_ev; diff --git a/bin/systrace/filter.c b/bin/systrace/filter.c index 6d9fcc1c277..576b848af2d 100644 --- a/bin/systrace/filter.c +++ b/bin/systrace/filter.c @@ -1,4 +1,4 @@ -/* $OpenBSD: filter.c,v 1.32 2006/05/02 19:49:05 sturm Exp $ */ +/* $OpenBSD: filter.c,v 1.33 2006/07/02 12:34:15 sturm Exp $ */ /* * Copyright 2002 Niels Provos * All rights reserved. @@ -397,6 +397,10 @@ filter_modifypolicy(int fd, int policynr, const char *emulation, { struct systrace_revalias *reverse = NULL; + /* + * Check if we are dealing with a system call that really + * is an alias for something else. + */ if (!noalias) reverse = systrace_find_reverse(emulation, name); if (reverse == NULL) { @@ -441,6 +445,12 @@ filter_quickpredicate(struct filter *filter) return (1); } +/* + * Processes the filters for a policy that have not been applied yet. + * Pre-filters get installed when reading a policy. This function + * installs a fast-path in the kernel. + */ + int filter_prepolicy(int fd, struct policy *policy) { @@ -549,10 +559,8 @@ filter_ask(int fd, struct intercept_tlq *tls, struct filterq *fls, while (1) { /* Special policy active that allows only yes or no */ - if (icpid->uflags & PROCESS_PROMPT) { - fprintf(stderr, "isprompt\n"); + if (icpid->uflags & PROCESS_PROMPT) isprompt = 1; - } filter = NULL; if (!allow) { diff --git a/bin/systrace/intercept.c b/bin/systrace/intercept.c index c17d6850320..0e8815942fd 100644 --- a/bin/systrace/intercept.c +++ b/bin/systrace/intercept.c @@ -1,4 +1,4 @@ -/* $OpenBSD: intercept.c,v 1.51 2006/04/26 20:19:25 sturm Exp $ */ +/* $OpenBSD: intercept.c,v 1.52 2006/07/02 12:34:15 sturm Exp $ */ /* * Copyright 2002 Niels Provos * All rights reserved. @@ -29,6 +29,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + #include #include #include @@ -241,7 +242,7 @@ intercept_register_pfreecb(void (*cb)(int, void *), void *arg) /* ARGSUSED */ static void sigusr1_handler(int signum) -{ +{ /* all we need to do is pretend to handle it */ got_sigusr1 = 1; } @@ -549,6 +550,9 @@ intercept_get_string(int fd, pid_t pid, void *addr) static char name[8192]; int off = 0, done = 0, stride; + if (addr == NULL) + return (NULL); + stride = 32; do { if (intercept.io(fd, pid, INTERCEPT_READ, (char *)addr + off, @@ -636,7 +640,6 @@ normalize_filename(int fd, pid_t pid, char *name, int userp) havecwd = 1; } - /* Need concatenated path for simplifypath */ if (havecwd && name[0] != '/') { if (strlcat(cwd, "/", sizeof(cwd)) >= sizeof(cwd)) return (NULL); @@ -658,8 +661,7 @@ normalize_filename(int fd, pid_t pid, char *name, int userp) if (userp == ICLINK_NOLAST) { /* Check if the last component has special meaning */ - if (strcmp(base, "..") == 0 || - strcmp(base, "/") == 0) + if (strcmp(base, "..") == 0 || strcmp(base, "/") == 0) userp = ICLINK_ALL; else goto nolast; @@ -867,7 +869,6 @@ intercept_newimage(int fd, pid_t pid, int policynr, icpid->name, intercept_newimagecbarg); } - int intercept_newpolicy(int fd) { @@ -884,6 +885,12 @@ intercept_assignpolicy(int fd, pid_t pid, int policynr) return (intercept.assignpolicy(fd, pid, policynr)); } +int +intercept_modifypolicy_nr(int fd, int policynr, int code, short policy) +{ + return (intercept.policy(fd, policynr, code, policy)); +} + int intercept_modifypolicy(int fd, int policynr, const char *emulation, const char *name, short policy) @@ -957,13 +964,28 @@ intercept_ugid(struct intercept_pid *icpid, uid_t uid, gid_t gid) icpid->gid = gid; } +/* + * Returns the number of a system call + */ + +int +intercept_getsyscallnumber(const char *emulation, const char *name) +{ + int nr = intercept.getsyscallnumber(emulation, name); + + if (nr >= INTERCEPT_MAXSYSCALLNR) + err(1, "%s: system call number too high: %d", __func__, nr); + + return (nr); +} + /* * Checks if the given emulation has a certain system call. * This is a very slow function. */ int -intercept_isvalidsystemcall(char *emulation, char *name) +intercept_isvalidsystemcall(const char *emulation, const char *name) { int res; diff --git a/bin/systrace/intercept.h b/bin/systrace/intercept.h index bf8f595eb48..d371dfb9e31 100644 --- a/bin/systrace/intercept.h +++ b/bin/systrace/intercept.h @@ -1,4 +1,4 @@ -/* $OpenBSD: intercept.h,v 1.23 2006/06/10 07:19:13 sturm Exp $ */ +/* $OpenBSD: intercept.h,v 1.24 2006/07/02 12:34:15 sturm Exp $ */ /* * Copyright 2002 Niels Provos * All rights reserved. @@ -101,7 +101,7 @@ struct intercept_pid { uid_t uid; /* current uid */ gid_t gid; /* current gid */ - char username[MAXLOGNAME]; + char username[LOGIN_NAME_MAX]; char home[MAXPATHLEN]; /* current home dir for uid */ void *data; @@ -110,6 +110,7 @@ struct intercept_pid { struct elevate *elevate; /* privilege elevation request */ }; +#define INTERCEPT_MAXSYSCALLNR 512 #define INTERCEPT_MAXSYSCALLARGS 10 struct intercept_translate { @@ -125,6 +126,7 @@ struct intercept_translate { size_t trans_size; char *trans_print; u_int trans_flags; + void *user; TAILQ_ENTRY(intercept_translate) next; }; @@ -148,6 +150,7 @@ int intercept_read(int); int intercept_newpolicy(int); int intercept_assignpolicy(int, pid_t, int); int intercept_modifypolicy(int, int, const char *, const char *, short); +int intercept_modifypolicy_nr(int, int, int, short); void intercept_child_info(pid_t, pid_t); void intercept_policy_free(int); @@ -196,11 +199,12 @@ void intercept_syscall(int, pid_t, u_int16_t, int, const char *, int, const char *, void *, int); void intercept_syscall_result(int, pid_t, u_int16_t, int, const char *, int, const char *, void *, int, int, void *); -void intercept_ugid(struct intercept_pid *, uid_t, gid_t); -void intercept_setpid(struct intercept_pid *, uid_t, gid_t); void intercept_newimage(int, pid_t, int, const char *, char *, struct intercept_pid *); +void intercept_ugid(struct intercept_pid *, uid_t, gid_t); +void intercept_setpid(struct intercept_pid *, uid_t, gid_t); -int intercept_isvalidsystemcall(char *, char *); +int intercept_getsyscallnumber(const char *, const char *); +int intercept_isvalidsystemcall(const char *, const char *); #endif /* _INTERCEPT_H_ */ diff --git a/bin/systrace/lex.l b/bin/systrace/lex.l index 247dfd187a1..3d5511d2dcf 100644 --- a/bin/systrace/lex.l +++ b/bin/systrace/lex.l @@ -1,4 +1,4 @@ -/* $OpenBSD: lex.l,v 1.17 2003/11/27 17:39:08 sturm Exp $ */ +/* $OpenBSD: lex.l,v 1.18 2006/07/02 12:34:15 sturm Exp $ */ /* * Copyright 2002 Niels Provos @@ -137,8 +137,8 @@ as { return AS; } yylval.string = strdup(quotestr); return CMDSTRING; } else { - strlcat(quotestr, "\"", sizeof(quotestr)); - quoteescape = 0; + strlcat(quotestr, "\"", sizeof(quotestr)); + quoteescape = 0; } } \[ { return LSQBRACE; } diff --git a/bin/systrace/linux-translate.c b/bin/systrace/linux-translate.c new file mode 100644 index 00000000000..c3d0af75cc9 --- /dev/null +++ b/bin/systrace/linux-translate.c @@ -0,0 +1,384 @@ +/* + * Copyright 2002 Marius Aamodt Eriksen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Niels Provos. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "linux_socketcall.h" + +#include "intercept.h" +#include "systrace.h" + +extern struct intercept_system intercept; + +/* XXX register_t */ +#define ARGSIZE(n) ((n) * sizeof(unsigned long)) +static unsigned char socketcall_argsize[18] = { + ARGSIZE(0), /* none */ + ARGSIZE(3), /* LINUX_SYS_socket */ + ARGSIZE(3), /* LINUX_SYS_bind */ + ARGSIZE(3), /* LINUX_SYS_connect */ + ARGSIZE(2), /* LINUX_SYS_listen */ + ARGSIZE(3), /* LINUX_SYS_accept */ + ARGSIZE(3), /* LINUX_SYS_getsockname */ + ARGSIZE(3), /* LINUX_SYS_getpeername */ + ARGSIZE(4), /* LINUX_SYS_socketpair */ + ARGSIZE(4), /* LINUX_SYS_send */ + ARGSIZE(4), /* LINUX_SYS_recv */ + ARGSIZE(6), /* LINUX_SYS_sendto */ + ARGSIZE(6), /* LINUX_SYS_recvfrom */ + ARGSIZE(2), /* LINUX_SYS_shutdown */ + ARGSIZE(5), /* LINUX_SYS_setsockopt */ + ARGSIZE(5), /* LINUX_SYS_getsockopt */ + ARGSIZE(3), /* LINUX_SYS_sendmsg */ + ARGSIZE(3) /* LINUX_SYS_recvmsg */ +}; + +/* ARGSUSED */ +static int +get_socketcall(struct intercept_translate *trans, int fd, pid_t pid, void *addr) +{ + int call = (intptr_t)addr; + + systrace_switch_alias("linux", "socketcall", "linux", + linux_socketcall_names[call]); + + /* We don't want to print the argument .. */ + trans->trans_valid = 0; + return (0); +} + +/* ARGSUSED */ +static int +print_socketcall(char *buf, size_t buflen, struct intercept_translate *tl) +{ + return (0); +} + +static int +get_socketcall_args(struct intercept_translate *trans, int fd, pid_t pid, + void *addr) +{ + int call = (intptr_t)trans->trans_addr2; + unsigned long argsize; + + if (call != (intptr_t)trans->user) { + trans->trans_valid = 0; + return (0); + } + + argsize = socketcall_argsize[call]; + + if ((trans->trans_data = malloc(argsize)) == NULL) + return (-1); + + if (intercept.io(fd, pid, INTERCEPT_READ, addr, + trans->trans_data, argsize) == -1) { + free(trans->trans_data); + return (-1); + } + + return (0); +} + +static int +print_socktype(char *buf, size_t buflen, struct intercept_translate *tl) +{ + char *what = NULL; + unsigned long *args = tl->trans_data; + int type = args[1]; + + switch (type) { + case SOCK_STREAM: + what = "SOCK_STREAM"; + break; + case SOCK_DGRAM: + what = "SOCK_DGRAM"; + break; + case SOCK_RAW: + what = "SOCK_RAW"; + break; + case SOCK_SEQPACKET: + what = "SOCK_SEQPACKET"; + break; + case SOCK_RDM: + what = "SOCK_RDM"; + break; + default: + snprintf(buf, buflen, "SOCK_UNKNOWN(%d)", type); + break; + } + + if (what != NULL) + strlcpy(buf, what, buflen); + + return (0); +} + +static int +print_sockdom(char *buf, size_t buflen, struct intercept_translate *tl) +{ + char *what = NULL; + unsigned long *args = tl->trans_data; + int domain = args[0]; + + switch (domain) { + case LINUX_AF_UNIX: + what = "AF_UNIX"; + break; + case LINUX_AF_INET: + what = "AF_INET"; + break; + case LINUX_AF_INET6: + what = "AF_INET6"; + break; + case LINUX_AF_IPX: + what = "AF_IPX"; + break; + default: + snprintf(buf, buflen, "AF_UNKNOWN(%d)", domain); + break; + } + + if (what != NULL) + strlcpy(buf, what, buflen); + + return (0); +} + +static int +get_sockaddr(struct intercept_translate *trans, int fd, pid_t pid, + void *addr) +{ + struct sockaddr_storage sa; + socklen_t len; + void *sockaddr_addr; + unsigned long *args; + int call = (intptr_t)trans->trans_addr2; + + if (get_socketcall_args(trans, fd, pid, addr) == -1) + return (-1); + + if (trans->trans_valid == 0) + return (0); + + args = trans->trans_data; + + len = call == LINUX_SYS_sendto ? args[5] : args[2]; + sockaddr_addr = (void *)(call == LINUX_SYS_sendto ? args[4] : args[1]); + + if (len == 0 || len > sizeof(struct sockaddr_storage)) + return (-1); + + if (intercept.io(fd, pid, INTERCEPT_READ, sockaddr_addr, + (void *)&sa, len) == -1) + return (-1); + + free(trans->trans_data); + trans->trans_data = malloc(len); + if (trans->trans_data == NULL) + return (-1); + trans->trans_size = len; + memcpy(trans->trans_data, &sa, len); + + return (0); +} + +#ifndef offsetof +#define offsetof(s, e) ((size_t)&((s *)0)->e) +#endif + +static int +print_sockaddr(char *buf, size_t buflen, struct intercept_translate *tl) +{ + char host[NI_MAXHOST]; + char serv[NI_MAXSERV]; + struct linux_sockaddr *linux_sa = tl->trans_data; + struct sockaddr sa; + socklen_t len = (socklen_t)tl->trans_size; + + /* XXX - Niels */ + tl->trans_size = 0; + + buf[0] = '\0'; + + switch (linux_sa->sa_family) { + case LINUX_AF_UNIX: + if (len <= offsetof(struct linux_sockaddr, sa_data)) + return (-1); + len -= offsetof(struct linux_sockaddr, sa_data); + if (buflen < len + 1) + len = buflen - 1; + memcpy(buf, linux_sa->sa_data, len); + buf[len] = '\0'; + return (0); + case LINUX_AF_INET: + case LINUX_AF_INET6: + break; + default: + snprintf(buf, buflen, "family(%d)", linux_sa->sa_family); + return (0); + } + + memcpy(&sa.sa_family, &linux_sa->sa_family, sizeof(sa.sa_family)); + memcpy(&sa.sa_data, &linux_sa->sa_data, sizeof(sa.sa_data)); +#ifdef HAVE_SOCKADDR_SA_LEN + sa.sa_len = len; +#endif /* HAVE_SOCKADDR_SA_LEN */ + if (getnameinfo(&sa, len, + host, sizeof(host), serv, sizeof(serv), + NI_NUMERICHOST | NI_NUMERICSERV)) { + warn("getnameinfo"); + return (-1); + } + + snprintf(buf, buflen, "inet-[%s]:%s", host, serv); + + return (0); +} + +static int +get_msghdr(struct intercept_translate *trans, int fd, pid_t pid, + void *addr) +{ + struct msghdr msg; + int len = sizeof(struct msghdr); + unsigned long *args; + + if (get_socketcall_args(trans, fd, pid, addr) == -1) + return (-1); + + if (trans->trans_valid == 0) + return (0); + + args = trans->trans_data; + if (intercept.io(fd, pid, INTERCEPT_READ, (void *)args[1], + (void *)&msg, len) == -1) + return (-1); + + if (msg.msg_name == NULL) { + trans->trans_data = NULL; + trans->trans_size = 0; + return (0); + } + + trans->trans_size = msg.msg_namelen; + trans->trans_data = malloc(len); + if (trans->trans_data == NULL) + return (-1); + if (intercept.io(fd, pid, INTERCEPT_READ, msg.msg_name, + (void *)trans->trans_data, trans->trans_size) == -1) + return (-1); + + return (0); +} + +static int +print_msghdr(char *buf, size_t buflen, struct intercept_translate *tl) +{ + int res = 0; + if (tl->trans_size == 0) { + snprintf(buf, buflen, ""); + } else { + res = print_sockaddr(buf, buflen, tl); + /* + * disable replacement of this argument because it's two levels + * deep and we cant replace that far. + */ + tl->trans_size = 0; + + /* TODO: make this less of a hack */ + } + + return (res); +} + +struct intercept_translate ic_linux_socket_sockdom = { + "sockdom", + get_socketcall_args, print_sockdom, + -1, + .user = (void *)LINUX_SYS_socket +}; + +struct intercept_translate ic_linux_socket_socktype = { + "socktype", + get_socketcall_args, print_socktype, + -1, + .user = (void *)LINUX_SYS_socket +}; + +struct intercept_translate ic_linux_connect_sockaddr = { + "sockaddr", + get_sockaddr, print_sockaddr, + -1, + .user = (void *)LINUX_SYS_connect +}; + +struct intercept_translate ic_linux_bind_sockaddr = { + "sockaddr", + get_sockaddr, print_sockaddr, + -1, + .user = (void *)LINUX_SYS_bind +}; + +struct intercept_translate ic_linux_sendto_sockaddr = { + "sockaddr", + get_sockaddr, print_sockaddr, + -1, + .user = (void *)LINUX_SYS_sendto +}; + +struct intercept_translate ic_linux_sendmsg_sockaddr = { + "sockaddr", + get_msghdr, print_msghdr, + -1, + .user = (void *)LINUX_SYS_sendmsg +}; + +struct intercept_translate ic_linux_socketcall_catchall = { + "call", + get_socketcall, print_socketcall, +}; diff --git a/bin/systrace/linux-translate.h b/bin/systrace/linux-translate.h new file mode 100644 index 00000000000..a65664b850e --- /dev/null +++ b/bin/systrace/linux-translate.h @@ -0,0 +1,43 @@ +/* + * Copyright 2002 Marius Aamodt Eriksen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Niels Provos. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef LINUX_TRANSLATE_H +#define LINUX_TRANSLATE_H + +extern struct intercept_translate ic_linux_socket_sockdom; +extern struct intercept_translate ic_linux_socket_socktype; +extern struct intercept_translate ic_linux_connect_sockaddr; +extern struct intercept_translate ic_linux_sendto_sockaddr; +extern struct intercept_translate ic_linux_sendmsg_sockaddr; +extern struct intercept_translate ic_linux_socketcall_catchall; +extern struct intercept_translate ic_linux_bind_sockaddr; + +#endif /* LINUX_TRANSLATE_H */ diff --git a/bin/systrace/linux_socketcall.h b/bin/systrace/linux_socketcall.h new file mode 100644 index 00000000000..be5337ee084 --- /dev/null +++ b/bin/systrace/linux_socketcall.h @@ -0,0 +1,196 @@ +/* $OpenBSD: linux_socketcall.h,v 1.1 2006/07/02 12:34:15 sturm Exp $ */ +/* $NetBSD: linux_socketcall.h,v 1.1 1995/02/28 23:26:05 fvdl Exp $ */ + +/* + * Copyright (c) 1995 Frank van der Linden + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project + * by Frank van der Linden + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _LINUX_SOCKETCALL_H +#define _LINUX_SOCKETCALL_H + +/* + * Values passed to the Linux socketcall() syscall, determining the actual + * action to take. + */ +char *linux_socketcall_names[] = { + NULL, +#define LINUX_SYS_socket 1 + "socket", +#define LINUX_SYS_bind 2 + "bind", +#define LINUX_SYS_connect 3 + "connect", +#define LINUX_SYS_listen 4 + "listen", +#define LINUX_SYS_accept 5 + "accept", +#define LINUX_SYS_getsockname 6 + "getsockname", +#define LINUX_SYS_getpeername 7 + "getpeername", +#define LINUX_SYS_socketpair 8 + "socketpair", +#define LINUX_SYS_send 9 + "send", +#define LINUX_SYS_recv 10 + "recv", +#define LINUX_SYS_sendto 11 + "sendto", +#define LINUX_SYS_recvfrom 12 + "recvfrom", +#define LINUX_SYS_shutdown 13 + "shutdown", +#define LINUX_SYS_setsockopt 14 + "setsockopt", +#define LINUX_SYS_getsockopt 15 + "getsockopt", +#define LINUX_SYS_sendmsg 16 + "sendmsg", +#define LINUX_SYS_recvmsg 17 + "recvmsg" }; + +/* + * Structures for the arguments of the different system calls. This looks + * a little better than copyin() of all values one by one. + */ +struct linux_socket_args { + int domain; + int type; + int protocol; +}; + +struct linux_bind_args { + int s; + struct sockaddr *name; + int namelen; +}; + +struct linux_connect_args { + int s; + struct sockaddr *name; + int namelen; +}; + +struct linux_listen_args { + int s; + int backlog; +}; + +struct linux_accept_args { + int s; + struct sockaddr *addr; + int *namelen; +}; + +struct linux_getsockname_args { + int s; + struct sockaddr *addr; + int *namelen; +}; + +struct linux_getpeername_args { + int s; + struct sockaddr *addr; + int *namelen; +}; + +struct linux_socketpair_args { + int domain; + int type; + int protocol; + int *rsv; +}; + +struct linux_send_args { + int s; + void *msg; + int len; + int flags; +}; + +struct linux_recv_args { + int s; + void *msg; + int len; + int flags; +}; + +struct linux_sendto_args { + int s; + void *msg; + int len; + int flags; + struct sockaddr *to; + int tolen; +}; + +struct linux_recvfrom_args { + int s; + void *buf; + int len; + int flags; + struct sockaddr *from; + int *fromlen; +}; + +struct linux_shutdown_args { + int s; + int how; +}; + +struct linux_getsockopt_args { + int s; + int level; + int optname; + void *optval; + int *optlen; +}; + +struct linux_setsockopt_args { + int s; + int level; + int optname; + void *optval; + int optlen; +}; + +struct linux_sendmsg_args { + int s; + struct msghdr *msg; + int flags; +}; + +struct linux_recvmsg_args { + int s; + struct msghdr *msg; + int flags; +}; + +#endif /* _LINUX_SOCKETCALL_H */ diff --git a/bin/systrace/openbsd-syscalls.c b/bin/systrace/openbsd-syscalls.c index d2ed8bbede7..53eabec7e22 100644 --- a/bin/systrace/openbsd-syscalls.c +++ b/bin/systrace/openbsd-syscalls.c @@ -1,4 +1,4 @@ -/* $OpenBSD: openbsd-syscalls.c,v 1.30 2006/05/02 19:49:05 sturm Exp $ */ +/* $OpenBSD: openbsd-syscalls.c,v 1.31 2006/07/02 12:34:15 sturm Exp $ */ /* * Copyright 2002 Niels Provos * All rights reserved. @@ -458,6 +458,8 @@ obsd_replace(int fd, pid_t pid, u_int16_t seqnr, size_t len, off; int i, ret; + memset(&replace, 0, sizeof(replace)); + for (i = 0, len = 0; i < repl->num; i++) { len += repl->len[i]; } diff --git a/bin/systrace/policy.c b/bin/systrace/policy.c index eb8339f0ac4..d00da3550aa 100644 --- a/bin/systrace/policy.c +++ b/bin/systrace/policy.c @@ -1,4 +1,4 @@ -/* $OpenBSD: policy.c,v 1.30 2006/03/18 19:03:23 robert Exp $ */ +/* $OpenBSD: policy.c,v 1.31 2006/07/02 12:34:15 sturm Exp $ */ /* * Copyright 2002 Niels Provos * All rights reserved. @@ -43,6 +43,7 @@ #include #include #include +#include #include "intercept.h" #include "systrace.h" @@ -144,7 +145,7 @@ systrace_initpolicy(char *file, char *path) } if (file != NULL) - return (systrace_readpolicy(file)); + return (systrace_readpolicy(file) != NULL ? 0 : -1); return (0); } @@ -159,6 +160,34 @@ systrace_findpolicy(const char *name) return (SPLAY_FIND(policytree, &policyroot, &tmp)); } +struct policy * +systrace_findpolicy_wildcard(const char *name) +{ + struct policy tmp, *res; + static char path[MAXPATHLEN], lookup[MAXPATHLEN]; + + if (strlcpy(path, name, sizeof(path)) >= sizeof(path)) + errx(1, "%s: path name overflow", __func__); + + strlcpy(lookup, "*/", sizeof(lookup)); + strlcat(lookup, basename(path), sizeof(lookup)); + + tmp.name = lookup; + res = SPLAY_FIND(policytree, &policyroot, &tmp); + if (res == NULL) + return (NULL); + + /* we found the wildcarded policy; now remove it and bind it */ + SPLAY_REMOVE(policytree, &policyroot, res); + + free((char *)res->name); + if ((res->name = strdup(name)) == NULL) + err(1, "%s: strdup", __func__); + + SPLAY_INSERT(policytree, &policyroot, res); + return (res); +} + struct policy * systrace_findpolnr(int nr) { @@ -188,11 +217,15 @@ systrace_newpolicynr(int fd, struct policy *tmp) struct policy * systrace_newpolicy(const char *emulation, const char *name) { + int i; struct policy *tmp; if ((tmp = systrace_findpolicy(name)) != NULL) return (tmp); + if ((tmp = systrace_findpolicy_wildcard(name)) != NULL) + return (tmp); + tmp = calloc(1, sizeof(struct policy)); if (tmp == NULL) return (NULL); @@ -209,20 +242,23 @@ systrace_newpolicy(const char *emulation, const char *name) TAILQ_INIT(&tmp->filters); TAILQ_INIT(&tmp->prefilters); + /* Set the default policy to ask */ + for (i = 0; i < INTERCEPT_MAXSYSCALLNR; i++) + tmp->kerneltable[i] = ICPOLICY_ASK; + return (tmp); } void -systrace_freepolicy(struct policy *policy) +systrace_cleanpolicy(struct policy *policy) { struct filter *filter; struct policy_syscall *pflq; + int i; - if (policy->flags & POLICY_CHANGED) { - if (systrace_writepolicy(policy) == -1) - fprintf(stderr, "Failed to write policy for %s\n", - policy->name); - } + /* Set the default policy to ask */ + for (i = 0; i < INTERCEPT_MAXSYSCALLNR; i++) + policy->kerneltable[i] = ICPOLICY_ASK; while ((filter = TAILQ_FIRST(&policy->prefilters)) != NULL) { TAILQ_REMOVE(&policy->prefilters, filter, policy_next); @@ -244,6 +280,18 @@ systrace_freepolicy(struct policy *policy) free(pflq); } +} + +void +systrace_freepolicy(struct policy *policy) +{ + if (policy->flags & POLICY_CHANGED) { + if (systrace_writepolicy(policy) == -1) + fprintf(stderr, "Failed to write policy for %s\n", + policy->name); + } + + systrace_cleanpolicy(policy); SPLAY_REMOVE(policytree, &policyroot, policy); if (policy->policynr != -1) @@ -282,7 +330,7 @@ int systrace_modifypolicy(int fd, int policynr, const char *name, short action) { struct policy *policy; - int res; + int res, nr; if ((policy = systrace_findpolnr(policynr)) == NULL) return (-1); @@ -290,6 +338,11 @@ systrace_modifypolicy(int fd, int policynr, const char *name, short action) res = intercept_modifypolicy(fd, policynr, policy->emulation, name, action); + /* Remember the kernel policy */ + if (res != -1 && + (nr = intercept_getsyscallnumber(policy->emulation, name)) != -1) + policy->kerneltable[nr] = action; + return (res); } @@ -323,8 +376,13 @@ systrace_policyfilename(char *dirname, const char *name) return (file); } -int -systrace_addpolicy(const char *name) +/* + * Converts a executeable name into the corresponding filename + * that contains the security policy. + */ + +char * +systrace_getpolicyname(const char *name) { char *file = NULL; @@ -336,13 +394,21 @@ systrace_addpolicy(const char *name) } /* Read global policy */ - if (file == NULL) { + if (file == NULL) file = systrace_policyfilename(POLICY_PATH, name); - if (file == NULL) - return (-1); - } - return (systrace_readpolicy(file)); + return (file); +} + +int +systrace_addpolicy(const char *name) +{ + char *file; + + if ((file = systrace_getpolicyname(name)) == NULL) + return (-1); + + return (systrace_readpolicy(file) != NULL ? 0 : -1); } /* @@ -411,6 +477,8 @@ systrace_templatedir(void) error: errx(1, "%s: template name too long", __func__); + + /* NOTREACHED */ } struct template * @@ -492,7 +560,9 @@ systrace_readtemplate(char *filename, struct policy *policy, goto out; } -/* Removes trailing whitespace and comments from the input line */ +/* + * Removes trailing whitespace and comments from the input line + */ static char * systrace_policyline(char *line) @@ -579,7 +649,8 @@ systrace_policyprocess(struct policy *policy, char *p) } else if (filter_parse_simple(rule, &action, &future) == 0) resolved = 1; - /* For now, everything that does not seem to be a valid syscall + /* + * For now, everything that does not seem to be a valid syscall * does not get fast kernel policies even though the aliasing * system supports it. */ @@ -613,8 +684,14 @@ systrace_policyprocess(struct policy *policy, char *p) return (0); } -int -systrace_readpolicy(char *filename) +/* + * Reads security policy from specified file. + * If policy exists already, this function appends new statements from the + * file to the existing policy. + */ + +struct policy * +systrace_readpolicy(const char *filename) { FILE *fp; struct policy *policy; @@ -624,7 +701,7 @@ systrace_readpolicy(char *filename) int res = -1; if ((fp = fopen(filename, "r")) == NULL) - return (-1); + return (NULL); policy = NULL; while (fgets(line, sizeof(line), fp)) { @@ -640,6 +717,8 @@ systrace_readpolicy(char *filename) continue; if (!strncasecmp(p, "Policy: ", 8)) { + struct timeval now; + p += 8; name = strsep(&p, ","); if (p == NULL) @@ -652,6 +731,10 @@ systrace_readpolicy(char *filename) policy = systrace_newpolicy(emulation, name); if (policy == NULL) goto error; + + /* Update access time */ + gettimeofday(&now, NULL); + TIMEVAL_TO_TIMESPEC(&now, &policy->ts_last); continue; } @@ -671,13 +754,65 @@ systrace_readpolicy(char *filename) out: fclose(fp); - return (res); + return (res == -1 ? NULL : policy); error: fprintf(stderr, "%s:%d: syntax error.\n", filename, linenumber); goto out; } +/* + * Appends new policy statements if the policy has been updated by + * another process. Assumes that policies are append-only. + * + * Returns: + * -1 if the policy could not be updated. + * 0 if the policy has been updated. + */ + +int +systrace_updatepolicy(int fd, struct policy *policy) +{ + struct stat sb; + struct timespec mtimespec; + int i, policynr = policy->policynr; + char *file; + + if ((file = systrace_getpolicyname(policy->name)) == NULL) + return (-1); + + if (stat(file, &sb) == -1) + return (-1); + + mtimespec = sb.st_mtimespec; + + /* Policy does not need updating */ + if (timespeccmp(&mtimespec, &policy->ts_last, <=)) + return (-1); + + /* Reset the existing policy */ + for (i = 0; i < INTERCEPT_MAXSYSCALLNR; i++) { + if (policy->kerneltable[i] == ICPOLICY_ASK) + continue; + if (intercept_modifypolicy_nr(fd, policynr, i, + ICPOLICY_ASK) == -1) + errx(1, "%s: failed to modify policy for %d", + __func__, i); + } + + /* Now clean up all filter structures in this policy */ + systrace_cleanpolicy(policy); + + /* XXX - This does not deal with Detached and Automatic */ + if (systrace_readpolicy(file) == NULL) + return (-1); + + /* Resets the changed flag */ + filter_prepolicy(fd, policy); + + return (0); +} + int systrace_writepolicy(struct policy *policy) { @@ -687,6 +822,7 @@ systrace_writepolicy(struct policy *policy) char tmpname[2*MAXPATHLEN]; char finalname[2*MAXPATHLEN]; struct filter *filter; + struct timeval now; if ((p = systrace_policyfilename(policydir, policy->name)) == NULL) return (-1); @@ -726,11 +862,34 @@ systrace_writepolicy(struct policy *policy) return (-1); } + /* Update access time */ + gettimeofday(&now, NULL); + TIMEVAL_TO_TIMESPEC(&now, &policy->ts_last); + return (0); } int -systrace_dumppolicy(void) +systrace_updatepolicies(int fd) +{ + struct policy *policy; + + SPLAY_FOREACH(policy, policytree, &policyroot) { + /* Check if the policy has been updated */ + systrace_updatepolicy(fd, policy); + } + + return (0); +} + +/* + * Write policy to disk if it has been changed. We need to + * call systrace_updatepolicies() before this, so that we + * don't clobber changes. + */ + +int +systrace_dumppolicies(int fd) { struct policy *policy; diff --git a/bin/systrace/register.c b/bin/systrace/register.c index 9e4c14c11ab..11b3838e9f0 100644 --- a/bin/systrace/register.c +++ b/bin/systrace/register.c @@ -1,4 +1,4 @@ -/* $OpenBSD: register.c,v 1.20 2006/06/10 07:19:13 sturm Exp $ */ +/* $OpenBSD: register.c,v 1.21 2006/07/02 12:34:15 sturm Exp $ */ /* * Copyright 2002 Niels Provos * All rights reserved. @@ -39,6 +39,7 @@ #include "intercept.h" #include "systrace.h" +#include "linux-translate.h" #define X(x) if ((x) == -1) \ err(1, "%s:%d: intercept failed", __func__, __LINE__) @@ -236,6 +237,23 @@ systrace_initcb(void) intercept_register_translink("linux", "chmod", 0); intercept_register_translation("linux", "chmod", 1, &ic_modeflags); + X(intercept_register_sccb("linux", "socketcall", trans_cb, NULL)); + alias = systrace_new_alias("linux", "socketcall", "linux", "_socketcall"); + tl = intercept_register_translation("linux", "socketcall", 1, &ic_linux_socket_sockdom); + systrace_alias_add_trans(alias, tl); + tl = intercept_register_translation("linux", "socketcall", 1, &ic_linux_socket_socktype); + systrace_alias_add_trans(alias, tl); + tl = intercept_register_translation("linux", "socketcall", 1, &ic_linux_connect_sockaddr); + systrace_alias_add_trans(alias, tl); + tl = intercept_register_translation("linux", "socketcall", 1, &ic_linux_bind_sockaddr); + systrace_alias_add_trans(alias, tl); + tl = intercept_register_translation("linux", "socketcall", 0, &ic_linux_socketcall_catchall); + systrace_alias_add_trans(alias, tl); + + X(intercept_register_sccb("linux", "kill", trans_cb, NULL)); + intercept_register_translation("linux", "kill", 0, &ic_pidname); + intercept_register_translation("linux", "kill", 1, &ic_signame); + X(intercept_register_execcb(execres_cb, NULL)); X(intercept_register_pfreecb(policyfree_cb, NULL)); } diff --git a/bin/systrace/systrace-translate.c b/bin/systrace/systrace-translate.c index 22d2d7a26d8..e965ec49adf 100644 --- a/bin/systrace/systrace-translate.c +++ b/bin/systrace/systrace-translate.c @@ -1,4 +1,4 @@ -/* $OpenBSD: systrace-translate.c,v 1.20 2006/05/02 19:49:05 sturm Exp $ */ +/* $OpenBSD: systrace-translate.c,v 1.21 2006/07/02 12:34:15 sturm Exp $ */ /* * Copyright 2002 Niels Provos * All rights reserved. @@ -193,15 +193,15 @@ print_sockdom(char *buf, size_t buflen, struct intercept_translate *tl) case AF_INET6: what = "AF_INET6"; break; + case AF_IPX: + what = "AF_IPX"; + break; case AF_ISO: what = "AF_ISO"; break; case AF_NS: what = "AF_NS"; break; - case AF_IPX: - what = "AF_IPX"; - break; case AF_IMPLINK: what = "AF_IMPLINK"; break; @@ -267,14 +267,20 @@ print_pidname(char *buf, size_t buflen, struct intercept_translate *tl) struct intercept_pid *icpid; pid_t pid = (intptr_t)tl->trans_addr; - if (pid != 0) { - icpid = intercept_getpid(pid); - strlcpy(buf, icpid->name != NULL ? icpid->name : "", - buflen); - if (icpid->name == NULL) - intercept_freepid(pid); - } else + if (pid > 0) { + icpid = intercept_findpid(pid); + strlcpy(buf, icpid != NULL ? icpid->name : "", buflen); + } else if (pid == 0) { strlcpy(buf, "", buflen); + } else if (pid == -1) { + strlcpy(buf, "", buflen); + } else { + /* pid is negative but not -1 - trying to signal pgroup */ + pid = -pid; + icpid = intercept_findpid(pid); + strlcpy(buf, "pg:", buflen); + strlcat(buf, icpid != NULL ? icpid->name : "unknown", buflen); + } return (0); } @@ -421,6 +427,32 @@ print_fcntlcmd(char *buf, size_t buflen, struct intercept_translate *tl) return (0); } +struct linux_i386_mmap_arg_struct { + unsigned long addr; + unsigned long len; + unsigned long prot; + unsigned long flags; + unsigned long fd; + unsigned long offset; +}; + +static int +get_linux_memprot(struct intercept_translate *trans, int fd, pid_t pid, + void *addr) +{ + struct linux_i386_mmap_arg_struct arg; + size_t len = sizeof(arg); + extern struct intercept_system intercept; + + if (intercept.io(fd, pid, INTERCEPT_READ, addr, + (void *)&arg, len) == -1) + return (-1); + + trans->trans_addr = (void *)arg.prot; + + return (0); +} + static int print_memprot(char *buf, size_t buflen, struct intercept_translate *tl) { @@ -653,6 +685,11 @@ struct intercept_translate ic_memprot = { NULL, print_memprot, }; +struct intercept_translate ic_linux_memprot = { + "prot", + get_linux_memprot, print_memprot, +}; + struct intercept_translate ic_fileflags = { "flags", NULL, print_fileflags, diff --git a/bin/systrace/systrace.1 b/bin/systrace/systrace.1 index 20830691e74..c2b7fe530c8 100644 --- a/bin/systrace/systrace.1 +++ b/bin/systrace/systrace.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: systrace.1,v 1.42 2006/05/03 05:16:26 sturm Exp $ +.\" $OpenBSD: systrace.1,v 1.43 2006/07/02 12:34:15 sturm Exp $ .\" .\" Copyright 2002 Niels Provos .\" All rights reserved. @@ -39,9 +39,10 @@ .Sh SYNOPSIS .Nm systrace .Bk -words -.Op Fl AaCeitUu +.Op Fl AaCeitUuV .Op Fl c Ar user:group .Op Fl d Ar policydir +.Op Fl E Ar logfile .Op Fl f Ar file .Op Fl g Ar gui .Op Fl p Ar pid @@ -120,6 +121,9 @@ root privilege. .It Fl d Ar policydir Specifies an alternative location for the user's directory from which policies are loaded and to which changed policies are stored. +.It Fl E Ar logfile +Logs all policy violations or specifically logged system calls to +.Ar logfile . .It Fl e Specifies to log to .Em stderr @@ -131,6 +135,9 @@ The policies specified in are added to the policies that .Nm knows about. +The dirname in the policy may contain an "*" to match any possible pathname. +The wildcard is removed from the policy database the first time that +a filename matches. .It Fl g Ar gui Specifies an alternative location for the notification user interface. .It Fl i @@ -158,6 +165,9 @@ and .Fn access are translated to .Fn fsread . +.It Fl V +Prints the version number of +.Nm . .El .Ss POLICY The policy is specified via the following grammar: diff --git a/bin/systrace/systrace.c b/bin/systrace/systrace.c index 442cb900eb2..37a52712f5c 100644 --- a/bin/systrace/systrace.c +++ b/bin/systrace/systrace.c @@ -1,4 +1,4 @@ -/* $OpenBSD: systrace.c,v 1.53 2006/05/02 19:49:05 sturm Exp $ */ +/* $OpenBSD: systrace.c,v 1.54 2006/07/02 12:34:15 sturm Exp $ */ /* * Copyright 2002 Niels Provos * All rights reserved. @@ -48,6 +48,7 @@ #include #include #include +#include #include "intercept.h" #include "systrace.h" @@ -56,6 +57,8 @@ #define CRADLE_SERVER "cradle_server" #define CRADLE_UI "cradle_ui" +#define VERSION "1.6d (OpenBSD)" + pid_t trpid; int trfd; int connected = 0; /* Connected to GUI */ @@ -66,15 +69,21 @@ int userpolicy = 1; /* Permit user defined policies */ int noalias = 0; /* Do not do system call aliasing */ int iamroot = 0; /* Set if we are running as root */ int cradle = 0; /* Set if we are running in cradle mode */ -int logstderr = 0; /* Log to STDERR instead of syslog */ +int logtofile = 0; /* Log to file instead of syslog */ +FILE *logfile; /* default logfile to send to if enabled */ char cwd[MAXPATHLEN]; /* Current working directory */ char home[MAXPATHLEN]; /* Home directory of user */ -char username[MAXLOGNAME]; /* Username: predicate match and expansion */ +char username[LOGIN_NAME_MAX]; /* Username: predicate match and expansion */ char *guipath = _PATH_XSYSTRACE; /* Path to GUI executable */ char dirpath[MAXPATHLEN]; +static struct event ev_read; +static struct event ev_timeout; + static void child_handler(int); static void log_msg(int, const char *, ...); +static void systrace_read(int, short, void *); +static void systrace_timeout(int, short, void *); static void usage(void); void @@ -164,7 +173,7 @@ trans_cb(int fd, pid_t pid, int policynr, const char *binname = NULL; char output[_POSIX2_LINE_MAX]; pid_t ppid; - int dolog = 0; + int done = 0, dolog = 0; action = ICPOLICY_PERMIT; @@ -181,48 +190,63 @@ trans_cb(int fd, pid_t pid, int policynr, ppid = ipid->ppid; /* Required to set up replacements */ - make_output(output, sizeof(output), binname, pid, ppid, policynr, - policy->name, policy->nfilters, emulation, name, code, - tls, repl); - - if ((pflq = systrace_policyflq(policy, emulation, name)) == NULL) - errx(1, "%s:%d: no filter queue", __func__, __LINE__); + do { + make_output(output, sizeof(output), binname, pid, ppid, + policynr, policy->name, policy->nfilters, + emulation, name, code, tls, repl); - action = filter_evaluate(tls, pflq, ipid); - if (action != ICPOLICY_ASK) - goto done; - - /* Do aliasing here */ - if (!noalias) - alias = systrace_find_alias(emulation, name); - if (alias != NULL) { - int i; - - /* Set up variables for further filter actions */ - tls = &alitls; - emulation = alias->aemul; - name = alias->aname; - - /* Create an aliased list for filter_evaluate */ - TAILQ_INIT(tls); - for (i = 0; i < alias->nargs; i++) { - memcpy(&alitl[i], alias->arguments[i], - sizeof(struct intercept_translate)); - TAILQ_INSERT_TAIL(tls, &alitl[i], next); - } + /* Fast-path checking */ + if ((action = policy->kerneltable[code]) != ICPOLICY_ASK) + goto out; - if ((pflq = systrace_policyflq(policy, - alias->aemul, alias->aname)) == NULL) + pflq = systrace_policyflq(policy, emulation, name); + if (pflq == NULL) errx(1, "%s:%d: no filter queue", __func__, __LINE__); action = filter_evaluate(tls, pflq, ipid); if (action != ICPOLICY_ASK) goto done; - make_output(output, sizeof(output), binname, pid, ppid, - policynr, policy->name, policy->nfilters, - alias->aemul, alias->aname, code, tls, NULL); - } + /* Do aliasing here */ + if (!noalias) + alias = systrace_find_alias(emulation, name); + if (alias != NULL) { + int i; + + /* Set up variables for further filter actions */ + tls = &alitls; + emulation = alias->aemul; + name = alias->aname; + + /* Create an aliased list for filter_evaluate */ + TAILQ_INIT(tls); + for (i = 0; i < alias->nargs; i++) { + memcpy(&alitl[i], alias->arguments[i], + sizeof(struct intercept_translate)); + TAILQ_INSERT_TAIL(tls, &alitl[i], next); + } + + if ((pflq = systrace_policyflq(policy, + alias->aemul, alias->aname)) == NULL) + errx(1, "%s:%d: no filter queue", + __func__, __LINE__); + + action = filter_evaluate(tls, pflq, ipid); + if (action != ICPOLICY_ASK) + goto done; + + make_output(output, sizeof(output), binname, pid, ppid, + policynr, policy->name, policy->nfilters, + alias->aemul, alias->aname, code, tls, NULL); + } + + /* + * At this point, we have to ask the user, but we may check + * if the policy has been updated in the meanwhile. + */ + if (systrace_updatepolicy(fd, policy) == -1) + done = 1; + } while (!done); if (policy->flags & POLICY_UNSUPERVISED) { action = ICPOLICY_NEVER; @@ -268,7 +292,7 @@ gen_cb(int fd, pid_t pid, int policynr, const char *name, int code, struct filterq *pflq = NULL; short action = ICPOLICY_PERMIT; short future; - int off, dolog = 0; + int off, done = 0, dolog = 0; size_t len; if (policynr == -1) @@ -295,18 +319,27 @@ gen_cb(int fd, pid_t pid, int policynr, const char *name, int code, if ((pflq = systrace_policyflq(policy, emulation, name)) == NULL) errx(1, "%s:%d: no filter queue", __func__, __LINE__); - action = filter_evaluate(NULL, pflq, ipid); + do { + /* Fast-path checking */ + if ((action = policy->kerneltable[code]) != ICPOLICY_ASK) + goto out; - if (ipid->uflags & SYSCALL_LOG) - dolog = 1; + action = filter_evaluate(NULL, pflq, ipid); - if (action != ICPOLICY_ASK) - goto out; + if (action != ICPOLICY_ASK) + goto haveresult; + /* + * At this point, we have to ask the user, but we may check + * if the policy has been updated in the meanwhile. + */ + if (systrace_updatepolicy(fd, policy) == -1) + done = 1; + } while (!done); if (policy->flags & POLICY_UNSUPERVISED) { action = ICPOLICY_NEVER; dolog = 1; - goto out; + goto haveresult; } action = filter_ask(fd, NULL, pflq, policynr, emulation, name, @@ -321,12 +354,15 @@ gen_cb(int fd, pid_t pid, int policynr, const char *name, int code, kill(pid, SIGKILL); return (ICPOLICY_NEVER); } - out: + + haveresult: + if (ipid->uflags & SYSCALL_LOG) + dolog = 1; if (dolog) log_msg(LOG_WARNING, "%s user: %s, prog: %s", action < ICPOLICY_NEVER ? "permit" : "deny", ipid->username, output); - + out: return (action); } @@ -426,9 +462,9 @@ log_msg(int priority, const char *fmt, ...) va_start(ap, fmt); - if (logstderr) { + if (logtofile) { vsnprintf(buf, sizeof(buf), fmt, ap); - fprintf(stderr, "%s: %s\n", __progname, buf); + fprintf(logfile, "%s: %s\n", __progname, buf); } else vsyslog(priority, fmt, ap); @@ -439,8 +475,8 @@ static void usage(void) { fprintf(stderr, - "Usage: systrace [-AaCeitUu] [-c uid:gid] [-d policydir] [-f file]\n" - "\t [-g gui] [-p pid] command ...\n"); + "Usage: systrace [-AaCeitUuV] [-c user:group] [-d policydir] [-E logfile]\n" + "\t [-f file] [-g gui] [-p pid] command ...\n"); exit(1); } @@ -575,6 +611,36 @@ get_uid_gid(const char *argument, uid_t *uid, gid_t *gid) return (0); } +static void +systrace_timeout(int fd, short what, void *arg) +{ + struct timeval tv; + + /* Reschedule timeout */ + timerclear(&tv); + tv.tv_sec = SYSTRACE_UPDATETIME; + evtimer_add(&ev_timeout, &tv); + + systrace_updatepolicies(trfd); + if (userpolicy) + systrace_dumppolicies(trfd); +} + +/* + * Read from the kernel if something happened. + */ + +static void +systrace_read(int fd, short what, void *arg) +{ + intercept_read(fd); + + if (!intercept_existpids()) { + event_del(&ev_read); + event_del(&ev_timeout); + } +} + int main(int argc, char **argv) { @@ -582,16 +648,19 @@ main(int argc, char **argv) char **args; char *filename = NULL; char *policypath = NULL; - struct timeval tv, tv_wait = {60, 0}; + struct timeval tv; pid_t pidattach = 0; - int usex11 = 1, count; + int usex11 = 1; int background; int setcredentials = 0; uid_t cr_uid; gid_t cr_gid; - while ((c = getopt(argc, argv, "c:aAeituUCd:g:f:p:")) != -1) { + while ((c = getopt(argc, argv, "Vc:aAeE:ituUCd:g:f:p:")) != -1) { switch (c) { + case 'V': + fprintf(stderr, "%s V%s\n", argv[0], VERSION); + exit(0); case 'c': setcredentials = 1; if (get_uid_gid(optarg, &cr_uid, &cr_gid) == -1) @@ -606,7 +675,15 @@ main(int argc, char **argv) policypath = optarg; break; case 'e': - logstderr = 1; + logtofile = 1; + logfile = stderr; + break; + case 'E': + logtofile = 1; + logfile = fopen(optarg, "a"); + if (logfile == NULL) + err(1, "Cannot open \"%s\" for writing", + optarg); break; case 'A': if (automatic) @@ -660,6 +737,10 @@ main(int argc, char **argv) usage(); } + /* Initalize libevent but without kqueue because of systrace fd */ + setenv("EVENT_NOKQUEUE", "yes", 0); + event_init(); + /* Local initialization */ systrace_initalias(); systrace_initpolicy(filename, policypath); @@ -708,7 +789,10 @@ main(int argc, char **argv) if (signal(SIGCHLD, child_handler) == SIG_ERR) err(1, "signal"); - /* Start the policy gui or cradle if necessary */ + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) + err(1, "signal"); + + /* Start the policy GUI or cradle if necessary */ if (usex11 && (!automatic && !allow)) { if (cradle) cradle_setup(guipath); @@ -717,34 +801,22 @@ main(int argc, char **argv) } - /* Loop on requests */ - count = 0; - while (intercept_read(trfd) != -1) { - if (!intercept_existpids()) - break; - if (userpolicy) { - /* Periodically save modified policies */ - if (count == 0) { - /* Set new wait time */ - gettimeofday(&tv, NULL); - timeradd(&tv, &tv_wait, &tv); - } else if (count > 10) { - struct timeval now; - gettimeofday(&now, NULL); - - count = 0; - if (timercmp(&now, &tv, >)) { - /* Dump policy and cause new time */ - systrace_dumppolicy(); - continue; - } - } - count++; - } + /* Register read events */ + event_set(&ev_read, trfd, EV_READ|EV_PERSIST, systrace_read, NULL); + event_add(&ev_read, NULL); + + if (userpolicy || automatic) { + evtimer_set(&ev_timeout, systrace_timeout, &ev_timeout); + timerclear(&tv); + tv.tv_sec = SYSTRACE_UPDATETIME; + evtimer_add(&ev_timeout, &tv); } + /* Wait for events */ + event_dispatch(); + if (userpolicy) - systrace_dumppolicy(); + systrace_dumppolicies(trfd); close(trfd); diff --git a/bin/systrace/systrace.h b/bin/systrace/systrace.h index ef148e91e85..2f9b9d75da0 100644 --- a/bin/systrace/systrace.h +++ b/bin/systrace/systrace.h @@ -1,4 +1,4 @@ -/* $OpenBSD: systrace.h,v 1.26 2006/03/12 20:56:10 sturm Exp $ */ +/* $OpenBSD: systrace.h,v 1.27 2006/07/02 12:34:15 sturm Exp $ */ /* * Copyright 2002 Niels Provos * All rights reserved. @@ -98,14 +98,17 @@ struct policy { const char *name; char emulation[16]; + struct timespec ts_last; /* last time we read the file */ + SPLAY_HEAD(syscalltree, policy_syscall) pflqs; - int policynr; + int policynr; /* in-kernel policy number */ + short kerneltable[INTERCEPT_MAXSYSCALLNR]; int flags; struct filterq filters; - int nfilters; - struct filterq prefilters; + int nfilters; /* nr of installed policy statements */ + struct filterq prefilters; /* filters we need to install*/ }; struct template { @@ -131,6 +134,8 @@ TAILQ_HEAD(tmplqueue, template); #define SYSCALL_LOG 0x04 /* Log this system call */ #define PROCESS_PROMPT 0x08 /* Prompt but nothing else */ +#define SYSTRACE_UPDATETIME 30 /* update policies every 30 seconds */ + void systrace_parameters(void); int systrace_initpolicy(char *, char *); void systrace_setupdir(char *); @@ -138,19 +143,24 @@ struct template *systrace_readtemplate(char *, struct policy *, struct template *); void systrace_initcb(void); struct policy *systrace_newpolicy(const char *, const char *); +void systrace_cleanpolicy(struct policy *); void systrace_freepolicy(struct policy *); int systrace_newpolicynr(int, struct policy *); int systrace_modifypolicy(int, int, const char *, short); struct policy *systrace_findpolicy(const char *); +struct policy *systrace_findpolicy_wildcard(const char *); struct policy *systrace_findpolnr(int); -int systrace_dumppolicy(void); -int systrace_readpolicy(char *); +int systrace_dumppolicies(int); +int systrace_updatepolicies(int); +struct policy *systrace_readpolicy(const char *); int systrace_addpolicy(const char *); +int systrace_updatepolicy(int fd, struct policy *policy); struct filterq *systrace_policyflq(struct policy *, const char *, const char *); +char *systrace_getpolicyname(const char *); int systrace_error_translate(char *); -#define SYSTRACE_MAXALIAS 5 +#define SYSTRACE_MAXALIAS 10 struct systrace_alias { SPLAY_ENTRY(systrace_alias) node; @@ -233,6 +243,7 @@ extern struct intercept_translate ic_pidname; extern struct intercept_translate ic_signame; extern struct intercept_translate ic_fcntlcmd; extern struct intercept_translate ic_memprot; +extern struct intercept_translate ic_linux_memprot; extern struct intercept_translate ic_fileflags; extern struct intercept_translate ic_linux_oflags; -- cgit v1.2.3