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