043c42
From 820dcc1db9f2130a21fdaf721217034376eb8e38 Mon Sep 17 00:00:00 2001
043c42
Message-Id: <820dcc1db9f2130a21fdaf721217034376eb8e38.1544785848.git.pmatilai@redhat.com>
043c42
From: Panu Matilainen <pmatilai@redhat.com>
043c42
Date: Fri, 30 Nov 2018 13:10:44 +0200
043c42
Subject: [PATCH] Add support for logging audit events for package installs as
043c42
 per OSPP v4.2
043c42
043c42
If enabled at build-time, log audit events for package install, update
043c42
and remove. The log includes the operation, package nevra, signature
043c42
check result, whether signatures are being enforced enforced and overall
043c42
success result. Package install/update/remove are logged as such,
043c42
obsoletion is logged as install + remove (whereas the erasure element
043c42
on updates is silent)
043c42
043c42
Loosely based on initial RHEL 7-8 implementations by Pavlina Moravcova
043c42
Varekova and Florian Festi (RhBug:1555326, RhBug:1607612)
043c42
043c42
(cherry picked from commit cfc9dde70fe65e91c83e03e9a9441e627b741489)
043c42
---
043c42
 configure.ac         |  21 +++++++++
043c42
 lib/Makefile.am      |   1 +
043c42
 lib/rpmte.c          |  11 +++++
043c42
 lib/rpmte_internal.h |   6 +++
043c42
 lib/transaction.c    | 104 +++++++++++++++++++++++++++++++++++++++++++
043c42
 5 files changed, 143 insertions(+)
043c42
043c42
diff --git a/configure.ac b/configure.ac
043c42
index 34ea85f9f..ab8a368d3 100644
043c42
--- a/configure.ac
043c42
+++ b/configure.ac
043c42
@@ -312,6 +312,27 @@ fi
043c42
 AC_SUBST(WITH_BEECRYPT_LIB)
043c42
 AC_SUBST(WITH_BEECRYPT_INCLUDE)
043c42
 
043c42
+
043c42
+#=================
043c42
+# Check for audit library.
043c42
+AC_ARG_WITH(audit,
043c42
+AS_HELP_STRING([--with-audit],[log results using Linux Audit]),
043c42
+with_audit=$withval,
043c42
+with_audit=auto)
043c42
+
043c42
+WITH_AUDIT_LIB=
043c42
+AS_IF([test "x$with_audit" != xno],[
043c42
+    AC_SEARCH_LIBS([audit_open],[audit],[
043c42
+    WITH_AUDIT_LIB="$ac_res"
043c42
+    AC_DEFINE(WITH_AUDIT, 1, [libaudit support])
043c42
+    ],
043c42
+       [if test "x$with_audit" != xauto; then
043c42
+		AC_MSG_ERROR([missing audit library])
043c42
+	fi
043c42
+    ])
043c42
+])
043c42
+AC_SUBST(WITH_AUDIT_LIB)
043c42
+
043c42
 #=================
043c42
 # Check for OpenSSL library.
043c42
 # We need evp.h from OpenSSL.
043c42
diff --git a/lib/Makefile.am b/lib/Makefile.am
043c42
index baf3238ee..c055962a3 100644
043c42
--- a/lib/Makefile.am
043c42
+++ b/lib/Makefile.am
043c42
@@ -51,6 +51,7 @@ librpm_la_LIBADD = \
043c42
 	@WITH_POPT_LIB@ \
043c42
 	@WITH_CAP_LIB@ \
043c42
 	@WITH_ACL_LIB@ \
043c42
+	@WITH_AUDIT_LIB@ \
043c42
 	@LIBINTL@
043c42
 
043c42
 if WITH_LUA
043c42
diff --git a/lib/rpmte.c b/lib/rpmte.c
043c42
index d980a37a4..bd5d53edc 100644
043c42
--- a/lib/rpmte.c
043c42
+++ b/lib/rpmte.c
043c42
@@ -69,6 +69,7 @@ struct rpmte_s {
043c42
     int nrelocs;		/*!< (TR_ADDED) No. of relocations. */
043c42
     uint8_t *badrelocs;		/*!< (TR_ADDED) Bad relocations (or NULL) */
043c42
     FD_t fd;			/*!< (TR_ADDED) Payload file descriptor. */
043c42
+    int verified;		/*!< (TR_ADDED) Verification status */
043c42
 
043c42
 #define RPMTE_HAVE_PRETRANS	(1 << 0)
043c42
 #define RPMTE_HAVE_POSTTRANS	(1 << 1)
043c42
@@ -753,6 +754,16 @@ rpmfs rpmteGetFileStates(rpmte te)
043c42
     return te->fs;
043c42
 }
