]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
unshare: load binfmt_misc interpreter
authorLaurent Vivier <laurent@vivier.eu>
Wed, 19 Jun 2024 08:19:51 +0000 (10:19 +0200)
committerKarel Zak <kzak@redhat.com>
Wed, 19 Jun 2024 10:39:56 +0000 (12:39 +0200)
add -l, --load-interp <file> to load a binfmt_misc interpreter at startup.

The interpreter is loaded from the initial fileystem if the 'F' flags is
provided, otherwise from inside the new namespace
This makes possible to start a chroot of another architecture without
being root.

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
sys-utils/unshare.1.adoc
sys-utils/unshare.c

index 48d1a55792827b48335358252d3ca22e10c7c8a3..6396c4892ddf4775dfaacc438863f6af2a55a062 100644 (file)
@@ -138,6 +138,10 @@ Set the user ID which will be used in the entered namespace.
 *-G*, *--setgid* _gid_::
 Set the group ID which will be used in the entered namespace and drop supplementary groups.
 
+*-l*, **--load-interp=**__string__::
+Load binfmt_misc definition in the namespace (implies *--mount-binfmt*). The __string__ argument is ``:name:type:offset:magic:mask:interpreter:flags``. For more details about new binary type registration see  https://www.kernel.org/doc/Documentation/admin-guide/binfmt-misc.rst.
+To manage the F flag in ``flags`` with **--root** parameter, binfmt_misc is mounted twice, once before the chroot to load the interpreter from the caller filesystem and once after to make it available from the chroot userspace.
+
 *--monotonic* _offset_::
 Set the offset of *CLOCK_MONOTONIC* which will be used in the entered time namespace. This option requires unsharing a time namespace with *--time*.
 
@@ -256,6 +260,20 @@ up 21 hours, 30 minutes
 up 9 years, 28 weeks, 1 day, 2 hours, 50 minutes
 ....
 
+The following example execute a chroot into the directory /chroot/powerpc/jessie and install the interpreter /bin/qemu-ppc-static to execute the powerpc binaries.
+
+....
+$  unshare --map-root-user --fork --pid --load-interp=":qemu-ppc:M::\\x7fELF\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x14:\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff:/bin/qemu-ppc-static:OCF" --root=/chroot/powerpc/jessie /bin/bash -l
+....
+
+The ``load-interp`` parameter can be read as following::
+``qemu-ppc``::: is the name of the new file created below ``/proc/sys/fs/binfmt_misc`` to register the interpreter
+``M``::: defines the interpreter for a given type of magic number
+``\\x7fELF\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x1``::: is the magic number to recognize the file to interpret (in this case, the ELF header for PPC32)
+``\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff``::: the mask to apply to the magic number
+``/bin/qemu-ppc-static``::: the interpreter to use with the file
+``OCF``::: the file is open by the kernel with credential and security tokens of the file itself and loaded as soon as we register it.
+
 == AUTHORS
 
 mailto:dottedmag@dottedmag.net[Mikhail Gusarov],
index d79aa112595510efad8bd9adc84b0dad2f2ced0a..e48e4c9f55a586c19dcfdfee76cd66362bc0cc4b 100644 (file)
@@ -725,6 +725,35 @@ static pid_t map_ids_from_child(int *fd, uid_t mapuser,
        exit(EXIT_SUCCESS);
 }
 
