Branch data Line data Source code
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 : 0 : int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) {
14 : 0 : _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1;
15 : 0 : int rfd = -1;
16 : :
17 [ # # ]: 0 : assert(pid >= 0);
18 : :
19 [ # # ]: 0 : if (mntns_fd) {
20 : : const char *mntns;
21 : :
22 [ # # # # : 0 : mntns = procfs_file_alloca(pid, "ns/mnt");
# # ]
23 : 0 : mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
24 [ # # ]: 0 : if (mntnsfd < 0)
25 : 0 : return -errno;
26 : : }
27 : :
28 [ # # ]: 0 : if (pidns_fd) {
29 : : const char *pidns;
30 : :
31 [ # # # # : 0 : pidns = procfs_file_alloca(pid, "ns/pid");
# # ]
32 : 0 : pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
33 [ # # ]: 0 : if (pidnsfd < 0)
34 : 0 : return -errno;
35 : : }
36 : :
37 [ # # ]: 0 : if (netns_fd) {
38 : : const char *netns;
39 : :
40 [ # # # # : 0 : netns = procfs_file_alloca(pid, "ns/net");
# # ]
41 : 0 : netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
42 [ # # ]: 0 : if (netnsfd < 0)
43 : 0 : return -errno;
44 : : }
45 : :
46 [ # # ]: 0 : if (userns_fd) {
47 : : const char *userns;
48 : :
49 [ # # # # : 0 : userns = procfs_file_alloca(pid, "ns/user");
# # ]
50 : 0 : usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
51 [ # # # # ]: 0 : if (usernsfd < 0 && errno != ENOENT)
52 : 0 : return -errno;
53 : : }
54 : :
55 [ # # ]: 0 : if (root_fd) {
56 : : const char *root;
57 : :
58 [ # # # # : 0 : root = procfs_file_alloca(pid, "root");
# # ]
59 : 0 : rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
60 [ # # ]: 0 : if (rfd < 0)
61 : 0 : return -errno;
62 : : }
63 : :
64 [ # # ]: 0 : if (pidns_fd)
65 : 0 : *pidns_fd = TAKE_FD(pidnsfd);
66 : :
67 [ # # ]: 0 : if (mntns_fd)
68 : 0 : *mntns_fd = TAKE_FD(mntnsfd);
69 : :
70 [ # # ]: 0 : if (netns_fd)
71 : 0 : *netns_fd = TAKE_FD(netnsfd);
72 : :
73 [ # # ]: 0 : if (userns_fd)
74 : 0 : *userns_fd = TAKE_FD(usernsfd);
75 : :
76 [ # # ]: 0 : if (root_fd)
77 : 0 : *root_fd = TAKE_FD(rfd);
78 : :
79 : 0 : return 0;
80 : : }
81 : :
82 : 0 : int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) {
83 [ # # ]: 0 : 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 [ # # ]: 0 : _cleanup_free_ char *userns_fd_path = NULL;
88 : : int r;
89 [ # # ]: 0 : if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0)
90 : 0 : return -ENOMEM;
91 : :
92 : 0 : r = files_same(userns_fd_path, "/proc/self/ns/user", 0);
93 [ # # ]: 0 : if (r < 0)
94 : 0 : return r;
95 [ # # ]: 0 : if (r)
96 : 0 : userns_fd = -1;
97 : : }
98 : :
99 [ # # ]: 0 : if (pidns_fd >= 0)
100 [ # # ]: 0 : if (setns(pidns_fd, CLONE_NEWPID) < 0)
101 : 0 : return -errno;
102 : :
103 [ # # ]: 0 : if (mntns_fd >= 0)
104 [ # # ]: 0 : if (setns(mntns_fd, CLONE_NEWNS) < 0)
105 : 0 : return -errno;
106 : :
107 [ # # ]: 0 : if (netns_fd >= 0)
108 [ # # ]: 0 : if (setns(netns_fd, CLONE_NEWNET) < 0)
109 : 0 : return -errno;
110 : :
111 [ # # ]: 0 : if (userns_fd >= 0)
112 [ # # ]: 0 : if (setns(userns_fd, CLONE_NEWUSER) < 0)
113 : 0 : return -errno;
114 : :
115 [ # # ]: 0 : if (root_fd >= 0) {
116 [ # # ]: 0 : if (fchdir(root_fd) < 0)
117 : 0 : return -errno;
118 : :
119 [ # # ]: 0 : if (chroot(".") < 0)
120 : 0 : return -errno;
121 : : }
122 : :
123 : 0 : return reset_uid_gid();
124 : : }
125 : :
126 : 20 : 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 [ - + ]: 20 : if (fstatfs(fd, &s) < 0)
138 : 0 : return -errno;
139 : :
140 [ + + ]: 20 : 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 [ - + ]: 12 : 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 [ # # ]: 0 : if (statfs("/proc/self/ns/net", &t) < 0)
152 : 0 : return -errno;
153 : :
154 [ # # ]: 0 : if (s.f_type == t.f_type)
155 : 0 : return -EUCLEAN; /* It's possible, we simply don't know */
156 : : }
157 : :
158 : 12 : return 0; /* No! */
159 : : }
160 : :
161 : 8 : r = ioctl(fd, NS_GET_NSTYPE);
162 [ - + ]: 8 : if (r < 0) {
163 [ # # ]: 0 : if (errno == ENOTTY) /* Old kernels didn't know this ioctl, let's also return a recognizable error in that case */
164 : 0 : return -EUCLEAN;
165 : :
166 : 0 : return -errno;
167 : : }
168 : :
169 : 8 : return r == CLONE_NEWNET;
170 : : }
|