Bug 2229997 - libdnf may create /run/user/0 directory, causing a bad context to be applied, leading to further issues
Summary: libdnf may create /run/user/0 directory, causing a bad context to be applied,...
Keywords:
Status: NEW
Alias: None
Product: Red Hat Enterprise Linux 8
Classification: Red Hat
Component: libdnf
Version: 8.8
Hardware: All
OS: Linux
high
high
Target Milestone: rc
: ---
Assignee: Marek Blaha
QA Contact: swm-qe
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2023-08-08 12:51 UTC by Renaud Métrich
Modified: 2023-08-09 09:44 UTC (History)
0 users

Fixed In Version:
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
Environment:
Last Closed:
Type: Bug
Target Upstream Version:
Embargoed:


Attachments (Terms of Use)


Links
System ID Private Priority Status Summary Last Updated
Red Hat Issue Tracker RHELPLAN-164825 0 None None None 2023-08-08 12:53:19 UTC

Description Renaud Métrich 2023-08-08 12:51:21 UTC
Description of problem:

This is observed on a customer system when insights-client executes.
In this specific case (but there are likely other cases), the /run/user/0 directory gets created with insights_client_tmp_t context, causing systemd's user-runtime-dir unit to fail forever:
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
Aug 08 13:24:22 vm-insights8 systemd[1]: Stopping User runtime directory /run/user/0...
Aug 08 13:24:22 vm-insights8 systemd-user-runtime-dir[34749]: Failed to remove runtime directory /run/user/0 (after unmounting): Permission denied
Aug 08 13:24:22 vm-insights8 systemd[1]: user-runtime-dir: Control process exited, code=exited status=1
Aug 08 13:24:22 vm-insights8 systemd[1]: user-runtime-dir: Failed with result 'exit-code'.
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

The root cause for this is the libdnf code forcibly creates the /run/user/0 directory when trying to import GPG keys:
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
 865 static void ensure_socket_dir_exists() {
 866     auto logger(Log::getLogger());
 867     std::string dirname = "/run/user/" + std::to_string(getuid());
 868     int res = mkdir(dirname.c_str(), 0700);     <<<<<<< HERE
 869     if (res != 0 && errno != EEXIST) {
 870         logger->debug(tfm::format("Failed to create directory \"%s\": %d - %s",
 871                                   dirname, errno, strerror(errno)));
 872     }
 873 }
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

This is not an issue when /run/user/0 already exists since mkdir is then a no-op, but this is not always the case.
For example, when the root user has no session, this is an issue if a libdnf client, such as yum, executes through sudo (e.g. "sudo yum check-update").
This code then creates the directory in the context of the caller, which may be problematic when there is no transition rule in the SELinux policy to automatically create the directory with proper "user_tmp_t" context.

In the case of "insights-client", the context is "insights_client_t", which leads to creating the directory with "insights_client_tmp_t" context, which then prevents systemd from deleting that directory once root user logged in and logged out again.


Version-Release number of selected component (if applicable):

libdnf-0.63.0-14.el8_8.x86_64

How reproducible:

Always when executing in "insights_client_t" contxt

Steps to Reproduce:
1. Setup EPEL and REMI repositories (to get the key for REMI repo imported)

  -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
  # yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
  # yum install https://rpms.remirepo.net/enterprise/remi-release-8.rpm
  # yum clean all
  -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

2. Create a wrapper to execute libdnf as a service running as if it was executed from insights-client

  You need to have insights-client package installed to get access to the context.

  -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
  # cat > /usr/local/bin/fake_insights << EOF
  #!/bin/sh
  exec /usr/libexec/platform-python /usr/bin/yum -y check-update
  EOF

  # chmod +x /usr/local/bin/fake_insights
  # chcon -t insights_client_exec_t /usr/local/bin/fake_insights
  -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

  Note: here above we use "/usr/libexec/platform-python" to avoid the transition to "rpm_t".

3. Create an admin user which will start the wrapper in insights-client context

  -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
  # useradd -G wheel admin
  # echo "redhat" | passwd --stdin admin
  -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

4. Login as "admin" and make sure no root session is present

  -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
  $ ssh admin@system
  $ sudo systemctl stop user-0.slice
  $ ls -Zd /run/user/0
  ls: cannot access '/run/user/0': No such file or directory
  -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

5. Execute "yum check-update" in insights-client context and wait for /run/user/0 to be created

  -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
  $ sudo systemd-run /usr/local/bin/fake_insights
  $ while :; do date; ls -Z /run/user; sleep 1; done
  Tue Aug  8 14:26:07 CEST 2023
  system_u:object_r:user_tmp_t:s0 1000
  [...]
  system_u:object_r:user_tmp_t:s0 1000
  Tue Aug  8 14:26:12 CEST 2023
  system_u:object_r:insights_client_tmp_t:s0 0             system_u:object_r:user_tmp_t:s0 1000
  -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

Actual results:

  Here above the directory got created with bad context.

Expected results:

  No directory /run/user/0 created at all or proper "user_tmp_t" context set.

Additional info:

  You may also reproduce with executing "subscription-manager repos --disable \*" instead of "yum" in the wrapper:
  -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
  # cat > /usr/local/bin/fake_insights << EOF
  #!/bin/sh
  exec subscription-manager repos --disable \*
  EOF

  # chmod +x /usr/local/bin/fake_insights
  # chcon -t insights_client_exec_t /usr/local/bin/fake_insights
  -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

  IMHO it's not up to libdnf to create the /run/user/0 directory but systemd and this should be considered as a systemd-private thing.
  I would expect the GPG command to not populate this directory at all either.
  The comment above function ensure_socket_dir_exists() tends to indicate this thing is a workaround for GPG issues actually.

  Not creating this directory is the only reliable solution to make sure whatever the caller is, this will work.


Note You need to log in before you can comment on or make changes to this bug.