diff -Naur dovecot-2.2.21.orig/src/anvil/anvil-connection.c dovecot-2.2.21/src/anvil/anvil-connection.c --- dovecot-2.2.21.orig/src/anvil/anvil-connection.c 2015-12-09 16:39:10.000000000 +0100 +++ dovecot-2.2.21/src/anvil/anvil-connection.c 2016-02-17 02:28:07.713478699 +0100 @@ -109,18 +109,26 @@ o_stream_nsend_str(conn->output, t_strdup_printf("%u %s\n", value, dec2str(stamp))); } else if (strcmp(cmd, "PENALTY-INC") == 0) { - if (args[0] == NULL || args[1] == NULL || args[2] == NULL) { + if (args[0] == NULL || args[1] == NULL) { *error_r = "PENALTY-INC: Not enough parameters"; return -1; } - if (str_to_uint(args[1], &checksum) < 0 || - str_to_uint(args[2], &value) < 0 || - value > PENALTY_MAX_VALUE || - (value == 0 && checksum != 0)) { + if (str_to_uint(args[1], &checksum) < 0) { *error_r = "PENALTY-INC: Invalid parameters"; return -1; } - penalty_inc(penalty, args[0], checksum, value); + penalty_inc(penalty, args[0], checksum); + } else if (strcmp(cmd, "PENALTY-SET") == 0) { + if (args[0] == NULL) { + *error_r = "PENALTY-INC: Not enough parameters"; + return -1; + } + if (str_to_uint(args[1], &value) < 0 || + value > PENALTY_MAX_VALUE) { + *error_r = "PENALTY-SET: Invalid parameters"; + return -1; + } + penalty_set(penalty, args[0], value); } else if (strcmp(cmd, "PENALTY-SET-EXPIRE-SECS") == 0) { if (args[0] == NULL || str_to_uint(args[0], &value) < 0) { *error_r = "PENALTY-SET-EXPIRE-SECS: " @@ -128,6 +136,14 @@ return -1; } penalty_set_expire_secs(penalty, value); + } else if (strcmp(cmd, "PENALTY-SET-MAX-PENALTY") == 0) { + if (args[0] == NULL || str_to_uint(args[0], &value) < 0 || + value > PENALTY_MAX_VALUE) { + *error_r = "PENALTY-SET-MAX-PENALTY: " + "Invalid parameters"; + return -1; + } + penalty_set_max_penalty(penalty, value); } else if (strcmp(cmd, "PENALTY-DUMP") == 0) { penalty_dump(penalty, conn->output); } else { diff -Naur dovecot-2.2.21.orig/src/anvil/penalty.c dovecot-2.2.21/src/anvil/penalty.c --- dovecot-2.2.21.orig/src/anvil/penalty.c 2015-12-09 16:39:10.000000000 +0100 +++ dovecot-2.2.21/src/anvil/penalty.c 2016-02-17 02:28:07.723478869 +0100 @@ -16,6 +16,7 @@ #include #define PENALTY_DEFAULT_EXPIRE_SECS (60*60) +#define PENALTY_DEFAULT_MAX_PENALTY 100 #define PENALTY_CHECKSUM_SAVE_COUNT #define CHECKSUM_VALUE_COUNT 2 #define CHECKSUM_VALUE_PTR_COUNT 10 @@ -46,6 +47,7 @@ struct penalty_rec *oldest, *newest; unsigned int expire_secs; + unsigned int max_penalty:16; struct timeout *to; }; @@ -56,6 +58,7 @@ penalty = i_new(struct penalty, 1); hash_table_create(&penalty->hash, default_pool, 0, str_hash, strcmp); penalty->expire_secs = PENALTY_DEFAULT_EXPIRE_SECS; + penalty->max_penalty = PENALTY_DEFAULT_MAX_PENALTY; return penalty; } @@ -88,6 +91,11 @@ penalty->expire_secs = expire_secs; } +void penalty_set_max_penalty(struct penalty *penalty, unsigned int max_penalty) +{ + penalty->max_penalty = max_penalty; +} + static bool penalty_bump_checksum(struct penalty_rec *rec, unsigned int checksum) { @@ -183,51 +191,84 @@ } } -void penalty_inc(struct penalty *penalty, const char *ident, - unsigned int checksum, unsigned int value) +static void penalty_update_times(struct penalty *penalty, + struct penalty_rec *rec) { - struct penalty_rec *rec; time_t diff; - i_assert(value > 0 || checksum == 0); - i_assert(value <= INT_MAX); + 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; + } else { + rec->last_update = diff; + } + + if (penalty->to == NULL) { + penalty->to = timeout_add(penalty->expire_secs * 1000, + penalty_timeout, penalty); + } +} + +void penalty_inc(struct penalty *penalty, const char *ident, + unsigned int checksum) +{ + struct penalty_rec *rec; + unsigned int new_penalty; rec = hash_table_lookup(penalty->hash, ident); if (rec == NULL) { rec = i_new(struct penalty_rec, 1); rec->ident = i_strdup(ident); + rec->penalty = 1; + rec->last_penalty = ioloop_time; hash_table_insert(penalty->hash, rec->ident, rec); + if (checksum != 0) + penalty_add_checksum(rec, checksum); } else { DLLIST2_REMOVE(&penalty->oldest, &penalty->newest, rec); - } - 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; + new_penalty = rec->penalty; + if (checksum == 0) { + new_penalty++; rec->last_penalty = ioloop_time; + } else { + if (!penalty_bump_checksum(rec, checksum)) { + penalty_add_checksum(rec, checksum); + new_penalty++; + rec->last_penalty = ioloop_time; + } } + rec->penalty = (new_penalty < penalty->max_penalty) ? + new_penalty : penalty->max_penalty; } - 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; + penalty_update_times(penalty, rec); + DLLIST2_APPEND(&penalty->oldest, &penalty->newest, rec); +} + +void penalty_set(struct penalty *penalty, const char *ident, + unsigned int value) +{ + struct penalty_rec *rec; + + rec = hash_table_lookup(penalty->hash, ident); + if (rec == NULL) { + if (value == 0) + return; + rec = i_new(struct penalty_rec, 1); + rec->ident = i_strdup(ident); + hash_table_insert(penalty->hash, rec->ident, rec); } else { - rec->last_update = diff; + DLLIST2_REMOVE(&penalty->oldest, &penalty->newest, rec); } - DLLIST2_APPEND(&penalty->oldest, &penalty->newest, rec); + rec->penalty = (value < penalty->max_penalty) ? value : + penalty->max_penalty; + rec->last_penalty = ioloop_time; - if (penalty->to == NULL) { - penalty->to = timeout_add(penalty->expire_secs * 1000, - penalty_timeout, penalty); - } + penalty_update_times(penalty, rec); + DLLIST2_APPEND(&penalty->oldest, &penalty->newest, rec); } bool penalty_has_checksum(struct penalty *penalty, const char *ident, diff -Naur dovecot-2.2.21.orig/src/anvil/penalty.h dovecot-2.2.21/src/anvil/penalty.h --- dovecot-2.2.21.orig/src/anvil/penalty.h 2015-12-09 16:39:10.000000000 +0100 +++ dovecot-2.2.21/src/anvil/penalty.h 2016-02-17 02:28:07.724478886 +0100 @@ -7,13 +7,14 @@ void penalty_deinit(struct penalty **penalty); void penalty_set_expire_secs(struct penalty *penalty, unsigned int expire_secs); +void penalty_set_max_penalty(struct penalty *penalty, unsigned int max_penalty); unsigned int penalty_get(struct penalty *penalty, const char *ident, time_t *last_penalty_r); -/* if checksum is non-zero and it already exists for ident, the value - is set to "value-1", otherwise it's set to "value". */ void penalty_inc(struct penalty *penalty, const char *ident, - unsigned int checksum, unsigned int value); + unsigned int checksum); +void penalty_set(struct penalty *penalty, const char *ident, + unsigned int value); bool penalty_has_checksum(struct penalty *penalty, const char *ident, unsigned int checksum); diff -Naur dovecot-2.2.21.orig/src/anvil/test-penalty.c dovecot-2.2.21/src/anvil/test-penalty.c --- dovecot-2.2.21.orig/src/anvil/test-penalty.c 2015-12-09 16:39:10.000000000 +0100 +++ dovecot-2.2.21/src/anvil/test-penalty.c 2016-02-17 02:28:07.724478886 +0100 @@ -18,9 +18,10 @@ penalty = penalty_init(); test_assert(penalty_get(penalty, "foo", &t) == 0); + penalty_set(penalty, "foo", 5); for (i = 1; i <= 10; i++) { ioloop_time = 12345678 + i; - penalty_inc(penalty, "foo", i, 5+i); + penalty_inc(penalty, "foo", i); for (j = I_MIN(1, i-1); j <= i; j++) { test_assert(penalty_get(penalty, "foo", &t) == 5+i); @@ -31,20 +32,20 @@ test_assert(t == (time_t)(12345678 + i)); test_assert(!penalty_has_checksum(penalty, "foo", j)); } - test_assert(penalty_get(penalty, "foo2", &t) == 0); /* overflows checksum array */ ioloop_time = 12345678 + i; - penalty_inc(penalty, "foo", i, 5 + i); - penalty_inc(penalty, "foo", i, 5 + i); - penalty_inc(penalty, "foo", 0, 5 + i); + penalty_set(penalty, "foo", 5+i); + penalty_inc(penalty, "foo", i); + penalty_inc(penalty, "foo", i); + penalty_inc(penalty, "foo", 0); - test_assert(penalty_get(penalty, "foo", &t) == 5+i); + test_assert(penalty_get(penalty, "foo", &t) == 5+2+i); test_assert(t == (time_t)(12345678 + i)); test_assert(!penalty_has_checksum(penalty, "foo", 1)); for (j = 2; j <= i; j++) { - test_assert(penalty_get(penalty, "foo", &t) == 5+i); + test_assert(penalty_get(penalty, "foo", &t) == 5+2+i); test_assert(t == (time_t)(12345678 + i)); test_assert(penalty_has_checksum(penalty, "foo", i)); } diff -Naur dovecot-2.2.21.orig/src/auth/auth-penalty.c dovecot-2.2.21/src/auth/auth-penalty.c --- dovecot-2.2.21.orig/src/auth/auth-penalty.c 2015-12-09 16:39:10.000000000 +0100 +++ dovecot-2.2.21/src/auth/auth-penalty.c 2016-02-17 02:28:14.849599750 +0100 @@ -39,6 +39,8 @@ else { anvil_client_cmd(penalty->client, t_strdup_printf( "PENALTY-SET-EXPIRE-SECS\t%u", AUTH_PENALTY_TIMEOUT)); + anvil_client_cmd(penalty->client, t_strdup_printf( + "PENALTY-SET-MAX-PENALTY\t%u", AUTH_PENALTY_MAX_PENALTY)); } return penalty; } @@ -149,7 +151,7 @@ auth_request->user); } -void auth_penalty_update(struct auth_penalty *penalty, +void auth_penalty_set(struct auth_penalty *penalty, struct auth_request *auth_request, unsigned int value) { const char *ident; @@ -158,18 +160,29 @@ if (penalty->disabled || ident == NULL || auth_request->no_penalty) return; - if (value > AUTH_PENALTY_MAX_PENALTY) { - /* even if the actual value doesn't change, the last_change - timestamp does. */ - value = AUTH_PENALTY_MAX_PENALTY; - } + T_BEGIN { + const char *cmd; + + cmd = t_strdup_printf("PENALTY-SET\t%s\t%u", ident, value); + anvil_client_cmd(penalty->client, cmd); + } T_END; +} + +void auth_penalty_inc(struct auth_penalty *penalty, + struct auth_request *auth_request) +{ + const char *ident; + + ident = auth_penalty_get_ident(auth_request); + if (penalty->disabled || ident == NULL || auth_request->no_penalty) + return; + T_BEGIN { const char *cmd; unsigned int checksum; - checksum = value == 0 ? 0 : get_userpass_checksum(auth_request); - cmd = t_strdup_printf("PENALTY-INC\t%s\t%u\t%u", - ident, checksum, value); + checksum = get_userpass_checksum(auth_request); + cmd = t_strdup_printf("PENALTY-INC\t%s\t%u", ident, checksum); anvil_client_cmd(penalty->client, cmd); } T_END; } diff -Naur dovecot-2.2.21.orig/src/auth/auth-penalty.h dovecot-2.2.21/src/auth/auth-penalty.h --- dovecot-2.2.21.orig/src/auth/auth-penalty.h 2015-12-09 16:39:10.000000000 +0100 +++ dovecot-2.2.21/src/auth/auth-penalty.h 2016-02-17 02:29:25.160792445 +0100 @@ -22,7 +22,9 @@ void auth_penalty_lookup(struct auth_penalty *penalty, struct auth_request *auth_request, auth_penalty_callback_t *callback); -void auth_penalty_update(struct auth_penalty *penalty, +void auth_penalty_set(struct auth_penalty *penalty, struct auth_request *auth_request, unsigned int value); +void auth_penalty_inc(struct auth_penalty *penalty, + struct auth_request *auth_request); #endif diff -Naur dovecot-2.2.21.orig/src/auth/auth-request-handler.c dovecot-2.2.21/src/auth/auth-request-handler.c --- dovecot-2.2.21.orig/src/auth/auth-request-handler.c 2015-12-09 16:39:10.000000000 +0100 +++ dovecot-2.2.21/src/auth/auth-request-handler.c 2016-02-17 02:28:14.857599885 +0100 @@ -228,8 +228,7 @@ handler->refcount++; if (auth_penalty != NULL) { - auth_penalty_update(auth_penalty, request, - request->last_penalty + 1); + auth_penalty_inc(auth_penalty, request); } auth_request_refresh_last_access(request); @@ -249,7 +248,7 @@ if (request->last_penalty != 0 && auth_penalty != NULL) { /* reset penalty */ - auth_penalty_update(auth_penalty, request, 0); + auth_penalty_set(auth_penalty, request, 0); } /* sanitize these fields, since the login code currently assumes they