dovecot-2.0-sslstream: auth: Added auth failure penalty tracking...

dovecot at dovecot.org dovecot at dovecot.org
Sat Feb 13 02:56:19 EET 2010


details:   http://hg.dovecot.org/dovecot-2.0-sslstream/rev/fbff8ca77d2e
changeset: 10302:fbff8ca77d2e
user:      Timo Sirainen <tss at iki.fi>
date:      Tue Nov 10 15:08:24 2009 -0500
description:
auth: Added auth failure penalty tracking based on remote IP address.

diffstat:

9 files changed, 226 insertions(+), 14 deletions(-)
src/auth/Makefile.am            |    2 
src/auth/auth-penalty.c         |  131 +++++++++++++++++++++++++++++++++++++++
src/auth/auth-penalty.h         |   28 ++++++++
src/auth/auth-request-handler.c |   56 +++++++++++++---
src/auth/auth-request.c         |    9 +-
src/auth/auth-request.h         |    8 +-
src/auth/mech.h                 |    1 
src/auth/passdb.h               |    2 
src/auth/userdb.h               |    3 

diffs (truncated from 416 to 300 lines):

diff -r 13da60f934da -r fbff8ca77d2e src/auth/Makefile.am
--- a/src/auth/Makefile.am	Tue Nov 10 15:07:49 2009 -0500
+++ b/src/auth/Makefile.am	Tue Nov 10 15:08:24 2009 -0500
@@ -56,6 +56,7 @@ auth_SOURCES = \
 	auth-master-connection.c \
 	mech-otp-skey-common.c \
 	mech-plain-common.c \
+	auth-penalty.c \
 	auth-request.c \
 	auth-request-handler.c \
 	auth-settings.c \
@@ -112,6 +113,7 @@ headers = \
 	auth-master-connection.h \
 	mech-otp-skey-common.h \
 	mech-plain-common.h \
+	auth-penalty.h \
 	auth-request.h \
 	auth-request-handler.h \
 	auth-settings.h \
