#include <ucw/clists.h>
#include <ucw/conf.h>
#include <ucw/mainloop.h>
+#include <ucw/opt.h>
#include <ucw/string.h>
#include <alloca.h>
/*** Configuration ***/
-static uns max_culprits = 3; // FIXME
-static uns max_failures = 3; // FIXME
-static uns max_idle_time = 600; // FIXME
-static uns max_banned_time = 600; // FIXME
+static uns max_failures = ~0U;
+static uns max_suspect_time = 86400;
+static uns max_banned_time = 86400;
+static uns max_suspects = ~0U;
+static uns max_banned = ~0U;
+
+/*
+ * FIXME:
+ * - names of ipsets
+ * - name of socket
+ * - logging
+ * - PAM module name(s) to match
+ */
+
+static struct cf_section bouncer_cf = {
+ CF_ITEMS {
+ CF_UNS("MaxSuspects", &max_suspects),
+ CF_UNS("MaxBanned", &max_banned),
+ CF_UNS("MaxSuspectTime", &max_suspect_time),
+ CF_UNS("MaxBannedTime", &max_banned_time),
+ CF_UNS("MaxFailures", &max_failures),
+ CF_END
+ }
+};
/*** An interface to IP sets ***/
/*** Handling of login failures ***/
struct culprit_node {
- cnode n;
+ cnode n; // In either suspect_list or banned_list
union {
struct addr addr;
byte addr_bytes[16];
};
- timestamp_t last_fail;
uns fail_count;
bool banned;
+ timestamp_t last_fail; // Not updated when banned
};
#define HASH_NODE struct culprit_node
#define HASH_LOOKUP_DETECT_NEW
#include <ucw/hashtable.h>
-static uns num_culprits;
-static clist culprit_lru, culprit_bans;
+static clist suspect_list, banned_list;
+static uns num_suspects, num_banned;
static struct main_timer cleanup_timer;
-static void cleanup_list(clist *list, timestamp_t max_time, timestamp_t *next)
+static void cleanup_list(clist *list, uns *counter, timestamp_t max_time, uns max_count, timestamp_t *next)
{
timestamp_t now = main_get_now();
break;
timestamp_t expire_in = c->last_fail + max_time - now;
- if (num_culprits > max_culprits)
- expire_in = 0;
+ if (*counter > max_count)
+ {
+ // FIXME: Warn with rate limit
+ expire_in = 0;
+ }
if (expire_in > now)
{
clist_remove(&c->n);
culprit_remove(c);
- num_culprits--;
+ (*counter)--;
}
}
static void culprit_cleanup(void)
{
timestamp_t next_cleanup = main_get_now() + (timestamp_t)3600 * 1000;
- cleanup_list(&culprit_bans, (timestamp_t)max_banned_time * 1000, &next_cleanup);
- cleanup_list(&culprit_lru, (timestamp_t)max_idle_time * 1000, &next_cleanup);
+ cleanup_list(&suspect_list, &num_suspects, (timestamp_t)max_suspect_time * 1000, max_suspects, &next_cleanup);
+ cleanup_list(&banned_list, &num_banned, (timestamp_t)max_banned_time * 1000, max_banned, &next_cleanup);
timer_add(&cleanup_timer, next_cleanup);
}
struct culprit_node *c = culprit_lookup((byte *) &addr, &is_new);
if (is_new)
{
+ c->last_fail = now;
c->fail_count = 1;
c->banned = 0;
- clist_add_tail(&culprit_lru, &c->n);
- num_culprits++;
- // FIXME: Warn on overflow, but not too frequently
+ clist_add_tail(&suspect_list, &c->n);
+ num_suspects++;
DBG("%s: first fail", AFMT(addr));
}
else if (!c->banned)
{
+ c->last_fail = now;
c->fail_count++;
clist_remove(&c->n);
- clist_add_tail(&culprit_lru, &c->n);
+ clist_add_tail(&suspect_list, &c->n);
DBG("%s: next fail, cnt=%u", AFMT(addr), c->fail_count);
}
- c->last_fail = now;
if (!c->banned && c->fail_count >= max_failures)
{
DBG("%s: banned", AFMT(addr));
c->banned = 1;
clist_remove(&c->n);
- clist_add_tail(&culprit_bans, &c->n);
+ num_suspects--;
+ clist_add_tail(&banned_list, &c->n);
+ num_banned++;
is_modify(1, c->addr);
}
+
+ culprit_cleanup();
}
static void culprit_timer(struct main_timer *tm UNUSED)
static void fail_init(void)
{
culprit_init();
- clist_init(&culprit_lru);
- clist_init(&culprit_bans);
+ clist_init(&suspect_list);
+ clist_init(&banned_list);
cleanup_timer.handler = culprit_timer;
}
/*** Main ***/
-int main(void)
+static struct opt_section options = {
+ OPT_ITEMS {
+ OPT_HELP("Bouncer -- A Daemon for Turning Away Mischievous Guests"),
+ OPT_HELP(""),
+ OPT_HELP("Options:"),
+ OPT_HELP_OPTION,
+ OPT_CONF_OPTIONS,
+ OPT_END
+ }
+};
+
+int main(int argc UNUSED, char **argv)
{
+ cf_def_file = "config"; // FIXME
+ cf_declare_section("Bouncer", &bouncer_cf, 0);
+ opt_parse(&options, argv+1);
+
main_init();
is_init();
fail_init();
- msg(L_INFO, "Clearing previous state");
+ // FIXME msg(L_INFO, "Clearing previous state");
is_flush(IS_IPV4);
is_flush(IS_IPV6);