From 7a675ff5e26745e2677123e84bc0cd0e83f51209 Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Wed, 25 Feb 2015 19:55:47 +0100 Subject: [PATCH] When sending BAN IP messages to main receive a reply on whether further actions should continue That allows to BAN a user even during an open connection. --- src/common.c | 8 +++++++ src/ipc.proto | 5 +++++ src/log.c | 4 +--- src/main-ban.c | 14 +++++++----- src/main-ban.h | 2 +- src/main-misc.c | 28 +++++++++++++++++++++-- src/main-sec-mod-cmd.c | 25 ++++++++++++++++++++- src/sec-mod-auth.c | 39 +++++++++++++++++++++++++++----- src/vpn.h | 4 +++- src/worker-auth.c | 9 ++------ src/worker-kkdcp.c | 2 +- src/worker-vpn.c | 50 +++++++++++++++++++++++++++++------------- src/worker.h | 3 +++ 13 files changed, 151 insertions(+), 42 deletions(-) diff --git a/src/common.c b/src/common.c index e07ffeba..1e392388 100644 --- a/src/common.c +++ b/src/common.c @@ -59,6 +59,10 @@ static char tmp[32]; return "session info"; case CMD_CLI_STATS: return "cli stats"; + case CMD_BAN_IP: + return "ban IP"; + case CMD_BAN_IP_REPLY: + return "ban IP reply"; case SM_CMD_CLI_STATS: return "sm: cli stats"; @@ -76,6 +80,10 @@ static char tmp[32]; return "sm: session close"; case SM_CMD_AUTH_SESSION_OPEN: return "sm: session open"; + case SM_CMD_AUTH_BAN_IP: + return "sm: ban IP"; + case SM_CMD_AUTH_BAN_IP_REPLY: + return "sm: ban IP reply"; default: snprintf(tmp, sizeof(tmp), "unknown (%u)", _cmd); return tmp; diff --git a/src/ipc.proto b/src/ipc.proto index 129a6503..ce1fbd13 100644 --- a/src/ipc.proto +++ b/src/ipc.proto @@ -144,6 +144,11 @@ message ban_ip_msg required uint32 score = 2; } +message ban_ip_reply_msg +{ + /* whether to disconnect the user */ + required AUTH_REP reply = 1; +} /* Messages to and from the security module */ diff --git a/src/log.c b/src/log.c index c42f4d10..8c38e02c 100644 --- a/src/log.c +++ b/src/log.c @@ -92,7 +92,6 @@ void __attribute__ ((format(printf, 3, 4))) _oclog(const worker_st * ws, int priority, const char *fmt, ...) { char buf[512]; - char ipbuf[128]; const char* ip; va_list args; @@ -111,8 +110,7 @@ void __attribute__ ((format(printf, 3, 4))) priority = LOG_DEBUG; } - ip = human_addr((void*)&ws->remote_addr, ws->remote_addr_len, - ipbuf, sizeof(ipbuf)); + ip = ws->remote_ip_str; va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); diff --git a/src/main-ban.c b/src/main-ban.c index 4fbabe64..e6ad47ee 100644 --- a/src/main-ban.c +++ b/src/main-ban.c @@ -106,16 +106,18 @@ struct htable *db = s->ban_db; return 0; } -void add_ip_to_ban_list(main_server_st *s, const char *ip, unsigned score) +/* returns -1 if the user is already banned, and zero otherwise */ +int add_ip_to_ban_list(main_server_st *s, const char *ip, unsigned score) { struct htable *db = s->ban_db; struct ban_entry_st *e; ban_entry_st t; time_t now = time(0); time_t reset_time = now + s->config->min_reauth_time; + int ret = 0; if (db == NULL || ip == NULL || ip[0] == 0) - return; + return 0; /* check if the IP is already there */ /* pass the current time somehow */ @@ -125,7 +127,7 @@ void add_ip_to_ban_list(main_server_st *s, const char *ip, unsigned score) if (e == NULL) { /* new entry */ e = talloc_zero(db, ban_entry_st); if (e == NULL) { - return; + return 0; } strlcpy(e->ip, ip, sizeof(e->ip)); @@ -147,14 +149,16 @@ void add_ip_to_ban_list(main_server_st *s, const char *ip, unsigned score) if (s->config->max_ban_score > 0 && e->score >= s->config->max_ban_score) { mslog(s, NULL, LOG_INFO,"added IP '%s' (with score %d) to ban list, will be reset at: %s", ip, e->score, ctime(&reset_time)); + ret = -1; } else { mslog(s, NULL, LOG_DEBUG,"added %d points (total %d) for IP '%s' to ban list", score, e->score, ip); + ret = 0; } - return; + return ret; fail: talloc_free(e); - return; + return ret; } void remove_ip_from_ban_list(main_server_st *s, const char *ip) diff --git a/src/main-ban.h b/src/main-ban.h index e20e82b2..6e28e3f3 100644 --- a/src/main-ban.h +++ b/src/main-ban.h @@ -25,7 +25,7 @@ void cleanup_banned_entries(main_server_st *s); unsigned check_if_banned(main_server_st *s, struct sockaddr_storage *addr, socklen_t addr_size); -void add_ip_to_ban_list(main_server_st *s, const char *ip, unsigned score); +int add_ip_to_ban_list(main_server_st *s, const char *ip, unsigned score); void remove_ip_from_ban_list(main_server_st *s, const char *ip); unsigned main_ban_db_elems(main_server_st *s); void main_ban_db_deinit(main_server_st *s); diff --git a/src/main-misc.c b/src/main-misc.c index 49ed278c..66b094a7 100644 --- a/src/main-misc.c +++ b/src/main-misc.c @@ -508,6 +508,7 @@ int handle_commands(main_server_st * s, struct proc_st *proc) switch (cmd) { case CMD_BAN_IP:{ BanIpMsg *tmsg; + BanIpReplyMsg reply = BAN_IP_REPLY_MSG__INIT; tmsg = ban_ip_msg__unpack(&pa, raw_len, raw); if (tmsg == NULL) { @@ -515,11 +516,34 @@ int handle_commands(main_server_st * s, struct proc_st *proc) ret = ERR_BAD_COMMAND; goto cleanup; } - add_ip_to_ban_list(s, tmsg->ip, tmsg->score); + + ret = add_ip_to_ban_list(s, tmsg->ip, tmsg->score); ban_ip_msg__free_unpacked(tmsg, &pa); - } + if (ret < 0) { + reply.reply = + AUTH__REP__FAILED; + } else { + reply.reply = + AUTH__REP__OK; + } + + ret = + send_msg_to_worker(s, NULL, CMD_BAN_IP_REPLY, &reply, + (pack_size_func) + ban_ip_reply_msg__get_packed_size, + (pack_func) + ban_ip_reply_msg__pack); + + if (ret < 0) { + mslog(s, NULL, LOG_ERR, + "could not send reply cmd %d.", + (unsigned)cmd); + ret = ERR_BAD_COMMAND; + goto cleanup; + } + } break; case CMD_TUN_MTU:{ TunMtuMsg *tmsg; diff --git a/src/main-sec-mod-cmd.c b/src/main-sec-mod-cmd.c index d92f3283..0dce1d9d 100644 --- a/src/main-sec-mod-cmd.c +++ b/src/main-sec-mod-cmd.c @@ -113,6 +113,7 @@ int handle_sec_mod_commands(main_server_st * s) switch (cmd) { case SM_CMD_AUTH_BAN_IP:{ BanIpMsg *tmsg; + BanIpReplyMsg reply = BAN_IP_REPLY_MSG__INIT; tmsg = ban_ip_msg__unpack(&pa, raw_len, raw); if (tmsg == NULL) { @@ -120,9 +121,31 @@ int handle_sec_mod_commands(main_server_st * s) ret = ERR_BAD_COMMAND; goto cleanup; } - add_ip_to_ban_list(s, tmsg->ip, tmsg->score); + ret = add_ip_to_ban_list(s, tmsg->ip, tmsg->score); ban_ip_msg__free_unpacked(tmsg, &pa); + + if (ret < 0) { + reply.reply = + AUTH__REP__FAILED; + } else { + reply.reply = + AUTH__REP__OK; + } + + + mslog(s, NULL, LOG_DEBUG, "sending msg %s to sec-mod", cmd_request_to_str(SM_CMD_AUTH_BAN_IP_REPLY)); + + ret = send_msg(NULL, s->sec_mod_fd, SM_CMD_AUTH_BAN_IP_REPLY, + &reply, (pack_size_func)ban_ip_reply_msg__get_packed_size, + (pack_func)ban_ip_reply_msg__pack); + if (ret < 0) { + mslog(s, NULL, LOG_ERR, + "could not send reply cmd %d.", + (unsigned)cmd); + ret = ERR_BAD_COMMAND; + goto cleanup; + } } break; diff --git a/src/sec-mod-auth.c b/src/sec-mod-auth.c index 0254611b..3a4a5f9f 100644 --- a/src/sec-mod-auth.c +++ b/src/sec-mod-auth.c @@ -72,19 +72,22 @@ void sec_auth_init(sec_mod_st * sec, struct cfg_st *config) config->acct.amod->global_init(sec, config->server_name, config->acct.additional); } +/* returns a negative number if we have reached the score for this client. + */ static -void sec_mod_add_score_to_ip(sec_mod_st *sec, void *pool, const char *ip, unsigned points) +int sec_mod_add_score_to_ip(sec_mod_st *sec, void *pool, const char *ip, unsigned points) { - void *lpool; + void *lpool = talloc_new(pool); int ret, e; BanIpMsg msg = BAN_IP_MSG__INIT; + BanIpReplyMsg *reply = NULL; + PROTOBUF_ALLOCATOR(pa, lpool); msg.ip = (char*)ip; msg.score = points; - lpool = talloc_new(pool); if (lpool == NULL) { - return; + return 0; } ret = send_msg(lpool, sec->cmd_fd, SM_CMD_AUTH_BAN_IP, &msg, @@ -93,10 +96,30 @@ void sec_mod_add_score_to_ip(sec_mod_st *sec, void *pool, const char *ip, unsign if (ret < 0) { e = errno; seclog(sec, LOG_WARNING, "error in sending BAN IP message: %s", strerror(e)); + ret = -1; + goto fail; } + + ret = recv_msg(lpool, sec->cmd_fd, SM_CMD_AUTH_BAN_IP_REPLY, (void*)&reply, + (unpack_func) ban_ip_reply_msg__unpack); + if (ret < 0) { + seclog(sec, LOG_ERR, "error receiving BAN IP reply message"); + ret = -1; + goto fail; + } + + if (reply->reply != AUTH__REP__OK) { + /* we have exceeded the maximum score */ + ret = -1; + } else { + ret = 0; + } + ban_ip_reply_msg__free_unpacked(reply, &pa); + + fail: talloc_free(lpool); - return; + return ret; } static int generate_cookie(sec_mod_st * sec, client_entry_st * entry) @@ -299,7 +322,11 @@ int handle_sec_auth_res(int cfd, sec_mod_st * sec, client_entry_st * e, int resu 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) { - sec_mod_add_score_to_ip(sec, e, e->auth_info.remote_ip, PASSWORD_POINTS); + ret = sec_mod_add_score_to_ip(sec, e, e->auth_info.remote_ip, PASSWORD_POINTS); + if (ret < 0) { + e->status = PS_AUTH_FAILED; + return send_sec_auth_reply(cfd, sec, e, AUTH__REP__FAILED); + } } ret = send_sec_auth_reply_msg(cfd, sec, e); diff --git a/src/vpn.h b/src/vpn.h index c3ea3bb2..e3772e27 100644 --- a/src/vpn.h +++ b/src/vpn.h @@ -145,6 +145,7 @@ typedef enum { CMD_SESSION_INFO = 13, CMD_CLI_STATS = 15, CMD_BAN_IP = 16, + CMD_BAN_IP_REPLY = 17, /* from worker to sec-mod */ SM_CMD_AUTH_INIT = 120, @@ -155,10 +156,11 @@ typedef enum { SM_CMD_CLI_STATS, /* from main to sec-mod and vice versa */ - SM_CMD_AUTH_SESSION_OPEN, + SM_CMD_AUTH_SESSION_OPEN=240, SM_CMD_AUTH_SESSION_CLOSE, SM_CMD_AUTH_SESSION_REPLY, SM_CMD_AUTH_BAN_IP, + SM_CMD_AUTH_BAN_IP_REPLY, } cmd_request_t; #define MAX_IP_STR 46 diff --git a/src/worker-auth.c b/src/worker-auth.c index ba43bed8..191de41b 100644 --- a/src/worker-auth.c +++ b/src/worker-auth.c @@ -1167,7 +1167,6 @@ int post_auth_handler(worker_st * ws, unsigned http_ver) char *username = NULL; char *password = NULL; char *groupname = NULL; - char ipbuf[128]; char *msg = NULL; unsigned def_group = 0; @@ -1265,9 +1264,7 @@ int post_auth_handler(worker_st * ws, unsigned http_ver) } ireq.hostname = req->hostname; - ireq.ip = - human_addr2((void *)&ws->remote_addr, ws->remote_addr_len, - ipbuf, sizeof(ipbuf), 0); + ireq.ip = ws->remote_ip_str; sd = connect_to_secmod(ws); if (sd == -1) { @@ -1292,9 +1289,7 @@ 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); + areq.ip = ws->remote_ip_str; if (ws->selected_auth->type & AUTH_TYPE_GSSAPI) { if (req->authorization == NULL || req->authorization_size <= 10) { diff --git a/src/worker-kkdcp.c b/src/worker-kkdcp.c index 19d76d4e..4a45c6ad 100644 --- a/src/worker-kkdcp.c +++ b/src/worker-kkdcp.c @@ -140,7 +140,7 @@ int post_kkdcp_handler(worker_st *ws, unsigned http_ver) return -1; } - ws->ban_points += KKDCP_POINTS; + ws_add_score_to_ip(ws, KKDCP_POINTS, 0); oclog(ws, LOG_HTTP_DEBUG, "HTTP processing kkdcp framed request: %u bytes", (unsigned)req->body_length); ret = der_decode((uint8_t*)req->body, req->body_length, buf, &length, realm, sizeof(realm), &e); diff --git a/src/worker-vpn.c b/src/worker-vpn.c index c2ec5113..a99373d3 100644 --- a/src/worker-vpn.c +++ b/src/worker-vpn.c @@ -240,29 +240,49 @@ static int setup_dtls_connection(struct worker_st *ws) return -1; } -static -void worker_add_score_to_ip(worker_st *ws, const char *ip, unsigned points) +void ws_add_score_to_ip(worker_st *ws, unsigned points, unsigned final) { - void *lpool; int ret, e; BanIpMsg msg = BAN_IP_MSG__INIT; + BanIpReplyMsg *reply = NULL; + PROTOBUF_ALLOCATOR(pa, ws); - msg.ip = (char*)ip; - msg.score = points; - - lpool = talloc_new(ws); - if (lpool == NULL) { - return; + /* In final call, no score added, we simply send */ + if (final == 0) { + ws->ban_points += points; + /* do not use IPC for small values */ + if (points < PASSWORD_POINTS) + return; } - ret = send_msg(lpool, ws->cmd_fd, CMD_BAN_IP, &msg, + msg.ip = ws->remote_ip_str; + msg.score = points; + + ret = send_msg(ws, ws->cmd_fd, CMD_BAN_IP, &msg, (pack_size_func) ban_ip_msg__get_packed_size, (pack_func) ban_ip_msg__pack); if (ret < 0) { e = errno; oclog(ws, LOG_WARNING, "error in sending BAN IP message: %s", strerror(e)); + return; } - talloc_free(lpool); + + if (final != 0) + return; + + ret = recv_msg(ws, ws->cmd_fd, CMD_BAN_IP_REPLY, + (void *)&reply, (unpack_func) ban_ip_reply_msg__unpack); + if (ret < 0) { + oclog(ws, LOG_ERR, "error receiving BAN IP reply message"); + return; + } + + if (reply->reply != AUTH__REP__OK) { + /* we have exceeded the maximum score */ + exit(1); + } + + ban_ip_reply_msg__free_unpacked(reply, &pa); return; } @@ -272,8 +292,6 @@ void worker_add_score_to_ip(worker_st *ws, const char *ip, unsigned points) */ void exit_worker(worker_st * ws) { - char buf[MAX_IP_STR]; - /* send statistics to parent */ if (ws->auth_state == S_AUTH_COMPLETE) { CliStatsMsg msg = CLI_STATS_MSG__INIT; @@ -292,8 +310,8 @@ void exit_worker(worker_st * ws) (unsigned long)msg.bytes_out); } - if (ws->ban_points > 0 && human_addr2((void*)&ws->remote_addr, ws->remote_addr_len, buf, sizeof(buf), 0) != NULL) - worker_add_score_to_ip(ws, buf, ws->ban_points); + if (ws->ban_points > 0) + ws_add_score_to_ip(ws, 0, 1); talloc_free(ws->main_pool); closelog(); @@ -402,6 +420,8 @@ void vpn_server(struct worker_st *ws) settings.on_body = http_body_cb; http_req_init(ws); + human_addr2((void*)&ws->remote_addr, ws->remote_addr_len, ws->remote_ip_str, sizeof(ws->remote_ip_str), 0); + ws->session = session; ws->parser = &parser; diff --git a/src/worker.h b/src/worker.h index 1f987fa7..5ecdace7 100644 --- a/src/worker.h +++ b/src/worker.h @@ -179,6 +179,8 @@ typedef struct worker_st { struct sockaddr_storage remote_addr; /* peer's address */ socklen_t remote_addr_len; + char remote_ip_str[MAX_IP_STR]; + int proto; /* AF_INET or AF_INET6 */ time_t session_start_time; @@ -332,6 +334,7 @@ void exit_worker(worker_st * ws); int ws_switch_auth_to(struct worker_st *ws, unsigned auth); void ws_disable_auth(struct worker_st *ws, unsigned auth); +void ws_add_score_to_ip(worker_st *ws, unsigned points, unsigned final); int connect_to_secmod(worker_st * ws); inline static