diff options
Diffstat (limited to 'tools/regression/priv/main.c')
-rw-r--r-- | tools/regression/priv/main.c | 455 |
1 files changed, 384 insertions, 71 deletions
diff --git a/tools/regression/priv/main.c b/tools/regression/priv/main.c index 90622ad..a9faa98 100644 --- a/tools/regression/priv/main.c +++ b/tools/regression/priv/main.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2006 nCircle Network Security, Inc. + * Copyright (c) 2007 Robert N. M. Watson * All rights reserved. * * This software was developed by Robert N. M. Watson for the TrustedBSD @@ -30,16 +31,20 @@ */ /* - * Privilege test framework. Each test is encapsulated on a .c file exporting - * a function that implements the test. Each test is run from its own child - * process, and they are run in sequence one at a time. + * Privilege test framework. Each test is encapsulated on a .c file + * exporting a function that implements the test. Each test is run from its + * own child process, and they are run in sequence one at a time. */ -#include <sys/types.h> +#include <sys/param.h> +#include <sys/jail.h> #include <sys/stat.h> #include <sys/wait.h> +#include <netinet/in.h> + #include <err.h> +#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -48,86 +53,402 @@ #include "main.h" /* - * Common routines used across many tests. + * Registration table of privilege tests. Each test registers a name, a test + * function, and a cleanup function to run after the test has completed, + * regardless of success/failure. */ +static struct test tests[] = { + { "priv_acct_enable", priv_acct_setup, priv_acct_enable, + priv_acct_cleanup }, + + { "priv_acct_disable", priv_acct_setup, priv_acct_disable, + priv_acct_cleanup }, + + { "priv_acct_rotate", priv_acct_setup, priv_acct_rotate, + priv_acct_cleanup }, + + { "priv_acct_noopdisable", priv_acct_setup, priv_acct_noopdisable, + priv_acct_cleanup }, + + { "priv_adjtime_set", priv_adjtime_setup, priv_adjtime_set, + priv_adjtime_cleanup }, + + { "priv_audit_submit", priv_audit_submit_setup, priv_audit_submit, + priv_audit_submit_cleanup }, + + { "priv_audit_control", priv_audit_control_setup, priv_audit_control, + priv_audit_control_cleanup }, + + { "priv_audit_getaudit", priv_audit_getaudit_setup, + priv_audit_getaudit, priv_audit_getaudit_cleanup }, + + { "priv_audit_getaudit_addr", priv_audit_getaudit_setup, + priv_audit_getaudit_addr, priv_audit_getaudit_cleanup }, + + { "priv_audit_setaudit", priv_audit_setaudit_setup, + priv_audit_setaudit, priv_audit_setaudit_cleanup }, + + { "priv_audit_setaudit_addr", priv_audit_setaudit_setup, + priv_audit_setaudit_addr, priv_audit_setaudit_cleanup }, + + { "priv_clock_settime", priv_clock_settime_setup, priv_clock_settime, + priv_clock_settime_cleanup }, + + { "priv_cred_setuid", priv_cred_setup, priv_cred_setuid, + priv_cred_cleanup }, + + { "priv_cred_seteuid", priv_cred_setup, priv_cred_seteuid, + priv_cred_cleanup }, + + { "priv_cred_setgid", priv_cred_setup, priv_cred_setgid, + priv_cred_cleanup }, + + { "priv_cred_setegid", priv_cred_setup, priv_cred_setegid, + priv_cred_cleanup }, + + { "priv_cred_setgroups", priv_cred_setup, priv_cred_setgroups, + priv_cred_cleanup }, + + { "priv_cred_setreuid", priv_cred_setup, priv_cred_setreuid, + priv_cred_cleanup }, + + { "priv_cred_setregid", priv_cred_setup, priv_cred_setregid, + priv_cred_cleanup }, + + { "priv_cred_setresuid", priv_cred_setup, priv_cred_setresuid, + priv_cred_cleanup }, + + { "priv_cred_setresgid", priv_cred_setup, priv_cred_setresgid, + priv_cred_cleanup }, + + { "priv_io", priv_io_setup, priv_io, priv_io_cleanup }, + + { "priv_kenv_set", priv_kenv_set_setup, priv_kenv_set, + priv_kenv_set_cleanup }, + + { "priv_kenv_unset", priv_kenv_unset_setup, priv_kenv_unset, + priv_kenv_unset_cleanup }, + + { "priv_msgbuf_privonly", priv_msgbuf_privonly_setup, + priv_msgbuf_privonly, priv_msgbuf_cleanup }, + + { "priv_msgbuf_unprivok", priv_msgbuf_unprivok_setup, + priv_msgbuf_unprivok, priv_msgbuf_cleanup }, + + { "priv_netinet_raw", priv_netinet_raw_setup, priv_netinet_raw, + priv_netinet_raw_cleanup }, + + { "priv_proc_setlogin", priv_proc_setlogin_setup, priv_proc_setlogin, + priv_proc_setlogin_cleanup }, + + { "priv_proc_setrlimit_raisemax", priv_proc_setrlimit_setup, + priv_proc_setrlimit_raisemax, priv_proc_setrlimit_cleanup }, + + { "priv_proc_setrlimit_raisecur", priv_proc_setrlimit_setup, + priv_proc_setrlimit_raisecur, priv_proc_setrlimit_cleanup }, + + { "priv_proc_setrlimit_raisecur_nopriv", priv_proc_setrlimit_setup, + priv_proc_setrlimit_raisecur_nopriv, + priv_proc_setrlimit_cleanup }, + + { "priv_sched_rtprio_curproc_normal", priv_sched_rtprio_setup, + priv_sched_rtprio_curproc_normal, priv_sched_rtprio_cleanup }, + + { "priv_sched_rtprio_curproc_idle", priv_sched_rtprio_setup, + priv_sched_rtprio_curproc_idle, priv_sched_rtprio_cleanup }, + + { "priv_sched_rtprio_curproc_realtime", priv_sched_rtprio_setup, + priv_sched_rtprio_curproc_realtime, priv_sched_rtprio_cleanup }, + + { "priv_sched_rtprio_myproc_normal", priv_sched_rtprio_setup, + priv_sched_rtprio_myproc_normal, priv_sched_rtprio_cleanup }, + + { "priv_sched_rtprio_myproc_idle", priv_sched_rtprio_setup, + priv_sched_rtprio_myproc_idle, priv_sched_rtprio_cleanup }, + + { "priv_sched_rtprio_myproc_realtime", priv_sched_rtprio_setup, + priv_sched_rtprio_myproc_realtime, priv_sched_rtprio_cleanup }, + + { "priv_sched_rtprio_aproc_normal", priv_sched_rtprio_setup, + priv_sched_rtprio_aproc_normal, priv_sched_rtprio_cleanup }, + + { "priv_sched_rtprio_aproc_idle", priv_sched_rtprio_setup, + priv_sched_rtprio_aproc_idle, priv_sched_rtprio_cleanup }, + + { "priv_sched_rtprio_aproc_realtime", priv_sched_rtprio_setup, + priv_sched_rtprio_aproc_realtime, priv_sched_rtprio_cleanup }, + + { "priv_sched_setpriority_curproc", priv_sched_setpriority_setup, + priv_sched_setpriority_curproc, priv_sched_setpriority_cleanup }, + + { "priv_sched_setpriority_myproc", priv_sched_setpriority_setup, + priv_sched_setpriority_myproc, priv_sched_setpriority_cleanup }, + + { "priv_sched_setpriority_aproc", priv_sched_setpriority_setup, + priv_sched_setpriority_aproc, priv_sched_setpriority_cleanup }, + + { "priv_settimeofday", priv_settimeofday_setup, priv_settimeofday, + priv_settimeofday_cleanup }, + + { "priv_sysctl_write", priv_sysctl_write_setup, priv_sysctl_write, + priv_sysctl_write_cleanup }, + + { "priv_sysctl_writejail", priv_sysctl_write_setup, + priv_sysctl_writejail, priv_sysctl_write_cleanup }, + + { "priv_vfs_chflags_froot_uflags", priv_vfs_chflags_froot_setup, + priv_vfs_chflags_froot_uflags, priv_vfs_chflags_cleanup }, + + { "priv_vfs_chflags_froot_sflags", priv_vfs_chflags_froot_setup, + priv_vfs_chflags_froot_sflags, priv_vfs_chflags_cleanup }, + + { "priv_vfs_chflags_fowner_uflags", priv_vfs_chflags_fowner_setup, + priv_vfs_chflags_fowner_uflags, priv_vfs_chflags_cleanup }, + + { "priv_vfs_chflags_fowner_sflags", priv_vfs_chflags_fowner_setup, + priv_vfs_chflags_fowner_sflags, priv_vfs_chflags_cleanup }, + + { "priv_vfs_chflags_fother_uflags", priv_vfs_chflags_fother_setup, + priv_vfs_chflags_fother_uflags, priv_vfs_chflags_cleanup }, + + { "priv_vfs_chflags_fother_sflags", priv_vfs_chflags_fother_setup, + priv_vfs_chflags_fother_sflags, priv_vfs_chflags_cleanup }, + + { "priv_vfs_chmod_froot", priv_vfs_chmod_froot_setup, + priv_vfs_chmod_froot, priv_vfs_chmod_cleanup }, + + { "priv_vfs_chmod_fowner", priv_vfs_chmod_fowner_setup, + priv_vfs_chmod_fowner, priv_vfs_chmod_cleanup }, + + { "priv_vfs_chmod_fother", priv_vfs_chmod_fother_setup, + priv_vfs_chmod_fother, priv_vfs_chmod_cleanup }, + + { "priv_vfs_chown_uid", priv_vfs_chown_uid_setup, priv_vfs_chown_uid, + priv_vfs_chown_cleanup }, + + { "priv_vfs_chown_mygid", priv_vfs_chown_mygid_setup, + priv_vfs_chown_mygid, priv_vfs_chown_cleanup }, + + { "priv_vfs_chown_othergid", priv_vfs_chown_othergid_setup, + priv_vfs_chown_othergid, priv_vfs_chown_cleanup }, + + { "priv_vfs_chroot", priv_vfs_chroot_setup, priv_vfs_chroot, + priv_vfs_chroot_cleanup }, + + { "priv_vfs_clearsugid_chgrp", priv_vfs_clearsugid_setup, + priv_vfs_clearsugid_chgrp, priv_vfs_clearsugid_cleanup }, + + { "priv_vfs_clearsugid_extattr", priv_vfs_clearsugid_setup, + priv_vfs_clearsugid_extattr, priv_vfs_clearsugid_cleanup }, + + { "priv_vfs_clearsugid_write", priv_vfs_clearsugid_setup, + priv_vfs_clearsugid_write, priv_vfs_clearsugid_cleanup }, + + { "priv_vfs_extattr_system", priv_vfs_extattr_system_setup, + priv_vfs_extattr_system, priv_vfs_extattr_system_cleanup }, + + { "priv_vfs_fhopen", priv_vfs_fhopen_setup, priv_vfs_fhopen, + priv_vfs_fhopen_cleanup }, + + { "priv_vfs_fhstat", priv_vfs_fhstat_setup, priv_vfs_fhstat, + priv_vfs_fhstat_cleanup }, + + { "priv_vfs_fhstatfs", priv_vfs_fhstatfs_setup, priv_vfs_fhstatfs, + priv_vfs_fhstatfs_cleanup }, + + { "priv_vfs_generation", priv_vfs_generation_setup, + priv_vfs_generation, priv_vfs_generation_cleanup }, + + { "priv_vfs_getfh", priv_vfs_getfh_setup, priv_vfs_getfh, + priv_vfs_getfh_cleanup }, + + { "priv_vfs_readwrite_fowner", priv_vfs_readwrite_fowner_setup, + priv_vfs_readwrite_fowner, priv_vfs_readwrite_cleanup }, + + { "priv_vfs_readwrite_fgroup", priv_vfs_readwrite_fgroup_setup, + priv_vfs_readwrite_fgroup, priv_vfs_readwrite_cleanup }, + + { "priv_vfs_readwrite_fother", priv_vfs_readwrite_fother_setup, + priv_vfs_readwrite_fother, priv_vfs_readwrite_cleanup }, + + { "priv_vfs_setgid_fowner", priv_vfs_setgid_fowner_setup, + priv_vfs_setgid_fowner, priv_vfs_setgid_cleanup }, + + { "priv_vfs_setgid_fother", priv_vfs_setgid_fother_setup, + priv_vfs_setgid_fother, priv_vfs_setgid_cleanup }, + + { "priv_vfs_stickyfile_dir_fowner", + priv_vfs_stickyfile_dir_fowner_setup, + priv_vfs_stickyfile_dir_fowner, + priv_vfs_stickyfile_dir_cleanup }, + + { "priv_vfs_stickyfile_dir_fother", + priv_vfs_stickyfile_dir_fother_setup, + priv_vfs_stickyfile_dir_fother, + priv_vfs_stickyfile_dir_cleanup }, + + { "priv_vfs_stickyfile_file_fowner", + priv_vfs_stickyfile_file_fowner_setup, + priv_vfs_stickyfile_file_fowner, + priv_vfs_stickyfile_file_cleanup }, + + { "priv_vfs_stickyfile_file_fother", + priv_vfs_stickyfile_file_fother_setup, + priv_vfs_stickyfile_file_fother, + priv_vfs_stickyfile_file_cleanup }, + + { "priv_vfs_utimes_froot", priv_vfs_utimes_froot_setup, + priv_vfs_utimes_froot, priv_vfs_utimes_cleanup }, + + { "priv_vfs_utimes_froot_null", priv_vfs_utimes_froot_setup, + priv_vfs_utimes_froot_null, priv_vfs_utimes_cleanup }, + + { "priv_vfs_utimes_fowner", priv_vfs_utimes_fowner_setup, + priv_vfs_utimes_fowner, priv_vfs_utimes_cleanup }, + + { "priv_vfs_utimes_fowner_null", priv_vfs_utimes_fowner_setup, + priv_vfs_utimes_fowner_null, priv_vfs_utimes_cleanup }, + + { "priv_vfs_utimes_fother", priv_vfs_utimes_fother_setup, + priv_vfs_utimes_fother, priv_vfs_utimes_cleanup }, + + { "priv_vfs_utimes_fother_null", priv_vfs_utimes_fother_setup, + priv_vfs_utimes_fother_null, priv_vfs_utimes_cleanup }, + + { "priv_vm_madv_protect", priv_vm_madv_protect_setup, + priv_vm_madv_protect, priv_vm_madv_protect_cleanup }, + + { "priv_vm_mlock", priv_vm_mlock_setup, priv_vm_mlock, + priv_vm_mlock_cleanup }, + + { "priv_vm_munlock", priv_vm_munlock_setup, priv_vm_munlock, + priv_vm_munlock_cleanup }, + +}; +static int tests_count = sizeof(tests) / sizeof(struct test); + +void +expect(const char *test, int error, int expected_error, int expected_errno) +{ + + if (error == 0) { + if (expected_error != 0) + warnx("%s: returned 0", test); + } else { + if (expected_error == 0) + warn("%s: returned (%d, %d)", test, error, errno); + else if (expected_errno != errno) + warn("%s: returned (%d, %d)", test, error, errno); + } +} + void -assert_root(void) +setup_dir(const char *test, char *dpathp, uid_t uid, gid_t gid, mode_t mode) { - if (getuid() != UID_ROOT || geteuid() != UID_ROOT) - err(-1, "must be run as root"); + strcpy(dpathp, "/tmp/priv.XXXXXXXXXXX"); + if (mkdtemp(dpathp) == NULL) + err(-1, "test %s: mkdtemp", test); + + if (chown(dpathp, uid, gid) < 0) + err(-1, "test %s: chown(%s, %d, %d)", test, dpathp, uid, + gid); + + if (chmod(dpathp, mode) < 0) + err(-1, "test %s: chmod(%s, 0%o)", test, dpathp, mode); } void -setup_file(char *fpathp, uid_t uid, gid_t gid, mode_t mode) +setup_file(const char *test, char *fpathp, uid_t uid, gid_t gid, mode_t mode) { int fd; strcpy(fpathp, "/tmp/priv.XXXXXXXXXXX"); fd = mkstemp(fpathp); if (fd < 0) - err(-1, "mkstemp"); + err(-1, "test %s: mkstemp", test); if (fchown(fd, uid, gid) < 0) - err(-1, "fchown(%s, %d, %d)", fpathp, uid, gid); + err(-1, "test %s: fchown(%s, %d, %d)", test, fpathp, uid, + gid); if (fchmod(fd, mode) < 0) - err(-1, "chmod(%s, 0%o)", fpathp, mode); + err(-1, "test %s: chmod(%s, 0%o)", test, fpathp, mode); close(fd); } /* - * When downgrading privileges, set the gid before the uid; when upgrading, - * set uid before gid. + * Irrevocably set credentials to specific uid and gid. */ -void -set_creds(uid_t uid, gid_t gid) +static void +set_creds(const char *test, uid_t uid, gid_t gid) { + gid_t gids[1] = { gid }; - if (setegid(gid) < 0) - err(-1, "setegid(%d)", gid); - if (seteuid(uid) < 0) - err(-1, "seteuid(%d)", uid); + if (setgid(gid) < 0) + err(-1, "test %s: setegid(%d)", test, gid); + if (setgroups(sizeof(gids)/sizeof(gid_t), gids) < 0) + err(-1, "test %s: setgroups(%d)", test, gid); + if (setuid(uid) < 0) + err(-1, "test %s: seteuid(%d)", test, uid); } -void -set_euid(uid_t uid) +static void +enter_jail(const char *test) { + struct jail j; - if (seteuid(uid) < 0) - err(-1, "seteuid(%d)", uid); + bzero(&j, sizeof(j)); + j.version = 0; + j.path = "/"; + j.hostname = "test"; + j.ip_number = htonl(INADDR_LOOPBACK); + if (jail(&j) < 0) + err(-1, "test %s: jail", test); } -void -restore_creds(void) +static void +run_child(struct test *test, int asroot, int injail) { - if (seteuid(UID_ROOT) < 0) - err(-1, "seteuid(%d)", UID_ROOT); - if (setegid(GID_WHEEL) < 0) - err(-1, "setegid(%d)", GID_WHEEL); + setprogname(test->t_name); + if (injail) + enter_jail(test->t_name); + if (!asroot) + set_creds(test->t_name, UID_OWNER, GID_OWNER); + test->t_test_func(asroot, injail, test); } /* - * Execute tests in a child process so they don't contaminate each other, - * especially with regard to file descriptors, credentials, working - * directories, and chroot status. + * Run a test in a particular credential context -- always call the setup and + * cleanup routines; if setup succeeds, also run the test. Test cleanup must + * handle cases where the setup has failed, so may need to maintain their own + * state in order to know what needs cleaning up (such as whether temporary + * files were created). */ static void -run(const char *funcname, void (*func)(void)) +run(struct test *test, int asroot, int injail) { pid_t childpid, pid; - printf("running %s\n", funcname); + if (test->t_setup_func != NULL) { + if ((test->t_setup_func)(asroot, injail, test) != 0) { + warnx("run(%s, %d, %d) setup failed", test->t_name, + asroot, injail); + goto cleanup; + } + } fflush(stdout); fflush(stderr); childpid = fork(); - if (childpid == -1) - err(-1, "test %s unable to fork", funcname); + if (childpid == -1) { + warn("run(%s, %d, %d) fork failed", test->t_name, asroot, + injail); + goto cleanup; + } if (childpid == 0) { - setprogname(funcname); - func(); + run_child(test, asroot, injail); fflush(stdout); fflush(stderr); exit(0); @@ -135,50 +456,42 @@ run(const char *funcname, void (*func)(void)) while (1) { pid = waitpid(childpid, NULL, 0); if (pid == -1) - warn("waitpid %s", funcname); + warn("test: waitpid %s", test->t_name); if (pid == childpid) break; } } fflush(stdout); fflush(stderr); +cleanup: + if (test->t_cleanup_func != NULL) + test->t_cleanup_func(asroot, injail, test); } int main(int argc, char *argv[]) { + int i; - run("priv_acct", priv_acct); - run("priv_adjtime", priv_adjtime); - run("priv_clock_settime", priv_clock_settime); - run("priv_io", priv_io); - run("priv_kenv_set", priv_kenv_set); - run("priv_kenv_unset", priv_kenv_unset); - run("priv_proc_setlogin", priv_proc_setlogin); - run("priv_proc_setrlimit", priv_proc_setrlimit); - run("priv_sched_rtprio", priv_sched_rtprio); - run("priv_sched_setpriority", priv_sched_setpriority); - run("priv_settimeofday", priv_settimeofday); - run("priv_sysctl_write", priv_sysctl_write); - run("priv_vfs_admin", priv_vfs_admin); - run("priv_vfs_chown", priv_vfs_chown); - run("priv_vfs_chroot", priv_vfs_chroot); - run("priv_vfs_clearsugid", priv_vfs_clearsugid); - run("priv_vfs_extattr_system", priv_vfs_extattr_system); - run("priv_vfs_fhopen", priv_vfs_fhopen); - run("priv_vfs_fhstat", priv_vfs_fhstat); - run("priv_vfs_fhstatfs", priv_vfs_fhstatfs); - run("priv_vfs_generation", priv_vfs_generation); - run("priv_vfs_getfh", priv_vfs_getfh); - run("priv_vfs_read", priv_vfs_read); - run("priv_vfs_setgid", priv_vfs_setgid); - run("priv_vfs_stickyfile", priv_vfs_stickyfile); - run("priv_vfs_write", priv_vfs_write); - run("priv_vm_madv_protect", priv_vm_madv_protect); - run("priv_vm_mlock", priv_vm_mlock); - run("priv_vm_munlock", priv_vm_munlock); - - run("test_utimes", test_utimes); + /* + * This test suite will need to become quite a bit more enlightened + * if the notion of privilege is truly separated from root, as tests + * make assumptions about when privilege will be present. In + * particular, VFS-related tests need to manage uids in order to + * force the use of privilege, and will likely need checking. + */ + if (getuid() != 0 && geteuid() != 0) + errx(-1, "priv: must be run as root"); + /* + * Run each test four times, varying whether the process is running + * as root and in jail in order to test all possible combinations. + */ + for (i = 0; i < tests_count; i++) { + run(&tests[i], 0, 0); + run(&tests[i], 0, 1); + run(&tests[i], 1, 0); + run(&tests[i], 1, 1); + } return (0); } |