diff options
Diffstat (limited to 'tools/regression')
32 files changed, 4026 insertions, 0 deletions
diff --git a/tools/regression/priv/Makefile b/tools/regression/priv/Makefile new file mode 100644 index 0000000..7a8d7a0 --- /dev/null +++ b/tools/regression/priv/Makefile @@ -0,0 +1,40 @@ +# +# $FreeBSD$ +# + +PROG= main +SRCS= main.c \ + priv_acct.c \ + priv_adjtime.c \ + priv_clock_settime.c \ + priv_io.c \ + priv_kenv_set.c \ + priv_kenv_unset.c \ + priv_proc_setlogin.c \ + priv_proc_setrlimit.c \ + priv_sched_rtprio.c \ + priv_sched_setpriority.c \ + priv_settimeofday.c \ + priv_sysctl_write.c \ + priv_vfs_admin.c \ + priv_vfs_chown.c \ + priv_vfs_chroot.c \ + priv_vfs_clearsugid.c \ + priv_vfs_extattr_system.c \ + priv_vfs_fhopen.c \ + priv_vfs_fhstat.c \ + priv_vfs_fhstatfs.c \ + priv_vfs_generation.c \ + priv_vfs_getfh.c \ + priv_vfs_read_write.c \ + priv_vfs_setgid.c \ + priv_vfs_stickyfile.c \ + priv_vm_mlock.c \ + priv_vm_munlock.c \ + priv_vm_madv_protect.c \ + \ + test_utimes.c +NO_MAN= +WARNS= 3 + +.include <bsd.prog.mk> diff --git a/tools/regression/priv/main.c b/tools/regression/priv/main.c new file mode 100644 index 0000000..90622ad --- /dev/null +++ b/tools/regression/priv/main.c @@ -0,0 +1,184 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * 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/stat.h> +#include <sys/wait.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "main.h" + +/* + * Common routines used across many tests. + */ +void +assert_root(void) +{ + + if (getuid() != UID_ROOT || geteuid() != UID_ROOT) + err(-1, "must be run as root"); +} + +void +setup_file(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"); + + if (fchown(fd, uid, gid) < 0) + err(-1, "fchown(%s, %d, %d)", fpathp, uid, gid); + + if (fchmod(fd, mode) < 0) + err(-1, "chmod(%s, 0%o)", fpathp, mode); + + close(fd); +} + +/* + * When downgrading privileges, set the gid before the uid; when upgrading, + * set uid before gid. + */ +void +set_creds(uid_t uid, gid_t gid) +{ + + if (setegid(gid) < 0) + err(-1, "setegid(%d)", gid); + if (seteuid(uid) < 0) + err(-1, "seteuid(%d)", uid); +} + +void +set_euid(uid_t uid) +{ + + if (seteuid(uid) < 0) + err(-1, "seteuid(%d)", uid); +} + +void +restore_creds(void) +{ + + if (seteuid(UID_ROOT) < 0) + err(-1, "seteuid(%d)", UID_ROOT); + if (setegid(GID_WHEEL) < 0) + err(-1, "setegid(%d)", GID_WHEEL); +} + +/* + * 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. + */ +static void +run(const char *funcname, void (*func)(void)) +{ + pid_t childpid, pid; + + printf("running %s\n", funcname); + fflush(stdout); + fflush(stderr); + childpid = fork(); + if (childpid == -1) + err(-1, "test %s unable to fork", funcname); + if (childpid == 0) { + setprogname(funcname); + func(); + fflush(stdout); + fflush(stderr); + exit(0); + } else { + while (1) { + pid = waitpid(childpid, NULL, 0); + if (pid == -1) + warn("waitpid %s", funcname); + if (pid == childpid) + break; + } + } + fflush(stdout); + fflush(stderr); +} + +int +main(int argc, char *argv[]) +{ + + 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); + + return (0); +} diff --git a/tools/regression/priv/main.h b/tools/regression/priv/main.h new file mode 100644 index 0000000..6c5616b --- /dev/null +++ b/tools/regression/priv/main.h @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +#define UID_ROOT 0 +#define UID_OWNER 100 +#define UID_OTHER 200 +#define UID_THIRD 300 + +#define GID_WHEEL 0 +#define GID_OWNER 100 +#define GID_OTHER 200 + +#define KENV_VAR_NAME "test" +#define KENV_VAR_VALUE "test" + +/* + * Library routines used by many tests. + */ +void assert_root(void); +void setup_file(char *fpathp, uid_t uid, gid_t gid, mode_t mode); +void set_creds(uid_t uid, gid_t gid); +void set_euid(uid_t uid); +void restore_creds(void); + +/* + * Tests for specific privileges. + */ +void priv_acct(void); +void priv_adjtime(void); +void priv_clock_settime(void); +void priv_io(void); +void priv_kenv_set(void); +void priv_kenv_unset(void); +void priv_proc_setlogin(void); +void priv_proc_setrlimit(void); +void priv_sched_rtprio(void); +void priv_sched_setpriority(void); +void priv_settimeofday(void); +void priv_sysctl_write(void); +void priv_vfs_admin(void); +void priv_vfs_chown(void); +void priv_vfs_chroot(void); +void priv_vfs_clearsugid(void); +void priv_vfs_extattr_system(void); +void priv_vfs_fhopen(void); +void priv_vfs_fhstat(void); +void priv_vfs_fhstatfs(void); +void priv_vfs_generation(void); +void priv_vfs_getfh(void); +void priv_vfs_read(void); +void priv_vfs_setgid(void); +void priv_vfs_stickyfile(void); +void priv_vfs_write(void); +void priv_vm_madv_protect(void); +void priv_vm_mlock(void); +void priv_vm_munlock(void); + +/* + * Tests for more complex access control logic involving more than one + * privilege, or privilege combined with DAC. + */ +void test_utimes(void); diff --git a/tools/regression/priv/priv_acct.c b/tools/regression/priv/priv_acct.c new file mode 100644 index 0000000..bc4a41e7 --- /dev/null +++ b/tools/regression/priv/priv_acct.c @@ -0,0 +1,178 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Test that configuring accounting requires privilege. First check that + * accounting is not in use on the system to prevent disrupting the + * accounting service. Confirm three different state transitions, both as + * privileged and non-privileged: disabled to enabled, rotate, and enabled to + * disabled. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/sysctl.h> + +#include <err.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> + +#include "main.h" + +#define SYSCTL_NAME "kern.acct_configured" +#define PATH_TEMPLATE "/tmp/acct.XXXXXXXXXXX" + +void +priv_acct(void) +{ + char fpath1[1024] = PATH_TEMPLATE; + char fpath2[1024] = PATH_TEMPLATE; + int error, fd, i; + size_t len; + + assert_root(); + + /* + * Check that accounting isn't already configured in the kernel. + */ + len = sizeof(i); + if (sysctlbyname(SYSCTL_NAME, &i, &len, NULL, 0) < 0) + err(-1, "sysctlbyname(%s)", SYSCTL_NAME); + if (i != 0) + errx(-1, "sysctlbyname(%s) indicates accounting configured", + SYSCTL_NAME); + + /* + * Create two temporary files to use as accounting targets. + */ + fd = mkstemp(fpath1); + if (fd < 0) + err(-1, "mkstemp"); + close(fd); + fd = mkstemp(fpath2); + if (fd < 0) { + warn("mkstemp"); + (void)unlink(fpath1); + exit(-1); + } + + /* + * Change the permissions on the file so that access control on the + * file doesn't come into play. + */ + if (chmod(fpath1, 0666) < 0) { + warn("chmod(%s, 0666)", fpath1); + goto out; + } + + if (chmod(fpath2, 0666) < 0) { + warn("chmod(%s, 0600)", fpath2); + goto out; + } + + /* + * Test that privileged can move through entire life cycle. + */ + if (acct(fpath1) < 0) { + warn("acct(NULL -> %s) as root", fpath1); + goto out; + } + + if (acct(fpath2) < 0) { + warn("acct(%s -> %s) as root", fpath1, fpath2); + goto out; + } + + if (acct(NULL) < 0) { + warn("acct(%s -> NULL) as root", fpath1); + goto out; + } + + /* + * Testing for unprivileged is a bit more tricky, as expect each step + * to fail, so must replay various bits of the setup process as root + * so that each step can be tested as !root. + */ + set_euid(UID_OTHER); + error = acct(fpath1); + if (error == 0) { + warnx("acct(NULL -> %s) succeeded as !root", fpath1); + goto out; + } + if (errno != EPERM) { + warn("acct(NULL -> %s) wrong errno %d as !root", fpath1, + errno); + goto out; + } + + set_euid(UID_ROOT); + if (acct(fpath1) < 0) { + err(-1, "acct(NULL -> %s) setup for !root", fpath1); + goto out; + } + + set_euid(UID_OTHER); + error = acct(fpath2); + if (error == 0) { + warnx("acct(%s -> %s) succeeded as !root", fpath1, fpath2); + goto out; + } + if (errno != EPERM) { + warn("acct(%s -> %s) wrong errno %d as !root", fpath1, + fpath2, errno); + goto out; + } + + set_euid(UID_ROOT); + if (acct(fpath2) < 0) { + err(-1, "acct(%s -> %s) setup for !root", fpath1, fpath2); + goto out; + } + + set_euid(UID_OTHER); + error = acct(NULL); + if (error == 0) { + warnx("acct(%s -> NULL) succeeded as !root", fpath2); + goto out; + } + if (errno != EPERM) { + warn("acct(%s -> NULL) wrong errno %d as !root", fpath2, + errno); + goto out; + } + +out: + (void)seteuid(UID_ROOT); + (void)acct(NULL); + (void)unlink(fpath1); + (void)unlink(fpath2); +} diff --git a/tools/regression/priv/priv_adjtime.c b/tools/regression/priv/priv_adjtime.c new file mode 100644 index 0000000..791de97 --- /dev/null +++ b/tools/regression/priv/priv_adjtime.c @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Confirm that privilege is required to invoke adjtime(); first query, then + * try setting first with, and then without privilege. + */ + +#include <sys/time.h> + +#include <err.h> +#include <errno.h> +#include <unistd.h> + +#include "main.h" + +void +priv_adjtime(void) +{ + struct timeval tv; + int error; + + assert_root(); + + /* + * Query time adjustment. + */ + if (adjtime(NULL, &tv) < 0) + err(-1, "adjtime"); + + /* + * Set with privilege. + */ + if (adjtime(&tv, NULL) < 0) + err(-1, "adjtime as root"); + + /* + * Set without privilege. + */ + set_euid(UID_OTHER); + + error = adjtime(&tv, NULL); + if (error == 0) + errx(-1, "adjtime succeeded as !root"); + if (errno != EPERM) + errx(-1, "adjtime wrong errno %d as !root", errno); +} diff --git a/tools/regression/priv/priv_clock_settime.c b/tools/regression/priv/priv_clock_settime.c new file mode 100644 index 0000000..e3d1089 --- /dev/null +++ b/tools/regression/priv/priv_clock_settime.c @@ -0,0 +1,76 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Confirm that privilege is required to invoke clock_settime(). So as not + * to mess up the clock too much, first query the time, then immediately set + * it. Test only CLOCK_REALTIME. + */ + +#include <sys/time.h> + +#include <err.h> +#include <errno.h> +#include <unistd.h> + +#include "main.h" + +void +priv_clock_settime(void) +{ + struct timespec ts; + int error; + + assert_root(); + + /* + * Query time. + */ + if (clock_gettime(CLOCK_REALTIME, &ts) < 0) + err(-1, "clock_gettime"); + + /* + * Set with privilege. + */ + if (clock_settime(CLOCK_REALTIME, &ts) < 0) + err(-1, "clock_settime as root"); + + /* + * Set without privilege. + */ + set_euid(UID_OTHER); + + error = clock_settime(CLOCK_REALTIME, &ts); + if (error == 0) + errx(-1, "clock_settime succeeded as !root"); + if (errno != EPERM) + errx(-1, "clock_settime wrong errno %d as !root", errno); +} diff --git a/tools/regression/priv/priv_io.c b/tools/regression/priv/priv_io.c new file mode 100644 index 0000000..1af1847 --- /dev/null +++ b/tools/regression/priv/priv_io.c @@ -0,0 +1,135 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Test privilege check on /dev/io. By default, the permissions also protect + * against non-superuser access, so this program will modify permissions on + * /dev/io to allow group access for the wheel group, and revert the change + * on exit. This is not good for run-time security, but is necessary to test + * the checks properly. + */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +#include "main.h" + +#define NEW_PERMS 0660 +#define DEV_IO "/dev/io" +#define EXPECTED_PERMS 0600 + +static mode_t saved_perms; + +static void +save_perms(void) +{ + struct stat sb; + + if (stat(DEV_IO, &sb) < 0) + err(-1, "save_perms: stat(%s)", DEV_IO); + + saved_perms = sb.st_mode & ALLPERMS; + + if (saved_perms != EXPECTED_PERMS) + err(-1, "save_perms: perms = 0%o; expected 0%o", saved_perms, + EXPECTED_PERMS); + +} + +static void +set_perms(void) +{ + + if (chmod(DEV_IO, NEW_PERMS) < 0) + err(-1, "set_perms: chmod(%s, 0%o)", DEV_IO, NEW_PERMS); +} + +static void +restore_perms(void) +{ + + if (chmod(DEV_IO, saved_perms) < 0) + err(-1, "restore_perms: chmod(%s, 0%o)", DEV_IO, saved_perms); +} + +static void +try_open(const char *test_case, uid_t uid, int expected) +{ + int fd; + + set_euid(uid); + fd = open(DEV_IO, O_RDONLY); + if (expected == 0) { + if (fd == -1) { + warn("try_open: %s open(%s) errno %d", DEV_IO, + test_case, errno); + goto out; + } + close(fd); + goto out; + } + if (fd >= 0) { + warn("try_open: %s open(%s) unexpected success", test_case, + DEV_IO); + close(fd); + goto out; + } + if (errno == expected) + goto out; + warn("try_open: %s open(%s) wrong errno %d, expected %d", DEV_IO, + test_case, errno, expected); +out: + set_euid(UID_ROOT); +} + +void +priv_io(void) +{ + + assert_root(); + + save_perms(); + + try_open("root:0600", UID_ROOT, 0); + try_open("other", UID_OTHER, EACCES); + + set_perms(); + + try_open("root:0660", UID_ROOT, 0); + try_open("other", UID_OTHER, EPERM); + + restore_perms(); +} diff --git a/tools/regression/priv/priv_kenv_set.c b/tools/regression/priv/priv_kenv_set.c new file mode 100644 index 0000000..96e785d --- /dev/null +++ b/tools/regression/priv/priv_kenv_set.c @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Test that setting a kernel environment variable requires privilege, by + * first trying it with privilege, then without. + */ + +#include <sys/types.h> + +#include <err.h> +#include <errno.h> +#include <kenv.h> +#include <string.h> +#include <unistd.h> + +#include "main.h" + +void +priv_kenv_set(void) +{ + int error; + + assert_root(); + + error = kenv(KENV_SET, KENV_VAR_NAME, KENV_VAR_VALUE, + strlen(KENV_VAR_VALUE)+1); + if (error) + err(-1, "kenv(KENV_SET, ...) as root"); + + set_euid(UID_OTHER); + + error = kenv(KENV_SET, KENV_VAR_NAME, KENV_VAR_VALUE, + strlen(KENV_VAR_VALUE)+1); + if (error == 0) + errx(-1, "kenv(KENV_SET, ...) as !root succeeded"); + if (errno != EPERM) + err(-1, "kenv(KENV_SET, ...) as !root wrong errno %d", + errno); +} diff --git a/tools/regression/priv/priv_kenv_unset.c b/tools/regression/priv/priv_kenv_unset.c new file mode 100644 index 0000000..2bb800d --- /dev/null +++ b/tools/regression/priv/priv_kenv_unset.c @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Test that setting a kernel environment variable, then trying to unset it + * without and with privilege. + */ + +#include <sys/types.h> + +#include <err.h> +#include <errno.h> +#include <kenv.h> +#include <string.h> +#include <unistd.h> + +#include "main.h" + +void +priv_kenv_unset(void) +{ + int error; + + assert_root(); + + error = kenv(KENV_SET, KENV_VAR_NAME, KENV_VAR_VALUE, + strlen(KENV_VAR_VALUE)+1); + if (error) + err(-1, "kenv(KENV_SET, ...) as root"); + + error = kenv(KENV_UNSET, KENV_VAR_NAME, NULL, 0); + if (error) + err(-1, "kenv(KENV_UNSET, ...) as root"); + + /* Set it again for !root. */ + error = kenv(KENV_SET, KENV_VAR_NAME, KENV_VAR_VALUE, + strlen(KENV_VAR_VALUE)+1); + if (error) + err(-1, "kenv(KENV_SET, ...) as root"); + + set_euid(UID_OTHER); + + error = kenv(KENV_UNSET, KENV_VAR_NAME, NULL, 0); + if (error == 0) + errx(-1, "kenv(KENV_UNSET, ...) as !root succeeded"); + if (errno != EPERM) + err(-1, "kenv(KENV_UNSET, ...) as !root wrong errno %d", + errno); +} diff --git a/tools/regression/priv/priv_proc_setlogin.c b/tools/regression/priv/priv_proc_setlogin.c new file mode 100644 index 0000000..6f5e756 --- /dev/null +++ b/tools/regression/priv/priv_proc_setlogin.c @@ -0,0 +1,73 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Test that privilege is required to call setlogin(). Do so by first + * querying with getlogin(), then setting the result back using setlogin(), + * at first with privilege, then without. + */ + +#include <err.h> +#include <errno.h> +#include <unistd.h> + +#include "main.h" + +void +priv_proc_setlogin(void) +{ + char *loginname; + int error; + + assert_root(); + + loginname = getlogin(); + if (loginname == NULL) + err(-1, "getlogin"); + + /* + * First, with privilege. + */ + error = setlogin(loginname); + if (error) + err(-1, "setlogin(%s) as root", loginname); + + /* + * Then again, without privilege. + */ + set_euid(UID_OTHER); + + error = setlogin(loginname); + if (error == 0) + errx(-1, "setlogin(%s) succeeded as !root", loginname); + if (errno != EPERM) + err(-1, "setlogin(%s) wrong errno %d", loginname, errno); +} diff --git a/tools/regression/priv/priv_proc_setrlimit.c b/tools/regression/priv/priv_proc_setrlimit.c new file mode 100644 index 0000000..adf471b --- /dev/null +++ b/tools/regression/priv/priv_proc_setrlimit.c @@ -0,0 +1,137 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Test that raising current resource limits above hard resource limits + * requires privilege. There is one privilege check, but two conditions: + * + * - To raise the current above the maximum. + * + * - To raise the maximum. + */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> + +#include <err.h> +#include <errno.h> +#include <unistd.h> + +#include "main.h" + +void +priv_proc_setrlimit(void) +{ + struct rlimit rl, rl_lower, rl_raise_max, rl_raise_cur; + int error; + + assert_root(); + + /* + * To make sure that there is room to raise the resource limit, we + * must first lower it. Otherwise, if the resource limit is already + * at the global maximum, that complicates matters. In principle, we + * can bump into privilege failures during setup, but there's not + * much we can do about that. Keep this prototypical setting around + * as the target to restore to later. + */ + if (getrlimit(RLIMIT_DATA, &rl) < 0) + err(-1, "getrlimit(RLIMIT_DATA)"); + + /* + * What to lower to before trying to raise. + */ + rl_lower = rl; + rl_lower.rlim_cur -= 10; + rl_lower.rlim_max = rl_lower.rlim_cur; + + /* + * Raise the maximum. + */ + rl_raise_max = rl; + rl_raise_max.rlim_max += 10; + + /* + * Raise the current above the maximum. + */ + rl_raise_cur = rl; + rl_raise_cur.rlim_cur += 10; + + /* + * Test raising the maximum with privilege. + */ + if (setrlimit(RLIMIT_DATA, &rl_lower) < 0) + err(-1, "setrlimit(RLIMIT_DATA, lower) as root"); + + if (setrlimit(RLIMIT_DATA, &rl_raise_max) < 0) + err(-1, "setrlimit(RLIMIT_DATA, raise_max) as root"); + + /* + * Test raising the current above the maximum with privilege. + */ + if (setrlimit(RLIMIT_DATA, &rl_lower) < 0) + err(-1, "setrlimit(RLIMIT_DATA, lower) as root"); + + if (setrlimit(RLIMIT_DATA, &rl_raise_cur) < 0) + err(-1, "setrlimit(RLIMIT_DATA, raise_cur) as root"); + + /* + * Test raising the maximum without privilege. + */ + if (setrlimit(RLIMIT_DATA, &rl_lower) < 0) + err(-1, "setrlimit(RLIMIT_DATA, lower) as root"); + + set_euid(UID_OTHER); + error = setrlimit(RLIMIT_DATA, &rl_raise_max); + if (error == 0) + errx(-1, + "setrlimit(RLIMIT_DATA, raise_max) succeeded as !root"); + if (errno != EPERM) + err(-1, "setrlimit(RLIMIT_DATA, raise_max) wrong errno %d " + "as !root", errno); + + /* + * Test raising the current above the maximum without privilege. + */ + set_euid(UID_ROOT); + if (setrlimit(RLIMIT_DATA, &rl_lower) < 0) + err(-1, "setrlimit(RLIMIT_DATA, lower) as root"); + set_euid(UID_OTHER); + + error = setrlimit(RLIMIT_DATA, &rl_raise_cur); + if (error == 0) + errx(-1, + "setrlimit(RLIMIT_DATA, raise_cur) succeeded as !root"); + if (errno != EPERM) + err(-1, "setrlimit(RLIMIT_DATA, raise_cur) wrong errno %d " + "as !root", errno); +} diff --git a/tools/regression/priv/priv_sched_rtprio.c b/tools/regression/priv/priv_sched_rtprio.c new file mode 100644 index 0000000..72d1756 --- /dev/null +++ b/tools/regression/priv/priv_sched_rtprio.c @@ -0,0 +1,234 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Test privilege associated with real time process settings. There are + * three relevant notions of privilege: + * + * - Privilege to set the real-time priority of the current process. + * - Privilege to set the real-time priority of another process. + * - Privilege to set the idle priority of another process. + * - No privilege to set the idle priority of the current process. + * + * This requires a test process and a target (dummy) process running with + * various uids. This test is based on the code in the setpriority() test. + */ + +#include <sys/types.h> +#include <sys/rtprio.h> +#include <sys/wait.h> + +#include <err.h> +#include <errno.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> + +#include "main.h" + +static void +dummy(void) +{ + + while (1) + sleep(1); +} + +static void +collect(pid_t test_pid, pid_t dummy_pid) +{ + pid_t pid; + + /* + * First, collect the main test process. When it has exited, then + * kill off the dummy process. + */ + if (test_pid > 0) { + while (1) { + pid = waitpid(test_pid, NULL, 0); + if (pid == -1) + warn("waitpid(%d (test), NULL, 0)", test_pid); + if (pid == test_pid) + break; + } + } + + if (kill(dummy_pid, SIGKILL) < 0) + err(-1, "kill(%d, SIGKILL)", dummy_pid); + + while (1) { + pid = waitpid(dummy_pid, NULL, 0); + if (pid == -1) + warn("waitpid(%d, NULL, 0)", dummy_pid); + if (pid == dummy_pid) + return; + } +} + +static void +test(pid_t dummy_pid) +{ + struct rtprio rtp; + int error; + + /* + * Tests first as root. Test that we can set normal, realtime, and + * idle priorities on the current thread and on the dummy thread. + */ + rtp.type = RTP_PRIO_REALTIME; + rtp.prio = 0; + if (rtprio(RTP_SET, 0, &rtp) < 0) + err(-1, "rtprio(RTP_SET, 0, {REALTIME, 0}) as root"); + + rtp.type = RTP_PRIO_IDLE; + rtp.prio = 0; + if (rtprio(RTP_SET, 0, &rtp) < 0) + err(-1, "rtprio(RTP_SET, 0, {IDLE, 0}) as root"); + + rtp.type = RTP_PRIO_NORMAL; + rtp.prio = 0; + if (rtprio(RTP_SET, 0, &rtp) < 0) + err(-1, "rtprio(RTP_SET, 0, {NORMAL, 0) as root"); + + rtp.type = RTP_PRIO_REALTIME; + rtp.prio = 0; + if (rtprio(RTP_SET, dummy_pid, &rtp) < 0) + err(-1, "rtprio(RTP_SET, %d, {REALTIME, 0}) as root", + dummy_pid); + + rtp.type = RTP_PRIO_IDLE; + rtp.prio = 0; + if (rtprio(RTP_SET, dummy_pid, &rtp) < 0) + err(-1, "rtprio(RTP_SET, %d, {IDLE, 0}) as root", dummy_pid); + + rtp.type = RTP_PRIO_NORMAL; + rtp.prio = 0; + if (rtprio(RTP_SET, dummy_pid, &rtp) < 0) + err(-1, "rtprio(RTP_SET, %d, {NORMAL, 0) as root", + dummy_pid); + + /* + * Then test again as a different credential. + */ + if (setresuid(UID_OTHER, UID_OTHER, UID_OTHER) < 0) + err(-1, "setresuid(%d)", UID_OTHER); + + rtp.type = RTP_PRIO_REALTIME; + rtp.prio = 0; + error = rtprio(RTP_SET, 0, &rtp); + if (error == 0) + errx(-1, + "rtprio(RTP_SET, 0, {REALTIME, 0}) succeeded as !root"); + if (errno != EPERM) + err(-1, "rtprio(RTP_SET, 0, {REALTIME, 0}) wrong errno %d as" + " !root", errno); + + rtp.type = RTP_PRIO_IDLE; + rtp.prio = 0; + error = rtprio(RTP_SET, 0, &rtp); + if (error == 0) + errx(-1, "rtprio(RTP_SET, 0, {IDLE, 0}) succeeded as !root"); + if (errno != EPERM) + err(-1, "rtprio(RTP_SET, 0, {IDLE, 0}) wrong errno %d as " + "!root", errno); + + rtp.type = RTP_PRIO_NORMAL; + rtp.prio = 0; + if (rtprio(RTP_SET, 0, &rtp) < 0) + err(-1, "rtprio(RTP_SET, 0, {NORMAL, 0}) as !root"); + + rtp.type = RTP_PRIO_REALTIME; + rtp.prio = 0; + error = rtprio(RTP_SET, dummy_pid, &rtp); + if (error == 0) + errx(-1, + "rtprio(RTP_SET, %d, {REALTIME, 0}) succeeded as !root", + dummy_pid); + if (errno != EPERM) + err(-1, "rtprio(RTP_SET, %d, {REALTIME, 0}) wrong errno %d as" + " !root", dummy_pid, errno); + + rtp.type = RTP_PRIO_IDLE; + rtp.prio = 0; + error = rtprio(RTP_SET, dummy_pid, &rtp); + if (error == 0) + errx(-1, "rtprio(RTP_SET, %d, {IDLE, 0}) succeeded as !root", + dummy_pid); + if (errno != EPERM) + err(-1, + "rtprio(RTP_SET, %d, {IDLE, 0}) wrong errno %d as !root", + dummy_pid, errno); + + rtp.type = RTP_PRIO_NORMAL; + rtp.prio = 0; + error = rtprio(RTP_SET, dummy_pid, &rtp); + if (error == 0) + errx(-1, + "rtprio(RTP_SET, %d, {NORMAL, 0) succeeded as !root", + dummy_pid); + if (errno != EPERM) + err(-1, "rtprio(RTP_SET, %d, {NORMAL, 0}) wrong errno %d as " + "!root", dummy_pid, errno); + + exit(0); +} + +void +priv_sched_rtprio(void) +{ + pid_t dummy_pid, test_pid; + + assert_root(); + + /* + * Set up dummy process, which we will kill before exiting. + */ + dummy_pid = fork(); + if (dummy_pid < 0) + err(-1, "fork - dummy"); + if (dummy_pid == 0) { + if (setresuid(UID_THIRD, UID_THIRD, UID_THIRD) < 0) + err(-1, "setresuid(%d)", UID_THIRD); + dummy(); + } + sleep(1); /* Allow dummy thread to change uids. */ + + test_pid = fork(); + if (test_pid < 0) { + warn("fork - test"); + collect(-1, dummy_pid); + return; + } + if (test_pid == 0) + test(dummy_pid); + + collect(test_pid, dummy_pid); +} diff --git a/tools/regression/priv/priv_sched_setpriority.c b/tools/regression/priv/priv_sched_setpriority.c new file mode 100644 index 0000000..4d56813 --- /dev/null +++ b/tools/regression/priv/priv_sched_setpriority.c @@ -0,0 +1,163 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Test that privilege is required to lower nice value; first test with, then + * without. There are two failure modes associated with privilege: the right + * to renice a process with a different uid, and the right to renice to a + * lower priority. Because both the real and effective uid are part of the + * permissions test, we have to create two children processes with different + * uids. + */ + +#include <sys/types.h> +#include <sys/resource.h> +#include <sys/wait.h> + +#include <err.h> +#include <errno.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> + +#include "main.h" + +static void +dummy(void) +{ + + while (1) + sleep(1); +} + +static void +collect(pid_t test_pid, pid_t dummy_pid) +{ + pid_t pid; + + /* + * First, collect the main test process. When it has exited, then + * kill off the dummy process. + */ + if (test_pid > 0) { + while (1) { + pid = waitpid(test_pid, NULL, 0); + if (pid == -1) + warn("waitpid(%d (test), NULL, 0)", test_pid); + if (pid == test_pid) + break; + } + } + + if (kill(dummy_pid, SIGKILL) < 0) + err(-1, "kill(%d, SIGKILL)", dummy_pid); + + while (1) { + pid = waitpid(dummy_pid, NULL, 0); + if (pid == -1) + warn("waitpid(%d, NULL, 0)", dummy_pid); + if (pid == dummy_pid) + return; + } +} + +static void +test(pid_t dummy_pid) +{ + int error; + + /* + * Tests first as root. + */ + if (setpriority(PRIO_PROCESS, 0, -1) < 0) + err(-1, "setpriority(PRIO_PROCESS, 0, -1) as root"); + + if (setpriority(PRIO_PROCESS, dummy_pid, -1) < 0) + err(-1, "setpriority(PRIO_PROCESS, %d, -1) as root", + dummy_pid); + + /* + * Then test again as a different credential. + */ + if (setresuid(UID_OTHER, UID_OTHER, UID_OTHER) < 0) + err(-1, "setresuid(%d)", UID_OTHER); + + error = setpriority(PRIO_PROCESS, 0, -2); + if (error == 0) + errx(-1, + "setpriority(PRIO_PROCESS, 0, -2) succeeded as !root"); + if (errno != EACCES) + err(-1, "setpriority(PRIO_PROCESS, 0, 2) wrong errno %d as " + "!root", errno); + + error = setpriority(PRIO_PROCESS, dummy_pid, -2); + if (error == 0) + errx(-1, + "setpriority(PRIO_PROCESS, %d, -2) succeeded as !root", + dummy_pid); + if (errno != EPERM) + err(-1, "setpriority(PRIO_PROCESS, %d, 2) wrong errno %d as " + "!root", dummy_pid, errno); + + exit(0); +} + +void +priv_sched_setpriority(void) +{ + pid_t dummy_pid, test_pid; + + assert_root(); + + /* + * Set up dummy process, which we will kill before exiting. + */ + dummy_pid = fork(); + if (dummy_pid < 0) + err(-1, "fork - dummy"); + if (dummy_pid == 0) { + if (setresuid(UID_THIRD, UID_THIRD, UID_THIRD) < 0) + err(-1, "setresuid(%d)", UID_THIRD); + dummy(); + } + sleep(1); /* Allow dummy thread to change uids. */ + + test_pid = fork(); + if (test_pid < 0) { + warn("fork - test"); + collect(-1, dummy_pid); + return; + } + if (test_pid == 0) + test(dummy_pid); + + collect(test_pid, dummy_pid); +} diff --git a/tools/regression/priv/priv_settimeofday.c b/tools/regression/priv/priv_settimeofday.c new file mode 100644 index 0000000..d18582f --- /dev/null +++ b/tools/regression/priv/priv_settimeofday.c @@ -0,0 +1,76 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Confirm that privilege is required to invoke settimeofday(). So as not + * to mess up the clock too much, first query the time, then immediately set + * it. + */ + +#include <sys/time.h> + +#include <err.h> +#include <errno.h> +#include <unistd.h> + +#include "main.h" + +void +priv_settimeofday(void) +{ + struct timeval tv; + int error; + + assert_root(); + + /* + * Query time. + */ + if (gettimeofday(&tv, NULL) < 0) + err(-1, "gettimeofday"); + + /* + * Set with privilege. + */ + if (settimeofday(&tv, NULL) < 0) + err(-1, "settimeofday as root"); + + /* + * Set without privilege. + */ + set_euid(UID_OTHER); + + error = settimeofday(&tv, NULL); + if (error == 0) + errx(-1, "settimeofday succeeded as !root"); + if (errno != EPERM) + errx(-1, "settimeofday wrong errno %d as !root", errno); +} diff --git a/tools/regression/priv/priv_sysctl_write.c b/tools/regression/priv/priv_sysctl_write.c new file mode 100644 index 0000000..becff06 --- /dev/null +++ b/tools/regression/priv/priv_sysctl_write.c @@ -0,0 +1,89 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Test that sysctls can only be written with privilege by trying first with, + * then without privilege. Do this by first reading, then setting the + * hostname as a no-op. + */ + +#include <sys/types.h> +#include <sys/sysctl.h> + +#include <err.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include "main.h" + +#define KERN_HOSTNAME_STRING "kern.hostname" + +void +priv_sysctl_write(void) +{ + char buffer[1024]; + size_t len; + int error; + + assert_root(); + + /* + * First query the current value. + */ + len = sizeof(buffer); + error = sysctlbyname(KERN_HOSTNAME_STRING, buffer, &len, NULL, 0); + if (error) + err(-1, "sysctlbyname(\"%s\") query", KERN_HOSTNAME_STRING); + + /* + * Now try to set with privilege. + */ + error = sysctlbyname(KERN_HOSTNAME_STRING, NULL, NULL, buffer, + strlen(buffer)); + if (error) + err(-1, "sysctlbyname(\"%s\") set as root", + KERN_HOSTNAME_STRING); + + /* + * Now without privilege. + */ + set_euid(UID_OTHER); + + error = sysctlbyname(KERN_HOSTNAME_STRING, NULL, NULL, buffer, + strlen(buffer)); + if (error == 0) + errx(-1, "sysctlbyname(\"%s\") succeeded as !root", + KERN_HOSTNAME_STRING); + if (errno != EPERM) + err(-1, "sysctlbyname(\"%s\") wrong errno %d", + KERN_HOSTNAME_STRING, errno); +} diff --git a/tools/regression/priv/priv_vfs_admin.c b/tools/regression/priv/priv_vfs_admin.c new file mode 100644 index 0000000..f06e9b2 --- /dev/null +++ b/tools/regression/priv/priv_vfs_admin.c @@ -0,0 +1,328 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Check that privilege is required for a variety of administrative + * activities on a file owned by another user. Admin privileges are required + * for the following services: + * + * - Set file flags. + * - Set utimes to non-NULL. + * - Set file mode. + * - Set file ownership. + * - Remove a file from a sticky directory. (XXXRW: Not tested here.) + * - Set the ACL on a file. (XXXRW: Not tested here.) + * - Delete the ACL on a file. (XXXRW: Not tested here.) + */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "main.h" + +static u_long +getflags(char *fpathp) +{ + struct stat sb; + + if (stat(fpathp, &sb) < 0) + err(-1, "stat(%s)", fpathp); + + return (sb.st_flags); +} + +static void +priv_vfs_admin_chflags(void) +{ + char fpath[1024]; + u_long flags; + int error; + + /* + * Test that setting file flags works as and not as the file owner + * when running with privilege, but only as the file owner when + * running without privilege. + */ + setup_file(fpath, UID_ROOT, GID_WHEEL, 0600); + flags = getflags(fpath); + flags |= UF_NODUMP; + if (chflags(fpath, flags) < 0) { + warn("chflags(%s, UF_NODUMP) owner as root", fpath); + goto out; + } + (void)unlink(fpath); + + setup_file(fpath, UID_OWNER, GID_OWNER, 0600); + flags = getflags(fpath); + flags |= UF_NODUMP; + if (chflags(fpath, flags) < 0) { + warn("chflags(%s, UF_NODUMP) !owner as root", fpath); + goto out; + } + (void)unlink(fpath); + + setup_file(fpath, UID_OWNER, GID_OWNER, 0600); + flags = getflags(fpath); + flags |= UF_NODUMP; + set_euid(UID_OWNER); + if (chflags(fpath, flags) < 0) { + warn("chflags(%s, UF_NODUMP) owner as !root", fpath); + goto out; + } + set_euid(UID_ROOT); + (void)unlink(fpath); + + setup_file(fpath, UID_OWNER, GID_OWNER, 0600); + flags = getflags(fpath); + flags |= UF_NODUMP; + set_euid(UID_OTHER); + error = chflags(fpath, flags); + if (error == 0) { + warnx("chflags(%s, UF_NODUMP) succeeded !owner as !root", + fpath); + goto out; + } + if (errno != EPERM) { + warn("chflags(%s, UF_NODUMP) wrong errno %d !owner a !root", + fpath, errno); + goto out; + } + set_euid(UID_ROOT); + (void)unlink(fpath); + +out: + (void)seteuid(UID_ROOT); + (void)unlink(fpath); +} + +static void +priv_vfs_admin_utimes(void) +{ + struct timeval tv[2]; + char fpath[1024]; + int error; + + /* + * Actual values don't matter here. + */ + tv[0].tv_sec = 0; + tv[0].tv_usec = 0; + tv[1].tv_sec = 0; + tv[1].tv_usec = 0; + + /* + * When using a non-NULL argument to utimes(), must either hold + * privilege or be the file owner. Check all four possible + * combinations of privilege, ownership. + */ + setup_file(fpath, UID_ROOT, GID_WHEEL, 0600); + if (utimes(fpath, tv) < 0) { + warn("utimes(%s, !NULL) owner as root", fpath); + goto out; + } + (void)unlink(fpath); + + setup_file(fpath, UID_OWNER, GID_OWNER, 0600); + if (utimes(fpath, tv) < 0) { + warn("utimes(%s, !NULL) !owner as root", fpath); + goto out; + } + (void)unlink(fpath); + + setup_file(fpath, UID_OWNER, GID_OWNER, 0600); + set_euid(UID_OWNER); + if (utimes(fpath, tv) < 0) { + warn("utimes(%s, !NULL) owner as !root", fpath); + goto out; + } + set_euid(UID_ROOT); + (void)unlink(fpath); + + setup_file(fpath, UID_OWNER, GID_OWNER, 0600); + set_euid(UID_OTHER); + error = utimes(fpath, tv); + if (error == 0) { + warnx("utimes(%s, !NULL) succeeded !owner as !root", + fpath); + goto out; + } + if (errno != EPERM) { + warn("utimes(%s, !NULL) wrong errno %d !owner a !root", + fpath, errno); + goto out; + } + set_euid(UID_ROOT); + (void)unlink(fpath); + +out: + (void)seteuid(UID_ROOT); + (void)unlink(fpath); +} + +static void +priv_vfs_admin_chmod(void) +{ + char fpath[1024]; + int error; + + /* + * Test that setting file permissions works either as file owner or + * not when running with privilege, but only as file owner when + * running without privilege. + */ + setup_file(fpath, UID_ROOT, GID_WHEEL, 0600); + if (chmod(fpath, 0640) < 0) { + warn("chmod(%s, 0640) owner as root", fpath); + goto out; + } + (void)unlink(fpath); + + setup_file(fpath, UID_OWNER, GID_OWNER, 0600); + if (chmod(fpath, 0640) < 0) { + warn("chmod(%s, 0640) !owner as root", fpath); + goto out; + } + (void)unlink(fpath); + + setup_file(fpath, UID_OWNER, GID_OWNER, 0600); + set_euid(UID_OWNER); + if (chmod(fpath, 0640) < 0) { + warn("chmod(%s, 0640) owner as !root", fpath); + goto out; + } + set_euid(UID_ROOT); + (void)unlink(fpath); + + setup_file(fpath, UID_OWNER, GID_OWNER, 0600); + set_euid(UID_OTHER); + error = chmod(fpath, 0640); + if (error == 0) { + warnx("chmod(%s, 0640) succeeded !owner as !root", + fpath); + goto out; + } + if (errno != EPERM) { + warn("chmod(%s, 0640) wrong errno %d !owner a !root", + fpath, errno); + goto out; + } + set_euid(UID_ROOT); + (void)unlink(fpath); + +out: + (void)seteuid(UID_ROOT); + (void)unlink(fpath); +} + +static const gid_t gidset[] = { GID_WHEEL, GID_OWNER, GID_OTHER}; + +static void +priv_vfs_admin_chown(void) +{ + char fpath[1024]; + int error; + + /* + * Test that the group of the file can only be changed with privilege + * or as the owner. These test is run last as it frobs the group + * context. We change the file group from one group we're in to + * another we're in to avoid any other access control checks failing. + */ + if (setgroups(3, gidset) < 0) + err(-1, "priv_vfs_admin_chown:setgroups(3, {%d, %d, %d})", + GID_WHEEL, GID_OWNER, GID_OTHER); + + /* + * Test that setting file permissions works either as file owner or + * not when running with privilege, but only as file owner when + * running without privilege. + */ + setup_file(fpath, UID_ROOT, GID_WHEEL, 0600); + if (chown(fpath, -1, GID_OWNER) < 0) { + warn("chown(%s, %d) owner as root", fpath, GID_OWNER); + goto out; + } + (void)unlink(fpath); + + setup_file(fpath, UID_OWNER, GID_OWNER, 0600); + if (chown(fpath, -1, GID_OWNER) < 0) { + warn("chown(%s, %d) !owner as root", fpath, GID_OWNER); + goto out; + } + (void)unlink(fpath); + + setup_file(fpath, UID_OWNER, GID_OWNER, 0600); + set_euid(UID_OWNER); + if (chown(fpath, -1, GID_OWNER) < 0) { + warn("chown(%s, %d) owner as !root", fpath, GID_OWNER); + goto out; + } + set_euid(UID_ROOT); + (void)unlink(fpath); + + setup_file(fpath, UID_OWNER, GID_OWNER, 0600); + set_euid(UID_OTHER); + error = chown(fpath, -1, GID_OWNER); + if (error == 0) { + warnx("chown(%s, %d) succeeded !owner as !root", + fpath, GID_OWNER); + goto out; + } + if (errno != EPERM) { + warn("chown(%s, %d) wrong errno %d !owner a !root", + fpath, GID_OWNER, errno); + goto out; + } + set_euid(UID_ROOT); + (void)unlink(fpath); + +out: + (void)seteuid(UID_ROOT); + (void)unlink(fpath); +} + +void +priv_vfs_admin(void) +{ + + assert_root(); + + priv_vfs_admin_chflags(); + priv_vfs_admin_utimes(); + priv_vfs_admin_chmod(); + priv_vfs_admin_chown(); /* Run this last. */ +} diff --git a/tools/regression/priv/priv_vfs_chown.c b/tools/regression/priv/priv_vfs_chown.c new file mode 100644 index 0000000..8d72ac2 --- /dev/null +++ b/tools/regression/priv/priv_vfs_chown.c @@ -0,0 +1,170 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Confirm that privilege is required in the cases using chown(): + * + * - If the process euid does not match the file uid. + * + * - If the target uid is different than the current uid. + * + * - If the target gid changes and we the process is not a member of the new + * group. + */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> + +#include "main.h" + +const gid_t gidset[] = {GID_WHEEL, GID_OWNER}; + +void +priv_vfs_chown(void) +{ + char fpath[1024]; + int error; + + assert_root(); + + /* + * Before beginning, set up group set for process. Place in wheel + * and owner groups; don't put in other group so that when we chown + * to the other group, it's as a non-member. + */ + if (setgroups(2, gidset) < 0) + err(-1, "setgroups(2, {%d, %d})", GID_WHEEL, GID_OWNER); + + /* + * In the first pass, confirm that all works as desired with + * privilege. + * + * Check that chown when non-owner works fine. Do a no-op change to + * avoid other permission checks. Note that we can't request + * (-1, -1) and get an access control check, we have to request + * specific uid/gid that are not the same. + */ + setup_file(fpath, UID_OWNER, GID_OWNER, 0600); + if (chown(fpath, -1, GID_OWNER) < 0) { + warn("chown(%s, -1, %d) as root", fpath, GID_OWNER); + goto out; + } + (void)unlink(fpath); + + /* + * Check that chown changing uid works with privilege. + */ + setup_file(fpath, UID_OWNER, GID_OWNER, 0600); + if (chown(fpath, UID_OTHER, -1) < 0) { + warn("chown(%s, %d, -1) as root", fpath, UID_OTHER); + goto out; + } + (void)unlink(fpath); + + /* + * Check that can change the file group to one we are not a member of + * when running with privilege. + */ + setup_file(fpath, UID_OWNER, GID_OWNER, 0600); + if (chown(fpath, -1, GID_OTHER) < 0) { + warn("chown(%s, -1, %d) as root", fpath, GID_OTHER); + goto out; + } + (void)unlink(fpath); + + /* + * Now, the same again, but without privilege. + * + * Confirm that we can't chown a file we don't own, even as a no-op. + */ + setup_file(fpath, UID_OWNER, GID_OWNER, 0600); + set_euid(UID_OTHER); + error = chown(fpath, -1, GID_OWNER); + if (error == 0) { + warnx("chown(%s, -1, %d) succeeded as !root, non-owner", + fpath, GID_OWNER); + goto out; + } + if (errno != EPERM) { + warn("chown(%s, -1, %d) wrong errno %d as !root, non-owner", + fpath, GID_OWNER, errno); + goto out; + } + set_euid(UID_ROOT); + (void)unlink(fpath); + + /* + * Check that we can't change the uid of the file without privilege, + * even though we own the file. + */ + setup_file(fpath, UID_OWNER, GID_OWNER, 0600); + set_euid(UID_OWNER); + error = chown(fpath, UID_OTHER, -1); + if (error == 0) { + warnx("chown(%s, %d, -1) succeeded as !root", fpath, + UID_OTHER); + goto out; + } + if (errno != EPERM) { + warn("chown(%s, %d, -1) wrong errno %d as !root", fpath, + UID_OTHER, errno); + goto out; + } + set_euid(UID_ROOT); + (void)unlink(fpath); + + /* + * Check that can't change the file group to one we are not a member + * of when running without privilege. + */ + setup_file(fpath, UID_OWNER, GID_OWNER, 0600); + set_euid(UID_OWNER); + error = chown(fpath, -1, GID_OTHER); + if (error == 0) { + warn("chown(%s, -1, %d) succeeded as !root", fpath, GID_OTHER); + goto out; + } + if (errno != EPERM) { + warn("chown(%s, -1, %d) wrong errno %d as !root", fpath, + errno, GID_OTHER); + goto out; + } + set_euid(UID_ROOT); + (void)unlink(fpath); +out: + (void)seteuid(UID_ROOT); + (void)unlink(fpath); +} diff --git a/tools/regression/priv/priv_vfs_chroot.c b/tools/regression/priv/priv_vfs_chroot.c new file mode 100644 index 0000000..06c36d4 --- /dev/null +++ b/tools/regression/priv/priv_vfs_chroot.c @@ -0,0 +1,63 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Test that chroot() requires privilege; try with, and without. Do a no-op + * chroot() to "/". + * + * XXXRW: Would also be good to check fchroot() permission, but that is not + * exposed via the BSD API. + */ + +#include <err.h> +#include <errno.h> +#include <unistd.h> + +#include "main.h" + +void +priv_vfs_chroot(void) +{ + int error; + + assert_root(); + + if (chroot("/") < 0) + err(-1, "chroot(\"/\") as root"); + + set_euid(UID_OTHER); + + error = chroot("/"); + if (error == 0) + errx(-1, "chroot(\"/\") succeeded as !root"); + if (errno != EPERM) + err(-1, "chroot(\"/\") wrong errno %d as !root", errno); +} diff --git a/tools/regression/priv/priv_vfs_clearsugid.c b/tools/regression/priv/priv_vfs_clearsugid.c new file mode 100644 index 0000000..a8ddcd5 --- /dev/null +++ b/tools/regression/priv/priv_vfs_clearsugid.c @@ -0,0 +1,215 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * There are three cases in which the file system will clear the setuid or + * setgid bits on a file when running as !root: + * + * - When the file is chown()'d and either of the uid or the gid is changed. + * + * - The file is written to succeesfully. + * + * - An extended attribute of the file is written to successfully. + * + * Test each case first as root (that flags aren't cleared), and then as + * !root, to check they are cleared. + */ + +#include <sys/types.h> +#include <sys/extattr.h> +#include <sys/stat.h> + +#include <err.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "main.h" + +static const gid_t gidset[] = {GID_WHEEL, GID_OWNER, GID_OTHER}; + +/* + * Confirm that the setuid bit is set on a file. Don't return on failure. + */ +static void +confirm_setuid(char *fpathp, char *test_case) +{ + struct stat sb; + + if (stat(fpathp, &sb) < 0) { + warn("%s stat(%s)", test_case, fpathp); + (void)seteuid(UID_ROOT); + (void)unlink(fpathp); + exit(-1); + } + if (!(sb.st_mode & S_ISUID)) { + warnx("case %s stat(%s) not setuid", test_case, fpathp); + (void)seteuid(UID_ROOT); + (void)unlink(fpathp); + exit(-1); + } +} + +/* + * Confirm that the setuid bit is not set on a file. Don't return on failure. + */ +static void +confirm_notsetuid(char *fpathp, char *test_case) +{ + struct stat sb; + + if (stat(fpathp, &sb) < 0) { + warn("%s stat(%s)", test_case, fpathp); + (void)seteuid(UID_ROOT); + (void)unlink(fpathp); + exit(-1); + } + if (sb.st_mode & S_ISUID) { + warnx("case %s stat(%s) is setuid", test_case, fpathp); + (void)seteuid(UID_ROOT); + (void)unlink(fpathp); + exit(-1); + } +} + +#define EA_NAMESPACE EXTATTR_NAMESPACE_USER +#define EA_NAME "clearsugid" +#define EA_DATA "test" +#define EA_SIZE (strlen(EA_DATA)) +void +priv_vfs_clearsugid(void) +{ + char ch, fpath[1024]; + int fd; + + assert_root(); + + /* + * Before starting on work, set up group IDs so that the process can + * change the group ID of the file without privilege, in order to see + * the effects. That way privilege is only required to maintain the + * setuid bit. For the chown() test, we change only the group id, as + * that can be done with or without privilege. + */ + if (setgroups(3, gidset) < 0) + err(-1, "setgroups(2, {%d, %d})", GID_WHEEL, GID_OWNER); + + /* + * chown() with privilege. + */ + setup_file(fpath, UID_ROOT, GID_WHEEL, 0600 | S_ISUID); + if (chown(fpath, -1, GID_OTHER) < 0) + warn("chown(%s, -1, %d) as root", fpath, GID_OTHER); + confirm_setuid(fpath, "chown as root"); + (void)unlink(fpath); + + /* + * write() with privilege. + */ + setup_file(fpath, UID_ROOT, GID_WHEEL, 0600 | S_ISUID); + fd = open(fpath, O_RDWR); + if (fd < 0) { + warn("open(%s) as root", fpath); + goto out; + } + ch = 0; + if (write(fd, &ch, sizeof(ch)) < 0) { + warn("write(%s) as root", fpath); + goto out; + } + close(fd); + confirm_setuid(fpath, "write as root"); + (void)unlink(fpath); + + /* + * extwrite() with privilege. + */ + setup_file(fpath, UID_ROOT, GID_WHEEL, 0600 | S_ISUID); + if (extattr_set_file(fpath, EA_NAMESPACE, EA_NAME, EA_DATA, EA_SIZE) + < 0) { + warn("extattr_set_file(%s, user, %s, %s, %d) as root", + fpath, EA_NAME, EA_DATA, EA_SIZE); + goto out; + } + confirm_setuid(fpath, "extwrite as root"); + (void)unlink(fpath); + + /* + * chown() without privilege. + */ + setup_file(fpath, UID_OWNER, GID_OWNER, 0600 | S_ISUID); + set_euid(UID_OWNER); + if (chown(fpath, -1, GID_OTHER) < 0) + warn("chown(%s, -1, %d) as !root", fpath, GID_OTHER); + set_euid(UID_ROOT); + confirm_notsetuid(fpath, "chown as !root"); + (void)unlink(fpath); + + /* + * write() without privilege. + */ + setup_file(fpath, UID_OWNER, GID_OWNER, 0600 | S_ISUID); + set_euid(UID_OWNER); + fd = open(fpath, O_RDWR); + if (fd < 0) { + warn("open(%s) as !root", fpath); + goto out; + } + ch = 0; + if (write(fd, &ch, sizeof(ch)) < 0) { + warn("write(%s) as !root", fpath); + goto out; + } + close(fd); + set_euid(UID_ROOT); + confirm_notsetuid(fpath, "write as !root"); + (void)unlink(fpath); + + /* + * extwrite() without privilege. + */ + setup_file(fpath, UID_OWNER, GID_OWNER, 0600 | S_ISUID); + set_euid(UID_OWNER); + if (extattr_set_file(fpath, EA_NAMESPACE, EA_NAME, EA_DATA, EA_SIZE) + < 0) { + warn("extattr_set_file(%s, user, %s, %s, %d) as !root", + fpath, EA_NAME, EA_DATA, EA_SIZE); + goto out; + } + set_euid(UID_ROOT); + confirm_notsetuid(fpath, "extwrite as !root"); + (void)unlink(fpath); + +out: + (void)seteuid(UID_ROOT); + (void)unlink(fpath); +} diff --git a/tools/regression/priv/priv_vfs_extattr_system.c b/tools/regression/priv/priv_vfs_extattr_system.c new file mode 100644 index 0000000..d67c550 --- /dev/null +++ b/tools/regression/priv/priv_vfs_extattr_system.c @@ -0,0 +1,96 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Test that privilege is required to write to the system extended attribute + * namespace. + */ + +#include <sys/types.h> +#include <sys/extattr.h> + +#include <err.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include "main.h" + +#define EA_NAMESPACE EXTATTR_NAMESPACE_SYSTEM +#define EA_NAME "test" +#define EA_DATA "test" +#define EA_SIZE strlen(EA_DATA) + +void +priv_vfs_extattr_system(void) +{ + char fpath[1024]; + int error; + + assert_root(); + + /* + * Set file perms so that discretionary access control would grant + * write rights on non-system EAs on the file. + */ + setup_file(fpath, UID_ROOT, GID_WHEEL, 0666); + + /* + * Try with privilege. + */ + if (extattr_set_file(fpath, EA_NAMESPACE, EA_NAME, EA_DATA, EA_SIZE) + < 0) { + warn("extattr_set_file(SYSTEM, %s, %s, %d) as root", + EA_NAME, EA_DATA, EA_SIZE); + goto out; + } + + set_euid(UID_OTHER); + + /* + * Try without privilege. + */ + error = extattr_set_file(fpath, EA_NAMESPACE, EA_NAME, EA_DATA, + EA_SIZE); + if (error == 0) { + warn("extattr_set_file(SYSTEM, %s, %s, %d) succeeded as !root", + EA_NAME, EA_DATA, EA_SIZE); + goto out; + } + if (errno != EPERM) { + warn("extattr_set_file(SYSTEM, %s, %s, %d) wrong errno %d " + "as !root", EA_NAME, EA_DATA, EA_SIZE, errno); + goto out; + } +out: + seteuid(UID_ROOT); + (void)unlink(fpath); +} diff --git a/tools/regression/priv/priv_vfs_fhopen.c b/tools/regression/priv/priv_vfs_fhopen.c new file mode 100644 index 0000000..cb4006d --- /dev/null +++ b/tools/regression/priv/priv_vfs_fhopen.c @@ -0,0 +1,92 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Confirm that calls to fhopen() require privilege, trying with, and + * without. We create a temporary file and grab the file handle using + * getfh() before starting. + */ + +#include <sys/param.h> +#include <sys/mount.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +#include "main.h" + +void +priv_vfs_fhopen(void) +{ + char fpath[1024]; + fhandle_t fh; + int fd; + + assert_root(); + + setup_file(fpath, UID_ROOT, GID_WHEEL, 0644); + + if (getfh(fpath, &fh) < 0) { + warn("getfh(%s)", fpath); + goto out; + } + + /* + * First, try with privilege. + */ + fd = fhopen(&fh, O_RDONLY); + if (fd < 0) { + warn("fhopen(%s) as root", fpath); + goto out; + } + close(fd); + + /* + * Now, without privilege. + */ + set_euid(UID_OTHER); + + fd = fhopen(&fh, O_RDONLY); + if (fd >= 0) { + warnx("fhopen(%s) succeeded as !root", fpath); + close(fd); + goto out; + } + if (errno != EPERM) { + warn("fhopen(%s) wrong errno %d as !root", fpath, errno); + goto out; + } +out: + seteuid(UID_ROOT); + (void)unlink(fpath); +} diff --git a/tools/regression/priv/priv_vfs_fhstat.c b/tools/regression/priv/priv_vfs_fhstat.c new file mode 100644 index 0000000..c8e58e8 --- /dev/null +++ b/tools/regression/priv/priv_vfs_fhstat.c @@ -0,0 +1,89 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Confirm that calls to fhstat() require privilege, trying with, and + * without. We create a temporary file and grab the file handle using + * getfh() before starting. + */ + +#include <sys/param.h> +#include <sys/mount.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <unistd.h> + +#include "main.h" + +void +priv_vfs_fhstat(void) +{ + char fpath[1024]; + struct stat sb; + fhandle_t fh; + int error; + + assert_root(); + + setup_file(fpath, UID_ROOT, GID_WHEEL, 0644); + + if (getfh(fpath, &fh) < 0) { + warn("getfh(%s)", fpath); + goto out; + } + + /* + * First, try with privilege. + */ + if (fhstat(&fh, &sb) < 0) { + warn("fhstat(%s) as root", fpath); + goto out; + } + + /* + * Now, without privilege. + */ + set_euid(UID_OTHER); + error = fhstat(&fh, &sb); + if (error == 0) { + warn("fhstat(%s) succeeded as !root", fpath); + goto out; + } + if (errno != EPERM) { + warn("fhstat(%s) wrong errno %d as !root", fpath, errno); + goto out; + } +out: + seteuid(UID_ROOT); + (void)unlink(fpath); +} diff --git a/tools/regression/priv/priv_vfs_fhstatfs.c b/tools/regression/priv/priv_vfs_fhstatfs.c new file mode 100644 index 0000000..4709fe2 --- /dev/null +++ b/tools/regression/priv/priv_vfs_fhstatfs.c @@ -0,0 +1,89 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Confirm that calls to fhstatfs() require privilege, trying with, and + * without. We create a temporary file and grab the file handle using + * getfh() before starting. + */ + +#include <sys/param.h> +#include <sys/mount.h> + +#include <err.h> +#include <errno.h> +#include <unistd.h> + +#include "main.h" + +void +priv_vfs_fhstatfs(void) +{ + char fpath[1024]; + struct statfs sf; + fhandle_t fh; + int error; + + assert_root(); + + setup_file(fpath, UID_ROOT, GID_WHEEL, 0644); + + if (getfh(fpath, &fh) < 0) { + warn("getfh(%s)", fpath); + goto out; + } + + /* + * First, try with privilege. + */ + if (fhstatfs(&fh, &sf) < 0) { + warn("fhstatfs(%s) as root", fpath); + goto out; + } + + /* + * Now, without privilege. + */ + set_euid(UID_OTHER); + + error = fhstatfs(&fh, &sf); + if (error == 0) { + warnx("fhstatfs(%s) succeeded as !root", fpath); + goto out; + } + if (errno != EPERM) { + warn("fhstatfs(%s) wrong errno %d as !root", fpath, errno); + goto out; + } +out: + seteuid(UID_ROOT); + (void)unlink(fpath); +} diff --git a/tools/regression/priv/priv_vfs_generation.c b/tools/regression/priv/priv_vfs_generation.c new file mode 100644 index 0000000..2ccca79 --- /dev/null +++ b/tools/regression/priv/priv_vfs_generation.c @@ -0,0 +1,113 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Confirm that a generation number isn't returned by stat() when not running + * with privilege. In order to differentiate between a generation of 0 and + * a generation not being returned, we have to create a temporary file known + * to have a non-0 generation. We try up to 10 times, and then give up, + * which is non-ideal, but better than not testing for a problem. + */ + +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include "main.h" + +/* + * Can't use setup_file() since the resulting file needs to have specific + * properties. + */ +void +priv_vfs_generation(void) +{ + char fpath[1024] = "/tmp/priv.XXXXXXXXXX"; + struct stat sb; + int fd, i; + + assert_root(); + + /* + * Create a file with a non-0 generation number. Try ten times, + * which gives a high chance of succeeds, fail otherwise. Not ideal, + * since we can't distinguish the file having a generation of 0 from + * not being able to query it for access control reasons. The perils + * of an API that changes behavior based on lack of privilege rather + * than failing... + */ + for (i = 0; i < 10; i++) { + fd = mkstemp(fpath); + if (fd < 0) + err(-1, "mkstemp"); + if (fstat(fd, &sb) < 0) { + warn("fstat(%s)", fpath); + close(fd); + goto out; + } + if (sb.st_gen != 0) + break; + close(fd); + (void)unlink(fpath); + strcpy(fpath, "/tmp/generation.XXXXXXXXXX"); + fd = -1; + } + if (fd == -1) + errx(-1, + "could not create file with non-0 generation as root"); + close(fd); + + /* + * We've already tested that fstat() works, but try stat() to be + * consistent between privileged and unprivileged tests. + */ + if (stat(fpath, &sb) < 0) { + warn("stat(%s) as root", fpath); + goto out; + } + + set_euid(UID_OTHER); + + if (stat(fpath, &sb) < 0) { + warn("stat(%s) as !root", fpath); + goto out; + } + + if (sb.st_gen != 0) + warn("stat(%s) returned generation as !root", fpath); + +out: + (void)seteuid(UID_ROOT); + (void)unlink(fpath); +} diff --git a/tools/regression/priv/priv_vfs_getfh.c b/tools/regression/priv/priv_vfs_getfh.c new file mode 100644 index 0000000..ff5d6fe --- /dev/null +++ b/tools/regression/priv/priv_vfs_getfh.c @@ -0,0 +1,78 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Check that getfh() requires privilege; run it with, and without, + * privilege. + */ + +#include <sys/param.h> +#include <sys/mount.h> + +#include <err.h> +#include <errno.h> +#include <unistd.h> + +#include "main.h" + +void +priv_vfs_getfh(void) +{ + char fpath[1024]; + fhandle_t fh; + int error; + + assert_root(); + + setup_file(fpath, UID_ROOT, GID_WHEEL, 0644); + + if (getfh(fpath, &fh) < 0) { + warn("getfh(%s) as root", fpath); + goto out; + } + + set_euid(UID_OTHER); + + error = getfh(fpath, &fh); + if (error == 0) { + warnx("getfh(%s) succeeded as !root", fpath); + goto out; + } + + if (errno != EPERM) { + warn("getfh(%s) wrong errno %d as !root", fpath, errno); + goto out; + } + +out: + (void)seteuid(UID_ROOT); + (void)unlink(fpath); +} diff --git a/tools/regression/priv/priv_vfs_read_write.c b/tools/regression/priv/priv_vfs_read_write.c new file mode 100644 index 0000000..c7f479d --- /dev/null +++ b/tools/regression/priv/priv_vfs_read_write.c @@ -0,0 +1,370 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * This is a joint test of both the read and write privileges with respect to + * discretionary file system access control (permissions). Only permissions, + * not ACL semantics, and only privilege-related checks are performed. + */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "main.h" + +struct test_arguments { + int open_flags; + uid_t proc_uid; + gid_t proc_gid; + uid_t file_uid; + gid_t file_gid; +}; + +/* + * Rather special-purpose, don't reuse. Will need updating if anything other + * than O_RDONLY and O_WRONLY are to be used in tests. + */ +static const char * +flags_to_string(int flags) +{ + + switch (flags) { + case O_RDONLY: + return ("O_RDONLY"); + + case O_WRONLY: + return ("O_WRONLY"); + + default: + return ("unknown"); + } +} + +static void +test_perm(struct test_arguments ta, mode_t file_mode, int expected) +{ + uid_t proc_uid, file_uid; + gid_t proc_gid, file_gid; + int fd, open_flags; + char fpath[1024]; + + proc_uid = ta.proc_uid; + proc_gid = ta.proc_gid; + file_uid = ta.file_uid; + file_gid = ta.file_gid; + open_flags = ta.open_flags; + + setup_file(fpath, file_uid, file_gid, file_mode); + set_creds(proc_uid, proc_gid); + + fd = open(fpath, open_flags); + + if (expected == 0) { + if (fd <= 0) { + warn("test_perm(%s, %d, %d, %d, %d, %04o, %d) " + "returned %d instead of %d", + flags_to_string(open_flags), proc_uid, proc_gid, + file_uid, file_gid, file_mode, expected, + errno, expected); + restore_creds(); + (void)unlink(fpath); + exit(-1); + } + close(fd); + } else { + if (fd >= 0) { + warnx("test_perm(%s, %d, %d, %d, %d, %04o, %d)" + " returned 0 instead of %d", + flags_to_string(open_flags), proc_uid, proc_gid, + file_uid, file_gid, file_mode, expected, + expected); + close(fd); + restore_creds(); + (void)unlink(fpath); + exit(-1); + } else if (errno != expected) { + warn("test_perm(%s, %d, %d, %d, %d, %04o, %d)" + " returned %d instead of %d", + flags_to_string(open_flags), proc_uid, proc_gid, + file_uid, file_gid, file_mode, expected, + errno, expected); + restore_creds(); + (void)unlink(fpath); + exit(-1); + } + } + + restore_creds(); + (void)unlink(fpath); +} + +static const gid_t gidset[] = { GID_WHEEL }; + +static void +preamble(void) +{ + + if (getuid() != UID_ROOT) + errx(-1, "must be run as root"); + if (setgroups(1, gidset) < 0) + err(-1, "setgroups(1, {%d})", GID_WHEEL); +} + +void +priv_vfs_read(void) +{ + struct test_arguments ta; + + preamble(); + + ta.open_flags = O_RDONLY; + + /* + * Privileged user and file owner. All tests should pass. + */ + ta.proc_uid = UID_ROOT; + ta.proc_gid = GID_WHEEL; + ta.file_uid = UID_ROOT; + ta.file_gid = GID_WHEEL; + + test_perm(ta, 0000, 0); + test_perm(ta, 0100, 0); + test_perm(ta, 0200, 0); + test_perm(ta, 0300, 0); + test_perm(ta, 0400, 0); + test_perm(ta, 0500, 0); + test_perm(ta, 0600, 0); + test_perm(ta, 0700, 0); + + /* + * Privileged user and file group. All tests should pass. + */ + ta.proc_uid = UID_ROOT; + ta.proc_gid = GID_WHEEL; + ta.file_uid = UID_OWNER; + ta.file_gid = GID_WHEEL; + + test_perm(ta, 0000, 0); + test_perm(ta, 0010, 0); + test_perm(ta, 0020, 0); + test_perm(ta, 0030, 0); + test_perm(ta, 0040, 0); + test_perm(ta, 0050, 0); + test_perm(ta, 0060, 0); + test_perm(ta, 0070, 0); + + /* + * Privileged user and file other. All tests should pass. + */ + ta.proc_uid = UID_ROOT; + ta.proc_gid = GID_WHEEL; + ta.file_uid = UID_OWNER; + ta.file_gid = GID_OWNER; + + test_perm(ta, 0000, 0); + test_perm(ta, 0001, 0); + test_perm(ta, 0002, 0); + test_perm(ta, 0003, 0); + test_perm(ta, 0004, 0); + test_perm(ta, 0005, 0); + test_perm(ta, 0006, 0); + test_perm(ta, 0007, 0); + + /* + * Unprivileged user and file owner. Various DAC failures. + */ + ta.proc_uid = UID_OWNER; + ta.proc_gid = GID_OWNER; + ta.file_uid = UID_OWNER; + ta.file_gid = GID_OWNER; + + test_perm(ta, 0000, EACCES); + test_perm(ta, 0100, EACCES); + test_perm(ta, 0200, EACCES); + test_perm(ta, 0300, EACCES); + test_perm(ta, 0400, 0); + test_perm(ta, 0500, 0); + test_perm(ta, 0600, 0); + test_perm(ta, 0700, 0); + + /* + * Unprivileged user and file group. Various DAC failures. + */ + ta.proc_uid = UID_OTHER; + ta.proc_gid = GID_OWNER; + ta.file_uid = UID_OWNER; + ta.file_gid = GID_OWNER; + + test_perm(ta, 0000, EACCES); + test_perm(ta, 0010, EACCES); + test_perm(ta, 0020, EACCES); + test_perm(ta, 0030, EACCES); + test_perm(ta, 0040, 0); + test_perm(ta, 0050, 0); + test_perm(ta, 0060, 0); + test_perm(ta, 0070, 0); + + /* + * Unprivileged user and file other. Various DAC failures. + */ + ta.proc_uid = UID_OTHER; + ta.proc_gid = GID_OTHER; + ta.file_uid = UID_OWNER; + ta.file_gid = GID_OWNER; + + test_perm(ta, 0000, EACCES); + test_perm(ta, 0001, EACCES); + test_perm(ta, 0002, EACCES); + test_perm(ta, 0003, EACCES); + test_perm(ta, 0004, 0); + test_perm(ta, 0005, 0); + test_perm(ta, 0006, 0); + test_perm(ta, 0007, 0); +} + +void +priv_vfs_write(void) +{ + struct test_arguments ta; + + preamble(); + + ta.open_flags = O_WRONLY; + + /* + * Privileged user and file owner. All tests should pass. + */ + ta.proc_uid = UID_ROOT; + ta.proc_gid = GID_WHEEL; + ta.file_uid = UID_ROOT; + ta.file_gid = GID_WHEEL; + + test_perm(ta, 0000, 0); + test_perm(ta, 0100, 0); + test_perm(ta, 0200, 0); + test_perm(ta, 0300, 0); + test_perm(ta, 0400, 0); + test_perm(ta, 0500, 0); + test_perm(ta, 0600, 0); + test_perm(ta, 0700, 0); + + /* + * Privileged user and file group. All tests should pass. + */ + ta.proc_uid = UID_ROOT; + ta.proc_gid = GID_WHEEL; + ta.file_uid = UID_OWNER; + ta.file_gid = GID_WHEEL; + + test_perm(ta, 0000, 0); + test_perm(ta, 0010, 0); + test_perm(ta, 0020, 0); + test_perm(ta, 0030, 0); + test_perm(ta, 0040, 0); + test_perm(ta, 0050, 0); + test_perm(ta, 0060, 0); + test_perm(ta, 0070, 0); + + /* + * Privileged user and file other. All tests should pass. + */ + ta.proc_uid = UID_ROOT; + ta.proc_gid = GID_WHEEL; + ta.file_uid = UID_OWNER; + ta.file_gid = GID_OWNER; + + test_perm(ta, 0000, 0); + test_perm(ta, 0001, 0); + test_perm(ta, 0002, 0); + test_perm(ta, 0003, 0); + test_perm(ta, 0004, 0); + test_perm(ta, 0005, 0); + test_perm(ta, 0006, 0); + test_perm(ta, 0007, 0); + + /* + * Unprivileged user and file owner. Various DAC failures. + */ + ta.proc_uid = UID_OWNER; + ta.proc_gid = GID_OWNER; + ta.file_uid = UID_OWNER; + ta.file_gid = GID_OWNER; + + test_perm(ta, 0000, EACCES); + test_perm(ta, 0100, EACCES); + test_perm(ta, 0200, 0); + test_perm(ta, 0300, 0); + test_perm(ta, 0400, EACCES); + test_perm(ta, 0500, EACCES); + test_perm(ta, 0600, 0); + test_perm(ta, 0700, 0); + + /* + * Unprivileged user and file group. Various DAC failures. + */ + ta.proc_uid = UID_OTHER; + ta.proc_gid = GID_OWNER; + ta.file_uid = UID_OWNER; + ta.file_gid = GID_OWNER; + + test_perm(ta, 0000, EACCES); + test_perm(ta, 0010, EACCES); + test_perm(ta, 0020, 0); + test_perm(ta, 0030, 0); + test_perm(ta, 0040, EACCES); + test_perm(ta, 0050, EACCES); + test_perm(ta, 0060, 0); + test_perm(ta, 0070, 0); + + /* + * Unprivileged user and file other. Various DAC failures. + */ + ta.proc_uid = UID_OTHER; + ta.proc_gid = GID_OTHER; + ta.file_uid = UID_OWNER; + ta.file_gid = GID_OWNER; + + test_perm(ta, 0000, EACCES); + test_perm(ta, 0001, EACCES); + test_perm(ta, 0002, 0); + test_perm(ta, 0003, 0); + test_perm(ta, 0004, EACCES); + test_perm(ta, 0005, EACCES); + test_perm(ta, 0006, 0); + test_perm(ta, 0007, 0); +} diff --git a/tools/regression/priv/priv_vfs_setgid.c b/tools/regression/priv/priv_vfs_setgid.c new file mode 100644 index 0000000..b763ef6 --- /dev/null +++ b/tools/regression/priv/priv_vfs_setgid.c @@ -0,0 +1,143 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Test that privilege is required to set the sgid bit on a file with a group + * that isn't in the process credential. The file uid owner is set to the + * uid being tested with, as we are not interested in testing privileges + * associated with file ownership. + */ + +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +#include "main.h" + +static const gid_t gidset_without[] = {GID_WHEEL}; +static const gid_t gidset_with[] = {GID_WHEEL, GID_OWNER}; + +void +priv_vfs_setgid(void) +{ + char fpath[1024]; + int error, fd; + + assert_root(); + + setup_file(fpath, UID_ROOT, GID_OWNER, 0644); + + if (setgroups(1, gidset_without) < 0) { + warn("setgroups(1, {%d})", gidset_without[0]); + goto out; + } + + fd = open(fpath, O_RDWR); + if (fd < 0) { + warn("open(%s, O_RDWR)", fpath); + goto out; + } + + /* + * With privilege, set mode on file. + */ + if (fchmod(fd, 0600 | S_ISGID) < 0) { + warn("fchmod(%s, 0600 | S_ISGID) as root", fpath); + goto out; + } + + /* + * Reset mode and chown file before dropping privilege. + */ + if (fchmod(fd, 0600) < 0) { + warn("fchmod(%s, 0600) as root", fpath); + goto out; + } + + if (fchown(fd, UID_OWNER, GID_OWNER) < 0) { + warn("fchown(%s, %d, %d) as root", fpath, UID_OWNER, + GID_OTHER); + goto out; + } + + /* + * Drop privilege. + */ + set_euid(UID_OWNER); + + /* + * Without privilege, set mode on file. + */ + error = fchmod(fd, 0600 | S_ISGID); + if (error == 0) { + warnx("fchmod(%s, 0600 | S_ISGID) succeeded as !root", + fpath); + goto out; + } + if (errno != EPERM) { + warn("fchmod(%s, 0600 | S_ISGID) wrong errno %d as !root", + fpath, errno); + goto out; + } + + /* + * Turn privilege back on so that we confirm privilege isn't required + * if we are a group member of the file's group. + */ + set_euid(UID_ROOT); + + if (setgroups(2, gidset_with) < 0) { + warn("setgroups(2, {%d, %d})", gidset_with[0], + gidset_with[1]); + goto out; + } + + if (seteuid(UID_OWNER) < 0) { + warn("seteuid(%d) pass 2", UID_OWNER); + goto out; + } + + /* + * Without privilege, set mode on file (this time with right gid). + */ + if (fchmod(fd, 0600 | S_ISGID) < 0) { + warnx("fchmod(%s, 0600 | S_ISGID) pass 2 as !root", fpath); + sleep(10); + goto out; + } + +out: + seteuid(UID_ROOT); + (void)unlink(fpath); +} diff --git a/tools/regression/priv/priv_vfs_stickyfile.c b/tools/regression/priv/priv_vfs_stickyfile.c new file mode 100644 index 0000000..11c5d5a --- /dev/null +++ b/tools/regression/priv/priv_vfs_stickyfile.c @@ -0,0 +1,140 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Check that privilege is required to set the sticky bit on a file, but not + * a directory. Try with and without privilege. + */ + +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <unistd.h> + +#include "main.h" + +static void +cleanup(const char *fpath, const char *dpath) +{ + + (void)seteuid(UID_ROOT); + (void)unlink(fpath); + if (dpath != NULL) + (void)rmdir(dpath); +} + +void +priv_vfs_stickyfile(void) +{ + char fpath[1024] = "/tmp/stickyfile.XXXXXXXXXXX"; + char dpath[1024] = "/tmp/stickyfile.XXXXXXXXXXX", *dpathp; + int error, fd; + + assert_root(); + + fd = mkstemp(fpath); + if (fd < 0) + err(-1, "mkstemp"); + + dpathp = mkdtemp(dpath); + if (dpathp == NULL) { + warn("mkdtemp"); + goto out; + } + + /* + * First, with privilege, set and clear the sticky bit on the file + * and directory. + */ + if (fchmod(fd, 0600 | S_ISTXT) < 0) { + warn("fchmod(%s, 0600 | S_ISTXT) on file as root", fpath); + goto out; + } + + if (chmod(dpathp, 0700 | S_ISTXT) < 0) { + warn("chmod(%s, 0600 | S_ISTXT) on dir as root", dpath); + goto out; + } + + /* + * Reset to remove sticky bit before changing credential. + */ + if (fchmod(fd, 0600) < 0) { + warn("fchmod(%s, 0600) on file as root", fpath); + goto out; + } + + if (chmod(dpath, 0700) < 0) { + warn("chmod(%s, 0600) on dir as root", dpath); + goto out; + } + + /* + * Chown the file and directory to target user -- we're checking for + * the specific right to set the sticky bit, not the general right to + * chmod(). + */ + if (fchown(fd, UID_OTHER, -1) < 0) { + warn("fchown(%s, %d, -1)", fpath, UID_OTHER); + goto out; + } + + if (chown(dpath, UID_OTHER, -1) < 0) { + warn("chown(%s, %d, -1)", fpath, UID_OTHER); + goto out; + } + + /* + * Change credential and try again. + */ + set_euid(UID_OTHER); + + error = fchmod(fd, 0600 | S_ISTXT); + if (error == 0) { + warnx("fchmod(%s, 0600 | S_ISTXT) succeeded on file as " + "!root", fpath); + goto out; + } + if (errno != EFTYPE) { + warn("fchmod(%s, 0600 | S_ISTXT) wrong errno %d as !root", + fpath, errno); + goto out; + } + + if (chmod(dpathp, 0700 | S_ISTXT) < 0) { + warn("chmod(%s, 0600 | S_ISTXT) on dir as !root", dpath); + goto out; + } +out: + setuid(UID_ROOT); + cleanup(fpath, dpathp); +} diff --git a/tools/regression/priv/priv_vm_madv_protect.c b/tools/regression/priv/priv_vm_madv_protect.c new file mode 100644 index 0000000..737adbf --- /dev/null +++ b/tools/regression/priv/priv_vm_madv_protect.c @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Test that madvise(..., MADV_PROTECT) requires privilege by running it first + * with privilege, then again without. + */ + +#include <sys/types.h> +#include <sys/mman.h> + +#include <err.h> +#include <errno.h> +#include <unistd.h> + +#include "main.h" + +void +priv_vm_madv_protect(void) +{ + int error; + + assert_root(); + + error = madvise(NULL, 0, MADV_PROTECT); + if (error) + err(-1, "madvise(..., MADV_PROTECT) as root"); + + set_euid(UID_OTHER); + + error = madvise(NULL, 0, MADV_PROTECT); + if (error == 0) + errx(-1, "mlock as !root succeeded"); + if (errno != EPERM) + err(-1, "mlock as !root wrong errno %d", errno); +} diff --git a/tools/regression/priv/priv_vm_mlock.c b/tools/regression/priv/priv_vm_mlock.c new file mode 100644 index 0000000..3da06d3 --- /dev/null +++ b/tools/regression/priv/priv_vm_mlock.c @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Test that mlock() requires privilege by running it first with privilege, + * then again without. + */ + +#include <sys/types.h> +#include <sys/mman.h> + +#include <err.h> +#include <errno.h> +#include <unistd.h> + +#include "main.h" + +void +priv_vm_mlock(void) +{ + int error; + + assert_root(); + + error = mlock(&error, getpagesize()); + if (error) + err(-1, "mlock as root"); + + set_euid(UID_OTHER); + + error = mlock(&error, getpagesize()); + if (error == 0) + errx(-1, "mlock as !root succeeded"); + if (errno != EPERM) + err(-1, "mlock as !root wrong errno %d", errno); +} diff --git a/tools/regression/priv/priv_vm_munlock.c b/tools/regression/priv/priv_vm_munlock.c new file mode 100644 index 0000000..9e8713a --- /dev/null +++ b/tools/regression/priv/priv_vm_munlock.c @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * Test that munlock() requires privilege by running it first with privilege, + * then again without. + */ + +#include <sys/types.h> +#include <sys/mman.h> + +#include <err.h> +#include <errno.h> +#include <unistd.h> + +#include "main.h" + +void +priv_vm_munlock(void) +{ + int error; + + assert_root(); + + error = munlock(&error, getpagesize()); + if (error) + err(-1, "munlock as root"); + + set_euid(UID_OTHER); + + error = munlock(&error, getpagesize()); + if (error == 0) + errx(-1, "munlock as !root succeeded"); + if (errno != EPERM) + err(-1, "munlock as !root wrong errno %d", errno); +} diff --git a/tools/regression/priv/test_utimes.c b/tools/regression/priv/test_utimes.c new file mode 100644 index 0000000..51cef52 --- /dev/null +++ b/tools/regression/priv/test_utimes.c @@ -0,0 +1,153 @@ +/*- + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * 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, NCIRCLE NETWORK SECURITY, + * INC., 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$ + */ + +/* + * If times is NULL, ... The caller must be the owner of the file, have + * permission to write the file, or be the super-user. + * + * If times is non-NULL, ... The caller must be the owner of the file or be + * the super-user. + * + * To test these, create a temporary file owned by uid_owner; then run a + * series of tests as root, owner, and other, along with various modes, to + * see what is permitted, and if not, what error is returned. + */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "main.h" + +static int +try_utimes(const char *path, mode_t mode, uid_t uid, + struct timeval *timestamp, int expected) +{ + int error; + + if (chmod(path, mode) < 0) { + warn("try_utimes(%s, %d, %d, 0x%08x): chmod", path, mode, uid, + (u_int)timestamp); + (void)unlink(path); + exit(-1); + } + + if (seteuid(uid) < 0) { + warn("try_utimes(%s, %d, %d, 0x%08x): seteuid(%d)", path, + mode, uid, (u_int)timestamp, uid); + (void)unlink(path); + exit(-1); + } + + error = utimes(path, timestamp); + + if (seteuid(UID_ROOT) < 0) { + warn("try_utimes(%s, %d, %d, 0x%08x): seteuid(UID_ROOT)", + path, mode, uid, (u_int)timestamp); + (void)unlink(path); + exit(-1); + } + + if (expected == 0) { + if (error != 0) { + (void)unlink(path); + errx(-1, "try_utimes(%s, 0%o, %d, 0x%08x) failed %d", + path, mode, uid, (u_int)timestamp, errno); + } + return (0); + } + + if (expected == errno) + return (0); + + (void)unlink(path); + errx(-1, "try_utimes(%s, 0%o, %d, 0x%08x) wrong err %d", path, mode, + uid, (u_int)timestamp, errno); +} + +void +test_utimes(void) +{ + char path[128] = "/tmp/utimes.XXXXXXXXX"; + struct timeval timestamp[2]; + int fd; + + if (getuid() != 0) + errx(-1, "must be run as root"); + + fd = mkstemp(path); + if (fd == -1) + err(-1, "mkstemp"); + + if (chown(path, UID_OWNER, -1) < 0) { + warn("chown(%s, %d)", path, UID_OWNER); + (void)unlink(path); + return; + } + + bzero(timestamp, sizeof(timestamp)); + + try_utimes(path, 0444, UID_ROOT, NULL, 0); + try_utimes(path, 0444, UID_OWNER, NULL, 0); + /* Denied by permissions. */ + try_utimes(path, 0444, UID_OTHER, NULL, EACCES); + + try_utimes(path, 0444, UID_ROOT, timestamp, 0); + try_utimes(path, 0444, UID_OWNER, timestamp, 0); + try_utimes(path, 0444, UID_OTHER, timestamp, EPERM); + + try_utimes(path, 0644, UID_ROOT, NULL, 0); + try_utimes(path, 0644, UID_OWNER, NULL, 0); + /* Denied by permissions. */ + try_utimes(path, 0644, UID_OTHER, NULL, EACCES); + + try_utimes(path, 0644, UID_ROOT, timestamp, 0); + try_utimes(path, 0644, UID_OWNER, timestamp, 0); + /* Denied as not owner. */ + try_utimes(path, 0644, UID_OTHER, timestamp, EPERM); + + try_utimes(path, 0666, UID_ROOT, NULL, 0); + try_utimes(path, 0666, UID_OWNER, NULL, 0); + try_utimes(path, 0666, UID_OTHER, NULL, 0); + + try_utimes(path, 0666, UID_ROOT, timestamp, 0); + try_utimes(path, 0666, UID_OWNER, timestamp, 0); + /* Denied as not owner. */ + try_utimes(path, 0666, UID_OTHER, timestamp, EPERM); + + (void)unlink(path); +} |