dovecot-2.0: auth/anvil: Penalty is no longer increased if the s...
dovecot at dovecot.org
dovecot at dovecot.org
Sat Feb 20 08:49:01 EET 2010
details: http://hg.dovecot.org/dovecot-2.0/rev/4cdb58bb0360
changeset: 10773:4cdb58bb0360
user: Timo Sirainen <tss at iki.fi>
date: Sat Feb 20 08:48:54 2010 +0200
description:
auth/anvil: Penalty is no longer increased if the same user+pass combination was recently used.
This should avoid penalty increasing for IPs where a user's misconfigured
client tries to keep authenticating with wrong user/pass. This check works
only for plaintext authentication.
Currently the code that keeps track of what user/passwords have been tried
is pretty simple, and hardcoded to remember max. 10 of them.
diffstat:
src/anvil/Makefile.am | 20 +++++
src/anvil/anvil-connection.c | 17 +++-
src/anvil/penalty.c | 161 ++++++++++++++++++++++++++++++++++++----
src/anvil/penalty.h | 13 ++-
src/anvil/test-penalty.c | 64 ++++++++++++++++
src/auth/auth-penalty.c | 27 +++++-
6 files changed, 270 insertions(+), 32 deletions(-)
diffs (truncated from 464 to 300 lines):
diff -r 5380ee17392f -r 4cdb58bb0360 src/anvil/Makefile.am
--- a/src/anvil/Makefile.am Sat Feb 20 05:54:46 2010 +0200
+++ b/src/anvil/Makefile.am Sat Feb 20 08:48:54 2010 +0200
@@ -4,6 +4,7 @@
AM_CPPFLAGS = \
-I$(top_srcdir)/src/lib \
+ -I$(top_srcdir)/src/lib-test \
-I$(top_srcdir)/src/lib-settings \
-I$(top_srcdir)/src/lib-master
@@ -25,3 +26,22 @@
common.h \
connect-limit.h \
penalty.h
+
+test_programs = \
+ test-penalty
+
+noinst_PROGRAMS = $(test_programs)
+
+test_libs = \
+ ../lib-test/libtest.la \
+ ../lib/liblib.la
+
+test_penalty_SOURCES = test-penalty.c
+test_penalty_LDADD = penalty.lo $(test_libs)
+test_penalty_DEPENDENCIES = penalty.lo $(test_libs)
+
+check: check-am check-test
+check-test: all-am
+ for bin in $(test_programs); do \
+ if ! ./$$bin; then exit 1; fi; \
+ done
diff -r 5380ee17392f -r 4cdb58bb0360 src/anvil/anvil-connection.c
--- a/src/anvil/anvil-connection.c Sat Feb 20 05:54:46 2010 +0200
+++ b/src/anvil/anvil-connection.c Sat Feb 20 08:48:54 2010 +0200
@@ -47,7 +47,7 @@
const char *const *args, const char **error_r)
{
const char *cmd = args[0];
- unsigned int value;
+ unsigned int value, checksum;
time_t stamp;
pid_t pid;
@@ -99,12 +99,19 @@
value = penalty_get(penalty, args[0], &stamp);
(void)o_stream_send_str(conn->output,
t_strdup_printf("%u %s\n", value, dec2str(stamp)));
- } else if (strcmp(cmd, "PENALTY-SET") == 0) {
- if (args[0] == NULL || args[1] == NULL) {
- *error_r = "PENALTY-SET: Not enough parameters";
+ } else if (strcmp(cmd, "PENALTY-INC") == 0) {
+ if (args[0] == NULL || args[1] == NULL || args[2] == NULL) {
+ *error_r = "PENALTY-INC: Not enough parameters";
return -1;
}
- penalty_set(penalty, args[0], strtoul(args[1], NULL, 10));
+ checksum = strtoul(args[1], NULL, 10);
+ value = strtoul(args[2], NULL, 10);
+ if (value > PENALTY_MAX_VALUE ||
+ (value == 0 && checksum != 0)) {
+ *error_r = "PENALTY-INC: Invalid parameters";
+ return -1;
+ }
+ penalty_inc(penalty, args[0], checksum, value);
} else if (strcmp(cmd, "PENALTY-SET-EXPIRE-SECS") == 0) {
if (args[0] == NULL) {
*error_r = "PENALTY-SET-EXPIRE-SECS: "
diff -r 5380ee17392f -r 4cdb58bb0360 src/anvil/penalty.c
--- a/src/anvil/penalty.c Sat Feb 20 05:54:46 2010 +0200
+++ b/src/anvil/penalty.c Sat Feb 20 08:48:54 2010 +0200
@@ -9,14 +9,28 @@
#include <time.h>
#define PENALTY_DEFAULT_EXPIRE_SECS (60*60)
+#define PENALTY_CHECKSUM_SAVE_COUNT
+#define CHECKSUM_VALUE_COUNT 2
+#define CHECKSUM_VALUE_PTR_COUNT 10
+
+#define LAST_UPDATE_BITS 15
struct penalty_rec {
/* ordered by last_update */
struct penalty_rec *prev, *next;
char *ident;
- unsigned int penalty;
- time_t last_update;
+ unsigned int last_penalty;
+
+ unsigned int penalty:16;
+ unsigned int last_update:LAST_UPDATE_BITS; /* last_penalty + n */
+ unsigned int checksum_is_pointer:1;
+ /* we use value up to two different checksums.
+ after that switch to pointer. */
+ union {
+ unsigned int value[CHECKSUM_VALUE_COUNT];
+ unsigned int *value_ptr;
+ } checksum;
};
struct penalty {
@@ -43,6 +57,8 @@
static void penalty_rec_free(struct penalty *penalty, struct penalty_rec *rec)
{
DLLIST2_REMOVE(&penalty->oldest, &penalty->newest, rec);
+ if (rec->checksum_is_pointer)
+ i_free(rec->checksum.value_ptr);
i_free(rec->ident);
i_free(rec);
}
@@ -67,44 +83,111 @@
penalty->expire_secs = expire_secs;
}
+static bool
+penalty_bump_checksum(struct penalty_rec *rec, unsigned int checksum)
+{
+ unsigned int *checksums;
+ unsigned int i, count;
+
+ if (!rec->checksum_is_pointer) {
+ checksums = rec->checksum.value;
+ count = CHECKSUM_VALUE_COUNT;
+ } else {
+ checksums = rec->checksum.value_ptr;
+ count = CHECKSUM_VALUE_PTR_COUNT;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (checksums[i] == checksum) {
+ if (i > 0) {
+ memcpy(checksums + 1, checksums,
+ sizeof(checksums[0]) * i);
+ checksums[0] = checksum;
+ }
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static void penalty_add_checksum(struct penalty_rec *rec, unsigned int checksum)
+{
+ unsigned int *checksums;
+
+ i_assert(checksum != 0);
+
+ if (!rec->checksum_is_pointer) {
+ if (rec->checksum.value[CHECKSUM_VALUE_COUNT-1] == 0) {
+ memcpy(rec->checksum.value + 1, rec->checksum.value,
+ sizeof(rec->checksum.value[0]) *
+ (CHECKSUM_VALUE_COUNT-1));
+ rec->checksum.value[0] = checksum;
+ return;
+ }
+
+ /* switch to using a pointer */
+ checksums = i_new(unsigned int, CHECKSUM_VALUE_PTR_COUNT);
+ memcpy(checksums, rec->checksum.value,
+ sizeof(checksums[0]) * CHECKSUM_VALUE_COUNT);
+ rec->checksum.value_ptr = checksums;
+ rec->checksum_is_pointer = TRUE;
+ }
+
+ memcpy(rec->checksum.value_ptr + 1, rec->checksum.value_ptr,
+ sizeof(rec->checksum.value_ptr[0]) *
+ (CHECKSUM_VALUE_PTR_COUNT-1));
+ rec->checksum.value_ptr[0] = checksum;
+}
+
unsigned int penalty_get(struct penalty *penalty, const char *ident,
- time_t *last_update_r)
+ time_t *last_penalty_r)
{
struct penalty_rec *rec;
rec = hash_table_lookup(penalty->hash, ident);
if (rec == NULL) {
- *last_update_r = 0;
+ *last_penalty_r = 0;
return 0;
- } else {
- *last_update_r = rec->last_update;
- return rec->penalty;
}
+
+ *last_penalty_r = rec->last_penalty;
+ return rec->penalty;
}
static void penalty_timeout(struct penalty *penalty)
{
+ struct penalty_rec *rec;
time_t expire_time;
expire_time = ioloop_time - penalty->expire_secs;
- while (penalty->oldest != NULL &&
- penalty->oldest->last_update <= expire_time) {
- hash_table_remove(penalty->hash, penalty->oldest->ident);
- penalty_rec_free(penalty, penalty->oldest);
+ while (penalty->oldest != NULL) {
+ rec = penalty->oldest;
+
+ if (rec->last_penalty + rec->last_update > expire_time)
+ break;
+ hash_table_remove(penalty->hash, rec->ident);
+ penalty_rec_free(penalty, rec);
}
timeout_remove(&penalty->to);
- if (penalty->oldest != NULL) {
- unsigned int diff = penalty->oldest->last_update - expire_time;
+ rec = penalty->oldest;
+ if (rec != NULL) {
+ unsigned int diff;
+
+ diff = rec->last_penalty + rec->last_update - expire_time;
penalty->to = timeout_add(diff * 1000,
penalty_timeout, penalty);
}
}
-void penalty_set(struct penalty *penalty, const char *ident,
- unsigned int value)
+void penalty_inc(struct penalty *penalty, const char *ident,
+ unsigned int checksum, unsigned int value)
{
struct penalty_rec *rec;
+ time_t diff;
+
+ i_assert(value > 0 || checksum == 0);
+ i_assert(value <= INT_MAX);
rec = hash_table_lookup(penalty->hash, ident);
if (rec == NULL) {
@@ -114,8 +197,26 @@
} else {
DLLIST2_REMOVE(&penalty->oldest, &penalty->newest, rec);
}
- rec->penalty = value;
- rec->last_update = time(NULL);
+
+ if (checksum == 0) {
+ rec->penalty = value;
+ rec->last_penalty = ioloop_time;
+ } else {
+ if (penalty_bump_checksum(rec, checksum))
+ rec->penalty = value - 1;
+ else {
+ penalty_add_checksum(rec, checksum);
+ rec->penalty = value;
+ rec->last_penalty = ioloop_time;
+ }
+ }
+
+ diff = ioloop_time - rec->last_penalty;
+ if (diff >= (1 << LAST_UPDATE_BITS)) {
+ rec->last_update = (1 << LAST_UPDATE_BITS) - 1;
+ rec->last_penalty = ioloop_time - rec->last_update;
+ }
+
DLLIST2_APPEND(&penalty->oldest, &penalty->newest, rec);
if (penalty->to == NULL) {
@@ -123,3 +224,29 @@
penalty_timeout, penalty);
}
}
+
+bool penalty_has_checksum(struct penalty *penalty, const char *ident,
+ unsigned int checksum)
+{
+ struct penalty_rec *rec;
+ const unsigned int *checksums;
+ unsigned int i, count;
+
+ rec = hash_table_lookup(penalty->hash, ident);
+ if (rec == NULL)
+ return FALSE;
+
+ if (!rec->checksum_is_pointer) {
+ checksums = rec->checksum.value;
+ count = CHECKSUM_VALUE_COUNT;
+ } else {
+ checksums = rec->checksum.value_ptr;
+ count = CHECKSUM_VALUE_PTR_COUNT;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (checksums[i] == checksum)
+ return TRUE;
+ }
+ return FALSE;
+}
diff -r 5380ee17392f -r 4cdb58bb0360 src/anvil/penalty.h
--- a/src/anvil/penalty.h Sat Feb 20 05:54:46 2010 +0200
+++ b/src/anvil/penalty.h Sat Feb 20 08:48:54 2010 +0200
More information about the dovecot-cvs
mailing list