From: igo95862 Date: Sun, 26 Mar 2023 15:04:11 +0000 (+0600) Subject: Add `--user-parent` option to nsenter X-Git-Tag: v2.40-rc1~362^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=01a6d803b5980c6409c42b5f269441fd1a9d935c;p=thirdparty%2Futil-linux.git Add `--user-parent` option to nsenter When this option is used nsenter will fetch the parent user namespace from any namespace file descriptors available. It can be combined with existing `--user` option in which case the parent user namespace will be fetched from the user namespace and replace it. The usecase of this option is when a user namespace that owns the other type namespaces we want to switch to is not actually bound to any process. Without using ioctl it is impossible to acquire namespace file descriptor. For example, bubblewrap `bwrap` command creates unbinded user namespace when `--dev` option is used. --- diff --git a/bash-completion/nsenter b/bash-completion/nsenter index 50763837ef..3ffd813714 100644 --- a/bash-completion/nsenter +++ b/bash-completion/nsenter @@ -42,6 +42,7 @@ _nsenter_module() --pid= --cgroup= --user= + --user-parent --time= --setuid --setgid diff --git a/sys-utils/nsenter.1.adoc b/sys-utils/nsenter.1.adoc index cf7e338301..b7e92d6b19 100644 --- a/sys-utils/nsenter.1.adoc +++ b/sys-utils/nsenter.1.adoc @@ -97,6 +97,10 @@ Enter the PID namespace. If no file is specified, enter the PID namespace of the *-U*, *--user*[=_file_]:: Enter the user namespace. If no file is specified, enter the user namespace of the target process. If _file_ is specified, enter the user namespace specified by _file_. See also the *--setuid* and *--setgid* options. +*--user-parent*:: +Enter the parent user namespace. Parent user namespace will be acquired from any other enabled namespace. +If combined with *--user* option the parent user namespace will be fetched from the user namespace and replace it. + *-C*, *--cgroup*[=_file_]:: Enter the cgroup namespace. If no file is specified, enter the cgroup namespace of the target process. If _file_ is specified, enter the cgroup namespace specified by _file_. @@ -139,6 +143,10 @@ Set the SELinux security context used for executing a new process according to a include::man-common/help-version.adoc[] +== NOTES + +The *--user-parent* option requires Linux 4.9 or higher, older kernels will raise inappropriate ioctl for device error. + == AUTHORS mailto:biederm@xmission.com[Eric Biederman], diff --git a/sys-utils/nsenter.c b/sys-utils/nsenter.c index 7a4619d31a..74975313d5 100644 --- a/sys-utils/nsenter.c +++ b/sys-utils/nsenter.c @@ -31,6 +31,14 @@ #include #include +#include +#ifdef HAVE_LINUX_NSFS_H +# include +#endif +#ifndef NS_GET_USERNS +# define NS_GET_USERNS _IO(0xb7, 0x1) +#endif + #ifdef HAVE_LIBSELINUX # include #endif @@ -92,6 +100,7 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" -p, --pid[=] enter pid namespace\n"), out); fputs(_(" -C, --cgroup[=] enter cgroup namespace\n"), out); fputs(_(" -U, --user[=] enter user namespace\n"), out); + fputs(_(" --user-parent enter parent user namespace\n"), out); fputs(_(" -T, --time[=] enter time namespace\n"), out); fputs(_(" -S, --setuid[=] set uid in entered namespace\n"), out); fputs(_(" -G, --setgid[=] set gid in entered namespace\n"), out); @@ -119,6 +128,33 @@ static int wd_fd = -1; static int env_fd = -1; static int uid_gid_fd = -1; +static void set_parent_user_ns_fd(void) +{ + struct namespace_file *nsfile = NULL; + struct namespace_file *user_nsfile = NULL; + int parent_ns = -1; + + for (nsfile = namespace_files; nsfile->nstype; nsfile++) { + if (nsfile->nstype == CLONE_NEWUSER) + user_nsfile = nsfile; + + if (nsfile->fd == -1) + continue; + + parent_ns = ioctl(nsfile->fd, NS_GET_USERNS); + if (parent_ns < 0) + err(EXIT_FAILURE, _("failed to open parent ns of %s"), nsfile->name); + + break; + } + + if (parent_ns < 0) + errx(EXIT_FAILURE, _("no namespaces to get parent of")); + + user_nsfile->fd = parent_ns; +} + + static void open_target_fd(int *fd, const char *type, const char *path) { char pathbuf[PATH_MAX]; @@ -236,6 +272,7 @@ int main(int argc, char *argv[]) enum { OPT_PRESERVE_CRED = CHAR_MAX + 1, OPT_KEEPCAPS, + OPT_USER_PARENT, }; static const struct option longopts[] = { { "all", no_argument, NULL, 'a' }, @@ -259,6 +296,7 @@ int main(int argc, char *argv[]) { "no-fork", no_argument, NULL, 'F' }, { "preserve-credentials", no_argument, NULL, OPT_PRESERVE_CRED }, { "keep-caps", no_argument, NULL, OPT_KEEPCAPS }, + { "user-parent", no_argument, NULL, OPT_USER_PARENT}, #ifdef HAVE_LIBSELINUX { "follow-context", no_argument, NULL, 'Z' }, #endif @@ -273,7 +311,8 @@ int main(int argc, char *argv[]) struct namespace_file *nsfile; int c, pass, namespaces = 0, setgroups_nerrs = 0, preserve_cred = 0; bool do_rd = false, do_wd = false, do_uid = false, force_uid = false, - do_gid = false, force_gid = false, do_env = false, do_all = false; + do_gid = false, force_gid = false, do_env = false, do_all = false, + do_user_parent = false; int do_fork = -1; /* unknown yet */ char *wdns = NULL; uid_t uid = 0; @@ -392,6 +431,9 @@ int main(int argc, char *argv[]) case OPT_KEEPCAPS: keepcaps = 1; break; + case OPT_USER_PARENT: + do_user_parent = true; + break; #ifdef HAVE_LIBSELINUX case 'Z': selinux = 1; @@ -450,6 +492,12 @@ int main(int argc, char *argv[]) if (do_uid || do_gid) open_target_fd(&uid_gid_fd, "", NULL); + /* + * Get parent userns from any available ns. + */ + if (do_user_parent) + set_parent_user_ns_fd(); + /* * Update namespaces variable to contain all requested namespaces */