Index: tests/sys/vfs/Makefile =================================================================== --- tests/sys/vfs/Makefile +++ tests/sys/vfs/Makefile @@ -4,6 +4,8 @@ TESTSDIR= ${TESTSBASE}/sys/vfs +ATF_TESTS_C+= lookup_cap_dotdot + PLAIN_TESTS_SH+= trailing_slash .include Index: tests/sys/vfs/lookup_cap_dotdot.c =================================================================== --- /dev/null +++ tests/sys/vfs/lookup_cap_dotdot.c @@ -0,0 +1,182 @@ +/*- + * Copyright (c) 2016 Conrad Meyer + * + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include +#include +#include + +static void cleanup_dotdot_tests(void); + +static const char testdir_template[] = "/tmp/temp.XXXXXX"; +static char testdir[32]; +static int testdir_fd = -1; + +static void +prepare_dotdot_tests(void) +{ + + strcpy(testdir, testdir_template); + if (mkdtemp(testdir) == NULL) + atf_tc_fail("mkdtemp: %s", strerror(errno)); + if ((testdir_fd = open(testdir, O_RDONLY)) < 0) { + atf_tc_fail_nonfatal("open(%s): %s", testdir, strerror(errno)); + goto fail; + } + if (mkdirat(testdir_fd, "b", 0700) < 0) { + atf_tc_fail_nonfatal("mkdirat(%s, \"b\"): %s", testdir, + strerror(errno)); + goto fail; + } + + return; +fail: + cleanup_dotdot_tests(); + atf_tc_fail("Aborting due to previous failure"); +} + +static void +cleanup_dotdot_tests(void) +{ + + if (testdir_fd >= 0) { + (void)unlinkat(testdir_fd, "b", AT_REMOVEDIR); + close(testdir_fd); + testdir_fd = -1; + } + if (strcmp(testdir, testdir_template) != 0) + (void)rmdir(testdir); +} + +static void +check_capsicum(void) +{ + int sval, rc; + size_t len; + + len = sizeof(sval); + rc = sysctlbyname("kern.features.security_capabilities", &sval, &len, + NULL, 0); + if (rc < 0 || sval == 0) + atf_tc_skip("System doesn't have Capsicum"); + + len = sizeof(sval); + rc = sysctlbyname("kern.features.security_capability_mode", &sval, + &len, NULL, 0); + if (rc < 0 || sval == 0) + atf_tc_skip("System doesn't have Capsicum enabled"); +} + +/* + * Positive tests + */ +ATF_TC_WITH_CLEANUP(lookup_cap_dotdot__basic); +ATF_TC_HEAD(lookup_cap_dotdot__basic, tc) +{ + atf_tc_set_md_var(tc, "require.user", "root"); + atf_tc_set_md_var(tc, "descr", + "Validate cap-mode (a)/b/.. lookup"); +} + +ATF_TC_BODY(lookup_cap_dotdot__basic, tc) +{ + cap_rights_t rights; + int fd; + + check_capsicum(); + prepare_dotdot_tests(); + + cap_rights_init(&rights, CAP_LOOKUP, CAP_READ); + if (cap_rights_limit(testdir_fd, &rights) < 0) + atf_tc_fail("cap_rights_limit: %s", strerror(errno)); + + fd = openat(testdir_fd, "b/..", O_RDONLY); + if (fd < 0) + atf_tc_fail("openat(%s, b/../b): %s", testdir, + strerror(errno)); +} + +ATF_TC_CLEANUP(lookup_cap_dotdot__basic, tc) +{ + + cleanup_dotdot_tests(); +} + +/* + * Negative tests + */ +ATF_TC_WITH_CLEANUP(lookup_cap_dotdot__negative); +ATF_TC_HEAD(lookup_cap_dotdot__negative, tc) +{ + atf_tc_set_md_var(tc, "require.user", "root"); + atf_tc_set_md_var(tc, "descr", + "Validate cap-mode (a)/.. lookup fails"); +} + +ATF_TC_BODY(lookup_cap_dotdot__negative, tc) +{ + cap_rights_t rights; + int fd; + + check_capsicum(); + prepare_dotdot_tests(); + + cap_rights_init(&rights, CAP_LOOKUP, CAP_READ); + if (cap_rights_limit(testdir_fd, &rights) < 0) + atf_tc_fail("cap_rights_limit: %s", strerror(errno)); + + errno = 0; + fd = openat(testdir_fd, "..", O_RDONLY); + if (fd >= 0 || errno != ENOTCAPABLE) + atf_tc_fail("openat(%s, ..): %d", testdir, errno); + + errno = 0; + fd = openat(testdir_fd, "b/../..", O_RDONLY); + if (fd >= 0 || errno != ENOTCAPABLE) + atf_tc_fail("openat(%s, ..): %d", testdir, errno); +} + +ATF_TC_CLEANUP(lookup_cap_dotdot__negative, tc) +{ + + cleanup_dotdot_tests(); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, lookup_cap_dotdot__basic); + ATF_TP_ADD_TC(tp, lookup_cap_dotdot__negative); + + return (atf_no_error()); +}