From 81602bb83ee7454452b4a18fb8c5c49d4860df5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= Date: Thu, 12 Oct 2023 13:46:06 +0200 Subject: [PATCH] PGP: Set a default creation SELinux labels on GnuPG directories This is another way how to fix mismatching SELinux context on /run/user directories without moving the directories to /run/gnupg/user. librepo used to precreate the directory in /run/user to make sure a GnuPG agent executed by GPGME library places its socket there. The directories there are normally created and removed by systemd (logind PAM session). librepo created them for a case when a package manager is invoked out of systemd session, before the super user logs in. E.g. by a timer job to cache repository metadata. A problem was when this out-of-session process was a SELinux-confined process creating files with its own SELinux label different from a DNF program. Then the directory was created with a SELinux label different from the one expected by systemd and when logging out a corresponding user, the mismatching label clashed with systemd. This patch fixes the issue by choosing a SELinux label of those directories to the label defined in a default SELinux file context database. This patch adds a new -DENABLE_SELINUX=OFF CMake option to disable the new dependency on libselinux. A default behavior is to support SELinux only if GPGME backend is selected with -DUSE_GPGME=ON. https://issues.redhat.com/browse/RHEL-10720 --- CMakeLists.txt | 8 +++++ librepo.spec | 14 ++++++++- librepo/CMakeLists.txt | 3 ++ librepo/gpg_gpgme.c | 71 ++++++++++++++++++++++++++++++++++++++---- 4 files changed, 89 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b8aae8e..6c000241 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ OPTION (WITH_ZCHUNK "Build with zchunk support" ON) OPTION (ENABLE_PYTHON "Build Python bindings" ON) OPTION (USE_GPGME "Use GpgMe (instead of rpm library) for OpenPGP key support" ON) OPTION (USE_RUN_GNUPG_USER_SOCKET "Create a directory for gpg-agent socket in /run/gnugp/user (instead of /run/user)" OFF) +OPTION (ENABLE_SELINUX "Restore SELinux labels on GnuPG directories" ON) INCLUDE (${CMAKE_SOURCE_DIR}/VERSION.cmake) SET (VERSION "${LIBREPO_MAJOR}.${LIBREPO_MINOR}.${LIBREPO_PATCH}") @@ -37,6 +38,9 @@ FIND_PACKAGE(CURL 7.52.0 REQUIRED) IF (USE_GPGME) FIND_PACKAGE(Gpgme REQUIRED) + IF (ENABLE_SELINUX) + PKG_CHECK_MODULES(SELINUX REQUIRED libselinux) + ENDIF(ENABLE_SELINUX) ELSE (USE_GPGME) PKG_CHECK_MODULES(RPM REQUIRED rpm>=4.18.0) @@ -93,6 +97,10 @@ ENDIF (NOT CURL_FOUND) INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIRS}) INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR}) #INCLUDE_DIRECTORIES(${CHECK_INCLUDE_DIR}) +IF (USE_GPGME AND ENABLE_SELINUX) + INCLUDE_DIRECTORIES(${SELINUX_INCLUDE_DIRS}) + ADD_DEFINITIONS(-DENABLE_SELINUX=1) +ENDIF (USE_GPGME AND ENABLE_SELINUX) include (GNUInstallDirs) # Python stuff diff --git a/librepo.spec b/librepo.spec index b8c81c1f..c8a635b9 100644 --- a/librepo.spec +++ b/librepo.spec @@ -10,13 +10,21 @@ %if 0%{?fedora} >= 39 %bcond_with use_gpgme +%bcond_with use_selinux %else %bcond_without use_gpgme +%bcond_without use_selinux %endif # Needs to match how gnupg2 is compiled %bcond_with run_gnupg_user_socket +%if %{with use_gpgme} && %{with use_selinux} +%global need_selinux 1 +%else +%global need_selinux 0 +%endif + %global dnf_conflict 2.8.8 Name: librepo @@ -42,6 +50,9 @@ BuildRequires: libattr-devel BuildRequires: libcurl-devel >= %{libcurl_version} BuildRequires: pkgconfig(libxml-2.0) BuildRequires: pkgconfig(libcrypto) +%if %{need_selinux} +BuildRequires: pkgconfig(libselinux) +%endif BuildRequires: pkgconfig(openssl) %if %{with zchunk} BuildRequires: pkgconfig(zck) >= 0.9.11 @@ -82,7 +93,8 @@ Python 3 bindings for the librepo library. %cmake \ -DWITH_ZCHUNK=%{?with_zchunk:ON}%{!?with_zchunk:OFF} \ -DUSE_GPGME=%{?with_use_gpgme:ON}%{!?with_use_gpgme:OFF} \ - -DUSE_RUN_GNUPG_USER_SOCKET=%{?with_run_gnupg_user_socket:ON}%{!?with_run_gnupg_user_socket:OFF} + -DUSE_RUN_GNUPG_USER_SOCKET=%{?with_run_gnupg_user_socket:ON}%{!?with_run_gnupg_user_socket:OFF} \ + -DENABLE_SELINUX=%{?need_selinux:ON}%{!?need_selinux:OFF} %cmake_build %check diff --git a/librepo/CMakeLists.txt b/librepo/CMakeLists.txt index 92dd721e..5efb4f83 100644 --- a/librepo/CMakeLists.txt +++ b/librepo/CMakeLists.txt @@ -71,6 +71,9 @@ TARGET_LINK_LIBRARIES(librepo ) IF (USE_GPGME) TARGET_LINK_LIBRARIES(librepo ${GPGME_VANILLA_LIBRARIES}) + IF (ENABLE_SELINUX) + TARGET_LINK_LIBRARIES(librepo ${SELINUX_LIBRARIES}) + ENDIF(ENABLE_SELINUX) ELSE(USE_GPGME) TARGET_LINK_LIBRARIES(librepo ${RPM_LIBRARIES}) ENDIF (USE_GPGME) diff --git a/librepo/gpg_gpgme.c b/librepo/gpg_gpgme.c index 84f422e4..c4addb21 100644 --- a/librepo/gpg_gpgme.c +++ b/librepo/gpg_gpgme.c @@ -28,6 +28,11 @@ #include #include +#if ENABLE_SELINUX +#include +#include +#endif + #include "gpg.h" #include "gpg_internal.h" #include "rcodes.h" @@ -45,10 +50,14 @@ * Previous solution was to send the agent a "KILLAGENT" message, but that * would cause a race condition with calling gpgme_release(), see [2], [3]. * - * Another previous solution used /run/user/$UID which showed problematic when - * this library was used out of an systemd-logind session. Then /run/user/$UID, - * normally maintained by systemd, was assigned a SELinux label unexpected by - * systemd causing errors on a user logout [4]. + * Current solution with precreating /run/user/$UID showed problematic when + * this library was used out of an systemd-logind session. Then + * /run/user/$UID, normally maintained by systemd, was assigned a SELinux + * label unexpected by systemd causing errors on a user logout [4]. + * + * We remedy it by choosing the label according to a default file context + * policy (ENABLE_SELINUX macro) or by using a different path supported by + * some GnuPG configurations (DUSE_RUN_GNUPG_USER_SOCKET macro). * * Since the agent doesn't clean up its sockets properly, by creating this * directory we make sure they are in a place that is not causing trouble with @@ -72,19 +81,69 @@ lr_gpg_ensure_socket_dir_exists() const uid_t uid = getuid(); char dirname[32]; int res; +#if ENABLE_SELINUX + char *old_default_context = NULL; + int old_default_context_was_retrieved = 0; + struct selabel_handle *labeling_handle = NULL; + + /* A purpose of this piece of code is to deal with applications whose + * security policy overrides a file context for temporary files but don't + * know that librepo executes GnuPG which expects a default file context. */ + if (0 == getfscreatecon(&old_default_context)) { + old_default_context_was_retrieved = 1; + } else { + g_debug("Failed to retrieve a default SELinux context"); + } + labeling_handle = selabel_open(SELABEL_CTX_FILE, NULL, 0); + if (labeling_handle == NULL) { + g_debug("Failed to open a SELinux labeling handle: %s", strerror(errno)); + } +#endif for (int i = 0; templates[i] != NULL; i++) { res = snprintf(dirname, sizeof(dirname), templates[i], (uintmax_t)uid); if (res >= sizeof(dirname)) { g_debug("Failed to format a GnuPG agent socket path because of a small buffer"); - return; + goto exit; } + +#if ENABLE_SELINUX + if (labeling_handle != NULL) { + char *new_default_context = NULL; + if (selabel_lookup(labeling_handle, &new_default_context, dirname, modes[i])) { + /* Here we could hard-code "system_u:object_r:user_tmp_t:s0", but + * that value should be really defined in default file context + * SELinux policy. Only log that the policy is incomplete. */ + g_debug("Failed to look up a default SELinux label for \"%s\"", dirname); + } else { + if (setfscreatecon(new_default_context)) { + g_debug("Failed to set default SELinux context to \"%s\"", + new_default_context); + } + freecon(new_default_context); + } + } +#endif + res = mkdir(dirname, modes[i]); if (res != 0 && errno != EEXIST) { g_debug("Failed to create \"%s\": %d - %s\n", dirname, errno, strerror(errno)); - return; + goto exit; + } + } + +exit: +#if ENABLE_SELINUX + if (labeling_handle != NULL) { + selabel_close(labeling_handle); + } + if (old_default_context_was_retrieved) { + if (setfscreatecon(old_default_context)) { + g_debug("Failed to restore a default SELinux context"); } } + freecon(old_default_context); +#endif } static gpgme_ctx_t