Blame SOURCES/fapolicyd-exclude-list.patch

1bfbac
From 4066d92395c18ad435ee6ff8e1da2745a68bacc1 Mon Sep 17 00:00:00 2001
1bfbac
From: Radovan Sroka <rsroka@redhat.com>
1bfbac
Date: Fri, 24 Jun 2022 15:03:28 +0200
1bfbac
Subject: [PATCH] Introduce filtering of rpmdb
1bfbac
1bfbac
- this feature introduces very flexible filter syntax
1bfbac
- original filter was compiled in so this is very useful
1bfbac
- filter needs to keep a minimal set of files that will be excuted
1bfbac
  on the system eventually
1bfbac
- all the configuration can be done in /etc/fapolicyd/rpm-filter.conf
1bfbac
1bfbac
Signed-off-by: Radovan Sroka <rsroka@redhat.com>
1bfbac
---
1bfbac
 doc/Makefile.am           |   3 +-
1bfbac
 doc/rpm-filter.conf.5     |  63 +++++
1bfbac
 fapolicyd.spec            |   1 +
1bfbac
 init/Makefile.am          |   2 +
1bfbac
 init/rpm-filter.conf      |  42 ++++
1bfbac
 src/Makefile.am           |   8 +-
1bfbac
 src/library/llist.c       |  23 +-
1bfbac
 src/library/llist.h       |   1 +
1bfbac
 src/library/rpm-backend.c |  79 ++-----
1bfbac
 src/library/rpm-filter.c  | 487 ++++++++++++++++++++++++++++++++++++++
1bfbac
 src/library/rpm-filter.h  |  67 ++++++
1bfbac
 src/library/stack.c       |  89 +++++++
1bfbac
 src/library/stack.h       |  41 ++++
1bfbac
 13 files changed, 837 insertions(+), 69 deletions(-)
1bfbac
 create mode 100644 doc/rpm-filter.conf.5
1bfbac
 create mode 100644 init/rpm-filter.conf
1bfbac
 create mode 100644 src/library/rpm-filter.c
1bfbac
 create mode 100644 src/library/rpm-filter.h
1bfbac
 create mode 100644 src/library/stack.c
1bfbac
 create mode 100644 src/library/stack.h
1bfbac
1bfbac
diff --git a/doc/Makefile.am b/doc/Makefile.am
1bfbac
index f0b79080..726218ed 100644
1bfbac
--- a/doc/Makefile.am
1bfbac
+++ b/doc/Makefile.am
1bfbac
@@ -28,4 +28,5 @@ man_MANS = \
1bfbac
 	fapolicyd-cli.1 \
1bfbac
 	fapolicyd.rules.5 \
1bfbac
 	fapolicyd.trust.5 \
1bfbac
-	fapolicyd.conf.5
1bfbac
+	fapolicyd.conf.5 \
1bfbac
+	rpm-filter.conf.5
1bfbac
diff --git a/doc/rpm-filter.conf.5 b/doc/rpm-filter.conf.5
1bfbac
new file mode 100644
1bfbac
index 00000000..d415bd80
1bfbac
--- /dev/null
1bfbac
+++ b/doc/rpm-filter.conf.5
1bfbac
@@ -0,0 +1,63 @@
1bfbac
+.TH RPM_FILTER.CONF: "5" "January 2023" "Red Hat" "System Administration Utilities"
1bfbac
+.SH NAME
1bfbac
+rpm-filter.conf \- fapolicyd filter configuration file
1bfbac
+.SH DESCRIPTION
1bfbac
+The file
1bfbac
+.I /etc/fapolicyd/rpm-filter.conf
1bfbac
+contains configuration of the filter for the application allowlisting daemon. This filter specifies an allow or exclude list of files from rpm. Valid line starts with character '+', '-' or '#' for comments. The rest of the line contains a path specification. Space can be used as indentation to add more specific filters to the previous one. Note, that only one space is required for one level of an indent. If  there are multiple specifications on the same indentation level they extend the previous line with lower indentation, usually a directory.  The path may be specified using the glob pattern. A directory specification has to end with a slash ‘/’.
1bfbac
+
1bfbac
+The filters are processed as follows: Starting from the up the to bottom while in case of a match the result (+/-) is set unless there is an indented block which describes more detailed specification of the parent level match. The same processing logic is applied to the inner filters definitions. If there is no match, the parent’s result is set. If there is no match at all, the default result is minus (-).
1bfbac
+
1bfbac
+If the result was a plus (+), the respective file from the rpmdb is imported to the TrustDB. Vice versa, if the result was a minus (-), the respective file is not imported.
1bfbac
+
1bfbac
+From a performance point of view it is better to design an indented filter because in the ideal situation each component of the path is compared only once. In contrast to it, a filter without any indentation has to contain a full path which makes the pattern more complicated and thus slower to process. The motivation behind this is to have a flexible configuration and keep the TrustDB as small as possible to make the look-ups faster.
1bfbac
+
1bfbac
+
1bfbac
+
1bfbac
+.nf
1bfbac
+.B # this is simple allow list
1bfbac
+.B - /usr/bin/some_binary1
1bfbac
+.B - /usr/bin/some_binary2
1bfbac
+.B + /
1bfbac
+.fi
1bfbac
+
1bfbac
+.nf
1bfbac
+.B # this is the same
1bfbac
+.B + /
1bfbac
+.B \ + usr/bin/
1bfbac
+.B \ \ - some_binary1
1bfbac
+.B \ \ - some_binary2
1bfbac
+.fi
1bfbac
+
1bfbac
+.nf
1bfbac
+.B # this is similar allow list with a wildcard
1bfbac
+.B - /usr/bin/some_binary?
1bfbac
+.B + /
1bfbac
+.fi
1bfbac
+
1bfbac
+.nf
1bfbac
+.B # this is similar with another wildcard
1bfbac
+.B + /
1bfbac
+.B \ - usr/bin/some_binary*
1bfbac
+.fi
1bfbac
+
1bfbac
+.nf
1bfbac
+.B # keeps everything except usr/share except python and perl files
1bfbac
+.B # /usr/bin/ls - result is '+'
1bfbac
+.B # /usr/share/something - result is '-'
1bfbac
+.B # /usr/share/abcd.py - result is '+'
1bfbac
+.B + /
1bfbac
+.B \ - usr/share/
1bfbac
+.B \ \ + *.py
1bfbac
+.B \ \ + *.pl
1bfbac
+.fi
1bfbac
+
1bfbac
+.SH "SEE ALSO"
1bfbac
+.BR fapolicyd (8),
1bfbac
+.BR fapolicyd-cli (1)
1bfbac
+.BR fapolicy.rules (5)
1bfbac
+and
1bfbac
+.BR glob (7)
1bfbac
+
1bfbac
+.SH AUTHOR
1bfbac
+Radovan Sroka
1bfbac
diff --git a/init/Makefile.am b/init/Makefile.am
1bfbac
index da948e4e..1f23dffe 100644
1bfbac
--- a/init/Makefile.am
1bfbac
+++ b/init/Makefile.am
1bfbac
@@ -1,6 +1,7 @@
1bfbac
 EXTRA_DIST = \
