auth-penalty.c revision e76073ebaf90fa29abfdc364873acf78983949aa
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "lib.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "ioloop.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "network.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "anvil-client.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "auth-request.h"
a62dad9ec88bb112079dd95be456d258c6c86369Timo Sirainen#include "auth-penalty.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
a62dad9ec88bb112079dd95be456d258c6c86369Timo Sirainen#include <stdio.h>
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomistruct auth_penalty_request {
a7ad754fca008f60d348f4296e5831e31ce8cc71Timo Sirainen struct auth_request *auth_request;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi auth_penalty_callback_t *callback;
f86bd28cf4dadc4de794eebf34b28aefc8a3fc3aAki Tuomi};
f86bd28cf4dadc4de794eebf34b28aefc8a3fc3aAki Tuomi
f86bd28cf4dadc4de794eebf34b28aefc8a3fc3aAki Tuomistruct auth_penalty {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct anvil_client *client;
a62dad9ec88bb112079dd95be456d258c6c86369Timo Sirainen
a62dad9ec88bb112079dd95be456d258c6c86369Timo Sirainen unsigned int disabled:1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi};
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomistruct auth_penalty *auth_penalty_init(const char *path)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi{
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct auth_penalty *penalty;
a7ad754fca008f60d348f4296e5831e31ce8cc71Timo Sirainen
a7ad754fca008f60d348f4296e5831e31ce8cc71Timo Sirainen penalty = i_new(struct auth_penalty, 1);
a7ad754fca008f60d348f4296e5831e31ce8cc71Timo Sirainen penalty->client = anvil_client_init(path, NULL,
d86910a95633a25cf7577ea2dff0472b5a489bc3Teemu Huovila ANVIL_CLIENT_FLAG_HIDE_ENOENT);
a7ad754fca008f60d348f4296e5831e31ce8cc71Timo Sirainen if (anvil_client_connect(penalty->client, TRUE) < 0)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi penalty->disabled = TRUE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi else {
a7ad754fca008f60d348f4296e5831e31ce8cc71Timo Sirainen anvil_client_cmd(penalty->client, t_strdup_printf(
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi "PENALTY-SET-EXPIRE-SECS\t%u", AUTH_PENALTY_TIMEOUT));
a62dad9ec88bb112079dd95be456d258c6c86369Timo Sirainen }
a62dad9ec88bb112079dd95be456d258c6c86369Timo Sirainen return penalty;
a62dad9ec88bb112079dd95be456d258c6c86369Timo Sirainen}
a62dad9ec88bb112079dd95be456d258c6c86369Timo Sirainen
a62dad9ec88bb112079dd95be456d258c6c86369Timo Sirainenvoid auth_penalty_deinit(struct auth_penalty **_penalty)
a62dad9ec88bb112079dd95be456d258c6c86369Timo Sirainen{
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct auth_penalty *penalty = *_penalty;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi *_penalty = NULL;
f86bd28cf4dadc4de794eebf34b28aefc8a3fc3aAki Tuomi anvil_client_deinit(&penalty->client);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi i_free(penalty);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi}
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomiunsigned int auth_penalty_to_secs(unsigned int penalty)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi{
a7ad754fca008f60d348f4296e5831e31ce8cc71Timo Sirainen unsigned int i, secs = AUTH_PENALTY_INIT_SECS;
a62dad9ec88bb112079dd95be456d258c6c86369Timo Sirainen
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi for (i = 0; i < penalty; i++)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi secs *= 2;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return secs < AUTH_PENALTY_MAX_SECS ? secs : AUTH_PENALTY_MAX_SECS;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi}
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomistatic void auth_penalty_anvil_callback(const char *reply, void *context)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi{
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct auth_penalty_request *request = context;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi unsigned int penalty = 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi unsigned long last_update = 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi unsigned int secs, drop_penalty;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (reply == NULL) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* internal failure */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi } else if (sscanf(reply, "%u %lu", &penalty, &last_update) != 2) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi i_error("Invalid PENALTY-GET reply: %s", reply);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi } else {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if ((time_t)last_update > ioloop_time) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* time moved backwards? */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi last_update = ioloop_time;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* update penalty. */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi drop_penalty = AUTH_PENALTY_MAX_PENALTY;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi while (penalty > 0) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi secs = auth_penalty_to_secs(drop_penalty);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (ioloop_time - last_update < secs)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi break;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi drop_penalty--;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi penalty--;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi request->callback(penalty, request->auth_request);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi}
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomivoid auth_penalty_lookup(struct auth_penalty *penalty,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct auth_request *auth_request,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi auth_penalty_callback_t *callback)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi{
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct auth_penalty_request *request;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const char *ident;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi ident = net_ip2addr(&auth_request->remote_ip);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (penalty->disabled || ident == NULL) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi callback(0, auth_request);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi request = i_new(struct auth_penalty_request, 1);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi request->auth_request = auth_request;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi request->callback = callback;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi T_BEGIN {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi anvil_client_query(penalty->client,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi t_strdup_printf("PENALTY-GET\t%s", ident),
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi auth_penalty_anvil_callback, request);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi } T_END;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi}
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomivoid auth_penalty_update(struct auth_penalty *penalty,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct auth_request *auth_request, unsigned int value)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi{
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const char *ident;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi ident = net_ip2addr(&auth_request->remote_ip);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (penalty->disabled || ident == NULL)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (value > AUTH_PENALTY_MAX_PENALTY) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* even if the actual value doesn't change, the last_change
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi timestamp does. */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi value = AUTH_PENALTY_MAX_PENALTY;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi T_BEGIN {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const char *cmd =
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi t_strdup_printf("PENALTY-SET\t%s\t%u", ident, value);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi anvil_client_cmd(penalty->client, cmd);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi } T_END;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi}
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi