X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilcclient%2Fclient_channel.c;h=0f952373a55b747708aa10c6ea73b551526e5577;hb=6394d86063413bc1c473723f3ef971840195bcd3;hp=62396c5f0dda0a4e98acb598ddb8526ebbf2049d;hpb=4559604b1e55af1f277b2420be645583450bb6e0;p=silc.git diff --git a/lib/silcclient/client_channel.c b/lib/silcclient/client_channel.c index 62396c5f..0f952373 100644 --- a/lib/silcclient/client_channel.c +++ b/lib/silcclient/client_channel.c @@ -49,6 +49,7 @@ void silc_client_send_channel_message(SilcClient client, SilcHmac hmac; unsigned char *id_string; uint32 iv_len; + int block_len; SILC_LOG_DEBUG(("Sending packet to channel")); @@ -86,13 +87,16 @@ void silc_client_send_channel_message(SilcClient client, if (!cipher || !hmac) return; + block_len = silc_cipher_get_block_len(cipher); + /* Generate IV */ iv_len = silc_cipher_get_block_len(cipher); if (channel->iv[0] == '\0') for (i = 0; i < iv_len; i++) channel->iv[i] = silc_rng_get_byte(client->rng); else - silc_hash_make(client->md5hash, channel->iv, iv_len, channel->iv); + silc_hash_make(client->internal->md5hash, channel->iv, iv_len, + channel->iv); /* Encode the channel payload. This also encrypts the message payload. */ payload = silc_channel_message_payload_encode(flags, data_len, data, iv_len, @@ -109,16 +113,16 @@ 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; packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN + packetdata.src_id_len + - packetdata.dst_id_len)); + packetdata.dst_id_len), block_len); /* Prepare outgoing data buffer for packet sending */ silc_packet_send_prepare(sock, @@ -134,11 +138,12 @@ void silc_client_send_channel_message(SilcClient client, silc_buffer_put(sock->outbuf, payload->data, payload->len); /* Create the outgoing packet */ - silc_packet_assemble(&packetdata); + silc_packet_assemble(&packetdata, cipher); /* Encrypt the header and padding of the packet. This is encrypted with normal session key shared with our server. */ - silc_packet_encrypt(cipher, hmac, sock->outbuf, SILC_PACKET_HEADER_LEN + + silc_packet_encrypt(cipher, hmac, conn->psn_send++, + sock->outbuf, SILC_PACKET_HEADER_LEN + packetdata.src_id_len + packetdata.dst_id_len + packetdata.padlen); @@ -146,11 +151,49 @@ void silc_client_send_channel_message(SilcClient client, sock->outbuf->data, sock->outbuf->len); /* Now actually send the packet */ - silc_client_packet_send_real(client, sock, force_send, FALSE); + silc_client_packet_send_real(client, sock, force_send); silc_buffer_free(payload); silc_free(id_string); } +typedef struct { + SilcChannelMessagePayload payload; + SilcChannelID *channel_id; +} *SilcChannelClientResolve; + +static void silc_client_channel_message_cb(SilcClient client, + SilcClientConnection conn, + SilcClientEntry *clients, + uint32 clients_count, + void *context) +{ + SilcChannelClientResolve res = (SilcChannelClientResolve)context; + + if (clients_count == 1) { + SilcIDCacheEntry id_cache = NULL; + SilcChannelEntry channel; + unsigned char *message; + + if (!silc_idcache_find_by_id_one(conn->channel_cache, res->channel_id, + &id_cache)) + goto out; + + channel = (SilcChannelEntry)id_cache->context; + message = silc_channel_message_get_data(res->payload, NULL); + + /* Pass the message to application */ + client->internal->ops->channel_message( + client, conn, clients[0], channel, + silc_channel_message_get_flags(res->payload), + message); + } + + out: + silc_channel_message_payload_free(res->payload); + silc_free(res->channel_id); + silc_free(res); +} + /* Process received message to a channel (or from a channel, really). This decrypts the channel message with channel specific key and parses the channel payload. Finally it displays the message on the screen. */ @@ -167,7 +210,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")); @@ -185,8 +228,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; @@ -196,17 +238,31 @@ void silc_client_channel_message(SilcClient client, all private keys and check what decrypts correctly. */ if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) { /* Parse the channel message payload. This also decrypts the payload */ - payload = silc_channel_message_payload_parse(buffer, channel->channel_key, + payload = silc_channel_message_payload_parse(buffer->data, buffer->len, + 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->data, buffer->len, + channel->old_channel_key, + channel->old_hmac); + if (!payload) + goto out; + } } else if (channel->private_keys) { SilcChannelPrivateKey entry; silc_dlist_start(channel->private_keys); while ((entry = silc_dlist_get(channel->private_keys)) != SILC_LIST_END) { /* Parse the channel message payload. This also decrypts the payload */ - payload = silc_channel_message_payload_parse(buffer, entry->cipher, + payload = silc_channel_message_payload_parse(buffer->data, buffer->len, + entry->cipher, entry->hmac); if (payload) break; @@ -217,22 +273,36 @@ 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) && + chu->client->nickname) { found = TRUE; break; } } + if (!found) { + /* Resolve the client info */ + SilcChannelClientResolve res = silc_calloc(1, sizeof(*res)); + res->payload = payload; + res->channel_id = id; + silc_client_get_client_by_id_resolve(client, conn, client_id, + silc_client_channel_message_cb, + res); + payload = NULL; + id = NULL; + 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, - silc_channel_message_get_flags(payload), - message); + client->internal->ops->channel_message( + client, conn, chu->client, channel, + silc_channel_message_get_flags(payload), + message); out: if (id) @@ -243,6 +313,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. */ @@ -251,13 +338,14 @@ void silc_client_save_channel_key(SilcClientConnection conn, SilcBuffer key_payload, SilcChannelEntry channel) { - unsigned char *id_string, *key, *cipher, hash[32]; + unsigned char *id_string, *key, *cipher, *hmac, hash[32]; uint32 tmp_len; SilcChannelID *id; SilcIDCacheEntry id_cache = NULL; SilcChannelKeyPayload payload; - payload = silc_channel_key_payload_parse(key_payload); + payload = silc_channel_key_payload_parse(key_payload->data, + key_payload->len); if (!payload) return; @@ -275,14 +363,36 @@ 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 ? (char *)silc_hmac_get_name(channel->hmac) : + SILC_DEFAULT_HMAC); + + /* Save the old key for a short period of time so that we can decrypt + channel message even after the rekey if some client would be sending + messages with the old key after the rekey. */ + if (channel->old_channel_key) + silc_cipher_free(channel->old_channel_key); + if (channel->old_hmac) + silc_hmac_free(channel->old_hmac); + if (channel->rekey_task) + silc_schedule_task_del(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); @@ -291,8 +401,11 @@ 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->internal->ops->say( + conn->client, conn, + SILC_CLIENT_MESSAGE_AUDIT, + "Cannot talk to channel: unsupported cipher %s", + cipher); goto out; } @@ -300,10 +413,10 @@ 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_hash_make(channel->hmac->hash, key, tmp_len, hash); - silc_hmac_set_key(channel->hmac, hash, silc_hash_len(channel->hmac->hash)); + silc_hmac_alloc(hmac, NULL, &channel->hmac); + silc_hash_make(silc_hmac_get_hash(channel->hmac), key, tmp_len, hash); + silc_hmac_set_key(channel->hmac, hash, + silc_hash_len(silc_hmac_get_hash(channel->hmac))); memset(hash, 0, sizeof(hash)); out: @@ -372,9 +485,9 @@ int silc_client_add_channel_private_key(SilcClient client, 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; @@ -385,7 +498,7 @@ int silc_client_add_channel_private_key(SilcClient client, /* Produce the key material */ keymat = silc_calloc(1, sizeof(*keymat)); if (silc_ske_process_key_material_data(key, key_len, 16, 256, 16, - client->md5hash, keymat) + client->internal->md5hash, keymat) != SILC_SKE_STATUS_OK) return FALSE; @@ -418,8 +531,10 @@ int silc_client_add_channel_private_key(SilcClient client, /* Generate HMAC key from the channel key data and set it */ silc_hmac_alloc(hmac, NULL, &entry->hmac); - silc_hash_make(entry->hmac->hash, entry->key, entry->key_len, hash); - silc_hmac_set_key(entry->hmac, hash, silc_hash_len(entry->hmac->hash)); + silc_hash_make(silc_hmac_get_hash(entry->hmac), entry->key, + entry->key_len, hash); + silc_hmac_set_key(entry->hmac, hash, + silc_hash_len(silc_hmac_get_hash(entry->hmac))); memset(hash, 0, sizeof(hash)); /* Add to the private keys list */