diff -r 13da60f934da -r fbff8ca77d2e src/auth/auth-penalty.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/auth-penalty.c	Tue Nov 10 15:08:24 2009 -0500
@@ -0,0 +1,131 @@
+/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "network.h"
+#include "anvil-client.h"
+#include "auth-request.h"
+#include "auth-penalty.h"
+
+#include <stdio.h>
+
+struct auth_penalty_request {
+	struct auth_request *auth_request;
+	auth_penalty_callback_t *callback;
+};
+
+struct auth_penalty {
+	struct anvil_client *client;
+
+	unsigned int disabled:1;
+};
+
+struct auth_penalty *auth_penalty_init(const char *path)
+{
+	struct auth_penalty *penalty;
+
+	penalty = i_new(struct auth_penalty, 1);
+	penalty->client = anvil_client_init(path, NULL,
+					    ANVIL_CLIENT_FLAG_HIDE_ENOENT);
+	if (anvil_client_connect(penalty->client, TRUE) < 0)
+		penalty->disabled = TRUE;
+	else {
+		anvil_client_cmd(penalty->client, t_strdup_printf(
+			"PENALTY-SET-EXPIRE-SECS\t%u", AUTH_PENALTY_TIMEOUT));
+	}
+	return penalty;
+}
+
+void auth_penalty_deinit(struct auth_penalty **_penalty)
+{
+	struct auth_penalty *penalty = *_penalty;
+
+	*_penalty = NULL;
+	anvil_client_deinit(&penalty->client);
+	i_free(penalty);
+}
+
+unsigned int auth_penalty_to_secs(unsigned int penalty)
+{
+	unsigned int i, secs = AUTH_PENALTY_INIT_SECS;
+
+	for (i = 0; i < penalty; i++)
+		secs *= 2;
+	return secs < AUTH_PENALTY_MAX_SECS ? secs : AUTH_PENALTY_MAX_SECS;
+}
+
+static void auth_penalty_anvil_callback(const char *reply, void *context)
+{
+	struct auth_penalty_request *request = context;
+	unsigned int penalty = 0;
+	unsigned long last_update = 0;
+	unsigned int secs, drop_penalty;
+
+	if (reply == NULL) {
+		/* internal failure */
+	} else if (sscanf(reply, "%u %lu", &penalty, &last_update) != 2) {
+		i_error("Invalid PENALTY-GET reply: %s", reply);
+	} else {
+		if ((time_t)last_update > ioloop_time) {
+			/* time moved backwards? */
+			last_update = ioloop_time;
+		}
+
+		/* update penalty. */
+		drop_penalty = AUTH_PENALTY_MAX_PENALTY;
+		while (penalty > 0) {
+			secs = auth_penalty_to_secs(drop_penalty);
+			if (ioloop_time - last_update < secs)
+				break;
+			drop_penalty--;
+			penalty--;
+		}
+	}
+
+	request->callback(penalty, request->auth_request);
+}
+
+void auth_penalty_lookup(struct auth_penalty *penalty,
+			 struct auth_request *auth_request,
+			 auth_penalty_callback_t *callback)
+{
+	struct auth_penalty_request *request;
+	const char *ident;
+
+	ident = net_ip2addr(&auth_request->remote_ip);
+	if (penalty->disabled || ident == NULL) {
+		callback(0, auth_request);
+		return;
+	}
+
+	request = i_new(struct auth_penalty_request, 1);
+	request->auth_request = auth_request;
+	request->callback = callback;
+
+	T_BEGIN {
+		anvil_client_query(penalty->client,
+				   t_strdup_printf("PENALTY-GET\t%s", ident),
+				   auth_penalty_anvil_callback, request);
+	} T_END;
+}
+
+void auth_penalty_update(struct auth_penalty *penalty,
+			 struct auth_request *auth_request, unsigned int value)
+{
+	const char *ident;
+
+	ident = net_ip2addr(&auth_request->remote_ip);
+	if (penalty->disabled || ident == NULL)
+		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 =
+			t_strdup_printf("PENALTY-SET\t%s\t%u", ident, value);
+		anvil_client_cmd(penalty->client, cmd);
+	} T_END;
+}
diff -r 13da60f934da -r fbff8ca77d2e src/auth/auth-penalty.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/auth-penalty.h	Tue Nov 10 15:08:24 2009 -0500
@@ -0,0 +1,28 @@
+#ifndef AUTH_PENALTY_H
+#define AUTH_PENALTY_H
+
+struct auth_request;
+
+#define AUTH_PENALTY_INIT_SECS 2
+#define AUTH_PENALTY_MAX_SECS 15
+/* timeout specifies how long it takes for penalty to be irrelevant. */
+#define AUTH_PENALTY_TIMEOUT \
+	(AUTH_PENALTY_INIT_SECS + 4 + 8 + AUTH_PENALTY_MAX_SECS)
+#define AUTH_PENALTY_MAX_PENALTY 4
+
+/* If lookup failed, penalty and last_update are both zero */
+typedef void auth_penalty_callback_t(unsigned int penalty,
+				     struct auth_request *request);
+
+struct auth_penalty *auth_penalty_init(const char *path);
+void auth_penalty_deinit(struct auth_penalty **penalty);
+
+unsigned int auth_penalty_to_secs(unsigned int penalty);
+
+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,
+			 struct auth_request *auth_request, unsigned int value);
+
+#endif
diff -r 13da60f934da -r fbff8ca77d2e src/auth/auth-request-handler.c
--- a/src/auth/auth-request-handler.c	Tue Nov 10 15:07:49 2009 -0500
+++ b/src/auth/auth-request-handler.c	Tue Nov 10 15:08:24 2009 -0500
@@ -8,6 +8,7 @@
 #include "hash.h"
 #include "str.h"
 #include "str-sanitize.h"
