mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-02-10 08:46:58 +08:00
Ban an IP only when the MAX_PASSWORD_TRIES attempts have been exceeded
This commit is contained in:
committed by
Nikos Mavrogiannopoulos
parent
1fc59e0099
commit
108d34f613
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 */
|
||||
};
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user