1*28ef38a9SChristian Brauner // SPDX-License-Identifier: GPL-2.0
2*28ef38a9SChristian Brauner #define _GNU_SOURCE
3*28ef38a9SChristian Brauner #include <errno.h>
4*28ef38a9SChristian Brauner #include <fcntl.h>
5*28ef38a9SChristian Brauner #include <grp.h>
6*28ef38a9SChristian Brauner #include <limits.h>
7*28ef38a9SChristian Brauner #include <sched.h>
8*28ef38a9SChristian Brauner #include <stdio.h>
9*28ef38a9SChristian Brauner #include <stdlib.h>
10*28ef38a9SChristian Brauner #include <string.h>
11*28ef38a9SChristian Brauner #include <sys/mount.h>
12*28ef38a9SChristian Brauner #include <sys/stat.h>
13*28ef38a9SChristian Brauner #include <sys/types.h>
14*28ef38a9SChristian Brauner #include <sys/wait.h>
15*28ef38a9SChristian Brauner #include <unistd.h>
16*28ef38a9SChristian Brauner #include <linux/unistd.h>
17*28ef38a9SChristian Brauner #include "../kselftest_harness.h"
18*28ef38a9SChristian Brauner
19*28ef38a9SChristian Brauner #ifndef FD_NSFS_ROOT
20*28ef38a9SChristian Brauner #define FD_NSFS_ROOT -10003 /* Root of the nsfs filesystem */
21*28ef38a9SChristian Brauner #endif
22*28ef38a9SChristian Brauner
TEST(nsfs_net_handle)23*28ef38a9SChristian Brauner TEST(nsfs_net_handle)
24*28ef38a9SChristian Brauner {
25*28ef38a9SChristian Brauner struct file_handle *handle;
26*28ef38a9SChristian Brauner int mount_id;
27*28ef38a9SChristian Brauner int ret;
28*28ef38a9SChristian Brauner int fd;
29*28ef38a9SChristian Brauner int ns_fd;
30*28ef38a9SChristian Brauner struct stat st1, st2;
31*28ef38a9SChristian Brauner
32*28ef38a9SChristian Brauner /* Drop to unprivileged uid/gid */
33*28ef38a9SChristian Brauner ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
34*28ef38a9SChristian Brauner ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
35*28ef38a9SChristian Brauner
36*28ef38a9SChristian Brauner handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
37*28ef38a9SChristian Brauner ASSERT_NE(handle, NULL);
38*28ef38a9SChristian Brauner
39*28ef38a9SChristian Brauner /* Open a namespace file descriptor */
40*28ef38a9SChristian Brauner ns_fd = open("/proc/self/ns/net", O_RDONLY);
41*28ef38a9SChristian Brauner ASSERT_GE(ns_fd, 0);
42*28ef38a9SChristian Brauner
43*28ef38a9SChristian Brauner /* Get handle for the namespace */
44*28ef38a9SChristian Brauner handle->handle_bytes = MAX_HANDLE_SZ;
45*28ef38a9SChristian Brauner ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
46*28ef38a9SChristian Brauner if (ret < 0 && errno == EOPNOTSUPP) {
47*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd);
48*28ef38a9SChristian Brauner return, "nsfs doesn't support file handles");
49*28ef38a9SChristian Brauner }
50*28ef38a9SChristian Brauner ASSERT_EQ(ret, 0);
51*28ef38a9SChristian Brauner ASSERT_GT(handle->handle_bytes, 0);
52*28ef38a9SChristian Brauner
53*28ef38a9SChristian Brauner /* Try to open using FD_NSFS_ROOT as unprivileged user */
54*28ef38a9SChristian Brauner fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
55*28ef38a9SChristian Brauner if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
56*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd);
57*28ef38a9SChristian Brauner return,
58*28ef38a9SChristian Brauner "open_by_handle_at with FD_NSFS_ROOT not supported");
59*28ef38a9SChristian Brauner }
60*28ef38a9SChristian Brauner if (fd < 0 && errno == EPERM) {
61*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd);
62*28ef38a9SChristian Brauner return,
63*28ef38a9SChristian Brauner "Permission denied for unprivileged user (expected)");
64*28ef38a9SChristian Brauner }
65*28ef38a9SChristian Brauner ASSERT_GE(fd, 0);
66*28ef38a9SChristian Brauner
67*28ef38a9SChristian Brauner /* Verify we opened the correct namespace */
68*28ef38a9SChristian Brauner ASSERT_EQ(fstat(ns_fd, &st1), 0);
69*28ef38a9SChristian Brauner ASSERT_EQ(fstat(fd, &st2), 0);
70*28ef38a9SChristian Brauner ASSERT_EQ(st1.st_ino, st2.st_ino);
71*28ef38a9SChristian Brauner ASSERT_EQ(st1.st_dev, st2.st_dev);
72*28ef38a9SChristian Brauner
73*28ef38a9SChristian Brauner close(fd);
74*28ef38a9SChristian Brauner close(ns_fd);
75*28ef38a9SChristian Brauner free(handle);
76*28ef38a9SChristian Brauner }
77*28ef38a9SChristian Brauner
TEST(nsfs_uts_handle)78*28ef38a9SChristian Brauner TEST(nsfs_uts_handle)
79*28ef38a9SChristian Brauner {
80*28ef38a9SChristian Brauner struct file_handle *handle;
81*28ef38a9SChristian Brauner int mount_id;
82*28ef38a9SChristian Brauner int ret;
83*28ef38a9SChristian Brauner int fd;
84*28ef38a9SChristian Brauner int ns_fd;
85*28ef38a9SChristian Brauner struct stat st1, st2;
86*28ef38a9SChristian Brauner
87*28ef38a9SChristian Brauner /* Drop to unprivileged uid/gid */
88*28ef38a9SChristian Brauner ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
89*28ef38a9SChristian Brauner ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
90*28ef38a9SChristian Brauner
91*28ef38a9SChristian Brauner handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
92*28ef38a9SChristian Brauner ASSERT_NE(handle, NULL);
93*28ef38a9SChristian Brauner
94*28ef38a9SChristian Brauner /* Open UTS namespace file descriptor */
95*28ef38a9SChristian Brauner ns_fd = open("/proc/self/ns/uts", O_RDONLY);
96*28ef38a9SChristian Brauner ASSERT_GE(ns_fd, 0);
97*28ef38a9SChristian Brauner
98*28ef38a9SChristian Brauner /* Get handle for the namespace */
99*28ef38a9SChristian Brauner handle->handle_bytes = MAX_HANDLE_SZ;
100*28ef38a9SChristian Brauner ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
101*28ef38a9SChristian Brauner if (ret < 0 && errno == EOPNOTSUPP) {
102*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd);
103*28ef38a9SChristian Brauner return, "nsfs doesn't support file handles");
104*28ef38a9SChristian Brauner }
105*28ef38a9SChristian Brauner ASSERT_EQ(ret, 0);
106*28ef38a9SChristian Brauner ASSERT_GT(handle->handle_bytes, 0);
107*28ef38a9SChristian Brauner
108*28ef38a9SChristian Brauner /* Try to open using FD_NSFS_ROOT */
109*28ef38a9SChristian Brauner fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
110*28ef38a9SChristian Brauner if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
111*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd);
112*28ef38a9SChristian Brauner return,
113*28ef38a9SChristian Brauner "open_by_handle_at with FD_NSFS_ROOT not supported");
114*28ef38a9SChristian Brauner }
115*28ef38a9SChristian Brauner ASSERT_GE(fd, 0);
116*28ef38a9SChristian Brauner
117*28ef38a9SChristian Brauner /* Verify we opened the correct namespace */
118*28ef38a9SChristian Brauner ASSERT_EQ(fstat(ns_fd, &st1), 0);
119*28ef38a9SChristian Brauner ASSERT_EQ(fstat(fd, &st2), 0);
120*28ef38a9SChristian Brauner ASSERT_EQ(st1.st_ino, st2.st_ino);
121*28ef38a9SChristian Brauner ASSERT_EQ(st1.st_dev, st2.st_dev);
122*28ef38a9SChristian Brauner
123*28ef38a9SChristian Brauner close(fd);
124*28ef38a9SChristian Brauner close(ns_fd);
125*28ef38a9SChristian Brauner free(handle);
126*28ef38a9SChristian Brauner }
127*28ef38a9SChristian Brauner
TEST(nsfs_ipc_handle)128*28ef38a9SChristian Brauner TEST(nsfs_ipc_handle)
129*28ef38a9SChristian Brauner {
130*28ef38a9SChristian Brauner struct file_handle *handle;
131*28ef38a9SChristian Brauner int mount_id;
132*28ef38a9SChristian Brauner int ret;
133*28ef38a9SChristian Brauner int fd;
134*28ef38a9SChristian Brauner int ns_fd;
135*28ef38a9SChristian Brauner struct stat st1, st2;
136*28ef38a9SChristian Brauner
137*28ef38a9SChristian Brauner /* Drop to unprivileged uid/gid */
138*28ef38a9SChristian Brauner ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
139*28ef38a9SChristian Brauner ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
140*28ef38a9SChristian Brauner
141*28ef38a9SChristian Brauner handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
142*28ef38a9SChristian Brauner ASSERT_NE(handle, NULL);
143*28ef38a9SChristian Brauner
144*28ef38a9SChristian Brauner /* Open IPC namespace file descriptor */
145*28ef38a9SChristian Brauner ns_fd = open("/proc/self/ns/ipc", O_RDONLY);
146*28ef38a9SChristian Brauner ASSERT_GE(ns_fd, 0);
147*28ef38a9SChristian Brauner
148*28ef38a9SChristian Brauner /* Get handle for the namespace */
149*28ef38a9SChristian Brauner handle->handle_bytes = MAX_HANDLE_SZ;
150*28ef38a9SChristian Brauner ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
151*28ef38a9SChristian Brauner if (ret < 0 && errno == EOPNOTSUPP) {
152*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd);
153*28ef38a9SChristian Brauner return, "nsfs doesn't support file handles");
154*28ef38a9SChristian Brauner }
155*28ef38a9SChristian Brauner ASSERT_EQ(ret, 0);
156*28ef38a9SChristian Brauner ASSERT_GT(handle->handle_bytes, 0);
157*28ef38a9SChristian Brauner
158*28ef38a9SChristian Brauner /* Try to open using FD_NSFS_ROOT */
159*28ef38a9SChristian Brauner fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
160*28ef38a9SChristian Brauner if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
161*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd);
162*28ef38a9SChristian Brauner return,
163*28ef38a9SChristian Brauner "open_by_handle_at with FD_NSFS_ROOT not supported");
164*28ef38a9SChristian Brauner }
165*28ef38a9SChristian Brauner ASSERT_GE(fd, 0);
166*28ef38a9SChristian Brauner
167*28ef38a9SChristian Brauner /* Verify we opened the correct namespace */
168*28ef38a9SChristian Brauner ASSERT_EQ(fstat(ns_fd, &st1), 0);
169*28ef38a9SChristian Brauner ASSERT_EQ(fstat(fd, &st2), 0);
170*28ef38a9SChristian Brauner ASSERT_EQ(st1.st_ino, st2.st_ino);
171*28ef38a9SChristian Brauner ASSERT_EQ(st1.st_dev, st2.st_dev);
172*28ef38a9SChristian Brauner
173*28ef38a9SChristian Brauner close(fd);
174*28ef38a9SChristian Brauner close(ns_fd);
175*28ef38a9SChristian Brauner free(handle);
176*28ef38a9SChristian Brauner }
177*28ef38a9SChristian Brauner
TEST(nsfs_pid_handle)178*28ef38a9SChristian Brauner TEST(nsfs_pid_handle)
179*28ef38a9SChristian Brauner {
180*28ef38a9SChristian Brauner struct file_handle *handle;
181*28ef38a9SChristian Brauner int mount_id;
182*28ef38a9SChristian Brauner int ret;
183*28ef38a9SChristian Brauner int fd;
184*28ef38a9SChristian Brauner int ns_fd;
185*28ef38a9SChristian Brauner struct stat st1, st2;
186*28ef38a9SChristian Brauner
187*28ef38a9SChristian Brauner /* Drop to unprivileged uid/gid */
188*28ef38a9SChristian Brauner ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
189*28ef38a9SChristian Brauner ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
190*28ef38a9SChristian Brauner
191*28ef38a9SChristian Brauner handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
192*28ef38a9SChristian Brauner ASSERT_NE(handle, NULL);
193*28ef38a9SChristian Brauner
194*28ef38a9SChristian Brauner /* Open PID namespace file descriptor */
195*28ef38a9SChristian Brauner ns_fd = open("/proc/self/ns/pid", O_RDONLY);
196*28ef38a9SChristian Brauner ASSERT_GE(ns_fd, 0);
197*28ef38a9SChristian Brauner
198*28ef38a9SChristian Brauner /* Get handle for the namespace */
199*28ef38a9SChristian Brauner handle->handle_bytes = MAX_HANDLE_SZ;
200*28ef38a9SChristian Brauner ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
201*28ef38a9SChristian Brauner if (ret < 0 && errno == EOPNOTSUPP) {
202*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd);
203*28ef38a9SChristian Brauner return, "nsfs doesn't support file handles");
204*28ef38a9SChristian Brauner }
205*28ef38a9SChristian Brauner ASSERT_EQ(ret, 0);
206*28ef38a9SChristian Brauner ASSERT_GT(handle->handle_bytes, 0);
207*28ef38a9SChristian Brauner
208*28ef38a9SChristian Brauner /* Try to open using FD_NSFS_ROOT */
209*28ef38a9SChristian Brauner fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
210*28ef38a9SChristian Brauner if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
211*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd);
212*28ef38a9SChristian Brauner return,
213*28ef38a9SChristian Brauner "open_by_handle_at with FD_NSFS_ROOT not supported");
214*28ef38a9SChristian Brauner }
215*28ef38a9SChristian Brauner ASSERT_GE(fd, 0);
216*28ef38a9SChristian Brauner
217*28ef38a9SChristian Brauner /* Verify we opened the correct namespace */
218*28ef38a9SChristian Brauner ASSERT_EQ(fstat(ns_fd, &st1), 0);
219*28ef38a9SChristian Brauner ASSERT_EQ(fstat(fd, &st2), 0);
220*28ef38a9SChristian Brauner ASSERT_EQ(st1.st_ino, st2.st_ino);
221*28ef38a9SChristian Brauner ASSERT_EQ(st1.st_dev, st2.st_dev);
222*28ef38a9SChristian Brauner
223*28ef38a9SChristian Brauner close(fd);
224*28ef38a9SChristian Brauner close(ns_fd);
225*28ef38a9SChristian Brauner free(handle);
226*28ef38a9SChristian Brauner }
227*28ef38a9SChristian Brauner
TEST(nsfs_mnt_handle)228*28ef38a9SChristian Brauner TEST(nsfs_mnt_handle)
229*28ef38a9SChristian Brauner {
230*28ef38a9SChristian Brauner struct file_handle *handle;
231*28ef38a9SChristian Brauner int mount_id;
232*28ef38a9SChristian Brauner int ret;
233*28ef38a9SChristian Brauner int fd;
234*28ef38a9SChristian Brauner int ns_fd;
235*28ef38a9SChristian Brauner struct stat st1, st2;
236*28ef38a9SChristian Brauner
237*28ef38a9SChristian Brauner /* Drop to unprivileged uid/gid */
238*28ef38a9SChristian Brauner ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
239*28ef38a9SChristian Brauner ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
240*28ef38a9SChristian Brauner
241*28ef38a9SChristian Brauner handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
242*28ef38a9SChristian Brauner ASSERT_NE(handle, NULL);
243*28ef38a9SChristian Brauner
244*28ef38a9SChristian Brauner /* Open mount namespace file descriptor */
245*28ef38a9SChristian Brauner ns_fd = open("/proc/self/ns/mnt", O_RDONLY);
246*28ef38a9SChristian Brauner ASSERT_GE(ns_fd, 0);
247*28ef38a9SChristian Brauner
248*28ef38a9SChristian Brauner /* Get handle for the namespace */
249*28ef38a9SChristian Brauner handle->handle_bytes = MAX_HANDLE_SZ;
250*28ef38a9SChristian Brauner ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
251*28ef38a9SChristian Brauner if (ret < 0 && errno == EOPNOTSUPP) {
252*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd);
253*28ef38a9SChristian Brauner return, "nsfs doesn't support file handles");
254*28ef38a9SChristian Brauner }
255*28ef38a9SChristian Brauner ASSERT_EQ(ret, 0);
256*28ef38a9SChristian Brauner ASSERT_GT(handle->handle_bytes, 0);
257*28ef38a9SChristian Brauner
258*28ef38a9SChristian Brauner /* Try to open using FD_NSFS_ROOT */
259*28ef38a9SChristian Brauner fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
260*28ef38a9SChristian Brauner if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
261*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd);
262*28ef38a9SChristian Brauner return,
263*28ef38a9SChristian Brauner "open_by_handle_at with FD_NSFS_ROOT not supported");
264*28ef38a9SChristian Brauner }
265*28ef38a9SChristian Brauner ASSERT_GE(fd, 0);
266*28ef38a9SChristian Brauner
267*28ef38a9SChristian Brauner /* Verify we opened the correct namespace */
268*28ef38a9SChristian Brauner ASSERT_EQ(fstat(ns_fd, &st1), 0);
269*28ef38a9SChristian Brauner ASSERT_EQ(fstat(fd, &st2), 0);
270*28ef38a9SChristian Brauner ASSERT_EQ(st1.st_ino, st2.st_ino);
271*28ef38a9SChristian Brauner ASSERT_EQ(st1.st_dev, st2.st_dev);
272*28ef38a9SChristian Brauner
273*28ef38a9SChristian Brauner close(fd);
274*28ef38a9SChristian Brauner close(ns_fd);
275*28ef38a9SChristian Brauner free(handle);
276*28ef38a9SChristian Brauner }
277*28ef38a9SChristian Brauner
TEST(nsfs_user_handle)278*28ef38a9SChristian Brauner TEST(nsfs_user_handle)
279*28ef38a9SChristian Brauner {
280*28ef38a9SChristian Brauner struct file_handle *handle;
281*28ef38a9SChristian Brauner int mount_id;
282*28ef38a9SChristian Brauner int ret;
283*28ef38a9SChristian Brauner int fd;
284*28ef38a9SChristian Brauner int ns_fd;
285*28ef38a9SChristian Brauner struct stat st1, st2;
286*28ef38a9SChristian Brauner
287*28ef38a9SChristian Brauner /* Drop to unprivileged uid/gid */
288*28ef38a9SChristian Brauner ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
289*28ef38a9SChristian Brauner ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
290*28ef38a9SChristian Brauner
291*28ef38a9SChristian Brauner handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
292*28ef38a9SChristian Brauner ASSERT_NE(handle, NULL);
293*28ef38a9SChristian Brauner
294*28ef38a9SChristian Brauner /* Open user namespace file descriptor */
295*28ef38a9SChristian Brauner ns_fd = open("/proc/self/ns/user", O_RDONLY);
296*28ef38a9SChristian Brauner ASSERT_GE(ns_fd, 0);
297*28ef38a9SChristian Brauner
298*28ef38a9SChristian Brauner /* Get handle for the namespace */
299*28ef38a9SChristian Brauner handle->handle_bytes = MAX_HANDLE_SZ;
300*28ef38a9SChristian Brauner ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
301*28ef38a9SChristian Brauner if (ret < 0 && errno == EOPNOTSUPP) {
302*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd);
303*28ef38a9SChristian Brauner return, "nsfs doesn't support file handles");
304*28ef38a9SChristian Brauner }
305*28ef38a9SChristian Brauner ASSERT_EQ(ret, 0);
306*28ef38a9SChristian Brauner ASSERT_GT(handle->handle_bytes, 0);
307*28ef38a9SChristian Brauner
308*28ef38a9SChristian Brauner /* Try to open using FD_NSFS_ROOT */
309*28ef38a9SChristian Brauner fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
310*28ef38a9SChristian Brauner if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
311*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd);
312*28ef38a9SChristian Brauner return,
313*28ef38a9SChristian Brauner "open_by_handle_at with FD_NSFS_ROOT not supported");
314*28ef38a9SChristian Brauner }
315*28ef38a9SChristian Brauner ASSERT_GE(fd, 0);
316*28ef38a9SChristian Brauner
317*28ef38a9SChristian Brauner /* Verify we opened the correct namespace */
318*28ef38a9SChristian Brauner ASSERT_EQ(fstat(ns_fd, &st1), 0);
319*28ef38a9SChristian Brauner ASSERT_EQ(fstat(fd, &st2), 0);
320*28ef38a9SChristian Brauner ASSERT_EQ(st1.st_ino, st2.st_ino);
321*28ef38a9SChristian Brauner ASSERT_EQ(st1.st_dev, st2.st_dev);
322*28ef38a9SChristian Brauner
323*28ef38a9SChristian Brauner close(fd);
324*28ef38a9SChristian Brauner close(ns_fd);
325*28ef38a9SChristian Brauner free(handle);
326*28ef38a9SChristian Brauner }
327*28ef38a9SChristian Brauner
TEST(nsfs_cgroup_handle)328*28ef38a9SChristian Brauner TEST(nsfs_cgroup_handle)
329*28ef38a9SChristian Brauner {
330*28ef38a9SChristian Brauner struct file_handle *handle;
331*28ef38a9SChristian Brauner int mount_id;
332*28ef38a9SChristian Brauner int ret;
333*28ef38a9SChristian Brauner int fd;
334*28ef38a9SChristian Brauner int ns_fd;
335*28ef38a9SChristian Brauner struct stat st1, st2;
336*28ef38a9SChristian Brauner
337*28ef38a9SChristian Brauner /* Drop to unprivileged uid/gid */
338*28ef38a9SChristian Brauner ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
339*28ef38a9SChristian Brauner ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
340*28ef38a9SChristian Brauner
341*28ef38a9SChristian Brauner handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
342*28ef38a9SChristian Brauner ASSERT_NE(handle, NULL);
343*28ef38a9SChristian Brauner
344*28ef38a9SChristian Brauner /* Open cgroup namespace file descriptor */
345*28ef38a9SChristian Brauner ns_fd = open("/proc/self/ns/cgroup", O_RDONLY);
346*28ef38a9SChristian Brauner if (ns_fd < 0) {
347*28ef38a9SChristian Brauner SKIP(free(handle); return, "cgroup namespace not available");
348*28ef38a9SChristian Brauner }
349*28ef38a9SChristian Brauner
350*28ef38a9SChristian Brauner /* Get handle for the namespace */
351*28ef38a9SChristian Brauner handle->handle_bytes = MAX_HANDLE_SZ;
352*28ef38a9SChristian Brauner ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
353*28ef38a9SChristian Brauner if (ret < 0 && errno == EOPNOTSUPP) {
354*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd);
355*28ef38a9SChristian Brauner return, "nsfs doesn't support file handles");
356*28ef38a9SChristian Brauner }
357*28ef38a9SChristian Brauner ASSERT_EQ(ret, 0);
358*28ef38a9SChristian Brauner ASSERT_GT(handle->handle_bytes, 0);
359*28ef38a9SChristian Brauner
360*28ef38a9SChristian Brauner /* Try to open using FD_NSFS_ROOT */
361*28ef38a9SChristian Brauner fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
362*28ef38a9SChristian Brauner if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
363*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd);
364*28ef38a9SChristian Brauner return,
365*28ef38a9SChristian Brauner "open_by_handle_at with FD_NSFS_ROOT not supported");
366*28ef38a9SChristian Brauner }
367*28ef38a9SChristian Brauner ASSERT_GE(fd, 0);
368*28ef38a9SChristian Brauner
369*28ef38a9SChristian Brauner /* Verify we opened the correct namespace */
370*28ef38a9SChristian Brauner ASSERT_EQ(fstat(ns_fd, &st1), 0);
371*28ef38a9SChristian Brauner ASSERT_EQ(fstat(fd, &st2), 0);
372*28ef38a9SChristian Brauner ASSERT_EQ(st1.st_ino, st2.st_ino);
373*28ef38a9SChristian Brauner ASSERT_EQ(st1.st_dev, st2.st_dev);
374*28ef38a9SChristian Brauner
375*28ef38a9SChristian Brauner close(fd);
376*28ef38a9SChristian Brauner close(ns_fd);
377*28ef38a9SChristian Brauner free(handle);
378*28ef38a9SChristian Brauner }
379*28ef38a9SChristian Brauner
TEST(nsfs_time_handle)380*28ef38a9SChristian Brauner TEST(nsfs_time_handle)
381*28ef38a9SChristian Brauner {
382*28ef38a9SChristian Brauner struct file_handle *handle;
383*28ef38a9SChristian Brauner int mount_id;
384*28ef38a9SChristian Brauner int ret;
385*28ef38a9SChristian Brauner int fd;
386*28ef38a9SChristian Brauner int ns_fd;
387*28ef38a9SChristian Brauner struct stat st1, st2;
388*28ef38a9SChristian Brauner
389*28ef38a9SChristian Brauner /* Drop to unprivileged uid/gid */
390*28ef38a9SChristian Brauner ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
391*28ef38a9SChristian Brauner ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
392*28ef38a9SChristian Brauner
393*28ef38a9SChristian Brauner handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
394*28ef38a9SChristian Brauner ASSERT_NE(handle, NULL);
395*28ef38a9SChristian Brauner
396*28ef38a9SChristian Brauner /* Open time namespace file descriptor */
397*28ef38a9SChristian Brauner ns_fd = open("/proc/self/ns/time", O_RDONLY);
398*28ef38a9SChristian Brauner if (ns_fd < 0) {
399*28ef38a9SChristian Brauner SKIP(free(handle); return, "time namespace not available");
400*28ef38a9SChristian Brauner }
401*28ef38a9SChristian Brauner
402*28ef38a9SChristian Brauner /* Get handle for the namespace */
403*28ef38a9SChristian Brauner handle->handle_bytes = MAX_HANDLE_SZ;
404*28ef38a9SChristian Brauner ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
405*28ef38a9SChristian Brauner if (ret < 0 && errno == EOPNOTSUPP) {
406*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd);
407*28ef38a9SChristian Brauner return, "nsfs doesn't support file handles");
408*28ef38a9SChristian Brauner }
409*28ef38a9SChristian Brauner ASSERT_EQ(ret, 0);
410*28ef38a9SChristian Brauner ASSERT_GT(handle->handle_bytes, 0);
411*28ef38a9SChristian Brauner
412*28ef38a9SChristian Brauner /* Try to open using FD_NSFS_ROOT */
413*28ef38a9SChristian Brauner fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
414*28ef38a9SChristian Brauner if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
415*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd);
416*28ef38a9SChristian Brauner return,
417*28ef38a9SChristian Brauner "open_by_handle_at with FD_NSFS_ROOT not supported");
418*28ef38a9SChristian Brauner }
419*28ef38a9SChristian Brauner ASSERT_GE(fd, 0);
420*28ef38a9SChristian Brauner
421*28ef38a9SChristian Brauner /* Verify we opened the correct namespace */
422*28ef38a9SChristian Brauner ASSERT_EQ(fstat(ns_fd, &st1), 0);
423*28ef38a9SChristian Brauner ASSERT_EQ(fstat(fd, &st2), 0);
424*28ef38a9SChristian Brauner ASSERT_EQ(st1.st_ino, st2.st_ino);
425*28ef38a9SChristian Brauner ASSERT_EQ(st1.st_dev, st2.st_dev);
426*28ef38a9SChristian Brauner
427*28ef38a9SChristian Brauner close(fd);
428*28ef38a9SChristian Brauner close(ns_fd);
429*28ef38a9SChristian Brauner free(handle);
430*28ef38a9SChristian Brauner }
431*28ef38a9SChristian Brauner
TEST(nsfs_user_net_namespace_isolation)432*28ef38a9SChristian Brauner TEST(nsfs_user_net_namespace_isolation)
433*28ef38a9SChristian Brauner {
434*28ef38a9SChristian Brauner struct file_handle *handle;
435*28ef38a9SChristian Brauner int mount_id;
436*28ef38a9SChristian Brauner int ret;
437*28ef38a9SChristian Brauner int fd;
438*28ef38a9SChristian Brauner int ns_fd;
439*28ef38a9SChristian Brauner pid_t pid;
440*28ef38a9SChristian Brauner int status;
441*28ef38a9SChristian Brauner int pipefd[2];
442*28ef38a9SChristian Brauner char result;
443*28ef38a9SChristian Brauner
444*28ef38a9SChristian Brauner handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
445*28ef38a9SChristian Brauner ASSERT_NE(handle, NULL);
446*28ef38a9SChristian Brauner
447*28ef38a9SChristian Brauner /* Create pipe for communication */
448*28ef38a9SChristian Brauner ASSERT_EQ(pipe(pipefd), 0);
449*28ef38a9SChristian Brauner
450*28ef38a9SChristian Brauner /* Get handle for current network namespace */
451*28ef38a9SChristian Brauner ns_fd = open("/proc/self/ns/net", O_RDONLY);
452*28ef38a9SChristian Brauner ASSERT_GE(ns_fd, 0);
453*28ef38a9SChristian Brauner
454*28ef38a9SChristian Brauner handle->handle_bytes = MAX_HANDLE_SZ;
455*28ef38a9SChristian Brauner ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
456*28ef38a9SChristian Brauner if (ret < 0 && errno == EOPNOTSUPP) {
457*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd); close(pipefd[0]);
458*28ef38a9SChristian Brauner close(pipefd[1]);
459*28ef38a9SChristian Brauner return, "nsfs doesn't support file handles");
460*28ef38a9SChristian Brauner }
461*28ef38a9SChristian Brauner ASSERT_EQ(ret, 0);
462*28ef38a9SChristian Brauner close(ns_fd);
463*28ef38a9SChristian Brauner
464*28ef38a9SChristian Brauner pid = fork();
465*28ef38a9SChristian Brauner ASSERT_GE(pid, 0);
466*28ef38a9SChristian Brauner
467*28ef38a9SChristian Brauner if (pid == 0) {
468*28ef38a9SChristian Brauner /* Child process */
469*28ef38a9SChristian Brauner close(pipefd[0]);
470*28ef38a9SChristian Brauner
471*28ef38a9SChristian Brauner /* First create new user namespace to drop privileges */
472*28ef38a9SChristian Brauner ret = unshare(CLONE_NEWUSER);
473*28ef38a9SChristian Brauner if (ret < 0) {
474*28ef38a9SChristian Brauner write(pipefd[1], "U",
475*28ef38a9SChristian Brauner 1); /* Unable to create user namespace */
476*28ef38a9SChristian Brauner close(pipefd[1]);
477*28ef38a9SChristian Brauner exit(0);
478*28ef38a9SChristian Brauner }
479*28ef38a9SChristian Brauner
480*28ef38a9SChristian Brauner /* Write uid/gid mappings to maintain some capabilities */
481*28ef38a9SChristian Brauner int uid_map_fd = open("/proc/self/uid_map", O_WRONLY);
482*28ef38a9SChristian Brauner int gid_map_fd = open("/proc/self/gid_map", O_WRONLY);
483*28ef38a9SChristian Brauner int setgroups_fd = open("/proc/self/setgroups", O_WRONLY);
484*28ef38a9SChristian Brauner
485*28ef38a9SChristian Brauner if (uid_map_fd < 0 || gid_map_fd < 0 || setgroups_fd < 0) {
486*28ef38a9SChristian Brauner write(pipefd[1], "M", 1); /* Unable to set mappings */
487*28ef38a9SChristian Brauner close(pipefd[1]);
488*28ef38a9SChristian Brauner exit(0);
489*28ef38a9SChristian Brauner }
490*28ef38a9SChristian Brauner
491*28ef38a9SChristian Brauner /* Disable setgroups to allow gid mapping */
492*28ef38a9SChristian Brauner write(setgroups_fd, "deny", 4);
493*28ef38a9SChristian Brauner close(setgroups_fd);
494*28ef38a9SChristian Brauner
495*28ef38a9SChristian Brauner /* Map current uid/gid to root in the new namespace */
496*28ef38a9SChristian Brauner char mapping[64];
497*28ef38a9SChristian Brauner snprintf(mapping, sizeof(mapping), "0 %d 1", getuid());
498*28ef38a9SChristian Brauner write(uid_map_fd, mapping, strlen(mapping));
499*28ef38a9SChristian Brauner close(uid_map_fd);
500*28ef38a9SChristian Brauner
501*28ef38a9SChristian Brauner snprintf(mapping, sizeof(mapping), "0 %d 1", getgid());
502*28ef38a9SChristian Brauner write(gid_map_fd, mapping, strlen(mapping));
503*28ef38a9SChristian Brauner close(gid_map_fd);
504*28ef38a9SChristian Brauner
505*28ef38a9SChristian Brauner /* Now create new network namespace */
506*28ef38a9SChristian Brauner ret = unshare(CLONE_NEWNET);
507*28ef38a9SChristian Brauner if (ret < 0) {
508*28ef38a9SChristian Brauner write(pipefd[1], "N",
509*28ef38a9SChristian Brauner 1); /* Unable to create network namespace */
510*28ef38a9SChristian Brauner close(pipefd[1]);
511*28ef38a9SChristian Brauner exit(0);
512*28ef38a9SChristian Brauner }
513*28ef38a9SChristian Brauner
514*28ef38a9SChristian Brauner /* Try to open parent's network namespace handle from new user+net namespace */
515*28ef38a9SChristian Brauner fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
516*28ef38a9SChristian Brauner
517*28ef38a9SChristian Brauner if (fd >= 0) {
518*28ef38a9SChristian Brauner /* Should NOT succeed - we're in a different user namespace */
519*28ef38a9SChristian Brauner write(pipefd[1], "S", 1); /* Unexpected success */
520*28ef38a9SChristian Brauner close(fd);
521*28ef38a9SChristian Brauner } else if (errno == ESTALE) {
522*28ef38a9SChristian Brauner /* Expected: Stale file handle */
523*28ef38a9SChristian Brauner write(pipefd[1], "P", 1);
524*28ef38a9SChristian Brauner } else {
525*28ef38a9SChristian Brauner /* Other error */
526*28ef38a9SChristian Brauner write(pipefd[1], "F", 1);
527*28ef38a9SChristian Brauner }
528*28ef38a9SChristian Brauner
529*28ef38a9SChristian Brauner close(pipefd[1]);
530*28ef38a9SChristian Brauner exit(0);
531*28ef38a9SChristian Brauner }
532*28ef38a9SChristian Brauner
533*28ef38a9SChristian Brauner /* Parent process */
534*28ef38a9SChristian Brauner close(pipefd[1]);
535*28ef38a9SChristian Brauner ASSERT_EQ(read(pipefd[0], &result, 1), 1);
536*28ef38a9SChristian Brauner
537*28ef38a9SChristian Brauner waitpid(pid, &status, 0);
538*28ef38a9SChristian Brauner ASSERT_TRUE(WIFEXITED(status));
539*28ef38a9SChristian Brauner ASSERT_EQ(WEXITSTATUS(status), 0);
540*28ef38a9SChristian Brauner
541*28ef38a9SChristian Brauner if (result == 'U') {
542*28ef38a9SChristian Brauner SKIP(free(handle); close(pipefd[0]);
543*28ef38a9SChristian Brauner return, "Cannot create new user namespace");
544*28ef38a9SChristian Brauner }
545*28ef38a9SChristian Brauner if (result == 'M') {
546*28ef38a9SChristian Brauner SKIP(free(handle); close(pipefd[0]);
547*28ef38a9SChristian Brauner return, "Cannot set uid/gid mappings");
548*28ef38a9SChristian Brauner }
549*28ef38a9SChristian Brauner if (result == 'N') {
550*28ef38a9SChristian Brauner SKIP(free(handle); close(pipefd[0]);
551*28ef38a9SChristian Brauner return, "Cannot create new network namespace");
552*28ef38a9SChristian Brauner }
553*28ef38a9SChristian Brauner
554*28ef38a9SChristian Brauner /* Should fail with permission denied since we're in a different user namespace */
555*28ef38a9SChristian Brauner ASSERT_EQ(result, 'P');
556*28ef38a9SChristian Brauner
557*28ef38a9SChristian Brauner close(pipefd[0]);
558*28ef38a9SChristian Brauner free(handle);
559*28ef38a9SChristian Brauner }
560*28ef38a9SChristian Brauner
TEST(nsfs_user_uts_namespace_isolation)561*28ef38a9SChristian Brauner TEST(nsfs_user_uts_namespace_isolation)
562*28ef38a9SChristian Brauner {
563*28ef38a9SChristian Brauner struct file_handle *handle;
564*28ef38a9SChristian Brauner int mount_id;
565*28ef38a9SChristian Brauner int ret;
566*28ef38a9SChristian Brauner int fd;
567*28ef38a9SChristian Brauner int ns_fd;
568*28ef38a9SChristian Brauner pid_t pid;
569*28ef38a9SChristian Brauner int status;
570*28ef38a9SChristian Brauner int pipefd[2];
571*28ef38a9SChristian Brauner char result;
572*28ef38a9SChristian Brauner
573*28ef38a9SChristian Brauner handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
574*28ef38a9SChristian Brauner ASSERT_NE(handle, NULL);
575*28ef38a9SChristian Brauner
576*28ef38a9SChristian Brauner /* Create pipe for communication */
577*28ef38a9SChristian Brauner ASSERT_EQ(pipe(pipefd), 0);
578*28ef38a9SChristian Brauner
579*28ef38a9SChristian Brauner /* Get handle for current UTS namespace */
580*28ef38a9SChristian Brauner ns_fd = open("/proc/self/ns/uts", O_RDONLY);
581*28ef38a9SChristian Brauner ASSERT_GE(ns_fd, 0);
582*28ef38a9SChristian Brauner
583*28ef38a9SChristian Brauner handle->handle_bytes = MAX_HANDLE_SZ;
584*28ef38a9SChristian Brauner ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
585*28ef38a9SChristian Brauner if (ret < 0 && errno == EOPNOTSUPP) {
586*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd); close(pipefd[0]);
587*28ef38a9SChristian Brauner close(pipefd[1]);
588*28ef38a9SChristian Brauner return, "nsfs doesn't support file handles");
589*28ef38a9SChristian Brauner }
590*28ef38a9SChristian Brauner ASSERT_EQ(ret, 0);
591*28ef38a9SChristian Brauner close(ns_fd);
592*28ef38a9SChristian Brauner
593*28ef38a9SChristian Brauner pid = fork();
594*28ef38a9SChristian Brauner ASSERT_GE(pid, 0);
595*28ef38a9SChristian Brauner
596*28ef38a9SChristian Brauner if (pid == 0) {
597*28ef38a9SChristian Brauner /* Child process */
598*28ef38a9SChristian Brauner close(pipefd[0]);
599*28ef38a9SChristian Brauner
600*28ef38a9SChristian Brauner /* First create new user namespace to drop privileges */
601*28ef38a9SChristian Brauner ret = unshare(CLONE_NEWUSER);
602*28ef38a9SChristian Brauner if (ret < 0) {
603*28ef38a9SChristian Brauner write(pipefd[1], "U",
604*28ef38a9SChristian Brauner 1); /* Unable to create user namespace */
605*28ef38a9SChristian Brauner close(pipefd[1]);
606*28ef38a9SChristian Brauner exit(0);
607*28ef38a9SChristian Brauner }
608*28ef38a9SChristian Brauner
609*28ef38a9SChristian Brauner /* Write uid/gid mappings to maintain some capabilities */
610*28ef38a9SChristian Brauner int uid_map_fd = open("/proc/self/uid_map", O_WRONLY);
611*28ef38a9SChristian Brauner int gid_map_fd = open("/proc/self/gid_map", O_WRONLY);
612*28ef38a9SChristian Brauner int setgroups_fd = open("/proc/self/setgroups", O_WRONLY);
613*28ef38a9SChristian Brauner
614*28ef38a9SChristian Brauner if (uid_map_fd < 0 || gid_map_fd < 0 || setgroups_fd < 0) {
615*28ef38a9SChristian Brauner write(pipefd[1], "M", 1); /* Unable to set mappings */
616*28ef38a9SChristian Brauner close(pipefd[1]);
617*28ef38a9SChristian Brauner exit(0);
618*28ef38a9SChristian Brauner }
619*28ef38a9SChristian Brauner
620*28ef38a9SChristian Brauner /* Disable setgroups to allow gid mapping */
621*28ef38a9SChristian Brauner write(setgroups_fd, "deny", 4);
622*28ef38a9SChristian Brauner close(setgroups_fd);
623*28ef38a9SChristian Brauner
624*28ef38a9SChristian Brauner /* Map current uid/gid to root in the new namespace */
625*28ef38a9SChristian Brauner char mapping[64];
626*28ef38a9SChristian Brauner snprintf(mapping, sizeof(mapping), "0 %d 1", getuid());
627*28ef38a9SChristian Brauner write(uid_map_fd, mapping, strlen(mapping));
628*28ef38a9SChristian Brauner close(uid_map_fd);
629*28ef38a9SChristian Brauner
630*28ef38a9SChristian Brauner snprintf(mapping, sizeof(mapping), "0 %d 1", getgid());
631*28ef38a9SChristian Brauner write(gid_map_fd, mapping, strlen(mapping));
632*28ef38a9SChristian Brauner close(gid_map_fd);
633*28ef38a9SChristian Brauner
634*28ef38a9SChristian Brauner /* Now create new UTS namespace */
635*28ef38a9SChristian Brauner ret = unshare(CLONE_NEWUTS);
636*28ef38a9SChristian Brauner if (ret < 0) {
637*28ef38a9SChristian Brauner write(pipefd[1], "N",
638*28ef38a9SChristian Brauner 1); /* Unable to create UTS namespace */
639*28ef38a9SChristian Brauner close(pipefd[1]);
640*28ef38a9SChristian Brauner exit(0);
641*28ef38a9SChristian Brauner }
642*28ef38a9SChristian Brauner
643*28ef38a9SChristian Brauner /* Try to open parent's UTS namespace handle from new user+uts namespace */
644*28ef38a9SChristian Brauner fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
645*28ef38a9SChristian Brauner
646*28ef38a9SChristian Brauner if (fd >= 0) {
647*28ef38a9SChristian Brauner /* Should NOT succeed - we're in a different user namespace */
648*28ef38a9SChristian Brauner write(pipefd[1], "S", 1); /* Unexpected success */
649*28ef38a9SChristian Brauner close(fd);
650*28ef38a9SChristian Brauner } else if (errno == ESTALE) {
651*28ef38a9SChristian Brauner /* Expected: Stale file handle */
652*28ef38a9SChristian Brauner write(pipefd[1], "P", 1);
653*28ef38a9SChristian Brauner } else {
654*28ef38a9SChristian Brauner /* Other error */
655*28ef38a9SChristian Brauner write(pipefd[1], "F", 1);
656*28ef38a9SChristian Brauner }
657*28ef38a9SChristian Brauner
658*28ef38a9SChristian Brauner close(pipefd[1]);
659*28ef38a9SChristian Brauner exit(0);
660*28ef38a9SChristian Brauner }
661*28ef38a9SChristian Brauner
662*28ef38a9SChristian Brauner /* Parent process */
663*28ef38a9SChristian Brauner close(pipefd[1]);
664*28ef38a9SChristian Brauner ASSERT_EQ(read(pipefd[0], &result, 1), 1);
665*28ef38a9SChristian Brauner
666*28ef38a9SChristian Brauner waitpid(pid, &status, 0);
667*28ef38a9SChristian Brauner ASSERT_TRUE(WIFEXITED(status));
668*28ef38a9SChristian Brauner ASSERT_EQ(WEXITSTATUS(status), 0);
669*28ef38a9SChristian Brauner
670*28ef38a9SChristian Brauner if (result == 'U') {
671*28ef38a9SChristian Brauner SKIP(free(handle); close(pipefd[0]);
672*28ef38a9SChristian Brauner return, "Cannot create new user namespace");
673*28ef38a9SChristian Brauner }
674*28ef38a9SChristian Brauner if (result == 'M') {
675*28ef38a9SChristian Brauner SKIP(free(handle); close(pipefd[0]);
676*28ef38a9SChristian Brauner return, "Cannot set uid/gid mappings");
677*28ef38a9SChristian Brauner }
678*28ef38a9SChristian Brauner if (result == 'N') {
679*28ef38a9SChristian Brauner SKIP(free(handle); close(pipefd[0]);
680*28ef38a9SChristian Brauner return, "Cannot create new UTS namespace");
681*28ef38a9SChristian Brauner }
682*28ef38a9SChristian Brauner
683*28ef38a9SChristian Brauner /* Should fail with ESTALE since we're in a different user namespace */
684*28ef38a9SChristian Brauner ASSERT_EQ(result, 'P');
685*28ef38a9SChristian Brauner
686*28ef38a9SChristian Brauner close(pipefd[0]);
687*28ef38a9SChristian Brauner free(handle);
688*28ef38a9SChristian Brauner }
689*28ef38a9SChristian Brauner
TEST(nsfs_user_ipc_namespace_isolation)690*28ef38a9SChristian Brauner TEST(nsfs_user_ipc_namespace_isolation)
691*28ef38a9SChristian Brauner {
692*28ef38a9SChristian Brauner struct file_handle *handle;
693*28ef38a9SChristian Brauner int mount_id;
694*28ef38a9SChristian Brauner int ret;
695*28ef38a9SChristian Brauner int fd;
696*28ef38a9SChristian Brauner int ns_fd;
697*28ef38a9SChristian Brauner pid_t pid;
698*28ef38a9SChristian Brauner int status;
699*28ef38a9SChristian Brauner int pipefd[2];
700*28ef38a9SChristian Brauner char result;
701*28ef38a9SChristian Brauner
702*28ef38a9SChristian Brauner handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
703*28ef38a9SChristian Brauner ASSERT_NE(handle, NULL);
704*28ef38a9SChristian Brauner
705*28ef38a9SChristian Brauner /* Create pipe for communication */
706*28ef38a9SChristian Brauner ASSERT_EQ(pipe(pipefd), 0);
707*28ef38a9SChristian Brauner
708*28ef38a9SChristian Brauner /* Get handle for current IPC namespace */
709*28ef38a9SChristian Brauner ns_fd = open("/proc/self/ns/ipc", O_RDONLY);
710*28ef38a9SChristian Brauner ASSERT_GE(ns_fd, 0);
711*28ef38a9SChristian Brauner
712*28ef38a9SChristian Brauner handle->handle_bytes = MAX_HANDLE_SZ;
713*28ef38a9SChristian Brauner ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
714*28ef38a9SChristian Brauner if (ret < 0 && errno == EOPNOTSUPP) {
715*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd); close(pipefd[0]);
716*28ef38a9SChristian Brauner close(pipefd[1]);
717*28ef38a9SChristian Brauner return, "nsfs doesn't support file handles");
718*28ef38a9SChristian Brauner }
719*28ef38a9SChristian Brauner ASSERT_EQ(ret, 0);
720*28ef38a9SChristian Brauner close(ns_fd);
721*28ef38a9SChristian Brauner
722*28ef38a9SChristian Brauner pid = fork();
723*28ef38a9SChristian Brauner ASSERT_GE(pid, 0);
724*28ef38a9SChristian Brauner
725*28ef38a9SChristian Brauner if (pid == 0) {
726*28ef38a9SChristian Brauner /* Child process */
727*28ef38a9SChristian Brauner close(pipefd[0]);
728*28ef38a9SChristian Brauner
729*28ef38a9SChristian Brauner /* First create new user namespace to drop privileges */
730*28ef38a9SChristian Brauner ret = unshare(CLONE_NEWUSER);
731*28ef38a9SChristian Brauner if (ret < 0) {
732*28ef38a9SChristian Brauner write(pipefd[1], "U",
733*28ef38a9SChristian Brauner 1); /* Unable to create user namespace */
734*28ef38a9SChristian Brauner close(pipefd[1]);
735*28ef38a9SChristian Brauner exit(0);
736*28ef38a9SChristian Brauner }
737*28ef38a9SChristian Brauner
738*28ef38a9SChristian Brauner /* Write uid/gid mappings to maintain some capabilities */
739*28ef38a9SChristian Brauner int uid_map_fd = open("/proc/self/uid_map", O_WRONLY);
740*28ef38a9SChristian Brauner int gid_map_fd = open("/proc/self/gid_map", O_WRONLY);
741*28ef38a9SChristian Brauner int setgroups_fd = open("/proc/self/setgroups", O_WRONLY);
742*28ef38a9SChristian Brauner
743*28ef38a9SChristian Brauner if (uid_map_fd < 0 || gid_map_fd < 0 || setgroups_fd < 0) {
744*28ef38a9SChristian Brauner write(pipefd[1], "M", 1); /* Unable to set mappings */
745*28ef38a9SChristian Brauner close(pipefd[1]);
746*28ef38a9SChristian Brauner exit(0);
747*28ef38a9SChristian Brauner }
748*28ef38a9SChristian Brauner
749*28ef38a9SChristian Brauner /* Disable setgroups to allow gid mapping */
750*28ef38a9SChristian Brauner write(setgroups_fd, "deny", 4);
751*28ef38a9SChristian Brauner close(setgroups_fd);
752*28ef38a9SChristian Brauner
753*28ef38a9SChristian Brauner /* Map current uid/gid to root in the new namespace */
754*28ef38a9SChristian Brauner char mapping[64];
755*28ef38a9SChristian Brauner snprintf(mapping, sizeof(mapping), "0 %d 1", getuid());
756*28ef38a9SChristian Brauner write(uid_map_fd, mapping, strlen(mapping));
757*28ef38a9SChristian Brauner close(uid_map_fd);
758*28ef38a9SChristian Brauner
759*28ef38a9SChristian Brauner snprintf(mapping, sizeof(mapping), "0 %d 1", getgid());
760*28ef38a9SChristian Brauner write(gid_map_fd, mapping, strlen(mapping));
761*28ef38a9SChristian Brauner close(gid_map_fd);
762*28ef38a9SChristian Brauner
763*28ef38a9SChristian Brauner /* Now create new IPC namespace */
764*28ef38a9SChristian Brauner ret = unshare(CLONE_NEWIPC);
765*28ef38a9SChristian Brauner if (ret < 0) {
766*28ef38a9SChristian Brauner write(pipefd[1], "N",
767*28ef38a9SChristian Brauner 1); /* Unable to create IPC namespace */
768*28ef38a9SChristian Brauner close(pipefd[1]);
769*28ef38a9SChristian Brauner exit(0);
770*28ef38a9SChristian Brauner }
771*28ef38a9SChristian Brauner
772*28ef38a9SChristian Brauner /* Try to open parent's IPC namespace handle from new user+ipc namespace */
773*28ef38a9SChristian Brauner fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
774*28ef38a9SChristian Brauner
775*28ef38a9SChristian Brauner if (fd >= 0) {
776*28ef38a9SChristian Brauner /* Should NOT succeed - we're in a different user namespace */
777*28ef38a9SChristian Brauner write(pipefd[1], "S", 1); /* Unexpected success */
778*28ef38a9SChristian Brauner close(fd);
779*28ef38a9SChristian Brauner } else if (errno == ESTALE) {
780*28ef38a9SChristian Brauner /* Expected: Stale file handle */
781*28ef38a9SChristian Brauner write(pipefd[1], "P", 1);
782*28ef38a9SChristian Brauner } else {
783*28ef38a9SChristian Brauner /* Other error */
784*28ef38a9SChristian Brauner write(pipefd[1], "F", 1);
785*28ef38a9SChristian Brauner }
786*28ef38a9SChristian Brauner
787*28ef38a9SChristian Brauner close(pipefd[1]);
788*28ef38a9SChristian Brauner exit(0);
789*28ef38a9SChristian Brauner }
790*28ef38a9SChristian Brauner
791*28ef38a9SChristian Brauner /* Parent process */
792*28ef38a9SChristian Brauner close(pipefd[1]);
793*28ef38a9SChristian Brauner ASSERT_EQ(read(pipefd[0], &result, 1), 1);
794*28ef38a9SChristian Brauner
795*28ef38a9SChristian Brauner waitpid(pid, &status, 0);
796*28ef38a9SChristian Brauner ASSERT_TRUE(WIFEXITED(status));
797*28ef38a9SChristian Brauner ASSERT_EQ(WEXITSTATUS(status), 0);
798*28ef38a9SChristian Brauner
799*28ef38a9SChristian Brauner if (result == 'U') {
800*28ef38a9SChristian Brauner SKIP(free(handle); close(pipefd[0]);
801*28ef38a9SChristian Brauner return, "Cannot create new user namespace");
802*28ef38a9SChristian Brauner }
803*28ef38a9SChristian Brauner if (result == 'M') {
804*28ef38a9SChristian Brauner SKIP(free(handle); close(pipefd[0]);
805*28ef38a9SChristian Brauner return, "Cannot set uid/gid mappings");
806*28ef38a9SChristian Brauner }
807*28ef38a9SChristian Brauner if (result == 'N') {
808*28ef38a9SChristian Brauner SKIP(free(handle); close(pipefd[0]);
809*28ef38a9SChristian Brauner return, "Cannot create new IPC namespace");
810*28ef38a9SChristian Brauner }
811*28ef38a9SChristian Brauner
812*28ef38a9SChristian Brauner /* Should fail with ESTALE since we're in a different user namespace */
813*28ef38a9SChristian Brauner ASSERT_EQ(result, 'P');
814*28ef38a9SChristian Brauner
815*28ef38a9SChristian Brauner close(pipefd[0]);
816*28ef38a9SChristian Brauner free(handle);
817*28ef38a9SChristian Brauner }
818*28ef38a9SChristian Brauner
TEST(nsfs_user_mnt_namespace_isolation)819*28ef38a9SChristian Brauner TEST(nsfs_user_mnt_namespace_isolation)
820*28ef38a9SChristian Brauner {
821*28ef38a9SChristian Brauner struct file_handle *handle;
822*28ef38a9SChristian Brauner int mount_id;
823*28ef38a9SChristian Brauner int ret;
824*28ef38a9SChristian Brauner int fd;
825*28ef38a9SChristian Brauner int ns_fd;
826*28ef38a9SChristian Brauner pid_t pid;
827*28ef38a9SChristian Brauner int status;
828*28ef38a9SChristian Brauner int pipefd[2];
829*28ef38a9SChristian Brauner char result;
830*28ef38a9SChristian Brauner
831*28ef38a9SChristian Brauner handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
832*28ef38a9SChristian Brauner ASSERT_NE(handle, NULL);
833*28ef38a9SChristian Brauner
834*28ef38a9SChristian Brauner /* Create pipe for communication */
835*28ef38a9SChristian Brauner ASSERT_EQ(pipe(pipefd), 0);
836*28ef38a9SChristian Brauner
837*28ef38a9SChristian Brauner /* Get handle for current mount namespace */
838*28ef38a9SChristian Brauner ns_fd = open("/proc/self/ns/mnt", O_RDONLY);
839*28ef38a9SChristian Brauner ASSERT_GE(ns_fd, 0);
840*28ef38a9SChristian Brauner
841*28ef38a9SChristian Brauner handle->handle_bytes = MAX_HANDLE_SZ;
842*28ef38a9SChristian Brauner ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
843*28ef38a9SChristian Brauner if (ret < 0 && errno == EOPNOTSUPP) {
844*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd); close(pipefd[0]);
845*28ef38a9SChristian Brauner close(pipefd[1]);
846*28ef38a9SChristian Brauner return, "nsfs doesn't support file handles");
847*28ef38a9SChristian Brauner }
848*28ef38a9SChristian Brauner ASSERT_EQ(ret, 0);
849*28ef38a9SChristian Brauner close(ns_fd);
850*28ef38a9SChristian Brauner
851*28ef38a9SChristian Brauner pid = fork();
852*28ef38a9SChristian Brauner ASSERT_GE(pid, 0);
853*28ef38a9SChristian Brauner
854*28ef38a9SChristian Brauner if (pid == 0) {
855*28ef38a9SChristian Brauner /* Child process */
856*28ef38a9SChristian Brauner close(pipefd[0]);
857*28ef38a9SChristian Brauner
858*28ef38a9SChristian Brauner /* First create new user namespace to drop privileges */
859*28ef38a9SChristian Brauner ret = unshare(CLONE_NEWUSER);
860*28ef38a9SChristian Brauner if (ret < 0) {
861*28ef38a9SChristian Brauner write(pipefd[1], "U",
862*28ef38a9SChristian Brauner 1); /* Unable to create user namespace */
863*28ef38a9SChristian Brauner close(pipefd[1]);
864*28ef38a9SChristian Brauner exit(0);
865*28ef38a9SChristian Brauner }
866*28ef38a9SChristian Brauner
867*28ef38a9SChristian Brauner /* Write uid/gid mappings to maintain some capabilities */
868*28ef38a9SChristian Brauner int uid_map_fd = open("/proc/self/uid_map", O_WRONLY);
869*28ef38a9SChristian Brauner int gid_map_fd = open("/proc/self/gid_map", O_WRONLY);
870*28ef38a9SChristian Brauner int setgroups_fd = open("/proc/self/setgroups", O_WRONLY);
871*28ef38a9SChristian Brauner
872*28ef38a9SChristian Brauner if (uid_map_fd < 0 || gid_map_fd < 0 || setgroups_fd < 0) {
873*28ef38a9SChristian Brauner write(pipefd[1], "M", 1); /* Unable to set mappings */
874*28ef38a9SChristian Brauner close(pipefd[1]);
875*28ef38a9SChristian Brauner exit(0);
876*28ef38a9SChristian Brauner }
877*28ef38a9SChristian Brauner
878*28ef38a9SChristian Brauner /* Disable setgroups to allow gid mapping */
879*28ef38a9SChristian Brauner write(setgroups_fd, "deny", 4);
880*28ef38a9SChristian Brauner close(setgroups_fd);
881*28ef38a9SChristian Brauner
882*28ef38a9SChristian Brauner /* Map current uid/gid to root in the new namespace */
883*28ef38a9SChristian Brauner char mapping[64];
884*28ef38a9SChristian Brauner snprintf(mapping, sizeof(mapping), "0 %d 1", getuid());
885*28ef38a9SChristian Brauner write(uid_map_fd, mapping, strlen(mapping));
886*28ef38a9SChristian Brauner close(uid_map_fd);
887*28ef38a9SChristian Brauner
888*28ef38a9SChristian Brauner snprintf(mapping, sizeof(mapping), "0 %d 1", getgid());
889*28ef38a9SChristian Brauner write(gid_map_fd, mapping, strlen(mapping));
890*28ef38a9SChristian Brauner close(gid_map_fd);
891*28ef38a9SChristian Brauner
892*28ef38a9SChristian Brauner /* Now create new mount namespace */
893*28ef38a9SChristian Brauner ret = unshare(CLONE_NEWNS);
894*28ef38a9SChristian Brauner if (ret < 0) {
895*28ef38a9SChristian Brauner write(pipefd[1], "N",
896*28ef38a9SChristian Brauner 1); /* Unable to create mount namespace */
897*28ef38a9SChristian Brauner close(pipefd[1]);
898*28ef38a9SChristian Brauner exit(0);
899*28ef38a9SChristian Brauner }
900*28ef38a9SChristian Brauner
901*28ef38a9SChristian Brauner /* Try to open parent's mount namespace handle from new user+mnt namespace */
902*28ef38a9SChristian Brauner fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
903*28ef38a9SChristian Brauner
904*28ef38a9SChristian Brauner if (fd >= 0) {
905*28ef38a9SChristian Brauner /* Should NOT succeed - we're in a different user namespace */
906*28ef38a9SChristian Brauner write(pipefd[1], "S", 1); /* Unexpected success */
907*28ef38a9SChristian Brauner close(fd);
908*28ef38a9SChristian Brauner } else if (errno == ESTALE) {
909*28ef38a9SChristian Brauner /* Expected: Stale file handle */
910*28ef38a9SChristian Brauner write(pipefd[1], "P", 1);
911*28ef38a9SChristian Brauner } else {
912*28ef38a9SChristian Brauner /* Other error */
913*28ef38a9SChristian Brauner write(pipefd[1], "F", 1);
914*28ef38a9SChristian Brauner }
915*28ef38a9SChristian Brauner
916*28ef38a9SChristian Brauner close(pipefd[1]);
917*28ef38a9SChristian Brauner exit(0);
918*28ef38a9SChristian Brauner }
919*28ef38a9SChristian Brauner
920*28ef38a9SChristian Brauner /* Parent process */
921*28ef38a9SChristian Brauner close(pipefd[1]);
922*28ef38a9SChristian Brauner ASSERT_EQ(read(pipefd[0], &result, 1), 1);
923*28ef38a9SChristian Brauner
924*28ef38a9SChristian Brauner waitpid(pid, &status, 0);
925*28ef38a9SChristian Brauner ASSERT_TRUE(WIFEXITED(status));
926*28ef38a9SChristian Brauner ASSERT_EQ(WEXITSTATUS(status), 0);
927*28ef38a9SChristian Brauner
928*28ef38a9SChristian Brauner if (result == 'U') {
929*28ef38a9SChristian Brauner SKIP(free(handle); close(pipefd[0]);
930*28ef38a9SChristian Brauner return, "Cannot create new user namespace");
931*28ef38a9SChristian Brauner }
932*28ef38a9SChristian Brauner if (result == 'M') {
933*28ef38a9SChristian Brauner SKIP(free(handle); close(pipefd[0]);
934*28ef38a9SChristian Brauner return, "Cannot set uid/gid mappings");
935*28ef38a9SChristian Brauner }
936*28ef38a9SChristian Brauner if (result == 'N') {
937*28ef38a9SChristian Brauner SKIP(free(handle); close(pipefd[0]);
938*28ef38a9SChristian Brauner return, "Cannot create new mount namespace");
939*28ef38a9SChristian Brauner }
940*28ef38a9SChristian Brauner
941*28ef38a9SChristian Brauner /* Should fail with ESTALE since we're in a different user namespace */
942*28ef38a9SChristian Brauner ASSERT_EQ(result, 'P');
943*28ef38a9SChristian Brauner
944*28ef38a9SChristian Brauner close(pipefd[0]);
945*28ef38a9SChristian Brauner free(handle);
946*28ef38a9SChristian Brauner }
947*28ef38a9SChristian Brauner
TEST(nsfs_user_cgroup_namespace_isolation)948*28ef38a9SChristian Brauner TEST(nsfs_user_cgroup_namespace_isolation)
949*28ef38a9SChristian Brauner {
950*28ef38a9SChristian Brauner struct file_handle *handle;
951*28ef38a9SChristian Brauner int mount_id;
952*28ef38a9SChristian Brauner int ret;
953*28ef38a9SChristian Brauner int fd;
954*28ef38a9SChristian Brauner int ns_fd;
955*28ef38a9SChristian Brauner pid_t pid;
956*28ef38a9SChristian Brauner int status;
957*28ef38a9SChristian Brauner int pipefd[2];
958*28ef38a9SChristian Brauner char result;
959*28ef38a9SChristian Brauner
960*28ef38a9SChristian Brauner handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
961*28ef38a9SChristian Brauner ASSERT_NE(handle, NULL);
962*28ef38a9SChristian Brauner
963*28ef38a9SChristian Brauner /* Create pipe for communication */
964*28ef38a9SChristian Brauner ASSERT_EQ(pipe(pipefd), 0);
965*28ef38a9SChristian Brauner
966*28ef38a9SChristian Brauner /* Get handle for current cgroup namespace */
967*28ef38a9SChristian Brauner ns_fd = open("/proc/self/ns/cgroup", O_RDONLY);
968*28ef38a9SChristian Brauner if (ns_fd < 0) {
969*28ef38a9SChristian Brauner SKIP(free(handle); close(pipefd[0]); close(pipefd[1]);
970*28ef38a9SChristian Brauner return, "cgroup namespace not available");
971*28ef38a9SChristian Brauner }
972*28ef38a9SChristian Brauner
973*28ef38a9SChristian Brauner handle->handle_bytes = MAX_HANDLE_SZ;
974*28ef38a9SChristian Brauner ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
975*28ef38a9SChristian Brauner if (ret < 0 && errno == EOPNOTSUPP) {
976*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd); close(pipefd[0]);
977*28ef38a9SChristian Brauner close(pipefd[1]);
978*28ef38a9SChristian Brauner return, "nsfs doesn't support file handles");
979*28ef38a9SChristian Brauner }
980*28ef38a9SChristian Brauner ASSERT_EQ(ret, 0);
981*28ef38a9SChristian Brauner close(ns_fd);
982*28ef38a9SChristian Brauner
983*28ef38a9SChristian Brauner pid = fork();
984*28ef38a9SChristian Brauner ASSERT_GE(pid, 0);
985*28ef38a9SChristian Brauner
986*28ef38a9SChristian Brauner if (pid == 0) {
987*28ef38a9SChristian Brauner /* Child process */
988*28ef38a9SChristian Brauner close(pipefd[0]);
989*28ef38a9SChristian Brauner
990*28ef38a9SChristian Brauner /* First create new user namespace to drop privileges */
991*28ef38a9SChristian Brauner ret = unshare(CLONE_NEWUSER);
992*28ef38a9SChristian Brauner if (ret < 0) {
993*28ef38a9SChristian Brauner write(pipefd[1], "U",
994*28ef38a9SChristian Brauner 1); /* Unable to create user namespace */
995*28ef38a9SChristian Brauner close(pipefd[1]);
996*28ef38a9SChristian Brauner exit(0);
997*28ef38a9SChristian Brauner }
998*28ef38a9SChristian Brauner
999*28ef38a9SChristian Brauner /* Write uid/gid mappings to maintain some capabilities */
1000*28ef38a9SChristian Brauner int uid_map_fd = open("/proc/self/uid_map", O_WRONLY);
1001*28ef38a9SChristian Brauner int gid_map_fd = open("/proc/self/gid_map", O_WRONLY);
1002*28ef38a9SChristian Brauner int setgroups_fd = open("/proc/self/setgroups", O_WRONLY);
1003*28ef38a9SChristian Brauner
1004*28ef38a9SChristian Brauner if (uid_map_fd < 0 || gid_map_fd < 0 || setgroups_fd < 0) {
1005*28ef38a9SChristian Brauner write(pipefd[1], "M", 1); /* Unable to set mappings */
1006*28ef38a9SChristian Brauner close(pipefd[1]);
1007*28ef38a9SChristian Brauner exit(0);
1008*28ef38a9SChristian Brauner }
1009*28ef38a9SChristian Brauner
1010*28ef38a9SChristian Brauner /* Disable setgroups to allow gid mapping */
1011*28ef38a9SChristian Brauner write(setgroups_fd, "deny", 4);
1012*28ef38a9SChristian Brauner close(setgroups_fd);
1013*28ef38a9SChristian Brauner
1014*28ef38a9SChristian Brauner /* Map current uid/gid to root in the new namespace */
1015*28ef38a9SChristian Brauner char mapping[64];
1016*28ef38a9SChristian Brauner snprintf(mapping, sizeof(mapping), "0 %d 1", getuid());
1017*28ef38a9SChristian Brauner write(uid_map_fd, mapping, strlen(mapping));
1018*28ef38a9SChristian Brauner close(uid_map_fd);
1019*28ef38a9SChristian Brauner
1020*28ef38a9SChristian Brauner snprintf(mapping, sizeof(mapping), "0 %d 1", getgid());
1021*28ef38a9SChristian Brauner write(gid_map_fd, mapping, strlen(mapping));
1022*28ef38a9SChristian Brauner close(gid_map_fd);
1023*28ef38a9SChristian Brauner
1024*28ef38a9SChristian Brauner /* Now create new cgroup namespace */
1025*28ef38a9SChristian Brauner ret = unshare(CLONE_NEWCGROUP);
1026*28ef38a9SChristian Brauner if (ret < 0) {
1027*28ef38a9SChristian Brauner write(pipefd[1], "N",
1028*28ef38a9SChristian Brauner 1); /* Unable to create cgroup namespace */
1029*28ef38a9SChristian Brauner close(pipefd[1]);
1030*28ef38a9SChristian Brauner exit(0);
1031*28ef38a9SChristian Brauner }
1032*28ef38a9SChristian Brauner
1033*28ef38a9SChristian Brauner /* Try to open parent's cgroup namespace handle from new user+cgroup namespace */
1034*28ef38a9SChristian Brauner fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
1035*28ef38a9SChristian Brauner
1036*28ef38a9SChristian Brauner if (fd >= 0) {
1037*28ef38a9SChristian Brauner /* Should NOT succeed - we're in a different user namespace */
1038*28ef38a9SChristian Brauner write(pipefd[1], "S", 1); /* Unexpected success */
1039*28ef38a9SChristian Brauner close(fd);
1040*28ef38a9SChristian Brauner } else if (errno == ESTALE) {
1041*28ef38a9SChristian Brauner /* Expected: Stale file handle */
1042*28ef38a9SChristian Brauner write(pipefd[1], "P", 1);
1043*28ef38a9SChristian Brauner } else {
1044*28ef38a9SChristian Brauner /* Other error */
1045*28ef38a9SChristian Brauner write(pipefd[1], "F", 1);
1046*28ef38a9SChristian Brauner }
1047*28ef38a9SChristian Brauner
1048*28ef38a9SChristian Brauner close(pipefd[1]);
1049*28ef38a9SChristian Brauner exit(0);
1050*28ef38a9SChristian Brauner }
1051*28ef38a9SChristian Brauner
1052*28ef38a9SChristian Brauner /* Parent process */
1053*28ef38a9SChristian Brauner close(pipefd[1]);
1054*28ef38a9SChristian Brauner ASSERT_EQ(read(pipefd[0], &result, 1), 1);
1055*28ef38a9SChristian Brauner
1056*28ef38a9SChristian Brauner waitpid(pid, &status, 0);
1057*28ef38a9SChristian Brauner ASSERT_TRUE(WIFEXITED(status));
1058*28ef38a9SChristian Brauner ASSERT_EQ(WEXITSTATUS(status), 0);
1059*28ef38a9SChristian Brauner
1060*28ef38a9SChristian Brauner if (result == 'U') {
1061*28ef38a9SChristian Brauner SKIP(free(handle); close(pipefd[0]);
1062*28ef38a9SChristian Brauner return, "Cannot create new user namespace");
1063*28ef38a9SChristian Brauner }
1064*28ef38a9SChristian Brauner if (result == 'M') {
1065*28ef38a9SChristian Brauner SKIP(free(handle); close(pipefd[0]);
1066*28ef38a9SChristian Brauner return, "Cannot set uid/gid mappings");
1067*28ef38a9SChristian Brauner }
1068*28ef38a9SChristian Brauner if (result == 'N') {
1069*28ef38a9SChristian Brauner SKIP(free(handle); close(pipefd[0]);
1070*28ef38a9SChristian Brauner return, "Cannot create new cgroup namespace");
1071*28ef38a9SChristian Brauner }
1072*28ef38a9SChristian Brauner
1073*28ef38a9SChristian Brauner /* Should fail with ESTALE since we're in a different user namespace */
1074*28ef38a9SChristian Brauner ASSERT_EQ(result, 'P');
1075*28ef38a9SChristian Brauner
1076*28ef38a9SChristian Brauner close(pipefd[0]);
1077*28ef38a9SChristian Brauner free(handle);
1078*28ef38a9SChristian Brauner }
1079*28ef38a9SChristian Brauner
TEST(nsfs_user_pid_namespace_isolation)1080*28ef38a9SChristian Brauner TEST(nsfs_user_pid_namespace_isolation)
1081*28ef38a9SChristian Brauner {
1082*28ef38a9SChristian Brauner struct file_handle *handle;
1083*28ef38a9SChristian Brauner int mount_id;
1084*28ef38a9SChristian Brauner int ret;
1085*28ef38a9SChristian Brauner int fd;
1086*28ef38a9SChristian Brauner int ns_fd;
1087*28ef38a9SChristian Brauner pid_t pid;
1088*28ef38a9SChristian Brauner int status;
1089*28ef38a9SChristian Brauner int pipefd[2];
1090*28ef38a9SChristian Brauner char result;
1091*28ef38a9SChristian Brauner
1092*28ef38a9SChristian Brauner handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
1093*28ef38a9SChristian Brauner ASSERT_NE(handle, NULL);
1094*28ef38a9SChristian Brauner
1095*28ef38a9SChristian Brauner /* Create pipe for communication */
1096*28ef38a9SChristian Brauner ASSERT_EQ(pipe(pipefd), 0);
1097*28ef38a9SChristian Brauner
1098*28ef38a9SChristian Brauner /* Get handle for current PID namespace */
1099*28ef38a9SChristian Brauner ns_fd = open("/proc/self/ns/pid", O_RDONLY);
1100*28ef38a9SChristian Brauner ASSERT_GE(ns_fd, 0);
1101*28ef38a9SChristian Brauner
1102*28ef38a9SChristian Brauner handle->handle_bytes = MAX_HANDLE_SZ;
1103*28ef38a9SChristian Brauner ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
1104*28ef38a9SChristian Brauner if (ret < 0 && errno == EOPNOTSUPP) {
1105*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd); close(pipefd[0]);
1106*28ef38a9SChristian Brauner close(pipefd[1]);
1107*28ef38a9SChristian Brauner return, "nsfs doesn't support file handles");
1108*28ef38a9SChristian Brauner }
1109*28ef38a9SChristian Brauner ASSERT_EQ(ret, 0);
1110*28ef38a9SChristian Brauner close(ns_fd);
1111*28ef38a9SChristian Brauner
1112*28ef38a9SChristian Brauner pid = fork();
1113*28ef38a9SChristian Brauner ASSERT_GE(pid, 0);
1114*28ef38a9SChristian Brauner
1115*28ef38a9SChristian Brauner if (pid == 0) {
1116*28ef38a9SChristian Brauner /* Child process */
1117*28ef38a9SChristian Brauner close(pipefd[0]);
1118*28ef38a9SChristian Brauner
1119*28ef38a9SChristian Brauner /* First create new user namespace to drop privileges */
1120*28ef38a9SChristian Brauner ret = unshare(CLONE_NEWUSER);
1121*28ef38a9SChristian Brauner if (ret < 0) {
1122*28ef38a9SChristian Brauner write(pipefd[1], "U",
1123*28ef38a9SChristian Brauner 1); /* Unable to create user namespace */
1124*28ef38a9SChristian Brauner close(pipefd[1]);
1125*28ef38a9SChristian Brauner exit(0);
1126*28ef38a9SChristian Brauner }
1127*28ef38a9SChristian Brauner
1128*28ef38a9SChristian Brauner /* Write uid/gid mappings to maintain some capabilities */
1129*28ef38a9SChristian Brauner int uid_map_fd = open("/proc/self/uid_map", O_WRONLY);
1130*28ef38a9SChristian Brauner int gid_map_fd = open("/proc/self/gid_map", O_WRONLY);
1131*28ef38a9SChristian Brauner int setgroups_fd = open("/proc/self/setgroups", O_WRONLY);
1132*28ef38a9SChristian Brauner
1133*28ef38a9SChristian Brauner if (uid_map_fd < 0 || gid_map_fd < 0 || setgroups_fd < 0) {
1134*28ef38a9SChristian Brauner write(pipefd[1], "M", 1); /* Unable to set mappings */
1135*28ef38a9SChristian Brauner close(pipefd[1]);
1136*28ef38a9SChristian Brauner exit(0);
1137*28ef38a9SChristian Brauner }
1138*28ef38a9SChristian Brauner
1139*28ef38a9SChristian Brauner /* Disable setgroups to allow gid mapping */
1140*28ef38a9SChristian Brauner write(setgroups_fd, "deny", 4);
1141*28ef38a9SChristian Brauner close(setgroups_fd);
1142*28ef38a9SChristian Brauner
1143*28ef38a9SChristian Brauner /* Map current uid/gid to root in the new namespace */
1144*28ef38a9SChristian Brauner char mapping[64];
1145*28ef38a9SChristian Brauner snprintf(mapping, sizeof(mapping), "0 %d 1", getuid());
1146*28ef38a9SChristian Brauner write(uid_map_fd, mapping, strlen(mapping));
1147*28ef38a9SChristian Brauner close(uid_map_fd);
1148*28ef38a9SChristian Brauner
1149*28ef38a9SChristian Brauner snprintf(mapping, sizeof(mapping), "0 %d 1", getgid());
1150*28ef38a9SChristian Brauner write(gid_map_fd, mapping, strlen(mapping));
1151*28ef38a9SChristian Brauner close(gid_map_fd);
1152*28ef38a9SChristian Brauner
1153*28ef38a9SChristian Brauner /* Now create new PID namespace - requires fork to take effect */
1154*28ef38a9SChristian Brauner ret = unshare(CLONE_NEWPID);
1155*28ef38a9SChristian Brauner if (ret < 0) {
1156*28ef38a9SChristian Brauner write(pipefd[1], "N",
1157*28ef38a9SChristian Brauner 1); /* Unable to create PID namespace */
1158*28ef38a9SChristian Brauner close(pipefd[1]);
1159*28ef38a9SChristian Brauner exit(0);
1160*28ef38a9SChristian Brauner }
1161*28ef38a9SChristian Brauner
1162*28ef38a9SChristian Brauner /* Fork again for PID namespace to take effect */
1163*28ef38a9SChristian Brauner pid_t child_pid = fork();
1164*28ef38a9SChristian Brauner if (child_pid < 0) {
1165*28ef38a9SChristian Brauner write(pipefd[1], "N",
1166*28ef38a9SChristian Brauner 1); /* Unable to fork in PID namespace */
1167*28ef38a9SChristian Brauner close(pipefd[1]);
1168*28ef38a9SChristian Brauner exit(0);
1169*28ef38a9SChristian Brauner }
1170*28ef38a9SChristian Brauner
1171*28ef38a9SChristian Brauner if (child_pid == 0) {
1172*28ef38a9SChristian Brauner /* Grandchild in new PID namespace */
1173*28ef38a9SChristian Brauner /* Try to open parent's PID namespace handle from new user+pid namespace */
1174*28ef38a9SChristian Brauner fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
1175*28ef38a9SChristian Brauner
1176*28ef38a9SChristian Brauner if (fd >= 0) {
1177*28ef38a9SChristian Brauner /* Should NOT succeed - we're in a different user namespace */
1178*28ef38a9SChristian Brauner write(pipefd[1], "S",
1179*28ef38a9SChristian Brauner 1); /* Unexpected success */
1180*28ef38a9SChristian Brauner close(fd);
1181*28ef38a9SChristian Brauner } else if (errno == ESTALE) {
1182*28ef38a9SChristian Brauner /* Expected: Stale file handle */
1183*28ef38a9SChristian Brauner write(pipefd[1], "P", 1);
1184*28ef38a9SChristian Brauner } else {
1185*28ef38a9SChristian Brauner /* Other error */
1186*28ef38a9SChristian Brauner write(pipefd[1], "F", 1);
1187*28ef38a9SChristian Brauner }
1188*28ef38a9SChristian Brauner
1189*28ef38a9SChristian Brauner close(pipefd[1]);
1190*28ef38a9SChristian Brauner exit(0);
1191*28ef38a9SChristian Brauner }
1192*28ef38a9SChristian Brauner
1193*28ef38a9SChristian Brauner /* Wait for grandchild */
1194*28ef38a9SChristian Brauner waitpid(child_pid, NULL, 0);
1195*28ef38a9SChristian Brauner exit(0);
1196*28ef38a9SChristian Brauner }
1197*28ef38a9SChristian Brauner
1198*28ef38a9SChristian Brauner /* Parent process */
1199*28ef38a9SChristian Brauner close(pipefd[1]);
1200*28ef38a9SChristian Brauner ASSERT_EQ(read(pipefd[0], &result, 1), 1);
1201*28ef38a9SChristian Brauner
1202*28ef38a9SChristian Brauner waitpid(pid, &status, 0);
1203*28ef38a9SChristian Brauner ASSERT_TRUE(WIFEXITED(status));
1204*28ef38a9SChristian Brauner ASSERT_EQ(WEXITSTATUS(status), 0);
1205*28ef38a9SChristian Brauner
1206*28ef38a9SChristian Brauner if (result == 'U') {
1207*28ef38a9SChristian Brauner SKIP(free(handle); close(pipefd[0]);
1208*28ef38a9SChristian Brauner return, "Cannot create new user namespace");
1209*28ef38a9SChristian Brauner }
1210*28ef38a9SChristian Brauner if (result == 'M') {
1211*28ef38a9SChristian Brauner SKIP(free(handle); close(pipefd[0]);
1212*28ef38a9SChristian Brauner return, "Cannot set uid/gid mappings");
1213*28ef38a9SChristian Brauner }
1214*28ef38a9SChristian Brauner if (result == 'N') {
1215*28ef38a9SChristian Brauner SKIP(free(handle); close(pipefd[0]);
1216*28ef38a9SChristian Brauner return, "Cannot create new PID namespace");
1217*28ef38a9SChristian Brauner }
1218*28ef38a9SChristian Brauner
1219*28ef38a9SChristian Brauner /* Should fail with ESTALE since we're in a different user namespace */
1220*28ef38a9SChristian Brauner ASSERT_EQ(result, 'P');
1221*28ef38a9SChristian Brauner
1222*28ef38a9SChristian Brauner close(pipefd[0]);
1223*28ef38a9SChristian Brauner free(handle);
1224*28ef38a9SChristian Brauner }
1225*28ef38a9SChristian Brauner
TEST(nsfs_user_time_namespace_isolation)1226*28ef38a9SChristian Brauner TEST(nsfs_user_time_namespace_isolation)
1227*28ef38a9SChristian Brauner {
1228*28ef38a9SChristian Brauner struct file_handle *handle;
1229*28ef38a9SChristian Brauner int mount_id;
1230*28ef38a9SChristian Brauner int ret;
1231*28ef38a9SChristian Brauner int fd;
1232*28ef38a9SChristian Brauner int ns_fd;
1233*28ef38a9SChristian Brauner pid_t pid;
1234*28ef38a9SChristian Brauner int status;
1235*28ef38a9SChristian Brauner int pipefd[2];
1236*28ef38a9SChristian Brauner char result;
1237*28ef38a9SChristian Brauner
1238*28ef38a9SChristian Brauner handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
1239*28ef38a9SChristian Brauner ASSERT_NE(handle, NULL);
1240*28ef38a9SChristian Brauner
1241*28ef38a9SChristian Brauner /* Create pipe for communication */
1242*28ef38a9SChristian Brauner ASSERT_EQ(pipe(pipefd), 0);
1243*28ef38a9SChristian Brauner
1244*28ef38a9SChristian Brauner /* Get handle for current time namespace */
1245*28ef38a9SChristian Brauner ns_fd = open("/proc/self/ns/time", O_RDONLY);
1246*28ef38a9SChristian Brauner if (ns_fd < 0) {
1247*28ef38a9SChristian Brauner SKIP(free(handle); close(pipefd[0]); close(pipefd[1]);
1248*28ef38a9SChristian Brauner return, "time namespace not available");
1249*28ef38a9SChristian Brauner }
1250*28ef38a9SChristian Brauner
1251*28ef38a9SChristian Brauner handle->handle_bytes = MAX_HANDLE_SZ;
1252*28ef38a9SChristian Brauner ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
1253*28ef38a9SChristian Brauner if (ret < 0 && errno == EOPNOTSUPP) {
1254*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd); close(pipefd[0]);
1255*28ef38a9SChristian Brauner close(pipefd[1]);
1256*28ef38a9SChristian Brauner return, "nsfs doesn't support file handles");
1257*28ef38a9SChristian Brauner }
1258*28ef38a9SChristian Brauner ASSERT_EQ(ret, 0);
1259*28ef38a9SChristian Brauner close(ns_fd);
1260*28ef38a9SChristian Brauner
1261*28ef38a9SChristian Brauner pid = fork();
1262*28ef38a9SChristian Brauner ASSERT_GE(pid, 0);
1263*28ef38a9SChristian Brauner
1264*28ef38a9SChristian Brauner if (pid == 0) {
1265*28ef38a9SChristian Brauner /* Child process */
1266*28ef38a9SChristian Brauner close(pipefd[0]);
1267*28ef38a9SChristian Brauner
1268*28ef38a9SChristian Brauner /* First create new user namespace to drop privileges */
1269*28ef38a9SChristian Brauner ret = unshare(CLONE_NEWUSER);
1270*28ef38a9SChristian Brauner if (ret < 0) {
1271*28ef38a9SChristian Brauner write(pipefd[1], "U",
1272*28ef38a9SChristian Brauner 1); /* Unable to create user namespace */
1273*28ef38a9SChristian Brauner close(pipefd[1]);
1274*28ef38a9SChristian Brauner exit(0);
1275*28ef38a9SChristian Brauner }
1276*28ef38a9SChristian Brauner
1277*28ef38a9SChristian Brauner /* Write uid/gid mappings to maintain some capabilities */
1278*28ef38a9SChristian Brauner int uid_map_fd = open("/proc/self/uid_map", O_WRONLY);
1279*28ef38a9SChristian Brauner int gid_map_fd = open("/proc/self/gid_map", O_WRONLY);
1280*28ef38a9SChristian Brauner int setgroups_fd = open("/proc/self/setgroups", O_WRONLY);
1281*28ef38a9SChristian Brauner
1282*28ef38a9SChristian Brauner if (uid_map_fd < 0 || gid_map_fd < 0 || setgroups_fd < 0) {
1283*28ef38a9SChristian Brauner write(pipefd[1], "M", 1); /* Unable to set mappings */
1284*28ef38a9SChristian Brauner close(pipefd[1]);
1285*28ef38a9SChristian Brauner exit(0);
1286*28ef38a9SChristian Brauner }
1287*28ef38a9SChristian Brauner
1288*28ef38a9SChristian Brauner /* Disable setgroups to allow gid mapping */
1289*28ef38a9SChristian Brauner write(setgroups_fd, "deny", 4);
1290*28ef38a9SChristian Brauner close(setgroups_fd);
1291*28ef38a9SChristian Brauner
1292*28ef38a9SChristian Brauner /* Map current uid/gid to root in the new namespace */
1293*28ef38a9SChristian Brauner char mapping[64];
1294*28ef38a9SChristian Brauner snprintf(mapping, sizeof(mapping), "0 %d 1", getuid());
1295*28ef38a9SChristian Brauner write(uid_map_fd, mapping, strlen(mapping));
1296*28ef38a9SChristian Brauner close(uid_map_fd);
1297*28ef38a9SChristian Brauner
1298*28ef38a9SChristian Brauner snprintf(mapping, sizeof(mapping), "0 %d 1", getgid());
1299*28ef38a9SChristian Brauner write(gid_map_fd, mapping, strlen(mapping));
1300*28ef38a9SChristian Brauner close(gid_map_fd);
1301*28ef38a9SChristian Brauner
1302*28ef38a9SChristian Brauner /* Now create new time namespace - requires fork to take effect */
1303*28ef38a9SChristian Brauner ret = unshare(CLONE_NEWTIME);
1304*28ef38a9SChristian Brauner if (ret < 0) {
1305*28ef38a9SChristian Brauner write(pipefd[1], "N",
1306*28ef38a9SChristian Brauner 1); /* Unable to create time namespace */
1307*28ef38a9SChristian Brauner close(pipefd[1]);
1308*28ef38a9SChristian Brauner exit(0);
1309*28ef38a9SChristian Brauner }
1310*28ef38a9SChristian Brauner
1311*28ef38a9SChristian Brauner /* Fork again for time namespace to take effect */
1312*28ef38a9SChristian Brauner pid_t child_pid = fork();
1313*28ef38a9SChristian Brauner if (child_pid < 0) {
1314*28ef38a9SChristian Brauner write(pipefd[1], "N",
1315*28ef38a9SChristian Brauner 1); /* Unable to fork in time namespace */
1316*28ef38a9SChristian Brauner close(pipefd[1]);
1317*28ef38a9SChristian Brauner exit(0);
1318*28ef38a9SChristian Brauner }
1319*28ef38a9SChristian Brauner
1320*28ef38a9SChristian Brauner if (child_pid == 0) {
1321*28ef38a9SChristian Brauner /* Grandchild in new time namespace */
1322*28ef38a9SChristian Brauner /* Try to open parent's time namespace handle from new user+time namespace */
1323*28ef38a9SChristian Brauner fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
1324*28ef38a9SChristian Brauner
1325*28ef38a9SChristian Brauner if (fd >= 0) {
1326*28ef38a9SChristian Brauner /* Should NOT succeed - we're in a different user namespace */
1327*28ef38a9SChristian Brauner write(pipefd[1], "S",
1328*28ef38a9SChristian Brauner 1); /* Unexpected success */
1329*28ef38a9SChristian Brauner close(fd);
1330*28ef38a9SChristian Brauner } else if (errno == ESTALE) {
1331*28ef38a9SChristian Brauner /* Expected: Stale file handle */
1332*28ef38a9SChristian Brauner write(pipefd[1], "P", 1);
1333*28ef38a9SChristian Brauner } else {
1334*28ef38a9SChristian Brauner /* Other error */
1335*28ef38a9SChristian Brauner write(pipefd[1], "F", 1);
1336*28ef38a9SChristian Brauner }
1337*28ef38a9SChristian Brauner
1338*28ef38a9SChristian Brauner close(pipefd[1]);
1339*28ef38a9SChristian Brauner exit(0);
1340*28ef38a9SChristian Brauner }
1341*28ef38a9SChristian Brauner
1342*28ef38a9SChristian Brauner /* Wait for grandchild */
1343*28ef38a9SChristian Brauner waitpid(child_pid, NULL, 0);
1344*28ef38a9SChristian Brauner exit(0);
1345*28ef38a9SChristian Brauner }
1346*28ef38a9SChristian Brauner
1347*28ef38a9SChristian Brauner /* Parent process */
1348*28ef38a9SChristian Brauner close(pipefd[1]);
1349*28ef38a9SChristian Brauner ASSERT_EQ(read(pipefd[0], &result, 1), 1);
1350*28ef38a9SChristian Brauner
1351*28ef38a9SChristian Brauner waitpid(pid, &status, 0);
1352*28ef38a9SChristian Brauner ASSERT_TRUE(WIFEXITED(status));
1353*28ef38a9SChristian Brauner ASSERT_EQ(WEXITSTATUS(status), 0);
1354*28ef38a9SChristian Brauner
1355*28ef38a9SChristian Brauner if (result == 'U') {
1356*28ef38a9SChristian Brauner SKIP(free(handle); close(pipefd[0]);
1357*28ef38a9SChristian Brauner return, "Cannot create new user namespace");
1358*28ef38a9SChristian Brauner }
1359*28ef38a9SChristian Brauner if (result == 'M') {
1360*28ef38a9SChristian Brauner SKIP(free(handle); close(pipefd[0]);
1361*28ef38a9SChristian Brauner return, "Cannot set uid/gid mappings");
1362*28ef38a9SChristian Brauner }
1363*28ef38a9SChristian Brauner if (result == 'N') {
1364*28ef38a9SChristian Brauner SKIP(free(handle); close(pipefd[0]);
1365*28ef38a9SChristian Brauner return, "Cannot create new time namespace");
1366*28ef38a9SChristian Brauner }
1367*28ef38a9SChristian Brauner
1368*28ef38a9SChristian Brauner /* Should fail with ESTALE since we're in a different user namespace */
1369*28ef38a9SChristian Brauner ASSERT_EQ(result, 'P');
1370*28ef38a9SChristian Brauner
1371*28ef38a9SChristian Brauner close(pipefd[0]);
1372*28ef38a9SChristian Brauner free(handle);
1373*28ef38a9SChristian Brauner }
1374*28ef38a9SChristian Brauner
TEST(nsfs_open_flags)1375*28ef38a9SChristian Brauner TEST(nsfs_open_flags)
1376*28ef38a9SChristian Brauner {
1377*28ef38a9SChristian Brauner struct file_handle *handle;
1378*28ef38a9SChristian Brauner int mount_id;
1379*28ef38a9SChristian Brauner int ret;
1380*28ef38a9SChristian Brauner int fd;
1381*28ef38a9SChristian Brauner int ns_fd;
1382*28ef38a9SChristian Brauner
1383*28ef38a9SChristian Brauner handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
1384*28ef38a9SChristian Brauner ASSERT_NE(handle, NULL);
1385*28ef38a9SChristian Brauner
1386*28ef38a9SChristian Brauner /* Open a namespace file descriptor */
1387*28ef38a9SChristian Brauner ns_fd = open("/proc/self/ns/net", O_RDONLY);
1388*28ef38a9SChristian Brauner ASSERT_GE(ns_fd, 0);
1389*28ef38a9SChristian Brauner
1390*28ef38a9SChristian Brauner /* Get handle for the namespace */
1391*28ef38a9SChristian Brauner handle->handle_bytes = MAX_HANDLE_SZ;
1392*28ef38a9SChristian Brauner ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
1393*28ef38a9SChristian Brauner if (ret < 0 && errno == EOPNOTSUPP) {
1394*28ef38a9SChristian Brauner SKIP(free(handle); close(ns_fd);
1395*28ef38a9SChristian Brauner return, "nsfs doesn't support file handles");
1396*28ef38a9SChristian Brauner }
1397*28ef38a9SChristian Brauner ASSERT_EQ(ret, 0);
1398*28ef38a9SChristian Brauner ASSERT_GT(handle->handle_bytes, 0);
1399*28ef38a9SChristian Brauner
1400*28ef38a9SChristian Brauner /* Test invalid flags that should fail */
1401*28ef38a9SChristian Brauner fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_WRONLY);
1402*28ef38a9SChristian Brauner ASSERT_LT(fd, 0);
1403*28ef38a9SChristian Brauner ASSERT_EQ(errno, EPERM);
1404*28ef38a9SChristian Brauner
1405*28ef38a9SChristian Brauner fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDWR);
1406*28ef38a9SChristian Brauner ASSERT_LT(fd, 0);
1407*28ef38a9SChristian Brauner ASSERT_EQ(errno, EPERM);
1408*28ef38a9SChristian Brauner
1409*28ef38a9SChristian Brauner fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_TRUNC);
1410*28ef38a9SChristian Brauner ASSERT_LT(fd, 0);
1411*28ef38a9SChristian Brauner ASSERT_EQ(errno, EPERM);
1412*28ef38a9SChristian Brauner
1413*28ef38a9SChristian Brauner fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_DIRECT);
1414*28ef38a9SChristian Brauner ASSERT_LT(fd, 0);
1415*28ef38a9SChristian Brauner ASSERT_EQ(errno, EINVAL);
1416*28ef38a9SChristian Brauner
1417*28ef38a9SChristian Brauner fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_TMPFILE);
1418*28ef38a9SChristian Brauner ASSERT_LT(fd, 0);
1419*28ef38a9SChristian Brauner ASSERT_EQ(errno, EINVAL);
1420*28ef38a9SChristian Brauner
1421*28ef38a9SChristian Brauner fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_DIRECTORY);
1422*28ef38a9SChristian Brauner ASSERT_LT(fd, 0);
1423*28ef38a9SChristian Brauner ASSERT_EQ(errno, ENOTDIR);
1424*28ef38a9SChristian Brauner
1425*28ef38a9SChristian Brauner close(ns_fd);
1426*28ef38a9SChristian Brauner free(handle);
1427*28ef38a9SChristian Brauner }
1428*28ef38a9SChristian Brauner
1429*28ef38a9SChristian Brauner TEST_HARNESS_MAIN
1430