Browse Source

Merge branch 'master' into source-files

master
Alex 'AdUser' Z 8 years ago
parent
commit
990a75a2d0
  1. 23
      CMakeLists.txt
  2. 42
      ChangeLog
  3. 6
      configs/conf-available/10-backend-exec-ipfw.conf
  4. 2
      configs/conf-available/10-backend-exec-ipset.conf
  5. 2
      configs/conf-available/10-backend-redis.conf
  6. 2
      configs/conf-available/15-filter-pcre.conf
  7. 2
      configs/conf-available/15-filter-preg.conf
  8. 5
      configs/f2b.conf.in
  9. 6
      debian/changelog
  10. 3
      filters/ssh.preg
  11. 15
      src/CMakeLists.txt
  12. 4
      src/backends/CMakeLists.txt
  13. 5
      src/backends/exec.c
  14. 13
      src/backends/redis.c
  15. 4
      src/cmsg.h
  16. 27
      src/commands.c
  17. 2
      src/common.h
  18. 81
      src/daemon.c
  19. 29
      src/filter.c
  20. 3
      src/filter.h
  21. 4
      src/filters/CMakeLists.txt
  22. 14
      src/filters/filter.h
  23. 34
      src/filters/pcre.c
  24. 36
      src/filters/preg.c
  25. 15
      src/ipaddr.c
  26. 3
      src/ipaddr.h
  27. 26
      src/jail.c
  28. 2
      src/jail.h
  29. 6
      src/log.c
  30. 13
      src/logfile.c
  31. 4
      t/CMakeLists.txt

23
CMakeLists.txt

