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 42static int dirfd = -1; 43static char *abspath; 44 45static void 46touchat(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 55static void 56prepare_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 78static void 79check_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 */ 89ATF_TC(openat__basic_positive); 90ATF_TC_HEAD(openat__basic_positive, tc) 91{ 92 atf_tc_set_md_var(tc, "descr", "Basic positive openat testcases"); 93} 94 95ATF_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 111ATF_TC(lookup_cap_dotdot__basic); 112ATF_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 118ATF_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 134ATF_TC(lookup_cap_dotdot__advanced); 135ATF_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 141ATF_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 */ 162ATF_TC(openat__basic_negative); 163ATF_TC_HEAD(openat__basic_negative, tc) 164{ 165 atf_tc_set_md_var(tc, "descr", "Basic negative openat testcases"); 166} 167 168ATF_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 178ATF_TC(capmode__negative); 179ATF_TC_HEAD(capmode__negative, tc) 180{ 181 atf_tc_set_md_var(tc, "descr", "Negative Capability mode testcases"); 182} 183 184ATF_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 212ATF_TC(lookup_cap_dotdot__negative); 213ATF_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 219ATF_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 236ATF_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