Index: policy.c
===================================================================
RCS file: /cvsroot/src/bin/systrace/policy.c,v
retrieving revision 1.15
diff -u -p -r1.15 policy.c
--- policy.c	2003/08/01 05:42:48	1.15
+++ policy.c	2003/09/29 11:55:52
@@ -50,6 +50,8 @@ __RCSID("$NetBSD: policy.c,v 1.15 2003/0
 #include "intercept.h"
 #include "systrace.h"
 
+extern int erratic;
+
 static int psccompare(struct policy_syscall *, struct policy_syscall *);
 static int policycompare(struct policy *, struct policy *);
 static int polnrcompare(struct policy *, struct policy *);
@@ -579,8 +581,14 @@ systrace_policyprocess(struct policy *po
 			snprintf(line, sizeof(line), "true then %s", rule);
 			rule = line;
 		}
-	} else if (filter_parse_simple(rule, &action, &future) == 0)
-		resolved = 1;
+	} else if (filter_parse_simple(rule, &action, &future) == 0) {
+		if (erratic) {
+			/* Need to make a real policy out of it */
+			snprintf(line, sizeof(line), "true then %s", rule);
+			rule = line;
+		} else
+			resolved = 1;
+	}
 
 	/* For now, everything that does not seem to be a valid syscall
 	 * does not get fast kernel policies even though the aliasing
@@ -690,6 +698,7 @@ systrace_writepolicy(struct policy *poli
 	char tmpname[2*MAXPATHLEN];
 	char finalname[2*MAXPATHLEN];
 	struct filter *filter;
+	short action, future;
 
 	if ((p = systrace_policyfilename(policydir, policy->name)) == NULL)
 		return (-1);
@@ -717,8 +726,15 @@ systrace_writepolicy(struct policy *poli
 			    filter->emulation, filter->name, filter->rule);
 		}
 		TAILQ_FOREACH(filter, &policy->filters, policy_next) {
+			if (erratic &&
+			    !strncmp(filter->rule, "true then ", 10)) {
+				p = filter->rule + 10;
+				if (filter_parse_simple(p, &action, &future))
+					p = filter->rule;
+			} else
+				p = filter->rule;
 			fprintf(fp, "\t%s-%s: %s\n",
-			    filter->emulation, filter->name, filter->rule);
+			    filter->emulation, filter->name, p);
 		}
 	}
 	fprintf(fp, "\n");
Index: systrace.1
===================================================================
RCS file: /cvsroot/src/bin/systrace/systrace.1,v
retrieving revision 1.25
diff -u -p -r1.25 systrace.1
--- systrace.1	2003/08/20 01:28:44	1.25
+++ systrace.1	2003/09/29 11:55:52
@@ -39,12 +39,13 @@
 .Nd generate and enforce system call policies
 .Sh SYNOPSIS
 .Nm systrace
-.Op Fl AaitUu
+.Op Fl AaiktUu
 .Op Fl c Ar uid:gid
 .Op Fl d Ar policydir
 .Op Fl f Ar file
 .Op Fl g Ar gui
 .Op Fl p Ar pid
+.Op Fl r Ar chance Ns Op Ar -seed
 .Ar command ...
 .Sh DESCRIPTION
 The
@@ -108,6 +109,11 @@ knows about.
 Specifies an alternative location for the notification user interface.
 .It Fl i
 Inherits the policy - child processes inherit policy of the parent binary.
+.It Fl k
+When used in conjunction with
+.Fl a ,
+operations not covered by policy result in the process being killed
+after the operation has been logged.
 .It Fl p Ar pid
 Specifies the pid of a process that
 .Nm
@@ -115,6 +121,15 @@ should attach to.
 The full path name of the corresponding binary has to be specified
 as
 .Ar command .
+.It Fl r Ar chance Ns Op Ar -seed
+Randomly cause operations to fail with a probability of one in
+.Ar chance .
+.Pp
+.Ar seed
+specifies the initial conditions for the random number generator used.
+By specifying the same
+.Ar seed
+for multiple invocations, test cases may be reproduced.
 .It Fl t
 Uses text mode to ask for interactive policy generation.
 .It Fl U
Index: systrace.c
===================================================================
RCS file: /cvsroot/src/bin/systrace/systrace.c,v
retrieving revision 1.23
diff -u -p -r1.23 systrace.c
--- systrace.c	2003/08/25 09:12:46	1.23
+++ systrace.c	2003/09/29 11:55:52
@@ -35,6 +35,7 @@
 #include <sys/wait.h>
 #include <sys/tree.h>
 #include <sys/socket.h>
+#include <sys/time.h>
 #include <limits.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -61,6 +62,8 @@ int allow = 0;			/* Allow all and genera
 int userpolicy = 1;		/* Permit user defined policies */
 int noalias = 0;		/* Do not do system call aliasing */
 int iamroot = 0;		/* Set if we are running as root */
