diff --git a/src/auth/common.h b/src/auth/common.h index 36c4c5d9..54c96ac2 100644 --- a/src/auth/common.h +++ b/src/auth/common.h @@ -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; diff --git a/src/auth/plain.c b/src/auth/plain.c index 1d91b986..9690a393 100644 --- a/src/auth/plain.c +++ b/src/auth/plain.c @@ -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, diff --git a/src/auth/radius.c b/src/auth/radius.c index a0e3c434..b27496d0 100644 --- a/src/auth/radius.c +++ b/src/auth/radius.c @@ -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, diff --git a/src/main.h b/src/main.h index c82f25e6..df8ef73f 100644 --- a/src/main.h +++ b/src/main.h @@ -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 */ }; diff --git a/src/sec-mod-auth.c b/src/sec-mod-auth.c index bc07939d..c3a5aca5 100644 --- a/src/sec-mod-auth.c +++ b/src/sec-mod-auth.c @@ -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) { diff --git a/src/sec-mod-auth.h b/src/sec-mod-auth.h index 72fc15f9..c2da7580 100644 --- a/src/sec-mod-auth.h +++ b/src/sec-mod-auth.h @@ -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); diff --git a/src/sec-mod-ban.c b/src/sec-mod-ban.c index 7eb4c44c..eeb99ec8 100644 --- a/src/sec-mod-ban.c +++ b/src/sec-mod-ban.c @@ -39,11 +39,14 @@ #include #include #include +#include #include #include 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; } diff --git a/src/sec-mod.h b/src/sec-mod.h index c11b7912..885ec2d1 100644 --- a/src/sec-mod.h +++ b/src/sec-mod.h @@ -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); diff --git a/src/worker-auth.c b/src/worker-auth.c index c20496ef..cac0945a 100644 --- a/src/worker-auth.c +++ b/src/worker-auth.c @@ -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)