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 5 : 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 5 : if (fstatfs(fd, &s) < 0)
138 0 : return -errno;
139 :
140 5 : 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 3 : 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 3 : return 0; /* No! */
159 : }
160 :
161 2 : r = ioctl(fd, NS_GET_NSTYPE);
162 2 : 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 2 : return r == CLONE_NEWNET;
170 : }
|