+int auto_kill = 0;		/* Kill process when policy is violated */
+int erratic = 0;		/* Probability of artificial syscall denial */
 char cwd[MAXPATHLEN];		/* Current working directory */
 char home[MAXPATHLEN];		/* Home directory of user */
 char username[LOGIN_NAME_MAX];	/* Username: predicate match and expansion */
@@ -68,6 +71,8 @@ char username[LOGIN_NAME_MAX];	/* Userna
 static void child_handler(int);
 static void usage(void);
 static int requestor_start(char *);
+static int erratic_failure(short *);
+static void seed_erratic(char *);
 
 void
 systrace_parameters(void)
@@ -156,12 +161,14 @@ trans_cb(int fd, pid_t pid, int policynr
 	const char *binname = NULL;
 	char output[_POSIX2_LINE_MAX];
 	pid_t ppid;
-	int dolog = 0;
 
 	action = ICPOLICY_PERMIT;
 
 	if (policynr == -1)
-		goto out;
+		return (action);
+
+	if (erratic && erratic_failure(&action))
+		return (action);
 
 	if ((policy = systrace_findpolnr(policynr)) == NULL)
 		errx(1, "%s:%d: find %d", __func__, __LINE__,
@@ -218,8 +225,10 @@ trans_cb(int fd, pid_t pid, int policynr
 
 	if (policy->flags & POLICY_UNSUPERVISED) {
 		action = ICPOLICY_NEVER;
-		dolog = 1;
-		goto out;
+		ipid->uflags |= SYSCALL_LOG;
+		if (auto_kill)
+			action = ICPOLICY_KILL;
+		goto done;
 	}
 
 	action = filter_ask(fd, tls, pflq, policynr, emulation, name,
@@ -231,22 +240,20 @@ trans_cb(int fd, pid_t pid, int policynr
 		if (intercept_detach(fd, pid) == -1)
 			err(1, "intercept_detach");
 		return (action);
-	} else if (action == ICPOLICY_KILL) {
-		kill(pid, SIGKILL);
-		return (ICPOLICY_NEVER);
 	}
 
  done:
 	/* Log the result if requested */
 	if (ipid->uflags & SYSCALL_LOG)
-		dolog = 1;
-
- out:
-	if (dolog)
 		syslog(LOG_WARNING, "%s user: %s, prog: %s",
-		    action < ICPOLICY_NEVER ? "permit" : "deny",
+		    action < ICPOLICY_KILL ? "permit" : "deny",
 		    ipid->username, output);
 
+	if (action == ICPOLICY_KILL) {
+		kill(pid, SIGKILL);
+		return (ICPOLICY_NEVER);
+	}
+
 	/* Argument replacement in intercept might still fail */
 
 	return (action);
