]>
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) | |
65 | *pidns_fd = pidnsfd; | |
66 | ||
67 | if (mntns_fd) | |
68 | *mntns_fd = mntnsfd; | |
69 | ||
70 | if (netns_fd) | |
71 | *netns_fd = netnsfd; | |
72 | ||
73 | if (userns_fd) | |
74 | *userns_fd = usernsfd; | |
75 | ||
76 | if (root_fd) | |
77 | *root_fd = rfd; | |
78 | ||
79 | pidnsfd = mntnsfd = netnsfd = usernsfd = -1; | |
80 | ||
81 | return 0; | |
82 | } | |
83 | ||
84 | int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) { | |
85 | if (userns_fd >= 0) { | |
86 | /* Can't setns to your own userns, since then you could | |
87 | * escalate from non-root to root in your own namespace, so | |
88 | * check if namespaces equal before attempting to enter. */ | |
89 | _cleanup_free_ char *userns_fd_path = NULL; | |
90 | int r; | |
91 | if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0) | |
92 | return -ENOMEM; | |
93 | ||
94 | r = files_same(userns_fd_path, "/proc/self/ns/user", 0); | |
95 | if (r < 0) | |
96 | return r; | |
97 | if (r) | |
98 | userns_fd = -1; | |
99 | } | |
100 | ||
101 | if (pidns_fd >= 0) | |
102 | if (setns(pidns_fd, CLONE_NEWPID) < 0) | |
103 | return -errno; | |
104 | ||
105 | if (mntns_fd >= 0) | |
106 | if (setns(mntns_fd, CLONE_NEWNS) < 0) | |
107 | return -errno; | |
108 | ||
109 | if (netns_fd >= 0) | |
110 | if (setns(netns_fd, CLONE_NEWNET) < 0) | |
111 | return -errno; | |
112 | ||
113 | if (userns_fd >= 0) | |
114 | if (setns(userns_fd, CLONE_NEWUSER) < 0) | |
115 | return -errno; | |
116 | ||
117 | if (root_fd >= 0) { | |
118 | if (fchdir(root_fd) < 0) | |
119 | return -errno; | |
120 | ||
121 | if (chroot(".") < 0) | |
122 | return -errno; | |
123 | } | |
124 | ||
125 | return reset_uid_gid(); | |
126 | } | |
127 | ||
128 | int fd_is_network_ns(int fd) { | |
129 | struct statfs s; | |
130 | int r; | |
131 | ||
132 | /* Checks whether the specified file descriptor refers to a network namespace. On old kernels there's no nice | |
133 | * way to detect that, hence on those we'll return a recognizable error (EUCLEAN), so that callers can handle | |
134 | * this somewhat nicely. | |
135 | * | |
136 | * This function returns > 0 if the fd definitely refers to a network namespace, 0 if it definitely does not | |
137 | * refer to a network namespace, -EUCLEAN if we can't determine, and other negative error codes on error. */ | |
138 | ||
139 | if (fstatfs(fd, &s) < 0) | |
140 | return -errno; | |
141 | ||
142 | if (!is_fs_type(&s, NSFS_MAGIC)) { | |
143 | /* On really old kernels, there was no "nsfs", and network namespace sockets belonged to procfs | |
144 | * instead. Handle that in a somewhat smart way. */ | |
145 | ||
146 | if (is_fs_type(&s, PROC_SUPER_MAGIC)) { | |
147 | struct statfs t; | |
148 | ||
149 | /* OK, so it is procfs. Let's see if our own network namespace is procfs, too. If so, then the | |
150 | * passed fd might refer to a network namespace, but we can't know for sure. In that case, | |
151 | * return a recognizable error. */ | |
152 | ||
153 | if (statfs("/proc/self/ns/net", &t) < 0) | |
154 | return -errno; | |
155 | ||
156 | if (s.f_type == t.f_type) | |
157 | return -EUCLEAN; /* It's possible, we simply don't know */ | |
158 | } | |
159 | ||
160 | return 0; /* No! */ | |
161 | } | |
162 | ||
163 | r = ioctl(fd, NS_GET_NSTYPE); | |
164 | if (r < 0) { | |
165 | if (errno == ENOTTY) /* Old kernels didn't know this ioctl, let's also return a recognizable error in that case */ | |
166 | return -EUCLEAN; | |
167 | ||
168 | return -errno; | |
169 | } | |
170 | ||
171 | return r == CLONE_NEWNET; | |
172 | } |