X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=lib%2Fsilcclient%2Fclient_channel.c;h=88ccd4534aea5e2a6d50512ba67669c9672eb4b9;hp=26ee63f9d1955c9ccdfd43cc174629e8eb0c795e;hb=e5d8d3db6caa344b3d419b884556c21b15e7d123;hpb=9bc7aa726cf320ec4fad29466f884a0db5c89557 diff --git a/lib/silcclient/client_channel.c b/lib/silcclient/client_channel.c index 26ee63f9..88ccd453 100644 --- a/lib/silcclient/client_channel.c +++ b/lib/silcclient/client_channel.c @@ -38,7 +38,7 @@ void silc_client_send_channel_message(SilcClient client, SilcChannelPrivateKey key, SilcMessageFlags flags, unsigned char *data, - unsigned int data_len, + uint32 data_len, int force_send) { int i; @@ -48,17 +48,10 @@ void silc_client_send_channel_message(SilcClient client, SilcCipher cipher; SilcHmac hmac; unsigned char *id_string; - unsigned int iv_len; + uint32 iv_len; SILC_LOG_DEBUG(("Sending packet to channel")); - if (!channel || !channel->hmac || - (!channel->channel_key && !key && !channel->private_keys)) { - client->ops->say(client, conn, - "Cannot talk to channel: key does not exist"); - return; - } - /* Take the key to be used */ if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) { if (key) { @@ -90,6 +83,9 @@ void silc_client_send_channel_message(SilcClient client, hmac = channel->hmac; } + if (!cipher || !hmac) + return; + /* Generate IV */ iv_len = silc_cipher_get_block_len(cipher); if (channel->iv[0] == '\0') @@ -104,7 +100,7 @@ void silc_client_send_channel_message(SilcClient client, /* Get data used in packet header encryption, keys and stuff. */ cipher = conn->send_key; - hmac = conn->hmac; + hmac = conn->hmac_send; id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL); /* Set the packet context pointers. The destination ID is always @@ -113,10 +109,10 @@ void silc_client_send_channel_message(SilcClient client, packetdata.flags = 0; packetdata.type = SILC_PACKET_CHANNEL_MESSAGE; packetdata.src_id = conn->local_id_data; - packetdata.src_id_len = SILC_ID_CLIENT_LEN; + packetdata.src_id_len = silc_id_get_len(conn->local_id, SILC_ID_CLIENT); packetdata.src_id_type = SILC_ID_CLIENT; packetdata.dst_id = id_string; - packetdata.dst_id_len = SILC_ID_CHANNEL_LEN; + packetdata.dst_id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL); packetdata.dst_id_type = SILC_ID_CHANNEL; packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN + packetdata.src_id_len + packetdata.dst_id_len; @@ -155,6 +151,19 @@ void silc_client_send_channel_message(SilcClient client, silc_free(id_string); } +static void silc_client_channel_message_cb(SilcClient client, + SilcClientConnection conn, + SilcClientEntry *clients, + uint32 clients_count, + void *context) +{ + SilcPacketContext *packet = (SilcPacketContext *)context; + + if (clients) + silc_client_channel_message(client, conn->sock, packet); + silc_packet_context_free(packet); +} + /* 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. */ @@ -171,7 +180,7 @@ void silc_client_channel_message(SilcClient client, SilcChannelUser chu; SilcIDCacheEntry id_cache = NULL; SilcClientID *client_id = NULL; - int found = FALSE; + bool found = FALSE; unsigned char *message; SILC_LOG_DEBUG(("Start")); @@ -189,8 +198,7 @@ void silc_client_channel_message(SilcClient client, goto out; /* Find the channel entry from channels on this connection */ - if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id, - SILC_ID_CHANNEL, &id_cache)) + if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id, &id_cache)) goto out; channel = (SilcChannelEntry)id_cache->context; @@ -202,8 +210,20 @@ void silc_client_channel_message(SilcClient client, /* Parse the channel message payload. This also decrypts the payload */ payload = silc_channel_message_payload_parse(buffer, channel->channel_key, channel->hmac); - if (!payload) - goto out; + + /* 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; + + payload = silc_channel_message_payload_parse(buffer, + channel->old_channel_key, + channel->old_hmac); + if (!payload) + goto out; + } } else if (channel->private_keys) { SilcChannelPrivateKey entry; @@ -221,20 +241,27 @@ void silc_client_channel_message(SilcClient client, goto out; } - message = silc_channel_message_get_data(payload, NULL); - /* Find client entry */ silc_list_start(channel->clients); while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) { - if (!SILC_ID_CLIENT_COMPARE(chu->client->id, client_id)) { + if (SILC_ID_CLIENT_COMPARE(chu->client->id, client_id)) { found = TRUE; break; } } + if (!found) { + /* Resolve the client info */ + silc_client_get_client_by_id_resolve(client, conn, client_id, + silc_client_channel_message_cb, + silc_packet_context_dup(packet)); + goto out; + } + + message = silc_channel_message_get_data(payload, NULL); + /* Pass the message to application */ - client->ops->channel_message(client, conn, found ? chu->client : NULL, - channel, + client->ops->channel_message(client, conn, chu->client, channel, silc_channel_message_get_flags(payload), message); @@ -247,6 +274,23 @@ void silc_client_channel_message(SilcClient client, silc_channel_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. */ + +SILC_TASK_CALLBACK(silc_client_save_channel_key_rekey) +{ + SilcChannelEntry channel = (SilcChannelEntry)context; + + 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; +} + /* Saves channel key from encoded `key_payload'. This is used when we receive Channel Key Payload and when we are processing JOIN command reply. */ @@ -255,8 +299,8 @@ void silc_client_save_channel_key(SilcClientConnection conn, SilcBuffer key_payload, SilcChannelEntry channel) { - unsigned char *id_string, *key, *cipher, hash[32]; - unsigned int tmp_len; + unsigned char *id_string, *key, *cipher, *hmac, hash[32]; + uint32 tmp_len; SilcChannelID *id; SilcIDCacheEntry id_cache = NULL; SilcChannelKeyPayload payload; @@ -279,14 +323,35 @@ void silc_client_save_channel_key(SilcClientConnection conn, /* Find channel. */ if (!channel) { - if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id, - SILC_ID_CHANNEL, &id_cache)) + if (!silc_idcache_find_by_id_one(conn->channel_cache, + (void *)id, &id_cache)) goto out; /* Get channel entry */ channel = (SilcChannelEntry)id_cache->context; } + hmac = channel->hmac ? channel->hmac->hmac->name : 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(conn->client->schedule, channel->rekey_task); + channel->old_channel_key = channel->channel_key; + channel->old_hmac = channel->hmac; + channel->rekey_task = + silc_schedule_task_add(conn->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); + /* Save the key */ key = silc_channel_key_get_key(payload, &tmp_len); cipher = silc_channel_key_get_cipher(payload, NULL); @@ -295,8 +360,9 @@ void silc_client_save_channel_key(SilcClientConnection conn, memcpy(channel->key, key, tmp_len); if (!silc_cipher_alloc(cipher, &channel->channel_key)) { - conn->client->ops->say(conn->client, conn, - "Cannot talk to channel: unsupported cipher %s", cipher); + conn->client->ops->say(conn->client, conn, SILC_CLIENT_MESSAGE_AUDIT, + "Cannot talk to channel: unsupported cipher %s", + cipher); goto out; } @@ -304,15 +370,11 @@ void silc_client_save_channel_key(SilcClientConnection conn, silc_cipher_set_key(channel->channel_key, key, 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_hmac_alloc(hmac, NULL, &channel->hmac); silc_hash_make(channel->hmac->hash, key, tmp_len, hash); silc_hmac_set_key(channel->hmac, hash, silc_hash_len(channel->hmac->hash)); memset(hash, 0, sizeof(hash)); - /* Client is now joined to the channel */ - channel->on_channel = TRUE; - out: silc_free(id); silc_channel_key_payload_free(payload); @@ -342,7 +404,7 @@ 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 @@ -369,18 +431,19 @@ int silc_client_add_channel_private_key(SilcClient client, char *cipher, char *hmac, unsigned char *key, - unsigned int key_len) + uint32 key_len) { SilcChannelPrivateKey entry; unsigned char hash[32]; + SilcSKEKeyMaterial *keymat; if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) return FALSE; if (!cipher) - cipher = "aes-256-cbc"; + cipher = SILC_DEFAULT_CIPHER; if (!hmac) - hmac = "hmac-sha1-96"; + hmac = SILC_DEFAULT_HMAC; if (!silc_cipher_is_supported(cipher)) return FALSE; @@ -388,6 +451,13 @@ int silc_client_add_channel_private_key(SilcClient client, if (!silc_hmac_is_supported(hmac)) return FALSE; + /* Produce the key material */ + keymat = silc_calloc(1, sizeof(*keymat)); + if (silc_ske_process_key_material_data(key, key_len, 16, 256, 16, + client->md5hash, keymat) + != SILC_SKE_STATUS_OK) + return FALSE; + /* Remove the current key, if it exists. */ if (channel->channel_key) { silc_cipher_free(channel->channel_key); @@ -397,25 +467,27 @@ int silc_client_add_channel_private_key(SilcClient client, channel->key = NULL; channel->key_len = 0; } - if (channel->hmac) + if (channel->hmac) { silc_hmac_free(channel->hmac); + channel->hmac = NULL; + } if (!channel->private_keys) channel->private_keys = silc_dlist_init(); /* Save the key */ entry = silc_calloc(1, sizeof(*entry)); - entry->key = silc_calloc(key_len, sizeof(*entry->key)); - memcpy(entry->key, key, key_len); - entry->key_len = key_len; + entry->key = silc_calloc(keymat->enc_key_len / 8, sizeof(*entry->key)); + memcpy(entry->key, keymat->send_enc_key, keymat->enc_key_len / 8); + entry->key_len = keymat->enc_key_len / 8; /* Allocate the cipher and set the key*/ silc_cipher_alloc(cipher, &entry->cipher); - silc_cipher_set_key(entry->cipher, key, key_len * 8); + silc_cipher_set_key(entry->cipher, entry->key, keymat->enc_key_len); /* Generate HMAC key from the channel key data and set it */ silc_hmac_alloc(hmac, NULL, &entry->hmac); - silc_hash_make(entry->hmac->hash, key, key_len, hash); + silc_hash_make(entry->hmac->hash, entry->key, entry->key_len, hash); silc_hmac_set_key(entry->hmac, hash, silc_hash_len(entry->hmac->hash)); memset(hash, 0, sizeof(hash)); @@ -425,6 +497,9 @@ int silc_client_add_channel_private_key(SilcClient client, if (!channel->curr_key) channel->curr_key = entry; + /* Free the key material */ + silc_ske_free_key_material(keymat); + return TRUE; } @@ -510,10 +585,10 @@ SilcChannelPrivateKey * silc_client_list_channel_private_keys(SilcClient client, SilcClientConnection conn, SilcChannelEntry channel, - unsigned int *key_count) + uint32 *key_count) { SilcChannelPrivateKey *keys = NULL, entry; - unsigned int count = 0; + uint32 count = 0; if (!channel->private_keys) return NULL; @@ -534,7 +609,7 @@ silc_client_list_channel_private_keys(SilcClient client, /* Frees the SilcChannelPrivateKey array. */ void silc_client_free_channel_private_keys(SilcChannelPrivateKey *keys, - unsigned int key_count) + uint32 key_count) { silc_free(keys); }