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