@@ -262,11 +269,14 @@ gen_cb(int fd, pid_t pid, int policynr, 
 	struct filterq *pflq = NULL;
 	short action = ICPOLICY_PERMIT;
 	short future;
-	int len, off, dolog = 0;
+	int len, off;
 
 	if (policynr == -1)
-		goto out;
+		return (action);
 
+	if (erratic && erratic_failure(&action))
+		return (action);
+
 	if ((policy = systrace_findpolnr(policynr)) == NULL)
 		errx(1, "%s:%d: find %d", __func__, __LINE__,
 		    policynr);
@@ -290,16 +300,15 @@ gen_cb(int fd, pid_t pid, int policynr, 
 
 	action = filter_evaluate(NULL, pflq, ipid);
 
-	if (ipid->uflags & SYSCALL_LOG)
-		dolog = 1;
-	
 	if (action != ICPOLICY_ASK)
-		goto out;
+		goto done;
 
 	if (policy->flags & POLICY_UNSUPERVISED) {
 		action = ICPOLICY_NEVER;
-		dolog = 1;
-		goto out;
+		ipid->uflags |= SYSCALL_LOG;
+		if (auto_kill)
+			action = ICPOLICY_KILL;
+		goto done;
 	}
 
 	action = filter_ask(fd, NULL, pflq, policynr, emulation, name,
@@ -310,16 +319,19 @@ gen_cb(int fd, pid_t pid, int policynr, 
 	if (policy->flags & POLICY_DETACHED) {
 		if (intercept_detach(fd, pid) == -1)
 			err(1, "intercept_detach");
-	} else if (action == ICPOLICY_KILL) {
-		kill(pid, SIGKILL);
-		return (ICPOLICY_NEVER);
 	}
- out:
-	if (dolog)
+
+ done:
+	if (ipid->uflags & SYSCALL_LOG)
 		syslog(LOG_WARNING, "%s user: %s, prog: %s",
-		    action < ICPOLICY_NEVER ? "permit" : "deny",
+		    action < ICPOLICY_KILL ? "permit" : "deny",
 		    ipid->username, output);
 
+	if (action == ICPOLICY_KILL) {
+		kill(pid, SIGKILL);
+		return (ICPOLICY_NEVER);
+	}
+
 	return (action);
 }
 
@@ -394,6 +406,18 @@ policyfree_cb(int policynr, void *arg)
 	systrace_freepolicy(policy);
 }
 
+static int
+erratic_failure(short *action)
+{
+	short errnos[] = { EPERM, EINTR, EINVAL, EAGAIN, ENOENT };
+
+	if (!(random() % erratic)) {
+		*action = errnos[random()%(sizeof(errnos)/sizeof(errnos[0]))];
+		return 1;
+	}
+	return 0;
+}
+
 static void
 child_handler(int sig)
 {
@@ -413,8 +437,8 @@ static void
 usage(void)
 {
 	fprintf(stderr,
-	    "Usage: systrace [-AaitUu] [-c uid:gid] [-d policydir] [-f file]\n"
-	    "\t [-g gui] [-p pid] command ...\n");
+	    "Usage: systrace [-AaiktUu] [-c uid:gid] [-d policydir] [-f file]\n"
+	    "\t [-g gui] [-p pid] [-r chance[-seed]] command ...\n");
 	exit(1);
 }
 
@@ -513,6 +537,35 @@ get_uid_gid(const char *argument, uid_t 
 	return (0);
 }
 
+static void
+seed_erratic(char *p)
+{
+	struct timeval tv;
+	unsigned long seed;
+	char *ep;
+
+	erratic = strtoul(p, &ep, 10);
+	if (errno == ERANGE && erratic == ULONG_MAX) {
+		err(1, "invalid chance '%s'", p);
+	} else if (*ep == '\0') {
+		(void) gettimeofday(&tv, NULL);
+		seed = getpid() ^ tv.tv_sec ^ (tv.tv_usec << 1);
+	} else if (*ep == '-') {
+		ep++;
+		if (*ep == '\0')
+			errx(1, "%s: number expected after - is missing", p);
+		p = ep;
+		seed = strtoul(p, &ep, 10);
+		if (*ep != '\0')
+			errx(1, "could not parse seed");
+		if (errno == ERANGE && seed == ULONG_MAX)
+			err(1, "invalid seed '%s'", p);
+	} else
+		errx(1, "unknown trailing characters in `chance'");
+
+	srandom(seed);
+}
+
 int
 main(int argc, char **argv)
 {
@@ -529,7 +582,7 @@ main(int argc, char **argv)
 	uid_t cr_uid;
 	gid_t cr_gid;
 
-	while ((c = getopt(argc, argv, "c:aAituUd:g:f:p:")) != -1) {
+	while ((c = getopt(argc, argv, "c:aAiktuUd:g:f:p:r:")) != -1) {
 		switch (c) {
 		case 'c':
 			setcredentials = 1;
@@ -555,6 +608,9 @@ main(int argc, char **argv)
 		case 'i':
 			inherit = 1;
 			break;
+		case 'k':
+			auto_kill = 1;
+			break;
 		case 'g':
 			guipath = optarg;
 			break;
@@ -568,6 +624,9 @@ main(int argc, char **argv)
 				warnx("bad pid: %s", optarg);
 				usage();
 			}
+			break;
+		case 'r':
+			seed_erratic(optarg);
 			break;
 		case 't':
 			usex11 = 0;
