X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilccore%2Fsilcchannel.c;h=ae6ac4be73493973807189e916b875809ddc6176;hb=a818c5b5411bbc4436d1c5f011236985c96bb787;hp=bb183cb0870499fccf7ce9661d1be44305821885;hpb=0f9738ce962b8498bbed0a75d5fb6fa127e3577f;p=silc.git diff --git a/lib/silccore/silcchannel.c b/lib/silccore/silcchannel.c index bb183cb0..ae6ac4be 100644 --- a/lib/silccore/silcchannel.c +++ b/lib/silccore/silcchannel.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2000 Pekka Riikonen + Copyright (C) 1997 - 2001 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 @@ -17,63 +17,56 @@ GNU General Public License for more details. */ -/* - * $Id$ - * $Log$ - * Revision 1.1.1.1 2000/06/27 11:36:55 priikone - * Importet from internal CVS/Added Log headers. - * - * - */ +/* Channel Payload, Channel Message Payload and Channel Key Payload + implementations. */ +/* $Id$ */ #include "silcincludes.h" #include "silcchannel.h" -/* Channel Payload structure. Contents of this structure is parsed +/****************************************************************************** + + Channel Payload + +******************************************************************************/ + +/* Channel Message Payload structure. Contents of this structure is parsed from SILC packets. */ struct SilcChannelPayloadStruct { - unsigned short nick_len; - unsigned char *nick; - unsigned short data_len; - unsigned char *data; - unsigned short iv_len; - unsigned char *iv; + SilcUInt16 name_len; + unsigned char *channel_name; + SilcUInt16 id_len; + unsigned char *channel_id; + SilcUInt32 mode; }; -/* Channel Key Payload structrue. Channel keys are parsed from SILC - packets into this structure. */ -struct SilcChannelKeyPayloadStruct { - unsigned short id_len; - unsigned char *id; - unsigned short cipher_len; - unsigned char *cipher; - unsigned short key_len; - unsigned char *key; -}; +/* Parses channel payload returning new channel payload structure. */ -/* Parses channel payload returning new channel payload structure */ - -SilcChannelPayload silc_channel_parse_payload(SilcBuffer buffer) +SilcChannelPayload silc_channel_payload_parse(const unsigned char *payload, + SilcUInt32 payload_len) { + SilcBufferStruct buffer; SilcChannelPayload new; + int ret; SILC_LOG_DEBUG(("Parsing channel payload")); + silc_buffer_set(&buffer, (unsigned char *)payload, payload_len); new = silc_calloc(1, sizeof(*new)); - if (!new) { - SILC_LOG_ERROR(("Could not allocate new channel payload")); - return NULL; - } - /* Parse the Channel Payload. Ignore padding and IV, we don't need - them. */ - silc_buffer_unformat(buffer, - SILC_STR_UI16_NSTRING_ALLOC(&new->nick, &new->nick_len), - SILC_STR_UI16_NSTRING_ALLOC(&new->data, &new->data_len), - SILC_STR_UI16_NSTRING_ALLOC(NULL, NULL), - SILC_STR_END); + /* 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_END); + if (ret == -1) + goto err; - if (new->data_len < 1) { + if ((new->name_len < 1 || new->name_len > buffer.len) || + (new->id_len < 1 || new->id_len > buffer.len)) { SILC_LOG_ERROR(("Incorrect channel payload in packet, packet dropped")); goto err; } @@ -81,96 +74,369 @@ SilcChannelPayload silc_channel_parse_payload(SilcBuffer buffer) return new; err: - if (new->nick) - silc_free(new->nick); - if (new->data) - silc_free(new->data); - if (new->iv) - silc_free(new->iv); - silc_free(new); + silc_channel_payload_free(new); return NULL; } -/* Encodes channel payload into a buffer and returns it. This is used - to add channel payload into a packet. As the channel payload is +/* Parses list of channel payloads returning list of payloads. */ + +SilcDList silc_channel_payload_parse_list(const unsigned char *payload, + SilcUInt32 payload_len) +{ + SilcBufferStruct buffer; + SilcDList list; + SilcChannelPayload new; + int len, ret; + + SILC_LOG_DEBUG(("Parsing channel payload list")); + + silc_buffer_set(&buffer, (unsigned char *)payload, payload_len); + list = silc_dlist_init(); + + while (buffer.len) { + new = silc_calloc(1, sizeof(*new)); + 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_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)) { + SILC_LOG_ERROR(("Incorrect channel payload in packet, packet dropped")); + goto err; + } + + len = 2 + new->name_len + 2 + new->id_len + 4; + if (buffer.len < len) + break; + silc_buffer_pull(&buffer, len); + + silc_dlist_add(list, new); + } + + return list; + + err: + silc_channel_payload_list_free(list); + return NULL; +} + +/* Encode new channel payload and returns it as buffer. */ + +SilcBuffer silc_channel_payload_encode(const unsigned char *channel_name, + SilcUInt16 channel_name_len, + const unsigned char *channel_id, + 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)); + + /* Encode the Channel Payload */ + silc_buffer_format(buffer, + SILC_STR_UI_SHORT(channel_name_len), + SILC_STR_UI_XNSTRING(channel_name, channel_name_len), + SILC_STR_UI_SHORT(channel_id_len), + SILC_STR_UI_XNSTRING(channel_id, channel_id_len), + SILC_STR_UI_INT(mode), + SILC_STR_END); + + return buffer; +} + +/* Frees Channel Payload */ + +void silc_channel_payload_free(SilcChannelPayload payload) +{ + silc_free(payload->channel_name); + silc_free(payload->channel_id); + silc_free(payload); +} + +/* Free's list of Channel Payloads */ + +void silc_channel_payload_list_free(SilcDList list) +{ + SilcChannelPayload entry; + + silc_dlist_start(list); + while ((entry = silc_dlist_get(list)) != SILC_LIST_END) { + silc_free(entry->channel_name); + silc_free(entry->channel_id); + silc_dlist_del(list, entry); + silc_free(entry); + } + + silc_dlist_uninit(list); +} + +/* Return the channel name */ + +unsigned char *silc_channel_get_name(SilcChannelPayload payload, + SilcUInt32 *channel_name_len) +{ + if (channel_name_len) + *channel_name_len = payload->name_len; + + return payload->channel_name; +} + +/* Return the channel ID */ + +unsigned char *silc_channel_get_id(SilcChannelPayload payload, + SilcUInt32 *channel_id_len) +{ + if (channel_id_len) + *channel_id_len = payload->id_len; + + return payload->channel_id; +} + +/* Return the channel ID as parsed ID. */ + +SilcChannelID *silc_channel_get_id_parse(SilcChannelPayload payload) +{ + return silc_id_str2id(payload->channel_id, payload->id_len, + SILC_ID_CHANNEL); +} + +/* Return the mode. The mode is arbitrary. It can be the mode of the + channel or perhaps the mode of the client on the channel. The protocol + dictates what the usage of the mode is in different circumstances. */ + +SilcUInt32 silc_channel_get_mode(SilcChannelPayload payload) +{ + return payload->mode; +} + +/****************************************************************************** + + Channel Message 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; + SilcUInt16 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, + compute MAC and compare the MACs. If they match, the decryption was + successful and we have the channel message ready to be displayed. */ + +bool silc_channel_message_payload_decrypt(unsigned char *data, + size_t data_len, + SilcCipher cipher, + SilcHmac hmac) +{ + SilcUInt32 iv_len, mac_len; + unsigned char *end, *mac, mac2[32]; + unsigned char *dst, iv[SILC_CIPHER_MAX_IV_SIZE]; + + /* Push the IV out of the packet, and copy the IV since we do not want + to modify the original data buffer. */ + end = data + data_len; + iv_len = silc_cipher_get_block_len(cipher); + memcpy(iv, end - iv_len, iv_len); + + /* 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) + dst = silc_calloc(data_len - iv_len, sizeof(*dst)); + else + dst = data; + + /* Decrypt the channel message */ + silc_cipher_decrypt(cipher, data, dst, data_len - iv_len, iv); + + if (hmac) { + /* Take the MAC */ + end = dst + (data_len - iv_len); + mac_len = silc_hmac_len(hmac); + mac = (end - mac_len); + + /* 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); + if (memcmp(mac, mac2, mac_len)) { + SILC_LOG_DEBUG(("Channel message MACs does not match")); + silc_free(dst); + return FALSE; + } + SILC_LOG_DEBUG(("MAC is Ok")); + + /* Now copy the decrypted data into the buffer since it is verified + it decrypted correctly. */ + memcpy(data, dst, data_len - iv_len); + memset(dst, 0, data_len - iv_len); + silc_free(dst); + } + + return TRUE; +} + +/* Parses channel message payload returning new channel payload structure. + This also decrypts it and checks the MAC. */ + +SilcChannelMessagePayload +silc_channel_message_payload_parse(unsigned char *payload, + SilcUInt32 payload_len, + SilcCipher cipher, + SilcHmac hmac) +{ + SilcBufferStruct buffer; + SilcChannelMessagePayload new; + int ret; + SilcUInt32 iv_len, mac_len; + + SILC_LOG_DEBUG(("Parsing channel message payload")); + + silc_buffer_set(&buffer, payload, payload_len); + + /* Decrypt the payload */ + ret = silc_channel_message_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)); + + /* Parse the Channel Message Payload. Ignore the padding. */ + 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_END); + if (ret == -1) + goto err; + + if (new->data_len > buffer.len) { + SILC_LOG_ERROR(("Incorrect channel message payload in packet, " + "packet dropped")); + goto err; + } + + return new; + + err: + silc_channel_message_payload_free(new); + return NULL; +} + +/* 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_encode_payload(unsigned short nick_len, - unsigned char *nick, - unsigned short data_len, - unsigned char *data, - unsigned short iv_len, - unsigned char *iv, - SilcRng rng) +SilcBuffer silc_channel_message_payload_encode(SilcUInt16 flags, + SilcUInt16 data_len, + const unsigned char *data, + SilcUInt16 iv_len, + unsigned char *iv, + SilcCipher cipher, + SilcHmac hmac) { int i; SilcBuffer buffer; - unsigned int len, pad_len; - unsigned char pad[SILC_PACKET_MAX_PADLEN]; + SilcUInt32 len, pad_len, mac_len; + unsigned char pad[16]; + unsigned char mac[32]; - SILC_LOG_DEBUG(("Encoding channel payload")); + SILC_LOG_DEBUG(("Encoding channel message payload")); /* Calculate length of padding. IV is not included into the calculation since it is not encrypted. */ - len = 2 + nick_len + 2 + data_len + 2; - pad_len = SILC_PACKET_PADLEN((len + 2)); + mac_len = silc_hmac_len(hmac); + len = 6 + data_len + mac_len; + pad_len = SILC_CHANNEL_MESSAGE_PAD(len); /* Allocate channel payload buffer */ - len += pad_len; - buffer = silc_buffer_alloc(len + iv_len); - if (!buffer) - return NULL; + len += pad_len + iv_len; + buffer = silc_buffer_alloc(len); /* Generate padding */ - for (i = 0; i < pad_len; i++) - pad[i] = silc_rng_get_byte(rng); + for (i = 0; i < pad_len; i++) pad[i] = silc_rng_global_get_byte(); - silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer)); - - /* Encode the Channel Payload */ + /* Encode the Channel Message Payload */ + silc_buffer_pull_tail(buffer, 6 + data_len + pad_len); silc_buffer_format(buffer, - SILC_STR_UI_SHORT(nick_len), - SILC_STR_UI_XNSTRING(nick, nick_len), + SILC_STR_UI_SHORT(flags), SILC_STR_UI_SHORT(data_len), SILC_STR_UI_XNSTRING(data, data_len), SILC_STR_UI_SHORT(pad_len), 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); + + /* 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); + + memset(pad, 0, sizeof(pad)); + memset(mac, 0, sizeof(mac)); - memset(pad, 0, pad_len); return buffer; } -/* Free's Channel Payload */ +/* Free's Channel Message Payload */ -void silc_channel_free_payload(SilcChannelPayload payload) +void silc_channel_message_payload_free(SilcChannelMessagePayload payload) { - if (payload) { - if (payload->data) - silc_free(payload->data); - if (payload->iv) - silc_free(payload->iv); - silc_free(payload); + if (payload->data) { + memset(payload->data, 0, payload->data_len); + silc_free(payload->data); } + silc_free(payload); } -/* Return nickname */ +/* Return flags */ -unsigned char *silc_channel_get_nickname(SilcChannelPayload payload, - unsigned int *nick_len) +SilcMessageFlags +silc_channel_message_get_flags(SilcChannelMessagePayload payload) { - if (nick_len) - *nick_len = payload->nick_len; - - return payload->nick; + return payload->flags; } /* Return data */ -unsigned char *silc_channel_get_data(SilcChannelPayload payload, - unsigned int *data_len) +unsigned char *silc_channel_message_get_data(SilcChannelMessagePayload payload, + SilcUInt32 *data_len) { if (data_len) *data_len = payload->data_len; @@ -178,38 +444,62 @@ unsigned char *silc_channel_get_data(SilcChannelPayload payload, return payload->data; } -/* Return initial vector */ +/* Return MAC. The caller knows the length of the MAC */ -unsigned char *silc_channel_get_iv(SilcChannelPayload payload, - unsigned int *iv_len) +unsigned char *silc_channel_message_get_mac(SilcChannelMessagePayload payload) { - if (iv_len) - *iv_len = payload->iv_len; + return payload->mac; +} + +/* Return IV. The caller knows the length of the IV */ +unsigned char *silc_channel_message_get_iv(SilcChannelMessagePayload payload) +{ return payload->iv; } +/****************************************************************************** + + Channel Key Payload + +******************************************************************************/ + +/* Channel Key Payload structrue. Channel keys are parsed from SILC + packets into this structure. */ +struct SilcChannelKeyPayloadStruct { + SilcUInt16 id_len; + unsigned char *id; + SilcUInt16 cipher_len; + unsigned char *cipher; + SilcUInt16 key_len; + unsigned char *key; +}; + /* Parses channel key payload returning new channel key payload structure */ -SilcChannelKeyPayload silc_channel_key_parse_payload(SilcBuffer buffer) +SilcChannelKeyPayload +silc_channel_key_payload_parse(const unsigned char *payload, + SilcUInt32 payload_len) { + SilcBufferStruct buffer; SilcChannelKeyPayload new; + int ret; SILC_LOG_DEBUG(("Parsing channel key payload")); + silc_buffer_set(&buffer, (unsigned char *)payload, payload_len); new = silc_calloc(1, sizeof(*new)); - if (!new) { - SILC_LOG_ERROR(("Could not allocate new channel key payload")); - return NULL; - } /* Parse the Channel Key Payload */ - 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_END); + 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_END); + if (ret == -1) + goto err; if (new->id_len < 1 || new->key_len < 1 || new->cipher_len < 1) { SILC_LOG_ERROR(("Incorrect channel key payload in packet")); @@ -232,28 +522,22 @@ SilcChannelKeyPayload silc_channel_key_parse_payload(SilcBuffer buffer) /* 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_encode_payload(unsigned short id_len, - unsigned char *id, - unsigned short cipher_len, - unsigned char *cipher, - unsigned short key_len, - unsigned char *key) +SilcBuffer silc_channel_key_payload_encode(SilcUInt16 id_len, + const unsigned char *id, + SilcUInt16 cipher_len, + const unsigned char *cipher, + SilcUInt16 key_len, + const unsigned char *key) { SilcBuffer buffer; - unsigned int len; + SilcUInt32 len; SILC_LOG_DEBUG(("Encoding channel key payload")); - /* Sanity checks */ - if (!id_len || !key_len || !id || !key || !cipher_len || !cipher) - return NULL; - /* 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); - if (!buffer) - return NULL; silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer)); @@ -270,15 +554,13 @@ SilcBuffer silc_channel_key_encode_payload(unsigned short id_len, return buffer; } -/* Free's Channel Key Payload */ +/* Frees Channel Key Payload */ -void silc_channel_key_free_payload(SilcChannelKeyPayload payload) +void silc_channel_key_payload_free(SilcChannelKeyPayload payload) { if (payload) { - if (payload->id) - silc_free(payload->id); - if (payload->cipher) - silc_free(payload->cipher); + silc_free(payload->id); + silc_free(payload->cipher); if (payload->key) { memset(payload->key, 0, payload->key_len); silc_free(payload->key); @@ -290,7 +572,7 @@ void silc_channel_key_free_payload(SilcChannelKeyPayload payload) /* Return ID */ unsigned char *silc_channel_key_get_id(SilcChannelKeyPayload payload, - unsigned int *id_len) + SilcUInt32 *id_len) { if (id_len) *id_len = payload->id_len; @@ -301,7 +583,7 @@ unsigned char *silc_channel_key_get_id(SilcChannelKeyPayload payload, /* Return cipher name */ unsigned char *silc_channel_key_get_cipher(SilcChannelKeyPayload payload, - unsigned int *cipher_len) + SilcUInt32 *cipher_len) { if (cipher_len) *cipher_len = payload->cipher_len; @@ -312,7 +594,7 @@ unsigned char *silc_channel_key_get_cipher(SilcChannelKeyPayload payload, /* Return key */ unsigned char *silc_channel_key_get_key(SilcChannelKeyPayload payload, - unsigned int *key_len) + SilcUInt32 *key_len) { if (key_len) *key_len = payload->key_len;