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 #include <sys/wait.h> 35 36 #include <atf-c.h> 37 #include <assert.h> 38 #include <errno.h> 39 #include <stdlib.h> 40 #include <string.h> 41 42 #include "freebsd_test_suite/macros.h" 43 44 static char *abspath; 45 static int dirfd = -1; 46 47 typedef void (*child_test_fn_t)(void); 48 49 static void 50 touchat(int _dirfd, const char *name) 51 { 52 int fd; 53 54 ATF_REQUIRE((fd = openat(_dirfd, name, O_CREAT | O_TRUNC | O_WRONLY, 55 0777)) >= 0); 56 ATF_REQUIRE(close(fd) == 0); 57 } 58 59 static void 60 prepare_dotdot_tests(void) 61 { 62 char cwd[MAXPATHLEN]; 63 64 ATF_REQUIRE(getcwd(cwd, sizeof(cwd)) != NULL); 65 asprintf(&abspath, "%s/testdir/d1/f1", cwd); 66 67 ATF_REQUIRE(mkdir("testdir", 0777) == 0); 68 ATF_REQUIRE((dirfd = open("testdir", O_RDONLY)) >= 0); 69 70 ATF_REQUIRE(mkdirat(dirfd, "d1", 0777) == 0); 71 ATF_REQUIRE(mkdirat(dirfd, "d1/d2", 0777) == 0); 72 ATF_REQUIRE(mkdirat(dirfd, "d1/d2/d3", 0777) == 0); 73 touchat(dirfd, "d1/f1"); 74 touchat(dirfd, "d1/d2/f2"); 75 touchat(dirfd, "d1/d2/d3/f3"); 76 ATF_REQUIRE(symlinkat("d1/d2/d3", dirfd, "l3") == 0); 77 ATF_REQUIRE(symlinkat("../testdir/d1", dirfd, "lup") == 0); 78 ATF_REQUIRE(symlinkat("../..", dirfd, "d1/d2/d3/ld1") == 0); 79 ATF_REQUIRE(symlinkat("../../f1", dirfd, "d1/d2/d3/lf1") == 0); 80 } 81 82 static void 83 check_capsicum(void) 84 { 85 86 ATF_REQUIRE_FEATURE("security_capabilities"); 87 ATF_REQUIRE_FEATURE("security_capability_mode"); 88 } 89 90 static void 91 run_capsicum_test(child_test_fn_t test_func) 92 { 93 int child_exit_code, child_status; 94 pid_t child_pid; 95 96 check_capsicum(); 97 prepare_dotdot_tests(); 98 99 ATF_REQUIRE_MSG((child_pid = fork()) != -1, 100 "fork failed: %s", strerror(errno)); 101 102 if (child_pid == 0) { 103 test_func(); 104 _exit(0); 105 } 106 107 ATF_REQUIRE_MSG(waitpid(child_pid, &child_status, 0) != -1, 108 "waitpid failed: %s", strerror(errno)); 109 if (WIFEXITED(child_status)) { 110 child_exit_code = WEXITSTATUS(child_status); 111 ATF_REQUIRE_MSG(child_exit_code == 0, 112 "child exited with non-zero exit code: %d", 113 child_exit_code); 114 } else if (WIFSIGNALED(child_status)) 115 atf_tc_fail("child exited with signal: %d", 116 WTERMSIG(child_status)); 117 else 118 atf_tc_fail("child exited with unexpected status: %d", 119 child_status); 120 } 121 122 /* 123 * Positive tests 124 */ 125 ATF_TC(openat__basic_positive); 126 ATF_TC_HEAD(openat__basic_positive, tc) 127 { 128 atf_tc_set_md_var(tc, "descr", "Basic positive openat testcases"); 129 } 130 131 ATF_TC_BODY(openat__basic_positive, tc) 132 { 133 134 prepare_dotdot_tests(); 135 136 ATF_REQUIRE(openat(dirfd, "d1/d2/d3/f3", O_RDONLY) >= 0); 137 ATF_REQUIRE(openat(dirfd, "d1/d2/d3/../../f1", O_RDONLY) >= 0); 138 ATF_REQUIRE(openat(dirfd, "l3/f3", O_RDONLY) >= 0); 139 ATF_REQUIRE(openat(dirfd, "l3/../../f1", O_RDONLY) >= 0); 140 ATF_REQUIRE(openat(dirfd, "../testdir/d1/f1", O_RDONLY) >= 0); 141 ATF_REQUIRE(openat(dirfd, "lup/f1", O_RDONLY) >= 0); 142 ATF_REQUIRE(openat(dirfd, "l3/ld1", O_RDONLY) >= 0); 143 ATF_REQUIRE(openat(dirfd, "l3/lf1", O_RDONLY) >= 0); 144 ATF_REQUIRE(open(abspath, O_RDONLY) >= 0); 145 ATF_REQUIRE(openat(dirfd, abspath, O_RDONLY) >= 0); 146 } 147 148 ATF_TC(lookup_cap_dotdot__basic); 149 ATF_TC_HEAD(lookup_cap_dotdot__basic, tc) 150 { 151 atf_tc_set_md_var(tc, "descr", 152 "Validate cap-mode (testdir)/d1/.. lookup"); 153 } 154 155 static void 156 lookup_cap_dotdot__basic_child(void) 157 { 158 cap_rights_t rights; 159 160 cap_rights_init(&rights, CAP_LOOKUP, CAP_READ); 161 162 assert(cap_rights_limit(dirfd, &rights) >= 0); 163 assert(cap_enter() >= 0); 164 assert(openat(dirfd, "d1/..", O_RDONLY) >= 0); 165 } 166 167 ATF_TC_BODY(lookup_cap_dotdot__basic, tc) 168 { 169 170 run_capsicum_test(lookup_cap_dotdot__basic_child); 171 } 172 173 ATF_TC(lookup_cap_dotdot__advanced); 174 ATF_TC_HEAD(lookup_cap_dotdot__advanced, tc) 175 { 176 atf_tc_set_md_var(tc, "descr", 177 "Validate cap-mode (testdir)/d1/.. lookup"); 178 } 179 180 static void 181 lookup_cap_dotdot__advanced_child(void) 182 { 183 cap_rights_t rights; 184 185 cap_rights_init(&rights, CAP_LOOKUP, CAP_READ); 186 assert(cap_rights_limit(dirfd, &rights) >= 0); 187 188 assert(cap_enter() >= 0); 189 190 assert(openat(dirfd, "d1/d2/d3/../../f1", O_RDONLY) >= 0); 191 assert(openat(dirfd, "l3/../../f1", O_RDONLY) >= 0); 192 assert(openat(dirfd, "l3/ld1", O_RDONLY) >= 0); 193 assert(openat(dirfd, "l3/lf1", O_RDONLY) >= 0); 194 } 195 196 ATF_TC_BODY(lookup_cap_dotdot__advanced, tc) 197 { 198 199 run_capsicum_test(lookup_cap_dotdot__advanced_child); 200 } 201 202 /* 203 * Negative tests 204 */ 205 ATF_TC(openat__basic_negative); 206 ATF_TC_HEAD(openat__basic_negative, tc) 207 { 208 atf_tc_set_md_var(tc, "descr", "Basic negative openat testcases"); 209 } 210 211 ATF_TC_BODY(openat__basic_negative, tc) 212 { 213 214 prepare_dotdot_tests(); 215 216 ATF_REQUIRE_ERRNO(ENOENT, 217 openat(dirfd, "does-not-exist", O_RDONLY) < 0); 218 ATF_REQUIRE_ERRNO(ENOENT, 219 openat(dirfd, "l3/does-not-exist", O_RDONLY) < 0); 220 } 221 222 ATF_TC(capmode__negative); 223 ATF_TC_HEAD(capmode__negative, tc) 224 { 225 atf_tc_set_md_var(tc, "descr", "Negative Capability mode testcases"); 226 } 227 228 static void 229 capmode__negative_child(void) 230 { 231 int subdirfd; 232 233 assert(cap_enter() == 0); 234 235 /* open() not permitted in capability mode */ 236 assert(open("testdir", O_RDONLY) < 0); 237 assert(errno == ECAPMODE); 238 239 /* AT_FDCWD not permitted in capability mode */ 240 assert(openat(AT_FDCWD, "d1/f1", O_RDONLY) < 0); 241 assert(errno == ECAPMODE); 242 243 /* Relative path above dirfd not capable */ 244 assert(openat(dirfd, "..", O_RDONLY) < 0); 245 assert(errno == ENOTCAPABLE); 246 247 assert((subdirfd = openat(dirfd, "l3", O_RDONLY)) >= 0); 248 assert(openat(subdirfd, "../../f1", O_RDONLY) < 0); 249 assert(errno == ENOTCAPABLE); 250 (void)close(subdirfd); 251 252 /* Absolute paths not capable */ 253 assert(openat(dirfd, abspath, O_RDONLY) < 0); 254 assert(errno == ENOTCAPABLE); 255 256 /* Symlink above dirfd */ 257 assert(openat(dirfd, "lup/f1", O_RDONLY) < 0); 258 assert(errno == ENOTCAPABLE); 259 } 260 261 ATF_TC_BODY(capmode__negative, tc) 262 { 263 264 run_capsicum_test(capmode__negative_child); 265 } 266 267 ATF_TC(lookup_cap_dotdot__negative); 268 ATF_TC_HEAD(lookup_cap_dotdot__negative, tc) 269 { 270 atf_tc_set_md_var(tc, "descr", 271 "Validate cap-mode (testdir)/.. lookup fails"); 272 } 273 274 static void 275 lookup_cap_dotdot__negative_child(void) 276 { 277 cap_rights_t rights; 278 279 cap_rights_init(&rights, CAP_LOOKUP, CAP_READ); 280 assert(cap_rights_limit(dirfd, &rights) >= 0); 281 282 assert(cap_enter() >= 0); 283 284 assert(openat(dirfd, "..", O_RDONLY) < 0); 285 assert(errno == ENOTCAPABLE); 286 287 assert(openat(dirfd, "d1/../..", O_RDONLY) < 0); 288 assert(errno == ENOTCAPABLE); 289 290 assert(openat(dirfd, "../testdir/d1/f1", O_RDONLY) < 0); 291 assert(errno == ENOTCAPABLE); 292 } 293 294 ATF_TC_BODY(lookup_cap_dotdot__negative, tc) 295 { 296 297 run_capsicum_test(lookup_cap_dotdot__negative_child); 298 } 299 300 ATF_TP_ADD_TCS(tp) 301 { 302 303 ATF_TP_ADD_TC(tp, openat__basic_positive); 304 ATF_TP_ADD_TC(tp, openat__basic_negative); 305 306 ATF_TP_ADD_TC(tp, capmode__negative); 307 308 ATF_TP_ADD_TC(tp, lookup_cap_dotdot__basic); 309 ATF_TP_ADD_TC(tp, lookup_cap_dotdot__advanced); 310 ATF_TP_ADD_TC(tp, lookup_cap_dotdot__negative); 311 312 return (atf_no_error()); 313 } 314