Blob Blame History Raw
From 3b269a5e6d0901adc58ce27b9eae51deba679788 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= <jcerny@redhat.com>
Date: Wed, 23 Oct 2019 17:30:19 +0200
Subject: [PATCH] Use and return canonical paths in rpmverifyfile probe

This is done to prevent false positives in implementation of rules
file_permissions_unauthorized_suid and
file_permissions_unauthorized_sgid proposed in ComplianceAsCode
in https://github.com/ComplianceAsCode/content/pull/4648
caused by inconsistent symlink usage in rpmdb.

Also improves logic when an exact file path is defined in
rpmverifyfile_object we don't have to iterate over all RPMs in the
rpmdb but we query the rpmdb directly.
---
 src/OVAL/probes/unix/linux/rpmverifyfile.c | 45 ++++++++++++++++------
 1 file changed, 34 insertions(+), 11 deletions(-)

diff --git a/src/OVAL/probes/unix/linux/rpmverifyfile.c b/src/OVAL/probes/unix/linux/rpmverifyfile.c
index c13e3a629..e17f1612b 100644
--- a/src/OVAL/probes/unix/linux/rpmverifyfile.c
+++ b/src/OVAL/probes/unix/linux/rpmverifyfile.c
@@ -137,6 +137,7 @@ static int rpmverify_collect(probe_ctx *ctx,
 	Header pkgh;
 	pcre *re = NULL;
 	int  ret = -1;
+	char *file_realpath = NULL;
 
 	/* pre-compile regex if needed */
 	if (file_op == OVAL_OPERATION_PATTERN_MATCH) {
@@ -153,7 +154,16 @@ static int rpmverify_collect(probe_ctx *ctx,
 
 	RPMVERIFY_LOCK;
 
-	match = rpmtsInitIterator (g_rpm.rpmts, RPMDBI_PACKAGES, NULL, 0);
+	if (file != NULL && file_op == OVAL_OPERATION_EQUALS) {
+		/*
+		 * When we know the exact file path we look for, we don't need to
+		 * filter all RPM packages, but we can ask the rpmdb directly for
+		 * the package which provides this file, similar to `rpm -q -f`.
+		 */
+		match = rpmtsInitIterator(g_rpm.rpmts, RPMDBI_INSTFILENAMES, file, 0);
+	} else {
+		match = rpmtsInitIterator(g_rpm.rpmts, RPMDBI_PACKAGES, NULL, 0);
+	}
 	if (match == NULL) {
 		ret = 0;
 		goto ret;
@@ -183,6 +193,8 @@ static int rpmverify_collect(probe_ctx *ctx,
 	assume_d(RPMTAG_BASENAMES != 0, -1);
 	assume_d(RPMTAG_DIRNAMES  != 0, -1);
 
+	file_realpath = realpath(file, NULL);
+
 	while ((pkgh = rpmdbNextIterator (match)) != NULL) {
 		SEXP_t *ent;
 		rpmfi  fi;
@@ -190,6 +202,8 @@ static int rpmverify_collect(probe_ctx *ctx,
 		struct rpmverify_res res;
 		errmsg_t rpmerr;
 		int i;
+		const char *current_file;
+		char *current_file_realpath;
 
 		/*
 +SEXP_t *probe_ent_from_cstr(const char *name, oval_datatype_t type,
@@ -231,43 +245,51 @@ static int rpmverify_collect(probe_ctx *ctx,
 		  fi = rpmfiNew(g_rpm.rpmts, pkgh, tag[i], 1);
 
 		  while (rpmfiNext(fi) != -1) {
-				res.file = oscap_strdup(rpmfiFN(fi));
+				current_file = rpmfiFN(fi);
+				current_file_realpath = realpath(current_file, NULL);
 		    res.fflags = rpmfiFFlags(fi);
 		    res.oflags = omit;
 
 		    if (((res.fflags & RPMFILE_CONFIG) && (flags & RPMVERIFY_SKIP_CONFIG)) ||
 					((res.fflags & RPMFILE_GHOST)  && (flags & RPMVERIFY_SKIP_GHOST))) {
-					free(res.file);
+					free(current_file_realpath);
 					continue;
 				}
 
 		    switch(file_op) {
 		    case OVAL_OPERATION_EQUALS:
-					if (strcmp(res.file, file) != 0) {
-						free(res.file);
+					if (strcmp(current_file, file) != 0 &&
+							current_file_realpath && file_realpath &&
+							strcmp(current_file_realpath, file_realpath) != 0) {
+						free(current_file_realpath);
 						continue;
 					}
+					res.file = oscap_strdup(file);
 		      break;
 		    case OVAL_OPERATION_NOT_EQUAL:
-					if (strcmp(res.file, file) == 0) {
-						free(res.file);
+					if (strcmp(current_file, file) == 0 ||
+							(current_file_realpath && file_realpath &&
+							strcmp(current_file_realpath, file_realpath) == 0)) {
+						free(current_file_realpath);
 						continue;
 					}
+					res.file = current_file_realpath ? current_file_realpath : strdup(current_file);
 		      break;
 		    case OVAL_OPERATION_PATTERN_MATCH:
-		      ret = pcre_exec(re, NULL, res.file, strlen(res.file), 0, 0, NULL, 0);
+					ret = pcre_exec(re, NULL, current_file, strlen(current_file), 0, 0, NULL, 0);
 
 		      switch(ret) {
 		      case 0: /* match */
+						res.file = strdup(current_file);
 			break;
 		      case -1:
 			/* mismatch */
-			free(res.file);
+						free(current_file_realpath);
 			continue;
 		      default:
 			dE("pcre_exec() failed!");
 			ret = -1;
-			free(res.file);
+						free(current_file_realpath);
 			goto ret;
 		      }
 		      break;
@@ -275,7 +297,7 @@ static int rpmverify_collect(probe_ctx *ctx,
 		      /* unsupported operation */
 		      dE("Operation \"%d\" on `filepath' not supported", file_op);
 		      ret = -1;
-					free(res.file);
+						free(current_file_realpath);
 		      goto ret;
 		    }
 
@@ -301,6 +323,7 @@ ret:
 		pcre_free(re);
 
 	RPMVERIFY_UNLOCK;
+	free(file_realpath);
 	return (ret);
 }
 
-- 
2.21.0