Blame SOURCES/fapolicyd-exclude-list.patch

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