Blame SOURCES/fapolicyd-exclude-list.patch

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