Bug 2176844
| Summary: | fapolicyd can create RPM DB files /var/lib/rpm/__db.xxx` with bad ownership causing AVCs to occur | ||
|---|---|---|---|
| Product: | Red Hat Enterprise Linux 8 | Reporter: | Renaud Métrich <rmetrich> |
| Component: | fapolicyd | Assignee: | Radovan Sroka <rsroka> |
| Status: | CLOSED MIGRATED | QA Contact: | BaseOS QE Security Team <qe-baseos-security> |
| Severity: | medium | Docs Contact: | |
| Priority: | medium | ||
| Version: | 8.7 | CC: | ffesti, mdomonko, pmatilai |
| Target Milestone: | rc | Keywords: | MigratedToJIRA, Triaged |
| Target Release: | --- | Flags: | pm-rhel:
mirror+
|
| Hardware: | All | ||
| OS: | Linux | ||
| Whiteboard: | |||
| Fixed In Version: | Doc Type: | If docs needed, set a value | |
| Doc Text: | Story Points: | --- | |
| Clone Of: | Environment: | ||
| Last Closed: | 2023-07-20 12:17:34 UTC | Type: | Bug |
| Regression: | --- | Mount Type: | --- |
| Documentation: | --- | CRM: | |
| Verified Versions: | Category: | --- | |
| oVirt Team: | --- | RHEL 7.3 requirements from Atomic Host: | |
| Cloudforms Team: | --- | Target Upstream Version: | |
| Embargoed: | |||
@mdomonko @ffesti @pmatilai can you look at this? Fapolicyd is using librpm. It is iterating over all the files in installed packages. We don't want to write anything but there are some new files created by librpm under fapolicyd user and group. https://github.com/linux-application-whitelisting/fapolicyd/blob/main/src/library/rpm-backend.c How can we prevent such a behavior? These files are BerkeleyDB "shared regions" [1]. A quick look reveals that the use of rpmtsInitIterator() in rpm-backend.c leads to rpmtsOpenDB() in librpm which may explain where those files are being created. This likely isn't fapolicyd specific, the same could happen with any librpm application. It seems like an implementation detail of librpm (or BDB for that matter), not something that the calling application should need to care about so it remains to be seen whether it's just the librpm API lacking here or a straight up bug. Florian, Panu, any thoughts? [1] https://docs.oracle.com/cd/E17275_01/html/programmer_reference/env_region.html And by "bug", I mean the fact that there can be files in the /var/lib/rpm directory that are created as a result of an librpm API call and that are owned by the user/group running the process, not root. That's supposed to be a "can't happen" because /var/lib/rpm is supposed to be owned by root:root and nobody else can write there. What does 'ls -ld /var/lib/rpm/' say? If there's something loosening up the rpmdb directory permissions then that's pretty severe. Top directory permissions are correct:
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
[root@vm-stig8 ~]# ls -ld /var/lib/rpm
drwxr-xr-x. 2 root root 4096 Mar 10 12:49 /var/lib/rpm
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
Through stracing (in Permissive or else ...), I can see that capabilities are kept before switching user:
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
1738 12:55:13.504793 prctl(PR_SET_KEEPCAPS, 1) = 0 <0.000010>
1738 12:55:13.504830 capset({version=_LINUX_CAPABILITY_VERSION_3, pid=1738}, {effective=1<<CAP_DAC_OVERRIDE|1<<CAP_SETGID|1<<CAP_SETUID|1<<CAP_SYS_PTRACE|1<<CAP_SYS_ADMIN|1<<CAP_SYS_NICE|1<<CAP_SYS_RESOURCE|1<<CAP_AUDIT_WRITE, permitted=1<<CAP_DAC_OVERRIDE|1<<CAP_SETGID|1<<CAP_SETUID|1<<CAP_SYS_PTRACE|1<<CAP_SYS_ADMIN|1<<CAP_SYS_NICE|1<<CAP_SYS_RESOURCE|1<<CAP_AUDIT_WRITE, inheritable=0}) = 0 <0.000008>
1738 12:55:13.504864 setresgid(991, 991, 991) = 0 <0.000007>
1738 12:55:13.504892 setgroups(0, NULL) = 0 <0.000010>
1738 12:55:13.504921 setresuid(995, 995, 995) = 0 <0.000010>
1738 12:55:13.504949 prctl(PR_SET_KEEPCAPS, 0) = 0 <0.000006>
1738 12:55:13.504974 capset({version=_LINUX_CAPABILITY_VERSION_3, pid=1738}, {effective=1<<CAP_DAC_OVERRIDE|1<<CAP_SYS_PTRACE|1<<CAP_SYS_ADMIN|1<<CAP_SYS_NICE|1<<CAP_SYS_RESOURCE|1<<CAP_AUDIT_WRITE, permitted=1<<CAP_DAC_OVERRIDE|1<<CAP_SYS_PTRACE|1<<CAP_SYS_ADMIN|1<<CAP_SYS_NICE|1<<CAP_SYS_RESOURCE|1<<CAP_AUDIT_WRITE, inheritable=0}) = 0 <0.000006>
:
1738 12:55:13.517812 openat(AT_FDCWD</>, "/var/lib/rpm/__db.001", O_RDWR|O_CREAT|O_EXCL, 0644 <unfinished ...>
1738 12:55:13.517898 <... openat resumed>) = 8</var/lib/rpm/__db.001> <0.000069>
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
Using systemtap, I could find that capset() is called by:
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
(gdb) bt
#0 0x00007f73e16510bb in capset () from /lib64/libc.so.6
#1 0x00007f73e2839dbb in capng_apply () from /lib64/libcap-ng.so.0
#2 0x00007f73e2839ee5 in capng_change_id () from /lib64/libcap-ng.so.0
#3 0x000055d2d240a5e8 in main (argc=<optimized out>, argv=<optimized out>) at daemon/fapolicyd.c:493
(gdb) f 3
#3 0x000055d2d240a5e8 in main (argc=<optimized out>, argv=<optimized out>) at daemon/fapolicyd.c:493
493 if (capng_change_id(config.uid, config.gid,
(gdb) list
488 if (config.uid != 0) {
489 capng_clear(CAPNG_SELECT_BOTH);
490 capng_updatev(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED,
491 CAP_DAC_OVERRIDE, CAP_SYS_ADMIN, CAP_SYS_PTRACE,
492 CAP_SYS_NICE, CAP_SYS_RESOURCE, CAP_AUDIT_WRITE, -1);
493 if (capng_change_id(config.uid, config.gid,
494 CAPNG_DROP_SUPP_GRP)) {
495 msg(LOG_ERR, "Cannot change to uid %d", config.uid);
496 exit(1);
497 } else
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
This DAC_OVERRIDE makes sense to be able to check files from everybody, but at the time RPMDB is read, it should not have the capability enabled. (In reply to Panu Matilainen from comment #4) > That's supposed to be a "can't happen" because /var/lib/rpm is supposed to > be owned by root:root and nobody else can write there. > > What does 'ls -ld /var/lib/rpm/' say? If there's something loosening up the > rpmdb directory permissions then that's pretty severe. Fapolicyd has and needs CAP_DAC_OVERRIDE capability that's why files are created there... Would it make sense to implement a possibility to pass a flag to librpm that we don't want to create anything? Unfortunately not. BDB needs to create those __db.* environment files for shared locking, even if for read-only access. Regular user queries are forced to run in private locking mode because they can't create or write to those files, and regularly produce garbage because of that. Which is something you don't want in fapolicyd. So it's a damned if you do, damned if you don't situation. Do you think it's possible to reproduce also in RHEL9? I mean whether BDB can be used there? No, RHEL9 only has minimal readonly bdb-support which doesn't create such files. I've created fapolicyd downstream workaround for rhel8. It corrects those files with chown. What do you think?
diff -up ./src/daemon/fapolicyd.c.librpm-workaround ./src/daemon/fapolicyd.c
--- ./src/daemon/fapolicyd.c.librpm-workaround 2023-07-10 11:19:19.507044648 +0200
+++ ./src/daemon/fapolicyd.c 2023-07-10 11:19:19.509044621 +0200
@@ -572,7 +572,7 @@ int main(int argc, const char *argv[])
capng_clear(CAPNG_SELECT_BOTH);
capng_updatev(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED,
CAP_DAC_OVERRIDE, CAP_SYS_ADMIN, CAP_SYS_PTRACE,
- CAP_SYS_NICE, CAP_SYS_RESOURCE, CAP_AUDIT_WRITE, -1);
+ CAP_SYS_NICE, CAP_SYS_RESOURCE, CAP_AUDIT_WRITE, CAP_CHOWN, -1);
if (capng_change_id(config.uid, config.gid,
CAPNG_DROP_SUPP_GRP)) {
msg(LOG_ERR, "Cannot change to uid %d", config.uid);
diff -up ./src/library/rpm-backend.c.librpm-workaround ./src/library/rpm-backend.c
--- ./src/library/rpm-backend.c.librpm-workaround 2023-06-15 16:45:14.000000000 +0200
+++ ./src/library/rpm-backend.c 2023-07-10 11:22:07.066794595 +0200
@@ -32,7 +32,12 @@
#include <rpm/rpmdb.h>
#include <rpm/rpmpgp.h>
#include <fnmatch.h>
+#include <glob.h>
+#include <pwd.h>
+#include <grp.h>
+#include <fcntl.h>
+#include <unistd.h>
#include <uthash.h>
#include "message.h"
@@ -59,6 +64,50 @@ backend rpm_backend =
static rpmts ts = NULL;
static rpmdbMatchIterator mi = NULL;
+static void fix_files(void)
+{
+ glob_t glob_result;
+ const char *pattern = "/var/lib/rpm/__*";
+
+ struct passwd * usr = getpwnam("fapolicyd");
+ if (usr == NULL) {
+ return;
+ }
+
+ struct group * grp = getgrnam("fapolicyd");
+ if (grp == NULL) {
+ return;
+ }
+
+ int return_value = glob(pattern, 0, NULL, &glob_result);
+ if (return_value != 0) {
+ return;
+ }
+
+ for (int i = 0; i < glob_result.gl_pathc; ++i) {
+
+ int fd = open(glob_result.gl_pathv[i], O_NOFOLLOW);
+
+ if (fd == -1)
+ continue;
+
+ struct stat file_stat;
+ if (fstat(fd, &file_stat) != 0) {
+ continue;
+ }
+
+ if (file_stat.st_uid == usr->pw_uid &&
+ file_stat.st_gid == grp->gr_gid) {
+
+ fchown(fd, 0, 0);
+ }
+
+ close(fd);
+ }
+
+ globfree(&glob_result);
+}
+
static int init_rpm(void)
{
return rpmReadConfigFiles ((const char *)NULL, (const char *)NULL);
@@ -201,8 +250,13 @@ static int rpm_load_list(const conf_t *c
return rc;
}
+ int fixed = 0;
// Loop across the rpm database
while (get_next_package_rpm()) {
+ if (!fixed) {
+ fixed = 1;
+ fix_files();
+ }
// Loop across the packages
while (get_next_file_rpm()) {
// We do not want directories or symlinks in the
This bug is going to be migrated. Contact point for migration questions or issues: rsroka Guidance for Bugzilla users to test their Jira account or create one if needed: https://redhat.service-now.com/help?id=kb_article_view&sysparm_article=KB0016394 https://redhat.service-now.com/help?id=kb_article_view&sysparm_article=KB0016694 https://redhat.service-now.com/help?id=kb_article_view&sysparm_article=KB0016774 |
Description of problem: Because fapolicyd executes with fapolicyd:fapolicyd user/group, it may happen that upon start, fapolicyd creates RPM DB files and owns them: -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< -------- # ls -ld /var/lib/rpm/__* -rw-r-----. 1 fapolicyd fapolicyd 286720 Mar 9 14:13 /var/lib/rpm/__db.001 -rw-r-----. 1 fapolicyd fapolicyd 90112 Mar 9 14:13 /var/lib/rpm/__db.002 -rw-r-----. 1 fapolicyd fapolicyd 1318912 Mar 9 14:13 /var/lib/rpm/__db.003 -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< -------- This leads to other services, such as *rhsmcertd* to throw AVCs: -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< -------- ... type=PROCTITLE msg=audit(02/22/2023 22:01:44.253:71037) : proctitle=/usr/libexec/platform-python /usr/libexec/rhsmcertd-worker ... type=PATH msg=audit(02/22/2023 22:01:44.253:71037) : item=0 name=/var/lib/rpm/__db.001 inode=135 dev=fd:04 mode=file,640 ouid=fapolicyd ogid=fapolicyd rdev=00:00 obj=system_u:object_r:rpm_var_lib_t:s0 nametype=NORMAL cap_fp=none cap_fi=none cap_fe=0 cap_fver=0 cap_frootid=0 ... type=CWD msg=audit(02/22/2023 22:01:44.253:71037) : cwd=/ ... type=SYSCALL msg=audit(02/22/2023 22:01:44.253:71037) : arch=x86_64 syscall=openat success=no exit=EACCES(Permission denied) a0=0xffffff9c a1=0x55b6c41d8650 a2=O_RDWR a3=0x0 items=1 ppid=1831 pid=305395 auid=unset uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=(none) ses=unset comm=rhsmcertd-worke exe=/usr/libexec/platform-python3.6 subj=system_u:system_r:rhsmcertd_t:s0 key=(null) ... type=AVC msg=audit(02/22/2023 22:01:44.253:71037) : avc: denied { dac_override } for pid=305395 comm=rhsmcertd-worke capability=dac_override scontext=system_u:system_r:rhsmcertd_t:s0 tcontext=system_u:system_r:rhsmcertd_t:s0 tclass=capability permissive=0 -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< -------- Additionally, "rpm -V" then complains because it's not in accordance with expected permissions and owner: -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< -------- # rpm -V rpm .M...UG.. c /var/lib/rpm/__db.001 .M...UG.. c /var/lib/rpm/__db.002 .M...UG.. c /var/lib/rpm/__db.003 -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< -------- The expected permissions and ownership are 600 / root:root: -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< -------- # ls -ld /var/lib/rpm/__* -rw-------. 1 root root 286720 Mar 9 13:50 /var/lib/rpm/__db.001 -rw-------. 1 root root 90112 Mar 9 13:50 /var/lib/rpm/__db.002 -rw-------. 1 root root 1318912 Mar 9 13:50 /var/lib/rpm/__db.003 -------- 8< ---------------- 8< ---------------- 8< ---------------- 8< -------- Version-Release number of selected component (if applicable): fapolicyd-1.1.3-8.el8.x86_64 and latest fapolicyd-1.1.3-8.el8_7.1.x86_64 How reproducible: Always Steps to Reproduce: 1. Stop fapolicyd service # systemctl stop fapolicyd 2. Delete RPM files # rm /var/lib/rpm/__* 3. Start fapolicyd service # systemctl start fapolicyd 4. Check permissions and ownership Actual results: 640 / fapolicyd:fapolicyd Expected results: 600 / root:root Additional info: The reason for this is execution of rpm command (or librpm, didn't check) internally