Skip to content

Commit

Permalink
PGP: Set a default creation SELinux labels on GnuPG directories
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ppisar committed Oct 18, 2023
1 parent f4b915b commit d0ad6b7
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 1 deletion.
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ OPTION (ENABLE_TESTS "Build test?" ON)
OPTION (ENABLE_DOCS "Build docs?" ON)
OPTION (WITH_ZCHUNK "Build with zchunk support" ON)
OPTION (ENABLE_PYTHON "Build Python bindings" ON)
OPTION (ENABLE_SELINUX "Restore SELinux labels on GnuPG directories" ON)

INCLUDE (${CMAKE_SOURCE_DIR}/VERSION.cmake)
SET (VERSION "${LIBREPO_MAJOR}.${LIBREPO_MINOR}.${LIBREPO_PATCH}")
Expand Down Expand Up @@ -33,6 +34,9 @@ PKG_SEARCH_MODULE(LIBCRYPTO REQUIRED libcrypto openssl)
PKG_CHECK_MODULES(LIBXML2 libxml-2.0 REQUIRED)
FIND_PACKAGE(CURL 7.52.0 REQUIRED)
FIND_PACKAGE(Gpgme REQUIRED)
IF (ENABLE_SELINUX)
PKG_CHECK_MODULES(SELINUX REQUIRED libselinux)
ENDIF(ENABLE_SELINUX)


IF (WITH_ZCHUNK)
Expand Down Expand Up @@ -63,6 +67,10 @@ ENDIF (NOT CURL_FOUND)
INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIRS})
INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR})
#INCLUDE_DIRECTORIES(${CHECK_INCLUDE_DIR})
IF (ENABLE_SELINUX)
INCLUDE_DIRECTORIES(${SELINUX_INCLUDE_DIRS})
ADD_DEFINITIONS(-DENABLE_SELINUX=1)
ENDIF (ENABLE_SELINUX)

include (GNUInstallDirs)
# Python stuff
Expand Down
9 changes: 8 additions & 1 deletion librepo.spec
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
%bcond_without zchunk
%endif

%bcond_without selinux

%global dnf_conflict 2.8.8

Name: librepo
Expand All @@ -29,6 +31,9 @@ BuildRequires: libattr-devel
BuildRequires: libcurl-devel >= %{libcurl_version}
BuildRequires: pkgconfig(libxml-2.0)
BuildRequires: pkgconfig(libcrypto)
%if %{with selinux}
BuildRequires: pkgconfig(libselinux)
%endif
BuildRequires: pkgconfig(openssl)
%if %{with zchunk}
BuildRequires: pkgconfig(zck) >= 0.9.11
Expand Down Expand Up @@ -66,7 +71,9 @@ Python 3 bindings for the librepo library.
%autosetup -p1

%build
%cmake %{!?with_zchunk:-DWITH_ZCHUNK=OFF}
%cmake \
%{!?with_zchunk:-DWITH_ZCHUNK=OFF} \
-DENABLE_SELINUX=%{?with_selinux:ON}%{!?with_selinux:OFF}
%cmake_build

%check
Expand Down
4 changes: 4 additions & 0 deletions librepo/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ TARGET_LINK_LIBRARIES(librepo
${GPGME_VANILLA_LIBRARIES}
${GLIB2_LIBRARIES}
)
IF (ENABLE_SELINUX)
TARGET_LINK_LIBRARIES(librepo ${SELINUX_LIBRARIES})
ENDIF(ENABLE_SELINUX)

IF (WITH_ZCHUNK)
TARGET_LINK_LIBRARIES(librepo ${ZCHUNKLIB_LIBRARIES})
ENDIF (WITH_ZCHUNK)
Expand Down
64 changes: 64 additions & 0 deletions librepo/gpg.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
#include <gpgme.h>
#include <unistd.h>

#if ENABLE_SELINUX
#include <selinux/selinux.h>
#include <selinux/label.h>
#endif

#include "rcodes.h"
#include "util.h"
#include "gpg.h"
Expand All @@ -44,21 +49,80 @@
* Previous solution was to send the agent a "KILLAGENT" message, but that
* would cause a race condition with calling gpgme_release(), see [2], [3].
*
* 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).
*
* 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
* container images.
*
* [1] https://bugzilla.redhat.com/show_bug.cgi?id=1650266
* [2] https://bugzilla.redhat.com/show_bug.cgi?id=1769831
* [3] https://github.com/rpm-software-management/microdnf/issues/50
* [4] https://issues.redhat.com/browse/RHEL-10720
*/
void ensure_socket_dir_exists() {
char dirname[32];
#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

snprintf(dirname, sizeof(dirname), "/run/user/%u", getuid());

#if ENABLE_SELINUX
if (labeling_handle != NULL) {
char *new_default_context = NULL;
if (selabel_lookup(labeling_handle, &new_default_context, dirname, 0700)) {
/* 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

int res = mkdir(dirname, 0700);
if (res != 0 && errno != EEXIST) {
g_debug("Failed to create \"%s\": %d - %s\n", dirname, errno, strerror(errno));
}

#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
}

gboolean
Expand Down

0 comments on commit d0ad6b7

Please sign in to comment.