1 /*- 2 * Copyright (c) 2016 Ed Maste <emaste@FreeBSD.org> 3 * Copyright (c) 2016 Conrad Meyer <cem@FreeBSD.org> 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/capsicum.h> 32 #include <sys/sysctl.h> 33 #include <sys/stat.h> 34 35 #include <atf-c.h> 36 #include <errno.h> 37 #include <stdlib.h> 38 #include <string.h> 39 40 #include "freebsd_test_suite/macros.h" 41 42 static int dirfd = -1; 43 static char *abspath; 44 45 static void 46 touchat(int _dirfd, const char *name) 47 { 48 int fd; 49 50 ATF_REQUIRE((fd = openat(_dirfd, name, O_CREAT | O_TRUNC | O_WRONLY, 51 0777)) >= 0); 52 ATF_REQUIRE(close(fd) == 0); 53 } 54 55 static void 56 prepare_dotdot_tests(void) 57 { 58 char cwd[MAXPATHLEN]; 59 60 ATF_REQUIRE(getcwd(cwd, sizeof(cwd)) != NULL); 61 asprintf(&abspath, "%s/testdir/d1/f1", cwd); 62 63 ATF_REQUIRE(mkdir("testdir", 0777) == 0); 64 ATF_REQUIRE((dirfd = open("testdir", O_RDONLY)) >= 0); 65 66 ATF_REQUIRE(mkdirat(dirfd, "d1", 0777) == 0); 67 ATF_REQUIRE(mkdirat(dirfd, "d1/d2", 0777) == 0); 68 ATF_REQUIRE(mkdirat(dirfd, "d1/d2/d3", 0777) == 0); 69 touchat(dirfd, "d1/f1"); 70 touchat(dirfd, "d1/d2/f2"); 71 touchat(dirfd, "d1/d2/d3/f3"); 72 ATF_REQUIRE(symlinkat("d1/d2/d3", dirfd, "l3") == 0); 73 ATF_REQUIRE(symlinkat("../testdir/d1", dirfd, "lup") == 0); 74 ATF_REQUIRE(symlinkat("../..", dirfd, "d1/d2/d3/ld1") == 0); 75 ATF_REQUIRE(symlinkat("../../f1", dirfd, "d1/d2/d3/lf1") == 0); 76 } 77 78 static void 79 check_capsicum(void) 80 { 81 ATF_REQUIRE_FEATURE("security_capabilities"); 82 ATF_REQUIRE_FEATURE("security_capability_mode"); 83 } 84 85 /* 86 * Positive tests 87 */ 88 ATF_TC(openat__basic_positive); 89 ATF_TC_HEAD(openat__basic_positive, tc) 90 { 91 atf_tc_set_md_var(tc, "descr", "Basic positive openat testcases"); 92 } 93 94 ATF_TC_BODY(openat__basic_positive, tc) 95 { 96 prepare_dotdot_tests(); 97 98 ATF_REQUIRE(openat(dirfd, "d1/d2/d3/f3", O_RDONLY) >= 0); 99 ATF_REQUIRE(openat(dirfd, "d1/d2/d3/../../f1", O_RDONLY) >= 0); 100 ATF_REQUIRE(openat(dirfd, "l3/f3", O_RDONLY) >= 0); 101 ATF_REQUIRE(openat(dirfd, "l3/../../f1", O_RDONLY) >= 0); 102 ATF_REQUIRE(openat(dirfd, "../testdir/d1/f1", O_RDONLY) >= 0); 103 ATF_REQUIRE(openat(dirfd, "lup/f1", O_RDONLY) >= 0); 104 ATF_REQUIRE(openat(dirfd, "l3/ld1", O_RDONLY) >= 0); 105 ATF_REQUIRE(openat(dirfd, "l3/lf1", O_RDONLY) >= 0); 106 ATF_REQUIRE(open(abspath, O_RDONLY) >= 0); 107 ATF_REQUIRE(openat(dirfd, abspath, O_RDONLY) >= 0); 108 } 109 110 ATF_TC(lookup_cap_dotdot__basic); 111 ATF_TC_HEAD(lookup_cap_dotdot__basic, tc) 112 { 113 atf_tc_set_md_var(tc, "descr", 114 "Validate cap-mode (testdir)/d1/.. lookup"); 115 } 116 117 ATF_TC_BODY(lookup_cap_dotdot__basic, tc) 118 { 119 cap_rights_t rights; 120 121 check_capsicum(); 122 prepare_dotdot_tests(); 123 124 cap_rights_init(&rights, CAP_LOOKUP, CAP_READ); 125 ATF_REQUIRE(cap_rights_limit(dirfd, &rights) >= 0); 126 127 atf_tc_expect_signal(SIGABRT, "needs change done upstream in atf/kyua according to cem: bug 215690"); 128 129 ATF_REQUIRE(cap_enter() >= 0); 130 131 ATF_REQUIRE_MSG(openat(dirfd, "d1/..", O_RDONLY) >= 0, "%s", 132 strerror(errno)); 133 } 134 135 ATF_TC(lookup_cap_dotdot__advanced); 136 ATF_TC_HEAD(lookup_cap_dotdot__advanced, tc) 137 { 138 atf_tc_set_md_var(tc, "descr", 139 "Validate cap-mode (testdir)/d1/.. lookup"); 140 } 141 142 ATF_TC_BODY(lookup_cap_dotdot__advanced, tc) 143 { 144 cap_rights_t rights; 145 146 check_capsicum(); 147 prepare_dotdot_tests(); 148 149 atf_tc_expect_signal(SIGABRT, "needs change done upstream in atf/kyua according to cem: bug 215690"); 150 151 cap_rights_init(&rights, CAP_LOOKUP, CAP_READ); 152 ATF_REQUIRE(cap_rights_limit(dirfd, &rights) >= 0); 153 154 ATF_REQUIRE(cap_enter() >= 0); 155 156 ATF_REQUIRE(openat(dirfd, "d1/d2/d3/../../f1", O_RDONLY) >= 0); 157 ATF_REQUIRE(openat(dirfd, "l3/../../f1", O_RDONLY) >= 0); 158 ATF_REQUIRE(openat(dirfd, "l3/ld1", O_RDONLY) >= 0); 159 ATF_REQUIRE(openat(dirfd, "l3/lf1", O_RDONLY) >= 0); 160 } 161 162 /* 163 * Negative tests 164 */ 165 ATF_TC(openat__basic_negative); 166 ATF_TC_HEAD(openat__basic_negative, tc) 167 { 168 atf_tc_set_md_var(tc, "descr", "Basic negative openat testcases"); 169 } 170 171 ATF_TC_BODY(openat__basic_negative, tc) 172 { 173 prepare_dotdot_tests(); 174 175 ATF_REQUIRE_ERRNO(ENOENT, 176 openat(dirfd, "does-not-exist", O_RDONLY) < 0); 177 ATF_REQUIRE_ERRNO(ENOENT, 178 openat(dirfd, "l3/does-not-exist", O_RDONLY) < 0); 179 } 180 181 ATF_TC(capmode__negative); 182 ATF_TC_HEAD(capmode__negative, tc) 183 { 184 atf_tc_set_md_var(tc, "descr", "Negative Capability mode testcases"); 185 } 186 187 ATF_TC_BODY(capmode__negative, tc) 188 { 189 int subdirfd; 190 191 check_capsicum(); 192 prepare_dotdot_tests(); 193 194 atf_tc_expect_signal(SIGABRT, "needs change done upstream in atf/kyua according to cem: bug 215690"); 195 196 ATF_REQUIRE(cap_enter() == 0); 197 198 /* open() not permitted in capability mode */ 199 ATF_REQUIRE_ERRNO(ECAPMODE, open("testdir", O_RDONLY) < 0); 200 201 /* AT_FDCWD not permitted in capability mode */ 202 ATF_REQUIRE_ERRNO(ECAPMODE, openat(AT_FDCWD, "d1/f1", O_RDONLY) < 0); 203 204 /* Relative path above dirfd not capable */ 205 ATF_REQUIRE_ERRNO(ENOTCAPABLE, openat(dirfd, "..", O_RDONLY) < 0); 206 ATF_REQUIRE((subdirfd = openat(dirfd, "l3", O_RDONLY)) >= 0); 207 ATF_REQUIRE_ERRNO(ENOTCAPABLE, 208 openat(subdirfd, "../../f1", O_RDONLY) < 0); 209 210 /* Absolute paths not capable */ 211 ATF_REQUIRE_ERRNO(ENOTCAPABLE, openat(dirfd, abspath, O_RDONLY) < 0); 212 213 /* Symlink above dirfd */ 214 ATF_REQUIRE_ERRNO(ENOTCAPABLE, openat(dirfd, "lup/f1", O_RDONLY) < 0); 215 } 216 217 ATF_TC(lookup_cap_dotdot__negative); 218 ATF_TC_HEAD(lookup_cap_dotdot__negative, tc) 219 { 220 atf_tc_set_md_var(tc, "descr", 221 "Validate cap-mode (testdir)/.. lookup fails"); 222 } 223 224 ATF_TC_BODY(lookup_cap_dotdot__negative, tc) 225 { 226 cap_rights_t rights; 227 228 check_capsicum(); 229 prepare_dotdot_tests(); 230 231 cap_rights_init(&rights, CAP_LOOKUP, CAP_READ); 232 ATF_REQUIRE(cap_rights_limit(dirfd, &rights) >= 0); 233 234 atf_tc_expect_signal(SIGABRT, "needs change done upstream in atf/kyua according to cem: bug 215690"); 235 236 ATF_REQUIRE(cap_enter() >= 0); 237 238 ATF_REQUIRE_ERRNO(ENOTCAPABLE, openat(dirfd, "..", O_RDONLY) < 0); 239 ATF_REQUIRE_ERRNO(ENOTCAPABLE, openat(dirfd, "d1/../..", O_RDONLY) < 0); 240 ATF_REQUIRE_ERRNO(ENOTCAPABLE, openat(dirfd, "../testdir/d1/f1", O_RDONLY) < 0); 241 } 242 243 ATF_TP_ADD_TCS(tp) 244 { 245 246 ATF_TP_ADD_TC(tp, openat__basic_positive); 247 ATF_TP_ADD_TC(tp, openat__basic_negative); 248 249 ATF_TP_ADD_TC(tp, capmode__negative); 250 251 ATF_TP_ADD_TC(tp, lookup_cap_dotdot__basic); 252 ATF_TP_ADD_TC(tp, lookup_cap_dotdot__advanced); 253 ATF_TP_ADD_TC(tp, lookup_cap_dotdot__negative); 254 255 return (atf_no_error()); 256 } 257