updates.
[silc.git] / apps / silcd / server.c
index 765f6be552630cffa935a80a39e1c75e33439625..1487ee0f376bfd3194ffdea6351bbe4fc31044fb 100644 (file)
@@ -221,16 +221,18 @@ int silc_server_init(SilcServer server)
   }
 
   /* 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 
@@ -1026,7 +1028,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
 
   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);
@@ -1124,7 +1127,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
 
   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)
@@ -1374,6 +1378,37 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
                              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)
@@ -1388,7 +1423,8 @@ 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;
 
@@ -1404,6 +1440,20 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
   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. */
@@ -1905,27 +1955,49 @@ void silc_server_disconnect_remote(SilcServer server,
   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)
@@ -1968,7 +2040,7 @@ void silc_server_free_sock_user_data(SilcServer server,
 
       /* 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;
@@ -2024,7 +2096,8 @@ int silc_server_remove_clients_by_server(SilcServer server,
        }
 
        /* 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))
@@ -2049,7 +2122,8 @@ int silc_server_remove_clients_by_server(SilcServer server,
        }
 
        /* 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))
@@ -2101,7 +2175,9 @@ int silc_server_channel_has_local(SilcChannelEntry channel)
 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;
@@ -2148,7 +2224,7 @@ void silc_server_remove_from_channels(SilcServer server,
     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,
@@ -2164,12 +2240,24 @@ void silc_server_remove_from_channels(SilcServer server,
 
     /* 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);
@@ -2313,6 +2401,7 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
   SilcChannelID *channel_id;
   SilcChannelEntry entry;
   SilcCipher key;
+  SilcHmac newhmac;
 
   SILC_LOG_DEBUG(("Creating new channel"));
 
@@ -2325,13 +2414,19 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
   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;
@@ -2365,6 +2460,7 @@ silc_server_create_new_channel_with_id(SilcServer server,
 {
   SilcChannelEntry entry;
   SilcCipher key;
+  SilcHmac newhmac;
 
   SILC_LOG_DEBUG(("Creating new channel"));
 
@@ -2377,12 +2473,18 @@ silc_server_create_new_channel_with_id(SilcServer server,
   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;
@@ -2413,9 +2515,11 @@ void silc_server_create_channel_key(SilcServer server,
                                    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;
@@ -2431,8 +2535,7 @@ void silc_server_create_channel_key(SilcServer server,
   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) {
@@ -2445,6 +2548,13 @@ void silc_server_create_channel_key(SilcServer server,
   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 
@@ -2457,7 +2567,7 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
 {
   SilcChannelKeyPayload payload = NULL;
   SilcChannelID *id = NULL;
-  unsigned char *tmp;
+  unsigned char *tmp, hash[32];
   unsigned int tmp_len;
   char *cipher;
 
@@ -2519,8 +2629,16 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
   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)
@@ -2839,3 +2957,125 @@ SILC_TASK_CALLBACK(silc_server_failure_callback)
 
   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);
+    }
+  }
+}