updates.
authorPekka Riikonen <priikone@silcnet.org>
Fri, 9 Mar 2001 19:21:04 +0000 (19:21 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Fri, 9 Mar 2001 19:21:04 +0000 (19:21 +0000)
16 files changed:
CHANGES
apps/silcd/command.c
apps/silcd/command_reply.c
apps/silcd/idlist.c
apps/silcd/idlist.h
apps/silcd/packet_receive.c
apps/silcd/packet_send.c
apps/silcd/packet_send.h
apps/silcd/server.c
doc/draft-riikonen-silc-pp-01.nroff
lib/silcclient/client.c
lib/silccore/silcchannel.c
lib/silccore/silcchannel.h
lib/silccore/silcpacket.c
lib/silccore/silcpacket.h
lib/silccrypt/silchmac.c

diff --git a/CHANGES b/CHANGES
index 7d100fb51aace009e0f2a5a4bf7ff4799acc6035..b6fb793cf9a0b7e1de6b774436ad9dc6a4a4064d 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,12 +1,42 @@
 Fri Mar  9 12:40:42 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-       * Minor fix t the channel payload; allocate the data area, as it
+       * Minor fix to the channel payload; allocate the data area, as it
          needs to be of specific length.
 
        * If the key agreement port is zero then the operating
          system will define the bound port.  Affected files are
          lib/silcclient/silcapi.h and lib/silcclient/client_keyagr.c.
 
+       * Added new function silc_channel_payload_decrypt into the file
+         lib/silccore/silcchannel.[ch].
+
+       * Moved the channel message etc, check from silc_packet_decrypt
+         to applications.  The library calls now a generic 
+         SilcPacketCheckDecrypt callback which is to return TRUE or FALSE
+         when the packet is either normal or special.  This was done to
+         allow more wide range of checking that was not allowed when
+         the code was in library.  Now applications can do virtually any
+         checks to the packet and return to the library the decision how
+         the packet should be processed.  Affected files are
+         lib/silccore/silcpacket.[ch].
+
+         Added silc_server_packet_decrypt_check to the server and
+         silc_client_packet_decrypt_check to the client library.
+
+       * Added silc_server_packet_send_srcdest into silcd/packet_send.[ch]
+         to send with specified source and destination information.
+
+       * Channel message delivery between routers was broken after the
+         channel key distribution was fixed earlier.  The channel key
+         was used be to distributed to other routers as well which is not
+         allowed by the protocol.  Now this is fixed and channel keys
+         really are cell specific and the channel message delivery between
+         routers comply with the protocol specification.
+
+       * Fixed various commands in server to check also the global list
+         for the channel entry and not just the local list.  The affected
+         file silcd/command.c.
+
 Thu Mar  8 21:39:03 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 
        * Added assert()s to buffer formatting and unformatting routines
@@ -33,7 +63,7 @@ Thu Mar  8 21:39:03 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
        * Added Hmac field to the Channel Message Payload.  The integrity
          of plaintext channel messages are now protected by computing
          MAC of the message and attaching the MAC to the payload.  The
-         MAC is not encrypted.  Now, it is clear that this causes some
+         MAC is encrypted.  Now, it is clear that this causes some
          overhead to the size of the packet but rationale for this is that
          now the receiver can verify whether the channel message decrypted
          correctly and also when private keys are set for the channel the
@@ -50,13 +80,6 @@ Thu Mar  8 21:39:03 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
          lib/silccore/silcchannel.[ch].  The encode function now also
          encrypts the packet and parse function decrypts it.
 
-       * Channel message delivery between routers was broken after the
-         channel key distribution was fixed earlier.  The channel key
-         was used be to distributed to other routers as well which is not
-         allowed by the protocol.  Now this is fixed and channel keys
-         really are cell specific and the channel message delivery between
-         routers comply with the protocol specification.
-
 Wed Mar  7 20:58:50 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
 
        * Fixed a minor formatting bug in the SKE's key material processing.
index 779c0385f6cee58c88eccd6ac2083a4defdbf02d..de42e83417f730508fa63d4a88e2d519368d8a2b 100644 (file)
@@ -1370,9 +1370,13 @@ SILC_SERVER_CMD_FUNC(topic)
   channel = silc_idlist_find_channel_by_id(server->local_list, 
                                           channel_id, NULL);
   if (!channel) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                         SILC_STATUS_ERR_NO_SUCH_CHANNEL);
