From 25abd43600a693a47acd755b5713b342de86a7ca Mon Sep 17 00:00:00 2001 From: Andrei Karas Date: Tue, 6 Nov 2018 19:49:46 +0300 Subject: Add send packet validation for connections between server and clients. --- src/char/char.c | 17 +++++++++------- src/common/socket.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++------- src/common/socket.h | 9 +++++++-- src/login/login.c | 2 ++ src/map/chrif.c | 1 + src/map/clif.c | 1 + src/map/irc-bot.c | 1 + 7 files changed, 71 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/char/char.c b/src/char/char.c index 18269aeaf..5575ce6fe 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -4353,9 +4353,9 @@ static void char_delete2_cancel(int fd, struct char_session_data *sd) static void char_send_account_id(int fd, int account_id) { - WFIFOHEAD(fd,4); - WFIFOL(fd,0) = account_id; - WFIFOSET(fd,4); + WFIFOHEAD(fd, 4); + WFIFOL(fd, 0) = account_id; + WFIFOSET2(fd, 4); } static void char_parse_char_connect(int fd, struct char_session_data *sd, uint32 ipl) @@ -4908,10 +4908,10 @@ static void char_parse_char_delete2_cancel(int fd, struct char_session_data *sd) // 3 - error static void char_login_map_server_ack(int fd, uint8 flag) { - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x2af9; - WFIFOB(fd,2) = flag; - WFIFOSET(fd,3); + WFIFOHEAD(fd, 3); + WFIFOW(fd, 0) = 0x2af9; + WFIFOB(fd, 2) = flag; + WFIFOSET2(fd, 3); } static void char_parse_char_login_map_server(int fd, uint32 ipl) @@ -4938,6 +4938,7 @@ static void char_parse_char_login_map_server(int fd, uint32 ipl) chr->server[i].users = 0; sockt->session[fd]->func_parse = chr->parse_frommap; sockt->session[fd]->flag.server = 1; + sockt->session[fd]->flag.validate = 0; sockt->realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); chr->mapif_init(fd); } @@ -5307,6 +5308,7 @@ static int char_check_connect_login_server(int tid, int64 tick, int id, intptr_t sockt->session[chr->login_fd]->func_parse = chr->parse_fromlogin; sockt->session[chr->login_fd]->flag.server = 1; + sockt->session[chr->login_fd]->flag.validate = 0; sockt->realloc_fifo(chr->login_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); loginif->connect_to_server(); @@ -6299,6 +6301,7 @@ int do_init(int argc, char **argv) Sql_ShowDebug(inter->sql_handle); sockt->set_defaultparse(chr->parse_char); + sockt->validate = true; if ((chr->char_fd = sockt->make_listen_bind(bind_ip,chr->port)) == -1) { ShowFatalError("Failed to bind to port '"CL_WHITE"%d"CL_RESET"'\n",chr->port); diff --git a/src/common/socket.c b/src/common/socket.c index fd86414d6..5f74ccf2f 100644 --- a/src/common/socket.c +++ b/src/common/socket.c @@ -30,6 +30,7 @@ #include "common/memmgr.h" #include "common/mmo.h" #include "common/nullpo.h" +#include "common/packets.h" #include "common/showmsg.h" #include "common/strlib.h" #include "common/timer.h" @@ -580,6 +581,7 @@ static int connect_client(int listen_fd) create_session(fd, recv_to_fifo, send_from_fifo, default_func_parse); sockt->session[fd]->client_addr = ntohl(client_address.sin_addr.s_addr); + sockt->session[fd]->flag.validate = sockt->validate; return fd; } @@ -649,7 +651,6 @@ static int make_listen_bind(uint32 ip, uint16 port) create_session(fd, connect_client, null_send, null_parse); sockt->session[fd]->client_addr = 0; // just listens sockt->session[fd]->rdata_tick = 0; // disable timeouts on this socket - return fd; } @@ -821,14 +822,12 @@ static int rfifoskip(int fd, size_t len) } /// advance the WFIFO cursor (marking 'len' bytes for sending) -static int wfifoset(int fd, size_t len) +static int wfifoset(int fd, size_t len, bool validate) { - size_t newreserve; - struct socket_data* s; - if (!sockt->session_is_valid(fd)) return 0; - s = sockt->session[fd]; + + struct socket_data* s = sockt->session[fd]; if (s == NULL || s->wdata == NULL) return 0; @@ -867,6 +866,10 @@ static int wfifoset(int fd, size_t len) } } + + if (validate && s->flag.validate == 1) + sockt->validateWfifo(fd, len); + s->wdata_size += len; #ifdef SHOW_SERVER_STATS socket_data_qo += len; @@ -877,7 +880,7 @@ static int wfifoset(int fd, size_t len) // always keep a WFIFO_SIZE reserve in the buffer // For inter-server connections, let the reserve be 1/4th of the link size. - newreserve = s->flag.server ? FIFOSIZE_SERVERLINK / 4 : WFIFO_SIZE; + size_t newreserve = s->flag.server ? FIFOSIZE_SERVERLINK / 4 : WFIFO_SIZE; // readjust the buffer to include the chosen reserve sockt->realloc_writefifo(fd, newreserve); @@ -2049,6 +2052,43 @@ static void socket_net_config_read(const char *filename) return; } +static void socket_validateWfifo(int fd, size_t len) +{ + if (len < 2) { + ShowError("Sent packet with size smaller than 2\n"); + Assert_retv(0); + return; + } + const uint cmd = (uint)WFIFOW(fd, 0); + if (cmd < MIN_PACKET_DB || cmd > MAX_PACKET_DB) { + ShowError("Sent wrong packet id: 0x%04X\n", cmd); + Assert_retv(0); + return; + } + if (len > 65535) { + ShowError("Sent packet with size bigger than 65535\n"); + Assert_retv(0); + return; + } + int packet_len = packets->db[cmd]; + const int len2 = (int)len; + if (packet_len == -1) { + if (len2 < 4) { + ShowError("Sent packet with size smaller than 2\n"); + Assert_retv(0); + return; + } + packet_len = WFIFOW(fd, 2); + if (packet_len != len2) { + ShowError("Sent packet 0x%04X with dynamic size %d, but must be size %d \n", cmd, len2, packet_len); + Assert_retv(0); + } + } else if (packet_len != len2) { + ShowError("Sent packet 0x%04X with size %d, but must be size %d \n", cmd, len2, packet_len); + Assert_retv(0); + } +} + void socket_defaults(void) { sockt = &sockt_s; @@ -2060,6 +2100,7 @@ void socket_defaults(void) /* */ memset(&sockt->addr_, 0, sizeof(sockt->addr_)); sockt->naddr_ = 0; + sockt->validate = false; /* */ VECTOR_INIT(sockt->lan_subnets); VECTOR_INIT(sockt->allowed_ips); @@ -2099,4 +2140,5 @@ void socket_defaults(void) sockt->trusted_ip_check = socket_trusted_ip_check; sockt->net_config_read_sub = socket_net_config_read_sub; sockt->net_config_read = socket_net_config_read; + sockt->validateWfifo = socket_validateWfifo; } diff --git a/src/common/socket.h b/src/common/socket.h index 3c082e718..2d497ebfc 100644 --- a/src/common/socket.h +++ b/src/common/socket.h @@ -73,7 +73,8 @@ struct config_setting_t; } \ } while(0) -#define WFIFOSET(fd, len) (sockt->wfifoset(fd, len)) +#define WFIFOSET(fd, len) (sockt->wfifoset(fd, len, true)) +#define WFIFOSET2(fd, len) (sockt->wfifoset(fd, len, false)) #define RFIFOSKIP(fd, len) (sockt->rfifoskip(fd, len)) /* [Ind/Hercules] */ @@ -122,6 +123,7 @@ struct socket_data { unsigned char eof : 1; unsigned char server : 1; unsigned char ping : 2; + unsigned char validate : 1; } flag; uint32 client_addr; // remote client address @@ -178,9 +180,11 @@ struct socket_interface { /* */ time_t stall_time; time_t last_tick; + /* */ uint32 addr_[16]; // ip addresses of local host (host byte order) int naddr_; // # of ip addresses + bool validate; struct socket_data **session; @@ -200,9 +204,10 @@ struct socket_interface { int (*make_connection) (uint32 ip, uint16 port, struct hSockOpt *opt); int (*realloc_fifo) (int fd, unsigned int rfifo_size, unsigned int wfifo_size); int (*realloc_writefifo) (int fd, size_t addition); - int (*wfifoset) (int fd, size_t len); + int (*wfifoset) (int fd, size_t len, bool validate); int (*rfifoskip) (int fd, size_t len); void (*close) (int fd); + void (*validateWfifo) (int fd, size_t len); /* */ bool (*session_is_valid) (int fd); bool (*session_is_active) (int fd); diff --git a/src/login/login.c b/src/login/login.c index d54348834..c1844cb25 100644 --- a/src/login/login.c +++ b/src/login/login.c @@ -1457,6 +1457,7 @@ static void login_parse_request_connection(int fd, struct login_session_data* sd sockt->session[fd]->func_parse = login->parse_fromchar; sockt->session[fd]->flag.server = 1; + sockt->session[fd]->flag.validate = 0; sockt->realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); // send connection success @@ -2175,6 +2176,7 @@ int do_init(int argc, char **argv) // set default parser as lclif->parse function sockt->set_defaultparse(lclif->parse); + sockt->validate = true; // every 10 minutes cleanup online account db. timer->add_func_list(login->online_data_cleanup, "login->online_data_cleanup"); diff --git a/src/map/chrif.c b/src/map/chrif.c index 43ea7ebe3..af3504ca2 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -1592,6 +1592,7 @@ static int check_connect_char_server(int tid, int64 tick, int id, intptr_t data) sockt->session[chrif->fd]->func_parse = chrif->parse; sockt->session[chrif->fd]->flag.server = 1; + sockt->session[chrif->fd]->flag.validate = 0; sockt->realloc_fifo(chrif->fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); chrif->connect(chrif->fd); diff --git a/src/map/clif.c b/src/map/clif.c index 9f1038d2c..e1909b438 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -22254,6 +22254,7 @@ static int do_init_clif(bool minimal) packetdb_loaddb(); sockt->set_defaultparse(clif->parse); + sockt->validate = true; if (sockt->make_listen_bind(clif->bind_ip,clif->map_port) == -1) { ShowFatalError("Failed to bind to port '"CL_WHITE"%d"CL_RESET"'\n",clif->map_port); exit(EXIT_FAILURE); diff --git a/src/map/irc-bot.c b/src/map/irc-bot.c index 6f37fa4a6..996107fea 100644 --- a/src/map/irc-bot.c +++ b/src/map/irc-bot.c @@ -63,6 +63,7 @@ static int irc_connect_timer(int tid, int64 tick, int id, intptr_t data) if ((ircbot->fd = sockt->make_connection(ircbot->ip, channel->config->irc_server_port, &opt)) > 0) { sockt->session[ircbot->fd]->func_parse = ircbot->parse; sockt->session[ircbot->fd]->flag.server = 1; + sockt->session[ircbot->fd]->flag.validate = 0; timer->add(timer->gettick() + 3000, ircbot->identify_timer, 0, 0); ircbot->isOn = true; } -- cgit v1.2.3-60-g2f50