aboutsummaryrefslogtreecommitdiff
path: root/tools/regression/security/cap_test
diff options
context:
space:
mode:
Diffstat (limited to 'tools/regression/security/cap_test')
-rw-r--r--tools/regression/security/cap_test/Makefile24
-rw-r--r--tools/regression/security/cap_test/cap_test.c118
-rw-r--r--tools/regression/security/cap_test/cap_test.h156
-rw-r--r--tools/regression/security/cap_test/cap_test.t10
-rw-r--r--tools/regression/security/cap_test/cap_test_capabilities.c558
-rw-r--r--tools/regression/security/cap_test/cap_test_capmode.c195
-rw-r--r--tools/regression/security/cap_test/cap_test_fcntl.c114
-rw-r--r--tools/regression/security/cap_test/cap_test_pdfork.c108
-rw-r--r--tools/regression/security/cap_test/cap_test_pdkill.c96
-rw-r--r--tools/regression/security/cap_test/cap_test_relative.c147
-rw-r--r--tools/regression/security/cap_test/cap_test_sysctl.c62
11 files changed, 1588 insertions, 0 deletions
diff --git a/tools/regression/security/cap_test/Makefile b/tools/regression/security/cap_test/Makefile
new file mode 100644
index 000000000000..0fdc274edbed
--- /dev/null
+++ b/tools/regression/security/cap_test/Makefile
@@ -0,0 +1,24 @@
+PROG= cap_test
+SRCS= cap_test.c \
+ cap_test_capmode.c \
+ cap_test_capabilities.c \
+ cap_test_fcntl.c \
+ cap_test_pdfork.c \
+ cap_test_pdkill.c \
+ cap_test_relative.c \
+ cap_test_sysctl.c \
+
+WARNS= 3
+MAN=
+
+# Use headers and libc from the build, if available.
+KERNCONF?= GENERIC
+OBJROOT= ${.OBJDIR}/../../../../
+OBJKERN= ${OBJROOT}/sys/${KERNCONF}
+
+SRCROOT= ${.CURDIR}/../../../../
+
+CFLAGS+= -DMACHINE=\"${MACHINE}\" -I${OBJKERN} -I${SRCROOT}/sys
+LDFLAGS+= -L${OBJROOT}/lib/libc -lc
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/security/cap_test/cap_test.c b/tools/regression/security/cap_test/cap_test.c
new file mode 100644
index 000000000000..3544bf690214
--- /dev/null
+++ b/tools/regression/security/cap_test/cap_test.c
@@ -0,0 +1,118 @@
+/*-
+ * Copyright (c) 2008-2011 Robert N. M. Watson
+ * Copyright (c) 2011 Jonathan Anderson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "cap_test.h"
+
+/* Initialize a named test. Requires test_NAME() function to be declared. */
+#define TEST_INIT(name) { #name, test_##name, FAILED }
+
+/* All of the tests that can be run. */
+struct test all_tests[] = {
+ TEST_INIT(capmode),
+ TEST_INIT(capabilities),
+ TEST_INIT(fcntl),
+ TEST_INIT(pdfork),
+ TEST_INIT(pdkill),
+ TEST_INIT(relative),
+ TEST_INIT(sysctl),
+};
+int test_count = sizeof(all_tests) / sizeof(struct test);
+
+int
+main(int argc, char *argv[])
+{
+
+ /*
+ * If no tests have been specified at the command line, run them all.
+ */
+ if (argc == 1) {
+ printf("1..%d\n", test_count);
+
+ for (int i = 0; i < test_count; i++)
+ execute(i + 1, all_tests + i);
+ return (0);
+ }
+
+ /*
+ * Otherwise, run only the specified tests.
+ */
+ printf("1..%d\n", argc - 1);
+ for (int i = 1; i < argc; i++)
+ {
+ int found = 0;
+ for (int j = 0; j < test_count; j++) {
+ if (strncmp(argv[i], all_tests[j].t_name,
+ strlen(argv[i])) == 0) {
+ found = 1;
+ execute(i, all_tests + j);
+ break;
+ }
+ }
+
+ if (found == 0)
+ errx(-1, "No such test '%s'", argv[i]);
+ }
+
+ return (0);
+}
+
+int
+execute(int id, struct test *t) {
+ int result;
+
+ pid_t pid = fork();
+ if (pid < 0)
+ err(-1, "fork");
+ if (pid) {
+ /* Parent: wait for result from child. */
+ int status;
+ while (waitpid(pid, &status, 0) != pid) {}
+ if (WIFEXITED(status))
+ result = WEXITSTATUS(status);
+ else
+ result = FAILED;
+ } else {
+ /* Child process: run the test. */
+ exit(t->t_run());
+ }
+
+ printf("%s %d - %s\n",
+ (result == PASSED) ? "ok" : "not ok",
+ id, t->t_name);
+
+ return (result);
+}
diff --git a/tools/regression/security/cap_test/cap_test.h b/tools/regression/security/cap_test/cap_test.h
new file mode 100644
index 000000000000..da06fab0ef76
--- /dev/null
+++ b/tools/regression/security/cap_test/cap_test.h
@@ -0,0 +1,156 @@
+/*-
+ * Copyright (c) 2008-2011 Robert N. M. Watson
+ * Copyright (c) 2011 Jonathan Anderson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef CAP_TEST_H
+#define CAP_TEST_H
+
+#include <err.h>
+
+/*
+ * Define a file required by a test. The test can't complete without the file,
+ * so if we don't have it, just die.
+ */
+#define REQUIRE(fd) do { \
+ if ((fd) < 0) \
+ err(-1, "%s:%d: Missing required file '%s'", \
+ __FILE__, __LINE__, #fd); \
+} while (0)
+
+/* Whether a test passed or failed. */
+#define PASSED 0
+#define FAILED 1
+
+/* A test has failed; print a message and clear the 'success' flag. */
+#define FAIL(...) do { \
+ warn(__VA_ARGS__); \
+ success = FAILED; \
+} while (0)
+
+/* As above, but do not print the errno message. */
+#define FAILX(...) do { \
+ warnx(__VA_ARGS__); \
+ success = FAILED; \
+} while (0)
+
+/* Like an assertion, but don't kill the test, just fail and keep going. */
+#define CHECK(condition) do { \
+ if (!(condition)) \
+ FAILX("%s:%d: Assertion '%s' failed", \
+ __func__, __LINE__, #condition); \
+} while (0)
+
+/* Make sure that a system call's return value is >= 0. */
+#define CHECK_SYSCALL_SUCCEEDS(syscall, ...) do { \
+ if (syscall(__VA_ARGS__) < 0) \
+ FAIL("%s() at line %d: %s failed", \
+ __func__, __LINE__, #syscall); \
+} while (0)
+
+/* Make sure that a system call fails with the correct errno. */
+#define CHECK_SYSCALL_FAILS(expected_errno, syscall, ...) do { \
+ if (syscall(__VA_ARGS__) < 0) { \
+ if (errno != expected_errno) \
+ FAIL("%s() at line %d: %s", \
+ __func__, __LINE__, #syscall); \
+ } else { \
+ FAILX("%s() at line %d: %s succeeded; it should've failed", \
+ __func__, __LINE__, #syscall); \
+ } \
+} while (0)
+
+/* Make sure that a system call fails, but not with a particular errno. */
+#define CHECK_SYSCALL_FAILS_BUT_NOT_WITH(bad_errno, syscall, ...) do { \
+ if (syscall(__VA_ARGS__) < 0) { \
+ if (errno == bad_errno) \
+ FAIL("%s() at line %d: %s", \
+ __func__, __LINE__, #syscall); \
+ } else { \
+ FAILX("%s() at line %d: %s succeeded; it should've failed", \
+ __func__, __LINE__, #syscall); \
+ } \
+} while (0)
+
+/* A system call should fail with ECAPMODE. */
+#define CHECK_CAPMODE(...) \
+ CHECK_SYSCALL_FAILS(ECAPMODE, __VA_ARGS__)
+
+/* A system call should fail, but not with ECAPMODE. */
+#define CHECK_NOT_CAPMODE(...) \
+ CHECK_SYSCALL_FAILS_BUT_NOT_WITH(ECAPMODE, __VA_ARGS__)
+
+/* A system call should fail with ENOTCAPABLE. */
+#define CHECK_NOTCAPABLE(...) \
+ CHECK_SYSCALL_FAILS(ENOTCAPABLE, __VA_ARGS__)
+
+/* Ensure that 'rights' are a subset of 'max'. */
+#define CHECK_RIGHTS(rights, max) do { \
+ if ((success == PASSED) && (rights != max)) \
+ FAILX("Rights of opened file (%jx) > maximum (%jx)", \
+ (cap_rights_t) rights, (cap_rights_t) max); \
+} while (0)
+
+/* Create a capability from a file descriptor, make sure it succeeds. */
+#define MAKE_CAPABILITY(to, from, rights) do { \
+ cap_rights_t _rights; \
+ REQUIRE(to = cap_new(from, rights)); \
+ CHECK_SYSCALL_SUCCEEDS(cap_getrights, to, &_rights); \
+ if ((success == PASSED) && (_rights != (rights))) \
+ FAILX("New capability's rights (%jx) != %jx", \
+ _rights, (cap_rights_t) (rights)); \
+} while (0)
+
+/*
+ * A top-level test should take no arguments and return an integer value,
+ * either PASSED or FAILED.
+ *
+ * Errors such as SIGSEGV will be caught and interpreted as FAILED.
+ */
+typedef int (*test_function)(void);
+
+/* Information about a test. */
+struct test {
+ char *t_name;
+ test_function t_run;
+ int t_result;
+};
+
+/*
+ * Run a test in a child process so that cap_enter(2) doesn't mess up
+ * subsequent tests.
+ */
+int execute(int id, struct test*);
+
+int test_capmode(void);
+int test_capabilities(void);
+int test_fcntl(void);
+int test_pdfork(void);
+int test_pdkill(void);
+int test_pdwait(void);
+int test_relative(void);
+int test_sysctl(void);
+
+#endif /* CAP_TEST_H */
diff --git a/tools/regression/security/cap_test/cap_test.t b/tools/regression/security/cap_test/cap_test.t
new file mode 100644
index 000000000000..a13e1b204fb4
--- /dev/null
+++ b/tools/regression/security/cap_test/cap_test.t
@@ -0,0 +1,10 @@
+#!/bin/sh
+#
+#
+
+if test -z "${DIR}" ; then DIR=$( make -V .OBJDIR ); fi
+if test -z "${DIR}" ; then DIR=$( dirname $0 ); fi
+
+make > /dev/null || exit 1
+$DIR/cap_test $*
+
diff --git a/tools/regression/security/cap_test/cap_test_capabilities.c b/tools/regression/security/cap_test/cap_test_capabilities.c
new file mode 100644
index 000000000000..b22749360bb1
--- /dev/null
+++ b/tools/regression/security/cap_test/cap_test_capabilities.c
@@ -0,0 +1,558 @@
+/*-
+ * Copyright (c) 2009-2011 Robert N. M. Watson
+ * Copyright (c) 2011 Jonathan Anderson
+ * Copyright (c) 2012 FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by Pawel Jakub Dawidek under
+ * sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test whether various operations on capabilities are properly masked for
+ * various object types.
+ */
+
+#include <sys/param.h>
+#include <sys/capsicum.h>
+#include <sys/errno.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "cap_test.h"
+
+#define SYSCALL_FAIL(syscall, message) \
+ FAIL("%s:\t%s (rights 0x%jx)", #syscall, message, rights)
+
+/*
+ * Ensure that, if the capability had enough rights for the system call to
+ * pass, then it did. Otherwise, ensure that the errno is ENOTCAPABLE;
+ * capability restrictions should kick in before any other error logic.
+ */
+#define CHECK_RESULT(syscall, rights_needed, succeeded) do { \
+ if ((rights & (rights_needed)) == (rights_needed)) { \
+ if (succeeded) { \
+ if (success == -1) \
+ success = PASSED; \
+ } else { \
+ SYSCALL_FAIL(syscall, "failed"); \
+ } \
+ } else { \
+ if (succeeded) { \
+ FAILX("%s:\tsucceeded when it shouldn't have" \
+ " (rights 0x%jx)", #syscall, \
+ (uintmax_t)rights); \
+ } else if (errno != ENOTCAPABLE) { \
+ SYSCALL_FAIL(syscall, "errno != ENOTCAPABLE"); \
+ } \
+ } \
+ errno = 0; \
+} while (0)
+
+/*
+ * As above, but for the special mmap() case: unmap after successful mmap().
+ */
+#define CHECK_MMAP_RESULT(rights_needed) do { \
+ if ((rights & (rights_needed)) == (rights_needed)) { \
+ if (p == MAP_FAILED) \
+ SYSCALL_FAIL(mmap, "failed"); \
+ else { \
+ (void)munmap(p, getpagesize()); \
+ if (success == -1) \
+ success = PASSED; \
+ } \
+ } else { \
+ if (p != MAP_FAILED) { \
+ FAILX("%s:\tsucceeded when it shouldn't have" \
+ " (rights 0x%jx)", "mmap", rights); \
+ (void)munmap(p, getpagesize()); \
+ } else if (errno != ENOTCAPABLE) \
+ SYSCALL_FAIL(syscall, "errno != ENOTCAPABLE"); \
+ } \
+ errno = 0; \
+} while (0)
+
+/*
+ * Given a file descriptor, create a capability with specific rights and
+ * make sure only those rights work.
+*/
+static int
+try_file_ops(int filefd, int dirfd, cap_rights_t rights)
+{
+ struct stat sb;
+ struct statfs sf;
+ cap_rights_t erights;
+ int fd_cap, fd_capcap, dfd_cap;
+ ssize_t ssize, ssize2;
+ off_t off;
+ void *p;
+ char ch;
+ int ret, is_nfs;
+ struct pollfd pollfd;
+ int success = -1;
+
+ REQUIRE(fstatfs(filefd, &sf));
+ is_nfs = (strcmp("nfs", sf.f_fstypename) == 0);
+
+ REQUIRE(fd_cap = cap_new(filefd, rights));
+ CHECK(cap_getrights(fd_cap, &erights) == 0);
+ CHECK(rights == erights);
+ REQUIRE(fd_capcap = cap_new(fd_cap, rights));
+ CHECK(cap_getrights(fd_capcap, &erights) == 0);
+ CHECK(rights == erights);
+ CHECK(fd_capcap != fd_cap);
+ REQUIRE(dfd_cap = cap_new(dirfd, rights));
+ CHECK(cap_getrights(dfd_cap, &erights) == 0);
+ CHECK(rights == erights);
+
+ ssize = read(fd_cap, &ch, sizeof(ch));
+ CHECK_RESULT(read, CAP_READ, ssize >= 0);
+
+ ssize = write(fd_cap, &ch, sizeof(ch));
+ CHECK_RESULT(write, CAP_WRITE, ssize >= 0);
+
+ off = lseek(fd_cap, 0, SEEK_SET);
+ CHECK_RESULT(lseek, CAP_SEEK, off >= 0);
+
+ ssize = pread(fd_cap, &ch, sizeof(ch), 0);
+ ssize2 = pread(fd_cap, &ch, sizeof(ch), 0);
+ CHECK_RESULT(pread, CAP_PREAD, ssize >= 0);
+ CHECK(ssize == ssize2);
+
+ ssize = pwrite(fd_cap, &ch, sizeof(ch), 0);
+ CHECK_RESULT(pwrite, CAP_PWRITE, ssize >= 0);
+
+ p = mmap(NULL, getpagesize(), PROT_NONE, MAP_SHARED, fd_cap, 0);
+ CHECK_MMAP_RESULT(CAP_MMAP);
+
+ p = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd_cap, 0);
+ CHECK_MMAP_RESULT(CAP_MMAP_R);
+
+ p = mmap(NULL, getpagesize(), PROT_WRITE, MAP_SHARED, fd_cap, 0);
+ CHECK_MMAP_RESULT(CAP_MMAP_W);
+
+ p = mmap(NULL, getpagesize(), PROT_EXEC, MAP_SHARED, fd_cap, 0);
+ CHECK_MMAP_RESULT(CAP_MMAP_X);
+
+ p = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd_cap, 0);
+ CHECK_MMAP_RESULT(CAP_MMAP_RW);
+
+ p = mmap(NULL, getpagesize(), PROT_READ | PROT_EXEC, MAP_SHARED,
+ fd_cap, 0);
+ CHECK_MMAP_RESULT(CAP_MMAP_RX);
+
+ p = mmap(NULL, getpagesize(), PROT_EXEC | PROT_WRITE, MAP_SHARED,
+ fd_cap, 0);
+ CHECK_MMAP_RESULT(CAP_MMAP_WX);
+
+ p = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_SHARED, fd_cap, 0);
+ CHECK_MMAP_RESULT(CAP_MMAP_RWX);
+
+ ret = openat(dfd_cap, "cap_create", O_CREAT | O_RDONLY, 0600);
+ CHECK_RESULT(openat(O_CREATE | O_RDONLY),
+ CAP_CREATE | CAP_READ | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0);
+ ret = openat(dfd_cap, "cap_create", O_CREAT | O_WRONLY | O_APPEND,
+ 0600);
+ CHECK_RESULT(openat(O_CREATE | O_WRONLY | O_APPEND),
+ CAP_CREATE | CAP_WRITE | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0);
+ ret = openat(dfd_cap, "cap_create", O_CREAT | O_RDWR | O_APPEND, 0600);
+ CHECK_RESULT(openat(O_CREATE | O_RDWR | O_APPEND),
+ CAP_CREATE | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0);
+
+ ret = fsync(fd_cap);
+ CHECK_RESULT(fsync, CAP_FSYNC, ret == 0);
+
+ ret = openat(dirfd, "cap_fsync", O_CREAT, 0600);
+ CHECK(ret >= 0);
+ CHECK(close(ret) == 0);
+ ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_RDONLY);
+ CHECK_RESULT(openat(O_FSYNC | O_RDONLY),
+ CAP_FSYNC | CAP_READ | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_WRONLY | O_APPEND);
+ CHECK_RESULT(openat(O_FSYNC | O_WRONLY | O_APPEND),
+ CAP_FSYNC | CAP_WRITE | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_RDWR | O_APPEND);
+ CHECK_RESULT(openat(O_FSYNC | O_RDWR | O_APPEND),
+ CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_RDONLY);
+ CHECK_RESULT(openat(O_SYNC | O_RDONLY),
+ CAP_FSYNC | CAP_READ | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_WRONLY | O_APPEND);
+ CHECK_RESULT(openat(O_SYNC | O_WRONLY | O_APPEND),
+ CAP_FSYNC | CAP_WRITE | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_RDWR | O_APPEND);
+ CHECK_RESULT(openat(O_SYNC | O_RDWR | O_APPEND),
+ CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ CHECK(unlinkat(dirfd, "cap_fsync", 0) == 0);
+
+ ret = ftruncate(fd_cap, 0);
+ CHECK_RESULT(ftruncate, CAP_FTRUNCATE, ret == 0);
+
+ ret = openat(dirfd, "cap_ftruncate", O_CREAT, 0600);
+ CHECK(ret >= 0);
+ CHECK(close(ret) == 0);
+ ret = openat(dfd_cap, "cap_ftruncate", O_TRUNC | O_RDONLY);
+ CHECK_RESULT(openat(O_TRUNC | O_RDONLY),
+ CAP_FTRUNCATE | CAP_READ | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ ret = openat(dfd_cap, "cap_ftruncate", O_TRUNC | O_WRONLY);
+ CHECK_RESULT(openat(O_TRUNC | O_WRONLY),
+ CAP_FTRUNCATE | CAP_WRITE | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ ret = openat(dfd_cap, "cap_ftruncate", O_TRUNC | O_RDWR);
+ CHECK_RESULT(openat(O_TRUNC | O_RDWR),
+ CAP_FTRUNCATE | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ CHECK(unlinkat(dirfd, "cap_ftruncate", 0) == 0);
+
+ ret = openat(dfd_cap, "cap_create", O_CREAT | O_WRONLY, 0600);
+ CHECK_RESULT(openat(O_CREATE | O_WRONLY),
+ CAP_CREATE | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0);
+ ret = openat(dfd_cap, "cap_create", O_CREAT | O_RDWR, 0600);
+ CHECK_RESULT(openat(O_CREATE | O_RDWR),
+ CAP_CREATE | CAP_READ | CAP_WRITE | CAP_SEEK | CAP_LOOKUP,
+ ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0);
+
+ ret = openat(dirfd, "cap_fsync", O_CREAT, 0600);
+ CHECK(ret >= 0);
+ CHECK(close(ret) == 0);
+ ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_WRONLY);
+ CHECK_RESULT(openat(O_FSYNC | O_WRONLY),
+ CAP_FSYNC | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_RDWR);
+ CHECK_RESULT(openat(O_FSYNC | O_RDWR),
+ CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_WRONLY);
+ CHECK_RESULT(openat(O_SYNC | O_WRONLY),
+ CAP_FSYNC | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_RDWR);
+ CHECK_RESULT(openat(O_SYNC | O_RDWR),
+ CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ CHECK(unlinkat(dirfd, "cap_fsync", 0) == 0);
+
+ /*
+ * Note: this is not expected to work over NFS.
+ */
+ ret = fchflags(fd_cap, UF_NODUMP);
+ CHECK_RESULT(fchflags, CAP_FCHFLAGS,
+ ret == 0 || (is_nfs && errno == EOPNOTSUPP));
+
+ ret = openat(dirfd, "cap_chflagsat", O_CREAT, 0600);
+ CHECK(ret >= 0);
+ CHECK(close(ret) == 0);
+ ret = chflagsat(dfd_cap, "cap_chflagsat", UF_NODUMP, 0);
+ CHECK_RESULT(chflagsat, CAP_CHFLAGSAT | CAP_LOOKUP, ret == 0);
+ CHECK(unlinkat(dirfd, "cap_chflagsat", 0) == 0);
+
+ ret = fchown(fd_cap, -1, -1);
+ CHECK_RESULT(fchown, CAP_FCHOWN, ret == 0);
+
+ ret = openat(dirfd, "cap_fchownat", O_CREAT, 0600);
+ CHECK(ret >= 0);
+ CHECK(close(ret) == 0);
+ ret = fchownat(dfd_cap, "cap_fchownat", -1, -1, 0);
+ CHECK_RESULT(fchownat, CAP_FCHOWN | CAP_LOOKUP, ret == 0);
+ CHECK(unlinkat(dirfd, "cap_fchownat", 0) == 0);
+
+ ret = fchmod(fd_cap, 0644);
+ CHECK_RESULT(fchmod, CAP_FCHMOD, ret == 0);
+
+ ret = openat(dirfd, "cap_fchmodat", O_CREAT, 0600);
+ CHECK(ret >= 0);
+ CHECK(close(ret) == 0);
+ ret = fchmodat(dfd_cap, "cap_fchmodat", 0600, 0);
+ CHECK_RESULT(fchmodat, CAP_FCHMOD | CAP_LOOKUP, ret == 0);
+ CHECK(unlinkat(dirfd, "cap_fchmodat", 0) == 0);
+
+ ret = fcntl(fd_cap, F_GETFL);
+ CHECK_RESULT(fcntl(F_GETFL), CAP_FCNTL, ret >= 0);
+ ret = fcntl(fd_cap, F_SETFL, ret);
+ CHECK_RESULT(fcntl(F_SETFL), CAP_FCNTL, ret == 0);
+
+ /* XXX flock */
+
+ ret = fstat(fd_cap, &sb);
+ CHECK_RESULT(fstat, CAP_FSTAT, ret == 0);
+
+ ret = openat(dirfd, "cap_fstatat", O_CREAT, 0600);
+ CHECK(ret >= 0);
+ CHECK(close(ret) == 0);
+ ret = fstatat(dfd_cap, "cap_fstatat", &sb, 0);
+ CHECK_RESULT(fstatat, CAP_FSTAT | CAP_LOOKUP, ret == 0);
+ CHECK(unlinkat(dirfd, "cap_fstatat", 0) == 0);
+
+ ret = fstatfs(fd_cap, &sf);
+ CHECK_RESULT(fstatfs, CAP_FSTATFS, ret == 0);
+
+ ret = fpathconf(fd_cap, _PC_NAME_MAX);
+ CHECK_RESULT(fpathconf, CAP_FPATHCONF, ret >= 0);
+
+ ret = futimes(fd_cap, NULL);
+ CHECK_RESULT(futimes, CAP_FUTIMES, ret == 0);
+
+ ret = openat(dirfd, "cap_futimesat", O_CREAT, 0600);
+ CHECK(ret >= 0);
+ CHECK(close(ret) == 0);
+ ret = futimesat(dfd_cap, "cap_futimesat", NULL);
+ CHECK_RESULT(futimesat, CAP_FUTIMES | CAP_LOOKUP, ret == 0);
+ CHECK(unlinkat(dirfd, "cap_futimesat", 0) == 0);
+
+ ret = openat(dirfd, "cap_linkat_src", O_CREAT, 0600);
+ CHECK(ret >= 0);
+ CHECK(close(ret) == 0);
+ ret = linkat(dirfd, "cap_linkat_src", dfd_cap, "cap_linkat_dst", 0);
+ CHECK_RESULT(linkat, CAP_LINKAT | CAP_LOOKUP, ret == 0);
+ CHECK(unlinkat(dirfd, "cap_linkat_src", 0) == 0);
+ CHECK(ret == -1 || unlinkat(dirfd, "cap_linkat_dst", 0) == 0);
+
+ ret = mkdirat(dfd_cap, "cap_mkdirat", 0700);
+ CHECK_RESULT(mkdirat, CAP_MKDIRAT | CAP_LOOKUP, ret == 0);
+ CHECK(ret == -1 || unlinkat(dirfd, "cap_mkdirat", AT_REMOVEDIR) == 0);
+
+ ret = mkfifoat(dfd_cap, "cap_mkfifoat", 0600);
+ CHECK_RESULT(mkfifoat, CAP_MKFIFOAT | CAP_LOOKUP, ret == 0);
+ CHECK(ret == -1 || unlinkat(dirfd, "cap_mkfifoat", 0) == 0);
+
+ ret = mknodat(dfd_cap, "cap_mknodat", S_IFCHR | 0600, 0);
+ CHECK_RESULT(mknodat, CAP_MKNODAT | CAP_LOOKUP, ret == 0);
+ CHECK(ret == -1 || unlinkat(dirfd, "cap_mknodat", 0) == 0);
+
+ /* TODO: renameat(2) */
+
+ ret = symlinkat("test", dfd_cap, "cap_symlinkat");
+ CHECK_RESULT(symlinkat, CAP_SYMLINKAT | CAP_LOOKUP, ret == 0);
+ CHECK(ret == -1 || unlinkat(dirfd, "cap_symlinkat", 0) == 0);
+
+ ret = openat(dirfd, "cap_unlinkat", O_CREAT, 0600);
+ CHECK(ret >= 0);
+ CHECK(close(ret) == 0);
+ ret = unlinkat(dfd_cap, "cap_unlinkat", 0);
+ CHECK_RESULT(unlinkat, CAP_UNLINKAT | CAP_LOOKUP, ret == 0);
+ CHECK(ret == 0 || unlinkat(dirfd, "cap_unlinkat", 0) == 0);
+ ret = mkdirat(dirfd, "cap_unlinkat", 0700);
+ CHECK(ret == 0);
+ ret = unlinkat(dfd_cap, "cap_unlinkat", AT_REMOVEDIR);
+ CHECK_RESULT(unlinkat, CAP_UNLINKAT | CAP_LOOKUP, ret == 0);
+ CHECK(ret == 0 || unlinkat(dirfd, "cap_unlinkat", AT_REMOVEDIR) == 0);
+
+ pollfd.fd = fd_cap;
+ pollfd.events = POLLIN | POLLERR | POLLHUP;
+ pollfd.revents = 0;
+
+ ret = poll(&pollfd, 1, 0);
+ if (rights & CAP_EVENT)
+ CHECK((pollfd.revents & POLLNVAL) == 0);
+ else
+ CHECK((pollfd.revents & POLLNVAL) != 0);
+
+ /* XXX: select, kqueue */
+
+ close(fd_cap);
+ close(fd_capcap);
+
+ if (success == -1) {
+ fprintf(stderr, "No tests for rights 0x%jx.\n",
+ (uintmax_t)rights);
+ success = FAILED;
+ }
+ return (success);
+}
+
+#define TRY(rights) \
+do { \
+ if (success == PASSED) \
+ success = try_file_ops(filefd, dirfd, (rights)); \
+ else \
+ /* We've already failed, but try the test anyway. */ \
+ try_file_ops(filefd, dirfd, (rights)); \
+} while (0)
+
+#define KEEP_ERRNO(...) do { \
+ int _saved_errno = errno; \
+ __VA_ARGS__; \
+ errno = _saved_errno; \
+} while (0);
+
+int
+test_capabilities(void)
+{
+ int filefd, dirfd, tmpfd;
+ int success = PASSED;
+ char file[] = "/tmp/cap_test.XXXXXXXXXX";
+ char dir[] = "/tmp/cap_test.XXXXXXXXXX";
+
+ filefd = mkstemp(file);
+ if (filefd < 0)
+ err(-1, "mkstemp");
+ if (mkdtemp(dir) == NULL) {
+ KEEP_ERRNO(unlink(file));
+ err(-1, "mkdtemp");
+ }
+ dirfd = open(dir, O_RDONLY | O_DIRECTORY);
+ if (dirfd == -1) {
+ KEEP_ERRNO(unlink(file));
+ KEEP_ERRNO(rmdir(dir));
+ err(-1, "open");
+ }
+ tmpfd = open("/tmp", O_RDONLY | O_DIRECTORY);
+ if (tmpfd == -1) {
+ KEEP_ERRNO(unlink(file));
+ KEEP_ERRNO(rmdir(dir));
+ err(-1, "open");
+ }
+
+ if (cap_enter() == -1) {
+ KEEP_ERRNO(unlink(file));
+ KEEP_ERRNO(rmdir(dir));
+ err(-1, "cap_enter");
+ }
+
+ TRY(CAP_READ);
+ TRY(CAP_WRITE);
+ TRY(CAP_SEEK);
+ TRY(CAP_PREAD);
+ TRY(CAP_PWRITE);
+ TRY(CAP_READ | CAP_WRITE);
+ TRY(CAP_PREAD | CAP_PWRITE);
+ TRY(CAP_MMAP);
+ TRY(CAP_MMAP_R);
+ TRY(CAP_MMAP_W);
+ TRY(CAP_MMAP_X);
+ TRY(CAP_MMAP_RW);
+ TRY(CAP_MMAP_RX);
+ TRY(CAP_MMAP_WX);
+ TRY(CAP_MMAP_RWX);
+ TRY(CAP_CREATE | CAP_READ | CAP_LOOKUP);
+ TRY(CAP_CREATE | CAP_WRITE | CAP_LOOKUP);
+ TRY(CAP_CREATE | CAP_READ | CAP_WRITE | CAP_LOOKUP);
+#ifdef TODO
+ TRY(CAP_FEXECVE);
+#endif
+ TRY(CAP_FSYNC);
+ TRY(CAP_FSYNC | CAP_READ | CAP_LOOKUP);
+ TRY(CAP_FSYNC | CAP_WRITE | CAP_LOOKUP);
+ TRY(CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_LOOKUP);
+ TRY(CAP_FTRUNCATE);
+ TRY(CAP_FTRUNCATE | CAP_READ | CAP_LOOKUP);
+ TRY(CAP_FTRUNCATE | CAP_WRITE | CAP_LOOKUP);
+ TRY(CAP_FTRUNCATE | CAP_READ | CAP_WRITE | CAP_LOOKUP);
+#ifdef TODO
+ TRY(CAP_FCHDIR);
+#endif
+ TRY(CAP_FCHFLAGS);
+ TRY(CAP_FCHOWN);
+ TRY(CAP_FCHOWN | CAP_LOOKUP);
+ TRY(CAP_FCHMOD | CAP_LOOKUP);
+ TRY(CAP_FCNTL);
+#ifdef TODO
+ TRY(CAP_FLOCK);
+#endif
+ TRY(CAP_FPATHCONF);
+#ifdef TODO
+ TRY(CAP_FSCK);
+#endif
+ TRY(CAP_FSTAT | CAP_LOOKUP);
+ TRY(CAP_FSTATFS);
+ TRY(CAP_FUTIMES | CAP_LOOKUP);
+ TRY(CAP_LINKAT | CAP_LOOKUP);
+ TRY(CAP_MKDIRAT | CAP_LOOKUP);
+ TRY(CAP_MKFIFOAT | CAP_LOOKUP);
+ TRY(CAP_MKNODAT | CAP_LOOKUP);
+ TRY(CAP_SYMLINKAT | CAP_LOOKUP);
+ TRY(CAP_UNLINKAT | CAP_LOOKUP);
+ /* Rename needs CAP_RENAMEAT on source directory and CAP_LINKAT on destination directory. */
+ TRY(CAP_RENAMEAT | CAP_UNLINKAT | CAP_LOOKUP);
+#ifdef TODO
+ TRY(CAP_LOOKUP);
+ TRY(CAP_EXTATTR_DELETE);
+ TRY(CAP_EXTATTR_GET);
+ TRY(CAP_EXTATTR_LIST);
+ TRY(CAP_EXTATTR_SET);
+ TRY(CAP_ACL_CHECK);
+ TRY(CAP_ACL_DELETE);
+ TRY(CAP_ACL_GET);
+ TRY(CAP_ACL_SET);
+ TRY(CAP_ACCEPT);
+ TRY(CAP_BIND);
+ TRY(CAP_CONNECT);
+ TRY(CAP_GETPEERNAME);
+ TRY(CAP_GETSOCKNAME);
+ TRY(CAP_GETSOCKOPT);
+ TRY(CAP_LISTEN);
+ TRY(CAP_PEELOFF);
+ TRY(CAP_RECV);
+ TRY(CAP_SEND);
+ TRY(CAP_SETSOCKOPT);
+ TRY(CAP_SHUTDOWN);
+ TRY(CAP_MAC_GET);
+ TRY(CAP_MAC_SET);
+ TRY(CAP_SEM_GETVALUE);
+ TRY(CAP_SEM_POST);
+ TRY(CAP_SEM_WAIT);
+ TRY(CAP_POST_EVENT);
+ TRY(CAP_EVENT);
+ TRY(CAP_IOCTL);
+ TRY(CAP_TTYHOOK);
+ TRY(CAP_PDGETPID);
+ TRY(CAP_PDWAIT);
+ TRY(CAP_PDKILL);
+#endif
+
+ (void)unlinkat(tmpfd, file + strlen("/tmp/"), 0);
+ (void)unlinkat(tmpfd, dir + strlen("/tmp/"), AT_REMOVEDIR);
+
+ return (success);
+}
diff --git a/tools/regression/security/cap_test/cap_test_capmode.c b/tools/regression/security/cap_test/cap_test_capmode.c
new file mode 100644
index 000000000000..9d1eaf45ea85
--- /dev/null
+++ b/tools/regression/security/cap_test/cap_test_capmode.c
@@ -0,0 +1,195 @@
+/*-
+ * Copyright (c) 2008-2009 Robert N. M. Watson
+ * Copyright (c) 2011 Jonathan Anderson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test routines to make sure a variety of system calls are or are not
+ * available in capability mode. The goal is not to see if they work, just
+ * whether or not they return the expected ECAPMODE.
+ */
+
+#include <sys/param.h>
+#include <sys/capsicum.h>
+#include <sys/errno.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <machine/sysarch.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "cap_test.h"
+
+#define CHECK_SYSCALL_VOID_NOT_ECAPMODE(syscall, ...) do { \
+ errno = 0; \
+ (void)syscall(__VA_ARGS__); \
+ if (errno == ECAPMODE) \
+ FAIL("capmode: %s failed with ECAPMODE", #syscall); \
+} while (0)
+
+int
+test_capmode(void)
+{
+ struct statfs statfs;
+ struct stat sb;
+ long sysarch_arg = 0;
+ int fd_close, fd_dir, fd_file, fd_socket, fd2[2];
+ int success = PASSED;
+ pid_t pid, wpid;
+ char ch;
+
+ /* Open some files to play with. */
+ REQUIRE(fd_file = open("/tmp/cap_capmode", O_RDWR|O_CREAT, 0644));
+ REQUIRE(fd_close = open("/dev/null", O_RDWR));
+ REQUIRE(fd_dir = open("/tmp", O_RDONLY));
+ REQUIRE(fd_socket = socket(PF_INET, SOCK_DGRAM, 0));
+
+ /* Enter capability mode. */
+ REQUIRE(cap_enter());
+
+ /*
+ * System calls that are not permitted in capability mode.
+ */
+ CHECK_CAPMODE(access, "/tmp/cap_capmode_access", F_OK);
+ CHECK_CAPMODE(acct, "/tmp/cap_capmode_acct");
+ CHECK_CAPMODE(bind, PF_INET, NULL, 0);
+ CHECK_CAPMODE(chdir, "/tmp/cap_capmode_chdir");
+ CHECK_CAPMODE(chflags, "/tmp/cap_capmode_chflags", UF_NODUMP);
+ CHECK_CAPMODE(chmod, "/tmp/cap_capmode_chmod", 0644);
+ CHECK_CAPMODE(chown, "/tmp/cap_capmode_chown", -1, -1);
+ CHECK_CAPMODE(chroot, "/tmp/cap_capmode_chroot");
+ CHECK_CAPMODE(connect, PF_INET, NULL, 0);
+ CHECK_CAPMODE(creat, "/tmp/cap_capmode_creat", 0644);
+ CHECK_CAPMODE(fchdir, fd_dir);
+ CHECK_CAPMODE(getfsstat, &statfs, sizeof(statfs), MNT_NOWAIT);
+ CHECK_CAPMODE(link, "/tmp/foo", "/tmp/bar");
+ CHECK_CAPMODE(lstat, "/tmp/cap_capmode_lstat", &sb);
+ CHECK_CAPMODE(mknod, "/tmp/capmode_mknod", 06440, 0);
+ CHECK_CAPMODE(mount, "procfs", "/not_mounted", 0, NULL);
+ CHECK_CAPMODE(open, "/dev/null", O_RDWR);
+ CHECK_CAPMODE(readlink, "/tmp/cap_capmode_readlink", NULL, 0);
+ CHECK_CAPMODE(revoke, "/tmp/cap_capmode_revoke");
+ CHECK_CAPMODE(stat, "/tmp/cap_capmode_stat", &sb);
+ CHECK_CAPMODE(symlink,
+ "/tmp/cap_capmode_symlink_from",
+ "/tmp/cap_capmode_symlink_to");
+ CHECK_CAPMODE(unlink, "/tmp/cap_capmode_unlink");
+ CHECK_CAPMODE(unmount, "/not_mounted", 0);
+
+ /*
+ * System calls that are permitted in capability mode.
+ */
+ CHECK_SYSCALL_SUCCEEDS(close, fd_close);
+ CHECK_SYSCALL_SUCCEEDS(dup, fd_file);
+ CHECK_SYSCALL_SUCCEEDS(fstat, fd_file, &sb);
+ CHECK_SYSCALL_SUCCEEDS(lseek, fd_file, 0, SEEK_SET);
+ CHECK_SYSCALL_SUCCEEDS(msync, &fd_file, 8192, MS_ASYNC);
+ CHECK_SYSCALL_SUCCEEDS(profil, NULL, 0, 0, 0);
+ CHECK_SYSCALL_SUCCEEDS(read, fd_file, &ch, sizeof(ch));
+ CHECK_SYSCALL_SUCCEEDS(recvfrom, fd_socket, NULL, 0, 0, NULL, NULL);
+ CHECK_SYSCALL_SUCCEEDS(setuid, getuid());
+ CHECK_SYSCALL_SUCCEEDS(write, fd_file, &ch, sizeof(ch));
+
+ /*
+ * These calls will fail for lack of e.g. a proper name to send to,
+ * but they are allowed in capability mode, so errno != ECAPMODE.
+ */
+ CHECK_NOT_CAPMODE(accept, fd_socket, NULL, NULL);
+ CHECK_NOT_CAPMODE(getpeername, fd_socket, NULL, NULL);
+ CHECK_NOT_CAPMODE(getsockname, fd_socket, NULL, NULL);
+ CHECK_NOT_CAPMODE(fchflags, fd_file, UF_NODUMP);
+ CHECK_NOT_CAPMODE(recvmsg, fd_socket, NULL, 0);
+ CHECK_NOT_CAPMODE(sendmsg, fd_socket, NULL, 0);
+ CHECK_NOT_CAPMODE(sendto, fd_socket, NULL, 0, 0, NULL, 0);
+
+ /*
+ * System calls which should be allowed in capability mode, but which
+ * don't return errors, and are thus difficult to check.
+ *
+ * We will try anyway, by checking errno.
+ */
+ CHECK_SYSCALL_VOID_NOT_ECAPMODE(getegid);
+ CHECK_SYSCALL_VOID_NOT_ECAPMODE(geteuid);
+ CHECK_SYSCALL_VOID_NOT_ECAPMODE(getgid);
+ CHECK_SYSCALL_VOID_NOT_ECAPMODE(getpid);
+ CHECK_SYSCALL_VOID_NOT_ECAPMODE(getppid);
+ CHECK_SYSCALL_VOID_NOT_ECAPMODE(getuid);
+
+ /*
+ * Finally, tests for system calls that don't fit the pattern very well.
+ */
+ pid = fork();
+ if (pid >= 0) {
+ if (pid == 0) {
+ exit(0);
+ } else if (pid > 0) {
+ wpid = waitpid(pid, NULL, 0);
+ if (wpid < 0) {
+ if (errno != ECAPMODE)
+ FAIL("capmode:waitpid");
+ } else
+ FAIL("capmode:waitpid succeeded");
+ }
+ } else
+ FAIL("capmode:fork");
+
+ if (getlogin() == NULL)
+ FAIL("test_sycalls:getlogin %d", errno);
+
+ if (getsockname(fd_socket, NULL, NULL) < 0) {
+ if (errno == ECAPMODE)
+ FAIL("capmode:getsockname");
+ }
+
+ /* XXXRW: ktrace */
+
+ if (pipe(fd2) == 0) {
+ close(fd2[0]);
+ close(fd2[1]);
+ } else if (errno == ECAPMODE)
+ FAIL("capmode:pipe");
+
+ /* XXXRW: ptrace. */
+
+ /* sysarch() is, by definition, architecture-dependent */
+#if defined (__amd64__) || defined (__i386__)
+ CHECK_CAPMODE(sysarch, I386_SET_IOPERM, &sysarch_arg);
+#else
+ /* XXXJA: write a test for arm */
+ FAIL("capmode:no sysarch() test for current architecture");
+#endif
+
+ /* XXXRW: No error return from sync(2) to test. */
+
+ return (success);
+}
diff --git a/tools/regression/security/cap_test/cap_test_fcntl.c b/tools/regression/security/cap_test/cap_test_fcntl.c
new file mode 100644
index 000000000000..5ef9c55a3e4d
--- /dev/null
+++ b/tools/regression/security/cap_test/cap_test_fcntl.c
@@ -0,0 +1,114 @@
+/*-
+ * Copyright (c) 2009-2011 Robert N. M. Watson
+ * Copyright (c) 2011 Jonathan Anderson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test that fcntl works in capability mode.
+ */
+
+#include <sys/types.h>
+#include <sys/capsicum.h>
+#include <sys/errno.h>
+#include <sys/ipc.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "cap_test.h"
+
+/* A filename->descriptor mapping. */
+struct fd {
+ char *f_name;
+ int f_fd;
+};
+
+/*
+ * Ensure that fcntl() works consistently for both regular file descriptors and
+ * capability-wrapped ones.
+ */
+int
+test_fcntl(void)
+{
+ int success = PASSED;
+ cap_rights_t rights = CAP_READ | CAP_FCNTL;
+
+ /*
+ * Open some files of different types, and wrap them in capabilities.
+ */
+ struct fd files[] = {
+ { "file", open("/etc/passwd", O_RDONLY) },
+ { "socket", socket(PF_LOCAL, SOCK_STREAM, 0) },
+ { "SHM", shm_open(SHM_ANON, O_RDWR, 0600) },
+ };
+ REQUIRE(files[0].f_fd);
+ REQUIRE(files[1].f_fd);
+ REQUIRE(files[2].f_fd);
+
+ struct fd caps[] = {
+ { "file cap", cap_new(files[0].f_fd, rights) },
+ { "socket cap", cap_new(files[1].f_fd, rights) },
+ { "SHM cap", cap_new(files[2].f_fd, rights) },
+ };
+ REQUIRE(caps[0].f_fd);
+ REQUIRE(caps[1].f_fd);
+ REQUIRE(caps[2].f_fd);
+
+ struct fd all[] = {
+ files[0], caps[0],
+ files[1], caps[1],
+ files[2], caps[2],
+ };
+ const size_t len = sizeof(all) / sizeof(struct fd);
+
+ REQUIRE(cap_enter());
+
+ /*
+ * Ensure that we can fcntl() all the files that we opened above.
+ */
+ for (size_t i = 0; i < len; i++)
+ {
+ struct fd f = all[i];
+ int cap;
+
+ CHECK_SYSCALL_SUCCEEDS(fcntl, f.f_fd, F_GETFL, 0);
+ REQUIRE(cap = cap_new(f.f_fd, CAP_READ));
+ if (fcntl(f.f_fd, F_GETFL, 0) == -1)
+ FAIL("Error calling fcntl('%s', F_GETFL)", f.f_name);
+ else
+ CHECK_NOTCAPABLE(fcntl, cap, F_GETFL, 0);
+ }
+
+ return (success);
+}
+
diff --git a/tools/regression/security/cap_test/cap_test_pdfork.c b/tools/regression/security/cap_test/cap_test_pdfork.c
new file mode 100644
index 000000000000..fdd0b5d6ad04
--- /dev/null
+++ b/tools/regression/security/cap_test/cap_test_pdfork.c
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (c) 2009-2011 Robert N. M. Watson
+ * Copyright (c) 2011 Jonathan Anderson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test routines to make sure a variety of system calls are or are not
+ * available in capability mode. The goal is not to see if they work, just
+ * whether or not they return the expected ECAPMODE.
+ */
+
+#include <sys/types.h>
+
+#include <sys/capsium.h>
+#include <sys/errno.h>
+#include <sys/procdesc.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <stdio.h>
+#include <time.h>
+
+#include "cap_test.h"
+
+int
+test_pdfork(void)
+{
+ struct stat stat;
+ int success = PASSED;
+ int pd, error;
+ pid_t pid;
+ time_t now;
+
+ //cap_enter();
+
+ pid = pdfork(&pd, 0);
+ if (pid < 0)
+ err(-1, "pdfork");
+
+ else if (pid == 0) {
+ /*
+ * Child process.
+ *
+ * pd should not be a valid process descriptor.
+ */
+ error = pdgetpid(pd, &pid);
+ if (error != -1)
+ FAILX("pdgetpid succeeded");
+ else if (errno != EBADF)
+ FAIL("pdgetpid failed, but errno != EBADF");
+
+ exit(success);
+ }
+
+ /* Parent process. Ensure that [acm]times have been set correctly. */
+ REQUIRE(fstat(pd, &stat));
+
+ now = time(NULL);
+ CHECK(now != (time_t)-1);
+
+ CHECK(now >= stat.st_birthtime);
+ CHECK((now - stat.st_birthtime) < 2);
+ CHECK(stat.st_birthtime == stat.st_atime);
+ CHECK(stat.st_atime == stat.st_ctime);
+ CHECK(stat.st_ctime == stat.st_mtime);
+
+ /* Wait for the child to finish. */
+ error = pdgetpid(pd, &pid);
+ CHECK(error == 0);
+ CHECK(pid > 0);
+
+ int status;
+ while (waitpid(pid, &status, 0) != pid) {}
+ if ((success == PASSED) && WIFEXITED(status))
+ success = WEXITSTATUS(status);
+ else
+ success = FAILED;
+
+ return (success);
+}
diff --git a/tools/regression/security/cap_test/cap_test_pdkill.c b/tools/regression/security/cap_test/cap_test_pdkill.c
new file mode 100644
index 000000000000..2f2a18b3baa7
--- /dev/null
+++ b/tools/regression/security/cap_test/cap_test_pdkill.c
@@ -0,0 +1,96 @@
+/*-
+ * Copyright (c) 2009-2011 Robert N. M. Watson
+ * Copyright (c) 2011 Jonathan Anderson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test routines to make sure a variety of system calls are or are not
+ * available in capability mode. The goal is not to see if they work, just
+ * whether or not they return the expected ECAPMODE.
+ */
+
+#include <sys/types.h>
+
+#include <sys/capsicum.h>
+#include <sys/errno.h>
+#include <sys/procdesc.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <stdio.h>
+
+#include "cap_test.h"
+
+void handle_signal(int);
+void handle_signal(int sig) {
+ exit(PASSED);
+}
+
+int
+test_pdkill(void)
+{
+ int success = PASSED;
+ int pd, error;
+ pid_t pid;
+
+ //cap_enter();
+
+ error = pdfork(&pd, 0);
+ if (error < 0)
+ err(-1, "pdfork");
+
+ else if (error == 0) {
+ signal(SIGINT, handle_signal);
+ sleep(3600);
+ exit(FAILED);
+ }
+
+ /* Parent process; find the child's PID (we'll need it later). */
+ error = pdgetpid(pd, &pid);
+ if (error != 0)
+ FAIL("pdgetpid");
+
+ /* Kill the child! */
+ usleep(100);
+ error = pdkill(pd, SIGINT);
+ if (error != 0)
+ FAIL("pdkill");
+
+ /* Make sure the child finished properly. */
+ int status;
+ while (waitpid(pid, &status, 0) != pid) {}
+ if ((success == PASSED) && WIFEXITED(status))
+ success = WEXITSTATUS(status);
+ else
+ success = FAILED;
+
+ return (success);
+}
diff --git a/tools/regression/security/cap_test/cap_test_relative.c b/tools/regression/security/cap_test/cap_test_relative.c
new file mode 100644
index 000000000000..ef2e561b0158
--- /dev/null
+++ b/tools/regression/security/cap_test/cap_test_relative.c
@@ -0,0 +1,147 @@
+/*-
+ * Copyright (c) 2009-2011 Robert N. M. Watson
+ * Copyright (c) 2011 Jonathan Anderson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/capsicum.h>
+#include <sys/errno.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "cap_test.h"
+
+/*
+ * Test openat(2) in a variety of sitations to ensure that it obeys Capsicum
+ * "strict relative" rules:
+ *
+ * 1. Use strict relative lookups in capability mode or when operating
+ * relative to a capability.
+ * 2. When performing strict relative lookups, absolute paths (including
+ * symlinks to absolute paths) are not allowed, nor are paths containing
+ * '..' components.
+ */
+int
+test_relative(void)
+{
+ int success = PASSED;
+ int fd, etc, etc_cap, etc_cap_ro, etc_cap_base, etc_cap_all;
+ cap_rights_t baserights = CAP_READ | CAP_WRITE | CAP_SEEK | CAP_LOOKUP;
+ cap_rights_t rights;
+
+ REQUIRE(etc = open("/etc/", O_RDONLY));
+ CHECK_SYSCALL_SUCCEEDS(cap_getrights, etc, &rights);
+ CHECK_RIGHTS(rights, CAP_ALL);
+
+ MAKE_CAPABILITY(etc_cap, etc, CAP_READ);
+ MAKE_CAPABILITY(etc_cap_ro, etc, CAP_READ | CAP_LOOKUP);
+ MAKE_CAPABILITY(etc_cap_base, etc, baserights);
+ MAKE_CAPABILITY(etc_cap_all, etc, CAP_MASK_VALID);
+
+ /*
+ * openat(2) with regular file descriptors in non-capability mode
+ * should Just Work (tm).
+ */
+ CHECK_SYSCALL_SUCCEEDS(openat, etc, "/etc/passwd", O_RDONLY);
+ CHECK_SYSCALL_SUCCEEDS(openat, AT_FDCWD, "/etc/passwd", O_RDONLY);
+ CHECK_SYSCALL_SUCCEEDS(openat, etc, "passwd", O_RDONLY);
+ CHECK_SYSCALL_SUCCEEDS(openat, etc, "../etc/passwd", O_RDONLY);
+
+ /*
+ * Lookups relative to capabilities should be strictly relative.
+ *
+ * When not in capability mode, we don't actually require CAP_LOOKUP.
+ */
+ CHECK_SYSCALL_SUCCEEDS(openat, etc_cap_ro, "passwd", O_RDONLY);
+ CHECK_SYSCALL_SUCCEEDS(openat, etc_cap_base, "passwd", O_RDONLY);
+ CHECK_SYSCALL_SUCCEEDS(openat, etc_cap_all, "passwd", O_RDONLY);
+
+ CHECK_NOTCAPABLE(openat, etc_cap_ro, "../etc/passwd", O_RDONLY);
+ CHECK_NOTCAPABLE(openat, etc_cap_base, "../etc/passwd", O_RDONLY);
+
+ /*
+ * This requires discussion: do we treat a capability with
+ * CAP_MASK_VALID *exactly* like a non-capability file descriptor
+ * (currently, the implementation says yes)?
+ */
+ CHECK_SYSCALL_SUCCEEDS(openat, etc_cap_all, "../etc/passwd", O_RDONLY);
+
+ /*
+ * A file opened relative to a capability should itself be a capability.
+ */
+ CHECK_SYSCALL_SUCCEEDS(cap_getrights, etc_cap_base, &rights);
+
+ REQUIRE(fd = openat(etc_cap_base, "passwd", O_RDONLY));
+ CHECK_SYSCALL_SUCCEEDS(cap_getrights, fd, &rights);
+ CHECK_RIGHTS(rights, baserights);
+
+ /*
+ * Enter capability mode; now ALL lookups are strictly relative.
+ */
+ REQUIRE(cap_enter());
+
+ /*
+ * Relative lookups on regular files or capabilities with CAP_LOOKUP
+ * ought to succeed.
+ */
+ CHECK_SYSCALL_SUCCEEDS(openat, etc, "passwd", O_RDONLY);
+ CHECK_SYSCALL_SUCCEEDS(openat, etc_cap_ro, "passwd", O_RDONLY);
+ CHECK_SYSCALL_SUCCEEDS(openat, etc_cap_base, "passwd", O_RDONLY);
+ CHECK_SYSCALL_SUCCEEDS(openat, etc_cap_all, "passwd", O_RDONLY);
+
+ /*
+ * Lookup relative to capabilities without CAP_LOOKUP should fail.
+ */
+ CHECK_NOTCAPABLE(openat, etc_cap, "passwd", O_RDONLY);
+
+ /*
+ * Absolute lookups should fail.
+ */
+ CHECK_CAPMODE(openat, AT_FDCWD, "/etc/passwd", O_RDONLY);
+ CHECK_NOTCAPABLE(openat, etc, "/etc/passwd", O_RDONLY);
+
+ /*
+ * Lookups containing '..' should fail in capability mode.
+ */
+ CHECK_NOTCAPABLE(openat, etc, "../etc/passwd", O_RDONLY);
+ CHECK_NOTCAPABLE(openat, etc_cap_ro, "../etc/passwd", O_RDONLY);
+ CHECK_NOTCAPABLE(openat, etc_cap_base, "../etc/passwd", O_RDONLY);
+
+ REQUIRE(fd = openat(etc, "passwd", O_RDONLY));
+ CHECK_SYSCALL_SUCCEEDS(cap_getrights, fd, &rights);
+
+ /*
+ * A file opened relative to a capability should itself be a capability.
+ */
+ REQUIRE(fd = openat(etc_cap_base, "passwd", O_RDONLY));
+ CHECK_SYSCALL_SUCCEEDS(cap_getrights, fd, &rights);
+ CHECK_RIGHTS(rights, baserights);
+
+ return success;
+}
diff --git a/tools/regression/security/cap_test/cap_test_sysctl.c b/tools/regression/security/cap_test/cap_test_sysctl.c
new file mode 100644
index 000000000000..dcc6970606da
--- /dev/null
+++ b/tools/regression/security/cap_test/cap_test_sysctl.c
@@ -0,0 +1,62 @@
+/*-
+ * Copyright (c) 2008-2011 Robert N. M. Watson
+ * Copyright (c) 2011 Jonathan Anderson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test that various sysctls are (and aren't) available on capability mode.
+ */
+
+#include <sys/types.h>
+#include <sys/capsicum.h>
+#include <sys/errno.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "cap_test.h"
+
+/*
+ * Certain sysctls are permitted in capability mode, but most are not. Test
+ * for the ones that should be, and try one or two that shouldn't.
+ */
+int
+test_sysctl(void)
+{
+ int i, oid[2];
+ int success = PASSED;
+ size_t len;
+
+ oid[0] = CTL_KERN;
+ oid[1] = KERN_OSRELDATE;
+ len = sizeof(i);
+ CHECK(sysctl(oid, 2, &i, &len, NULL, 0) == 0);
+
+ return (success);
+}