-    goto out;
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      goto out;
+    }
   }
 
   if (argc > 1) {
@@ -1498,9 +1502,13 @@ SILC_SERVER_CMD_FUNC(invite)
   channel = silc_idlist_find_channel_by_id(server->local_list, 
                                           channel_id, NULL);
   if (!channel) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                         SILC_STATUS_ERR_NO_SUCH_CHANNEL);
-    goto out;
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      goto out;
+    }
   }
 
   /* Check whether the sender of this command is on the channel. */
@@ -2288,9 +2296,13 @@ SILC_SERVER_CMD_FUNC(cmode)
   channel = silc_idlist_find_channel_by_id(server->local_list, 
                                           channel_id, NULL);
   if (!channel) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                         SILC_STATUS_ERR_NO_SUCH_CHANNEL);
-    goto out;
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      goto out;
+    }
   }
 
   /* Check whether this client is on the channel */
@@ -2644,9 +2656,13 @@ SILC_SERVER_CMD_FUNC(cumode)
   channel = silc_idlist_find_channel_by_id(server->local_list, 
                                           channel_id, NULL);
   if (!channel) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NO_SUCH_CHANNEL);
-    goto out;
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      goto out;
+    }
   }
 
   /* Check whether sender is on the channel */
@@ -2835,9 +2851,13 @@ SILC_SERVER_CMD_FUNC(kick)
   channel = silc_idlist_find_channel_by_id(server->local_list, 
                                           channel_id, NULL);
   if (!channel) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                         SILC_STATUS_ERR_NO_SUCH_CHANNEL);
-    goto out;
+    channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      goto out;
+    }
   }
 
   /* Check whether sender is on the channel */
@@ -3142,9 +3162,12 @@ SILC_SERVER_CMD_FUNC(leave)
   /* Get channel entry */
   channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
   if (!channel) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
-                                         SILC_STATUS_ERR_NO_SUCH_CHANNEL);
-    goto out;
+    channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
+    if (!channel) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      goto out;
+    }
   }
 
   /* Check whether this client is on the channel */
index 0034de113b2c9ac442892a78fae7802cb9431e39..60e9f9f76c1b9fe0ca41843ec219f63c35172611 100644 (file)
@@ -376,9 +376,10 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
   SilcCommandStatus status;
   SilcChannelID *id;
   SilcChannelEntry entry;
+  SilcHmac hmac = NULL;
   unsigned int id_len, len;
   unsigned char *id_string;
-  char *channel_name, *tmp, *hmac;
+  char *channel_name, *tmp;
   unsigned int mode, created;
   SilcBuffer keyp;
 
@@ -421,7 +422,11 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
     goto out;
 
   /* Get hmac */
-  hmac = silc_argument_get_arg_type(cmd->args, 10, NULL);
+  tmp = silc_argument_get_arg_type(cmd->args, 10, NULL);
+  if (tmp) {
+    if (!silc_hmac_alloc(tmp, NULL, &hmac))
+      goto out;
+  }
 
   /* See whether we already have the channel. */
   entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
index b40ff6a6b1ed3826eaeefb27144a146d692f8951..5bb09466cde28b3c8c0624b484a15a94504ee804 100644 (file)
@@ -561,7 +561,7 @@ silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
 SilcChannelEntry
 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
                        SilcChannelID *id, SilcServerEntry router,
-                       SilcCipher channel_key, char *hmac)
+                       SilcCipher channel_key, SilcHmac hmac)
 {
   SilcChannelEntry channel;
 
@@ -571,7 +571,13 @@ silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
   channel->id = id;
   channel->router = router;
   channel->channel_key = channel_key;
-  channel->hmac = hmac ? strdup(hmac) : strdup("hmac-sha1-96");
+  channel->hmac = hmac;
+  if (!channel->hmac)
+    if (!silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac)) {
+      silc_free(channel);
+      return NULL;
+    }
+
   silc_list_init(channel->user_list, struct SilcChannelClientEntryStruct, 
                 channel_list);
 
