]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
import: make sure image mangling works unpriv too 39406/head
authorLennart Poettering <lennart@poettering.net>
Mon, 25 Aug 2025 09:34:53 +0000 (11:34 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 30 Oct 2025 21:57:43 +0000 (22:57 +0100)
src/import/import-common.c
src/import/import-common.h
src/import/import-fs.c
src/import/import-tar.c
src/import/pull-tar.c

index 70b3c3be023c6f8c86dd8f449e7c2c8ca83d5d5e..11a6c060bb43efd0f894c5525a1dd9d547c1514b 100644 (file)
@@ -148,7 +148,7 @@ int import_fork_tar_c(int tree_fd, int userns_fd, PidRef *ret_pid) {
         return TAKE_FD(pipefd[0]);
 }
 
-int import_mangle_os_tree_fd(int tree_fd) {
+int import_mangle_os_tree_fd(int tree_fd, int userns_fd, ImportFlags flags) {
         _cleanup_free_ char *child = NULL, *t = NULL, *joined = NULL;
         _cleanup_closedir_ DIR *d = NULL, *cd = NULL;
         struct dirent *dent;
@@ -157,6 +157,9 @@ int import_mangle_os_tree_fd(int tree_fd) {
 
         assert(tree_fd >= 0);
 
+        if (FLAGS_SET(flags, IMPORT_FOREIGN_UID) && userns_fd >= 0)
+                return import_mangle_os_tree_fd_foreign(tree_fd, userns_fd);
+
         /* Some tarballs contain a single top-level directory that contains the actual OS directory tree. Try to
          * recognize this, and move the tree one level up. */
 
@@ -274,14 +277,58 @@ int import_mangle_os_tree_fd(int tree_fd) {
         return 0;
 }
 
-int import_mangle_os_tree(const char *path) {
+int import_mangle_os_tree(const char *path, int userns_fd, ImportFlags flags) {
         assert(path);
 
         _cleanup_close_ int fd = open(path, O_DIRECTORY|O_CLOEXEC|O_PATH);
         if (fd < 0)
                 return log_error_errno(errno, "Failed to open '%s': %m", path);
 
-        return import_mangle_os_tree_fd(fd);
+        return import_mangle_os_tree_fd(fd, userns_fd, flags);
+}
+
+int import_mangle_os_tree_fd_foreign(
+                int tree_fd,
+                int userns_fd) {
+
+        int r;
+
+        assert(tree_fd >= 0);
+        assert(userns_fd >= 0);
+
+        r = safe_fork_full(
+                        "mangle-tree",
+                        /* stdio_fds= */ NULL,
+                        (int[]) { userns_fd, tree_fd }, 2,
+                        FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_REOPEN_LOG|FORK_WAIT,
+                        /* ret_pid= */ NULL);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                /* child */
+
+                r = namespace_enter(
+                                /* pidns_fd= */ -EBADF,
+                                /* mntns_fd= */ -EBADF,
+                                /* netns_fd= */ -EBADF,
+                                userns_fd,
+                                /* root_fd= */ -EBADF);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to join user namespace: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                r = import_mangle_os_tree_fd(tree_fd, /* userns_fd= */ -EBADF, /* flags= */ 0);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to mangle OS tree in foreign UID mode: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                _exit(EXIT_SUCCESS);
+        }
+
+        return 0;
+
 }
 
 bool import_validate_local(const char *name, ImportFlags flags) {
index 289b83b4e6bed4341ddfa4eec09470372929bb89..8fab3f380bba5e1abe0308fbbc18fa7504b30129 100644 (file)
@@ -37,8 +37,9 @@ typedef enum ImportFlags {
 int import_fork_tar_c(int tree_fd, int userns_fd, PidRef *ret_pid);
 int import_fork_tar_x(int tree_fd, int userns_fd, PidRef *ret_pid);
 
-int import_mangle_os_tree_fd(int tree_fd);
-int import_mangle_os_tree(const char *path);
+int import_mangle_os_tree_fd(int tree_fd, int userns_fd, ImportFlags flags);
+int import_mangle_os_tree(const char *path, int userns_fd, ImportFlags flags);
+int import_mangle_os_tree_fd_foreign(int tree_fd, int userns_fd);
 
 bool import_validate_local(const char *name, ImportFlags flags);
 
index 537fe46853a72e0e83090d525ed333fbd509cb81..947bc7c9b56c1a981eb7e7a3ac59fdd5e428c0a6 100644 (file)
@@ -238,7 +238,7 @@ static int import_fs(int argc, char *argv[], void *userdata) {
                         return log_error_errno(r, "Failed to copy directory: %m");
         }
 
-        r = import_mangle_os_tree(dest);
+        r = import_mangle_os_tree(dest, /* userns_fd= */ -EBADF, /* flags= */ 0);
         if (r < 0)
                 return r;
 
index e30be005fc7ea9242633a45d6eb6bb80227eaff9..5aa3a98640e2977853666bf7fa7ef9b6762c5fc7 100644 (file)
@@ -199,7 +199,7 @@ static int tar_import_finish(TarImport *i) {
 
         assert_se(d = i->temp_path ?: i->local);
 
-        r = import_mangle_os_tree_fd(i->tree_fd);
+        r = import_mangle_os_tree_fd(i->tree_fd, i->userns_fd, i->flags);
         if (r < 0)
                 return r;
 
index 65f88e7a8870265cfbcc4de4ffa02029b09eb7e4..23bdfb186a7e8701593d5e5ba254556aad4d9a97 100644 (file)
@@ -497,7 +497,7 @@ static void tar_pull_job_on_finished(PullJob *j) {
 
                 tar_pull_report_progress(i, TAR_FINALIZING);
 
-                r = import_mangle_os_tree_fd(i->tree_fd);
+                r = import_mangle_os_tree_fd(i->tree_fd, i->userns_fd, i->flags);
                 if (r < 0)
                         goto finish;
 
@@ -523,7 +523,7 @@ static void tar_pull_job_on_finished(PullJob *j) {
 
                         tar_pull_report_progress(i, TAR_FINALIZING);
 
-                        r = import_mangle_os_tree_fd(i->tree_fd);
+                        r = import_mangle_os_tree_fd(i->tree_fd, i->userns_fd, i->flags);
                         if (r < 0)
                                 goto finish;