X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=lib%2Fsilcclient%2Fclient_channel.c;h=4120695d76af08286853fa5fc5bdf31f0f767220;hp=4d320bfbe10abc364b8c4e685185cc65250acfe0;hb=7f51164d1ac93077d832de44a8dc71783fab8b33;hpb=c93d979c288b14b8ad95dc49eecf3016ba9eb22d diff --git a/lib/silcclient/client_channel.c b/lib/silcclient/client_channel.c index 4d320bfb..4120695d 100644 --- a/lib/silcclient/client_channel.c +++ b/lib/silcclient/client_channel.c @@ -40,6 +40,7 @@ SilcBool silc_client_send_channel_message(SilcClient client, SilcCipher cipher; SilcHmac hmac; SilcBool ret; + SilcID sid, rid; SILC_LOG_DEBUG(("Sending channel message")); @@ -73,12 +74,12 @@ SilcBool silc_client_send_channel_message(SilcClient client, if (channel->internal.private_keys) { if (key) { /* Use key application specified */ - cipher = key->cipher; + cipher = key->send_key; hmac = key->hmac; } else if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY && channel->internal.curr_key) { /* Use current private key */ - cipher = channel->internal.curr_key->cipher; + cipher = channel->internal.curr_key->send_key; hmac = channel->internal.curr_key->hmac; } else if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY && !channel->internal.curr_key && @@ -87,7 +88,7 @@ SilcBool silc_client_send_channel_message(SilcClient client, and private keys are set. */ silc_dlist_start(channel->internal.private_keys); key = silc_dlist_get(channel->internal.private_keys); - cipher = key->cipher; + cipher = key->send_key; hmac = key->hmac; /* Use this key as current private key */ @@ -109,9 +110,14 @@ SilcBool silc_client_send_channel_message(SilcClient client, } /* Encode the message payload. This also encrypts the message payload. */ + sid.type = SILC_ID_CLIENT; + sid.u.client_id = chu->client->id; + rid.type = SILC_ID_CHANNEL; + rid.u.channel_id = chu->channel->id; buffer = silc_message_payload_encode(flags, data, data_len, TRUE, FALSE, cipher, hmac, client->rng, NULL, - conn->private_key, hash, NULL); + conn->private_key, hash, &sid, &rid, + NULL); if (silc_unlikely(!buffer)) { SILC_LOG_ERROR(("Error encoding channel message")); return FALSE; @@ -182,7 +188,7 @@ SILC_FSM_STATE(silc_client_channel_message) /* Get sender client entry */ client_entry = silc_client_get_client_by_id(client, conn, &remote_id); - if (!client_entry || !client_entry->nickname[0]) { + if (!client_entry || !client_entry->internal.valid) { /** Resolve client info */ silc_client_unref_client(client, conn, client_entry); SILC_FSM_CALL(silc_client_get_client_by_id_resolve( @@ -225,8 +231,10 @@ SILC_FSM_STATE(silc_client_channel_message) payload = silc_message_payload_parse(silc_buffer_data(buffer), silc_buffer_len(buffer), FALSE, FALSE, channel->internal.receive_key, - channel->internal.hmac, NULL, - FALSE, NULL); + channel->internal.hmac, + packet->src_id, packet->src_id_len, + packet->dst_id, packet->dst_id_len, + NULL, FALSE, NULL); /* 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 @@ -251,6 +259,10 @@ SILC_FSM_STATE(silc_client_channel_message) payload = silc_message_payload_parse(silc_buffer_data(buffer), silc_buffer_len(buffer), FALSE, FALSE, cipher, hmac, + packet->src_id, + packet->src_id_len, + packet->dst_id, + packet->dst_id_len, NULL, FALSE, NULL); if (payload) break; @@ -266,8 +278,12 @@ SILC_FSM_STATE(silc_client_channel_message) silc_buffer_len(buffer), FALSE, FALSE, channel->internal.receive_key, - channel->internal.hmac, NULL, - FALSE, NULL); + channel->internal.hmac, + packet->src_id, + packet->src_id_len, + packet->dst_id, + packet->dst_id_len, + NULL, FALSE, NULL); if (!payload) { silc_dlist_start(channel->internal.private_keys); @@ -275,8 +291,12 @@ SILC_FSM_STATE(silc_client_channel_message) /* Parse the message payload. This also decrypts the payload */ payload = silc_message_payload_parse(silc_buffer_data(buffer), silc_buffer_len(buffer), - FALSE, FALSE, key->cipher, - key->hmac, NULL, FALSE, NULL); + FALSE, FALSE, key->receive_key, + key->hmac, packet->src_id, + packet->src_id_len, + packet->dst_id, + packet->dst_id_len, + NULL, FALSE, NULL); if (payload) break; } @@ -443,12 +463,16 @@ SilcBool silc_client_save_channel_key(SilcClient client, return FALSE; } + channel->cipher = silc_cipher_get_name(channel->internal.send_key); + channel->hmac = silc_hmac_get_name(channel->internal.hmac); + /* Set HMAC key */ silc_hash_make(silc_hmac_get_hash(channel->internal.hmac), key, tmp_len, hash); silc_hmac_set_key(channel->internal.hmac, hash, silc_hash_len(silc_hmac_get_hash(channel->internal.hmac))); memset(hash, 0, sizeof(hash)); + silc_channel_key_payload_free(payload); silc_client_unref_channel(client, conn, channel); @@ -524,20 +548,30 @@ SilcBool silc_client_add_channel_private_key(SilcClient client, entry->name = name ? strdup(name) : NULL; /* Allocate the cipher and set the key */ - if (!silc_cipher_alloc(cipher, &entry->cipher)) { + if (!silc_cipher_alloc(cipher, &entry->send_key)) { silc_free(entry); silc_free(entry->name); silc_ske_free_key_material(keymat); return FALSE; } - silc_cipher_set_key(entry->cipher, keymat->send_enc_key, + if (!silc_cipher_alloc(cipher, &entry->receive_key)) { + silc_free(entry); + silc_free(entry->name); + silc_cipher_free(entry->send_key); + silc_ske_free_key_material(keymat); + return FALSE; + } + silc_cipher_set_key(entry->send_key, keymat->send_enc_key, keymat->enc_key_len, TRUE); + silc_cipher_set_key(entry->receive_key, keymat->send_enc_key, + keymat->enc_key_len, FALSE); /* Generate HMAC key from the channel key data and set it */ if (!silc_hmac_alloc(hmac, NULL, &entry->hmac)) { silc_free(entry); silc_free(entry->name); - silc_cipher_free(entry->cipher); + silc_cipher_free(entry->send_key); + silc_cipher_free(entry->receive_key); silc_ske_free_key_material(keymat); return FALSE; } @@ -550,8 +584,11 @@ SilcBool silc_client_add_channel_private_key(SilcClient client, /* Add to the private keys list */ silc_dlist_add(channel->internal.private_keys, entry); - if (!channel->internal.curr_key) + if (!channel->internal.curr_key) { channel->internal.curr_key = entry; + channel->cipher = silc_cipher_get_name(entry->send_key); + channel->hmac = silc_cipher_get_name(entry->send_key); + } /* Free the key material */ silc_ske_free_key_material(keymat); @@ -582,12 +619,21 @@ SilcBool silc_client_del_channel_private_keys(SilcClient client, while ((entry = silc_dlist_get(channel->internal.private_keys))) { silc_dlist_del(channel->internal.private_keys, entry); silc_free(entry->name); - silc_cipher_free(entry->cipher); + silc_cipher_free(entry->send_key); + silc_cipher_free(entry->receive_key); silc_hmac_free(entry->hmac); silc_free(entry); } channel->internal.curr_key = NULL; + if (channel->internal.send_key) + channel->cipher = silc_cipher_get_name(channel->internal.send_key); + else + channel->cipher = NULL; + if (channel->internal.hmac) + channel->hmac = silc_hmac_get_name(channel->internal.hmac); + else + channel->hmac = NULL; silc_dlist_uninit(channel->internal.private_keys); channel->internal.private_keys = NULL; @@ -619,12 +665,16 @@ SilcBool silc_client_del_channel_private_key(SilcClient client, if (entry != key) continue; - if (channel->internal.curr_key == entry) + if (channel->internal.curr_key == entry) { channel->internal.curr_key = NULL; + channel->cipher = silc_cipher_get_name(channel->internal.send_key); + channel->hmac = silc_hmac_get_name(channel->internal.hmac); + } silc_dlist_del(channel->internal.private_keys, entry); silc_free(entry->name); - silc_cipher_free(entry->cipher); + silc_cipher_free(entry->send_key); + silc_cipher_free(entry->receive_key); silc_hmac_free(entry->hmac); silc_free(entry); @@ -681,6 +731,8 @@ void silc_client_current_channel_private_key(SilcClient client, if (!channel) return; channel->internal.curr_key = key; + channel->cipher = silc_cipher_get_name(key->send_key); + channel->hmac = silc_hmac_get_name(key->hmac); } /***************************** Utility routines *****************************/ @@ -702,7 +754,8 @@ SilcChannelUser silc_client_on_channel(SilcChannelEntry channel, } /* Adds client to channel. Returns TRUE if user was added or is already - added to the channel, FALSE on error. */ + added to the channel, FALSE on error. Must be called with both `channel' + and `client_entry' locked. */ SilcBool silc_client_add_to_channel(SilcClient client, SilcClientConnection conn, @@ -734,7 +787,8 @@ SilcBool silc_client_add_to_channel(SilcClient client, return TRUE; } -/* Removes client from a channel */ +/* Removes client from a channel. Returns FALSE if user is not on channel. + This handles entry locking internally. */ SilcBool silc_client_remove_from_channel(SilcClient client, SilcClientConnection conn, @@ -749,6 +803,9 @@ SilcBool silc_client_remove_from_channel(SilcClient client, SILC_LOG_DEBUG(("Remove client %s from channel", client_entry->nickname)); + silc_rwlock_wrlock(client_entry->internal.lock); + silc_rwlock_wrlock(channel->internal.lock); + silc_hash_table_del(chu->client->channels, chu->channel); silc_hash_table_del(chu->channel->user_list, chu->client); silc_free(chu); @@ -757,13 +814,17 @@ SilcBool silc_client_remove_from_channel(SilcClient client, if (!silc_hash_table_count(channel->user_list)) silc_client_del_channel(client, conn, channel); + silc_rwlock_unlock(client_entry->internal.lock); + silc_rwlock_unlock(channel->internal.lock); + silc_client_unref_client(client, conn, client_entry); silc_client_unref_channel(client, conn, channel); return TRUE; } -/* Removes a client entry from all channels it has joined. */ +/* Removes a client entry from all channels it has joined. This handles + entry locking internally. */ void silc_client_remove_from_channels(SilcClient client, SilcClientConnection conn, @@ -777,8 +838,12 @@ void silc_client_remove_from_channels(SilcClient client, SILC_LOG_DEBUG(("Remove client from all joined channels")); + silc_rwlock_wrlock(client_entry->internal.lock); + silc_hash_table_list(client_entry->channels, &htl); while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { + silc_rwlock_wrlock(chu->channel->internal.lock); + silc_hash_table_del(chu->client->channels, chu->channel); silc_hash_table_del(chu->channel->user_list, chu->client); @@ -786,15 +851,19 @@ void silc_client_remove_from_channels(SilcClient client, if (!silc_hash_table_count(chu->channel->user_list)) silc_client_del_channel(client, conn, chu->channel); + silc_rwlock_unlock(chu->channel->internal.lock); + silc_client_unref_client(client, conn, chu->client); silc_client_unref_channel(client, conn, chu->channel); silc_free(chu); } + silc_rwlock_unlock(client_entry->internal.lock); + silc_hash_table_list_reset(&htl); } -/* Empties channel from users. */ +/* Empties channel from users. This handles entry locking internally. */ void silc_client_empty_channel(SilcClient client, SilcClientConnection conn, @@ -803,6 +872,8 @@ void silc_client_empty_channel(SilcClient client, SilcHashTableList htl; SilcChannelUser chu; + silc_rwlock_wrlock(channel->internal.lock); + silc_hash_table_list(channel->user_list, &htl); while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { silc_hash_table_del(chu->client->channels, chu->channel); @@ -811,5 +882,73 @@ void silc_client_empty_channel(SilcClient client, silc_client_unref_channel(client, conn, chu->channel); silc_free(chu); } + + silc_rwlock_unlock(channel->internal.lock); + silc_hash_table_list_reset(&htl); } + +/* Save public keys to channel public key list. Removes keys that are + marked to be removed. Must be called with `channel' locked. */ + +SilcBool silc_client_channel_save_public_keys(SilcChannelEntry channel, + unsigned char *chpk_list, + SilcUInt32 chpk_list_len, + SilcBool remove_all) +{ + SilcArgumentDecodedList a, b; + SilcDList chpks; + SilcBool found; + + if (remove_all) { + /* Remove all channel public keys */ + if (!channel->channel_pubkeys) + return FALSE; + + silc_dlist_start(channel->channel_pubkeys); + while ((b = silc_dlist_get(channel->channel_pubkeys))) + silc_dlist_del(channel->channel_pubkeys, b); + + silc_dlist_uninit(channel->channel_pubkeys); + channel->channel_pubkeys = NULL; + + return TRUE; + } + + /* Parse channel public key list and add or remove public keys */ + chpks = silc_argument_list_parse_decoded(chpk_list, chpk_list_len, + SILC_ARGUMENT_PUBLIC_KEY); + if (!chpks) + return FALSE; + + if (!channel->channel_pubkeys) { + channel->channel_pubkeys = silc_dlist_init(); + if (!channel->channel_pubkeys) { + silc_argument_list_free(chpks, SILC_ARGUMENT_PUBLIC_KEY); + return FALSE; + } + } + + silc_dlist_start(chpks); + while ((a = silc_dlist_get(chpks))) { + found = FALSE; + silc_dlist_start(channel->channel_pubkeys); + while ((b = silc_dlist_get(channel->channel_pubkeys))) { + if (silc_pkcs_public_key_compare(a->argument, b->argument)) { + found = TRUE; + break; + } + } + + if ((a->arg_type == 0x00 || a->arg_type == 0x03) && !found) { + silc_dlist_add(channel->channel_pubkeys, a); + silc_dlist_del(chpks, a); + } else if (a->arg_type == 0x01 && found) { + silc_dlist_del(channel->channel_pubkeys, b); + } + } + + silc_argument_list_free(chpks, SILC_ARGUMENT_PUBLIC_KEY); + + return TRUE; +}