1bfbac
 	fapolicyd.service \
1bfbac
 	fapolicyd.conf \
1bfbac
+	rpm-filter.conf \
1bfbac
 	fapolicyd.trust \
1bfbac
 	fapolicyd-tmpfiles.conf \
1bfbac
 	fapolicyd-magic \
1bfbac
@@ -11,6 +12,7 @@ fapolicyddir = $(sysconfdir)/fapolicyd
1bfbac
 
1bfbac
 dist_fapolicyd_DATA = \
1bfbac
 	fapolicyd.conf \
1bfbac
+	rpm-filter.conf \
1bfbac
 	fapolicyd.trust
1bfbac
 
1bfbac
 systemdservicedir = $(systemdsystemunitdir)
1bfbac
diff --git a/init/rpm-filter.conf b/init/rpm-filter.conf
1bfbac
new file mode 100644
1bfbac
index 00000000..0c8fca40
1bfbac
--- /dev/null
1bfbac
+++ b/init/rpm-filter.conf
1bfbac
@@ -0,0 +1,42 @@
1bfbac
+# default filter file for fedora
1bfbac
+
1bfbac
++ /
1bfbac
+ - usr/include/
1bfbac
+ - usr/share/
1bfbac
+  # Python byte code
1bfbac
+  + *.py?
1bfbac
+  # Python text files
1bfbac
+  + *.py
1bfbac
+  # Some apps have a private libexec
1bfbac
+  + */libexec/*
1bfbac
+  # Ruby
1bfbac
+  + *.rb
1bfbac
+  # Perl
1bfbac
+  + *.pl
1bfbac
+  # System tap
1bfbac
+  + *.stp
1bfbac
+  # Javascript
1bfbac
+  + *.js
1bfbac
+  # Java archive
1bfbac
+  + *.jar
1bfbac
+  # M4
1bfbac
+  + *.m4
1bfbac
+  # PHP
1bfbac
+  + *.php
1bfbac
+  # Perl Modules
1bfbac
+  + *.pm
1bfbac
+  # Lua
1bfbac
+  + *.lua
1bfbac
+  # Java
1bfbac
+  + *.class
1bfbac
+  # Typescript
1bfbac
+  + *.ts
1bfbac
+  # Typescript JSX
1bfbac
+  + *.tsx
1bfbac
+  # Lisp
1bfbac
+  + *.el
1bfbac
+  # Compiled Lisp
1bfbac
+  + *.elc
1bfbac
+ - usr/src/kernel*/
1bfbac
+  + */scripts/*
1bfbac
+  + */tools/objtool/*
1bfbac
diff --git a/src/Makefile.am b/src/Makefile.am
1bfbac
index 547ea486..fd08eb06 100644
1bfbac
--- a/src/Makefile.am
1bfbac
+++ b/src/Makefile.am
1bfbac
@@ -62,13 +62,19 @@ libfapolicyd_la_SOURCES = \
1bfbac
 	library/subject-attr.h \
1bfbac
 	library/subject.c \
1bfbac
 	library/subject.h \
1bfbac
+	library/stack.c \
1bfbac
+	library/stack.h \
1bfbac
 	library/string-util.c \
1bfbac
 	library/string-util.h \
1bfbac
 	library/trust-file.c \
1bfbac
 	library/trust-file.h
1bfbac
 
1bfbac
 if WITH_RPM
1bfbac
-libfapolicyd_la_SOURCES += library/rpm-backend.c
1bfbac
+libfapolicyd_la_SOURCES += \
1bfbac
+	library/rpm-backend.c \
1bfbac
+	library/rpm-filter.c \
1bfbac
+	library/rpm-filter.h
1bfbac
+
1bfbac
 endif
1bfbac
 
1bfbac
 libfapolicyd_la_CFLAGS = $(fapolicyd_CFLAGS)
1bfbac
diff --git a/src/library/llist.c b/src/library/llist.c
1bfbac
index 6132805a..44cfb4a3 100644
1bfbac
--- a/src/library/llist.c
1bfbac
+++ b/src/library/llist.c
1bfbac
@@ -45,19 +45,36 @@ list_item_t *list_get_first(const list_t *list)
1bfbac
 	return list->first;
1bfbac
 }
1bfbac
 
1bfbac
-
1bfbac
-int list_append(list_t *list, const char *index, const char *data)
1bfbac
+static list_item_t * create_item(const char *index, const char *data)
1bfbac
 {
1bfbac
 	list_item_t *item = malloc(sizeof(list_item_t));
1bfbac
 	if (!item) {
1bfbac
 		msg(LOG_ERR, "Malloc failed");
1bfbac
-		return 1;
1bfbac
+		return item;
1bfbac
 	}
1bfbac
 
1bfbac
 	item->index = index;
1bfbac
 	item->data = data;
1bfbac
 	item->next = NULL;
1bfbac
 
1bfbac
+	return item;
1bfbac
+}
1bfbac
+
1bfbac
+int list_prepend(list_t *list, const char *index, const char *data)
1bfbac
+{
1bfbac
+	list_item_t *item = create_item(index, data);
1bfbac
+
1bfbac
+	item->next = list->first;
1bfbac
+	list->first = item;
1bfbac
+
1bfbac
+	++list->count;
1bfbac
+	return 0;
1bfbac
+}
1bfbac
+
1bfbac
+int list_append(list_t *list, const char *index, const char *data)
1bfbac
+{
1bfbac
+	list_item_t *item = create_item(index, data);
1bfbac
+
1bfbac
 	if (list->first) {
1bfbac
 		list->last->next = item;
1bfbac
 		list->last = item;
1bfbac
diff --git a/src/library/llist.h b/src/library/llist.h
1bfbac
index 0c1d85a7..59eccf17 100644
1bfbac
--- a/src/library/llist.h
1bfbac
+++ b/src/library/llist.h
1bfbac
@@ -40,6 +40,7 @@ typedef struct list_header {
1bfbac
 
1bfbac
 void list_init(list_t *list);
1bfbac
 list_item_t *list_get_first(const list_t *list);
1bfbac
+int list_prepend(list_t *list, const char *index, const char *data);
1bfbac
 int list_append(list_t *list, const char *index, const char *data);
1bfbac
 void list_destroy_item(list_item_t **item);
1bfbac
 void list_empty(list_t *list);
1bfbac
diff --git a/src/library/rpm-backend.c b/src/library/rpm-backend.c
1bfbac
index 7f1af438..0887d36a 100644
1bfbac
--- a/src/library/rpm-backend.c
1bfbac
+++ b/src/library/rpm-backend.c
1bfbac
@@ -40,6 +40,8 @@
1bfbac
 #include "fapolicyd-backend.h"
1bfbac
 #include "llist.h"
1bfbac
 
1bfbac
+#include "rpm-filter.h"
1bfbac
+
1bfbac
 static int rpm_init_backend(void);
1bfbac
 static int rpm_load_list(const conf_t *);
1bfbac
 static int rpm_destroy_backend(void);
1bfbac
@@ -176,69 +178,6 @@ static void close_rpm(void)
1bfbac
 	rpmlogClose();
1bfbac
 }
1bfbac
 
1bfbac
-// This function will check a passed file name to see if the path should
1bfbac
-// be kept or dropped. 1 means discard it, and 0 means keep it.
1bfbac
-static int drop_path(const char *file_name)
1bfbac
-{
1bfbac
-	const char *p = file_name;
1bfbac
-	if (!strncmp(p, "/usr", 4)) {
1bfbac
-		p += 4;
1bfbac
-
1bfbac
-		// Drop anything in /usr/include
1bfbac
-		if (!strncmp(p, "/include", 8))
1bfbac
-			return 1;
1bfbac
-
1bfbac
-		// Only keep languages from /usr/share
1bfbac
-		if (!strncmp(p, "/share", 6)) {
1bfbac
-			p += 6;
1bfbac
-			
1bfbac
-			// These are roughly ordered by quantity
1bfbac
-			static const char *arr_share[] = {
1bfbac
-				"*.py?",       // Python byte code
1bfbac
-				"*.py",        // Python text files
1bfbac
-				"*/libexec/*", // Some apps have a private libexec
1bfbac
-				"*.rb",        // Ruby
1bfbac
-				"*.pl",        // Perl
1bfbac
-				"*.stp",       // System tap
1bfbac
-				"*.js",        // Javascript
1bfbac
-				"*.jar",       // Java archive
1bfbac
-				"*.m4",        // M4
1bfbac
-				"*.php",       // PHP
1bfbac
-				"*.pm",        // Perl Modules
1bfbac
-				"*.lua",       // Lua
1bfbac
-				"*.class",     // Java
1bfbac
-				"*.ts",        // Typescript
1bfbac
-				"*.tsx",       // Typescript JSX
1bfbac
-				"*.el",        // Lisp
1bfbac
-				"*.elc",       // Compiled Lisp
1bfbac
-				NULL
1bfbac
-			};
1bfbac
-
1bfbac
-			for (int i = 0; arr_share[i]; ++i)
1bfbac
-				if (!fnmatch(arr_share[i], p, 0))
1bfbac
-					return 0;
1bfbac
-			return 1;
1bfbac
-		}
1bfbac
-
1bfbac
-		// Akmod needs scripts in /usr/src/kernel
1bfbac
-		if (!strncmp(p, "/src/kernel", 11)) {
1bfbac
-			p += 11;
1bfbac
-			
1bfbac
-			static const char *arr_src_kernel[] = {
1bfbac
-				"*/scripts/*",
1bfbac
-				"*/tools/objtool/*",
1bfbac
-				NULL
1bfbac
-			};
1bfbac
-			
1bfbac
-			for (int i = 0; arr_src_kernel[i]; ++i)
1bfbac
-				if (!fnmatch(arr_src_kernel[i], p, 0))
1bfbac
-					return 0;
1bfbac
-			return 1;
1bfbac
-		}
1bfbac
-	}
1bfbac
-	return 0;
1bfbac
-}
1bfbac
-
1bfbac
 struct _hash_record {
1bfbac
 	const char * key;
1bfbac
 	UT_hash_handle hh;
1bfbac
@@ -290,7 +229,8 @@ static int rpm_load_list(const conf_t *conf)
1bfbac
 			if (file_name == NULL)
1bfbac
 				continue;
1bfbac
 
1bfbac
-			if (drop_path(file_name)) {
1bfbac
+			// should we drop a path?
1bfbac
+			if (!filter_check(file_name)) {
1bfbac
 				free((void *)file_name);
1bfbac
 				free((void *)sha);
1bfbac
 				continue;
1bfbac
@@ -358,12 +298,23 @@ static int rpm_load_list(const conf_t *conf)
1bfbac
 
1bfbac
 static int rpm_init_backend(void)
1bfbac
 {
1bfbac
+	if (filter_init())
1bfbac
+		return 1;
1bfbac
+
1bfbac
+	if (filter_load_file()) {
1bfbac
+		filter_destroy();
1bfbac
+		return 1;
1bfbac
+	}
1bfbac
+
1bfbac
+
1bfbac
 	list_init(&rpm_backend.list);
1bfbac
+
1bfbac
 	return 0;
1bfbac
 }
1bfbac
 
1bfbac
 static int rpm_destroy_backend(void)
1bfbac
 {
1bfbac
+	filter_destroy();
1bfbac
 	list_empty(&rpm_backend.list);
1bfbac
 	return 0;
1bfbac
 }
1bfbac
diff --git a/src/library/rpm-filter.c b/src/library/rpm-filter.c
1bfbac
new file mode 100644
1bfbac
index 00000000..e3e3eb38
1bfbac
--- /dev/null
1bfbac
+++ b/src/library/rpm-filter.c
1bfbac
@@ -0,0 +1,487 @@
1bfbac
+/*
1bfbac
+* rpm-filter.c - filter for rpm trust source
1bfbac
+* Copyright (c) 2023 Red Hat Inc., Durham, North Carolina.
1bfbac
+* All Rights Reserved.
1bfbac
+*
1bfbac
+* This software may be freely redistributed and/or modified under the
1bfbac
+* terms of the GNU General Public License as published by the Free
1bfbac
+* Software Foundation; either version 2, or (at your option) any
1bfbac
+* later version.
1bfbac
+*
1bfbac
+* This program is distributed in the hope that it will be useful,
1bfbac
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
1bfbac
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1bfbac
+* GNU General Public License for more details.
1bfbac
+*
1bfbac
+* You should have received a copy of the GNU General Public License
1bfbac
+* along with this program; see the file COPYING. If not, write to the
1bfbac
+* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor
1bfbac
+* Boston, MA 02110-1335, USA.
1bfbac
+*
1bfbac
+* Authors:
1bfbac
+*   Radovan Sroka <rsroka@redhat.com>
1bfbac
+*/
1bfbac
+
1bfbac
+#include "rpm-filter.h"
1bfbac
+
1bfbac
+#include <stdio.h>
1bfbac
+#include <string.h>
1bfbac
+#include <ctype.h>
1bfbac
+#include <fnmatch.h>
1bfbac
+
1bfbac
+#include "llist.h"
1bfbac
+#include "stack.h"
1bfbac
+#include "message.h"
1bfbac
+#include "string-util.h"
1bfbac
+
1bfbac
+
1bfbac
+#define RPM_FILTER_FILE "/etc/fapolicyd/rpm-filter.conf"
1bfbac
+
1bfbac
+rpm_filter_t *global_filter = NULL;
1bfbac
+
1bfbac
+static rpm_filter_t *filter_create_obj(void);
1bfbac
+static void filter_destroy_obj(rpm_filter_t *_filter);
1bfbac
+
1bfbac
+// init fuction of this module
1bfbac
+int filter_init(void)
1bfbac
+{
1bfbac
+	global_filter = filter_create_obj();
1bfbac
+	if (global_filter == NULL)
1bfbac
+		return 1;
1bfbac
+
1bfbac
+	return 0;
1bfbac
+}
1bfbac
+
1bfbac
+// destroy funtion of this module
1bfbac
+void filter_destroy(void)
1bfbac
+{
1bfbac
+	filter_destroy_obj(global_filter);
1bfbac
+	global_filter = NULL;
1bfbac
+}
1bfbac
+
1bfbac
+// alocate new filter object and fill with the defaults
1bfbac
+static rpm_filter_t *filter_create_obj(void)
1bfbac
+{
1bfbac
+	rpm_filter_t *filter = malloc(sizeof(rpm_filter_t));
1bfbac
+	if (filter) {
1bfbac
+		filter->type = NONE;
1bfbac
+		filter->path = NULL;
1bfbac
+		filter->len = 0;
1bfbac
+		filter->matched = 0;
1bfbac
+		filter->processed = 0;
1bfbac
+		list_init(&filter->list);
1bfbac
+	}
1bfbac
+	return filter;
1bfbac
+}
1bfbac
+
1bfbac
+// free all nested filters
1bfbac
+static void filter_destroy_obj(rpm_filter_t *_filter)
1bfbac
+{
1bfbac
+	if (_filter == NULL)
1bfbac
+		return;
1bfbac
+
1bfbac
+	rpm_filter_t *filter = _filter;
1bfbac
+	stack_t stack;
1bfbac
+	stack_init(&stack);
1bfbac
+
1bfbac
+	stack_push(&stack, filter);
1bfbac
+
1bfbac
+	while (!stack_is_empty(&stack)) {
1bfbac
+		filter = (rpm_filter_t*)stack_top(&stack);
1bfbac
+		if (filter->processed) {
1bfbac
+			(void)free(filter->path);
1bfbac
+			// asume that item->data is NULL
1bfbac
+			list_empty(&filter->list);
1bfbac
+			(void)free(filter);
1bfbac
+			stack_pop(&stack);
1bfbac
+			continue;
1bfbac
+		}
1bfbac
+
1bfbac
+		list_item_t *item = list_get_first(&filter->list);
1bfbac
+		for (; item != NULL ; item = item->next) {
1bfbac
+				rpm_filter_t *next_filter = (rpm_filter_t*)item->data;
1bfbac
+				// we can use list_empty() later
1bfbac
+				// we dont want to free filter right now
1bfbac
+				// it will freed after popping
1bfbac
+				item->data = NULL;
1bfbac
+				stack_push(&stack, next_filter);
1bfbac
+		}
1bfbac
+		filter->processed = 1;
1bfbac
+	}
1bfbac
+	stack_destroy(&stack);
1bfbac
+}
1bfbac
+
1bfbac
+// create struct and push it to the top of stack
1bfbac
+static void stack_push_vars(stack_t *_stack, int _level, int _offset, rpm_filter_t *_filter)
1bfbac
+{
1bfbac
+	if (_stack == NULL)
1bfbac
+		return;
1bfbac
+
1bfbac
+	stack_item_t *item = malloc(sizeof(stack_item_t));
1bfbac
+	if (item == NULL)
1bfbac
+		return;
1bfbac
+
1bfbac
+	item->level = _level;
1bfbac
+	item->offset = _offset;
1bfbac
+	item->filter = _filter;
1bfbac
+
1bfbac
+	stack_push(_stack, item);
1bfbac
+}
1bfbac
+
1bfbac
+// pop stack_item_t and free it
1bfbac
+static void stack_pop_vars(stack_t *_stack)
1bfbac
+{
1bfbac
+	if (_stack == NULL)
1bfbac
+		return;
1bfbac
+
1bfbac
+	stack_item_t * item = (stack_item_t*)stack_top(_stack);
1bfbac
+	free(item);
1bfbac
+	stack_pop(_stack);
1bfbac
+}
1bfbac
+
1bfbac
+// pop all the stack_item_t and free them
1bfbac
+static void stack_pop_all_vars(stack_t *_stack)
1bfbac
+{
1bfbac
+	if (_stack == NULL)
1bfbac
+		return;
1bfbac
+
1bfbac
+	while (!stack_is_empty(_stack))
1bfbac
+		stack_pop_vars(_stack);
1bfbac
+}
1bfbac
+
1bfbac
+// reset filter to default, pop top and free
1bfbac
+static void stack_pop_reset(stack_t *_stack)
1bfbac
+{
1bfbac
+	if (_stack == NULL)
1bfbac
+		return;
1bfbac
+
1bfbac
+	stack_item_t *stack_item = (stack_item_t*)stack_top(_stack);
1bfbac
+	if (stack_item) {
1bfbac
+		stack_item->filter->matched = 0;
1bfbac
+		stack_item->filter->processed = 0;
1bfbac
+	}
1bfbac
+	free(stack_item);
1bfbac
+	stack_pop(_stack);
1bfbac
+}
1bfbac
+
1bfbac
+// reset and pop all the stack_item_t
1bfbac
+static void stack_pop_all_reset(stack_t *_stack)
1bfbac
+{
1bfbac
+	if (_stack == NULL)
1bfbac
+		return;
1bfbac
+
1bfbac
+	while (!stack_is_empty(_stack))
1bfbac
+		stack_pop_reset(_stack);
1bfbac
+}
1bfbac
+
1bfbac
+// this funtion gets full path and checks it against filter
1bfbac
+// returns 1 for keeping the file and 0 for dropping it
1bfbac
+int filter_check(const char *_path)
1bfbac
+{
1bfbac
+	if (_path == NULL) {
1bfbac
+		msg(LOG_ERR, "filter_check: path is NULL, something is wrong!");
1bfbac
+		return 0;
1bfbac
+	}
1bfbac
+
1bfbac
+	rpm_filter_t *filter = global_filter;
1bfbac
+	char *path = strdup(_path);
1bfbac
+	size_t path_len = strlen(_path);
1bfbac
+	size_t offset = 0;
1bfbac
+	// Create a stack to store the filters that need to be checked
1bfbac
+	stack_t stack;
1bfbac
+	stack_init(&stack);
1bfbac
+
1bfbac
+	int res = 0;
1bfbac
+	int level = 0;
1bfbac
+
1bfbac
+	stack_push_vars(&stack, level, offset, filter);
1bfbac
+
1bfbac
+	while(!stack_is_empty(&stack)) {
1bfbac
+		int matched = 0;
1bfbac
+		filter->processed = 1;
1bfbac
+
1bfbac
+		// this is starting branch of the algo
1bfbac
+		// assuming that in root filter filter->path is NULL
1bfbac
+		if (filter->path == NULL) {
1bfbac
+			list_item_t *item = list_get_first(&filter->list);
1bfbac
+			// push all the descendants to the stack
1bfbac
+			for (; item != NULL ; item = item->next) {
1bfbac
+				rpm_filter_t *next_filter = (rpm_filter_t*)item->data;
1bfbac
+				stack_push_vars(&stack, level+1, offset, next_filter);
1bfbac
+			}
1bfbac
+
1bfbac
+		// usual branch, start with processing
1bfbac
+		} else {
1bfbac
+			// wildcard contition
1bfbac
+			char *is_wildcard = strpbrk(filter->path, "?*[");
1bfbac
+			if (is_wildcard) {
1bfbac
+				int count = 0;
1bfbac
+				char *filter_lim, *filter_old_lim;
1bfbac
+				filter_lim = filter_old_lim = filter->path;
1bfbac
+
1bfbac
+				char *path_lim, *path_old_lim;
1bfbac
+				path_lim = path_old_lim = path+offset;
1bfbac
+
1bfbac
+				// there can be wildcard in the dir name as well
1bfbac
+				// we need to count how many chars can be eaten by wildcard
1bfbac
+				while(1) {
1bfbac
+					filter_lim = strchr(filter_lim, '/');
1bfbac
+					path_lim = strchr(path_lim, '/');
1bfbac
+
1bfbac
+					if (filter_lim) {
1bfbac
+						count++;
1bfbac
+						filter_old_lim = filter_lim;
1bfbac
+						filter_lim++;
1bfbac
+					} else
1bfbac
+						break;
1bfbac
+
1bfbac
+					if (path_lim) {
1bfbac
+						path_old_lim = path_lim;
1bfbac
+						path_lim++;
1bfbac
+					} else
1bfbac
+						break;
1bfbac
+
1bfbac
+				}
1bfbac
+				// put 0 after the last /
1bfbac
+				char tmp = '\0';
1bfbac
+				if (count && *(filter_old_lim+1) == '\0') {
1bfbac
+					 tmp = *(path_old_lim+1);
1bfbac
+					*(path_old_lim+1) = '\0';
1bfbac
+				}
1bfbac
+
1bfbac
+				// check fnmatch
1bfbac
+				matched = !fnmatch(filter->path, path+offset, 0);
1bfbac
+
1bfbac
+				// and set back
1bfbac
+				if (count && *(filter_old_lim+1) == '\0')
1bfbac
+					*(path_old_lim+1) = tmp;
1bfbac
+
1bfbac
+				if (matched) {
1bfbac
+					offset = path_old_lim - path+offset;
1bfbac
+				}
1bfbac
+			} else {
1bfbac
+				// match normal path or just specific part of it
1bfbac
+				matched = !strncmp(path+offset, filter->path, filter->len);
1bfbac
+				if (matched)
1bfbac
+					offset += filter->len;
1bfbac
+			}
1bfbac
+
1bfbac
+			if (matched) {
1bfbac
+				level++;
1bfbac
+				filter->matched = 1;
1bfbac
+
1bfbac
+				// if matched we need ot push descendants to the stack
1bfbac
+				list_item_t *item = list_get_first(&filter->list);
1bfbac
+
1bfbac
+				// if there are no descendants and it is a wildcard then it's a match
1bfbac
+				if (item == NULL && is_wildcard) {
1bfbac
+					// if '+' ret 1 and if '-' ret 0
1bfbac
+					res = filter->type == ADD ? 1 : 0;
1bfbac
+					goto end;
1bfbac
+				}
1bfbac
+
1bfbac
+				// no descendants, and already compared whole path string so its a match
1bfbac
+				if (item == NULL && path_len == offset) {
1bfbac
+					// if '+' ret 1 and if '-' ret 0
1bfbac
+					res = filter->type == ADD ? 1 : 0;
1bfbac
+					goto end;
1bfbac
+				}
1bfbac
+
1bfbac
+				// push descendants to the stack
1bfbac
+				for (; item != NULL ; item = item->next) {
1bfbac
+					rpm_filter_t *next_filter = (rpm_filter_t*)item->data;
1bfbac
+					stack_push_vars(&stack, level, offset, next_filter);
1bfbac
+				}
1bfbac
+
1bfbac
+			}
1bfbac
+
1bfbac
+		}
1bfbac
+
1bfbac
+		stack_item_t * stack_item = NULL;
1bfbac
+		// popping processed filters from the top of the stack
1bfbac
+		do {
1bfbac
+			if (stack_item) {
1bfbac
+				filter = stack_item->filter;
1bfbac
+				offset = stack_item->offset;
1bfbac
+				level = stack_item->level;
1bfbac
+
1bfbac
+				// assuimg that nothing has matched on the upper level so it's a directory match
1bfbac
+				if (filter->matched && filter->path[filter->len-1] == '/') {
1bfbac
+					res = filter->type == ADD ? 1 : 0;
1bfbac
+					goto end;
1bfbac
+				}
1bfbac
+
1bfbac
+				// reset processed flag
1bfbac
+				stack_pop_reset(&stack);
1bfbac
+			}
1bfbac
+
1bfbac
+			stack_item = (stack_item_t*)stack_top(&stack);
1bfbac
+		} while(stack_item && stack_item->filter->processed);
1bfbac
+
1bfbac
+		if (!stack_item)
1bfbac
+			break;
1bfbac
+
1bfbac
+		filter = stack_item->filter;
1bfbac
+		offset = stack_item->offset;
1bfbac
+		level = stack_item->level;
1bfbac
+	}
1bfbac
+
1bfbac
+end:
1bfbac
+	// Clean up the stack
1bfbac
+	stack_pop_all_reset(&stack);
1bfbac
+	stack_destroy(&stack);
1bfbac
+	free(path);
1bfbac
+	return res;
1bfbac
+}
1bfbac
+
1bfbac
+// load rpm filter configuration file and fill the filter structure
1bfbac
+int filter_load_file(void)
1bfbac
+{
1bfbac
+	int res = 0;
1bfbac
+	FILE *stream = fopen(RPM_FILTER_FILE, "r");
1bfbac
+
1bfbac
+	if (stream == NULL) {
1bfbac
+		msg(LOG_ERR, "Cannot open filter file %s", RPM_FILTER_FILE);
1bfbac
+		return 1;
1bfbac
+	}
1bfbac
+
1bfbac
+	ssize_t nread;
1bfbac
+	size_t len = 0;
1bfbac
+	char * line = NULL;
1bfbac
+	long line_number = 0;
1bfbac
+	int last_level = 0;
1bfbac
+
1bfbac
+	stack_t stack;
1bfbac
+	stack_init(&stack);
1bfbac
+	stack_push_vars(&stack, last_level, 0, global_filter);
1bfbac
+
1bfbac
+	while ((nread = getline(&line, &len, stream)) != -1) {
1bfbac
+		line_number++;
1bfbac
+
1bfbac
+		if (line[0] == '\0' || line[0] == '\n') {
1bfbac
+			free(line);
1bfbac
+			line = NULL;
1bfbac
+			continue;
1bfbac
+		}
1bfbac
+
1bfbac
+		// get rid of the new line char
1bfbac
+		char * new_line = strchr(line, '\n');
1bfbac
+		if (new_line) {
1bfbac
+			*new_line = '\0';
1bfbac
+			len--;
1bfbac
+		}
1bfbac
+
1bfbac
+		int level = 1;
1bfbac
+		char * rest = line;
1bfbac
+		rpm_filter_type_t type = NONE;
1bfbac
+
1bfbac
+		for (size_t i = 0 ; i < len ; i++) {
1bfbac
+			switch (line[i]) {
1bfbac
+				case ' ':
1bfbac
+					level++;
1bfbac
+					continue;
1bfbac
+				case '+':
1bfbac
+					type = ADD;
1bfbac
+					break;
1bfbac
+				case '-':
1bfbac
+					type = SUB;
1bfbac
+					break;
1bfbac
+				case '#':
1bfbac
+					type = COMMENT;
1bfbac
+					break;
1bfbac
+				default:
1bfbac
+					type = BAD;
1bfbac
+					break;
1bfbac
+			}
1bfbac
+
1bfbac
+			// continue with next char
1bfbac
+			// skip + and space
1bfbac
+			rest = fapolicyd_strtrim(&(line[i+2]));
1bfbac
+			break;
1bfbac
+		}
1bfbac
+
1bfbac
+		// ignore comment
1bfbac
+		if (type == COMMENT) {
1bfbac
+			free(line);
1bfbac
+			line = NULL;
1bfbac
+			continue;
1bfbac
+		}
1bfbac
+
1bfbac
+		// if something bad return error
1bfbac
+		if (type == BAD) {
1bfbac
+			msg(LOG_ERR, "filter_load_file: cannot parse line number %ld, \"%s\"", line_number, line);
1bfbac
+			free(line);
1bfbac
+			line = NULL;
1bfbac
+			goto bad;
1bfbac
+		}
1bfbac
+
1bfbac
+		rpm_filter_t * filter = filter_create_obj();
1bfbac
+
1bfbac
+		if (filter) {
1bfbac
+			filter->path = strdup(rest);
1bfbac
+			filter->len = strlen(filter->path);
1bfbac
+			filter->type = type;
1bfbac
+		}
1bfbac
+
1bfbac
+		// comparing level of indetantion between the last line and the current one
1bfbac
+		last_level = ((stack_item_t*)stack_top(&stack))->level;
1bfbac
+		if (level == last_level) {
1bfbac
+
1bfbac
+			// since we are at the same level as filter before
1bfbac
+			// we need to pop the previous filter from the top
1bfbac
+			stack_pop_vars(&stack);
1bfbac
+
1bfbac
+			// pushing filter to the list of top's children list
1bfbac
+			list_prepend(&((stack_item_t*)stack_top(&stack))->filter->list, NULL, (void*)filter);
1bfbac
+
1bfbac
+			// pushing filter to the top of the stack
1bfbac
+			stack_push_vars(&stack, level, 0, filter);
1bfbac
+
1bfbac
+		} else if (level == last_level + 1) {
1bfbac
+			// this filter has higher level tha privious one
1bfbac
+			// we wont do pop just push
1bfbac
+
1bfbac
+			// pushing filter to the list of top's children list
1bfbac
+			list_prepend(&((stack_item_t*)stack_top(&stack))->filter->list, NULL, (void*)filter);
1bfbac
+
1bfbac
+			// pushing filter to the top of the stack
1bfbac
+			stack_push_vars(&stack, level, 0, filter);
1bfbac
+
1bfbac
+		} else if (level < last_level){
1bfbac
+			// level of indentation dropped
1bfbac
+			// we need to pop
1bfbac
+			// +1 is meant for getting rid of the current level so we can again push
1bfbac
+			for (int i = 0 ; i < last_level - level + 1; i++) {
1bfbac
+				stack_pop_vars(&stack);
1bfbac
+			}
1bfbac
+
1bfbac
+			// pushing filter to the list of top's children list
1bfbac
+			list_prepend(&((stack_item_t*)stack_top(&stack))->filter->list, NULL, (void*)filter);
1bfbac
+
1bfbac
+			// pushing filter to the top of the stack
1bfbac
+			stack_push_vars(&stack, level, 0, filter);
1bfbac
+
1bfbac
+		} else {
1bfbac
+			msg(LOG_ERR, "filter_load_file: paring error line: %ld, \"%s\"", line_number, line);
1bfbac
+			filter_destroy_obj(filter);
1bfbac
+			free(line);
1bfbac
+			goto bad;
1bfbac
+		}
1bfbac
+
1bfbac
+		free(line);
1bfbac
+		line = NULL;
1bfbac
+	}
1bfbac
+
1bfbac
+	goto good;
1bfbac
+bad:
1bfbac
+	res = 1;
1bfbac
+
1bfbac
+good:
1bfbac
+	fclose(stream);
1bfbac
+	stack_pop_all_vars(&stack);
1bfbac
+	stack_destroy(&stack);
1bfbac
+	if (global_filter->list.count == 0) {
1bfbac
+		msg(LOG_ERR, "filter_load_file: no valid filter provided in %s", RPM_FILTER_FILE);
1bfbac
+	}
1bfbac
+	return res;
1bfbac
+}
1bfbac
diff --git a/src/library/rpm-filter.h b/src/library/rpm-filter.h
1bfbac
new file mode 100644
1bfbac
index 00000000..2c49d338
1bfbac
--- /dev/null
1bfbac
+++ b/src/library/rpm-filter.h
1bfbac
@@ -0,0 +1,67 @@
1bfbac
+/*
1bfbac
+* rpm-filter.h - Header for rpm filter implementation
1bfbac
+* Copyright (c) 2023 Red Hat Inc., Durham, North Carolina.
1bfbac
+* All Rights Reserved.
1bfbac
+*
1bfbac
+* This software may be freely redistributed and/or modified under the
1bfbac
+* terms of the GNU General Public License as published by the Free
1bfbac
+* Software Foundation; either version 2, or (at your option) any
1bfbac
+* later version.
1bfbac
+*
1bfbac
+* This program is distributed in the hope that it will be useful,
1bfbac
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
1bfbac
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1bfbac
+* GNU General Public License for more details.
1bfbac
+*
1bfbac
+* You should have received a copy of the GNU General Public License
1bfbac
+* along with this program; see the file COPYING. If not, write to the
1bfbac
+* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor
1bfbac
+* Boston, MA 02110-1335, USA.
1bfbac
+*
1bfbac
+* Authors:
1bfbac
+*   Radovan Sroka <rsroka@redhat.com>
1bfbac
+*/
1bfbac
+
1bfbac
+#ifndef FILTER_H_
1bfbac
+#define FILTER_H_
1bfbac
+
1bfbac
+#include <stdlib.h>
1bfbac
+#include <stddef.h>
1bfbac
+
1bfbac
+#include "llist.h"
1bfbac
+
1bfbac
+typedef enum rpm_filter_type
1bfbac
+{
1bfbac
+	NONE,
1bfbac
+	ADD,
1bfbac
+	SUB,
1bfbac
+	COMMENT,
1bfbac
+	BAD,
1bfbac
+} rpm_filter_type_t;
1bfbac
+
1bfbac
+typedef struct _rpm_filter
1bfbac
+{
1bfbac
+	rpm_filter_type_t type;
1bfbac
+	char * path;
1bfbac
+	size_t len;
1bfbac
+	int processed;
1bfbac
+	int matched;
1bfbac
+	list_t list;
1bfbac
+} rpm_filter_t;
1bfbac
+
1bfbac
+
1bfbac
+typedef struct _stack_item
1bfbac
+{
1bfbac
+	int level;
1bfbac
+	int offset;
1bfbac
+	rpm_filter_t *filter;
1bfbac
+} stack_item_t;
1bfbac
+
1bfbac
+
1bfbac
+int filter_init(void);
1bfbac
+void filter_destroy(void);
1bfbac
+int filter_check(const char *_path);
1bfbac
+int filter_load_file(void);
1bfbac
+
1bfbac
+
1bfbac
+#endif // FILTER_H_
1bfbac
diff --git a/src/library/stack.c b/src/library/stack.c
1bfbac
new file mode 100644
1bfbac
index 00000000..93141b2c
1bfbac
--- /dev/null
1bfbac
+++ b/src/library/stack.c
1bfbac
@@ -0,0 +1,89 @@
1bfbac
+/*
1bfbac
+* stack.c - generic stack impementation
1bfbac
+* Copyright (c) 2023 Red Hat Inc., Durham, North Carolina.
1bfbac
+* All Rights Reserved.
1bfbac
+*
1bfbac
+* This software may be freely redistributed and/or modified under the
1bfbac
+* terms of the GNU General Public License as published by the Free
1bfbac
+* Software Foundation; either version 2, or (at your option) any
1bfbac
+* later version.
1bfbac
+*
1bfbac
+* This program is distributed in the hope that it will be useful,
1bfbac
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
1bfbac
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1bfbac
+* GNU General Public License for more details.
1bfbac
+*
1bfbac
+* You should have received a copy of the GNU General Public License
1bfbac
+* along with this program; see the file COPYING. If not, write to the
1bfbac
+* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor
1bfbac
+* Boston, MA 02110-1335, USA.
1bfbac
+*
1bfbac
+* Authors:
1bfbac
+*   Radovan Sroka <rsroka@redhat.com>
1bfbac
+*/
1bfbac
+
1bfbac
+#include "stack.h"
1bfbac
+#include <stddef.h>
1bfbac
+
1bfbac
+// init of the stack struct
1bfbac
+void stack_init(stack_t *_stack)
1bfbac
+{
1bfbac
+	if (_stack == NULL)
1bfbac
+		return;
1bfbac
+
1bfbac
+	list_init(_stack);
1bfbac
+}
1bfbac
+
1bfbac
+// free all the resources from the stack
1bfbac
+void stack_destroy(stack_t *_stack)
1bfbac
+{
1bfbac
+	if (_stack == NULL)
1bfbac
+		return;
1bfbac
+
1bfbac
+	list_empty(_stack);
1bfbac
+}
1bfbac
+
1bfbac
+// push to the top of the stack
1bfbac
+void stack_push(stack_t *_stack, void *_data)
1bfbac
+{
1bfbac
+	if (_stack == NULL)
1bfbac
+		return;
1bfbac
+
1bfbac
+	list_prepend(_stack, NULL, (void *)_data);
1bfbac
+}
1bfbac
+
1bfbac
+// pop the the top without returning what was on the top
1bfbac
+void stack_pop(stack_t *_stack)
1bfbac
+{
1bfbac
+	if (_stack == NULL)
1bfbac
+		return;
1bfbac
+
1bfbac
+	list_item_t *first = _stack->first;
1bfbac
+	_stack->first = first->next;
1bfbac
+	first->data = NULL;
1bfbac
+	list_destroy_item(&first);
1bfbac
+	_stack->count--;
1bfbac
+
1bfbac
+	return;
1bfbac
+}
1bfbac
+
1bfbac
+// function returns 1 if stack is emtpy 0 if it's not
1bfbac
+int stack_is_empty(stack_t *_stack)
1bfbac
+{
1bfbac
+	if (_stack == NULL)
1bfbac
+		return -1;
1bfbac
+
1bfbac
+	if (_stack->count == 0)
1bfbac
+		return 1;
1bfbac
+
1bfbac
+	return 0;
1bfbac
+}
1bfbac
+
1bfbac
+// return top of the stack without popping
1bfbac
+const void *stack_top(stack_t *_stack)
1bfbac
+{
1bfbac
+	if (_stack == NULL)
1bfbac
+		return NULL;
1bfbac
+
1bfbac
+	return _stack->first ? _stack->first->data : NULL;
1bfbac
+}
1bfbac
diff --git a/src/library/stack.h b/src/library/stack.h
1bfbac
new file mode 100644
1bfbac
index 00000000..042476e3
1bfbac
--- /dev/null
1bfbac
+++ b/src/library/stack.h
1bfbac
@@ -0,0 +1,41 @@
1bfbac
+/*
1bfbac
+* stack.h - header for generic stack implementation
1bfbac
+* Copyright (c) 2023 Red Hat Inc., Durham, North Carolina.
1bfbac
+* All Rights Reserved.
1bfbac
+*
1bfbac
+* This software may be freely redistributed and/or modified under the
1bfbac
+* terms of the GNU General Public License as published by the Free
1bfbac
+* Software Foundation; either version 2, or (at your option) any
1bfbac
+* later version.
1bfbac
+*
1bfbac
+* This program is distributed in the hope that it will be useful,
1bfbac
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
1bfbac
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1bfbac
+* GNU General Public License for more details.
1bfbac
+*
1bfbac
+* You should have received a copy of the GNU General Public License
1bfbac
+* along with this program; see the file COPYING. If not, write to the
1bfbac
+* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor
1bfbac
+* Boston, MA 02110-1335, USA.
1bfbac
+*
1bfbac
+* Authors:
1bfbac
+*   Radovan Sroka <rsroka@redhat.com>
1bfbac
+*/
1bfbac
+
1bfbac
+
1bfbac
+#ifndef STACK_H_
1bfbac
+#define STACK_H_
1bfbac
+
1bfbac
+#include "llist.h"
1bfbac
+
1bfbac
+typedef list_t stack_t;
1bfbac
+
1bfbac
+void stack_init(stack_t *_stack);
1bfbac
+void stack_destroy(stack_t *_stack);
1bfbac
+void stack_push(stack_t *_stack, void *_data);
1bfbac
+void stack_pop(stack_t *_stack);
1bfbac
+int stack_is_empty(stack_t *_stack);
1bfbac
+const void *stack_top(stack_t *_stack);
1bfbac
+
1bfbac
+
1bfbac
+#endif // STACK_H_