From 6ef74bad35256fee51eea502bf040696848ee81f Mon Sep 17 00:00:00 2001 From: Martin Johansson Date: Tue, 6 Mar 2012 20:40:45 +0100 Subject: [PATCH] Add banlist message handling. Bans can now be edited, added and removed via the banlist editor in Mumble. --- src/ban.c | 123 +++++++++++++++++++++++++++++++++++++++---- src/ban.h | 8 +++ src/messagehandler.c | 30 ++++++++++- src/messages.c | 107 ++++++++++++++++++++++++++++++++++++- src/messages.h | 10 +++- 5 files changed, 263 insertions(+), 15 deletions(-) diff --git a/src/ban.c b/src/ban.c index fbf711a..7101485 100644 --- a/src/ban.c +++ b/src/ban.c @@ -30,6 +30,7 @@ */ #include +#include #include "log.h" #include "list.h" #include "ban.h" @@ -38,51 +39,69 @@ declare_list(banlist); static int bancount; /* = 0 */ +static int ban_duration; +void Ban_init(void) +{ + ban_duration = getIntConf(BAN_LENGTH); + /* Read ban file here */ +} + +void Ban_deinit(void) +{ + /* Save banlist */ +} void Ban_UserBan(client_t *client, char *reason) { ban_t *ban; char hexhash[41]; ban = malloc(sizeof(ban_t)); + if (ban == NULL) + Log_fatal("Out of memory"); + memset(ban, 0, sizeof(ban_t)); + memcpy(ban->hash, client->hash, 20); memcpy(&ban->address, &client->remote_tcp.sin_addr, sizeof(in_addr_t)); + ban->mask = 128; ban->reason = strdup(reason); ban->name = strdup(client->username); + ban->time = time(NULL); + ban->duration = ban_duration; Timer_init(&ban->startTime); list_add_tail(&ban->node, &banlist); + bancount++; SSLi_hash2hex(ban->hash, hexhash); Log_info_client(client, "User kickbanned. Reason: '%s' Hash: %s IP: %s Banned for: %d seconds", ban->name, ban->reason, hexhash, inet_ntoa(*((struct in_addr *)&ban->address)), - getIntConf(BAN_LENGTH)); + ban->duration); } void Ban_pruneBanned() { struct dlist *itr; - static int64_t bantime = 0; ban_t *ban; char hexhash[41]; - - if (bantime == 0) { - bantime = getIntConf(BAN_LENGTH) * 1000000LL; - } - + uint64_t bantime_long; + list_iterate(itr, &banlist) { ban = list_get_entry(itr, ban_t, node); + bantime_long = ban->duration * 1000000LL; #ifdef DEBUG SSLi_hash2hex(ban->hash, hexhash); Log_debug("BL: User %s Reason: '%s' Hash: %s IP: %s Time left: %d", ban->name, ban->reason, hexhash, inet_ntoa(*((struct in_addr *)&ban->address)), - bantime / 1000000LL - Timer_elapsed(&ban->startTime) / 1000000LL); + bantime_long / 1000000LL - Timer_elapsed(&ban->startTime) / 1000000LL); #endif - if (Timer_isElapsed(&ban->startTime, bantime)) { + /* Duration of 0 = forever */ + if (ban->duration != 0 && Timer_isElapsed(&ban->startTime, bantime_long)) { free(ban->name); free(ban->reason); list_del(&ban->node); free(ban); + bancount--; } } } @@ -104,11 +123,95 @@ bool_t Ban_isBannedAddr(in_addr_t *addr) { struct dlist *itr; ban_t *ban; + int mask; + in_addr_t tempaddr1, tempaddr2; + list_iterate(itr, &banlist) { ban = list_get_entry(itr, ban_t, node); - if (memcmp(&ban->address, addr, sizeof(in_addr_t)) == 0) + mask = ban->mask - 96; + if (mask < 32) { /* XXX - only ipv4 support */ + memcpy(&tempaddr1, addr, sizeof(in_addr_t)); + memcpy(&tempaddr2, &ban->address, sizeof(in_addr_t)); + tempaddr1 &= (2 ^ mask) - 1; + tempaddr2 &= (2 ^ mask) - 1; + } + if (memcmp(&tempaddr1, &tempaddr2, sizeof(in_addr_t)) == 0) return true; } return false; } +int Ban_getBanCount(void) +{ + return bancount; +} + +message_t *Ban_getBanList(void) +{ + int i = 0; + struct dlist *itr; + ban_t *ban; + message_t *msg; + struct tm timespec; + char timestr[32]; + char hexhash[41]; + uint8_t address[16]; + + msg = Msg_banList_create(bancount); + list_iterate(itr, &banlist) { + ban = list_get_entry(itr, ban_t, node); + gmtime_r(&ban->time, ×pec); + strftime(timestr, 32, "%Y-%m-%dT%H:%M:%S", ×pec); + SSLi_hash2hex(ban->hash, hexhash); + /* ipv4 representation as ipv6 address. */ + memset(address, 0, 16); + memcpy(&address[12], &ban->address, 4); + memset(&address[10], 0xff, 2); /* IPv4 */ + Msg_banList_addEntry(msg, i++, address, ban->mask, ban->name, + hexhash, ban->reason, timestr, ban->duration); + } + return msg; +} + +void Ban_clearBanList() +{ + ban_t *ban; + struct dlist *itr, *save; + list_iterate_safe(itr, save, &banlist) { + ban = list_get_entry(itr, ban_t, node); + free(ban->name); + free(ban->reason); + list_del(&ban->node); + free(ban); + bancount--; + } +} + +void Ban_putBanList(message_t *msg, int n_bans) +{ + int i = 0; + struct tm timespec; + ban_t *ban; + char *hexhash, *name, *reason, *start; + uint32_t duration, mask; + uint8_t *address; + + for (i = 0; i < n_bans; i++) { + Msg_banList_getEntry(msg, i, &address, &mask, &name, &hexhash, &reason, &start, &duration); + ban = malloc(sizeof(ban_t)); + if (ban == NULL) + Log_fatal("Out of memory"); + memset(ban, 0, sizeof(ban_t)); + SSLi_hex2hash(hexhash, ban->hash); + memcpy(&ban->address, &address[12], 4); + ban->mask = mask; + ban->reason = strdup(reason); + ban->name = strdup(name); + strptime(start, "%Y-%m-%dT%H:%M:%S", ×pec); + ban->time = mktime(×pec); + Timer_init(&ban->startTime); + ban->duration = duration; + list_add_tail(&ban->node, &banlist); + bancount++; + } +} diff --git a/src/ban.h b/src/ban.h index 61d5774..f74fd7a 100644 --- a/src/ban.h +++ b/src/ban.h @@ -39,8 +39,11 @@ typedef struct { uint8_t hash[20]; in_addr_t address; + uint32_t mask; char *reason; char *name; + time_t time; + uint32_t duration; etimer_t startTime; struct dlist node; } ban_t; @@ -49,5 +52,10 @@ void Ban_UserBan(client_t *client, char *reason); void Ban_pruneBanned(); bool_t Ban_isBanned(client_t *client); bool_t Ban_isBannedAddr(in_addr_t *addr); +int Ban_getBanCount(void); +message_t *Ban_getBanList(void); +void Ban_putBanList(message_t *msg, int n_bans); +void Ban_init(void); +void Ban_deinit(void); #endif diff --git a/src/messagehandler.c b/src/messagehandler.c index 50b648a..f07e3ca 100644 --- a/src/messagehandler.c +++ b/src/messagehandler.c @@ -40,6 +40,7 @@ #include "channel.h" #include "conf.h" #include "voicetarget.h" +#include "ban.h" #define MAX_TEXT 512 #define MAX_USERNAME 128 @@ -627,6 +628,8 @@ void Mh_handle_message(client_t *client, message_t *msg) if (!getBoolConf(ALLOW_TEXTMESSAGE)) msg->payload.permissionQuery->permissions &= ~PERM_TEXTMESSAGE; + if (!getBoolConf(ENABLE_BAN)) + msg->payload.permissionQuery->permissions &= ~PERM_BAN; Client_send_message(client, msg); break; @@ -800,6 +803,7 @@ void Mh_handle_message(client_t *client, message_t *msg) memset(sendmsg->payload.userStats->address.data, 0, 16); /* ipv4 representation as ipv6 address. Supposedly correct. */ memcpy(&sendmsg->payload.userStats->address.data[12], &target->remote_tcp.sin_addr, 4); + memset(&sendmsg->payload.userStats->address.data[10], 0xff, 2); /* IPv4 */ sendmsg->payload.userStats->address.len = 16; } /* BW */ @@ -835,7 +839,10 @@ void Mh_handle_message(client_t *client, message_t *msg) msg->payload.userRemove->actor = client->sessionId; if (msg->payload.userRemove->has_ban && msg->payload.userRemove->ban) { - Ban_UserBan(target, msg->payload.userRemove->reason); + if (!getBoolConf(ENABLE_BAN)) + sendPermissionDenied(client, "Permission denied"); + else + Ban_UserBan(target, msg->payload.userRemove->reason); } else { Log_info_client(target, "User kicked. Reason: '%s'", strlen(msg->payload.userRemove->reason) == 0 ? "N/A" : msg->payload.userRemove->reason); @@ -846,13 +853,32 @@ void Mh_handle_message(client_t *client, message_t *msg) Client_send_message_except(NULL, msg); Client_close(target); break; + case BanList: + /* Only admin can issue this */ + if (!client->isAdmin) { + sendPermissionDenied(client, "Permission denied"); + break; + } + if (!getBoolConf(ENABLE_BAN)) { + sendPermissionDenied(client, "Permission denied"); + break; + } + if (msg->payload.banList->has_query && msg->payload.banList->query) { + /* Create banlist message and add banentrys */ + sendmsg = Ban_getBanList(); + Client_send_message(client, sendmsg); + } else { + /* Clear banlist and set the new one */ + Ban_clearBanList(); + Ban_putBanList(msg, msg->payload.banList->n_bans); + } + break; /* Permission denied for all these messages. Not implemented. */ case ChannelRemove: case ContextAction: case ContextActionAdd: case ACL: - case BanList: case UserList: case QueryUsers: sendPermissionDenied(client, "Not supported by uMurmur"); diff --git a/src/messages.c b/src/messages.c index 893708a..b9ce9a7 100644 --- a/src/messages.c +++ b/src/messages.c @@ -247,6 +247,16 @@ int Msg_messageToNetwork(message_t *msg, uint8_t *buffer) mumble_proto__server_config__pack(msg->payload.serverConfig, bufptr); break; + case BanList: + len = mumble_proto__ban_list__get_packed_size(msg->payload.banList); + if (len > MAX_MSGSIZE) { + Log_warn("Too big tx message. Discarding"); + break; + } + Msg_addPreamble(buffer, msg->messageType, len); + Log_debug("Msg_MessageToNetwork: BanList size %d", len); + mumble_proto__ban_list__pack(msg->payload.banList, bufptr); + break; default: Log_warn("Msg_MessageToNetwork: Unsupported message %d", msg->messageType); return 0; @@ -270,6 +280,7 @@ static message_t *Msg_create_nopayload(messageType_t messageType) message_t *Msg_create(messageType_t messageType) { message_t *msg = Msg_create_nopayload(messageType); + int i; switch (messageType) { case Version: @@ -370,6 +381,73 @@ message_t *Msg_create(messageType_t messageType) return msg; } +message_t *Msg_banList_create(int n_bans) +{ + message_t *msg = Msg_create_nopayload(BanList); + int i; + + msg->payload.banList = malloc(sizeof(MumbleProto__BanList)); + if (msg->payload.banList == NULL) + Log_fatal("Out of memory"); + memset(msg->payload.banList, 0, sizeof(MumbleProto__BanList)); + mumble_proto__ban_list__init(msg->payload.banList); + msg->payload.banList->n_bans = n_bans; + msg->payload.banList->bans = malloc(sizeof(MumbleProto__BanList__BanEntry *) * n_bans); + if (msg->payload.banList->bans == NULL) + Log_fatal("Out of memory"); + for (i = 0; i < n_bans; i++) { + msg->payload.banList->bans[i] = malloc(sizeof(MumbleProto__BanList__BanEntry)); + if (msg->payload.banList->bans[i] == NULL) + Log_fatal("Out of memory"); + memset(msg->payload.banList->bans[i], 0, sizeof(MumbleProto__BanList__BanEntry)); + mumble_proto__ban_list__ban_entry__init(msg->payload.banList->bans[i]); + } + return msg; +} + +void Msg_banList_addEntry(message_t *msg, int index, uint8_t *address, uint32_t mask, + char *name, char *hash, char *reason, char *start, uint32_t duration) +{ + MumbleProto__BanList__BanEntry *entry = msg->payload.banList->bans[index]; + + entry->address.data = malloc(16); + if (!entry->address.data) + Log_fatal("Out of memory"); + memcpy(entry->address.data, address, 16); + entry->address.len = 16; + entry->mask = mask; + entry->name = strdup(name); + entry->hash = strdup(hash); + entry->reason = strdup(reason); + entry->start = strdup(start); + if (!entry->name || !entry->hash || !entry->reason || !entry->start) + Log_fatal("Out of memory"); + + if (duration > 0) { + entry->duration = duration; + entry->has_duration = true; + } + Log_debug("Msg_banList_addEntry: %s %s %s %s %s", entry->name, entry->hash, entry->address.data, entry->reason, entry->start); +} + +void Msg_banList_getEntry(message_t *msg, int index, uint8_t **address, uint32_t *mask, + char **name, char **hash, char **reason, char **start, uint32_t *duration) +{ + MumbleProto__BanList__BanEntry *entry = msg->payload.banList->bans[index]; + + *address = entry->address.data; + *mask = entry->mask; + *name = entry->name; + *hash = entry->hash; + *reason = entry->reason; + *start = entry->start; + if (entry->has_duration) + *duration = entry->duration; + else + *duration = 0; +} + + void Msg_inc_ref(message_t *msg) { msg->refcount++; @@ -377,6 +455,8 @@ void Msg_inc_ref(message_t *msg) void Msg_free(message_t *msg) { + int i; + if (msg->refcount) msg->refcount--; if (msg->refcount > 0) return; @@ -563,6 +643,22 @@ void Msg_free(message_t *msg) free(msg->payload.serverConfig); } break; + case BanList: + if (msg->unpacked) + mumble_proto__ban_list__free_unpacked(msg->payload.banList, NULL); + else { + for (i = 0; i < msg->payload.banList->n_bans; i++) { + free(msg->payload.banList->bans[i]->address.data); + free(msg->payload.banList->bans[i]->name); + free(msg->payload.banList->bans[i]->hash); + free(msg->payload.banList->bans[i]->reason); + free(msg->payload.banList->bans[i]->start); + free(msg->payload.banList->bans[i]); + } + free(msg->payload.banList->bans); + free(msg->payload.banList); + } + break; default: Log_warn("Msg_free: Unsupported message %d", msg->messageType); @@ -758,9 +854,18 @@ message_t *Msg_networkToMessage(uint8_t *data, int size) goto err_out; break; } + case BanList: + { + msg = Msg_create_nopayload(BanList); + msg->unpacked = true; + msg->payload.banList = mumble_proto__ban_list__unpack(NULL, msgLen, msgData); + if (msg->payload.banList == NULL) + goto err_out; + break; + } default: - Log_warn("Unsupported message %d", messageType); + Log_warn("Msg_networkToMessage: Unsupported message %d", messageType); break; } return msg; diff --git a/src/messages.h b/src/messages.h index e92fe2a..69013a0 100644 --- a/src/messages.h +++ b/src/messages.h @@ -32,6 +32,7 @@ #define MESSAGES_H_89768 #include +#include #include "Mumble.pb-c.h" #include "list.h" #include "types.h" @@ -111,8 +112,7 @@ typedef union payload { struct _MumbleProto__ChannelState *channelState; struct _MumbleProto__UserRemove *userRemove; struct _MumbleProto__UserState *userState; - /* BanEntry not supported */ - /* BanList not supported */ + struct _MumbleProto__BanList *banList; struct _MumbleProto__TextMessage *textMessage; struct _MumbleProto__PermissionDenied *permissionDenied; /* ChanACL not supported */ @@ -147,5 +147,11 @@ void Msg_inc_ref(message_t *msg); message_t *Msg_CreateVoiceMsg(uint8_t *data, int size); message_t *Msg_create(messageType_t messageType); +void Msg_banList_addEntry(message_t *msg, int index, uint8_t *address, uint32_t mask, + char *name, char *hash, char *reason, char *start, uint32_t duration); +void Msg_banList_getEntry(message_t *msg, int index, uint8_t **address, uint32_t *mask, + char **name, char **hash, char **reason, char **start, uint32_t *duration); +message_t *Msg_banList_create(int n_bans); + #endif -- 2.30.2