index bbce9ea529ef391d9e34a8a9efcacf07fdccca34..d018bd10b6d29c5b52013b140f0614244a5cdc54 100644 (file)
@@ -361,10 +361,9 @@ struct SilcClientEntryStruct {
        Current initial vector. Initial vector is received always along
        with the channel packet. By default this is filled with NULL.
 
-   char *hmac;
+   SilcHmac hmac;
 
-       HMAC of the channel.  Server only saves the name of the HMAC as
-       it never actually needs to compute the MAC.
+       HMAC of the channel.
 
 */
 struct SilcChannelEntryStruct {
@@ -396,7 +395,7 @@ struct SilcChannelEntryStruct {
   unsigned char *key;
   unsigned int key_len;
   unsigned char iv[SILC_CIPHER_MAX_IV_SIZE];
-  char *hmac;
+  SilcHmac hmac;
 };
 
 /* 
@@ -506,7 +505,7 @@ silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
 SilcChannelEntry
 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
                        SilcChannelID *id, SilcServerEntry router,
-                       SilcCipher channel_key, char *hmac);
+                       SilcCipher channel_key, SilcHmac hmac);
 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry);
 SilcChannelEntry
 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
index 403c5928493d82f0e81757297f43537c7ba66af1..eb3f94556926063c5530ca9b6abbb6a6c0aeeb94 100644 (file)
@@ -920,6 +920,34 @@ void silc_server_channel_message(SilcServer server,
     }
   }
 
+  /* If we are router and the packet came from router and private key
+     has not been set for the channel then we must encrypt the packet
+     as it was decrypted with the session key shared between us and the
+     router which sent it. This is so, because cells does not share the
+     same channel key */
+  if (server->server_type == SILC_ROUTER &&
+      sock->type == SILC_SOCKET_TYPE_ROUTER &&
+      !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+    SilcBuffer chp;
+    unsigned int iv_len, i, data_len;
+
+    iv_len = silc_cipher_get_block_len(channel->channel_key);
+    if (channel->iv[0] == '\0')
+      for (i = 0; i < iv_len; i++) channel->iv[i] = 
+                                    silc_rng_get_byte(server->rng);
+    else
+      silc_hash_make(server->md5hash, channel->iv, iv_len, channel->iv);
+    
+    /* Encode new payload. This encrypts it also. */
+    SILC_GET16_MSB(data_len, packet->buffer->data);
+    chp = silc_channel_payload_encode(data_len, packet->buffer->data + 2,
+                                     iv_len, channel->iv,
+                                     channel->channel_key,
+                                     channel->hmac, server->rng);
+    silc_buffer_put(packet->buffer, chp->data, chp->len);
+    silc_buffer_free(chp);
+  }
+
   /* Distribute the packet to our local clients. This will send the
      packet for further routing as well, if needed. */
   silc_server_packet_relay_to_channel(server, sock, channel, sender,
index 282289870851f003af97613b35d449288e184c40..69bd04cd7bafad2e0470750f58f43b2be9f845d7 100644 (file)
@@ -179,6 +179,102 @@ void silc_server_packet_send_dest(SilcServer server,
     silc_free(packetdata.dst_id);
 }
 
+/* Assembles a new packet to be sent out to network. This doesn't actually
+   send the packet but creates the packet and fills the outgoing data
+   buffer and marks the packet ready to be sent to network. However, If 
+   argument force_send is TRUE the packet is sent immediately and not put 
+   to queue. Normal case is that the packet is not sent immediately. 
+   The source and destination information is sent as argument for this
+   function. */
+
+void silc_server_packet_send_srcdest(SilcServer server,
+                                    SilcSocketConnection sock, 
+                                    SilcPacketType type, 
+                                    SilcPacketFlags flags,
+                                    void *src_id,
+                                    SilcIdType src_id_type,
+                                    void *dst_id,
+                                    SilcIdType dst_id_type,
+                                    unsigned char *data, 
+                                    unsigned int data_len,
+                                    int force_send)
+{
+  SilcPacketContext packetdata;
+  SilcIDListData idata;
+  SilcCipher cipher = NULL;
+  SilcHmac hmac = NULL;
+  unsigned char *dst_id_data = NULL;
+  unsigned int dst_id_len = 0;
+  unsigned char *src_id_data = NULL;
+  unsigned int src_id_len = 0;
+
+  SILC_LOG_DEBUG(("Sending packet, type %d", type));
+
+  /* Get data used in the packet sending, keys and stuff */
+  idata = (SilcIDListData)sock->user_data;
+
+  if (dst_id) {
+    dst_id_data = silc_id_id2str(dst_id, dst_id_type);
+    dst_id_len = silc_id_get_len(dst_id_type);
+  }
+
+  if (src_id) {
+    src_id_data = silc_id_id2str(src_id, src_id_type);
+    src_id_len = silc_id_get_len(src_id_type);
+  }
+
+  /* Set the packet context pointers */
+  packetdata.type = type;
+  packetdata.flags = flags;
+  packetdata.src_id = src_id_data;
+  packetdata.src_id_len = src_id_len;
+  packetdata.src_id_type = src_id_type;
+  packetdata.dst_id = dst_id_data;
+  packetdata.dst_id_len = dst_id_len;
+  packetdata.dst_id_type = dst_id_type;
+  packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
+    packetdata.src_id_len + dst_id_len;
+  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
+
+  /* Prepare outgoing data buffer for packet sending */
+  silc_packet_send_prepare(sock, 
+                          SILC_PACKET_HEADER_LEN +
+                          packetdata.src_id_len + 
+                          packetdata.dst_id_len,
+                          packetdata.padlen,
+                          data_len);
+
+  SILC_LOG_DEBUG(("Putting data to outgoing buffer, len %d", data_len));
+
+  packetdata.buffer = sock->outbuf;
+
+  /* Put the data to the buffer */
+  if (data && data_len)
+    silc_buffer_put(sock->outbuf, data, data_len);
+
+  /* Create the outgoing packet */
+  silc_packet_assemble(&packetdata);
+
+  if (idata) {
+    cipher = idata->send_key;
+    hmac = idata->hmac;
+  }
+
+  /* Encrypt the packet */
+  silc_packet_encrypt(cipher, hmac, sock->outbuf, sock->outbuf->len);
+
+  SILC_LOG_HEXDUMP(("Outgoing packet, len %d", sock->outbuf->len),
+                  sock->outbuf->data, sock->outbuf->len);
+
+  /* Now actually send the packet */
+  silc_server_packet_send_real(server, sock, force_send);
+
+  if (packetdata.src_id)
+    silc_free(packetdata.src_id);
+  if (packetdata.dst_id)
+    silc_free(packetdata.dst_id);
+}
+
 /* Broadcast received packet to our primary route. This function is used
    by router to further route received broadcast packet. It is expected
    that the broadcast flag from the packet is checked before calling this
@@ -538,12 +634,6 @@ void silc_server_packet_relay_to_channel(SilcServer server,
                        silc_id_render(client->id, SILC_ID_CLIENT),
                        sock->hostname, sock->ip));
 
-       /* Send the packet */
-       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
-                                               idata->send_key, idata->hmac, 
-                                               data, data_len, TRUE, 
-                                               force_send);
-       
        /* We want to make sure that the packet is routed to same router
           only once. Mark this route as sent route. */
        k = routed_count;
@@ -551,6 +641,55 @@ void silc_server_packet_relay_to_channel(SilcServer server,
        routed[k] = client->router;
        routed_count++;
        
+       /* If the remote connection is router then we'll decrypt the
+          channel message and re-encrypt it with the session key shared
+          between us and the remote router. This is done because the
+          channel keys are cell specific and we have different channel
+          key than the remote router has. */
+       if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
+
+         /* If private key mode is not set then decrypt the packet
+            and re-encrypt it */
+         if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+           unsigned char *tmp = silc_calloc(data_len, sizeof(*data));
+           memcpy(tmp, data, data_len);
+
+           /* Decrypt the channel message (we don't check the MAC) */
+           if (!silc_channel_payload_decrypt(tmp, data_len, 
+                                             channel->channel_key,
+                                             NULL)) {
+             memset(tmp, 0, data_len);
+             silc_free(tmp);
+             continue;
+           }
+
+           /* Now re-encrypt and send it to the router */
+           silc_server_packet_send_srcdest(server, sock, 
+                                           SILC_PACKET_CHANNEL_MESSAGE, 0,
+                                           sender, sender_type,
+                                           channel->id, SILC_ID_CHANNEL,
+                                           tmp, data_len, force_send);
+           
+           /* Free the copy of the channel message */
+           memset(tmp, 0, data_len);
+           silc_free(tmp);
+         } else {
+           /* Private key mode is set, we don't have the channel key, so
+              just re-encrypt the entire packet and send it to the router. */
+           silc_server_packet_send_dest(server, sock, 
+                                        SILC_PACKET_CHANNEL_MESSAGE, 0,
+                                        channel->id, SILC_ID_CHANNEL,
+                                        data, data_len, force_send);
+         }
+         continue;
+       }
+  
+       /* Send the packet (to normal server) */
+       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
+                                               idata->send_key, idata->hmac, 
+                                               data, data_len, TRUE, 
+                                               force_send);
+
        continue;
       }
 
