Skip to content

Commit

Permalink
Add a new plugin to enable Linux-specific namespace functionality
Browse files Browse the repository at this point in the history
A plugin is a convenient place to hide Linux-specific functionality.
Implemented in this initial version are:

- Optional private mounts during scriptlet execution, useful for
  protecting the system from scriptlets (eg /home) and the scriptlets
  from themselves (eg insecure /tmp usage)
- Optionally disable network access during scriptlet execution

Note that at this time, scriplets executed with the embedded Lua
interpreter are not covered by this because they run inside the main rpm
process instead of forking (#2635).

Suggested-by: Johannes Segitz <[email protected]>

Fixes: #2632
Fixes: #2665
  • Loading branch information
pmatilai committed Sep 15, 2023
1 parent 1120c8c commit 587e778
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 0 deletions.
3 changes: 3 additions & 0 deletions docs/man/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ if (ENABLE_PLUGINS)
if (WITH_SELINUX)
list(APPEND manuals rpm-plugin-selinux.8)
endif()
if (HAVE_UNSHARE)
list(APPEND manuals rpm-plugin-unshare.8)
endif()
endif()

foreach(man ${manuals})
Expand Down
5 changes: 5 additions & 0 deletions macros.in
Original file line number Diff line number Diff line change
Expand Up @@ -1194,6 +1194,11 @@ Supplements: (%{name} = %{version}-%{release} and langpacks-%{1})\
%__transaction_prioreset %{__plugindir}/prioreset.so
%__transaction_audit %{__plugindir}/audit.so
%__transaction_dbus_announce %{__plugindir}/dbus_announce.so
%__transaction_unshare %{__plugindir}/unshare.so

# Unshare specific configuration
%__transaction_unshare_paths /tmp:/home
%__transaction_unshare_nonet 1

#------------------------------------------------------------------------------
# Macros for further automated spec %setup and patch application
Expand Down
4 changes: 4 additions & 0 deletions plugins/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ if(WITH_FSVERITY)
target_include_directories(fsverity PRIVATE ${CMAKE_SOURCE_DIR}/sign)
endif()

if (HAVE_UNSHARE)
add_library(unshare MODULE unshare.c)
endif()

set(plugindir ${CMAKE_INSTALL_FULL_LIBDIR}/rpm-plugins)

get_property(plugins DIRECTORY PROPERTY BUILDSYSTEM_TARGETS)
Expand Down
74 changes: 74 additions & 0 deletions plugins/unshare.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#include "system.h"

#include <sched.h>
#include <sys/mount.h>
#include <errno.h>
#include <string.h>

#include <rpm/rpmts.h>
#include <rpm/rpmplugin.h>
#include <rpm/rpmlog.h>
#include <rpm/rpmmacro.h>

#include "debug.h"

static ARGV_t private_mounts = NULL;
static int unshare_flags = 0;

static rpmRC unshare_init(rpmPlugin plugin, rpmts ts)
{
char *paths = rpmExpand("%{?__transaction_unshare_paths}", NULL);
private_mounts = argvSplitString(paths, ":", ARGV_SKIPEMPTY);
if (private_mounts)
unshare_flags |= CLONE_NEWNS;
free(paths);

if (rpmExpandNumeric("%{?__transaction_unshare_nonet}"))
unshare_flags |= CLONE_NEWNET;

return RPMRC_OK;
}

static void unshare_cleanup(rpmPlugin plugin)
{
/* ensure clean state for possible next transaction */
private_mounts = argvFree(private_mounts);
unshare_flags = 0;
}

static rpmRC unshare_scriptlet_fork_post(rpmPlugin plugin,
const char *path, int type)
{
rpmRC rc = RPMRC_FAIL;

if (unshare_flags && (unshare(unshare_flags) == -1)) {
rpmlog(RPMLOG_ERR, _("unshare with flags x%x failed: %s\n"),
unshare_flags, strerror(errno));
goto exit;
}

if (private_mounts) {
if (mount("/", "/", NULL, MS_REC | MS_PRIVATE, NULL) == -1) {
rpmlog(RPMLOG_ERR, _("failed to mount private %s: %s\n"),
"/", strerror(errno));
goto exit;
}
for (ARGV_t mnt = private_mounts; mnt && *mnt; mnt++) {
if (mount("none", *mnt, "tmpfs", 0, NULL) == -1) {
rpmlog(RPMLOG_ERR, _("failed to mount private %s: %s\n"),
*mnt, strerror(errno));
goto exit;
}
}
}
rc = RPMRC_OK;

exit:
return rc;
}

struct rpmPluginHooks_s unshare_hooks = {
.init = unshare_init,
.cleanup = unshare_cleanup,
.scriptlet_fork_post = unshare_scriptlet_fork_post,
};

0 comments on commit 587e778

Please sign in to comment.