diff --git a/SOURCES/fapolicyd-exclude-list.patch b/SOURCES/fapolicyd-exclude-list.patch index 1373c54..87d00e8 100644 --- a/SOURCES/fapolicyd-exclude-list.patch +++ b/SOURCES/fapolicyd-exclude-list.patch @@ -1,4 +1,4 @@ -From e80c6641546f23a305ff84f8dac9ca6f8105beb3 Mon Sep 17 00:00:00 2001 +From 4066d92395c18ad435ee6ff8e1da2745a68bacc1 Mon Sep 17 00:00:00 2001 From: Radovan Sroka Date: Fri, 24 Jun 2022 15:03:28 +0200 Subject: [PATCH] Introduce filtering of rpmdb @@ -12,19 +12,19 @@ Subject: [PATCH] Introduce filtering of rpmdb Signed-off-by: Radovan Sroka --- doc/Makefile.am | 3 +- - doc/rpm-filter.conf.5 | 61 ++++++ + doc/rpm-filter.conf.5 | 63 +++++ fapolicyd.spec | 1 + init/Makefile.am | 2 + init/rpm-filter.conf | 42 ++++ src/Makefile.am | 8 +- - src/library/llist.c | 23 ++- + src/library/llist.c | 23 +- src/library/llist.h | 1 + - src/library/rpm-backend.c | 76 ++----- - src/library/rpm-filter.c | 419 ++++++++++++++++++++++++++++++++++++++ + src/library/rpm-backend.c | 79 ++----- + src/library/rpm-filter.c | 487 ++++++++++++++++++++++++++++++++++++++ src/library/rpm-filter.h | 67 ++++++ - src/library/stack.c | 82 ++++++++ + src/library/stack.c | 89 +++++++ src/library/stack.h | 41 ++++ - 13 files changed, 757 insertions(+), 69 deletions(-) + 13 files changed, 837 insertions(+), 69 deletions(-) create mode 100644 doc/rpm-filter.conf.5 create mode 100644 init/rpm-filter.conf create mode 100644 src/library/rpm-filter.c @@ -45,34 +45,35 @@ index f0b79080..726218ed 100644 + rpm-filter.conf.5 diff --git a/doc/rpm-filter.conf.5 b/doc/rpm-filter.conf.5 new file mode 100644 -index 00000000..5a7e1e3e +index 00000000..d415bd80 --- /dev/null +++ b/doc/rpm-filter.conf.5 -@@ -0,0 +1,61 @@ +@@ -0,0 +1,63 @@ +.TH RPM_FILTER.CONF: "5" "January 2023" "Red Hat" "System Administration Utilities" +.SH NAME +rpm-filter.conf \- fapolicyd filter configuration file +.SH DESCRIPTION +The file +.I /etc/fapolicyd/rpm-filter.conf -+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. ++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 ‘/’. + -+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. ++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 (-). + +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. + +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. + + ++ +.nf -+.B # this is simle black list ++.B # this is simple allow list +.B - /usr/bin/some_binary1 +.B - /usr/bin/some_binary2 +.B + / +.fi + +.nf -+.B # this is the same ++.B # this is the same +.B + / +.B \ + usr/bin/ +.B \ \ - some_binary1 @@ -80,7 +81,7 @@ index 00000000..5a7e1e3e +.fi + +.nf -+.B # this is similar black list with a wildcard ++.B # this is similar allow list with a wildcard +.B - /usr/bin/some_binary? +.B + / +.fi @@ -105,8 +106,9 @@ index 00000000..5a7e1e3e +.SH "SEE ALSO" +.BR fapolicyd (8), +.BR fapolicyd-cli (1) ++.BR fapolicy.rules (5) +and -+.BR fapolicy.rules (5). ++.BR glob (7) + +.SH AUTHOR +Radovan Sroka @@ -260,7 +262,7 @@ index 0c1d85a7..59eccf17 100644 void list_destroy_item(list_item_t **item); void list_empty(list_t *list); diff --git a/src/library/rpm-backend.c b/src/library/rpm-backend.c -index 7f1af438..cc6427ab 100644 +index 7f1af438..0887d36a 100644 --- a/src/library/rpm-backend.c +++ b/src/library/rpm-backend.c @@ -40,6 +40,8 @@ @@ -352,15 +354,18 @@ index 7f1af438..cc6427ab 100644 free((void *)file_name); free((void *)sha); continue; -@@ -358,12 +298,20 @@ static int rpm_load_list(const conf_t *conf) +@@ -358,12 +298,23 @@ static int rpm_load_list(const conf_t *conf) static int rpm_init_backend(void) { + if (filter_init()) + return 1; + -+ if (filter_load_file()) ++ if (filter_load_file()) { ++ filter_destroy(); + return 1; ++ } ++ + list_init(&rpm_backend.list); + @@ -375,10 +380,10 @@ index 7f1af438..cc6427ab 100644 } diff --git a/src/library/rpm-filter.c b/src/library/rpm-filter.c new file mode 100644 -index 00000000..806989e9 +index 00000000..e3e3eb38 --- /dev/null +++ b/src/library/rpm-filter.c -@@ -0,0 +1,419 @@ +@@ -0,0 +1,487 @@ +/* +* rpm-filter.c - filter for rpm trust source +* Copyright (c) 2023 Red Hat Inc., Durham, North Carolina. @@ -437,6 +442,7 @@ index 00000000..806989e9 +void filter_destroy(void) +{ + filter_destroy_obj(global_filter); ++ global_filter = NULL; +} + +// alocate new filter object and fill with the defaults @@ -454,20 +460,41 @@ index 00000000..806989e9 + return filter; +} + -+// recusively free all the nested filters -+// and also free itself ++// free all nested filters +static void filter_destroy_obj(rpm_filter_t *_filter) +{ + if (_filter == NULL) + return; + -+ list_item_t *item = list_get_first(&_filter->list); -+ for (; item != NULL ; item = item->next) { -+ filter_destroy_obj(((stack_item_t *)item->data)->filter); -+ } ++ rpm_filter_t *filter = _filter; ++ stack_t stack; ++ stack_init(&stack); ++ ++ stack_push(&stack, filter); ++ ++ while (!stack_is_empty(&stack)) { ++ filter = (rpm_filter_t*)stack_top(&stack); ++ if (filter->processed) { ++ (void)free(filter->path); ++ // asume that item->data is NULL ++ list_empty(&filter->list); ++ (void)free(filter); ++ stack_pop(&stack); ++ continue; ++ } + -+ (void)free(_filter->path); -+ list_empty(&_filter->list); ++ list_item_t *item = list_get_first(&filter->list); ++ for (; item != NULL ; item = item->next) { ++ rpm_filter_t *next_filter = (rpm_filter_t*)item->data; ++ // we can use list_empty() later ++ // we dont want to free filter right now ++ // it will freed after popping ++ item->data = NULL; ++ stack_push(&stack, next_filter); ++ } ++ filter->processed = 1; ++ } ++ stack_destroy(&stack); +} + +// create struct and push it to the top of stack @@ -487,23 +514,62 @@ index 00000000..806989e9 + stack_push(_stack, item); +} + -+// helper function to reset to some default values when the top is going to be popped -+static void stack_set_pop(stack_t *_stack) ++// pop stack_item_t and free it ++static void stack_pop_vars(stack_t *_stack) +{ ++ if (_stack == NULL) ++ return; ++ ++ stack_item_t * item = (stack_item_t*)stack_top(_stack); ++ free(item); ++ stack_pop(_stack); ++} ++ ++// pop all the stack_item_t and free them ++static void stack_pop_all_vars(stack_t *_stack) ++{ ++ if (_stack == NULL) ++ return; ++ ++ while (!stack_is_empty(_stack)) ++ stack_pop_vars(_stack); ++} ++ ++// reset filter to default, pop top and free ++static void stack_pop_reset(stack_t *_stack) ++{ ++ if (_stack == NULL) ++ return; ++ + stack_item_t *stack_item = (stack_item_t*)stack_top(_stack); + if (stack_item) { + stack_item->filter->matched = 0; + stack_item->filter->processed = 0; + } ++ free(stack_item); + stack_pop(_stack); +} + ++// reset and pop all the stack_item_t ++static void stack_pop_all_reset(stack_t *_stack) ++{ ++ if (_stack == NULL) ++ return; ++ ++ while (!stack_is_empty(_stack)) ++ stack_pop_reset(_stack); ++} ++ +// this funtion gets full path and checks it against filter +// returns 1 for keeping the file and 0 for dropping it +int filter_check(const char *_path) +{ -+ rpm_filter_t *filter = global_filter; ++ if (_path == NULL) { ++ msg(LOG_ERR, "filter_check: path is NULL, something is wrong!"); ++ return 0; ++ } + ++ rpm_filter_t *filter = global_filter; + char *path = strdup(_path); + size_t path_len = strlen(_path); + size_t offset = 0; @@ -516,22 +582,20 @@ index 00000000..806989e9 + + stack_push_vars(&stack, level, offset, filter); + -+ // start of the non recursive check + while(!stack_is_empty(&stack)) { -+ + int matched = 0; + filter->processed = 1; + + // this is starting branch of the algo -+ // assuming that in root filter filter_> path is NULL ++ // assuming that in root filter filter->path is NULL + if (filter->path == NULL) { + list_item_t *item = list_get_first(&filter->list); -+ + // push all the descendants to the stack + for (; item != NULL ; item = item->next) { -+ rpm_filter_t *next_filter = ((stack_item_t*)item->data)->filter; ++ rpm_filter_t *next_filter = (rpm_filter_t*)item->data; + stack_push_vars(&stack, level+1, offset, next_filter); + } ++ + // usual branch, start with processing + } else { + // wildcard contition @@ -544,9 +608,8 @@ index 00000000..806989e9 + char *path_lim, *path_old_lim; + path_lim = path_old_lim = path+offset; + -+ // there can be wildcard in the dir name so we will -+ // how many chars will be eaten -+ // the intention is count the offset down below ++ // there can be wildcard in the dir name as well ++ // we need to count how many chars can be eaten by wildcard + while(1) { + filter_lim = strchr(filter_lim, '/'); + path_lim = strchr(path_lim, '/'); @@ -583,14 +646,12 @@ index 00000000..806989e9 + offset = path_old_lim - path+offset; + } + } else { -+ // match normal path or just specific part of it ++ // match normal path or just specific part of it + matched = !strncmp(path+offset, filter->path, filter->len); -+ + if (matched) + offset += filter->len; + } + -+ + if (matched) { + level++; + filter->matched = 1; @@ -614,7 +675,7 @@ index 00000000..806989e9 + + // push descendants to the stack + for (; item != NULL ; item = item->next) { -+ rpm_filter_t *next_filter = ((stack_item_t*)item->data)->filter; ++ rpm_filter_t *next_filter = (rpm_filter_t*)item->data; + stack_push_vars(&stack, level, offset, next_filter); + } + @@ -623,7 +684,6 @@ index 00000000..806989e9 + } + + stack_item_t * stack_item = NULL; -+ + // popping processed filters from the top of the stack + do { + if (stack_item) { @@ -637,7 +697,8 @@ index 00000000..806989e9 + goto end; + } + -+ stack_set_pop(&stack); ++ // reset processed flag ++ stack_pop_reset(&stack); + } + + stack_item = (stack_item_t*)stack_top(&stack); @@ -653,8 +714,7 @@ index 00000000..806989e9 + +end: + // Clean up the stack -+ while(!stack_is_empty(&stack)) -+ {stack_set_pop(&stack);} ++ stack_pop_all_reset(&stack); + stack_destroy(&stack); + free(path); + return res; @@ -663,7 +723,7 @@ index 00000000..806989e9 +// load rpm filter configuration file and fill the filter structure +int filter_load_file(void) +{ -+ ++ int res = 0; + FILE *stream = fopen(RPM_FILTER_FILE, "r"); + + if (stream == NULL) { @@ -676,11 +736,10 @@ index 00000000..806989e9 + char * line = NULL; + long line_number = 0; + int last_level = 0; -+ rpm_filter_t * last_filter = global_filter; + + stack_t stack; + stack_init(&stack); -+ stack_push_vars(&stack, last_level, 0, last_filter); ++ stack_push_vars(&stack, last_level, 0, global_filter); + + while ((nread = getline(&line, &len, stream)) != -1) { + line_number++; @@ -739,10 +798,7 @@ index 00000000..806989e9 + msg(LOG_ERR, "filter_load_file: cannot parse line number %ld, \"%s\"", line_number, line); + free(line); + line = NULL; -+ fclose(stream); -+ stack_destroy(&stack); -+ filter_destroy(); -+ return 1; ++ goto bad; + } + + rpm_filter_t * filter = filter_create_obj(); @@ -753,50 +809,67 @@ index 00000000..806989e9 + filter->type = type; + } + -+ // comparing level of indetantion between the last line and the current one ++ // comparing level of indetantion between the last line and the current one + last_level = ((stack_item_t*)stack_top(&stack))->level; + if (level == last_level) { -+ stack_pop(&stack); + -+ stack_push_vars(&((stack_item_t*)stack_top(&stack))->filter->list, level, 0, filter); ++ // since we are at the same level as filter before ++ // we need to pop the previous filter from the top ++ stack_pop_vars(&stack); ++ ++ // pushing filter to the list of top's children list ++ list_prepend(&((stack_item_t*)stack_top(&stack))->filter->list, NULL, (void*)filter); ++ ++ // pushing filter to the top of the stack + stack_push_vars(&stack, level, 0, filter); + + } else if (level == last_level + 1) { ++ // this filter has higher level tha privious one ++ // we wont do pop just push ++ ++ // pushing filter to the list of top's children list ++ list_prepend(&((stack_item_t*)stack_top(&stack))->filter->list, NULL, (void*)filter); + -+ stack_push_vars(&((stack_item_t*)stack_top(&stack))->filter->list, level, 0, filter); ++ // pushing filter to the top of the stack + stack_push_vars(&stack, level, 0, filter); + + } else if (level < last_level){ ++ // level of indentation dropped ++ // we need to pop ++ // +1 is meant for getting rid of the current level so we can again push + for (int i = 0 ; i < last_level - level + 1; i++) { -+ stack_pop(&stack); ++ stack_pop_vars(&stack); + } + -+ stack_push_vars(&((stack_item_t*)stack_top(&stack))->filter->list, level, 0, filter); ++ // pushing filter to the list of top's children list ++ list_prepend(&((stack_item_t*)stack_top(&stack))->filter->list, NULL, (void*)filter); ++ ++ // pushing filter to the top of the stack + stack_push_vars(&stack, level, 0, filter); ++ + } else { + msg(LOG_ERR, "filter_load_file: paring error line: %ld, \"%s\"", line_number, line); ++ filter_destroy_obj(filter); + free(line); -+ line = NULL; -+ fclose(stream); -+ stack_destroy(&stack); -+ filter_destroy(); -+ return 1; ++ goto bad; + } + -+ last_filter = filter; -+ + free(line); + line = NULL; + } + ++ goto good; ++bad: ++ res = 1; ++ ++good: + fclose(stream); ++ stack_pop_all_vars(&stack); + stack_destroy(&stack); + if (global_filter->list.count == 0) { -+ filter_destroy(); + msg(LOG_ERR, "filter_load_file: no valid filter provided in %s", RPM_FILTER_FILE); -+ return 1; + } -+ return 0; ++ return res; +} diff --git a/src/library/rpm-filter.h b/src/library/rpm-filter.h new file mode 100644 @@ -873,10 +946,10 @@ index 00000000..2c49d338 +#endif // FILTER_H_ diff --git a/src/library/stack.c b/src/library/stack.c new file mode 100644 -index 00000000..2b04db86 +index 00000000..93141b2c --- /dev/null +++ b/src/library/stack.c -@@ -0,0 +1,82 @@ +@@ -0,0 +1,89 @@ +/* +* stack.c - generic stack impementation +* Copyright (c) 2023 Red Hat Inc., Durham, North Carolina. @@ -909,21 +982,25 @@ index 00000000..2b04db86 +{ + if (_stack == NULL) + return; ++ + list_init(_stack); +} + +// free all the resources from the stack +void stack_destroy(stack_t *_stack) +{ -+ + if (_stack == NULL) + return; ++ + list_empty(_stack); +} + +// push to the top of the stack +void stack_push(stack_t *_stack, void *_data) +{ ++ if (_stack == NULL) ++ return; ++ + list_prepend(_stack, NULL, (void *)_data); +} + @@ -935,6 +1012,7 @@ index 00000000..2b04db86 + + list_item_t *first = _stack->first; + _stack->first = first->next; ++ first->data = NULL; + list_destroy_item(&first); + _stack->count--; + @@ -945,7 +1023,7 @@ index 00000000..2b04db86 +int stack_is_empty(stack_t *_stack) +{ + if (_stack == NULL) -+ return 1; ++ return -1; + + if (_stack->count == 0) + return 1; @@ -956,6 +1034,8 @@ index 00000000..2b04db86 +// return top of the stack without popping +const void *stack_top(stack_t *_stack) +{ ++ if (_stack == NULL) ++ return NULL; + + return _stack->first ? _stack->first->data : NULL; +} diff --git a/SPECS/fapolicyd.spec b/SPECS/fapolicyd.spec index 97a00cb..534666e 100644 --- a/SPECS/fapolicyd.spec +++ b/SPECS/fapolicyd.spec @@ -5,7 +5,7 @@ Summary: Application Whitelisting Daemon Name: fapolicyd Version: 1.1.3 -Release: 10%{?dist} +Release: 12%{?dist} License: GPLv3+ URL: http://people.redhat.com/sgrubb/fapolicyd Source0: https://people.redhat.com/sgrubb/fapolicyd/%{name}-%{version}.tar.gz @@ -271,7 +271,7 @@ fi %selinux_relabel_post -s %{selinuxtype} %changelog -* Thu Jan 12 2023 Radovan Sroka - 1.1.3-9 +* Mon Jan 30 2023 Radovan Sroka - 1.1.3-12 RHEL 8.8.0 ERRATUM - statically linked app can execute untrusted app Resolves: rhbz#2088349 @@ -279,6 +279,8 @@ Resolves: rhbz#2088349 Resolves: rhbz#2103352 - Cannot execute /usr/libexec/grepconf.sh when falcon-sensor is enabled Resolves: rhbz#2087040 +- fapolicyd: Introduce filtering of rpmdb +Resolves: rhbz#2165645 * Fri Aug 05 2022 Radovan Sroka - 1.1.3-8 RHEL 8.7.0 ERRATUM