summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNiels Provos <provos@cvs.openbsd.org>2002-06-04 17:20:05 +0000
committerNiels Provos <provos@cvs.openbsd.org>2002-06-04 17:20:05 +0000
commit46a8c64495ad8a9d221a6c0fa12e7d77b6e2420b (patch)
tree9c7200a8a35c2bee11d017386d12aab0d8bca720
parent72975dabb915c98862a98d34d585bdc781702b4d (diff)
initial import of systrace. don't touch this, more stuff coming in a while
-rw-r--r--bin/systrace/Makefile17
-rw-r--r--bin/systrace/filter.c468
-rw-r--r--bin/systrace/intercept-translate.c273
-rw-r--r--bin/systrace/intercept.c525
-rw-r--r--bin/systrace/intercept.h144
-rw-r--r--bin/systrace/openbsd-syscalls.c510
-rw-r--r--bin/systrace/policy.c442
-rw-r--r--bin/systrace/systrace-errno.h120
-rw-r--r--bin/systrace/systrace-error.c147
-rw-r--r--bin/systrace/systrace-translate.c173
-rw-r--r--bin/systrace/systrace.c450
-rw-r--r--bin/systrace/systrace.h132
-rw-r--r--bin/systrace/util.c208
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++;
+ }
+}