+#include "auth-penalty.h"
 #include "auth-request.h"
 #include "auth-master-connection.h"
 #include "auth-request-handler.h"
@@ -15,11 +16,13 @@
 #include <stdlib.h>
 
 #define AUTH_FAILURE_DELAY_CHECK_MSECS 500
+#define AUTH_PENALTY_ANVIL_PATH "anvil-auth-penalty"
 
 struct auth_request_handler {
 	int refcount;
 	pool_t pool;
 	struct hash_table *requests;
+	struct auth_penalty *penalty;
 
         struct auth *auth;
         unsigned int connect_uid, client_pid;
@@ -55,6 +58,7 @@ auth_request_handler_create(struct auth 
 	handler->callback = callback;
 	handler->context = context;
 	handler->master_callback = master_callback;
+	handler->penalty = auth_penalty_init(AUTH_PENALTY_ANVIL_PATH);
 	return handler;
 }
 
@@ -80,6 +84,7 @@ void auth_request_handler_unref(struct a
 	/* notify parent that we're done with all requests */
 	handler->callback(NULL, handler->context);
 
+	auth_penalty_deinit(&handler->penalty);
 	hash_table_destroy(&handler->requests);
 	pool_unref(&handler->pool);
 }
@@ -188,6 +193,9 @@ auth_request_handle_failure(struct auth_
 	request->delayed_failure = TRUE;
 	handler->refcount++;
 
+	auth_penalty_update(handler->penalty, request,
+			    request->last_penalty + 1);
+
 	request->last_access = ioloop_time;
 	aqueue_append(auth_failures, &request);
 	if (to_auth_failures == NULL) {
@@ -220,6 +228,11 @@ static void auth_callback(struct auth_re
 		break;
 	case AUTH_CLIENT_RESULT_SUCCESS:
 		auth_request_proxy_finish(request, TRUE);
+
+		if (request->last_penalty != 0) {
+			/* reset penalty */
+			auth_penalty_update(handler->penalty, request, 0);
+		}
 
 		auth_stream_reply_add(reply, "OK", NULL);
 		auth_stream_reply_add(reply, NULL, dec2str(request->id));
@@ -281,14 +294,36 @@ static void auth_request_handler_auth_fa
 	auth_request_handler_remove(handler, request);
 }
 
+static void auth_request_penalty_finish(struct auth_request *request)
+{
+	timeout_remove(&request->to_penalty);
+	auth_request_initial(request);
+}
+
+static void
+auth_penalty_callback(unsigned int penalty, struct auth_request *request)
+{
+	unsigned int secs;
+
+	request->last_penalty = penalty;
+
+	if (penalty == 0)
+		auth_request_initial(request);
+	else {
+		secs = auth_penalty_to_secs(penalty);
+		request->to_penalty = timeout_add(secs * 1000,
+						  auth_request_penalty_finish,
+						  request);
+	}
+}
+
 bool auth_request_handler_auth_begin(struct auth_request_handler *handler,
 				     const char *args)
 {
 	const struct mech_module *mech;
 	struct auth_request *request;
 	const char *const *list, *name, *arg, *initial_resp;
-	const void *initial_resp_data;
-	size_t initial_resp_len;
+	void *initial_resp_data;
 	unsigned int id;
 	buffer_t *buf;
 
@@ -365,11 +400,9 @@ bool auth_request_handler_auth_begin(str
 	/* Empty initial response is a "=" base64 string. Completely empty
 	   string shouldn't really be sent, but at least Exim does it,
 	   so just allow it for backwards compatibility.. */
-	if (initial_resp == NULL || *initial_resp == '\0') {
-		initial_resp_data = NULL;
-		initial_resp_len = 0;
-	} else {
+	if (initial_resp != NULL && *initial_resp != '\0') {
 		size_t len = strlen(initial_resp);
+
 		buf = buffer_create_dynamic(pool_datastack_create(),


More information about the dovecot-cvs mailing list