@ -1,5 +1,5 @@
set(CNAME "f2b")
set(VERSION 0.2)
set(VERSION 0.3)
project(${CNAME} C)
cmake_minimum_required(VERSION 2.6)
@ -8,9 +8,9 @@ include(CTest)
option(WITH_CLIENT "Simple client for configuring daemon" ON)
option(WITH_CSOCKET "Unix control socket support for daemon" ON)
option(WITH_HARDENING "Enable hardening options" OFF)
option(WITH_HARDENING "Enable hardening options" ON)
option(WITH_PCRE "Build pcre-compatible filter" ON)
option(WITH_REDIS "Build redis backend" ON)
option(WITH_REDIS "Build redis backend" OFF)
if (NOT DEFINED CMAKE_INSTALL_PREFIX)
set(CMAKE_INSTALL_PREFIX "/usr")
@ -22,7 +22,12 @@ endif ()
include(GNUInstallDirs)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -pedantic -std=c99")
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
add_definitions("-D_XOPEN_SOURCE=600")
else ()
include_directories(AFTER SYSTEM "/usr/local/include")
link_directories("/usr/local/lib")
endif ()
if (WITH_HARDENING)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wformat -Wformat-security -Werror=format-security" )
@ -52,8 +57,16 @@ add_subdirectory(t)
set_property(DIRECTORY "t" PROPERTY COMPILE_FLAGS "-g;-ggdb;-Wall;-Wextra;-pedantic;-O0")
install(DIRECTORY "filters" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/f2b")
install(DIRECTORY "configs/" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/f2b")
install(FILES "configs/f2b.conf.sample" DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}/f2b/" RENAME "f2b.conf")
file(GLOB_RECURSE CONFIGS "*.conf.in")
foreach(CONFIG ${CONFIGS})
string(REPLACE ".conf.in" ".conf" GENERATED ${CONFIG})
configure_file(${CONFIG} ${GENERATED})
endforeach()
install(DIRECTORY "configs/" DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}/f2b"
FILES_MATCHING PATTERN "*.conf")
install(FILES "configs/f2b.conf" DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}/f2b"
RENAME "f2b.conf.sample")
file(MAKE_DIRECTORY "${CMAKE_INSTALL_SYSCONFDIR}/f2b/conf-enabled")
add_custom_target("dist" COMMAND
"git" "archive" "--format=tar.gz"

42
ChangeLog

@ -0,0 +1,42 @@
# Change Log
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## Unreleased
## [0.3] - 2016-09-12
### Added
* "jail <jail> regex stats" command
* "jail <jail> regex add" command
* apply CMAKE_INSTALL_PREFIX to configs
* added config for exec backend for ipfw
* redis backend (experimental)
* added config reload
* log file rotation
### Changed
* enable 'icase' for filters by default
* enable 'sharing' for backends by default
* tune configs location
* enable hardening in build opts by default
* fix ssh filter patterns
* use strl*() instead snprintf()/strncpy() in backends
* rename tests utils
* print date/time in log file
* disable buffering for logfile
* add stats() funtion to filter's api
### Fixed
* fix segfault in preg filter
* fix cppcheck warnings
* fix bsd build
* fix termination of daemon
## [0.2] - 2016-08-21
* Initial public release

6
configs/conf-available/10-backend-exec-ipfw.conf

@ -0,0 +1,6 @@
[backend:exec-ipfw]
load = libf2b_backend_exec.so
ban = /sbin/ipfw table <ID> add <IP>
unban = /sbin/ipfw table <ID> delete <IP>
timeout = 2
shared = yes

2
configs/conf-available/10-backend-exec-ipset.conf

@ -8,4 +8,4 @@ ban = /sbin/ipset -! add <ID> <IP>
check = /sbin/ipset -! test <ID> <IP>
unban = /sbin/ipset -! del <ID> <IP>
timeout = 2
shared = no
shared = yes

2
configs/conf-available/10-backend-redis.conf

@ -1,6 +1,6 @@
[backend:redis]
load = libf2b_backend_redis.so
shared = no
shared = yes
timeout = 2
host = 127.0.0.1
port = 6379

2
configs/conf-available/15-filter-pcre.conf

@ -1,5 +1,5 @@
[filter:pcre]
load = libf2b_filter_pcre.so
icase = no
icase = yes
study = yes
usejit = no

2
configs/conf-available/15-filter-preg.conf

@ -1,3 +1,3 @@
[filter:preg]
load = libf2b_filter_preg.so
icase = no
icase = yes

5
configs/f2b.conf.sample → configs/f2b.conf.in

@ -1,5 +1,5 @@
[main]
includes = /etc/f2b/conf-enabled
includes = ${CMAKE_INSTALL_FULL_SYSCONFDIR}/f2b/conf-enabled
pidfile = /var/run/f2b.pid
logdest = syslog
loglevel = info
@ -17,9 +17,8 @@ incr_bantime = 0.0
incr_findtime = 0.0
maxretry = 5
source = files:/var/log/messages
; filter = preg:/etc/f2b/filters/$someservice.preg
backend = exec-ipset:banned
[jail:ssh]
source = files:/var/log/auth.log
filter = preg:/etc/f2b/filters/ssh.preg
filter = preg:${CMAKE_INSTALL_FULL_DATAROOTDIR}/f2b/filters/ssh.preg

6
debian/changelog vendored

@ -1,3 +1,9 @@
f2b (0.3-1) unstable; urgency=medium
* new version
-- Alex 'AdUser' Z <ad_user@runbox.com> Tue, 13 Sep 2016 16:55:43 +1000
f2b (0.2-1) unstable; urgency=low
* Initial release

3
filters/ssh.preg

@ -5,7 +5,8 @@ refused connect from [[:print:]]+ \(<HOST>\)
Received disconnect from <HOST>: [0-9]*: [[:print:]]+: Auth fail
Did not receive identification string from <HOST>
Invalid user [[:print:]]+ from <HOST>
Connection closed by <HOST> \[preauth\]
Connection closed by <HOST>( port [0-9]+)? \[preauth\]
Postponed keyboard-interactive for invalid user [[:print:]]+ from <HOST> port [0-9]+
User [[:print:]]+ from <HOST> not allowed because not listed in AllowUsers
User [[:print:]]+ from <HOST> not allowed because listed in DenyUsers
User [[:print:]]+ from <HOST> not allowed because not in any group

15
src/CMakeLists.txt

@ -8,7 +8,6 @@ if (WITH_CSOCKET)
endif ()
add_executable("f2b" ${SOURCES})
target_link_libraries(f2b "dl")
install(TARGETS "f2b" RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR})
@ -19,12 +18,18 @@ if (WITH_CLIENT)
endif ()
set(SOURCES "strlcpy.c" "backend-test.c" "log.c" "config.c" "backend.c")
add_executable("backend-test" ${SOURCES})
target_link_libraries("backend-test" "dl")
add_executable("f2b-backend-test" ${SOURCES})
set(SOURCES "strlcpy.c" "filter-test.c" "log.c" "config.c" "filter.c")
add_executable("filter-test" ${SOURCES})
target_link_libraries("filter-test" "dl")
add_executable("f2b-filter-test" ${SOURCES})
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
target_link_libraries(f2b "dl")
target_link_libraries("f2b-backend-test" "dl")
target_link_libraries("f2b-filter-test" "dl")
endif ()
install(TARGETS "f2b-filter-test" "f2b-backend-test" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
add_subdirectory("backends")
add_subdirectory("filters")

4
src/backends/CMakeLists.txt

@ -1,12 +1,12 @@
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(BACKENDS "")
add_library("f2b_backend_exec" MODULE "exec.c")
add_library("f2b_backend_exec" MODULE "exec.c" "../strlcpy.c")
list(APPEND BACKENDS "f2b_backend_exec")
find_library(REDIS_FOUND "pcre")
if (WITH_REDIS AND REDIS_FOUND)
add_library("f2b_backend_redis" MODULE "redis.c")
add_library("f2b_backend_redis" MODULE "redis.c" "../strlcpy.c")
target_link_libraries("f2b_backend_redis" "hiredis")
list(APPEND BACKENDS "f2b_backend_redis")
endif ()

5
src/backends/exec.c

@ -13,6 +13,8 @@
#include <sys/wait.h>
#include <unistd.h>
#include "../strlcpy.h"
#include "backend.h"
#include "shared.c"
@ -157,7 +159,7 @@ create(const char *id) {
if ((cfg = calloc(1, sizeof(cfg_t))) == NULL)
return NULL;
snprintf(cfg->name, sizeof(cfg->name), "%s", id);
strlcpy(cfg->name, id, sizeof(cfg->name));
return cfg;
}
@ -268,6 +270,7 @@ check(cfg_t *cfg, const char *ip) {
bool
ping(cfg_t *cfg) {
assert(cfg != NULL);
(void)(cfg); /* suppress warning about unused variable */
return true;
}

13
src/backends/redis.c

@ -17,6 +17,8 @@
#include <hiredis/hiredis.h>
#include "../strlcpy.h"
#include "backend.h"
#include "shared.c"
@ -98,8 +100,9 @@ create(const char *id) {
if ((cfg = calloc(1, sizeof(cfg_t))) == NULL)
return NULL;
snprintf(cfg->name, sizeof(cfg->name), "%s", id);
snprintf(cfg->hash, sizeof(cfg->hash), "f2b-banned-%s", id);
strlcpy(cfg->name, id, sizeof(cfg->name));
strlcpy(cfg->hash, "f2b-banned-", sizeof(cfg->hash));
strlcat(cfg->hash, id, sizeof(cfg->hash));
return cfg;
}
@ -119,7 +122,7 @@ config(cfg_t *cfg, const char *key, const char *value) {
return true;
}
if (strcmp(key, "host") == 0) {
snprintf(cfg->host, sizeof(cfg->host), "%s", value);
strlcpy(cfg->host, value, sizeof(cfg->host));
return true;
}
if (strcmp(key, "port") == 0) {
@ -131,7 +134,7 @@ config(cfg_t *cfg, const char *key, const char *value) {
return true;
}
if (strcmp(key, "password") == 0) {
snprintf(cfg->password, sizeof(cfg->password), "%s", value);
strlcpy(cfg->password, value, sizeof(cfg->password));
return true;
}
@ -239,7 +242,7 @@ ping(cfg_t *cfg) {
if (reply) {
bool result = true;
if (reply->type == REDIS_REPLY_ERROR) {
snprintf(cfg->error, sizeof(cfg->error), "%s", reply->str);
strlcpy(cfg->error, reply->str, sizeof(cfg->error));
result = false;
}
freeReplyObject(reply);

4
src/cmsg.h

@ -3,7 +3,7 @@
#include <sys/uio.h>
#define DATA_LEN_MAX 496 /* 512 - 16 bytes of header */
#define DATA_LEN_MAX 1476 /* 1500 - (16 bytes of cmsg header + 8 bytes of udp) */
#define DATA_ARGS_MAX 6 /* number of args in data */
#define F2B_PROTO_VER 1
@ -21,6 +21,8 @@ enum f2b_cmsg_type {
CMD_JAIL_IP_SHOW,
CMD_JAIL_IP_BAN,
CMD_JAIL_IP_RELEASE,
CMD_JAIL_REGEX_STATS,
CMD_JAIL_REGEX_ADD,
CMD_MAX_NUMBER,
};

27
src/commands.c

@ -12,7 +12,7 @@ struct f2b_cmd_t {
const char *help;
const char *tokens[CMD_TOKENS_MAX];
char *data;
} commands[] = {
} commands[CMD_MAX_NUMBER] = {
[CMD_NONE] = {
.tokens = { NULL },
.help = "Unspecified command"
@ -65,6 +65,14 @@ struct f2b_cmd_t {
.tokens = { "jail", "<jailname>", "release", "<ip>", NULL },
.help = "Forcefully release some ip in given jail",
},
[CMD_JAIL_REGEX_STATS] = {
.tokens = { "jail", "<jailname>", "regex", "stats", NULL },
.help = "Show matches stats for jail regexps",
},
[CMD_JAIL_REGEX_ADD] = {
.tokens = { "jail", "<jailname>", "regex", "add", "<regex>", NULL },
.help = "Add new regexp to jail",
},
};
void
@ -158,6 +166,23 @@ f2b_cmd_parse(const char *src, char *buf, size_t buflen) {
strlcat(buf, "\n", buflen);
return CMD_JAIL_IP_RELEASE;
}
if (tokenc == 4 && strcmp(tokens[2], "regex") == 0 && strcmp(tokens[3], "stats") == 0) {
return CMD_JAIL_REGEX_STATS;
}
if (tokenc >= 5 && strcmp(tokens[2], "regex") == 0 && strcmp(tokens[3], "add") == 0) {
/* TODO: rewrite, this version is very error-prone */
char *regex = strstr(src, "add");
regex += strlen("add");
while (isblank(*regex))
regex++;
if (*regex == '\0') {
/* empty regex */
return CMD_NONE;
}
strlcat(buf, regex, buflen);
strlcat(buf, "\n", buflen);
return CMD_JAIL_REGEX_ADD;
}
}
return CMD_NONE;

2
src/common.h

@ -7,11 +7,11 @@
#ifndef F2B_COMMON_H_
#define F2B_COMMON_H_
#include <alloca.h>
#include <assert.h>
#include <ctype.h>
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdarg.h>
#include <stdbool.h>

81
src/daemon.c

@ -85,7 +85,6 @@ f2b_cmsg_process(const f2b_cmsg_t *msg, char *res, size_t ressize) {
void
f2b_cmsg_process(const f2b_cmsg_t *msg, char *res, size_t ressize) {
const char *args[DATA_ARGS_MAX];
const char *fmt;
f2b_jail_t *jail = NULL;
f2b_ipaddr_t *addr = NULL;
char line[LINE_MAX];
@ -94,10 +93,13 @@ f2b_cmsg_process(const f2b_cmsg_t *msg, char *res, size_t ressize) {
assert(res != NULL);
assert(msg->type < CMD_MAX_NUMBER);
if (msg->type == CMD_NONE)
return;
memset(args, 0x0, sizeof(args));
f2b_cmsg_extract_args(msg, args);
if (msg->type >= CMD_JAIL_STATUS && msg->type <= CMD_JAIL_IP_RELEASE) {
if (msg->type >= CMD_JAIL_STATUS && msg->type <= CMD_MAX_NUMBER) {
if (args[0] == NULL) {
strlcpy(res, "can't find jail: no args\n", ressize);
return;
@ -113,10 +115,6 @@ f2b_cmsg_process(const f2b_cmsg_t *msg, char *res, size_t ressize) {
strlcpy(res, "can't find ip: no args", ressize);
return;
}
if ((addr = f2b_addrlist_lookup(jail->ipaddrs, args[1])) == NULL) {
snprintf(res, ressize, "can't find ip '%s' in jail '%s'\n", args[1], args[0]);
return;
}
}
if (msg->type == CMD_PING) {
@ -139,38 +137,47 @@ f2b_cmsg_process(const f2b_cmsg_t *msg, char *res, size_t ressize) {
strlcat(res, line, ressize);
}
} else if (msg->type == CMD_JAIL_STATUS) {
fmt = "name: %s\n"
"enabled: %s\n"
"maxretry: %d\n"
"times:\n"
" bantime: %d\n"
" findtime: %d\n"
" expiretime: %d\n"
"incr:\n"
" bantime: %.1f\n"
" findtime: %.1f\n"
"stats:\n"
" banned: %d\n"
" matched: %d\n";
snprintf(res, ressize, fmt, jail->name, jail->enabled ? "yes" : "no", jail->maxretry,
jail->bantime, jail->findtime, jail->expiretime,
jail->incr_bantime, jail->incr_findtime,
jail->bancount, jail->matchcount);
f2b_jail_get_status(jail, res, ressize);
} else if (msg->type == CMD_JAIL_IP_SHOW) {
fmt = "ipaddr: %s\n"
"banned: %s\n"
"bancount: %d\n"
"lastseen: %d\n"
"banned_at: %d\n"
"release_at: %d\n";
snprintf(res, ressize, fmt, addr->text, addr->banned ? "yes" : "no",
addr->bancount, addr->lastseen, addr->banned_at, addr->release_at);
if ((addr = f2b_addrlist_lookup(jail->ipaddrs, args[1])) != NULL) {
f2b_ipaddr_status(addr, res, ressize);
} else {
snprintf(res, ressize, "can't find ip '%s' in jail '%s'\n", args[1], args[0]);
}
} else if (msg->type == CMD_JAIL_IP_BAN) {
if ((addr = f2b_addrlist_lookup(jail->ipaddrs, args[1])) == NULL) {
/* TODO: this is copy-paste from f2b_jail_process */
time_t now = time(NULL);
addr = f2b_ipaddr_create(args[1], jail->maxretry);
if (!addr) {
snprintf(res, ressize, "cat't parse ip address: %s", args[1]);
return;
}
addr->lastseen = now;
f2b_matches_append(&addr->matches, now);
jail->ipaddrs = f2b_addrlist_append(jail->ipaddrs, addr);
}
f2b_jail_ban(jail, addr);
strlcpy(res, "ok", ressize);
} else if (msg->type == CMD_JAIL_IP_RELEASE) {
if ((addr = f2b_addrlist_lookup(jail->ipaddrs, args[1])) != NULL) {
f2b_jail_unban(jail, addr);
} else {
snprintf(res, ressize, "can't find ip '%s' in jail '%s'\n", args[1], args[0]);
}
strlcpy(res, "ok", ressize);
} else if (msg->type == CMD_JAIL_REGEX_STATS) {
f2b_filter_stats(jail->filter, res, ressize);
} else if (msg->type == CMD_JAIL_REGEX_ADD) {
if (args[1] == NULL) {
strlcpy(res, "can't find regex: no args", ressize);
return;
}
if (f2b_filter_append(jail->filter, args[1])) {
strlcpy(res, "ok", ressize);
} else {
strlcpy(res, f2b_filter_error(jail->filter), ressize);
}
} else {
strlcpy(res, "error: unsupported command type", ressize);
}
@ -264,9 +271,14 @@ jails_start(f2b_config_t *config) {
void
jails_stop(f2b_jail_t *jails) {
for (f2b_jail_t *jail = jails; jail != NULL; jail = jail->next)
f2b_jail_t *jail = jails;
f2b_jail_t *next = NULL;
for (; jail != NULL; ) {
next = jail->next;
f2b_jail_stop(jail);
jails = NULL;
free(jail);
jail = next;
}
}
int main(int argc, char *argv[]) {
@ -380,8 +392,10 @@ int main(int argc, char *argv[]) {
}
if (state == reconfig) {
state = run;
memset(&config, 0x0, sizeof(config));
if (f2b_config_load(&config, opts.config_path, true)) {
jails_stop(jails);
jails = NULL;
if (config.defaults)
f2b_jail_set_defaults(config.defaults);
jails_start(&config);
@ -395,6 +409,7 @@ int main(int argc, char *argv[]) {
f2b_csocket_destroy(opts.csock, opts.csocket_path);
jails_stop(jails);
jails = NULL;
return EXIT_SUCCESS;
}

29
src/filter.c

@ -96,6 +96,8 @@ f2b_filter_create(f2b_config_section_t *config, const char *file) {
goto cleanup;
if ((*(void **) (&filter->ready) = dlsym(filter->h, "ready")) == NULL)
goto cleanup;
if ((*(void **) (&filter->stats) = dlsym(filter->h, "stats")) == NULL)
goto cleanup;
if ((*(void **) (&filter->match) = dlsym(filter->h, "match")) == NULL)
goto cleanup;
if ((*(void **) (&filter->destroy) = dlsym(filter->h, "destroy")) == NULL)
@ -147,6 +149,14 @@ f2b_filter_destroy(f2b_filter_t *filter) {
free(filter);
}
bool
f2b_filter_append(f2b_filter_t *filter, const char *pattern) {
assert(filter != NULL);
assert(pattern != NULL);
return filter->append(filter->cfg, pattern);
}
bool
f2b_filter_match(f2b_filter_t *filter, const char *line, char *buf, size_t buf_size) {
assert(filter != NULL);
@ -161,3 +171,22 @@ f2b_filter_error(f2b_filter_t *filter) {
assert(filter != NULL);
return filter->error(filter->cfg);
}
void
f2b_filter_stats(f2b_filter_t *filter, char *res, size_t ressize) {
assert(filter != NULL);
assert(res != NULL);
bool reset = true;
char *pattern;
int matches;
char buf[256];
const char *fmt =
"- pattern: %s\n"
" matches: %d\n";
res[0] = '\0';
while (filter->stats(filter->cfg, &matches, &pattern, reset)) {
snprintf(buf, sizeof(buf), fmt, pattern, matches);
strlcat(res, buf, ressize);
reset = false;
}
}

3
src/filter.h

@ -18,6 +18,7 @@ typedef struct f2b_filter_t {
bool (*append) (void *cfg, const char *pattern);
char *(*error) (void *cfg);
bool (*ready) (void *cfg);
bool (*stats) (void *cfg, int *matches, char **pattern, bool reset);
bool (*match) (void *cfg, const char *line, char *buf, size_t buf_size);
void (*destroy) (void *cfg);
} f2b_filter_t;
@ -25,7 +26,9 @@ typedef struct f2b_filter_t {
f2b_filter_t * f2b_filter_create (f2b_config_section_t *config, const char *id);
void f2b_filter_destroy(f2b_filter_t *b);
bool f2b_filter_append(f2b_filter_t *b, const char *pattern);
bool f2b_filter_match(f2b_filter_t *b, const char *line, char *buf, size_t buf_size);
const char * f2b_filter_error(f2b_filter_t *b);
void f2b_filter_stats (f2b_filter_t *b, char *res, size_t ressize);
#endif /* F2B_FILTER_H_ */

4
src/filters/CMakeLists.txt

@ -1,12 +1,12 @@
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(FILTERS "")
add_library("f2b_filter_preg" MODULE "preg.c")
add_library("f2b_filter_preg" MODULE "preg.c" "../strlcpy.c")
list(APPEND FILTERS "f2b_filter_preg")
find_library(PCRE_FOUND "pcre")
if (WITH_PCRE AND PCRE_FOUND)
add_library("f2b_filter_pcre" MODULE "pcre.c")
add_library("f2b_filter_pcre" MODULE "pcre.c" "../strlcpy.c")
target_link_libraries("f2b_filter_pcre" "pcre")
list(APPEND FILTERS "f2b_filter_pcre")
endif ()

14
src/filters/filter.h

@ -4,8 +4,19 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#if defined(__linux__)
#include <alloca.h>
#endif
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../strlcpy.h"
#define ID_MAX 32
#define PATTERN_MAX 256
#define HOST_TOKEN "<HOST>"
typedef struct _config cfg_t;
@ -15,5 +26,6 @@ extern const char *error(cfg_t *c);
extern bool config(cfg_t *c, const char *key, const char *value);
extern bool append(cfg_t *c, const char *pattern);
extern bool ready(cfg_t *c);
extern bool match(cfg_t *c, const char *line, char *buf, size_t buf_size);
extern bool stats(cfg_t *c, int *matches, char **pattern, bool reset);
extern bool match(cfg_t *c, const char *line, char *buf, size_t bufsize);
extern void destroy(cfg_t *c);

34
src/filters/pcre.c

@ -4,12 +4,6 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <alloca.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "filter.h"
#include <pcre.h>
@ -18,18 +12,20 @@
typedef struct f2b_regex_t {
struct f2b_regex_t *next;
char pattern[PATTERN_MAX];
int matches;
pcre *regex;
pcre_extra *data;
} f2b_regex_t;
struct _config {
char id[32];
char id[ID_MAX];
char error[256];
bool icase;
bool study;
bool usejit;
f2b_regex_t *regexps;
f2b_regex_t *statp;
};
cfg_t *
@ -38,7 +34,7 @@ create(const char *id) {
if ((cfg = calloc(1, sizeof(cfg_t))) == NULL)
return NULL;
snprintf(cfg->id, sizeof(cfg->id), "%s", id);
strlcpy(cfg->id, id, sizeof(cfg->id));
return cfg;
}
@ -89,8 +85,8 @@ append(cfg_t *cfg, const char *pattern) {
memset(buf, 0x0, bufsize);
memcpy(buf, pattern, token - pattern);
strcat(buf, HOST_REGEX);
strcat(buf, token + strlen(HOST_TOKEN));
strlcat(buf, HOST_REGEX, bufsize);
strlcat(buf, token + strlen(HOST_TOKEN), bufsize);
if ((regex = calloc(1, sizeof(f2b_regex_t))) == NULL)
return false;
@ -115,6 +111,7 @@ append(cfg_t *cfg, const char *pattern) {
regex->next = cfg->regexps;
cfg->regexps = regex;
strlcpy(regex->pattern, pattern, sizeof(regex->pattern));
return true;
}
@ -126,6 +123,23 @@ ready(cfg_t *cfg) {
return false;
}
bool
stats(cfg_t *cfg, int *matches, char **pattern, bool reset) {
assert(cfg != NULL);
if (reset)
cfg->statp = cfg->regexps;
if (cfg->statp) {
*matches = cfg->statp->matches;
*pattern = cfg->statp->pattern;
cfg->statp = cfg->statp->next;
return true;
}
return false;
}
const char *
error(cfg_t *cfg) {
assert(cfg != NULL);

36
src/filters/preg.c

@ -4,12 +4,6 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <alloca.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "filter.h"
#include <regex.h>
@ -19,15 +13,17 @@
typedef struct f2b_regex_t {
struct f2b_regex_t *next;
char pattern[PATTERN_MAX];
int matches;
regex_t regex;
} f2b_regex_t;
struct _config {
char id[32];
char id[ID_MAX];
char error[256];
bool icase;
f2b_regex_t *regexps;
f2b_regex_t *statp;
};
cfg_t *
@ -36,7 +32,7 @@ create(const char *id) {
if ((cfg = calloc(1, sizeof(cfg_t))) == NULL)
return NULL;
snprintf(cfg->id, sizeof(cfg->id), "%s", id);
strlcpy(cfg->id, id, sizeof(cfg->id));
return cfg;
}
@ -77,8 +73,8 @@ append(cfg_t *cfg, const char *pattern) {
memset(buf, 0x0, bufsize);
memcpy(buf, pattern, token - pattern);
strcat(buf, HOST_REGEX);
strcat(buf, token + strlen(HOST_TOKEN));
strlcat(buf, HOST_REGEX, bufsize);
strlcat(buf, token + strlen(HOST_TOKEN), bufsize);
if ((regex = calloc(1, sizeof(f2b_regex_t))) == NULL)
return false;
@ -86,6 +82,7 @@ append(cfg_t *cfg, const char *pattern) {
if (regcomp(&regex->regex, buf, flags) == 0) {
regex->next = cfg->regexps;
cfg->regexps = regex;
strlcpy(regex->pattern, pattern, sizeof(regex->pattern));
return true;
}
@ -101,6 +98,23 @@ ready(cfg_t *cfg) {
return false;
}
bool
stats(cfg_t *cfg, int *matches, char **pattern, bool reset) {
assert(cfg != NULL);
if (reset)
cfg->statp = cfg->regexps;
if (cfg->statp) {
*matches = cfg->statp->matches;
*pattern = cfg->statp->pattern;
cfg->statp = cfg->statp->next;
return true;
}
return false;
}
const char *
error(cfg_t *cfg) {
assert(cfg != NULL);
@ -127,7 +141,7 @@ match(cfg_t *cfg, const char *line, char *buf, size_t buf_size) {
assert(buf_size > match_len);
memcpy(buf, &line[match[1].rm_so], match_len);
buf[match_len] = '\0';
buf[buf_size] = '\0';
buf[buf_size - 1] = '\0';
return true;
}

15
src/ipaddr.c

@ -44,6 +44,21 @@ f2b_ipaddr_destroy(f2b_ipaddr_t *ipaddr) {
free(ipaddr);
}
void
f2b_ipaddr_status(f2b_ipaddr_t *addr, char *res, size_t ressize) {
assert(addr != NULL);
assert(res != NULL);
const char *fmt =
"ipaddr: %s\n"
"banned: %s\n"
"bancount: %d\n"
"lastseen: %d\n"
"banned_at: %d\n"
"release_at: %d\n";
snprintf(res, ressize, fmt, addr->text, addr->banned ? "yes" : "no",
addr->bancount, addr->lastseen, addr->banned_at, addr->release_at);
}
f2b_ipaddr_t *
f2b_addrlist_append(f2b_ipaddr_t *list, f2b_ipaddr_t *ipaddr) {
assert(ipaddr != NULL);

3
src/ipaddr.h

@ -7,6 +7,8 @@
#ifndef F2B_IPADDR_H_
#define F2B_IPADDR_H_
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "matches.h"
@ -31,6 +33,7 @@ typedef struct f2b_ipaddr_t {
f2b_ipaddr_t * f2b_ipaddr_create (const char *addr, size_t max_matches);
void f2b_ipaddr_destroy(f2b_ipaddr_t *ipaddr);
void f2b_ipaddr_status (f2b_ipaddr_t *ipaddr, char *res, size_t ressize);
f2b_ipaddr_t * f2b_addrlist_append(f2b_ipaddr_t *list, f2b_ipaddr_t *ipaddr);
f2b_ipaddr_t * f2b_addrlist_lookup(f2b_ipaddr_t *list, const char *addr);

26
src/jail.c

@ -77,7 +77,7 @@ f2b_jail_apply_config(f2b_jail_t *jail, f2b_config_section_t *section) {
}
if (strcmp(param->name, "maxretry") == 0) {
jail->maxretry = atoi(param->value);
if (jail->maxretry <= 0)
if (jail->maxretry == 0)
jail->maxretry = DEFAULT_MAXRETRY;
continue;
}
@ -412,3 +412,27 @@ f2b_jail_stop(f2b_jail_t *jail) {
return errors;
}
void
f2b_jail_get_status(f2b_jail_t *jail, char *res, size_t ressize) {
assert(jail != NULL);
assert(res != NULL);
const char *fmt =
"name: %s\n"
"enabled: %s\n"
"maxretry: %d\n"
"times:\n"
" bantime: %d\n"
" findtime: %d\n"
" expiretime: %d\n"
"incr:\n"
" bantime: %.1f\n"
" findtime: %.1f\n"
"stats:\n"
" banned: %d\n"
" matched: %d\n";
snprintf(res, ressize, fmt, jail->name, jail->enabled ? "yes" : "no", jail->maxretry,
jail->bantime, jail->findtime, jail->expiretime,
jail->incr_bantime, jail->incr_findtime,
jail->bancount, jail->matchcount);
}

2
src/jail.h

@ -52,4 +52,6 @@ bool f2b_jail_unban (f2b_jail_t *jail, f2b_ipaddr_t *addr);
bool f2b_jail_init (f2b_jail_t *jail, f2b_config_t *config);
size_t f2b_jail_process (f2b_jail_t *jail);
bool f2b_jail_stop (f2b_jail_t *jail);
void f2b_jail_get_status(f2b_jail_t *jail, char *res, size_t ressize);
#endif /* F2B_JAIL_H_ */

6
src/log.c

@ -39,6 +39,8 @@ get_facility(log_msgtype_t l) {
void f2b_log_msg(log_msgtype_t l, const char *fmt, ...) {
va_list args;
char msg[LOGLINE_MAX] = "";
char when[64] = "";
time_t now = time(NULL);
if (l < minlevel)
return;
@ -55,7 +57,8 @@ void f2b_log_msg(log_msgtype_t l, const char *fmt, ...) {
case log_stderr:
logfile = stderr;
case log_file:
fprintf(logfile, "[%s] %s\n", loglevels[l], msg);
strftime(when, sizeof(when), "%F %H:%M:%S", localtime(&now));
fprintf(logfile, "%s [%s] %s\n", when, loglevels[l], msg);
break;
}
@ -83,6 +86,7 @@ void f2b_log_to_file(const char *path) {
if (path == NULL || *path == '\0')
return;
if ((new = fopen(path, "a")) != NULL) {
setvbuf(new, NULL , _IONBF, 0);
if (logfile && logfile != stderr)
fclose(logfile);
dest = log_file;

13
src/logfile.c

@ -41,8 +41,12 @@ f2b_logfile_open(f2b_logfile_t *file, const char *path) {
void
f2b_logfile_close(f2b_logfile_t *file) {
assert(file != NULL);
if (file->fd)
fclose(file->fd);
file->opened = false;
file->fd = NULL;
}
bool
@ -51,6 +55,9 @@ f2b_logfile_rotated(const f2b_logfile_t *file) {
assert(file != NULL);
if (!file->opened)
return true;
if (stat(file->path, &st) != 0)
return true;
@ -64,6 +71,12 @@ f2b_logfile_rotated(const f2b_logfile_t *file) {
bool
f2b_logfile_getline(const f2b_logfile_t *file, char *buf, size_t bufsize) {
assert(file != NULL);
assert(buf != NULL);
if (feof(file->fd))
clearerr(file->fd);
/* fread()+EOF set is implementation defined */
if (fgets(buf, bufsize, file->fd) != NULL)
return true;

4
t/CMakeLists.txt

@ -14,11 +14,11 @@ add_test("tests/f2b_matches_*" "t_matches")
add_test("tests/f2b_ipaddr_*" "t_ipaddr")
add_test("tests/f2b_config_param*" "t_config_param")
add_executable("t_filter_preg" "t_filters.c" "${SRC_DIR}/filters/preg.c")
add_executable("t_filter_preg" "t_filters.c" "${SRC_DIR}/filters/preg.c" "${SRC_DIR}/strlcpy.c")
add_test("tests/filter/preg" "t_filter_preg")
if (WITH_PCRE)
add_test("tests/filter/pcre" "t_filter_pcre")
add_executable("t_filter_pcre" "t_filters.c" "${SRC_DIR}/filters/pcre.c")
add_executable("t_filter_pcre" "t_filters.c" "${SRC_DIR}/filters/pcre.c" "${SRC_DIR}/strlcpy.c")
target_link_libraries("t_filter_pcre" "pcre")
endif ()

Loading…
Cancel
Save