summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/regression/security/proc_to_proc/Makefile12
-rw-r--r--tools/regression/security/proc_to_proc/README53
-rw-r--r--tools/regression/security/proc_to_proc/scenario.c399
-rw-r--r--tools/regression/security/proc_to_proc/scenario.h34
-rw-r--r--tools/regression/security/proc_to_proc/testuid.c65
5 files changed, 563 insertions, 0 deletions
diff --git a/tools/regression/security/proc_to_proc/Makefile b/tools/regression/security/proc_to_proc/Makefile
new file mode 100644
index 0000000..4880879
--- /dev/null
+++ b/tools/regression/security/proc_to_proc/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+PROG= testuid
+NOMAN= yes
+
+#ADDITIONAL_FLAGS=-DSETSUGID_SUPPORTED
+#ADDITIONAL_FLAGS= -DSETSUGID_SUPPORTED_BUT_NO_LIBC_STUB
+ADDITIONAL_FLAGS+= -DCHECK_CRED_SET
+CFLAGS+= ${ADDITIONAL_FLAGS}
+SRCS= testuid.c scenario.c
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/security/proc_to_proc/README b/tools/regression/security/proc_to_proc/README
new file mode 100644
index 0000000..978ef68
--- /dev/null
+++ b/tools/regression/security/proc_to_proc/README
@@ -0,0 +1,53 @@
+$FreeBSD$
+
+ Inter-Process Authorization Test Suite
+ Robert Watson, TrustedBSD Project
+
+This test suite attempts to determine the behavior of inter-process
+authorization policy present in the kernel. It analyzes a series of
+important scenarios using specifically crafted process credentials
+and a set of operations. It then reports on any divergence from the
+expected results.
+
+Test operations:
+
+ptrace cred1 attempts ptrace attach to cred2
+signal cred1 attempts SIGHUP of cred2
+see cred1 attempts getpriority() on cred2
+sched cred1 attempts setpriority() on cred2
+
+Test scenarioes:
+
+priv on priv root process on another root process
+priv on unpriv1 root process on a non-root process
+unpriv1 on priv non-root process on a root process
+unpriv1 on unpriv1 non-root process on a similar non-root process
+unpriv1 on unpriv2 non-root process on a different non-root process
+unpriv1 on daemon1 non-root process on a root daemon process acting with
+ same non-root effective credentials
+unpriv1 on daemon2 non-root process on a root daemon process acting with
+ different non-root effective credentials
+unpriv1 on setuid1 non-root process on a setuid-root process with same
+ non-root real credentials
+unpriv1 on setuid2 non-root process on a setuid-root process with
+ different non-root real credentials
+
+The credential elements supported by the test suite are:
+
+ effective uid
+ real uid
+ saved uid
+ P_SUGID flag
+
+Other untested aspects of interest include groups, as well as session
+relationship. Other test operations that might be of interest are SIGCONT,
+SIGIO, and SIGSEGV.
+
+The current set of tests includes some tests where normally the P_SUGID
+flag is set, but isn't in the test. The result is that some tests fail
+that may not reflect real-world software configurations. However, they
+do point to possible changes that could be made in the authorization system
+to improve resilience to failure or violation of invariants.
+
+These tests rely on __setugid(), a system call enabled using options
+REGRESSION.
diff --git a/tools/regression/security/proc_to_proc/scenario.c b/tools/regression/security/proc_to_proc/scenario.c
new file mode 100644
index 0000000..1eafd1c
--- /dev/null
+++ b/tools/regression/security/proc_to_proc/scenario.c
@@ -0,0 +1,399 @@
+/*-
+ * Copyright (c) 2001 Robert N. M. Watson
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * Relevant parts of a process credential.
+ */
+struct cred {
+ uid_t cr_euid, cr_ruid, cr_svuid;
+ int cr_issetugid;
+};
+
+/*
+ * Description of a scenario.
+ */
+struct scenario {
+ struct cred *sc_cred1, *sc_cred2; /* credentials of p1 and p2 */
+ int sc_candebug_errno; /* desired ptrace failure */
+ int sc_cansignal_errno; /* desired SIGHUP failure */
+ int sc_cansee_errno; /* desired getprio failure */
+ int sc_cansched_errno; /* desired setprio failure */
+ char *sc_name; /* test name */
+};
+
+/*
+ * Table of relevant credential combinations.
+ */
+static struct cred creds[] = {
+/* euid ruid svuid issetugid */
+/* 0 */ { 0, 0, 0, 0 }, /* privileged */
+/* 1 */ { 0, 0, 0, 1 }, /* privileged + issetugid */
+/* 2 */ { 1000, 1000, 1000, 0 }, /* unprivileged1 */
+/* 3 */ { 1000, 1000, 1000, 1 }, /* unprivileged1 + issetugid */
+/* 4 */ { 1001, 1001, 1001, 0 }, /* unprivileged2 */
+/* 5 */ { 1001, 1001, 1001, 1 }, /* unprivileged2 + issetugid */
+/* 6 */ { 1000, 0, 0, 0 }, /* daemon1 */
+/* 7 */ { 1000, 0, 0, 1 }, /* daemon1 + issetugid */
+/* 8 */ { 1001, 0, 0, 0 }, /* daemon2 */
+/* 9 */ { 1001, 0, 0, 1 }, /* daemon2 + issetugid */
+/* 10 */{ 0, 1000, 1000, 0 }, /* setuid1 */
+/* 11 */{ 0, 1000, 1000, 1 }, /* setuid1 + issetugid */
+/* 12 */{ 0, 1001, 1001, 0 }, /* setuid2 */
+/* 13 */{ 0, 1001, 1001, 1 }, /* setuid2 + issetugid */
+};
+
+/*
+ * Table of scenarios.
+ */
+static const struct scenario scenarios[] = {
+/* cred1 cred2 debug signal see sched name */
+{ &creds[0], &creds[0], 0, 0, 0, 0, "0. priv on priv"},
+{ &creds[0], &creds[1], 0, 0, 0, 0, "1. priv on priv"},
+{ &creds[1], &creds[0], 0, 0, 0, 0, "2. priv on priv"},
+{ &creds[1], &creds[1], 0, 0, 0, 0, "3. priv on priv"},
+/* privileged on unprivileged */
+{ &creds[0], &creds[2], 0, 0, 0, 0, "4. priv on unpriv1"},
+{ &creds[0], &creds[3], 0, 0, 0, 0, "5. priv on unpriv1"},
+{ &creds[1], &creds[2], 0, 0, 0, 0, "6. priv on unpriv1"},
+{ &creds[1], &creds[3], 0, 0, 0, 0, "7. priv on unpriv1"},
+/* unprivileged on privileged */
+{ &creds[2], &creds[0], EPERM, EPERM, 0, EPERM, "8. unpriv1 on priv"},
+{ &creds[2], &creds[1], EPERM, EPERM, 0, EPERM, "9. unpriv1 on priv"},
+{ &creds[3], &creds[0], EPERM, EPERM, 0, EPERM, "10. unpriv1 on priv"},
+{ &creds[3], &creds[1], EPERM, EPERM, 0, EPERM, "11. unpriv1 on priv"},
+/* unprivileged on same unprivileged */
+{ &creds[2], &creds[2], 0, 0, 0, 0, "12. unpriv1 on unpriv1"},
+{ &creds[2], &creds[3], EPERM, 0, 0, 0, "13. unpriv1 on unpriv1"},
+{ &creds[3], &creds[2], 0, 0, 0, 0, "14. unpriv1 on unpriv1"},
+{ &creds[3], &creds[3], EPERM, 0, 0, 0, "15. unpriv1 on unpriv1"},
+/* unprivileged on different unprivileged */
+{ &creds[2], &creds[4], EPERM, EPERM, 0, EPERM, "16. unpriv1 on unpriv2"},
+{ &creds[2], &creds[5], EPERM, EPERM, 0, EPERM, "17. unpriv1 on unpriv2"},
+{ &creds[3], &creds[4], EPERM, EPERM, 0, EPERM, "18. unpriv1 on unpriv2"},
+{ &creds[3], &creds[5], EPERM, EPERM, 0, EPERM, "19. unpriv1 on unpriv2"},
+/* unprivileged on daemon, same */
+{ &creds[2], &creds[6], EPERM, EPERM, 0, EPERM, "20. unpriv1 on daemon1"},
+{ &creds[2], &creds[7], EPERM, EPERM, 0, EPERM, "21. unpriv1 on daemon1"},
+{ &creds[3], &creds[6], EPERM, EPERM, 0, EPERM, "22. unpriv1 on daemon1"},
+{ &creds[3], &creds[7], EPERM, EPERM, 0, EPERM, "23. unpriv1 on daemon1"},
+/* unprivileged on daemon, different */
+{ &creds[2], &creds[8], EPERM, EPERM, 0, EPERM, "24. unpriv1 on daemon2"},
+{ &creds[2], &creds[9], EPERM, EPERM, 0, EPERM, "25. unpriv1 on daemon2"},
+{ &creds[3], &creds[8], EPERM, EPERM, 0, EPERM, "26. unpriv1 on daemon2"},
+{ &creds[3], &creds[9], EPERM, EPERM, 0, EPERM, "27. unpriv1 on daemon2"},
+/* unprivileged on setuid, same */
+{ &creds[2], &creds[10], EPERM, 0, 0, 0, "28. unpriv1 on setuid1"},
+{ &creds[2], &creds[11], EPERM, 0, 0, 0, "29. unpriv1 on setuid1"},
+{ &creds[3], &creds[10], EPERM, 0, 0, 0, "30. unpriv1 on setuid1"},
+{ &creds[3], &creds[11], EPERM, 0, 0, 0, "31. unpriv1 on setuid1"},
+/* unprivileged on setuid, different */
+{ &creds[2], &creds[12], EPERM, EPERM, 0, EPERM, "32. unpriv1 on setuid2"},
+{ &creds[2], &creds[13], EPERM, EPERM, 0, EPERM, "33. unpriv1 on setuid2"},
+{ &creds[3], &creds[12], EPERM, EPERM, 0, EPERM, "34. unpriv1 on setuid2"},
+{ &creds[3], &creds[13], EPERM, EPERM, 0, EPERM, "35. unpriv1 on setuid2"},
+};
+int scenarios_count = sizeof(scenarios) / sizeof(struct scenario);
+
+/*
+ * Convert an error number to a compact string representation. For now,
+ * implement only the error numbers we are likely to see.
+ */
+static char *
+errno_to_string(int error)
+{
+
+ switch (error) {
+ case EPERM:
+ return ("EPERM");
+ case EACCES:
+ return ("EACCES");
+ case EINVAL:
+ return ("EINVAL");
+ case ENOSYS:
+ return ("ENOSYS");
+ case ESRCH:
+ return ("ESRCH");
+ case 0:
+ return ("0");
+ default:
+ return ("unknown");
+ }
+}
+
+/*
+ * Return a process credential describing the current process.
+ */
+static int
+cred_get(struct cred *cred)
+{
+ int error;
+
+ error = getresuid(&cred->cr_ruid, &cred->cr_euid, &cred->cr_svuid);
+ if (error)
+ return (error);
+
+ cred->cr_issetugid = issetugid();
+
+ return (0);
+}
+
+/*
+ * Userland stub for __setsugid() to take into account possible presence
+ * in C library, kernel, et al.
+ */
+int
+setugid(int flag)
+{
+
+#ifdef SETSUGID_SUPPORTED
+ return (__setugid(flag));
+#else
+#ifdef SETSUGID_SUPPORTED_BUT_NO_LIBC_STUB
+ return (syscall(374, flag));
+#else
+ return (ENOSYS);
+#endif
+#endif
+}
+
+/*
+ * Set the current process's credentials to match the passed credential.
+ */
+static int
+cred_set(struct cred *cred)
+{
+ int error;
+
+ error = setresuid(cred->cr_ruid, cred->cr_euid, cred->cr_svuid);
+ if (error)
+ return (error);
+
+ error = setugid(cred->cr_issetugid);
+ if (error) {
+ perror("__setugid");
+ return (error);
+ }
+
+#ifdef CHECK_CRED_SET
+ {
+ uid_t ruid, euid, svuid;
+ error = getresuid(&ruid, &euid, &svuid);
+ if (error) {
+ perror("getresuid");
+ return (-1);
+ }
+ assert(ruid == cred->cr_ruid);
+ assert(euid == cred->cr_euid);
+ assert(svuid == cred->cr_svuid);
+ assert(cred->cr_issetugid == issetugid());
+ }
+#endif /* !CHECK_CRED_SET */
+
+ return (0);
+}
+
+/*
+ * Print the passed process credential to the passed I/O stream.
+ */
+static void
+cred_print(FILE *output, struct cred *cred)
+{
+
+ fprintf(output, "(e:%d r:%d s:%d P_SUGID:%d)", cred->cr_euid,
+ cred->cr_ruid, cred->cr_svuid, cred->cr_issetugid);
+}
+
+#define LOOP_PTRACE 0
+#define LOOP_SIGNAL 1
+#define LOOP_SEE 2
+#define LOOP_SCHED 3
+#define LOOP_MAX LOOP_SCHED
+
+/*
+ * Enact a scenario by looping through the four test cases for the scenario,
+ * spawning off pairs of processes with the desired credentials, and
+ * reporting results to stdout.
+ */
+static int
+enact_scenario(int scenario)
+{
+ pid_t pid1, pid2;
+ char *name;
+ int error, desirederror, loop;
+
+ for (loop = 0; loop < LOOP_MAX+1; loop++) {
+ /*
+ * Spawn the first child, target of the operation.
+ */
+ pid1 = fork();
+ switch (pid1) {
+ case -1:
+ return (-1);
+ case 0:
+ /* child */
+ error = cred_set(scenarios[scenario].sc_cred2);
+ if (error) {
+ perror("cred_set");
+ return (error);
+ }
+ /* 200 seconds should be plenty of time. */
+ sleep(200);
+ exit(0);
+ default:
+ /* parent */
+ }
+
+ /*
+ * XXX
+ * This really isn't ideal -- give proc 1 a chance to set
+ * its credentials, or we may get spurious errors. Really,
+ * some for of IPC should be used to allow the parent to
+ * wait for the first child to be ready before spawning
+ * the second child.
+ */
+ sleep(1);
+
+ /*
+ * Spawn the second child, source of the operation.
+ */
+ pid2 = fork();
+ switch (pid2) {
+ case -1:
+ return (-1);
+
+ case 0:
+ /* child */
+ error = cred_set(scenarios[scenario].sc_cred1);
+ if (error) {
+ perror("cred_set");
+ return (error);
+ }
+
+ /*
+ * Initialize errno to zero so as to catch any
+ * generated errors. In each case, perform the
+ * operation. Preserve the error number for later
+ * use so it doesn't get stomped on by any I/O.
+ * Determine the desired error for the given case
+ * by extracting it from the scenario table.
+ * Initialize a function name string for output
+ * prettiness.
+ */
+ errno = 0;
+ switch (loop) {
+ case LOOP_PTRACE:
+ error = ptrace(PT_ATTACH, pid1, NULL, 0);
+ error = errno;
+ name = "ptrace";
+ desirederror =
+ scenarios[scenario].sc_candebug_errno;
+ break;
+ case LOOP_SIGNAL:
+ error = kill(pid1, SIGHUP);
+ error = errno;
+ name = "signal";
+ desirederror =
+ scenarios[scenario].sc_cansignal_errno;
+ break;
+ case LOOP_SEE:
+ getpriority(PRIO_PROCESS, pid1);
+ error = errno;
+ name = "see";
+ desirederror =
+ scenarios[scenario].sc_cansee_errno;
+ break;
+ case LOOP_SCHED:
+ error = setpriority(PRIO_PROCESS, pid1,
+ 0);
+ error = errno;
+ name = "sched";
+ desirederror =
+ scenarios[scenario].sc_cansched_errno;
+ break;
+ default:
+ name = "broken";
+ }
+
+ if (error != desirederror) {
+ fprintf(stdout,
+ "[%s].%s: expected %s, got %s\n ",
+ scenarios[scenario].sc_name, name,
+ errno_to_string(desirederror),
+ errno_to_string(error));
+ cred_print(stdout,
+ scenarios[scenario].sc_cred1);
+ cred_print(stdout,
+ scenarios[scenario].sc_cred2);
+ fprintf(stdout, "\n");
+ }
+
+ exit(0);
+
+ default:
+ /* parent */
+ }
+
+ error = waitpid(pid2, NULL, 0);
+ /*
+ * Once pid2 has died, it's safe to kill pid1, if it's still
+ * alive. Mask signal failure in case the test actually
+ * killed pid1 (not unlikely: can occur in both signal and
+ * ptrace cases).
+ */
+ kill(pid1, SIGKILL);
+ error = waitpid(pid2, NULL, 0);
+ }
+
+ return (0);
+}
+
+void
+enact_scenarios(void)
+{
+ int i, error;
+
+ for (i = 0; i < scenarios_count; i++) {
+ error = enact_scenario(i);
+ if (error)
+ perror("enact_scenario");
+ }
+}
diff --git a/tools/regression/security/proc_to_proc/scenario.h b/tools/regression/security/proc_to_proc/scenario.h
new file mode 100644
index 0000000..da08720
--- /dev/null
+++ b/tools/regression/security/proc_to_proc/scenario.h
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 2001 Robert N. M. Watson
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ *
+ * $FreeBSD$
+ */
+#ifndef _SCENARIO_H
+#define _SCENARIO_H
+
+int setugid __P((int flag));
+int enact_scenarios __P((void));
+
+#endif /* !_SCENARIO_H */
diff --git a/tools/regression/security/proc_to_proc/testuid.c b/tools/regression/security/proc_to_proc/testuid.c
new file mode 100644
index 0000000..a9b81a08
--- /dev/null
+++ b/tools/regression/security/proc_to_proc/testuid.c
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 2001 Robert N. M. Watson
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "scenario.h"
+
+int
+main(int argc, char *argv[])
+{
+ int error;
+
+ fprintf(stderr, "test capabilities: ");
+#ifdef SETSUGID_SUPPORTED
+ fprintf(stderr, "[SETSUGID_SUPPORTED] ");
+#endif
+#ifdef SETSUGID_SUPPORTED_BUT_NO_LIBC_STUB
+ fprintf(stderr, "[SETSUGID_SUPPORTED_BUT_NO_LIBC_STUB] ");
+#endif
+#ifdef CHECK_CRED_SET
+ fprintf(stderr, "[CHECK_CRED_SET] ");
+#endif
+ fprintf(stderr, "\n");
+
+ error = setugid(1);
+ if (error) {
+ perror("setugid");
+ fprintf(stderr,
+ "This test suite requires options REGRESSION\n");
+ return (-1);
+ }
+
+ enact_scenarios();
+
+ return (0);
+}
+
OpenPOWER on IntegriCloud