index 89daefe2d3a74126292d747fbbbed8057be53728..b402c6aad3472077cc315ca9a024a8f7de8c6ec6 100644 (file)
@@ -42,6 +42,17 @@ void silc_server_packet_send_dest(SilcServer server,
                                  unsigned char *data, 
                                  unsigned int data_len,
                                  int force_send);
+void silc_server_packet_send_srcdest(SilcServer server,
+                                    SilcSocketConnection sock, 
+                                    SilcPacketType type, 
+                                    SilcPacketFlags flags,
+                                    void *src_id,
+                                    SilcIdType src_id_type,
+                                    void *dst_id,
+                                    SilcIdType dst_id_type,
+                                    unsigned char *data, 
+                                    unsigned int data_len,
+                                    int force_send);
 void silc_server_packet_broadcast(SilcServer server,
                                  SilcSocketConnection sock,
                                  SilcPacketContext *packet);
index 765f6be552630cffa935a80a39e1c75e33439625..a7385384c09d9dffc6c3d7fb5be23b43d66d5a32 100644 (file)
@@ -1374,6 +1374,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 +1419,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;
 
@@ -2313,6 +2345,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 +2358,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 +2404,7 @@ silc_server_create_new_channel_with_id(SilcServer server,
 {
   SilcChannelEntry entry;
   SilcCipher key;
+  SilcHmac newhmac;
 
   SILC_LOG_DEBUG(("Creating new channel"));
 
@@ -2377,12 +2417,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,7 +2459,7 @@ 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;
 
   if (!channel->channel_key)
@@ -2431,8 +2477,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 +2490,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 +2509,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 +2571,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)
index a8d9194720751c97d39e538ef98a712c081e0d4e..0b1dcf77f1add69d195fa9b41c3169a418cece22 100644 (file)
@@ -2121,6 +2121,12 @@ their own channel keys thus the channel message traveling from one
 cell to another must be protected as it would be any normal SILC
 packet.
 
+If the SILC_CMODE_PRIVKEY channel mode has been set for the channel
+then the router cannot decrypt the packet as it does not know the
+private key.  In this case the entire packet is encrypted with the
+session key and sent to the router.  The router receiving the packet
+must check the channel mode and decrypt the packet accordingly.
+
 
 .ti 0
 2.5.3 Private Message Encryption And Decryption
index 5b68a7715232c3399075bd1d5c7f8fffff855352..33d51e100bf8d48c6bc274157af95c390298738a 100644 (file)
@@ -625,6 +625,31 @@ SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process)
   }
 }
 
