}
/* Initialize ID caches */
- server->local_list->clients = silc_idcache_alloc(0);
- server->local_list->servers = silc_idcache_alloc(0);
- server->local_list->channels = silc_idcache_alloc(0);
+ server->local_list->clients =
+ silc_idcache_alloc(0, silc_idlist_client_destructor);
+ server->local_list->servers = silc_idcache_alloc(0, NULL);
+ server->local_list->channels = silc_idcache_alloc(0, NULL);
/* These are allocated for normal server as well as these hold some
global information that the server has fetched from its router. For
router these are used as they are supposed to be used on router. */
- server->global_list->clients = silc_idcache_alloc(0);
- server->global_list->servers = silc_idcache_alloc(0);
- server->global_list->channels = silc_idcache_alloc(0);
+ server->global_list->clients =
+ silc_idcache_alloc(0, silc_idlist_client_destructor);
+ server->global_list->servers = silc_idcache_alloc(0, NULL);
+ server->global_list->channels = silc_idcache_alloc(0, NULL);
/* Allocate the entire socket list that is used in server. Eventually
all connections will have entry in this table (it is a table of
SILC_LOG_DEBUG(("Start"));
- if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+ if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+ protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
/* Error occured during protocol */
silc_protocol_free(protocol);
silc_ske_free_key_material(ctx->keymat);
SILC_LOG_DEBUG(("Start"));
- if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+ if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+ protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
/* Error occured during protocol */
silc_protocol_free(protocol);
if (ctx->packet)
server);
}
+/* Callback function that the silc_packet_decrypt will call to make the
+ decision whether the packet is normal or special packet. We will
+ return TRUE if it is normal and FALSE if it is special */
+
+static int silc_server_packet_decrypt_check(SilcPacketType packet_type,
+ SilcBuffer buffer,
+ SilcPacketContext *packet,
+ void *context)
+{
+ SilcPacketParserContext *parse_ctx = (SilcPacketParserContext *)context;
+ SilcServer server = (SilcServer)parse_ctx->context;
+
+ /* Packet is normal packet, if:
+
+ 1) packet is private message packet and does not have private key set
+ 2) is other packet than channel message packet
+ 3) is channel message packet and remote is router and we are router
+
+ all other packets are special packets
+ */
+ if ((packet_type == SILC_PACKET_PRIVATE_MESSAGE &&
+ !(buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY)) ||
+ packet_type != SILC_PACKET_CHANNEL_MESSAGE ||
+ (packet_type == SILC_PACKET_CHANNEL_MESSAGE &&
+ parse_ctx->sock->type == SILC_SOCKET_TYPE_ROUTER &&
+ server->server_type == SILC_ROUTER))
+ return TRUE;
+
+ return FALSE;
+}
+
/* Parses whole packet, received earlier. */
SILC_TASK_CALLBACK(silc_server_packet_parse_real)
/* Decrypt the received packet */
ret = silc_packet_decrypt(parse_ctx->cipher, parse_ctx->hmac,
- packet->buffer, packet);
+ packet->buffer, packet,
+ silc_server_packet_decrypt_check, parse_ctx);
if (ret < 0)
goto out;
if (ret == SILC_PACKET_NONE)
goto out;
+ /* Check that the the current client ID is same as in the client's packet. */
+ if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
+ SilcClientEntry client = (SilcClientEntry)sock->user_data;
+ if (client && client->id) {
+ void *id = silc_id_str2id(packet->src_id, packet->src_id_len,
+ packet->src_id_type);
+ if (SILC_ID_CLIENT_COMPARE(client->id, id)) {
+ silc_free(id);
+ goto out;
+ }
+ silc_free(id);
+ }
+ }
+
if (server->server_type == SILC_ROUTER) {
/* Route the packet if it is not destined to us. Other ID types but
server are handled separately after processing them. */
silc_server_close_connection(server, sock);
}
+typedef struct {
+ SilcServer server;
+ SilcClientEntry client;
+} *FreeClientInternal;
+
+SILC_TASK_CALLBACK(silc_server_free_client_data_timeout)
+{
+ FreeClientInternal i = (FreeClientInternal)context;
+
+ silc_idlist_del_data(i->client);
+ silc_idcache_purge_by_context(i->server->local_list->clients, i->client);
+ silc_free(i);
+}
+
/* Frees client data and notifies about client's signoff. */
void silc_server_free_client_data(SilcServer server,
SilcSocketConnection sock,
- SilcClientEntry user_data, char *signoff)
+ SilcClientEntry client, char *signoff)
{
- /* Send REMOVE_ID packet to routers. */
+ FreeClientInternal i = silc_calloc(1, sizeof(*i));
+
+ /* Send SIGNOFF notify to routers. */
if (!server->standalone && server->router)
silc_server_send_notify_signoff(server, server->router->connection,
server->server_type == SILC_SERVER ?
- FALSE : TRUE, user_data->id,
+ FALSE : TRUE, client->id,
SILC_ID_CLIENT_LEN, signoff);
/* Remove client from all channels */
- silc_server_remove_from_channels(server, sock, user_data, signoff);
+ silc_server_remove_from_channels(server, sock, client, TRUE, signoff, TRUE);
- /* XXX must take some info to history before freeing */
+ /* We will not delete the client entry right away. We will take it
+ into history (for WHOWAS command) for 5 minutes */
+ i->server = server;
+ i->client = client;
+ silc_task_register(server->timeout_queue, 0,
+ silc_server_free_client_data_timeout,
+ (void *)i, 300, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+ client->data.registered = FALSE;
/* Free the client entry and everything in it */
- silc_idlist_del_data(user_data);
- silc_idlist_del_client(server->local_list, user_data);
server->stat.my_clients--;
server->stat.clients--;
if (server->server_type == SILC_ROUTER)
/* If this was our primary router connection then we're lost to
the outside world. */
- if (server->server_type == SILC_SERVER && server->router == user_data) {
+ if (server->router == user_data) {
server->id_entry->router = NULL;
server->router = NULL;
server->standalone = TRUE;
}
/* Remove the client entry */
- silc_server_remove_from_channels(server, NULL, client, NULL);
+ silc_server_remove_from_channels(server, NULL, client, TRUE,
+ NULL, TRUE);
silc_idlist_del_client(server->local_list, client);
if (!silc_idcache_list_next(list, &id_cache))
}
/* Remove the client entry */
- silc_server_remove_from_channels(server, NULL, client, NULL);
+ silc_server_remove_from_channels(server, NULL, client, TRUE,
+ NULL, TRUE);
silc_idlist_del_client(server->global_list, client);
if (!silc_idcache_list_next(list, &id_cache))
void silc_server_remove_from_channels(SilcServer server,
SilcSocketConnection sock,
SilcClientEntry client,
- char *signoff_message)
+ int notify,
+ char *signoff_message,
+ int keygen)
{
SilcChannelEntry channel;
SilcChannelClientEntry chl;
if (server->server_type == SILC_SERVER &&
!silc_server_channel_has_local(channel)) {
/* Notify about leaving client if this channel has global users. */
- if (channel->global_users)
+ if (notify && channel->global_users)
silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
SILC_NOTIFY_TYPE_SIGNOFF,
signoff_message ? 2 : 1,
/* Send notify to channel about client leaving SILC and thus
the entire channel. */
- silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
- SILC_NOTIFY_TYPE_SIGNOFF,
- signoff_message ? 2 : 1,
- clidp->data, clidp->len,
- signoff_message, signoff_message ?
- strlen(signoff_message) : 0);
+ if (notify)
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+ SILC_NOTIFY_TYPE_SIGNOFF,
+ signoff_message ? 2 : 1,
+ clidp->data, clidp->len,
+ signoff_message, signoff_message ?
+ strlen(signoff_message) : 0);
+
+ if (keygen) {
+ /* Re-generate channel key */
+ silc_server_create_channel_key(server, channel, 0);
+
+ /* Send the channel key to the channel. The key of course is not sent
+ to the client who was removed f rom the channel. */
+ silc_server_send_channel_key(server, client->connection, channel,
+ server->server_type == SILC_ROUTER ?
+ FALSE : !server->standalone);
+ }
}
silc_buffer_free(clidp);
SilcChannelID *channel_id;
SilcChannelEntry entry;
SilcCipher key;
+ SilcHmac newhmac;
SILC_LOG_DEBUG(("Creating new channel"));
if (!silc_cipher_alloc(cipher, &key))
return NULL;
+ /* Allocate hmac */
+ if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
+ silc_cipher_free(key);
+ return NULL;
+ }
+
channel_name = strdup(channel_name);
/* Create the channel */
silc_id_create_channel_id(router_id, server->rng, &channel_id);
entry = silc_idlist_add_channel(server->local_list, channel_name,
SILC_CHANNEL_MODE_NONE, channel_id,
- NULL, key, hmac);
+ NULL, key, newhmac);
if (!entry) {
silc_free(channel_name);
return NULL;
{
SilcChannelEntry entry;
SilcCipher key;
+ SilcHmac newhmac;
SILC_LOG_DEBUG(("Creating new channel"));
if (!silc_cipher_alloc(cipher, &key))
return NULL;
+ /* Allocate hmac */
+ if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
+ silc_cipher_free(key);
+ return NULL;
+ }
+
channel_name = strdup(channel_name);
/* Create the channel */
entry = silc_idlist_add_channel(server->local_list, channel_name,
SILC_CHANNEL_MODE_NONE, channel_id,
- NULL, key, hmac);
+ NULL, key, newhmac);
if (!entry) {
silc_free(channel_name);
return NULL;
unsigned int key_len)
{
int i;
- unsigned char channel_key[32];
+ unsigned char channel_key[32], hash[32];
unsigned int len;
+ SILC_LOG_DEBUG(("Generating channel key"));
+
if (!channel->channel_key)
if (!silc_cipher_alloc("aes-256-cbc", &channel->channel_key))
return;
for (i = 0; i < len; i++) channel_key[i] = silc_rng_get_byte(server->rng);
/* Set the key */
- channel->channel_key->cipher->set_key(channel->channel_key->context,
- channel_key, len);
+ silc_cipher_set_key(channel->channel_key, channel_key, len * 8);
/* Remove old key if exists */
if (channel->key) {
channel->key = silc_calloc(len, sizeof(*channel->key));
memcpy(channel->key, channel_key, len);
memset(channel_key, 0, sizeof(channel_key));
+
+ /* Generate HMAC key from the channel key data and set it */
+ if (!channel->hmac)
+ silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac);
+ silc_hash_make(channel->hmac->hash, channel->key, len, hash);
+ silc_hmac_set_key(channel->hmac, hash, silc_hash_len(channel->hmac->hash));
+ memset(hash, 0, sizeof(hash));
}
/* Saves the channel key found in the encoded `key_payload' buffer. This
{
SilcChannelKeyPayload payload = NULL;
SilcChannelID *id = NULL;
- unsigned char *tmp;
+ unsigned char *tmp, hash[32];
unsigned int tmp_len;
char *cipher;
channel->key_len = tmp_len * 8;
channel->key = silc_calloc(tmp_len, sizeof(unsigned char));
memcpy(channel->key, tmp, tmp_len);
- channel->channel_key->cipher->set_key(channel->channel_key->context,
- tmp, tmp_len);
+ silc_cipher_set_key(channel->channel_key, tmp, channel->key_len);
+
+ /* Generate HMAC key from the channel key data and set it */
+ if (!channel->hmac)
+ silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac);
+ silc_hash_make(channel->hmac->hash, tmp, tmp_len, hash);
+ silc_hmac_set_key(channel->hmac, hash, silc_hash_len(channel->hmac->hash));
+
+ memset(hash, 0, sizeof(hash));
+ memset(tmp, 0, tmp_len);
out:
if (id)
silc_free(f);
}
+
+/* Assembles user list and users mode list from the `channel'. */
+
+void silc_server_get_users_on_channel(SilcServer server,
+ SilcChannelEntry channel,
+ SilcBuffer *user_list,
+ SilcBuffer *mode_list,
+ unsigned int *user_count)
+{
+ SilcChannelClientEntry chl;
+ SilcBuffer client_id_list;
+ SilcBuffer client_mode_list;
+ SilcBuffer idp;
+ unsigned int list_count = 0;
+
+ client_id_list = silc_buffer_alloc((SILC_ID_CLIENT_LEN + 4) *
+ silc_list_count(channel->user_list));
+ client_mode_list = silc_buffer_alloc(4 *
+ silc_list_count(channel->user_list));
+ silc_buffer_pull_tail(client_id_list, SILC_BUFFER_END(client_id_list));
+ silc_buffer_pull_tail(client_mode_list, SILC_BUFFER_END(client_mode_list));
+ silc_list_start(channel->user_list);
+ while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+ /* Client ID */
+ idp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
+ silc_buffer_put(client_id_list, idp->data, idp->len);
+ silc_buffer_pull(client_id_list, idp->len);
+ silc_buffer_free(idp);
+
+ /* Client's mode on channel */
+ SILC_PUT32_MSB(chl->mode, client_mode_list->data);
+ silc_buffer_pull(client_mode_list, 4);
+
+ list_count++;
+ }
+ silc_buffer_push(client_id_list,
+ client_id_list->data - client_id_list->head);
+ silc_buffer_push(client_mode_list,
+ client_mode_list->data - client_mode_list->head);
+
+ *user_list = client_id_list;
+ *mode_list = client_mode_list;
+ *user_count = list_count;
+}
+
+/* Saves users and their modes to the `channel'. */
+
+void silc_server_save_users_on_channel(SilcServer server,
+ SilcSocketConnection sock,
+ SilcChannelEntry channel,
+ SilcClientID *noadd,
+ SilcBuffer user_list,
+ SilcBuffer mode_list,
+ unsigned int user_count)
+{
+ int i;
+
+ /* Cache the received Client ID's and modes. This cache expires
+ whenever server sends notify message to channel. It means two things;
+ some user has joined or leaved the channel. XXX TODO! */
+ for (i = 0; i < user_count; i++) {
+ unsigned short idp_len;
+ unsigned int mode;
+ SilcClientID *client_id;
+ SilcClientEntry client;
+
+ /* Client ID */
+ SILC_GET16_MSB(idp_len, user_list->data + 2);
+ idp_len += 4;
+ client_id = silc_id_payload_parse_id(user_list->data, idp_len);
+ silc_buffer_pull(user_list, idp_len);
+ if (!client_id)
+ continue;
+
+ /* Mode */
+ SILC_GET32_MSB(mode, mode_list->data);
+ silc_buffer_pull(mode_list, 4);
+
+ if (noadd && !SILC_ID_CLIENT_COMPARE(client_id, noadd)) {
+ silc_free(client_id);
+ continue;
+ }
+
+ /* Check if we have this client cached already. */
+ client = silc_idlist_find_client_by_id(server->local_list, client_id,
+ NULL);
+ if (!client)
+ client = silc_idlist_find_client_by_id(server->global_list,
+ client_id, NULL);
+ if (!client) {
+ /* If router did not find such Client ID in its lists then this must
+ be bogus client or some router in the net is buggy. */
+ if (server->server_type == SILC_ROUTER) {
+ silc_free(client_id);
+ continue;
+ }
+
+ /* We don't have that client anywhere, add it. The client is added
+ to global list since server didn't have it in the lists so it must be
+ global. */
+ client = silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
+ silc_id_dup(client_id, SILC_ID_CLIENT),
+ sock->user_data, NULL);
+ if (!client) {
+ silc_free(client_id);
+ continue;
+ }
+ }
+
+ silc_free(client_id);
+
+ if (!silc_server_client_on_channel(client, channel)) {
+ /* Client was not on the channel, add it. */
+ SilcChannelClientEntry chl = silc_calloc(1, sizeof(*chl));
+ chl->client = client;
+ chl->mode = mode;
+ chl->channel = channel;
+ silc_list_add(channel->user_list, chl);
+ silc_list_add(client->channels, chl);
+ }
+ }
+}