]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
Add `--user-parent` option to nsenter
authorigo95862 <igo95862@yandex.ru>
Sun, 26 Mar 2023 15:04:11 +0000 (21:04 +0600)
committerigo95862 <igo95862@yandex.ru>
Thu, 22 Jun 2023 18:26:13 +0000 (00:26 +0600)
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.

bash-completion/nsenter
sys-utils/nsenter.1.adoc
sys-utils/nsenter.c

index 50763837ef2d20f86b2ca8f251ff6fcc561baa1a..3ffd813714baf3069e5281382433c67b77b5a5db 100644 (file)
@@ -42,6 +42,7 @@ _nsenter_module()
                                --pid=
                                --cgroup=
                                --user=
+                               --user-parent
                                --time=
                                --setuid
                                --setgid
index cf7e338301bc0de3c045daeed8046c2ab0f710e8..b7e92d6b19d851098e0c454e9de3dedbeb8409e2 100644 (file)
@@ -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],
index 7a4619d31a0e59f953c74d11cc7cc6ad344f9379..74975313d5487df1bdf489867f8eff45352cff23 100644 (file)
 #include <grp.h>
 #include <sys/stat.h>
 
+#include <sys/ioctl.h>
+#ifdef HAVE_LINUX_NSFS_H
+# include <linux/nsfs.h>
+#endif
+#ifndef NS_GET_USERNS
+# define NS_GET_USERNS           _IO(0xb7, 0x1)
+#endif
+
 #ifdef HAVE_LIBSELINUX
 # include <selinux/selinux.h>
 #endif
@@ -92,6 +100,7 @@ static void __attribute__((__noreturn__)) usage(void)
        fputs(_(" -p, --pid[=<file>]     enter pid namespace\n"), out);
        fputs(_(" -C, --cgroup[=<file>]  enter cgroup namespace\n"), out);
        fputs(_(" -U, --user[=<file>]    enter user namespace\n"), out);
+       fputs(_("     --user-parent      enter parent user namespace\n"), out);
        fputs(_(" -T, --time[=<file>]    enter time namespace\n"), out);
        fputs(_(" -S, --setuid[=<uid>]   set uid in entered namespace\n"), out);
        fputs(_(" -G, --setgid[=<gid>]   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
         */