t_chroot.c revision 1.1
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
45ATF_TC(chroot_basic);
46ATF_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
52ATF_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
109ATF_TC(chroot_err);
110ATF_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
116ATF_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
132ATF_TC(chroot_perm);
133ATF_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
139ATF_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
170ATF_TC(fchroot_basic);
171ATF_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
177ATF_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
232ATF_TC(fchroot_err);
233ATF_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
239ATF_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
255ATF_TC(fchroot_perm);
256ATF_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
262ATF_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
302ATF_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