Download | Plain Text | Line Numbers


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 <time.h>
 
 #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