From: Michael Tremer Date: Tue, 31 Oct 2023 16:11:44 +0000 (+0000) Subject: pakfire: Create a new user namespace for every pakfire instance X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=ce2785b7910d46436ac587ce99259af412792dd8;p=pakfire.git pakfire: Create a new user namespace for every pakfire instance Signed-off-by: Michael Tremer --- diff --git a/Makefile.am b/Makefile.am index 0d44a1a14..a6319d924 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 index 000000000..2e6f63151 --- /dev/null +++ b/src/libpakfire/include/pakfire/namespace.h @@ -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 . # +# # +#############################################################################*/ + +#ifndef PAKFIRE_NAMESPACE_H +#define PAKFIRE_NAMESPACE_H + +#ifdef PAKFIRE_PRIVATE + +#include + +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 index 000000000..faa66e929 --- /dev/null +++ b/src/libpakfire/namespace.c @@ -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 . # +# # +#############################################################################*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +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; +} diff --git a/src/libpakfire/pakfire.c b/src/libpakfire/pakfire.c index 8ad78a580..be9b3d2d1 100644 --- a/src/libpakfire/pakfire.c +++ b/src/libpakfire/pakfire.c @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -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);