diff options
-rw-r--r-- | ci/Dockerfile | 2 | ||||
-rw-r--r-- | configure.ac | 24 | ||||
-rw-r--r-- | macros.in | 1 | ||||
-rw-r--r-- | plugins/Makefile.am | 6 | ||||
-rw-r--r-- | plugins/audit.c | 100 |
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 @@ -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, +}; |