1 /* $OpenBSD: t_chroot.c,v 1.3 2021/12/13 16:56:48 deraadt Exp $ */ 2 /* $NetBSD: t_chroot.c,v 1.2 2017/01/10 22:36:29 christos Exp $ */ 3 4 /*- 5 * Copyright (c) 2011 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Jukka Ruohonen. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include "macros.h" 34 35 #include <sys/wait.h> 36 #include <sys/stat.h> 37 38 #include "atf-c.h" 39 #include <errno.h> 40 #include <fcntl.h> 41 #include <limits.h> 42 #include <pwd.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <unistd.h> 46 47 ATF_TC(chroot_basic); 48 ATF_TC_HEAD(chroot_basic, tc) 49 { 50 atf_tc_set_md_var(tc, "descr", "A basic test of chroot(2)"); 51 atf_tc_set_md_var(tc, "require.user", "root"); 52 } 53 54 ATF_TC_BODY(chroot_basic, tc) 55 { 56 char buf[PATH_MAX]; 57 int fd, sta; 58 pid_t pid; 59 60 (void)memset(buf, '\0', sizeof(buf)); 61 (void)getcwd(buf, sizeof(buf)); 62 (void)strlcat(buf, "/dir", sizeof(buf)); 63 64 ATF_REQUIRE(mkdir(buf, 0500) == 0); 65 ATF_REQUIRE(chdir(buf) == 0); 66 67 pid = fork(); 68 ATF_REQUIRE(pid >= 0); 69 70 if (pid == 0) { 71 72 if (chroot(buf) != 0) 73 _exit(EXIT_FAILURE); 74 75 errno = 0; 76 77 if (chroot("/root") != -1) 78 _exit(EXIT_FAILURE); 79 80 if (errno != ENOENT) 81 _exit(EXIT_FAILURE); 82 83 fd = open("file", O_RDONLY | O_CREAT, 0600); 84 85 if (fd < 0) 86 _exit(EXIT_FAILURE); 87 88 if (close(fd) != 0) 89 _exit(EXIT_FAILURE); 90 91 _exit(EXIT_SUCCESS); 92 } 93 94 (void)wait(&sta); 95 96 if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS) 97 atf_tc_fail("chroot(2) failed"); 98 99 (void)chdir("/"); 100 (void)strlcat(buf, "/file", sizeof(buf)); 101 102 fd = open(buf, O_RDONLY); 103 104 if (fd < 0) 105 atf_tc_fail("chroot(2) did not change the root directory"); 106 107 ATF_REQUIRE(close(fd) == 0); 108 ATF_REQUIRE(unlink(buf) == 0); 109 } 110 111 ATF_TC(chroot_err); 112 ATF_TC_HEAD(chroot_err, tc) 113 { 114 atf_tc_set_md_var(tc, "descr", "Test error conditions of chroot(2)"); 115 atf_tc_set_md_var(tc, "require.user", "root"); 116 } 117 118 ATF_TC_BODY(chroot_err, tc) 119 { 120 char buf[PATH_MAX + 1]; 121 122 (void)memset(buf, 'x', sizeof(buf)); 123 124 errno = 0; 125 ATF_REQUIRE_ERRNO(ENAMETOOLONG, chroot(buf) == -1); 126 127 errno = 0; 128 ATF_REQUIRE_ERRNO(EFAULT, chroot((void *)-1) == -1); 129 130 errno = 0; 131 ATF_REQUIRE_ERRNO(ENOENT, chroot("/a/b/c/d/e/f/g/h/i/j") == -1); 132 } 133 134 ATF_TC(chroot_perm); 135 ATF_TC_HEAD(chroot_perm, tc) 136 { 137 atf_tc_set_md_var(tc, "descr", "Test permissions with chroot(2)"); 138 atf_tc_set_md_var(tc, "require.user", "unprivileged"); 139 } 140 141 ATF_TC_BODY(chroot_perm, tc) 142 { 143 static char buf[LINE_MAX]; 144 pid_t pid; 145 int sta; 146 147 (void)memset(buf, '\0', sizeof(buf)); 148 ATF_REQUIRE(getcwd(buf, sizeof(buf)) != NULL); 149 150 pid = fork(); 151 ATF_REQUIRE(pid >= 0); 152 153 if (pid == 0) { 154 155 errno = 0; 156 157 if (chroot(buf) != -1) 158 _exit(EXIT_FAILURE); 159 160 if (errno != EPERM) 161 _exit(EXIT_FAILURE); 162 163 _exit(EXIT_SUCCESS); 164 } 165 166 (void)wait(&sta); 167 168 if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS) 169 atf_tc_fail("chroot(2) succeeded as unprivileged user"); 170 } 171 172 ATF_TC(fchroot_basic); 173 ATF_TC_HEAD(fchroot_basic, tc) 174 { 175 atf_tc_set_md_var(tc, "descr", "A basic test of fchroot(2)"); 176 atf_tc_set_md_var(tc, "require.user", "root"); 177 } 178 179 ATF_TC_BODY(fchroot_basic, tc) 180 { 181 char buf[PATH_MAX]; 182 int fd, sta; 183 pid_t pid; 184 185 (void)memset(buf, '\0', sizeof(buf)); 186 (void)getcwd(buf, sizeof(buf)); 187 (void)strlcat(buf, "/dir", sizeof(buf)); 188 189 ATF_REQUIRE(mkdir(buf, 0500) == 0); 190 ATF_REQUIRE(chdir(buf) == 0); 191 192 fd = open(buf, O_RDONLY); 193 ATF_REQUIRE(fd >= 0); 194 195 pid = fork(); 196 ATF_REQUIRE(pid >= 0); 197 198 if (pid == 0) { 199 200 if (fchroot(fd) != 0) 201 _exit(EXIT_FAILURE); 202 203 if (close(fd) != 0) 204 _exit(EXIT_FAILURE); 205 206 fd = open("file", O_RDONLY | O_CREAT, 0600); 207 208 if (fd < 0) 209 _exit(EXIT_FAILURE); 210 211 if (close(fd) != 0) 212 _exit(EXIT_FAILURE); 213 214 _exit(EXIT_SUCCESS); 215 } 216 217 (void)wait(&sta); 218 219 if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS) 220 atf_tc_fail("fchroot(2) failed"); 221 222 (void)chdir("/"); 223 (void)strlcat(buf, "/file", sizeof(buf)); 224 225 fd = open(buf, O_RDONLY); 226 227 if (fd < 0) 228 atf_tc_fail("fchroot(2) did not change the root directory"); 229 230 ATF_REQUIRE(close(fd) == 0); 231 ATF_REQUIRE(unlink(buf) == 0); 232 } 233 234 ATF_TC(fchroot_err); 235 ATF_TC_HEAD(fchroot_err, tc) 236 { 237 atf_tc_set_md_var(tc, "descr", "Test error conditions of fchroot(2)"); 238 atf_tc_set_md_var(tc, "require.user", "root"); 239 } 240 241 ATF_TC_BODY(fchroot_err, tc) 242 { 243 int fd; 244 245 fd = open("/etc/passwd", O_RDONLY); 246 ATF_REQUIRE(fd >= 0); 247 248 errno = 0; 249 ATF_REQUIRE_ERRNO(EBADF, fchroot(-1) == -1); 250 251 errno = 0; 252 ATF_REQUIRE_ERRNO(ENOTDIR, fchroot(fd) == -1); 253 254 ATF_REQUIRE(close(fd) == 0); 255 } 256 257 ATF_TC(fchroot_perm); 258 ATF_TC_HEAD(fchroot_perm, tc) 259 { 260 atf_tc_set_md_var(tc, "descr", "Test permissions with fchroot(2)"); 261 atf_tc_set_md_var(tc, "require.user", "root"); 262 } 263 264 ATF_TC_BODY(fchroot_perm, tc) 265 { 266 static char buf[LINE_MAX]; 267 struct passwd *pw; 268 int fd, sta; 269 pid_t pid; 270 271 (void)memset(buf, '\0', sizeof(buf)); 272 ATF_REQUIRE(getcwd(buf, sizeof(buf)) != NULL); 273 274 pw = getpwnam("nobody"); 275 fd = open(buf, O_RDONLY); 276 277 ATF_REQUIRE(fd >= 0); 278 ATF_REQUIRE(pw != NULL); 279 280 pid = fork(); 281 ATF_REQUIRE(pid >= 0); 282 283 if (pid == 0) { 284 285 (void)setuid(pw->pw_uid); 286 287 errno = 0; 288 289 if (fchroot(fd) != -1) 290 _exit(EXIT_FAILURE); 291 292 if (errno != EPERM) 293 _exit(EXIT_FAILURE); 294 295 _exit(EXIT_SUCCESS); 296 } 297 298 (void)wait(&sta); 299 300 if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS) 301 atf_tc_fail("fchroot(2) succeeded as unprivileged user"); 302 } 303 304 ATF_TP_ADD_TCS(tp) 305 { 306 307 ATF_TP_ADD_TC(tp, chroot_basic); 308 ATF_TP_ADD_TC(tp, chroot_err); 309 ATF_TP_ADD_TC(tp, chroot_perm); 310 #ifndef __OpenBSD__ 311 /* fchroot(2) not available */ 312 ATF_TP_ADD_TC(tp, fchroot_basic); 313 ATF_TP_ADD_TC(tp, fchroot_err); 314 ATF_TP_ADD_TC(tp, fchroot_perm); 315 #endif 316 317 return atf_no_error(); 318 } 319