+/* 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_client_packet_decrypt_check(SilcPacketType packet_type,
+                                           SilcBuffer buffer,
+                                           SilcPacketContext *packet,
+                                           void *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
+
+     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)
+    return TRUE;
+
+  return FALSE;
+}
+
 /* Parses whole packet, received earlier. */
 
 SILC_TASK_CALLBACK(silc_client_packet_parse_real)
@@ -640,7 +665,8 @@ SILC_TASK_CALLBACK(silc_client_packet_parse_real)
   SILC_LOG_DEBUG(("Start"));
 
   /* Decrypt the received packet */
-  ret = silc_packet_decrypt(conn->receive_key, conn->hmac, buffer, packet);
+  ret = silc_packet_decrypt(conn->receive_key, conn->hmac, buffer, packet,
+                           silc_client_packet_decrypt_check, parse_ctx);
   if (ret < 0)
     goto out;
 
index b3956cd291408c6193f459678d14673d57382c9b..433236eb4be27dacb695362e8938fb0da334039c 100644 (file)
@@ -38,48 +38,68 @@ struct SilcChannelPayloadStruct {
   unsigned char *iv;
 };
 
-/* Parses channel payload returning new channel payload structure. This
-   also decrypts it and checks the MAC. */
+/* Decrypts the channel message payload. */
 
