summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ci/Dockerfile2
-rw-r--r--configure.ac24
-rw-r--r--macros.in1
-rw-r--r--plugins/Makefile.am6
-rw-r--r--plugins/audit.c100
5 files changed, 133 insertions, 0 deletions
diff --git a/ci/Dockerfile b/ci/Dockerfile
index 15ac99600..04daf7633 100644
--- a/ci/Dockerfile
+++ b/ci/Dockerfile
@@ -30,6 +30,7 @@ RUN dnf -y install \
ima-evm-utils \
libcap-devel \
libacl-devel \
+ audit-libs-devel \
lua-devel readline-devel \
python3-devel \
dbus-devel \
@@ -48,6 +49,7 @@ RUN ./configure \
--with-cap \
--with-acl \
--with-lua \
+ --with-audit \
--enable-python \
--enable-silent-rules
RUN make
diff --git a/configure.ac b/configure.ac
index 9fc9b55c4..99ce7df32 100644
--- a/configure.ac
+++ b/configure.ac
@@ -970,6 +970,30 @@ AS_IF([test "$enable_plugins" != no],[
])
AM_CONDITIONAL(IMA, [test "x$ac_cv_func_lsetxattr" = xyes])
+#=================
+# Check for audit library.
+AC_ARG_WITH(audit,
+AS_HELP_STRING([--with-audit],[Linux audit plugin]),
+with_audit=$withval,
+with_audit=auto)
+
+WITH_AUDIT_LIB=
+AS_IF([test "$enable_plugins" != no],[
+ AS_IF([test "x$with_audit" != xno],[
+ AC_SEARCH_LIBS([audit_open],[audit],[
+ WITH_AUDIT_LIB="$ac_res"
+ AC_DEFINE(WITH_AUDIT, 1, [libaudit support])
+ with_audit=yes
+ ],[
+ if test "x$with_audit" != xauto; then
+ AC_MSG_ERROR([missing audit library])
+ fi
+ ])
+ ])
+])
+AC_SUBST(WITH_AUDIT_LIB)
+AM_CONDITIONAL(AUDIT,[test "$with_audit" = yes])
+
with_dmalloc=no
AC_ARG_WITH(dmalloc, [AS_HELP_STRING([--with-dmalloc],[build with dmalloc debugging support])])
if test "$with_dmalloc" = yes ; then
diff --git a/macros.in b/macros.in
index b40ca07c7..fc587997d 100644
--- a/macros.in
+++ b/macros.in
@@ -1145,6 +1145,7 @@ package or when debugging this package.\
%__transaction_syslog %{__plugindir}/syslog.so
%__transaction_ima %{__plugindir}/ima.so
%__transaction_prioreset %{__plugindir}/prioreset.so
+%__transaction_audit %{__plugindir}/audit.so
#------------------------------------------------------------------------------
# Macros for further automated spec %setup and patch application
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index ab4eee34f..d4ef039ed 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -42,3 +42,9 @@ ima_la_sources = ima.c
ima_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la
plugins_LTLIBRARIES += ima.la
endif
+
+if AUDIT
+audit_la_sources = audit.c
+audit_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la @WITH_AUDIT_LIB@
+plugins_LTLIBRARIES += audit.la
+endif
diff --git a/plugins/audit.c b/plugins/audit.c
new file mode 100644
index 000000000..e2532267d
--- /dev/null
+++ b/plugins/audit.c
@@ -0,0 +1,100 @@
+#include "system.h"
+
+#include <libaudit.h>
+
+#include <rpm/rpmts.h>
+#include "lib/rpmplugin.h"
+
+struct teop {
+ rpmte te;
+ const char *op;
+};
+
+/*
+ * Figure out the actual operations:
+ * Install and remove are straightforward. Updates need to be discovered
+ * via their erasure element: locate the updating element, adjust it's
+ * op to update and silence the erasure part. Obsoletion is handled as
+ * as install + remove, which it technically is.
+ */
+static void getAuditOps(rpmts ts, struct teop *ops, int nelem)
+{
+ rpmtsi pi = rpmtsiInit(ts);
+ rpmte p;
+ int i = 0;
+ while ((p = rpmtsiNext(pi, 0)) != NULL) {
+ const char *op = NULL;
+ if (rpmteType(p) == TR_ADDED) {
+ op = "install";
+ } else {
+ op = "remove";
+ rpmte d = rpmteDependsOn(p);
+ /* Fixup op on updating elements, silence the cleanup stage */
+ if (d != NULL && rstreq(rpmteN(d), rpmteN(p))) {
+ /* Linear lookup, but we're only dealing with a few thousand */
+ for (int x = 0; x < i; x++) {
+ if (ops[x].te == d) {
+ ops[x].op = "update";
+ op = NULL;
+ break;
+ }
+ }
+ }
+ }
+ ops[i].te = p;
+ ops[i].op = op;
+ i++;
+ }
+ rpmtsiFree(pi);
+}
+
+/*
+ * If enabled, log audit events for the operations in this transaction.
+ * In the event values, 1 means true/success and 0 false/failure. Shockingly.
+ */
+static rpmRC audit_tsm_post(rpmPlugin plugin, rpmts ts, int res)
+{
+ if (rpmtsFlags(ts) & (RPMTRANS_FLAG_TEST|RPMTRANS_FLAG_BUILD_PROBS))
+ goto exit;
+
+ int auditFd = audit_open();
+ if (auditFd < 0)
+ goto exit;
+
+ int nelem = rpmtsNElements(ts);
+ struct teop *ops = xcalloc(nelem, sizeof(*ops));
+ char *dir = audit_encode_nv_string("root_dir", rpmtsRootDir(ts), 0);
+ int enforce = (rpmtsVfyLevel(ts) & RPMSIG_SIGNATURE_TYPE) != 0;
+
+ getAuditOps(ts, ops, nelem);
+
+ for (int i = 0; i < nelem; i++) {
+ const char *op = ops[i].op;
+ if (op) {
+ rpmte p = ops[i].te;
+ char *nevra = audit_encode_nv_string("sw", rpmteNEVRA(p), 0);
+ char *eventTxt = NULL;
+ int verified = (rpmteVerified(p) & RPMSIG_SIGNATURE_TYPE) ? 1 : 0;
+ int result = (rpmteFailed(p) == 0);
+
+ rasprintf(&eventTxt,
+ "op=%s %s sw_type=rpm key_enforce=%u gpg_res=%u %s",
+ op, nevra, enforce, verified, dir);
+ audit_log_user_comm_message(auditFd, AUDIT_SOFTWARE_UPDATE,
+ eventTxt, NULL, NULL, NULL, NULL, result);
+ free(nevra);
+ free(eventTxt);
+ }
+ }
+
+ free(dir);
+ free(ops);
+ audit_close(auditFd);
+
+exit:
+ return RPMRC_OK;
+}
+
+struct rpmPluginHooks_s audit_hooks = {
+ .tsm_post = audit_tsm_post,
+};