[PATCH] filter: Implement an in-list() filter
The in-list filter allows us to do simple file-based black- or whitelisting. The syntax is: filter f_whitelist { in-list("/path/to/file.list", value("PROGRAM")); }; Implementing a blacklist is as simple as negating the in-list() filter. The filter works by pulling in the whole file (one entry per line) and putting it into a tree structure, and comparing against that on eval. Signed-off-by: Gergely Nagy <algernon@balabit.hu> --- Makefile.am | 2 + lib/filter/Makefile.am | 2 + lib/filter/filter-expr-grammar.ym | 21 ++++++- lib/filter/filter-expr-parser.c | 1 + lib/filter/filter-in-list.c | 95 +++++++++++++++++++++++++++++++ lib/filter/filter-in-list.h | 33 +++++++++++ lib/filter/tests/Makefile.am | 13 ++++- lib/filter/tests/test.list | 1 + lib/filter/tests/test_filters_in_list.c | 53 +++++++++++++++++ 9 files changed, 215 insertions(+), 6 deletions(-) create mode 100644 lib/filter/filter-in-list.c create mode 100644 lib/filter/filter-in-list.h create mode 100644 lib/filter/tests/test.list create mode 100644 lib/filter/tests/test_filters_in_list.c diff --git a/Makefile.am b/Makefile.am index 265de8d..345ee76 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,8 @@ SUBDIRS = AM_MAKEFLAGS = --no-print-directory +AM_TESTS_ENVIRONMENT = top_srcdir="$(top_srcdir)" + ACLOCAL_AMFLAGS = -I m4 --install EXTRA_DIST = ${BUILT_SOURCES} VERSION \ diff --git a/lib/filter/Makefile.am b/lib/filter/Makefile.am index 7425491..aeb4f2b 100644 --- a/lib/filter/Makefile.am +++ b/lib/filter/Makefile.am @@ -2,6 +2,7 @@ filter_headers = \ lib/filter/filter-expr.h \ lib/filter/filter-op.h \ lib/filter/filter-cmp.h \ + lib/filter/filter-in-list.h \ lib/filter/filter-tags.h \ lib/filter/filter-netmask.h \ lib/filter/filter-call.h \ @@ -14,6 +15,7 @@ filter_sources = \ lib/filter/filter-expr.c \ lib/filter/filter-op.c \ lib/filter/filter-cmp.c \ + lib/filter/filter-in-list.c \ lib/filter/filter-tags.c \ lib/filter/filter-netmask.c \ lib/filter/filter-call.c \ diff --git a/lib/filter/filter-expr-grammar.ym b/lib/filter/filter-expr-grammar.ym index 1d4fec4..a41932b 100644 --- a/lib/filter/filter-expr-grammar.ym +++ b/lib/filter/filter-expr-grammar.ym @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2011 BalaBit IT Ltd, Budapest, Hungary + * Copyright (c) 2002-2013 BalaBit IT Ltd, Budapest, Hungary * Copyright (c) 1998-2011 Balázs Scheidler * * This library is free software; you can redistribute it and/or @@ -34,6 +34,7 @@ #include "filter/filter-netmask.h" #include "filter/filter-op.h" #include "filter/filter-cmp.h" +#include "filter/filter-in-list.h" #include "filter/filter-tags.h" #include "filter/filter-call.h" #include "filter/filter-re.h" @@ -55,7 +56,7 @@ FilterRE *last_re_filter; /* INCLUDE_DECLS */ -%token KW_PROGRAM +%token KW_PROGRAM KW_IN_LIST %left ';' %left KW_OR @@ -96,7 +97,21 @@ filter_simple_expr | KW_FILTER '(' string ')' { $$ = filter_call_new($3, configuration); free($3); } | KW_NETMASK '(' string ')' { $$ = filter_netmask_new($3); free($3); } | KW_TAGS '(' string_list ')' { $$ = filter_tags_new($3); } - | KW_PROGRAM '(' string + | KW_IN_LIST '(' string string ')' + { + const gchar *p = $4; + if (p[0] == '$') + { + msg_warning("Value references in filters should not use the '$' prefix, those are only needed in templates", + evt_tag_str("value", $4), + NULL); + p++; + } + $$ = filter_in_list_new($3, p); + free($3); + free($4); + } + | KW_PROGRAM '(' string { last_re_filter = (FilterRE *) filter_re_new(LM_V_PROGRAM); } diff --git a/lib/filter/filter-expr-parser.c b/lib/filter/filter-expr-parser.c index c2c2e7b..781220a 100644 --- a/lib/filter/filter-expr-parser.c +++ b/lib/filter/filter-expr-parser.c @@ -55,6 +55,7 @@ static CfgLexerKeyword filter_expr_keywords[] = { { "match", KW_MATCH }, { "netmask", KW_NETMASK }, { "tags", KW_TAGS, 0x0301 }, + { "in_list", KW_IN_LIST, 0x0305 }, { "type", KW_TYPE, 0x0300 }, { "value", KW_VALUE, 0x0300 }, diff --git a/lib/filter/filter-in-list.c b/lib/filter/filter-in-list.c new file mode 100644 index 0000000..6736196 --- /dev/null +++ b/lib/filter/filter-in-list.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2013 BalaBit IT Ltd, Budapest, Hungary + * Copyright (c) 2013 Gergely Nagy <algernon@balabit.hu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * As an additional exemption you are allowed to compile & link against the + * OpenSSL libraries as published by the OpenSSL project. See the file + * COPYING for details. + * + */ + +#include "filter-in-list.h" +#include "logmsg.h" +#include "misc.h" + +#include <stdlib.h> +#include <stdio.h> + +typedef struct _FilterInList +{ + FilterExprNode super; + NVHandle value_handle; + GTree *tree; +} FilterInList; + +static gboolean +filter_in_list_eval(FilterExprNode *s, LogMessage **msgs, gint num_msg) +{ + FilterInList *self = (FilterInList *)s; + LogMessage *msg = msgs[0]; + const gchar *value; + gssize len = 0; + + value = log_msg_get_value(msg, self->value_handle, &len); + APPEND_ZERO(value, value, len); + + return (g_tree_lookup(self->tree, value) != NULL) ^ s->comp; +} + +static void +filter_in_list_free(FilterExprNode *s) +{ + FilterInList *self = (FilterInList *)s; + + g_tree_unref(self->tree); +} + +FilterExprNode * +filter_in_list_new(const gchar *list_file, const gchar *property) +{ + FilterInList *self; + FILE *stream; + size_t n; + gchar *line = NULL; + + stream = fopen(list_file, "r"); + if (!stream) + { + msg_error("Error opening in-list filter list file", + evt_tag_str("file", list_file), + evt_tag_errno("errno", errno), + NULL); + return NULL; + } + + self = g_new0(FilterInList, 1); + filter_expr_node_init(&self->super); + self->value_handle = log_msg_get_value_handle(property); + self->tree = g_tree_new((GCompareFunc) strcmp); + + while (getline(&line, &n, stream) != -1) + { + line[strlen(line) - 1] = '\0'; + g_tree_insert(self->tree, line, GINT_TO_POINTER(1)); + line = NULL; + } + fclose(stream); + + self->super.eval = filter_in_list_eval; + self->super.free_fn = filter_in_list_free; + return &self->super; +} diff --git a/lib/filter/filter-in-list.h b/lib/filter/filter-in-list.h new file mode 100644 index 0000000..9cf3c0d --- /dev/null +++ b/lib/filter/filter-in-list.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013 BalaBit IT Ltd, Budapest, Hungary + * Copyright (c) 2013 Gergely Nagy <algernon@balabit.hu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * As an additional exemption you are allowed to compile & link against the + * OpenSSL libraries as published by the OpenSSL project. See the file + * COPYING for details. + * + */ + +#ifndef FILTER_IN_LIST_H_INCLUDED +#define FILTER_IN_LIST_H_INCLUDED + +#include "filter-expr.h" + +FilterExprNode *filter_in_list_new(const gchar *list_file, + const gchar *property); + +#endif diff --git a/lib/filter/tests/Makefile.am b/lib/filter/tests/Makefile.am index 2b1e11a..3d72441 100644 --- a/lib/filter/tests/Makefile.am +++ b/lib/filter/tests/Makefile.am @@ -1,13 +1,20 @@ lib_filter_tests_TESTS = \ - lib/filter/tests/test_filters + lib/filter/tests/test_filters \ + lib/filter/tests/test_filters_in_list check_PROGRAMS += ${lib_filter_tests_TESTS} lib_filter_tests_test_filters_CFLAGS = $(TEST_CFLAGS) \ -I${top_srcdir}/lib/filter/tests - lib_filter_tests_test_filters_LDADD = $(TEST_LDADD) \ $(PREOPEN_SYSLOGFORMAT) - lib_filter_tests_test_filters_SOURCES = \ lib/filter/tests/test_filters.c + +lib_filter_tests_test_filters_in_list_CFLAGS = $(TEST_CFLAGS) \ + -I${top_srcdir}/lib/filter/tests +lib_filter_tests_test_filters_in_list_LDADD = $(TEST_LDADD) \ + $(PREOPEN_SYSLOGFORMAT) + +EXTRA_DIST += \ + lib/filter/tests/test.list diff --git a/lib/filter/tests/test.list b/lib/filter/tests/test.list new file mode 100644 index 0000000..dca8351 --- /dev/null +++ b/lib/filter/tests/test.list @@ -0,0 +1 @@ +test-program diff --git a/lib/filter/tests/test_filters_in_list.c b/lib/filter/tests/test_filters_in_list.c new file mode 100644 index 0000000..9844fa2 --- /dev/null +++ b/lib/filter/tests/test_filters_in_list.c @@ -0,0 +1,53 @@ +#include <stdlib.h> +#include <glib.h> + +#include "cfg.h" +#include "messages.h" +#include "syslog-names.h" +#include "logmsg.h" +#include "apphook.h" +#include "plugin.h" +#include "filter/filter-in-list.h" + +#include "testutils.h" + +static MsgFormatOptions parse_options; + +static LogMessage * +create_test_message (void) +{ + const gchar *log_message = "<15>Sep 4 15:03:55 localhost test-program[3086]: some random message"; + + return log_msg_new(log_message, strlen(log_message), NULL, &parse_options); +} + +int +main(int argc G_GNUC_UNUSED, char *argv[] G_GNUC_UNUSED) +{ + LogMessage *msg; + FilterExprNode *filter_node; + char *top_srcdir = getenv("top_srcdir"); + gchar *list_file; + + app_startup(); + + configuration = cfg_new(0x0305); + plugin_load_module("syslogformat", configuration, NULL); + msg_format_options_defaults(&parse_options); + msg_format_options_init(&parse_options, configuration); + + assert_not_null(top_srcdir, "The $top_srcdir environment variable MUST NOT be empty!"); + + list_file = g_strdup_printf("%s/lib/filter/tests/test.list", top_srcdir); + filter_node = filter_in_list_new(list_file, "PROGRAM"); + assert_not_null(filter_node, "Constructing an in-list filter"); + g_free (list_file); + + msg = create_test_message(); + + assert_true(filter_expr_eval(filter_node, msg), "in-list filter matches"); + + app_shutdown(); + + return 0; +} -- 1.7.10.4
Great stuff in here. I wasjust wondering whethet substring matches aren't the expectrd behaviour here?
Balazs Scheidler <bazsi77@gmail.com> writes:
Great stuff in here. I wasjust wondering whethet substring matches aren't the expectrd behaviour here?
Not for the use cases the feature was developed for. If there's demand for it, it's easy to add a setting to turn on substring matching (or even regexp matching). -- |8]
participants (2)
-
Balazs Scheidler
-
Gergely Nagy