From: Karel Zak Date: Fri, 18 Oct 2024 10:16:04 +0000 (+0200) Subject: nsenter: use pidfd to enter target namespaces X-Git-Tag: v2.42-start~159^2~4 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=f18be0ca5aa764404aa626c84cff315256c9ee73;p=thirdparty%2Futil-linux.git nsenter: use pidfd to enter target namespaces The typical use case is to enter namespaces of the task (--target ). The original nsenter opens /proc//ns/* files and uses the file descriptors to enter the namespaces by setns(). The recent kernel allows using the pid file descriptor instead of the files in /proc, making it possible to enter multiple namespaces with one setns call. This solution reduces the number of syscalls (open+setns for each namespace), removes the dependence on /proc, and allows entering nested namespaces. This commit should be backwardly compatible, meaning it can be used on systems without pidfd_open(). Explicitly specified namespaces by filenames are still supported, and user namespaces are still entered first/last according to permissions privileging/deprivileging. Addresses: https://github.com/util-linux/util-linux/pull/301 Signed-off-by: Karel Zak --- diff --git a/sys-utils/nsenter.c b/sys-utils/nsenter.c index 66f278aad..53c3641fb 100644 --- a/sys-utils/nsenter.c +++ b/sys-utils/nsenter.c @@ -211,6 +211,17 @@ static void enable_namespace(int nstype, const char *path) assert(nsfile->nstype); } +static void disable_namespaces(int namespaces) +{ + struct namespace_file *n; + + for (n = namespace_files; n->nstype; n++) { + if (!(namespaces & n->nstype)) + continue; + disable_nsfile(n); + } +} + /* Returns mask of all enabled namespaces */ static int get_namespaces(void) { @@ -252,24 +263,38 @@ static void open_namespaces(int namespaces) } } -static void enter_namespaces(int namespaces, bool ignore_errors) +static int do_setns(int fd, int ns, const char *name, bool ignore_errors) +{ + int rc = setns(fd, ns); + + if (rc < 0 && !ignore_errors) { + if (name) + err(EXIT_FAILURE, _("reassociate to namespace '%s' failed"), name); + else + err(EXIT_FAILURE, _("reassociate to namespaces failed")); + } + return rc; +} + +static void enter_namespaces(int pid_fd, int namespaces, bool ignore_errors) { struct namespace_file *n; + if (pid_fd) { + int ns = 0; + for (n = namespace_files; n->nstype; n++) { + if (n->enabled && (n->nstype & namespaces) && n->fd < 0) + ns |= n->nstype; + } + if (ns && do_setns(pid_fd, ns, NULL, ignore_errors) == 0) + disable_namespaces(ns); + } + for (n = namespace_files; n->nstype; n++) { - if (!n->enabled) + if (!n->enabled || !(n->nstype & namespaces) || n->fd < 0) continue; - if (!(n->nstype & namespaces)) - continue; - if (n->fd < 0) - continue; - - if (setns(n->fd, n->nstype) == 0) - disable_nsfile(n); /* sucess */ - else if (!ignore_errors) - err(EXIT_FAILURE, - _("reassociate to namespace '%s' failed"), - n->name); + if (do_setns(n->fd, n->nstype, n->name, ignore_errors) == 0) + disable_nsfile(n); } } @@ -486,6 +511,7 @@ int main(int argc, char *argv[]) gid_t gid = 0; int keepcaps = 0; int sock_fd = -1; + int pid_fd = -1; struct ul_env_list *envls; #ifdef HAVE_LIBSELINUX bool selinux = 0; @@ -633,7 +659,12 @@ int main(int argc, char *argv[]) if (namespaces) { if (!namespace_target_pid) errx(EXIT_FAILURE, _("no target PID specified")); - open_namespaces(namespaces); + + pid_fd = pidfd_open(namespace_target_pid, 0); + if (pid_fd < 0) { + /* fallback to classic way */ + open_namespaces(namespaces); + } } if (do_rd) @@ -688,11 +719,14 @@ int main(int argc, char *argv[]) * namespace last and if we're privileging it then we enter the user * namespace first (because the initial setns will fail). */ - enter_namespaces(namespaces & ~CLONE_NEWUSER, 1); /* ignore errors */ + enter_namespaces(pid_fd, namespaces & ~CLONE_NEWUSER, 1); /* ignore errors */ namespaces = get_namespaces(); if (namespaces) - enter_namespaces(namespaces, 0); /* report errors */ + enter_namespaces(pid_fd, namespaces, 0); /* report errors */ + + if (pid_fd >= 0) + close(pid_fd); /* Remember the current working directory if I'm not changing it */ if (root_fd >= 0 && wd_fd < 0 && wdns == NULL) {