+static int is_fixed(const char *interp)
+{
+       const char *flags;
+
+       flags = strrchr(interp, ':');
+
+       return strchr(flags, 'F') != NULL;
+}
+
+static void load_interp(const char *binfmt_mnt, const char *interp)
+{
+       int dirfd, fd;
+
+       dirfd = open(binfmt_mnt, O_PATH | O_DIRECTORY);
+       if (dirfd < 0)
+               err(EXIT_FAILURE, _("cannot open %s"), binfmt_mnt);
+
+       fd = openat(dirfd, "register", O_WRONLY);
+       if (fd < 0)
+               err(EXIT_FAILURE, _("cannot open %s/register"), binfmt_mnt);
+
+       if (write_all(fd, interp, strlen(interp)))
+               err(EXIT_FAILURE, _("write failed %s/register"), binfmt_mnt);
+
+       close(fd);
+
+       close(dirfd);
+}
+
 static void __attribute__((__noreturn__)) usage(void)
 {
        FILE *out = stdout;
@@ -772,6 +801,7 @@ static void __attribute__((__noreturn__)) usage(void)
        fputs(_(" -G, --setgid <gid>        set gid in entered namespace\n"), out);
        fputs(_(" --monotonic <offset>      set clock monotonic offset (seconds) in time namespaces\n"), out);
        fputs(_(" --boottime <offset>       set clock boottime offset (seconds) in time namespaces\n"), out);
+       fputs(_(" -l, --load-interp <file>  load binfmt definition in the namespace (implies --mount-binfmt)\n"), out);
 
        fputs(USAGE_SEPARATOR, out);
        fprintf(out, USAGE_HELP_OPTIONS(27));
@@ -830,6 +860,7 @@ int main(int argc, char *argv[])
                { "wd",            required_argument, NULL, 'w'             },
                { "monotonic",     required_argument, NULL, OPT_MONOTONIC   },
                { "boottime",      required_argument, NULL, OPT_BOOTTIME    },
+               { "load-interp",   required_argument, NULL, 'l'             },
                { NULL, 0, NULL, 0 }
        };
 
@@ -846,6 +877,7 @@ int main(int argc, char *argv[])
        const char *newroot = NULL;
        const char *newdir = NULL;
        pid_t pid_bind = 0, pid_idmap = 0;
+       const char *newinterp = NULL;
        pid_t pid = 0;
 #ifdef UL_HAVE_PIDFD
        int fd_parent_pid = -1;
@@ -868,7 +900,7 @@ int main(int argc, char *argv[])
        textdomain(PACKAGE);
        close_stdout_atexit();
 
-       while ((c = getopt_long(argc, argv, "+fhVmuinpCTUrR:w:S:G:c", longopts, NULL)) != -1) {
+       while ((c = getopt_long(argc, argv, "+fhVmuinpCTUrR:w:S:G:cl:", longopts, NULL)) != -1) {
                switch (c) {
                case 'f':
                        forkit = 1;
@@ -1011,6 +1043,15 @@ int main(int argc, char *argv[])
                        boottime = strtos64_or_err(optarg, _("failed to parse boottime offset"));
                        force_boottime = 1;
                        break;
+               case 'l':
+                       unshare_flags |= CLONE_NEWNS | CLONE_NEWUSER;
+                       if (!binfmt_mnt) {
+                               if (!procmnt)
+                                       procmnt = "/proc";
+                               binfmt_mnt = _PATH_PROC_BINFMT_MISC;
+                       }
+                       newinterp = optarg;
+                       break;
 
                case 'h':
                        usage();
@@ -1165,6 +1206,13 @@ int main(int argc, char *argv[])
        if ((unshare_flags & CLONE_NEWNS) && propagation)
                set_propagation(propagation);
 
+       if (newinterp && is_fixed(newinterp) && newroot) {
+               if (mount("binfmt_misc", _PATH_PROC_BINFMT_MISC, "binfmt_misc",
+                         MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0)
+                       err(EXIT_FAILURE, _("mount %s failed"), _PATH_PROC_BINFMT_MISC);
+               load_interp(_PATH_PROC_BINFMT_MISC, newinterp);
+       }
+
        if (newroot) {
                if (chroot(newroot) != 0)
                        err(EXIT_FAILURE,
@@ -1196,6 +1244,8 @@ int main(int argc, char *argv[])
                          MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0)
                        err(EXIT_FAILURE, _("mount %s failed"), binfmt_mnt);
        }
+       if (newinterp && !(is_fixed(newinterp) && newroot))
+               load_interp(binfmt_mnt, newinterp);
 
        if (force_gid) {
                if (setgroups(0, NULL) != 0)    /* drop supplementary groups */