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 ATF_REQUIRE_SYSCTL_INT("kern.trap_enotcap", 0); 84 } 85 86 /* 87 * Positive tests 88 */ 89 ATF_TC(openat__basic_positive); 90 ATF_TC_HEAD(openat__basic_positive, tc) 91 { 92 atf_tc_set_md_var(tc, "descr", "Basic positive openat testcases"); 93 } 94 95 ATF_TC_BODY(openat__basic_positive, tc) 96 { 97 prepare_dotdot_tests(); 98 99 ATF_REQUIRE(openat(dirfd, "d1/d2/d3/f3", O_RDONLY) >= 0); 100 ATF_REQUIRE(openat(dirfd, "d1/d2/d3/../../f1", O_RDONLY) >= 0); 101 ATF_REQUIRE(openat(dirfd, "l3/f3", O_RDONLY) >= 0); 102 ATF_REQUIRE(openat(dirfd, "l3/../../f1", O_RDONLY) >= 0); 103 ATF_REQUIRE(openat(dirfd, "../testdir/d1/f1", O_RDONLY) >= 0); 104 ATF_REQUIRE(openat(dirfd, "lup/f1", O_RDONLY) >= 0); 105 ATF_REQUIRE(openat(dirfd, "l3/ld1", O_RDONLY) >= 0); 106 ATF_REQUIRE(openat(dirfd, "l3/lf1", O_RDONLY) >= 0); 107 ATF_REQUIRE(open(abspath, O_RDONLY) >= 0); 108 ATF_REQUIRE(openat(dirfd, abspath, O_RDONLY) >= 0); 109 } 110 111 ATF_TC(lookup_cap_dotdot__basic); 112 ATF_TC_HEAD(lookup_cap_dotdot__basic, tc) 113 { 114 atf_tc_set_md_var(tc, "descr", 115 "Validate cap-mode (testdir)/d1/.. lookup"); 116 } 117 118 ATF_TC_BODY(lookup_cap_dotdot__basic, tc) 119 { 120 cap_rights_t rights; 121 122 check_capsicum(); 123 prepare_dotdot_tests(); 124 125 cap_rights_init(&rights, CAP_LOOKUP, CAP_READ); 126 ATF_REQUIRE(cap_rights_limit(dirfd, &rights) >= 0); 127 128 ATF_REQUIRE(cap_enter() >= 0); 129 130 ATF_REQUIRE_MSG(openat(dirfd, "d1/..", O_RDONLY) >= 0, "%s", 131 strerror(errno)); 132 } 133 134 ATF_TC(lookup_cap_dotdot__advanced); 135 ATF_TC_HEAD(lookup_cap_dotdot__advanced, tc) 136 { 137 atf_tc_set_md_var(tc, "descr", 138 "Validate cap-mode (testdir)/d1/.. lookup"); 139 } 140 141 ATF_TC_BODY(lookup_cap_dotdot__advanced, tc) 142 { 143 cap_rights_t rights; 144 145 check_capsicum(); 146 prepare_dotdot_tests(); 147 148 cap_rights_init(&rights, CAP_LOOKUP, CAP_READ); 149 ATF_REQUIRE(cap_rights_limit(dirfd, &rights) >= 0); 150 151 ATF_REQUIRE(cap_enter() >= 0); 152 153 ATF_REQUIRE(openat(dirfd, "d1/d2/d3/../../f1", O_RDONLY) >= 0); 154 ATF_REQUIRE(openat(dirfd, "l3/../../f1", O_RDONLY) >= 0); 155 ATF_REQUIRE(openat(dirfd, "l3/ld1", O_RDONLY) >= 0); 156 ATF_REQUIRE(openat(dirfd, "l3/lf1", O_RDONLY) >= 0); 157 } 158 159 /* 160 * Negative tests 161 */ 162 ATF_TC(openat__basic_negative); 163 ATF_TC_HEAD(openat__basic_negative, tc) 164 { 165 atf_tc_set_md_var(tc, "descr", "Basic negative openat testcases"); 166 } 167 168 ATF_TC_BODY(openat__basic_negative, tc) 169 { 170 prepare_dotdot_tests(); 171 172 ATF_REQUIRE_ERRNO(ENOENT, 173 openat(dirfd, "does-not-exist", O_RDONLY) < 0); 174 ATF_REQUIRE_ERRNO(ENOENT, 175 openat(dirfd, "l3/does-not-exist", O_RDONLY) < 0); 176 } 177 178 ATF_TC(capmode__negative); 179 ATF_TC_HEAD(capmode__negative, tc) 180 { 181 atf_tc_set_md_var(tc, "descr", "Negative Capability mode testcases"); 182 } 183 184 ATF_TC_BODY(capmode__negative, tc) 185 { 186 int subdirfd; 187 188 check_capsicum(); 189 prepare_dotdot_tests(); 190 191 ATF_REQUIRE(cap_enter() == 0); 192 193 /* open() not permitted in capability mode */ 194 ATF_REQUIRE_ERRNO(ECAPMODE, open("testdir", O_RDONLY) < 0); 195 196 /* AT_FDCWD not permitted in capability mode */ 197 ATF_REQUIRE_ERRNO(ECAPMODE, openat(AT_FDCWD, "d1/f1", O_RDONLY) < 0); 198 199 /* Relative path above dirfd not capable */ 200 ATF_REQUIRE_ERRNO(ENOTCAPABLE, openat(dirfd, "..", O_RDONLY) < 0); 201 ATF_REQUIRE((subdirfd = openat(dirfd, "l3", O_RDONLY)) >= 0); 202 ATF_REQUIRE_ERRNO(ENOTCAPABLE, 203 openat(subdirfd, "../../f1", O_RDONLY) < 0); 204 205 /* Absolute paths not capable */ 206 ATF_REQUIRE_ERRNO(ENOTCAPABLE, openat(dirfd, abspath, O_RDONLY) < 0); 207 208 /* Symlink above dirfd */ 209 ATF_REQUIRE_ERRNO(ENOTCAPABLE, openat(dirfd, "lup/f1", O_RDONLY) < 0); 210 } 211 212 ATF_TC(lookup_cap_dotdot__negative); 213 ATF_TC_HEAD(lookup_cap_dotdot__negative, tc) 214 { 215 atf_tc_set_md_var(tc, "descr", 216 "Validate cap-mode (testdir)/.. lookup fails"); 217 } 218 219 ATF_TC_BODY(lookup_cap_dotdot__negative, tc) 220 { 221 cap_rights_t rights; 222 223 check_capsicum(); 224 prepare_dotdot_tests(); 225 226 cap_rights_init(&rights, CAP_LOOKUP, CAP_READ); 227 ATF_REQUIRE(cap_rights_limit(dirfd, &rights) >= 0); 228 229 ATF_REQUIRE(cap_enter() >= 0); 230 231 ATF_REQUIRE_ERRNO(ENOTCAPABLE, openat(dirfd, "..", O_RDONLY) < 0); 232 ATF_REQUIRE_ERRNO(ENOTCAPABLE, openat(dirfd, "d1/../..", O_RDONLY) < 0); 233 ATF_REQUIRE_ERRNO(ENOTCAPABLE, openat(dirfd, "../testdir/d1/f1", O_RDONLY) < 0); 234 } 235 236 ATF_TP_ADD_TCS(tp) 237 { 238 239 ATF_TP_ADD_TC(tp, openat__basic_positive); 240 ATF_TP_ADD_TC(tp, openat__basic_negative); 241 242 ATF_TP_ADD_TC(tp, capmode__negative); 243 244 ATF_TP_ADD_TC(tp, lookup_cap_dotdot__basic); 245 ATF_TP_ADD_TC(tp, lookup_cap_dotdot__advanced); 246 ATF_TP_ADD_TC(tp, lookup_cap_dotdot__negative); 247 248 return (atf_no_error()); 249 } 250