Protocol version 1.2 integrations
[silc.git] / lib / silccore / silcchannel.c
index fef7a197cd482787af40d732e44fa6e53cc1c35d..adc857ee88c34449c406e164e43503811ebbbc8b 100644 (file)
 
 #include "silcincludes.h"
 #include "silcchannel.h"
-
-/******************************************************************************
-
-                              Channel Payload
-
-******************************************************************************/
-
-/* Channel Message Payload structure. Contents of this structure is parsed
-   from SILC packets. */
-struct SilcChannelPayloadStruct {
-  uint16 name_len;
-  unsigned char *channel_name;
-  uint16 id_len;
-  unsigned char *channel_id;
-  uint32 mode;
-};
+#include "silcchannel_i.h"
 
 /* Parses channel payload returning new channel payload structure. */
 
 SilcChannelPayload silc_channel_payload_parse(const unsigned char *payload,
-                                             uint32 payload_len)
+                                             SilcUInt32 payload_len)
 {
   SilcBufferStruct buffer;
-  SilcChannelPayload new;
+  SilcChannelPayload newp;
   int ret;
 
   SILC_LOG_DEBUG(("Parsing channel payload"));
 
   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
-  new = silc_calloc(1, sizeof(*new));
+  newp = silc_calloc(1, sizeof(*newp));
+  if (!newp)
+    return NULL;
 
   /* Parse the Channel Payload. Ignore the padding. */
   ret = silc_buffer_unformat(&buffer,
-                            SILC_STR_UI16_NSTRING_ALLOC(&new->channel_name, 
-                                                        &new->name_len),
-                            SILC_STR_UI16_NSTRING_ALLOC(&new->channel_id, 
-                                                        &new->id_len),
-                            SILC_STR_UI_INT(&new->mode),
+                            SILC_STR_UI16_NSTRING_ALLOC(&newp->channel_name, 
+                                                        &newp->name_len),
+                            SILC_STR_UI16_NSTRING_ALLOC(&newp->channel_id, 
+                                                        &newp->id_len),
+                            SILC_STR_UI_INT(&newp->mode),
                             SILC_STR_END);
   if (ret == -1)
     goto err;
 
-  if ((new->name_len < 1 || new->name_len > buffer.len) ||
-      (new->id_len < 1 || new->id_len > buffer.len)) {
+  if ((newp->name_len < 1 || newp->name_len > buffer.len - 8) ||
+      (newp->id_len < 1 || newp->id_len > buffer.len - 8) ||
+      (newp->id_len + newp->name_len > buffer.len - 8)) {
     SILC_LOG_ERROR(("Incorrect channel payload in packet, packet dropped"));
     goto err;
   }
 
-  return new;
+  return newp;
 
  err:
-  silc_channel_payload_free(new);
+  silc_channel_payload_free(newp);
   return NULL;
 }
 
 /* Parses list of channel payloads returning list of payloads. */
 
 SilcDList silc_channel_payload_parse_list(const unsigned char *payload,
-                                         uint32 payload_len)
+                                         SilcUInt32 payload_len)
 {
   SilcBufferStruct buffer;
   SilcDList list;
-  SilcChannelPayload new;
+  SilcChannelPayload newp;
   int len, ret;
 
   SILC_LOG_DEBUG(("Parsing channel payload list"));
@@ -94,29 +82,31 @@ SilcDList silc_channel_payload_parse_list(const unsigned char *payload,
   list = silc_dlist_init();
 
   while (buffer.len) {
-    new = silc_calloc(1, sizeof(*new));
+    newp = silc_calloc(1, sizeof(*newp));
+    if (!newp)
+      goto err;
     ret = silc_buffer_unformat(&buffer,
-                              SILC_STR_UI16_NSTRING_ALLOC(&new->channel_name, 
-                                                          &new->name_len),
-                              SILC_STR_UI16_NSTRING_ALLOC(&new->channel_id, 
-                                                          &new->id_len),
-                              SILC_STR_UI_INT(&new->mode),
+                              SILC_STR_UI16_NSTRING_ALLOC(&newp->channel_name, 
+                                                          &newp->name_len),
+                              SILC_STR_UI16_NSTRING_ALLOC(&newp->channel_id, 
+                                                          &newp->id_len),
+                              SILC_STR_UI_INT(&newp->mode),
                               SILC_STR_END);
     if (ret == -1)
       goto err;
 
-    if ((new->name_len < 1 || new->name_len > buffer.len) ||
-       (new->id_len < 1 || new->id_len > buffer.len)) {
+    if ((newp->name_len < 1 || newp->name_len > buffer.len) ||
+       (newp->id_len < 1 || newp->id_len > buffer.len)) {
       SILC_LOG_ERROR(("Incorrect channel payload in packet, packet dropped"));
       goto err;
     }
 
-    len = 2 + new->name_len + 2 + new->id_len + 4;
+    len = 2 + newp->name_len + 2 + newp->id_len + 4;
     if (buffer.len < len)
       break;
     silc_buffer_pull(&buffer, len);
 
-    silc_dlist_add(list, new);
+    silc_dlist_add(list, newp);
   }
   
   return list;
@@ -129,17 +119,19 @@ SilcDList silc_channel_payload_parse_list(const unsigned char *payload,
 /* Encode new channel payload and returns it as buffer. */
 
 SilcBuffer silc_channel_payload_encode(const unsigned char *channel_name,
-                                      uint16 channel_name_len,
+                                      SilcUInt16 channel_name_len,
                                       const unsigned char *channel_id,
-                                      uint32 channel_id_len,
-                                      uint32 mode)
+                                      SilcUInt32 channel_id_len,
+                                      SilcUInt32 mode)
 {
   SilcBuffer buffer;
 
   SILC_LOG_DEBUG(("Encoding message payload"));
 
-  buffer = silc_buffer_alloc(2 + channel_name_len + 2 + channel_id_len + 4);
-  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+  buffer = silc_buffer_alloc_size(2 + channel_name_len + 2 + 
+                                 channel_id_len + 4);
+  if (!buffer)
+    return NULL;
 
   /* Encode the Channel Payload */
   silc_buffer_format(buffer, 
@@ -172,8 +164,8 @@ void silc_channel_payload_list_free(SilcDList list)
   while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
     silc_free(entry->channel_name);
     silc_free(entry->channel_id);
-    silc_free(entry);
     silc_dlist_del(list, entry);
+    silc_free(entry);
   }
 
   silc_dlist_uninit(list);
@@ -182,7 +174,7 @@ void silc_channel_payload_list_free(SilcDList list)
 /* Return the channel name */
 
 unsigned char *silc_channel_get_name(SilcChannelPayload payload,
-                                    uint32 *channel_name_len)
+                                    SilcUInt32 *channel_name_len)
 {
   if (channel_name_len)
     *channel_name_len = payload->name_len;
@@ -193,7 +185,7 @@ unsigned char *silc_channel_get_name(SilcChannelPayload payload,
 /* Return the channel ID */
 
 unsigned char *silc_channel_get_id(SilcChannelPayload payload,
-                                  uint32 *channel_id_len)
+                                  SilcUInt32 *channel_id_len)
 {
   if (channel_id_len)
     *channel_id_len = payload->id_len;
@@ -213,7 +205,7 @@ SilcChannelID *silc_channel_get_id_parse(SilcChannelPayload payload)
    channel or perhaps the mode of the client on the channel.  The protocol
    dictates what the usage of the mode is in different circumstances. */
 
-uint32 silc_channel_get_mode(SilcChannelPayload payload)
+SilcUInt32 silc_channel_get_mode(SilcChannelPayload payload)
 {
   return payload->mode;
 }
@@ -224,18 +216,6 @@ uint32 silc_channel_get_mode(SilcChannelPayload payload)
 
 ******************************************************************************/
 
-#define SILC_CHANNEL_MESSAGE_PAD(__payloadlen) (16 - (__payloadlen) % 16)
-
-/* Channel Message Payload structure. Contents of this structure is parsed
-   from SILC packets. */
-struct SilcChannelMessagePayloadStruct {
-  SilcMessageFlags flags;
-  uint16 data_len;
-  unsigned char *data;
-  unsigned char *mac;
-  unsigned char *iv;
-};
-
 /* Decrypts the channel message payload. 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, 
@@ -247,7 +227,7 @@ bool silc_channel_message_payload_decrypt(unsigned char *data,
                                          SilcCipher cipher,
                                          SilcHmac hmac)
 {
-  uint32 iv_len, mac_len;
+  SilcUInt32 iv_len, mac_len;
   unsigned char *end, *mac, mac2[32];
   unsigned char *dst, iv[SILC_CIPHER_MAX_IV_SIZE];
 
@@ -260,10 +240,13 @@ bool silc_channel_message_payload_decrypt(unsigned char *data,
   /* Allocate destination decryption buffer since we do not want to modify
      the original data buffer, since we might want to call this function 
      many times for same payload. */
-  if (hmac)
+  if (hmac) {
     dst = silc_calloc(data_len - iv_len, sizeof(*dst));
-  else
+    if (!dst)
+      return FALSE;
+  } else {
     dst = data;
+  }
 
   /* Decrypt the channel message */
   silc_cipher_decrypt(cipher, data, dst, data_len - iv_len, iv);
@@ -276,7 +259,10 @@ bool silc_channel_message_payload_decrypt(unsigned char *data,
 
     /* Check the MAC of the message */
     SILC_LOG_DEBUG(("Checking channel message MACs"));
-    silc_hmac_make(hmac, dst, (data_len - iv_len - mac_len), mac2, &mac_len);
+    silc_hmac_init(hmac);
+    silc_hmac_update(hmac, dst, (data_len - iv_len - mac_len));
+    silc_hmac_update(hmac, data + (data_len - iv_len), iv_len);
+    silc_hmac_final(hmac, mac2, &mac_len);
     if (memcmp(mac, mac2, mac_len)) {
       SILC_LOG_DEBUG(("Channel message MACs does not match"));
       silc_free(dst);
@@ -299,14 +285,14 @@ bool silc_channel_message_payload_decrypt(unsigned char *data,
 
 SilcChannelMessagePayload 
 silc_channel_message_payload_parse(unsigned char *payload,
-                                  uint32 payload_len,
+                                  SilcUInt32 payload_len,
                                   SilcCipher cipher,
                                   SilcHmac hmac)
 {
   SilcBufferStruct buffer;
-  SilcChannelMessagePayload new;
+  SilcChannelMessagePayload newp;
   int ret;
-  uint32 iv_len, mac_len;
+  SilcUInt32 iv_len, mac_len;
 
   SILC_LOG_DEBUG(("Parsing channel message payload"));
 
@@ -321,66 +307,120 @@ silc_channel_message_payload_parse(unsigned char *payload,
   iv_len = silc_cipher_get_block_len(cipher);
   mac_len = silc_hmac_len(hmac);
 
-  new = silc_calloc(1, sizeof(*new));
+  newp = silc_calloc(1, sizeof(*newp));
+  if (!newp)
+    return NULL;
 
-  /* Parse the Channel Message Payload. Ignore the padding. */
+  /* Parse the Channel Message Payload. */
   ret = silc_buffer_unformat(&buffer,
-                            SILC_STR_UI_SHORT(&new->flags),
-                            SILC_STR_UI16_NSTRING_ALLOC(&new->data, 
-                                                        &new->data_len),
-                            SILC_STR_UI16_NSTRING(NULL, NULL),
-                            SILC_STR_UI_XNSTRING(&new->mac, mac_len),
-                            SILC_STR_UI_XNSTRING(&new->iv, iv_len),
+                            SILC_STR_UI_SHORT(&newp->flags),
+                            SILC_STR_UI16_NSTRING_ALLOC(&newp->data, 
+                                                        &newp->data_len),
+                            SILC_STR_UI16_NSTRING_ALLOC(&newp->pad, 
+                                                        &newp->pad_len),
+                            SILC_STR_UI_XNSTRING(&newp->mac, mac_len),
+                            SILC_STR_UI_XNSTRING(&newp->iv, iv_len),
                             SILC_STR_END);
   if (ret == -1)
     goto err;
 
-  if (new->data_len > buffer.len) {
+  if ((newp->data_len > buffer.len - 6 - mac_len - iv_len) ||
+      (newp->pad_len + newp->data_len > buffer.len - 6 - mac_len - iv_len)) {
     SILC_LOG_ERROR(("Incorrect channel message payload in packet, "
                    "packet dropped"));
     goto err;
   }
 
-  return new;
+  newp->iv_len = iv_len;
+
+  return newp;
 
  err:
-  silc_channel_message_payload_free(new);
+  silc_channel_message_payload_free(newp);
   return NULL;
 }
 
+/* This function is used to encrypt the Channel Messsage Payload which is
+   the `data' and `data_len'.  This is used internally by the Channel Message
+   Payload encoding routines but application may call this too if needed. 
+   The `data_len' is the data lenght which is used to create MAC out of.
+   The `true_len' is the true length of `data' message payload and is used
+   assemble rest of the packet after MAC creation. The `true_len' length
+   packet will then be encrypted. */
+
+bool silc_channel_message_payload_encrypt(unsigned char *data,
+                                         SilcUInt32 data_len,
+                                         SilcUInt32 true_len,
+                                         unsigned char *iv,
+                                         SilcUInt32 iv_len,
+                                         SilcCipher cipher,
+                                         SilcHmac hmac)
+{
+  unsigned char mac[32];
+  SilcUInt32 mac_len;
+  SilcBufferStruct buf;
+
+  /* Compute the MAC of the channel message data */
+  silc_hmac_init(hmac);
+  silc_hmac_update(hmac, data, data_len);
+  silc_hmac_update(hmac, iv, iv_len);
+  silc_hmac_final(hmac, mac, &mac_len);
+
+  /* Put rest of the data to the payload */
+  silc_buffer_set(&buf, data, true_len);
+  silc_buffer_pull(&buf, data_len);
+  silc_buffer_format(&buf, 
+                    SILC_STR_UI_XNSTRING(mac, mac_len),
+                    SILC_STR_UI_XNSTRING(iv, iv_len),
+                    SILC_STR_END);
+
+  /* Encrypt payload of the packet. This is encrypted with the channel key. */
+  silc_cipher_encrypt(cipher, data, data, true_len - iv_len, iv);
+
+  memset(mac, 0, sizeof(mac));
+  return TRUE;
+}
+
 /* Encodes channel message payload into a buffer and returns it. This is used 
    to add channel message payload into a packet. As the channel payload is
    encrypted separately from other parts of the packet padding must
    be applied to the payload. */
 
-SilcBuffer silc_channel_message_payload_encode(uint16 flags,
-                                              uint16 data_len,
+SilcBuffer silc_channel_message_payload_encode(SilcMessageFlags flags,
+                                              SilcUInt16 data_len,
                                               const unsigned char *data,
-                                              uint16 iv_len,
+                                              SilcUInt16 iv_len,
                                               unsigned char *iv,
                                               SilcCipher cipher,
-                                              SilcHmac hmac)
+                                              SilcHmac hmac,
+                                              SilcRng rng)
 {
   int i;
   SilcBuffer buffer;
-  uint32 len, pad_len, mac_len;
+  SilcUInt32 len, pad_len, mac_len;
   unsigned char pad[16];
-  unsigned char mac[32];
 
   SILC_LOG_DEBUG(("Encoding channel message payload"));
 
   /* Calculate length of padding. IV is not included into the calculation
      since it is not encrypted. */
   mac_len = silc_hmac_len(hmac);
+  data_len = SILC_CHANNEL_MESSAGE_DATALEN(data_len, mac_len + iv_len);
   len = 6 + data_len + mac_len;
   pad_len = SILC_CHANNEL_MESSAGE_PAD(len);
 
   /* Allocate channel payload buffer */
   len += pad_len + iv_len;
   buffer = silc_buffer_alloc(len);
+  if (!buffer)
+    return NULL;
 
   /* Generate padding */
-  for (i = 0; i < pad_len; i++) pad[i] = silc_rng_global_get_byte();
+  if (rng) {
+    for (i = 0; i < pad_len; i++) pad[i] = silc_rng_get_byte_fast(rng);
+  } else {
+    for (i = 0; i < pad_len; i++) pad[i] = silc_rng_global_get_byte_fast();
+  }
 
   /* Encode the Channel Message Payload */
   silc_buffer_pull_tail(buffer, 6 + data_len + pad_len);
@@ -392,24 +432,16 @@ SilcBuffer silc_channel_message_payload_encode(uint16 flags,
                     SILC_STR_UI_XNSTRING(pad, pad_len),
                     SILC_STR_END);
 
-  /* Compute the MAC of the channel message data */
-  silc_hmac_make(hmac, buffer->data, buffer->len, mac, &mac_len);
-
-  /* Put rest of the data to the payload */
-  silc_buffer_pull_tail(buffer, mac_len + iv_len);
-  silc_buffer_pull(buffer, 6 + data_len + pad_len);
-  silc_buffer_format(buffer, 
-                    SILC_STR_UI_XNSTRING(mac, mac_len),
-                    SILC_STR_UI_XNSTRING(iv, iv_len),
-                    SILC_STR_END);
-  silc_buffer_push(buffer, 6 + data_len + pad_len);
+  memset(pad, 0, sizeof(pad));
 
-  /* Encrypt payload of the packet. This is encrypted with the channel key. */
-  silc_cipher_encrypt(cipher, buffer->data, buffer->data, 
-                     buffer->len - iv_len, iv);
+  if (!silc_channel_message_payload_encrypt(buffer->data, buffer->len,
+                                           buffer->truelen, iv, iv_len,
+                                           cipher, hmac)) {
+    silc_buffer_free(buffer);
+    return NULL;
+  }
 
-  memset(pad, 0, sizeof(pad));
-  memset(mac, 0, sizeof(mac));
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer) - buffer->len);
 
   return buffer;
 }
@@ -436,7 +468,7 @@ silc_channel_message_get_flags(SilcChannelMessagePayload payload)
 /* Return data */
 
 unsigned char *silc_channel_message_get_data(SilcChannelMessagePayload payload,
-                                            uint32 *data_len)
+                                            SilcUInt32 *data_len)
 {
   if (data_len)
     *data_len = payload->data_len;
@@ -464,82 +496,75 @@ unsigned char *silc_channel_message_get_iv(SilcChannelMessagePayload payload)
 
 ******************************************************************************/
 
-/* Channel Key Payload structrue. Channel keys are parsed from SILC
-   packets into this structure. */
-struct SilcChannelKeyPayloadStruct {
-  uint16 id_len;
-  unsigned char *id;
-  uint16 cipher_len;
-  unsigned char *cipher;
-  uint16 key_len;
-  unsigned char *key;
-};
-
 /* Parses channel key payload returning new channel key payload structure */
 
 SilcChannelKeyPayload 
 silc_channel_key_payload_parse(const unsigned char *payload,
-                              uint32 payload_len)
+                              SilcUInt32 payload_len)
 {
   SilcBufferStruct buffer;
-  SilcChannelKeyPayload new;
+  SilcChannelKeyPayload newp;
   int ret;
 
   SILC_LOG_DEBUG(("Parsing channel key payload"));
 
   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
-  new = silc_calloc(1, sizeof(*new));
+  newp = silc_calloc(1, sizeof(*newp));
+  if (!newp)
+    return NULL;
 
   /* Parse the Channel Key Payload */
   ret =
     silc_buffer_unformat(&buffer,
-                        SILC_STR_UI16_NSTRING_ALLOC(&new->id, &new->id_len),
-                        SILC_STR_UI16_NSTRING_ALLOC(&new->cipher, 
-                                                    &new->cipher_len),
-                        SILC_STR_UI16_NSTRING_ALLOC(&new->key, &new->key_len),
+                        SILC_STR_UI16_NSTRING_ALLOC(&newp->id, &newp->id_len),
+                        SILC_STR_UI16_NSTRING_ALLOC(&newp->cipher, 
+                                                    &newp->cipher_len),
+                        SILC_STR_UI16_NSTRING_ALLOC(&newp->key, 
+                                                    &newp->key_len),
                         SILC_STR_END);
   if (ret == -1)
     goto err;
 
-  if (new->id_len < 1 || new->key_len < 1 || new->cipher_len < 1) {
+  if (newp->id_len < 1 || newp->key_len < 1 || newp->cipher_len < 1 ||
+      newp->id_len + newp->cipher_len + newp->key_len > buffer.len - 6) {
     SILC_LOG_ERROR(("Incorrect channel key payload in packet"));
     goto err;
   }
 
-  return new;
+  return newp;
 
  err:
-  if (new->id)
-    silc_free(new->id);
-  if (new->cipher)
-    silc_free(new->cipher);
-  if (new->key)
-    silc_free(new->key);
-  silc_free(new);
+  if (newp->id)
+    silc_free(newp->id);
+  if (newp->cipher)
+    silc_free(newp->cipher);
+  if (newp->key)
+    silc_free(newp->key);
+  silc_free(newp);
   return NULL;
 }
 
 /* Encodes channel key payload into a buffer and returns it. This is used 
    to add channel key payload into a packet. */
 
-SilcBuffer silc_channel_key_payload_encode(uint16 id_len,
+SilcBuffer silc_channel_key_payload_encode(SilcUInt16 id_len,
                                           const unsigned char *id,
-                                          uint16 cipher_len,
+                                          SilcUInt16 cipher_len,
                                           const unsigned char *cipher,
-                                          uint16 key_len,
+                                          SilcUInt16 key_len,
                                           const unsigned char *key)
 {
   SilcBuffer buffer;
-  uint32 len;
+  SilcUInt32 len;
 
   SILC_LOG_DEBUG(("Encoding channel key payload"));
 
   /* Allocate channel payload buffer. Length is 2 + id + 2 + key + 
      2 + cipher */
   len = 2 + id_len + 2 + key_len + 2 + cipher_len;
-  buffer = silc_buffer_alloc(len);
-
-  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+  buffer = silc_buffer_alloc_size(len);
+  if (!buffer)
+    return NULL;
 
   /* Encode the Channel Payload */
   silc_buffer_format(buffer, 
@@ -572,7 +597,7 @@ void silc_channel_key_payload_free(SilcChannelKeyPayload payload)
 /* Return ID */
 
 unsigned char *silc_channel_key_get_id(SilcChannelKeyPayload payload, 
-                                      uint32 *id_len)
+                                      SilcUInt32 *id_len)
 {
   if (id_len)
     *id_len = payload->id_len;
@@ -583,7 +608,7 @@ unsigned char *silc_channel_key_get_id(SilcChannelKeyPayload payload,
 /* Return cipher name */
 
 unsigned char *silc_channel_key_get_cipher(SilcChannelKeyPayload payload,
-                                          uint32 *cipher_len)
+                                          SilcUInt32 *cipher_len)
 {
   if (cipher_len)
     *cipher_len = payload->cipher_len;
@@ -594,7 +619,7 @@ unsigned char *silc_channel_key_get_cipher(SilcChannelKeyPayload payload,
 /* Return key */
 
 unsigned char *silc_channel_key_get_key(SilcChannelKeyPayload payload,
-                                       uint32 *key_len)
+                                       SilcUInt32 *key_len)
 {
   if (key_len)
     *key_len = payload->key_len;