Merged silc_1_0_branch to trunk.
[silc.git] / lib / silcclient / client_channel.c
index 485decf0867e70e8fec29bfba533bcf6f4d2364e..0d4384096b62779c95409bb0d786a5a6f8b8fd5a 100644 (file)
@@ -2,15 +2,14 @@
 
   client_channel.c
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2001 Pekka Riikonen
+  Copyright (C) 1997 - 2003 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
@@ -19,7 +18,7 @@
 */
 /* $Id$ */
 /* This file includes channel message sending and receiving routines,
-   channel key receiving and setting, and channel private key handling 
+   channel key receiving and setting, and channel private key handling
    routines. */
 
 #include "silcincludes.h"
 #include "client_internal.h"
 
 /* Sends packet to the `channel'. Packet to channel is always encrypted
-   differently from "normal" packets. SILC header of the packet is 
+   differently from "normal" packets. SILC header of the packet is
    encrypted with the next receiver's key and the rest of the packet is
    encrypted with the channel specific key. Padding and HMAC is computed
    with the next receiver's key. The `data' is the channel message. If
    the `force_send' is TRUE then the packet is sent immediately. */
 
-void silc_client_send_channel_message(SilcClient client, 
+bool silc_client_send_channel_message(SilcClient client,
                                      SilcClientConnection conn,
                                      SilcChannelEntry channel,
                                      SilcChannelPrivateKey key,
                                      SilcMessageFlags flags,
-                                     unsigned char *data, 
-                                     SilcUInt32 data_len, 
-                                     int force_send)
+                                     unsigned char *data,
+                                     SilcUInt32 data_len,
+                                     bool force_send)
 {
-  int i;
-  SilcSocketConnection sock = conn->sock;
+  SilcSocketConnection sock;
   SilcBuffer payload;
   SilcPacketContext packetdata;
   const SilcBufferStruct packet;
   SilcCipher cipher;
   SilcHmac hmac;
   unsigned char *id_string;
-  SilcUInt32 iv_len;
   int block_len;
+  SilcChannelUser chu;
+  bool ret = FALSE;
 
+  assert(client && conn && channel);
+  sock = conn->sock;
   SILC_LOG_DEBUG(("Sending packet to channel"));
 
+  chu = silc_client_on_channel(channel, conn->local_entry);
+  if (!chu) {
+    SILC_LOG_ERROR(("Cannot send message to channel we are not joined"));
+    return FALSE;
+  }
+
+  /* Check if it is allowed to send messages to this channel by us. */
+  if (channel->mode & SILC_CHANNEL_MODE_SILENCE_USERS && !chu->mode)
+    return FALSE;
+  if (channel->mode & SILC_CHANNEL_MODE_SILENCE_OPERS &&
+      chu->mode & SILC_CHANNEL_UMODE_CHANOP &&
+      !(chu->mode & SILC_CHANNEL_UMODE_CHANFO))
+    return FALSE;
+  if (chu->mode & SILC_CHANNEL_UMODE_QUIET)
+    return FALSE;
+
   /* Take the key to be used */
   if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
     if (key) {
@@ -66,13 +83,13 @@ void silc_client_send_channel_message(SilcClient client,
       cipher = channel->curr_key->cipher;
       hmac = channel->curr_key->hmac;
     } else if (!channel->curr_key && channel->private_keys) {
-      /* Use just some private key since we don't know what to use 
+      /* Use just some private key since we don't know what to use
         and private keys are set. */
       silc_dlist_start(channel->private_keys);
       key = silc_dlist_get(channel->private_keys);
       cipher = key->cipher;
       hmac = key->hmac;
-      
+
       /* Use this key as current private key */
       channel->curr_key = key;
     } else {
@@ -86,28 +103,25 @@ void silc_client_send_channel_message(SilcClient client,
     hmac = channel->hmac;
   }
 
-  if (!cipher || !hmac)
-    return;
+  if (!cipher || !hmac) {
+    SILC_LOG_ERROR(("No cipher and HMAC for channel"));
+    return FALSE;
+  }
 
   block_len = silc_cipher_get_block_len(cipher);
 
-  /* Generate IV */
-  iv_len = silc_cipher_get_block_len(cipher);
-  if (channel->iv[0] == '\0')
-    for (i = 0; i < iv_len; i++) channel->iv[i] = 
-                                  silc_rng_get_byte(client->rng);
-  else
-    silc_hash_make(client->internal->md5hash, channel->iv, iv_len, 
-                  channel->iv);
-
-  /* Encode the channel payload. This also encrypts the message payload. */
-  payload = silc_channel_message_payload_encode(flags, data_len, data, iv_len, 
-                                               channel->iv, cipher, hmac,
-                                               client->rng);
+  /* Encode the message payload. This also encrypts the message payload. */
+  payload = silc_message_payload_encode(flags, data, data_len, TRUE, FALSE,
+                                       cipher, hmac, client->rng, NULL,
+                                       client->private_key, client->sha1hash);
+  if (!payload) {
+    SILC_LOG_ERROR(("Error encoding channel message"));
+    return FALSE;
+  }
 
   /* Get data used in packet header encryption, keys and stuff. */
-  cipher = conn->send_key;
-  hmac = conn->hmac_send;
+  cipher = conn->internal->send_key;
+  hmac = conn->internal->hmac_send;
   id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
 
   /* Set the packet context pointers. The destination ID is always
@@ -126,11 +140,11 @@ void silc_client_send_channel_message(SilcClient client,
   data_len = SILC_PACKET_DATALEN(data_len, SILC_PACKET_HEADER_LEN +
                                 packetdata.src_id_len +
                                 packetdata.dst_id_len);
-  packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
+  packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN +
     packetdata.src_id_len + packetdata.dst_id_len;
-  packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
-                                         packetdata.src_id_len +
-                                         packetdata.dst_id_len), block_len);
+  SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
+                     packetdata.src_id_len +
+                     packetdata.dst_id_len), block_len, packetdata.padlen);
 
   /* Create the outgoing packet */
   if (!silc_packet_assemble(&packetdata, client->rng, cipher, hmac, sock,
@@ -139,10 +153,10 @@ void silc_client_send_channel_message(SilcClient client,
     goto out;
   }
 
-  /* Encrypt the header and padding of the packet. This is encrypted 
+  /* Encrypt the header and padding of the packet. This is encrypted
      with normal session key shared with our server. */
-  silc_packet_encrypt(cipher, hmac, conn->psn_send++,
-                     (SilcBuffer)&packet, SILC_PACKET_HEADER_LEN + 
+  silc_packet_encrypt(cipher, hmac, conn->internal->psn_send++,
+                     (SilcBuffer)&packet, SILC_PACKET_HEADER_LEN +
                      packetdata.src_id_len + packetdata.dst_id_len +
                      packetdata.padlen);
 
@@ -152,13 +166,23 @@ void silc_client_send_channel_message(SilcClient client,
   /* Now actually send the packet */
   silc_client_packet_send_real(client, sock, force_send);
 
+  /* Check for mandatory rekey */
+  if (conn->internal->psn_send == SILC_CLIENT_REKEY_THRESHOLD)
+    silc_schedule_task_add(client->schedule, sock->sock,
+                          silc_client_rekey_callback, sock, 0, 1,
+                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+
+  ret = TRUE;
+
  out:
   silc_buffer_free(payload);
   silc_free(id_string);
+
+  return ret;
 }
 
 typedef struct {
-  SilcChannelMessagePayload payload;
+  SilcMessagePayload payload;
   SilcChannelID *channel_id;
 } *SilcChannelClientResolve;
 
@@ -173,6 +197,7 @@ static void silc_client_channel_message_cb(SilcClient client,
   if (clients_count == 1) {
     SilcChannelEntry channel;
     unsigned char *message;
+    SilcUInt32 message_len;
 
     channel = silc_client_get_channel_by_id(client, conn, res->channel_id);
     if (!channel)
@@ -188,39 +213,40 @@ static void silc_client_channel_message_cb(SilcClient client,
       silc_hash_table_add(clients[0]->channels, channel, chu);
     }
 
-    message = silc_channel_message_get_data(res->payload, NULL);
-    
+    message = silc_message_get_data(res->payload, &message_len);
+
     /* Pass the message to application */
     client->internal->ops->channel_message(
-                              client, conn, clients[0], channel,
-                              silc_channel_message_get_flags(res->payload),
-                              message);
+                           client, conn, clients[0], channel, res->payload,
+                           silc_message_get_flags(res->payload),
+                           message, message_len);
   }
 
  out:
-  silc_channel_message_payload_free(res->payload);
+  silc_message_payload_free(res->payload);
   silc_free(res->channel_id);
   silc_free(res);
 }
 
 /* Process received message to a channel (or from a channel, really). This
    decrypts the channel message with channel specific key and parses the
-   channel payload. Finally it displays the message on the screen. */
+   message payload. Finally it displays the message on the screen. */
 
-void silc_client_channel_message(SilcClient client, 
-                                SilcSocketConnection sock, 
+void silc_client_channel_message(SilcClient client,
+                                SilcSocketConnection sock,
                                 SilcPacketContext *packet)
 {
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
   SilcBuffer buffer = packet->buffer;
-  SilcChannelMessagePayload payload = NULL;
+  SilcMessagePayload payload = NULL;
   SilcChannelID *id = NULL;
   SilcChannelEntry channel;
   SilcClientEntry client_entry;
   SilcClientID *client_id = NULL;
   unsigned char *message;
+  SilcUInt32 message_len;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Received channel message"));
 
   /* Sanity checks */
   if (packet->dst_id_type != SILC_ID_CHANNEL)
@@ -239,39 +265,54 @@ void silc_client_channel_message(SilcClient client,
   if (!channel)
     goto out;
 
-  /* If there is no channel private key then just decrypt the message 
+  /* If there is no channel private key then just decrypt the message
      with the channel key. If private keys are set then just go through
      all private keys and check what decrypts correctly. */
   if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
     /* Parse the channel message payload. This also decrypts the payload */
-    payload = silc_channel_message_payload_parse(buffer->data, buffer->len, 
-                                                channel->channel_key,
-                                                channel->hmac);
+    payload = silc_message_payload_parse(buffer->data, buffer->len, FALSE,
+                                        FALSE, channel->channel_key,
+                                        channel->hmac);
 
     /* If decryption failed and we have just performed channel key rekey
        we will use the old key in decryption. If that fails too then we
        cannot do more and will drop the packet. */
     if (!payload) {
-      if (!channel->old_channel_key) {
-       goto out;
-      }
+      SilcCipher key;
+      SilcHmac hmac;
+      int i;
 
-      payload = silc_channel_message_payload_parse(buffer->data, buffer->len, 
-                                                  channel->old_channel_key,
-                                                  channel->old_hmac);
-      if (!payload) {
+      if (!channel->old_channel_keys ||
+         !silc_dlist_count(channel->old_channel_keys))
        goto out;
+
+      SILC_LOG_DEBUG(("Attempting to decrypt with old channel key(s)"));
+
+      silc_dlist_end(channel->old_channel_keys);
+      silc_dlist_end(channel->old_hmacs);
+      for (i = 0; i < silc_dlist_count(channel->old_channel_keys); i++) {
+       key = silc_dlist_get(channel->old_channel_keys);
+       hmac = silc_dlist_get(channel->old_hmacs);
+       if (!key || !hmac)
+         break;
+
+       payload = silc_message_payload_parse(buffer->data, buffer->len,
+                                            FALSE, FALSE, key, hmac);
+       if (payload)
+         break;
       }
+      if (!payload)
+       goto out;
     }
   } else if (channel->private_keys) {
     SilcChannelPrivateKey entry;
 
     silc_dlist_start(channel->private_keys);
     while ((entry = silc_dlist_get(channel->private_keys)) != SILC_LIST_END) {
-      /* Parse the channel message payload. This also decrypts the payload */
-      payload = silc_channel_message_payload_parse(buffer->data, buffer->len, 
-                                                  entry->cipher,
-                                                  entry->hmac);
+      /* Parse the message payload. This also decrypts the payload */
+      payload = silc_message_payload_parse(buffer->data, buffer->len,
+                                          FALSE, FALSE,
+                                          entry->cipher, entry->hmac);
       if (payload)
        break;
     }
@@ -283,12 +324,13 @@ void silc_client_channel_message(SilcClient client,
 
   /* Find client entry */
   client_entry = silc_client_get_client_by_id(client, conn, client_id);
-  if (!client_entry || !client_entry->nickname) {
+  if (!client_entry || !client_entry->nickname ||
+      !silc_client_on_channel(channel, client_entry)) {
     /* Resolve the client info */
     SilcChannelClientResolve res = silc_calloc(1, sizeof(*res));
     res->payload = payload;
     res->channel_id = id;
-    silc_client_get_client_by_id_resolve(client, conn, client_id,
+    silc_client_get_client_by_id_resolve(client, conn, client_id, NULL,
                                         silc_client_channel_message_cb,
                                         res);
     payload = NULL;
@@ -296,50 +338,57 @@ void silc_client_channel_message(SilcClient client,
     goto out;
   }
 
-  if (!silc_client_on_channel(channel, client_entry)) {
-    SILC_LOG_WARNING(("Received channel message from client not on channel"));
-    goto out;
-  }
-
-  message = silc_channel_message_get_data(payload, NULL);
+  message = silc_message_get_data(payload, &message_len);
 
   /* Pass the message to application */
   client->internal->ops->channel_message(
-                                client, conn, client_entry, channel,
-                                silc_channel_message_get_flags(payload),
-                                message);
+                            client, conn, client_entry, channel, payload,
+                            silc_message_get_flags(payload),
+                            message, message_len);
 
  out:
   silc_free(id);
   silc_free(client_id);
   if (payload)
-    silc_channel_message_payload_free(payload);
+    silc_message_payload_free(payload);
 }
 
 /* Timeout callback that is called after a short period of time after the
-   new channel key has been created. This removes the old channel key all
-   together. */
+   new channel key has been created.  This removes the first channel key
+   in the list. */
 
 SILC_TASK_CALLBACK(silc_client_save_channel_key_rekey)
 {
   SilcChannelEntry channel = (SilcChannelEntry)context;
+  SilcCipher key;
+  SilcHmac hmac;
+
+  if (channel->old_channel_keys) {
+    silc_dlist_start(channel->old_channel_keys);
+    key = silc_dlist_get(channel->old_channel_keys);
+    if (key) {
+      silc_dlist_del(channel->old_channel_keys, key);
+      silc_cipher_free(key);
+    }
+  }
 
-  if (channel->old_channel_key)
-    silc_cipher_free(channel->old_channel_key);
-  if (channel->old_hmac)
-    silc_hmac_free(channel->old_hmac);
-  channel->old_channel_key = NULL;
-  channel->old_hmac = NULL;
-  channel->rekey_task = NULL;
+  if (channel->old_hmacs) {
+    silc_dlist_start(channel->old_hmacs);
+    hmac = silc_dlist_get(channel->old_hmacs);
+    if (hmac) {
+      silc_dlist_del(channel->old_hmacs, hmac);
+      silc_hmac_free(hmac);
+    }
+  }
 }
 
 /* Saves channel key from encoded `key_payload'. This is used when we
-   receive Channel Key Payload and when we are processing JOIN command 
+   receive Channel Key Payload and when we are processing JOIN command
    reply. */
 
 void silc_client_save_channel_key(SilcClient client,
                                  SilcClientConnection conn,
-                                 SilcBuffer key_payload, 
+                                 SilcBuffer key_payload,
                                  SilcChannelEntry channel)
 {
   unsigned char *id_string, *key, *cipher, *hmac, hash[32];
@@ -371,24 +420,21 @@ void silc_client_save_channel_key(SilcClient client,
       goto out;
   }
 
-  hmac = (channel->hmac ? (char *)silc_hmac_get_name(channel->hmac) : 
+  hmac = (channel->hmac ? (char *)silc_hmac_get_name(channel->hmac) :
          SILC_DEFAULT_HMAC);
 
   /* Save the old key for a short period of time so that we can decrypt
      channel message even after the rekey if some client would be sending
      messages with the old key after the rekey. */
-  if (channel->old_channel_key)
-    silc_cipher_free(channel->old_channel_key);
-  if (channel->old_hmac)
-    silc_hmac_free(channel->old_hmac);
-  if (channel->rekey_task)
-    silc_schedule_task_del(client->schedule, channel->rekey_task);
-  channel->old_channel_key = channel->channel_key;
-  channel->old_hmac = channel->hmac;
-  channel->rekey_task = 
-    silc_schedule_task_add(client->schedule, 0,
-                          silc_client_save_channel_key_rekey, channel,
-                          10, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+  if (!channel->old_channel_keys)
+    channel->old_channel_keys = silc_dlist_init();
+  if (!channel->old_hmacs)
+    channel->old_hmacs = silc_dlist_init();
+  silc_dlist_add(channel->old_channel_keys, channel->channel_key);
+  silc_dlist_add(channel->old_hmacs, channel->hmac);
+  silc_schedule_task_add(client->schedule, 0,
+                        silc_client_save_channel_key_rekey, channel,
+                        10, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 
   /* Free the old channel key data */
   silc_free(channel->key);
@@ -401,9 +447,9 @@ void silc_client_save_channel_key(SilcClient client,
 
   if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
     client->internal->ops->say(
-                          conn->client, conn, 
+                          conn->client, conn,
                           SILC_CLIENT_MESSAGE_AUDIT,
-                          "Cannot talk to channel: unsupported cipher %s", 
+                          "Cannot talk to channel: unsupported cipher %s",
                           cipher);
     goto out;
   }
@@ -414,7 +460,7 @@ void silc_client_save_channel_key(SilcClient client,
   /* Generate HMAC key from the channel key data and set it */
   silc_hmac_alloc(hmac, NULL, &channel->hmac);
   silc_hash_make(silc_hmac_get_hash(channel->hmac), key, tmp_len, hash);
-  silc_hmac_set_key(channel->hmac, hash, 
+  silc_hmac_set_key(channel->hmac, hash,
                    silc_hash_len(silc_hmac_get_hash(channel->hmac)));
   memset(hash, 0, sizeof(hash));
 
@@ -425,7 +471,7 @@ void silc_client_save_channel_key(SilcClient client,
 
 /* Processes received key for channel. The received key will be used
    to protect the traffic on the channel for now on. Client must receive
-   the key to the channel before talking on the channel is possible. 
+   the key to the channel before talking on the channel is possible.
    This is the key that server has generated, this is not the channel
    private key, it is entirely local setting. */
 
@@ -447,17 +493,17 @@ void silc_client_receive_channel_key(SilcClient client,
    several private keys per one channel. In this case only some of the
    clients on the channel may know the one key and only some the other key.
 
-   If `cipher' and/or `hmac' is NULL then default values will be used 
+   If `cipher' and/or `hmac' is NULL then default values will be used
    (aes-256-cbc for cipher and hmac-sha1-96 for hmac).
 
    The private key for channel is optional. If it is not set then the
    channel messages are encrypted using the channel key generated by the
-   server. However, setting the private key (or keys) for the channel 
+   server. However, setting the private key (or keys) for the channel
    significantly adds security. If more than one key is set the library
    will automatically try all keys at the message decryption phase. Note:
    setting many keys slows down the decryption phase as all keys has to
    be tried in order to find the correct decryption key. However, setting
-   a few keys does not have big impact to the decryption performace. 
+   a few keys does not have big impact to the decryption performace.
 
    NOTE: that this is entirely local setting. The key set using this function
    is not sent to the network at any phase.
@@ -468,9 +514,10 @@ void silc_client_receive_channel_key(SilcClient client,
    currently it is not expected that the SKE key material would be used
    as channel private key. However, this API allows it. */
 
-int silc_client_add_channel_private_key(SilcClient client,
+bool silc_client_add_channel_private_key(SilcClient client,
                                        SilcClientConnection conn,
                                        SilcChannelEntry channel,
+                                       const char *name,
                                        char *cipher,
                                        char *hmac,
                                        unsigned char *key,
@@ -480,6 +527,8 @@ int silc_client_add_channel_private_key(SilcClient client,
   unsigned char hash[32];
   SilcSKEKeyMaterial *keymat;
 
+  assert(client && channel);
+
   if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
     return FALSE;
 
@@ -496,8 +545,8 @@ int silc_client_add_channel_private_key(SilcClient client,
 
   /* Produce the key material */
   keymat = silc_calloc(1, sizeof(*keymat));
-  if (silc_ske_process_key_material_data(key, key_len, 16, 256, 16, 
-                                        client->internal->md5hash, keymat) 
+  if (silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
+                                        client->sha1hash, keymat)
       != SILC_SKE_STATUS_OK)
     return FALSE;
 
@@ -520,6 +569,7 @@ int silc_client_add_channel_private_key(SilcClient client,
 
   /* Save the key */
   entry = silc_calloc(1, sizeof(*entry));
+  entry->name = name ? strdup(name) : NULL;
   entry->key = silc_memdup(keymat->send_enc_key, keymat->enc_key_len / 8);
   entry->key_len = keymat->enc_key_len / 8;
 
@@ -529,9 +579,9 @@ int silc_client_add_channel_private_key(SilcClient client,
 
   /* Generate HMAC key from the channel key data and set it */
   silc_hmac_alloc(hmac, NULL, &entry->hmac);
-  silc_hash_make(silc_hmac_get_hash(entry->hmac), entry->key, 
+  silc_hash_make(silc_hmac_get_hash(entry->hmac), entry->key,
                 entry->key_len, hash);
-  silc_hmac_set_key(entry->hmac, hash, 
+  silc_hmac_set_key(entry->hmac, hash,
                    silc_hash_len(silc_hmac_get_hash(entry->hmac)));
   memset(hash, 0, sizeof(hash));
 
@@ -551,12 +601,14 @@ int silc_client_add_channel_private_key(SilcClient client,
    after calling this to protect the channel messages. Returns FALSE on
    on error, TRUE otherwise. */
 
-int silc_client_del_channel_private_keys(SilcClient client,
+bool silc_client_del_channel_private_keys(SilcClient client,
                                         SilcClientConnection conn,
                                         SilcChannelEntry channel)
 {
   SilcChannelPrivateKey entry;
 
+  assert(client && channel);
+
   if (!channel->private_keys)
     return FALSE;
 
@@ -565,6 +617,7 @@ int silc_client_del_channel_private_keys(SilcClient client,
     silc_dlist_del(channel->private_keys, entry);
     memset(entry->key, 0, entry->key_len);
     silc_free(entry->key);
+    silc_free(entry->name);
     silc_cipher_free(entry->cipher);
     silc_hmac_free(entry->hmac);
     silc_free(entry);
@@ -584,13 +637,15 @@ int silc_client_del_channel_private_keys(SilcClient client,
    old channel key is used hereafter to protect the channel messages. This
    returns FALSE on error, TRUE otherwise. */
 
-int silc_client_del_channel_private_key(SilcClient client,
+bool silc_client_del_channel_private_key(SilcClient client,
                                        SilcClientConnection conn,
                                        SilcChannelEntry channel,
                                        SilcChannelPrivateKey key)
 {
   SilcChannelPrivateKey entry;
 
+  assert(client && channel);
+
   if (!channel->private_keys)
     return FALSE;
 
@@ -603,6 +658,7 @@ int silc_client_del_channel_private_key(SilcClient client,
       silc_dlist_del(channel->private_keys, entry);
       memset(entry->key, 0, entry->key_len);
       silc_free(entry->key);
+      silc_free(entry->name);
       silc_cipher_free(entry->cipher);
       silc_hmac_free(entry->hmac);
       silc_free(entry);
@@ -634,6 +690,8 @@ silc_client_list_channel_private_keys(SilcClient client,
   SilcChannelPrivateKey *keys = NULL, entry;
   SilcUInt32 count = 0;
 
+  assert(client && channel);
+
   if (!channel->private_keys)
     return NULL;
 
@@ -658,7 +716,20 @@ void silc_client_free_channel_private_keys(SilcChannelPrivateKey *keys,
   silc_free(keys);
 }
 
-/* Returns the SilcChannelUser entry if the `client_entry' is joined on the 
+/* Sets the `key' to be used as current channel private key on the
+   `channel'.  Packet sent after calling this function will be secured
+   with `key'. */
+
+void silc_client_current_channel_private_key(SilcClient client,
+                                            SilcClientConnection conn,
+                                            SilcChannelEntry channel,
+                                            SilcChannelPrivateKey key)
+{
+  assert(client && channel);
+  channel->curr_key = key;
+}
+
+/* Returns the SilcChannelUser entry if the `client_entry' is joined on the
    channel indicated by the `channel'. NULL if client is not joined on
    the channel. */
 
@@ -667,7 +738,7 @@ SilcChannelUser silc_client_on_channel(SilcChannelEntry channel,
 {
   SilcChannelUser chu;
 
-  if (silc_hash_table_find(channel->user_list, client_entry, NULL, 
+  if (silc_hash_table_find(channel->user_list, client_entry, NULL,
                           (void *)&chu))
     return chu;