-SilcChannelPayload silc_channel_payload_parse(SilcBuffer buffer,
-                                             SilcCipher cipher,
-                                             SilcHmac hmac)
+int silc_channel_payload_decrypt(unsigned char *data,
+                                size_t data_len,
+                                SilcCipher cipher,
+                                SilcHmac hmac)
 {
-  SilcChannelPayload new;
-  int ret;
   unsigned int iv_len, mac_len;
-  unsigned char *mac, mac2[32];
-
-  SILC_LOG_DEBUG(("Parsing channel payload"));
+  unsigned char *end, *mac, mac2[32];
 
   /* Decrypt the channel message. First push the IV out of the packet.
      The IV is used in the decryption process. Then decrypt the message.
      After decyprtion, take the MAC from the decrypted packet, compute MAC
      and compare the MACs.  If they match, the decryption was successfull
      and we have the channel message ready to be displayed. */
+  end = data + data_len;
 
-  /* Push the IV out of the packet (it will be in buffer->tail) */
+  /* Push the IV out of the packet */
   iv_len = silc_cipher_get_block_len(cipher);
-  silc_buffer_push_tail(buffer, iv_len);
 
   /* Decrypt the channel message */
-  silc_cipher_decrypt(cipher, buffer->data, buffer->data,
-                     buffer->len, buffer->tail);
+  silc_cipher_decrypt(cipher, data, data, data_len - iv_len, (end - iv_len));
 
   /* Take the MAC */
-  mac_len = silc_hmac_len(hmac);
-  silc_buffer_push_tail(buffer, mac_len);
-  mac = buffer->tail;
-
-  /* Check the MAC of the message */
-  SILC_LOG_DEBUG(("Checking channel message MACs"));
-  silc_hmac_make(hmac, buffer->data, buffer->len, mac2, &mac_len);
-  if (memcmp(mac, mac2, mac_len)) {
-    SILC_LOG_DEBUG(("Channel message MACs does not match"));
-    return NULL;
+  if (hmac) {
+    mac_len = silc_hmac_len(hmac);
+    mac = (end - iv_len - mac_len);
+
+    /* Check the MAC of the message */
+    SILC_LOG_DEBUG(("Checking channel message MACs"));
+    silc_hmac_make(hmac, data, (data_len - iv_len - mac_len), mac2, &mac_len);
+    if (memcmp(mac, mac2, mac_len)) {
+      SILC_LOG_DEBUG(("Channel message MACs does not match"));
+      return FALSE;
+    }
+    SILC_LOG_DEBUG(("MAC is Ok"));
   }
-  SILC_LOG_DEBUG(("MAC is Ok"));
-  silc_buffer_pull_tail(buffer, iv_len + mac_len);
+
+  return TRUE;
+}
+
+/* Parses channel payload returning new channel payload structure. This
+   also decrypts it and checks the MAC. */
+
+SilcChannelPayload silc_channel_payload_parse(SilcBuffer buffer,
+                                             SilcCipher cipher,
+                                             SilcHmac hmac)
+{
+  SilcChannelPayload new;
+  int ret;
+  unsigned int iv_len, mac_len;
+
+  SILC_LOG_DEBUG(("Parsing channel payload"));
+
+  /* Decrypt the payload */
+  ret = silc_channel_payload_decrypt(buffer->data, buffer->len,
+                                    cipher, hmac);
+  if (ret == FALSE)
+    return NULL;
+
+  iv_len = silc_cipher_get_block_len(cipher);
+  mac_len = silc_hmac_len(hmac);
 
   new = silc_calloc(1, sizeof(*new));
 
@@ -176,8 +196,10 @@ SilcBuffer silc_channel_payload_encode(unsigned short data_len,
 void silc_channel_payload_free(SilcChannelPayload payload)
 {
   if (payload) {
-    if (payload->data)
+    if (payload->data) {
+      memset(payload->data, 0, payload->data_len);
       silc_free(payload->data);
+    }
     silc_free(payload);
   }
 }
index 2ac4d7f68d5b5b8fa8c97942dc6388d0d908da8e..0c6a41e525e040b453b3a73e4443e1a94a4c079f 100644 (file)
@@ -30,6 +30,10 @@ typedef struct SilcChannelPayloadStruct *SilcChannelPayload;
 typedef struct SilcChannelKeyPayloadStruct *SilcChannelKeyPayload;
 
 /* Prototypes */
+int silc_channel_payload_decrypt(unsigned char *data,
+                                size_t data_len,
+                                SilcCipher cipher,
+                                SilcHmac hmac);
 SilcChannelPayload silc_channel_payload_parse(SilcBuffer buffer,
                                              SilcCipher cipher,
                                              SilcHmac hmac);
index f331cb09d75b7328db0614f0e6b9ac7bf68e88d7..3112f15830a6da0c35cd1ea141de8c8145140484 100644 (file)
@@ -572,24 +572,33 @@ static int silc_packet_decrypt_rest_special(SilcCipher cipher,
    the HMAC of the packet. If any other special or customized decryption
    processing is required this function cannot be used. This returns
    -1 on error, 0 when packet is normal packet and 1 when the packet
-   is special and requires special processing. */
+   is special and requires special processing. 
+
+   The `check_packet' is a callback funtion that this function will 
+   call.  The callback relates to the checking whether the packet is
+   normal packet or special packet and how it should be processed.  If
+   the callback return TRUE the packet is normal and FALSE if the packet
+   is special and requires special procesing. */
 
 int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac,
-                       SilcBuffer buffer, SilcPacketContext *packet)
+                       SilcBuffer buffer, SilcPacketContext *packet,
+                       SilcPacketCheckDecrypt check_packet,
+                       void *context)
 {
+  int check;
 
   /* Decrypt start of the packet header */
   if (cipher)
-    cipher->cipher->decrypt(cipher->context, buffer->data + 2,
-                           buffer->data + 2, SILC_PACKET_MIN_HEADER_LEN - 2,
-                           cipher->iv);
+    silc_cipher_decrypt(cipher, buffer->data + 2, buffer->data + 2, 
+                       SILC_PACKET_MIN_HEADER_LEN - 2, cipher->iv);
+
+  /* Do packet checking, whether the packet is normal or special */ 
+  check = check_packet((SilcPacketType)buffer->data[3], buffer,
+                      packet, context);
 
   /* If the packet type is not any special type lets decrypt rest
      of the packet here. */
-  if ((buffer->data[3] == SILC_PACKET_PRIVATE_MESSAGE &&
-      !(buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY)) ||
-      buffer->data[3] != SILC_PACKET_CHANNEL_MESSAGE) {
-
+  if (check == TRUE) {
     /* Normal packet, decrypt rest of the packet */
     if (!silc_packet_decrypt_rest(cipher, hmac, buffer))
       return -1;
index fd4302303f50f10eaaac75cf8db1e72a4cd0f9ba..e3f98a524901cad2cce5420c36e6ecf9e34198c0 100644 (file)
@@ -186,6 +186,11 @@ typedef struct {
 typedef void (*SilcPacketParserCallback)(SilcPacketParserContext 
                                         *parse_context);
 
+/* The packet check callback in decryption phase */
+typedef int (*SilcPacketCheckDecrypt)(SilcPacketType packet_type,
+                                     SilcBuffer buffer,
+                                     SilcPacketContext *packet,
+                                     void *context);
 
 /* SILC Packet types. */
 #define SILC_PACKET_NONE                0       /* NULL, never sent */
@@ -245,7 +250,9 @@ void silc_packet_send_prepare(SilcSocketConnection sock,
 int silc_packet_read(int sock, SilcBuffer dest);
 int silc_packet_receive(SilcSocketConnection sock);
 int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac,
-                       SilcBuffer buffer, SilcPacketContext *packet);
+                       SilcBuffer buffer, SilcPacketContext *packet,
+                       SilcPacketCheckDecrypt check_packet,
+                       void *context);
 void silc_packet_receive_process(SilcSocketConnection sock,
                                 SilcCipher cipher, SilcHmac hmac,
                                 SilcPacketParserCallback parser,
index 1edfb3813049610adf3f17f67cc704ea9a2b2b77..d3484983c7ce2f0d39fae90c744d79e5232714f7 100644 (file)
@@ -190,6 +190,10 @@ char *silc_hmac_get_supported()
 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
                       unsigned int key_len)
 {
+  if (hmac->key) {
+    memset(hmac->key, 0, hmac->key_len);
+    silc_free(hmac->key);
+  }
   hmac->key = silc_calloc(key_len, sizeof(unsigned char));
   hmac->key_len = key_len;
   memcpy(hmac->key, key, key_len);