]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/namespace-util.c
namespace-util: make use of TAKE_FD()
[thirdparty/systemd.git] / src / basic / namespace-util.c
CommitLineData
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
13int 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
82int 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
126int 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}