a5e32e
commit 39595ccee321497dc3b08c7cab8a10304345429c
a5e32e
Author: Radovan Sroka <rsroka@redhat.com>
a5e32e
Date:   Tue Oct 27 16:18:04 2020 +0100
a5e32e
a5e32e
    Added fapolicyd rpm plugin
a5e32e
    
a5e32e
    Fapolicyd (File Access Policy Daemon) implements application whitelisting
a5e32e
    to decide file access rights. Applications that are known via a reputation
a5e32e
    source are allowed access while unknown applications are not.
a5e32e
    
a5e32e
    The rpm plugin allows us to use rpm database as a source of trust.
a5e32e
    We used dnf plugin since the beggining but it only provides notification
a5e32e
    when transaction ends. With "integrity checking" requirement we need
a5e32e
    a continual addition of files which are installed during the system
a5e32e
    update. With fapolicyd rpm plugin we can allow using of recently
a5e32e
    added/updated files in scriptlets during rpm transaction.
a5e32e
    
a5e32e
    The fapolicyd plugin gathers metadata of currently installed files.
a5e32e
    It sends the information about files and about ongoing rpm transaction
a5e32e
    to the fapolicyd daemon. The information is written to Linux pipe which
a5e32e
    is placed in /var/run/fapolicyd/fapolicyd.fifo.
a5e32e
    
a5e32e
    The data format is "%s %lu %64s\n". [path, size, sha256]
a5e32e
    
a5e32e
    The fapolicyd rpm plugin can be enabled with "--with-fapolicyd"
a5e32e
    configure option.
a5e32e
    
a5e32e
    Related PRs:
a5e32e
    https://github.com/linux-application-whitelisting/fapolicyd/pull/105
a5e32e
    https://github.com/linux-application-whitelisting/fapolicyd/pull/106
a5e32e
    
a5e32e
    Signed-off-by: Radovan Sroka <rsroka@redhat.com>
a5e32e
a5e32e
    Backported into 4.16.1.3, together with commit
a5e32e
    6d61b7118adcc14631b7ee5163a481472af940b8 (covscan fix)
