1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include "errno-util.h"
10 #include "missing_fs.h"
11 #include "missing_magic.h"
12 #include "namespace-util.h"
13 #include "process-util.h"
14 #include "stat-util.h"
15 #include "stdio-util.h"
16 #include "user-util.h"
18 int namespace_open(pid_t pid
, int *pidns_fd
, int *mntns_fd
, int *netns_fd
, int *userns_fd
, int *root_fd
) {
19 _cleanup_close_
int pidnsfd
= -1, mntnsfd
= -1, netnsfd
= -1, usernsfd
= -1;
27 mntns
= procfs_file_alloca(pid
, "ns/mnt");
28 mntnsfd
= open(mntns
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
36 pidns
= procfs_file_alloca(pid
, "ns/pid");
37 pidnsfd
= open(pidns
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
45 netns
= procfs_file_alloca(pid
, "ns/net");
46 netnsfd
= open(netns
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
54 userns
= procfs_file_alloca(pid
, "ns/user");
55 usernsfd
= open(userns
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
56 if (usernsfd
< 0 && errno
!= ENOENT
)
63 root
= procfs_file_alloca(pid
, "root");
64 rfd
= open(root
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
|O_DIRECTORY
);
70 *pidns_fd
= TAKE_FD(pidnsfd
);
73 *mntns_fd
= TAKE_FD(mntnsfd
);
76 *netns_fd
= TAKE_FD(netnsfd
);
79 *userns_fd
= TAKE_FD(usernsfd
);
82 *root_fd
= TAKE_FD(rfd
);
87 int namespace_enter(int pidns_fd
, int mntns_fd
, int netns_fd
, int userns_fd
, int root_fd
) {
91 /* Can't setns to your own userns, since then you could escalate from non-root to root in
92 * your own namespace, so check if namespaces are equal before attempting to enter. */
94 r
= files_same(FORMAT_PROC_FD_PATH(userns_fd
), "/proc/self/ns/user", 0);
102 if (setns(pidns_fd
, CLONE_NEWPID
) < 0)
106 if (setns(mntns_fd
, CLONE_NEWNS
) < 0)
110 if (setns(netns_fd
, CLONE_NEWNET
) < 0)
114 if (setns(userns_fd
, CLONE_NEWUSER
) < 0)
118 if (fchdir(root_fd
) < 0)
125 return reset_uid_gid();
128 int fd_is_ns(int fd
, unsigned long nsflag
) {
132 /* Checks whether the specified file descriptor refers to a namespace created by specifying nsflag in clone().
133 * On old kernels there's no nice way to detect that, hence on those we'll return a recognizable error (EUCLEAN),
134 * so that callers can handle this somewhat nicely.
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. */
139 if (fstatfs(fd
, &s
) < 0)
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. */
146 if (is_fs_type(&s
, PROC_SUPER_MAGIC
)) {
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. */
153 if (statfs("/proc/self/ns/net", &t
) < 0)
156 if (s
.f_type
== t
.f_type
)
157 return -EUCLEAN
; /* It's possible, we simply don't know */
163 r
= ioctl(fd
, NS_GET_NSTYPE
);
165 if (errno
== ENOTTY
) /* Old kernels didn't know this ioctl, let's also return a recognizable error in that case */
171 return (unsigned long) r
== nsflag
;
174 int detach_mount_namespace(void) {
176 /* Detaches the mount namespace, disabling propagation from our namespace to the host */
178 if (unshare(CLONE_NEWNS
) < 0)
181 return RET_NERRNO(mount(NULL
, "/", NULL
, MS_SLAVE
| MS_REC
, NULL
));
184 int userns_acquire(const char *uid_map
, const char *gid_map
) {
185 char path
[STRLEN("/proc//uid_map") + DECIMAL_STR_MAX(pid_t
) + 1];
186 _cleanup_(sigkill_waitp
) pid_t pid
= 0;
187 _cleanup_close_
int userns_fd
= -1;
193 /* Forks off a process in a new userns, configures the specified uidmap/gidmap, acquires an fd to it,
194 * and then kills the process again. This way we have a userns fd that is not bound to any
195 * process. We can use that for file system mounts and similar. */
197 r
= safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG
|FORK_NEW_USERNS
, &pid
);
201 /* Child. We do nothing here, just freeze until somebody kills us. */
204 xsprintf(path
, "/proc/" PID_FMT
"/uid_map", pid
);
205 r
= write_string_file(path
, uid_map
, WRITE_STRING_FILE_DISABLE_BUFFER
);
207 return log_error_errno(r
, "Failed to write UID map: %m");
209 xsprintf(path
, "/proc/" PID_FMT
"/gid_map", pid
);
210 r
= write_string_file(path
, gid_map
, WRITE_STRING_FILE_DISABLE_BUFFER
);
212 return log_error_errno(r
, "Failed to write GID map: %m");
214 r
= namespace_open(pid
, NULL
, NULL
, NULL
, &userns_fd
, NULL
);
216 return log_error_errno(r
, "Failed to open netns fd: %m");
218 return TAKE_FD(userns_fd
);