Add plugin for logging audit events for package operations as per OSPP v4.2

When enabled, log audit events for package install, update and remove.
The log includes the operation, package nevra, signature check result,
whether signatures are being enforced enforced and overall operation
result code. Package install/update/remove are logged as such,
obsoletion is logged as install + remove (whereas the erasure element
on updates is silent). Enable compilation in CI.

Loosely based on initial RHEL 7-8 implementations by Pavlina Moravcova
Varekova and Florian Festi (RhBug:1555326, RhBug:1607612)
This commit is contained in:
Panu Matilainen 2019-03-28 13:56:07 +02:00 committed by Florian Festi
parent eecd3d5d05
commit ad16a04b6e
5 changed files with 133 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

100
plugins/audit.c Normal file
View File

@ -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,
};