Ban an IP only when the MAX_PASSWORD_TRIES attempts have been exceeded

This commit is contained in:
Nikos Mavrogiannopoulos
2015-02-14 08:29:42 +01:00
committed by Nikos Mavrogiannopoulos
parent 1fc59e0099
commit 108d34f613
9 changed files with 114 additions and 40 deletions

View File

@@ -1,7 +1,7 @@
#ifndef AUTH_COMMON_H
# define AUTH_COMMON_H
#define MAX_TRIES 3
#define MAX_PASSWORD_TRIES 3
extern const char* pass_msg_first;
extern const char* pass_msg_second;

View File

@@ -248,7 +248,7 @@ static int plain_auth_pass(void *ctx, const char *pass, unsigned pass_len)
&& strcmp(crypt(pass, pctx->cpass), pctx->cpass) == 0)
return 0;
else {
if (pctx->retries++ < MAX_TRIES-1) {
if (pctx->retries++ < MAX_PASSWORD_TRIES-1) {
pctx->pass_msg = pass_msg_failed;
return ERR_AUTH_CONTINUE;
} else {
@@ -370,6 +370,7 @@ static void plain_group_list(void *pool, void *additional, char ***groupname, un
const struct auth_mod_st plain_auth_funcs = {
.type = AUTH_TYPE_PLAIN | AUTH_TYPE_USERNAME_PASS,
.allows_retries = 1,
.global_init = plain_global_init,
.auth_init = plain_auth_init,
.auth_deinit = plain_auth_deinit,

View File

@@ -281,7 +281,7 @@ static int radius_auth_pass(void *ctx, const char *pass, unsigned pass_len)
if (ret == PW_ACCESS_CHALLENGE) {
pctx->pass_msg = pass_msg_second;
return ERR_AUTH_CONTINUE;
} else if (pctx->retries++ < MAX_TRIES-1) {
} else if (pctx->retries++ < MAX_PASSWORD_TRIES-1) {
pctx->pass_msg = pass_msg_failed;
return ERR_AUTH_CONTINUE;
} else {
@@ -508,6 +508,7 @@ VALUE_PAIR *send = NULL, *recvd = NULL;
const struct auth_mod_st radius_auth_funcs = {
.type = AUTH_TYPE_RADIUS | AUTH_TYPE_USERNAME_PASS,
.allows_retries = 1,
.global_init = radius_global_init,
.global_deinit = radius_global_deinit,
.auth_init = radius_auth_init,

View File

@@ -77,6 +77,7 @@ enum {
PS_AUTH_INACTIVE, /* no comm with worker */
PS_AUTH_FAILED, /* no tried authenticated but failed */
PS_AUTH_INIT, /* worker has sent an auth init msg */
PS_AUTH_CONT, /* worker has sent an auth cont msg */
PS_AUTH_COMPLETED, /* successful authentication */
};

View File

@@ -255,7 +255,21 @@ int handle_sec_auth_res(int cfd, sec_mod_st * sec, client_entry_st * e, int resu
{
int ret;
if ((result == ERR_AUTH_CONTINUE || result == 0) && e->module) {
ret = e->module->auth_msg(e->auth_ctx, e, &e->msg_str);
if (ret < 0) {
e->status = PS_AUTH_FAILED;
seclog(sec, LOG_ERR, "error getting auth msg");
return ret;
}
}
if (result == ERR_AUTH_CONTINUE) {
/* if the module allows multiple retries for the password */
if (e->status != PS_AUTH_INIT && e->module && e->module->allows_retries) {
add_ip_to_ban_list(sec, e->ip, 1, time(0) + sec->config->min_reauth_time);
}
ret = send_sec_auth_reply_msg(cfd, sec, e);
if (ret < 0) {
e->status = PS_AUTH_FAILED;
@@ -272,11 +286,13 @@ int handle_sec_auth_res(int cfd, sec_mod_st * sec, client_entry_st * e, int resu
seclog(sec, LOG_ERR, "could not send reply auth cmd.");
return ret;
}
remove_ip_from_ban_list(sec, e->ip);
ret = 0;
} else {
e->status = PS_AUTH_FAILED;
add_ip_to_ban_list(sec, e->ip, time(0) + sec->config->min_reauth_time);
add_ip_to_ban_list(sec, e->ip, 1, time(0) + sec->config->min_reauth_time);
ret = send_sec_auth_reply(cfd, sec, e, AUTH__REP__FAILED);
if (ret < 0) {
@@ -448,7 +464,7 @@ int handle_sec_auth_cont(int cfd, sec_mod_st * sec, const SecAuthContMsg * req)
return -1;
}
if (e->status != PS_AUTH_INIT) {
if (e->status != PS_AUTH_INIT && e->status != PS_AUTH_CONT) {
seclog(sec, LOG_ERR, "auth cont received but we are on state %u!", e->status);
ret = -1;
goto cleanup;
@@ -469,6 +485,8 @@ int handle_sec_auth_cont(int cfd, sec_mod_st * sec, const SecAuthContMsg * req)
goto cleanup;
}
e->status = PS_AUTH_CONT;
ret =
e->module->auth_pass(e->auth_ctx, req->password,
strlen(req->password));
@@ -476,6 +494,7 @@ int handle_sec_auth_cont(int cfd, sec_mod_st * sec, const SecAuthContMsg * req)
seclog(sec, LOG_DEBUG,
"error in password given in auth cont for user '%s'",
e->username);
goto cleanup;
}
cleanup:
@@ -558,12 +577,6 @@ int handle_sec_auth_init(int cfd, sec_mod_st * sec, const SecAuthInitMsg * req)
if (ret != 0 && req->user_name != NULL) {
strlcpy(e->username, req->user_name, sizeof(e->username));
}
ret = e->module->auth_msg(e->auth_ctx, e, &e->msg_str);
if (ret < 0) {
ret = -1;
goto cleanup;
}
}
if (e->auth_type & AUTH_TYPE_CERTIFICATE) {

View File

@@ -29,6 +29,7 @@
struct auth_mod_st {
unsigned int type;
unsigned int allows_retries; /* whether the module allows retries of the same password */
void (*global_init)(void *pool, void* additional);
void (*global_deinit)(void);
int (*auth_init)(void** ctx, void *pool, const char* username, const char* ip);

View File

@@ -39,11 +39,14 @@
#include <vpn.h>
#include <tlslib.h>
#include <sec-mod.h>
#include <auth/common.h>
#include <ccan/hash/hash.h>
#include <ccan/htable/htable.h>
typedef struct ban_entry_st {
char ip[MAX_IP_STR];
unsigned failed_attempts;
time_t expires; /* the time after the client is allowed to login */
} ban_entry_st;
@@ -54,6 +57,20 @@ static size_t rehash(const void *_e, void *unused)
}
/* The first argument is the entry from the hash, and
* the second is the entry from check_if_banned().
*/
static bool ban_entry_cmp(const void *_c1, void *_c2)
{
const struct ban_entry_st *c1 = _c1;
struct ban_entry_st *c2 = _c2;
if (strcmp(c1->ip, c2->ip) == 0)
return 1;
return 0;
}
void *sec_mod_ban_db_init(sec_mod_st *sec)
{
struct htable *db = talloc(sec, struct htable);
@@ -86,62 +103,97 @@ struct htable *db = sec->ban_db;
return 0;
}
void add_ip_to_ban_list(sec_mod_st *sec, const char *ip, time_t reenable_time)
void add_ip_to_ban_list(sec_mod_st *sec, const char *ip, unsigned attempts, time_t reset_time)
{
struct htable *db = sec->ban_db;
struct ban_entry_st *e;
ban_entry_st t;
time_t now = time(0);
if (db == NULL)
if (db == NULL || ip == NULL || ip[0] == 0)
return;
e = talloc_zero(db, ban_entry_st);
if (e == NULL) {
return;
/* check if the IP is already there */
/* pass the current time somehow */
strlcpy(t.ip, ip, sizeof(t.ip));
e = htable_get(db, rehash(&t, NULL), ban_entry_cmp, &t);
if (e == NULL) { /* new entry */
e = talloc_zero(db, ban_entry_st);
if (e == NULL) {
return;
}
strlcpy(e->ip, ip, sizeof(e->ip));
if (htable_add(db, rehash(e, NULL), e) == 0) {
seclog(sec, LOG_INFO,
"could not add ban entry to hash table");
goto fail;
}
} else {
if (now > e->expires) {
e->failed_attempts = 0;
}
}
e->failed_attempts += attempts;
e->expires = reset_time;
if (e->failed_attempts >= MAX_PASSWORD_TRIES) {
seclog(sec, LOG_INFO,"added IP '%s' (with failed attempts %d) to ban list, will be reset at: %s", ip, e->failed_attempts, ctime(&reset_time));
} else {
seclog(sec, LOG_DEBUG,"added failed attempt for IP '%s' to ban list, will be reset at: %s", ip, ctime(&reset_time));
}
strlcpy(e->ip, ip, sizeof(e->ip));
e->expires = reenable_time;
if (htable_add(db, rehash(e, NULL), e) == 0) {
seclog(sec, LOG_INFO,
"could not add ban entry to hash table");
goto fail;
}
seclog(sec, LOG_INFO,"added IP '%s' to ban list, will be removed at: %s", ip, ctime(&reenable_time));
return;
fail:
talloc_free(e);
return;
}
/* The first argument is the entry from the hash, and
* the second is the entry from check_if_banned().
*/
static bool ban_entry_cmp(const void *_c1, void *_c2)
void remove_ip_from_ban_list(sec_mod_st *sec, const char *ip)
{
const struct ban_entry_st *c1 = _c1;
struct ban_entry_st *c2 = _c2;
struct htable *db = sec->ban_db;
struct ban_entry_st *e;
ban_entry_st t;
if (strcmp(c1->ip, c2->ip) == 0 && c2->expires < c1->expires)
return 1;
return 0;
if (db == NULL || ip == NULL || ip[0] == 0)
return;
/* check if the IP is already there */
/* pass the current time somehow */
strlcpy(t.ip, ip, sizeof(t.ip));
e = htable_get(db, rehash(&t, NULL), ban_entry_cmp, &t);
if (e != NULL) { /* new entry */
e->failed_attempts = 0;
e->expires = 0;
}
return;
}
unsigned check_if_banned(sec_mod_st *sec, const char *ip)
{
struct htable *db = sec->ban_db;
ban_entry_st t;
time_t now;
ban_entry_st t, *e;
if (db == NULL || ip == NULL || ip[0] == 0)
return 0;
/* pass the current time somehow */
t.expires = time(0);
now = time(0);
strlcpy(t.ip, ip, sizeof(t.ip));
if (htable_get(db, rehash(&t, NULL), ban_entry_cmp, &t) != 0)
return 1;
e = htable_get(db, rehash(&t, NULL), ban_entry_cmp, &t);
if (e != NULL) {
if (now > e->expires)
return 0;
if (e->failed_attempts >= MAX_PASSWORD_TRIES)
return 1;
}
return 0;
}

View File

@@ -117,7 +117,8 @@ void sec_mod_server(void *main_pool, struct cfg_st *config, const char *socket_f
void cleanup_banned_entries(sec_mod_st *sec);
unsigned check_if_banned(sec_mod_st *sec, const char *ip);
void add_ip_to_ban_list(sec_mod_st *sec, const char *ip, time_t reenable_time);
void add_ip_to_ban_list(sec_mod_st *sec, const char *ip, unsigned attempts, time_t reset_time);
void remove_ip_from_ban_list(sec_mod_st *sec, const char *ip);
void *sec_mod_ban_db_init(sec_mod_st *sec);
void sec_mod_ban_db_deinit(sec_mod_st *sec);
unsigned sec_mod_ban_db_elems(sec_mod_st *sec);

View File

@@ -1284,6 +1284,10 @@ int post_auth_handler(worker_st * ws, unsigned http_ver)
|| ws->auth_state == S_AUTH_REQ) {
SecAuthContMsg areq = SEC_AUTH_CONT_MSG__INIT;
areq.ip =
human_addr2((void *)&ws->remote_addr, ws->remote_addr_len,
ipbuf, sizeof(ipbuf), 0);
if (ws->selected_auth->type & AUTH_TYPE_GSSAPI) {
if (req->authorization == NULL || req->authorization_size <= 10) {
if (req->authorization != NULL)