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