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