]> git.ipfire.org Git - pakfire.git/commitdiff
pakfire: Create a new user namespace for every pakfire instance
authorMichael Tremer <michael.tremer@ipfire.org>
Tue, 31 Oct 2023 16:11:44 +0000 (16:11 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Tue, 31 Oct 2023 16:11:44 +0000 (16:11 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Makefile.am
src/libpakfire/include/pakfire/namespace.h [new file with mode: 0644]
src/libpakfire/namespace.c [new file with mode: 0644]
src/libpakfire/pakfire.c

index 0d44a1a147bd74a35214fde29d30ef06b3cfd894..a6319d924697b03320fa94fd33be251dcec450c7 100644 (file)
@@ -235,6 +235,7 @@ libpakfire_la_SOURCES = \
        src/libpakfire/mirror.c \
        src/libpakfire/mirrorlist.c \
        src/libpakfire/mount.c \
+       src/libpakfire/namespace.c \
        src/libpakfire/os.c \
        src/libpakfire/package.c \
        src/libpakfire/packager.c \
@@ -282,6 +283,7 @@ pkginclude_HEADERS += \
        src/libpakfire/include/pakfire/mirror.h \
        src/libpakfire/include/pakfire/mirrorlist.h \
        src/libpakfire/include/pakfire/mount.h \
+       src/libpakfire/include/pakfire/namespace.h \
        src/libpakfire/include/pakfire/os.h \
        src/libpakfire/include/pakfire/package.h \
        src/libpakfire/include/pakfire/packager.h \
diff --git a/src/libpakfire/include/pakfire/namespace.h b/src/libpakfire/include/pakfire/namespace.h
new file mode 100644 (file)
index 0000000..2e6f631
--- /dev/null
@@ -0,0 +1,32 @@
+/*#############################################################################
+#                                                                             #
+# Pakfire - The IPFire package management system                              #
+# Copyright (C) 2023 Pakfire development team                                 #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+#############################################################################*/
+
+#ifndef PAKFIRE_NAMESPACE_H
+#define PAKFIRE_NAMESPACE_H
+
+#ifdef PAKFIRE_PRIVATE
+
+#include <pakfire/ctx.h>
+
+int pakfire_setup_namespace(struct pakfire_ctx* ctx, int* userfd);
+
+#endif /* PAKFIRE_PRIVATE */
+
+#endif /* PAKFIRE_NAMESPACE_H */
diff --git a/src/libpakfire/namespace.c b/src/libpakfire/namespace.c
new file mode 100644 (file)
index 0000000..faa66e9
--- /dev/null
@@ -0,0 +1,165 @@
+/*#############################################################################
+#                                                                             #
+# Pakfire - The IPFire package management system                              #
+# Copyright (C) 2023 Pakfire development team                                 #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+#############################################################################*/
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/sched.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/eventfd.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <pakfire/ctx.h>
+#include <pakfire/logging.h>
+#include <pakfire/namespace.h>
+
+static int clone3(struct clone_args* args, size_t size) {
+       return syscall(__NR_clone3, args, size);
+}
+
+static int pakfire_namespace_parent(struct pakfire_ctx* ctx, int* userfd, int efd, int pidfd) {
+       siginfo_t status;
+       int r;
+
+       // Read from the file descriptor
+       r = eventfd_read(efd, (eventfd_t*)userfd);
+       if (r) {
+               CTX_ERROR(ctx, "Could not read file descriptor: %s\n", strerror(errno));
+               return -errno;
+       }
+
+       CTX_DEBUG(ctx, "Waiting for the child process to set up the namespace...\n");
+
+       // Wait for the child process to finish
+       r = waitid(P_PIDFD, pidfd, &status, WEXITED);
+       if (r) {
+               CTX_ERROR(ctx, "Failed waiting for the child process: %s\n", strerror(errno));
+               return -errno;
+       }
+
+       switch (status.si_code) {
+               case CLD_EXITED:
+                       if (status.si_status) {
+                               CTX_ERROR(ctx, "Child process exited with status %d\n", status.si_status);
+
+                               // Pass exit code
+                               return status.si_status;
+                       }
+                       break;
+
+               case CLD_KILLED:
+                       CTX_ERROR(ctx, "The child process was killed\n");
+                       return 139;
+
+               case CLD_DUMPED:
+                       CTX_ERROR(ctx, "The child process terminated abnormally\n");
+                       return 1;
+
+               // Log anything else
+               default:
+                       CTX_ERROR(ctx, "Unknown child exit code: %d\n", status.si_code);
+                       break;
+       }
+
+       CTX_DEBUG(ctx, "User Namespace successfully set up as fd %d\n", *userfd);
+
+       return 0;
+}
+
+static void pakfire_namespace_child(struct pakfire_ctx* ctx, int efd) {
+       pid_t pid = getpid();
+       int fd = -1;
+       int r;
+
+       CTX_DEBUG(ctx, "Launched a new process in a new namespace as PID %d\n", pid);
+
+       // Open the usernamespace
+       fd = open("/proc/self/ns/user", O_RDONLY);
+       if (fd < 0) {
+               CTX_ERROR(ctx, "Could not open the user namespace: %s\n", strerror(errno));
+               _exit(1);
+       }
+
+       // Send the file descriptor to the parent process
+       r = eventfd_write(efd, (eventfd_t)fd);
+       if (r) {
+               CTX_ERROR(ctx, "Could not write the file descriptor: %s\n", strerror(errno));
+               _exit(1);
+       }
+
+       _exit(0);
+}
+
+int pakfire_setup_namespace(struct pakfire_ctx* ctx, int* userfd) {
+       int pidfd = -1;
+       int efd = -1;
+       int r;
+
+       CTX_DEBUG(ctx, "Creating a new user namespace...\n");
+
+       struct clone_args args = {
+               .flags =
+                       CLONE_NEWUSER |
+                       CLONE_FILES |
+                       CLONE_PIDFD,
+               .exit_signal = SIGCHLD,
+               .pidfd = (long long unsigned int)&pidfd,
+       };
+
+       // Setup an event file descriptor
+       efd = eventfd(0, EFD_CLOEXEC);
+       if (efd < 0) {
+               CTX_ERROR(ctx, "eventfd() failed: %s\n", strerror(errno));
+               r = -errno;
+               goto ERROR;
+       }
+
+       // Fork a new process
+       pid_t pid = clone3(&args, sizeof(args));
+
+       // Fail on error
+       if (pid < 0) {
+               CTX_ERROR(ctx, "Could not create a new namespace: %s\n", strerror(errno));
+               return -errno;
+
+       // Child process
+       } else if (pid == 0) {
+               pakfire_namespace_child(ctx, efd);
+
+               // We should never reach this
+               abort();
+
+       // Parent process
+       } else {
+               r = pakfire_namespace_parent(ctx, userfd, efd, pidfd);
+       }
+
+ERROR:
+       if (pidfd >= 0)
+               close(pidfd);
+       if (efd >= 0)
+               close(efd);
+
+       return r;
+}
index 8ad78a580d97df564547d93f17e531a1c0a95259..be9b3d2d1a7e10fe98d913b3cf53102b0a252424 100644 (file)
@@ -52,6 +52,7 @@
 #include <pakfire/dist.h>
 #include <pakfire/logging.h>
 #include <pakfire/mount.h>
+#include <pakfire/namespace.h>
 #include <pakfire/os.h>
 #include <pakfire/package.h>
 #include <pakfire/packagelist.h>
@@ -82,6 +83,9 @@ struct pakfire {
 
        int flags;
 
+       // User Namespace
+       int userfd;
+
        // Lock
        FILE* lock;
 
@@ -412,6 +416,10 @@ static void pakfire_free(struct pakfire* pakfire) {
                pakfire_rmtree(pakfire->path, 0);
        }
 
+       // Release the namespace
+       if (pakfire->userfd >= 0)
+               close(pakfire->userfd);
+
        pakfire_repo_free_all(pakfire);
 
        if (pakfire->pool)
@@ -812,6 +820,11 @@ PAKFIRE_EXPORT int pakfire_create(struct pakfire** pakfire, struct pakfire_ctx*
                goto ERROR;
        }
 
+       // Setup a new namespace
+       r = pakfire_setup_namespace(p->ctx, &p->userfd);
+       if (r)
+               goto ERROR;
+
        // Create a ramdisk if no path was given
        if (!path) {
                r = pakfire_make_ramdisk(p, tempdir, NULL);