a5e32e
a5e32e
diff -up rpm-4.16.1.3/configure.ac.orig rpm-4.16.1.3/configure.ac
a5e32e
--- rpm-4.16.1.3/configure.ac.orig	2021-03-22 11:05:07.311635968 +0100
a5e32e
+++ rpm-4.16.1.3/configure.ac	2021-07-22 16:18:29.352006782 +0200
a5e32e
@@ -891,6 +891,14 @@ AS_IF([test "$enable_plugins" != no],[
a5e32e
 AM_CONDITIONAL(IMA, [test "x$ac_cv_func_lsetxattr" = xyes])
a5e32e
 
a5e32e
 #=================
a5e32e
+# Check for fapolicyd support
a5e32e
+AC_ARG_WITH(fapolicyd,
a5e32e
+AS_HELP_STRING([--with-fapolicyd],[build with File Access Policy Daemon support]),
a5e32e
+with_fapolicyd=$withval,
a5e32e
+with_fapolicyd=auto)
a5e32e
+AM_CONDITIONAL(FAPOLICYD,[test "$with_fapolicyd" = yes])
a5e32e
+
a5e32e
+#=================
a5e32e
 # Check for audit library.
a5e32e
 AC_ARG_WITH(audit,
a5e32e
 AS_HELP_STRING([--with-audit],[Linux audit plugin]),
a5e32e
diff -up rpm-4.16.1.3/doc/Makefile.am.orig rpm-4.16.1.3/doc/Makefile.am
a5e32e
--- rpm-4.16.1.3/doc/Makefile.am.orig	2020-06-23 14:13:01.895628382 +0200
a5e32e
+++ rpm-4.16.1.3/doc/Makefile.am	2021-07-22 16:18:29.352006782 +0200
a5e32e
@@ -25,6 +25,9 @@ endif
a5e32e
 if IMA
a5e32e
 man_man8_DATA += rpm-plugin-ima.8
a5e32e
 endif
a5e32e
+if FAPOLICYD
a5e32e
+man_man8_DATA += rpm-plugin-fapolicyd.8
a5e32e
+endif
a5e32e
 if SELINUX
a5e32e
 man_man8_DATA += rpm-plugin-selinux.8
a5e32e
 endif
a5e32e
@@ -37,6 +40,8 @@ endif
a5e32e
 EXTRA_DIST += rpm-plugins.8 rpm-plugin-prioreset.8 rpm-plugin-syslog.8 
a5e32e
 EXTRA_DIST += rpm-plugin-audit.8 rpm-plugin-systemd-inhibit.8 
a5e32e
 EXTRA_DIST += rpm-plugin-ima.8 rpm-plugin-selinux.8 rpm2archive.8
a5e32e
+EXTRA_DIST += rpm-plugin-fapolicyd.8
a5e32e
+
a5e32e
 
a5e32e
 man_fr_man8dir = $(mandir)/fr/man8
a5e32e
 man_fr_man8_DATA = fr/rpm.8
a5e32e
diff -up rpm-4.16.1.3/doc/rpm-plugin-fapolicyd.8.orig rpm-4.16.1.3/doc/rpm-plugin-fapolicyd.8
a5e32e
--- rpm-4.16.1.3/doc/rpm-plugin-fapolicyd.8.orig	2021-07-22 16:18:29.353006800 +0200
a5e32e
+++ rpm-4.16.1.3/doc/rpm-plugin-fapolicyd.8	2021-07-22 16:18:29.353006800 +0200
a5e32e
@@ -0,0 +1,21 @@
a5e32e
+'\" t
a5e32e
+.TH "RPM-FAPOLICYD" "8" "28 Jan 2021" "Red Hat, Inc."
a5e32e
+.SH NAME
a5e32e
+rpm-plugin-fapolicyd \- Fapolicyd plugin for the RPM Package Manager
a5e32e
+
a5e32e
+.SH Description
a5e32e
+
a5e32e
+The plugin gathers metadata of currently installed files. It sends the
a5e32e
+information about files and about ongoing rpm transaction to the fapolicyd daemon.
a5e32e
+The information is written to Linux pipe which is placed in
a5e32e
+/var/run/fapolicyd/fapolicyd.fifo.
a5e32e
+
a5e32e
+.SH Configuration
a5e32e
+
a5e32e
+There are currently no options for this plugin in particular. See
a5e32e
+.BR rpm-plugins (8)
a5e32e
+on how to control plugins in general.
a5e32e
+
a5e32e
+.SH SEE ALSO
a5e32e
+.IR fapolicyd (8)
a5e32e
+.IR rpm-plugins (8)
a5e32e
diff -up rpm-4.16.1.3/macros.in.orig rpm-4.16.1.3/macros.in
a5e32e
--- rpm-4.16.1.3/macros.in.orig	2021-07-22 16:18:20.525844141 +0200
a5e32e
+++ rpm-4.16.1.3/macros.in	2021-07-22 16:19:36.196238525 +0200
a5e32e
@@ -1208,6 +1208,7 @@ package or when debugging this package.\
a5e32e
 %__transaction_selinux		%{__plugindir}/selinux.so
a5e32e
 %__transaction_syslog		%{__plugindir}/syslog.so
a5e32e
 %__transaction_ima		%{__plugindir}/ima.so
a5e32e
+%__transaction_fapolicyd	%{__plugindir}/fapolicyd.so
a5e32e
 %__transaction_prioreset	%{__plugindir}/prioreset.so
a5e32e
 %__transaction_audit		%{__plugindir}/audit.so
a5e32e
 
a5e32e
diff -up rpm-4.16.1.3/Makefile.am.orig rpm-4.16.1.3/Makefile.am
a5e32e
--- rpm-4.16.1.3/Makefile.am.orig	2021-07-22 16:18:29.350006745 +0200
a5e32e
+++ rpm-4.16.1.3/Makefile.am	2021-07-22 16:19:18.223907346 +0200
a5e32e
@@ -14,6 +14,7 @@ DISTCHECK_CONFIGURE_FLAGS = \
a5e32e
 	--with-audit \
a5e32e
 	--with-selinux \
a5e32e
 	--with-imaevm \
a5e32e
+	--with-fapolicyd \
a5e32e
 	--disable-dependency-tracking
a5e32e
 
a5e32e
 include $(top_srcdir)/rpm.am
a5e32e
diff -up rpm-4.16.1.3/plugins/fapolicyd.c.orig rpm-4.16.1.3/plugins/fapolicyd.c
a5e32e
--- rpm-4.16.1.3/plugins/fapolicyd.c.orig	2021-07-22 16:18:29.356006855 +0200
a5e32e
+++ rpm-4.16.1.3/plugins/fapolicyd.c	2021-07-22 16:18:35.380117862 +0200
a5e32e
@@ -0,0 +1,191 @@
a5e32e
+#include "system.h"
a5e32e
+
a5e32e
+#include <rpm/rpmts.h>
a5e32e
+#include <rpm/rpmlog.h>
a5e32e
+#include "lib/rpmplugin.h"
a5e32e
+
a5e32e
+#include <fcntl.h>
a5e32e
+#include <errno.h>
a5e32e
+#include <unistd.h>
a5e32e
+#include <sys/stat.h>
a5e32e
+
a5e32e
+struct fapolicyd_data {
a5e32e
+    int fd;
a5e32e
+    long changed_files;
a5e32e
+    const char * fifo_path;
a5e32e
+};
a5e32e
+
a5e32e
+static struct fapolicyd_data fapolicyd_state = {
a5e32e
+    .fd = -1,
a5e32e
+    .changed_files = 0,
a5e32e
+    .fifo_path = "/run/fapolicyd/fapolicyd.fifo",
a5e32e
+};
a5e32e
+
a5e32e
+static rpmRC open_fifo(struct fapolicyd_data* state)
a5e32e
+{
a5e32e
+    int fd = -1;
a5e32e
+    struct stat s;
a5e32e
+
a5e32e
+    fd = open(state->fifo_path, O_RDWR);
a5e32e
+    if (fd == -1) {
a5e32e
+        rpmlog(RPMLOG_DEBUG, "Open: %s -> %s\n", state->fifo_path, strerror(errno));
a5e32e
+        goto bad;
a5e32e
+    }
a5e32e
+
a5e32e
+    if (stat(state->fifo_path, &s) == -1) {
a5e32e
+        rpmlog(RPMLOG_DEBUG, "Stat: %s -> %s\n", state->fifo_path, strerror(errno));
a5e32e
+        goto bad;
a5e32e
+    }
a5e32e
+
a5e32e
+    if (!S_ISFIFO(s.st_mode)) {
a5e32e
+        rpmlog(RPMLOG_DEBUG, "File: %s exists but it is not a pipe!\n", state->fifo_path);
a5e32e
+        goto bad;
a5e32e
+    }
a5e32e
+
a5e32e
+    /* keep only file's permition bits */
a5e32e
+    mode_t mode = s.st_mode & ~S_IFMT;
a5e32e
+
a5e32e
+    /* we require pipe to have 0660 permission */
a5e32e
+    if (mode != 0660) {
a5e32e
+        rpmlog(RPMLOG_ERR, "File: %s has %o instead of 0660 \n",
a5e32e
+               state->fifo_path,
a5e32e
+               mode );
a5e32e
+        goto bad;
a5e32e
+    }
a5e32e
+
a5e32e
+    state->fd = fd;
a5e32e
+    /* considering success */
a5e32e
+    return RPMRC_OK;
a5e32e
+
a5e32e
+ bad:
a5e32e
+    if (fd >= 0)
a5e32e
+        close(fd);
a5e32e
+    return RPMRC_FAIL;
a5e32e
+}
a5e32e
+
a5e32e
+static rpmRC write_fifo(struct fapolicyd_data* state, const char * str)
a5e32e
+{
a5e32e
+    ssize_t len = strlen(str);
a5e32e
+    ssize_t written = 0;
a5e32e
+    ssize_t n = 0;
a5e32e
+
a5e32e
+    while (written < len) {
a5e32e
+        if ((n = write(state->fd, str + written, len - written)) < 0) {
a5e32e
+            if (errno == EINTR || errno == EAGAIN)
a5e32e
+                continue;
a5e32e
+            rpmlog(RPMLOG_DEBUG, "Write: %s -> %s\n", state->fifo_path, strerror(errno));
a5e32e
+            goto bad;
a5e32e
+        }
a5e32e
+        written += n;
a5e32e
+    }
a5e32e
+
a5e32e
+    return RPMRC_OK;
a5e32e
+
a5e32e
+ bad:
a5e32e
+    return RPMRC_FAIL;
a5e32e
+}
a5e32e
+
a5e32e
+static rpmRC fapolicyd_init(rpmPlugin plugin, rpmts ts)
a5e32e
+{
a5e32e
+    if (rpmtsFlags(ts) & (RPMTRANS_FLAG_TEST|RPMTRANS_FLAG_BUILD_PROBS))
a5e32e
+        goto end;
a5e32e
+
a5e32e
+    if (!rstreq(rpmtsRootDir(ts), "/"))
a5e32e
+        goto end;
a5e32e
+
a5e32e
+    (void) open_fifo(&fapolicyd_state);
a5e32e
+
a5e32e
+ end:
a5e32e
+    return RPMRC_OK;
a5e32e
+}
a5e32e
+
a5e32e
+static void fapolicyd_cleanup(rpmPlugin plugin)
a5e32e
+{
a5e32e
+    if (fapolicyd_state.fd > 0)
a5e32e
+        (void) close(fapolicyd_state.fd);
a5e32e
+
a5e32e
+    fapolicyd_state.fd = -1;
a5e32e
+}
a5e32e
+
a5e32e
+static rpmRC fapolicyd_tsm_post(rpmPlugin plugin, rpmts ts, int res)
a5e32e
+{
a5e32e
+    if (rpmtsFlags(ts) & (RPMTRANS_FLAG_TEST|RPMTRANS_FLAG_BUILD_PROBS))
a5e32e
+        goto end;
a5e32e
+
a5e32e
+    /* we are ready */
a5e32e
+    if (fapolicyd_state.fd > 0) {
a5e32e
+        /* send a signal that transaction is over */
a5e32e
+        (void) write_fifo(&fapolicyd_state, "1\n");
a5e32e
+        /* flush cache */
a5e32e
+        (void) write_fifo(&fapolicyd_state, "2\n");
a5e32e
+    }
a5e32e
+
a5e32e
+ end:
a5e32e
+    return RPMRC_OK;
a5e32e
+}
a5e32e
+
a5e32e
+static rpmRC fapolicyd_scriptlet_pre(rpmPlugin plugin, const char *s_name,
a5e32e
+                                     int type)
a5e32e
+{
a5e32e
+    if (fapolicyd_state.fd == -1)
a5e32e
+        goto end;
a5e32e
+
a5e32e
+    if (fapolicyd_state.changed_files > 0) {
a5e32e
+        /* send signal to flush cache */
a5e32e
+        (void) write_fifo(&fapolicyd_state, "2\n");
a5e32e
+
a5e32e
+        /* optimize flushing */
a5e32e
+        /* flush only when there was an actual change */
a5e32e
+        fapolicyd_state.changed_files = 0;
a5e32e
+    }
a5e32e
+
a5e32e
+ end:
a5e32e
+    return RPMRC_OK;
a5e32e
+}
a5e32e
+
a5e32e
+static rpmRC fapolicyd_fsm_file_prepare(rpmPlugin plugin, rpmfi fi,
a5e32e
+                                        const char *path, const char *dest,
a5e32e
+                                        mode_t file_mode, rpmFsmOp op)
a5e32e
+{
a5e32e
+    /* not ready  */
a5e32e
+    if (fapolicyd_state.fd == -1)
a5e32e
+        goto end;
a5e32e
+
a5e32e
+    rpmFileAction action = XFO_ACTION(op);
a5e32e
+
a5e32e
+    /* Ignore skipped files and unowned directories */
a5e32e
+    if (XFA_SKIPPING(action) || (op & FAF_UNOWNED)) {
a5e32e
+        rpmlog(RPMLOG_DEBUG, "fapolicyd skipping early: path %s dest %s\n",
a5e32e
+               path, dest);
a5e32e
+        goto end;
a5e32e
+    }
a5e32e
+
a5e32e
+    if (!S_ISREG(rpmfiFMode(fi))) {
a5e32e
+        rpmlog(RPMLOG_DEBUG, "fapolicyd skipping non regular: path %s dest %s\n",
a5e32e
+               path, dest);
a5e32e
+        goto end;
a5e32e
+    }
a5e32e
+
a5e32e
+    fapolicyd_state.changed_files++;
a5e32e
+
a5e32e
+    char buffer[4096];
a5e32e
+
a5e32e
+    rpm_loff_t size = rpmfiFSize(fi);
a5e32e
+    char * sha = rpmfiFDigestHex(fi, NULL);
a5e32e
+
a5e32e
+    snprintf(buffer, 4096, "%s %lu %64s\n", dest, size, sha);
a5e32e
+    (void) write_fifo(&fapolicyd_state, buffer);
a5e32e
+
a5e32e
+    free(sha);
a5e32e
+
a5e32e
+ end:
a5e32e
+    return RPMRC_OK;
a5e32e
+}
a5e32e
+
a5e32e
+struct rpmPluginHooks_s fapolicyd_hooks = {
a5e32e
+    .init = fapolicyd_init,
a5e32e
+    .cleanup = fapolicyd_cleanup,
a5e32e
+    .scriptlet_pre = fapolicyd_scriptlet_pre,
a5e32e
+    .tsm_post = fapolicyd_tsm_post,
a5e32e
+    .fsm_file_prepare = fapolicyd_fsm_file_prepare,
a5e32e
+};
a5e32e
diff -up rpm-4.16.1.3/plugins/Makefile.am.orig rpm-4.16.1.3/plugins/Makefile.am
a5e32e
--- rpm-4.16.1.3/plugins/Makefile.am.orig	2021-07-22 16:18:23.022890155 +0200
a5e32e
+++ rpm-4.16.1.3/plugins/Makefile.am	2021-07-22 16:18:55.797494098 +0200
a5e32e
@@ -43,6 +43,12 @@ ima_la_LIBADD = $(top_builddir)/lib/libr
a5e32e
 plugins_LTLIBRARIES += ima.la
a5e32e
 endif
a5e32e
 
a5e32e
+if FAPOLICYD
a5e32e
+fapolicyd_la_sources = fapolicyd.c
a5e32e
+fapolicyd_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la
a5e32e
+plugins_LTLIBRARIES += fapolicyd.la
a5e32e
+endif
a5e32e
+
a5e32e
 if AUDIT
a5e32e
 audit_la_sources = audit.c
a5e32e
 audit_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la @WITH_AUDIT_LIB@