diff options
author | Niels Provos <provos@cvs.openbsd.org> | 2002-06-04 17:20:05 +0000 |
---|---|---|
committer | Niels Provos <provos@cvs.openbsd.org> | 2002-06-04 17:20:05 +0000 |
commit | 46a8c64495ad8a9d221a6c0fa12e7d77b6e2420b (patch) | |
tree | 9c7200a8a35c2bee11d017386d12aab0d8bca720 | |
parent | 72975dabb915c98862a98d34d585bdc781702b4d (diff) |
initial import of systrace. don't touch this, more stuff coming in a while
-rw-r--r-- | bin/systrace/Makefile | 17 | ||||
-rw-r--r-- | bin/systrace/filter.c | 468 | ||||
-rw-r--r-- | bin/systrace/intercept-translate.c | 273 | ||||
-rw-r--r-- | bin/systrace/intercept.c | 525 | ||||
-rw-r--r-- | bin/systrace/intercept.h | 144 | ||||
-rw-r--r-- | bin/systrace/openbsd-syscalls.c | 510 | ||||
-rw-r--r-- | bin/systrace/policy.c | 442 | ||||
-rw-r--r-- | bin/systrace/systrace-errno.h | 120 | ||||
-rw-r--r-- | bin/systrace/systrace-error.c | 147 | ||||
-rw-r--r-- | bin/systrace/systrace-translate.c | 173 | ||||
-rw-r--r-- | bin/systrace/systrace.c | 450 | ||||
-rw-r--r-- | bin/systrace/systrace.h | 132 | ||||
-rw-r--r-- | bin/systrace/util.c | 208 |
13 files changed, 3609 insertions, 0 deletions
diff --git a/bin/systrace/Makefile b/bin/systrace/Makefile new file mode 100644 index 00000000000..615c85b152f --- /dev/null +++ b/bin/systrace/Makefile @@ -0,0 +1,17 @@ +# $OpenBSD: Makefile,v 1.1 2002/06/04 17:20:04 provos Exp $ + +PROG= systrace +CFLAGS+= -I. +SRCS= filter.c intercept-translate.c intercept.c \ + openbsd-syscalls.c util.c \ + policy.c systrace-errno.h systrace-error.c \ + systrace-translate.c systrace.c \ + parse.c lex.l +YFLAGS= -d +CLEANFILES+= parse.c parse.h +NOMAN= + +parse.c: parse.y + ${YACC} -d -o parse.c ${.CURDIR}/parse.y + +.include <bsd.prog.mk> diff --git a/bin/systrace/filter.c b/bin/systrace/filter.c new file mode 100644 index 00000000000..ea317f8a441 --- /dev/null +++ b/bin/systrace/filter.c @@ -0,0 +1,468 @@ +/* $OpenBSD: filter.c,v 1.1 2002/06/04 17:20:04 provos Exp $ */ +/* + * Copyright 2002 Niels Provos <provos@citi.umich.edu> + * 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 <sys/types.h> +#include <sys/wait.h> +#include <sys/tree.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <fnmatch.h> +#include <err.h> + +#include "intercept.h" +#include "systrace.h" + +extern int connected; + +int +filter_match(struct intercept_tlq *tls, struct logic *logic) +{ + struct intercept_translate *tl; + int off = 0; + + switch (logic->op) { + case LOGIC_NOT: + return (!filter_match(tls, logic->left)); + case LOGIC_OR: + if (filter_match(tls, logic->left)) + return (1); + return (filter_match(tls, logic->right)); + case LOGIC_AND: + if (!filter_match(tls, logic->left)) + return (0); + return (filter_match(tls, logic->right)); + default: + break; + } + + /* Now we just have a logic single */ + if (logic->type == NULL) + goto match; + + TAILQ_FOREACH(tl, tls, next) { + if (!tl->trans_valid) + return (0); + + if (strcasecmp(tl->name, logic->type)) + continue; + + if (logic->typeoff == -1 || logic->typeoff == off) + break; + + off++; + } + + if (tl == NULL) + return (0); + + match: + return (logic->filter_match(tl, logic)); +} + +short +filter_evaluate(struct intercept_tlq *tls, struct filterq *fls, int *pflags) +{ + struct filter *filter; + short action; + + TAILQ_FOREACH(filter, fls, next) { + if (filter_match(tls, filter->logicroot)) { + action = filter->match_action; + if (action == ICPOLICY_NEVER) + action = filter->match_error; + *pflags = filter->match_flags; + return (action); + } + } + + return (ICPOLICY_ASK); +} + +void +logic_free(struct logic *logic) +{ + if (logic->left) + logic_free(logic->left); + if (logic->right) + logic_free(logic->right); + if (logic->type) + free(logic->type); + if (logic->filterdata) + free(logic->filterdata); + free(logic); +} + +void +filter_free(struct filter *filter) +{ + if (filter->logicroot) + logic_free(filter->logicroot); + if (filter->rule) + free(filter->rule); + free(filter); +} + +void +filter_review(struct filterq *fls) +{ + struct filter *filter; + int i = 0; + + printf("Filter review:\n"); + + TAILQ_FOREACH(filter, fls, next) { + i++; + printf("%d. %s\n", i, filter->rule); + } +} + +void +filter_policyrecord(struct policy *policy, struct filter *filter, + char *emulation, char *name, char *rule) +{ + /* Record the filter in the policy */ + if (filter == NULL) { + filter = calloc(1, sizeof(struct filter)); + if (filter == NULL) + err(1, "%s:%d: calloc", __FUNCTION__, __LINE__); + if ((filter->rule = strdup(rule)) == NULL) + err(1, "%s:%d: strdup", __FUNCTION__, __LINE__); + } + + strlcpy(filter->name, name, sizeof(filter->name)); + strlcpy(filter->emulation, emulation, sizeof(filter->emulation)); + + TAILQ_INSERT_TAIL(&policy->filters, filter, policy_next); + policy->nfilters++; + + policy->flags |= POLICY_CHANGED; +} + +int +filter_parse(char *line, struct filter **pfilter) +{ + char *rule; + + if (parse_filter(line, pfilter) == -1) + return (-1); + + if ((rule = strdup(line)) == NULL) + err(1, "%s:%d: strdup", __FUNCTION__, __LINE__); + + (*pfilter)->rule = rule; + + return (0); +} + +/* Translate a simple action like "permit" or "deny[einval]" to numbers */ + +int +filter_parse_simple(char *rule, short *paction, short *pfuture) +{ + char buf[1024]; + int isfuture = 1; + char *line, *p; + + strlcpy(buf, rule, sizeof(buf)); + line = buf; + + if (!strcmp("permit", line)) { + *paction = *pfuture = ICPOLICY_PERMIT; + return (0); + } else if (!strcmp("permit-now", line)) { + *paction = ICPOLICY_PERMIT; + return (0); + } else if (strncmp("deny", line, 4)) + return (-1); + + line +=4 ; + if (!strncmp("-now", line, 4)) { + line += 4; + isfuture = 0; + } + + *paction = ICPOLICY_NEVER; + + switch (line[0]) { + case '\0': + break; + case '[': + line++; + p = strsep(&line, "]"); + if (line == NULL || *line != '\0') + return (-1); + + *paction = systrace_error_translate(p); + if (*paction == -1) + return (-1); + break; + default: + return (-1); + } + + if (isfuture) + *pfuture = *paction; + + return (NULL); +} + +int +filter_prepolicy(int fd, struct policy *policy) +{ + int res; + struct filter *filter, *parsed; + struct filterq *fls; + short action, future; + + /* Commit all matching pre-filters */ + for (filter = TAILQ_FIRST(&policy->prefilters); + filter; filter = TAILQ_FIRST(&policy->prefilters)) { + future = ICPOLICY_ASK; + + TAILQ_REMOVE(&policy->prefilters, filter, policy_next); + + res = 0; + parsed = NULL; + /* Special rules that are not real filters */ + if (filter_parse_simple(filter->rule, &action, &future) == -1) + res = filter_parse(filter->rule, &parsed); + if (res == -1) + errx(1, "%s:%d: can not parse \"%s\"", + __FUNCTION__, __LINE__, filter->rule); + + if (future == ICPOLICY_ASK) { + fls = systrace_policyflq(policy, policy->emulation, + filter->name); + TAILQ_INSERT_TAIL(fls, parsed, next); + } else { + res = systrace_modifypolicy(fd, policy->policynr, + filter->name, future); + if (res == -1) + errx(1, "%s:%d: modify policy for \"%s\"", + __FUNCTION__, __LINE__, filter->rule); + } + filter_policyrecord(policy, parsed, policy->emulation, + filter->name, filter->rule); + + filter_free(filter); + } + + /* Existing policy applied undo changed flag */ + policy->flags &= ~POLICY_CHANGED; + + return (0); +} + +short +filter_ask(struct intercept_tlq *tls, struct filterq *fls, + int policynr, char *emulation, char *name, + char *output, short *pfuture, int *pflags) +{ + char line[1024], *p; + struct filter *filter; + struct policy *policy; + short action; + int first = 0; + + *pfuture = ICPOLICY_ASK; + *pflags = 0; + + if ((policy = systrace_findpolnr(policynr)) == NULL) + errx(1, "%s:%d: no policy %d\n", __FUNCTION__, __LINE__, + policynr); + + printf("%s\n", output); + + while (1) { + filter = NULL; + + if (!connected) + printf("Answer: "); + else { + /* Do not prompt the first time */ + if (first) { + printf("WRONG\n"); + } + first = 1; + } + + fgets(line, sizeof(line), stdin); + p = line; + strsep(&p, "\n"); + + /* Simple keywords */ + if (!strcasecmp(line, "detach")) { + if (policy->nfilters) { + policy->flags |= POLICY_UNSUPERVISED; + action = ICPOLICY_NEVER; + } else { + policy->flags |= POLICY_DETACHED; + policy->flags |= POLICY_CHANGED; + action = ICPOLICY_PERMIT; + } + goto out; + } else if (!strcasecmp(line, "kill")) { + action = ICPOLICY_KILL; + goto out; + } else if (!strcasecmp(line, "review") && fls != NULL) { + filter_review(fls); + continue; + } + + if (filter_parse_simple(line, &action, pfuture) != -1) { + if (*pfuture == ICPOLICY_ASK) + goto out; + break; + } + + if (fls == NULL) { + printf("Syntax error.\n"); + continue; + } + + if (filter_parse(line, &filter) == -1) + continue; + + TAILQ_INSERT_TAIL(fls, filter, next); + action = filter_evaluate(tls, fls, pflags); + if (action == ICPOLICY_ASK) { + TAILQ_REMOVE(fls, filter, next); + printf("Filter unmatched. Freeing it\n"); + filter_free(filter); + continue; + } + + break; + } + + filter_policyrecord(policy, filter, emulation, name, line); + + out: + if (connected) + printf("OKAY\n"); + return (action); + +} + +void +filter_replace(char *buf, size_t buflen, char *match, char *repl) +{ + while (strrpl(buf, buflen, match, repl) != NULL) + ; +} + +char * +filter_expand(char *data) +{ + static char expand[1024]; + char *what; + + if (data != NULL) + strlcpy(expand, data, sizeof(expand)); + + what = getenv("HOME"); + if (what != NULL) + filter_replace(expand, sizeof(expand), "$HOME", what); + what = getenv("USER"); + if (what != NULL) + filter_replace(expand, sizeof(expand), "$USER", what); + + return (expand); +} + +int +filter_fnmatch(struct intercept_translate *tl, struct logic *logic) +{ + int res; + char *line; + + if (tl->trans_size == 0) + return (0); + + if ((line = intercept_translate_print(tl)) == NULL) + return (0); + res = fnmatch(logic->filterdata, line, FNM_PATHNAME | FNM_LEADING_DIR); + + return (res == 0); +} + +int +filter_substrmatch(struct intercept_translate *tl, struct logic *logic) +{ + char *line; + + if ((line = intercept_translate_print(tl)) == NULL) + return (0); + + return (strstr(line, logic->filterdata) != NULL); +} + +int +filter_negsubstrmatch(struct intercept_translate *tl, struct logic *logic) +{ + char *line; + + if ((line = intercept_translate_print(tl)) == NULL) + return (0); + + return (strstr(line, logic->filterdata) == NULL); +} + +int +filter_stringmatch(struct intercept_translate *tl, struct logic *logic) +{ + char *line; + + if ((line = intercept_translate_print(tl)) == NULL) + return (0); + + return (!strcasecmp(line, logic->filterdata)); +} + +int +filter_negstringmatch(struct intercept_translate *tl, struct logic *logic) +{ + char *line; + + if ((line = intercept_translate_print(tl)) == NULL) + return (1); + + return (strcasecmp(line, logic->filterdata) != 0); +} + +int +filter_true(struct intercept_translate *tl, struct logic *logic) +{ + return (1); +} diff --git a/bin/systrace/intercept-translate.c b/bin/systrace/intercept-translate.c new file mode 100644 index 00000000000..4bd1be11a1c --- /dev/null +++ b/bin/systrace/intercept-translate.c @@ -0,0 +1,273 @@ +/* $OpenBSD: intercept-translate.c,v 1.1 2002/06/04 17:20:04 provos Exp $ */ +/* + * Copyright 2002 Niels Provos <provos@citi.umich.edu> + * 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 <sys/types.h> +#include <sys/param.h> +#include <sys/tree.h> +#include <sys/socket.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netdb.h> +#include <err.h> + +#include "intercept.h" + +char *error_msg = "error"; + +void +ic_trans_free(struct intercept_translate *trans) +{ + if (trans->trans_data) + free(trans->trans_data); + if (trans->trans_print) + free(trans->trans_print); + trans->trans_valid = 0; + trans->trans_data = NULL; + trans->trans_print = NULL; + trans->trans_size = 0; + trans->trans_addr = NULL; +} + +extern struct intercept_system intercept; + +/* Takes a translation structure and retrieves the right data */ + +int +intercept_translate(struct intercept_translate *trans, + int fd, pid_t pid, int off, void *args, int argsize) +{ + void *addr, *addr2; + + ic_trans_free(trans); + + if (intercept.getarg(off, args, argsize, &addr) == -1) + return (-1); + if (trans->off2) { + if (intercept.getarg(trans->off + trans->off2, + args, argsize, &addr2) == -1) + return (-1); + trans->trans_addr2 = addr2; + } + + trans->trans_valid = 1; + trans->trans_addr = addr; + + if (trans->translate == NULL) + return (0); + + if ((*trans->translate)(trans, fd, pid, addr) == -1) { + trans->trans_valid = 0; + return (-1); + } + + return (0); +} + +char * +intercept_translate_print(struct intercept_translate *trans) +{ + char line[1024]; + + if (trans->trans_print == NULL) { + if (trans->print(line, sizeof(line), trans) == -1) + return (error_msg); + + if ((trans->trans_print = strdup(line)) == NULL) + return (error_msg); + } + + return (trans->trans_print); +} + +int +ic_print_filename(char *buf, size_t buflen, struct intercept_translate *tl) +{ + strlcpy(buf, tl->trans_data, buflen); + + return (0); +} + +int +ic_get_filename(struct intercept_translate *trans, int fd, pid_t pid, + void *addr) +{ + char buf[MAXPATHLEN]; + char *path, *name; + int len; + + name = intercept_filename(fd, pid, addr); + if (name == NULL) + return (-1); + + /* If realpath fails then the filename does not exist */ + path = buf; + if (realpath(name, path) == NULL) + path = "<non-existant filename>"; + + len = strlen(path) + 1; + trans->trans_data = malloc(len); + if (trans->trans_data == NULL) + return (-1); + + trans->trans_size = len; + memcpy(trans->trans_data, path, len); + + return (0); +} + +int +ic_get_string(struct intercept_translate *trans, int fd, pid_t pid, void *addr) +{ + char *name; + int len; + + name = intercept_get_string(fd, pid, addr); + if (name == NULL) + return (-1); + + len = strlen(name) + 1; + trans->trans_data = malloc(len); + if (trans->trans_data == NULL) + return (-1); + + trans->trans_size = len; + memcpy(trans->trans_data, name, len); + + return (0); +} + +int +ic_get_linkname(struct intercept_translate *trans, int fd, pid_t pid, + void *addr) +{ + char *name; + int len; + + name = intercept_filename(fd, pid, addr); + if (name == NULL) + return (-1); + + len = strlen(name) + 1; + trans->trans_data = malloc(len); + if (trans->trans_data == NULL) + return (-1); + + trans->trans_size = len; + memcpy(trans->trans_data, name, len); + + return (0); +} + +int +ic_get_sockaddr(struct intercept_translate *trans, int fd, pid_t pid, + void *addr) +{ + struct sockaddr_storage sa; + socklen_t len; + + len = (socklen_t )trans->trans_addr2; + if (len == 0 || len > sizeof(struct sockaddr_storage)) + return (-1); + + if (intercept.io(fd, pid, INTERCEPT_READ, addr, + (void *)&sa, len) == -1) + return (-1); + + trans->trans_data = malloc(len); + if (trans->trans_data == NULL) + return (-1); + trans->trans_size = len; + memcpy(trans->trans_data, &sa, len); + + return (0); +} + +int +ic_print_sockaddr(char *buf, size_t buflen, struct intercept_translate *tl) +{ + char host[NI_MAXHOST]; + char serv[NI_MAXSERV]; + struct sockaddr *sa = tl->trans_data; + socklen_t len = (socklen_t)tl->trans_size; + + buf[0] = '\0'; + + switch (sa->sa_family) { + case PF_LOCAL: + if (sa->sa_len < len) + len = sa->sa_len; + if (buflen < len + 1) + len = buflen - 1; + memcpy(buf, sa->sa_data, len); + buf[len] = '\0'; + return (0); + case PF_INET: + case PF_INET6: + break; + default: + snprintf(buf, buflen, "family(%d)", sa->sa_family); + return (0); + } + + sa->sa_len = 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); +} + +struct intercept_translate ic_translate_string = { + "string", + ic_get_string, ic_print_filename, +}; + +struct intercept_translate ic_translate_filename = { + "filename", + ic_get_filename, ic_print_filename, +}; + +struct intercept_translate ic_translate_linkname = { + "filename", + ic_get_linkname, ic_print_filename, +}; + +struct intercept_translate ic_translate_connect = { + "sockaddr", + ic_get_sockaddr, ic_print_sockaddr, + /* XXX - Special handling */ 1, +}; diff --git a/bin/systrace/intercept.c b/bin/systrace/intercept.c new file mode 100644 index 00000000000..75a29f573f1 --- /dev/null +++ b/bin/systrace/intercept.c @@ -0,0 +1,525 @@ +/* $OpenBSD: intercept.c,v 1.1 2002/06/04 17:20:04 provos Exp $ */ +/* + * Copyright 2002 Niels Provos <provos@citi.umich.edu> + * 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 <sys/types.h> +#include <sys/param.h> +#include <sys/tree.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <poll.h> +#include <fcntl.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <err.h> + +#include "intercept.h" + +void simplify_path(char *); + +struct intercept_syscall { + SPLAY_ENTRY(intercept_syscall) node; + + char name[64]; + char emulation[16]; + + short (*cb)(int, pid_t, int, char *, int, char *, void *, int, + struct intercept_tlq *, void *); + void *cb_arg; + + struct intercept_tlq tls; +}; + +static SPLAY_HEAD(pidtree, intercept_pid) pids; +static SPLAY_HEAD(sctree, intercept_syscall) scroot; + +/* Generic callback functions */ + +void (*intercept_newimagecb)(int, pid_t, int, char *, char *, void *) = NULL; +void *intercept_newimagecbarg = NULL; +short (*intercept_gencb)(int, pid_t, int, char *, int, char *, void *, int, void *) = NULL; +void *intercept_gencbarg = NULL; + +int +sccompare(struct intercept_syscall *a, struct intercept_syscall *b) +{ + int diff; + diff = strcmp(a->emulation, b->emulation); + if (diff) + return (diff); + return (strcmp(a->name, b->name)); +} + +int +pidcompare(struct intercept_pid *a, struct intercept_pid *b) +{ + int diff = a->pid - b->pid; + + if (diff == 0) + return (0); + if (diff > 0) + return (1); + return (-1); +} + +SPLAY_PROTOTYPE(sctree, intercept_syscall, node, sccompare); +SPLAY_GENERATE(sctree, intercept_syscall, node, sccompare); + +SPLAY_PROTOTYPE(pidtree, intercept_pid, next, pidcompare); +SPLAY_GENERATE(pidtree, intercept_pid, next, pidcompare); + +extern struct intercept_system intercept; + +int +intercept_init(void) +{ + SPLAY_INIT(&pids); + SPLAY_INIT(&scroot); + + intercept_newimagecb = NULL; + intercept_gencb = NULL; + + return (intercept.init()); +} + +struct intercept_syscall * +intercept_sccb_find(char *emulation, char *name) +{ + struct intercept_syscall tmp; + + strlcpy(tmp.name, name, sizeof(tmp.name)); + strlcpy(tmp.emulation, emulation, sizeof(tmp.emulation)); + return (SPLAY_FIND(sctree, &scroot, &tmp)); +} + +int +intercept_register_translation(char *emulation, char *name, int offset, + struct intercept_translate *tl) +{ + struct intercept_syscall *tmp; + struct intercept_translate *tlnew; + + if (offset >= INTERCEPT_MAXSYSCALLARGS) + return (-1); + + tmp = intercept_sccb_find(emulation, name); + if (tmp == NULL) + return (-1); + + tlnew = malloc(sizeof(struct intercept_translate)); + if (tlnew == NULL) + return (-1); + + memcpy(tlnew, tl, sizeof(struct intercept_translate)); + tlnew->off = offset; + + TAILQ_INSERT_TAIL(&tmp->tls, tlnew, next); + + return (0); +} + +void * +intercept_sccb_cbarg(char *emulation, char *name) +{ + struct intercept_syscall *tmp; + + if ((tmp = intercept_sccb_find(emulation, name)) == NULL) + return (NULL); + + return (tmp->cb_arg); +} + +int +intercept_register_sccb(char *emulation, char *name, + short (*cb)(int, pid_t, int, char *, int, char *, void *, int, + struct intercept_tlq *, void *), + void *cbarg) +{ + struct intercept_syscall *tmp; + + if (intercept_sccb_find(emulation, name)) + return (-1); + + if (intercept.getsyscallnumber(emulation, name) == -1) { + warnx("%s: %d: unknown syscall: %s-%s", __FUNCTION__, __LINE__, + emulation, name); + return (-1); + } + + if ((tmp = calloc(1, sizeof(struct intercept_syscall))) == NULL) { + warn("%s:%d: malloc", __FUNCTION__, __LINE__); + return (-1); + } + + TAILQ_INIT(&tmp->tls); + strlcpy(tmp->name, name, sizeof(tmp->name)); + strlcpy(tmp->emulation, emulation, sizeof(tmp->emulation)); + tmp->cb = cb; + tmp->cb_arg = cbarg; + + SPLAY_INSERT(sctree, &scroot, tmp); + + return (0); +} + +int +intercept_register_gencb(short (*cb)(int, pid_t, int, char *, int, char *, void *, int, void *), void *arg) +{ + intercept_gencb = cb; + intercept_gencbarg = arg; + + return (0); +} + +int +intercept_register_execcb(void (*cb)(int, pid_t, int, char *, char *, void *), void *arg) +{ + intercept_newimagecb = cb; + intercept_newimagecbarg = arg; + + return (0); +} + +pid_t +intercept_run(int fd, char *path, char *const argv[]) +{ + pid_t pid; + + pid = fork(); + if (pid == -1) + return (-1); + if (pid == 0) { + /* Needs to be closed */ + close(fd); + + /* Stop myself */ + raise(SIGSTOP); + + execvp(path, argv); + + /* Error */ + err(1, "execvp"); + } + + sleep(1); /* XXX */ + + return (pid); +} + +int +intercept_existpids(void) +{ + return (SPLAY_ROOT(&pids) != NULL); +} + +void +intercept_freepid(pid_t pidnr) +{ + struct intercept_pid *pid, tmp2; + + tmp2.pid = pidnr; + pid = SPLAY_FIND(pidtree, &pids, &tmp2); + if (pid == NULL) + return; + + intercept.freepid(pid); + + SPLAY_REMOVE(pidtree, &pids, pid); + if (pid->name) + free(pid->name); + if (pid->newname) + free(pid->newname); + free(pid); +} + +struct intercept_pid * +intercept_getpid(pid_t pid) +{ + struct intercept_pid *tmp, tmp2; + + tmp2.pid = pid; + tmp = SPLAY_FIND(pidtree, &pids, &tmp2); + + if (tmp) + return (tmp); + + if ((tmp = malloc(sizeof(struct intercept_pid))) == NULL) + err(1, "%s: malloc", __FUNCTION__); + + memset(tmp, 0, sizeof(struct intercept_pid)); + tmp->pid = pid; + + SPLAY_INSERT(pidtree, &pids, tmp); + + return (tmp); +} + +int +intercept_open(void) +{ + int fd; + + fd = intercept.open(); + if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) + warn("fcntl(O_NONBLOCK)"); + + return (fd); +} + +int +intercept_attach(int fd, pid_t pid) +{ + return (intercept.attach(fd, pid)); +} + +int +intercept_detach(int fd, pid_t pid) +{ + int res; + + res = intercept.detach(fd, pid); + if (res != -1) + intercept_freepid(pid); + return (res); +} + +int +intercept_read(int fd) +{ + struct pollfd pollfd; + int n; + + pollfd.fd = fd; + pollfd.events = POLLIN; + + do { + n = poll(&pollfd, 1, -1); + if (n == -1) { + if (errno != EINTR && errno != EAGAIN) + return (-1); + } + } while (n <= 0); + + if (!(pollfd.revents & (POLLIN|POLLRDNORM))) + return (-1); + + return (intercept.read(fd)); +} + +char * +intercept_get_string(int fd, pid_t pid, void *addr) +{ + static char name[1024]; + int off = 0; + + do { + if (intercept.io(fd, pid, INTERCEPT_READ, addr + off, + &name[off], 4) == -1) { + warn("ioctl"); + return (NULL); + } + + off += 4; + name[off] = '\0'; + if (strlen(name) < off) + break; + + } while (off < sizeof(name)); + + return (name); +} + +char * +intercept_filename(int fd, pid_t pid, void *addr) +{ + static char cwd[1024]; + char *name; + + name = intercept_get_string(fd, pid, addr); + if (name == NULL) + err(1, "%s:%d: getstring", __FUNCTION__, __LINE__); + + if (name[0] != '/') { + if (intercept.getcwd(fd, pid, cwd, sizeof(cwd)) == NULL) + err(1, "%s:%d: getcwd", __FUNCTION__, __LINE__); + + strlcat(cwd, "/", sizeof(cwd)); + strlcat(cwd, name, sizeof(cwd)); + } else + strlcpy(cwd, name, sizeof(cwd)); + + simplify_path(cwd); + + return (cwd); +} + +void +intercept_syscall(int fd, pid_t pid, int policynr, char *name, int code, + char *emulation, void *args, int argsize) +{ + short action, flags = 0; + struct intercept_syscall *sc; + int error = 0; + + action = ICPOLICY_PERMIT; + flags = 0; + + /* Special handling for the exec call */ + if (!strcmp(name, "execve")) { + struct intercept_pid *icpid; + void *addr; + + if ((icpid = intercept_getpid(pid)) == NULL) + err(1, "intercept_getpid"); + + icpid->execve_code = code; + icpid->policynr = policynr; + + if (icpid->newname) + free(icpid->newname); + + intercept.getarg(0, args, argsize, &addr); + icpid->newname = strdup(intercept_filename(fd, pid, addr)); + if (icpid->newname == NULL) + err(1, "%s:%d: strdup", __FUNCTION__, __LINE__); + + /* We need to know the result from this system call */ + flags = ICFLAGS_RESULT; + } + + sc = intercept_sccb_find(emulation, name); + if (sc != NULL) { + struct intercept_translate *tl; + + TAILQ_FOREACH(tl, &sc->tls, next) { + if (intercept_translate(tl, fd, pid, tl->off, + args, argsize) == -1) + break; + } + + action = (*sc->cb)(fd, pid, policynr, name, code, emulation, + args, argsize, &sc->tls, sc->cb_arg); + } else if (intercept_gencb != NULL) + action = (*intercept_gencb)(fd, pid, policynr, name, code, + emulation, args, argsize, intercept_gencbarg); + + if (action > 0) { + error = action; + action = ICPOLICY_NEVER; + } + + /* Resume execution of the process */ + intercept.answer(fd, pid, action, error, flags); +} + +void +intercept_syscall_result(int fd, pid_t pid, int policynr, + char *name, int code, char *emulation, void *args, int argsize, + int result, void *rval) +{ + struct intercept_pid *icpid; + + if (!strcmp("execve", name)) { + if (result) + goto out; + + /* Commit the name of the new image */ + icpid = intercept_getpid(pid); + if (icpid->name) + free(icpid->name); + icpid->name = icpid->newname; + icpid->newname = NULL; + + if (intercept_newimagecb != NULL) + (*intercept_newimagecb)(fd, pid, policynr, emulation, + icpid->name, intercept_newimagecbarg); + + } + out: + /* Resume execution of the process */ + intercept.answer(fd, pid, 0, 0, 0); +} + +int +intercept_newpolicy(int fd) +{ + int policynr; + + policynr = intercept.newpolicy(fd); + + return (policynr); +} + +int +intercept_assignpolicy(int fd, pid_t pid, int policynr) +{ + return (intercept.assignpolicy(fd, pid, policynr)); +} + +int +intercept_modifypolicy(int fd, int policynr, char *emulation, char *name, + short policy) +{ + int code; + + code = intercept.getsyscallnumber(emulation, name); + if (code == -1) + return (-1); + + return (intercept.policy(fd, policynr, code, policy)); +} + +void +intercept_child_info(pid_t opid, pid_t npid) +{ + struct intercept_pid *ipid, *inpid, tmp; + + /* A child just died on us */ + if (npid == -1) { + intercept_freepid(opid); + return; + } + + tmp.pid = opid; + ipid = SPLAY_FIND(pidtree, &pids, &tmp); + if (ipid == NULL) + return; + + inpid = intercept_getpid(npid); + + inpid->policynr = ipid->policynr; + if (ipid->name != NULL) + inpid->name = strdup(ipid->name); + + /* XXX - keeps track of emulation */ + intercept.clonepid(ipid, inpid); +} diff --git a/bin/systrace/intercept.h b/bin/systrace/intercept.h new file mode 100644 index 00000000000..5d188339096 --- /dev/null +++ b/bin/systrace/intercept.h @@ -0,0 +1,144 @@ +/* $OpenBSD: intercept.h,v 1.1 2002/06/04 17:20:04 provos Exp $ */ +/* + * Copyright 2002 Niels Provos <provos@citi.umich.edu> + * 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 _INTERCEPT_H_ +#define _INTERCEPT_H_ +#include <sys/queue.h> + +struct intercept_pid; + +struct intercept_system { + char *name; + int (*init)(void); + int (*open)(void); + int (*attach)(int, pid_t); + int (*detach)(int, pid_t); + int (*read)(int); + int (*getsyscallnumber)(char *, char *); + char *(*getcwd)(int, pid_t, char *, size_t); + int (*io)(int, pid_t, int, void *, u_char *, size_t); + int (*getarg)(int, void *, int, void **); + int (*answer)(int, pid_t, short, int, short); + int (*newpolicy)(int); + int (*assignpolicy)(int, pid_t, int); + int (*policy)(int, int, int, short); + void (*clonepid)(struct intercept_pid *, struct intercept_pid *); + void (*freepid)(struct intercept_pid *); +}; + +#define INTERCEPT_READ 1 +#define INTERCEPT_WRITE 2 + +#define ICPOLICY_ASK 0 +#define ICPOLICY_PERMIT -1 +#define ICPOLICY_KILL -2 +#define ICPOLICY_NEVER 1 + +#define ICFLAGS_RESULT 1 + +struct intercept_pid { + SPLAY_ENTRY(intercept_pid) next; + pid_t pid; + + short policynr; + int execve_code; + short execve_policy; + char *name; + char *newname; + + void *data; + + int uflags; /* Flags that can be used by external application */ +}; + +#define INTERCEPT_MAXSYSCALLARGS 10 + +struct intercept_translate { + char *name; + int (*translate)(struct intercept_translate *, int, pid_t, void *); + int (*print)(char *, size_t, struct intercept_translate *); + int off2; + int off; + u_char trans_valid; + void *trans_addr; + void *trans_addr2; + void *trans_data; + size_t trans_size; + char *trans_print; + TAILQ_ENTRY(intercept_translate) next; +}; + +TAILQ_HEAD(intercept_tlq, intercept_translate); + +int intercept_init(void); +pid_t intercept_run(int, char *, char * const *); +int intercept_open(void); +int intercept_attach(int, pid_t); +int intercept_detach(int, pid_t); +int intercept_read(int); +int intercept_newpolicy(int); +int intercept_assignpolicy(int, pid_t, int); +int intercept_modifypolicy(int, int, char *, char *, short); + +int intercept_register_sccb(char *, char *, + short (*)(int, pid_t, int, char *, int, char *, void *, int, + struct intercept_tlq *, void *), + void *); +void *intercept_sccb_cbarg(char *, char *); + +int intercept_register_gencb(short (*)(int, pid_t, int, char *, int, char *, void *, int, void *), void *); +int intercept_register_execcb(void (*)(int, pid_t, int, char *, char *, void *), void *); + +int intercept_register_translation(char *, char *, int, + struct intercept_translate *); +int intercept_translate(struct intercept_translate *, int, pid_t, int, void *, int); +char *intercept_translate_print(struct intercept_translate *); + +#define intercept_register_transstring(x,y,z) \ + intercept_register_translation(x, y, z, &ic_translate_string) +#define intercept_register_transfn(x,y,z) \ + intercept_register_translation(x, y, z, &ic_translate_filename) +#define intercept_register_translink(x,y,z) \ + intercept_register_translation(x, y, z, &ic_translate_linkname) + +extern struct intercept_translate ic_translate_string; +extern struct intercept_translate ic_translate_filename; +extern struct intercept_translate ic_translate_linkname; +extern struct intercept_translate ic_translate_connect; + +void intercept_freepid(pid_t); +struct intercept_pid *intercept_getpid(pid_t); +int intercept_existpids(void); + +char *intercept_get_string(int, pid_t, void *); +char *intercept_filename(int, pid_t, void *); + +#endif /* _INTERCEPT_H_ */ diff --git a/bin/systrace/openbsd-syscalls.c b/bin/systrace/openbsd-syscalls.c new file mode 100644 index 00000000000..d6bab1b8596 --- /dev/null +++ b/bin/systrace/openbsd-syscalls.c @@ -0,0 +1,510 @@ +/* $OpenBSD: openbsd-syscalls.c,v 1.1 2002/06/04 17:20:04 provos Exp $ */ +/* + * Copyright 2002 Niels Provos <provos@citi.umich.edu> + * 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 <sys/types.h> +#include <sys/param.h> + +#include <sys/syscall.h> + +#include "/sys/compat/bsdos/bsdos_syscall.h" +#include "/sys/compat/freebsd/freebsd_syscall.h" +#include "/sys/compat/netbsd/netbsd_syscall.h" +#include "/sys/compat/hpux/hpux_syscall.h" +#include "/sys/compat/ibcs2/ibcs2_syscall.h" +#include "/sys/compat/linux/linux_syscall.h" +#include "/sys/compat/osf1/osf1_syscall.h" +#include "/sys/compat/sunos/sunos_syscall.h" +#include "/sys/compat/svr4/svr4_syscall.h" +#include "/sys/compat/ultrix/ultrix_syscall.h" + +#define KTRACE +#define NFSCLIENT +#define NFSSERVER +#define SYSVSEM +#define SYSVMSG +#define SYSVSHM +#define LFS +#define NTP +#include "/sys/kern/syscalls.c" + +#include "/sys/compat/bsdos/bsdos_syscalls.c" +#include "/sys/compat/freebsd/freebsd_syscalls.c" +#include "/sys/compat/netbsd/netbsd_syscalls.c" +#include "/sys/compat/hpux/hpux_syscalls.c" +#include "/sys/compat/ibcs2/ibcs2_syscalls.c" +#include "/sys/compat/linux/linux_syscalls.c" +#include "/sys/compat/osf1/osf1_syscalls.c" +#include "/sys/compat/sunos/sunos_syscalls.c" +#include "/sys/compat/svr4/svr4_syscalls.c" +#include "/sys/compat/ultrix/ultrix_syscalls.c" +#undef KTRACE +#undef NFSCLIENT +#undef NFSSERVER +#undef SYSVSEM +#undef SYSVMSG +#undef SYSVSHM +#undef LFS +#undef NTP + +#include <sys/ioctl.h> +#include <sys/tree.h> +#include <dev/systrace.h> + +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <err.h> + +#include "intercept.h" + +/* Callback into main library */ +void intercept_child_info(pid_t, pid_t); +void intercept_syscall(int, pid_t, int, char *, int, char *, void *, int); +void intercept_syscall_result(int, pid_t, int, char *, int, char *, void *, + int, int, void *); + +struct emulation { + char *name; /* Emulation name */ + char **sysnames; /* Array of system call names */ + int nsysnames; /* Number of */ +}; + +static struct emulation emulations[] = { + { "native", syscallnames, SYS_MAXSYSCALL }, + { "hpux", hpux_syscallnames, HPUX_SYS_MAXSYSCALL }, + { "ibcs2", ibcs2_syscallnames, IBCS2_SYS_MAXSYSCALL }, + { "linux", linux_syscallnames, LINUX_SYS_MAXSYSCALL }, + { "osf1", osf1_syscallnames, OSF1_SYS_MAXSYSCALL }, + { "sunos", sunos_syscallnames, SUNOS_SYS_MAXSYSCALL }, + { "svr4", svr4_syscallnames, SVR4_SYS_MAXSYSCALL }, + { "ultrix", ultrix_syscallnames, ULTRIX_SYS_MAXSYSCALL }, + { "bsdos", bsdos_syscallnames, BSDOS_SYS_MAXSYSCALL }, + { "freebsd", freebsd_syscallnames, FREEBSD_SYS_MAXSYSCALL }, + { "netbsd", netbsd_syscallnames, NETBSD_SYS_MAXSYSCALL }, + { NULL, NULL, NULL } +}; + +struct obsd_data { + struct emulation *current; + struct emulation *commit; +}; + +int +obsd_init(void) +{ + return (0); +} + +int +obsd_attach(int fd, pid_t pid) +{ + if (ioctl(fd, STRIOCATTACH, &pid) == -1) + return (-1); + + return (0); +} + +int +obsd_detach(int fd, pid_t pid) +{ + if (ioctl(fd, STRIOCDETACH, &pid) == -1) + return (-1); + + return (0); +} + +int +obsd_open(void) +{ + char *path = "/dev/systrace"; + int fd, cfd = -1; + + fd = open(path, O_RDONLY, 0); + if (fd == -1) { + warn("open: %s", path); + return (-1); + } + + if (ioctl(fd, SYSTR_CLONE, &cfd) == -1) { + warn("ioctl(SYSTR_CLONE)"); + goto out; + } + + if (fcntl(cfd, F_SETFD, 1) == -1) + warn("fcntl(F_SETFD)"); + + out: + close (fd); + return (cfd); +} + +struct intercept_pid * +obsd_getpid(pid_t pid) +{ + struct intercept_pid *icpid; + struct obsd_data *data; + + icpid = intercept_getpid(pid); + if (icpid == NULL) + return (NULL); + if (icpid->data != NULL) + return (icpid); + + if ((icpid->data = malloc(sizeof(struct obsd_data))) == NULL) + err(1, "%s:%d: malloc", __FUNCTION__, __LINE__); + + data = icpid->data; + data->current = &emulations[0]; + data->commit = NULL; + + return (icpid); +} + +void +obsd_freepid(struct intercept_pid *ipid) +{ + if (ipid->data != NULL) + free(ipid->data); +} + +void +obsd_clonepid(struct intercept_pid *opid, struct intercept_pid *npid) +{ + if (opid->data == NULL) { + npid->data = NULL; + return; + } + + if ((npid->data = malloc(sizeof(struct obsd_data))) == NULL) + err(1, "%s:%d: malloc", __FUNCTION__, __LINE__); + memcpy(npid->data, opid->data, sizeof(struct obsd_data)); +} + +struct emulation * +obsd_find_emulation(char *name) +{ + struct emulation *tmp; + + tmp = emulations; + while (tmp->name) { + if (!strcmp(tmp->name, name)) + break; + tmp++; + } + + if (!tmp->name) + return (NULL); + + return (tmp); +} + +int +obsd_set_emulation(pid_t pidnr, char *name) +{ + struct emulation *tmp; + struct intercept_pid *pid; + struct obsd_data *data; + + if ((tmp = obsd_find_emulation(name)) == NULL) + return (-1); + + pid = intercept_getpid(pidnr); + if (pid == NULL) + return (-1); + data = pid->data; + + data->commit = tmp; + + return (0); +} + +char * +obsd_syscall_name(pid_t pidnr, int number) +{ + struct intercept_pid *pid; + struct emulation *current; + + pid = obsd_getpid(pidnr); + if (pid == NULL) + return (NULL); + current = ((struct obsd_data *)pid->data)->current; + + if (number < 0 || number >= current->nsysnames) + return (NULL); + + return (current->sysnames[number]); +} + +int +obsd_syscall_number(char *emulation, char *name) +{ + struct emulation *current; + int i; + + current = obsd_find_emulation(emulation); + if (current == NULL) + return (-1); + + for (i = 0; i < current->nsysnames; i++) + if (!strcmp(name, current->sysnames[i])) + return (i); + + return (-1); +} + +short +obsd_translate_policy(short policy) +{ + switch (policy) { + case ICPOLICY_ASK: + return (SYSTR_POLICY_ASK); + case ICPOLICY_PERMIT: + return (SYSTR_POLICY_PERMIT); + case ICPOLICY_NEVER: + default: + return (SYSTR_POLICY_NEVER); + } +} + +short +obsd_translate_flags(short flags) +{ + switch (flags) { + case ICFLAGS_RESULT: + return (SYSTR_FLAGS_RESULT); + default: + return (0); + } +} + +int +obsd_translate_errno(int errno) +{ + return (errno); +} + +int +obsd_answer(int fd, pid_t pid, short policy, int errno, short flags) +{ + struct systrace_answer ans; + + ans.stra_pid = pid; + ans.stra_policy = obsd_translate_policy(policy); + ans.stra_flags = obsd_translate_flags(flags); + ans.stra_error = obsd_translate_errno(errno); + + if (ioctl(fd, STRIOCANSWER, &ans) == -1) + return (-1); + + return (0); +} + +int +obsd_newpolicy(int fd) +{ + struct systrace_policy pol; + + pol.strp_op = SYSTR_POLICY_NEW; + pol.strp_num = -1; + pol.strp_maxents = 512; + + if (ioctl(fd, STRIOCPOLICY, &pol) == -1) + return (-1); + + return (pol.strp_num); +} + +int +obsd_assignpolicy(int fd, pid_t pid, int num) +{ + struct systrace_policy pol; + + pol.strp_op = SYSTR_POLICY_ASSIGN; + pol.strp_num = num; + pol.strp_pid = pid; + + if (ioctl(fd, STRIOCPOLICY, &pol) == -1) + return (-1); + + return (0); +} + +int +obsd_modifypolicy(int fd, int num, int code, short policy) +{ + struct systrace_policy pol; + + pol.strp_op = SYSTR_POLICY_MODIFY; + pol.strp_num = num; + pol.strp_code = code; + pol.strp_policy = obsd_translate_policy(policy); + + if (ioctl(fd, STRIOCPOLICY, &pol) == -1) + return (-1); + + return (0); +} + +int +obsd_io(int fd, pid_t pid, int op, void *addr, u_char *buf, size_t size) +{ + struct systrace_io io; + + memset(&io, 0, sizeof(io)); + io.strio_pid = pid; + io.strio_addr = buf; + io.strio_len = size; + io.strio_offs = addr; + io.strio_op = (op == INTERCEPT_READ ? SYSTR_READ : SYSTR_WRITE); + if (ioctl(fd, STRIOCIO, &io) == -1) + return (-1); + + return (0); +} + +char * +obsd_getcwd(int fd, pid_t pid, char *buf, size_t size) +{ + char *path; + + if (ioctl(fd, STRIOCGETCWD, &pid) == -1) + return (NULL); + + path = getcwd(buf, size); + + if (ioctl(fd, STRIOCRESCWD, 0) == -1) + warn("%s: ioctl", __FUNCTION__); /* XXX */ + + return (path); +} + +int +obsd_argument(int off, void *pargs, int argsize, void **pres) +{ + register_t *args = (register_t *)pargs; + + if (off >= argsize / sizeof(register_t)) + return (-1); + + *pres = (void *)args[off]; + + return (0); +} + +int +obsd_read(int fd) +{ + struct str_message msg; + struct intercept_pid *icpid; + struct obsd_data *data; + struct emulation *current; + + char name[SYSTR_EMULEN+1], *sysname; + int code; + + if (read(fd, &msg, sizeof(msg)) != sizeof(msg)) + return (-1); + + icpid = obsd_getpid(msg.msg_pid); + if (icpid == NULL) + return (-1); + data = icpid->data; + + current = data->current; + + switch(msg.msg_type) { + case SYSTR_MSG_ASK: + code = msg.msg_data.msg_ask.code; + sysname = obsd_syscall_name(msg.msg_pid, code); + + intercept_syscall(fd, msg.msg_pid, msg.msg_policy, + sysname, code, current->name, + (void *)msg.msg_data.msg_ask.args, + msg.msg_data.msg_ask.argsize); + break; + + case SYSTR_MSG_RES: + code = msg.msg_data.msg_ask.code; + sysname = obsd_syscall_name(msg.msg_pid, code); + + /* Switch emulation around at the right time */ + if (data->commit != NULL) { + current = data->current = data->commit; + data->commit = NULL; + } + + intercept_syscall_result(fd, msg.msg_pid, msg.msg_policy, + sysname, code, current->name, + (void *)msg.msg_data.msg_ask.args, + msg.msg_data.msg_ask.argsize, + msg.msg_data.msg_ask.result, + msg.msg_data.msg_ask.rval); + break; + + case SYSTR_MSG_EMUL: + memcpy(name, msg.msg_data.msg_emul.emul, SYSTR_EMULEN); + name[SYSTR_EMULEN] = '\0'; + + if (obsd_set_emulation(msg.msg_pid, name) == -1) + errx(1, "%s:%d: set_emulation(%s)", + __FUNCTION__, __LINE__, name); + + if (obsd_answer(fd, msg.msg_pid, 0, 0, 0) == -1) + err(1, "%s:%d: answer", __FUNCTION__, __LINE__); + break; + + case SYSTR_MSG_CHILD: + intercept_child_info(msg.msg_pid, + msg.msg_data.msg_child.new_pid); + break; + } + return (0); +} + +struct intercept_system intercept = { + "openbsd", + obsd_init, + obsd_open, + obsd_attach, + obsd_detach, + obsd_read, + obsd_syscall_number, + obsd_getcwd, + obsd_io, + obsd_argument, + obsd_answer, + obsd_newpolicy, + obsd_assignpolicy, + obsd_modifypolicy, + obsd_clonepid, + obsd_freepid, +}; diff --git a/bin/systrace/policy.c b/bin/systrace/policy.c new file mode 100644 index 00000000000..080a539ba4b --- /dev/null +++ b/bin/systrace/policy.c @@ -0,0 +1,442 @@ +/* $OpenBSD: policy.c,v 1.1 2002/06/04 17:20:04 provos Exp $ */ +/* + * Copyright 2002 Niels Provos <provos@citi.umich.edu> + * 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 <sys/types.h> +#include <sys/param.h> + +#include <sys/stat.h> +#include <sys/tree.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <ctype.h> +#include <err.h> + +#include "intercept.h" +#include "systrace.h" + +static int +psccompare(struct policy_syscall *a, struct policy_syscall *b) +{ + int diff; + diff = strcmp(a->emulation, b->emulation); + if (diff) + return (diff); + return (strcmp(a->name, b->name)); +} + +SPLAY_PROTOTYPE(syscalltree, policy_syscall, node, psccompare); +SPLAY_GENERATE(syscalltree, policy_syscall, node, psccompare); + +static SPLAY_HEAD(policytree, policy) policyroot; +static SPLAY_HEAD(polnrtree, policy) polnrroot; + +int +policycompare(struct policy *a, struct policy *b) +{ + return (strcmp(a->name, b->name)); +} + +int +polnrcompare(struct policy *a, struct policy *b) +{ + int diff = a->policynr - b->policynr; + + if (diff == 0) + return (0); + if (diff > 0 ) + return (1); + return (-1); +} + +SPLAY_PROTOTYPE(policytree, policy, node, policycompare); +SPLAY_GENERATE(policytree, policy, node, policycompare); + +SPLAY_PROTOTYPE(polnrtree, policy, nrnode, polnrcompare); +SPLAY_GENERATE(polnrtree, policy, nrnode, polnrcompare); + +char policydir[MAXPATHLEN]; + +void +systrace_setupdir(void) +{ + char *home; + struct stat sb; + + home = getenv("HOME"); + + if (home == NULL) + errx(1, "No HOME environment set"); + + if (strlcpy(policydir, home, sizeof(policydir)) >= sizeof(policydir)) + errx(1, "HOME too long"); + + if (strlcat(policydir, "/.systrace", sizeof(policydir)) >= sizeof(policydir)) + errx(1, "HOME too long"); + + if (stat(policydir, &sb) != -1) { + if (!(sb.st_mode & S_IFDIR)) + errx(1, "Not a directory: \"%s\"", policydir); + } else if (mkdir(policydir, 0700) == -1) + err(1, "mdkdir(%s)", policydir); +} + +int +systrace_initpolicy(char *file) +{ + SPLAY_INIT(&policyroot); + SPLAY_INIT(&polnrroot); + + systrace_setupdir(); + + if (file != NULL) + return (systrace_readpolicy(file)); + + return (0); +} + +struct policy * +systrace_findpolicy(char *name) +{ + struct policy tmp; + + tmp.name = name; + + return (SPLAY_FIND(policytree, &policyroot, &tmp)); +} + +struct policy * +systrace_findpolnr(int nr) +{ + struct policy tmp; + + tmp.policynr = nr; + + return (SPLAY_FIND(polnrtree, &polnrroot, &tmp)); +} + +int +systrace_newpolicynr(int fd, struct policy *tmp) +{ + if (tmp->policynr != -1) + return (-1); + + if ((tmp->policynr = intercept_newpolicy(fd)) == -1) { + free(tmp); + return (-1); + } + + SPLAY_INSERT(polnrtree, &polnrroot, tmp); + + return (tmp->policynr); +} + +struct policy * +systrace_newpolicy(char *emulation, char *name) +{ + struct policy *tmp; + + if ((tmp = systrace_findpolicy(name)) != NULL) + return (tmp); + + tmp = calloc(1, sizeof(struct policy)); + if (tmp == NULL) + return (NULL); + + tmp->policynr = -1; + + /* New policies requires intialization */ + if ((tmp->name = strdup(name)) == NULL) + err(1, "%s:%d: strdup", __FUNCTION__, __LINE__); + strlcpy(tmp->emulation, emulation, sizeof(tmp->emulation)); + + SPLAY_INSERT(policytree, &policyroot, tmp); + SPLAY_INIT(&tmp->pflqs); + TAILQ_INIT(&tmp->filters); + TAILQ_INIT(&tmp->prefilters); + + return (tmp); +} + +struct filterq * +systrace_policyflq(struct policy *policy, char *emulation, char *name) +{ + struct policy_syscall tmp2, *tmp; + + strlcpy(tmp2.emulation, emulation, sizeof(tmp2.emulation)); + strlcpy(tmp2.name, name, sizeof(tmp2.name)); + + tmp = SPLAY_FIND(syscalltree, &policy->pflqs, &tmp2); + if (tmp != NULL) + return (&tmp->flq); + + if ((tmp = calloc(1, sizeof(struct policy_syscall))) == NULL) + err(1, "%s:%d: out of memory", __FUNCTION__, __LINE__); + + strlcpy(tmp->emulation, emulation, sizeof(tmp->emulation)); + strlcpy(tmp->name, name, sizeof(tmp->name)); + TAILQ_INIT(&tmp->flq); + + SPLAY_INSERT(syscalltree, &policy->pflqs, tmp); + + return (&tmp->flq); +} + +int +systrace_modifypolicy(int fd, int policynr, char *name, short action) +{ + struct policy *policy; + int res; + + if ((policy = systrace_findpolnr(policynr)) == NULL) + return (-1); + + res = intercept_modifypolicy(fd, policynr, policy->emulation, + name, action); + + return (res); +} + +char * +systrace_policyfilename(char *dirname, char *name) +{ + static char file[MAXPATHLEN]; + char *p; + int i, plen; + + if (strlen(name) + strlen(dirname) + 1 >= sizeof(file)) + return (NULL); + + strlcpy(file, dirname, sizeof(file)); + i = strlen(file); + file[i++] = '/'; + plen = i; + + p = name; + while (*p) { + if (!isalnum(*p)) { + if (i != plen) + file[i++] = '_'; + } else + file[i++] = *p; + p++; + } + + file[i] = '\0'; + + return (file); +} + +int +systrace_addpolicy(char *name) +{ + char *file; + + if ((file = systrace_policyfilename(policydir, name)) == NULL) + return (-1); + /* Check if the user policy file exists */ + if (access(file, R_OK) == -1) { + file = systrace_policyfilename(POLICY_PATH, name); + if (file == NULL) + return (-1); + } + + return (systrace_readpolicy(file)); +} + +int +systrace_readpolicy(char *filename) +{ + FILE *fp; + struct policy *policy; + char line[1024], *p; + int linenumber = 0; + char *name, *emulation, *rule; + struct filter *filter, *parsed; + short action, future; + int res = -1; + + if ((fp = fopen(filename, "r")) == NULL) + return (-1); + + policy = NULL; + while (fgets(line, sizeof(line), fp)) { + linenumber++; + if ((p = strchr(line, '\n')) == NULL) { + fprintf(stderr, "%s:%d: input line too long.\n", + filename, linenumber); + goto out; + } + *p = '\0'; + + p = line; + strsep(&p, "#"); + + p = line; + p += strspn(p, " \t"); + if (strlen(p) == 0) + continue; + + if (!strncasecmp(p, "Policy: ", 8)) { + p += 8; + name = strsep(&p, ","); + if (p == NULL) + goto error; + if (strncasecmp(p, " Emulation: ", 12)) + goto error; + p += 12; + emulation = p; + + policy = systrace_newpolicy(emulation, name); + if (policy == NULL) + goto error; + continue; + } + + if (policy == NULL) + goto error; + + if (!strncasecmp(p, "detached", 8)) { + policy->flags |= POLICY_DETACHED; + policy = NULL; + continue; + } + + emulation = strsep(&p, "-"); + if (p == NULL || *p == '\0') + goto error; + + if (strcmp(emulation, policy->emulation)) + goto error; + + name = strsep(&p, ":"); + if (p == NULL || *p != ' ') + goto error; + p++; + rule = p; + + if (filter_parse_simple(rule, &action, &future) == -1) { + if (parse_filter(rule, &parsed) == -1) + goto error; + filter_free(parsed); + } + + filter = calloc(1, sizeof(struct filter)); + if (filter == NULL) + err(1, "%s:%d: calloc", __FUNCTION__, __LINE__); + + filter->rule = strdup(rule); + strlcpy(filter->name, name, sizeof(filter->name)); + strlcpy(filter->emulation,emulation,sizeof(filter->emulation)); + + TAILQ_INSERT_TAIL(&policy->prefilters, filter, policy_next); + } + res = 0; + + out: + fclose(fp); + return (res); + + error: + fprintf(stderr, "%s:%d: systax error.\n", + filename, linenumber); + goto out; +} + +int +systrace_writepolicy(struct policy *policy) +{ + FILE *fp; + int fd; + char *p; + char tmpname[MAXPATHLEN]; + char finalname[MAXPATHLEN]; + struct filter *filter; + + if ((p = systrace_policyfilename(policydir, policy->name)) == NULL) + return (-1); + strlcpy(finalname, p, sizeof(finalname)); + if ((p = systrace_policyfilename(policydir, "tmpXXXXXXXX")) == NULL) + return (-1); + strlcpy(tmpname, p, sizeof(tmpname)); + if ((fd = mkstemp(tmpname)) == -1 || + (fp = fdopen(fd, "w+")) == NULL) { + if (fd != -1) { + unlink(tmpname); + close(fd); + } + return (-1); + } + + + fprintf(fp, "Policy: %s, Emulation: %s\n", + policy->name, policy->emulation); + if (policy->flags & POLICY_DETACHED) { + fprintf(fp, "detached\n"); + } else { + TAILQ_FOREACH(filter, &policy->prefilters, policy_next) { + fprintf(fp, "\t%s-%s: %s\n", + filter->emulation, filter->name, filter->rule); + } + TAILQ_FOREACH(filter, &policy->filters, policy_next) { + fprintf(fp, "\t%s-%s: %s\n", + filter->emulation, filter->name, filter->rule); + } + } + fprintf(fp, "\n"); + fclose(fp); + + if (rename(tmpname, finalname) == -1) { + warn("rename(%s, %s)", tmpname, finalname); + return (-1); + } + + return (0); +} + +int +systrace_dumppolicy(void) +{ + struct policy *policy; + + SPLAY_FOREACH(policy, policytree, &policyroot) { + if (!(policy->flags & POLICY_CHANGED)) + continue; + + if (systrace_writepolicy(policy) == -1) + fprintf(stderr, "Failed to write policy for %s\n", + policy->name); + } + + return (0); +} diff --git a/bin/systrace/systrace-errno.h b/bin/systrace/systrace-errno.h new file mode 100644 index 00000000000..a80a1ce84ad --- /dev/null +++ b/bin/systrace/systrace-errno.h @@ -0,0 +1,120 @@ +/* $OpenBSD: systrace-errno.h,v 1.1 2002/06/04 17:20:04 provos Exp $ */ +/* + * Copyright 2002 Niels Provos <provos@citi.umich.edu> + * 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 _SYSTRACE_ERRNO_H_ +#define _SYSTRACE_ERRNO_H_ + +#define SYSTRACE_EPERM 1 +#define SYSTRACE_ENOENT 2 +#define SYSTRACE_ESRCH 3 +#define SYSTRACE_EINTR 4 +#define SYSTRACE_EIO 5 +#define SYSTRACE_ENXIO 6 +#define SYSTRACE_E2BIG 7 +#define SYSTRACE_ENOEXEC 8 +#define SYSTRACE_EBADF 9 +#define SYSTRACE_ECHILD 10 +#define SYSTRACE_EDEADLK 11 +#define SYSTRACE_ENOMEM 12 +#define SYSTRACE_EACCES 13 +#define SYSTRACE_EFAULT 14 +#define SYSTRACE_ENOTBLK 15 +#define SYSTRACE_EBUSY 16 +#define SYSTRACE_EEXIST 17 +#define SYSTRACE_EXDEV 18 +#define SYSTRACE_ENODEV 19 +#define SYSTRACE_ENOTDIR 20 +#define SYSTRACE_EISDIR 21 +#define SYSTRACE_EINVAL 22 +#define SYSTRACE_ENFILE 23 +#define SYSTRACE_EMFILE 24 +#define SYSTRACE_ENOTTY 25 +#define SYSTRACE_ETXTBSY 26 +#define SYSTRACE_EFBIG 27 +#define SYSTRACE_ENOSPC 28 +#define SYSTRACE_ESPIPE 29 +#define SYSTRACE_EROFS 30 +#define SYSTRACE_EMLINK 31 +#define SYSTRACE_EPIPE 32 +#define SYSTRACE_EDOM 33 +#define SYSTRACE_ERANGE 34 +#define SYSTRACE_EAGAIN 35 +#define SYSTRACE_EWOULDBLOCK 35 +#define SYSTRACE_EINPROGRESS 36 +#define SYSTRACE_EALREADY 37 +#define SYSTRACE_ENOTSOCK 38 +#define SYSTRACE_EDESTADDRREQ 39 +#define SYSTRACE_EMSGSIZE 40 +#define SYSTRACE_EPROTOTYPE 41 +#define SYSTRACE_ENOPROTOOPT 42 +#define SYSTRACE_EPROTONOSUPPORT 43 +#define SYSTRACE_ESOCKTNOSUPPORT 44 +#define SYSTRACE_EOPNOTSUPP 45 +#define SYSTRACE_EPFNOSUPPORT 46 +#define SYSTRACE_EAFNOSUPPORT 47 +#define SYSTRACE_EADDRINUSE 48 +#define SYSTRACE_EADDRNOTAVAIL 49 +#define SYSTRACE_ENETDOWN 50 +#define SYSTRACE_ENETUNREACH 51 +#define SYSTRACE_ENETRESET 52 +#define SYSTRACE_ECONNABORTED 53 +#define SYSTRACE_ECONNRESET 54 +#define SYSTRACE_ENOBUFS 55 +#define SYSTRACE_EISCONN 56 +#define SYSTRACE_ENOTCONN 57 +#define SYSTRACE_ESHUTDOWN 58 +#define SYSTRACE_ETOOMANYREFS 59 +#define SYSTRACE_ETIMEDOUT 60 +#define SYSTRACE_ECONNREFUSED 61 +#define SYSTRACE_ELOOP 62 +#define SYSTRACE_ENAMETOOLONG 63 +#define SYSTRACE_EHOSTDOWN 64 +#define SYSTRACE_EHOSTUNREACH 65 +#define SYSTRACE_ENOTEMPTY 66 +#define SYSTRACE_EPROCLIM 67 +#define SYSTRACE_EUSERS 68 +#define SYSTRACE_EDQUOT 69 +#define SYSTRACE_ESTALE 70 +#define SYSTRACE_EREMOTE 71 +#define SYSTRACE_EBADRPC 72 +#define SYSTRACE_ERPCMISMATCH 73 +#define SYSTRACE_EPROGUNAVAIL 74 +#define SYSTRACE_EPROGMISMATCH 75 +#define SYSTRACE_EPROCUNAVAIL 76 +#define SYSTRACE_ENOLCK 77 +#define SYSTRACE_ENOSYS 78 +#define SYSTRACE_EFTYPE 79 +#define SYSTRACE_EAUTH 80 +#define SYSTRACE_ENEEDAUTH 81 +#define SYSTRACE_EIPSEC 82 +#define SYSTRACE_ELAST 82 + +#endif diff --git a/bin/systrace/systrace-error.c b/bin/systrace/systrace-error.c new file mode 100644 index 00000000000..e022cebfbd2 --- /dev/null +++ b/bin/systrace/systrace-error.c @@ -0,0 +1,147 @@ +/* $OpenBSD: systrace-error.c,v 1.1 2002/06/04 17:20:04 provos Exp $ */ +/* + * Copyright 2002 Niels Provos <provos@citi.umich.edu> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or withou + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyrigh + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyrigh + * 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, BU + * 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 TOR + * (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 <sys/types.h> +#include <sys/wait.h> +#include <sys/tree.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> + +#include "intercept.h" +#include "systrace.h" +#include "systrace-errno.h" + +struct systrace_error { + char *name; + int errno; +} systrace_errors[] = { + { "EPERM", SYSTRACE_EPERM }, + { "ENOENT", SYSTRACE_ENOENT }, + { "ESRCH", SYSTRACE_ESRCH }, + { "EINTR", SYSTRACE_EINTR }, + { "EIO", SYSTRACE_EIO }, + { "ENXIO", SYSTRACE_ENXIO }, + { "E2BIG", SYSTRACE_E2BIG }, + { "ENOEXEC", SYSTRACE_ENOEXEC }, + { "EBADF", SYSTRACE_EBADF }, + { "ECHILD", SYSTRACE_ECHILD }, + { "EDEADLK", SYSTRACE_EDEADLK }, + { "ENOMEM", SYSTRACE_ENOMEM }, + { "EACCES", SYSTRACE_EACCES }, + { "EFAULT", SYSTRACE_EFAULT }, + { "ENOTBLK", SYSTRACE_ENOTBLK }, + { "EBUSY", SYSTRACE_EBUSY }, + { "EEXIST", SYSTRACE_EEXIST }, + { "EXDEV", SYSTRACE_EXDEV }, + { "ENODEV", SYSTRACE_ENODEV }, + { "ENOTDIR", SYSTRACE_ENOTDIR }, + { "EISDIR", SYSTRACE_EISDIR }, + { "EINVAL", SYSTRACE_EINVAL }, + { "ENFILE", SYSTRACE_ENFILE }, + { "EMFILE", SYSTRACE_EMFILE }, + { "ENOTTY", SYSTRACE_ENOTTY }, + { "ETXTBSY", SYSTRACE_ETXTBSY }, + { "EFBIG", SYSTRACE_EFBIG }, + { "ENOSPC", SYSTRACE_ENOSPC }, + { "ESPIPE", SYSTRACE_ESPIPE }, + { "EROFS", SYSTRACE_EROFS }, + { "EMLINK", SYSTRACE_EMLINK }, + { "EPIPE", SYSTRACE_EPIPE }, + { "EDOM", SYSTRACE_EDOM }, + { "ERANGE", SYSTRACE_ERANGE }, + { "EAGAIN", SYSTRACE_EAGAIN }, + { "EWOULDBLOCK", SYSTRACE_EWOULDBLOCK }, + { "EINPROGRESS", SYSTRACE_EINPROGRESS }, + { "EALREADY", SYSTRACE_EALREADY }, + { "ENOTSOCK", SYSTRACE_ENOTSOCK }, + { "EDESTADDRREQ", SYSTRACE_EDESTADDRREQ }, + { "EMSGSIZE", SYSTRACE_EMSGSIZE }, + { "EPROTOTYPE", SYSTRACE_EPROTOTYPE }, + { "ENOPROTOOPT", SYSTRACE_ENOPROTOOPT }, + { "EPROTONOSUPPORT", SYSTRACE_EPROTONOSUPPORT }, + { "ESOCKTNOSUPPORT", SYSTRACE_ESOCKTNOSUPPORT }, + { "EOPNOTSUPP", SYSTRACE_EOPNOTSUPP }, + { "EPFNOSUPPORT", SYSTRACE_EPFNOSUPPORT }, + { "EAFNOSUPPORT", SYSTRACE_EAFNOSUPPORT }, + { "EADDRINUSE", SYSTRACE_EADDRINUSE }, + { "EADDRNOTAVAIL", SYSTRACE_EADDRNOTAVAIL }, + { "ENETDOWN", SYSTRACE_ENETDOWN }, + { "ENETUNREACH", SYSTRACE_ENETUNREACH }, + { "ENETRESET", SYSTRACE_ENETRESET }, + { "ECONNABORTED", SYSTRACE_ECONNABORTED }, + { "ECONNRESET", SYSTRACE_ECONNRESET }, + { "ENOBUFS", SYSTRACE_ENOBUFS }, + { "EISCONN", SYSTRACE_EISCONN }, + { "ENOTCONN", SYSTRACE_ENOTCONN }, + { "ESHUTDOWN", SYSTRACE_ESHUTDOWN }, + { "ETOOMANYREFS", SYSTRACE_ETOOMANYREFS }, + { "ETIMEDOUT", SYSTRACE_ETIMEDOUT }, + { "ECONNREFUSED", SYSTRACE_ECONNREFUSED }, + { "ELOOP", SYSTRACE_ELOOP }, + { "ENAMETOOLONG", SYSTRACE_ENAMETOOLONG }, + { "EHOSTDOWN", SYSTRACE_EHOSTDOWN }, + { "EHOSTUNREACH", SYSTRACE_EHOSTUNREACH }, + { "ENOTEMPTY", SYSTRACE_ENOTEMPTY }, + { "EPROCLIM", SYSTRACE_EPROCLIM }, + { "EUSERS", SYSTRACE_EUSERS }, + { "EDQUOT", SYSTRACE_EDQUOT }, + { "ESTALE", SYSTRACE_ESTALE }, + { "EREMOTE", SYSTRACE_EREMOTE }, + { "EBADRPC", SYSTRACE_EBADRPC }, + { "ERPCMISMATCH", SYSTRACE_ERPCMISMATCH }, + { "EPROGUNAVAIL", SYSTRACE_EPROGUNAVAIL }, + { "EPROGMISMATCH", SYSTRACE_EPROGMISMATCH }, + { "EPROCUNAVAIL", SYSTRACE_EPROCUNAVAIL }, + { "ENOLCK", SYSTRACE_ENOLCK }, + { "ENOSYS", SYSTRACE_ENOSYS }, + { "EFTYPE", SYSTRACE_EFTYPE }, + { "EAUTH", SYSTRACE_EAUTH }, + { "ENEEDAUTH", SYSTRACE_ENEEDAUTH }, + { "EIPSEC", SYSTRACE_EIPSEC }, + { "ELAST", SYSTRACE_ELAST }, + { NULL, 0} +}; + +int +systrace_error_translate(char *name) +{ + struct systrace_error *error = systrace_errors; + + while (error->name != NULL) { + if (!strcasecmp(error->name, name)) + return (error->errno); + error++; + } + + return (-1); +} diff --git a/bin/systrace/systrace-translate.c b/bin/systrace/systrace-translate.c new file mode 100644 index 00000000000..f9682477e86 --- /dev/null +++ b/bin/systrace/systrace-translate.c @@ -0,0 +1,173 @@ +/* $OpenBSD: systrace-translate.c,v 1.1 2002/06/04 17:20:04 provos Exp $ */ +/* + * Copyright 2002 Niels Provos <provos@citi.umich.edu> + * 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 <sys/types.h> +#include <sys/wait.h> +#include <sys/tree.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <err.h> + +#include "/sys/compat/linux/linux_types.h" +#include "/sys/compat/linux/linux_fcntl.h" + +#include "intercept.h" +#include "systrace.h" + +#define FL(w,c) do { \ + if (flags & (w)) \ + *p++ = (c); \ +} while (0) + +int +print_oflags(char *buf, size_t buflen, struct intercept_translate *tl) +{ + char str[32], *p; + int flags = (int)tl->trans_addr; + + p = str; + switch (flags & O_ACCMODE) { + case O_RDONLY: + strcpy(p, "ro"); + break; + case O_WRONLY: + strcpy(p, "wo"); + break; + case O_RDWR: + strcpy(p, "rw"); + break; + default: + strcpy(p, "--"); + break; + } + + p += 2; + + FL(O_NONBLOCK, 'n'); + FL(O_APPEND, 'a'); + FL(O_CREAT, 'c'); + FL(O_TRUNC, 't'); + + *p = '\0'; + + strlcpy(buf, str, buflen); + + return (0); +} + +int +linux_print_oflags(char *buf, size_t buflen, struct intercept_translate *tl) +{ + char str[32], *p; + int flags = (int)tl->trans_addr; + + p = str; + switch (flags & LINUX_O_ACCMODE) { + case LINUX_O_RDONLY: + strcpy(p, "ro"); + break; + case LINUX_O_WRONLY: + strcpy(p, "wo"); + break; + case LINUX_O_RDWR: + strcpy(p, "rw"); + break; + default: + strcpy(p, "--"); + break; + } + + p += 2; + + FL(LINUX_O_APPEND, 'a'); + FL(LINUX_O_CREAT, 'c'); + FL(LINUX_O_TRUNC, 't'); + + *p = '\0'; + + strlcpy(buf, str, buflen); + + return (0); +} + +int +print_modeflags(char *buf, size_t buflen, struct intercept_translate *tl) +{ + int mode = (int)tl->trans_addr; + + mode &= 00007777; + snprintf(buf, buflen, "%o", mode); + + return (0); +} + +int +print_number(char *buf, size_t buflen, struct intercept_translate *tl) +{ + int number = (int)tl->trans_addr; + + snprintf(buf, buflen, "%d", number); + + return (0); +} + +struct intercept_translate oflags = { + "oflags", + NULL, print_oflags, +}; + +struct intercept_translate linux_oflags = { + "oflags", + NULL, linux_print_oflags, +}; + +struct intercept_translate modeflags = { + "mode", + NULL, print_modeflags, +}; + +struct intercept_translate uidt = { + "uid", + NULL, print_number, +}; + +struct intercept_translate gidt = { + "gid", + NULL, print_number, +}; + +struct intercept_translate fdt = { + "fd", + NULL, print_number, +}; diff --git a/bin/systrace/systrace.c b/bin/systrace/systrace.c new file mode 100644 index 00000000000..e6f929440e6 --- /dev/null +++ b/bin/systrace/systrace.c @@ -0,0 +1,450 @@ +/* $OpenBSD: systrace.c,v 1.1 2002/06/04 17:20:04 provos Exp $ */ +/* + * Copyright 2002 Niels Provos <provos@citi.umich.edu> + * 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 <sys/types.h> +#include <sys/wait.h> +#include <sys/tree.h> +#include <sys/socket.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <signal.h> +#include <err.h> +#include <errno.h> + +#include "intercept.h" +#include "systrace.h" + +pid_t pid; +int fd; +int connected = 0; /* Connected to GUI */ +int inherit = 0; /* Inherit policy to childs */ +int automatic = 0; /* Do not run interactively */ + +short +trans_cb(int fd, pid_t pid, int policynr, + char *name, int code, char *emulation, + void *args, int argsize, struct intercept_tlq *tls, void *cbarg) +{ + short action, future; + struct policy *policy; + struct intercept_translate *tl; + struct intercept_pid *ipid; + struct filterq *pflq = NULL; + char output[1024], *p, *line; + int size; + + action = ICPOLICY_PERMIT; + + if (policynr == -1) + goto out; + + if ((policy = systrace_findpolnr(policynr)) == NULL) + errx(1, "%s:%d: find %d\n", __FUNCTION__, __LINE__, + policynr); + + if ((pflq = systrace_policyflq(policy, emulation, name)) == NULL) + errx(1, "%s:%d: no filter queue\n", __FUNCTION__, __LINE__); + + ipid = intercept_getpid(pid); + ipid->uflags = 0; + snprintf(output, sizeof(output), + "%s, pid: %d(%d), policy: %s, filters: %d, syscall: %s-%s(%d)", + ipid->name != NULL ? ipid->name : policy->name, pid, policynr, + policy->name, policy->nfilters, emulation, name, code); + p = output + strlen(output); + size = sizeof(output) - strlen(output); + + TAILQ_FOREACH(tl, tls, next) { + if (!tl->trans_valid) + break; + line = intercept_translate_print(tl); + if (line != NULL) { + snprintf(p, size, ", %s: %s", tl->name, line); + p = output + strlen(output); + size = sizeof(output) - strlen(output); + } + } + + action = filter_evaluate(tls, pflq, &ipid->uflags); + if (action != ICPOLICY_ASK) + goto out; + if (policy->flags & POLICY_UNSUPERVISED) { + action = ICPOLICY_NEVER; + goto out; + } + + action = filter_ask(tls, pflq, policynr, emulation, name, + output, &future, &ipid->uflags); + if (future != ICPOLICY_ASK) + systrace_modifypolicy(fd, policynr, name, future); + + if (policy->flags & POLICY_DETACHED) { + if (intercept_detach(fd, pid) == -1) + err(1, "intercept_detach"); + } else if (action == ICPOLICY_KILL) { + kill(pid, SIGKILL); + action = ICPOLICY_NEVER; + } + out: + return (action); +} + +short +gen_cb(int fd, pid_t pid, int policynr, char *name, int code, + char *emulation, void *args, int argsize, void *cbarg) +{ + char output[1024]; + struct policy *policy; + struct intercept_pid *ipid; + short action = ICPOLICY_PERMIT; + short future; + + if (policynr == -1) + goto out; + + if ((policy = systrace_findpolnr(policynr)) == NULL) + errx(1, "%s:%d: find %d\n", __FUNCTION__, __LINE__, + policynr); + + if (policy->flags & POLICY_UNSUPERVISED) { + action = ICPOLICY_NEVER; + goto out; + } + + ipid = intercept_getpid(pid); + ipid->uflags = 0; + snprintf(output, sizeof(output), + "%s, pid: %d(%d), policy: %s, filters: %d, syscall: %s-%s(%d), args: %d", + ipid->name != NULL ? ipid->name : policy->name, pid, policynr, + policy->name, policy->nfilters, emulation, name, code, argsize); + + action = filter_ask(NULL, NULL, policynr, emulation, name, + output, &future, &ipid->uflags); + if (future != ICPOLICY_ASK) + systrace_modifypolicy(fd, policynr, name, future); + + if (policy->flags & POLICY_DETACHED) { + if (intercept_detach(fd, pid) == -1) + err(1, "intercept_detach"); + } else if (action == ICPOLICY_KILL) { + kill(pid, SIGKILL); + action = ICPOLICY_NEVER; + } + out: + return (action); +} + +void +execres_cb(int fd, pid_t pid, int policynr, char *emulation, char *name, void *arg) +{ + struct policy *policy; + + if (policynr != -1) { + struct intercept_pid *ipid; + + if (inherit) + return; + + ipid = intercept_getpid(pid); + if (ipid->uflags & PROCESS_INHERIT_POLICY) + return; + } + if ((policy = systrace_newpolicy(emulation, name)) == NULL) + goto error; + + /* See if this policies runs without interactive feedback */ + if (automatic) + policy->flags |= POLICY_UNSUPERVISED; + + policynr = policy->policynr; + + /* Try to find existing policy in file system */ + if (policynr == -1 && TAILQ_FIRST(&policy->prefilters) == NULL) + systrace_addpolicy(name); + + if (policy->flags & POLICY_DETACHED) { + if (intercept_detach(fd, pid) == -1) + err(1, "intercept_detach"); + return; + } + + if (policynr == -1) { + policynr = systrace_newpolicynr(fd, policy); + if (policynr == -1) + goto error; + } + + if (intercept_assignpolicy(fd, pid, policynr) == -1) + goto error; + + if (TAILQ_FIRST(&policy->prefilters) != NULL) + filter_prepolicy(fd, policy); + + return; + + error: + kill(pid, SIGKILL); + fprintf(stderr, "Terminating %d: %s\n", pid, name); +} + +void +child_handler(int sig) +{ + int s = errno, status; + + if (signal(SIGCHLD, child_handler) == SIG_ERR) { + close(fd); + } + + while (wait4(-1, &status, WNOHANG, NULL) > 0) + ; + + errno = s; +} + +#define X(x) if ((x) == -1) \ + err(1, "%s:%d: intercept failed", __FUNCTION__, __LINE__) + +void +systrace_initcb(void) +{ + X(intercept_init()); + + X(intercept_register_gencb(gen_cb, NULL)); + X(intercept_register_sccb("native", "open", trans_cb, NULL)); + X(intercept_register_transfn("native", "open", 0)); + X(intercept_register_translation("native", "open", 1, &oflags)); + + X(intercept_register_sccb("native", "connect", trans_cb, NULL)); + X(intercept_register_translation("native", "connect", 1, + &ic_translate_connect)); + X(intercept_register_sccb("native", "sendto", trans_cb, NULL)); + X(intercept_register_translation("native", "sendto", 4, + &ic_translate_connect)); + X(intercept_register_sccb("native", "bind", trans_cb, NULL)); + X(intercept_register_translation("native", "bind", 1, + &ic_translate_connect)); + X(intercept_register_sccb("native", "execve", trans_cb, NULL)); + X(intercept_register_transfn("native", "execve", 0)); + X(intercept_register_sccb("native", "stat", trans_cb, NULL)); + X(intercept_register_transfn("native", "stat", 0)); + X(intercept_register_sccb("native", "lstat", trans_cb, NULL)); + X(intercept_register_translink("native", "lstat", 0)); + X(intercept_register_sccb("native", "unlink", trans_cb, NULL)); + X(intercept_register_transfn("native", "unlink", 0)); + X(intercept_register_sccb("native", "chown", trans_cb, NULL)); + X(intercept_register_transfn("native", "chown", 0)); + X(intercept_register_translation("native", "chown", 1, &uidt)); + X(intercept_register_translation("native", "chown", 2, &gidt)); + X(intercept_register_sccb("native", "fchown", trans_cb, NULL)); + X(intercept_register_translation("native", "fchown", 0, &fdt)); + X(intercept_register_translation("native", "fchown", 1, &uidt)); + X(intercept_register_translation("native", "fchown", 2, &gidt)); + X(intercept_register_sccb("native", "chmod", trans_cb, NULL)); + X(intercept_register_transfn("native", "chmod", 0)); + X(intercept_register_translation("native", "chmod", 1, &modeflags)); + X(intercept_register_sccb("native", "readlink", trans_cb, NULL)); + X(intercept_register_translink("native", "readlink", 0)); + X(intercept_register_sccb("native", "chdir", trans_cb, NULL)); + X(intercept_register_transfn("native", "chdir", 0)); + X(intercept_register_sccb("native", "access", trans_cb, NULL)); + X(intercept_register_transfn("native", "access", 0)); + X(intercept_register_sccb("native", "mkdir", trans_cb, NULL)); + X(intercept_register_transfn("native", "mkdir", 0)); + X(intercept_register_sccb("native", "rmdir", trans_cb, NULL)); + X(intercept_register_transfn("native", "rmdir", 0)); + X(intercept_register_sccb("native", "rename", trans_cb, NULL)); + X(intercept_register_transfn("native", "rename", 0)); + X(intercept_register_transfn("native", "rename", 1)); + X(intercept_register_sccb("native", "symlink", trans_cb, NULL)); + X(intercept_register_transstring("native", "symlink", 0)); + X(intercept_register_translink("native", "symlink", 1)); + + X(intercept_register_sccb("linux", "open", trans_cb, NULL)); + X(intercept_register_translink("linux", "open", 0)); + X(intercept_register_translation("linux", "open", 1, &linux_oflags)); + X(intercept_register_sccb("linux", "stat", trans_cb, NULL)); + X(intercept_register_translink("linux", "stat", 0)); + X(intercept_register_sccb("linux", "lstat", trans_cb, NULL)); + X(intercept_register_translink("linux", "lstat", 0)); + X(intercept_register_sccb("linux", "execve", trans_cb, NULL)); + X(intercept_register_translink("linux", "execve", 0)); + X(intercept_register_sccb("linux", "access", trans_cb, NULL)); + X(intercept_register_translink("linux", "access", 0)); + X(intercept_register_sccb("linux", "symlink", trans_cb, NULL)); + X(intercept_register_transstring("linux", "symlink", 0)); + X(intercept_register_translink("linux", "symlink", 1)); + X(intercept_register_sccb("linux", "readlink", trans_cb, NULL)); + X(intercept_register_translink("linux", "readlink", 0)); + X(intercept_register_sccb("linux", "rename", trans_cb, NULL)); + X(intercept_register_translink("linux", "rename", 0)); + X(intercept_register_translink("linux", "rename", 1)); + X(intercept_register_sccb("linux", "mkdir", trans_cb, NULL)); + X(intercept_register_translink("linux", "mkdir", 0)); + X(intercept_register_sccb("linux", "rmdir", trans_cb, NULL)); + X(intercept_register_translink("linux", "rmdir", 0)); + X(intercept_register_sccb("linux", "unlink", trans_cb, NULL)); + X(intercept_register_translink("linux", "unlink", 0)); + X(intercept_register_sccb("linux", "chmod", trans_cb, NULL)); + X(intercept_register_translink("linux", "chmod", 0)); + X(intercept_register_translation("linux", "chmod", 1, &modeflags)); + + X(intercept_register_execcb(execres_cb, NULL)); +} + +void +usage(void) +{ + fprintf(stderr, "Usage: systrace [-ait] [-f policy] command ...\n"); + exit(1); +} + +int +requestor_start(char *path) +{ + char *argv[2]; + int pair[2]; + pid_t pid; + + argv[0] = path; + argv[1] = NULL; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) + err(1, "socketpair"); + + pid = fork(); + if (pid == -1) + err(1, "fork"); + if (pid == 0) { + close(pair[0]); + if (dup2(pair[1], fileno(stdin)) == -1) + err(1, "dup2"); + if (dup2(pair[1], fileno(stdout)) == -1) + err(1, "dup2"); + setlinebuf(stdout); + + close(pair[1]); + + execvp(path, argv); + + err(1, "execvp"); + } + + close(pair[1]); + if (dup2(pair[0], fileno(stdin)) == -1) + err(1, "dup2"); + + if (dup2(pair[0], fileno(stdout)) == -1) + err(1, "dup2"); + + close(pair[0]); + + setlinebuf(stdout); + + connected = 1; + + return (0); +} + +int +main(int argc, char **argv) +{ + int i, c; + char **args; + char *filename = NULL; + int usex11 = 1; + + while ((c = getopt(argc, argv, "aitf:")) != -1) { + switch (c) { + case 'a': + automatic = 1; + break; + case 'i': + inherit = 1; + break; + case 'f': + filename = optarg; + break; + case 't': + usex11 = 0; + break; + default: + usage(); + break; + } + } + argc -= optind; + argv += optind; + + if (argc == 0) + usage(); + + if ((args = malloc((argc + 1) * sizeof(char *))) == NULL) + err(1, "malloc"); + + for (i = 0; i < argc; i++) + args[i] = argv[i]; + args[i] = NULL; + + if (signal(SIGCHLD, child_handler) == SIG_ERR) + err(1, "signal"); + + /* Local initalization */ + systrace_initpolicy(filename); + systrace_initcb(); + + X(fd = intercept_open()); + + pid = intercept_run(fd, args[0], args); + if (pid == -1) + err(1, "fork"); + + if (intercept_attach(fd, pid) == -1) + err(1, "attach"); + + if (usex11 && !automatic) + requestor_start("./notification/src/notification"); + + if (kill(pid, SIGCONT) == -1) + err(1, "kill"); + + while (intercept_read(fd) != -1) + if (!intercept_existpids()) + break; + + systrace_dumppolicy(); + + close(fd); + + exit(0); +} diff --git a/bin/systrace/systrace.h b/bin/systrace/systrace.h new file mode 100644 index 00000000000..b584925afde --- /dev/null +++ b/bin/systrace/systrace.h @@ -0,0 +1,132 @@ +/* $OpenBSD: systrace.h,v 1.1 2002/06/04 17:20:04 provos Exp $ */ +/* + * Copyright 2002 Niels Provos <provos@citi.umich.edu> + * 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 _SYSTRACE_H_ +#define _SYSTRACE_H_ +#include <sys/queue.h> + +enum logicop { LOGIC_AND, LOGIC_OR, LOGIC_NOT, LOGIC_SINGLE }; + +struct logic { + enum logicop op; + struct logic *left; + struct logic *right; + char *type; + int typeoff; + void *filterdata; + size_t filterlen; + int (*filter_match)(struct intercept_translate *, struct logic *); +}; + +struct filter { + TAILQ_ENTRY(filter) next; + TAILQ_ENTRY(filter) policy_next; + + char *rule; + char name[32]; + char emulation[16]; + struct logic *logicroot; + short match_action; + int match_error; + int match_flags; +}; + +TAILQ_HEAD(filterq, filter); + +struct policy_syscall { + SPLAY_ENTRY(policy_syscall) node; + + char name[64]; + char emulation[16]; + + struct filterq flq; +}; + +struct policy { + SPLAY_ENTRY(policy) node; + SPLAY_ENTRY(policy) nrnode; + + char *name; + char emulation[16]; + + SPLAY_HEAD(syscalltree, policy_syscall) pflqs; + + int policynr; + int flags; + + struct filterq filters; + int nfilters; + struct filterq prefilters; +}; + +#define POLICY_PATH "/etc/systrace" + +#define POLICY_UNSUPERVISED 0x01 /* Auto-Pilot */ +#define POLICY_DETACHED 0x02 /* Ignore this program */ +#define POLICY_CHANGED 0x04 + +#define PROCESS_INHERIT_POLICY 0x01 /* Process inherits policy */ + +int systrace_initpolicy(char *); +struct policy *systrace_newpolicy(char *, char *); +int systrace_newpolicynr(int, struct policy *); +int systrace_modifypolicy(int, int, char *, short); +struct policy *systrace_findpolicy(char *); +struct policy *systrace_findpolnr(int); +int systrace_dumppolicy(void); +int systrace_readpolicy(char *); +int systrace_addpolicy(char *); +struct filterq *systrace_policyflq(struct policy *, char *, char *); + +int systrace_error_translate(char *); + +short filter_evaluate(struct intercept_tlq *, struct filterq *, int *); +short filter_ask(struct intercept_tlq *, struct filterq *, int, char *, + char *, char *, short *, int *); +void filter_free(struct filter *); + +int filter_parse_simple(char *, short *, short *); +int filter_parse(char *, struct filter **); +int filter_prepolicy(int, struct policy *); +char *filter_expand(char *data); + +int parse_filter(char *, struct filter **); + +char *strrpl(char *, size_t, char *, char *); + +extern struct intercept_translate oflags; +extern struct intercept_translate modeflags; +extern struct intercept_translate fdt; +extern struct intercept_translate uidt; +extern struct intercept_translate gidt; + +extern struct intercept_translate linux_oflags; +#endif /* _SYSTRACE_H_ */ diff --git a/bin/systrace/util.c b/bin/systrace/util.c new file mode 100644 index 00000000000..a81e882b667 --- /dev/null +++ b/bin/systrace/util.c @@ -0,0 +1,208 @@ +/* $OpenBSD: util.c,v 1.1 2002/06/04 17:20:04 provos Exp $ */ +/* + * Copyright 2002 Niels Provos <provos@citi.umich.edu> + * 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 <sys/types.h> +#include <string.h> +#include <ctype.h> + +char * +strrpl(char *str, size_t size, char *match, char *value) +{ + char *p, *e; + int len, rlen; + + p = str; + e = p + strlen(p); + len = strlen(match); + + /* Try to match against the variable */ + while ((p = strchr(p, match[0])) != NULL) { + if (!strncmp(p, match, len) && !isalnum(p[len])) + break; + p += len; + + if (p >= e) + return (NULL); + + } + + if (p == NULL) + return (NULL); + + rlen = strlen(value); + + if (strlen(str) - len + rlen > size) + return (NULL); + + memmove(p + rlen, p + len, strlen(p + len) + 1); + memcpy(p, value, rlen); + + return (p); +} + +/* ISABSPATH() means path is fully and completely specified, + * ISROOTEDPATH() means a .. as the first component is a no-op, + * ISRELPATH() means $PWD can be tacked on to get an absolute path. + * + * OS Path ISABSPATH ISROOTEDPATH ISRELPATH + * unix /foo yes yes no + * unix foo no no yes + * unix ../foo no no yes + * os2+cyg a:/foo yes yes no + * os2+cyg a:foo no no no + * os2+cyg /foo no yes no + * os2+cyg foo no no yes + * os2+cyg ../foo no no yes + * cyg //foo yes yes no + */ +#ifdef OS2 +# define PATHSEP ';' +# define DIRSEP '/' /* even though \ is native */ +# define DIRSEPSTR "\\" +# define ISDIRSEP(c) ((c) == '\\' || (c) == '/') +# define ISABSPATH(s) (((s)[0] && (s)[1] == ':' && ISDIRSEP((s)[2]))) +# define ISROOTEDPATH(s) (ISDIRSEP((s)[0]) || ISABSPATH(s)) +# define ISRELPATH(s) (!(s)[0] || ((s)[1] != ':' && !ISDIRSEP((s)[0]))) +# define FILECHCONV(c) (isascii(c) && isupper(c) ? tolower(c) : c) +# define FILECMP(s1, s2) stricmp(s1, s2) +# define FILENCMP(s1, s2, n) strnicmp(s1, s2, n) +extern char *ksh_strchr_dirsep(const char *path); +extern char *ksh_strrchr_dirsep(const char *path); +# define chdir _chdir2 +# define getcwd _getcwd2 +#else +# define PATHSEP ':' +# define DIRSEP '/' +# define DIRSEPSTR "/" +# define ISDIRSEP(c) ((c) == '/') +#ifdef __CYGWIN__ +# define ISABSPATH(s) \ + (((s)[0] && (s)[1] == ':' && ISDIRSEP((s)[2])) || ISDIRSEP((s)[0])) +# define ISRELPATH(s) (!(s)[0] || ((s)[1] != ':' && !ISDIRSEP((s)[0]))) +#else /* __CYGWIN__ */ +# define ISABSPATH(s) ISDIRSEP((s)[0]) +# define ISRELPATH(s) (!ISABSPATH(s)) +#endif /* __CYGWIN__ */ +# define ISROOTEDPATH(s) ISABSPATH(s) +# define FILECHCONV(c) c +# define FILECMP(s1, s2) strcmp(s1, s2) +# define FILENCMP(s1, s2, n) strncmp(s1, s2, n) +# define ksh_strchr_dirsep(p) strchr(p, DIRSEP) +# define ksh_strrchr_dirsep(p) strrchr(p, DIRSEP) +#endif + +/* simplify_path is from pdksh */ + +/* + * Simplify pathnames containing "." and ".." entries. + * ie, simplify_path("/a/b/c/./../d/..") returns "/a/b" + */ +void +simplify_path(path) + char *path; +{ + char *cur; + char *t; + int isrooted; + char *very_start = path; + char *start; + + if (!*path) + return; + + if ((isrooted = ISROOTEDPATH(path))) + very_start++; +#if defined (OS2) || defined (__CYGWIN__) + if (path[0] && path[1] == ':') /* skip a: */ + very_start += 2; +#endif /* OS2 || __CYGWIN__ */ + + /* Before After + * /foo/ /foo + * /foo/../../bar /bar + * /foo/./blah/.. /foo + * . . + * .. .. + * ./foo foo + * foo/../../../bar ../../bar + * OS2 and CYGWIN: + * a:/foo/../.. a:/ + * a:. a: + * a:.. a:.. + * a:foo/../../blah a:../blah + */ + +#ifdef __CYGWIN__ + /* preserve leading double-slash on pathnames (for UNC paths) */ + if (path[0] && ISDIRSEP(path[0]) && path[1] && ISDIRSEP(path[1])) + very_start++; +#endif /* __CYGWIN__ */ + + for (cur = t = start = very_start; ; ) { + /* treat multiple '/'s as one '/' */ + while (ISDIRSEP(*t)) + t++; + + if (*t == '\0') { + if (cur == path) + /* convert empty path to dot */ + *cur++ = '.'; + *cur = '\0'; + break; + } + + if (t[0] == '.') { + if (!t[1] || ISDIRSEP(t[1])) { + t += 1; + continue; + } else if (t[1] == '.' && (!t[2] || ISDIRSEP(t[2]))) { + if (!isrooted && cur == start) { + if (cur != very_start) + *cur++ = DIRSEP; + *cur++ = '.'; + *cur++ = '.'; + start = cur; + } else if (cur != start) + while (--cur > start && !ISDIRSEP(*cur)) + ; + t += 2; + continue; + } + } + + if (cur != very_start) + *cur++ = DIRSEP; + + /* find/copy next component of pathname */ + while (*t && !ISDIRSEP(*t)) + *cur++ = *t++; + } +} |