diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/regression/security/proc_to_proc/Makefile | 12 | ||||
-rw-r--r-- | tools/regression/security/proc_to_proc/README | 53 | ||||
-rw-r--r-- | tools/regression/security/proc_to_proc/scenario.c | 399 | ||||
-rw-r--r-- | tools/regression/security/proc_to_proc/scenario.h | 34 | ||||
-rw-r--r-- | tools/regression/security/proc_to_proc/testuid.c | 65 |
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); +} + |