043c42
 
043c42
+void rpmteSetVerified(rpmte te, int verified)
043c42
+{
043c42
+    te->verified = verified;
043c42
+}
043c42
+
043c42
+int rpmteGetVerified(rpmte te)
043c42
+{
043c42
+    return te->verified;
043c42
+}
043c42
+
043c42
 int rpmteProcess(rpmte te, pkgGoal goal, int num)
043c42
 {
043c42
     /* Only install/erase resets pkg file info */
043c42
diff --git a/lib/rpmte_internal.h b/lib/rpmte_internal.h
043c42
index a5a991ec5..2895925ce 100644
043c42
--- a/lib/rpmte_internal.h
043c42
+++ b/lib/rpmte_internal.h
043c42
@@ -86,6 +86,12 @@ int rpmteHaveTransScript(rpmte te, rpmTagVal tag);
043c42
 /* XXX should be internal too but build code needs for now... */
043c42
 rpmfs rpmteGetFileStates(rpmte te);
043c42
 
043c42
+RPM_GNUC_INTERNAL
043c42
+void rpmteSetVerified(rpmte te, int verified);
043c42
+
043c42
+RPM_GNUC_INTERNAL
043c42
+int rpmteGetVerified(rpmte te);
043c42
+
043c42
 /** \ingroup rpmte
043c42
  * Retrieve size in bytes of package header.
043c42
  * @param te		transaction element
043c42
diff --git a/lib/transaction.c b/lib/transaction.c
043c42
index 67b9db579..866e87fc2 100644
043c42
--- a/lib/transaction.c
043c42
+++ b/lib/transaction.c
043c42
@@ -7,6 +7,10 @@
043c42
 #include <inttypes.h>
043c42
 #include <libgen.h>
043c42
 
043c42
+#if WITH_AUDIT
043c42
+#include <libaudit.h>
043c42
+#endif
043c42
+
043c42
 #include <rpm/rpmlib.h>		/* rpmMachineScore, rpmReadPackageFile */
043c42
 #include <rpm/rpmmacro.h>	/* XXX for rpmExpand */
043c42
 #include <rpm/rpmlog.h>
043c42
@@ -1195,12 +1199,17 @@ static rpm_loff_t countPkgs(rpmts ts, rpmElementTypes types)
043c42
 
043c42
 struct vfydata_s {
043c42
     char *msg;
043c42
+    int signature;
043c42
     int vfylevel;
043c42
 };
043c42
 
043c42
 static int vfyCb(struct rpmsinfo_s *sinfo, void *cbdata)
043c42
 {
043c42
     struct vfydata_s *vd = cbdata;
043c42
+
043c42
+    if (sinfo->type == RPMSIG_SIGNATURE_TYPE && sinfo->rc == RPMRC_OK)
043c42
+	vd->signature = RPMRC_OK;
043c42
+
043c42
     switch (sinfo->rc) {
043c42
     case RPMRC_OK:
043c42
 	break;
043c42
@@ -1241,6 +1250,7 @@ static int verifyPackageFiles(rpmts ts, rpm_loff_t total)
043c42
 	struct rpmvs_s *vs = rpmvsCreate(vfylevel, vsflags, keyring);
043c42
 	struct vfydata_s vd = {
043c42
 	    .msg = NULL,
043c42
+	    .signature = RPMRC_NOTFOUND,
043c42
 	    .vfylevel = vfylevel,
043c42
 	};
043c42
 	rpmRC prc = RPMRC_FAIL;
043c42
@@ -1255,6 +1265,9 @@ static int verifyPackageFiles(rpmts ts, rpm_loff_t total)
043c42
 	if (prc == RPMRC_OK)
043c42
 	    prc = rpmvsVerify(vs, RPMSIG_VERIFIABLE_TYPE, vfyCb, &vd);
043c42
 
043c42
+	/* Record verify result, signatures only for now */
043c42
+	rpmteSetVerified(p, vd.signature == RPMRC_OK);
043c42
+
043c42
 	if (prc)
043c42
 	    rpmteAddProblem(p, RPMPROB_VERIFY, NULL, vd.msg, 0);
043c42
 
043c42
@@ -1619,6 +1632,95 @@ rpmRC runScript(rpmts ts, rpmte te, Header h, ARGV_const_t prefixes,
043c42
     return rc;
043c42
 }
043c42
 
043c42
+#if WITH_AUDIT
043c42
+struct teop {
043c42
+    rpmte te;
043c42
+    const char *op;
043c42
+};
043c42
+
043c42
+/*
043c42
+ * Figure out the actual operations:
043c42
+ * Install and remove are straightforward. Updates need to discovered 
043c42
+ * via their erasure element: locate the updating element, adjust it's
043c42
+ * op to update and silence the erasure part. Obsoletion is handled as
043c42
+ * as install + remove, which it technically is.
043c42
+ */
043c42
+static void getAuditOps(rpmts ts, struct teop *ops, int nelem)
043c42
+{
043c42
+    rpmtsi pi = rpmtsiInit(ts);
043c42
+    rpmte p;
043c42
+    int i = 0;
043c42
+    while ((p = rpmtsiNext(pi, 0)) != NULL) {
043c42
+	const char *op = NULL;
043c42
+	if (rpmteType(p) == TR_ADDED) {
043c42
+	    op = "install";
043c42
+	} else {
043c42
+	    op = "remove";
043c42
+	    rpmte d = rpmteDependsOn(p);
043c42
+	    /* Fixup op on updating elements, silence the cleanup stage */
043c42
+	    if (d != NULL && rstreq(rpmteN(d), rpmteN(p))) {
043c42
+		/* Linear lookup, but we're only dealing with a few thousand */
043c42
+		for (int x = 0; x < i; x++) {
043c42
+		    if (ops[x].te == d) {
043c42
+			ops[x].op = "update";
043c42
+			op = NULL;
043c42
+			break;
043c42
+		    }
043c42
+		}
043c42
+	    }
043c42
+	}
043c42
+	ops[i].te = p;
043c42
+	ops[i].op = op;
043c42
+	i++;
043c42
+    }
043c42
+    rpmtsiFree(pi);
043c42
+}
043c42
+
043c42
+/*
043c42
+ * If enabled, log audit events for the operations in this transaction.
043c42
+ * In the event values, 1 means true/success and 0 false/failure. Shockingly.
043c42
+ */
043c42
+static void rpmtsAudit(rpmts ts)
043c42
+{
043c42
+    int auditFd = audit_open();
043c42
+    if (auditFd < 0)
043c42
+	return;
043c42
+
043c42
+    int nelem = rpmtsNElements(ts);
043c42
+    struct teop *ops = xcalloc(nelem, sizeof(*ops));
043c42
+    char *dir = audit_encode_nv_string("root_dir", rpmtsRootDir(ts), 0);
043c42
+    int enforce = (rpmtsVfyLevel(ts) & RPMSIG_SIGNATURE_TYPE) != 0;
043c42
+
043c42
+    getAuditOps(ts, ops, nelem);
043c42
+
043c42
+    for (int i = 0; i < nelem; i++) {
043c42
+	const char *op = ops[i].op;
043c42
+	if (op) {
043c42
+	    rpmte p = ops[i].te;
043c42
+	    char *nevra = audit_encode_nv_string("sw", rpmteNEVRA(p), 0);
043c42
+	    char eventTxt[256];
043c42
+	    int verified = rpmteGetVerified(p);
043c42
+	    int result = (rpmteFailed(p) == 0);
043c42
+
043c42
+	    snprintf(eventTxt, sizeof(eventTxt),
043c42
+		    "op=%s %s sw_type=rpm key_enforce=%u gpg_res=%u %s",
043c42
+		    op, nevra, enforce, verified, dir);
043c42
+	    audit_log_user_comm_message(auditFd, AUDIT_SOFTWARE_UPDATE,
043c42
+				    eventTxt, NULL, NULL, NULL, NULL, result);
043c42
+	    free(nevra);
043c42
+	}
043c42
+    }
043c42
+
043c42
+    free(dir);
043c42
+    free(ops);
043c42
+    audit_close(auditFd);
043c42
+}
043c42
+#else
043c42
+static void rpmtsAudit(rpmts ts)
043c42
+{
043c42
+}
043c42
+#endif
043c42
+
043c42
 int rpmtsRun(rpmts ts, rpmps okProbs, rpmprobFilterFlags ignoreSet)
043c42
 {
043c42
     int rc = -1; /* assume failure */
043c42
@@ -1732,6 +1834,8 @@ exit:
043c42
 	rpmpluginsCallTsmPost(rpmtsPlugins(ts), ts, rc);
043c42
 
043c42
     /* Finish up... */
043c42
+    if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_TEST|RPMTRANS_FLAG_BUILD_PROBS)))
043c42
+	rpmtsAudit(ts);
043c42
     (void) umask(oldmask);
043c42
     (void) rpmtsFinish(ts);
043c42
     rpmpsFree(tsprobs);
043c42
-- 
043c42
2.19.2
043c42