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