1 /* $OpenBSD: t_chroot.c,v 1.2 2020/11/09 23:18:51 bluhm 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/cdefs.h> 36 __RCSID("$NetBSD: t_chroot.c,v 1.2 2017/01/10 22:36:29 christos Exp $"); 37 38 #include <sys/wait.h> 39 #include <sys/stat.h> 40 41 #include "atf-c.h" 42 #include <errno.h> 43 #include <fcntl.h> 44 #include <limits.h> 45 #include <pwd.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 50 ATF_TC(chroot_basic); 51 ATF_TC_HEAD(chroot_basic, tc) 52 { 53 atf_tc_set_md_var(tc, "descr", "A basic test of chroot(2)"); 54 atf_tc_set_md_var(tc, "require.user", "root"); 55 } 56 57 ATF_TC_BODY(chroot_basic, tc) 58 { 59 char buf[PATH_MAX]; 60 int fd, sta; 61 pid_t pid; 62 63 (void)memset(buf, '\0', sizeof(buf)); 64 (void)getcwd(buf, sizeof(buf)); 65 (void)strlcat(buf, "/dir", sizeof(buf)); 66 67 ATF_REQUIRE(mkdir(buf, 0500) == 0); 68 ATF_REQUIRE(chdir(buf) == 0); 69 70 pid = fork(); 71 ATF_REQUIRE(pid >= 0); 72 73 if (pid == 0) { 74 75 if (chroot(buf) != 0) 76 _exit(EXIT_FAILURE); 77 78 errno = 0; 79 80 if (chroot("/root") != -1) 81 _exit(EXIT_FAILURE); 82 83 if (errno != ENOENT) 84 _exit(EXIT_FAILURE); 85 86 fd = open("file", O_RDONLY | O_CREAT, 0600); 87 88 if (fd < 0) 89 _exit(EXIT_FAILURE); 90 91 if (close(fd) != 0) 92 _exit(EXIT_FAILURE); 93 94 _exit(EXIT_SUCCESS); 95 } 96 97 (void)wait(&sta); 98 99 if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS) 100 atf_tc_fail("chroot(2) failed"); 101 102 (void)chdir("/"); 103 (void)strlcat(buf, "/file", sizeof(buf)); 104 105 fd = open(buf, O_RDONLY); 106 107 if (fd < 0) 108 atf_tc_fail("chroot(2) did not change the root directory"); 109 110 ATF_REQUIRE(close(fd) == 0); 111 ATF_REQUIRE(unlink(buf) == 0); 112 } 113 114 ATF_TC(chroot_err); 115 ATF_TC_HEAD(chroot_err, tc) 116 { 117 atf_tc_set_md_var(tc, "descr", "Test error conditions of chroot(2)"); 118 atf_tc_set_md_var(tc, "require.user", "root"); 119 } 120 121 ATF_TC_BODY(chroot_err, tc) 122 { 123 char buf[PATH_MAX + 1]; 124 125 (void)memset(buf, 'x', sizeof(buf)); 126 127 errno = 0; 128 ATF_REQUIRE_ERRNO(ENAMETOOLONG, chroot(buf) == -1); 129 130 errno = 0; 131 ATF_REQUIRE_ERRNO(EFAULT, chroot((void *)-1) == -1); 132 133 errno = 0; 134 ATF_REQUIRE_ERRNO(ENOENT, chroot("/a/b/c/d/e/f/g/h/i/j") == -1); 135 } 136 137 ATF_TC(chroot_perm); 138 ATF_TC_HEAD(chroot_perm, tc) 139 { 140 atf_tc_set_md_var(tc, "descr", "Test permissions with chroot(2)"); 141 atf_tc_set_md_var(tc, "require.user", "unprivileged"); 142 } 143 144 ATF_TC_BODY(chroot_perm, tc) 145 { 146 static char buf[LINE_MAX]; 147 pid_t pid; 148 int sta; 149 150 (void)memset(buf, '\0', sizeof(buf)); 151 ATF_REQUIRE(getcwd(buf, sizeof(buf)) != NULL); 152 153 pid = fork(); 154 ATF_REQUIRE(pid >= 0); 155 156 if (pid == 0) { 157 158 errno = 0; 159 160 if (chroot(buf) != -1) 161 _exit(EXIT_FAILURE); 162 163 if (errno != EPERM) 164 _exit(EXIT_FAILURE); 165 166 _exit(EXIT_SUCCESS); 167 } 168 169 (void)wait(&sta); 170 171 if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS) 172 atf_tc_fail("chroot(2) succeeded as unprivileged user"); 173 } 174 175 ATF_TC(fchroot_basic); 176 ATF_TC_HEAD(fchroot_basic, tc) 177 { 178 atf_tc_set_md_var(tc, "descr", "A basic test of fchroot(2)"); 179 atf_tc_set_md_var(tc, "require.user", "root"); 180 } 181 182 ATF_TC_BODY(fchroot_basic, tc) 183 { 184 char buf[PATH_MAX]; 185 int fd, sta; 186 pid_t pid; 187 188 (void)memset(buf, '\0', sizeof(buf)); 189 (void)getcwd(buf, sizeof(buf)); 190 (void)strlcat(buf, "/dir", sizeof(buf)); 191 192 ATF_REQUIRE(mkdir(buf, 0500) == 0); 193 ATF_REQUIRE(chdir(buf) == 0); 194 195 fd = open(buf, O_RDONLY); 196 ATF_REQUIRE(fd >= 0); 197 198 pid = fork(); 199 ATF_REQUIRE(pid >= 0); 200 201 if (pid == 0) { 202 203 if (fchroot(fd) != 0) 204 _exit(EXIT_FAILURE); 205 206 if (close(fd) != 0) 207 _exit(EXIT_FAILURE); 208 209 fd = open("file", O_RDONLY | O_CREAT, 0600); 210 211 if (fd < 0) 212 _exit(EXIT_FAILURE); 213 214 if (close(fd) != 0) 215 _exit(EXIT_FAILURE); 216 217 _exit(EXIT_SUCCESS); 218 } 219 220 (void)wait(&sta); 221 222 if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS) 223 atf_tc_fail("fchroot(2) failed"); 224 225 (void)chdir("/"); 226 (void)strlcat(buf, "/file", sizeof(buf)); 227 228 fd = open(buf, O_RDONLY); 229 230 if (fd < 0) 231 atf_tc_fail("fchroot(2) did not change the root directory"); 232 233 ATF_REQUIRE(close(fd) == 0); 234 ATF_REQUIRE(unlink(buf) == 0); 235 } 236 237 ATF_TC(fchroot_err); 238 ATF_TC_HEAD(fchroot_err, tc) 239 { 240 atf_tc_set_md_var(tc, "descr", "Test error conditions of fchroot(2)"); 241 atf_tc_set_md_var(tc, "require.user", "root"); 242 } 243 244 ATF_TC_BODY(fchroot_err, tc) 245 { 246 int fd; 247 248 fd = open("/etc/passwd", O_RDONLY); 249 ATF_REQUIRE(fd >= 0); 250 251 errno = 0; 252 ATF_REQUIRE_ERRNO(EBADF, fchroot(-1) == -1); 253 254 errno = 0; 255 ATF_REQUIRE_ERRNO(ENOTDIR, fchroot(fd) == -1); 256 257 ATF_REQUIRE(close(fd) == 0); 258 } 259 260 ATF_TC(fchroot_perm); 261 ATF_TC_HEAD(fchroot_perm, tc) 262 { 263 atf_tc_set_md_var(tc, "descr", "Test permissions with fchroot(2)"); 264 atf_tc_set_md_var(tc, "require.user", "root"); 265 } 266 267 ATF_TC_BODY(fchroot_perm, tc) 268 { 269 static char buf[LINE_MAX]; 270 struct passwd *pw; 271 int fd, sta; 272 pid_t pid; 273 274 (void)memset(buf, '\0', sizeof(buf)); 275 ATF_REQUIRE(getcwd(buf, sizeof(buf)) != NULL); 276 277 pw = getpwnam("nobody"); 278 fd = open(buf, O_RDONLY); 279 280 ATF_REQUIRE(fd >= 0); 281 ATF_REQUIRE(pw != NULL); 282 283 pid = fork(); 284 ATF_REQUIRE(pid >= 0); 285 286 if (pid == 0) { 287 288 (void)setuid(pw->pw_uid); 289 290 errno = 0; 291 292 if (fchroot(fd) != -1) 293 _exit(EXIT_FAILURE); 294 295 if (errno != EPERM) 296 _exit(EXIT_FAILURE); 297 298 _exit(EXIT_SUCCESS); 299 } 300 301 (void)wait(&sta); 302 303 if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS) 304 atf_tc_fail("fchroot(2) succeeded as unprivileged user"); 305 } 306 307 ATF_TP_ADD_TCS(tp) 308 { 309 310 ATF_TP_ADD_TC(tp, chroot_basic); 311 ATF_TP_ADD_TC(tp, chroot_err); 312 ATF_TP_ADD_TC(tp, chroot_perm); 313 #ifndef __OpenBSD__ 314 /* fchroot(2) not available */ 315 ATF_TP_ADD_TC(tp, fchroot_basic); 316 ATF_TP_ADD_TC(tp, fchroot_err); 317 ATF_TP_ADD_TC(tp, fchroot_perm); 318 #endif 319 320 return atf_no_error(); 321 } 322