-/* Copyright (C) 2009-2010, Martin Johansson <martin@fatbob.nu>
- Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>
+/* Copyright (C) 2009-2013, Martin Johansson <martin@fatbob.nu>
+ Copyright (C) 2005-2013, Thorvald Natvig <thorvald@natvig.com>
All rights reserved.
#include "channel.h"
#include "conf.h"
#include "voicetarget.h"
+#include "ban.h"
#define MAX_TEXT 512
+#define MAX_USERNAME 128
+
+#define NO_CELT_MESSAGE "<strong>WARNING:</strong> Your client doesn't support the CELT codec, you won't be able to talk to or hear most clients. Please make sure your client was built with CELT support."
+
extern channel_t *defaultChan;
extern int iCodecAlpha, iCodecBeta;
-extern bool_t bPreferAlpha;
+extern bool_t bPreferAlpha, bOpus;
+
+static bool_t fake_celt_support;
static void sendServerReject(client_t *client, const char *reason, MumbleProto__Reject__RejectType type)
{
Client_send_message(client, msg);
}
+static void addTokens(client_t *client, message_t *msg)
+{
+ int i;
+ if (client->tokencount + msg->payload.authenticate->n_tokens < MAX_TOKENS) {
+ /* Check lengths first */
+ for (i = 0; i < msg->payload.authenticate->n_tokens; i++) {
+ if (strlen(msg->payload.authenticate->tokens[i]) > MAX_TOKENSIZE - 1) {
+ sendPermissionDenied(client, "Too long token");
+ return;
+ }
+ }
+
+ for (i = 0; i < msg->payload.authenticate->n_tokens; i++) {
+ Log_debug("Adding token '%s' to client '%s'", msg->payload.authenticate->tokens[i], client->username);
+ Client_token_add(client, msg->payload.authenticate->tokens[i]);
+ }
+ }
+ else
+ sendPermissionDenied(client, "Too many tokens");
+}
+
void Mh_handle_message(client_t *client, message_t *msg)
{
message_t *sendmsg = NULL;
channel_t *ch_itr = NULL;
- client_t *client_itr;
+ client_t *client_itr, *target;
if (!client->authenticated && !(msg->messageType == Authenticate ||
msg->messageType == Version)) {
Log_debug("Authenticate message received");
if (IS_AUTH(client) || !msg->payload.authenticate->username) {
- /* Authenticate message might be sent when a token is set by the user.*/
+ /* Authenticate message might be sent when a tokens are changed by the user.*/
+ Client_token_free(client); /* Clear the token list */
if (msg->payload.authenticate->n_tokens > 0) {
- Log_debug("Tokens in auth message from %s", client->username);
+ Log_debug("Tokens in auth message from '%s'. n_tokens = %d", client->username,
+ msg->payload.authenticate->n_tokens);
+ addTokens(client, msg);
}
break;
}
+ if (SSLi_getSHA1Hash(client->ssl, client->hash) && Ban_isBanned(client)) {
+ char hexhash[41];
+ SSLi_hash2hex(client->hash, hexhash);
+ Log_info("Client with hash '%s' is banned. Disconnecting", hexhash);
+ goto disconnect;
+ }
+
client->authenticated = true;
client_itr = NULL;
while (Client_iterate(&client_itr) != NULL) {
if (!IS_AUTH(client_itr))
continue;
- if (client_itr->username && strncmp(client_itr->username, msg->payload.authenticate->username, MAX_TEXT) == 0) {
+ if (client_itr->username && strncmp(client_itr->username, msg->payload.authenticate->username, MAX_USERNAME) == 0) {
char buf[64];
sprintf(buf, "Username already in use");
Log_debug("Username already in use");
}
}
if (strlen(msg->payload.authenticate->username) == 0 ||
- strlen(msg->payload.authenticate->username) >= MAX_TEXT) { /* XXX - other invalid names? */
+ strlen(msg->payload.authenticate->username) >= MAX_USERNAME) { /* XXX - other invalid names? */
char buf[64];
sprintf(buf, "Invalid username");
Log_debug("Invalid username");
if (Client_count() >= getIntConf(MAX_CLIENTS)) {
char buf[64];
- sprintf(buf, "Server is full (max %d users)", getIntConf(MAX_CLIENTS));
+ snprintf(buf, 64, "Server is full (max %d users)", getIntConf(MAX_CLIENTS));
sendServerReject(client, buf, MUMBLE_PROTO__REJECT__REJECT_TYPE__ServerFull);
goto disconnect;
}
- /* Name & password */
+ /* Name */
client->username = strdup(msg->payload.authenticate->username);
+
+ /* Tokens */
+ if (msg->payload.authenticate->n_tokens > 0)
+ addTokens(client, msg);
+
+ /* Check if admin PW among tokens */
+ if (strlen(getStrConf(ADMIN_PASSPHRASE)) > 0 &&
+ Client_token_match(client, getStrConf(ADMIN_PASSPHRASE))) {
+ client->isAdmin = true;
+ Log_info_client(client, "User provided admin password");
+ }
/* Setup UDP encryption */
CryptState_init(&client->cryptState);
Log_debug("Client %d CELT codec ver 0x%x", client->sessionId, codec_itr->codec);
} else {
- Client_codec_add(client, (int32_t)0x8000000a);
+ Client_codec_add(client, (int32_t)0x8000000b);
client->codec_count = 1;
+ fake_celt_support = true;
}
+ if (msg->payload.authenticate->opus)
+ client->bOpus = true;
- recheckCodecVersions();
+ recheckCodecVersions(client);
sendmsg = Msg_create(CodecVersion);
sendmsg->payload.codecVersion->alpha = iCodecAlpha;
sendmsg->payload.codecVersion->beta = iCodecBeta;
sendmsg->payload.codecVersion->prefer_alpha = bPreferAlpha;
+ sendmsg->payload.codecVersion->has_opus = true;
+ sendmsg->payload.codecVersion->opus = bOpus;
Client_send_message(client, sendmsg);
-
+
+ if (!bOpus && client->bOpus && fake_celt_support) {
+ Client_textmessage(client, NO_CELT_MESSAGE);
+ }
+
/* Iterate channels and send channel info */
ch_itr = NULL;
while (Chan_iterate(&ch_itr) != NULL) {
sendmsg->payload.channelState->name = strdup(ch_itr->name);
if (ch_itr->desc)
sendmsg->payload.channelState->description = strdup(ch_itr->desc);
+ if (ch_itr->position != 0) {
+ sendmsg->payload.channelState->has_position = true;
+ sendmsg->payload.channelState->position = ch_itr->position;
+ }
Log_debug("Send channel info: %s", sendmsg->payload.channelState->name);
Client_send_message(client, sendmsg);
}
sendmsg->payload.userState->name = strdup(client->username);
sendmsg->payload.userState->has_channel_id = true;
sendmsg->payload.userState->channel_id = ((channel_t *)client->channel)->id;
-
+
Client_send_message_except(client, sendmsg);
client_itr = NULL;
sendmsg->payload.userState->has_channel_id = true;
sendmsg->payload.userState->channel_id = ((channel_t *)client_itr->channel)->id;
- /* Only self_mute/deaf supported */
- if (client_itr->deaf) {
+ if (client_itr->self_deaf) {
sendmsg->payload.userState->has_self_deaf = true;
sendmsg->payload.userState->self_deaf = true;
}
- if (client_itr->mute) {
+ if (client_itr->self_mute) {
sendmsg->payload.userState->has_self_mute = true;
sendmsg->payload.userState->self_mute = true;
}
+ if (client_itr->deaf) {
+ sendmsg->payload.userState->has_deaf = true;
+ sendmsg->payload.userState->deaf = true;
+ }
+ if (client_itr->mute) {
+ sendmsg->payload.userState->has_mute = true;
+ sendmsg->payload.userState->mute = true;
+ }
+ if (client_itr->recording) {
+ sendmsg->payload.userState->has_recording = true;
+ sendmsg->payload.userState->recording = true;
+ }
Client_send_message(client, sendmsg);
}
}
break;
case UserState:
- /* Only allow state changes for for the self user */
+ target = NULL;
+ /* Only allow state changes for for the self user unless an admin is issuing */
if (msg->payload.userState->has_session &&
- msg->payload.userState->session != client->sessionId) {
+ msg->payload.userState->session != client->sessionId && !client->isAdmin) {
sendPermissionDenied(client, "Permission denied");
break;
}
- if (msg->payload.userState->has_user_id || msg->payload.userState->has_mute ||
- msg->payload.userState->has_deaf || msg->payload.userState->has_suppress ||
- msg->payload.userState->has_texture) {
-
+ if (msg->payload.userState->has_session && msg->payload.userState->session != client->sessionId) {
+ while (Client_iterate(&target) != NULL) {
+ if (target->sessionId == msg->payload.userState->session)
+ break;
+ }
+ if (target == NULL) {
+ Log_warn("Client with sessionID %d not found", msg->payload.userState->session);
+ break;
+ }
+ }
+
+ if (msg->payload.userState->has_user_id || msg->payload.userState->has_suppress ||
+ msg->payload.userState->has_priority_speaker || msg->payload.userState->has_texture) {
sendPermissionDenied(client, "Not supported by uMurmur");
break;
}
+
+ if (target == NULL)
+ target = client;
msg->payload.userState->has_session = true;
- msg->payload.userState->session = client->sessionId;
+ msg->payload.userState->session = target->sessionId;
msg->payload.userState->has_actor = true;
msg->payload.userState->actor = client->sessionId;
+ if (msg->payload.userState->has_deaf) {
+ target->deaf = msg->payload.userState->deaf;
+ if (target->deaf) {
+ msg->payload.userState->has_mute = true;
+ msg->payload.userState->mute = true;
+ }
+ }
+ if (msg->payload.userState->has_mute) {
+ target->mute = msg->payload.userState->mute;
+ if (!target->mute) {
+ msg->payload.userState->has_deaf = true;
+ msg->payload.userState->deaf = false;
+ target->deaf = false;
+ }
+ }
if (msg->payload.userState->has_self_deaf) {
- client->deaf = msg->payload.userState->self_deaf;
+ client->self_deaf = msg->payload.userState->self_deaf;
+ if (client->self_deaf) {
+ msg->payload.userState->has_self_mute = true;
+ msg->payload.userState->self_mute = true;
+ }
}
if (msg->payload.userState->has_self_mute) {
- client->mute = msg->payload.userState->self_mute;
- if (!client->mute) {
+ client->self_mute = msg->payload.userState->self_mute;
+ if (!client->self_mute) {
msg->payload.userState->has_self_deaf = true;
msg->payload.userState->self_deaf = false;
- client->deaf = false;
+ client->self_deaf = false;
}
}
+ if (msg->payload.userState->has_recording &&
+ msg->payload.userState->recording != client->recording) {
+ client->recording = msg->payload.userState->recording;
+ char *message;
+ uint32_t *tree_id;
+
+ message = malloc(strlen(client->username) + 32);
+ if (!message)
+ Log_fatal("Out of memory");
+ tree_id = malloc(sizeof(uint32_t));
+ if (!tree_id)
+ Log_fatal("Out of memory");
+ *tree_id = 0;
+ sendmsg = Msg_create(TextMessage);
+ sendmsg->payload.textMessage->message = message;
+ sendmsg->payload.textMessage->n_tree_id = 1;
+ sendmsg->payload.textMessage->tree_id = tree_id;
+ if (client->recording)
+ sprintf(message, "User %s started recording", client->username);
+ else
+ sprintf(message, "User %s stopped recording", client->username);
+ Client_send_message_except_ver(NULL, sendmsg, ~0x010203);
+ sendmsg = NULL;
+ }
if (msg->payload.userState->has_channel_id) {
int leave_id;
- if (!Chan_userJoin_id_test(msg->payload.userState->channel_id))
- break;
- leave_id = Chan_userJoin_id(msg->payload.userState->channel_id, client);
+ channelJoinResult_t chjoin_rc = Chan_userJoin_id_test(msg->payload.userState->channel_id, target);
+
+ if (chjoin_rc != CHJOIN_OK) {
+ if (chjoin_rc == CHJOIN_WRONGPW) {
+ if (target == client && !client->isAdmin) {
+ sendPermissionDenied(client, "Wrong channel password");
+ break;
+ }
+ /* Tricky one: if user hasn't the password, but is moved to the channel by admin then let
+ * the user in. Also let admin user in regardless of channel password.
+ * Take no action on other errors.
+ */
+ else if (!client->isAdmin)
+ break;
+ }
+ else break;
+ }
+
+ leave_id = Chan_userJoin_id(msg->payload.userState->channel_id, target);
if (leave_id > 0) {
Log_debug("Removing channel ID %d", leave_id);
sendmsg = Msg_create(ChannelRemove);
break; /* Don't inform other users about this state */
}
-
/* Re-use message */
Msg_inc_ref(msg);
break;
case TextMessage:
+ if (!getBoolConf(ALLOW_TEXTMESSAGE))
+ break;
msg->payload.textMessage->has_actor = true;
msg->payload.textMessage->actor = client->sessionId;
list_iterate(itr, &ch_itr->clients) {
client_t *c;
c = list_get_entry(itr, client_t, chan_node);
- if (c != client && !c->deaf) {
+ if (c != client && !c->deaf && !c->self_deaf) {
Msg_inc_ref(msg);
Client_send_message(c, msg);
Log_debug("Text message to session ID %d", c->sessionId);
if (!IS_AUTH(itr))
continue;
if (itr->sessionId == msg->payload.textMessage->session[i]) {
- if (!itr->deaf) {
+ if (!itr->deaf && !itr->self_deaf) {
Msg_inc_ref(msg);
Client_send_message(itr, msg);
Log_debug("Text message to session ID %d", itr->sessionId);
case PermissionQuery:
Msg_inc_ref(msg); /* Re-use message */
msg->payload.permissionQuery->has_permissions = true;
- msg->payload.permissionQuery->permissions = PERM_DEFAULT;
+
+ if (client->isAdmin)
+ msg->payload.permissionQuery->permissions = PERM_ADMIN;
+ else
+ msg->payload.permissionQuery->permissions = PERM_DEFAULT;
+
+ 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;
newchan = Chan_createChannel(msg->payload.channelState->name,
msg->payload.channelState->description);
newchan->temporary = true;
+ if (msg->payload.channelState->has_position)
+ newchan->position = msg->payload.channelState->position;
Chan_addChannel(parent, newchan);
msg->payload.channelState->has_channel_id = true;
msg->payload.channelState->channel_id = newchan->id;
sendmsg->payload.userStats->version->has_version = true;
sendmsg->payload.userStats->version->version = target->version;
- sendmsg->payload.userStats->version->release = strdup(target->release);
- sendmsg->payload.userStats->version->os = strdup(target->os);
- sendmsg->payload.userStats->version->os_version = strdup(target->os_version);
+ if (target->release)
+ sendmsg->payload.userStats->version->release = strdup(target->release);
+ if (target->os)
+ sendmsg->payload.userStats->version->os = strdup(target->os);
+ if (target->os_version)
+ sendmsg->payload.userStats->version->os_version = strdup(target->os_version);
sendmsg->payload.userStats->n_celt_versions = target->codec_count;
sendmsg->payload.userStats->celt_versions = malloc(sizeof(int32_t) * target->codec_count);
while (Client_codec_iterate(target, &codec_itr) != NULL)
sendmsg->payload.userStats->celt_versions[i++] = codec_itr->codec;
+ sendmsg->payload.userStats->has_opus = true;
+ sendmsg->payload.userStats->opus = target->bOpus;
+
/* Address */
sendmsg->payload.userStats->has_address = true;
sendmsg->payload.userStats->address.data = malloc(sizeof(uint8_t) * 16);
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 */
Client_send_message(client, sendmsg);
}
break;
+ case UserRemove:
+ target = NULL;
+ /* Only admin can issue this */
+ if (!client->isAdmin) {
+ sendPermissionDenied(client, "Permission denied");
+ break;
+ }
+ while (Client_iterate(&target) != NULL) {
+ if (target->sessionId == msg->payload.userRemove->session)
+ break;
+ }
+ if (target == NULL) {
+ Log_warn("Client with sessionId %d not found", msg->payload.userRemove->session);
+ break;
+ }
+ msg->payload.userRemove->session = target->sessionId;
+ msg->payload.userRemove->has_actor = true;
+ msg->payload.userRemove->actor = client->sessionId;
+
+ if (msg->payload.userRemove->has_ban && msg->payload.userRemove->ban) {
+ 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);
+ }
+ /* Re-use message */
+ Msg_inc_ref(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");