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