]>
Commit | Line | Data |
---|---|---|
0cb8e3d1 LP |
1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
2 | ||
3 | #include <fcntl.h> | |
4 | #include <linux/magic.h> | |
5 | ||
6 | #include "fd-util.h" | |
7 | #include "missing.h" | |
8 | #include "namespace-util.h" | |
9 | #include "process-util.h" | |
10 | #include "stat-util.h" | |
11 | #include "user-util.h" | |
12 | ||
13 | int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) { | |
14 | _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1; | |
15 | int rfd = -1; | |
16 | ||
17 | assert(pid >= 0); | |
18 | ||
19 | if (mntns_fd) { | |
20 | const char *mntns; | |
21 | ||
22 | mntns = procfs_file_alloca(pid, "ns/mnt"); | |
23 | mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC); | |
24 | if (mntnsfd < 0) | |
25 | return -errno; | |
26 | } | |
27 | ||
28 | if (pidns_fd) { | |
29 | const char *pidns; | |
30 | ||
31 | pidns = procfs_file_alloca(pid, "ns/pid"); | |
32 | pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC); | |
33 | if (pidnsfd < 0) | |
34 | return -errno; | |
35 | } | |
36 | ||
37 | if (netns_fd) { | |
38 | const char *netns; | |
39 | ||
40 | netns = procfs_file_alloca(pid, "ns/net"); | |
41 | netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC); | |
42 | if (netnsfd < 0) | |
43 | return -errno; | |
44 | } | |
45 | ||
46 | if (userns_fd) { | |
47 | const char *userns; | |
48 | ||
49 | userns = procfs_file_alloca(pid, "ns/user"); | |
50 | usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC); | |
51 | if (usernsfd < 0 && errno != ENOENT) | |
52 | return -errno; | |
53 | } | |
54 | ||
55 | if (root_fd) { | |
56 | const char *root; | |
57 | ||
58 | root = procfs_file_alloca(pid, "root"); | |
59 | rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); | |
60 | if (rfd < 0) | |
61 | return -errno; | |
62 | } | |
63 | ||
64 | if (pidns_fd) | |
04c84cd1 | 65 | *pidns_fd = TAKE_FD(pidnsfd); |
0cb8e3d1 LP |
66 | |
67 | if (mntns_fd) | |
04c84cd1 | 68 | *mntns_fd = TAKE_FD(mntnsfd); |
0cb8e3d1 LP |
69 | |
70 | if (netns_fd) | |
04c84cd1 | 71 | *netns_fd = TAKE_FD(netnsfd); |
0cb8e3d1 LP |
72 | |
73 | if (userns_fd) | |
04c84cd1 | 74 | *userns_fd = TAKE_FD(usernsfd); |
0cb8e3d1 LP |
75 | |
76 | if (root_fd) | |
04c84cd1 | 77 | *root_fd = TAKE_FD(rfd); |
0cb8e3d1 LP |
78 | |
79 | return 0; | |
80 | } | |
81 | ||
82 | int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) { | |
83 | if (userns_fd >= 0) { | |
84 | /* Can't setns to your own userns, since then you could | |
85 | * escalate from non-root to root in your own namespace, so | |
86 | * check if namespaces equal before attempting to enter. */ | |
87 | _cleanup_free_ char *userns_fd_path = NULL; | |
88 | int r; | |
89 | if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0) | |
90 | return -ENOMEM; | |
91 | ||
92 | r = files_same(userns_fd_path, "/proc/self/ns/user", 0); | |
93 | if (r < 0) | |
94 | return r; | |
95 | if (r) | |
96 | userns_fd = -1; | |
97 | } | |
98 | ||
99 | if (pidns_fd >= 0) | |
100 | if (setns(pidns_fd, CLONE_NEWPID) < 0) | |
101 | return -errno; | |
102 | ||
103 | if (mntns_fd >= 0) | |
104 | if (setns(mntns_fd, CLONE_NEWNS) < 0) | |
105 | return -errno; | |
106 | ||
107 | if (netns_fd >= 0) | |
108 | if (setns(netns_fd, CLONE_NEWNET) < 0) | |
109 | return -errno; | |
110 | ||
111 | if (userns_fd >= 0) | |
112 | if (setns(userns_fd, CLONE_NEWUSER) < 0) | |
113 | return -errno; | |
114 | ||
115 | if (root_fd >= 0) { | |
116 | if (fchdir(root_fd) < 0) | |
117 | return -errno; | |
118 | ||
119 | if (chroot(".") < 0) | |
120 | return -errno; | |
121 | } | |
122 | ||
123 | return reset_uid_gid(); | |
124 | } | |
125 | ||
126 | int fd_is_network_ns(int fd) { | |
127 | struct statfs s; | |
128 | int r; | |
129 | ||
130 | /* Checks whether the specified file descriptor refers to a network namespace. On old kernels there's no nice | |
131 | * way to detect that, hence on those we'll return a recognizable error (EUCLEAN), so that callers can handle | |
132 | * this somewhat nicely. | |
133 | * | |
134 | * This function returns > 0 if the fd definitely refers to a network namespace, 0 if it definitely does not | |
135 | * refer to a network namespace, -EUCLEAN if we can't determine, and other negative error codes on error. */ | |
136 | ||
137 | if (fstatfs(fd, &s) < 0) | |
138 | return -errno; | |
139 | ||
140 | if (!is_fs_type(&s, NSFS_MAGIC)) { | |
141 | /* On really old kernels, there was no "nsfs", and network namespace sockets belonged to procfs | |
142 | * instead. Handle that in a somewhat smart way. */ | |
143 | ||
144 | if (is_fs_type(&s, PROC_SUPER_MAGIC)) { | |
145 | struct statfs t; | |
146 | ||
147 | /* OK, so it is procfs. Let's see if our own network namespace is procfs, too. If so, then the | |
148 | * passed fd might refer to a network namespace, but we can't know for sure. In that case, | |
149 | * return a recognizable error. */ | |
150 | ||
151 | if (statfs("/proc/self/ns/net", &t) < 0) | |
152 | return -errno; | |
153 | ||
154 | if (s.f_type == t.f_type) | |
155 | return -EUCLEAN; /* It's possible, we simply don't know */ | |
156 | } | |
157 | ||
158 | return 0; /* No! */ | |
159 | } | |
160 | ||
161 | r = ioctl(fd, NS_GET_NSTYPE); | |
162 | if (r < 0) { | |
163 | if (errno == ENOTTY) /* Old kernels didn't know this ioctl, let's also return a recognizable error in that case */ | |
164 | return -EUCLEAN; | |
165 | ||
166 | return -errno; | |
167 | } | |
168 | ||
169 | return r == CLONE_NEWNET; | |
170 | } |